]> git.vomp.tv Git - vompclient.git/blob - playerradio.cc
dvbsubtitles fixup part 2
[vompclient.git] / playerradio.cc
1 /*
2     Copyright 2004-2006 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21 #include "playerradio.h"
22
23 #include "log.h"
24 #include "audio.h"
25 #include "video.h"
26 #include "demuxervdr.h"
27 #include "demuxerts.h"
28 #include "remote.h"
29 #include "vdr.h"
30 #include "message.h"
31 #include "messagequeue.h"
32
33 // ----------------------------------- Called from outside, one offs or info funcs
34
35 PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver)
36 : afeed(this)
37 {
38   messageQueue = tmessageQueue;
39   messageReceiver = tmessageReceiver;
40   audio = Audio::getInstance();
41   logger = Log::getInstance();
42   vdr = VDR::getInstance();
43   initted = false;
44   lengthBytes = 0;
45   lengthPackets = 0;
46   streamPos = 0;
47   state = S_STOP;
48
49   startPTS = 0;
50   lengthSeconds = 0;
51
52   threadBuffer = NULL;
53
54   blockSize = 10000;
55   startupBlockSize = 20000;
56
57   Video::getInstance()->turnVideoOff();
58 }
59
60 PlayerRadio::~PlayerRadio()
61 {
62   if (initted) shutdown();
63 }
64
65 int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets, bool isPesRecording)
66 {
67   if (initted) return 0;
68 #ifndef WIN32
69   pthread_mutex_init(&mutex, NULL);
70 #else
71   mutex=CreateMutex(NULL,FALSE,NULL);
72 #endif
73
74   if (isPesRecording)
75     demuxer = new DemuxerVDR();
76   else
77     demuxer = new DemuxerTS();
78   if (!demuxer) return 0;
79
80   if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))
81   {
82     logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");
83     shutdown();
84     return 0;
85   }
86
87   afeed.init();
88   audio->stop();
89
90   lengthBytes = tlengthBytes;
91   lengthPackets = tlengthPackets;
92
93   logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);
94
95   UINT thisRead = 0;
96   int success;
97
98   UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);
99   if (!buffer)
100   {
101     logger->log("PlayerRadio", Log::ERR, "Failed to get start block");
102     shutdown();
103     if (!vdr->isConnected()) doConnectionLost();
104     return 0;
105   }
106
107   success = demuxer->findPTS(buffer, thisRead, &startPTS);
108   if (!success)
109   {
110     logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");
111     free(buffer);
112     shutdown();
113     return 0;
114   }
115
116   free(buffer);
117
118   if (!setLengthSeconds())
119   {
120     logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");
121     shutdown();
122     return 0;
123   }
124
125   initted = true;
126   return 1;
127 }
128
129 bool PlayerRadio::setLengthSeconds()
130 {
131   int success;
132   ULLONG endPTS = 0;
133   UINT thisRead = 0;
134   UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);
135   if (!buffer)
136   {
137     logger->log("PlayerRadio", Log::ERR, "Failed to get end block");
138     if (!vdr->isConnected()) doConnectionLost();    
139     return false;
140   }
141
142   success = demuxer->findPTS(buffer, thisRead, &endPTS);
143   free(buffer);
144   if (!success)
145   {
146     logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");
147     return false;
148   }
149
150   if (startPTS < endPTS)
151   {
152     lengthSeconds = (endPTS - startPTS) / 90000;
153   }
154   else
155   {
156     lengthSeconds = (startPTS - endPTS) / 90000;
157   }
158
159   return true;
160 }
161
162 int PlayerRadio::shutdown()
163 {
164   if (!initted) return 0;
165   switchState(S_STOP);
166   initted = false;
167
168   delete demuxer;
169   demuxer = NULL;
170
171 #ifdef WIN32
172   CloseHandle(mutex);
173 #endif
174
175   return 1;
176 }
177
178
179 void PlayerRadio::setStartBytes(ULLONG startBytes)
180 {
181   streamPos = startBytes;
182 }
183
184 ULONG PlayerRadio::getLengthSeconds()
185 {
186   return lengthSeconds;
187 }
188
189 ULONG PlayerRadio::getCurrentSeconds()
190 {
191   if (startup) return 0;
192
193   long long currentPTS = demuxer->getAudioPTS();
194   currentPTS -= startPTS;
195   if (currentPTS < 0) currentPTS += 8589934592ULL;
196   ULONG ret = currentPTS / 90000;
197   return ret;
198 }
199
200 // ----------------------------------- Externally called events
201
202 void PlayerRadio::play()
203 {
204   if (!initted) return;
205   if (state == S_PLAY) return;
206   lock();
207   switchState(S_PLAY);
208   unLock();
209 }
210
211
212 void PlayerRadio::playpause()
213 {
214   if (!initted) return;
215   lock();
216   if (state==S_PLAY) {
217           switchState(S_PAUSE_P);
218   } else {
219           switchState(S_PLAY);
220   }
221   unLock();
222 }
223
224 void PlayerRadio::stop()
225 {
226   if (!initted) return;
227   if (state == S_STOP) return;
228   lock();
229   logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");
230   switchState(S_STOP);
231   unLock();
232 }
233
234 void PlayerRadio::pause()
235 {
236   if (!initted) return;
237   lock();
238
239   if (state == S_PAUSE_P)
240   {
241     switchState(S_PLAY);
242   }
243   else
244   {
245     switchState(S_PAUSE_P);
246   }
247
248   unLock();
249 }
250
251 void PlayerRadio::jumpToPercent(double percent)
252 {
253   lock();
254   logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);
255   ULONG newPacket = (ULONG)(percent * lengthPackets / 100);
256   switchState(S_JUMP, newPacket);
257   unLock();
258 }
259
260 void PlayerRadio::skipForward(UINT seconds)
261 {
262   lock();
263   logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
264   ULONG currentSeconds = getCurrentSeconds();
265   ULONG currentPacket = demuxer->getPacketNum();
266
267   if (currentSeconds == 0) { unLock(); return; } // div by zero
268   if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
269
270   ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds);
271   if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }
272   else switchState(S_JUMP, newPacket);
273   unLock();
274 }
275
276 void PlayerRadio::skipBackward(UINT seconds)
277 {
278   lock();
279   logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
280
281   ULONG currentSeconds = getCurrentSeconds();
282   ULONG currentPacket = demuxer->getPacketNum();
283
284   if (currentSeconds == 0) { unLock(); return; } // div by zero
285   if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid
286
287   ULONG newPacket;
288   if ((UINT)seconds > currentSeconds)
289     newPacket = 0;
290   else
291     newPacket = currentPacket - (currentPacket * seconds / currentSeconds);
292
293   switchState(S_JUMP, newPacket);
294   unLock();
295 }
296
297 // ----------------------------------- Implementations called events
298
299 void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)
300 {
301   if (!initted) return;
302
303   logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);
304
305   switch(state) // current state selector
306   {
307     case S_PLAY: // from S_PLAY -----------------------------------
308     {
309       switch(toState)
310       {
311         case S_PLAY: // to S_PLAY
312         {
313           return;
314         }
315         case S_PAUSE_P: // to S_PAUSE_P
316         {
317           audio->pause();
318           state = S_PAUSE_P;
319           return;
320         }
321         case S_STOP: // to S_STOP
322         {
323           afeed.stop();
324           threadStop();
325           audio->stop();
326           audio->unPause();
327           demuxer->reset();
328           state = S_STOP;
329           return;
330         }
331         case S_JUMP: // to S_JUMP
332         {
333           restartAtPacket(jumpPacket);
334           return;
335         }
336       }
337     }
338     case S_PAUSE_P: // from S_PAUSE_P -----------------------------------
339     {
340       switch(toState)
341       {
342         case S_PLAY: // to S_PLAY
343         {
344           audio->unPause();
345           state = S_PLAY;
346           return;
347         }
348         case S_PAUSE_P: // to S_PAUSE_P
349         {
350           return;
351         }
352         case S_STOP: // to S_STOP
353         {
354           afeed.stop();
355           threadStop();
356           audio->stop();
357           audio->unPause();
358           demuxer->reset();
359           audio->systemMuteOff();
360           state = S_STOP;
361           return;
362         }
363         case S_JUMP: // to S_JUMP
364         {
365           state = S_PLAY;
366           audio->unPause();
367           restartAtPacket(jumpPacket);
368           return;
369         }
370       }
371     }
372     case S_STOP: // from S_STOP -----------------------------------
373     {
374       switch(toState)
375       {
376         case S_PLAY: // to S_PLAY
377         {
378           startup = true;
379
380           audio->reset();
381                                         audio->setStreamType(Audio::MPEG2_PES);
382           audio->systemMuteOff();
383           demuxer->reset();
384
385           // FIXME use restartAtPacket here?
386           if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;
387           demuxer->setPacketNum(currentPacketNumber);
388           state = S_PLAY;
389           threadStart();
390           logger->log("PlayerRadio", Log::DEBUG, "Immediate play");
391           afeed.start();
392           audio->play();
393
394           return;
395         }
396         case S_PAUSE_P: // to S_PAUSE_P
397         {
398           return;
399         }
400         case S_STOP: // to S_STOP
401         {
402           return;
403         }
404         case S_JUMP: // to S_JUMP
405         {
406           return;
407         }
408       }
409     }
410     // case S_JUMP cannot be selected as a start state because it auto flips to play
411   }
412 }
413
414 // ----------------------------------- Internal functions
415
416 void PlayerRadio::lock()
417 {
418 #ifndef WIN32
419   pthread_mutex_lock(&mutex);
420   logger->log("PlayerRadio", Log::DEBUG, "LOCKED");
421
422 #else
423    WaitForSingleObject(mutex, INFINITE);
424 #endif
425 }
426
427 void PlayerRadio::unLock()
428 {
429 #ifndef WIN32
430   logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");
431   pthread_mutex_unlock(&mutex);
432 #else
433    ReleaseMutex(mutex);
434 #endif
435 }
436
437 void PlayerRadio::restartAtPacket(ULONG newPacket)
438 {
439   afeed.stop();
440   threadStop();
441   audio->reset();
442         audio->setStreamType(Audio::MPEG2_PES);
443   demuxer->flush();
444   currentPacketNumber = newPacket;
445   demuxer->setPacketNum(newPacket);
446   afeed.start();
447   threadStart();
448   audio->play();
449   audio->systemMuteOff();
450   audio->doMuting();
451 }
452
453 void PlayerRadio::doConnectionLost()
454 {
455   logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");
456   Message* m = new Message();
457   m->to = messageReceiver;
458   m->from = this;
459   m->message = Message::PLAYER_EVENT;
460   m->parameter = PlayerRadio::CONNECTION_LOST;
461   messageQueue->postMessage(m);
462 }
463
464 // ----------------------------------- Callback
465
466 void PlayerRadio::call(void* caller)
467 {
468   threadSignalNoLock();
469 }
470
471 // ----------------------------------- Feed thread
472
473 void PlayerRadio::threadMethod()
474 {
475   if (state == S_PLAY) threadFeedPlay();
476 }
477
478 void PlayerRadio::threadFeedPlay()
479 {
480   ULLONG feedPosition;
481   UINT thisRead, writeLength, thisWrite, askFor;
482   time_t lastRescan = time(NULL);
483
484   feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);
485   if (!vdr->isConnected()) { doConnectionLost(); return; }
486   logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);
487
488
489   while(1)
490   {
491     thisRead = 0;
492     writeLength = 0;
493     thisWrite = 0;
494
495     threadCheckExit();
496
497     // If we havn't rescanned for a while..
498     if ((lastRescan + 60) < time(NULL))
499     {
500       lengthBytes = vdr->rescanRecording(&lengthPackets);
501       if (!vdr->isConnected()) { doConnectionLost(); return; }
502       logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);
503       lastRescan = time(NULL);
504
505       if (!setLengthSeconds())
506       {
507         logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");
508         return;
509       }
510     }
511
512     if (feedPosition >= lengthBytes) break;  // finished playback
513
514     if (startup)
515     {
516       if (startupBlockSize > lengthBytes)
517         askFor = lengthBytes; // is a very small recording!
518       else
519         askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
520     }
521     else
522     {
523       if ((feedPosition + blockSize) > lengthBytes) // last block of recording
524         askFor = lengthBytes - feedPosition;
525       else // normal
526         askFor = blockSize;
527     }
528
529     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
530     feedPosition += thisRead;
531
532     if (!vdr->isConnected())
533     {
534       doConnectionLost();
535       return;
536     }
537
538     if (!threadBuffer) break;
539
540     if (startup)
541     {
542       int a_stream = demuxer->scan(threadBuffer, thisRead);
543       demuxer->setAudioStream(a_stream);
544       logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
545       startup = false;
546     }
547
548     threadCheckExit();
549
550     while(writeLength < thisRead)
551     {
552       thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);
553       writeLength += thisWrite;
554
555       if (!thisWrite)
556       {
557         // demuxer is full and can't take anymore
558         threadLock();
559         threadWaitForSignal();
560         threadUnlock();
561       }
562
563       threadCheckExit();
564     }
565
566     free(threadBuffer);
567     threadBuffer = NULL;
568
569   }
570
571   // end of recording
572   logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");
573
574   threadCheckExit();
575
576
577   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
578   m->to = messageReceiver;
579   m->from = this;
580   m->message = Message::PLAYER_EVENT;
581   m->parameter = PlayerRadio::STOP_PLAYBACK;
582   logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);
583   messageQueue->postMessage(m);
584 }
585
586 void PlayerRadio::threadPostStopCleanup()
587 {
588   if (threadBuffer)
589   {
590     free(threadBuffer);
591     threadBuffer = NULL;
592   }
593 }
594