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