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