MythTV  master
mythplayer.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 #undef HAVE_AV_CONFIG_H
4 
5 // C++ headers
6 #include <algorithm>
7 #include <cassert>
8 #include <cmath> // for fabs, ceil, round, signbit
9 #include <cstdint>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <unistd.h>
13 using namespace std;
14 
15 // Qt headers
16 #include <QCoreApplication>
17 #include <QDir>
18 #include <QHash> // for QHash
19 #include <QMap> // for QMap<>::iterator, etc
20 #include <QThread> // for QThread, etc
21 #include <QtCore/qnumeric.h> // for qIsNaN
22 #include <utility>
23 
24 // MythTV headers
25 #include "mthread.h"
26 #include "mythconfig.h"
27 #include "mythplayer.h"
28 #include "DetectLetterbox.h"
29 #include "audioplayer.h"
30 #include "interactivescreen.h"
31 #include "programinfo.h"
32 #include "mythcorecontext.h"
33 #include "livetvchain.h"
34 #include "decoderbase.h"
35 #include "nuppeldecoder.h"
36 #include "avformatdecoder.h"
37 #include "dummydecoder.h"
38 #include "tv_play.h"
39 #include "interactivetv.h"
40 #include "mythsystemevent.h"
41 #include "mythlogging.h"
42 #include "mythmiscutil.h"
44 #include "audiooutput.h"
45 #include "cardutil.h"
46 #include "mythavutil.h"
47 #include "jitterometer.h"
48 #include "mythtimer.h"
49 #include "mythuiactions.h"
50 #include "io/mythmediabuffer.h"
51 #include "tv_actions.h"
52 #include "mythcodeccontext.h"
53 
54 // MythUI headers
55 #include <mythmainwindow.h>
56 
57 extern "C" {
58 #include "libavcodec/avcodec.h"
59 }
60 
61 #include "remoteencoder.h"
62 
63 #if ! HAVE_ROUND
64 #define round(x) ((int) ((x) + 0.5))
65 #endif
66 
67 static unsigned dbg_ident(const MythPlayer* /*player*/);
68 
69 #define LOC QString("Player(%1): ").arg(dbg_ident(this),0,36)
70 #define LOC_DEC QString("Player(%1): ").arg(dbg_ident(m_mp),0,36)
71 
74 
75 // Exact frame seeking, no inaccuracy allowed.
76 const double MythPlayer::kInaccuracyNone = 0;
77 
78 // By default, when seeking, snap to a keyframe if the keyframe's
79 // distance from the target frame is less than 10% of the total seek
80 // distance.
81 const double MythPlayer::kInaccuracyDefault = 0.1;
82 
83 // Allow greater inaccuracy (50%) in the cutlist editor (unless the
84 // editor seek distance is set to 1 frame or 1 keyframe).
85 const double MythPlayer::kInaccuracyEditor = 0.5;
86 
87 // Any negative value means completely inexact, i.e. seek to the
88 // keyframe that is closest to the target.
89 const double MythPlayer::kInaccuracyFull = -1.0;
90 
92 {
93  RunProlog();
94  LOG(VB_PLAYBACK, LOG_INFO, LOC_DEC + "Decoder thread starting.");
95  if (m_mp)
96  m_mp->DecoderLoop(m_startPaused);
97  LOG(VB_PLAYBACK, LOG_INFO, LOC_DEC + "Decoder thread exiting.");
98  RunEpilog();
99 }
100 
101 static int toCaptionType(int type)
102 {
103  if (kTrackTypeCC608 == type) return kDisplayCC608;
104  if (kTrackTypeCC708 == type) return kDisplayCC708;
109  return 0;
110 }
111 
112 static int toTrackType(int type)
113 {
114  if (kDisplayCC608 == type) return kTrackTypeCC608;
115  if (kDisplayCC708 == type) return kTrackTypeCC708;
120  return kTrackTypeUnknown;
121 }
122 
123 MythMultiLocker::MythMultiLocker(std::initializer_list<QMutex*> Locks)
124  : m_locks(Locks)
125 {
126  Relock();
127 }
128 
130 {
131  Unlock();
132 }
133 
135 {
136  for (QVector<QMutex*>::const_reverse_iterator it = m_locks.crbegin(); it != m_locks.crend(); ++it)
137  if (*it)
138  (*it)->unlock();
139 }
140 
142 {
143  foreach (auto lock, m_locks)
144  if (lock)
145  lock->lock();
146 }
147 
149  : m_playerFlags(flags),
150  m_display((flags & kVideoIsNull) ? nullptr : MythDisplay::AcquireRelease()),
151  // CC608/708
152  m_cc608(this), m_cc708(this),
153  // Audio
154  m_audio(this, (flags & kAudioMuted) != 0),
155  // Debugging variables
156  m_outputJmeter(new Jitterometer(LOC))
157 {
158  m_playerThread = QThread::currentThread();
159 #ifdef Q_OS_ANDROID
160  m_playerThreadId = gettid();
161 #endif
162  // Playback (output) zoom control
164 
167  m_itvEnabled = gCoreContext->GetBoolSetting("EnableMHEG", false);
168  m_clearSavedPosition = gCoreContext->GetNumSetting("ClearSavedPosition", 1);
169  m_endExitPrompt = gCoreContext->GetNumSetting("EndOfRecordingExitPrompt");
171 
172  // Get VBI page number
173  QString mypage = gCoreContext->GetSetting("VBIpageNr", "888");
174  bool valid = false;
175  uint tmp = mypage.toInt(&valid, 16);
176  m_ttPageNum = (valid) ? tmp : m_ttPageNum;
178  m_avTimer.start();
179 }
180 
182 {
183  // NB the interactiveTV thread is a client of OSD so must be deleted
184  // before locking and deleting the OSD
185  {
186  QMutexLocker lk0(&m_itvLock);
187  delete m_interactiveTV;
188  m_interactiveTV = nullptr;
189  }
190 
192 
193  delete m_osd;
194  m_osd = nullptr;
195 
196  SetDecoder(nullptr);
197 
198  delete m_decoderThread;
199  m_decoderThread = nullptr;
200 
201  delete m_videoOutput;
202  m_videoOutput = nullptr;
203 
204  delete m_outputJmeter;
205  m_outputJmeter = nullptr;
206 
207  delete m_detectLetterBox;
208  m_detectLetterBox = nullptr;
209 
210  if (m_display)
212 }
213 
215 {
216  m_watchingRecording = mode;
217  if (m_decoder)
219 }
220 
222 {
225 }
226 
228 {
229  m_bufferPauseLock.lock();
230  if (m_playerCtx->m_buffer)
231  {
234  }
235  m_bufferPaused = true;
236  m_bufferPauseLock.unlock();
237 }
238 
240 {
241  m_bufferPauseLock.lock();
242  if (m_playerCtx->m_buffer)
244  m_bufferPaused = false;
245  m_bufferPauseLock.unlock();
246 }
247 
249 {
250  while (!m_pauseLock.tryLock(100))
251  {
252  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waited 100ms to get pause lock.");
254  }
255  bool already_paused = m_allPaused;
256  if (already_paused)
257  {
258  m_pauseLock.unlock();
259  return already_paused;
260  }
261  m_nextPlaySpeed = 0.0;
262  m_nextNormalSpeed = false;
263  PauseVideo();
264  m_audio.Pause(true);
265  PauseDecoder();
266  PauseBuffer();
267  if (!m_decoderPaused)
268  PauseDecoder(); // Retry in case audio only stream
270  {
273  else if (m_videoOutput && !FlagIsSet(kVideoIsNull))
275  }
276  m_pauseLock.unlock();
277  return already_paused;
278 }
279 
280 bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio)
281 {
282  m_pauseLock.lock();
283  LOG(VB_PLAYBACK, LOG_INFO, LOC +
284  QString("Play(%1, normal %2, unpause audio %3)")
285  .arg(speed,5,'f',1).arg(normal).arg(unpauseaudio));
286 
287  if (m_deleteMap.IsEditing())
288  {
289  LOG(VB_GENERAL, LOG_ERR, LOC + "Ignoring Play(), in edit mode.");
290  m_pauseLock.unlock();
291  return false;
292  }
293  m_rtcBase = 0;
296  m_lastFix = 0.0;
298  UnpauseBuffer();
299  UnpauseDecoder();
300  if (unpauseaudio)
301  m_audio.Pause(false);
302  UnpauseVideo();
303  m_allPaused = false;
304  m_nextPlaySpeed = speed;
305  m_nextNormalSpeed = normal;
306  m_pauseLock.unlock();
307  return true;
308 }
309 
311 {
312  m_videoPauseLock.lock();
313  m_needNewPauseFrame = true;
314  m_videoPaused = true;
315  m_videoPauseLock.unlock();
316 }
317 
319 {
320  m_videoPauseLock.lock();
321  m_videoPaused = false;
322  m_videoPauseLock.unlock();
323 }
324 
326 {
328  if (!m_playerCtx)
329  return;
330 
331  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
332  m_playerCtx->SetPlayingInfo(&pginfo);
333  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
334 }
335 
336 void MythPlayer::SetPlaying(bool is_playing)
337 {
338  QMutexLocker locker(&m_playingLock);
339 
340  m_playing = is_playing;
341 
342  m_playingWaitCond.wakeAll();
343 }
344 
345 bool MythPlayer::IsPlaying(uint wait_in_msec, bool wait_for) const
346 {
347  QMutexLocker locker(&m_playingLock);
348 
349  if (!wait_in_msec)
350  return m_playing;
351 
352  MythTimer t;
353  t.start();
354 
355  while ((wait_for != m_playing) && ((uint)t.elapsed() < wait_in_msec))
356  {
357  m_playingWaitCond.wait(
358  &m_playingLock, max(0,(int)wait_in_msec - t.elapsed()));
359  }
360 
361  return m_playing;
362 }
363 
365 {
366  if (!m_playerCtx)
367  return false;
368 
369  PIPState pipState = m_playerCtx->GetPIPState();
370 
371  if (!m_decoder)
372  {
373  LOG(VB_GENERAL, LOG_ERR, LOC +
374  "Cannot create a video renderer without a decoder.");
375  return false;
376  }
377 
384 
385  if (!m_videoOutput)
386  {
387  LOG(VB_GENERAL, LOG_ERR, LOC +
388  "Couldn't create VideoOutput instance. Exiting..");
389  SetErrored(tr("Failed to initialize video output"));
390  return false;
391  }
392 
393  if (m_embedding && pipState == kPIPOff)
395 
396  return true;
397 }
398 
400 {
402  {
403  m_osdLock.lock();
405  {
406  m_reinitOsd = true;
407  m_osdLock.unlock();
408  return;
409  }
410  QRect visible;
411  QRect total;
412  float aspect = NAN;
413  float scaling = NAN;
414  m_videoOutput->GetOSDBounds(total, visible, aspect,
415  scaling, 1.0F);
416  if (m_osd)
417  {
419  int stretch = lroundf(aspect * 100);
420  if ((m_osd->Bounds() != visible) ||
421  (m_osd->GetFontStretch() != stretch))
422  {
423  uint old = m_textDisplayMode;
424  ToggleCaptions(old);
425  m_osd->Reinit(visible, aspect);
426  EnableCaptions(old, false);
427  if (m_deleteMap.IsEditing())
428  {
429  bool const changed = m_deleteMap.IsChanged();
430  m_deleteMap.SetChanged(true);
432  m_deleteMap.SetChanged(changed);
433  }
434  }
435  }
436 
437 #ifdef USING_MHEG
438  if (GetInteractiveTV())
439  {
440  QMutexLocker locker(&m_itvLock);
441  m_interactiveTV->Reinit(total, visible, aspect);
442  m_itvVisible = false;
443  }
444 #endif // USING_MHEG
445  m_reinitOsd = false;
446  m_osdLock.unlock();
447  }
448 }
449 
450 void MythPlayer::ReinitVideo(bool ForceUpdate)
451 {
452 
453  bool aspect_only = false;
454  {
456 
457  m_videoOutput->SetVideoFrameRate(static_cast<float>(m_videoFrameRate));
458  float aspect = (m_forcedVideoAspect > 0) ? m_forcedVideoAspect : m_videoAspect;
460  m_decoder->GetVideoCodecID(), aspect_only, &locker,
461  m_maxReferenceFrames, ForceUpdate))
462  {
463  LOG(VB_GENERAL, LOG_ERR, LOC +
464  "Failed to Reinitialize Video. Exiting..");
465  SetErrored(tr("Failed to reinitialize video output"));
466  return;
467  }
468 
469  if (m_osd)
471  ReinitOSD();
472  }
473 
474  if (!aspect_only)
475  ClearAfterSeek();
476 
477  if (m_textDisplayMode)
478  EnableSubtitles(true);
479 
480  AutoVisualise();
481 }
482 
483 static inline QString toQString(FrameScanType scan) {
484  switch (scan) {
485  case kScan_Ignore: return QString("Ignore Scan");
486  case kScan_Detect: return QString("Detect Scan");
487  case kScan_Interlaced: return QString("Interlaced Scan");
488  case kScan_Progressive: return QString("Progressive Scan");
489  default: return QString("Unknown Scan");
490  }
491 }
492 
495  float fps, int video_height) const
496 {
497  QString dbg = QString("detectInterlace(") + toQString(newScan) +
498  QString(", ") + toQString(scan) + QString(", ") +
499  QString("%1").arg(static_cast<double>(fps)) + QString(", ") +
500  QString("%1").arg(video_height) + QString(") ->");
501 
502  if (kScan_Ignore != newScan || kScan_Detect == scan)
503  {
504  // The scanning mode should be decoded from the stream, but if it
505  // isn't, we have to guess.
506 
507  scan = kScan_Interlaced; // default to interlaced
508  if ((720 == video_height) || // ATSC 720p
509  (fps > 45)) // software deinterlacing
511 
512  if (kScan_Detect != newScan)
513  scan = newScan;
514  };
515 
516  LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg +toQString(scan));
517 
518  return scan;
519 }
520 
521 void MythPlayer::SetKeyframeDistance(int keyframedistance)
522 {
523  m_keyframeDist = (keyframedistance > 0) ? static_cast<uint>(keyframedistance) : m_keyframeDist;
524 }
525 
546 void MythPlayer::AutoDeint(VideoFrame *frame, bool allow_lock)
547 {
548  if (!frame)
549  return;
550 
552  {
553  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Locking scan override to '%1'")
554  .arg(ScanTypeToString(m_scanOverride, true)));
556  }
557 
558  // This is currently only signalled for H264 content
559  if (frame->new_gop)
560  {
562  ((frame->interlaced_frame && !is_interlaced(m_scan)) ||
563  (!frame->interlaced_frame && is_interlaced(m_scan))))
564  {
565  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Unlocking frame scan");
566  m_scanLocked = false;
567  }
568  }
569 
570  if (m_scanLocked)
571  return;
572 
573  if (frame->interlaced_frame)
574  {
575  if (m_scanTracker < 0)
576  {
577  LOG(VB_PLAYBACK, LOG_INFO, LOC +
578  QString("Interlaced frame seen after %1 progressive frames")
579  .arg(abs(m_scanTracker)));
580  m_scanTracker = 2;
581  if (allow_lock)
582  {
583  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Locking scan to Interlaced.");
585  return;
586  }
587  }
588  m_scanTracker++;
589  }
590  else
591  {
592  if (m_scanTracker > 0)
593  {
594  LOG(VB_PLAYBACK, LOG_INFO, LOC +
595  QString("Progressive frame seen after %1 interlaced frames")
596  .arg(m_scanTracker));
597  m_scanTracker = 0;
598  }
599  m_scanTracker--;
600  }
601 
602  int min_count = !allow_lock ? 0 : 2;
603  if (abs(m_scanTracker) <= min_count)
604  return;
605 
607  m_scanLocked = false;
608 }
609 
611 {
612  int next = m_scanOverride + 1;
613  if (next > kScan_Progressive)
614  next = kScan_Detect;
615  return static_cast<FrameScanType>(next);
616 }
617 
619 {
620  if (m_scanOverride == Scan)
621  return;
622  m_scanOverride = Scan;
624  {
625  m_scanLocked = false;
626  m_scanInitialized = false;
627  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Reverting to auto detection of scan");
628  }
629 }
630 
632 {
634  return m_scanOverride;
635  return m_scan;
636 }
637 
639 {
641  {
642  m_resetScan = Scan;
643  return;
644  }
645 
646  if (!m_videoOutput)
647  return;
648 
650 
652  return;
653 
654  m_scanLocked = (Scan != kScan_Detect);
655  m_scanInitialized = true;
657 
658  if (is_interlaced(Scan))
659  {
661  bool normal = m_playSpeed > 0.99F && m_playSpeed < 1.01F && m_normalSpeed;
662  m_doubleFramerate = CanSupportDoubleRate() && normal && !forced;
664  }
665  else if (kScan_Progressive == Scan)
666  {
667  m_doubleFramerate = false;
668  m_videoOutput->SetDeinterlacing(false, false);
669  }
670 
671  m_scan = Scan;
672 }
673 
674 void MythPlayer::SetVideoParams(int width, int height, double fps,
675  float aspect, bool ForceUpdate,
676  int ReferenceFrames, FrameScanType scan, const QString& codecName)
677 {
678  bool paramsChanged = ForceUpdate;
679 
680  if (width >= 0 && height >= 0)
681  {
682  paramsChanged = true;
683  m_videoDim = m_videoDispDim = QSize(width, height);
684  m_videoAspect = aspect > 0.0F ? aspect : static_cast<float>(width) / height;
685  }
686 
687  if (!qIsNaN(fps) && fps > 0.0 && fps < 121.0)
688  {
689  paramsChanged = true;
690  m_videoFrameRate = fps;
691  if (m_ffrewSkip != 0 && m_ffrewSkip != 1)
692  {
693  UpdateFFRewSkip();
694  }
695  else
696  {
697  float temp_speed = (m_playSpeed == 0.0F) ?
700  1.0 / (m_videoFrameRate * static_cast<double>(temp_speed)));
701  }
702  }
703 
704  if (!codecName.isEmpty())
705  {
706  m_codecName = codecName;
707  paramsChanged = true;
708  }
709 
710  if (ReferenceFrames > 0)
711  {
712  m_maxReferenceFrames = ReferenceFrames;
713  paramsChanged = true;
714  }
715 
716  if (!paramsChanged)
717  return;
718 
719  if (m_videoOutput)
720  ReinitVideo(ForceUpdate);
721 
722  if (IsErrored())
723  return;
724 
725  // ensure deinterlacers are correctly reset after a change
726  m_scanInitialized = false;
728  m_videoDispDim.height()));
729  m_scanLocked = false;
730  m_scanTracker = (m_scan == kScan_Interlaced) ? 2 : 0;
731 }
732 
733 
734 void MythPlayer::SetFrameRate(double fps)
735 {
736  m_videoFrameRate = fps;
737  float temp_speed = (m_playSpeed == 0.0F) ?
740  1.0 / (m_videoFrameRate * static_cast<double>(temp_speed)));
741 }
742 
743 void MythPlayer::SetFileLength(int total, int frames)
744 {
745  m_totalLength = total;
746  m_totalFrames = frames;
747 }
748 
749 void MythPlayer::SetDuration(int duration)
750 {
751  m_totalDuration = duration;
752 }
753 
755 {
756  m_isDummy = true;
757 
758  if (!m_videoOutput)
759  {
761  SetVideoParams(720, 576, 25.00, 1.25F, false, 2);
762  }
763 
764  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
765  auto *dec = new DummyDecoder(this, *(m_playerCtx->m_playingInfo));
766  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
767  SetDecoder(dec);
768 }
769 
770 void MythPlayer::CreateDecoder(char *TestBuffer, int TestSize)
771 {
772  if (AvFormatDecoder::CanHandle(TestBuffer, m_playerCtx->m_buffer->GetFilename(), TestSize))
773  {
775  return;
776  }
777 
778  if (NuppelDecoder::CanHandle(TestBuffer, TestSize))
780 }
781 
782 int MythPlayer::OpenFile(int Retries)
783 {
784  // Sanity check
786  return -1;
787 
788  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opening '%1'")
790 
791  // Disable hardware acceleration for second PBP
794  {
795  m_playerFlags = static_cast<PlayerFlags>(m_playerFlags - kDecodeAllowGPU);
796  }
797 
798  m_isDummy = false;
800 
801  // Dummy setup for livetv transtions. Can we get rid of this?
802  if (m_playerCtx->m_tvchain)
803  {
804  int currentposition = m_playerCtx->m_tvchain->GetCurPos();
805  if (m_playerCtx->m_tvchain->GetInputType(currentposition) == "DUMMY")
806  {
807  OpenDummy();
808  return 0;
809  }
810  }
811 
812  // Start the RingBuffer read ahead thread
814 
816  char *testbuf = new char[kDecoderProbeBufferSize];
817  UnpauseBuffer();
818 
819  // delete any pre-existing recorder
820  SetDecoder(nullptr);
821  int testreadsize = 2048;
822 
823  // Test the incoming buffer and create a suitable decoder
824  MythTimer bigTimer;
825  bigTimer.start();
826  int timeout = max((Retries + 1) * 500, 30000);
827  while (testreadsize <= kDecoderProbeBufferSize)
828  {
829  MythTimer peekTimer;
830  peekTimer.start();
831  while (m_playerCtx->m_buffer->Peek(testbuf, testreadsize) != testreadsize)
832  {
833  // NB need to allow for streams encountering network congestion
834  if (peekTimer.elapsed() > 30000 || bigTimer.elapsed() > timeout
836  {
837  LOG(VB_GENERAL, LOG_ERR, LOC +
838  QString("OpenFile(): Could not read first %1 bytes of '%2'")
839  .arg(testreadsize)
840  .arg(m_playerCtx->m_buffer->GetFilename()));
841  delete[] testbuf;
842  SetErrored(tr("Could not read first %1 bytes").arg(testreadsize));
843  return -1;
844  }
845  LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenFile() waiting on data");
846  usleep(50 * 1000);
847  }
848 
849  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
850  CreateDecoder(testbuf, testreadsize);
851  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
852  if (m_decoder || (bigTimer.elapsed() > timeout))
853  break;
854  testreadsize <<= 1;
855  }
856 
857  // Fail
858  if (!m_decoder)
859  {
860  LOG(VB_GENERAL, LOG_ERR, LOC +
861  QString("Couldn't find an A/V decoder for: '%1'")
862  .arg(m_playerCtx->m_buffer->GetFilename()));
863  SetErrored(tr("Could not find an A/V decoder"));
864 
865  delete[] testbuf;
866  return -1;
867  }
868 
869  if (m_decoder->IsErrored())
870  {
871  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not initialize A/V decoder.");
872  SetDecoder(nullptr);
873  SetErrored(tr("Could not initialize A/V decoder"));
874 
875  delete[] testbuf;
876  return -1;
877  }
878 
879  // Pre-init the decoder
884 
885  // Open the decoder
886  int result = m_decoder->OpenFile(m_playerCtx->m_buffer, false, testbuf, testreadsize);
887  delete[] testbuf;
888 
889  if (result < 0)
890  {
891  LOG(VB_GENERAL, LOG_ERR, QString("Couldn't open decoder for: %1")
892  .arg(m_playerCtx->m_buffer->GetFilename()));
893  SetErrored(tr("Could not open decoder"));
894  return -1;
895  }
896 
897  // Disable audio if necessary
899 
900  // Livetv, recording or in-progress
901  if (result > 0)
902  {
903  m_hasFullPositionMap = true;
906  }
907 
908  // Determine the initial bookmark and update it for the cutlist
912 
915  {
916  gCoreContext->SaveSetting("DefaultChanid",
917  static_cast<int>(m_playerCtx->m_playingInfo->GetChanID()));
918  QString callsign = m_playerCtx->m_playingInfo->GetChannelSchedulingID();
919  QString channum = m_playerCtx->m_playingInfo->GetChanNum();
920  gCoreContext->SaveSetting("DefaultChanKeys", callsign + "[]:[]" + channum);
922  {
923  uint cardid = static_cast<uint>(m_playerCtx->m_recorder->GetRecorderNumber());
924  CardUtil::SetStartChannel(cardid, channum);
925  }
926  }
927 
928  return IsErrored() ? -1 : 0;
929 }
930 
931 void MythPlayer::SetFramesPlayed(uint64_t played)
932 {
933  m_framesPlayed = played;
934  if (m_videoOutput)
936 }
937 
942 {
943  if (m_videoOutput)
944  return m_videoOutput->FreeVideoFrames();
945  return 0;
946 }
947 
950 {
951  static VideoFrameType s_defaultFormats[] = { FMT_YV12, FMT_NONE };
952  if (m_videoOutput)
954  return &s_defaultFormats[0];
955 }
956 
967 {
968  if (m_videoOutput)
970  return nullptr;
971 }
972 
977  int64_t timecode,
978  bool wrap)
979 {
980  if (wrap)
981  WrapTimecode(timecode, TC_VIDEO);
982  buffer->timecode = timecode;
983  m_latestVideoTimecode = timecode;
984 
985  if (m_videoOutput)
986  m_videoOutput->ReleaseFrame(buffer);
987 
988  m_detectLetterBox->Detect(buffer);
989  if (m_allPaused)
990  CheckAspectRatio(buffer);
991 }
992 
997 {
998  if (m_videoOutput)
999  m_videoOutput->DiscardFrame(buffer);
1000 }
1001 
1015 void MythPlayer::DiscardVideoFrames(bool KeyFrame, bool Flushed)
1016 {
1017  if (m_videoOutput)
1018  m_videoOutput->DiscardFrames(KeyFrame, Flushed);
1019 }
1020 
1022 {
1023  EofState eof = GetEof();
1024  if (eof != kEofStateNone && !m_allPaused)
1025  return true;
1026  if (GetEditMode())
1027  return false;
1028  if (m_liveTV)
1029  return false;
1031  return true;
1032  return false;
1033 }
1034 
1036 {
1037  w = m_videoDim.width();
1038  h = m_videoDim.height();
1039 
1040  VideoFrame *retval = nullptr;
1041 
1042  m_vidExitLock.lock();
1043  if (m_videoOutput)
1044  retval = m_videoOutput->GetLastShownFrame();
1045 
1046  if (!retval)
1047  m_vidExitLock.unlock();
1048 
1049  return retval;
1050 }
1051 
1053 {
1054  if (m_videoOutput)
1055  m_videoOutput->DeLimboFrame(frame);
1056 }
1057 
1059 {
1060  if (frame)
1061  m_vidExitLock.unlock();
1062 }
1063 
1065 {
1066  if (m_videoOutput)
1068  else
1069  {
1070  m_embedRect = rect;
1071  m_embedding = true;
1072  }
1073 }
1074 
1076 {
1077  if (m_videoOutput)
1078  {
1080  ReinitOSD();
1081  }
1082  else
1083  {
1084  m_embedRect = QRect();
1085  m_embedding = false;
1086  }
1087 }
1088 
1089 void MythPlayer::WindowResized(const QSize &new_size)
1090 {
1091  if (m_videoOutput)
1092  m_videoOutput->WindowResized(new_size);
1093  ReinitOSD();
1094 }
1095 
1097 {
1098  QMutexLocker locker(&m_osdLock);
1099  if (!m_osd)
1100  return;
1101 
1102  m_osd->EnableTeletext(true, page);
1105 }
1106 
1108 {
1109  QMutexLocker locker(&m_osdLock);
1110  if (!m_osd)
1111  return;
1112 
1113  m_osd->EnableTeletext(false, 0);
1115 
1116  /* If subtitles are enabled before the teletext menu was displayed,
1117  re-enabled them. */
1120 }
1121 
1123 {
1124  QMutexLocker locker(&m_osdLock);
1125  if (!m_osd)
1126  return;
1127 
1128  m_osd->TeletextReset();
1129 }
1130 
1135 {
1136  m_osdLock.lock();
1138  m_ttPageNum = page;
1142  m_osdLock.unlock();
1143 }
1144 
1146 {
1148  return false;
1149 
1150  bool handled = true;
1151 
1152  m_osdLock.lock();
1153  if (action == "MENU" || action == ACTION_TOGGLETT || action == "ESCAPE")
1154  DisableTeletext();
1155  else if (m_osd)
1156  handled = m_osd->TeletextAction(action);
1157  m_osdLock.unlock();
1158 
1159  return handled;
1160 }
1161 
1163 {
1164  QMutexLocker locker(&m_osdLock);
1165  if (!m_osd)
1166  return;
1167 
1174  {
1175  m_osd->ClearSubtitles();
1176  }
1179  {
1180  m_osd->TeletextClear();
1181  }
1182 }
1183 
1184 void MythPlayer::DisableCaptions(uint mode, bool osd_msg)
1185 {
1186  if (m_textDisplayMode)
1188  m_textDisplayMode &= ~mode;
1189  ResetCaptions();
1190 
1191  QMutexLocker locker(&m_osdLock);
1192 
1193  bool newTextDesired = (m_textDisplayMode & kDisplayAllTextCaptions) != 0U;
1194  // Only turn off textDesired if the Operator requested it.
1195  if (osd_msg || newTextDesired)
1196  m_textDesired = newTextDesired;
1197  QString msg = "";
1198  if (kDisplayNUVTeletextCaptions & mode)
1199  msg += tr("TXT CAP");
1200  if (kDisplayTeletextCaptions & mode)
1201  {
1202  if (m_decoder != nullptr)
1205  DisableTeletext();
1206  }
1210  if ((kDisplayCC608 & mode) || (kDisplayCC708 & mode) ||
1211  (kDisplayAVSubtitle & mode) || (kDisplayRawTextSubtitle & mode))
1212  {
1213  int type = toTrackType(mode);
1214  if (m_decoder != nullptr)
1216  if (m_osd)
1217  m_osd->EnableSubtitles(preserve);
1218  }
1219  if (kDisplayTextSubtitle & mode)
1220  {
1221  msg += tr("Text subtitles");
1222  if (m_osd)
1223  m_osd->EnableSubtitles(preserve);
1224  }
1225  if (!msg.isEmpty() && osd_msg)
1226  {
1227  msg += " " + tr("Off");
1229  }
1230 }
1231 
1232 void MythPlayer::EnableCaptions(uint mode, bool osd_msg)
1233 {
1234  QMutexLocker locker(&m_osdLock);
1235  bool newTextDesired = (mode & kDisplayAllTextCaptions) != 0U;
1236  // Only turn off textDesired if the Operator requested it.
1237  if (osd_msg || newTextDesired)
1238  m_textDesired = newTextDesired;
1239  QString msg = "";
1240  if ((kDisplayCC608 & mode) || (kDisplayCC708 & mode) ||
1241  (kDisplayAVSubtitle & mode) || kDisplayRawTextSubtitle & mode)
1242  {
1243  int type = toTrackType(mode);
1244  if (m_decoder != nullptr)
1246  if (m_osd)
1247  m_osd->EnableSubtitles(mode);
1248  }
1249  if (kDisplayTextSubtitle & mode)
1250  {
1251  if (m_osd)
1253  msg += tr("Text subtitles");
1254  }
1255  if (kDisplayNUVTeletextCaptions & mode)
1256  msg += tr("TXT %1").arg(m_ttPageNum, 3, 16);
1257  if ((kDisplayTeletextCaptions & mode) && (m_decoder != nullptr))
1258  {
1261 
1262  int page = m_decoder->GetTrackLanguageIndex(
1265 
1266  EnableTeletext(page);
1268  }
1269 
1270  msg += " " + tr("On");
1271 
1272  LOG(VB_PLAYBACK, LOG_INFO, QString("EnableCaptions(%1) msg: %2")
1273  .arg(mode).arg(msg));
1274 
1275  m_textDisplayMode = mode;
1276  if (m_textDisplayMode)
1278  if (osd_msg)
1280 }
1281 
1283 {
1285  return m_textDisplayMode != 0U;
1286 }
1287 
1289 {
1290  QMutexLocker locker(&m_osdLock);
1291  uint mode = toCaptionType(type);
1292  uint origMode = m_textDisplayMode;
1293 
1294  if (m_textDisplayMode)
1295  DisableCaptions(m_textDisplayMode, (origMode & mode) != 0U);
1296  if (origMode & mode)
1297  return m_textDisplayMode != 0U;
1298  if (mode)
1299  EnableCaptions(mode);
1300  return m_textDisplayMode != 0U;
1301 }
1302 
1303 void MythPlayer::SetCaptionsEnabled(bool enable, bool osd_msg)
1304 {
1305  QMutexLocker locker(&m_osdLock);
1307  uint origMode = m_textDisplayMode;
1308 
1309  // Only turn off textDesired if the Operator requested it.
1310  if (osd_msg || enable)
1311  m_textDesired = enable;
1312 
1313  if (!enable)
1314  {
1315  DisableCaptions(origMode, osd_msg);
1316  return;
1317  }
1320  if (origMode != (uint)mode)
1321  {
1322  DisableCaptions(origMode, false);
1323 
1324  if (kDisplayNone == mode)
1325  {
1326  if (osd_msg)
1327  {
1328  SetOSDMessage(tr("No captions",
1329  "CC/Teletext/Subtitle text not available"),
1330  kOSDTimeout_Med);
1331  }
1332  LOG(VB_PLAYBACK, LOG_INFO,
1333  "No captions available yet to enable.");
1334  }
1335  else if (mode)
1336  {
1337  EnableCaptions(mode, osd_msg);
1338  }
1339  }
1340  ResetCaptions();
1341 }
1342 
1344 {
1353 }
1354 
1356 {
1357  if (m_decoder)
1358  return m_decoder->GetTracks(type);
1359  return QStringList();
1360 }
1361 
1363 {
1364  if (m_decoder)
1365  return m_decoder->GetTrackCount(type);
1366  return 0;
1367 }
1368 
1369 int MythPlayer::SetTrack(uint type, int trackNo)
1370 {
1371  int ret = -1;
1372  if (!m_decoder)
1373  return ret;
1374 
1375  ret = m_decoder->SetTrack(type, trackNo);
1376  if (kTrackTypeAudio == type)
1377  {
1378  QString msg = "";
1379  if (m_decoder)
1381  kOSDTimeout_Med);
1382  return ret;
1383  }
1384 
1385  uint subtype = toCaptionType(type);
1386  if (subtype)
1387  {
1389  EnableCaptions(subtype, true);
1390  if ((kDisplayCC708 == subtype || kDisplayCC608 == subtype) && m_decoder)
1391  {
1392  int sid = m_decoder->GetTrackInfo(type, trackNo).m_stream_id;
1393  if (sid >= 0)
1394  {
1395  (kDisplayCC708 == subtype) ? m_cc708.SetCurrentService(sid) :
1396  m_cc608.SetMode(sid);
1397  }
1398  }
1399  }
1400  return ret;
1401 }
1402 
1409 {
1410  if (trackType >= kTrackTypeSubtitle &&
1411  trackType <= kTrackTypeTeletextCaptions && m_textDesired)
1412  {
1413  m_enableCaptions = true;
1414  }
1415 }
1416 
1418 {
1419  if (enable)
1420  m_enableCaptions = true;
1421  else
1422  m_disableCaptions = true;
1423 }
1424 
1426 {
1427  if (enable)
1428  m_enableForcedSubtitles = true;
1429  else
1430  m_disableForcedSubtitles = true;
1431 }
1432 
1434 {
1435  m_allowForcedSubtitles = allow;
1437  tr("Forced Subtitles On") :
1438  tr("Forced Subtitles Off"),
1439  kOSDTimeout_Med);
1440 }
1441 
1443 {
1444  m_disableForcedSubtitles = false;
1445  m_osdLock.lock();
1446  if (m_osd)
1448  m_osdLock.unlock();
1449 }
1450 
1452 {
1453  m_enableForcedSubtitles = false;
1455  return;
1456 
1457  m_osdLock.lock();
1458  if (m_osd)
1459  m_osd->EnableSubtitles(kDisplayAVSubtitle, true /*forced only*/);
1460  m_osdLock.unlock();
1461 }
1462 
1464 {
1465  if (m_decoder)
1466  return m_decoder->GetTrack(type);
1467  return -1;
1468 }
1469 
1471 {
1472  if (!m_decoder)
1473  return -1;
1474 
1475  int retval = m_decoder->ChangeTrack(type, dir);
1476  if (retval >= 0)
1477  {
1479  kOSDTimeout_Med);
1480  return retval;
1481  }
1482  return -1;
1483 }
1484 
1486 {
1487  if (!m_decoder || (dir < 0))
1488  return;
1489 
1493  {
1494  int tracktype = toTrackType(m_textDisplayMode);
1495  if (GetTrack(tracktype) < m_decoder->NextTrack(tracktype))
1496  {
1497  SetTrack(tracktype, m_decoder->NextTrack(tracktype));
1498  return;
1499  }
1500  }
1501  int nextmode = NextCaptionTrack(m_textDisplayMode);
1502  if ((nextmode == kDisplayTextSubtitle) ||
1503  (nextmode == kDisplayNUVTeletextCaptions) ||
1504  (nextmode == kDisplayNone))
1505  {
1507  if (nextmode != kDisplayNone)
1508  EnableCaptions(nextmode, true);
1509  }
1510  else
1511  {
1512  int tracktype = toTrackType(nextmode);
1513  int tracks = m_decoder->GetTrackCount(tracktype);
1514  if (tracks)
1515  {
1517  SetTrack(tracktype, 0);
1518  }
1519  }
1520 }
1521 
1523 {
1524  if (mode == kDisplayNone)
1525  return false;
1526  if (((mode == kDisplayTextSubtitle) && HasTextSubtitles()) ||
1527  (mode == kDisplayNUVTeletextCaptions))
1528  {
1529  return true;
1530  }
1531  if (!(mode == kDisplayTextSubtitle) &&
1533  {
1534  return true;
1535  }
1536  return false;
1537 }
1538 
1540 {
1541  // Text->TextStream->708->608->AVSubs->Teletext->NUV->None
1542  // NUV only offerred if PAL
1543  bool pal = (m_vbiMode == VBIMode::PAL_TT);
1544  int nextmode = kDisplayNone;
1545 
1546  if (kDisplayTextSubtitle == mode)
1547  nextmode = kDisplayRawTextSubtitle;
1548  else if (kDisplayRawTextSubtitle == mode)
1549  nextmode = kDisplayCC708;
1550  else if (kDisplayCC708 == mode)
1551  nextmode = kDisplayCC608;
1552  else if (kDisplayCC608 == mode)
1553  nextmode = kDisplayAVSubtitle;
1554  else if (kDisplayAVSubtitle == mode)
1555  nextmode = kDisplayTeletextCaptions;
1556  else if (kDisplayTeletextCaptions == mode)
1557  nextmode = pal ? kDisplayNUVTeletextCaptions : kDisplayNone;
1558  else if ((kDisplayNUVTeletextCaptions == mode) && pal)
1559  nextmode = kDisplayNone;
1560  else if (kDisplayNone == mode)
1561  nextmode = kDisplayTextSubtitle;
1562 
1563  if (nextmode == kDisplayNone || HasCaptionTrack(nextmode))
1564  return nextmode;
1565 
1566  return NextCaptionTrack(nextmode);
1567 }
1568 
1570 {
1571  if (m_decoder)
1573  m_frameInterval = static_cast<int>(lround(1000000.0 * frame_period) / m_fpsMultiplier);
1574 
1575  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetFrameInterval Interval:%1 Speed:%2 Scan:%3 (Multiplier: %4)")
1576  .arg(m_frameInterval).arg(static_cast<double>(m_playSpeed)).arg(toQString(scan)).arg(m_fpsMultiplier));
1577 }
1578 
1580 {
1581  m_avsyncAvg = 0;
1582  m_prevTc = 0;
1583  m_rtcBase = 0;
1586  m_lastFix = 0.0;
1587  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + "A/V sync reset");
1588 }
1589 
1591 {
1592  m_rtcBase = 0;
1595  m_lastFix = 0.0;
1596 
1597  if (!FlagIsSet(kVideoIsNull))
1598  {
1599  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display Refresh Rate: %1 Video Frame Rate: %2")
1600  .arg(1000000.0 / m_display->GetRefreshInterval(m_frameInterval), 0, 'f', 3)
1601  .arg(1000000.0 / m_frameInterval, 0, 'f', 3));
1602 
1603  SetFrameInterval(m_scan, 1.0 / (m_videoFrameRate * static_cast<double>(m_playSpeed)));
1604 
1605  // try to get preferential scheduling, but ignore if we fail to.
1606  myth_nice(-19);
1607  }
1608 }
1609 
1610 void MythPlayer::WaitForTime(int64_t framedue)
1611 {
1612  int64_t unow = m_avTimer.nsecsElapsed() / 1000;
1613  int64_t delay = framedue - unow;
1614  if (delay > 0)
1615  QThread::usleep(static_cast<unsigned long>(delay));
1616 }
1617 
1624 {
1625  int maxtries = 6;
1626  int64_t timenow = m_avTimer.nsecsElapsed() / 1000;
1627  auto playspeed1000 = static_cast<int64_t>(1000.0F / m_playSpeed);
1628 
1629  while (maxtries--)
1630  {
1632  return false;
1633 
1636  if (!last)
1637  return false;
1638 
1639  m_videoOutput->ProcessFrame(last, nullptr, m_pipPlayers, m_scan);
1640 
1641  int64_t videotimecode = last->timecode & 0x0000ffffffffffff;
1642  if (videotimecode != last->timecode)
1643  videotimecode = m_maxTcVal;
1644  if (videotimecode == 0)
1645  {
1647  return true;
1648  }
1649  m_maxTcVal = videotimecode;
1650 
1651  if (m_rtcBase == 0)
1652  m_rtcBase = timenow - (videotimecode * playspeed1000);
1653 
1654  int64_t framedue = m_rtcBase + (videotimecode * playspeed1000);
1655  if (framedue > timenow)
1656  return true;
1657 
1659  }
1660  return true;
1661 }
1662 
1663 #define AVSYNC_MAX_LATE 10000000
1665 {
1666  if (m_videoOutput->IsErrored())
1667  {
1668  LOG(VB_GENERAL, LOG_ERR, LOC +
1669  "AVSync: Unknown error in videoOutput, aborting playback.");
1670  SetErrored(tr("Failed to initialize A/V Sync"));
1671  return;
1672  }
1673  int64_t videotimecode = 0;
1674 
1675  bool dropframe = false;
1676  bool pause_audio = false;
1677  int64_t framedue = 0;
1678  int64_t audio_adjustment = 0;
1679  int64_t unow = 0;
1680  int64_t lateness = 0;
1681  auto playspeed1000 = static_cast<int64_t>(1000.0F / m_playSpeed);
1682  bool reset = false;
1683  // controller gain
1684  static float const s_av_control_gain = 0.4F;
1685  // time weighted exponential filter coefficient
1686  static float const s_sync_fc = 0.9F;
1687 
1688  while (framedue == 0)
1689  {
1690  if (buffer)
1691  {
1692  videotimecode = buffer->timecode & 0x0000ffffffffffff;
1693  // Detect bogus timecodes from DVD and ignore them.
1694  if (videotimecode != buffer->timecode)
1695  videotimecode = m_maxTcVal;
1696  }
1697 
1698  unow = m_avTimer.nsecsElapsed() / 1000;
1699 
1701  {
1702  framedue = unow + m_frameInterval;
1703  break;
1704  }
1705  // first time or after a seek - setup of m_rtcBase
1706  if (m_rtcBase == 0)
1707  {
1708  // cater for DVB radio
1709  if (videotimecode == 0)
1710  videotimecode = m_audio.GetAudioTime();;
1711 
1712  // cater for data only streams (i.e. MHEG)
1713  bool dataonly = !m_audio.HasAudioIn() && m_videoDim.isEmpty();
1714 
1715  // On first frame we get nothing, so exit out.
1716  // FIXME - does this mean we skip the first frame? Should be avoidable.
1717  if (videotimecode == 0 && !dataonly)
1718  return;
1719 
1720  m_rtcBase = unow - videotimecode * playspeed1000;
1721  m_maxTcVal = 0;
1722  m_maxTcFrames = 0;
1723  m_numDroppedFrames = 0;
1724  m_timeOffsetBase = static_cast<int64_t>(TranslatePositionFrameToMs(m_framesPlayed, false)) - videotimecode;
1725  }
1726 
1727  if (videotimecode == 0)
1728  videotimecode = m_maxTcVal + m_frameInterval/1000;
1729  int64_t tcincr = videotimecode - m_maxTcVal;
1730  if (tcincr > 0 || tcincr < -100)
1731  {
1732  m_maxTcVal = videotimecode;
1733  m_maxTcFrames = 0;
1734  }
1735  else
1736  {
1737  m_maxTcFrames++;
1738  videotimecode = m_maxTcVal + m_maxTcFrames * m_frameInterval/1000;
1739  }
1740 
1741  if (m_playSpeed > 0.0F)
1742  framedue = m_rtcBase + videotimecode * playspeed1000;
1743  else
1744  framedue = unow + m_frameInterval / 2;
1745 
1746  // This code is disabled as it appears to cause multiple issues. It is
1747  // retained for future reference...
1748  // recalculate m_framesPlayed to conform to actual time code.
1749  //m_framesPlayed = TranslatePositionMsToFrame(static_cast<uint64_t>(videotimecode + m_timeOffsetBase), false);
1750  //m_decoder->SetFramesPlayed(static_cast<long long>(m_framesPlayed));
1751 
1752  lateness = unow - framedue;
1753  dropframe = false;
1754  if (lateness > 30000)
1755  dropframe = m_numDroppedFrames < 10;
1756 
1757  if (lateness <= 30000 && m_priorAudioTimecode > 0
1758  && m_priorVideoTimecode > 0)
1759  {
1760  // Get video in sync with audio
1761  audio_adjustment = m_priorAudioTimecode - m_priorVideoTimecode;
1762  // If there is excess audio - throw it away.
1763  if (audio_adjustment < -200)
1764  {
1765  m_audio.Reset();
1766  audio_adjustment = 0;
1767  }
1768  int sign = audio_adjustment < 0 ? -1 : 1;
1769  float fix_amount = (m_lastFix * s_sync_fc + (1 - s_sync_fc) * audio_adjustment) * sign * s_av_control_gain;
1770  m_lastFix = fix_amount * sign;
1771  auto speedup1000 = static_cast<int64_t>(1000 * m_playSpeed);
1772  m_rtcBase -= static_cast<int64_t>(1000000 * fix_amount * sign / speedup1000);
1773  if (audio_adjustment * sign > 20)
1774  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AV Sync: Audio %1 by %2 ms")
1775  .arg(audio_adjustment > 0 ? "ahead" : "behind").arg(abs(audio_adjustment)));
1776  if (audio_adjustment > 200)
1777  pause_audio = true;
1778  }
1779  // sanity check - reset m_rtcBase if time codes have gone crazy.
1780  if ((lateness > AVSYNC_MAX_LATE || lateness < - AVSYNC_MAX_LATE))
1781  {
1782  framedue = 0;
1783  m_rtcBase = 0;
1784  if (reset)
1785  {
1786  LOG(VB_GENERAL, LOG_ERR, LOC +
1787  QString("Resetting AV Sync2 failed, lateness = %1").arg(lateness));
1788  SetErrored(tr("Failed to initialize A/V Sync"));
1789  return;
1790  }
1791  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1792  QString("Resetting AV Sync2, lateness = %1").arg(lateness));
1793  reset = true;
1794  }
1795  }
1796  m_priorVideoTimecode = videotimecode;
1797  m_dispTimecode = videotimecode;
1798 
1800  m_avsyncAvg = static_cast<int>(m_lastFix * 1000 / s_av_control_gain);
1801 
1802  bool decoderdeint = buffer && buffer->already_deinterlaced;
1803  FrameScanType ps = m_scan;
1804  if (kScan_Detect == m_scan || kScan_Ignore == m_scan || decoderdeint)
1805  {
1806  ps = kScan_Progressive;
1807  }
1808  else if (buffer && is_interlaced(ps))
1809  {
1810  ps = kScan_Interlaced;
1812  }
1813 
1814  // only display the second field if needed
1816 
1817  if (buffer && !dropframe)
1818  {
1819  m_osdLock.lock();
1821  m_osdLock.unlock();
1822  }
1823 
1824  if (!pause_audio && m_avsyncAudioPaused)
1825  {
1826  m_avsyncAudioPaused = false;
1827  m_audio.Pause(false);
1828  }
1829  if (pause_audio && !m_avsyncAudioPaused)
1830  {
1831  m_avsyncAudioPaused = true;
1832  m_audio.Pause(true);
1833  }
1834 
1835  if (dropframe)
1837  else
1838  m_numDroppedFrames = 0;
1839 
1840  if (dropframe)
1841  {
1842  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1843  QString("Dropping frame: Video is behind by %1ms").arg(lateness / 1000));
1844  m_videoOutput->SetFramesPlayed(static_cast<long long>(++m_framesPlayed));
1845  }
1846  else if (!FlagIsSet(kVideoIsNull) && buffer)
1847  {
1848  // if we get here, we're actually going to do video output
1849  m_osdLock.lock();
1850  m_videoOutput->PrepareFrame(buffer, ps, m_osd);
1851  m_osdLock.unlock();
1852  // Don't wait for sync if this is a secondary PBP otherwise
1853  // the primary PBP will become out of sync
1854  if (!m_playerCtx->IsPBP() || m_playerCtx->IsPrimaryPBP())
1855  WaitForTime(framedue);
1856  // get time codes for calculating difference next time
1858  m_videoOutput->Show(ps);
1859  if (m_videoOutput->IsErrored())
1860  {
1861  LOG(VB_GENERAL, LOG_ERR, LOC + "Error condition detected "
1862  "in videoOutput after Show(), aborting playback.");
1863  SetErrored(tr("Serious error detected in Video Output"));
1864  return;
1865  }
1866  if (m_doubleFramerate)
1867  {
1868  //second stage of deinterlacer processing
1869  if (kScan_Interlaced == ps)
1870  ps = kScan_Intr2ndField;
1871  m_osdLock.lock();
1872  // Only double rate CPU deinterlacers require an extra call to ProcessFrame
1875  m_videoOutput->PrepareFrame(buffer, ps, m_osd);
1876  m_osdLock.unlock();
1877  // Display the second field
1878  if (!m_playerCtx->IsPBP() || m_playerCtx->IsPrimaryPBP())
1879  {
1880  int64_t due = framedue + m_frameInterval / 2;
1881  WaitForTime(due);
1882  }
1883  m_videoOutput->Show(ps);
1884  }
1885  }
1886  else if (!m_playerCtx->IsPiPOrSecondaryPBP())
1887  {
1888  WaitForTime(framedue);
1889  }
1890 
1891  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
1892  QString("A/V timecodes audio=%1 video=%2 frameinterval=%3 "
1893  "audioadj=%4 tcoffset=%5 unow=%6 udue=%7 ")
1894  .arg(m_priorAudioTimecode)
1895  .arg(m_priorVideoTimecode)
1896  .arg(m_frameInterval)
1897  .arg(audio_adjustment)
1898  .arg(m_tcWrap[TC_AUDIO])
1899  .arg(unow)
1900  .arg(framedue)
1901  );
1902 
1903 }
1904 
1906 {
1907  if (m_needNewPauseFrame)
1908  {
1910  {
1912  m_needNewPauseFrame = false;
1913 
1914  if (m_deleteMap.IsEditing())
1915  {
1916  m_osdLock.lock();
1917  if (m_osd)
1919  m_osdLock.unlock();
1920  }
1921  }
1922  else
1923  {
1924  m_decodeOneFrame = true;
1925  }
1926  }
1927 }
1928 
1930 {
1931  if (!m_videoOutput)
1932  return;
1933 
1934  if (m_videoOutput->IsErrored())
1935  {
1936  SetErrored(tr("Serious error detected in Video Output"));
1937  return;
1938  }
1939 
1940  // clear the buffering state
1941  SetBuffering(false);
1942 
1944  PreProcessNormalFrame(); // Allow interactiveTV to draw on pause frame
1945 
1947  m_osdLock.lock();
1949  m_videoOutput->PrepareFrame(nullptr, scan, m_osd);
1950  m_osdLock.unlock();
1952 }
1953 
1954 void MythPlayer::SetBuffering(bool new_buffering)
1955 {
1956  if (!m_buffering && new_buffering)
1957  {
1958  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for video buffers...");
1959  m_buffering = true;
1960  m_bufferingStart = QTime::currentTime();
1961  m_bufferingLastMsg = QTime::currentTime();
1962  }
1963  else if (m_buffering && !new_buffering)
1964  {
1965  m_buffering = false;
1966  }
1967 }
1968 
1969 // For debugging playback set this to increase the timeout so that
1970 // playback does not fail if stepping through code.
1971 // Set PREBUFFERDEBUG to any value and you will get 30 minutes.
1972 static char *preBufferDebug = getenv("PREBUFFERDEBUG");
1973 
1975 {
1976  if (!m_videoOutput)
1977  return false;
1978 
1979  if (!(min_buffers ? (m_videoOutput->ValidVideoFrames() >= min_buffers) :
1981  {
1982  SetBuffering(true);
1983 
1984  // This piece of code is to address the problem, when starting
1985  // Live TV, of jerking and stuttering. Without this code
1986  // that could go on forever, but is cured by a pause and play.
1987  // This code inserts a brief pause and play when the potential
1988  // for the jerking is detected.
1989 
1990  bool watchingTV = IsWatchingInprogress();
1991  if ((m_liveTV || watchingTV) && !FlagIsSet(kMusicChoice))
1992  {
1993  uint64_t frameCount = GetCurrentFrameCount();
1994  uint64_t framesLeft = frameCount - m_framesPlayed;
1995  auto margin = static_cast<uint64_t>(m_videoFrameRate * 3);
1996  if (framesLeft < margin)
1997  {
1998  if (m_rtcBase)
1999  {
2000  LOG(VB_PLAYBACK, LOG_NOTICE, LOC + "Pause to allow live tv catch up");
2001  LOG(VB_PLAYBACK, LOG_NOTICE, LOC + QString("Played: %1 Avail: %2 Buffered: %3 Margin: %4")
2002  .arg(m_framesPlayed).arg(frameCount)
2003  .arg(m_videoOutput->ValidVideoFrames()).arg(margin));
2004  }
2005  m_audio.Pause(true);
2006  m_avsyncAudioPaused = true;
2007  m_rtcBase = 0;
2008  }
2009  }
2010  usleep(static_cast<uint>(m_frameInterval >> 3));
2011  int waited_for = m_bufferingStart.msecsTo(QTime::currentTime());
2012  int last_msg = m_bufferingLastMsg.msecsTo(QTime::currentTime());
2013  if (last_msg > 100 && !FlagIsSet(kMusicChoice))
2014  {
2015  if (++m_bufferingCounter == 10)
2016  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2017  "To see more buffering messages use -v playback");
2018  if (m_bufferingCounter >= 10)
2019  {
2020  LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
2021  QString("Waited %1ms for video buffers %2")
2022  .arg(waited_for).arg(m_videoOutput->GetFrameStatus()));
2023  }
2024  else
2025  {
2026  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2027  QString("Waited %1ms for video buffers %2")
2028  .arg(waited_for).arg(m_videoOutput->GetFrameStatus()));
2029  }
2030  m_bufferingLastMsg = QTime::currentTime();
2032  && gCoreContext->GetBoolSetting("MusicChoiceEnabled", false))
2033  {
2034  m_playerFlags = static_cast<PlayerFlags>(m_playerFlags | kMusicChoice);
2035  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2036  "Music Choice program detected - disabling AV Sync.");
2037  m_avsyncAudioPaused = false;
2038  m_audio.Pause(false);
2039  }
2040  if (waited_for > 7000 && m_audio.IsBufferAlmostFull()
2041  && !FlagIsSet(kMusicChoice))
2042  {
2043  // We are likely to enter this condition
2044  // if the audio buffer was too full during GetFrame in AVFD
2045  LOG(VB_GENERAL, LOG_NOTICE, LOC + "Resetting audio buffer");
2046  m_audio.Reset();
2047  }
2048  // Finish audio pause for sync after 1 second
2049  // in case of infrequent video frames (e.g. music choice)
2050  if (m_avsyncAudioPaused && waited_for > 1000)
2051  {
2052  m_avsyncAudioPaused = false;
2053  m_audio.Pause(false);
2054  }
2055  }
2056  int msecs = 500;
2057  if (preBufferDebug)
2058  msecs = 1800000;
2059  if ((waited_for > msecs /*500*/) && !m_videoOutput->EnoughFreeFrames())
2060  {
2061  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2062  "Timed out waiting for frames, and"
2063  "\n\t\t\tthere are not enough free frames. "
2064  "Discarding buffered frames.");
2065  // This call will result in some ugly frames, but allows us
2066  // to recover from serious problems if frames get leaked.
2067  DiscardVideoFrames(true, true);
2068  }
2069  msecs = 30000;
2070  if (preBufferDebug)
2071  msecs = 1800000;
2072  if (waited_for > msecs /*30000*/) // 30 seconds for internet streamed media
2073  {
2074  LOG(VB_GENERAL, LOG_ERR, LOC +
2075  "Waited too long for decoder to fill video buffers. Exiting..");
2076  SetErrored(tr("Video frame buffering failed too many times."));
2077  }
2078  return false;
2079  }
2080 
2081  if (!m_avsyncAudioPaused)
2082  m_audio.Pause(false);
2083  SetBuffering(false);
2084  return true;
2085 }
2086 
2088 {
2089  if (!frame)
2090  return;
2091 
2092  if (!qFuzzyCompare(frame->aspect, m_videoAspect) && frame->aspect > 0.0F)
2093  {
2094  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2095  QString("Video Aspect ratio changed from %1 to %2")
2096  .arg(m_videoAspect).arg(frame->aspect));
2097  m_videoAspect = frame->aspect;
2098  if (m_videoOutput)
2099  {
2101  ReinitOSD();
2102  }
2103  }
2104 }
2105 
2106 void MythPlayer::DisplayNormalFrame(bool check_prebuffer)
2107 {
2108  if (m_allPaused)
2109  return;
2110 
2111  bool ispip = m_playerCtx->IsPIP();
2112  if (ispip)
2113  {
2115  return;
2116  }
2117  else if (check_prebuffer && !PrebufferEnoughFrames())
2118  {
2119  return;
2120  }
2121 
2122  // clear the buffering state
2123  SetBuffering(false);
2124 
2125  // If PiP then release the last shown frame to the decoding queue
2126  if (ispip)
2127  if (!PipSync())
2128  return;
2129 
2130  // retrieve the next frame
2133 
2134  // Check aspect ratio
2135  CheckAspectRatio(frame);
2136 
2138  UpdateFFRewSkip();
2139 
2140  // Player specific processing (dvd, bd, mheg etc)
2142 
2143  // handle scan type changes
2144  AutoDeint(frame);
2145  m_detectLetterBox->SwitchTo(frame);
2146 
2147  if (!ispip)
2148  AVSync(frame);
2149 
2150  // Update details for debug OSD
2153  // We use the underlying pix_fmt as it retains the distinction between hardware
2154  // and software frames for decode only decoders.
2155  m_lastFrameCodec = PixelFormatToFrameType(static_cast<AVPixelFormat>(frame->pix_fmt));
2156 
2157  // If PiP then keep this frame for MythPlayer::GetCurrentFrame
2158  if (!ispip)
2160 }
2161 
2163 {
2164 #ifdef USING_MHEG
2165  // handle Interactive TV
2166  if (GetInteractiveTV())
2167  {
2168  m_osdLock.lock();
2169  m_itvLock.lock();
2170  if (m_osd && m_videoOutput->GetOSDPainter())
2171  {
2172  InteractiveScreen *window =
2173  dynamic_cast<InteractiveScreen *>(m_osd->GetWindow(OSD_WIN_INTERACT));
2174  if ((m_interactiveTV->ImageHasChanged() || !m_itvVisible) && window)
2175  {
2177  m_itvVisible = true;
2178  }
2179  }
2180  m_itvLock.unlock();
2181  m_osdLock.unlock();
2182  }
2183 #endif // USING_MHEG
2184 }
2185 
2187 {
2188  int refreshinterval = 1;
2189  if (m_display)
2190  refreshinterval = m_display->GetRefreshInterval(m_frameInterval);
2191 
2192  // At this point we may not have the correct frame rate.
2193  // Since interlaced is always at 25 or 30 fps, if the interval
2194  // is less than 30000 (33fps) it must be representing one
2195  // field and not one frame, so multiply by 2.
2196  int realfi = m_frameInterval;
2197  if (m_frameInterval < 30000)
2198  realfi = m_frameInterval * 2;
2199  return ((realfi / 2.0) > (refreshinterval * 0.995));
2200 }
2201 
2203 {
2204  if (!m_outputJmeter)
2205  return;
2206  bool verbose = VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_ANY);
2207  double rate = enable ? m_videoFrameRate : verbose ? (m_videoFrameRate * 4) : 0.0;
2208  m_outputJmeter->SetNumCycles(static_cast<int>(rate));
2209 }
2210 
2211 void MythPlayer::ForceDeinterlacer(bool DoubleRate, MythDeintType Deinterlacer)
2212 {
2213  if (m_videoOutput)
2214  m_videoOutput->SetDeinterlacing(true, DoubleRate, Deinterlacer);
2215 }
2216 
2218 {
2219  if (!FlagIsSet(kVideoIsNull) && !m_playerCtx->IsPIP())
2220  {
2221  QRect visible;
2222  QRect total;
2223  float aspect = NAN;
2224  float scaling = NAN;
2225 
2226  m_osdLock.lock();
2227  m_osd = new OSD(this, m_tv, m_videoOutput->GetOSDPainter());
2228  m_videoOutput->GetOSDBounds(total, visible, aspect, scaling, 1.0F);
2229  m_osd->Init(visible, aspect);
2231 
2232 #ifdef USING_MHEG
2233  if (GetInteractiveTV())
2234  {
2235  QMutexLocker locker(&m_itvLock);
2236  m_interactiveTV->Reinit(total, visible, aspect);
2237  }
2238 #endif // USING_MHEG
2239 
2240  // If there is a forced text subtitle track (which is possible
2241  // in e.g. a .mkv container), and forced subtitles are
2242  // allowed, then start playback with that subtitle track
2243  // selected. Otherwise, use the frontend settings to decide
2244  // which captions/subtitles (if any) to enable at startup.
2245  // TODO: modify the fix to #10735 to use this approach
2246  // instead.
2247  bool hasForcedTextTrack = false;
2248  uint forcedTrackNumber = 0;
2250  {
2251  uint numTextTracks = m_decoder->GetTrackCount(kTrackTypeRawText);
2252  for (uint i = 0; !hasForcedTextTrack && i < numTextTracks; ++i)
2253  {
2255  {
2256  hasForcedTextTrack = true;
2257  forcedTrackNumber = i;
2258  }
2259  }
2260  }
2261  if (hasForcedTextTrack)
2262  SetTrack(kTrackTypeRawText, forcedTrackNumber);
2263  else
2265 
2266  m_osdLock.unlock();
2267  }
2268 
2269  SetPlaying(true);
2270  ClearAfterSeek(false);
2271 
2272  m_avsyncAvg = 0; // Frames till next sync check
2273 
2275 
2276  // Default to interlaced playback but set the tracker to progressive
2277  // Enable autodetection of interlaced/progressive from video stream
2278  // Previously we set to interlaced and the scan tracker to 2 but this
2279  // mis-'detected' a number of streams as interlaced when they are progressive.
2280  // This significantly reduces the number of errors and also ensures we do not
2281  // needlessly setup deinterlacers - which may consume significant resources.
2282  // We set to interlaced for those streams whose frame rate is initially detected
2283  // as e.g. 59.9 when it is actually 29.97 interlaced.
2285  m_scanLocked = false;
2286  m_doubleFramerate = false;
2287  m_scanTracker = -2;
2288 
2290  {
2293  }
2294 
2295  InitAVSync();
2296  AutoVisualise();
2297 }
2298 
2300 {
2301  ProcessCallbacks();
2302 
2303  if (m_videoPaused || m_isDummy)
2304  {
2305  switch (m_playerCtx->GetPIPState())
2306  {
2307  case kPIPonTV:
2308  case kPBPRight:
2309  break;
2310  case kPIPOff:
2311  case kPIPStandAlone:
2312  case kPBPLeft: // PrimaryBPB
2313  usleep(m_frameInterval);
2314  break;
2315  }
2317  }
2318  else
2320 
2323  else if (m_decoder && m_decoder->GetEof() != kEofStateNone)
2324  ++m_framesPlayed;
2325  else
2327  return !IsErrored();
2328 }
2329 
2331 {
2332  m_osdLock.lock();
2333  m_vidExitLock.lock();
2334  delete m_osd;
2335  delete m_videoOutput;
2336  m_osd = nullptr;
2337  m_videoOutput = nullptr;
2338  m_vidExitLock.unlock();
2339  m_osdLock.unlock();
2340 }
2341 
2342 bool MythPlayer::FastForward(float seconds)
2343 {
2344  if (!m_videoOutput)
2345  return false;
2346 
2347  if (m_ffTime <= 0)
2348  {
2349  float current = ComputeSecs(m_framesPlayed, true);
2350  float dest = current + seconds;
2351  float length = ComputeSecs(m_totalFrames, true);
2352 
2353  if (dest > length)
2354  {
2355  int64_t pos = TranslatePositionMsToFrame(seconds * 1000, false);
2356  if (CalcMaxFFTime(pos) < 0)
2357  return true;
2358  // Reach end of recording, go to 1 or 3s before the end
2359  dest = (m_liveTV || IsWatchingInprogress()) ? -3.0 : -1.0;
2360  }
2361  uint64_t target = FindFrame(dest, true);
2362  m_ffTime = target - m_framesPlayed;
2363  }
2364  return m_ffTime > CalcMaxFFTime(m_ffTime, false);
2365 }
2366 
2367 bool MythPlayer::Rewind(float seconds)
2368 {
2369  if (!m_videoOutput)
2370  return false;
2371 
2372  if (m_rewindTime <= 0)
2373  {
2374  float current = ComputeSecs(m_framesPlayed, true);
2375  float dest = current - seconds;
2376  if (dest < 0)
2377  {
2378  int64_t pos = TranslatePositionMsToFrame(seconds * 1000, false);
2379  if (CalcRWTime(pos) < 0)
2380  return true;
2381  dest = 0;
2382  }
2383  uint64_t target = FindFrame(dest, true);
2384  m_rewindTime = m_framesPlayed - target;
2385  }
2386  return (uint64_t)m_rewindTime >= m_framesPlayed;
2387 }
2388 
2389 bool MythPlayer::JumpToFrame(uint64_t frame)
2390 {
2391  if (!m_videoOutput)
2392  return false;
2393 
2394  bool ret = false;
2395  m_ffTime = m_rewindTime = 0;
2396  if (frame > m_framesPlayed)
2397  {
2398  m_ffTime = frame - m_framesPlayed;
2399  ret = m_ffTime > CalcMaxFFTime(m_ffTime, false);
2400  }
2401  else if (frame < m_framesPlayed)
2402  {
2403  m_rewindTime = m_framesPlayed - frame;
2404  ret = m_ffTime > CalcMaxFFTime(m_ffTime, false);
2405  }
2406  return ret;
2407 }
2408 
2409 
2410 void MythPlayer::JumpChapter(int chapter)
2411 {
2412  if (m_jumpChapter == 0)
2413  m_jumpChapter = chapter;
2414 }
2415 
2416 void MythPlayer::ResetPlaying(bool resetframes)
2417 {
2418  ClearAfterSeek();
2419  m_ffrewSkip = 1;
2420  if (resetframes)
2421  m_framesPlayed = 0;
2422  if (m_decoder)
2423  {
2424  m_decoder->Reset(true, true, true);
2425  if (m_decoder->IsErrored())
2426  SetErrored("Unable to reset video decoder");
2427  }
2428 }
2429 
2431 {
2432  bool last = !(m_playerCtx->m_tvchain->HasNext());
2433  SetWatchingRecording(last);
2434 }
2435 
2437 {
2438  if (!IsReallyNearEnd())
2439  return;
2440 
2441  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchToProgram - start");
2442  bool discontinuity = false;
2443  bool newtype = false;
2444  int newid = -1;
2446  discontinuity, newtype, newid);
2447  if (!pginfo)
2448  return;
2449 
2450  bool newIsDummy = m_playerCtx->m_tvchain->GetInputType(newid) == "DUMMY";
2451 
2452  SetPlayingInfo(*pginfo);
2453  Pause();
2454  ChangeSpeed();
2455 
2456  // Release all frames to ensure the current decoder resources are released
2457  DiscardVideoFrames(true, true);
2458 
2459  if (newIsDummy)
2460  {
2461  OpenDummy();
2462  ResetPlaying();
2464  delete pginfo;
2465  return;
2466  }
2467 
2469  {
2470  // Restore original ringbuffer
2471  auto *ic = dynamic_cast<MythInteractiveBuffer*>(m_playerCtx->m_buffer);
2472  if (ic)
2473  m_playerCtx->m_buffer = ic->TakeBuffer();
2474  delete ic;
2475  }
2476 
2479 
2480  if (!m_playerCtx->m_buffer->IsOpen())
2481  {
2482  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram's OpenFile failed " +
2483  QString("(input type: %1).")
2484  .arg(m_playerCtx->m_tvchain->GetInputType(newid)));
2485  LOG(VB_GENERAL, LOG_ERR, m_playerCtx->m_tvchain->toString());
2487  SetErrored(tr("Error opening switch program buffer"));
2488  delete pginfo;
2489  return;
2490  }
2491 
2492  if (GetEof() != kEofStateNone)
2493  {
2494  discontinuity = true;
2495  ResetCaptions();
2496  }
2497 
2498  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SwitchToProgram(void) "
2499  "discont: %1 newtype: %2 newid: %3 decoderEof: %4")
2500  .arg(discontinuity).arg(newtype).arg(newid).arg(GetEof()));
2501 
2502  if (discontinuity || newtype)
2503  {
2504  m_playerCtx->m_tvchain->SetProgram(*pginfo);
2505  if (m_decoder)
2506  m_decoder->SetProgramInfo(*pginfo);
2507 
2508  m_playerCtx->m_buffer->Reset(true);
2509  if (newtype)
2510  {
2511  if (OpenFile() < 0)
2512  SetErrored(tr("Error opening switch program file"));
2513  }
2514  else
2515  ResetPlaying();
2516  }
2517  else
2518  {
2520  if (m_decoder)
2521  {
2524  }
2525  }
2526  delete pginfo;
2527 
2528  if (IsErrored())
2529  {
2530  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram failed.");
2532  return;
2533  }
2534 
2536 
2537  // the bitrate is reset by m_playerCtx->m_buffer->OpenFile()...
2538  if (m_decoder)
2541 
2542  if (discontinuity || newtype)
2543  {
2544  CheckTVChain();
2545  m_forcePositionMapSync = true;
2546  }
2547 
2548  Play();
2549  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchToProgram - end");
2550 }
2551 
2552 // This is called from decoder thread. Set an indicator that will
2553 // be checked and actioned in the player thread.
2555 {
2556  LOG(VB_PLAYBACK, LOG_INFO, LOC + "FileChangedCallback");
2557  m_fileChanged = true;
2558 }
2559 
2560 // Called from the player thread.
2562 {
2563  m_fileChanged = false;
2564  LOG(VB_PLAYBACK, LOG_INFO, LOC + "FileChanged");
2565 
2566  Pause();
2567  ChangeSpeed();
2568  if (dynamic_cast<AvFormatDecoder *>(m_decoder))
2569  m_playerCtx->m_buffer->Reset(false, true);
2570  else
2571  m_playerCtx->m_buffer->Reset(false, true, true);
2573  Play();
2574 
2576 
2577  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
2579  if (m_decoder)
2581  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
2582 
2583  CheckTVChain();
2584  m_forcePositionMapSync = true;
2585 }
2586 
2587 
2588 
2589 
2591 {
2592  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToProgram - start");
2593  bool discontinuity = false;
2594  bool newtype = false;
2595  int newid = -1;
2596  long long nextpos = m_playerCtx->m_tvchain->GetJumpPos();
2598  discontinuity, newtype, newid);
2599  if (!pginfo)
2600  return;
2601 
2602  m_inJumpToProgramPause = true;
2603 
2604  bool newIsDummy = m_playerCtx->m_tvchain->GetInputType(newid) == "DUMMY";
2605  SetPlayingInfo(*pginfo);
2606 
2607  Pause();
2608  ChangeSpeed();
2609  ResetCaptions();
2610 
2611  // Release all frames to ensure the current decoder resources are released
2612  DiscardVideoFrames(true, true);
2613 
2614  m_playerCtx->m_tvchain->SetProgram(*pginfo);
2615  m_playerCtx->m_buffer->Reset(true);
2616 
2617  if (newIsDummy)
2618  {
2619  OpenDummy();
2620  ResetPlaying();
2622  delete pginfo;
2623  m_inJumpToProgramPause = false;
2624  return;
2625  }
2626 
2627  SendMythSystemPlayEvent("PLAY_CHANGED", pginfo);
2628 
2630  {
2631  // Restore original ringbuffer
2632  auto *ic = dynamic_cast<MythInteractiveBuffer*>(m_playerCtx->m_buffer);
2633  if (ic)
2634  m_playerCtx->m_buffer = ic->TakeBuffer();
2635  delete ic;
2636  }
2637 
2639  QString subfn = m_playerCtx->m_buffer->GetSubtitleFilename();
2640  TVState desiredState = m_playerCtx->GetState();
2641  bool isInProgress = (desiredState == kState_WatchingRecording ||
2642  desiredState == kState_WatchingLiveTV);
2643  if (GetSubReader())
2644  GetSubReader()->LoadExternalSubtitles(subfn, isInProgress &&
2645  !subfn.isEmpty());
2646 
2647  if (!m_playerCtx->m_buffer->IsOpen())
2648  {
2649  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram's OpenFile failed " +
2650  QString("(input type: %1).")
2651  .arg(m_playerCtx->m_tvchain->GetInputType(newid)));
2652  LOG(VB_GENERAL, LOG_ERR, m_playerCtx->m_tvchain->toString());
2654  SetErrored(tr("Error opening jump program file buffer"));
2655  delete pginfo;
2656  m_inJumpToProgramPause = false;
2657  return;
2658  }
2659 
2660  bool wasDummy = m_isDummy;
2661  if (newtype || wasDummy)
2662  {
2663  if (OpenFile() < 0)
2664  SetErrored(tr("Error opening jump program file"));
2665  }
2666  else
2667  ResetPlaying();
2668 
2669  if (IsErrored() || !m_decoder)
2670  {
2671  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram failed.");
2672  if (!IsErrored())
2673  SetErrored(tr("Error reopening video decoder"));
2674  delete pginfo;
2675  m_inJumpToProgramPause = false;
2676  return;
2677  }
2678 
2680 
2681  // the bitrate is reset by m_playerCtx->m_buffer->OpenFile()...
2684 
2685  m_decoder->SetProgramInfo(*pginfo);
2686  delete pginfo;
2687 
2688  CheckTVChain();
2689  m_forcePositionMapSync = true;
2690  m_inJumpToProgramPause = false;
2691  Play();
2692  ChangeSpeed();
2693 
2694  // check that we aren't too close to the end of program.
2695  // and if so set it to 10s from the end if completed recordings
2696  // or 3s if live
2697  long long duration = m_playerCtx->m_tvchain->GetLengthAtCurPos();
2698  int maxpos = m_playerCtx->m_tvchain->HasNext() ? 10 : 3;
2699 
2700  if (nextpos > (duration - maxpos))
2701  {
2702  nextpos = duration - maxpos;
2703  if (nextpos < 0)
2704  {
2705  nextpos = 0;
2706  }
2707  }
2708  else if (nextpos < 0)
2709  {
2710  // it's a relative position to the end
2711  nextpos += duration;
2712  }
2713 
2714  // nextpos is the new position to use in seconds
2715  nextpos = TranslatePositionMsToFrame(nextpos * 1000, true);
2716 
2717  if (nextpos > 10)
2718  DoJumpToFrame(nextpos, kInaccuracyNone);
2719 
2721  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToProgram - end");
2722 }
2723 
2725 {
2726  if (OpenFile() < 0)
2727  {
2728  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to open video file.");
2729  return false;
2730  }
2731 
2732  m_framesPlayed = 0;
2733  m_rewindTime = m_ffTime = 0;
2735  m_jumpChapter = 0;
2738 
2739  if (!InitVideo())
2740  {
2741  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to initialize video.");
2743  return false;
2744  }
2745 
2746  bool seek = m_bookmarkSeek > 30;
2747  EventStart();
2748  DecoderStart(true);
2749  if (seek)
2750  InitialSeek();
2751  VideoStart();
2752 
2753  m_playerThread->setPriority(QThread::TimeCriticalPriority);
2754 #ifdef Q_OS_ANDROID
2755  setpriority(PRIO_PROCESS, m_playerThreadId, -20);
2756 #endif
2757  ProcessCallbacks();
2758  UnpauseDecoder();
2759  return !IsErrored();
2760 }
2761 
2763 {
2764  // TODO handle initial commskip and/or cutlist skip as well
2765  if (m_bookmarkSeek > 30)
2766  {
2769  SetBookmark(true);
2770  }
2771 }
2772 
2773 
2775 {
2776  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StopPlaying - begin"));
2777  m_playerThread->setPriority(QThread::NormalPriority);
2778 #ifdef Q_OS_ANDROID
2779  setpriority(PRIO_PROCESS, m_playerThreadId, 0);
2780 #endif
2781 
2782  ProcessCallbacks();
2783  DecoderEnd();
2784  VideoEnd();
2785  AudioEnd();
2786 
2787  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StopPlaying - end"));
2788 }
2789 
2791 {
2792  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
2793  {
2795  {
2796  // When initial playback gets underway, we override the ProgramInfo
2797  // flags such that future calls to GetBookmark() will consider only
2798  // an actual bookmark and not progstart or lastplaypos information.
2802  }
2803  }
2804  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
2806 }
2807 
2809 {
2810  // Handle decoder callbacks
2811  ProcessCallbacks();
2812 
2813  // Live TV program change
2814  if (m_fileChanged)
2815  FileChanged();
2816 
2817  // recreate the osd if a reinit was triggered by another thread
2818  if (m_reinitOsd)
2819  ReinitOSD();
2820 
2821  // reselect subtitle tracks if triggered by the decoder
2822  if (m_enableCaptions)
2823  SetCaptionsEnabled(true, false);
2824  if (m_disableCaptions)
2825  SetCaptionsEnabled(false, false);
2826 
2827  // enable/disable forced subtitles if signalled by the decoder
2832 
2833  // reset the scan (and hence deinterlacers) if triggered by the decoder
2834  if (m_resetScan != kScan_Ignore)
2836 
2837  // refresh the position map for an in-progress recording while editing
2839  {
2840  if (m_editUpdateTimer.hasExpired(2000))
2841  {
2842  // N.B. the positionmap update and osd refresh are asynchronous
2843  m_forcePositionMapSync = true;
2844  m_osdLock.lock();
2846  m_osdLock.unlock();
2847  m_editUpdateTimer.start();
2848  }
2849  }
2850 
2851  // Refresh the programinfo in use status
2852  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
2855  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
2856 
2857  // Disable timestretch if we are too close to the end of the buffer
2858  if (m_ffrewSkip == 1 && (m_playSpeed > 1.0F) && IsNearEnd())
2859  {
2860  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near end, Slowing down playback.");
2861  Play(1.0F, true, true);
2862  }
2863 
2865  {
2866  // Switch from the dummy recorder to the tuned program in livetv
2867  m_playerCtx->m_tvchain->JumpToNext(true, 0);
2868  JumpToProgram();
2869  }
2870  else if ((!m_allPaused || GetEof() != kEofStateNone) &&
2873  {
2874  // Switch to the next program in livetv
2876  SwitchToProgram();
2877  }
2878 
2879  // Jump to the next program in livetv
2881  {
2882  JumpToProgram();
2883  }
2884 
2885  // Change interactive stream if requested
2886  { QMutexLocker locker(&m_streamLock);
2887  if (!m_newStream.isEmpty())
2888  {
2889  QString stream = m_newStream;
2890  m_newStream.clear();
2891  locker.unlock();
2892  JumpToStream(stream);
2893  }}
2894 
2895  // Disable fastforward if we are too close to the end of the buffer
2896  if (m_ffrewSkip > 1 && (CalcMaxFFTime(100, false) < 100))
2897  {
2898  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near end, stopping fastforward.");
2899  Play(1.0F, true, true);
2900  }
2901 
2902  // Disable rewind if we are too close to the beginning of the buffer
2903  if (m_ffrewSkip < 0 && CalcRWTime(-m_ffrewSkip) >= 0 &&
2905  {
2906  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near start, stopping rewind.");
2907  float stretch = (m_ffrewSkip > 0) ? 1.0F : m_audio.GetStretchFactor();
2908  Play(stretch, true, true);
2909  }
2910 
2911  // Check for error
2913  {
2914  LOG(VB_GENERAL, LOG_ERR, LOC +
2915  "Unknown recorder error, exiting decoder");
2916  if (!IsErrored())
2917  SetErrored(tr("Irrecoverable recorder error"));
2918  m_killDecoder = true;
2919  return;
2920  }
2921 
2922  // Handle speed change
2923  if (m_playSpeed != m_nextPlaySpeed &&
2924  (!m_playerCtx->m_tvchain ||
2926  {
2927  ChangeSpeed();
2928  return;
2929  }
2930 
2931  // Check if we got a communication error, and if so pause playback
2933  {
2934  Pause();
2936  }
2937 
2938  // Handle end of file
2939  EofState eof = GetEof();
2940  if (HasReachedEof())
2941  {
2942 #ifdef USING_MHEG
2944  {
2945  Pause();
2946  return;
2947  }
2948 #endif
2950  {
2951  LOG(VB_GENERAL, LOG_NOTICE, LOC + "LiveTV forcing JumpTo 1");
2952  m_playerCtx->m_tvchain->JumpToNext(true, 0);
2953  return;
2954  }
2955 
2956  bool videoDrained =
2958  bool audioDrained =
2959  !m_audio.GetAudioOutput() ||
2960  m_audio.IsPaused() ||
2962  if (eof != kEofStateDelayed || (videoDrained && audioDrained))
2963  {
2964  if (eof == kEofStateDelayed)
2965  {
2966  LOG(VB_PLAYBACK, LOG_INFO,
2967  QString("waiting for no video frames %1")
2968  .arg(m_videoOutput->ValidVideoFrames()));
2969  }
2970  LOG(VB_PLAYBACK, LOG_INFO,
2971  QString("HasReachedEof() at framesPlayed=%1 totalFrames=%2")
2972  .arg(m_framesPlayed).arg(GetCurrentFrameCount()));
2973  Pause();
2974  SetPlaying(false);
2975  return;
2976  }
2977  }
2978 
2979  // Handle rewind
2980  if (m_rewindTime > 0 && (m_ffrewSkip == 1 || m_ffrewSkip == 0))
2981  {
2983  if (m_rewindTime > 0)
2985  }
2986 
2987  // Handle fast forward
2988  if (m_ffTime > 0 && (m_ffrewSkip == 1 || m_ffrewSkip == 0))
2989  {
2991  if (m_ffTime > 0)
2992  {
2994  if (GetEof() != kEofStateNone)
2995  return;
2996  }
2997  }
2998 
2999  // Handle chapter jump
3000  if (m_jumpChapter != 0)
3002 
3003  // Handle commercial skipping
3004  if (m_commBreakMap.GetSkipCommercials() != 0 && (m_ffrewSkip == 1))
3005  {
3006  if (!m_commBreakMap.HasMap())
3007  {
3008  //: The commercials/adverts have not been flagged
3009  SetOSDStatus(tr("Not Flagged"), kOSDTimeout_Med);
3010  QString message = "COMMFLAG_REQUEST ";
3011  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3012  message += QString("%1").arg(m_playerCtx->m_playingInfo->GetChanID()) +
3014  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3015  gCoreContext->SendMessage(message);
3016  }
3017  else
3018  {
3019  QString msg;
3020  uint64_t jumpto = 0;
3021  uint64_t frameCount = GetCurrentFrameCount();
3022  // XXX CommBreakMap should use duration map not m_videoFrameRate
3023  bool jump = m_commBreakMap.DoSkipCommercials(jumpto, m_framesPlayed,
3025  frameCount, msg);
3026  if (!msg.isEmpty())
3028  if (jump)
3029  DoJumpToFrame(jumpto, kInaccuracyNone);
3030  }
3032  return;
3033  }
3034 
3035  // Handle automatic commercial skipping
3036  uint64_t jumpto = 0;
3037  if (m_deleteMap.IsEmpty() && (m_ffrewSkip == 1) &&
3040  {
3041  QString msg;
3042  uint64_t frameCount = GetCurrentFrameCount();
3043  // XXX CommBreakMap should use duration map not m_videoFrameRate
3044  bool jump = m_commBreakMap.AutoCommercialSkip(jumpto, m_framesPlayed,
3046  frameCount, msg);
3047  if (!msg.isEmpty())
3049  if (jump)
3050  DoJumpToFrame(jumpto, kInaccuracyNone);
3051  }
3052 
3053  // Handle cutlist skipping
3054  if (!m_allPaused && (m_ffrewSkip == 1) &&
3056  {
3057  if (jumpto == m_totalFrames)
3058  {
3059  if (!(m_endExitPrompt == 1 && !m_playerCtx->IsPIP() &&
3061  {
3063  }
3064  }
3065  else
3066  {
3067  DoJumpToFrame(jumpto, kInaccuracyNone);
3068  }
3069  }
3070 }
3071 
3073 {
3075 }
3076 
3083  void *Opaque1, void *Opaque2)
3084 {
3085  if (!Function)
3086  return;
3087 
3088  m_decoderCallbackLock.lock();
3089  QAtomicInt ready{0};
3090  QWaitCondition wait;
3091  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Queuing callback for %1").arg(Debug));
3092  m_decoderCallbacks.append(DecoderCallback(Debug, Function, &ready, &wait, Opaque1, Opaque2));
3093  int count = 0;
3094  while (!ready && !wait.wait(&m_decoderCallbackLock, 100) && (count += 100))
3095  LOG(VB_GENERAL, LOG_WARNING, QString("Waited %1ms for %2").arg(count).arg(Debug));
3096  m_decoderCallbackLock.unlock();
3097 }
3098 
3100 {
3101  m_decoderCallbackLock.lock();
3102  for (auto *it = m_decoderCallbacks.begin(); it != m_decoderCallbacks.end(); ++it)
3103  {
3104  if (it->m_function)
3105  {
3106  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Executing %1").arg(it->m_debug));
3107  it->m_function(it->m_opaque1, it->m_opaque2, it->m_opaque3);
3108  }
3109  if (it->m_ready)
3110  it->m_ready->ref();
3111  }
3112  m_decoderCallbacks.clear();
3113  m_decoderCallbackLock.unlock();
3114 }
3115 
3117 {
3118  m_decoderPauseLock.lock();
3120  {
3121  m_decoderPaused = true;
3122  m_decoderThreadPause.wakeAll();
3123  m_decoderPauseLock.unlock();
3124  return m_decoderPaused;
3125  }
3126 
3127  int tries = 0;
3128  m_pauseDecoder = true;
3129  while (m_decoderThread && !m_killDecoder && (tries++ < 100) &&
3131  {
3132  ProcessCallbacks();
3133  LOG(VB_GENERAL, LOG_WARNING, LOC + "Waited 100ms for decoder to pause");
3134  }
3135  m_pauseDecoder = false;
3136  m_decoderPauseLock.unlock();
3137  return m_decoderPaused;
3138 }
3139 
3141 {
3142  m_decoderPauseLock.lock();
3143 
3145  {
3146  m_decoderPaused = false;
3147  m_decoderThreadUnpause.wakeAll();
3148  m_decoderPauseLock.unlock();
3149  return;
3150  }
3151 
3152  if (!IsInStillFrame())
3153  {
3154  int tries = 0;
3155  m_unpauseDecoder = true;
3156  while (m_decoderThread && !m_killDecoder && (tries++ < 100) &&
3158  {
3159  ProcessCallbacks();
3160  LOG(VB_GENERAL, LOG_WARNING, LOC + "Waited 100ms for decoder to unpause");
3161  }
3162  m_unpauseDecoder = false;
3163  }
3164  m_decoderPauseLock.unlock();
3165 }
3166 
3167 void MythPlayer::DecoderStart(bool start_paused)
3168 {
3169  if (m_decoderThread)
3170  {
3171  if (m_decoderThread->isRunning())
3172  {
3173  LOG(VB_GENERAL, LOG_ERR, LOC + "Decoder thread already running");
3174  }
3175  delete m_decoderThread;
3176  }
3177 
3178  m_killDecoder = false;
3179  m_decoderPaused = start_paused;
3180  m_decoderThread = new DecoderThread(this, start_paused);
3181  if (m_decoderThread)
3183 }
3184 
3186 {
3187  PauseDecoder();
3188  SetPlaying(false);
3189  // Ensure any hardware frames are released (after pausing the decoder) to
3190  // allow the decoder to exit cleanly
3191  DiscardVideoFrames(true, true);
3192 
3193  m_killDecoder = true;
3194  int tries = 0;
3195  while (m_decoderThread && !m_decoderThread->wait(100) && (tries++ < 50))
3196  LOG(VB_PLAYBACK, LOG_INFO, LOC +
3197  "Waited 100ms for decoder loop to stop");
3198 
3200  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to stop decoder loop.");
3201  else
3202  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exited decoder loop.");
3203  SetDecoder(nullptr);
3204 }
3205 
3207 {
3209  {
3210  if (m_pauseDecoder)
3211  PauseDecoder();
3212  if (m_unpauseDecoder)
3213  UnpauseDecoder();
3214  }
3215 }
3216 
3219 {
3222 
3223  if (!m_decoderChangeLock.tryLock(50))
3224  return kEofStateNone;
3225 
3227  m_decoderChangeLock.unlock();
3228  return eof;
3229 }
3230 
3232 {
3234  {
3235  if (m_decoder)
3236  m_decoder->SetEofState(eof);
3237  return;
3238  }
3239 
3240  if (!m_decoderChangeLock.tryLock(50))
3241  return;
3242 
3243  if (m_decoder)
3244  m_decoder->SetEofState(eof);
3245  m_decoderChangeLock.unlock();
3246 }
3248 
3249 void MythPlayer::DecoderLoop(bool pause)
3250 {
3251  if (pause)
3252  PauseDecoder();
3253 
3254  while (!m_killDecoder && !IsErrored())
3255  {
3257 
3259  {
3260  usleep(1000);
3261  continue;
3262  }
3263 
3265  {
3266  if (!m_decoderChangeLock.tryLock(1))
3267  continue;
3268  if (m_decoder)
3269  {
3270  m_forcePositionMapSync = false;
3272  }
3273  m_decoderChangeLock.unlock();
3274  }
3275 
3276  if (m_decoderSeek >= 0)
3277  {
3278  if (!m_decoderChangeLock.tryLock(1))
3279  continue;
3280  if (m_decoder)
3281  {
3282  m_decoderSeekLock.lock();
3283  if (((uint64_t)m_decoderSeek < m_framesPlayed) && m_decoder)
3285  else if (m_decoder)
3287  m_decoderSeek = -1;
3288  m_decoderSeekLock.unlock();
3289  }
3290  m_decoderChangeLock.unlock();
3291  }
3292 
3293  bool obey_eof = (GetEof() != kEofStateNone) &&
3295  if (m_isDummy || ((m_decoderPaused || m_ffrewSkip == 0 || obey_eof) &&
3296  !m_decodeOneFrame))
3297  {
3298  usleep(1000);
3299  continue;
3300  }
3301 
3304 
3305  DecoderGetFrame(dt);
3306  m_decodeOneFrame = false;
3307  }
3308 
3309  // Clear any wait conditions
3311  m_decoderSeek = -1;
3312 }
3313 
3315 {
3316  if (!m_decoder)
3317  return false;
3318 
3319  if (m_ffrewSkip > 0)
3320  {
3321  long long delta = m_decoder->GetFramesRead() - m_framesPlayed;
3322  long long real_skip = CalcMaxFFTime(m_ffrewSkip - m_ffrewAdjust + delta) - delta;
3323  long long target_frame = m_decoder->GetFramesRead() + real_skip;
3324  if (real_skip >= 0)
3325  {
3326  m_decoder->DoFastForward(target_frame, false);
3327  }
3328  long long seek_frame = m_decoder->GetFramesRead();
3329  m_ffrewAdjust = seek_frame - target_frame;
3330  }
3331  else if (CalcRWTime(-m_ffrewSkip) >= 0)
3332  {
3334  }
3336 }
3337 
3339 {
3340  long long cur_frame = m_decoder->GetFramesPlayed();
3341  bool toBegin = -cur_frame > m_ffrewSkip + m_ffrewAdjust;
3342  long long real_skip = (toBegin) ? -cur_frame : m_ffrewSkip + m_ffrewAdjust;
3343  long long target_frame = cur_frame + real_skip;
3344  bool ret = m_decoder->DoRewind(target_frame, false);
3345  long long seek_frame = m_decoder->GetFramesPlayed();
3346  m_ffrewAdjust = target_frame - seek_frame;
3347  return ret;
3348 }
3349 
3350 bool MythPlayer::DecoderGetFrame(DecodeType decodetype, bool unsafe)
3351 {
3352  bool ret = false;
3353  if (!m_videoOutput)
3354  return false;
3355 
3356  // Wait for frames to be available for decoding onto
3357  int tries = 0;
3358  while (!unsafe &&
3360  {
3362  return false;
3363 
3364  if (++tries > 10)
3365  {
3366  if (++m_videobufRetries >= 2000)
3367  {
3368  LOG(VB_GENERAL, LOG_ERR, LOC +
3369  "Decoder timed out waiting for free video buffers.");
3370  // We've tried for 20 seconds now, give up so that we don't
3371  // get stuck permanently in this state
3372  SetErrored("Decoder timed out waiting for free video buffers.");
3373  }
3374  return false;
3375  }
3376 
3377  usleep(1000);
3378  }
3379  m_videobufRetries = 0;
3380 
3381  if (!m_decoderChangeLock.tryLock(5))
3382  return false;
3384  {
3385  m_decoderChangeLock.unlock();
3386  return false;
3387  }
3388 
3389  if (m_ffrewSkip == 1 || m_decodeOneFrame)
3390  ret = DoGetFrame(decodetype);
3391  else if (m_ffrewSkip != 0)
3392  ret = DecoderGetFrameFFREW();
3393  m_decoderChangeLock.unlock();
3394  return ret;
3395 }
3396 
3411 {
3412  bool ret = false;
3413  QElapsedTimer timeout;
3414  timeout.start();
3415  bool retry = true;
3416  // retry for a maximum of 5 seconds
3417  while (retry && !m_pauseDecoder && !m_killDecoder && !timeout.hasExpired(5000))
3418  {
3419  retry = false;
3420  ret = m_decoder->GetFrame(Type, retry);
3421  if (retry)
3422  {
3423  m_decoderChangeLock.unlock();
3424  QThread::usleep(10000);
3425  m_decoderChangeLock.lock();
3426  }
3427  }
3428 
3429  if (timeout.hasExpired(5000))
3430  return false;
3431  return ret;
3432 }
3433 
3435 {
3436  m_transcoding = value;
3437 
3438  if (m_decoder)
3439  m_decoder->SetTranscoding(value);
3440 }
3441 
3443 {
3445  {
3446  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot add PiP from another thread");
3447  return false;
3448  }
3449 
3450  if (m_pipPlayers.contains(pip))
3451  {
3452  LOG(VB_GENERAL, LOG_ERR, LOC + "PiPMap already contains PiP.");
3453  return false;
3454  }
3455 
3456  QList<PIPLocation> locs = m_pipPlayers.values();
3457  if (locs.contains(loc))
3458  {
3459  LOG(VB_GENERAL, LOG_ERR, LOC +"Already have a PiP at that location.");
3460  return false;
3461  }
3462 
3463  m_pipPlayers.insert(pip, loc);
3464  return true;
3465 }
3466 
3468 {
3470  return false;
3471 
3472  if (!m_pipPlayers.contains(pip))
3473  return false;
3474 
3475  m_pipPlayers.remove(pip);
3476  if (m_videoOutput)
3477  m_videoOutput->RemovePIP(pip);
3478  return true;
3479 }
3480 
3482 {
3484  return kPIP_END;
3485 
3486  if (m_pipPlayers.isEmpty())
3487  return m_pipDefaultLoc;
3488 
3489  // order of preference, could be stored in db if we want it configurable
3490  PIPLocation ols[] =
3492 
3493  for (auto & ol : ols)
3494  {
3495  PIPMap::const_iterator it = m_pipPlayers.begin();
3496  for (; it != m_pipPlayers.end() && (*it != ol); ++it);
3497 
3498  if (it == m_pipPlayers.end())
3499  return ol;
3500  }
3501 
3502  return kPIP_END;
3503 }
3504 
3505 int64_t MythPlayer::AdjustAudioTimecodeOffset(int64_t v, int newsync)
3506 {
3507  if ((newsync >= -1000) && (newsync <= 1000))
3508  m_tcWrap[TC_AUDIO] = newsync;
3509  else
3510  m_tcWrap[TC_AUDIO] += v;
3511  return m_tcWrap[TC_AUDIO];
3512 }
3513 
3514 void MythPlayer::WrapTimecode(int64_t &timecode, TCTypes tc_type)
3515 {
3516  timecode += m_tcWrap[tc_type];
3517 }
3518 
3519 bool MythPlayer::PrepareAudioSample(int64_t &timecode)
3520 {
3521  WrapTimecode(timecode, TC_AUDIO);
3522  return false;
3523 }
3524 
3539 void MythPlayer::SetWatched(bool forceWatched)
3540 {
3541  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3542  if (!m_playerCtx->m_playingInfo)
3543  {
3544  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3545  return;
3546  }
3547 
3548  uint64_t numFrames = GetCurrentFrameCount();
3549 
3550  // For recordings we want to ignore the post-roll and account for
3551  // in-progress recordings where totalFrames doesn't represent
3552  // the full length of the recording. For videos we can only rely on
3553  // totalFrames as duration metadata can be wrong
3557  {
3558 
3559  // If the recording is stopped early we need to use the recording end
3560  // time, not the programme end time
3561 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
3562  uint endtime;
3563  if (m_playerCtx->m_playingInfo->GetRecordingEndTime().toTime_t() <
3565  {
3566  endtime = m_playerCtx->m_playingInfo->GetRecordingEndTime().toTime_t();
3567  }
3568  else
3569  {
3570  endtime = m_playerCtx->m_playingInfo->GetScheduledEndTime().toTime_t();
3571  }
3572 
3573  numFrames = (long long)
3574  ((endtime -
3577 #else
3579  qint64 starttime = pi->GetRecordingStartTime().toSecsSinceEpoch();
3580  qint64 endactual = pi->GetRecordingEndTime().toSecsSinceEpoch();
3581  qint64 endsched = pi->GetScheduledEndTime().toSecsSinceEpoch();
3582  qint64 endtime = min(endactual, endsched);
3583  numFrames = (long long) ((endtime - starttime) * m_videoFrameRate);
3584 #endif
3585  }
3586 
3587  int offset = (int) round(0.14 * (numFrames / m_videoFrameRate));
3588 
3589  if (offset < 240)
3590  offset = 240; // 4 Minutes Min
3591  else if (offset > 720)
3592  offset = 720; // 12 Minutes Max
3593 
3594  if (forceWatched || m_framesPlayed > numFrames - (offset * m_videoFrameRate))
3595  {
3597  LOG(VB_GENERAL, LOG_INFO, LOC +
3598  QString("Marking recording as watched using offset %1 minutes")
3599  .arg(offset/60));
3600  }
3601 
3602  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3603 }
3604 
3606 {
3607  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3610  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3611 }
3612 
3614 {
3615  uint64_t bookmark = 0;
3616 
3619  bookmark = 0;
3620  else
3621  {
3622  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3623  if (const ProgramInfo *pi = m_playerCtx->m_playingInfo)
3624  {
3625  bookmark = pi->QueryBookmark();
3626  // Disable progstart if the program has a cutlist.
3627  if (bookmark == 0 && !pi->HasCutlist())
3628  bookmark = pi->QueryProgStart();
3629  if (bookmark == 0)
3630  bookmark = pi->QueryLastPlayPos();
3631  }
3632  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3633  }
3634 
3635  return bookmark;
3636 }
3637 
3639 {
3640  bool skip_changed = false;
3641 
3642  float temp_speed = (m_playSpeed == 0.0F) ?
3644  if (m_playSpeed >= 0.0F && m_playSpeed <= 3.0F)
3645  {
3646  skip_changed = (m_ffrewSkip != 1);
3647  if (m_decoder)
3649  m_frameInterval = (int) (1000000.0 / m_videoFrameRate / static_cast<double>(temp_speed))
3650  / m_fpsMultiplier;
3651  m_ffrewSkip = static_cast<int>(m_playSpeed != 0.0F);
3652  }
3653  else
3654  {
3655  skip_changed = true;
3656  m_frameInterval = 200000;
3657  m_frameInterval = (fabs(m_playSpeed) >= 3.0F) ? 133466 : m_frameInterval;
3658  m_frameInterval = (fabs(m_playSpeed) >= 5.0F) ? 133466 : m_frameInterval;
3659  m_frameInterval = (fabs(m_playSpeed) >= 8.0F) ? 250250 : m_frameInterval;
3660  m_frameInterval = (fabs(m_playSpeed) >= 10.0F) ? 133466 : m_frameInterval;
3661  m_frameInterval = (fabs(m_playSpeed) >= 16.0F) ? 187687 : m_frameInterval;
3662  m_frameInterval = (fabs(m_playSpeed) >= 20.0F) ? 150150 : m_frameInterval;
3663  m_frameInterval = (fabs(m_playSpeed) >= 30.0F) ? 133466 : m_frameInterval;
3664  m_frameInterval = (fabs(m_playSpeed) >= 60.0F) ? 133466 : m_frameInterval;
3665  m_frameInterval = (fabs(m_playSpeed) >= 120.0F) ? 133466 : m_frameInterval;
3666  m_frameInterval = (fabs(m_playSpeed) >= 180.0F) ? 133466 : m_frameInterval;
3667  float ffw_fps = fabs(static_cast<double>(m_playSpeed)) * m_videoFrameRate;
3668  float dis_fps = 1000000.0F / m_frameInterval;
3669  m_ffrewSkip = (int)ceil(ffw_fps / dis_fps);
3671  m_ffrewAdjust = 0;
3672  }
3673 
3674  return skip_changed;
3675 }
3676 
3678 {
3679  float last_speed = m_playSpeed;
3682  m_rtcBase = 0;
3683 
3684  bool skip_changed = UpdateFFRewSkip();
3685 
3686  if (skip_changed && m_videoOutput)
3687  {
3689  if (m_playSpeed != 0.0F && !(last_speed == 0.0F && m_ffrewSkip == 1))
3691  }
3692 
3693  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Play speed: " +
3694  QString("rate: %1 speed: %2 skip: %3 => new interval %4")
3695  .arg(m_videoFrameRate).arg(static_cast<double>(m_playSpeed))
3696  .arg(m_ffrewSkip).arg(m_frameInterval));
3697 
3698  if (m_videoOutput)
3699  m_videoOutput->SetVideoFrameRate(static_cast<float>(m_videoFrameRate));
3700 
3701  // ensure we re-check double rate support following a speed change
3702  m_scanInitialized = false;
3703  m_scanLocked = false;
3704 
3706  {
3709  }
3710 }
3711 
3712 bool MythPlayer::DoRewind(uint64_t frames, double inaccuracy)
3713 {
3715  return false;
3716 
3717  uint64_t number = frames + 1;
3718  uint64_t desiredFrame = (m_framesPlayed > number) ? m_framesPlayed - number : 0;
3719 
3720  m_limitKeyRepeat = false;
3721  if (desiredFrame < m_videoFrameRate)
3722  m_limitKeyRepeat = true;
3723 
3724  uint64_t seeksnap_wanted = UINT64_MAX;
3725  if (inaccuracy != kInaccuracyFull)
3726  seeksnap_wanted = frames * inaccuracy;
3727  ClearBeforeSeek(frames);
3728  WaitForSeek(desiredFrame, seeksnap_wanted);
3729  m_rewindTime = 0;
3730  ClearAfterSeek();
3731  return true;
3732 }
3733 
3734 bool MythPlayer::DoRewindSecs(float secs, double inaccuracy, bool use_cutlist)
3735 {
3736  float current = ComputeSecs(m_framesPlayed, use_cutlist);
3737  float target = current - secs;
3738  if (target < 0)
3739  target = 0;
3740  uint64_t targetFrame = FindFrame(target, use_cutlist);
3741  return DoRewind(m_framesPlayed - targetFrame, inaccuracy);
3742 }
3743 
3749 long long MythPlayer::CalcRWTime(long long rw) const
3750 {
3751  bool hasliveprev = (m_liveTV && m_playerCtx->m_tvchain &&
3753 
3754  if (!hasliveprev || ((int64_t)m_framesPlayed >= rw))
3755  {
3756  return rw;
3757  }
3758 
3759  m_playerCtx->m_tvchain->JumpToNext(false, ((int64_t)m_framesPlayed - rw) / m_videoFrameRate);
3760 
3761  return -1;
3762 }
3763 
3768 long long MythPlayer::CalcMaxFFTime(long long ffframes, bool setjump) const
3769 {
3770  float maxtime = 1.0;
3771  bool islivetvcur = (m_liveTV && m_playerCtx->m_tvchain &&
3773 
3774  if (m_liveTV || IsWatchingInprogress())
3775  maxtime = 3.0;
3776 
3777  long long ret = ffframes;
3778  float ff = ComputeSecs(ffframes, true);
3779  float secsPlayed = ComputeSecs(m_framesPlayed, true);
3780  float secsWritten = ComputeSecs(m_totalFrames, true);
3781 
3782  m_limitKeyRepeat = false;
3783 
3784  if (m_liveTV && !islivetvcur && m_playerCtx->m_tvchain)
3785  {
3786  // recording has completed, totalFrames will always be up to date
3787  if ((ffframes + m_framesPlayed > m_totalFrames) && setjump)
3788  {
3789  ret = -1;
3790  // Number of frames to be skipped is from the end of the current segment
3791  m_playerCtx->m_tvchain->JumpToNext(true, ((int64_t)m_totalFrames - (int64_t)m_framesPlayed - ffframes) / m_videoFrameRate);
3792  }
3793  }
3794  else if (islivetvcur || IsWatchingInprogress())
3795  {
3796  if ((ff + secsPlayed) > secsWritten)
3797  {
3798  // If we attempt to seek past the last known duration,
3799  // check for up to date data
3800  long long framesWritten = m_playerCtx->m_recorder->GetFramesWritten();
3801 
3802  secsWritten = ComputeSecs(framesWritten, true);
3803  }
3804 
3805  float behind = secsWritten - secsPlayed;
3806 
3807  if (behind < maxtime) // if we're close, do nothing
3808  ret = 0;
3809  else if (behind - ff <= maxtime)
3810  ret = TranslatePositionMsToFrame(1000 * (secsWritten - maxtime),
3811  true) - m_framesPlayed;
3812 
3813  if (behind < maxtime * 3)
3814  m_limitKeyRepeat = true;
3815  }
3816  else if (IsPaused())
3817  {
3818  uint64_t lastFrame =
3820  if (m_framesPlayed + ffframes >= lastFrame)
3821  ret = lastFrame - 1 - m_framesPlayed;
3822  }
3823  else
3824  {
3825  float secsMax = secsWritten - 2.F * maxtime;
3826  if (secsMax <= 0.F)
3827  ret = 0;
3828  else if (secsMax < secsPlayed + ff)
3829  ret = TranslatePositionMsToFrame(1000 * secsMax, true)
3830  - m_framesPlayed;
3831  }
3832 
3833  return ret;
3834 }
3835 
3843 {
3844  if (!m_videoOutput || !m_decoder)
3845  return false;
3846 
3847  return m_playerCtx->m_buffer->IsNearEnd(
3849 }
3850 
3854 {
3855  if (!m_playerCtx)
3856  return false;
3857 
3858  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3860  !m_decoder)
3861  {
3862  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3863  return false;
3864  }
3865  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3866 
3867  auto margin = (long long)(m_videoFrameRate * 2);
3868  margin = (long long) (margin * m_audio.GetStretchFactor());
3869  bool watchingTV = IsWatchingInprogress();
3870 
3871  uint64_t framesRead = m_framesPlayed;
3872  uint64_t framesLeft = 0;
3873 
3874  if (!m_playerCtx->IsPIP() &&
3876  {
3877  if (framesRead >= m_deleteMap.GetLastFrame())
3878  return true;
3879  uint64_t frameCount = GetCurrentFrameCount();
3880  framesLeft = (frameCount > framesRead) ? frameCount - framesRead : 0;
3881  return (framesLeft < (uint64_t)margin);
3882  }
3883 
3884  if (!m_liveTV && !watchingTV)
3885  return false;
3886 
3888  return false;
3889 
3890  if (m_playerCtx->m_recorder)
3891  {
3892  framesLeft =
3894 
3895  // if it looks like we are near end, get an updated GetFramesWritten()
3896  if (framesLeft < (uint64_t)margin)
3897  framesLeft = m_playerCtx->m_recorder->GetFramesWritten() - framesRead;
3898  }
3899 
3900  return (framesLeft < (uint64_t)margin);
3901 }
3902 
3903 bool MythPlayer::DoFastForward(uint64_t frames, double inaccuracy)
3904 {
3906  return false;
3907 
3908  uint64_t number = (frames ? frames - 1 : 0);
3909  uint64_t desiredFrame = m_framesPlayed + number;
3910 
3911  if (!m_deleteMap.IsEditing() && IsInDelete(desiredFrame))
3912  {
3913  uint64_t endcheck = m_deleteMap.GetLastFrame();
3914  if (desiredFrame > endcheck)
3915  desiredFrame = endcheck;
3916  }
3917 
3918  uint64_t seeksnap_wanted = UINT64_MAX;
3919  if (inaccuracy != kInaccuracyFull)
3920  seeksnap_wanted = frames * inaccuracy;
3921  ClearBeforeSeek(frames);
3922  WaitForSeek(desiredFrame, seeksnap_wanted);
3923  m_ffTime = 0;
3924  ClearAfterSeek(false);
3925  return true;
3926 }
3927 
3928 bool MythPlayer::DoFastForwardSecs(float secs, double inaccuracy,
3929  bool use_cutlist)
3930 {
3931  float current = ComputeSecs(m_framesPlayed, use_cutlist);
3932  float target = current + secs;
3933  uint64_t targetFrame = FindFrame(target, use_cutlist);
3934  return DoFastForward(targetFrame - m_framesPlayed, inaccuracy);
3935 }
3936 
3937 void MythPlayer::DoJumpToFrame(uint64_t frame, double inaccuracy)
3938 {
3939  if (frame > m_framesPlayed)
3940  DoFastForward(frame - m_framesPlayed, inaccuracy);
3941  else if (frame <= m_framesPlayed)
3942  DoRewind(m_framesPlayed - frame, inaccuracy);
3943 }
3944 
3945 void MythPlayer::WaitForSeek(uint64_t frame, uint64_t seeksnap_wanted)
3946 {
3947  if (!m_decoder)
3948  return;
3949 
3951  m_decoder->SetSeekSnap(seeksnap_wanted);
3952 
3953  bool islivetvcur = (m_liveTV && m_playerCtx->m_tvchain &&
3955 
3956  uint64_t max = GetCurrentFrameCount();
3957  if (islivetvcur || IsWatchingInprogress())
3958  {
3959  max = (uint64_t)m_playerCtx->m_recorder->GetFramesWritten();
3960  }
3961  if (frame >= max)
3962  frame = max - 1;
3963 
3964  m_decoderSeekLock.lock();
3965  m_decoderSeek = frame;
3966  m_decoderSeekLock.unlock();
3967 
3968  int count = 0;
3969  bool need_clear = false;
3970  while (m_decoderSeek >= 0)
3971  {
3972  // Waiting blocks the main UI thread but the decoder may
3973  // have initiated a callback into the UI thread to create
3974  // certain resources. Ensure the callback is processed.
3975  // Ideally MythPlayer should be fully event driven and these
3976  // calls wouldn't be necessary.
3977  ProcessCallbacks();
3978 
3979  usleep(50 * 1000);
3980 
3981  // provide some on screen feedback if seeking is slow
3982  count++;
3983  if (!(count % 3) && !m_hasFullPositionMap)
3984  {
3985  int num = count % 3;
3986  SetOSDMessage(tr("Searching") + QString().fill('.', num),
3989  need_clear = true;
3990  }
3991  }
3992  if (need_clear)
3993  {
3994  m_osdLock.lock();
3995  if (m_osd)
3996  m_osd->HideWindow("osd_message");
3997  m_osdLock.unlock();
3998  }
3999 }
4000 
4013 void MythPlayer::ClearAfterSeek(bool clearvideobuffers)
4014 {
4015  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("ClearAfterSeek(%1)")
4016  .arg(clearvideobuffers));
4017 
4018  if (clearvideobuffers && m_videoOutput)
4020 
4021  int64_t savedTC = m_tcWrap[TC_AUDIO];
4022 
4023  for (int j = 0; j < TCTYPESMAX; j++)
4024  m_tcWrap[j] = m_tcLastVal[j] = 0;
4025 
4026  m_tcWrap[TC_AUDIO] = savedTC;
4027 
4028  m_audio.Reset();
4029  // Reenable (or re-disable) subtitles, which ultimately does
4030  // nothing except to call ResetCaptions() to erase any captions
4031  // currently on-screen. The key is that the erasing is done in
4032  // the UI thread, not the decoder thread.
4037  m_needNewPauseFrame = true;
4038  ResetAVSync();
4039 }
4040 
4048 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
4049 void MythPlayer::ClearBeforeSeek(uint64_t Frames)
4050 {
4051 #ifdef USING_MEDIACODEC
4052  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("ClearBeforeSeek: decoder %1 frames %2 recording %3 livetv %4")
4053  .arg(m_codecName).arg(Frames).arg(m_watchingRecording).arg(m_liveTV));
4054 
4055  if ((Frames < 2) || !m_videoOutput /*|| !(m_liveTV || m_watchingRecording)*/)
4056  return;
4057 
4058  m_decoderChangeLock.lock();
4060  m_decoderChangeLock.unlock();
4061  if (codec_is_mediacodec(codec))
4062  m_videoOutput->DiscardFrames(true, true);
4063 #else
4064  Q_UNUSED(Frames);
4065 #endif
4066 }
4067 
4068 void MythPlayer::SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
4069 {
4071  m_tv = tv;
4072  m_parentWidget = widget;
4073  m_playerCtx = ctx;
4074  m_liveTV = ctx->m_tvchain;
4075 }
4076 
4078 {
4079  m_deleteMap.SetEditing(false);
4080 
4081  if (!m_hasFullPositionMap)
4082  {
4083  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot edit - no full position map");
4084  SetOSDStatus(tr("No Seektable"), kOSDTimeout_Med);
4085  return false;
4086  }
4087 
4088  if (m_deleteMap.IsFileEditing())
4089  return false;
4090 
4091  QMutexLocker locker(&m_osdLock);
4092  if (!m_osd)
4093  return false;
4094 
4096  int sample_rate = GetAudio()->GetSampleRate();
4097  m_audiograph.SetSampleRate(sample_rate);
4098  m_audiograph.SetSampleCount((unsigned)(sample_rate / m_videoFrameRate));
4100 
4102  m_tcWrap[TC_AUDIO] = 0;
4103 
4106  m_deleteMap.SetEditing(true);
4107  m_osd->DialogQuit();
4108  ResetCaptions();
4109  m_osd->HideAll();
4110 
4111  bool loadedAutoSave = m_deleteMap.LoadAutoSaveMap();
4112  if (loadedAutoSave)
4113  {
4114  SetOSDMessage(tr("Using previously auto-saved cuts"),
4116  }
4117 
4121  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
4124  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
4125  m_editUpdateTimer.start();
4126 
4127  return m_deleteMap.IsEditing();
4128 }
4129 
4137 void MythPlayer::DisableEdit(int howToSave)
4138 {
4139  QMutexLocker locker(&m_osdLock);
4140  if (!m_osd)
4141  return;
4142 
4143  m_deleteMap.SetEditing(false, m_osd);
4144  if (howToSave == 0)
4145  m_deleteMap.LoadMap();
4146  // Unconditionally save to remove temporary marks from the DB.
4147  if (howToSave >= 0)
4148  m_deleteMap.SaveMap();
4150  m_deleteMap.SetFileEditing(false);
4151  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
4154  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
4156  m_audiograph.Reset();
4159 
4160  if (!m_pausedBeforeEdit)
4162  else
4163  SetOSDStatus(tr("Paused"), kOSDTimeout_None);
4164 }
4165 
4166 bool MythPlayer::HandleProgramEditorActions(QStringList &actions)
4167 {
4168  bool handled = false;
4169  bool refresh = true;
4170  long long frame = GetFramesPlayed();
4171 
4172  for (int i = 0; i < actions.size() && !handled; i++)
4173  {
4174  QString action = actions[i];
4175  handled = true;
4176  float seekamount = m_deleteMap.GetSeekAmount();
4177  if (action == ACTION_LEFT)
4178  {
4179  if (seekamount == 0) // 1 frame
4181  else if (seekamount > 0)
4182  {
4183  // Use fully-accurate seeks for less than 1 second.
4184  DoRewindSecs(seekamount, seekamount < 1.0F ? kInaccuracyNone :
4185  kInaccuracyEditor, false);
4186  }
4187  else
4188  {
4189  HandleArbSeek(false);
4190  }
4191  }
4192  else if (action == ACTION_RIGHT)
4193  {
4194  if (seekamount == 0) // 1 frame
4196  else if (seekamount > 0)
4197  {
4198  // Use fully-accurate seeks for less than 1 second.
4199  DoFastForwardSecs(seekamount, seekamount < 1.0F ? kInaccuracyNone :
4200  kInaccuracyEditor, false);
4201  }
4202  else
4203  {
4204  HandleArbSeek(true);
4205  }
4206  }
4207  else if (action == ACTION_LOADCOMMSKIP)
4208  {
4209  if (m_commBreakMap.HasMap())
4210  {
4211  frm_dir_map_t map;
4212  m_commBreakMap.GetMap(map);
4214  }
4215  }
4216  else if (action == ACTION_PREVCUT)
4217  {
4218  float old_seekamount = m_deleteMap.GetSeekAmount();
4220  HandleArbSeek(false);
4221  m_deleteMap.SetSeekAmount(old_seekamount);
4222  }
4223  else if (action == ACTION_NEXTCUT)
4224  {
4225  float old_seekamount = m_deleteMap.GetSeekAmount();
4227  HandleArbSeek(true);
4228  m_deleteMap.SetSeekAmount(old_seekamount);
4229  }
4230 #define FFREW_MULTICOUNT 10.0F
4231  else if (action == ACTION_BIGJUMPREW)
4232  {
4233  if (seekamount == 0)
4235  else if (seekamount > 0)
4236  {
4237  DoRewindSecs(seekamount * FFREW_MULTICOUNT,
4238  kInaccuracyEditor, false);
4239  }
4240  else
4241  {
4243  kInaccuracyNone, false);
4244  }
4245  }
4246  else if (action == ACTION_BIGJUMPFWD)
4247  {
4248  if (seekamount == 0)
4250  else if (seekamount > 0)
4251  {
4252  DoFastForwardSecs(seekamount * FFREW_MULTICOUNT,
4253  kInaccuracyEditor, false);
4254  }
4255  else
4256  {
4258  kInaccuracyNone, false);
4259  }
4260  }
4261  else if (action == ACTION_SELECT)
4262  {
4263  m_deleteMap.NewCut(frame);
4264  SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
4265  refresh = true;
4266  }
4267  else if (action == "DELETE")
4268  {
4269  m_deleteMap.Delete(frame, tr("Delete"));
4270  refresh = true;
4271  }
4272  else if (action == "REVERT")
4273  {
4274  m_deleteMap.LoadMap(tr("Undo Changes"));
4275  refresh = true;
4276  }
4277  else if (action == "REVERTEXIT")
4278  {
4279  DisableEdit(0);
4280  refresh = false;
4281  }
4282  else if (action == ACTION_SAVEMAP)
4283  {
4284  m_deleteMap.SaveMap();
4285  refresh = true;
4286  }
4287  else if (action == "EDIT" || action == "SAVEEXIT")
4288  {
4289  DisableEdit(1);
4290  refresh = false;
4291  }
4292  else
4293  {
4294  QString undoMessage = m_deleteMap.GetUndoMessage();
4295  QString redoMessage = m_deleteMap.GetRedoMessage();
4296  handled = m_deleteMap.HandleAction(action, frame);
4297  if (handled && (action == "CUTTOBEGINNING" ||
4298  action == "CUTTOEND" || action == "NEWCUT"))
4299  {
4300  SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
4301  }
4302  else if (handled && action == "UNDO")
4303  {
4304  //: %1 is the undo message
4305  SetOSDMessage(tr("Undo - %1").arg(undoMessage),
4307  }
4308  else if (handled && action == "REDO")
4309  {
4310  //: %1 is the redo message
4311  SetOSDMessage(tr("Redo - %1").arg(redoMessage),
4313  }
4314  }
4315  }
4316 
4317  if (handled && refresh)
4318  {
4319  m_osdLock.lock();
4320  if (m_osd)
4321  {
4323  }
4324  m_osdLock.unlock();
4325  }
4326 
4327  return handled;
4328 }
4329 
4330 bool MythPlayer::IsInDelete(uint64_t frame)
4331 {
4332  return m_deleteMap.IsInDelete(frame);
4333 }
4334 
4335 uint64_t MythPlayer::GetNearestMark(uint64_t frame, bool right)
4336 {
4337  return m_deleteMap.GetNearestMark(frame, right);
4338 }
4339 
4340 bool MythPlayer::IsTemporaryMark(uint64_t frame)
4341 {
4342  return m_deleteMap.IsTemporaryMark(frame);
4343 }
4344 
4346 {
4347  return m_deleteMap.HasTemporaryMark();
4348 }
4349 
4351 {
4352  if (m_deleteMap.GetSeekAmount() == -2)
4353  {
4354  uint64_t framenum = m_deleteMap.GetNearestMark(m_framesPlayed, right);
4355  if (right && (framenum > m_framesPlayed))
4357  else if (!right && (m_framesPlayed > framenum))
4359  }
4360  else
4361  {
4362  if (right)
4364  else
4366  }
4367 }
4368 
4370 {
4371  if (m_videoOutput)
4372  return m_videoOutput->GetAspectOverride();
4373  return kAspect_Off;
4374 }
4375 
4377 {
4378  if (m_videoOutput)
4379  return m_videoOutput->GetAdjustFill();
4380  return kAdjustFill_Off;
4381 }
4382 
4384 {
4385  if (m_videoOutput)
4386  {
4387  m_videoOutput->ToggleAspectOverride(aspectMode);
4388  ReinitOSD();
4389  }
4390 }
4391 
4393 {
4394  if (m_videoOutput)
4395  {
4397  m_videoOutput->ToggleAdjustFill(adjustfillMode);
4398  ReinitOSD();
4399  }
4400 }
4401 
4403 {
4404  if (m_videoOutput)
4405  {
4406  m_videoOutput->Zoom(direction);
4407  ReinitOSD();
4408  }
4409 }
4410 
4412 {
4413  if (m_videoOutput)
4414  {
4416  ReinitOSD();
4417  }
4418 }
4419 
4421 {
4422  if (m_videoOutput)
4424 }
4425 
4427 {
4428  if (m_videoOutput)
4429  return m_videoOutput->IsEmbedding();
4430  return false;
4431 }
4432 
4434 {
4436 }
4437 
4453 char *MythPlayer::GetScreenGrab(int SecondsIn, int &BufferSize,
4454  int &FrameWidth, int &FrameHeight, float &AspectRatio)
4455 {
4456  auto frameNum = static_cast<uint64_t>(SecondsIn * m_videoFrameRate);
4457  return GetScreenGrabAtFrame(frameNum, false, BufferSize, FrameWidth, FrameHeight, AspectRatio);
4458 }
4459 
4476 char *MythPlayer::GetScreenGrabAtFrame(uint64_t FrameNum, bool Absolute,
4477  int &BufferSize, int &FrameWidth, int &FrameHeight,
4478  float &AspectRatio)
4479 {
4480  BufferSize = 0;
4481  FrameWidth = FrameHeight = 0;
4482  AspectRatio = 0;
4483 
4484  if (OpenFile(0) < 0)
4485  {
4486  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not open file for preview.");
4487  return nullptr;
4488  }
4489 
4490  if ((m_videoDim.width() <= 0) || (m_videoDim.height() <= 0))
4491  {
4492  LOG(VB_PLAYBACK, LOG_ERR, LOC +
4493  QString("Video Resolution invalid %1x%2")
4494  .arg(m_videoDim.width()).arg(m_videoDim.height()));
4495 
4496  // This is probably an audio file, just return a grey frame.
4497  FrameWidth = 640;
4498  FrameHeight = 480;
4499  AspectRatio = 4.0F / 3.0F;
4500 
4501  BufferSize = FrameWidth * FrameHeight * 4;
4502  char* result = new char[BufferSize];
4503  memset(result, 0x3f, static_cast<size_t>(BufferSize) * sizeof(char));
4504  return result;
4505  }
4506 
4507  if (!InitVideo())
4508  {
4509  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to initialize video for screen grab.");
4510  return nullptr;
4511  }
4512 
4513  ClearAfterSeek();
4514  if (!m_decoderThread)
4515  DecoderStart(true /*start paused*/);
4516  uint64_t dummy = 0;
4517  SeekForScreenGrab(dummy, FrameNum, Absolute);
4518  int tries = 0;
4519  while (!m_videoOutput->ValidVideoFrames() && ((tries++) < 500))
4520  {
4521  m_decodeOneFrame = true;
4522  usleep(10000);
4523  if ((tries & 10) == 10)
4524  LOG(VB_PLAYBACK, LOG_INFO, LOC + "ScreenGrab: Waited 100ms for video frame");
4525  }
4526 
4527  VideoFrame *frame = nullptr;
4528  if (!(frame = m_videoOutput->GetLastDecodedFrame()))
4529  return nullptr;
4530  if (!frame->buf)
4531  return nullptr;
4532 
4533  if (frame->interlaced_frame)
4534  {
4535  // Use medium quality - which is currently yadif
4536  frame->deinterlace_double = DEINT_NONE;
4538  MythDeinterlacer deinterlacer;
4539  deinterlacer.Filter(frame, kScan_Interlaced, nullptr, true);
4540  }
4541  unsigned char *result = CreateBuffer(FMT_RGB32, m_videoDim.width(), m_videoDim.height());
4542  MythAVCopy copyCtx;
4543  AVFrame retbuf;
4544  memset(&retbuf, 0, sizeof(AVFrame));
4545  copyCtx.Copy(&retbuf, frame, result, AV_PIX_FMT_RGB32);
4546  FrameWidth = m_videoDispDim.width();
4547  FrameHeight = m_videoDispDim.height();
4548  AspectRatio = frame->aspect;
4549 
4550  if (frame)
4551  DiscardVideoFrame(frame);
4552 
4553  return reinterpret_cast<char*>(result);
4554 }
4555 
4556 void MythPlayer::SeekForScreenGrab(uint64_t &number, uint64_t frameNum,
4557  bool absolute)
4558 {
4559  number = frameNum;
4560  if (number >= m_totalFrames)
4561  {
4562  LOG(VB_PLAYBACK, LOG_ERR, LOC +
4563  "Screen grab requested for frame number beyond end of file.");
4564  number = m_totalFrames / 2;
4565  }
4566 
4567  if (!absolute && m_hasFullPositionMap)
4568  {
4570  // Use the bookmark if we should, otherwise make sure we aren't
4571  // in the cutlist or a commercial break
4572  if (m_bookmarkSeek > 30)
4573  {
4574  number = m_bookmarkSeek;
4575  }
4576  else
4577  {
4578  uint64_t oldnumber = number;
4579  m_deleteMap.LoadMap();
4581 
4582  bool started_in_break_map = false;
4583  while (m_commBreakMap.IsInCommBreak(number) ||
4584  IsInDelete(number))
4585  {
4586  started_in_break_map = true;
4587  number += (uint64_t) (30 * m_videoFrameRate);
4588  if (number >= m_totalFrames)
4589  {
4590  number = oldnumber;
4591  break;
4592  }
4593  }
4594 
4595  // Advance a few seconds from the end of the break
4596  if (started_in_break_map)
4597  {
4598  oldnumber = number;
4599  number += (long long) (10 * m_videoFrameRate);
4600  if (number >= m_totalFrames)
4601  number = oldnumber;
4602  }
4603  }
4604  }
4605 
4607  DoJumpToFrame(number, kInaccuracyNone);
4608 }
4609 
4618 {
4619  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
4622  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
4623 
4624  if (!m_decoderThread)
4625  DecoderStart(false);
4626 
4627  if (frameNumber >= 0)
4628  {
4629  DoJumpToFrame(frameNumber, kInaccuracyNone);
4630  ClearAfterSeek();
4631  }
4632 
4633  int tries = 0;
4634  while (!m_videoOutput->ValidVideoFrames() && ((tries++) < 100))
4635  {
4636  m_decodeOneFrame = true;
4637  usleep(10000);
4638  if ((tries & 10) == 10)
4639  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waited 100ms for video frame");
4640  }
4641 
4643  return m_videoOutput->GetLastShownFrame();
4644 }
4645 
4646 QString MythPlayer::GetEncodingType(void) const
4647 {
4648  if (m_decoder)
4650  return QString();
4651 }
4652 
4654 {
4655  infoMap["audiocodec"] = ff_codec_id_string(m_audio.GetCodec());
4656  infoMap["audiochannels"] = QString::number(m_audio.GetOrigChannels());
4657 
4658  int width = m_videoDispDim.width();
4659  int height = m_videoDispDim.height();
4660  infoMap["videocodec"] = GetEncodingType();
4661  if (m_decoder)
4662  infoMap["videocodecdesc"] = m_decoder->GetRawEncodingType();
4663  infoMap["videowidth"] = QString::number(width);
4664  infoMap["videoheight"] = QString::number(height);
4665  infoMap["videoframerate"] = QString::number(m_videoFrameRate, 'f', 2);
4666  infoMap["deinterlacer"] = DeinterlacerName(m_lastDeinterlacer,
4668 
4669  if (width < 640)
4670  return;
4671 
4672  bool interlaced = is_interlaced(m_scan);
4673  if (width == 1920 || height == 1080 || height == 1088)
4674  infoMap["videodescrip"] = interlaced ? "HD_1080_I" : "HD_1080_P";
4675  else if ((width == 1280 || height == 720) && !interlaced)
4676  infoMap["videodescrip"] = "HD_720_P";
4677  else if (height >= 720)
4678  infoMap["videodescrip"] = "HD";
4679  else infoMap["videodescrip"] = "SD";
4680 }
4681 
4683 {
4684  if (m_decoder)
4685  return m_decoder->GetRawAudioState();
4686  return false;
4687 }
4688 
4689 QString MythPlayer::GetXDS(const QString &key) const
4690 {
4691  if (!m_decoder)
4692  return QString();
4693  return m_decoder->GetXDS(key);
4694 }
4695 
4696 void MythPlayer::InitForTranscode(bool copyaudio, bool copyvideo)
4697 {
4698  // Are these really needed?
4699  SetPlaying(true);
4700  m_keyframeDist = 30;
4701 
4702  if (!InitVideo())
4703  {
4704  LOG(VB_GENERAL, LOG_ERR, LOC +
4705  "Unable to initialize video for transcode.");
4706  SetPlaying(false);
4707  return;
4708  }
4709 
4710  m_framesPlayed = 0;
4711  ClearAfterSeek();
4712 
4713  if (copyvideo && m_decoder)
4714  m_decoder->SetRawVideoState(true);
4715  if (copyaudio && m_decoder)
4716  m_decoder->SetRawAudioState(true);
4717 
4718  if (m_decoder)
4719  {
4720  m_decoder->SetSeekSnap(0);
4721  }
4722 }
4723 
4725  int &did_ff, bool &is_key, bool honorCutList)
4726 {
4727  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
4730  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
4731 
4732  int64_t lastDecodedFrameNumber =
4734 
4735  if ((lastDecodedFrameNumber == 0) && honorCutList)
4737 
4738  if (!m_decoderThread)
4739  DecoderStart(true/*start paused*/);
4740 
4741  if (!m_decoder)
4742  return false;
4743 
4744  {
4745  QMutexLocker decoderlocker(&m_decoderChangeLock);
4746  if (!DoGetFrame(kDecodeAV))
4747  return false;
4748  }
4749 
4750  if (GetEof() != kEofStateNone)
4751  return false;
4752 
4753  if (honorCutList && !m_deleteMap.IsEmpty())
4754  {
4755  if (m_totalFrames && lastDecodedFrameNumber >= (int64_t)m_totalFrames)
4756  return false;
4757 
4758  uint64_t jumpto = 0;
4759  if (m_deleteMap.TrackerWantsToJump(lastDecodedFrameNumber, jumpto))
4760  {
4761  LOG(VB_GENERAL, LOG_INFO, LOC +
4762  QString("Fast-Forwarding from %1 to %2")
4763  .arg(lastDecodedFrameNumber).arg(jumpto));
4764  if (jumpto >= m_totalFrames)
4765  {
4767  return false;
4768  }
4769 
4770  // For 0.25, move this to DoJumpToFrame(jumpto)
4771  WaitForSeek(jumpto, 0);
4773  ClearAfterSeek();
4774  m_decoderChangeLock.lock();
4776  m_decoderChangeLock.unlock();
4777  did_ff = 1;
4778  }
4779  }
4780  if (GetEof() != kEofStateNone)
4781  return false;
4782  is_key = m_decoder->IsLastFrameKey();
4783  return true;
4784 }
4785 
4786 long MythPlayer::UpdateStoredFrameNum(long curFrameNum)
4787 {
4788  if (m_decoder)
4789  return m_decoder->UpdateStoredFrameNum(curFrameNum);
4790  return 0;
4791 }
4792 
4793 void MythPlayer::SetCutList(const frm_dir_map_t &newCutList)
4794 {
4795  m_deleteMap.SetMap(newCutList);
4796 }
4797 
4799  bool Writevideo, long TimecodeOffset)
4800 {
4801  if (!m_decoder)
4802  return false;
4803  if (Writevideo && !m_decoder->GetRawVideoState())
4804  Writevideo = false;
4805  m_decoder->WriteStoredData(OutBuffer, Writevideo, TimecodeOffset);
4806  return Writevideo;
4807 }
4808 
4810 {
4812  m_forcePositionMapSync = true;
4813 }
4814 
4816 {
4817  double spos = 0.0;
4818 
4819  if (m_liveTV || IsWatchingInprogress())
4820  {
4821  spos = 1000.0 * m_framesPlayed / m_playerCtx->m_recorder->GetFramesWritten();
4822  }
4823  else if (m_totalFrames)
4824  {
4825  spos = 1000.0 * m_framesPlayed / m_totalFrames;
4826  }
4827 
4828  return((int)spos);
4829 }
4830 
4832 {
4833  QString samplerate = MythMediaBuffer::BitrateToString(m_audio.GetSampleRate(), true);
4834  infoMap.insert("samplerate", samplerate);
4835  infoMap.insert("filename", m_playerCtx->m_buffer->GetSafeFilename());
4836  infoMap.insert("decoderrate", m_playerCtx->m_buffer->GetDecoderRate());
4837  infoMap.insert("storagerate", m_playerCtx->m_buffer->GetStorageRate());
4838  infoMap.insert("bufferavail", m_playerCtx->m_buffer->GetAvailableBuffer());
4839  infoMap.insert("buffersize", QString::number(m_playerCtx->m_buffer->GetBufferSize() >> 20));
4840  int avsync = m_avsyncAvg / 1000;
4841  infoMap.insert("avsync", tr("%1 ms").arg(avsync));
4842 
4843  if (m_videoOutput)
4844  {
4845  QString frames = QString("%1/%2").arg(m_videoOutput->ValidVideoFrames())
4846  .arg(m_videoOutput->FreeVideoFrames());
4847  infoMap.insert("videoframes", frames);
4848  }
4849  if (m_decoder)
4850  infoMap["videodecoder"] = m_decoder->GetCodecDecoderName();
4851  if (m_outputJmeter)
4852  {
4853  infoMap["framerate"] = QString("%1%2%3")
4854  .arg(m_outputJmeter->GetLastFPS(), 0, 'f', 2)
4855  .arg(QChar(0xB1, 0))
4856  .arg(m_outputJmeter->GetLastSD(), 0, 'f', 2);
4857  infoMap["load"] = m_outputJmeter->GetLastCPUStats();
4858  }
4859  GetCodecDescription(infoMap);
4860 }
4861 
4862 int64_t MythPlayer::GetSecondsPlayed(bool honorCutList, int divisor)
4863 {
4864  int64_t pos = TranslatePositionFrameToMs(m_framesPlayed, honorCutList);
4865  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
4866  QString("GetSecondsPlayed: framesPlayed %1, honorCutList %2, divisor %3, pos %4")
4867  .arg(m_framesPlayed).arg(honorCutList).arg(divisor).arg(pos));
4868  return TranslatePositionFrameToMs(m_framesPlayed, honorCutList) / divisor;
4869 }
4870 
4871 int64_t MythPlayer::GetTotalSeconds(bool honorCutList, int divisor) const
4872 {
4873  uint64_t pos = m_totalFrames;
4874 
4875  if (IsWatchingInprogress())
4876  pos = UINT64_MAX;
4877 
4878  return TranslatePositionFrameToMs(pos, honorCutList) / divisor;
4879 }
4880 
4881 // Returns the total frame count, as totalFrames for a completed
4882 // recording, or the most recent frame count from the recorder for
4883 // live TV or an in-progress recording.
4885 {
4886  uint64_t result = m_totalFrames;
4887  if (IsWatchingInprogress())
4889  return result;
4890 }
4891 
4892 // Finds the frame number associated with the given time offset. A
4893 // positive offset or +0.0F indicate offset from the beginning. A
4894 // negative offset or -0.0F indicate offset from the end. Limit the
4895 // result to within bounds of the video.
4896 uint64_t MythPlayer::FindFrame(float offset, bool use_cutlist) const
4897 {
4898  bool islivetvcur = (m_liveTV && m_playerCtx->m_tvchain &&
4900  uint64_t length_ms = TranslatePositionFrameToMs(m_totalFrames, use_cutlist);
4901  uint64_t position_ms = 0;
4902 
4903  if (signbit(offset))
4904  {
4905  // Always get an updated totalFrame value for in progress recordings
4906  if (islivetvcur || IsWatchingInprogress())
4907  {
4908  uint64_t framesWritten = m_playerCtx->m_recorder->GetFramesWritten();
4909 
4910  if (m_totalFrames < framesWritten)
4911  {
4912  // Known duration is less than what the backend reported, use new value
4913  length_ms =
4914  TranslatePositionFrameToMs(framesWritten, use_cutlist);
4915  }
4916  }
4917  uint64_t offset_ms = llroundf(-offset * 1000);
4918  position_ms = (offset_ms > length_ms) ? 0 : length_ms - offset_ms;
4919  }
4920  else
4921  {
4922  position_ms = llroundf(offset * 1000);
4923 
4924  if (offset > length_ms)
4925  {
4926  // Make sure we have an updated totalFrames
4927  if ((islivetvcur || IsWatchingInprogress()) &&
4928  (length_ms < offset))
4929  {
4930  long long framesWritten =
4932 
4933  length_ms =
4934  TranslatePositionFrameToMs(framesWritten, use_cutlist);
4935  }
4936  position_ms = min(position_ms, length_ms);
4937  }
4938  }
4939  return TranslatePositionMsToFrame(position_ms, use_cutlist);
4940 }
4941 
4942 void MythPlayer::calcSliderPos(osdInfo &info, bool paddedFields)
4943 {
4944  if (!m_decoder)
4945  return;
4946 
4947  bool islive = false;
4948  info.text.insert("chapteridx", QString());
4949  info.text.insert("totalchapters", QString());
4950  info.text.insert("titleidx", QString());
4951  info.text.insert("totaltitles", QString());
4952  info.text.insert("angleidx", QString());
4953  info.text.insert("totalangles", QString());
4954  info.values.insert("position", 0);
4955  info.values.insert("progbefore", 0);
4956  info.values.insert("progafter", 0);
4957 
4958  int playbackLen = 0;
4959  bool fixed_playbacklen = false;
4960 
4961  if (m_decoder->GetCodecDecoderName() == "nuppel")
4962  {
4963  playbackLen = m_totalLength;
4964  fixed_playbacklen = true;
4965  }
4966 
4967  if (m_liveTV && m_playerCtx->m_tvchain)
4968  {
4969  info.values["progbefore"] = (int)m_playerCtx->m_tvchain->HasPrev();
4970  info.values["progafter"] = (int)m_playerCtx->m_tvchain->HasNext();
4971  playbackLen = m_playerCtx->m_tvchain->GetLengthAtCurPos();
4972  islive = true;
4973  fixed_playbacklen = true;
4974  }
4975  else if (IsWatchingInprogress())
4976  {
4977  islive = true;
4978  }
4979  else
4980  {
4981  int chapter = GetCurrentChapter();
4982  int chapters = GetNumChapters();
4983  if (chapter && chapters > 1)
4984  {
4985  info.text["chapteridx"] = QString::number(chapter + 1);
4986  info.text["totalchapters"] = QString::number(chapters);
4987  }
4988 
4989  int title = GetCurrentTitle();
4990  int titles = GetNumTitles();
4991  if (title && titles > 1)
4992  {
4993  info.text["titleidx"] = QString::number(title + 1);
4994  info.text["totaltitles"] = QString::number(titles);
4995  }
4996 
4997  int angle = GetCurrentAngle();
4998  int angles = GetNumAngles();
4999  if (angle && angles > 1)
5000  {
5001  info.text["angleidx"] = QString::number(angle + 1);
5002  info.text["totalangles"] = QString::number(angles);
5003  }
5004  }
5005 
5006  // Set the raw values, followed by the translated values.
5007  for (int i = 0; i < 2 ; ++i)
5008  {
5009  bool honorCutList = (i > 0);
5010  bool stillFrame = false;
5011  int pos = 0;
5012 
5013  QString relPrefix = (honorCutList ? "rel" : "");
5014  if (!fixed_playbacklen)
5015  playbackLen = GetTotalSeconds(honorCutList);
5016  int secsplayed = GetSecondsPlayed(honorCutList);
5017 
5018  stillFrame = (secsplayed < 0);
5019  playbackLen = max(playbackLen, 0);
5020  secsplayed = min(playbackLen, max(secsplayed, 0));
5021  int secsbehind = max((playbackLen - secsplayed), 0);
5022 
5023  if (playbackLen > 0)
5024  pos = (int)(1000.0F * (secsplayed / (float)playbackLen));
5025 
5026  info.values.insert(relPrefix + "secondsplayed", secsplayed);
5027  info.values.insert(relPrefix + "totalseconds", playbackLen);
5028  info.values[relPrefix + "position"] = pos;
5029 
5030  QString text1;
5031  QString text2;
5032  QString text3;
5033  if (paddedFields)
5034  {
5035  text1 = MythFormatTime(secsplayed, "HH:mm:ss");
5036  text2 = MythFormatTime(playbackLen, "HH:mm:ss");
5037  text3 = MythFormatTime(secsbehind, "HH:mm:ss");
5038  }
5039  else
5040  {
5041  QString fmt = (playbackLen >= ONEHOURINSEC) ? "H:mm:ss" : "mm:ss";
5042  text1 = MythFormatTime(secsplayed, fmt);
5043  text2 = MythFormatTime(playbackLen, fmt);
5044 
5045  if (secsbehind >= ONEHOURINSEC)
5046  {
5047  text3 = MythFormatTime(secsbehind, "H:mm:ss");
5048  }
5049  else if (secsbehind >= ONEMININSEC)
5050  {
5051  text3 = MythFormatTime(secsbehind, "mm:ss");
5052  }
5053  else
5054  {
5055  text3 = tr("%n second(s)", "", secsbehind);
5056  }
5057  }
5058 
5059  QString desc = stillFrame ? tr("Still Frame") :
5060  tr("%1 of %2").arg(text1).arg(text2);
5061 
5062  info.text[relPrefix + "description"] = desc;
5063  info.text[relPrefix + "playedtime"] = text1;
5064  info.text[relPrefix + "totaltime"] = text2;
5065  info.text[relPrefix + "remainingtime"] = islive ? QString() : text3;
5066  info.text[relPrefix + "behindtime"] = islive ? text3 : QString();
5067  }
5068 }
5069 
5070 // If position == -1, it signifies that we are computing the current
5071 // duration of an in-progress recording. In this case, we fetch the
5072 // current frame rate and frame count from the recorder.
5073 uint64_t MythPlayer::TranslatePositionFrameToMs(uint64_t position,
5074  bool use_cutlist) const
5075 {
5076  float frameRate = GetFrameRate();
5077  if (position == UINT64_MAX &&
5079  {
5080  float recorderFrameRate = m_playerCtx->m_recorder->GetFrameRate();
5081  if (recorderFrameRate > 0)
5082  frameRate = recorderFrameRate;
5083  position = m_playerCtx->m_recorder->GetFramesWritten();
5084  }
5085  return m_deleteMap.TranslatePositionFrameToMs(position, frameRate,
5086  use_cutlist);
5087 }
5088 
5090 {
5091  if (m_decoder)
5092  return m_decoder->GetNumChapters();
5093  return 0;
5094 }
5095 
5097 {
5098  if (m_decoder)
5100  return 0;
5101 }
5102 
5103 void MythPlayer::GetChapterTimes(QList<long long> &times)
5104 {
5105  if (m_decoder)
5106  return m_decoder->GetChapterTimes(times);
5107 }
5108 
5109 bool MythPlayer::DoJumpChapter(int chapter)
5110 {
5111  int64_t desiredFrame = -1;
5112  int total = GetNumChapters();
5113  int current = GetCurrentChapter();
5114 
5115  if (chapter < 0 || chapter > total)
5116  {
5117 
5118  if (chapter < 0)
5119  {
5120  chapter = current -1;
5121  if (chapter < 0) chapter = 0;
5122  }
5123  else if (chapter > total)
5124  {
5125  chapter = current + 1;
5126  if (chapter > total) chapter = total;
5127  }
5128  }
5129 
5130  desiredFrame = GetChapter(chapter);
5131  LOG(VB_PLAYBACK, LOG_INFO, LOC +
5132  QString("DoJumpChapter: current %1 want %2 (frame %3)")
5133  .arg(current).arg(chapter).arg(desiredFrame));
5134 
5135  if (desiredFrame < 0)
5136  {
5137  LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("DoJumpChapter failed."));
5138  m_jumpChapter = 0;
5139  return false;
5140  }
5141 
5142  DoJumpToFrame(desiredFrame, kInaccuracyNone);
5143  m_jumpChapter = 0;
5144  return true;
5145 }
5146 
5147 int64_t MythPlayer::GetChapter(int chapter)
5148 {
5149  if (m_decoder)
5150  return m_decoder->GetChapter(chapter);
5151  return 0;
5152 }
5153 
5155 {
5156 #ifdef USING_MHEG
5158  {
5159  MythMultiLocker locker({&m_osdLock, &m_itvLock});
5160  if (!m_interactiveTV && m_osd)
5161  m_interactiveTV = new InteractiveTV(this);
5162  }
5163 #endif // USING_MHEG
5164  return m_interactiveTV;
5165 }
5166 
5168 {
5169  bool result = false;
5170 
5171 #ifdef USING_MHEG
5172  if (!GetInteractiveTV())
5173  return result;
5174 
5175  QMutexLocker locker(&m_itvLock);
5176  result = m_interactiveTV->OfferKey(action);
5177 #else
5178  Q_UNUSED(action);
5179 #endif // USING_MHEG
5180 
5181  return result;
5182 }
5183 
5187 void MythPlayer::ITVRestart(uint chanid, uint cardid, bool isLiveTV)
5188 {
5189 #ifdef USING_MHEG
5190  if (!GetInteractiveTV())
5191  return;
5192 
5193  QMutexLocker locker(&m_itvLock);
5194  m_interactiveTV->Restart(chanid, cardid, isLiveTV);
5195  m_itvVisible = false;
5196 #else
5197  Q_UNUSED(chanid);
5198  Q_UNUSED(cardid);
5199  Q_UNUSED(isLiveTV);
5200 #endif // USING_MHEG
5201 }
5202 
5203 // Called from the interactiveTV (MHIContext) thread
5204 void MythPlayer::SetVideoResize(const QRect &videoRect)
5205 {
5206  QMutexLocker locker(&m_osdLock);
5207  if (m_videoOutput)
5208  m_videoOutput->SetVideoResize(videoRect);
5209 }
5210 
5214 // Called from the interactiveTV (MHIContext) thread
5216 {
5217  QMutexLocker locker(&m_decoderChangeLock);
5218  if (m_decoder)
5219  return m_decoder->SetAudioByComponentTag(tag);
5220  return false;
5221 }
5222 
5226 // Called from the interactiveTV (MHIContext) thread
5228 {
5229  QMutexLocker locker(&m_decoderChangeLock);
5230  if (m_decoder)
5231  return m_decoder->SetVideoByComponentTag(tag);
5232  return false;
5233 }
5234 
5235 static inline double SafeFPS(DecoderBase *m_decoder)
5236 {
5237  if (!m_decoder)
5238  return 25;
5239  double fps = m_decoder->GetFPS();
5240  return fps > 0 ? fps : 25.0;
5241 }
5242 
5243 // Called from the interactiveTV (MHIContext) thread
5244 bool MythPlayer::SetStream(const QString &stream)
5245 {
5246  // The stream name is empty if the stream is closing
5247  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStream '%1'").arg(stream));
5248 
5249  QMutexLocker locker(&m_streamLock);
5250  m_newStream = stream;
5251  locker.unlock();
5252  // Stream will be changed by JumpToStream called from EventLoop
5253  // If successful will call m_interactiveTV->StreamStarted();
5254 
5255  if (stream.isEmpty() && m_playerCtx->m_tvchain &&
5257  {
5258  // Restore livetv
5260  m_playerCtx->m_tvchain->JumpToNext(false, 0);
5261  m_playerCtx->m_tvchain->JumpToNext(true, 0);
5262  }
5263 
5264  return !stream.isEmpty();
5265 }
5266 
5267 // Called from EventLoop pn the main application thread
5268 void MythPlayer::JumpToStream(const QString &stream)
5269 {
5270  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToStream - begin");
5271 
5272  if (stream.isEmpty())
5273  return; // Shouldn't happen
5274 
5275  Pause();
5276  ResetCaptions();
5277 
5278  ProgramInfo pginfo(stream);
5279  SetPlayingInfo(pginfo);
5280 
5283  else
5284  m_playerCtx->m_buffer->OpenFile(stream);
5285 
5286  if (!m_playerCtx->m_buffer->IsOpen())
5287  {
5288  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToStream buffer OpenFile failed");
5290  SetErrored(tr("Error opening remote stream buffer"));
5291  return;
5292  }
5293 
5294  m_watchingRecording = false;
5295  m_totalLength = 0;
5296  m_totalFrames = 0;
5297  m_totalDuration = 0;
5298 
5299  if (OpenFile(120) < 0) // 120 retries ~= 60 seconds
5300  {
5301  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToStream OpenFile failed.");
5303  SetErrored(tr("Error opening remote stream"));
5304  return;
5305  }
5306 
5307  if (m_totalLength == 0)
5308  {
5309  long long len = m_playerCtx->m_buffer->GetRealFileSize();
5310  m_totalLength = (int)(len / ((m_decoder->GetRawBitrate() * 1000) / 8));
5312  }
5313  LOG(VB_PLAYBACK, LOG_INFO, LOC +
5314  QString("JumpToStream length %1 bytes @ %2 Kbps = %3 Secs, %4 frames @ %5 fps")
5316  .arg(m_totalLength).arg(m_totalFrames).arg(m_decoder->GetFPS()) );
5317 
5319 
5320  // the bitrate is reset by m_playerCtx->m_buffer->OpenFile()...
5322  m_decoder->SetProgramInfo(pginfo);
5323 
5324  Play();
5325  ChangeSpeed();
5326 
5328 #ifdef USING_MHEG
5330 #endif
5331 
5332  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToStream - end");
5333 }
5334 
5335 // Called from the interactiveTV (MHIContext) thread
5337 {
5338  return (long)((1000 * GetFramesPlayed()) / SafeFPS(m_decoder));
5339 }
5340 
5341 // Called from the interactiveTV (MHIContext) thread
5343 {
5344  long maxpos = (long)(1000 * (m_totalDuration > 0 ? m_totalDuration : m_totalLength));
5345  long pos = GetStreamPos();
5346  return maxpos > pos ? maxpos : pos;
5347 }
5348 
5349 // Called from the interactiveTV (MHIContext) thread
5351 {
5352  auto frameNum = (uint64_t)((ms * SafeFPS(m_decoder)) / 1000);
5353  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStreamPos %1 mS = frame %2, now=%3")
5354  .arg(ms).arg(frameNum).arg(GetFramesPlayed()) );
5355  JumpToFrame(frameNum);
5356  return ms;
5357 }
5358 
5359 // Called from the interactiveTV (MHIContext) thread
5360 void MythPlayer::StreamPlay(bool play)
5361 {
5362  if (play)
5363  Play();
5364  else
5365  Pause();
5366 }
5367 
5372 {
5373  m_totalDecoderPause = true;
5374  PauseDecoder();
5375 
5376  {
5377  while (!m_decoderChangeLock.tryLock(10))
5378  LOG(VB_GENERAL, LOG_INFO, LOC + "Waited 10ms for decoder lock");
5379  delete m_decoder;
5380  m_decoder = dec;
5381  m_decoderChangeLock.unlock();
5382</