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