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 "filtermanager.h"
34 #include "livetvchain.h"
35 #include "decoderbase.h"
36 #include "nuppeldecoder.h"
37 #include "avformatdecoder.h"
38 #include "dummydecoder.h"
39 #include "tv_play.h"
40 #include "interactivetv.h"
41 #include "mythsystemevent.h"
42 #include "mythlogging.h"
43 #include "mythmiscutil.h"
44 #include "icringbuffer.h"
45 #include "audiooutput.h"
46 #include "cardutil.h"
47 #include "mythavutil.h"
48 #include "jitterometer.h" // for Jitterometer
49 #include "mythtimer.h" // for MythTimer
50 #include "mythuiactions.h" // for ACTION_LEFT, ACTION_RIGHT, etc
51 #include "ringbuffer.h" // for RingBuffer, etc
52 #include "tv_actions.h" // for ACTION_BIGJUMPFWD, etc
53 #include "mythcodeccontext.h"
54 
55 // MythUI headers
56 #include <mythmainwindow.h>
57 
58 extern "C" {
59 #include "vsync.h"
60 #include "libavcodec/avcodec.h"
61 }
62 
63 #include "remoteencoder.h"
64 
65 #if ! HAVE_ROUND
66 #define round(x) ((int) ((x) + 0.5))
67 #endif
68 
69 static unsigned dbg_ident(const MythPlayer* /*player*/);
70 
71 #define LOC QString("Player(%1): ").arg(dbg_ident(this),0,36)
72 #define LOC_DEC QString("Player(%1): ").arg(dbg_ident(m_mp),0,36)
73 
76 
77 // Exact frame seeking, no inaccuracy allowed.
78 const double MythPlayer::kInaccuracyNone = 0;
79 
80 // By default, when seeking, snap to a keyframe if the keyframe's
81 // distance from the target frame is less than 10% of the total seek
82 // distance.
83 const double MythPlayer::kInaccuracyDefault = 0.1;
84 
85 // Allow greater inaccuracy (50%) in the cutlist editor (unless the
86 // editor seek distance is set to 1 frame or 1 keyframe).
87 const double MythPlayer::kInaccuracyEditor = 0.5;
88 
89 // Any negative value means completely inexact, i.e. seek to the
90 // keyframe that is closest to the target.
91 const double MythPlayer::kInaccuracyFull = -1.0;
92 
94 {
95  RunProlog();
96  LOG(VB_PLAYBACK, LOG_INFO, LOC_DEC + "Decoder thread starting.");
97  if (m_mp)
98  m_mp->DecoderLoop(m_start_paused);
99  LOG(VB_PLAYBACK, LOG_INFO, LOC_DEC + "Decoder thread exiting.");
100  RunEpilog();
101 }
102 
103 static int toCaptionType(int type)
104 {
105  if (kTrackTypeCC608 == type) return kDisplayCC608;
106  if (kTrackTypeCC708 == type) return kDisplayCC708;
111  return 0;
112 }
113 
114 static int toTrackType(int type)
115 {
116  if (kDisplayCC608 == type) return kTrackTypeCC608;
117  if (kDisplayCC708 == type) return kTrackTypeCC708;
122  return kTrackTypeUnknown;
123 }
124 
126  : playerFlags(flags),
127  decoder(nullptr), decoder_change_lock(QMutex::Recursive),
128  videoOutput(nullptr), player_ctx(nullptr),
129  decoderThread(nullptr), playerThread(nullptr),
130 #ifdef Q_OS_ANDROID
131  playerThreadId(0),
132 #endif
133  // Window stuff
134  parentWidget(nullptr), embedding(false), embedRect(QRect()),
135  // State
136  totalDecoderPause(false), decoderPaused(false),
137  inJumpToProgramPause(false),
138  pauseDecoder(false), unpauseDecoder(false),
139  killdecoder(false), decoderSeek(-1), decodeOneFrame(false),
140  needNewPauseFrame(false),
141  bufferPaused(false), videoPaused(false),
142  allpaused(false), playing(false),
143  m_double_framerate(false), m_double_process(false),
144  m_deint_possible(true),
145  livetv(false),
146  watchingrecording(false),
147  transcoding(false),
148  hasFullPositionMap(false), limitKeyRepeat(false),
149  errorType(kError_None),
150  // Chapter stuff
151  jumpchapter(0),
152  // Bookmark stuff
153  bookmarkseek(0),
154  // Seek
155  fftime(0),
156  // Playback misc.
157  videobuf_retries(0), framesPlayed(0),
158  framesPlayedExtra(0),
159  totalFrames(0), totalLength(0),
160  totalDuration(0),
161  rewindtime(0),
162  m_latestVideoTimecode(-1),
163  // Input Video Attributes
164  video_disp_dim(0,0), video_dim(0,0),
165  video_frame_rate(29.97F), video_aspect(4.0F / 3.0F),
166  forced_video_aspect(-1),
167  resetScan(kScan_Ignore), m_scan(kScan_Interlaced),
168  m_scan_locked(false), m_scan_tracker(0), m_scan_initialized(false),
169  keyframedist(30),
170  // Prebuffering
171  buffering(false),
172  // General Caption/Teletext/Subtitle support
173  textDisplayMode(kDisplayNone),
174  prevTextDisplayMode(kDisplayNone),
175  prevNonzeroTextDisplayMode(kDisplayNone),
176  // Support for analog captions and teletext
177  vbimode(VBIMode::None),
178  ttPageNum(0x888),
179  // Support for captions, teletext, etc. decoded by libav
180  textDesired(false), enableCaptions(false), disableCaptions(false),
181  enableForcedSubtitles(false), disableForcedSubtitles(false),
182  allowForcedSubtitles(true),
183  // CC608/708
184  cc608(this), cc708(this),
185  // MHEG/MHI Interactive TV visible in OSD
186  itvVisible(false),
187  interactiveTV(nullptr),
188  itvEnabled(false),
189  // OSD stuff
190  osd(nullptr), reinit_osd(false), osdLock(QMutex::Recursive),
191  // Audio
192  audio(this, (flags & kAudioMuted) != 0),
193  // Picture-in-Picture stuff
194  pip_active(false), pip_visible(true),
195  // Filters
196  videoFiltersForProgram(""), videoFiltersOverride(""),
197  postfilt_width(0), postfilt_height(0),
198  videoFilters(nullptr), FiltMan(new FilterManager()),
199 
200  forcePositionMapSync(false), pausedBeforeEdit(false),
201  speedBeforeEdit(1.0F),
202  // Playback (output) speed control
203  decoder_lock(QMutex::Recursive),
204  next_play_speed(1.0F), next_normal_speed(true),
205  play_speed(1.0F), normal_speed(true),
206  frame_interval((int)(1000000.0F / 30)), m_frame_interval(0),
207  m_fpsMultiplier(1),
208  ffrew_skip(1),ffrew_adjust(0),
209  fileChanged(false),
210  // Audio and video synchronization stuff
211  videosync(nullptr), avsync_delay(0),
212  avsync_adjustment(0), avsync_avg(0),
213  avsync_predictor(0), avsync_predictor_enabled(false),
214  refreshrate(0),
215  lastsync(false), repeat_delay(0),
216  disp_timecode(0), avsync_audiopaused(false),
217  // AVSync for Raspberry Pi digital streams
218  avsync_averaging(4), // Number of frames to average
219  avsync_interval(0), // Number of frames skip between sync checks
220  avsync_next(0), // Frames till next sync check
221  // Time Code stuff
222  prevtc(0), prevrp(0),
223  savedAudioTimecodeOffset(0),
224  rtcbase(0),
225  maxtcval(0), maxtcframes(0),
226  numdroppedframes(0),
227  prior_audiotimecode(0),
228  prior_videotimecode(0),
229  m_timeOffsetBase(0),
230  // LiveTVChain stuff
231  m_tv(nullptr), isDummy(false),
232  // Counter for buffering messages
233  bufferingCounter(0),
234  // Debugging variables
235  output_jmeter(new Jitterometer(LOC)),
236  disable_passthrough(false)
237 {
238  memset(&tc_lastval, 0, sizeof(tc_lastval));
239  memset(&tc_wrap, 0, sizeof(tc_wrap));
241  ("PlayerMaxDiverge", 3.0));
242  if (max_diverge < 1.0F)
243  max_diverge = 1.0F;
244 
245  playerThread = QThread::currentThread();
246 #ifdef Q_OS_ANDROID
247  playerThreadId = gettid();
248 #endif
249  // Playback (output) zoom control
251 
253 
255  gCoreContext->GetFloatSettingOnHost("XineramaMonitorAspectRatio",
256  gCoreContext->GetHostName(), 1.7777);
258  decode_extra_audio = gCoreContext->GetBoolSetting("DecodeExtraAudio", false);
259  itvEnabled = gCoreContext->GetBoolSetting("EnableMHEG", false);
260  clearSavedPosition = gCoreContext->GetNumSetting("ClearSavedPosition", 1);
261  endExitPrompt = gCoreContext->GetNumSetting("EndOfRecordingExitPrompt");
263 
264  // Get VBI page number
265  QString mypage = gCoreContext->GetSetting("VBIpageNr", "888");
266  bool valid = false;
267  uint tmp = mypage.toInt(&valid, 16);
268  ttPageNum = (valid) ? tmp : ttPageNum;
270  avsync2adjustms = (int64_t)gCoreContext->GetNumSetting("AVSync2AdjustMS", 10);
271  if (avsync2adjustms < 1)
272  avsync2adjustms = 1;
273  if (avsync2adjustms > 40)
274  avsync2adjustms = 40;
275  m_avTimer.start();
276 }
277 
279 {
280  // NB the interactiveTV thread is a client of OSD so must be deleted
281  // before locking and deleting the OSD
282  {
283  QMutexLocker lk0(&itvLock);
284  delete interactiveTV;
285  interactiveTV = nullptr;
286  }
287 
288  QMutexLocker lk1(&osdLock);
289  QMutexLocker lk2(&vidExitLock);
290  QMutexLocker lk3(&videofiltersLock);
291 
292  if (osd)
293  {
294  delete osd;
295  osd = nullptr;
296  }
297 
298  SetDecoder(nullptr);
299 
300  if (decoderThread)
301  {
302  delete decoderThread;
303  decoderThread = nullptr;
304  }
305 
306  if (FiltMan)
307  {
308  delete FiltMan;
309  FiltMan = nullptr;
310  }
311 
312  if (videoFilters)
313  {
314  delete videoFilters;
315  videoFilters = nullptr;
316  }
317 
318  if (videosync)
319  {
320  delete videosync;
321  videosync = nullptr;
322  }
323 
324  if (videoOutput)
325  {
326  delete videoOutput;
327  videoOutput = nullptr;
328  }
329 
330  if (output_jmeter)
331  {
332  delete output_jmeter;
333  output_jmeter = nullptr;
334  }
335 
336  if (detect_letter_box)
337  {
338  delete detect_letter_box;
339  detect_letter_box = nullptr;
340  }
341 }
342 
344 {
345  watchingrecording = mode;
346  if (decoder)
348 }
349 
351 {
354 }
355 
357 {
358  bufferPauseLock.lock();
359  if (player_ctx->m_buffer)
360  {
363  }
364  bufferPaused = true;
365  bufferPauseLock.unlock();
366 }
367 
369 {
370  bufferPauseLock.lock();
371  if (player_ctx->m_buffer)
373  bufferPaused = false;
374  bufferPauseLock.unlock();
375 }
376 
378 {
379  if (!pauseLock.tryLock(100))
380  {
381  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waited 100ms to get pause lock.");
383  }
384  bool already_paused = allpaused;
385  if (already_paused)
386  {
387  pauseLock.unlock();
388  return already_paused;
389  }
390  next_play_speed = 0.0;
391  next_normal_speed = false;
392  PauseVideo();
393  audio.Pause(true);
394  PauseDecoder();
395  PauseBuffer();
396  if (!decoderPaused)
397  PauseDecoder(); // Retry in case audio only stream
399  {
402  else if (videoOutput && !FlagIsSet(kVideoIsNull))
404  }
405  pauseLock.unlock();
406  return already_paused;
407 }
408 
409 bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio)
410 {
411  pauseLock.lock();
412  LOG(VB_PLAYBACK, LOG_INFO, LOC +
413  QString("Play(%1, normal %2, unpause audio %3)")
414  .arg(speed,5,'f',1).arg(normal).arg(unpauseaudio));
415 
416  if (deleteMap.IsEditing())
417  {
418  LOG(VB_GENERAL, LOG_ERR, LOC + "Ignoring Play(), in edit mode.");
419  pauseLock.unlock();
420  return false;
421  }
422  rtcbase = 0;
426  UnpauseBuffer();
427  UnpauseDecoder();
428  if (unpauseaudio)
429  audio.Pause(false);
430  UnpauseVideo();
431  allpaused = false;
432  next_play_speed = speed;
433  next_normal_speed = normal;
434  pauseLock.unlock();
435  return true;
436 }
437 
439 {
440  videoPauseLock.lock();
441  needNewPauseFrame = true;
442  videoPaused = true;
443  videoPauseLock.unlock();
444 }
445 
447 {
448  videoPauseLock.lock();
449  videoPaused = false;
450  if (videoOutput)
452  videoPauseLock.unlock();
453 }
454 
456 {
458  if (!player_ctx)
459  return;
460 
461  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
462  player_ctx->SetPlayingInfo(&pginfo);
463  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
464 
465  SetVideoFilters("");
466  InitFilters();
467 }
468 
469 void MythPlayer::SetPlaying(bool is_playing)
470 {
471  QMutexLocker locker(&playingLock);
472 
473  playing = is_playing;
474 
475  playingWaitCond.wakeAll();
476 }
477 
478 bool MythPlayer::IsPlaying(uint wait_in_msec, bool wait_for) const
479 {
480  QMutexLocker locker(&playingLock);
481 
482  if (!wait_in_msec)
483  return playing;
484 
485  MythTimer t;
486  t.start();
487 
488  while ((wait_for != playing) && ((uint)t.elapsed() < wait_in_msec))
489  {
490  playingWaitCond.wait(
491  &playingLock, max(0,(int)wait_in_msec - t.elapsed()));
492  }
493 
494  return playing;
495 }
496 
498 {
499  if (!player_ctx)
500  return false;
501 
502  PIPState pipState = player_ctx->GetPIPState();
503 
504  if (!decoder)
505  {
506  LOG(VB_GENERAL, LOG_ERR, LOC +
507  "Cannot create a video renderer without a decoder.");
508  return false;
509  }
510 
518 
519  if (!videoOutput)
520  {
521  LOG(VB_GENERAL, LOG_ERR, LOC +
522  "Couldn't create VideoOutput instance. Exiting..");
523  SetErrored(tr("Failed to initialize video output"));
524  return false;
525  }
526 
527  if (embedding && pipState == kPIPOff)
529 
530  if (decoder && decoder->GetVideoInverted())
532 
533  InitFilters();
534 
535  return true;
536 }
537 
539 {
541  {
542  osdLock.lock();
544  {
545  reinit_osd = true;
546  osdLock.unlock();
547  return;
548  }
549  QRect visible, total;
550  float aspect, scaling;
551  videoOutput->GetOSDBounds(total, visible, aspect,
552  scaling, 1.0F);
553  if (osd)
554  {
556  int stretch = lroundf(aspect * 100);
557  if ((osd->Bounds() != visible) ||
558  (osd->GetFontStretch() != stretch))
559  {
560  uint old = textDisplayMode;
561  ToggleCaptions(old);
562  osd->Reinit(visible, aspect);
563  EnableCaptions(old, false);
564  if (deleteMap.IsEditing())
565  {
566  bool const changed = deleteMap.IsChanged();
567  deleteMap.SetChanged(true);
569  deleteMap.SetChanged(changed);
570  }
571  }
572  }
573 
574 #ifdef USING_MHEG
575  if (GetInteractiveTV())
576  {
577  QMutexLocker locker(&itvLock);
578  interactiveTV->Reinit(total, visible, aspect);
579  itvVisible = false;
580  }
581 #endif // USING_MHEG
582  reinit_osd = false;
583  osdLock.unlock();
584  }
585 }
586 
588 {
589 
590  bool aspect_only = false;
591  {
592  QMutexLocker locker1(&osdLock);
593  QMutexLocker locker2(&vidExitLock);
594  QMutexLocker locker3(&videofiltersLock);
595 
597  float aspect = (forced_video_aspect > 0) ? forced_video_aspect :
598  video_aspect;
602  aspect_only))
603  {
604  LOG(VB_GENERAL, LOG_ERR, LOC +
605  "Failed to Reinitialize Video. Exiting..");
606  SetErrored(tr("Failed to reinitialize video output"));
607  return;
608  }
609 
610  // the display refresh rate may have been changed by VideoOutput
611  if (videosync)
612  {
614  if (ri != videosync->getRefreshInterval())
615  {
616  LOG(VB_PLAYBACK, LOG_INFO, LOC +
617  QString("Refresh rate has changed from %1 to %2")
619  .arg(ri));
621  }
622  }
623 
624  if (osd)
626  ReinitOSD();
627  }
628 
629  if (!aspect_only)
630  {
631  ClearAfterSeek();
632  InitFilters();
633  }
634 
635  if (textDisplayMode)
636  {
637  EnableSubtitles(true);
638  }
639 }
640 
641 static inline QString toQString(FrameScanType scan) {
642  switch (scan) {
643  case kScan_Ignore: return QString("Ignore Scan");
644  case kScan_Detect: return QString("Detect Scan");
645  case kScan_Interlaced: return QString("Interlaced Scan");
646  case kScan_Progressive: return QString("Progressive Scan");
647  default: return QString("Unknown Scan");
648  }
649 }
650 
653  float fps, int video_height)
654 {
655  QString dbg = QString("detectInterlace(") + toQString(newScan) +
656  QString(", ") + toQString(scan) + QString(", ") +
657  QString("%1").arg(fps) + QString(", ") +
658  QString("%1").arg(video_height) + QString(") ->");
659 
660  if (kScan_Ignore != newScan || kScan_Detect == scan)
661  {
662  // The scanning mode should be decoded from the stream, but if it
663  // isn't, we have to guess.
664 
665  scan = kScan_Interlaced; // default to interlaced
666  if (720 == video_height) // ATSC 720p
668  else if (fps > 45) // software deinterlacing
670 
671  if (kScan_Detect != newScan)
672  scan = newScan;
673  };
674 
675  LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg+toQString(scan));
676 
677  return scan;
678 }
679 
680 void MythPlayer::SetKeyframeDistance(int keyframedistance)
681 {
682  keyframedist = (keyframedistance > 0) ? keyframedistance : keyframedist;
683 }
684 
689 {
690  m_double_framerate = false;
691  m_double_process = false;
692 
693  if (videoOutput)
694  {
696  bool hwset = decoder->GetMythCodecContext()->FallbackDeint();
697  if (!hwset)
699  }
700 }
701 
702 void MythPlayer::AutoDeint(VideoFrame *frame, bool allow_lock)
703 {
704  if (!frame || m_scan_locked)
705  return;
706 
707  if (frame->interlaced_frame)
708  {
709  if (m_scan_tracker < 0)
710  {
711  LOG(VB_PLAYBACK, LOG_INFO, LOC +
712  QString("interlaced frame seen after %1 progressive frames")
713  .arg(abs(m_scan_tracker)));
714  m_scan_tracker = 2;
715  if (allow_lock)
716  {
717  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Locking scan to Interlaced.");
719  return;
720  }
721  }
722  m_scan_tracker++;
723  }
724  else
725  {
726  if (m_scan_tracker > 0)
727  {
728  LOG(VB_PLAYBACK, LOG_INFO, LOC +
729  QString("progressive frame seen after %1 interlaced frames")
730  .arg(m_scan_tracker));
731  m_scan_tracker = 0;
732  }
733  m_scan_tracker--;
734  }
735 
736  if ((m_scan_tracker % 400) == 0)
737  {
738  QString type;
739  // = (m_scan_tracker < 0) ? "progressive" : "interlaced";
740  if (m_scan_tracker < 0)
741  {
743  type = "codec-deinterlaced";
744  else
745  type = "progressive";
746  }
747  else
748  type = "interlaced";
749 
750  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1 %2 frames seen.")
751  .arg(abs(m_scan_tracker)).arg(type));
752  }
753 
754  int min_count = !allow_lock ? 0 : 2;
755  if (abs(m_scan_tracker) <= min_count)
756  return;
757 
759  m_scan_locked = false;
760 }
761 
763 {
764  QMutexLocker locker(&videofiltersLock);
765 
767  {
768  resetScan = scan;
769  return;
770  }
771 
772  if (!videoOutput || !videosync)
773  return; // hopefully this will be called again later...
774 
776 
777  if (m_scan_initialized &&
778  m_scan == scan &&
780  return;
781 
783 
784  m_scan_initialized = true;
786 
787  bool interlaced = is_interlaced(scan);
788 
789  if (interlaced && !m_deint_possible)
790  {
791  m_scan = scan;
792  return;
793  }
794 
795  if (interlaced)
796  {
798  if (!m_deint_possible)
799  {
800  LOG(VB_GENERAL, LOG_INFO, LOC + "Unable to enable Video Output based deinterlacing");
801  m_scan = scan;
802  return;
803  }
805  {
806  m_double_framerate = true;
807  if (!CanSupportDoubleRate())
808  {
809  LOG(VB_GENERAL, LOG_ERR, LOC +
810  "Video sync method can't support double framerate "
811  "(refresh rate too low for 2x deint)");
812  FallbackDeint();
813  }
814  }
816  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Enabled Video Output based deinterlacing");
817  }
818  else
819  {
820  if (kScan_Progressive == scan)
821  {
822  m_double_process = false;
823  m_double_framerate = false;
825  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Disabled Video Output based deinterlacing");
826  }
827  }
828 
829  m_scan = scan;
830 }
831 
832 void MythPlayer::SetVideoParams(int width, int height, double fps,
833  FrameScanType scan, const QString& codecName)
834 {
835  bool paramsChanged = false;
836 
837  if (width >= 1 && height >= 1)
838  {
839  paramsChanged = true;
840  video_dim = QSize((width + 15) & ~0xf, (height + 15) & ~0xf);
841  video_disp_dim = QSize(width, height);
842  video_aspect = (float)width / height;
843  }
844 
845  if (!qIsNaN(fps) && fps > 0.0 && fps < 121.0)
846  {
847  paramsChanged = true;
848  video_frame_rate = fps;
849  if (ffrew_skip != 0 && ffrew_skip != 1)
850  {
851  UpdateFFRewSkip();
852  }
853  else
854  {
855  float temp_speed = (play_speed == 0.0F) ?
858  1.0 / (video_frame_rate * static_cast<double>(temp_speed)));
859  }
860  }
861 
862  if (!codecName.isEmpty())
863  {
864  m_codecName = codecName;
865  paramsChanged = true;
866  }
867 
868  if (!paramsChanged)
869  return;
870 
871  if (videoOutput)
872  ReinitVideo();
873 
874  if (IsErrored())
875  return;
876 
878  video_disp_dim.height()));
879  m_scan_locked = false;
880  m_scan_tracker = (m_scan == kScan_Interlaced) ? 2 : 0;
881 }
882 
883 
884 void MythPlayer::SetFrameRate(double fps)
885 {
886  video_frame_rate = fps;
887  float temp_speed = (play_speed == 0.0F) ?
890  1.0 / (video_frame_rate * static_cast<double>(temp_speed)));
891 }
892 
893 void MythPlayer::SetFileLength(int total, int frames)
894 {
895  totalLength = total;
897 }
898 
899 void MythPlayer::SetDuration(int duration)
900 {
901  totalDuration = duration;
902 }
903 
905 {
906  isDummy = true;
907 
908  if (!videoOutput)
909  {
911  SetVideoParams(720, 576, 25.00);
912  }
913 
914  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
915  DummyDecoder *dec = new DummyDecoder(this, *(player_ctx->m_playingInfo));
916  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
917  SetDecoder(dec);
918 }
919 
920 void MythPlayer::CreateDecoder(char *testbuf, int testreadsize)
921 {
922  if (NuppelDecoder::CanHandle(testbuf, testreadsize))
924  else if (AvFormatDecoder::CanHandle(testbuf,
926  testreadsize))
927  {
929  playerFlags));
930  }
931 }
932 
934 {
935  // Disable hardware acceleration for second PBP
936  if (player_ctx && (player_ctx->IsPBP() && !player_ctx->IsPrimaryPBP()) &&
938  {
940  }
941 
942  isDummy = false;
943 
944  if (!player_ctx || !player_ctx->m_buffer)
945  return -1;
946 
948 
949  if (player_ctx->m_tvchain &&
951  "DUMMY")
952  {
953  OpenDummy();
954  return 0;
955  }
956 
959  char *testbuf = new char[kDecoderProbeBufferSize];
960  UnpauseBuffer();
961 
962  // delete any pre-existing recorder
963  SetDecoder(nullptr);
964  int testreadsize = 2048;
965 
966  MythTimer bigTimer; bigTimer.start();
967  int timeout = max((retries + 1) * 500, 30000U);
968  while (testreadsize <= kDecoderProbeBufferSize)
969  {
970  MythTimer peekTimer; peekTimer.start();
971  while (player_ctx->m_buffer->Peek(testbuf, testreadsize) != testreadsize)
972  {
973  // NB need to allow for streams encountering network congestion
974  if (peekTimer.elapsed() > 30000 || bigTimer.elapsed() > timeout
976  {
977  LOG(VB_GENERAL, LOG_ERR, LOC +
978  QString("OpenFile(): Could not read first %1 bytes of '%2'")
979  .arg(testreadsize)
980  .arg(player_ctx->m_buffer->GetFilename()));
981  delete[] testbuf;
982  SetErrored(tr("Could not read first %1 bytes").arg(testreadsize));
983  return -1;
984  }
985  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenFile() waiting on data");
986  usleep(50 * 1000);
987  }
988 
989  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
990  CreateDecoder(testbuf, testreadsize);
991  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
992  if (decoder || (bigTimer.elapsed() > timeout))
993  break;
994  testreadsize <<= 1;
995  }
996 
997  if (!decoder)
998  {
999  LOG(VB_GENERAL, LOG_ERR, LOC +
1000  QString("Couldn't find an A/V decoder for: '%1'")
1001  .arg(player_ctx->m_buffer->GetFilename()));
1002  SetErrored(tr("Could not find an A/V decoder"));
1003 
1004  delete[] testbuf;
1005  return -1;
1006  }
1007  if (decoder->IsErrored())
1008  {
1009  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not initialize A/V decoder.");
1010  SetDecoder(nullptr);
1011  SetErrored(tr("Could not initialize A/V decoder"));
1012 
1013  delete[] testbuf;
1014  return -1;
1015  }
1016 
1017  decoder->SetSeekSnap(0);
1021 
1022  // Set 'no_video_decode' to true for audio only decodeing
1023  bool no_video_decode = false;
1024 
1025  // We want to locate decoder for video even if using_null_videoout
1026  // is true, only disable if no_video_decode is true.
1027  int ret = decoder->OpenFile(
1028  player_ctx->m_buffer, no_video_decode, testbuf, testreadsize);
1029  delete[] testbuf;
1030 
1031  if (ret < 0)
1032  {
1033  LOG(VB_GENERAL, LOG_ERR, QString("Couldn't open decoder for: %1")
1034  .arg(player_ctx->m_buffer->GetFilename()));
1035  SetErrored(tr("Could not open decoder"));
1036  return -1;
1037  }
1038 
1039  audio.CheckFormat();
1040 
1041  if (ret > 0)
1042  {
1043  hasFullPositionMap = true;
1044  deleteMap.LoadMap();
1046  }
1047 
1048  // Determine the initial bookmark and update it for the cutlist
1052 
1053  if (!gCoreContext->IsDatabaseIgnored() &&
1055  {
1057  "DefaultChanid", player_ctx->m_playingInfo->GetChanID());
1058  QString callsign = player_ctx->m_playingInfo->GetChannelSchedulingID();
1059  QString channum = player_ctx->m_playingInfo->GetChanNum();
1061  "DefaultChanKeys", callsign + "[]:[]" + channum);
1063  {
1064  int cardid = player_ctx->m_recorder->GetRecorderNumber();
1065  CardUtil::SetStartChannel(cardid, channum);
1066  }
1067  }
1068 
1069  return IsErrored() ? -1 : 0;
1070 }
1071 
1072 void MythPlayer::SetFramesPlayed(uint64_t played)
1073 {
1074  framesPlayed = played;
1075  framesPlayedExtra = 0;
1076  if (videoOutput)
1077  videoOutput->SetFramesPlayed(played);
1078 }
1079 
1080 void MythPlayer::SetVideoFilters(const QString &override)
1081 {
1082  videoFiltersOverride = override;
1084  (FlagIsSet(kVideoIsNull)) ? "onefield" : "");
1085 }
1086 
1088 {
1089  QString filters = "";
1090  if (videoOutput)
1091  filters = videoOutput->GetFilters();
1092 
1093  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
1094  QString("InitFilters() vo '%1' prog '%2' over '%3'")
1095  .arg(filters).arg(videoFiltersForProgram)
1096  .arg(videoFiltersOverride));
1097 
1098  if (!videoFiltersForProgram.isEmpty())
1099  {
1100  if (videoFiltersForProgram[0] != '+')
1101  {
1102  filters = videoFiltersForProgram;
1103  }
1104  else
1105  {
1106  if ((filters.length() > 1) && (!filters.endsWith(",")))
1107  filters += ",";
1108  filters += videoFiltersForProgram.mid(1);
1109  }
1110  }
1111 
1112  if (!videoFiltersOverride.isEmpty())
1113  filters = videoFiltersOverride;
1114 
1115  AvFormatDecoder *afd = dynamic_cast<AvFormatDecoder *>(decoder);
1116  if (afd && afd->GetVideoInverted() && !filters.contains("vflip"))
1117  filters += ",vflip";
1118 
1119  videofiltersLock.lock();
1120 
1121  if (videoFilters)
1122  {
1123  delete videoFilters;
1124  videoFilters = nullptr;
1125  }
1126 
1127  if (!filters.isEmpty())
1128  {
1129  VideoFrameType itmp = FMT_YV12;
1130  VideoFrameType otmp = FMT_YV12;
1131  int btmp;
1132  postfilt_width = video_dim.width();
1133  postfilt_height = video_dim.height();
1134 
1136  filters, itmp, otmp, postfilt_width, postfilt_height, btmp);
1137  }
1138 
1139  videofiltersLock.unlock();
1140 
1141  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("LoadFilters('%1'..) -> 0x%2")
1142  .arg(filters).arg((uint64_t)videoFilters,0,16));
1143 }
1144 
1149 {
1150  if (videoOutput)
1151  return videoOutput->FreeVideoFrames();
1152  return 0;
1153 }
1154 
1165 {
1166  if (videoOutput)
1167  return videoOutput->GetNextFreeFrame();
1168  return nullptr;
1169 }
1170 
1175  int64_t timecode,
1176  bool wrap)
1177 {
1178  if (wrap)
1179  WrapTimecode(timecode, TC_VIDEO);
1180  buffer->timecode = timecode;
1181  m_latestVideoTimecode = timecode;
1182 
1183  if (videoOutput)
1184  videoOutput->ReleaseFrame(buffer);
1185 
1186  detect_letter_box->Detect(buffer);
1187  if (allpaused)
1188  CheckAspectRatio(buffer);
1189 }
1190 
1195 {
1196  if (videoOutput)
1197  videoOutput->ClearDummyFrame(frame);
1198 }
1199 
1204 {
1205  if (videoOutput)
1206  videoOutput->DiscardFrame(buffer);
1207 }
1208 
1220 void MythPlayer::DiscardVideoFrames(bool next_frame_keyframe)
1221 {
1222  if (videoOutput)
1223  videoOutput->DiscardFrames(next_frame_keyframe);
1224 }
1225 
1226 void MythPlayer::DrawSlice(VideoFrame *frame, int x, int y, int w, int h)
1227 {
1228  if (videoOutput)
1229  videoOutput->DrawSlice(frame, x, y, w, h);
1230 }
1231 
1232 void* MythPlayer::GetDecoderContext(unsigned char* buf, uint8_t*& id)
1233 {
1234  if (videoOutput)
1235  return videoOutput->GetDecoderContext(buf, id);
1236  return nullptr;
1237 }
1238 
1240 {
1241  EofState eof = GetEof();
1242  if (eof != kEofStateNone && !allpaused)
1243  return true;
1244  if (GetEditMode())
1245  return false;
1246  if (livetv)
1247  return false;
1249  return true;
1250  return false;
1251 }
1252 
1254 {
1255  w = video_dim.width();
1256  h = video_dim.height();
1257 
1258  VideoFrame *retval = nullptr;
1259 
1260  vidExitLock.lock();
1261  if (videoOutput)
1262  {
1263  retval = videoOutput->GetLastShownFrame();
1264  videofiltersLock.lock();
1265  if (videoFilters && player_ctx->IsPIP())
1266  videoFilters->ProcessFrame(retval);
1267  videofiltersLock.unlock();
1268  }
1269 
1270  if (!retval)
1271  vidExitLock.unlock();
1272 
1273  return retval;
1274 }
1275 
1277 {
1278  if (videoOutput)
1279  videoOutput->DeLimboFrame(frame);
1280 }
1281 
1283 {
1284  if (frame)
1285  vidExitLock.unlock();
1286 }
1287 
1289 {
1290  if (videoOutput)
1291  videoOutput->EmbedInWidget(rect);
1292  else
1293  {
1294  embedRect = rect;
1295  embedding = true;
1296  }
1297 }
1298 
1300 {
1301  if (videoOutput)
1302  {
1304  ReinitOSD();
1305  }
1306  else
1307  {
1308  embedRect = QRect();
1309  embedding = false;
1310  }
1311 }
1312 
1313 void MythPlayer::WindowResized(const QSize &new_size)
1314 {
1315  if (videoOutput)
1316  videoOutput->WindowResized(new_size);
1317 }
1318 
1320 {
1321  QMutexLocker locker(&osdLock);
1322  if (!osd)
1323  return;
1324 
1325  osd->EnableTeletext(true, page);
1328 }
1329 
1331 {
1332  QMutexLocker locker(&osdLock);
1333  if (!osd)
1334  return;
1335 
1336  osd->EnableTeletext(false, 0);
1338 
1339  /* If subtitles are enabled before the teletext menu was displayed,
1340  re-enabled them. */
1343 }
1344 
1346 {
1347  QMutexLocker locker(&osdLock);
1348  if (!osd)
1349  return;
1350 
1351  osd->TeletextReset();
1352 }
1353 
1358 {
1359  osdLock.lock();
1361  ttPageNum = page;
1365  osdLock.unlock();
1366 }
1367 
1369 {
1371  return false;
1372 
1373  bool handled = true;
1374 
1375  osdLock.lock();
1376  if (action == "MENU" || action == ACTION_TOGGLETT || action == "ESCAPE")
1377  DisableTeletext();
1378  else if (osd)
1379  handled = osd->TeletextAction(action);
1380  osdLock.unlock();
1381 
1382  return handled;
1383 }
1384 
1386 {
1387  QMutexLocker locker(&osdLock);
1388  if (!osd)
1389  return;
1390 
1397  {
1398  osd->ClearSubtitles();
1399  }
1402  {
1403  osd->TeletextClear();
1404  }
1405 }
1406 
1407 void MythPlayer::DisableCaptions(uint mode, bool osd_msg)
1408 {
1409  if (textDisplayMode)
1411  textDisplayMode &= ~mode;
1412  ResetCaptions();
1413 
1414  QMutexLocker locker(&osdLock);
1415 
1416  bool newTextDesired = (textDisplayMode & kDisplayAllTextCaptions) != 0U;
1417  // Only turn off textDesired if the Operator requested it.
1418  if (osd_msg || newTextDesired)
1419  textDesired = newTextDesired;
1420  QString msg = "";
1421  if (kDisplayNUVTeletextCaptions & mode)
1422  msg += tr("TXT CAP");
1423  if (kDisplayTeletextCaptions & mode)
1424  {
1427  DisableTeletext();
1428  }
1429  int preserve = textDisplayMode & (kDisplayCC608 | kDisplayTextSubtitle |
1432  if ((kDisplayCC608 & mode) || (kDisplayCC708 & mode) ||
1433  (kDisplayAVSubtitle & mode) || (kDisplayRawTextSubtitle & mode))
1434  {
1435  int type = toTrackType(mode);
1436  msg += decoder->GetTrackDesc(type, GetTrack(type));
1437  if (osd)
1438  osd->EnableSubtitles(preserve);
1439  }
1440  if (kDisplayTextSubtitle & mode)
1441  {
1442  msg += tr("Text subtitles");
1443  if (osd)
1444  osd->EnableSubtitles(preserve);
1445  }
1446  if (!msg.isEmpty() && osd_msg)
1447  {
1448  msg += " " + tr("Off");
1450  }
1451 }
1452 
1453 void MythPlayer::EnableCaptions(uint mode, bool osd_msg)
1454 {
1455  QMutexLocker locker(&osdLock);
1456  bool newTextDesired = (mode & kDisplayAllTextCaptions) != 0U;
1457  // Only turn off textDesired if the Operator requested it.
1458  if (osd_msg || newTextDesired)
1459  textDesired = newTextDesired;
1460  QString msg = "";
1461  if ((kDisplayCC608 & mode) || (kDisplayCC708 & mode) ||
1462  (kDisplayAVSubtitle & mode) || kDisplayRawTextSubtitle & mode)
1463  {
1464  int type = toTrackType(mode);
1465  msg += decoder->GetTrackDesc(type, GetTrack(type));
1466  if (osd)
1467  osd->EnableSubtitles(mode);
1468  }
1469  if (kDisplayTextSubtitle & mode)
1470  {
1471  if (osd)
1473  msg += tr("Text subtitles");
1474  }
1475  if (kDisplayNUVTeletextCaptions & mode)
1476  msg += tr("TXT %1").arg(ttPageNum, 3, 16);
1477  if (kDisplayTeletextCaptions & mode)
1478  {
1481 
1482  int page = decoder->GetTrackLanguageIndex(
1485 
1486  EnableTeletext(page);
1488  }
1489 
1490  msg += " " + tr("On");
1491 
1492  LOG(VB_PLAYBACK, LOG_INFO, QString("EnableCaptions(%1) msg: %2")
1493  .arg(mode).arg(msg));
1494 
1495  textDisplayMode = mode;
1496  if (textDisplayMode)
1498  if (osd_msg)
1500 }
1501 
1503 {
1505  return textDisplayMode;
1506 }
1507 
1509 {
1510  QMutexLocker locker(&osdLock);
1511  uint mode = toCaptionType(type);
1512  uint origMode = textDisplayMode;
1513 
1514  if (textDisplayMode)
1515  DisableCaptions(textDisplayMode, (origMode & mode) != 0U);
1516  if (origMode & mode)
1517  return textDisplayMode;
1518  if (mode)
1519  EnableCaptions(mode);
1520  return textDisplayMode;
1521 }
1522 
1523 void MythPlayer::SetCaptionsEnabled(bool enable, bool osd_msg)
1524 {
1525  QMutexLocker locker(&osdLock);
1526  enableCaptions = disableCaptions = false;
1527  uint origMode = textDisplayMode;
1528 
1529  // Only turn off textDesired if the Operator requested it.
1530  if (osd_msg || enable)
1531  textDesired = enable;
1532 
1533  if (!enable)
1534  {
1535  DisableCaptions(origMode, osd_msg);
1536  return;
1537  }
1540  if (origMode != (uint)mode)
1541  {
1542  DisableCaptions(origMode, false);
1543 
1544  if (kDisplayNone == mode)
1545  {
1546  if (osd_msg)
1547  {
1548  SetOSDMessage(tr("No captions",
1549  "CC/Teletext/Subtitle text not available"),
1550  kOSDTimeout_Med);
1551  }
1552  LOG(VB_PLAYBACK, LOG_INFO,
1553  "No captions available yet to enable.");
1554  }
1555  else if (mode)
1556  {
1557  EnableCaptions(mode, osd_msg);
1558  }
1559  }
1560  ResetCaptions();
1561 }
1562 
1564 {
1573 }
1574 
1576 {
1577  if (decoder)
1578  return decoder->GetTracks(type);
1579  return QStringList();
1580 }
1581 
1583 {
1584  if (decoder)
1585  return decoder->GetTrackCount(type);
1586  return 0;
1587 }
1588 
1589 int MythPlayer::SetTrack(uint type, int trackNo)
1590 {
1591  int ret = -1;
1592  if (!decoder)
1593  return ret;
1594 
1595  ret = decoder->SetTrack(type, trackNo);
1596  if (kTrackTypeAudio == type)
1597  {
1598  QString msg = "";
1599  if (decoder)
1601  kOSDTimeout_Med);
1602  return ret;
1603  }
1604 
1605  uint subtype = toCaptionType(type);
1606  if (subtype)
1607  {
1609  EnableCaptions(subtype, true);
1610  if ((kDisplayCC708 == subtype || kDisplayCC608 == subtype) && decoder)
1611  {
1612  int sid = decoder->GetTrackInfo(type, trackNo).m_stream_id;
1613  if (sid >= 0)
1614  {
1615  (kDisplayCC708 == subtype) ? cc708.SetCurrentService(sid) :
1616  cc608.SetMode(sid);
1617  }
1618  }
1619  }
1620  return ret;
1621 }
1622 
1629 {
1630  if (trackType >= kTrackTypeSubtitle &&
1631  trackType <= kTrackTypeTeletextCaptions && textDesired)
1632  {
1633  enableCaptions = true;
1634  }
1635 }
1636 
1638 {
1639  if (enable)
1640  enableCaptions = true;
1641  else
1642  disableCaptions = true;
1643 }
1644 
1646 {
1647  if (enable)
1648  enableForcedSubtitles = true;
1649  else
1650  disableForcedSubtitles = true;
1651 }
1652 
1654 {
1655  allowForcedSubtitles = allow;
1657  tr("Forced Subtitles On") :
1658  tr("Forced Subtitles Off"),
1659  kOSDTimeout_Med);
1660 }
1661 
1663 {
1664  disableForcedSubtitles = false;
1665  osdLock.lock();
1666  if (osd)
1668  osdLock.unlock();
1669 }
1670 
1672 {
1673  enableForcedSubtitles = false;
1674  if (!allowForcedSubtitles)
1675  return;
1676 
1677  osdLock.lock();
1678  if (osd)
1679  osd->EnableSubtitles(kDisplayAVSubtitle, true /*forced only*/);
1680  osdLock.unlock();
1681 }
1682 
1684 {
1685  if (decoder)
1686  return decoder->GetTrack(type);
1687  return -1;
1688 }
1689 
1691 {
1692  if (!decoder)
1693  return -1;
1694 
1695  int retval = decoder->ChangeTrack(type, dir);
1696  if (retval >= 0)
1697  {
1699  kOSDTimeout_Med);
1700  return retval;
1701  }
1702  return -1;
1703 }
1704 
1706 {
1707  if (!decoder || (dir < 0))
1708  return;
1709 
1713  {
1714  int tracktype = toTrackType(textDisplayMode);
1715  if (GetTrack(tracktype) < decoder->NextTrack(tracktype))
1716  {
1717  SetTrack(tracktype, decoder->NextTrack(tracktype));
1718  return;
1719  }
1720  }
1721  int nextmode = NextCaptionTrack(textDisplayMode);
1722  if ((nextmode == kDisplayTextSubtitle) ||
1723  (nextmode == kDisplayNUVTeletextCaptions) ||
1724  (nextmode == kDisplayNone))
1725  {
1727  if (nextmode != kDisplayNone)
1728  EnableCaptions(nextmode, true);
1729  }
1730  else
1731  {
1732  int tracktype = toTrackType(nextmode);
1733  int tracks = decoder->GetTrackCount(tracktype);
1734  if (tracks)
1735  {
1737  SetTrack(tracktype, 0);
1738  }
1739  }
1740 }
1741 
1743 {
1744  if (mode == kDisplayNone)
1745  return false;
1746  if (((mode == kDisplayTextSubtitle) && HasTextSubtitles()) ||
1747  (mode == kDisplayNUVTeletextCaptions))
1748  {
1749  return true;
1750  }
1751  if (!(mode == kDisplayTextSubtitle) &&
1753  {
1754  return true;
1755  }
1756  return false;
1757 }
1758 
1760 {
1761  // Text->TextStream->708->608->AVSubs->Teletext->NUV->None
1762  // NUV only offerred if PAL
1763  bool pal = (vbimode == VBIMode::PAL_TT);
1764  int nextmode = kDisplayNone;
1765 
1766  if (kDisplayTextSubtitle == mode)
1767  nextmode = kDisplayRawTextSubtitle;
1768  else if (kDisplayRawTextSubtitle == mode)
1769  nextmode = kDisplayCC708;
1770  else if (kDisplayCC708 == mode)
1771  nextmode = kDisplayCC608;
1772  else if (kDisplayCC608 == mode)
1773  nextmode = kDisplayAVSubtitle;
1774  else if (kDisplayAVSubtitle == mode)
1775  nextmode = kDisplayTeletextCaptions;
1776  else if (kDisplayTeletextCaptions == mode)
1777  nextmode = pal ? kDisplayNUVTeletextCaptions : kDisplayNone;
1778  else if ((kDisplayNUVTeletextCaptions == mode) && pal)
1779  nextmode = kDisplayNone;
1780  else if (kDisplayNone == mode)
1781  nextmode = kDisplayTextSubtitle;
1782 
1783  if (nextmode == kDisplayNone || HasCaptionTrack(nextmode))
1784  return nextmode;
1785 
1786  return NextCaptionTrack(nextmode);
1787 }
1788 
1790 {
1791  if (decoder)
1793  frame_interval = lround(1000000.0 * frame_period)
1794  / m_fpsMultiplier;
1796  avsync_predictor = 0;
1797  avsync_predictor_enabled = false;
1798 
1799  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetFrameInterval ps:%1 scan:%2")
1800  .arg(play_speed).arg(scan));
1801  if (play_speed < 1 || play_speed > 2 || refreshrate <= 0)
1802  return;
1803 
1805  refreshrate);
1806 }
1807 
1809 {
1810  avsync_avg = 0;
1812  avsync_predictor = 0;
1813  prevtc = 0;
1814  avsync_next = avsync_interval; // Frames till next sync check
1815  rtcbase = 0;
1816  prior_audiotimecode = 0;
1817  prior_videotimecode = 0;
1818  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + "A/V sync reset");
1819 }
1820 
1822 {
1823  videosync->Start();
1824 
1825  avsync_adjustment = 0;
1826 
1827  repeat_delay = 0;
1828 
1830 
1831  // Number of frames over which to average time divergence
1832  avsync_averaging=4;
1833  rtcbase = 0;
1834  prior_audiotimecode = 0;
1835  prior_videotimecode = 0;
1836 
1837  // Special averaging default of 60 for OpenMAX passthru
1838  QString device = gCoreContext->GetSetting("AudioOutputDevice","");
1839  int ac3pass = gCoreContext->GetNumSetting("AC3PassThru",-1);
1840  if (device == "OpenMAX:hdmi" && ac3pass == 1)
1841  avsync_averaging=60;
1842 
1843  // Allow override of averaging value
1844  avsync_averaging = gCoreContext->GetNumSetting("AVSyncAveraging", avsync_averaging); // Number of frames to average
1845  if (avsync_averaging < 4)
1846  avsync_averaging = 4;
1847  avsync_interval = avsync_averaging / max_diverge - 1; // Number of frames skip between sync checks
1848  if (avsync_interval < 0)
1849  avsync_interval = 0;
1850  avsync_next = avsync_interval; // Frames till next sync check
1851 
1852  if (!FlagIsSet(kVideoIsNull))
1853  {
1854  QString timing_type = videosync->getName();
1855 
1856  QString msg = QString("Video timing method: %1").arg(timing_type);
1857  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
1858  msg = QString("Display Refresh Rate: %1 Video Frame Rate: %2")
1859  .arg(1000000.0 / refreshrate, 0, 'f', 3)
1860  .arg(1000000.0 / frame_interval, 0, 'f', 3);
1861  LOG(VB_PLAYBACK, LOG_INFO, LOC + msg);
1862 
1864  1.0 / (video_frame_rate * static_cast<double>(play_speed)));
1865 
1866  // try to get preferential scheduling, but ignore if we fail to.
1867  myth_nice(-19);
1868  }
1869 }
1870 
1872 {
1873  int64_t currentaudiotime = 0;
1874  if (normal_speed)
1875  {
1876  currentaudiotime = audio.GetAudioTime();
1877  }
1878  return currentaudiotime;
1879 }
1880 
1881 void MythPlayer::AVSync(VideoFrame *buffer, bool limit_delay)
1882 {
1883  if (gCoreContext->GetBoolSetting("PlaybackAVSync2", false))
1884  {
1885  AVSync2(buffer);
1886  return;
1887  }
1888 
1889  int repeat_pict = 0;
1890  int64_t timecode = audio.GetAudioTime();
1891 
1892  if (buffer)
1893  {
1894  repeat_pict = buffer->repeat_pict;
1895  timecode = buffer->timecode;
1896  disp_timecode = buffer->disp_timecode;
1897  }
1898 
1899  float diverge = 0.0F;
1900  int frameDelay = m_double_framerate ? frame_interval / 2 : frame_interval;
1901  int vsync_delay_clock = 0;
1902  //int64_t currentaudiotime = 0;
1903 
1904  if (videoOutput->IsErrored())
1905  {
1906  LOG(VB_GENERAL, LOG_ERR, LOC +
1907  "AVSync: Unknown error in videoOutput, aborting playback.");
1908  SetErrored(tr("Failed to initialize A/V Sync"));
1909  return;
1910  }
1911 
1912  if (normal_speed && avsync_next==0)
1913  {
1914  diverge = (float)avsync_avg / (float)frame_interval;
1915  }
1916 
1917  if (avsync_next > 0)
1918  avsync_next--;
1919  else {
1920  int divisor = int(abs(diverge) - max_diverge - 1.0F);
1921  if (divisor < 1)
1922  divisor=1;
1923  avsync_next = avsync_interval/divisor;
1924  }
1925 
1926  FrameScanType ps = m_scan;
1927  if (kScan_Detect == m_scan || kScan_Ignore == m_scan)
1928  ps = kScan_Progressive;
1929 
1930  bool max_video_behind = diverge < -max_diverge;
1931  bool dropframe = false;
1932  QString dbg;
1933 
1935  {
1938  {
1939  int refreshperiodsinframe = avsync_predictor/refreshrate;
1940  avsync_predictor -= refreshrate * refreshperiodsinframe;
1941  }
1942  else
1943  {
1944  dropframe = !FlagIsSet(kMusicChoice);
1945  dbg = QString("A/V predict drop frame, refreshrate %1, avsync_predictor %2, diverge %3, ")
1946  .arg(refreshrate).arg(avsync_predictor).arg(diverge);
1947  }
1948  }
1949 
1950  if (max_video_behind)
1951  {
1952  dropframe = !FlagIsSet(kMusicChoice);
1953  // If video is way behind of audio, adjust for it...
1954  dbg = QString("Video is %1 frames behind audio (too slow), ")
1955  .arg(-diverge);
1956  }
1957 
1958  if (!dropframe && avsync_audiopaused)
1959  {
1960  avsync_audiopaused = false;
1961  audio.Pause(false);
1962  }
1963 
1964  if (!dropframe)
1965  {
1966  // PGB this was orignally in the calling methods
1967  // MythPlayer::DisplayNormalFrame and MythDVDPlayer::DisplayLastFrame
1968  // Moved here to reduce CPU usage since the OSD was being merged
1969  // into frames that were not being displayed, thereby causing
1970  // interruptions and slowdowns.
1971  osdLock.lock();
1972  videofiltersLock.lock();
1974  videofiltersLock.unlock();
1975  osdLock.unlock();
1976  }
1977 
1978  if (dropframe)
1979  {
1980  // Reset A/V Sync
1981  lastsync = true;
1982  //currentaudiotime = AVSyncGetAudiotime();
1983  LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg + "dropping frame to catch up.");
1984  if (max_video_behind)
1985  {
1986  audio.Pause(true);
1987  avsync_audiopaused = true;
1988  }
1989  }
1990  else if (!FlagIsSet(kVideoIsNull))
1991  {
1992  // if we get here, we're actually going to do video output
1993  osdLock.lock();
1994  videoOutput->PrepareFrame(buffer, ps, osd);
1995  osdLock.unlock();
1996  // Don't wait for sync if this is a secondary PBP otherwise
1997  // the primary PBP will become out of sync
1998  if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
1999  {
2000  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO,
2001  LOC + QString("AVSync waitforframe %1 %2 %3")
2002  .arg(frameDelay).arg(avsync_adjustment).arg(m_double_framerate));
2003  vsync_delay_clock = videosync->WaitForFrame(frameDelay, avsync_adjustment + repeat_delay);
2004  }
2005  else
2006  {
2007  vsync_delay_clock = 0;
2008  lastsync = true;
2009  }
2010  //currentaudiotime = AVSyncGetAudiotime();
2011  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + "AVSync show");
2012  videoOutput->Show(ps);
2013 
2014  if (videoOutput->IsErrored())
2015  {
2016  LOG(VB_GENERAL, LOG_ERR, LOC + "Error condition detected "
2017  "in videoOutput after Show(), aborting playback.");
2018  SetErrored(tr("Serious error detected in Video Output"));
2019  return;
2020  }
2021 
2022  if (m_double_framerate)
2023  {
2024  //second stage of deinterlacer processing
2025  ps = (kScan_Intr2ndField == ps) ?
2027  osdLock.lock();
2028  if (m_double_process && ps != kScan_Progressive)
2029  {
2030  videofiltersLock.lock();
2032  buffer, osd, videoFilters, pip_players, ps);
2033  videofiltersLock.unlock();
2034  }
2035 
2036  videoOutput->PrepareFrame(buffer, ps, osd);
2037  osdLock.unlock();
2038  // Display the second field
2039  if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
2040  {
2041  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO,
2042  LOC + QString("AVSync waitforframe %1 %2 %3")
2043  .arg(frameDelay).arg(avsync_adjustment).arg(m_double_framerate));
2044  vsync_delay_clock = videosync->WaitForFrame(frameDelay, avsync_adjustment);
2045  }
2046  videoOutput->Show(ps);
2047  }
2048 
2049  repeat_delay = frame_interval * repeat_pict * 0.5;
2050 
2051  if (repeat_delay)
2052  LOG(VB_TIMESTAMP, LOG_INFO, LOC +
2053  QString("A/V repeat_pict, adding %1 repeat delay")
2054  .arg(repeat_delay));
2055  }
2056  else
2057  {
2058  vsync_delay_clock = videosync->WaitForFrame(frameDelay, 0);
2059  //currentaudiotime = AVSyncGetAudiotime();
2060  }
2061 
2063  {
2064  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
2065  QString("A/V avsync_delay: %1, avsync_avg: %2")
2066  .arg(avsync_delay / 1000).arg(avsync_avg / 1000));
2067  }
2068 
2069  avsync_adjustment = 0;
2070 
2071  if (diverge > max_diverge)
2072  {
2073  // If audio is way behind of video, adjust for it...
2074  // by cutting the frame rate in half for the length of this frame
2076  lastsync = true;
2077  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2078  QString("Video is %1 frames ahead of audio,\n"
2079  "\t\t\tdoubling video frame interval to slow down.")
2080  .arg(diverge));
2081  }
2082 
2083  if (audio.HasAudioOut() && normal_speed)
2084  {
2085  // must be sampled here due to Show delays
2086  int64_t currentaudiotime = audio.GetAudioTime();
2087  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
2088  QString("A/V timecodes audio %1 video %2 frameinterval %3 "
2089  "avdel %4 avg %5 tcoffset %6 avp %7 avpen %8 avdc %9 "
2090  "diverge %10")
2091  .arg(currentaudiotime)
2092  .arg(timecode)
2093  .arg(frame_interval)
2094  .arg(timecode - currentaudiotime -
2095  (int)(vsync_delay_clock*audio.GetStretchFactor()+500)/1000)
2096  .arg(avsync_avg)
2097  .arg(tc_wrap[TC_AUDIO])
2098  .arg(avsync_predictor)
2100  .arg(vsync_delay_clock)
2101  .arg(diverge)
2102  );
2103  if (currentaudiotime != 0 && timecode != 0)
2104  { // currentaudiotime == 0 after a seek
2105  // The time at the start of this frame (ie, now) is given by
2106  // last->timecode
2107  if (prevtc != 0)
2108  {
2109  int delta = (int)((timecode - prevtc)/play_speed) -
2110  (frame_interval / 1000);
2111  // If timecode is off by a frame (dropped frame) wait to sync
2112  if (delta > frame_interval / 1200 &&
2113  delta < frame_interval / 1000 * 3 &&
2114  prevrp == 0)
2115  {
2116  // wait an extra frame interval
2117  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
2118  QString("A/V delay %1").arg(delta));
2120  // If we're duplicating a frame, it may be because
2121  // the container frame rate doesn't match the
2122  // stream frame rate. In this case, we increment
2123  // the fake frame counter so that avformat
2124  // timestamp-based seeking will work.
2125  if (!decoder->HasPositionMap())
2127  }
2128  }
2129  prevtc = timecode;
2130  prevrp = repeat_pict;
2131 
2132  // usec
2133  avsync_delay = (timecode - currentaudiotime) * 1000 -
2134  (int)(vsync_delay_clock*audio.GetStretchFactor());
2135 
2136  // prevents major jitter when pts resets during dvd title
2137  if (avsync_delay > 2000000 && limit_delay)
2138  avsync_delay = 90000;
2140 
2141  int avsync_used = avsync_avg;
2142  if (labs(avsync_used) > labs(avsync_delay))
2143  avsync_used = avsync_delay;
2144 
2145  /* If the audio time codes and video diverge, shift
2146  the video by one interlaced field (1/2 frame) */
2147  if (!lastsync)
2148  {
2149  if (avsync_used > refreshrate)
2150  {
2152  }
2153  else if (avsync_used < 0 - refreshrate)
2154  {
2156  }
2157  }
2158  else
2159  lastsync = false;
2160  }
2161  else
2162  {
2163  ResetAVSync();
2164  }
2165  }
2166  else
2167  {
2168  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
2169  QString("A/V no sync proc ns:%1").arg(normal_speed));
2170  }
2171 }
2172 
2173 void MythPlayer::WaitForTime(int64_t framedue)
2174 {
2175  int64_t unow = m_avTimer.nsecsElapsed() / 1000;
2176  int64_t delay = framedue - unow;
2177  if (delay > 0)
2178  QThread::usleep(delay);
2179 }
2180 
2181 #define AVSYNC_MAX_LATE 10000000
2183 {
2184  if (videoOutput->IsErrored())
2185  {
2186  LOG(VB_GENERAL, LOG_ERR, LOC +
2187  "AVSync: Unknown error in videoOutput, aborting playback.");
2188  SetErrored(tr("Failed to initialize A/V Sync"));
2189  return;
2190  }
2191  int64_t videotimecode = 0;
2192 
2193  bool dropframe = false;
2194  bool pause_audio = false;
2195  int64_t framedue = 0;
2196  int64_t audio_adjustment = 0;
2197  int64_t unow = 0;
2198  int64_t lateness = 0;
2199  int64_t playspeed1000 = (float)1000 / play_speed;
2200  bool reset = false;
2201 
2202  while (framedue == 0)
2203  {
2204  if (buffer)
2205  {
2206  videotimecode = buffer->timecode & 0x0000ffffffffffff;
2207  // Detect bogus timecodes from DVD and ignore them.
2208  if (videotimecode != buffer->timecode)
2209  videotimecode = maxtcval;
2210  }
2211 
2212  unow = m_avTimer.nsecsElapsed() / 1000;
2213 
2215  {
2216  framedue = unow + frame_interval;
2217  break;
2218  }
2219  // first time or after a seek - setup of rtcbase
2220  if (rtcbase == 0)
2221  {
2222  // cater for DVB radio
2223  if (videotimecode == 0)
2224  videotimecode = audio.GetAudioTime();;
2225  // On first frame we get nothing, so exit out.
2226  if (videotimecode == 0)
2227  return;
2228  rtcbase = unow - videotimecode * playspeed1000;
2229  maxtcval = 0;
2230  maxtcframes = 0;
2231  numdroppedframes = 0;
2232  m_timeOffsetBase = TranslatePositionFrameToMs(framesPlayed, false) - videotimecode;
2233  }
2234 
2235  if (videotimecode == 0)
2236  videotimecode = maxtcval + frame_interval/1000;
2237  int64_t tcincr = videotimecode - maxtcval;
2238  if (tcincr > 0 || tcincr < -100)
2239  {
2240  maxtcval = videotimecode;
2241  maxtcframes = 0;
2242  }
2243  else
2244  {
2245  maxtcframes++;
2246  videotimecode = maxtcval + maxtcframes * frame_interval/1000;
2247  }
2248 
2249  if (play_speed > 0.0F)
2250  framedue = rtcbase + videotimecode * playspeed1000;
2251  else
2252  framedue = unow + frame_interval / 2;
2253 
2254  // recalculate framesPlayed to conform to actual time code.
2255  framesPlayed = TranslatePositionMsToFrame(videotimecode + m_timeOffsetBase, false);
2257 
2258  lateness = unow - framedue;
2259  dropframe = false;
2260  if (lateness > 30000)
2261  dropframe = numdroppedframes < 10;
2262 
2263  if (lateness <= 30000 && prior_audiotimecode > 0
2264  && prior_videotimecode > 0)
2265  {
2266  // Get video in sync with audio
2267  audio_adjustment = prior_audiotimecode - prior_videotimecode;
2268  // If there is excess audio - throw it away.
2269  if (audio_adjustment < -200)
2270  {
2271  audio.Reset();
2272  audio_adjustment = 0;
2273  }
2274  int sign = audio_adjustment < 0 ? -1 : 1;
2275  int64_t fix_amount = audio_adjustment * sign;
2276  if (fix_amount > avsync2adjustms)
2277  fix_amount = avsync2adjustms;
2278  // Faster catch-up when off by more than 200 ms
2279  if (audio_adjustment * sign > 200)
2280  // fix the sync within 15 - 20 frames
2281  fix_amount = audio_adjustment * sign / 15;
2282  int64_t speedup1000 = (float)1000 * play_speed;
2283  rtcbase -= (int64_t)1000000 * fix_amount * sign / speedup1000;
2284  if (audio_adjustment * sign > 20 || (framesPlayed % 400 == 0))
2285  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2286  QString("AV Sync, audio ahead by %1 ms").arg(audio_adjustment));
2287  if (audio_adjustment > 200)
2288  pause_audio = true;
2289  }
2290  // sanity check - reset rtcbase if time codes have gone crazy.
2291  if ((lateness > AVSYNC_MAX_LATE || lateness < - AVSYNC_MAX_LATE))
2292  {
2293  framedue = 0;
2294  rtcbase = 0;
2295  if (reset)
2296  {
2297  LOG(VB_GENERAL, LOG_ERR, LOC +
2298  QString("Resetting AV Sync2 failed, lateness = %1").arg(lateness));
2299  SetErrored(tr("Failed to initialize A/V Sync"));
2300  return;
2301  }
2302  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2303  QString("Resetting AV Sync2, lateness = %1").arg(lateness));
2304  reset = true;
2305  }
2306  }
2307  prior_videotimecode = videotimecode;
2308  disp_timecode = videotimecode;
2309 
2311  avsync_avg = audio_adjustment * 1000;
2312 
2313  FrameScanType ps = m_scan;
2314  if (kScan_Detect == m_scan || kScan_Ignore == m_scan)
2315  ps = kScan_Progressive;
2316 
2317  if (buffer && !dropframe)
2318  {
2319  osdLock.lock();
2320  videofiltersLock.lock();
2322  videofiltersLock.unlock();
2323  osdLock.unlock();
2324  }
2325 
2326  if (!pause_audio && avsync_audiopaused)
2327  {
2328  avsync_audiopaused = false;
2329  audio.Pause(false);
2330  }
2331  if (pause_audio && !avsync_audiopaused)
2332  {
2333  avsync_audiopaused = true;
2334  audio.Pause(true);
2335  }
2336 
2337 
2338  if (dropframe)
2339  numdroppedframes++;
2340  else
2341  numdroppedframes = 0;
2342 
2343  if (dropframe)
2344  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2345  QString("dropping frame to catch up, lateness=%1 usec")
2346  .arg(lateness));
2347  else if (!FlagIsSet(kVideoIsNull) && buffer)
2348  {
2349  // if we get here, we're actually going to do video output
2350  osdLock.lock();
2351  videoOutput->PrepareFrame(buffer, ps, osd);
2352  osdLock.unlock();
2353  // Don't wait for sync if this is a secondary PBP otherwise
2354  // the primary PBP will become out of sync
2355  if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
2356  WaitForTime(framedue);
2357  // get time codes for calculating difference next time
2359  videoOutput->Show(ps);
2360  if (videoOutput->IsErrored())
2361  {
2362  LOG(VB_GENERAL, LOG_ERR, LOC + "Error condition detected "
2363  "in videoOutput after Show(), aborting playback.");
2364  SetErrored(tr("Serious error detected in Video Output"));
2365  return;
2366  }
2367  if (m_double_framerate)
2368  {
2369  //second stage of deinterlacer processing
2370  ps = (kScan_Intr2ndField == ps) ?
2372  osdLock.lock();
2373  if (m_double_process && ps != kScan_Progressive)
2374  {
2375  videofiltersLock.lock();
2377  buffer, osd, videoFilters, pip_players, ps);
2378  videofiltersLock.unlock();
2379  }
2380 
2381  videoOutput->PrepareFrame(buffer, ps, osd);
2382  osdLock.unlock();
2383  // Display the second field
2384  if (!player_ctx->IsPBP() || player_ctx->IsPrimaryPBP())
2385  {
2386  int64_t due = framedue + frame_interval / 2;
2387  WaitForTime(due);
2388  }
2389  videoOutput->Show(ps);
2390  }
2391  }
2392  else
2393  WaitForTime(framedue);
2394 
2395  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
2396  QString("A/V timecodes audio=%1 video=%2 frameinterval=%3 "
2397  "audioadj=%4 tcoffset=%5 unow=%6 udue=%7")
2398  .arg(prior_audiotimecode)
2399  .arg(prior_videotimecode)
2400  .arg(frame_interval)
2401  .arg(audio_adjustment)
2402  .arg(tc_wrap[TC_AUDIO])
2403  .arg(unow)
2404  .arg(framedue)
2405  );
2406 
2407 }
2408 
2410 {
2411  if (needNewPauseFrame)
2412  {
2414  {
2416  needNewPauseFrame = false;
2417 
2418  if (deleteMap.IsEditing())
2419  {
2420  osdLock.lock();
2421  if (osd)
2423  osdLock.unlock();
2424  }
2425  }
2426  else
2427  {
2428  decodeOneFrame = true;
2429  }
2430  }
2431 }
2432 
2434 {
2435  if (!videoOutput || ! videosync)
2436  return;
2437 
2438  if (videoOutput->IsErrored())
2439  {
2440  SetErrored(tr("Serious error detected in Video Output"));
2441  return;
2442  }
2443 
2444  // clear the buffering state
2445  SetBuffering(false);
2446 
2448  PreProcessNormalFrame(); // Allow interactiveTV to draw on pause frame
2449 
2450  osdLock.lock();
2451  videofiltersLock.lock();
2453  videofiltersLock.unlock();
2455  osdLock.unlock();
2457  videosync->Start();
2458 }
2459 
2460 void MythPlayer::SetBuffering(bool new_buffering)
2461 {
2462  if (!buffering && new_buffering)
2463  {
2464  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for video buffers...");
2465  buffering = true;
2466  buffering_start = QTime::currentTime();
2467  buffering_last_msg = QTime::currentTime();
2468  }
2469  else if (buffering && !new_buffering)
2470  {
2471  buffering = false;
2472  }
2473 }
2474 
2475 // For debugging playback set this to increase the timeout so that
2476 // playback does not fail if stepping through code.
2477 // Set PREBUFFERDEBUG to any value and you will get 30 minutes.
2478 static char *preBufferDebug = getenv("PREBUFFERDEBUG");
2479 
2481 {
2482  if (!videoOutput)
2483  return false;
2484 
2485  if (!(min_buffers ? (videoOutput->ValidVideoFrames() >= min_buffers) :
2486  (GetEof() != kEofStateNone) ||
2490  {
2491  SetBuffering(true);
2492 
2493  // This piece of code is to address the problem, when starting
2494  // Live TV, of jerking and stuttering. Without this code
2495  // that could go on forever, but is cured by a pause and play.
2496  // This code inserts a brief pause and play when the potential
2497  // for the jerking is detected.
2498 
2499  bool watchingTV = IsWatchingInprogress();
2500  if ( (livetv || watchingTV) && !FlagIsSet(kMusicChoice))
2501  {
2502  uint64_t frameCount = GetCurrentFrameCount();
2503  uint64_t framesLeft = frameCount - framesPlayed;
2504  uint64_t margin = (uint64_t) (video_frame_rate * 3);
2505  if (framesLeft < margin)
2506  {
2507  if (rtcbase)
2508  LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
2509  QString("Pause to allow live tv catch up. Position in sec. Current: %2, Total: %3")
2510  .arg(framesPlayed).arg(frameCount));
2511  audio.Pause(true);
2512  avsync_audiopaused = true;
2513  rtcbase = 0;
2514  }
2515  }
2516  usleep(frame_interval >> 3);
2517  int waited_for = buffering_start.msecsTo(QTime::currentTime());
2518  int last_msg = buffering_last_msg.msecsTo(QTime::currentTime());
2519  if (last_msg > 100 && !FlagIsSet(kMusicChoice))
2520  {
2521  if (++bufferingCounter == 10)
2522  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2523  "To see more buffering messages use -v playback");
2524  if (bufferingCounter >= 10)
2525  LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
2526  QString("Waited %1ms for video buffers %2")
2527  .arg(waited_for).arg(videoOutput->GetFrameStatus()));
2528  else
2529  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2530  QString("Waited %1ms for video buffers %2")
2531  .arg(waited_for).arg(videoOutput->GetFrameStatus()));
2532  buffering_last_msg = QTime::currentTime();
2534  && gCoreContext->GetBoolSetting("MusicChoiceEnabled", false))
2535  {
2537  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2538  "Music Choice program detected - disabling AV Sync.");
2539  avsync_audiopaused = false;
2540  audio.Pause(false);
2541  }
2542  if (waited_for > 7000 && audio.IsBufferAlmostFull()
2543  && !FlagIsSet(kMusicChoice))
2544  {
2545  // We are likely to enter this condition
2546  // if the audio buffer was too full during GetFrame in AVFD
2547  LOG(VB_GENERAL, LOG_NOTICE, LOC + "Resetting audio buffer");
2548  audio.Reset();
2549  }
2550  // Finish audio pause for sync after 1 second
2551  // in case of infrequent video frames (e.g. music choice)
2552  if (avsync_audiopaused && waited_for > 1000)
2553  {
2554  avsync_audiopaused = false;
2555  audio.Pause(false);
2556  }
2557  }
2558  int msecs = 500;
2559  if (preBufferDebug)
2560  msecs = 1800000;
2561  if ((waited_for > msecs /*500*/) && !videoOutput->EnoughFreeFrames())
2562  {
2563  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2564  "Timed out waiting for frames, and"
2565  "\n\t\t\tthere are not enough free frames. "
2566  "Discarding buffered frames.");
2567  // This call will result in some ugly frames, but allows us
2568  // to recover from serious problems if frames get leaked.
2569  DiscardVideoFrames(true);
2570  }
2571  msecs = 30000;
2572  if (preBufferDebug)
2573  msecs = 1800000;
2574  if (waited_for > msecs /*30000*/) // 30 seconds for internet streamed media
2575  {
2576  LOG(VB_GENERAL, LOG_ERR, LOC +
2577  "Waited too long for decoder to fill video buffers. Exiting..");
2578  SetErrored(tr("Video frame buffering failed too many times."));
2579  }
2580  if (normal_speed)
2581  videosync->Start();
2582  return false;
2583  }
2584 
2585  SetBuffering(false);
2586  return true;
2587 }
2588 
2590 {
2591  if (!frame)
2592  return;
2593 
2594  if (!qFuzzyCompare(frame->aspect, video_aspect) && frame->aspect > 0.0F)
2595  {
2596  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2597  QString("Video Aspect ratio changed from %1 to %2")
2598  .arg(video_aspect).arg(frame->aspect));
2599  video_aspect = frame->aspect;
2600  if (videoOutput)
2601  {
2603  ReinitOSD();
2604  }
2605  }
2606 }
2607 
2608 void MythPlayer::DisplayNormalFrame(bool check_prebuffer)
2609 {
2610  if (allpaused || (check_prebuffer && !PrebufferEnoughFrames()))
2611  return;
2612 
2613  // clear the buffering state
2614  SetBuffering(false);
2615 
2616  // If PiP then release the last shown frame to the decoding queue
2617  if (player_ctx->IsPIP())
2619 
2620  // retrieve the next frame
2623 
2624  // Check aspect ratio
2625  CheckAspectRatio(frame);
2626 
2628  UpdateFFRewSkip();
2629 
2630  // Player specific processing (dvd, bd, mheg etc)
2632 
2633  // handle scan type changes
2634  AutoDeint(frame);
2635  detect_letter_box->SwitchTo(frame);
2636 
2637  AVSync(frame, false);
2638 
2639  // If PiP then keep this frame for MythPlayer::GetCurrentFrame
2640  if (!player_ctx->IsPIP())
2642 }
2643 
2645 {
2646 #ifdef USING_MHEG
2647  // handle Interactive TV
2648  if (GetInteractiveTV())
2649  {
2650  osdLock.lock();
2651  itvLock.lock();
2652  if (osd && videoOutput->GetOSDPainter())
2653  {
2654  InteractiveScreen *window =
2656  if ((interactiveTV->ImageHasChanged() || !itvVisible) && window)
2657  {
2659  itvVisible = true;
2660  }
2661  }
2662  itvLock.unlock();
2663  osdLock.unlock();
2664  }
2665 #endif // USING_MHEG
2666 }
2667 
2669 {
2670  if (!videosync)
2671  return false;
2672  // At this point we may not have the correct frame rate.
2673  // Since interlaced is always at 25 or 30 fps, if the interval
2674  // is less than 30000 (33fps) it must be representing one
2675  // field and not one frame, so multiply by 2.
2676  int realfi = frame_interval;
2677  if (frame_interval < 30000)
2678  realfi = frame_interval * 2;
2679  return (realfi / 2.0 > videosync->getRefreshInterval() * 0.995);
2680 }
2681 
2683 {
2684  if (!output_jmeter)
2685  return;
2686  int rate = enable ? video_frame_rate :
2687  VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_ANY) ?
2688  (video_frame_rate * 4) : 0;
2689  output_jmeter->SetNumCycles(rate);
2690 }
2691 
2692 void MythPlayer::ForceDeinterlacer(const QString &overridefilter)
2693 {
2694  if (!videoOutput)
2695  return;
2696 
2697  bool normal = play_speed > 0.99F && play_speed < 1.01F && normal_speed;
2698  videofiltersLock.lock();
2699 
2700  bool hwset = decoder->GetMythCodecContext()->setDeinterlacer(true, overridefilter);
2701  if (hwset)
2702  {
2703  m_double_framerate = false;
2704  m_double_process = false;
2705  videoOutput->SetupDeinterlace(false);
2706  }
2707  else
2708  {
2710  videoOutput->SetupDeinterlace(true, overridefilter) &&
2713  }
2715  && (!CanSupportDoubleRate() || !normal))
2716  FallbackDeint();
2717 
2718  videofiltersLock.unlock();
2719 }
2720 
2722 {
2723  if (!FlagIsSet(kVideoIsNull) && !player_ctx->IsPIP())
2724  {
2725  QRect visible, total;
2726  float aspect, scaling;
2727 
2728  osdLock.lock();
2729  osd = new OSD(this, m_tv, videoOutput->GetOSDPainter());
2730 
2731  videoOutput->GetOSDBounds(total, visible, aspect, scaling, 1.0F);
2732  osd->Init(visible, aspect);
2735 
2736 #ifdef USING_MHEG
2737  if (GetInteractiveTV())
2738  {
2739  QMutexLocker locker(&itvLock);
2740  interactiveTV->Reinit(total, visible, aspect);
2741  }
2742 #endif // USING_MHEG
2743 
2744  // If there is a forced text subtitle track (which is possible
2745  // in e.g. a .mkv container), and forced subtitles are
2746  // allowed, then start playback with that subtitle track
2747  // selected. Otherwise, use the frontend settings to decide
2748  // which captions/subtitles (if any) to enable at startup.
2749  // TODO: modify the fix to #10735 to use this approach
2750  // instead.
2751  bool hasForcedTextTrack = false;
2752  uint forcedTrackNumber = 0;
2754  {
2755  uint numTextTracks = decoder->GetTrackCount(kTrackTypeRawText);
2756  for (uint i = 0; !hasForcedTextTrack && i < numTextTracks; ++i)
2757  {
2759  {
2760  hasForcedTextTrack = true;
2761  forcedTrackNumber = i;
2762  }
2763  }
2764  }
2765  if (hasForcedTextTrack)
2766  SetTrack(kTrackTypeRawText, forcedTrackNumber);
2767  else
2769 
2770  osdLock.unlock();
2771  }
2772 
2773  SetPlaying(true);
2774  ClearAfterSeek(false);
2775 
2776  avsync_delay = 0;
2777  avsync_avg = 0;
2778  avsync_next = avsync_interval; // Frames till next sync check
2779  refreshrate = 0;
2780  lastsync = false;
2781 
2784 
2785  float temp_speed = (play_speed == 0.0F) ? audio.GetStretchFactor() : play_speed;
2786  int fr_int = (1000000.0 / video_frame_rate / static_cast<double>(temp_speed));
2787  int rf_int = MythDisplay::GetDisplayInfo(fr_int).Rate();
2788 
2789  // Default to Interlaced playback to allocate the deinterlacer structures
2790  // Enable autodetection of interlaced/progressive from video stream
2791  // And initialoze m_scan_tracker to 2 which will immediately switch to
2792  // progressive if the first frame is progressive in AutoDeint().
2794  m_scan_locked = false;
2795  m_double_framerate = false;
2796  m_scan_tracker = 2;
2797 
2799  {
2801  }
2802  else if (FlagIsSet(kVideoIsNull))
2803  {
2805  }
2806  else if (videoOutput)
2807  {
2808  bool hwset = decoder->GetMythCodecContext()->setDeinterlacer(true);
2809  if (hwset)
2810  videoOutput->SetupDeinterlace(false);
2811  else
2812  {
2813  // Set up deinterlacing in the video output method
2815  (videoOutput->SetupDeinterlace(true) &&
2817 
2819  }
2821 
2822  // Make sure video sync can do it
2823  if (videosync != nullptr && m_double_framerate)
2824  {
2825  if (!CanSupportDoubleRate())
2826  {
2827  LOG(VB_GENERAL, LOG_ERR, LOC +
2828  "Video sync method can't support double framerate "
2829  "(refresh rate too low for 2x deint)");
2830  FallbackDeint();
2831  }
2832  }
2833  }
2834  if (!videosync)
2835  {
2836  videosync = new BusyWaitVideoSync(videoOutput, rf_int);
2837  }
2838 
2839  InitAVSync();
2840  videosync->Start();
2841 }
2842 
2844 {
2845  if (videoPaused || isDummy)
2846  {
2847  switch (player_ctx->GetPIPState())
2848  {
2849  case kPIPonTV:
2850  case kPBPRight:
2851  break;
2852  case kPIPOff:
2853  case kPIPStandAlone:
2854  case kPBPLeft: // PrimaryBPB
2855  usleep(frame_interval);
2856  break;
2857  }
2859  }
2860  else
2862 
2863  if (FlagIsSet(kVideoIsNull) && decoder)
2865  else if (decoder && decoder->GetEof() != kEofStateNone)
2866  ++framesPlayed;
2867  else
2869  return !IsErrored();
2870 }
2871 
2873 {
2874  osdLock.lock();
2875  vidExitLock.lock();
2876  delete osd;
2877  delete videosync;
2878  delete videoOutput;
2879  osd = nullptr;
2880  videosync = nullptr;
2881  videoOutput = nullptr;
2882  vidExitLock.unlock();
2883  osdLock.unlock();
2884 }
2885 
2886 bool MythPlayer::FastForward(float seconds)
2887 {
2888  if (!videoOutput)
2889  return false;
2890 
2891  if (fftime <= 0)
2892  {
2893  float current = ComputeSecs(framesPlayed, true);
2894  float dest = current + seconds;
2895  float length = ComputeSecs(totalFrames, true);
2896 
2897  if (dest > length)
2898  {
2899  int64_t pos = TranslatePositionMsToFrame(seconds * 1000, false);
2900  if (CalcMaxFFTime(pos) < 0)
2901  return true;
2902  // Reach end of recording, go to 1 or 3s before the end
2903  dest = (livetv || IsWatchingInprogress()) ? -3.0 : -1.0;
2904  }
2905  uint64_t target = FindFrame(dest, true);
2906  fftime = target - framesPlayed;
2907  }
2908  return fftime > CalcMaxFFTime(fftime, false);
2909 }
2910 
2911 bool MythPlayer::Rewind(float seconds)
2912 {
2913  if (!videoOutput)
2914  return false;
2915 
2916  if (rewindtime <= 0)
2917  {
2918  float current = ComputeSecs(framesPlayed, true);
2919  float dest = current - seconds;
2920  if (dest < 0)
2921  {
2922  int64_t pos = TranslatePositionMsToFrame(seconds * 1000, false);
2923  if (CalcRWTime(pos) < 0)
2924  return true;
2925  dest = 0;
2926  }
2927  uint64_t target = FindFrame(dest, true);
2928  rewindtime = framesPlayed - target;
2929  }
2930  return (uint64_t)rewindtime >= framesPlayed;
2931 }
2932 
2933 bool MythPlayer::JumpToFrame(uint64_t frame)
2934 {
2935  if (!videoOutput)
2936  return false;
2937 
2938  bool ret = false;
2939  fftime = rewindtime = 0;
2940  if (frame > framesPlayed)
2941  {
2942  fftime = frame - framesPlayed;
2943  ret = fftime > CalcMaxFFTime(fftime, false);
2944  }
2945  else if (frame < framesPlayed)
2946  {
2947  rewindtime = framesPlayed - frame;
2948  ret = fftime > CalcMaxFFTime(fftime, false);
2949  }
2950  return ret;
2951 }
2952 
2953 
2954 void MythPlayer::JumpChapter(int chapter)
2955 {
2956  if (jumpchapter == 0)
2957  jumpchapter = chapter;
2958 }
2959 
2960 void MythPlayer::ResetPlaying(bool resetframes)
2961 {
2962  ClearAfterSeek();
2963  ffrew_skip = 1;
2964  if (resetframes)
2966  if (decoder)
2967  {
2968  decoder->Reset(true, true, true);
2969  if (decoder->IsErrored())
2970  SetErrored("Unable to reset video decoder");
2971  }
2972 }
2973 
2975 {
2976  bool last = !(player_ctx->m_tvchain->HasNext());
2978 }
2979 
2981 {
2982  if (!IsReallyNearEnd())
2983  return;
2984 
2985  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchToProgram - start");
2986  bool discontinuity = false, newtype = false;
2987  int newid = -1;
2989  discontinuity, newtype, newid);
2990  if (!pginfo)
2991  return;
2992 
2993  bool newIsDummy = player_ctx->m_tvchain->GetInputType(newid) == "DUMMY";
2994 
2995  SetPlayingInfo(*pginfo);
2996  Pause();
2997  ChangeSpeed();
2998 
2999  if (newIsDummy)
3000  {
3001  OpenDummy();
3002  ResetPlaying();
3004  delete pginfo;
3005  return;
3006  }
3007 
3009  {
3010  // Restore original ringbuffer
3011  ICRingBuffer *ic = dynamic_cast< ICRingBuffer* >(player_ctx->m_buffer);
3012  if (ic) // should always be true
3013  player_ctx->m_buffer = ic->Take();
3014  delete ic;
3015  }
3016 
3019 
3020  if (!player_ctx->m_buffer->IsOpen())
3021  {
3022  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram's OpenFile failed " +
3023  QString("(input type: %1).")
3024  .arg(player_ctx->m_tvchain->GetInputType(newid)));
3025  LOG(VB_GENERAL, LOG_ERR, player_ctx->m_tvchain->toString());
3027  SetErrored(tr("Error opening switch program buffer"));
3028  delete pginfo;
3029  return;
3030  }
3031 
3032  if (GetEof() != kEofStateNone)
3033  {
3034  discontinuity = true;
3035  ResetCaptions();
3036  }
3037 
3038  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SwitchToProgram(void) "
3039  "discont: %1 newtype: %2 newid: %3 decoderEof: %4")
3040  .arg(discontinuity).arg(newtype).arg(newid).arg(GetEof()));
3041 
3042  if (discontinuity || newtype)
3043  {
3044  player_ctx->m_tvchain->SetProgram(*pginfo);
3045  if (decoder)
3046  decoder->SetProgramInfo(*pginfo);
3047 
3048  player_ctx->m_buffer->Reset(true);
3049  if (newtype)
3050  {
3051  if (OpenFile() < 0)
3052  SetErrored(tr("Error opening switch program file"));
3053  }
3054  else
3055  ResetPlaying();
3056  }
3057  else
3058  {
3060  if (decoder)
3061  {
3064  }
3065  }
3066  delete pginfo;
3067 
3068  if (IsErrored())
3069  {
3070  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram failed.");
3072  return;
3073  }
3074 
3076 
3077  // the bitrate is reset by player_ctx->m_buffer->OpenFile()...
3078  if (decoder)
3081 
3082  if (discontinuity || newtype)
3083  {
3084  CheckTVChain();
3085  forcePositionMapSync = true;
3086  }
3087 
3088  Play();
3089  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchToProgram - end");
3090 }
3091 
3092 // This is called from decoder thread. Set an indicator that will
3093 // be checked and actioned in the player thread.
3095 {
3096  LOG(VB_PLAYBACK, LOG_INFO, LOC + "FileChangedCallback");
3097  fileChanged = true;
3098 }
3099 
3100 // Called from the player thread.
3102 {
3103  fileChanged = false;
3104  LOG(VB_PLAYBACK, LOG_INFO, LOC + "FileChanged");
3105 
3106  Pause();
3107  ChangeSpeed();
3108  if (dynamic_cast<AvFormatDecoder *>(decoder))
3109  player_ctx->m_buffer->Reset(false, true);
3110  else
3111  player_ctx->m_buffer->Reset(false, true, true);
3113  Play();
3114 
3116 
3117  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3119  if (decoder)
3121  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3122 
3123  CheckTVChain();
3124  forcePositionMapSync = true;
3125 }
3126 
3127 
3128 
3129 
3131 {
3132  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToProgram - start");
3133  bool discontinuity = false, newtype = false;
3134  int newid = -1;
3135  long long nextpos = player_ctx->m_tvchain->GetJumpPos();
3137  discontinuity, newtype, newid);
3138  if (!pginfo)
3139  return;
3140 
3141  inJumpToProgramPause = true;
3142 
3143  bool newIsDummy = player_ctx->m_tvchain->GetInputType(newid) == "DUMMY";
3144  SetPlayingInfo(*pginfo);
3145 
3146  Pause();
3147  ChangeSpeed();
3148  ResetCaptions();
3149  player_ctx->m_tvchain->SetProgram(*pginfo);
3150  player_ctx->m_buffer->Reset(true);
3151 
3152  if (newIsDummy)
3153  {
3154  OpenDummy();
3155  ResetPlaying();
3157  delete pginfo;
3158  inJumpToProgramPause = false;
3159  return;
3160  }
3161 
3162  SendMythSystemPlayEvent("PLAY_CHANGED", pginfo);
3163 
3165  {
3166  // Restore original ringbuffer
3167  ICRingBuffer *ic = dynamic_cast< ICRingBuffer* >(player_ctx->m_buffer);
3168  if (ic) // should always be true
3169  player_ctx->m_buffer = ic->Take();
3170  delete ic;
3171  }
3172 
3175  QString subfn = player_ctx->m_buffer->GetSubtitleFilename();
3176  TVState desiredState = player_ctx->GetState();
3177  bool isInProgress = (desiredState == kState_WatchingRecording ||
3178  desiredState == kState_WatchingLiveTV);
3179  if (GetSubReader())
3180  GetSubReader()->LoadExternalSubtitles(subfn, isInProgress &&
3181  !subfn.isEmpty());
3182 
3183  if (!player_ctx->m_buffer->IsOpen())
3184  {
3185  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram's OpenFile failed " +
3186  QString("(input type: %1).")
3187  .arg(player_ctx->m_tvchain->GetInputType(newid)));
3188  LOG(VB_GENERAL, LOG_ERR, player_ctx->m_tvchain->toString());
3190  SetErrored(tr("Error opening jump program file buffer"));
3191  delete pginfo;
3192  inJumpToProgramPause = false;
3193  return;
3194  }
3195 
3196  bool wasDummy = isDummy;
3197  if (newtype || wasDummy)
3198  {
3199  if (OpenFile() < 0)
3200  SetErrored(tr("Error opening jump program file"));
3201  }
3202  else
3203  ResetPlaying();
3204 
3205  if (IsErrored() || !decoder)
3206  {
3207  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram failed.");
3208  if (!IsErrored())
3209  SetErrored(tr("Error reopening video decoder"));
3210  delete pginfo;
3211  inJumpToProgramPause = false;
3212  return;
3213  }
3214 
3216 
3217  // the bitrate is reset by player_ctx->m_buffer->OpenFile()...
3220 
3221  decoder->SetProgramInfo(*pginfo);
3222  delete pginfo;
3223 
3224  CheckTVChain();
3225  forcePositionMapSync = true;
3226  inJumpToProgramPause = false;
3227  Play();
3228  ChangeSpeed();
3229 
3230  // check that we aren't too close to the end of program.
3231  // and if so set it to 10s from the end if completed recordings
3232  // or 3s if live
3233  long long duration = player_ctx->m_tvchain->GetLengthAtCurPos();
3234  int maxpos = player_ctx->m_tvchain->HasNext() ? 10 : 3;
3235 
3236  if (nextpos > (duration - maxpos))
3237  {
3238  nextpos = duration - maxpos;
3239  if (nextpos < 0)
3240  {
3241  nextpos = 0;
3242  }
3243  }
3244  else if (nextpos < 0)
3245  {
3246  // it's a relative position to the end
3247  nextpos += duration;
3248  }
3249 
3250  // nextpos is the new position to use in seconds
3251  nextpos = TranslatePositionMsToFrame(nextpos * 1000, true);
3252 
3253  if (nextpos > 10)
3254  DoJumpToFrame(nextpos, kInaccuracyNone);
3255 
3257  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToProgram - end");
3258 }
3259 
3261 {
3262  if (OpenFile() < 0)
3263  {
3264  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to open video file.");
3265  return false;
3266  }
3267 
3268  framesPlayed = 0;
3269  framesPlayedExtra = 0;
3270  rewindtime = fftime = 0;
3272  jumpchapter = 0;
3274  bufferingCounter=0;
3275 
3276  if (!InitVideo())
3277  {
3278  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to initialize video.");
3279  audio.DeleteOutput();
3280  return false;
3281  }
3282 
3283  bool seek = bookmarkseek > 30;
3284  EventStart();
3285  DecoderStart(true);
3286  if (seek)
3287  InitialSeek();
3288  VideoStart();
3289 
3290  playerThread->setPriority(QThread::TimeCriticalPriority);
3291 #ifdef Q_OS_ANDROID
3292  setpriority(PRIO_PROCESS, playerThreadId, -20);
3293 #endif
3294  UnpauseDecoder();
3295  return !IsErrored();
3296 }
3297 
3299 {
3300  // TODO handle initial commskip and/or cutlist skip as well
3301  if (bookmarkseek > 30)
3302  {
3304  if (clearSavedPosition && !player_ctx->IsPIP())
3305  SetBookmark(true);
3306  }
3307 }
3308 
3309 
3311 {
3312  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StopPlaying - begin"));
3313  playerThread->setPriority(QThread::NormalPriority);
3314 #ifdef Q_OS_ANDROID
3315  setpriority(PRIO_PROCESS, playerThreadId, 0);
3316 #endif
3317 
3318  DecoderEnd();
3319  VideoEnd();
3320  AudioEnd();
3321 
3322  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StopPlaying - end"));
3323 }
3324 
3326 {
3327  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3328  {
3330  {
3331  // When initial playback gets underway, we override the ProgramInfo
3332  // flags such that future calls to GetBookmark() will consider only
3333  // an actual bookmark and not progstart or lastplaypos information.
3337  }
3338  }
3339  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3341 }
3342 
3344 {
3345  // Live TV program change
3346  if (fileChanged)
3347  FileChanged();
3348 
3349  // recreate the osd if a reinit was triggered by another thread
3350  if (reinit_osd)
3351  ReinitOSD();
3352 
3353  // reselect subtitle tracks if triggered by the decoder
3354  if (enableCaptions)
3355  SetCaptionsEnabled(true, false);
3356  if (disableCaptions)
3357  SetCaptionsEnabled(false, false);
3358 
3359  // enable/disable forced subtitles if signalled by the decoder
3364 
3365  // reset the scan (and hence deinterlacers) if triggered by the decoder
3366  if (resetScan != kScan_Ignore)
3368 
3369  // refresh the position map for an in-progress recording while editing
3371  {
3372  if (editUpdateTimer.elapsed() > 2000)
3373  {
3374  // N.B. the positionmap update and osd refresh are asynchronous
3375  forcePositionMapSync = true;
3376  osdLock.lock();
3378  osdLock.unlock();
3379  editUpdateTimer.start();
3380  }
3381  }
3382 
3383  // Refresh the programinfo in use status
3384  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3387  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3388 
3389  // Disable timestretch if we are too close to the end of the buffer
3390  if (ffrew_skip == 1 && (play_speed > 1.0F) && IsNearEnd())
3391  {
3392  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near end, Slowing down playback.");
3393  Play(1.0F, true, true);
3394  }
3395 
3397  {
3398  // Switch from the dummy recorder to the tuned program in livetv
3399  player_ctx->m_tvchain->JumpToNext(true, 0);
3400  JumpToProgram();
3401  }
3402  else if ((!allpaused || GetEof() != kEofStateNone) &&
3403  player_ctx->m_tvchain &&
3404  (decoder && !decoder->GetWaitForChange()))
3405  {
3406  // Switch to the next program in livetv
3408  SwitchToProgram();
3409  }
3410 
3411  // Jump to the next program in livetv
3413  {
3414  JumpToProgram();
3415  }
3416 
3417  // Change interactive stream if requested
3418  { QMutexLocker locker(&streamLock);
3419  if (!m_newStream.isEmpty())
3420  {
3421  QString stream = m_newStream;
3422  m_newStream.clear();
3423  locker.unlock();
3424  JumpToStream(stream);
3425  }}
3426 
3427  // Disable fastforward if we are too close to the end of the buffer
3428  if (ffrew_skip > 1 && (CalcMaxFFTime(100, false) < 100))
3429  {
3430  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near end, stopping fastforward.");
3431  Play(1.0F, true, true);
3432  }
3433 
3434  // Disable rewind if we are too close to the beginning of the buffer
3435  if (ffrew_skip < 0 && CalcRWTime(-ffrew_skip) >= 0 &&
3437  {
3438  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near start, stopping rewind.");
3439  float stretch = (ffrew_skip > 0) ? 1.0F : audio.GetStretchFactor();
3440  Play(stretch, true, true);
3441  }
3442 
3443  // Check for error
3445  {
3446  LOG(VB_GENERAL, LOG_ERR, LOC +
3447  "Unknown recorder error, exiting decoder");
3448  if (!IsErrored())
3449  SetErrored(tr("Irrecoverable recorder error"));
3450  killdecoder = true;
3451  return;
3452  }
3453 
3454  // Handle speed change
3455  if (play_speed != next_play_speed &&
3456  (!player_ctx->m_tvchain ||
3458  {
3459  ChangeSpeed();
3460  return;
3461  }
3462 
3463  // Check if we got a communication error, and if so pause playback
3465  {
3466  Pause();
3468  }
3469 
3470  // Handle end of file
3471  EofState eof = GetEof();
3472  if (HasReachedEof())
3473  {
3474 #ifdef USING_MHEG
3475  if (interactiveTV && interactiveTV->StreamStarted(false))
3476  {
3477  Pause();
3478  return;
3479  }
3480 #endif
3482  {
3483  LOG(VB_GENERAL, LOG_NOTICE, LOC + "LiveTV forcing JumpTo 1");
3484  player_ctx->m_tvchain->JumpToNext(true, 0);
3485  return;
3486  }
3487 
3488  bool videoDrained =
3490  bool audioDrained =
3491  !audio.GetAudioOutput() ||
3492  audio.IsPaused() ||
3494  if (eof != kEofStateDelayed || (videoDrained && audioDrained))
3495  {
3496  if (eof == kEofStateDelayed)
3497  LOG(VB_PLAYBACK, LOG_INFO,
3498  QString("waiting for no video frames %1")
3499  .arg(videoOutput->ValidVideoFrames()));
3500  LOG(VB_PLAYBACK, LOG_INFO,
3501  QString("HasReachedEof() at framesPlayed=%1 totalFrames=%2")
3502  .arg(framesPlayed).arg(GetCurrentFrameCount()));
3503  Pause();
3504  SetPlaying(false);
3505  return;
3506  }
3507  }
3508 
3509  // Handle rewind
3510  if (rewindtime > 0 && (ffrew_skip == 1 || ffrew_skip == 0))
3511  {
3513  if (rewindtime > 0)
3515  }
3516 
3517  // Handle fast forward
3518  if (fftime > 0 && (ffrew_skip == 1 || ffrew_skip == 0))
3519  {
3521  if (fftime > 0)
3522  {
3524  if (GetEof() != kEofStateNone)
3525  return;
3526  }
3527  }
3528 
3529  // Handle chapter jump
3530  if (jumpchapter != 0)
3532 
3533  // Handle commercial skipping
3534  if (commBreakMap.GetSkipCommercials() != 0 && (ffrew_skip == 1))
3535  {
3536  if (!commBreakMap.HasMap())
3537  {
3538  //: The commercials/adverts have not been flagged
3539  SetOSDStatus(tr("Not Flagged"), kOSDTimeout_Med);
3540  QString message = "COMMFLAG_REQUEST ";
3541  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3542  message += QString("%1").arg(player_ctx->m_playingInfo->GetChanID()) +
3544  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3545  gCoreContext->SendMessage(message);
3546  }
3547  else
3548  {
3549  QString msg;
3550  uint64_t jumpto = 0;
3551  uint64_t frameCount = GetCurrentFrameCount();
3552  // XXX CommBreakMap should use duration map not video_frame_rate
3553  bool jump = commBreakMap.DoSkipCommercials(jumpto, framesPlayed,
3555  frameCount, msg);
3556  if (!msg.isEmpty())
3558  if (jump)
3559  DoJumpToFrame(jumpto, kInaccuracyNone);
3560  }
3562  return;
3563  }
3564 
3565  // Handle automatic commercial skipping
3566  uint64_t jumpto = 0;
3567  if (deleteMap.IsEmpty() && (ffrew_skip == 1) &&
3569  commBreakMap.HasMap())
3570  {
3571  QString msg;
3572  uint64_t frameCount = GetCurrentFrameCount();
3573  // XXX CommBreakMap should use duration map not video_frame_rate
3574  bool jump = commBreakMap.AutoCommercialSkip(jumpto, framesPlayed,
3576  frameCount, msg);
3577  if (!msg.isEmpty())
3579  if (jump)
3580  DoJumpToFrame(jumpto, kInaccuracyNone);
3581  }
3582 
3583  // Handle cutlist skipping
3584  if (!allpaused && (ffrew_skip == 1) &&
3586  {
3587  if (jumpto == totalFrames)
3588  {
3589  if (!(endExitPrompt == 1 && !player_ctx->IsPIP() &&
3591  {
3593  }
3594  }
3595  else
3596  {
3597  DoJumpToFrame(jumpto, kInaccuracyNone);
3598  }
3599  }
3600 }
3601 
3603 {
3604  audio.DeleteOutput();
3605 }
3606 
3608 {
3609  decoderPauseLock.lock();
3611  {
3612  decoderPaused = true;
3613  decoderThreadPause.wakeAll();
3614  decoderPauseLock.unlock();
3615  return decoderPaused;
3616  }
3617 
3618  int tries = 0;
3619  pauseDecoder = true;
3620  while (decoderThread && !killdecoder && (tries++ < 100) &&
3621  !decoderThreadPause.wait(&decoderPauseLock, 100))
3622  {
3623  LOG(VB_GENERAL, LOG_WARNING, LOC + "Waited 100ms for decoder to pause");
3624  }
3625  pauseDecoder = false;
3626  decoderPauseLock.unlock();
3627  return decoderPaused;
3628 }
3629 
3631 {
3632  decoderPauseLock.lock();
3633 
3635  {
3636  decoderPaused = false;
3637  decoderThreadUnpause.wakeAll();
3638  decoderPauseLock.unlock();
3639  return;
3640  }
3641 
3642  if (!IsInStillFrame())
3643  {
3644  int tries = 0;
3645  unpauseDecoder = true;
3646  while (decoderThread && !killdecoder && (tries++ < 100) &&
3648  {
3649  LOG(VB_GENERAL, LOG_WARNING, LOC +
3650  "Waited 100ms for decoder to unpause");
3651  }
3652  unpauseDecoder = false;
3653  }
3654  decoderPauseLock.unlock();
3655 }
3656 
3657 void MythPlayer::DecoderStart(bool start_paused)
3658 {
3659  if (decoderThread)
3660  {
3661  if (decoderThread->isRunning())
3662  {
3663  LOG(VB_GENERAL, LOG_ERR, LOC + "Decoder thread already running");
3664  }
3665  delete decoderThread;
3666  }
3667 
3668  killdecoder = false;
3669  decoderPaused = start_paused;
3670  decoderThread = new DecoderThread(this, start_paused);
3671  if (decoderThread)
3672  decoderThread->start();
3673 }
3674 
3676 {
3677  PauseDecoder();
3678  SetPlaying(false);
3679  killdecoder = true;
3680  int tries = 0;
3681  while (decoderThread && !decoderThread->wait(100) && (tries++ < 50))
3682  LOG(VB_PLAYBACK, LOG_INFO, LOC +
3683  "Waited 100ms for decoder loop to stop");
3684 
3686  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to stop decoder loop.");
3687  else
3688  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exited decoder loop.");
3689  SetDecoder(nullptr);
3690 }
3691 
3693 {
3695  {
3696  if (pauseDecoder)
3697  PauseDecoder();
3698  if (unpauseDecoder)
3699  UnpauseDecoder();
3700  }
3701 }
3702 
3705 {
3707  return decoder ? decoder->GetEof() : kEofStateImmediate;
3708 
3709  if (!decoder_change_lock.tryLock(50))
3710  return kEofStateNone;
3711 
3713  decoder_change_lock.unlock();
3714  return eof;
3715 }
3716 
3718 {
3720  {
3721  if (decoder)
3722  decoder->SetEofState(eof);
3723  return;
3724  }
3725 
3726  if (!decoder_change_lock.tryLock(50))
3727  return;
3728 
3729  if (decoder)
3730  decoder->SetEofState(eof);
3731  decoder_change_lock.unlock();
3732 }
3734 
3735 void MythPlayer::DecoderLoop(bool pause)
3736 {
3737  if (pause)
3738  PauseDecoder();
3739 
3740  while (!killdecoder && !IsErrored())
3741  {
3743 
3745  {
3746  usleep(1000);
3747  continue;
3748  }
3749 
3751  {
3752  if (!decoder_change_lock.tryLock(1))
3753  continue;
3754  if (decoder)
3755  {
3756  forcePositionMapSync = false;
3758  }
3759  decoder_change_lock.unlock();
3760  }
3761 
3762  if (decoderSeek >= 0)
3763  {
3764  if (!decoder_change_lock.tryLock(1))
3765  continue;
3766  if (decoder)
3767  {
3768  decoderSeekLock.lock();
3769  if (((uint64_t)decoderSeek < framesPlayed) && decoder)
3771  else if (decoder)
3773  decoderSeek = -1;
3774  decoderSeekLock.unlock();
3775  }
3776  decoder_change_lock.unlock();
3777  }
3778 
3779  bool obey_eof = (GetEof() != kEofStateNone) &&
3780  !(player_ctx->m_tvchain && !allpaused);
3781  if (isDummy || ((decoderPaused || ffrew_skip == 0 || obey_eof) &&
3782  !decodeOneFrame))
3783  {
3784  usleep(1000);
3785  continue;
3786  }
3787 
3790 
3791  DecoderGetFrame(dt);
3792  decodeOneFrame = false;
3793  }
3794 
3795  // Clear any wait conditions
3797  decoderSeek = -1;
3798 }
3799 
3801 {
3802  if (!decoder)
3803  return false;
3804 
3805  if (ffrew_skip > 0)
3806  {
3807  long long delta = decoder->GetFramesRead() - framesPlayed;
3808  long long real_skip = CalcMaxFFTime(ffrew_skip - ffrew_adjust + delta) - delta;
3809  long long target_frame = decoder->GetFramesRead() + real_skip;
3810  if (real_skip >= 0)
3811  {
3812  decoder->DoFastForward(target_frame, false);
3813  }
3814  long long seek_frame = decoder->GetFramesRead();
3815  ffrew_adjust = seek_frame - target_frame;
3816  }
3817  else if (CalcRWTime(-ffrew_skip) >= 0)
3818  {
3820  }
3822 }
3823 
3825 {
3826  long long cur_frame = decoder->GetFramesPlayed();
3827  bool toBegin = -cur_frame > ffrew_skip + ffrew_adjust;
3828  long long real_skip = (toBegin) ? -cur_frame : ffrew_skip + ffrew_adjust;
3829  long long target_frame = cur_frame + real_skip;
3830  bool ret = decoder->DoRewind(target_frame, false);
3831  long long seek_frame = decoder->GetFramesPlayed();
3832  ffrew_adjust = target_frame - seek_frame;
3833  return ret;
3834 }
3835 
3836 bool MythPlayer::DecoderGetFrame(DecodeType decodetype, bool unsafe)
3837 {
3838  bool ret = false;
3839  if (!videoOutput)
3840  return false;
3841 
3842  // Wait for frames to be available for decoding onto
3843  int tries = 0;
3844  while (!unsafe &&
3846  {
3847  if (killdecoder)
3848  return false;
3849 
3850  if (++tries > 10)
3851  {
3852  if (++videobuf_retries >= 2000)
3853  {
3854  LOG(VB_GENERAL, LOG_ERR, LOC +
3855  "Decoder timed out waiting for free video buffers.");
3856  // We've tried for 20 seconds now, give up so that we don't
3857  // get stuck permanently in this state
3858  SetErrored("Decoder timed out waiting for free video buffers.");
3859  }
3860  return false;
3861  }
3862 
3863  usleep(1000);
3864  }
3865  videobuf_retries = 0;
3866 
3867  if (!decoder_change_lock.tryLock(5))
3868  return false;
3869  if (killdecoder || !decoder || IsErrored())
3870  {
3871  decoder_change_lock.unlock();
3872  return false;
3873  }
3874 
3875  if (ffrew_skip == 1 || decodeOneFrame)
3876  ret = decoder->GetFrame(decodetype);
3877  else if (ffrew_skip != 0)
3878  ret = DecoderGetFrameFFREW();
3879  decoder_change_lock.unlock();
3880  return ret;
3881 }
3882 
3884 {
3885  transcoding = value;
3886 
3887  if (decoder)
3888  decoder->SetTranscoding(value);
3889 }
3890 
3892 {
3894  {
3895  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot add PiP from another thread");
3896  return false;
3897  }
3898 
3899  if (pip_players.contains(pip))
3900  {
3901  LOG(VB_GENERAL, LOG_ERR, LOC + "PiPMap already contains PiP.");
3902  return false;
3903  }
3904 
3905  QList<PIPLocation> locs = pip_players.values();
3906  if (locs.contains(loc))
3907  {
3908  LOG(VB_GENERAL, LOG_ERR, LOC +"Already have a PiP at that location.");
3909  return false;
3910  }
3911 
3912  pip_players.insert(pip, loc);
3913  return true;
3914 }
3915 
3917 {
3919  return false;
3920 
3921  if (!pip_players.contains(pip))
3922  return false;
3923 
3924  pip_players.remove(pip);
3925  if (videoOutput)
3926  videoOutput->RemovePIP(pip);
3927  return true;
3928 }
3929 
3931 {
3933  return kPIP_END;
3934 
3935  if (pip_players.isEmpty())
3936  return pip_default_loc;
3937 
3938  // order of preference, could be stored in db if we want it configurable
3939  PIPLocation ols[] =
3941 
3942  for (size_t i = 0; i < sizeof(ols)/sizeof(PIPLocation); i++)
3943  {
3944  PIPMap::const_iterator it = pip_players.begin();
3945  for (; it != pip_players.end() && (*it != ols[i]); ++it);
3946 
3947  if (it == pip_players.end())
3948  return ols[i];
3949  }
3950 
3951  return kPIP_END;
3952 }
3953 
3954 int64_t MythPlayer::AdjustAudioTimecodeOffset(int64_t v, int newsync)
3955 {
3956  if ((newsync >= -1000) && (newsync <= 1000))
3957  tc_wrap[TC_AUDIO] = newsync;
3958  else
3959  tc_wrap[TC_AUDIO] += v;
3960  return tc_wrap[TC_AUDIO];
3961 }
3962 
3963 void MythPlayer::WrapTimecode(int64_t &timecode, TCTypes tc_type)
3964 {
3965  timecode += tc_wrap[tc_type];
3966 }
3967 
3968 bool MythPlayer::PrepareAudioSample(int64_t &timecode)
3969 {
3970  WrapTimecode(timecode, TC_AUDIO);
3971  return false;
3972 }
3973 
3988 void MythPlayer::SetWatched(bool forceWatched)
3989 {
3990  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
3991  if (!player_ctx->m_playingInfo)
3992  {
3993  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
3994  return;
3995  }
3996 
3997  uint64_t numFrames = GetCurrentFrameCount();
3998 
3999  // For recordings we want to ignore the post-roll and account for
4000  // in-progress recordings where totalFrames doesn't represent
4001  // the full length of the recording. For videos we can only rely on
4002  // totalFrames as duration metadata can be wrong
4006  {
4007 
4008  // If the recording is stopped early we need to use the recording end
4009  // time, not the programme end time
4010 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
4011  uint endtime;
4012  if (player_ctx->m_playingInfo->GetRecordingEndTime().toTime_t() <
4014  {
4015  endtime = player_ctx->m_playingInfo->GetRecordingEndTime().toTime_t();
4016  }
4017  else
4018  {
4019  endtime = player_ctx->m_playingInfo->GetScheduledEndTime().toTime_t();
4020  }
4021 
4022  numFrames = (long long)
4023  ((endtime -
4026 #else
4028  qint64 starttime = pi->GetRecordingStartTime().toSecsSinceEpoch();
4029  qint64 endactual = pi->GetRecordingEndTime().toSecsSinceEpoch();
4030  qint64 endsched = pi->GetScheduledEndTime().toSecsSinceEpoch();
4031  qint64 endtime = min(endactual, endsched);
4032  numFrames = (long long) ((endtime - starttime) * video_frame_rate);
4033 #endif
4034  }
4035 
4036  int offset = (int) round(0.14 * (numFrames / video_frame_rate));
4037 
4038  if (offset < 240)
4039  offset = 240; // 4 Minutes Min
4040  else if (offset > 720)
4041  offset = 720; // 12 Minutes Max
4042 
4043  if (forceWatched || framesPlayed > numFrames - (offset * video_frame_rate))
4044  {
4046  LOG(VB_GENERAL, LOG_INFO, LOC +
4047  QString("Marking recording as watched using offset %1 minutes")
4048  .arg(offset/60));
4049  }
4050 
4051  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4052 }
4053 
4055 {
4056  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4059  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4060 }
4061 
4063 {
4064  uint64_t bookmark = 0;
4065 
4068  bookmark = 0;
4069  else
4070  {
4071  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4072  if (const ProgramInfo *pi = player_ctx->m_playingInfo)
4073  {
4074  bookmark = pi->QueryBookmark();
4075  // Disable progstart if the program has a cutlist.
4076  if (bookmark == 0 && !pi->HasCutlist())
4077  bookmark = pi->QueryProgStart();
4078  if (bookmark == 0)
4079  bookmark = pi->QueryLastPlayPos();
4080  }
4081  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4082  }
4083 
4084  return bookmark;
4085 }
4086 
4088 {
4089  bool skip_changed;
4090 
4091  float temp_speed = (play_speed == 0.0F) ?
4093  if (play_speed >= 0.0F && play_speed <= 3.0F)
4094  {
4095  skip_changed = (ffrew_skip != 1);
4096  if (decoder)
4098  frame_interval = (int) (1000000.0 / video_frame_rate / static_cast<double>(temp_speed))
4099  / m_fpsMultiplier;
4100  ffrew_skip = (play_speed != 0.0F);
4101  }
4102  else
4103  {
4104  skip_changed = true;
4105  frame_interval = 200000;
4106  frame_interval = (fabs(play_speed) >= 3.0F) ? 133466 : frame_interval;
4107  frame_interval = (fabs(play_speed) >= 5.0F) ? 133466 : frame_interval;
4108  frame_interval = (fabs(play_speed) >= 8.0F) ? 250250 : frame_interval;
4109  frame_interval = (fabs(play_speed) >= 10.0F) ? 133466 : frame_interval;
4110  frame_interval = (fabs(play_speed) >= 16.0F) ? 187687 : frame_interval;
4111  frame_interval = (fabs(play_speed) >= 20.0F) ? 150150 : frame_interval;
4112  frame_interval = (fabs(play_speed) >= 30.0F) ? 133466 : frame_interval;
4113  frame_interval = (fabs(play_speed) >= 60.0F) ? 133466 : frame_interval;
4114  frame_interval = (fabs(play_speed) >= 120.0F) ? 133466 : frame_interval;
4115  frame_interval = (fabs(play_speed) >= 180.0F) ? 133466 : frame_interval;
4116  float ffw_fps = fabs(static_cast<double>(play_speed)) * video_frame_rate;
4117  float dis_fps = 1000000.0F / frame_interval;
4118  ffrew_skip = (int)ceil(ffw_fps / dis_fps);
4120  ffrew_adjust = 0;
4121  }
4122 
4123  return skip_changed;
4124 }
4125 
4127 {
4128  float last_speed = play_speed;
4131  rtcbase = 0;
4132 
4133  bool skip_changed = UpdateFFRewSkip();
4134 
4135  if (skip_changed && videoOutput)
4136  {
4138  if (play_speed != 0.0F && !(last_speed == 0.0F && ffrew_skip == 1))
4140  }
4141 
4142  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Play speed: " +
4143  QString("rate: %1 speed: %2 skip: %3 => new interval %4")
4144  .arg(video_frame_rate).arg(play_speed)
4145  .arg(ffrew_skip).arg(frame_interval));
4146 
4147  if (videoOutput && videosync)
4148  {
4149  // We need to tell it this for automatic deinterlacer settings
4151 
4152  // If using bob deinterlace, turn on or off if we
4153  // changed to or from synchronous playback speed.
4154  bool play_1 = play_speed > 0.99F && play_speed < 1.01F && normal_speed;
4155  bool inter = (kScan_Interlaced == m_scan ||
4157 
4158  videofiltersLock.lock();
4159  bool doublerate = m_double_framerate || decoder->GetMythCodecContext()->getDoubleRate();
4160  if (doublerate && !play_1)
4161  {
4162  bool hwdeint = decoder->GetMythCodecContext()->FallbackDeint();
4163  if (!hwdeint)
4165  }
4166  else if (!m_double_framerate && CanSupportDoubleRate() && play_1
4167  && (inter || decoder->GetMythCodecContext()->isDeinterlacing()))
4168  {
4169  videoOutput->SetupDeinterlace(false);
4170  bool hwdeint = decoder->GetMythCodecContext()->BestDeint();
4171  if (!hwdeint)
4173  }
4174  videofiltersLock.unlock();
4177  }
4178 
4179  if (normal_speed && audio.HasAudioOut())
4180  {
4183  }
4184 }
4185 
4186 bool MythPlayer::DoRewind(uint64_t frames, double inaccuracy)
4187 {
4189  return false;
4190 
4191  uint64_t number = frames + 1;
4192  uint64_t desiredFrame = (framesPlayed > number) ? framesPlayed - number : 0;
4193 
4194  limitKeyRepeat = false;
4195  if (desiredFrame < video_frame_rate)
4196  limitKeyRepeat = true;
4197 
4198  uint64_t seeksnap_wanted = UINT64_MAX;
4199  if (inaccuracy != kInaccuracyFull)
4200  seeksnap_wanted = frames * inaccuracy;
4201  WaitForSeek(desiredFrame, seeksnap_wanted);
4202  rewindtime = 0;
4203  ClearAfterSeek();
4204  return true;
4205 }
4206 
4207 bool MythPlayer::DoRewindSecs(float secs, double inaccuracy, bool use_cutlist)
4208 {
4209  float current = ComputeSecs(framesPlayed, use_cutlist);
4210  float target = current - secs;
4211  if (target < 0)
4212  target = 0;
4213  uint64_t targetFrame = FindFrame(target, use_cutlist);
4214  return DoRewind(framesPlayed - targetFrame, inaccuracy);
4215 }
4216 
4222 long long MythPlayer::CalcRWTime(long long rw) const
4223 {
4224  bool hasliveprev = (livetv && player_ctx->m_tvchain &&
4226 
4227  if (!hasliveprev || ((int64_t)framesPlayed >= rw))
4228  {
4229  return rw;
4230  }
4231 
4232  player_ctx->m_tvchain->JumpToNext(false, ((int64_t)framesPlayed - rw) / video_frame_rate);
4233 
4234  return -1;
4235 }
4236 
4241 long long MythPlayer::CalcMaxFFTime(long long ffframes, bool setjump) const
4242 {
4243  float maxtime = 1.0;
4244  bool islivetvcur = (livetv && player_ctx->m_tvchain &&
4246 
4247  if (livetv || IsWatchingInprogress())
4248  maxtime = 3.0;
4249 
4250  long long ret = ffframes;
4251  float ff = ComputeSecs(ffframes, true);
4252  float secsPlayed = ComputeSecs(framesPlayed, true);
4253  float secsWritten = ComputeSecs(totalFrames, true);
4254 
4255  limitKeyRepeat = false;
4256 
4257  if (livetv && !islivetvcur && player_ctx->m_tvchain)
4258  {
4259  // recording has completed, totalFrames will always be up to date
4260  if ((ffframes + framesPlayed > totalFrames) && setjump)
4261  {
4262  ret = -1;
4263  // Number of frames to be skipped is from the end of the current segment
4264  player_ctx->m_tvchain->JumpToNext(true, ((int64_t)totalFrames - (int64_t)framesPlayed - ffframes) / video_frame_rate);
4265  }
4266  }
4267  else if (islivetvcur || IsWatchingInprogress())
4268  {
4269  if ((ff + secsPlayed) > secsWritten)
4270  {
4271  // If we attempt to seek past the last known duration,
4272  // check for up to date data
4273  long long framesWritten = player_ctx->m_recorder->GetFramesWritten();
4274 
4275  secsWritten = ComputeSecs(framesWritten, true);
4276  }
4277 
4278  float behind = secsWritten - secsPlayed;
4279 
4280  if (behind < maxtime) // if we're close, do nothing
4281  ret = 0;
4282  else if (behind - ff <= maxtime)
4283  ret = TranslatePositionMsToFrame(1000 * (secsWritten - maxtime),
4284  true) - framesPlayed;
4285 
4286  if (behind < maxtime * 3)
4287  limitKeyRepeat = true;
4288  }
4289  else if (IsPaused())
4290  {
4291  uint64_t lastFrame = deleteMap.IsEmpty() ? totalFrames
4292  : deleteMap.GetLastFrame();
4293  if (framesPlayed + ffframes >= lastFrame)
4294  ret = lastFrame - 1 - framesPlayed;
4295  }
4296  else
4297  {
4298  float secsMax = secsWritten - 2.F * maxtime;
4299  if (secsMax <= 0.F)
4300  ret = 0;
4301  else if (secsMax < secsPlayed + ff)
4302  ret = TranslatePositionMsToFrame(1000 * secsMax, true)
4303  - framesPlayed;
4304  }
4305 
4306  return ret;
4307 }
4308 
4316 {
4317  if (!videoOutput || !decoder)
4318  return false;
4319 
4320  return player_ctx->m_buffer->IsNearEnd(
4322 }
4323 
4327 {
4328  uint64_t framesRead, framesLeft = 0;
4329 
4330  if (!player_ctx)
4331  return false;
4332 
4333  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4335  !decoder)
4336  {
4337  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4338  return false;
4339  }
4340  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4341 
4342  long long margin = (long long)(video_frame_rate * 2);
4343  margin = (long long) (margin * audio.GetStretchFactor());
4344  bool watchingTV = IsWatchingInprogress();
4345 
4346  framesRead = framesPlayed;
4347 
4348  if (!player_ctx->IsPIP() &&
4350  {
4351  if (framesRead >= deleteMap.GetLastFrame())
4352  return true;
4353  uint64_t frameCount = GetCurrentFrameCount();
4354  framesLeft = (frameCount > framesRead) ? frameCount - framesRead : 0;
4355  return (framesLeft < (uint64_t)margin);
4356  }
4357 
4358  if (!livetv && !watchingTV)
4359  return false;
4360 
4362  return false;
4363 
4364  if (player_ctx->m_recorder)
4365  {
4366  framesLeft =
4367  player_ctx->m_recorder->GetCachedFramesWritten() - framesRead;
4368 
4369  // if it looks like we are near end, get an updated GetFramesWritten()
4370  if (framesLeft < (uint64_t)margin)
4371  framesLeft = player_ctx->m_recorder->GetFramesWritten() - framesRead;
4372  }
4373 
4374  return (framesLeft < (uint64_t)margin);
4375 }
4376 
4377 bool MythPlayer::DoFastForward(uint64_t frames, double inaccuracy)
4378 {
4380  return false;
4381 
4382  uint64_t number = (frames ? frames - 1 : 0);
4383  uint64_t desiredFrame = framesPlayed + number;
4384 
4385  if (!deleteMap.IsEditing() && IsInDelete(desiredFrame))
4386  {
4387  uint64_t endcheck = deleteMap.GetLastFrame();
4388  if (desiredFrame > endcheck)
4389  desiredFrame = endcheck;
4390  }
4391 
4392  uint64_t seeksnap_wanted = UINT64_MAX;
4393  if (inaccuracy != kInaccuracyFull)
4394  seeksnap_wanted = frames * inaccuracy;
4395  WaitForSeek(desiredFrame, seeksnap_wanted);
4396  fftime = 0;
4397  ClearAfterSeek(false);
4398  return true;
4399 }
4400 
4401 bool MythPlayer::DoFastForwardSecs(float secs, double inaccuracy,
4402  bool use_cutlist)
4403 {
4404  float current = ComputeSecs(framesPlayed, use_cutlist);
4405  float target = current + secs;
4406  uint64_t targetFrame = FindFrame(target, use_cutlist);
4407  return DoFastForward(targetFrame - framesPlayed, inaccuracy);
4408 }
4409 
4410 void MythPlayer::DoJumpToFrame(uint64_t frame, double inaccuracy)
4411 {
4412  if (frame > framesPlayed)
4413  DoFastForward(frame - framesPlayed, inaccuracy);
4414  else if (frame <= framesPlayed)
4415  DoRewind(framesPlayed - frame, inaccuracy);
4416 }
4417 
4418 void MythPlayer::WaitForSeek(uint64_t frame, uint64_t seeksnap_wanted)
4419 {
4420  if (!decoder)
4421  return;
4422 
4424  decoder->SetSeekSnap(seeksnap_wanted);
4425 
4426  bool islivetvcur = (livetv && player_ctx->m_tvchain &&
4428 
4429  uint64_t max = GetCurrentFrameCount();
4430  if (islivetvcur || IsWatchingInprogress())
4431  {
4432  max = (uint64_t)player_ctx->m_recorder->GetFramesWritten();
4433  }
4434  if (frame >= max)
4435  frame = max - 1;
4436 
4437  decoderSeekLock.lock();
4438  decoderSeek = frame;
4439  decoderSeekLock.unlock();
4440 
4441  int count = 0;
4442  bool need_clear = false;
4443  while (decoderSeek >= 0)
4444  {
4445  usleep(50 * 1000);
4446 
4447  // provide some on screen feedback if seeking is slow
4448  count++;
4449  if (!(count % 3) && !hasFullPositionMap)
4450  {
4451  int num = count % 3;
4452  SetOSDMessage(tr("Searching") + QString().fill('.', num),
4455  need_clear = true;
4456  }
4457  }
4458  if (need_clear)
4459  {
4460  osdLock.lock();
4461  if (osd)
4462  osd->HideWindow("osd_message");
4463  osdLock.unlock();
4464  }
4465 }
4466 
4479 void MythPlayer::ClearAfterSeek(bool clearvideobuffers)
4480 {
4481  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("ClearAfterSeek(%1)")
4482  .arg(clearvideobuffers));
4483 
4484  if (clearvideobuffers && videoOutput)
4486 
4487  int64_t savedTC = tc_wrap[TC_AUDIO];
4488 
4489  for (int j = 0; j < TCTYPESMAX; j++)
4490  tc_wrap[j] = tc_lastval[j] = 0;
4491 
4492  tc_wrap[TC_AUDIO] = savedTC;
4493 
4494  audio.Reset();
4495  // Reenable (or re-disable) subtitles, which ultimately does
4496  // nothing except to call ResetCaptions() to erase any captions
4497  // currently on-screen. The key is that the erasing is done in
4498  // the UI thread, not the decoder thread.
4503  needNewPauseFrame = true;
4504  ResetAVSync();
4505 
4506  // Reset hardware deinterlacer
4508  if (ctx && ctx->isDeinterlacing())
4509  {
4510  QString currdeint = ctx->getDeinterlacerName();
4511  ctx->setDeinterlacer(false);
4512  ctx->setDeinterlacer(true, currdeint);
4513  }
4514 }
4515 
4516 void MythPlayer::SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
4517 {
4519  m_tv = tv;
4520  parentWidget = widget;
4521  player_ctx = ctx;
4522  livetv = ctx->m_tvchain;
4523 }
4524 
4526 {
4527  deleteMap.SetEditing(false);
4528 
4529  if (!hasFullPositionMap)
4530  {
4531  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot edit - no full position map");
4532  SetOSDStatus(tr("No Seektable"), kOSDTimeout_Med);
4533  return false;
4534  }
4535 
4536  if (deleteMap.IsFileEditing())
4537  return false;
4538 
4539  QMutexLocker locker(&osdLock);
4540  if (!osd)
4541  return false;
4542 
4544  int sample_rate = GetAudio()->GetSampleRate();
4545  m_audiograph.SetSampleRate(sample_rate);
4546  m_audiograph.SetSampleCount((unsigned)(sample_rate / video_frame_rate));
4548 
4550  tc_wrap[TC_AUDIO] = 0;
4551 
4553  pausedBeforeEdit = Pause();
4554  deleteMap.SetEditing(true);
4555  osd->DialogQuit();
4556  ResetCaptions();
4557  osd->HideAll();
4558 
4559  bool loadedAutoSave = deleteMap.LoadAutoSaveMap();
4560  if (loadedAutoSave)
4561  {
4562  SetOSDMessage(tr("Using previously auto-saved cuts"),
4564  }
4565 
4568  deleteMap.SetFileEditing(true);
4569  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4572  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4573  editUpdateTimer.start();
4574 
4575  return deleteMap.IsEditing();
4576 }
4577 
4585 void MythPlayer::DisableEdit(int howToSave)
4586 {
4587  QMutexLocker locker(&osdLock);
4588  if (!osd)
4589  return;
4590 
4591  deleteMap.SetEditing(false, osd);
4592  if (howToSave == 0)
4593  deleteMap.LoadMap();
4594  // Unconditionally save to remove temporary marks from the DB.
4595  if (howToSave >= 0)
4596  deleteMap.SaveMap();
4598  deleteMap.SetFileEditing(false);
4599  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
4602  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
4604  m_audiograph.Reset();
4607 
4608  if (!pausedBeforeEdit)
4610  else
4611  SetOSDStatus(tr("Paused"), kOSDTimeout_None);
4612 }
4613 
4614 bool MythPlayer::HandleProgramEditorActions(QStringList &actions)
4615 {
4616  bool handled = false;
4617  bool refresh = true;
4618  long long frame = GetFramesPlayed();
4619 
4620  for (int i = 0; i < actions.size() && !handled; i++)
4621  {
4622  QString action = actions[i];
4623  handled = true;
4624  float seekamount = deleteMap.GetSeekAmount();
4625  if (action == ACTION_LEFT)
4626  {
4627  if (seekamount == 0) // 1 frame
4629  else if (seekamount > 0)
4630  // Use fully-accurate seeks for less than 1 second.
4631  DoRewindSecs(seekamount, seekamount < 1.0F ? kInaccuracyNone :
4632  kInaccuracyEditor, false);
4633  else
4634  HandleArbSeek(false);
4635  }
4636  else if (action == ACTION_RIGHT)
4637  {
4638  if (seekamount == 0) // 1 frame
4640  else if (seekamount > 0)
4641  // Use fully-accurate seeks for less than 1 second.
4642  DoFastForwardSecs(seekamount, seekamount < 1.0F ? kInaccuracyNone :
4643  kInaccuracyEditor, false);
4644  else
4645  HandleArbSeek(true);
4646  }
4647  else if (action == ACTION_LOADCOMMSKIP)
4648  {
4649  if (commBreakMap.HasMap())
4650  {
4651  frm_dir_map_t map;
4652  commBreakMap.GetMap(map);
4654  }
4655  }
4656  else if (action == ACTION_PREVCUT)
4657  {
4658  float old_seekamount = deleteMap.GetSeekAmount();
4660  HandleArbSeek(false);
4661  deleteMap.SetSeekAmount(old_seekamount);
4662  }
4663  else if (action == ACTION_NEXTCUT)
4664  {
4665  float old_seekamount = deleteMap.GetSeekAmount();
4667  HandleArbSeek(true);
4668  deleteMap.SetSeekAmount(old_seekamount);
4669  }
4670 #define FFREW_MULTICOUNT 10.0F
4671  else if (action == ACTION_BIGJUMPREW)
4672  {
4673  if (seekamount == 0)
4675  else if (seekamount > 0)
4676  DoRewindSecs(seekamount * FFREW_MULTICOUNT,
4677  kInaccuracyEditor, false);
4678  else
4680  kInaccuracyNone, false);
4681  }
4682  else if (action == ACTION_BIGJUMPFWD)
4683  {
4684  if (seekamount == 0)
4686  else if (seekamount > 0)
4687  DoFastForwardSecs(seekamount * FFREW_MULTICOUNT,
4688  kInaccuracyEditor, false);
4689  else
4691  kInaccuracyNone, false);
4692  }
4693  else if (action == ACTION_SELECT)
4694  {
4695  deleteMap.NewCut(frame);
4696  SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
4697  refresh = true;
4698  }
4699  else if (action == "DELETE")
4700  {
4701  deleteMap.Delete(frame, tr("Delete"));
4702  refresh = true;
4703  }
4704  else if (action == "REVERT")
4705  {
4706  deleteMap.LoadMap(tr("Undo Changes"));
4707  refresh = true;
4708  }
4709  else if (action == "REVERTEXIT")
4710  {
4711  DisableEdit(0);
4712  refresh = false;
4713  }
4714  else if (action == ACTION_SAVEMAP)
4715  {
4716  deleteMap.SaveMap();
4717  refresh = true;
4718  }
4719  else if (action == "EDIT" || action == "SAVEEXIT")
4720  {
4721  DisableEdit(1);
4722  refresh = false;
4723  }
4724  else
4725  {
4726  QString undoMessage = deleteMap.GetUndoMessage();
4727  QString redoMessage = deleteMap.GetRedoMessage();
4728  handled = deleteMap.HandleAction(action, frame);
4729  if (handled && (action == "CUTTOBEGINNING" ||
4730  action == "CUTTOEND" || action == "NEWCUT"))
4731  {
4732  SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
4733  }
4734  else if (handled && action == "UNDO")
4735  {
4736  //: %1 is the undo message
4737  SetOSDMessage(tr("Undo - %1").arg(undoMessage),
4739  }
4740  else if (handled && action == "REDO")
4741  {
4742  //: %1 is the redo message
4743  SetOSDMessage(tr("Redo - %1").arg(redoMessage),
4745  }
4746  }
4747  }
4748 
4749  if (handled && refresh)
4750  {
4751  osdLock.lock();
4752  if (osd)
4753  {
4755  }
4756  osdLock.unlock();
4757  }
4758 
4759  return handled;
4760 }
4761 
4762 bool MythPlayer::IsInDelete(uint64_t frame)
4763 {
4764  return deleteMap.IsInDelete(frame);
4765 }
4766 
4767 uint64_t MythPlayer::GetNearestMark(uint64_t frame, bool right)
4768 {
4769  return deleteMap.GetNearestMark(frame, right);
4770 }
4771 
4772 bool MythPlayer::IsTemporaryMark(uint64_t frame)
4773 {
4774  return deleteMap.IsTemporaryMark(frame);
4775 }
4776 
4778 {
4779  return deleteMap.HasTemporaryMark();
4780 }
4781 
4783 {
4784  if (deleteMap.GetSeekAmount() == -2)
4785  {
4786  uint64_t framenum = deleteMap.GetNearestMark(framesPlayed, right);
4787  if (right && (framenum > framesPlayed))
4789  else if (!right && (framesPlayed > framenum))
4790  DoRewind(framesPlayed - framenum, kInaccuracyNone);
4791  }
4792  else
4793  {
4794  if (right)
4796  else
4798  }
4799 }
4800 
4802 {
4803  if (videoOutput)
4804  return videoOutput->GetAspectOverride();
4805  return kAspect_Off;
4806 }
4807 
4809 {
4810  if (videoOutput)
4811  return videoOutput->GetAdjustFill();
4812  return kAdjustFill_Off;
4813 }
4814 
4816 {
4817  if (videoOutput)
4818  {
4819  videoOutput->ToggleAspectOverride(aspectMode);
4820  ReinitOSD();
4821  }
4822 }
4823 
4825 {
4826  if (videoOutput)
4827  {
4829  videoOutput->ToggleAdjustFill(adjustfillMode);
4830  ReinitOSD();
4831  }
4832 }
4833 
4835 {
4836  if (videoOutput)
4837  {
4838  videoOutput->Zoom(direction);
4839  ReinitOSD();
4840  }
4841 }
4842 
4844 {
4845  if (videoOutput)
4846  {
4848  ReinitOSD();
4849  }
4850 }
4851 
4853 {
4854  if (videoOutput)
4856 }
4857 
4859 {
4860  if (videoOutput)
4862 }
4863 
4865 {
4866  if (videoOutput)
4867  return videoOutput->IsEmbedding();
4868  return false;
4869 }
4870 
4871 bool MythPlayer::GetScreenShot(int width, int height, QString filename)
4872 {
4873  if (videoOutput)
4874  return videoOutput->GetScreenShot(width, height, std::move(filename));
4875  return false;
4876 }
4877 
4879 {
4881 }
4882 
4898 char *MythPlayer::GetScreenGrab(int secondsin, int &bufflen,
4899  int &vw, int &vh, float &ar)
4900 {
4901  long long frameNum = (long long)(secondsin * video_frame_rate);
4902 
4903  return GetScreenGrabAtFrame(frameNum, false, bufflen, vw, vh, ar);
4904 }
4905 
4922 char *MythPlayer::GetScreenGrabAtFrame(uint64_t frameNum, bool absolute,
4923  int &bufflen, int &vw, int &vh,
4924  float &ar)
4925 {
4926  uint64_t number = 0;
4927  unsigned char *data = nullptr;
4928  unsigned char *outputbuf = nullptr;
4929  VideoFrame *frame = nullptr;
4930  AVFrame orig;
4931  AVFrame retbuf;
4932  MythAVCopy copyCtx;
4933  memset(&orig, 0, sizeof(AVFrame));
4934  memset(&retbuf, 0, sizeof(AVFrame));
4935 
4936  bufflen = 0;
4937  vw = vh = 0;
4938  ar = 0;
4939 
4940  if (OpenFile(0) < 0)
4941  {
4942  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not open file for preview.");
4943  return nullptr;
4944  }
4945 
4946  if ((video_dim.width() <= 0) || (video_dim.height() <= 0))
4947  {
4948  LOG(VB_PLAYBACK, LOG_ERR, LOC +
4949  QString("Video Resolution invalid %1x%2")
4950  .arg(video_dim.width()).arg(video_dim.height()));
4951 
4952  // This is probably an audio file, just return a grey frame.
4953  vw = 640;
4954  vh = 480;
4955  ar = 4.0F / 3.0F;
4956 
4957  bufflen = vw * vh * 4;
4958  outputbuf = new unsigned char[bufflen];
4959  memset(outputbuf, 0x3f, bufflen * sizeof(unsigned char));
4960  return (char*) outputbuf;
4961  }
4962 
4963  if (!InitVideo())
4964  {
4965  LOG(VB_GENERAL, LOG_ERR, LOC +
4966  "Unable to initialize video for screen grab.");
4967  return nullptr;
4968  }
4969 
4970  ClearAfterSeek();
4971  if (!decoderThread)
4972  DecoderStart(true /*start paused*/);
4973  SeekForScreenGrab(number, frameNum, absolute);
4974  int tries = 0;
4975  while (!videoOutput->ValidVideoFrames() && ((tries++) < 500))
4976  {
4977  decodeOneFrame = true;
4978  usleep(10000);
4979  if ((tries & 10) == 10)
4980  LOG(VB_PLAYBACK, LOG_INFO, LOC +
4981  "ScreenGrab: Waited 100ms for video frame");
4982  }
4983 
4984  if (!(frame = videoOutput->GetLastDecodedFrame()))
4985  {
4986  return nullptr;
4987  }
4988 
4989  while (true)
4990  {
4991  if (!(data = frame->buf))
4992  {
4993  break;
4994  }
4995 
4996  AVPictureFill(&orig, frame);
4997  float par = frame->aspect * video_dim.height() / video_dim.width();
4998  MythPictureDeinterlacer deinterlacer(AV_PIX_FMT_YUV420P,
4999  video_dim.width(), video_dim.height(),
5000  par);
5001  if (deinterlacer.DeinterlaceSingle(&orig, &orig) < 0)
5002  {
5003  break;
5004  }
5005 
5006  bufflen = video_dim.width() * video_dim.height() * 4;
5007  outputbuf = new unsigned char[bufflen];
5008  copyCtx.Copy(&retbuf, frame, outputbuf, AV_PIX_FMT_RGB32);
5009 
5010  vw = video_disp_dim.width();
5011  vh = video_disp_dim.height();
5012  ar = frame->aspect;
5013  break;
5014  }
5015  if (frame)
5016  {
5017  DiscardVideoFrame(frame);
5018  }
5019  return (char *)outputbuf;
5020 }
5021 
5022 void MythPlayer::SeekForScreenGrab(uint64_t &number, uint64_t frameNum,
5023  bool absolute)
5024 {
5025  number = frameNum;
5026  if (number >= totalFrames)
5027  {
5028  LOG(VB_PLAYBACK, LOG_ERR, LOC +
5029  "Screen grab requested for frame number beyond end of file.");
5030  number = totalFrames / 2;
5031  }
5032 
5033  if (!absolute && hasFullPositionMap)
5034  {
5036  // Use the bookmark if we should, otherwise make sure we aren't
5037  // in the cutlist or a commercial break
5038  if (bookmarkseek > 30)
5039  {
5040  number = bookmarkseek;
5041  }
5042  else
5043  {
5044  uint64_t oldnumber = number;
5045  deleteMap.LoadMap();
5047 
5048  bool started_in_break_map = false;
5049  while (commBreakMap.IsInCommBreak(number) ||
5050  IsInDelete(number))
5051  {
5052  started_in_break_map = true;
5053  number += (uint64_t) (30 * video_frame_rate);
5054  if (number >= totalFrames)
5055  {
5056  number = oldnumber;
5057  break;
5058  }
5059  }
5060 
5061  // Advance a few seconds from the end of the break
5062  if (started_in_break_map)
5063  {
5064  oldnumber = number;
5065  number += (long long) (10 * video_frame_rate);
5066  if (number >= totalFrames)
5067  number = oldnumber;
5068  }
5069  }
5070  }
5071 
5073  DoJumpToFrame(number, kInaccuracyNone);
5074 }
5075 
5084 {
5085  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
5088  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
5089 
5090  if (!decoderThread)
5091  DecoderStart(false);
5092 
5093  if (frameNumber >= 0)
5094  {
5095  DoJumpToFrame(frameNumber, kInaccuracyNone);
5096  ClearAfterSeek();
5097  }
5098 
5099  int tries = 0;
5100  while (!videoOutput->ValidVideoFrames() && ((tries++) < 100))
5101  {
5102  decodeOneFrame = true;
5103  usleep(10000);
5104  if ((tries & 10) == 10)
5105  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waited 100ms for video frame");
5106  }
5107 
5109  return videoOutput->GetLastShownFrame();
5110 }
5111 
5112 QString MythPlayer::GetEncodingType(void) const
5113 {
5114  if (decoder)
5116  return QString();
5117 }
5118 
5120 {
5121  infoMap["audiocodec"] = ff_codec_id_string(audio.GetCodec());
5122  infoMap["audiochannels"] = QString::number(audio.GetOrigChannels());
5123 
5124  int width = video_disp_dim.width();
5125  int height = video_disp_dim.height();
5126  infoMap["videocodec"] = GetEncodingType();
5127  if (decoder)
5128  infoMap["videocodecdesc"] = decoder->GetRawEncodingType();
5129  infoMap["videowidth"] = QString::number(width);
5130  infoMap["videoheight"] = QString::number(height);
5131  infoMap["videoframerate"] = QString::number(video_frame_rate, 'f', 2);
5132 
5133  if (width < 640)
5134  return;
5135 
5136  bool interlaced = is_interlaced(m_scan);
5137  if (width == 1920 || height == 1080 || height == 1088)
5138  infoMap["videodescrip"] = interlaced ? "HD_1080_I" : "HD_1080_P";
5139  else if ((width == 1280 || height == 720) && !interlaced)
5140  infoMap["videodescrip"] = "HD_720_P";
5141  else if (height >= 720)
5142  infoMap["videodescrip"] = "HD";
5143  else infoMap["videodescrip"] = "SD";
5144 }
5145 
5147 {
5148  if (decoder)
5149  return decoder->GetRawAudioState();
5150  return false;
5151 }
5152 
5153 QString MythPlayer::GetXDS(const QString &key) const
5154 {
5155  if (!decoder)
5156  return QString();
5157  return decoder->GetXDS(key);
5158 }
5159 
5160 void MythPlayer::InitForTranscode(bool copyaudio, bool copyvideo)
5161 {
5162  // Are these really needed?
5163  SetPlaying(true);
5164  keyframedist = 30;
5165 
5166  if (!InitVideo())
5167  {
5168  LOG(VB_GENERAL, LOG_ERR, LOC +
5169  "Unable to initialize video for transcode.");
5170  SetPlaying(false);
5171  return;
5172  }
5173 
5174  framesPlayed = 0;
5175  framesPlayedExtra = 0;
5176  ClearAfterSeek();
5177 
5178  if (copyvideo && decoder)
5179  decoder->SetRawVideoState(true);
5180  if (copyaudio && decoder)
5181  decoder->SetRawAudioState(true);
5182 
5183  if (decoder)
5184  {
5185  decoder->SetSeekSnap(0);
5186  }
5187 }
5188 
5190  int &did_ff, bool &is_key, bool honorCutList)
5191 {
5192  player_ctx->LockPlayingInfo(__FILE__, __LINE__);
5195  player_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
5196 
5197  int64_t lastDecodedFrameNumber =
5199 
5200  if ((lastDecodedFrameNumber == 0) && honorCutList)
5202 
5203  if (!decoderThread)
5204  DecoderStart(true/*start paused*/);
5205 
5206  if (!decoder)
5207  return false;
5208 
5209  if (!decoder->GetFrame(kDecodeAV))
5210  return false;
5211 
5212  if (GetEof() != kEofStateNone)
5213  return false;
5214 
5215  if (honorCutList && !deleteMap.IsEmpty())
5216  {
5217  if (totalFrames && lastDecodedFrameNumber >= (int64_t)totalFrames)
5218  return false;
5219 
5220  uint64_t jumpto = 0;
5221  if (deleteMap.TrackerWantsToJump(lastDecodedFrameNumber, jumpto))
5222  {
5223  LOG(VB_GENERAL, LOG_INFO, LOC +
5224  QString("Fast-Forwarding from %1 to %2")
5225  .arg(lastDecodedFrameNumber).arg(jumpto));
5226  if (jumpto >= totalFrames)
5227  {
5229  return false;
5230  }
5231 
5232  // For 0.25, move this to DoJumpToFrame(jumpto)
5233  WaitForSeek(jumpto, 0);
5235  ClearAfterSeek();
5237  did_ff = 1;
5238  }
5239  }
5240  if (GetEof() != kEofStateNone)
5241  return false;
5242  is_key = decoder->IsLastFrameKey();
5243 
5244  videofiltersLock.lock();
5245  if (videoFilters)
5246  {
5247  FrameScanType ps = m_scan;
5248  if (kScan_Detect == m_scan || kScan_Ignore == m_scan)
5249  ps = kScan_Progressive;
5250 
5252  }
5253  videofiltersLock.unlock();
5254 
5255  return true;
5256 }
5257 
5258 long MythPlayer::UpdateStoredFrameNum(long curFrameNum)
5259 {
5260  if (decoder)
5261  return decoder->UpdateStoredFrameNum(curFrameNum);
5262  return 0;
5263 }
5264 
5265 void MythPlayer::SetCutList(const frm_dir_map_t &newCutList)
5266 {
5267  deleteMap.SetMap(newCutList);
5268 }
5269 
5271  bool writevideo, long timecodeOffset)
5272 {
5273  if (!decoder)
5274  return false;
5275  if (writevideo && !decoder->GetRawVideoState())
5276  writevideo = false;
5277  decoder->WriteStoredData(outRingBuffer, writevideo, timecodeOffset);
5278  return writevideo;
5279 }
5280 
5282 {
5283  commBreakMap.SetMap(newMap, framesPlayed);
5284  forcePositionMapSync = true;
5285 }
5286 
5288 {
5289  double spos = 0.0;
5290 
5291  if (livetv || IsWatchingInprogress())
5292  {
5293  spos = 1000.0 * framesPlayed / player_ctx->m_recorder->GetFramesWritten();
5294  }
5295  else if (totalFrames)
5296  {
5297  spos = 1000.0 * framesPlayed / totalFrames;
5298  }
5299 
5300  return((int)spos);
5301 }
5302 
5304 {
5305  QString samplerate = RingBuffer::BitrateToString(audio.GetSampleRate(),
5306  true);
5307  infoMap.insert("samplerate", samplerate);
5308  infoMap.insert("filename", player_ctx->m_buffer->GetSafeFilename());
5309  infoMap.insert("decoderrate", player_ctx->m_buffer->GetDecoderRate());
5310  infoMap.insert("storagerate", player_ctx->m_buffer->GetStorageRate());
5311  infoMap.insert("bufferavail", player_ctx->m_buffer->GetAvailableBuffer());
5312  infoMap.insert("buffersize",
5313  QString::number(player_ctx->m_buffer->GetBufferSize() >> 20));
5314  if (gCoreContext->GetBoolSetting("PlaybackAVSync2", false))
5315  {
5316  int avsync =