MythTV  master
mythplayer.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 #undef HAVE_AV_CONFIG_H
4 
5 // C++ headers
6 #include <algorithm>
7 #include <cassert>
8 #include <cmath> // for fabs, ceil, round, signbit
9 #include <cstdint>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <unistd.h>
13 using namespace std;
14 
15 // Qt headers
16 #include <QCoreApplication>
17 #include <QDir>
18 #include <QHash> // for QHash
19 #include <QMap> // for QMap<>::iterator, etc
20 #include <QThread> // for QThread, etc
21 #include <QtCore/qnumeric.h> // for qIsNaN
22 #include <utility>
23 
24 // MythTV headers
25 #include "mthread.h"
26 #include "mythconfig.h"
27 #include "mythplayer.h"
28 #include "DetectLetterbox.h"
29 #include "audioplayer.h"
30 #include "interactivescreen.h"
31 #include "programinfo.h"
32 #include "mythcorecontext.h"
33 #include "livetvchain.h"
34 #include "decoderbase.h"
35 #include "nuppeldecoder.h"
36 #include "avformatdecoder.h"
37 #include "dummydecoder.h"
38 #include "tv_play.h"
39 #include "interactivetv.h"
40 #include "mythsystemevent.h"
41 #include "mythlogging.h"
42 #include "mythmiscutil.h"
43 #include "icringbuffer.h"
44 #include "audiooutput.h"
45 #include "cardutil.h"
46 #include "mythavutil.h"
47 #include "jitterometer.h" // for Jitterometer
48 #include "mythtimer.h" // for MythTimer
49 #include "mythuiactions.h" // for ACTION_LEFT, ACTION_RIGHT, etc
50 #include "ringbuffer.h" // for RingBuffer, etc
51 #include "tv_actions.h" // for ACTION_BIGJUMPFWD, etc
52 #include "mythcodeccontext.h"
53 
54 // MythUI headers
55 #include <mythmainwindow.h>
56 
57 extern "C" {
58 #include "vsync.h"
59 #include "libavcodec/avcodec.h"
60 }
61 
62 #include "remoteencoder.h"
63 
64 #if ! HAVE_ROUND
65 #define round(x) ((int) ((x) + 0.5))
66 #endif
67 
68 static unsigned dbg_ident(const MythPlayer* /*player*/);
69 
70 #define LOC QString("Player(%1): ").arg(dbg_ident(this),0,36)
71 #define LOC_DEC QString("Player(%1): ").arg(dbg_ident(m_mp),0,36)
72 
75 
76 // Exact frame seeking, no inaccuracy allowed.
77 const double MythPlayer::kInaccuracyNone = 0;
78 
79 // By default, when seeking, snap to a keyframe if the keyframe's
80 // distance from the target frame is less than 10% of the total seek
81 // distance.
82 const double MythPlayer::kInaccuracyDefault = 0.1;
83 
84 // Allow greater inaccuracy (50%) in the cutlist editor (unless the
85 // editor seek distance is set to 1 frame or 1 keyframe).
86 const double MythPlayer::kInaccuracyEditor = 0.5;
87 
88 // Any negative value means completely inexact, i.e. seek to the
89 // keyframe that is closest to the target.
90 const double MythPlayer::kInaccuracyFull = -1.0;
91 
93 {
94  RunProlog();
95  LOG(VB_PLAYBACK, LOG_INFO, LOC_DEC + "Decoder thread starting.");
96  if (m_mp)
97  m_mp->DecoderLoop(m_start_paused);
98  LOG(VB_PLAYBACK, LOG_INFO, LOC_DEC + "Decoder thread exiting.");
99  RunEpilog();
100 }
101 
102 static int toCaptionType(int type)
103 {
104  if (kTrackTypeCC608 == type) return kDisplayCC608;
105  if (kTrackTypeCC708 == type) return kDisplayCC708;
110  return 0;
111 }
112 
113 static int toTrackType(int type)
114 {
115  if (kDisplayCC608 == type) return kTrackTypeCC608;
116  if (kDisplayCC708 == type) return kTrackTypeCC708;
121  return kTrackTypeUnknown;
122 }
123 
124 MythMultiLocker::MythMultiLocker(std::initializer_list<QMutex*> Locks)
125  : m_locks(Locks)
126 {
127  Relock();
128 }
129 
131 {
132  Unlock();
133 }
134 
136 {
137  for (QVector<QMutex*>::const_reverse_iterator it = m_locks.crbegin(); it != m_locks.crend(); ++it)
138  if (*it)
139  (*it)->unlock();
140 }
141 
143 {
144  for (QVector<QMutex*>::const_iterator it = m_locks.cbegin(); it != m_locks.cend(); ++it)
145  if (*it)
146  (*it)->lock();
147 }
148 
150  : playerFlags(flags),
151  m_display((flags & kVideoIsNull) ? nullptr : MythDisplay::AcquireRelease()),
152  // CC608/708
153  cc608(this), cc708(this),
154  // Audio
155  audio(this, (flags & kAudioMuted) != 0),
156  // Debugging variables
157  output_jmeter(new Jitterometer(LOC))
158 {
160  ("PlayerMaxDiverge", 3.0));
161  if (max_diverge < 1.0F)
162  max_diverge = 1.0F;
163 
164  playerThread = QThread::currentThread();
165 #ifdef Q_OS_ANDROID
166  playerThreadId = gettid();
167 #endif
168  // Playback (output) zoom control
170 
173  decode_extra_audio = gCoreContext->GetBoolSetting("DecodeExtraAudio", false);
174  itvEnabled = gCoreContext->GetBoolSetting("EnableMHEG", false);
175  clearSavedPosition = gCoreContext->GetNumSetting("ClearSavedPosition", 1);
176  endExitPrompt = gCoreContext->GetNumSetting("EndOfRecordingExitPrompt");
178 
179  // Get VBI page number
180  QString mypage = gCoreContext->GetSetting("VBIpageNr", "888");
181  bool valid = false;
182  uint tmp = mypage.toInt(&valid, 16);
183  ttPageNum = (valid) ? tmp : ttPageNum;
185  avsync2adjustms = (int64_t)gCoreContext->GetNumSetting("AVSync2AdjustMS", 10);
186  if (avsync2adjustms < 1)
187  avsync2adjustms = 1;
188  if (avsync2adjustms > 40)
189  avsync2adjustms = 40;
190  m_avTimer.start();
191 }
192 
194 {
195  // NB the interactiveTV thread is a client of OSD so must be deleted
196  // before locking and deleting the OSD
197  {
198  QMutexLocker lk0(&itvLock);
199  delete interactiveTV;
200  interactiveTV = nullptr;
201  }
202 
203  MythMultiLocker locker({&osdLock, &vidExitLock});
204 
205  if (osd)
206  {
207  delete osd;
208  osd = nullptr;
209  }
210 
211  SetDecoder(nullptr);
212 
213  if (decoderThread)
214  {
215  delete decoderThread;
216  decoderThread = nullptr;
217  }
218 
219  if (videosync)
220  {
221  delete videosync;
222  videosync = nullptr;
223  }
224 
225  if (videoOutput)
226  {
227  delete videoOutput;
228  videoOutput = nullptr;
229  }
230 
231  if (output_jmeter)
232  {
233  delete output_jmeter;
234  output_jmeter = nullptr;
235  }
236 
237  if (detect_letter_box)
238  {
239  delete detect_letter_box;
240  detect_letter_box = nullptr;
241  }
242 
244 }
245 
247 {
248  watchingrecording = mode;
249  if (decoder)
251 }
252 
254 {
257 }
258 
260 {
261  bufferPauseLock.lock();
262  if (player_ctx->m_buffer)
263  {
266  }
267  bufferPaused = true;
268  bufferPauseLock.unlock();
269 }
270 
272 {
273  bufferPauseLock.lock();
274  if (player_ctx->m_buffer)
276  bufferPaused = false;
277  bufferPauseLock.unlock();
278 }
279 
281 {
282  if (!pauseLock.tryLock(100))
283  {
284  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waited 100ms to get pause lock.");
286  }
287  bool already_paused = allpaused;
288  if (already_paused)
289  {
290  pauseLock.unlock();
291  return already_paused;
292  }
293  next_play_speed = 0.0;
294  next_normal_speed = false;
295  PauseVideo();
296  audio.Pause(true);
297  PauseDecoder();
298  PauseBuffer();
299  if (!decoderPaused)
300  PauseDecoder(); // Retry in case audio only stream
302  {
305  else if (videoOutput && !FlagIsSet(kVideoIsNull))
307  }
308  pauseLock.unlock();
309  return already_paused;
310 }
311 
312 bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio)
313 {
314  pauseLock.lock();
315  LOG(VB_PLAYBACK, LOG_INFO, LOC +
316  QString("Play(%1, normal %2, unpause audio %3)")
317  .arg(speed,5,'f',1).arg(normal).arg(unpauseaudio));
318 
319  if (deleteMap.IsEditing())
320  {
321  LOG(VB_GENERAL, LOG_ERR, LOC + "Ignoring Play(), in edit mode.");
322  pauseLock.unlock();
323  return false;
324  }
325  rtcbase = 0;
329  UnpauseBuffer();
330  UnpauseDecoder();
331  if (unpauseaudio)
332  audio.Pause(false);
333  UnpauseVideo();
334  allpaused = false;
335  next_play_speed = speed;
336  next_normal_speed = normal;
337  pauseLock.unlock();
338  return true;
339 }
340 
342 {
343  videoPauseLock.lock();
344  needNewPauseFrame = true;
345  videoPaused = true;
346  videoPauseLock.unlock();
347 }
348 
350 {
351  videoPauseLock.lock();
352  videoPaused = false;
353  videoPauseLock.unlock();
354 }
355 
357 {
359  if (!player_ctx)
360  return;
361 
362  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
363  player_ctx->SetPlayingInfo(&pginfo);
364  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
365 }
366 
367 void MythPlayer::SetPlaying(bool is_playing)
368 {
369  QMutexLocker locker(&playingLock);
370 
371  playing = is_playing;
372 
373  playingWaitCond.wakeAll();
374 }
375 
376 bool MythPlayer::IsPlaying(uint wait_in_msec, bool wait_for) const
377 {
378  QMutexLocker locker(&playingLock);
379 
380  if (!wait_in_msec)
381  return playing;
382 
383  MythTimer t;
384  t.start();
385 
386  while ((wait_for != playing) && ((uint)t.elapsed() < wait_in_msec))
387  {
388  playingWaitCond.wait(
389  &playingLock, max(0,(int)wait_in_msec - t.elapsed()));
390  }
391 
392  return playing;
393 }
394 
396 {
397  if (!player_ctx)
398  return false;
399 
400  PIPState pipState = player_ctx->GetPIPState();
401 
402  if (!decoder)
403  {
404  LOG(VB_GENERAL, LOG_ERR, LOC +
405  "Cannot create a video renderer without a decoder.");
406  return false;
407  }
408 
415 
416  if (!videoOutput)
417  {
418  LOG(VB_GENERAL, LOG_ERR, LOC +
419  "Couldn't create VideoOutput instance. Exiting..");
420  SetErrored(tr("Failed to initialize video output"));
421  return false;
422  }
423 
424  if (embedding && pipState == kPIPOff)
426 
427  return true;
428 }
429 
431 {
433  {
434  osdLock.lock();
436  {
437  reinit_osd = true;
438  osdLock.unlock();
439  return;
440  }
441  QRect visible, total;
442  float aspect, scaling;
443  videoOutput->GetOSDBounds(total, visible, aspect,
444  scaling, 1.0F);
445  if (osd)
446  {
448  int stretch = lroundf(aspect * 100);
449  if ((osd->Bounds() != visible) ||
450  (osd->GetFontStretch() != stretch))
451  {
452  uint old = textDisplayMode;
453  ToggleCaptions(old);
454  osd->Reinit(visible, aspect);
455  EnableCaptions(old, false);
456  if (deleteMap.IsEditing())
457  {
458  bool const changed = deleteMap.IsChanged();
459  deleteMap.SetChanged(true);
461  deleteMap.SetChanged(changed);
462  }
463  }
464  }
465 
466 #ifdef USING_MHEG
467  if (GetInteractiveTV())
468  {
469  QMutexLocker locker(&itvLock);
470  interactiveTV->Reinit(total, visible, aspect);
471  itvVisible = false;
472  }
473 #endif // USING_MHEG
474  reinit_osd = false;
475  osdLock.unlock();
476  }
477 }
478 
479 void MythPlayer::ReinitVideo(bool ForceUpdate)
480 {
481 
482  bool aspect_only = false;
483  {
484  MythMultiLocker locker({&osdLock, &vidExitLock});
485 
486  videoOutput->SetVideoFrameRate(static_cast<float>(video_frame_rate));
487  float aspect = (forced_video_aspect > 0) ? forced_video_aspect : video_aspect;
489  decoder->GetVideoCodecID(), aspect_only, &locker,
490  m_maxReferenceFrames, ForceUpdate))
491  {
492  LOG(VB_GENERAL, LOG_ERR, LOC +
493  "Failed to Reinitialize Video. Exiting..");
494  SetErrored(tr("Failed to reinitialize video output"));
495  return;
496  }
497 
498  // the display refresh rate may have been changed by VideoOutput
499  if (videosync)
500  {
502  if (ri != videosync->getRefreshInterval())
503  {
504  LOG(VB_PLAYBACK, LOG_INFO, LOC +
505  QString("Refresh rate has changed from %1 to %2")
507  .arg(ri));
509  }
510  }
511 
512  if (osd)
514  ReinitOSD();
515  }
516 
517  if (!aspect_only)
518  ClearAfterSeek();
519 
520  if (textDisplayMode)
521  EnableSubtitles(true);
522 }
523 
524 static inline QString toQString(FrameScanType scan) {
525  switch (scan) {
526  case kScan_Ignore: return QString("Ignore Scan");
527  case kScan_Detect: return QString("Detect Scan");
528  case kScan_Interlaced: return QString("Interlaced Scan");
529  case kScan_Progressive: return QString("Progressive Scan");
530  default: return QString("Unknown Scan");
531  }
532 }
533 
536  float fps, int video_height)
537 {
538  QString dbg = QString("detectInterlace(") + toQString(newScan) +
539  QString(", ") + toQString(scan) + QString(", ") +
540  QString("%1").arg(static_cast<double>(fps)) + QString(", ") +
541  QString("%1").arg(video_height) + QString(") ->");
542 
543  if (kScan_Ignore != newScan || kScan_Detect == scan)
544  {
545  // The scanning mode should be decoded from the stream, but if it
546  // isn't, we have to guess.
547 
548  scan = kScan_Interlaced; // default to interlaced
549  if ((720 == video_height) || // ATSC 720p
550  (fps > 45)) // software deinterlacing
552 
553  if (kScan_Detect != newScan)
554  scan = newScan;
555  };
556 
557  LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg +toQString(scan));
558 
559  return scan;
560 }
561 
562 void MythPlayer::SetKeyframeDistance(int keyframedistance)
563 {
564  keyframedist = (keyframedistance > 0) ? keyframedistance : keyframedist;
565 }
566 
567 void MythPlayer::AutoDeint(VideoFrame *frame, bool allow_lock)
568 {
569  if (!frame || m_scan_locked)
570  return;
571 
572  if (frame->interlaced_frame)
573  {
574  if (m_scan_tracker < 0)
575  {
576  LOG(VB_PLAYBACK, LOG_INFO, LOC +
577  QString("Interlaced frame seen after %1 progressive frames")
578  .arg(abs(m_scan_tracker)));
579  m_scan_tracker = 2;
580  if (allow_lock)
581  {
582  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Locking scan to Interlaced.");
584  return;
585  }
586  }
587  m_scan_tracker++;
588  }
589  else
590  {
591  if (m_scan_tracker > 0)
592  {
593  LOG(VB_PLAYBACK, LOG_INFO, LOC +
594  QString("Progressive frame seen after %1 interlaced frames")
595  .arg(m_scan_tracker));
596  m_scan_tracker = 0;
597  }
598  m_scan_tracker--;
599  }
600 
601  int min_count = !allow_lock ? 0 : 2;
602  if (abs(m_scan_tracker) <= min_count)
603  return;
604 
606  m_scan_locked = false;
607 }
608 
610 {
612  {
613  resetScan = scan;
614  return;
615  }
616 
617  if (!videoOutput || !videosync)
618  return; // hopefully this will be called again later...
619 
621 
623  return;
624 
626 
627  m_scan_initialized = true;
629 
630  if (is_interlaced(scan))
631  {
632  bool normal = play_speed > 0.99F && play_speed < 1.01F && normal_speed;
635  }
636  else if (kScan_Progressive == scan)
637  {
638  m_double_framerate = false;
639  videoOutput->SetDeinterlacing(false, false);
640  }
641 
642  m_scan = scan;
643 }
644 
645 void MythPlayer::SetVideoParams(int width, int height, double fps,
646  float aspect, bool ForceUpdate,
647  int ReferenceFrames, FrameScanType scan, const QString& codecName)
648 {
649  bool paramsChanged = ForceUpdate;
650 
651  if (width >= 1 && height >= 1)
652  {
653  paramsChanged = true;
654  video_dim = video_disp_dim = QSize(width, height);
655  video_aspect = aspect > 0.0F ? aspect : static_cast<float>(width) / height;
656  }
657 
658  if (!qIsNaN(fps) && fps > 0.0 && fps < 121.0)
659  {
660  paramsChanged = true;
661  video_frame_rate = fps;
662  if (ffrew_skip != 0 && ffrew_skip != 1)
663  {
664  UpdateFFRewSkip();
665  }
666  else
667  {
668  float temp_speed = (play_speed == 0.0F) ?
671  1.0 / (video_frame_rate * static_cast<double>(temp_speed)));
672  }
673  }
674 
675  if (!codecName.isEmpty())
676  {
677  m_codecName = codecName;
678  paramsChanged = true;
679  }
680 
681  if (ReferenceFrames > 0)
682  {
683  m_maxReferenceFrames = ReferenceFrames;
684  paramsChanged = true;
685  }
686 
687  if (!paramsChanged)
688  return;
689 
690  if (videoOutput)
691  ReinitVideo(ForceUpdate);
692 
693  if (IsErrored())
694  return;
695 
696  // ensure deinterlacers are correctly reset after a change
697  m_scan_initialized = false;
699  video_disp_dim.height()));
700  m_scan_locked = false;
701  m_scan_tracker = (m_scan == kScan_Interlaced) ? 2 : 0;
702 }
703 
704 
705 void MythPlayer::SetFrameRate(double fps)
706 {
707  video_frame_rate = fps;
708  float temp_speed = (play_speed == 0.0F) ?
711  1.0 / (video_frame_rate * static_cast<double>(temp_speed)));
712 }
713 
714 void MythPlayer::SetFileLength(int total, int frames)
715 {
716  totalLength = total;
718 }
719 
720 void MythPlayer::SetDuration(int duration)
721 {
722  totalDuration = duration;
723 }
724 
726 {
727  isDummy = true;
728 
729  if (!videoOutput)
730  {
732  SetVideoParams(720, 576, 25.00, 1.25F, false, 2);
733  }
734 
735  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
736  auto *dec = new DummyDecoder(this, *(player_ctx->m_playingInfo));
737  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
738  SetDecoder(dec);
739 }
740 
741 void MythPlayer::CreateDecoder(char *testbuf, int testreadsize)
742 {
743  if (NuppelDecoder::CanHandle(testbuf, testreadsize))
745  else if (AvFormatDecoder::CanHandle(testbuf,
747  testreadsize))
748  {
750  playerFlags));
751  }
752 }
753 
755 {
756  // Disable hardware acceleration for second PBP
757  if (player_ctx && (player_ctx->IsPBP() && !player_ctx->IsPrimaryPBP()) &&
759  {
761  }
762 
763  isDummy = false;
764 
765  if (!player_ctx || !player_ctx->m_buffer)
766  return -1;
767 
769 
770  if (player_ctx->m_tvchain &&
772  "DUMMY")
773  {
774  OpenDummy();
775  return 0;
776  }
777 
780  char *testbuf = new char[kDecoderProbeBufferSize];
781  UnpauseBuffer();
782 
783  // delete any pre-existing recorder
784  SetDecoder(nullptr);
785  int testreadsize = 2048;
786 
787  MythTimer bigTimer; bigTimer.start();
788  int timeout = max((retries + 1) * 500, 30000U);
789  while (testreadsize <= kDecoderProbeBufferSize)
790  {
791  MythTimer peekTimer; peekTimer.start();
792  while (player_ctx->m_buffer->Peek(testbuf, testreadsize) != testreadsize)
793  {
794  // NB need to allow for streams encountering network congestion
795  if (peekTimer.elapsed() > 30000 || bigTimer.elapsed() > timeout
797  {
798  LOG(VB_GENERAL, LOG_ERR, LOC +
799  QString("OpenFile(): Could not read first %1 bytes of '%2'")
800  .arg(testreadsize)
801  .arg(player_ctx->m_buffer->GetFilename()));
802  delete[] testbuf;
803  SetErrored(tr("Could not read first %1 bytes").arg(testreadsize));
804  return -1;
805  }
806  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenFile() waiting on data");
807  usleep(50 * 1000);
808  }
809 
810  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
811  CreateDecoder(testbuf, testreadsize);
812  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
813  if (decoder || (bigTimer.elapsed() > timeout))
814  break;
815  testreadsize <<= 1;
816  }
817 
818  if (!decoder)
819  {
820  LOG(VB_GENERAL, LOG_ERR, LOC +
821  QString("Couldn't find an A/V decoder for: '%1'")
822  .arg(player_ctx->m_buffer->GetFilename()));
823  SetErrored(tr("Could not find an A/V decoder"));
824 
825  delete[] testbuf;
826  return -1;
827  }
828  if (decoder->IsErrored())
829  {
830  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not initialize A/V decoder.");
831  SetDecoder(nullptr);
832  SetErrored(tr("Could not initialize A/V decoder"));
833 
834  delete[] testbuf;
835  return -1;
836  }
837 
838  decoder->SetSeekSnap(0);
842 
843  // Set 'no_video_decode' to true for audio only decodeing
844  bool no_video_decode = false;
845 
846  // We want to locate decoder for video even if using_null_videoout
847  // is true, only disable if no_video_decode is true.
848  int ret = decoder->OpenFile(
849  player_ctx->m_buffer, no_video_decode, testbuf, testreadsize);
850  delete[] testbuf;
851 
852  if (ret < 0)
853  {
854  LOG(VB_GENERAL, LOG_ERR, QString("Couldn't open decoder for: %1")
855  .arg(player_ctx->m_buffer->GetFilename()));
856  SetErrored(tr("Could not open decoder"));
857  return -1;
858  }
859 
860  audio.CheckFormat();
861 
862  if (ret > 0)
863  {
864  hasFullPositionMap = true;
865  deleteMap.LoadMap();
867  }
868 
869  // Determine the initial bookmark and update it for the cutlist
873 
876  {
878  "DefaultChanid", player_ctx->m_playingInfo->GetChanID());
879  QString callsign = player_ctx->m_playingInfo->GetChannelSchedulingID();
880  QString channum = player_ctx->m_playingInfo->GetChanNum();
882  "DefaultChanKeys", callsign + "[]:[]" + channum);
884  {
885  int cardid = player_ctx->m_recorder->GetRecorderNumber();
886  CardUtil::SetStartChannel(cardid, channum);
887  }
888  }
889 
890  return IsErrored() ? -1 : 0;
891 }
892 
893 void MythPlayer::SetFramesPlayed(uint64_t played)
894 {
895  framesPlayed = played;
896  framesPlayedExtra = 0;
897  if (videoOutput)
898  videoOutput->SetFramesPlayed(played);
899 }
900 
905 {
906  if (videoOutput)
907  return videoOutput->FreeVideoFrames();
908  return 0;
909 }
910 
913 {
914  static VideoFrameType s_defaultFormats[] = { FMT_YV12, FMT_NONE };
915  if (videoOutput)
917  return &s_defaultFormats[0];
918 }
919 
930 {
931  if (videoOutput)
932  return videoOutput->GetNextFreeFrame();
933  return nullptr;
934 }
935 
940  int64_t timecode,
941  bool wrap)
942 {
943  if (wrap)
944  WrapTimecode(timecode, TC_VIDEO);
945  buffer->timecode = timecode;
946  m_latestVideoTimecode = timecode;
947 
948  if (videoOutput)
949  videoOutput->ReleaseFrame(buffer);
950 
951  detect_letter_box->Detect(buffer);
952  if (allpaused)
953  CheckAspectRatio(buffer);
954 }
955 
960 {
961  if (videoOutput)
962  videoOutput->DiscardFrame(buffer);
963 }
964 
978 void MythPlayer::DiscardVideoFrames(bool KeyFrame, bool Flushed)
979 {
980  if (videoOutput)
981  videoOutput->DiscardFrames(KeyFrame, Flushed);
982 }
983 
985 {
986  EofState eof = GetEof();
987  if (eof != kEofStateNone && !allpaused)
988  return true;
989  if (GetEditMode())
990  return false;
991  if (livetv)
992  return false;
994  return true;
995  return false;
996 }
997 
999 {
1000  w = video_dim.width();
1001  h = video_dim.height();
1002 
1003  VideoFrame *retval = nullptr;
1004 
1005  vidExitLock.lock();
1006  if (videoOutput)
1007  retval = videoOutput->GetLastShownFrame();
1008 
1009  if (!retval)
1010  vidExitLock.unlock();
1011 
1012  return retval;
1013 }
1014 
1016 {
1017  if (videoOutput)
1018  videoOutput->DeLimboFrame(frame);
1019 }
1020 
1022 {
1023  if (frame)
1024  vidExitLock.unlock();
1025 }
1026 
1028 {
1029  if (videoOutput)
1030  videoOutput->EmbedInWidget(rect);
1031  else
1032  {
1033  embedRect = rect;
1034  embedding = true;
1035  }
1036 }
1037 
1039 {
1040  if (videoOutput)
1041  {
1043  ReinitOSD();
1044  }
1045  else
1046  {
1047  embedRect = QRect();
1048  embedding = false;
1049  }
1050 }
1051 
1052 void MythPlayer::WindowResized(const QSize &new_size)
1053 {
1054  if (videoOutput)
1055  videoOutput->WindowResized(new_size);
1056  ReinitOSD();
1057 }
1058 
1060 {
1061  QMutexLocker locker(&osdLock);
1062  if (!osd)
1063  return;
1064 
1065  osd->EnableTeletext(true, page);
1068 }
1069 
1071 {
1072  QMutexLocker locker(&osdLock);
1073  if (!osd)
1074  return;
1075 
1076  osd->EnableTeletext(false, 0);
1078 
1079  /* If subtitles are enabled before the teletext menu was displayed,
1080  re-enabled them. */
1083 }
1084 
1086 {
1087  QMutexLocker locker(&osdLock);
1088  if (!osd)
1089  return;
1090 
1091  osd->TeletextReset();
1092 }
1093 
1098 {
1099  osdLock.lock();
1101  ttPageNum = page;
1105  osdLock.unlock();
1106 }
1107 
1109 {
1111  return false;
1112 
1113  bool handled = true;
1114 
1115  osdLock.lock();
1116  if (action == "MENU" || action == ACTION_TOGGLETT || action == "ESCAPE")
1117  DisableTeletext();
1118  else if (osd)
1119  handled = osd->TeletextAction(action);
1120  osdLock.unlock();
1121 
1122  return handled;
1123 }
1124 
1126 {
1127  QMutexLocker locker(&osdLock);
1128  if (!osd)
1129  return;
1130 
1137  {
1138  osd->ClearSubtitles();
1139  }
1142  {
1143  osd->TeletextClear();
1144  }
1145 }
1146 
1147 void MythPlayer::DisableCaptions(uint mode, bool osd_msg)
1148 {
1149  if (textDisplayMode)
1151  textDisplayMode &= ~mode;
1152  ResetCaptions();
1153 
1154  QMutexLocker locker(&osdLock);
1155 
1156  bool newTextDesired = (textDisplayMode & kDisplayAllTextCaptions) != 0U;
1157  // Only turn off textDesired if the Operator requested it.
1158  if (osd_msg || newTextDesired)
1159  textDesired = newTextDesired;
1160  QString msg = "";
1161  if (kDisplayNUVTeletextCaptions & mode)
1162  msg += tr("TXT CAP");
1163  if (kDisplayTeletextCaptions & mode)
1164  {
1167  DisableTeletext();
1168  }
1169  int preserve = textDisplayMode & (kDisplayCC608 | kDisplayTextSubtitle |
1172  if ((kDisplayCC608 & mode) || (kDisplayCC708 & mode) ||
1173  (kDisplayAVSubtitle & mode) || (kDisplayRawTextSubtitle & mode))
1174  {
1175  int type = toTrackType(mode);
1176  msg += decoder->GetTrackDesc(type, GetTrack(type));
1177  if (osd)
1178  osd->EnableSubtitles(preserve);
1179  }
1180  if (kDisplayTextSubtitle & mode)
1181  {
1182  msg += tr("Text subtitles");
1183  if (osd)
1184  osd->EnableSubtitles(preserve);
1185  }
1186  if (!msg.isEmpty() && osd_msg)
1187  {
1188  msg += " " + tr("Off");
1190  }
1191 }
1192 
1193 void MythPlayer::EnableCaptions(uint mode, bool osd_msg)
1194 {
1195  QMutexLocker locker(&osdLock);
1196  bool newTextDesired = (mode & kDisplayAllTextCaptions) != 0U;
1197  // Only turn off textDesired if the Operator requested it.
1198  if (osd_msg || newTextDesired)
1199  textDesired = newTextDesired;
1200  QString msg = "";
1201  if ((kDisplayCC608 & mode) || (kDisplayCC708 & mode) ||
1202  (kDisplayAVSubtitle & mode) || kDisplayRawTextSubtitle & mode)
1203  {
1204  int type = toTrackType(mode);
1205  msg += decoder->GetTrackDesc(type, GetTrack(type));
1206  if (osd)
1207  osd->EnableSubtitles(mode);
1208  }
1209  if (kDisplayTextSubtitle & mode)
1210  {
1211  if (osd)
1213  msg += tr("Text subtitles");
1214  }
1215  if (kDisplayNUVTeletextCaptions & mode)
1216  msg += tr("TXT %1").arg(ttPageNum, 3, 16);
1217  if (kDisplayTeletextCaptions & mode)
1218  {
1221 
1222  int page = decoder->GetTrackLanguageIndex(
1225 
1226  EnableTeletext(page);
1228  }
1229 
1230  msg += " " + tr("On");
1231 
1232  LOG(VB_PLAYBACK, LOG_INFO, QString("EnableCaptions(%1) msg: %2")
1233  .arg(mode).arg(msg));
1234 
1235  textDisplayMode = mode;
1236  if (textDisplayMode)
1238  if (osd_msg)
1240 }
1241 
1243 {
1245  return textDisplayMode;
1246 }
1247 
1249 {
1250  QMutexLocker locker(&osdLock);
1251  uint mode = toCaptionType(type);
1252  uint origMode = textDisplayMode;
1253 
1254  if (textDisplayMode)
1255  DisableCaptions(textDisplayMode, (origMode & mode) != 0U);
1256  if (origMode & mode)
1257  return textDisplayMode;
1258  if (mode)
1259  EnableCaptions(mode);
1260  return textDisplayMode;
1261 }
1262 
1263 void MythPlayer::SetCaptionsEnabled(bool enable, bool osd_msg)
1264 {
1265  QMutexLocker locker(&osdLock);
1266  enableCaptions = disableCaptions = false;
1267  uint origMode = textDisplayMode;
1268 
1269  // Only turn off textDesired if the Operator requested it.
1270  if (osd_msg || enable)
1271  textDesired = enable;
1272 
1273  if (!enable)
1274  {
1275  DisableCaptions(origMode, osd_msg);
1276  return;
1277  }
1280  if (origMode != (uint)mode)
1281  {
1282  DisableCaptions(origMode, false);
1283 
1284  if (kDisplayNone == mode)
1285  {
1286  if (osd_msg)
1287  {
1288  SetOSDMessage(tr("No captions",
1289  "CC/Teletext/Subtitle text not available"),
1290  kOSDTimeout_Med);
1291  }
1292  LOG(VB_PLAYBACK, LOG_INFO,
1293  "No captions available yet to enable.");
1294  }
1295  else if (mode)
1296  {
1297  EnableCaptions(mode, osd_msg);
1298  }
1299  }
1300  ResetCaptions();
1301 }
1302 
1304 {
1313 }
1314 
1316 {
1317  if (decoder)
1318  return decoder->GetTracks(type);
1319  return QStringList();
1320 }
1321 
1323 {
1324  if (decoder)
1325  return decoder->GetTrackCount(type);
1326  return 0;
1327 }
1328 
1329 int MythPlayer::SetTrack(uint type, int trackNo)
1330 {
1331  int ret = -1;
1332  if (!decoder)
1333  return ret;
1334 
1335  ret = decoder->SetTrack(type, trackNo);
1336  if (kTrackTypeAudio == type)
1337  {
1338  QString msg = "";
1339  if (decoder)
1341  kOSDTimeout_Med);
1342  return ret;
1343  }
1344 
1345  uint subtype = toCaptionType(type);
1346  if (subtype)
1347  {
1349  EnableCaptions(subtype, true);
1350  if ((kDisplayCC708 == subtype || kDisplayCC608 == subtype) && decoder)
1351  {
1352  int sid = decoder->GetTrackInfo(type, trackNo).m_stream_id;
1353  if (sid >= 0)
1354  {
1355  (kDisplayCC708 == subtype) ? cc708.SetCurrentService(sid) :
1356  cc608.SetMode(sid);
1357  }
1358  }
1359  }
1360  return ret;
1361 }
1362 
1369 {
1370  if (trackType >= kTrackTypeSubtitle &&
1371  trackType <= kTrackTypeTeletextCaptions && textDesired)
1372  {
1373  enableCaptions = true;
1374  }
1375 }
1376 
1378 {
1379  if (enable)
1380  enableCaptions = true;
1381  else
1382  disableCaptions = true;
1383 }
1384 
1386 {
1387  if (enable)
1388  enableForcedSubtitles = true;
1389  else
1390  disableForcedSubtitles = true;
1391 }
1392 
1394 {
1395  allowForcedSubtitles = allow;
1397  tr("Forced Subtitles On") :
1398  tr("Forced Subtitles Off"),
1399  kOSDTimeout_Med);
1400 }
1401 
1403 {
1404  disableForcedSubtitles = false;
1405  osdLock.lock();
1406  if (osd)
1408  osdLock.unlock();
1409 }
1410 
1412 {
1413  enableForcedSubtitles = false;
1414  if (!allowForcedSubtitles)
1415  return;
1416 
1417  osdLock.lock();
1418  if (osd)
1419  osd->EnableSubtitles(kDisplayAVSubtitle, true /*forced only*/);
1420  osdLock.unlock();
1421 }
1422 
1424 {
1425  if (decoder)
1426  return decoder->GetTrack(type);
1427  return -1;
1428 }
1429 
1431 {
1432  if (!decoder)
1433  return -1;
1434 
1435  int retval = decoder->ChangeTrack(type, dir);
1436  if (retval >= 0)
1437  {
1439  kOSDTimeout_Med);
1440  return retval;
1441  }
1442  return -1;
1443 }
1444 
1446 {
1447  if (!decoder || (dir < 0))
1448  return;
1449 
1453  {
1454  int tracktype = toTrackType(textDisplayMode);
1455  if (GetTrack(tracktype) < decoder->NextTrack(tracktype))
1456  {
1457  SetTrack(tracktype, decoder->NextTrack(tracktype));
1458  return;
1459  }
1460  }
1461  int nextmode = NextCaptionTrack(textDisplayMode);
1462  if ((nextmode == kDisplayTextSubtitle) ||
1463  (nextmode == kDisplayNUVTeletextCaptions) ||
1464  (nextmode == kDisplayNone))
1465  {
1467  if (nextmode != kDisplayNone)
1468  EnableCaptions(nextmode, true);
1469  }
1470  else
1471  {
1472  int tracktype = toTrackType(nextmode);
1473  int tracks = decoder->GetTrackCount(tracktype);
1474  if (tracks)
1475  {
1477  SetTrack(tracktype, 0);
1478  }
1479  }
1480 }
1481 
1483 {
1484  if (mode == kDisplayNone)
1485  return false;
1486  if (((mode == kDisplayTextSubtitle) && HasTextSubtitles()) ||
1487  (mode == kDisplayNUVTeletextCaptions))
1488  {
1489  return true;
1490  }
1491  if (!(mode == kDisplayTextSubtitle) &&
1493  {
1494  return true;
1495  }
1496  return false;
1497 }
1498 
1500 {
1501  // Text->TextStream->708->608->AVSubs->Teletext->NUV->None
1502  // NUV only offerred if PAL
1503  bool pal = (vbimode == VBIMode::PAL_TT);
1504  int nextmode = kDisplayNone;
1505 
1506  if (kDisplayTextSubtitle == mode)
1507  nextmode = kDisplayRawTextSubtitle;
1508  else if (kDisplayRawTextSubtitle == mode)
1509  nextmode = kDisplayCC708;
1510  else if (kDisplayCC708 == mode)
1511  nextmode = kDisplayCC608;
1512  else if (kDisplayCC608 == mode)
1513  nextmode = kDisplayAVSubtitle;
1514  else if (kDisplayAVSubtitle == mode)
1515  nextmode = kDisplayTeletextCaptions;
1516  else if (kDisplayTeletextCaptions == mode)
1517  nextmode = pal ? kDisplayNUVTeletextCaptions : kDisplayNone;
1518  else if ((kDisplayNUVTeletextCaptions == mode) && pal)
1519  nextmode = kDisplayNone;
1520  else if (kDisplayNone == mode)
1521  nextmode = kDisplayTextSubtitle;
1522 
1523  if (nextmode == kDisplayNone || HasCaptionTrack(nextmode))
1524  return nextmode;
1525 
1526  return NextCaptionTrack(nextmode);
1527 }
1528 
1530 {
1531  if (decoder)
1533  frame_interval = static_cast<int>(lround(1000000.0 * frame_period) / m_fpsMultiplier);
1535  avsync_predictor = 0;
1536  avsync_predictor_enabled = false;
1537 
1538  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetFrameInterval Interval:%1 Speed:%2 Scan:%3 (Multiplier: %4)")
1539  .arg(frame_interval).arg(static_cast<double>(play_speed)).arg(toQString(scan)).arg(m_fpsMultiplier));
1540  if (play_speed < 1 || play_speed > 2 || refreshrate <= 0)
1541  return;
1542 
1544 }
1545 
1547 {
1548  avsync_avg = 0;
1550  avsync_predictor = 0;
1551  prevtc = 0;
1552  avsync_next = avsync_interval; // Frames till next sync check
1553  rtcbase = 0;
1554  prior_audiotimecode = 0;
1555  prior_videotimecode = 0;
1556  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + "A/V sync reset");
1557 }
1558 
1560 {
1561  videosync->Start();
1562 
1563  avsync_adjustment = 0;
1564 
1565  repeat_delay = 0;
1566 
1568 
1569  // Number of frames over which to average time divergence
1570  avsync_averaging=4;
1571  rtcbase = 0;
1572  prior_audiotimecode = 0;
1573  prior_videotimecode = 0;
1574 
1575  // Allow override of averaging value
1576  avsync_averaging = gCoreContext->GetNumSetting("AVSyncAveraging", avsync_averaging); // Number of frames to average
1577  if (avsync_averaging < 4)
1578  avsync_averaging = 4;
1579  avsync_interval = avsync_averaging / max_diverge - 1; // Number of frames skip between sync checks
1580  if (avsync_interval < 0)
1581  avsync_interval = 0;
1582  avsync_next = avsync_interval; // Frames till next sync check
1583 
1584  if (!FlagIsSet(kVideoIsNull))
1585  {
1586  QString timing_type = videosync->getName();
1587 
1588  QString msg = QString("Video timing method: %1").arg(timing_type);
1589  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
1590  msg = QString("Display Refresh Rate: %1 Video Frame Rate: %2")
1591  .arg(1000000.0 / refreshrate, 0, 'f', 3)
1592  .arg(1000000.0 / frame_interval, 0, 'f', 3);
1593  LOG(VB_PLAYBACK, LOG_INFO, LOC + msg);
1594 
1596  1.0 / (video_frame_rate * static_cast<double>(play_speed)));
1597 
1598  // try to get preferential scheduling, but ignore if we fail to.
1599  myth_nice(-19);
1600  }
1601 }
1602 
1604 {
1605  int64_t currentaudiotime = 0;
1606  if (normal_speed)
1607  {
1608  currentaudiotime = audio.GetAudioTime();
1609  }
1610  return currentaudiotime;
1611 }
1612 
1613 void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
1614 {
1615  if (gCoreContext->GetBoolSetting("PlaybackAVSync2", false))
1616  {
1617  AVSync2(buffer);
1618  return;
1619  }
1620 
1621  int repeat_pict = 0;
1622  int64_t timecode = audio.GetAudioTime();
1623 
1624  if (buffer)
1625  {
1626  repeat_pict = buffer->repeat_pict;
1627  timecode = buffer->timecode;
1628  disp_timecode = buffer->disp_timecode;
1629  }
1630 
1631  bool decoderdeint = buffer && buffer->decoder_deinterlaced;
1632  FrameScanType ps = m_scan;
1633  if (kScan_Detect == m_scan || kScan_Ignore == m_scan || decoderdeint)
1634  {
1635  ps = kScan_Progressive;
1636  }
1637  else if (buffer && is_interlaced(ps))
1638  {
1639  ps = kScan_Interlaced;
1641  }
1642 
1643  // only display the second field if needed
1645 
1646  float diverge = 0.0F;
1647  int frameDelay = m_double_framerate ? frame_interval / 2 : frame_interval;
1648  int vsync_delay_clock = 0;
1649  //int64_t currentaudiotime = 0;
1650 
1651  if (videoOutput->IsErrored())
1652  {
1653  LOG(VB_GENERAL, LOG_ERR, LOC +
1654  "AVSync: Unknown error in videoOutput, aborting playback.");
1655  SetErrored(tr("Failed to initialize A/V Sync"));
1656  return;
1657  }
1658 
1659  if (normal_speed && avsync_next==0)
1660  {
1661  diverge = (float)avsync_avg / (float)frame_interval;
1662  }
1663 
1664  if (avsync_next > 0)
1665  {
1666  avsync_next--;
1667  }
1668  else
1669  {
1670  int divisor = int(abs(diverge) - max_diverge - 1.0F);
1671  if (divisor < 1)
1672  divisor=1;
1673  avsync_next = avsync_interval/divisor;
1674  }
1675 
1676  bool max_video_behind = diverge < -max_diverge;
1677  bool dropframe = false;
1678  QString dbg;
1679 
1681  {
1684  {
1685  int refreshperiodsinframe = avsync_predictor/refreshrate;
1686  avsync_predictor -= refreshrate * refreshperiodsinframe;
1687  }
1688  else
1689  {
1690  dropframe = !FlagIsSet(kMusicChoice);
1691  dbg = QString("A/V predict drop frame, refreshrate %1, avsync_predictor %2, diverge %3, ")
1692  .arg(refreshrate).arg(avsync_predictor).arg(diverge);
1693  }
1694  }
1695 
1696  if (max_video_behind)
1697  {
1698  dropframe = !FlagIsSet(kMusicChoice);
1699  // If video is way behind of audio, adjust for it...
1700  dbg = QString("Video is %1 frames behind audio (too slow), ")
1701  .arg(-diverge);
1702  }
1703 
1704  if (!dropframe && avsync_audiopaused)
1705  {
1706  avsync_audiopaused = false;
1707  audio.Pause(false);
1708  }
1709 
1710  if (!dropframe)
1711  {
1712  // PGB this was orignally in the calling methods
1713  // MythPlayer::DisplayNormalFrame and MythDVDPlayer::DisplayLastFrame
1714  // Moved here to reduce CPU usage since the OSD was being merged
1715  // into frames that were not being displayed, thereby causing
1716  // interruptions and slowdowns.
1717  osdLock.lock();
1718  videoOutput->ProcessFrame(buffer, osd, pip_players, ps);
1719  osdLock.unlock();
1720  }
1721 
1722  if (dropframe)
1723  {
1724  // Reset A/V Sync
1725  lastsync = true;
1726  //currentaudiotime = AVSyncGetAudiotime();
1727  LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg + "dropping frame to catch up.");
1728  if (max_video_behind)
1729  {
1730  audio.Pause(true);
1731  avsync_audiopaused = true;
1732  }
1733  }
1734  else if (!FlagIsSet(kVideoIsNull))
1735  {
1736  // if we get here, we're actually going to do video output
1737  osdLock.lock();
1738  videoOutput->PrepareFrame(buffer, ps, osd);
1739  osdLock.unlock();
1740  // Don't wait for sync if this is a secondary PBP otherwise
1741  // the primary PBP will become out of sync
1742  if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
1743  {
1744  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO,
1745  LOC + QString("AVSync waitforframe %1 %2 %3")
1746  .arg(frameDelay).arg(avsync_adjustment).arg(m_double_framerate));
1747  vsync_delay_clock = videosync->WaitForFrame(frameDelay, avsync_adjustment + repeat_delay);
1748  }
1749  else
1750  {
1751  vsync_delay_clock = 0;
1752  lastsync = true;
1753  }
1754  //currentaudiotime = AVSyncGetAudiotime();
1755  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + "AVSync show");
1756  videoOutput->Show(ps);
1757 
1758  if (videoOutput->IsErrored())
1759  {
1760  LOG(VB_GENERAL, LOG_ERR, LOC + "Error condition detected "
1761  "in videoOutput after Show(), aborting playback.");
1762  SetErrored(tr("Serious error detected in Video Output"));
1763  return;
1764  }
1765 
1766  if (m_double_framerate)
1767  {
1768  // second stage of deinterlacer processing
1769  if (ps == kScan_Interlaced)
1770  ps = kScan_Intr2ndField;
1771  osdLock.lock();
1772  // Only double rate CPU deinterlacers require an extra call to ProcessFrame
1774  videoOutput->ProcessFrame(buffer, osd, pip_players, ps);
1775  videoOutput->PrepareFrame(buffer, ps, osd);
1776  osdLock.unlock();
1777  // Display the second field
1778  if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
1779  {
1780  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + QString("AVSync waitforframe %1 %2 %3")
1781  .arg(frameDelay).arg(avsync_adjustment).arg(m_double_framerate));
1782  vsync_delay_clock = videosync->WaitForFrame(frameDelay, avsync_adjustment);
1783  }
1784  videoOutput->Show(ps);
1785  }
1786 
1787  repeat_delay = frame_interval * repeat_pict * 0.5;
1788 
1789  if (repeat_delay)
1790  LOG(VB_TIMESTAMP, LOG_INFO, LOC +
1791  QString("A/V repeat_pict, adding %1 repeat delay")
1792  .arg(repeat_delay));
1793  }
1794  else
1795  {
1796  vsync_delay_clock = videosync->WaitForFrame(frameDelay, 0);
1797  //currentaudiotime = AVSyncGetAudiotime();
1798  }
1799 
1801  {
1802  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
1803  QString("A/V avsync_delay: %1, avsync_avg: %2")
1804  .arg(avsync_delay / 1000).arg(avsync_avg / 1000));
1805  }
1806 
1807  avsync_adjustment = 0;
1808 
1809  if (diverge > max_diverge)
1810  {
1811  // If audio is way behind of video, adjust for it...
1812  // by cutting the frame rate in half for the length of this frame
1814  lastsync = true;
1815  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1816  QString("Video is %1 frames ahead of audio,\n"
1817  "\t\t\tdoubling video frame interval to slow down.")
1818  .arg(diverge));
1819  }
1820 
1821  if (audio.HasAudioOut() && normal_speed)
1822  {
1823  // must be sampled here due to Show delays
1824  int64_t currentaudiotime = audio.GetAudioTime();
1825  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
1826  QString("A/V timecodes audio %1 video %2 frameinterval %3 "
1827  "avdel %4 avg %5 tcoffset %6 avp %7 avpen %8 avdc %9 "
1828  "diverge %10")
1829  .arg(currentaudiotime)
1830  .arg(timecode)
1831  .arg(frame_interval)
1832  .arg(timecode - currentaudiotime -
1833  (int)(vsync_delay_clock*audio.GetStretchFactor()+500)/1000)
1834  .arg(avsync_avg)
1835  .arg(tc_wrap[TC_AUDIO])
1836  .arg(avsync_predictor)
1838  .arg(vsync_delay_clock)
1839  .arg(diverge)
1840  );
1841  if (currentaudiotime != 0 && timecode != 0)
1842  { // currentaudiotime == 0 after a seek
1843  // The time at the start of this frame (ie, now) is given by
1844  // last->timecode
1845  if (prevtc != 0)
1846  {
1847  int delta = (int)((timecode - prevtc)/play_speed) -
1848  (frame_interval / 1000);
1849  // If timecode is off by a frame (dropped frame) wait to sync
1850  if (delta > frame_interval / 1200 &&
1851  delta < frame_interval / 1000 * 3 &&
1852  prevrp == 0)
1853  {
1854  // wait an extra frame interval
1855  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
1856  QString("A/V delay %1").arg(delta));
1858  // If we're duplicating a frame, it may be because
1859  // the container frame rate doesn't match the
1860  // stream frame rate. In this case, we increment
1861  // the fake frame counter so that avformat
1862  // timestamp-based seeking will work.
1863  if (!decoder->HasPositionMap())
1865  }
1866  }
1867  prevtc = timecode;
1868  prevrp = repeat_pict;
1869 
1870  // usec
1871  avsync_delay = (timecode - currentaudiotime) * 1000 -
1872  (int)(vsync_delay_clock*audio.GetStretchFactor());
1873 
1874  // prevents major jitter when pts resets during dvd title
1875  if (avsync_delay > 2000000 && limit_delay)
1876  avsync_delay = 90000;
1878 
1879  int avsync_used = avsync_avg;
1880  if (labs(avsync_used) > labs(avsync_delay))
1881  avsync_used = avsync_delay;
1882 
1883  /* If the audio time codes and video diverge, shift
1884  the video by one interlaced field (1/2 frame) */
1885  if (!lastsync)
1886  {
1887  if (avsync_used > refreshrate)
1888  {
1890  }
1891  else if (avsync_used < 0 - refreshrate)
1892  {
1894  }
1895  }
1896  else
1897  lastsync = false;
1898  }
1899  else
1900  {
1901  ResetAVSync();
1902  }
1903  }
1904  else
1905  {
1906  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
1907  QString("A/V no sync proc ns:%1").arg(normal_speed));
1908  }
1909 }
1910 
1911 void MythPlayer::WaitForTime(int64_t framedue)
1912 {
1913  int64_t unow = m_avTimer.nsecsElapsed() / 1000;
1914  int64_t delay = framedue - unow;
1915  if (delay > 0)
1916  QThread::usleep(static_cast<unsigned long>(delay));
1917 }
1918 
1919 #define AVSYNC_MAX_LATE 10000000
1921 {
1922  if (videoOutput->IsErrored())
1923  {
1924  LOG(VB_GENERAL, LOG_ERR, LOC +
1925  "AVSync: Unknown error in videoOutput, aborting playback.");
1926  SetErrored(tr("Failed to initialize A/V Sync"));
1927  return;
1928  }
1929  int64_t videotimecode = 0;
1930 
1931  bool dropframe = false;
1932  bool pause_audio = false;
1933  int64_t framedue = 0;
1934  int64_t audio_adjustment = 0;
1935  int64_t unow = 0;
1936  int64_t lateness = 0;
1937  auto playspeed1000 = static_cast<int64_t>(1000.0F / play_speed);
1938  bool reset = false;
1939 
1940  while (framedue == 0)
1941  {
1942  if (buffer)
1943  {
1944  videotimecode = buffer->timecode & 0x0000ffffffffffff;
1945  // Detect bogus timecodes from DVD and ignore them.
1946  if (videotimecode != buffer->timecode)
1947  videotimecode = maxtcval;
1948  }
1949 
1950  unow = m_avTimer.nsecsElapsed() / 1000;
1951 
1953  {
1954  framedue = unow + frame_interval;
1955  break;
1956  }
1957  // first time or after a seek - setup of rtcbase
1958  if (rtcbase == 0)
1959  {
1960  // cater for DVB radio
1961  if (videotimecode == 0)
1962  videotimecode = audio.GetAudioTime();;
1963  // On first frame we get nothing, so exit out.
1964  if (videotimecode == 0)
1965  return;
1966  rtcbase = unow - videotimecode * playspeed1000;
1967  maxtcval = 0;
1968  maxtcframes = 0;
1969  numdroppedframes = 0;
1970  m_timeOffsetBase = static_cast<int64_t>(TranslatePositionFrameToMs(framesPlayed, false)) - videotimecode;
1971  }
1972 
1973  if (videotimecode == 0)
1974  videotimecode = maxtcval + frame_interval/1000;
1975  int64_t tcincr = videotimecode - maxtcval;
1976  if (tcincr > 0 || tcincr < -100)
1977  {
1978  maxtcval = videotimecode;
1979  maxtcframes = 0;
1980  }
1981  else
1982  {
1983  maxtcframes++;
1984  videotimecode = maxtcval + maxtcframes * frame_interval/1000;
1985  }
1986 
1987  if (play_speed > 0.0F)
1988  framedue = rtcbase + videotimecode * playspeed1000;
1989  else
1990  framedue = unow + frame_interval / 2;
1991 
1992  // recalculate framesPlayed to conform to actual time code.
1993  framesPlayed = TranslatePositionMsToFrame(static_cast<uint64_t>(videotimecode + m_timeOffsetBase), false);
1994  decoder->SetFramesPlayed(static_cast<long long>(framesPlayed));
1995 
1996  lateness = unow - framedue;
1997  dropframe = false;
1998  if (lateness > 30000)
1999  dropframe = numdroppedframes < 10;
2000 
2001  if (lateness <= 30000 && prior_audiotimecode > 0
2002  && prior_videotimecode > 0)
2003  {
2004  // Get video in sync with audio
2005  audio_adjustment = prior_audiotimecode - prior_videotimecode;
2006  // If there is excess audio - throw it away.
2007  if (audio_adjustment < -200)
2008  {
2009  audio.Reset();
2010  audio_adjustment = 0;
2011  }
2012  int sign = audio_adjustment < 0 ? -1 : 1;
2013  int64_t fix_amount = audio_adjustment * sign;
2014  if (fix_amount > avsync2adjustms)
2015  fix_amount = avsync2adjustms;
2016  // Faster catch-up when off by more than 200 ms
2017  if (audio_adjustment * sign > 200)
2018  // fix the sync within 15 - 20 frames
2019  fix_amount = audio_adjustment * sign / 15;
2020  auto speedup1000 = static_cast<int64_t>(1000 * play_speed);
2021  rtcbase -= static_cast<int64_t>(1000000 * fix_amount * sign / speedup1000);
2022  if (audio_adjustment * sign > 20 || (framesPlayed % 400 == 0))
2023  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AV Sync: Audio %1 by %2 ms")
2024  .arg(audio_adjustment > 0 ? "ahead" : "behind").arg(abs(audio_adjustment)));
2025  if (audio_adjustment > 200)
2026  pause_audio = true;
2027  }
2028  // sanity check - reset rtcbase if time codes have gone crazy.
2029  if ((lateness > AVSYNC_MAX_LATE || lateness < - AVSYNC_MAX_LATE))
2030  {
2031  framedue = 0;
2032  rtcbase = 0;
2033  if (reset)
2034  {
2035  LOG(VB_GENERAL, LOG_ERR, LOC +
2036  QString("Resetting AV Sync2 failed, lateness = %1").arg(lateness));
2037  SetErrored(tr("Failed to initialize A/V Sync"));
2038  return;
2039  }
2040  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2041  QString("Resetting AV Sync2, lateness = %1").arg(lateness));
2042  reset = true;
2043  }
2044  }
2045  prior_videotimecode = videotimecode;
2046  disp_timecode = videotimecode;
2047 
2049  avsync_avg = static_cast<int>(audio_adjustment * 1000);
2050 
2051  bool decoderdeint = buffer && buffer->decoder_deinterlaced;
2052  FrameScanType ps = m_scan;
2053  if (kScan_Detect == m_scan || kScan_Ignore == m_scan || decoderdeint)
2054  {
2055  ps = kScan_Progressive;
2056  }
2057  else if (buffer && is_interlaced(ps))
2058  {
2059  ps = kScan_Interlaced;
2061  }
2062 
2063  // only display the second field if needed
2065 
2066  if (buffer && !dropframe)
2067  {
2068  osdLock.lock();
2069  videoOutput->ProcessFrame(buffer, osd, pip_players, ps);
2070  osdLock.unlock();
2071  }
2072 
2073  if (!pause_audio && avsync_audiopaused)
2074  {
2075  avsync_audiopaused = false;
2076  audio.Pause(false);
2077  }
2078  if (pause_audio && !avsync_audiopaused)
2079  {
2080  avsync_audiopaused = true;
2081  audio.Pause(true);
2082  }
2083 
2084  if (dropframe)
2085  numdroppedframes++;
2086  else
2087  numdroppedframes = 0;
2088 
2089  if (dropframe)
2090  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2091  QString("dropping frame to catch up, lateness=%1 usec")
2092  .arg(lateness));
2093  else if (!FlagIsSet(kVideoIsNull) && buffer)
2094  {
2095  // if we get here, we're actually going to do video output
2096  osdLock.lock();
2097  videoOutput->PrepareFrame(buffer, ps, osd);
2098  osdLock.unlock();
2099  // Don't wait for sync if this is a secondary PBP otherwise
2100  // the primary PBP will become out of sync
2101  if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
2102  WaitForTime(framedue);
2103  // get time codes for calculating difference next time
2105  videoOutput->Show(ps);
2106  if (videoOutput->IsErrored())
2107  {
2108  LOG(VB_GENERAL, LOG_ERR, LOC + "Error condition detected "
2109  "in videoOutput after Show(), aborting playback.");
2110  SetErrored(tr("Serious error detected in Video Output"));
2111  return;
2112  }
2113  if (m_double_framerate)
2114  {
2115  //second stage of deinterlacer processing
2116  if (kScan_Interlaced == ps)
2117  ps = kScan_Intr2ndField;
2118  osdLock.lock();
2119  // Only double rate CPU deinterlacers require an extra call to ProcessFrame
2121  videoOutput->ProcessFrame(buffer, osd, pip_players, ps);
2122  videoOutput->PrepareFrame(buffer, ps, osd);
2123  osdLock.unlock();
2124  // Display the second field
2125  if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
2126  {
2127  int64_t due = framedue + frame_interval / 2;
2128  WaitForTime(due);
2129  }
2130  videoOutput->Show(ps);
2131  }
2132  }
2133  else
2134  WaitForTime(framedue);
2135 
2136  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
2137  QString("A/V timecodes audio=%1 video=%2 frameinterval=%3 "
2138  "audioadj=%4 tcoffset=%5 unow=%6 udue=%7")
2139  .arg(prior_audiotimecode)
2140  .arg(prior_videotimecode)
2141  .arg(frame_interval)
2142  .arg(audio_adjustment)
2143  .arg(tc_wrap[TC_AUDIO])
2144  .arg(unow)
2145  .arg(framedue)
2146  );
2147 
2148 }
2149 
2151 {
2152  if (needNewPauseFrame)
2153  {
2155  {
2157  needNewPauseFrame = false;
2158 
2159  if (deleteMap.IsEditing())
2160  {
2161  osdLock.lock();
2162  if (osd)
2164  osdLock.unlock();
2165  }
2166  }
2167  else
2168  {
2169  decodeOneFrame = true;
2170  }
2171  }
2172 }
2173 
2175 {
2176  if (!videoOutput || ! videosync)
2177  return;
2178 
2179  if (videoOutput->IsErrored())
2180  {
2181  SetErrored(tr("Serious error detected in Video Output"));
2182  return;
2183  }
2184 
2185  // clear the buffering state
2186  SetBuffering(false);
2187 
2189  PreProcessNormalFrame(); // Allow interactiveTV to draw on pause frame
2190 
2192  osdLock.lock();
2194  videoOutput->PrepareFrame(nullptr, scan, osd);
2195  osdLock.unlock();
2196  videoOutput->Show(scan);
2197  videosync->Start();
2198 }
2199 
2200 void MythPlayer::SetBuffering(bool new_buffering)
2201 {
2202  if (!buffering && new_buffering)
2203  {
2204  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for video buffers...");
2205  buffering = true;
2206  buffering_start = QTime::currentTime();
2207  buffering_last_msg = QTime::currentTime();
2208  }
2209  else if (buffering && !new_buffering)
2210  {
2211  buffering = false;
2212  }
2213 }
2214 
2215 // For debugging playback set this to increase the timeout so that
2216 // playback does not fail if stepping through code.
2217 // Set PREBUFFERDEBUG to any value and you will get 30 minutes.
2218 static char *preBufferDebug = getenv("PREBUFFERDEBUG");
2219 
2221 {
2222  if (!videoOutput)
2223  return false;
2224 
2225  if (!(min_buffers ? (videoOutput->ValidVideoFrames() >= min_buffers) :
2227  {
2228  SetBuffering(true);
2229 
2230  // This piece of code is to address the problem, when starting
2231  // Live TV, of jerking and stuttering. Without this code
2232  // that could go on forever, but is cured by a pause and play.
2233  // This code inserts a brief pause and play when the potential
2234  // for the jerking is detected.
2235 
2236  bool watchingTV = IsWatchingInprogress();
2237  if ( (livetv || watchingTV) && !FlagIsSet(kMusicChoice))
2238  {
2239  uint64_t frameCount = GetCurrentFrameCount();
2240  uint64_t framesLeft = frameCount - framesPlayed;
2241  auto margin = (uint64_t) (video_frame_rate * 3);
2242  if (framesLeft < margin)
2243  {
2244  if (rtcbase)
2245  LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
2246  QString("Pause to allow live tv catch up. Position in sec. Current: %2, Total: %3")
2247  .arg(framesPlayed).arg(frameCount));
2248  audio.Pause(true);
2249  avsync_audiopaused = true;
2250  rtcbase = 0;
2251  }
2252  }
2253  usleep(frame_interval >> 3);
2254  int waited_for = buffering_start.msecsTo(QTime::currentTime());
2255  int last_msg = buffering_last_msg.msecsTo(QTime::currentTime());
2256  if (last_msg > 100 && !FlagIsSet(kMusicChoice))
2257  {
2258  if (++bufferingCounter == 10)
2259  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2260  "To see more buffering messages use -v playback");
2261  if (bufferingCounter >= 10)
2262  LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
2263  QString("Waited %1ms for video buffers %2")
2264  .arg(waited_for).arg(videoOutput->GetFrameStatus()));
2265  else
2266  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2267  QString("Waited %1ms for video buffers %2")
2268  .arg(waited_for).arg(videoOutput->GetFrameStatus()));
2269  buffering_last_msg = QTime::currentTime();
2271  && gCoreContext->GetBoolSetting("MusicChoiceEnabled", false))
2272  {
2274  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2275  "Music Choice program detected - disabling AV Sync.");
2276  avsync_audiopaused = false;
2277  audio.Pause(false);
2278  }
2279  if (waited_for > 7000 && audio.IsBufferAlmostFull()
2280  && !FlagIsSet(kMusicChoice))
2281  {
2282  // We are likely to enter this condition
2283  // if the audio buffer was too full during GetFrame in AVFD
2284  LOG(VB_GENERAL, LOG_NOTICE, LOC + "Resetting audio buffer");
2285  audio.Reset();
2286  }
2287  // Finish audio pause for sync after 1 second
2288  // in case of infrequent video frames (e.g. music choice)
2289  if (avsync_audiopaused && waited_for > 1000)
2290  {
2291  avsync_audiopaused = false;
2292  audio.Pause(false);
2293  }
2294  }
2295  int msecs = 500;
2296  if (preBufferDebug)
2297  msecs = 1800000;
2298  if ((waited_for > msecs /*500*/) && !videoOutput->EnoughFreeFrames())
2299  {
2300  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2301  "Timed out waiting for frames, and"
2302  "\n\t\t\tthere are not enough free frames. "
2303  "Discarding buffered frames.");
2304  // This call will result in some ugly frames, but allows us
2305  // to recover from serious problems if frames get leaked.
2306  DiscardVideoFrames(true, true);
2307  }
2308  msecs = 30000;
2309  if (preBufferDebug)
2310  msecs = 1800000;
2311  if (waited_for > msecs /*30000*/) // 30 seconds for internet streamed media
2312  {
2313  LOG(VB_GENERAL, LOG_ERR, LOC +
2314  "Waited too long for decoder to fill video buffers. Exiting..");
2315  SetErrored(tr("Video frame buffering failed too many times."));
2316  }
2317  if (normal_speed)
2318  videosync->Start();
2319  return false;
2320  }
2321 
2322  SetBuffering(false);
2323  return true;
2324 }
2325 
2327 {
2328  if (!frame)
2329  return;
2330 
2331  if (!qFuzzyCompare(frame->aspect, video_aspect) && frame->aspect > 0.0F)
2332  {
2333  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2334  QString("Video Aspect ratio changed from %1 to %2")
2335  .arg(video_aspect).arg(frame->aspect));
2336  video_aspect = frame->aspect;
2337  if (videoOutput)
2338  {
2340  ReinitOSD();
2341  }
2342  }
2343 }
2344 
2345 void MythPlayer::DisplayNormalFrame(bool check_prebuffer)
2346 {
2347  if (allpaused || (check_prebuffer && !PrebufferEnoughFrames()))
2348  return;
2349 
2350  // clear the buffering state
2351  SetBuffering(false);
2352 
2353  // If PiP then release the last shown frame to the decoding queue
2354  if (player_ctx->IsPIP())
2356 
2357  // retrieve the next frame
2360 
2361  // Check aspect ratio
2362  CheckAspectRatio(frame);
2363 
2365  UpdateFFRewSkip();
2366 
2367  // Player specific processing (dvd, bd, mheg etc)
2369 
2370  // handle scan type changes
2371  AutoDeint(frame);
2372  detect_letter_box->SwitchTo(frame);
2373 
2374  AVSync(frame, false);
2375 
2376  // If PiP then keep this frame for MythPlayer::GetCurrentFrame
2377  if (!player_ctx->IsPIP())
2378  {
2381  // We use the underlying pix_fmt as it retains the distinction between hardware
2382  // and software frames for decode only decoders.
2383  m_lastFrameCodec = PixelFormatToFrameType(static_cast<AVPixelFormat>(frame->pix_fmt));
2385  }
2386 }
2387 
2389 {
2390 #ifdef USING_MHEG
2391  // handle Interactive TV
2392  if (GetInteractiveTV())
2393  {
2394  osdLock.lock();
2395  itvLock.lock();
2396  if (osd && videoOutput->GetOSDPainter())
2397  {
2398  InteractiveScreen *window =
2399  dynamic_cast<InteractiveScreen *>(osd->GetWindow(OSD_WIN_INTERACT));
2400  if ((interactiveTV->ImageHasChanged() || !itvVisible) && window)
2401  {
2403  itvVisible = true;
2404  }
2405  }
2406  itvLock.unlock();
2407  osdLock.unlock();
2408  }
2409 #endif // USING_MHEG
2410 }
2411 
2413 {
2414  int refreshinterval = 1;
2415  if (videosync)
2416  {
2417  refreshinterval = videosync->getRefreshInterval();
2418  }
2419  else if (m_display)
2420  {
2421  // used by the decoder before videosync is created
2422  refreshinterval = m_display->GetDisplayInfo(frame_interval).Rate();
2423  }
2424 
2425  // At this point we may not have the correct frame rate.
2426  // Since interlaced is always at 25 or 30 fps, if the interval
2427  // is less than 30000 (33fps) it must be representing one
2428  // field and not one frame, so multiply by 2.
2429  int realfi = frame_interval;
2430  if (frame_interval < 30000)
2431  realfi = frame_interval * 2;
2432  return ((realfi / 2.0) > (refreshinterval * 0.995));
2433 }
2434 
2436 {
2437  if (!output_jmeter)
2438  return;
2439  bool verbose = VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_ANY);
2440  double rate = enable ? video_frame_rate : verbose ? (video_frame_rate * 4) : 0.0;
2441  output_jmeter->SetNumCycles(static_cast<int>(rate));
2442 }
2443 
2444 void MythPlayer::ForceDeinterlacer(bool DoubleRate, MythDeintType Deinterlacer)
2445 {
2446  if (videoOutput)
2447  videoOutput->SetDeinterlacing(true, DoubleRate, Deinterlacer);
2448 }
2449 
2451 {
2452  if (!FlagIsSet(kVideoIsNull) && !player_ctx->IsPIP())
2453  {
2454  QRect visible, total;
2455  float aspect, scaling;
2456 
2457  osdLock.lock();
2458  osd = new OSD(this, m_tv, videoOutput->GetOSDPainter());
2459  videoOutput->GetOSDBounds(total, visible, aspect, scaling, 1.0F);
2460  osd->Init(visible, aspect);
2462 
2463 #ifdef USING_MHEG
2464  if (GetInteractiveTV())
2465  {
2466  QMutexLocker locker(&itvLock);
2467  interactiveTV->Reinit(total, visible, aspect);
2468  }
2469 #endif // USING_MHEG
2470 
2471  // If there is a forced text subtitle track (which is possible
2472  // in e.g. a .mkv container), and forced subtitles are
2473  // allowed, then start playback with that subtitle track
2474  // selected. Otherwise, use the frontend settings to decide
2475  // which captions/subtitles (if any) to enable at startup.
2476  // TODO: modify the fix to #10735 to use this approach
2477  // instead.
2478  bool hasForcedTextTrack = false;
2479  uint forcedTrackNumber = 0;
2481  {
2482  uint numTextTracks = decoder->GetTrackCount(kTrackTypeRawText);
2483  for (uint i = 0; !hasForcedTextTrack && i < numTextTracks; ++i)
2484  {
2486  {
2487  hasForcedTextTrack = true;
2488  forcedTrackNumber = i;
2489  }
2490  }
2491  }
2492  if (hasForcedTextTrack)
2493  SetTrack(kTrackTypeRawText, forcedTrackNumber);
2494  else
2496 
2497  osdLock.unlock();
2498  }
2499 
2500  SetPlaying(true);
2501  ClearAfterSeek(false);
2502 
2503  avsync_delay = 0;
2504  avsync_avg = 0;
2505  avsync_next = avsync_interval; // Frames till next sync check
2506  refreshrate = 0;
2507  lastsync = false;
2508 
2511 
2512  float temp_speed = (play_speed == 0.0F) ? audio.GetStretchFactor() : play_speed;
2513  int fr_int = (1000000.0 / video_frame_rate / static_cast<double>(temp_speed));
2514  int rf_int = m_display->GetDisplayInfo(fr_int).Rate();
2515 
2516  // Default to interlaced playback but set the tracker to progressive
2517  // Enable autodetection of interlaced/progressive from video stream
2518  // Previously we set to interlaced and the scan tracker to 2 but this
2519  // mis-'detected' a number of streams as interlaced when they are progressive.
2520  // This significantly reduces the number of errors and also ensures we do not
2521  // needlessly setup deinterlacers - which may consume significant resources.
2522  // We set to interlaced for those streams whose frame rate is initially detected
2523  // as e.g. 59.9 when it is actually 29.97 interlaced.
2525  m_scan_locked = false;
2526  m_double_framerate = false;
2527  m_scan_tracker = -2;
2528 
2530  {
2532  }
2533  else if (FlagIsSet(kVideoIsNull))
2534  {
2536  }
2537  else if (videoOutput)
2538  {
2539  videosync = VideoSync::BestMethod(videoOutput, static_cast<uint>(rf_int));
2540  m_double_framerate = CanSupportDoubleRate(); // needs videosync
2542  }
2543 
2544  if (!videosync)
2545  videosync = new BusyWaitVideoSync(videoOutput, rf_int);
2546 
2547  InitAVSync();
2548  videosync->Start();
2549 }
2550 
2552 {
2553  if (videoPaused || isDummy)
2554  {
2555  switch (player_ctx->GetPIPState())
2556  {
2557  case kPIPonTV:
2558  case kPBPRight:
2559  break;
2560  case kPIPOff:
2561  case kPIPStandAlone:
2562  case kPBPLeft: // PrimaryBPB
2563  usleep(frame_interval);
2564  break;
2565  }
2567  }
2568  else
2570 
2571  if (FlagIsSet(kVideoIsNull) && decoder)
2573  else if (decoder && decoder->GetEof() != kEofStateNone)
2574  ++framesPlayed;
2575  else
2577  return !IsErrored();
2578 }
2579 
2581 {
2582  osdLock.lock();
2583  vidExitLock.lock();
2584  delete osd;
2585  delete videosync;
2586  delete videoOutput;
2587  osd = nullptr;
2588  videosync = nullptr;
2589  videoOutput = nullptr;
2590  vidExitLock.unlock();
2591  osdLock.unlock();
2592 }
2593 
2594 bool MythPlayer::FastForward(float seconds)
2595 {
2596  if (!videoOutput)
2597  return false;
2598 
2599  if (fftime <= 0)
2600  {
2601  float current = ComputeSecs(framesPlayed, true);
2602  float dest = current + seconds;
2603  float length = ComputeSecs(totalFrames, true);
2604 
2605  if (dest > length)
2606  {
2607  int64_t pos = TranslatePositionMsToFrame(seconds * 1000, false);
2608  if (CalcMaxFFTime(pos) < 0)
2609  return true;
2610  // Reach end of recording, go to 1 or 3s before the end
2611  dest = (livetv || IsWatchingInprogress()) ? -3.0 : -1.0;
2612  }
2613  uint64_t target = FindFrame(dest, true);
2614  fftime = target - framesPlayed;
2615  }
2616  return fftime > CalcMaxFFTime(fftime, false);
2617 }
2618 
2619 bool MythPlayer::Rewind(float seconds)
2620 {
2621  if (!videoOutput)
2622  return false;
2623 
2624  if (rewindtime <= 0)
2625  {
2626  float current = ComputeSecs(framesPlayed, true);
2627  float dest = current - seconds;
2628  if (dest < 0)
2629  {
2630  int64_t pos = TranslatePositionMsToFrame(seconds * 1000, false);
2631  if (CalcRWTime(pos) < 0)
2632  return true;
2633  dest = 0;
2634  }
2635  uint64_t target = FindFrame(dest, true);
2636  rewindtime = framesPlayed - target;
2637  }
2638  return (uint64_t)rewindtime >= framesPlayed;
2639 }
2640 
2641 bool MythPlayer::JumpToFrame(uint64_t frame)
2642 {
2643  if (!videoOutput)
2644  return false;
2645 
2646  bool ret = false;
2647  fftime = rewindtime = 0;
2648  if (frame > framesPlayed)
2649  {
2650  fftime = frame - framesPlayed;
2651  ret = fftime > CalcMaxFFTime(fftime, false);
2652  }
2653  else if (frame < framesPlayed)
2654  {
2655  rewindtime = framesPlayed - frame;
2656  ret = fftime > CalcMaxFFTime(fftime, false);
2657  }
2658  return ret;
2659 }
2660 
2661 
2662 void MythPlayer::JumpChapter(int chapter)
2663 {
2664  if (jumpchapter == 0)
2665  jumpchapter = chapter;
2666 }
2667 
2668 void MythPlayer::ResetPlaying(bool resetframes)
2669 {
2670  ClearAfterSeek();
2671  ffrew_skip = 1;
2672  if (resetframes)
2674  if (decoder)
2675  {
2676  decoder->Reset(true, true, true);
2677  if (decoder->IsErrored())
2678  SetErrored("Unable to reset video decoder");
2679  }
2680 }
2681 
2683 {
2684  bool last = !(player_ctx->m_tvchain->HasNext());
2685  SetWatchingRecording(last);
2686 }
2687 
2689 {
2690  if (!IsReallyNearEnd())
2691  return;
2692 
2693  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchToProgram - start");
2694  bool discontinuity = false, newtype = false;
2695  int newid = -1;
2697  discontinuity, newtype, newid);
2698  if (!pginfo)
2699  return;
2700 
2701  bool newIsDummy = player_ctx->m_tvchain->GetInputType(newid) == "DUMMY";
2702 
2703  SetPlayingInfo(*pginfo);
2704  Pause();
2705  ChangeSpeed();
2706 
2707  // Release all frames to ensure the current decoder resources are released
2708  DiscardVideoFrames(true, true);
2709 
2710  if (newIsDummy)
2711  {
2712  OpenDummy();
2713  ResetPlaying();
2715  delete pginfo;
2716  return;
2717  }
2718 
2720  {
2721  // Restore original ringbuffer
2722  auto *ic = dynamic_cast< ICRingBuffer* >(player_ctx->m_buffer);
2723  if (ic) // should always be true
2724  player_ctx->m_buffer = ic->Take();
2725  delete ic;
2726  }
2727 
2730 
2731  if (!player_ctx->m_buffer->IsOpen())
2732  {
2733  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram's OpenFile failed " +
2734  QString("(input type: %1).")
2735  .arg(player_ctx->m_tvchain->GetInputType(newid)));
2736  LOG(VB_GENERAL, LOG_ERR, player_ctx->m_tvchain->toString());
2738  SetErrored(tr("Error opening switch program buffer"));
2739  delete pginfo;
2740  return;
2741  }
2742 
2743  if (GetEof() != kEofStateNone)
2744  {
2745  discontinuity = true;
2746  ResetCaptions();
2747  }
2748 
2749  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SwitchToProgram(void) "
2750  "discont: %1 newtype: %2 newid: %3 decoderEof: %4")
2751  .arg(discontinuity).arg(newtype).arg(newid).arg(GetEof()));
2752 
2753  if (discontinuity || newtype)
2754  {
2755  player_ctx->m_tvchain->SetProgram(*pginfo);
2756  if (decoder)
2757  decoder->SetProgramInfo(*pginfo);
2758 
2759  player_ctx->m_buffer->Reset(true);
2760  if (newtype)
2761  {
2762  if (OpenFile() < 0)
2763  SetErrored(tr("Error opening switch program file"));
2764  }
2765  else
2766  ResetPlaying();
2767  }
2768  else
2769  {
2771  if (decoder)
2772  {
2775  }
2776  }
2777  delete pginfo;
2778 
2779  if (IsErrored())
2780  {
2781  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram failed.");
2783  return;
2784  }
2785 
2787 
2788  // the bitrate is reset by player_ctx->m_buffer->OpenFile()...
2789  if (decoder)
2792 
2793  if (discontinuity || newtype)
2794  {
2795  CheckTVChain();
2796  forcePositionMapSync = true;
2797  }
2798 
2799  Play();
2800  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchToProgram - end");
2801 }
2802 
2803 // This is called from decoder thread. Set an indicator that will
2804 // be checked and actioned in the player thread.
2806 {
2807  LOG(VB_PLAYBACK, LOG_INFO, LOC + "FileChangedCallback");
2808  fileChanged = true;
2809 }
2810 
2811 // Called from the player thread.
2813 {
2814  fileChanged = false;
2815  LOG(VB_PLAYBACK, LOG_INFO, LOC + "FileChanged");
2816 
2817  Pause();
2818  ChangeSpeed();
2819  if (dynamic_cast<AvFormatDecoder *>(decoder))
2820  player_ctx->m_buffer->Reset(false, true);
2821  else
2822  player_ctx->m_buffer->Reset(false, true, true);
2824  Play();
2825 
2827 
2828  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
2830  if (decoder)
2832  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
2833 
2834  CheckTVChain();
2835  forcePositionMapSync = true;
2836 }
2837 
2838 
2839 
2840 
2842 {
2843  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToProgram - start");
2844  bool discontinuity = false, newtype = false;
2845  int newid = -1;
2846  long long nextpos = player_ctx->m_tvchain->GetJumpPos();
2848  discontinuity, newtype, newid);
2849  if (!pginfo)
2850  return;
2851 
2852  inJumpToProgramPause = true;
2853 
2854  bool newIsDummy = player_ctx->m_tvchain->GetInputType(newid) == "DUMMY";
2855  SetPlayingInfo(*pginfo);
2856 
2857  Pause();
2858  ChangeSpeed();
2859  ResetCaptions();
2860 
2861  // Release all frames to ensure the current decoder resources are released
2862  DiscardVideoFrames(true, true);
2863 
2864  player_ctx->m_tvchain->SetProgram(*pginfo);
2865  player_ctx->m_buffer->Reset(true);
2866 
2867  if (newIsDummy)
2868  {
2869  OpenDummy();
2870  ResetPlaying();
2872  delete pginfo;
2873  inJumpToProgramPause = false;
2874  return;
2875  }
2876 
2877  SendMythSystemPlayEvent("PLAY_CHANGED", pginfo);
2878 
2880  {
2881  // Restore original ringbuffer
2882  auto *ic = dynamic_cast< ICRingBuffer* >(player_ctx->m_buffer);
2883  if (ic) // should always be true
2884  player_ctx->m_buffer = ic->Take();
2885  delete ic;
2886  }
2887 
2890  QString subfn = player_ctx->m_buffer->GetSubtitleFilename();
2891  TVState desiredState = player_ctx->GetState();
2892  bool isInProgress = (desiredState == kState_WatchingRecording ||
2893  desiredState == kState_WatchingLiveTV);
2894  if (GetSubReader())
2895  GetSubReader()->LoadExternalSubtitles(subfn, isInProgress &&
2896  !subfn.isEmpty());
2897 
2898  if (!player_ctx->m_buffer->IsOpen())
2899  {
2900  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram's OpenFile failed " +
2901  QString("(input type: %1).")
2902  .arg(player_ctx->m_tvchain->GetInputType(newid)));
2903  LOG(VB_GENERAL, LOG_ERR, player_ctx->m_tvchain->toString());
2905  SetErrored(tr("Error opening jump program file buffer"));
2906  delete pginfo;
2907  inJumpToProgramPause = false;
2908  return;
2909  }
2910 
2911  bool wasDummy = isDummy;
2912  if (newtype || wasDummy)
2913  {
2914  if (OpenFile() < 0)
2915  SetErrored(tr("Error opening jump program file"));
2916  }
2917  else
2918  ResetPlaying();
2919 
2920  if (IsErrored() || !decoder)
2921  {
2922  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram failed.");
2923  if (!IsErrored())
2924  SetErrored(tr("Error reopening video decoder"));
2925  delete pginfo;
2926  inJumpToProgramPause = false;
2927  return;
2928  }
2929 
2931 
2932  // the bitrate is reset by player_ctx->m_buffer->OpenFile()...
2935 
2936  decoder->SetProgramInfo(*pginfo);
2937  delete pginfo;
2938 
2939  CheckTVChain();
2940  forcePositionMapSync = true;
2941  inJumpToProgramPause = false;
2942  Play();
2943  ChangeSpeed();
2944 
2945  // check that we aren't too close to the end of program.
2946  // and if so set it to 10s from the end if completed recordings
2947  // or 3s if live
2948  long long duration = player_ctx->m_tvchain->GetLengthAtCurPos();
2949  int maxpos = player_ctx->m_tvchain->HasNext() ? 10 : 3;
2950 
2951  if (nextpos > (duration - maxpos))
2952  {
2953  nextpos = duration - maxpos;
2954  if (nextpos < 0)
2955  {
2956  nextpos = 0;
2957  }
2958  }
2959  else if (nextpos < 0)
2960  {
2961  // it's a relative position to the end
2962  nextpos += duration;
2963  }
2964 
2965  // nextpos is the new position to use in seconds
2966  nextpos = TranslatePositionMsToFrame(nextpos * 1000, true);
2967 
2968  if (nextpos > 10)
2969  DoJumpToFrame(nextpos, kInaccuracyNone);
2970 
2972  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToProgram - end");
2973 }
2974 
2976 {
2977  if (OpenFile() < 0)
2978  {
2979  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to open video file.");
2980  return false;
2981  }
2982 
2983  framesPlayed = 0;
2984  framesPlayedExtra = 0;
2985  rewindtime = fftime = 0;
2987  jumpchapter = 0;
2989  bufferingCounter=0;
2990 
2991  if (!InitVideo())
2992  {
2993  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to initialize video.");
2994  audio.DeleteOutput();
2995  return false;
2996  }
2997 
2998  bool seek = bookmarkseek > 30;
2999  EventStart();
3000  DecoderStart(true);
3001  if (seek)
3002  InitialSeek();
3003  VideoStart();
3004 
3005  playerThread->setPriority(QThread::TimeCriticalPriority);
3006 #ifdef Q_OS_ANDROID
3007  setpriority(PRIO_PROCESS, playerThreadId, -20);
3008 #endif
3009  UnpauseDecoder();
3010  return !IsErrored();
3011 }
3012 
3014 {
3015  // TODO handle initial commskip and/or cutlist skip as well
3016  if (bookmarkseek > 30)
3017  {
3019  if (clearSavedPosition && !player_ctx->IsPIP())
3020  SetBookmark(true);
3021  }
3022 }
3023 
3024 
3026 {
3027  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StopPlaying - begin"));
3028  playerThread->setPriority(QThread::NormalPriority);
3029 #ifdef Q_OS_ANDROID
3030  setpriority(PRIO_PROCESS, playerThreadId, 0);
3031 #endif
3032 
3033  DecoderEnd();
3034  VideoEnd();
3035  AudioEnd();
3036 
3037  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StopPlaying - end"));
3038 }
3039 
3041 {
3042  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3043  {
3045  {
3046  // When initial playback gets underway, we override the ProgramInfo
3047  // flags such that future calls to GetBookmark() will consider only
3048  // an actual bookmark and not progstart or lastplaypos information.
3052  }
3053  }
3054  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3056 }
3057 
3059 {
3060  // Live TV program change
3061  if (fileChanged)
3062  FileChanged();
3063 
3064  // recreate the osd if a reinit was triggered by another thread
3065  if (reinit_osd)
3066  ReinitOSD();
3067 
3068  // reselect subtitle tracks if triggered by the decoder
3069  if (enableCaptions)
3070  SetCaptionsEnabled(true, false);
3071  if (disableCaptions)
3072  SetCaptionsEnabled(false, false);
3073 
3074  // enable/disable forced subtitles if signalled by the decoder
3079 
3080  // reset the scan (and hence deinterlacers) if triggered by the decoder
3081  if (resetScan != kScan_Ignore)
3083 
3084  // refresh the position map for an in-progress recording while editing
3086  {
3087  if (editUpdateTimer.elapsed() > 2000)
3088  {
3089  // N.B. the positionmap update and osd refresh are asynchronous
3090  forcePositionMapSync = true;
3091  osdLock.lock();
3093  osdLock.unlock();
3094  editUpdateTimer.start();
3095  }
3096  }
3097 
3098  // Refresh the programinfo in use status
3099  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3102  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3103 
3104  // Disable timestretch if we are too close to the end of the buffer
3105  if (ffrew_skip == 1 && (play_speed > 1.0F) && IsNearEnd())
3106  {
3107  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near end, Slowing down playback.");
3108  Play(1.0F, true, true);
3109  }
3110 
3112  {
3113  // Switch from the dummy recorder to the tuned program in livetv
3114  player_ctx->m_tvchain->JumpToNext(true, 0);
3115  JumpToProgram();
3116  }
3117  else if ((!allpaused || GetEof() != kEofStateNone) &&
3118  player_ctx->m_tvchain &&
3119  (decoder && !decoder->GetWaitForChange()))
3120  {
3121  // Switch to the next program in livetv
3123  SwitchToProgram();
3124  }
3125 
3126  // Jump to the next program in livetv
3128  {
3129  JumpToProgram();
3130  }
3131 
3132  // Change interactive stream if requested
3133  { QMutexLocker locker(&streamLock);
3134  if (!m_newStream.isEmpty())
3135  {
3136  QString stream = m_newStream;
3137  m_newStream.clear();
3138  locker.unlock();
3139  JumpToStream(stream);
3140  }}
3141 
3142  // Disable fastforward if we are too close to the end of the buffer
3143  if (ffrew_skip > 1 && (CalcMaxFFTime(100, false) < 100))
3144  {
3145  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near end, stopping fastforward.");
3146  Play(1.0F, true, true);
3147  }
3148 
3149  // Disable rewind if we are too close to the beginning of the buffer
3150  if (ffrew_skip < 0 && CalcRWTime(-ffrew_skip) >= 0 &&
3152  {
3153  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near start, stopping rewind.");
3154  float stretch = (ffrew_skip > 0) ? 1.0F : audio.GetStretchFactor();
3155  Play(stretch, true, true);
3156  }
3157 
3158  // Check for error
3160  {
3161  LOG(VB_GENERAL, LOG_ERR, LOC +
3162  "Unknown recorder error, exiting decoder");
3163  if (!IsErrored())
3164  SetErrored(tr("Irrecoverable recorder error"));
3165  killdecoder = true;
3166  return;
3167  }
3168 
3169  // Handle speed change
3170  if (play_speed != next_play_speed &&
3171  (!player_ctx->m_tvchain ||
3173  {
3174  ChangeSpeed();
3175  return;
3176  }
3177 
3178  // Check if we got a communication error, and if so pause playback
3180  {
3181  Pause();
3183  }
3184 
3185  // Handle end of file
3186  EofState eof = GetEof();
3187  if (HasReachedEof())
3188  {
3189 #ifdef USING_MHEG
3190  if (interactiveTV && interactiveTV->StreamStarted(false))
3191  {
3192  Pause();
3193  return;
3194  }
3195 #endif
3197  {
3198  LOG(VB_GENERAL, LOG_NOTICE, LOC + "LiveTV forcing JumpTo 1");
3199  player_ctx->m_tvchain->JumpToNext(true, 0);
3200  return;
3201  }
3202 
3203  bool videoDrained =
3205  bool audioDrained =
3206  !audio.GetAudioOutput() ||
3207  audio.IsPaused() ||
3209  if (eof != kEofStateDelayed || (videoDrained && audioDrained))
3210  {
3211  if (eof == kEofStateDelayed)
3212  LOG(VB_PLAYBACK, LOG_INFO,
3213  QString("waiting for no video frames %1")
3214  .arg(videoOutput->ValidVideoFrames()));
3215  LOG(VB_PLAYBACK, LOG_INFO,
3216  QString("HasReachedEof() at framesPlayed=%1 totalFrames=%2")
3217  .arg(framesPlayed).arg(GetCurrentFrameCount()));
3218  Pause();
3219  SetPlaying(false);
3220  return;
3221  }
3222  }
3223 
3224  // Handle rewind
3225  if (rewindtime > 0 && (ffrew_skip == 1 || ffrew_skip == 0))
3226  {
3228  if (rewindtime > 0)
3230  }
3231 
3232  // Handle fast forward
3233  if (fftime > 0 && (ffrew_skip == 1 || ffrew_skip == 0))
3234  {
3236  if (fftime > 0)
3237  {
3239  if (GetEof() != kEofStateNone)
3240  return;
3241  }
3242  }
3243 
3244  // Handle chapter jump
3245  if (jumpchapter != 0)
3247 
3248  // Handle commercial skipping
3249  if (commBreakMap.GetSkipCommercials() != 0 && (ffrew_skip == 1))
3250  {
3251  if (!commBreakMap.HasMap())
3252  {
3253  //: The commercials/adverts have not been flagged
3254  SetOSDStatus(tr("Not Flagged"), kOSDTimeout_Med);
3255  QString message = "COMMFLAG_REQUEST ";
3256  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3257  message += QString("%1").arg(player_ctx->m_playingInfo->GetChanID()) +
3259  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3260  gCoreContext->SendMessage(message);
3261  }
3262  else
3263  {
3264  QString msg;
3265  uint64_t jumpto = 0;
3266  uint64_t frameCount = GetCurrentFrameCount();
3267  // XXX CommBreakMap should use duration map not video_frame_rate
3268  bool jump = commBreakMap.DoSkipCommercials(jumpto, framesPlayed,
3270  frameCount, msg);
3271  if (!msg.isEmpty())
3273  if (jump)
3274  DoJumpToFrame(jumpto, kInaccuracyNone);
3275  }
3277  return;
3278  }
3279 
3280  // Handle automatic commercial skipping
3281  uint64_t jumpto = 0;
3282  if (deleteMap.IsEmpty() && (ffrew_skip == 1) &&
3284  commBreakMap.HasMap())
3285  {
3286  QString msg;
3287  uint64_t frameCount = GetCurrentFrameCount();
3288  // XXX CommBreakMap should use duration map not video_frame_rate
3289  bool jump = commBreakMap.AutoCommercialSkip(jumpto, framesPlayed,
3291  frameCount, msg);
3292  if (!msg.isEmpty())
3294  if (jump)
3295  DoJumpToFrame(jumpto, kInaccuracyNone);
3296  }
3297 
3298  // Handle cutlist skipping
3299  if (!allpaused && (ffrew_skip == 1) &&
3301  {
3302  if (jumpto == totalFrames)
3303  {
3304  if (!(endExitPrompt == 1 && !player_ctx->IsPIP() &&
3306  {
3308  }
3309  }
3310  else
3311  {
3312  DoJumpToFrame(jumpto, kInaccuracyNone);
3313  }
3314  }
3315 }
3316 
3318 {
3319  audio.DeleteOutput();
3320 }
3321 
3323 {
3324  decoderPauseLock.lock();
3326  {
3327  decoderPaused = true;
3328  decoderThreadPause.wakeAll();
3329  decoderPauseLock.unlock();
3330  return decoderPaused;
3331  }
3332 
3333  int tries = 0;
3334  pauseDecoder = true;
3335  while (decoderThread && !killdecoder && (tries++ < 100) &&
3336  !decoderThreadPause.wait(&decoderPauseLock, 100))
3337  {
3338  qApp->processEvents();
3339  LOG(VB_GENERAL, LOG_WARNING, LOC + "Waited 100ms for decoder to pause");
3340  }
3341  pauseDecoder = false;
3342  decoderPauseLock.unlock();
3343  return decoderPaused;
3344 }
3345 
3347 {
3348  decoderPauseLock.lock();
3349 
3351  {
3352  decoderPaused = false;
3353  decoderThreadUnpause.wakeAll();
3354  decoderPauseLock.unlock();
3355  return;
3356  }
3357 
3358  if (!IsInStillFrame())
3359  {
3360  int tries = 0;
3361  unpauseDecoder = true;
3362  while (decoderThread && !killdecoder && (tries++ < 100) &&
3364  {
3365  qApp->processEvents();
3366  LOG(VB_GENERAL, LOG_WARNING, LOC + "Waited 100ms for decoder to unpause");
3367  }
3368  unpauseDecoder = false;
3369  }
3370  decoderPauseLock.unlock();
3371 }
3372 
3373 void MythPlayer::DecoderStart(bool start_paused)
3374 {
3375  if (decoderThread)
3376  {
3377  if (decoderThread->isRunning())
3378  {
3379  LOG(VB_GENERAL, LOG_ERR, LOC + "Decoder thread already running");
3380  }
3381  delete decoderThread;
3382  }
3383 
3384  killdecoder = false;
3385  decoderPaused = start_paused;
3386  decoderThread = new DecoderThread(this, start_paused);
3387  if (decoderThread)
3388  decoderThread->start();
3389 }
3390 
3392 {
3393  PauseDecoder();
3394  SetPlaying(false);
3395  // Ensure any hardware frames are released (after pausing the decoder) to
3396  // allow the decoder to exit cleanly
3397  DiscardVideoFrames(true, true);
3398 
3399  killdecoder = true;
3400  int tries = 0;
3401  while (decoderThread && !decoderThread->wait(100) && (tries++ < 50))
3402  LOG(VB_PLAYBACK, LOG_INFO, LOC +
3403  "Waited 100ms for decoder loop to stop");
3404 
3406  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to stop decoder loop.");
3407  else
3408  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exited decoder loop.");
3409  SetDecoder(nullptr);
3410 }
3411 
3413 {
3415  {
3416  if (pauseDecoder)
3417  PauseDecoder();
3418  if (unpauseDecoder)
3419  UnpauseDecoder();
3420  }
3421 }
3422 
3425 {
3427  return decoder ? decoder->GetEof() : kEofStateImmediate;
3428 
3429  if (!decoder_change_lock.tryLock(50))
3430  return kEofStateNone;
3431 
3433  decoder_change_lock.unlock();
3434  return eof;
3435 }
3436 
3438 {
3440  {
3441  if (decoder)
3442  decoder->SetEofState(eof);
3443  return;
3444  }
3445 
3446  if (!decoder_change_lock.tryLock(50))
3447  return;
3448 
3449  if (decoder)
3450  decoder->SetEofState(eof);
3451  decoder_change_lock.unlock();
3452 }
3454 
3455 void MythPlayer::DecoderLoop(bool pause)
3456 {
3457  if (pause)
3458  PauseDecoder();
3459 
3460  while (!killdecoder && !IsErrored())
3461  {
3463 
3465  {
3466  usleep(1000);
3467  continue;
3468  }
3469 
3471  {
3472  if (!decoder_change_lock.tryLock(1))
3473  continue;
3474  if (decoder)
3475  {
3476  forcePositionMapSync = false;
3478  }
3479  decoder_change_lock.unlock();
3480  }
3481 
3482  if (decoderSeek >= 0)
3483  {
3484  if (!decoder_change_lock.tryLock(1))
3485  continue;
3486  if (decoder)
3487  {
3488  decoderSeekLock.lock();
3489  if (((uint64_t)decoderSeek < framesPlayed) && decoder)
3491  else if (decoder)
3493  decoderSeek = -1;
3494  decoderSeekLock.unlock();
3495  }
3496  decoder_change_lock.unlock();
3497  }
3498 
3499  bool obey_eof = (GetEof() != kEofStateNone) &&
3500  !(player_ctx->m_tvchain && !allpaused);
3501  if (isDummy || ((decoderPaused || ffrew_skip == 0 || obey_eof) &&
3502  !decodeOneFrame))
3503  {
3504  usleep(1000);
3505  continue;
3506  }
3507 
3510 
3511  DecoderGetFrame(dt);
3512  decodeOneFrame = false;
3513  }
3514 
3515  // Clear any wait conditions
3517  decoderSeek = -1;
3518 }
3519 
3521 {
3522  if (!decoder)
3523  return false;
3524 
3525  if (ffrew_skip > 0)
3526  {
3527  long long delta = decoder->GetFramesRead() - framesPlayed;
3528  long long real_skip = CalcMaxFFTime(ffrew_skip - ffrew_adjust + delta) - delta;
3529  long long target_frame = decoder->GetFramesRead() + real_skip;
3530  if (real_skip >= 0)
3531  {
3532  decoder->DoFastForward(target_frame, false);
3533  }
3534  long long seek_frame = decoder->GetFramesRead();
3535  ffrew_adjust = seek_frame - target_frame;
3536  }
3537  else if (CalcRWTime(-ffrew_skip) >= 0)
3538  {
3540  }
3542 }
3543 
3545 {
3546  long long cur_frame = decoder->GetFramesPlayed();
3547  bool toBegin = -cur_frame > ffrew_skip + ffrew_adjust;
3548  long long real_skip = (toBegin) ? -cur_frame : ffrew_skip + ffrew_adjust;
3549  long long target_frame = cur_frame + real_skip;
3550  bool ret = decoder->DoRewind(target_frame, false);
3551  long long seek_frame = decoder->GetFramesPlayed();
3552  ffrew_adjust = target_frame - seek_frame;
3553  return ret;
3554 }
3555 
3556 bool MythPlayer::DecoderGetFrame(DecodeType decodetype, bool unsafe)
3557 {
3558  bool ret = false;
3559  if (!videoOutput)
3560  return false;
3561 
3562  // Wait for frames to be available for decoding onto
3563  int tries = 0;
3564  while (!unsafe &&
3566  {
3567  if (killdecoder || pauseDecoder)
3568  return false;
3569 
3570  if (++tries > 10)
3571  {
3572  if (++videobuf_retries >= 2000)
3573  {
3574  LOG(VB_GENERAL, LOG_ERR, LOC +
3575  "Decoder timed out waiting for free video buffers.");
3576  // We've tried for 20 seconds now, give up so that we don't
3577  // get stuck permanently in this state
3578  SetErrored("Decoder timed out waiting for free video buffers.");
3579  }
3580  return false;
3581  }
3582 
3583  usleep(1000);
3584  }
3585  videobuf_retries = 0;
3586 
3587  if (!decoder_change_lock.tryLock(5))
3588  return false;
3589  if (killdecoder || !decoder || pauseDecoder || IsErrored())
3590  {
3591  decoder_change_lock.unlock();
3592  return false;
3593  }
3594 
3595  if (ffrew_skip == 1 || decodeOneFrame)
3596  ret = DoGetFrame(decodetype);
3597  else if (ffrew_skip != 0)
3598  ret = DecoderGetFrameFFREW();
3599  decoder_change_lock.unlock();
3600  return ret;
3601 }
3602 
3617 {
3618  bool ret = false;
3619  QElapsedTimer timeout;
3620  timeout.start();
3621  bool retry = true;
3622  // retry for a maximum of 5 seconds
3623  while (retry && !pauseDecoder && !killdecoder && !timeout.hasExpired(5000))
3624  {
3625  retry = false;
3626  ret = decoder->GetFrame(Type, retry);
3627  if (retry)
3628  {
3629  decoder_change_lock.unlock();
3630  QThread::usleep(10000);
3631  decoder_change_lock.lock();
3632  }
3633  }
3634 
3635  if (timeout.hasExpired(5000))
3636  return false;
3637  return ret;
3638 }
3639 
3641 {
3642  transcoding = value;
3643 
3644  if (decoder)
3645  decoder->SetTranscoding(value);
3646 }
3647 
3649 {
3651  {
3652  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot add PiP from another thread");
3653  return false;
3654  }
3655 
3656  if (pip_players.contains(pip))
3657  {
3658  LOG(VB_GENERAL, LOG_ERR, LOC + "PiPMap already contains PiP.");
3659  return false;
3660  }
3661 
3662  QList<PIPLocation> locs = pip_players.values();
3663  if (locs.contains(loc))
3664  {
3665  LOG(VB_GENERAL, LOG_ERR, LOC +"Already have a PiP at that location.");
3666  return false;
3667  }
3668 
3669  pip_players.insert(pip, loc);
3670  return true;
3671 }
3672 
3674 {
3676  return false;
3677 
3678  if (!pip_players.contains(pip))
3679  return false;
3680 
3681  pip_players.remove(pip);
3682  if (videoOutput)
3683  videoOutput->RemovePIP(pip);
3684  return true;
3685 }
3686 
3688 {
3690  return kPIP_END;
3691 
3692  if (pip_players.isEmpty())
3693  return pip_default_loc;
3694 
3695  // order of preference, could be stored in db if we want it configurable
3696  PIPLocation ols[] =
3698 
3699  for (size_t i = 0; i < sizeof(ols)/sizeof(PIPLocation); i++)
3700  {
3701  PIPMap::const_iterator it = pip_players.begin();
3702  for (; it != pip_players.end() && (*it != ols[i]); ++it);
3703 
3704  if (it == pip_players.end())
3705  return ols[i];
3706  }
3707 
3708  return kPIP_END;
3709 }
3710 
3711 int64_t MythPlayer::AdjustAudioTimecodeOffset(int64_t v, int newsync)
3712 {
3713  if ((newsync >= -1000) && (newsync <= 1000))
3714  tc_wrap[TC_AUDIO] = newsync;
3715  else
3716  tc_wrap[TC_AUDIO] += v;
3717  return tc_wrap[TC_AUDIO];
3718 }
3719 
3720 void MythPlayer::WrapTimecode(int64_t &timecode, TCTypes tc_type)
3721 {
3722  timecode += tc_wrap[tc_type];
3723 }
3724 
3725 bool MythPlayer::PrepareAudioSample(int64_t &timecode)
3726 {
3727  WrapTimecode(timecode, TC_AUDIO);
3728  return false;
3729 }
3730 
3745 void MythPlayer::SetWatched(bool forceWatched)
3746 {
3747  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3748  if (!player_ctx->m_playingInfo)
3749  {
3750  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3751  return;
3752  }
3753 
3754  uint64_t numFrames = GetCurrentFrameCount();
3755 
3756  // For recordings we want to ignore the post-roll and account for
3757  // in-progress recordings where totalFrames doesn't represent
3758  // the full length of the recording. For videos we can only rely on
3759  // totalFrames as duration metadata can be wrong
3763  {
3764 
3765  // If the recording is stopped early we need to use the recording end
3766  // time, not the programme end time
3767 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
3768  uint endtime;
3769  if (player_ctx->m_playingInfo->GetRecordingEndTime().toTime_t() <
3771  {
3772  endtime = player_ctx->m_playingInfo->GetRecordingEndTime().toTime_t();
3773  }
3774  else
3775  {
3776  endtime = player_ctx->m_playingInfo->GetScheduledEndTime().toTime_t();
3777  }
3778 
3779  numFrames = (long long)
3780  ((endtime -
3783 #else
3785  qint64 starttime = pi->GetRecordingStartTime().toSecsSinceEpoch();
3786  qint64 endactual = pi->GetRecordingEndTime().toSecsSinceEpoch();
3787  qint64 endsched = pi->GetScheduledEndTime().toSecsSinceEpoch();
3788  qint64 endtime = min(endactual, endsched);
3789  numFrames = (long long) ((endtime - starttime) * video_frame_rate);
3790 #endif
3791  }
3792 
3793  int offset = (int) round(0.14 * (numFrames / video_frame_rate));
3794 
3795  if (offset < 240)
3796  offset = 240; // 4 Minutes Min
3797  else if (offset > 720)
3798  offset = 720; // 12 Minutes Max
3799 
3800  if (forceWatched || framesPlayed > numFrames - (offset * video_frame_rate))
3801  {
3803  LOG(VB_GENERAL, LOG_INFO, LOC +
3804  QString("Marking recording as watched using offset %1 minutes")
3805  .arg(offset/60));
3806  }
3807 
3808  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3809 }
3810 
3812 {
3813  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3816  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3817 }
3818 
3820 {
3821  uint64_t bookmark = 0;
3822 
3825  bookmark = 0;
3826  else
3827  {
3828  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3829  if (const ProgramInfo *pi = player_ctx->m_playingInfo)
3830  {
3831  bookmark = pi->QueryBookmark();
3832  // Disable progstart if the program has a cutlist.
3833  if (bookmark == 0 && !pi->HasCutlist())
3834  bookmark = pi->QueryProgStart();
3835  if (bookmark == 0)
3836  bookmark = pi->QueryLastPlayPos();
3837  }
3838  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3839  }
3840 
3841  return bookmark;
3842 }
3843 
3845 {
3846  bool skip_changed;
3847 
3848  float temp_speed = (play_speed == 0.0F) ?
3850  if (play_speed >= 0.0F && play_speed <= 3.0F)
3851  {
3852  skip_changed = (ffrew_skip != 1);
3853  if (decoder)
3855  frame_interval = (int) (1000000.0 / video_frame_rate / static_cast<double>(temp_speed))
3856  / m_fpsMultiplier;
3857  ffrew_skip = (play_speed != 0.0F);
3858  }
3859  else
3860  {
3861  skip_changed = true;
3862  frame_interval = 200000;
3863  frame_interval = (fabs(play_speed) >= 3.0F) ? 133466 : frame_interval;
3864  frame_interval = (fabs(play_speed) >= 5.0F) ? 133466 : frame_interval;
3865  frame_interval = (fabs(play_speed) >= 8.0F) ? 250250 : frame_interval;
3866  frame_interval = (fabs(play_speed) >= 10.0F) ? 133466 : frame_interval;
3867  frame_interval = (fabs(play_speed) >= 16.0F) ? 187687 : frame_interval;
3868  frame_interval = (fabs(play_speed) >= 20.0F) ? 150150 : frame_interval;
3869  frame_interval = (fabs(play_speed) >= 30.0F) ? 133466 : frame_interval;
3870  frame_interval = (fabs(play_speed) >= 60.0F) ? 133466 : frame_interval;
3871  frame_interval = (fabs(play_speed) >= 120.0F) ? 133466 : frame_interval;
3872  frame_interval = (fabs(play_speed) >= 180.0F) ? 133466 : frame_interval;
3873  float ffw_fps = fabs(static_cast<double>(play_speed)) * video_frame_rate;
3874  float dis_fps = 1000000.0F / frame_interval;
3875  ffrew_skip = (int)ceil(ffw_fps / dis_fps);
3877  ffrew_adjust = 0;
3878  }
3879 
3880  return skip_changed;
3881 }
3882 
3884 {
3885  float last_speed = play_speed;
3888  rtcbase = 0;
3889 
3890  bool skip_changed = UpdateFFRewSkip();
3891 
3892  if (skip_changed && videoOutput)
3893  {
3895  if (play_speed != 0.0F && !(last_speed == 0.0F && ffrew_skip == 1))
3897  }
3898 
3899  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Play speed: " +
3900  QString("rate: %1 speed: %2 skip: %3 => new interval %4")
3901  .arg(video_frame_rate).arg(static_cast<double>(play_speed))
3902  .arg(ffrew_skip).arg(frame_interval));
3903 
3904  if (videoOutput && videosync)
3905  videoOutput->SetVideoFrameRate(static_cast<float>(video_frame_rate));
3906 
3907  // ensure we re-check double rate support following a speed change
3908  m_scan_initialized = false;
3909 
3910  if (normal_speed && audio.HasAudioOut())
3911  {
3914  }
3915 }
3916 
3917 bool MythPlayer::DoRewind(uint64_t frames, double inaccuracy)
3918 {
3920  return false;
3921 
3922  uint64_t number = frames + 1;
3923  uint64_t desiredFrame = (framesPlayed > number) ? framesPlayed - number : 0;
3924 
3925  limitKeyRepeat = false;
3926  if (desiredFrame < video_frame_rate)
3927  limitKeyRepeat = true;
3928 
3929  uint64_t seeksnap_wanted = UINT64_MAX;
3930  if (inaccuracy != kInaccuracyFull)
3931  seeksnap_wanted = frames * inaccuracy;
3933  WaitForSeek(desiredFrame, seeksnap_wanted);
3934  rewindtime = 0;
3935  ClearAfterSeek();
3936  return true;
3937 }
3938 
3939 bool MythPlayer::DoRewindSecs(float secs, double inaccuracy, bool use_cutlist)
3940 {
3941  float current = ComputeSecs(framesPlayed, use_cutlist);
3942  float target = current - secs;
3943  if (target < 0)
3944  target = 0;
3945  uint64_t targetFrame = FindFrame(target, use_cutlist);
3946  return DoRewind(framesPlayed - targetFrame, inaccuracy);
3947 }
3948 
3954 long long MythPlayer::CalcRWTime(long long rw) const
3955 {
3956  bool hasliveprev = (livetv && player_ctx->m_tvchain &&
3958 
3959  if (!hasliveprev || ((int64_t)framesPlayed >= rw))
3960  {
3961  return rw;
3962  }
3963 
3964  player_ctx->m_tvchain->JumpToNext(false, ((int64_t)framesPlayed - rw) / video_frame_rate);
3965 
3966  return -1;
3967 }
3968 
3973 long long MythPlayer::CalcMaxFFTime(long long ffframes, bool setjump) const
3974 {
3975  float maxtime = 1.0;
3976  bool islivetvcur = (livetv && player_ctx->m_tvchain &&
3978 
3979  if (livetv || IsWatchingInprogress())
3980  maxtime = 3.0;
3981 
3982  long long ret = ffframes;
3983  float ff = ComputeSecs(ffframes, true);
3984  float secsPlayed = ComputeSecs(framesPlayed, true);
3985  float secsWritten = ComputeSecs(totalFrames, true);
3986 
3987  limitKeyRepeat = false;
3988 
3989  if (livetv && !islivetvcur && player_ctx->m_tvchain)
3990  {
3991  // recording has completed, totalFrames will always be up to date
3992  if ((ffframes + framesPlayed > totalFrames) && setjump)
3993  {
3994  ret = -1;
3995  // Number of frames to be skipped is from the end of the current segment
3996  player_ctx->m_tvchain->JumpToNext(true, ((int64_t)totalFrames - (int64_t)framesPlayed - ffframes) / video_frame_rate);
3997  }
3998  }
3999  else if (islivetvcur || IsWatchingInprogress())
4000  {
4001  if ((ff + secsPlayed) > secsWritten)
4002  {
4003  // If we attempt to seek past the last known duration,
4004  // check for up to date data
4005  long long framesWritten = player_ctx->m_recorder->GetFramesWritten();
4006 
4007  secsWritten = ComputeSecs(framesWritten, true);
4008  }
4009 
4010  float behind = secsWritten - secsPlayed;
4011 
4012  if (behind < maxtime) // if we're close, do nothing
4013  ret = 0;
4014  else if (behind - ff <= maxtime)
4015  ret = TranslatePositionMsToFrame(1000 * (secsWritten - maxtime),
4016  true) - framesPlayed;
4017 
4018  if (behind < maxtime * 3)
4019  limitKeyRepeat = true;
4020  }
4021  else if (IsPaused())
4022  {
4023  uint64_t lastFrame = deleteMap.IsEmpty() ? totalFrames
4024  : deleteMap.GetLastFrame();
4025  if (framesPlayed + ffframes >= lastFrame)
4026  ret = lastFrame - 1 - framesPlayed;
4027  }
4028  else
4029  {
4030  float secsMax = secsWritten - 2.F * maxtime;
4031  if (secsMax <= 0.F)
4032  ret = 0;
4033  else if (secsMax < secsPlayed + ff)
4034  ret = TranslatePositionMsToFrame(1000 * secsMax, true)
4035  - framesPlayed;
4036  }
4037 
4038  return ret;
4039 }
4040 
4048 {
4049  if (!videoOutput || !decoder)
4050  return false;
4051 
4052  return player_ctx->m_buffer->IsNearEnd(
4054 }
4055 
4059 {
4060  uint64_t framesRead, framesLeft = 0;
4061 
4062  if (!player_ctx)
4063  return false;
4064 
4065  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4067  !decoder)
4068  {
4069  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4070  return false;
4071  }
4072  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4073 
4074  auto margin = (long long)(video_frame_rate * 2);
4075  margin = (long long) (margin * audio.GetStretchFactor());
4076  bool watchingTV = IsWatchingInprogress();
4077 
4078  framesRead = framesPlayed;
4079 
4080  if (!player_ctx->IsPIP() &&
4082  {
4083  if (framesRead >= deleteMap.GetLastFrame())
4084  return true;
4085  uint64_t frameCount = GetCurrentFrameCount();
4086  framesLeft = (frameCount > framesRead) ? frameCount - framesRead : 0;
4087  return (framesLeft < (uint64_t)margin);
4088  }
4089 
4090  if (!livetv && !watchingTV)
4091  return false;
4092 
4094  return false;
4095 
4096  if (player_ctx->m_recorder)
4097  {
4098  framesLeft =
4099  player_ctx->m_recorder->GetCachedFramesWritten() - framesRead;
4100 
4101  // if it looks like we are near end, get an updated GetFramesWritten()
4102  if (framesLeft < (uint64_t)margin)
4103  framesLeft = player_ctx->m_recorder->GetFramesWritten() - framesRead;
4104  }
4105 
4106  return (framesLeft < (uint64_t)margin);
4107 }
4108 
4109 bool MythPlayer::DoFastForward(uint64_t frames, double inaccuracy)
4110 {
4112  return false;
4113 
4114  uint64_t number = (frames ? frames - 1 : 0);
4115  uint64_t desiredFrame = framesPlayed + number;
4116 
4117  if (!deleteMap.IsEditing() && IsInDelete(desiredFrame))
4118  {
4119  uint64_t endcheck = deleteMap.GetLastFrame();
4120  if (desiredFrame > endcheck)
4121  desiredFrame = endcheck;
4122  }
4123 
4124  uint64_t seeksnap_wanted = UINT64_MAX;
4125  if (inaccuracy != kInaccuracyFull)
4126  seeksnap_wanted = frames * inaccuracy;
4128  WaitForSeek(desiredFrame, seeksnap_wanted);
4129  fftime = 0;
4130  ClearAfterSeek(false);
4131  return true;
4132 }
4133 
4134 bool MythPlayer::DoFastForwardSecs(float secs, double inaccuracy,
4135  bool use_cutlist)
4136 {
4137  float current = ComputeSecs(framesPlayed, use_cutlist);
4138  float target = current + secs;
4139  uint64_t targetFrame = FindFrame(target, use_cutlist);
4140  return DoFastForward(targetFrame - framesPlayed, inaccuracy);
4141 }
4142 
4143 void MythPlayer::DoJumpToFrame(uint64_t frame, double inaccuracy)
4144 {
4145  if (frame > framesPlayed)
4146  DoFastForward(frame - framesPlayed, inaccuracy);
4147  else if (frame <= framesPlayed)
4148  DoRewind(framesPlayed - frame, inaccuracy);
4149 }
4150 
4151 void MythPlayer::WaitForSeek(uint64_t frame, uint64_t seeksnap_wanted)
4152 {
4153  if (!decoder)
4154  return;
4155 
4157  decoder->SetSeekSnap(seeksnap_wanted);
4158 
4159  bool islivetvcur = (livetv && player_ctx->m_tvchain &&
4161 
4162  uint64_t max = GetCurrentFrameCount();
4163  if (islivetvcur || IsWatchingInprogress())
4164  {
4165  max = (uint64_t)player_ctx->m_recorder->GetFramesWritten();
4166  }
4167  if (frame >= max)
4168  frame = max - 1;
4169 
4170  decoderSeekLock.lock();
4171  decoderSeek = frame;
4172  decoderSeekLock.unlock();
4173 
4174  int count = 0;
4175  bool need_clear = false;
4176  while (decoderSeek >= 0)
4177  {
4178  // Waiting blocks the main UI thread but the decoder may
4179  // have initiated a callback into the UI thread to create
4180  // certain resources. Ensure the callback is processed.
4181  // Ideally MythPlayer should be fully event driven and these
4182  // calls wouldn't be necessary.
4183  qApp->processEvents();
4184 
4185  usleep(50 * 1000);
4186 
4187  // provide some on screen feedback if seeking is slow
4188  count++;
4189  if (!(count % 3) && !hasFullPositionMap)
4190  {
4191  int num = count % 3;
4192  SetOSDMessage(tr("Searching") + QString().fill('.', num),
4195  need_clear = true;
4196  }
4197  }
4198  if (need_clear)
4199  {
4200  osdLock.lock();
4201  if (osd)
4202  osd->HideWindow("osd_message");
4203  osdLock.unlock();
4204  }
4205 }
4206 
4219 void MythPlayer::ClearAfterSeek(bool clearvideobuffers)
4220 {
4221  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("ClearAfterSeek(%1)")
4222  .arg(clearvideobuffers));
4223 
4224  if (clearvideobuffers && videoOutput)
4226 
4227  int64_t savedTC = tc_wrap[TC_AUDIO];
4228 
4229  for (int j = 0; j < TCTYPESMAX; j++)
4230  tc_wrap[j] = tc_lastval[j] = 0;
4231 
4232  tc_wrap[TC_AUDIO] = savedTC;
4233 
4234  audio.Reset();
4235  // Reenable (or re-disable) subtitles, which ultimately does
4236  // nothing except to call ResetCaptions() to erase any captions
4237  // currently on-screen. The key is that the erasing is done in
4238  // the UI thread, not the decoder thread.
4243  needNewPauseFrame = true;
4244  ResetAVSync();
4245 }
4246 
4254 void MythPlayer::ClearBeforeSeek(uint64_t Frames)
4255 {
4256 #ifdef USING_MEDIACODEC
4257  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("ClearBeforeSeek: decoder %1 frames %2 recording %3 livetv %4")
4258  .arg(m_codecName).arg(Frames).arg(watchingrecording).arg(livetv));
4259 
4260  if ((Frames < 2) || !videoOutput /*|| !(livetv || watchingrecording)*/)
4261  return;
4262 
4263  decoder_change_lock.lock();
4265  decoder_change_lock.unlock();
4266  if (codec_is_mediacodec(codec))
4267  videoOutput->DiscardFrames(true, true);
4268 #else
4269  Q_UNUSED(Frames);
4270 #endif
4271 }
4272 
4273 void MythPlayer::SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
4274 {
4276  m_tv = tv;
4277  parentWidget = widget;
4278  player_ctx = ctx;
4279  livetv = ctx->m_tvchain;
4280 }
4281 
4283 {
4284  deleteMap.SetEditing(false);
4285 
4286  if (!hasFullPositionMap)
4287  {
4288  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot edit - no full position map");
4289  SetOSDStatus(tr("No Seektable"), kOSDTimeout_Med);
4290  return false;
4291  }
4292 
4293  if (deleteMap.IsFileEditing())
4294  return false;
4295 
4296  QMutexLocker locker(&osdLock);
4297  if (!osd)
4298  return false;
4299 
4301  int sample_rate = GetAudio()->GetSampleRate();
4302  m_audiograph.SetSampleRate(sample_rate);
4303  m_audiograph.SetSampleCount((unsigned)(sample_rate / video_frame_rate));
4305 
4307  tc_wrap[TC_AUDIO] = 0;
4308 
4310  pausedBeforeEdit = Pause();
4311  deleteMap.SetEditing(true);
4312  osd->DialogQuit();
4313  ResetCaptions();
4314  osd->HideAll();
4315 
4316  bool loadedAutoSave = deleteMap.LoadAutoSaveMap();
4317  if (loadedAutoSave)
4318  {
4319  SetOSDMessage(tr("Using previously auto-saved cuts"),
4321  }
4322 
4325  deleteMap.SetFileEditing(true);
4326  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4329  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4330  editUpdateTimer.start();
4331 
4332  return deleteMap.IsEditing();
4333 }
4334 
4342 void MythPlayer::DisableEdit(int howToSave)
4343 {
4344  QMutexLocker locker(&osdLock);
4345  if (!osd)
4346  return;
4347 
4348  deleteMap.SetEditing(false, osd);
4349  if (howToSave == 0)
4350  deleteMap.LoadMap();
4351  // Unconditionally save to remove temporary marks from the DB.
4352  if (howToSave >= 0)
4353  deleteMap.SaveMap();
4355  deleteMap.SetFileEditing(false);
4356  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4359  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4361  m_audiograph.Reset();
4364 
4365  if (!pausedBeforeEdit)
4367  else
4368  SetOSDStatus(tr("Paused"), kOSDTimeout_None);
4369 }
4370 
4371 bool MythPlayer::HandleProgramEditorActions(QStringList &actions)
4372 {
4373  bool handled = false;
4374  bool refresh = true;
4375  long long frame = GetFramesPlayed();
4376 
4377  for (int i = 0; i < actions.size() && !handled; i++)
4378  {
4379  QString action = actions[i];
4380  handled = true;
4381  float seekamount = deleteMap.GetSeekAmount();
4382  if (action == ACTION_LEFT)
4383  {
4384  if (seekamount == 0) // 1 frame
4386  else if (seekamount > 0)
4387  // Use fully-accurate seeks for less than 1 second.
4388  DoRewindSecs(seekamount, seekamount < 1.0F ? kInaccuracyNone :
4389  kInaccuracyEditor, false);
4390  else
4391  HandleArbSeek(false);
4392  }
4393  else if (action == ACTION_RIGHT)
4394  {
4395  if (seekamount == 0) // 1 frame
4397  else if (seekamount > 0)
4398  // Use fully-accurate seeks for less than 1 second.
4399  DoFastForwardSecs(seekamount, seekamount < 1.0F ? kInaccuracyNone :
4400  kInaccuracyEditor, false);
4401  else
4402  HandleArbSeek(true);
4403  }
4404  else if (action == ACTION_LOADCOMMSKIP)
4405  {
4406  if (commBreakMap.HasMap())
4407  {
4408  frm_dir_map_t map;
4409  commBreakMap.GetMap(map);
4411  }
4412  }
4413  else if (action == ACTION_PREVCUT)
4414  {
4415  float old_seekamount = deleteMap.GetSeekAmount();
4417  HandleArbSeek(false);
4418  deleteMap.SetSeekAmount(old_seekamount);
4419  }
4420  else if (action == ACTION_NEXTCUT)
4421  {
4422  float old_seekamount = deleteMap.GetSeekAmount();
4424  HandleArbSeek(true);
4425  deleteMap.SetSeekAmount(old_seekamount);
4426  }
4427 #define FFREW_MULTICOUNT 10.0F
4428  else if (action == ACTION_BIGJUMPREW)
4429  {
4430  if (seekamount == 0)
4432  else if (seekamount > 0)
4433  DoRewindSecs(seekamount * FFREW_MULTICOUNT,
4434  kInaccuracyEditor, false);
4435  else
4437  kInaccuracyNone, false);
4438  }
4439  else if (action == ACTION_BIGJUMPFWD)
4440  {
4441  if (seekamount == 0)
4443  else if (seekamount > 0)
4444  DoFastForwardSecs(seekamount * FFREW_MULTICOUNT,
4445  kInaccuracyEditor, false);
4446  else
4448  kInaccuracyNone, false);
4449  }
4450  else if (action == ACTION_SELECT)
4451  {
4452  deleteMap.NewCut(frame);
4453  SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
4454  refresh = true;
4455  }
4456  else if (action == "DELETE")
4457  {
4458  deleteMap.Delete(frame, tr("Delete"));
4459  refresh = true;
4460  }
4461  else if (action == "REVERT")
4462  {
4463  deleteMap.LoadMap(tr("Undo Changes"));
4464  refresh = true;
4465  }
4466  else if (action == "REVERTEXIT")
4467  {
4468  DisableEdit(0);
4469  refresh = false;
4470  }
4471  else if (action == ACTION_SAVEMAP)
4472  {
4473  deleteMap.SaveMap();
4474  refresh = true;
4475  }
4476  else if (action == "EDIT" || action == "SAVEEXIT")
4477  {
4478  DisableEdit(1);
4479  refresh = false;
4480  }
4481  else
4482  {
4483  QString undoMessage = deleteMap.GetUndoMessage();
4484  QString redoMessage = deleteMap.GetRedoMessage();
4485  handled = deleteMap.HandleAction(action, frame);
4486  if (handled && (action == "CUTTOBEGINNING" ||
4487  action == "CUTTOEND" || action == "NEWCUT"))
4488  {
4489  SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
4490  }
4491  else if (handled && action == "UNDO")
4492  {
4493  //: %1 is the undo message
4494  SetOSDMessage(tr("Undo - %1").arg(undoMessage),
4496  }
4497  else if (handled && action == "REDO")
4498  {
4499  //: %1 is the redo message
4500  SetOSDMessage(tr("Redo - %1").arg(redoMessage),
4502  }
4503  }
4504  }
4505 
4506  if (handled && refresh)
4507  {
4508  osdLock.lock();
4509  if (osd)
4510  {
4512  }
4513  osdLock.unlock();
4514  }
4515 
4516  return handled;
4517 }
4518 
4519 bool MythPlayer::IsInDelete(uint64_t frame)
4520 {
4521  return deleteMap.IsInDelete(frame);
4522 }
4523 
4524 uint64_t MythPlayer::GetNearestMark(uint64_t frame, bool right)
4525 {
4526  return deleteMap.GetNearestMark(frame, right);
4527 }
4528 
4529 bool MythPlayer::IsTemporaryMark(uint64_t frame)
4530 {
4531  return deleteMap.IsTemporaryMark(frame);
4532 }
4533 
4535 {
4536  return deleteMap.HasTemporaryMark();
4537 }
4538 
4540 {
4541  if (deleteMap.GetSeekAmount() == -2)
4542  {
4543  uint64_t framenum = deleteMap.GetNearestMark(framesPlayed, right);
4544  if (right && (framenum > framesPlayed))
4546  else if (!right && (framesPlayed > framenum))
4547  DoRewind(framesPlayed - framenum, kInaccuracyNone);
4548  }
4549  else
4550  {
4551  if (right)
4553  else
4555  }
4556 }
4557 
4559 {
4560  if (videoOutput)
4561  return videoOutput->GetAspectOverride();
4562  return kAspect_Off;
4563 }
4564 
4566 {
4567  if (videoOutput)
4568  return videoOutput->GetAdjustFill();
4569  return kAdjustFill_Off;
4570 }
4571 
4573 {
4574  if (videoOutput)
4575  {
4576  videoOutput->ToggleAspectOverride(aspectMode);
4577  ReinitOSD();
4578  }
4579 }
4580 
4582 {
4583  if (videoOutput)
4584  {
4586  videoOutput->ToggleAdjustFill(adjustfillMode);
4587  ReinitOSD();
4588  }
4589 }
4590 
4592 {
4593  if (videoOutput)
4594  {
4595  videoOutput->Zoom(direction);
4596  ReinitOSD();
4597  }
4598 }
4599 
4601 {
4602  if (videoOutput)
4603  {
4605  ReinitOSD();
4606  }
4607 }
4608 
4610 {
4611  if (videoOutput)
4613 }
4614 
4616 {
4617  if (videoOutput)
4618  return videoOutput->IsEmbedding();
4619  return false;
4620 }
4621 
4623 {
4625 }
4626 
4642 char *MythPlayer::GetScreenGrab(int secondsin, int &bufflen,
4643  int &vw, int &vh, float &ar)
4644 {
4645  auto frameNum = (long long)(secondsin * video_frame_rate);
4646 
4647  return GetScreenGrabAtFrame(frameNum, false, bufflen, vw, vh, ar);
4648 }
4649 
4666 char *MythPlayer::GetScreenGrabAtFrame(uint64_t frameNum, bool absolute,
4667  int &bufflen, int &vw, int &vh,
4668  float &ar)
4669 {
4670  uint64_t number = 0;
4671  unsigned char *data = nullptr;
4672  unsigned char *outputbuf = nullptr;
4673  VideoFrame *frame = nullptr;
4674  AVFrame orig;
4675  AVFrame retbuf;
4676  MythAVCopy copyCtx;
4677  memset(&orig, 0, sizeof(AVFrame));
4678  memset(&retbuf, 0, sizeof(AVFrame));
4679 
4680  bufflen = 0;
4681  vw = vh = 0;
4682  ar = 0;
4683 
4684  if (OpenFile(0) < 0)
4685  {
4686  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not open file for preview.");
4687  return nullptr;
4688  }
4689 
4690  if ((video_dim.width() <= 0) || (video_dim.height() <= 0))
4691  {
4692  LOG(VB_PLAYBACK, LOG_ERR, LOC +
4693  QString("Video Resolution invalid %1x%2")
4694  .arg(video_dim.width()).arg(video_dim.height()));
4695 
4696  // This is probably an audio file, just return a grey frame.
4697  vw = 640;
4698  vh = 480;
4699  ar = 4.0F / 3.0F;
4700 
4701  bufflen = vw * vh * 4;
4702  outputbuf = new unsigned char[bufflen];
4703  memset(outputbuf, 0x3f, bufflen * sizeof(unsigned char));
4704  return (char*) outputbuf;
4705  }
4706 
4707  if (!InitVideo())
4708  {
4709  LOG(VB_GENERAL, LOG_ERR, LOC +
4710  "Unable to initialize video for screen grab.");
4711  return nullptr;
4712  }
4713 
4714  ClearAfterSeek();
4715  if (!decoderThread)
4716  DecoderStart(true /*start paused*/);
4717  SeekForScreenGrab(number, frameNum, absolute);
4718  int tries = 0;
4719  while (!videoOutput->ValidVideoFrames() && ((tries++) < 500))
4720  {
4721  decodeOneFrame = true;
4722  usleep(10000);
4723  if ((tries & 10) == 10)
4724  LOG(VB_PLAYBACK, LOG_INFO, LOC +
4725  "ScreenGrab: Waited 100ms for video frame");
4726  }
4727 
4728  if (!(frame = videoOutput->GetLastDecodedFrame()))
4729  {
4730  return nullptr;
4731  }
4732 
4733  while (true)
4734  {
4735  if (!(data = frame->buf))
4736  {
4737  break;
4738  }
4739 
4740  AVPictureFill(&orig, frame);
4741  float par = frame->aspect * video_dim.height() / video_dim.width();
4742  MythPictureDeinterlacer deinterlacer(AV_PIX_FMT_YUV420P,
4743  video_dim.width(), video_dim.height(),
4744  par);
4745  if (deinterlacer.DeinterlaceSingle(&orig, &orig) < 0)
4746  {
4747  break;
4748  }
4749 
4750  outputbuf = CreateBuffer(FMT_RGB32, video_dim.width(), video_dim.height());
4751  copyCtx.Copy(&retbuf, frame, outputbuf, AV_PIX_FMT_RGB32);
4752  vw = video_disp_dim.width();
4753  vh = video_disp_dim.height();
4754  ar = frame->aspect;
4755  break;
4756  }
4757  if (frame)
4758  {
4759  DiscardVideoFrame(frame);
4760  }
4761  return reinterpret_cast<char*>(outputbuf);
4762 }
4763 
4764 void MythPlayer::SeekForScreenGrab(uint64_t &number, uint64_t frameNum,
4765  bool absolute)
4766 {
4767  number = frameNum;
4768  if (number >= totalFrames)
4769  {
4770  LOG(VB_PLAYBACK, LOG_ERR, LOC +
4771  "Screen grab requested for frame number beyond end of file.");
4772  number = totalFrames / 2;
4773  }
4774 
4775  if (!absolute && hasFullPositionMap)
4776  {
4778  // Use the bookmark if we should, otherwise make sure we aren't
4779  // in the cutlist or a commercial break
4780  if (bookmarkseek > 30)
4781  {
4782  number = bookmarkseek;
4783  }
4784  else
4785  {
4786  uint64_t oldnumber = number;
4787  deleteMap.LoadMap();
4789 
4790  bool started_in_break_map = false;
4791  while (commBreakMap.IsInCommBreak(number) ||
4792  IsInDelete(number))
4793  {
4794  started_in_break_map = true;
4795  number += (uint64_t) (30 * video_frame_rate);
4796  if (number >= totalFrames)
4797  {
4798  number = oldnumber;
4799  break;
4800  }
4801  }
4802 
4803  // Advance a few seconds from the end of the break
4804  if (started_in_break_map)
4805  {
4806  oldnumber = number;
4807  number += (long long) (10 * video_frame_rate);
4808  if (number >= totalFrames)
4809  number = oldnumber;
4810  }
4811  }
4812  }
4813 
4815  DoJumpToFrame(number, kInaccuracyNone);
4816 }
4817 
4826 {
4827  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4830  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4831 
4832  if (!decoderThread)
4833  DecoderStart(false);
4834 
4835  if (frameNumber >= 0)
4836  {
4837  DoJumpToFrame(frameNumber, kInaccuracyNone);
4838  ClearAfterSeek();
4839  }
4840 
4841  int tries = 0;
4842  while (!videoOutput->ValidVideoFrames() && ((tries++) < 100))
4843  {
4844  decodeOneFrame = true;
4845  usleep(10000);
4846  if ((tries & 10) == 10)
4847  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waited 100ms for video frame");
4848  }
4849 
4851  return videoOutput->GetLastShownFrame();
4852 }
4853 
4854 QString MythPlayer::GetEncodingType(void) const
4855 {
4856  if (decoder)
4858  return QString();
4859 }
4860 
4862 {
4863  infoMap["audiocodec"] = ff_codec_id_string(audio.GetCodec());
4864  infoMap["audiochannels"] = QString::number(audio.GetOrigChannels());
4865 
4866  int width = video_disp_dim.width();
4867  int height = video_disp_dim.height();
4868  infoMap["videocodec"] = GetEncodingType();
4869  if (decoder)
4870  infoMap["videocodecdesc"] = decoder->GetRawEncodingType();
4871  infoMap["videowidth"] = QString::number(width);
4872  infoMap["videoheight"] = QString::number(height);
4873  infoMap["videoframerate"] = QString::number(video_frame_rate, 'f', 2);
4874  infoMap["deinterlacer"] = DeinterlacerName(m_lastDeinterlacer,
4876 
4877  if (width < 640)
4878  return;
4879 
4880  bool interlaced = is_interlaced(m_scan);
4881  if (width == 1920 || height == 1080 || height == 1088)
4882  infoMap["videodescrip"] = interlaced ? "HD_1080_I" : "HD_1080_P";
4883  else if ((width == 1280 || height == 720) && !interlaced)
4884  infoMap["videodescrip"] = "HD_720_P";
4885  else if (height >= 720)
4886  infoMap["videodescrip"] = "HD";
4887  else infoMap["videodescrip"] = "SD";
4888 }
4889 
4891 {
4892  if (decoder)
4893  return decoder->GetRawAudioState();
4894  return false;
4895 }
4896 
4897 QString MythPlayer::GetXDS(const QString &key) const
4898 {
4899  if (!decoder)
4900  return QString();
4901  return decoder->GetXDS(key);
4902 }
4903 
4904 void MythPlayer::InitForTranscode(bool copyaudio, bool copyvideo)
4905 {
4906  // Are these really needed?
4907  SetPlaying(true);
4908  keyframedist = 30;
4909 
4910  if (!InitVideo())
4911  {
4912  LOG(VB_GENERAL, LOG_ERR, LOC +
4913  "Unable to initialize video for transcode.");
4914  SetPlaying(false);
4915  return;
4916  }
4917 
4918  framesPlayed = 0;
4919  framesPlayedExtra = 0;
4920  ClearAfterSeek();
4921 
4922  if (copyvideo && decoder)
4923  decoder->SetRawVideoState(true);
4924  if (copyaudio && decoder)
4925  decoder->SetRawAudioState(true);
4926 
4927  if (decoder)
4928  {
4929  decoder->SetSeekSnap(0);
4930  }
4931 }
4932 
4934  int &did_ff, bool &is_key, bool honorCutList)
4935 {
4936  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4939  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4940 
4941  int64_t lastDecodedFrameNumber =
4943 
4944  if ((lastDecodedFrameNumber == 0) && honorCutList)
4946 
4947  if (!decoderThread)
4948  DecoderStart(true/*start paused*/);
4949 
4950  if (!decoder)
4951  return false;
4952 
4953  {
4954  QMutexLocker decoderlocker(&decoder_change_lock);
4955  if (!DoGetFrame(kDecodeAV))
4956  return false;
4957  }
4958 
4959  if (GetEof() != kEofStateNone)
4960  return false;
4961 
4962  if (honorCutList && !deleteMap.IsEmpty())
4963  {
4964  if (totalFrames && lastDecodedFrameNumber >= (int64_t)totalFrames)
4965  return false;
4966 
4967  uint64_t jumpto = 0;
4968  if (deleteMap.TrackerWantsToJump(lastDecodedFrameNumber, jumpto))
4969  {
4970  LOG(VB_GENERAL, LOG_INFO, LOC +
4971  QString("Fast-Forwarding from %1 to %2")
4972  .arg(lastDecodedFrameNumber).arg(jumpto));
4973  if (jumpto >= totalFrames)
4974  {
4976  return false;
4977  }
4978 
4979  // For 0.25, move this to DoJumpToFrame(jumpto)
4980  WaitForSeek(jumpto, 0);
4982  ClearAfterSeek();
4983  decoder_change_lock.lock();
4985  decoder_change_lock.unlock();
4986  did_ff = 1;
4987  }
4988  }
4989  if (GetEof() != kEofStateNone)
4990  return false;
4991  is_key = decoder->IsLastFrameKey();
4992  return true;
4993 }
4994 
4995 long MythPlayer::UpdateStoredFrameNum(long curFrameNum)
4996 {
4997  if (decoder)
4998  return decoder->UpdateStoredFrameNum(curFrameNum);
4999  return 0;
5000 }
5001 
5002 void MythPlayer::SetCutList(const frm_dir_map_t &newCutList)
5003 {
5004  deleteMap.SetMap(newCutList);
5005 }
5006 
5008  bool writevideo, long timecodeOffset)
5009 {
5010  if (!decoder)
5011  return false;
5012  if (writevideo && !decoder->GetRawVideoState())
5013  writevideo = false;
5014  decoder->WriteStoredData(outRingBuffer, writevideo, timecodeOffset);
5015  return writevideo;
5016 }
5017 
5019 {
5020  commBreakMap.SetMap(newMap, framesPlayed);
5021  forcePositionMapSync = true;
5022 }
5023 
5025 {
5026  double spos = 0.0;
5027 
5028  if (livetv || IsWatchingInprogress())
5029  {
5030  spos = 1000.0 * framesPlayed / player_ctx->m_recorder->GetFramesWritten();
5031  }
5032  else if (totalFrames)
5033  {
5034  spos = 1000.0 * framesPlayed / totalFrames;
5035  }
5036 
5037  return((int)spos);
5038 }
5039 
5041 {
5042  QString samplerate = RingBuffer::BitrateToString(audio.GetSampleRate(), true);
5043  infoMap.insert("samplerate", samplerate);
5044  infoMap.insert("filename", player_ctx->m_buffer->GetSafeFilename());
5045  infoMap.insert("decoderrate", player_ctx->m_buffer->GetDecoderRate());
5046  infoMap.insert("storagerate", player_ctx->m_buffer->GetStorageRate());
5047  infoMap.insert("bufferavail", player_ctx->m_buffer->GetAvailableBuffer());
5048  infoMap.insert("buffersize", QString::number(player_ctx->m_buffer->GetBufferSize() >> 20));
5049  if (gCoreContext->GetBoolSetting("PlaybackAVSync2", false))
5050  {
5051  int avsync = avsync_avg / 1000;
5052  infoMap.insert("avsync", tr("%1 ms").arg(avsync));
5053  }
5054  else
5055  infoMap.insert("avsync", QString::number((float)avsync_avg / (float)frame_interval, 'f', 2));
5056  if (videoOutput)
5057  {
5058  QString frames = QString("%1/%2").arg(videoOutput->ValidVideoFrames())
5059  .arg(videoOutput->FreeVideoFrames());
5060  infoMap.insert("videoframes", frames);
5061  }
5062  if (decoder)
5063  infoMap["videodecoder"] = decoder->GetCodecDecoderName();
5064  if (output_jmeter)
5065  {
5066  infoMap["framerate"] = QString("%1%2%3")
5067  .arg(output_jmeter->GetLastFPS(), 0, 'f', 2)
5068  .arg(QChar(0xB1, 0))
5069  .arg(output_jmeter->GetLastSD(), 0, 'f', 2);
5070  infoMap["load"] = output_jmeter->GetLastCPUStats();
5071  }
5072  GetCodecDescription(infoMap);
5073 }
5074 
5075 int64_t MythPlayer::GetSecondsPlayed(bool honorCutList, int divisor)
5076 {
5077  int64_t pos = TranslatePositionFrameToMs(framesPlayed, honorCutList);
5078  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
5079  QString("GetSecondsPlayed: framesPlayed %1, honorCutList %2, divisor %3, pos %4")
5080  .arg(framesPlayed).arg(honorCutList).arg(divisor).arg(pos));
5081  return TranslatePositionFrameToMs(framesPlayed, honorCutList) / divisor;
5082 }
5083 
5084 int64_t MythPlayer::GetTotalSeconds(bool honorCutList, int divisor) const
5085 {
5086  uint64_t pos = totalFrames;
5087 
5088  if (IsWatchingInprogress())
5089  pos = (uint64_t)-1;
5090 
5091  return TranslatePositionFrameToMs(pos, honorCutList) / divisor;
5092 }
5093 
5094 // Returns the total frame count, as totalFrames for a completed
5095 // recording, or the most recent frame count from the recorder for
5096 // live TV or an in-progress recording.
5098 {
5099  uint64_t result = totalFrames;
5100  if (IsWatchingInprogress())
5101  result = player_ctx->m_recorder->GetFramesWritten();
5102  return result;
5103 }
5104 
5105 // Finds the frame number associated with the given time offset. A
5106 // positive offset or +0.0F indicate offset from the beginning. A
5107 // negative offset or -0.0F indicate offset from the end. Limit the
5108 // result to within bounds of the video.
5109 uint64_t MythPlayer::FindFrame(float offset, bool use_cutlist) const
5110 {
5111  bool islivetvcur = (livetv && player_ctx->m_tvchain &&
5113  uint64_t length_ms = TranslatePositionFrameToMs(totalFrames, use_cutlist);
5114  uint64_t position_ms;
5115 
5116  if (signbit(offset))
5117  {
5118  // Always get an updated totalFrame value for in progress recordings
5119  if (islivetvcur || IsWatchingInprogress())
5120  {
5121  uint64_t framesWritten = player_ctx->m_recorder->GetFramesWritten();
5122 
5123  if (totalFrames < framesWritten)
5124  {
5125  // Known duration is less than what the backend reported, use new value
5126  length_ms =
5127  TranslatePositionFrameToMs(framesWritten, use_cutlist);
5128  }
5129  }
5130  uint64_t offset_ms = llroundf(-offset * 1000);
5131  position_ms = (offset_ms > length_ms) ? 0 : length_ms - offset_ms;
5132  }
5133  else
5134  {
5135  position_ms = llroundf(offset * 1000);
5136 
5137  if (offset > length_ms)
5138  {
5139  // Make sure we have an updated totalFrames
5140  if ((islivetvcur || IsWatchingInprogress()) &&
5141  (length_ms < offset))
5142  {
5143  long long framesWritten =
5145 
5146  length_ms =
5147  TranslatePositionFrameToMs(framesWritten, use_cutlist);
5148  }
5149  position_ms = min(position_ms, length_ms);
5150  }
5151  }
5152  return TranslatePositionMsToFrame(position_ms, use_cutlist);
5153 }
5154 
5155 void MythPlayer::calcSliderPos(osdInfo &info, bool paddedFields)
5156 {
5157  if (!decoder)
5158  return;
5159 
5160  bool islive = false;
5161  info.text.insert("chapteridx", QString());
5162  info.text.insert("totalchapters", QString());
5163  info.text.insert("titleidx", QString());
5164  info.text.insert("totaltitles", QString());
5165  info.text.insert("angleidx", QString());
5166  info.text.insert("totalangles", QString());
5167  info.values.insert("position", 0);
5168  info.values.insert("progbefore", 0);
5169  info.values.insert("progafter", 0);
5170 
5171  int playbackLen = 0;
5172  bool fixed_playbacklen = false;
5173 
5174  if (decoder->GetCodecDecoderName() == "nuppel")
5175  {
5176  playbackLen = totalLength;
5177  fixed_playbacklen = true;
5178  }
5179 
5180  if (livetv && player_ctx->m_tvchain)
5181  {
5182  info.values["progbefore"] = (int)player_ctx->m_tvchain->HasPrev();
5183  info.values["progafter"] = (int)player_ctx->m_tvchain->HasNext();
5184  playbackLen = player_ctx->m_tvchain->GetLengthAtCurPos();
5185  islive = true;
5186  fixed_playbacklen = true;
5187  }
5188  else if (IsWatchingInprogress())
5189  {
5190  islive = true;
5191  }
5192  else
5193  {
5194  int chapter = GetCurrentChapter();
5195  int chapters = GetNumChapters();
5196  if (chapter && chapters > 1)
5197  {
5198  info.text["chapteridx"] = QString::number(chapter + 1);
5199  info.text["totalchapters"] = QString::number(chapters);
5200  }
5201 
5202  int title = GetCurrentTitle();
5203  int titles = GetNumTitles();
5204  if (title && titles > 1)
5205  {
5206  info.text["titleidx"] = QString::number(title + 1);
5207  info.text["totaltitles"] = QString::number(titles);
5208  }
5209 
5210  int angle = GetCurrentAngle();
5211  int angles = GetNumAngles();
5212  if (angle && angles > 1)
5213  {
5214  info.text["angleidx"] = QString::number(angle + 1);
5215  info.text["totalangles"] = QString::number(angles);
5216  }
5217  }
5218 
5219  // Set the raw values, followed by the translated values.
5220  for (int i = 0; i < 2 ; ++i)
5221  {
5222  bool honorCutList = (i > 0);
5223  bool stillFrame = false;
5224  int pos = 0;
5225 
5226  QString relPrefix = (honorCutList ? "rel" : "");
5227  if (!fixed_playbacklen)
5228  playbackLen = GetTotalSeconds(honorCutList);
5229  int secsplayed = GetSecondsPlayed(honorCutList);
5230 
5231  stillFrame = (secsplayed < 0);
5232  playbackLen = max(playbackLen, 0);
5233  secsplayed = min(playbackLen, max(secsplayed, 0));
5234 
5235  if (playbackLen > 0)
5236  pos = (int)(1000.0F * (secsplayed / (float)playbackLen));
5237 
5238  info.values.insert(relPrefix + "secondsplayed", secsplayed);
5239  info.values.insert(relPrefix + "totalseconds", playbackLen);
5240  info.values[relPrefix + "position"] = pos;
5241 
5242  int phours = secsplayed / 3600;
5243  int pmins = (secsplayed - phours * 3600) / 60;
5244  int psecs = (secsplayed - phours * 3600 - pmins * 60);
5245 
5246  int shours = playbackLen / 3600;
5247  int smins = (playbackLen - shours * 3600) / 60;
5248  int ssecs = (playbackLen - shours * 3600 - smins * 60);
5249 
5250  int secsbehind = max((playbackLen - secsplayed), 0);
5251  int sbhours = secsbehind / 3600;
5252  int sbmins = (secsbehind - sbhours * 3600) / 60;
5253  int sbsecs = (secsbehind - sbhours * 3600 - sbmins * 60);
5254 
5255  QString text1, text2, text3;
5256  if (paddedFields)
5257  {
5258  text1.sprintf("%02d:%02d:%02d", phours, pmins, psecs);
5259  text2.sprintf("%02d:%02d:%02d", shours, smins, ssecs);
5260  text3.sprintf("%02d:%02d:%02d", sbhours, sbmins, sbsecs);
5261  }
5262  else
5263  {
5264  if (shours > 0)
5265  {
5266  text1.sprintf("%d:%02d:%02d", phours, pmins, psecs);
5267  text2.sprintf("%d:%02d:%02d", shours, smins, ssecs);
5268  }
5269  else
5270  {
5271  text1.sprintf("%d:%02d", pmins, psecs);
5272  text2.sprintf("%d:%02d", smins, ssecs);
5273  }
5274 
5275  if (sbhours > 0)
5276  {
5277  text3.sprintf("%d:%02d:%02d", sbhours, sbmins, sbsecs);
5278  }
5279  else if (sbmins > 0)
5280  {
5281  text3.sprintf("%d:%02d", sbmins, sbsecs);
5282  }
5283  else
5284  {
5285  text3 = tr("%n second(s)", "", sbsecs);
5286  }
5287  }
5288 
5289  QString desc = stillFrame ? tr("Still Frame") :
5290  tr("%1 of %2").arg(text1).arg(text2);
5291 
5292  info.text[relPrefix + "description"] = desc;
5293  info.text[relPrefix + "playedtime"] = text1;
5294  info.text[relPrefix + "totaltime"] = text2;
5295  info.text[relPrefix + "remainingtime"] = islive ? QString() : text3;
5296  info.text[relPrefix + "behindtime"] = islive ? text3 : QString();
5297  }
5298 }
5299 
5300 // If position == -1, it signifies that we are computing the current
5301 // duration of an in-progress recording. In this case, we fetch the
5302 // current frame rate and frame count from the recorder.
5303 uint64_t MythPlayer::TranslatePositionFrameToMs(uint64_t position,
5304  bool use_cutlist) const
5305 {
5306  float frameRate = GetFrameRate();
5307  if (position == (uint64_t)-1 &&
5309  {
5310  float recorderFrameRate = player_ctx->m_recorder->GetFrameRate();
5311  if (recorderFrameRate > 0)
5312  frameRate = recorderFrameRate;
5313  position = player_ctx->m_recorder->GetFramesWritten();
5314  }
5315  return deleteMap.TranslatePositionFrameToMs(position, frameRate,
5316  use_cutlist);
5317 }
5318 
5320 {
5321  if (decoder)
5322  return decoder->GetNumChapters();
5323  return 0;
5324 }
5325 
5327 {
5328  if (decoder)
5330  return 0;
5331 }
5332 
5333 void MythPlayer::GetChapterTimes(QList<long long> &times)
5334 {
5335  if (decoder)
5336  return decoder->GetChapterTimes(times);