~ubuntu-branches/ubuntu/wily/me-tv/wily

« back to all changes in this revision

Viewing changes to src/frontend_thread.cc

  • Committer: Bazaar Package Importer
  • Author(s): Michael Lamothe
  • Date: 2010-06-20 21:51:41 UTC
  • mfrom: (1.1.13 upstream) (3.1.12 sid)
  • Revision ID: james.westby@ubuntu.com-20100620215141-jfzya7s0a5g8xxl4
Tags: 1.2.6-1
* New upstream release
* Fix "needs to depend on libxine1-x" updated control file to
  depend on ${xine-x:Depends} (Closes: #575122)
* Fix "EPG texts with ampersands not shown" encoded XML strings
  in GTK controls that use markup (Closes: #582613)
* Updated the description of the application to remove reference
  to MythTV

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
#include "application.h"
23
23
#include "dvb_si.h"
24
24
 
25
 
class Lock : public Glib::RecMutex::Lock
26
 
{
27
 
public:
28
 
        Lock(Glib::RecMutex& mutex, const Glib::ustring& name) :
29
 
                Glib::RecMutex::Lock(mutex) {}
30
 
        ~Lock() {}
31
 
};
 
25
#define READ_TIMEOUT 10 /* In milliseconds */
32
26
 
33
27
FrontendThread::FrontendThread(Dvb::Frontend& f) : Thread("Frontend"), frontend(f)
34
28
{
35
29
        g_debug("Creating FrontendThread (%s)", frontend.get_path().c_str());
36
30
        
37
31
        epg_thread = NULL;
38
 
        is_tuned = false;
39
 
 
 
32
 
 
33
        Glib::ustring input_path = frontend.get_adapter().get_dvr_path();
 
34
 
 
35
        g_debug("Opening frontend device '%s' for reading ...", input_path.c_str());
 
36
        if ( (fd = ::open(input_path.c_str(), O_RDONLY | O_NONBLOCK) ) < 0 )
 
37
        {
 
38
                throw SystemException("Failed to open frontend device");
 
39
        }
 
40
        
40
41
        g_debug("FrontendThread created (%s)", frontend.get_path().c_str());
41
42
}
42
43
 
43
44
FrontendThread::~FrontendThread()
44
45
{
45
46
        g_debug("Destroying FrontendThread (%s)", frontend.get_path().c_str());
 
47
        
 
48
        g_debug("About to close input channel ...");
 
49
        ::close(fd);
 
50
        
46
51
        stop();
 
52
        
 
53
        g_debug("FrontendThread destroyed (%s)", frontend.get_path().c_str());
47
54
}
48
55
 
49
56
void FrontendThread::start()
50
57
{
51
 
        is_tuned = false;
52
 
        g_debug("Starting frontend thread (%s)", frontend.get_path().c_str());
53
 
        Thread::start();
 
58
        if (is_terminated())
 
59
        {
 
60
                g_debug("Starting frontend thread (%s)", frontend.get_path().c_str());
 
61
                Thread::start();
 
62
        }
54
63
}
55
64
 
56
65
void FrontendThread::stop()
57
66
{
58
67
        g_debug("Stopping frontend thread (%s)", frontend.get_path().c_str());
59
 
 
60
68
        stop_epg_thread();
61
 
 
62
 
        g_debug("Deleting channel streams");
63
 
        ChannelStreamList::iterator iterator = streams.begin();
64
 
        while (iterator != streams.end())
65
 
        {
66
 
                ChannelStream* channel_stream = *iterator;
67
 
                delete channel_stream;
68
 
                iterator = streams.erase(iterator);
69
 
        }
70
 
 
71
69
        join(true);
72
 
        g_debug("Frontend thread stopped (%s)", frontend.get_path().c_str());
 
70
        g_debug("Frontend thread stopped and joined (%s)", frontend.get_path().c_str());
73
71
}
74
72
 
75
73
void FrontendThread::run()
76
74
{
77
75
        g_debug("Frontend thread running (%s)", frontend.get_path().c_str());
78
76
 
 
77
        struct pollfd pfds[1];
 
78
        pfds[0].fd = fd;
 
79
        pfds[0].events = POLLIN;
 
80
        
79
81
        guchar buffer[TS_PACKET_SIZE * PACKET_BUFFER_SIZE];
80
82
        guchar pat[TS_PACKET_SIZE];
81
83
        guchar pmt[TS_PACKET_SIZE];
82
84
 
83
 
        Glib::ustring input_path = frontend.get_adapter().get_dvr_path();
84
 
 
85
 
        int fd = 0;
86
 
        if ( (fd = ::open( input_path.c_str(), O_RDONLY | O_NONBLOCK) ) < 0 )
87
 
        {
88
 
                throw SystemException("Failed to open frontend");
89
 
        }
90
 
                
91
 
        g_debug("Opening frontend device '%s' for reading ...", input_path.c_str());
92
 
        Glib::RefPtr<Glib::IOChannel> input_channel = Glib::IOChannel::create_from_fd(fd);
93
 
        input_channel->set_encoding("");
94
 
        g_debug("Frontend device opened (%s)", frontend.get_path().c_str());
95
 
        
96
85
        guint last_insert_time = 0;
97
 
        gsize bytes_read;
98
86
        
99
87
        g_debug("Entering FrontendThread loop (%s)", frontend.get_path().c_str());
100
88
        while (!is_terminated())
101
89
        {
102
 
                if (!is_tuned)
103
 
                {
104
 
                        g_debug("Frontend is not tuned, waiting (%s)", frontend.get_path().c_str());
105
 
                        usleep(1000000);
106
 
                }
107
 
                else
108
 
                {
109
 
                        usleep(10000);
110
 
 
111
 
                        try
112
 
                        {
113
 
                                Lock lock(mutex, __PRETTY_FUNCTION__);
114
 
 
115
 
                                if (input_channel->read((gchar*)buffer, TS_PACKET_SIZE * PACKET_BUFFER_SIZE, bytes_read) == Glib::IO_STATUS_NORMAL)
116
 
                                {
117
 
                                        // Insert PAT/PMT every second second
118
 
                                        time_t now = time(NULL);
119
 
                                        if (now - last_insert_time > 2)
120
 
                                        {
121
 
                                                g_debug("Writing PAT/PMT header");
122
 
 
123
 
                                                for (ChannelStreamList::iterator i = streams.begin(); i != streams.end(); i++)
124
 
                                                {
125
 
                                                        ChannelStream& channel_stream = **i;
126
 
 
127
 
                                                        channel_stream.stream.build_pat(pat);
128
 
                                                        channel_stream.stream.build_pmt(pmt);
129
 
 
130
 
                                                        channel_stream.write(pat, TS_PACKET_SIZE);
131
 
                                                        channel_stream.write(pmt, TS_PACKET_SIZE);
132
 
                                                }
133
 
                                                last_insert_time = now;
134
 
                                        }
135
 
 
136
 
                                        for (guint offset = 0; offset < bytes_read; offset += TS_PACKET_SIZE)
137
 
                                        {
138
 
                                                guint pid = ((buffer[offset+1] & 0x1f) << 8) + buffer[offset+2];
139
 
 
140
 
                                                for (ChannelStreamList::iterator i = streams.begin(); i != streams.end(); i++)
141
 
                                                {
142
 
                                                        ChannelStream& channel_stream = **i;
143
 
                                                        if (channel_stream.stream.contains_pid(pid))
144
 
                                                        {
145
 
                                                                channel_stream.write(buffer+offset, TS_PACKET_SIZE);
146
 
                                                        }
147
 
                                                }
148
 
                                        }
149
 
                                }
150
 
                        }
151
 
                        catch(...)
152
 
                        {
153
 
                                // The show must go on!
154
 
                        }
 
90
                if (streams.empty())
 
91
                {
 
92
                        usleep(100000);
 
93
                        continue;
 
94
                }
 
95
        
 
96
                try
 
97
                {
 
98
                        if (::poll(pfds, 1, READ_TIMEOUT) < 0)
 
99
                        {
 
100
                                throw SystemException("Frontend poll failed");
 
101
                        }
 
102
 
 
103
                        gint bytes_read = ::read(fd, buffer, TS_PACKET_SIZE * PACKET_BUFFER_SIZE);
 
104
 
 
105
                        if (bytes_read < 0)
 
106
                        {
 
107
                                if (errno == EAGAIN)
 
108
                                {
 
109
                                        continue;
 
110
                                }
 
111
 
 
112
                                Glib::ustring message = Glib::ustring::compose("Frontend read failed (%1)", frontend.get_path().c_str());
 
113
                                throw SystemException(message);
 
114
                        }
 
115
 
 
116
                        // Insert PAT/PMT every second second
 
117
                        time_t now = time(NULL);
 
118
                        if (now - last_insert_time > 2)
 
119
                        {
 
120
                                g_debug("Writing PAT/PMT header");
 
121
 
 
122
                                for (ChannelStreamList::iterator i = streams.begin(); i != streams.end(); i++)
 
123
                                {
 
124
                                        ChannelStream& channel_stream = **i;
 
125
 
 
126
                                        channel_stream.stream.build_pat(pat);
 
127
                                        channel_stream.stream.build_pmt(pmt);
 
128
 
 
129
                                        channel_stream.write(pat, TS_PACKET_SIZE);
 
130
                                        channel_stream.write(pmt, TS_PACKET_SIZE);
 
131
                                }
 
132
                                last_insert_time = now;
 
133
                        }
 
134
 
 
135
                        for (guint offset = 0; offset < (guint)bytes_read; offset += TS_PACKET_SIZE)
 
136
                        {
 
137
                                guint pid = ((buffer[offset+1] & 0x1f) << 8) + buffer[offset+2];
 
138
 
 
139
                                for (ChannelStreamList::iterator i = streams.begin(); i != streams.end(); i++)
 
140
                                {
 
141
                                        ChannelStream& channel_stream = **i;
 
142
                                        if (channel_stream.stream.contains_pid(pid))
 
143
                                        {
 
144
                                                channel_stream.write(buffer+offset, TS_PACKET_SIZE);
 
145
                                        }
 
146
                                }
 
147
                        }
 
148
                }
 
149
                catch(...)
 
150
                {
 
151
                        // The show must go on!
155
152
                }
156
153
        }
157
154
                
158
155
        g_debug("FrontendThread loop exited (%s)", frontend.get_path().c_str());
159
 
        
160
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
161
 
 
162
 
        g_debug("Removing streams ...");
163
 
        ChannelStreamList::iterator iterator = streams.begin();
164
 
        while (iterator != streams.end())
165
 
        {
166
 
                (*iterator)->output_channel.reset();
167
 
                streams.pop_back();
168
 
                delete *iterator;
169
 
                iterator = streams.begin();
170
 
        }
171
 
        g_debug("Streams removed");
172
 
 
173
 
        g_debug("About to close input channel ...");
174
 
        input_channel->close(true);
175
 
        input_channel.reset();
176
 
        g_debug("Input channel reset (%s)", frontend.get_path().c_str());
177
156
}
178
157
 
179
158
void FrontendThread::setup_dvb(ChannelStream& channel_stream)
180
159
{
181
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
182
 
        
183
160
        Glib::ustring demux_path = frontend.get_adapter().get_demux_path();
184
161
 
185
162
        Buffer buffer;
186
163
        const Channel& channel = channel_stream.channel;
187
164
        
188
165
        channel_stream.clear_demuxers();
189
 
        if (!is_tuned || channel.transponder != transponder)
 
166
        if (channel.transponder != frontend.get_frontend_parameters())
190
167
        {
191
168
                stop_epg_thread();
192
 
                is_tuned = false;
193
169
                frontend.tune_to(channel.transponder);
194
 
                transponder = channel_stream.channel.transponder;
195
170
                start_epg_thread();
196
171
        }
197
172
        
243
218
        }
244
219
 
245
220
        g_debug("Finished setting up DVB (%s)", frontend.get_path().c_str());
246
 
 
247
 
        is_tuned = true;
248
221
}
249
222
 
250
223
void FrontendThread::start_epg_thread()
252
225
        if (!disable_epg_thread)
253
226
        {
254
227
                stop_epg_thread();
255
 
 
256
 
                Lock lock(mutex, __PRETTY_FUNCTION__);
257
 
 
258
228
                epg_thread = new EpgThread(frontend);
259
229
                epg_thread->start();
260
230
                g_debug("EPG thread started");
265
235
{
266
236
        if (!disable_epg_thread)
267
237
        {
268
 
                Lock lock(mutex, __PRETTY_FUNCTION__);
269
 
 
270
238
                if (epg_thread != NULL)
271
239
                {
272
240
                        g_debug("Stopping EPG thread (%s)", frontend.get_path().c_str());
280
248
void FrontendThread::start_display(Channel& channel)
281
249
{
282
250
        g_debug("FrontendThread::start_display(%s)", channel.name.c_str());
283
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
 
251
        stop();
284
252
        
285
253
        g_debug("Creating new stream output");
286
254
        
307
275
        close(fd);
308
276
        setup_dvb(*channel_stream);
309
277
        streams.push_back(channel_stream);
 
278
 
 
279
        start();
310
280
}
311
281
 
312
282
void FrontendThread::stop_display()
313
283
{
314
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
 
284
        stop();
315
285
 
316
286
        ChannelStreamList::iterator iterator = streams.begin();
317
287
 
329
299
                        iterator++;
330
300
                }
331
301
        }
 
302
 
 
303
        start();
 
304
}
 
305
 
 
306
Glib::ustring make_recording_filename(Channel& channel, const Glib::ustring& description)
 
307
{
 
308
        Glib::ustring start_time = get_local_time_text("%c");
 
309
        Glib::ustring filename;
 
310
        Glib::ustring title = description;
 
311
        
 
312
        if (title.empty())
 
313
        {
 
314
                EpgEvent epg_event;
 
315
                if (channel.epg_events.get_current(epg_event))
 
316
                {
 
317
                        title = epg_event.get_title();
 
318
                }
 
319
        }
 
320
        
 
321
        if (title.empty())
 
322
        {
 
323
                filename = Glib::ustring::compose
 
324
                (
 
325
                        "%1 - %2.mpeg",
 
326
                        channel.name,
 
327
                        start_time
 
328
                );
 
329
        }
 
330
        else
 
331
        {
 
332
                filename = Glib::ustring::compose
 
333
                (
 
334
                        "%1 - %2 - %3.mpeg",
 
335
                        title,
 
336
                        channel.name,
 
337
                        start_time
 
338
                );
 
339
        }
 
340
 
 
341
        // Clean filename
 
342
        Glib::ustring::size_type position = Glib::ustring::npos;
 
343
        while ((position = filename.find('/')) != Glib::ustring::npos)
 
344
        {
 
345
                filename.replace(position, 1, "_");
 
346
        }
 
347
 
 
348
        if (get_application().get_boolean_configuration_value("remove_colon"))
 
349
        {
 
350
                while ((position = filename.find(':')) != Glib::ustring::npos )
 
351
                {
 
352
                        filename.replace(position, 1, "_");
 
353
                }
 
354
        }
 
355
 
 
356
        Glib::ustring fixed_filename = Glib::filename_from_utf8(filename);
 
357
        
 
358
        return Glib::build_filename(
 
359
            get_application().get_string_configuration_value("recording_directory"),
 
360
            fixed_filename);
332
361
}
333
362
 
334
363
bool is_recording_stream(ChannelStream* channel_stream)
336
365
        return channel_stream->type == CHANNEL_STREAM_TYPE_RECORDING || channel_stream->type == CHANNEL_STREAM_TYPE_SCHEDULED_RECORDING;
337
366
}
338
367
 
339
 
void FrontendThread::start_recording(Channel& channel, const Glib::ustring& filename, gboolean scheduled)
 
368
void FrontendThread::start_recording(Channel& channel, const Glib::ustring& description, gboolean scheduled)
340
369
{
341
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
342
 
        
 
370
        stop(); 
 
371
 
343
372
        ChannelStreamType requested_type = scheduled ? CHANNEL_STREAM_TYPE_SCHEDULED_RECORDING :
344
373
                CHANNEL_STREAM_TYPE_RECORDING;
345
374
        ChannelStreamType current_type = CHANNEL_STREAM_TYPE_NONE;
379
408
                {
380
409
                        g_debug("Channel '%s' is currently not being recorded", channel.name.c_str());
381
410
 
382
 
                        if (channel.transponder != transponder)
 
411
                        if (channel.transponder != frontend.get_frontend_parameters())
383
412
                        {
384
413
                                g_debug("Need to change transponders to record this channel");
385
414
 
407
436
 
408
437
                        ChannelStream* channel_stream = new ChannelStream(
409
438
                                scheduled ? CHANNEL_STREAM_TYPE_SCHEDULED_RECORDING : CHANNEL_STREAM_TYPE_RECORDING,
410
 
                                channel, filename);
 
439
                                channel, make_recording_filename(channel, scheduled ? description : ""));
411
440
                        setup_dvb(*channel_stream);
412
441
                        streams.push_back(channel_stream);
413
442
                }
414
443
        }
415
 
                
416
444
        
417
445
        g_debug("New recording channel created (%s)", frontend.get_path().c_str());
 
446
 
 
447
        start();
418
448
}
419
449
 
420
450
void FrontendThread::stop_recording(const Channel& channel)
421
451
{
422
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
 
452
        stop();
423
453
 
424
454
        ChannelStreamList::iterator iterator = streams.begin();
425
455
 
436
466
                        iterator++;
437
467
                }
438
468
        }
 
469
 
 
470
        start();
439
471
}
440
472
 
441
473
guint FrontendThread::get_last_epg_update_time()
442
474
{
443
475
        guint result = 0;
444
476
 
445
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
446
477
        if (epg_thread != NULL)
447
478
        {
448
479
                result = epg_thread->get_last_epg_update_time();
453
484
 
454
485
gboolean FrontendThread::is_recording()
455
486
{
456
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
457
 
 
458
487
        for (ChannelStreamList::iterator i = streams.begin(); i != streams.end(); i++)
459
488
        {
460
489
                if (is_recording_stream(*i))
468
497
 
469
498
gboolean FrontendThread::is_display()
470
499
{
471
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
472
 
 
473
500
        for (ChannelStreamList::iterator i = streams.begin(); i != streams.end(); i++)
474
501
        {
475
502
                if ((*i)->type == CHANNEL_STREAM_TYPE_DISPLAY)
483
510
 
484
511
gboolean FrontendThread::is_recording(const Channel& channel)
485
512
{
486
 
        Lock lock(mutex, __PRETTY_FUNCTION__);
487
 
 
488
513
        for (ChannelStreamList::iterator i = streams.begin(); i != streams.end(); i++)
489
514
        {
490
515
                ChannelStream* channel_stream = *i;
496
521
 
497
522
        return false;
498
523
}
499