]> git.vomp.tv Git - vompclient-marten.git/blob - player.cc
Windows port. New sync code. Various other bug fixes.
[vompclient-marten.git] / player.cc
1 /*
2     Copyright 2004-2005 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 "player.h"
22
23 Player::Player(MessageQueue* messageQueue, UCHAR tIsRecording, UCHAR isRadio)
24 : vfeed(this), afeed(this)
25 {
26   commandMessageQueue = messageQueue;
27   audio = Audio::getInstance();
28   video = Video::getInstance();
29   logger = Log::getInstance();
30   initted = 0;
31   paused = false;
32   playing = false;
33   ffwd = false;
34   fbwd = false;
35   streamLength = 0;
36   feedPosition = 0;
37   feedMode = MODE_NORMAL;
38   videoStartup = false;
39   isRecording = tIsRecording;
40   lastRescan = 0;
41   startTS = 0;
42   endTS = 0;
43   startup = 1;
44   threadBuffer = NULL;
45
46   if (isRadio)
47   {
48     blockSize = 20000;
49     startupBlockSize = 60000;
50   video->turnVideoOff();
51   }
52   else
53   {
54     blockSize = 100000;
55     startupBlockSize = 250000;
56   video->turnVideoOn();
57   }
58 }
59
60 Player::~Player()
61 {
62   if (initted) shutdown();
63 }
64
65 int Player::init()
66 {
67   if (initted) return 0;
68
69   if (!demuxer.init(this))
70   {
71     logger->log("Player", Log::ERR, "Demuxer failed to init");
72     shutdown();
73     return 0;
74   }
75 #ifndef NEW_DEMUXER
76   vfeed.init(video->getFD());
77   afeed.init(audio->getFD());
78 #else
79   vfeed.init(video);
80   afeed.init(audio);
81 #endif
82
83   video->stop();
84   video->blank();
85   audio->stop();
86
87   initted = 1;
88   return 1;
89 }
90
91 int Player::shutdown()
92 {
93   if (!initted) return 0;
94   initted = 0;
95
96   logger->log("Player", Log::DEBUG, "Player shutdown...");
97
98   // copy of stop
99   if (playing)
100   {
101     playing = false;
102     threadStop();
103     video->stop();
104     video->blank();
105     audio->stop();
106     vfeed.stop();
107     afeed.stop();
108     video->reset();
109     demuxer.reset();
110     feedPosition = 0;
111   }
112   logger->log("Player", Log::DEBUG, "Player shutdown done");
113
114   return 1;
115 }
116
117 void Player::resyncAudio()
118 {
119   // Temp hopefully
120   if (!initted) return;
121   audio->pause();
122   MILLISLEEP(500);
123   audio->unPause();
124 }
125
126 void Player::resyncVideo()
127 {
128   // Temp hopefully
129   if (!initted) return;
130   video->pause();
131   MILLISLEEP(500);
132   video->unPause();
133 }
134
135 int Player::play()
136 {
137   if (!initted) return 0;
138
139   // If we are just paused, unpause!
140   if (paused)
141   {
142     togglePause();
143     return 1;
144   }
145
146   // If we are fast forwarding, set to normal
147   if (ffwd)
148   {
149     toggleFastForward();
150     return 1;
151   }
152
153   // If we are fast backwarding, set to normal
154   if (fbwd)
155   {
156     toggleFastBackward();
157     return 1;
158   }
159
160   // If we are already playing, bail // FIXME - resync?
161   if (playing)
162   {
163     logger->log("Player", Log::DEBUG, "DOING RESYNC");
164     resyncVideo();
165     return 1;
166   }
167
168   // Standard play start
169   logger->log("Player", Log::DEBUG, "Standard play start");
170
171   audio->reset();
172   video->reset();
173   demuxer.reset();
174
175 // ------------------------ This one works, but doesn't allow any pre-buffering.
176   videoStartup = true;
177   threadStart();
178   afeed.start();
179   video->sync();
180   audio->sync();
181   audio->play();
182   video->pause();
183
184
185 // ------------------------ This one doesn't work, but it should, and would allow for prebuffering.
186
187
188 // ------------------------------------------------------------------------------------------------
189
190   playing = true;
191   return 1;
192 }
193
194 void Player::stop()
195 {
196   if (!initted) return;
197   if (!playing) return;
198
199   logger->log("Player", Log::DEBUG, "Stop called", streamLength);
200
201   if (ffwd || fbwd)
202   {
203     ffwd = false;
204     fbwd = false;
205     afeed.enable();
206     video->unFastForward();
207     audio->systemMuteOff();
208     feedMode = MODE_NORMAL;
209   }
210
211   playing = false;
212   paused = false;
213
214   vfeed.stop();
215   afeed.stop();
216   threadStop();
217   video->stop();
218   video->blank();
219   audio->stop();
220   audio->unPause();
221   video->reset();
222   demuxer.reset();
223
224   feedPosition = 0;
225 }
226
227 void Player::togglePause()
228 {
229   if (!initted) return;
230   if (!playing) return;
231
232   if (ffwd) toggleFastForward();
233   if (fbwd) toggleFastBackward();
234
235   if (paused)
236   {
237     video->unPause();
238     audio->unPause();
239     paused = false;
240   }
241   else
242   {
243     video->pause();
244     audio->pause();
245     paused = true;
246   }
247 }
248
249 void Player::setPosition(ULLONG position)
250 {
251   feedPosition = position;
252 }
253
254 void Player::setLength(ULLONG length)
255 {
256   lastRescan = time(NULL);
257   streamLength = length;
258   logger->log("Player", Log::DEBUG, "Player has received length of %llu", streamLength);
259 }
260
261 void Player::restartAt(ULLONG timecode)
262 {
263   if (paused) togglePause();
264   if (ffwd) toggleFastForward();
265
266   ULONG wantedFrameNumber = video->timecodeToFrameNumber(timecode);
267   ULLONG newPosition = VDR::getInstance()->positionFromFrameNumber(wantedFrameNumber);
268   if (!VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
269   logger->log("Player", Log::DEBUG, "wantedframe %i feedpos %llu goto %llu", wantedFrameNumber, feedPosition, newPosition);
270
271   vfeed.stop();
272   afeed.stop();
273   threadStop();
274   video->stop();
275   video->reset();
276   audio->reset();
277   demuxer.flush();
278   feedPosition = newPosition;
279   videoStartup = true;
280   afeed.start();
281   threadStart();
282   audio->play();
283   video->sync();
284   audio->sync();
285   audio->systemMuteOff();
286   audio->doMuting();
287   fbwd = false;
288 }
289
290 void Player::skipForward(int seconds)
291 {
292   logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);
293   restartAt(getPositionTS() + (seconds * 90000));
294 }
295
296 void Player::skipBackward(int seconds)
297 {
298   logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);
299   long long newTimeCode = getPositionTS() - (seconds * 90000);
300   if (newTimeCode < 0) newTimeCode = 0;
301   restartAt(newTimeCode);
302 }
303
304 void Player::jumpToPercent(int percent)
305 {
306   logger->log("Player", Log::DEBUG, "JUMP TO %i%%", percent);
307   ULLONG newTimeCode = (ULLONG)(getEndTS() * ((float)percent / 100));
308   restartAt(newTimeCode);
309 }
310
311 void Player::toggleFastForward()
312 {
313   if (!initted) return;
314   if (!playing) return;
315
316   if (paused) togglePause();
317   if (fbwd) toggleFastBackward();
318
319   if (ffwd)
320   {
321     ffwd = false;
322     threadStop();
323     vfeed.stop();
324     afeed.stop();
325     video->stop();
326     audio->stop();
327     video->reset();
328     audio->reset();
329     demuxer.flush();
330
331     videoStartup = true;
332     afeed.enable();
333     afeed.start();
334     threadStart();
335     audio->play();
336     video->sync();
337     audio->sync();
338     audio->systemMuteOff();
339     audio->doMuting();
340     fbwd = false;
341   }
342   else
343   {
344     ffwd = true;
345     afeed.disable();
346     audio->systemMuteOn();
347     video->fastForward();
348   }
349 }
350
351 void Player::toggleFastBackward()
352 {
353   if (!initted) return;
354   if (!playing) return;
355
356   if (paused) togglePause();
357   if (ffwd) toggleFastForward();
358
359   if (fbwd)
360   {
361     fbwd = false;
362     afeed.enable();
363     audio->systemMuteOff();
364
365 //    threadStop();
366     feedMode = MODE_NORMAL;
367 //    threadStart();
368   }
369   else
370   {
371     fbwd = false;
372     afeed.disable();
373     audio->systemMuteOn();
374
375     threadStop();
376     feedMode = MODE_BACKWARDS;
377     video->reset();
378     video->play();
379     demuxer.flush();
380     threadStart();
381   }
382 }
383
384 ULLONG Player::getPositionTS()
385 {
386   if (startup) return 0ULL;
387   long long currentTS = video->getCurrentTimestamp() - startTS;
388   if (currentTS < 0) currentTS += 8589934592ULL;
389   return (ULLONG)currentTS;
390 }
391
392 ULLONG Player::getEndTS()
393 {
394   long long rendTS = endTS - startTS;
395   if (rendTS < 0) rendTS += 8589934592ULL;
396   return (ULLONG)rendTS;
397 }
398
399 void Player::call(void* caller)
400 {
401   if (caller == &demuxer)
402   {
403     logger->log("Player", Log::DEBUG, "Callback from demuxer");
404
405     if (video->getTVsize() == Video::ASPECT4X3)
406     {
407       logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");
408       return;
409     }
410
411     int dxCurrentAspect = demuxer.getAspectRatio();
412     if (dxCurrentAspect == Demuxer::ASPECT_4_3)
413     {
414       logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");
415       video->setAspectRatio(Video::ASPECT4X3);
416     }
417     else if (dxCurrentAspect == Demuxer::ASPECT_16_9)
418     {
419       logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");
420       video->setAspectRatio(Video::ASPECT16X9);
421     }
422     else
423     {
424       logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");
425     }
426
427   }
428   else
429   {
430     if (videoStartup)
431     {
432       videoStartup = false;
433       logger->log("Player", Log::DEBUG, "Starting VFeed");
434       MILLISLEEP(500);
435       logger->log("Player", Log::DEBUG, "Starting VFeed2");
436       video->reset();
437       video->play();
438       video->sync();
439       vfeed.start();
440     }
441
442     threadSignalNoLock();
443   }
444 }
445 void Player::doConnectionLost()
446 {
447   Message* m = new Message();
448   m->message = Message::CONNECTION_LOST;
449   m->to = this;
450   commandMessageQueue->postMessage(m);
451 }
452
453 // Feed thread
454
455 void Player::threadMethod()
456 {
457   UINT thisRead;
458   UINT writeLength;
459   UINT thisWrite;
460
461   VDR* vdr = VDR::getInstance();
462
463   UINT askFor;
464   while(1)
465   {
466     thisRead = 0;
467     writeLength = 0;
468     thisWrite = 0;
469
470     threadCheckExit();
471
472     // If we havn't rescanned for a while..
473     if (isRecording && ((lastRescan + 60) < time(NULL)))
474     {
475       streamLength = vdr->rescanRecording();
476       if (!vdr->isConnected()) { doConnectionLost(); return; }
477       logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", streamLength);
478       lastRescan = time(NULL);
479       setEndTS();
480     }
481
482     if (streamLength) // is playing a recording
483     {
484       if (feedPosition >= streamLength) break;  // finished playback
485
486       if (startup)
487       {
488         if (startupBlockSize > streamLength)
489           askFor = streamLength; // is a very small recording!
490         else
491           askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams
492       }
493       else
494       {
495         if ((feedPosition + blockSize) > streamLength) // last block of recording
496           askFor = streamLength - feedPosition;
497         else // normal
498           askFor = blockSize;
499       }
500     }
501     else // is playing live
502     {
503       if (startup)
504         askFor = startupBlockSize; // find audio streams sized block
505       else
506         askFor = blockSize; // normal
507     }
508
509     threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);
510     if (!vdr->isConnected())
511     {
512       doConnectionLost();
513       return;
514     }
515
516     if (!threadBuffer) break;
517
518     if (startup)
519     {
520       int a_stream = demuxer.scan(threadBuffer, thisRead);
521       demuxer.setAudioStream(a_stream);
522       logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);
523
524       setStartTS(thisRead);
525
526       if (isRecording) setEndTS();
527
528       startup = 0;
529     }
530
531     if (feedMode == MODE_NORMAL)
532     {
533       feedPosition += thisRead;
534     }
535     else if (feedMode == MODE_BACKWARDS)
536     {
537       if (feedPosition >= blockSize)
538       {
539         feedPosition -= blockSize;
540         demuxer.seek();
541       }
542       else
543       {
544         // got to the start of the recording.. revert to play mode? how?
545         feedPosition += thisRead;
546       }
547     }
548
549     threadCheckExit();
550
551     while(writeLength < thisRead)
552     {
553       thisWrite = demuxer.put(threadBuffer + writeLength, thisRead - writeLength);
554       writeLength += thisWrite;
555
556       if (!thisWrite)
557       {
558 //        logger->log("Player", Log::DEBUG, "DEMUXER FULL!!!");
559         // demuxer is full and cant take anymore
560         threadLock();
561         threadWaitForSignal();
562         threadUnlock();
563 //        logger->log("Player", Log::DEBUG, "BACK FROM WAIT");
564       }
565
566       threadCheckExit();
567     }
568
569     free(threadBuffer);
570     threadBuffer = NULL;
571
572   }
573
574   // end of recording
575   logger->log("Player", Log::DEBUG, "Recording playback ends");
576
577   threadCheckExit();
578
579   Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex
580   if (streamLength) m->message = Message::STOP_PLAYBACK;  // recording
581   else m->message = Message::STREAM_END;                  // live
582   logger->log("Player", Log::DEBUG, "Posting message to %p...", commandMessageQueue);
583   commandMessageQueue->postMessage(m);
584   logger->log("Player", Log::DEBUG, "Message posted...");
585 }
586
587 void Player::threadPostStopCleanup()
588 {
589   logger->log("Player", Log::DEBUG, "Post stop cleanup 1");
590   if (threadBuffer)
591   {
592     logger->log("Player", Log::DEBUG, "Post stop cleanup 2");
593     free(threadBuffer);
594     threadBuffer = NULL;
595   }
596   logger->log("Player", Log::DEBUG, "Post stop cleanup 3");
597 }
598
599 void Player::setStartTS(UINT dataInBuffer)
600 {
601   if (isRecording && feedPosition) // (feedPosition != 0)
602   {
603     // FIXME find out how much data need to get to find a TS
604     // Need to get the actual start of the recording
605
606     UINT thisRead;
607     UCHAR* tempBuffer = VDR::getInstance()->getBlock(0, 100000, &thisRead);
608     if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
609     if (!tempBuffer) return;
610     if (thisRead) demuxer.findVideoPTS(tempBuffer, thisRead, &startTS);
611     free(tempBuffer);
612   }
613   else
614   {
615     demuxer.findVideoPTS(threadBuffer, dataInBuffer, &startTS);
616   }
617 }
618
619 void Player::setEndTS()
620 {
621   logger->log("Player", Log::DEBUG, "Setting end TS");
622
623   UINT thisRead;
624   UCHAR* tempBuffer = VDR::getInstance()->getBlock((streamLength - 100000), 100000, &thisRead);
625   if (!tempBuffer && !VDR::getInstance()->isConnected()) { doConnectionLost(); return; }
626   if (!tempBuffer) return;
627   if (thisRead) demuxer.findVideoPTS(tempBuffer, thisRead, &endTS);
628   free(tempBuffer);
629   logger->log("Player", Log::DEBUG, "Set end TS");
630 }
631
632 #ifdef DEV
633 void Player::test1()
634 {
635   logger->log("Player", Log::DEBUG, "PLAYER TEST 1");
636   video->setAspectRatio(Video::ASPECT4X3);
637 }
638
639 void Player::test2()
640 {
641   logger->log("Player", Log::DEBUG, "PLAYER TEST 2");
642   video->setAspectRatio(Video::ASPECT16X9);
643 }
644 #endif