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