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  for (auto *lock : qAsConst(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'")
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  {
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)
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'")
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")
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
1874  {
1875  // the first deinterlacing pass will have marked the frame as already deinterlaced
1876  buffer->already_deinterlaced = false;
1878  }
1879  m_videoOutput->PrepareFrame(buffer, ps, m_osd);
1880  m_osdLock.unlock();
1881  // Display the second field
1882  if (!m_playerCtx->IsPBP() || m_playerCtx->IsPrimaryPBP())
1883  {
1884  int64_t due = framedue + m_frameInterval / 2;
1885  WaitForTime(due);
1886  }
1887  m_videoOutput->Show(ps);
1888  }
1889  }
1890  else if (!m_playerCtx->IsPiPOrSecondaryPBP())
1891  {
1892  WaitForTime(framedue);
1893  }
1894 
1895  LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC +
1896  QString("A/V timecodes audio=%1 video=%2 frameinterval=%3 "
1897  "audioadj=%4 tcoffset=%5 unow=%6 udue=%7 ")
1901  .arg(audio_adjustment)
1902  .arg(m_tcWrap[TC_AUDIO])
1903  .arg(unow)
1904  .arg(framedue)
1905  );
1906 
1907 }
1908 
1910 {
1911  if (m_needNewPauseFrame)
1912  {
1914  {
1916  m_needNewPauseFrame = false;
1917 
1918  if (m_deleteMap.IsEditing())
1919  {
1920  m_osdLock.lock();
1921  if (m_osd)
1923  m_osdLock.unlock();
1924  }
1925  }
1926  else
1927  {
1928  m_decodeOneFrame = true;
1929  }
1930  }
1931 }
1932 
1934 {
1935  if (!m_videoOutput)
1936  return;
1937 
1938  if (m_videoOutput->IsErrored())
1939  {
1940  SetErrored(tr("Serious error detected in Video Output"));
1941  return;
1942  }
1943 
1944  // clear the buffering state
1945  SetBuffering(false);
1946 
1948  PreProcessNormalFrame(); // Allow interactiveTV to draw on pause frame
1949 
1951  m_osdLock.lock();
1953  m_videoOutput->PrepareFrame(nullptr, scan, m_osd);
1954  m_osdLock.unlock();
1956 }
1957 
1958 void MythPlayer::SetBuffering(bool new_buffering)
1959 {
1960  if (!m_buffering && new_buffering)
1961  {
1962  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for video buffers...");
1963  m_buffering = true;
1964  m_bufferingStart = QTime::currentTime();
1965  m_bufferingLastMsg = QTime::currentTime();
1966  }
1967  else if (m_buffering && !new_buffering)
1968  {
1969  m_buffering = false;
1970  }
1971 }
1972 
1973 // For debugging playback set this to increase the timeout so that
1974 // playback does not fail if stepping through code.
1975 // Set PREBUFFERDEBUG to any value and you will get 30 minutes.
1976 static char *preBufferDebug = getenv("PREBUFFERDEBUG");
1977 
1979 {
1980  if (!m_videoOutput)
1981  return false;
1982 
1983  if (!(min_buffers ? (m_videoOutput->ValidVideoFrames() >= min_buffers) :
1985  {
1986  SetBuffering(true);
1987 
1988  // This piece of code is to address the problem, when starting
1989  // Live TV, of jerking and stuttering. Without this code
1990  // that could go on forever, but is cured by a pause and play.
1991  // This code inserts a brief pause and play when the potential
1992  // for the jerking is detected.
1993 
1994  bool watchingTV = IsWatchingInprogress();
1995  if ((m_liveTV || watchingTV) && !FlagIsSet(kMusicChoice))
1996  {
1997  uint64_t frameCount = GetCurrentFrameCount();
1998  uint64_t framesLeft = frameCount - m_framesPlayed;
1999  auto margin = static_cast<uint64_t>(m_videoFrameRate * 3);
2000  if (framesLeft < margin)
2001  {
2002  if (m_rtcBase)
2003  {
2004  LOG(VB_PLAYBACK, LOG_NOTICE, LOC + "Pause to allow live tv catch up");
2005  LOG(VB_PLAYBACK, LOG_NOTICE, LOC + QString("Played: %1 Avail: %2 Buffered: %3 Margin: %4")
2006  .arg(m_framesPlayed).arg(frameCount)
2007  .arg(m_videoOutput->ValidVideoFrames()).arg(margin));
2008  }
2009  m_audio.Pause(true);
2010  m_avsyncAudioPaused = true;
2011  m_rtcBase = 0;
2012  }
2013  }
2014  usleep(static_cast<uint>(m_frameInterval >> 3));
2015  int waited_for = m_bufferingStart.msecsTo(QTime::currentTime());
2016  int last_msg = m_bufferingLastMsg.msecsTo(QTime::currentTime());
2017  if (last_msg > 100 && !FlagIsSet(kMusicChoice))
2018  {
2019  if (++m_bufferingCounter == 10)
2020  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2021  "To see more buffering messages use -v playback");
2022  if (m_bufferingCounter >= 10)
2023  {
2024  LOG(VB_PLAYBACK, LOG_NOTICE, LOC +
2025  QString("Waited %1ms for video buffers %2")
2026  .arg(waited_for).arg(m_videoOutput->GetFrameStatus()));
2027  }
2028  else
2029  {
2030  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2031  QString("Waited %1ms for video buffers %2")
2032  .arg(waited_for).arg(m_videoOutput->GetFrameStatus()));
2033  }
2034  m_bufferingLastMsg = QTime::currentTime();
2036  && gCoreContext->GetBoolSetting("MusicChoiceEnabled", false))
2037  {
2039  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2040  "Music Choice program detected - disabling AV Sync.");
2041  m_avsyncAudioPaused = false;
2042  m_audio.Pause(false);
2043  }
2044  if (waited_for > 7000 && m_audio.IsBufferAlmostFull()
2045  && !FlagIsSet(kMusicChoice))
2046  {
2047  // We are likely to enter this condition
2048  // if the audio buffer was too full during GetFrame in AVFD
2049  LOG(VB_GENERAL, LOG_NOTICE, LOC + "Resetting audio buffer");
2050  m_audio.Reset();
2051  }
2052  // Finish audio pause for sync after 1 second
2053  // in case of infrequent video frames (e.g. music choice)
2054  if (m_avsyncAudioPaused && waited_for > 1000)
2055  {
2056  m_avsyncAudioPaused = false;
2057  m_audio.Pause(false);
2058  }
2059  }
2060  int msecs = 500;
2061  if (preBufferDebug)
2062  msecs = 1800000;
2063  if ((waited_for > msecs /*500*/) && !m_videoOutput->EnoughFreeFrames())
2064  {
2065  LOG(VB_GENERAL, LOG_NOTICE, LOC +
2066  "Timed out waiting for frames, and"
2067  "\n\t\t\tthere are not enough free frames. "
2068  "Discarding buffered frames.");
2069  // This call will result in some ugly frames, but allows us
2070  // to recover from serious problems if frames get leaked.
2071  DiscardVideoFrames(true, true);
2072  }
2073  msecs = 30000;
2074  if (preBufferDebug)
2075  msecs = 1800000;
2076  if (waited_for > msecs /*30000*/) // 30 seconds for internet streamed media
2077  {
2078  LOG(VB_GENERAL, LOG_ERR, LOC +
2079  "Waited too long for decoder to fill video buffers. Exiting..");
2080  SetErrored(tr("Video frame buffering failed too many times."));
2081  }
2082  return false;
2083  }
2084 
2085  if (!m_avsyncAudioPaused)
2086  m_audio.Pause(false);
2087  SetBuffering(false);
2088  return true;
2089 }
2090 
2092 {
2093  if (!frame)
2094  return;
2095 
2096  if (!qFuzzyCompare(frame->aspect, m_videoAspect) && frame->aspect > 0.0F)
2097  {
2098  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2099  QString("Video Aspect ratio changed from %1 to %2")
2100  .arg(m_videoAspect).arg(frame->aspect));
2101  m_videoAspect = frame->aspect;
2102  if (m_videoOutput)
2103  {
2105  ReinitOSD();
2106  }
2107  }
2108 }
2109 
2110 void MythPlayer::DisplayNormalFrame(bool check_prebuffer)
2111 {
2112  if (m_allPaused)
2113  return;
2114 
2115  bool ispip = m_playerCtx->IsPIP();
2116  if (ispip)
2117  {
2119  return;
2120  }
2121  else if (check_prebuffer && !PrebufferEnoughFrames())
2122  {
2123  return;
2124  }
2125 
2126  // clear the buffering state
2127  SetBuffering(false);
2128 
2129  // If PiP then release the last shown frame to the decoding queue
2130  if (ispip)
2131  if (!PipSync())
2132  return;
2133 
2134  // retrieve the next frame
2137 
2138  // Check aspect ratio
2139  CheckAspectRatio(frame);
2140 
2142  UpdateFFRewSkip();
2143 
2144  // Player specific processing (dvd, bd, mheg etc)
2146 
2147  // handle scan type changes
2148  AutoDeint(frame);
2149  m_detectLetterBox->SwitchTo(frame);
2150 
2151  if (!ispip)
2152  AVSync(frame);
2153 
2154  // Update details for debug OSD
2157  // We use the underlying pix_fmt as it retains the distinction between hardware
2158  // and software frames for decode only decoders.
2159  m_lastFrameCodec = PixelFormatToFrameType(static_cast<AVPixelFormat>(frame->pix_fmt));
2160 
2161  // If PiP then keep this frame for MythPlayer::GetCurrentFrame
2162  if (!ispip)
2164 }
2165 
2167 {
2168 #ifdef USING_MHEG
2169  // handle Interactive TV
2170  if (GetInteractiveTV())
2171  {
2172  m_osdLock.lock();
2173  m_itvLock.lock();
2174  if (m_osd && m_videoOutput->GetOSDPainter())
2175  {
2176  InteractiveScreen *window =
2178  if ((m_interactiveTV->ImageHasChanged() || !m_itvVisible) && window)
2179  {
2181  m_itvVisible = true;
2182  }
2183  }
2184  m_itvLock.unlock();
2185  m_osdLock.unlock();
2186  }
2187 #endif // USING_MHEG
2188 }
2189 
2191 {
2192  int refreshinterval = 1;
2193  if (m_display)
2194  refreshinterval = m_display->GetRefreshInterval(m_frameInterval);
2195 
2196  // At this point we may not have the correct frame rate.
2197  // Since interlaced is always at 25 or 30 fps, if the interval
2198  // is less than 30000 (33fps) it must be representing one
2199  // field and not one frame, so multiply by 2.
2200  int realfi = m_frameInterval;
2201  if (m_frameInterval < 30000)
2202  realfi = m_frameInterval * 2;
2203  return ((realfi / 2.0) > (refreshinterval * 0.995));
2204 }
2205 
2207 {
2208  if (!m_outputJmeter)
2209  return;
2210  bool verbose = VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_ANY);
2211  double rate = enable ? m_videoFrameRate : verbose ? (m_videoFrameRate * 4) : 0.0;
2212  m_outputJmeter->SetNumCycles(static_cast<int>(rate));
2213 }
2214 
2215 void MythPlayer::ForceDeinterlacer(bool DoubleRate, MythDeintType Deinterlacer)
2216 {
2217  if (m_videoOutput)
2218  m_videoOutput->SetDeinterlacing(true, DoubleRate, Deinterlacer);
2219 }
2220 
2222 {
2223  if (!FlagIsSet(kVideoIsNull) && !m_playerCtx->IsPIP())
2224  {
2225  QRect visible;
2226  QRect total;
2227  float aspect = NAN;
2228  float scaling = NAN;
2229 
2230  m_osdLock.lock();
2231  m_osd = new OSD(this, m_tv, m_videoOutput->GetOSDPainter());
2232  m_videoOutput->GetOSDBounds(total, visible, aspect, scaling, 1.0F);
2233  m_osd->Init(visible, aspect);
2235 
2236 #ifdef USING_MHEG
2237  if (GetInteractiveTV())
2238  {
2239  QMutexLocker locker(&m_itvLock);
2240  m_interactiveTV->Reinit(total, visible, aspect);
2241  }
2242 #endif // USING_MHEG
2243 
2244  // If there is a forced text subtitle track (which is possible
2245  // in e.g. a .mkv container), and forced subtitles are
2246  // allowed, then start playback with that subtitle track
2247  // selected. Otherwise, use the frontend settings to decide
2248  // which captions/subtitles (if any) to enable at startup.
2249  // TODO: modify the fix to #10735 to use this approach
2250  // instead.
2251  bool hasForcedTextTrack = false;
2252  uint forcedTrackNumber = 0;
2254  {
2255  uint numTextTracks = m_decoder->GetTrackCount(kTrackTypeRawText);
2256  for (uint i = 0; !hasForcedTextTrack && i < numTextTracks; ++i)
2257  {
2259  {
2260  hasForcedTextTrack = true;
2261  forcedTrackNumber = i;
2262  }
2263  }
2264  }
2265  if (hasForcedTextTrack)
2266  SetTrack(kTrackTypeRawText, forcedTrackNumber);
2267  else
2269 
2270  m_osdLock.unlock();
2271  }
2272 
2273  SetPlaying(true);
2274  ClearAfterSeek(false);
2275 
2276  m_avsyncAvg = 0; // Frames till next sync check
2277 
2279 
2280  // Default to interlaced playback but set the tracker to progressive
2281  // Enable autodetection of interlaced/progressive from video stream
2282  // Previously we set to interlaced and the scan tracker to 2 but this
2283  // mis-'detected' a number of streams as interlaced when they are progressive.
2284  // This significantly reduces the number of errors and also ensures we do not
2285  // needlessly setup deinterlacers - which may consume significant resources.
2286  // We set to interlaced for those streams whose frame rate is initially detected
2287  // as e.g. 59.9 when it is actually 29.97 interlaced.
2289  m_scanLocked = false;
2290  m_doubleFramerate = false;
2291  m_scanTracker = -2;
2292 
2294  {
2297  }
2298 
2299  InitAVSync();
2300  AutoVisualise();
2301 }
2302 
2304 {
2305  ProcessCallbacks();
2306 
2307  if (m_videoPaused || m_isDummy)
2308  {
2309  switch (m_playerCtx->GetPIPState())
2310  {
2311  case kPIPonTV:
2312  case kPBPRight:
2313  break;
2314  case kPIPOff:
2315  case kPIPStandAlone:
2316  case kPBPLeft: // PrimaryBPB
2317  usleep(m_frameInterval);
2318  break;
2319  }
2321  }
2322  else
2324 
2327  else if (m_decoder && m_decoder->GetEof() != kEofStateNone)
2328  ++m_framesPlayed;
2329  else
2331  return !IsErrored();
2332 }
2333 
2335 {
2336  m_osdLock.lock();
2337  m_vidExitLock.lock();
2338  delete m_osd;
2339  delete m_videoOutput;
2340  m_osd = nullptr;
2341  m_videoOutput = nullptr;
2342  m_vidExitLock.unlock();
2343  m_osdLock.unlock();
2344 }
2345 
2346 bool MythPlayer::FastForward(float seconds)
2347 {
2348  if (!m_videoOutput)
2349  return false;
2350 
2351  if (m_ffTime <= 0)
2352  {
2353  float current = ComputeSecs(m_framesPlayed, true);
2354  float dest = current + seconds;
2355  float length = ComputeSecs(m_totalFrames, true);
2356 
2357  if (dest > length)
2358  {
2359  int64_t pos = TranslatePositionMsToFrame(seconds * 1000, false);
2360  if (CalcMaxFFTime(pos) < 0)
2361  return true;
2362  // Reach end of recording, go to 1 or 3s before the end
2363  dest = (m_liveTV || IsWatchingInprogress()) ? -3.0 : -1.0;
2364  }
2365  uint64_t target = FindFrame(dest, true);
2366  m_ffTime = target - m_framesPlayed;
2367  }
2368  return m_ffTime > CalcMaxFFTime(m_ffTime, false);
2369 }
2370 
2371 bool MythPlayer::Rewind(float seconds)
2372 {
2373  if (!m_videoOutput)
2374  return false;
2375 
2376  if (m_rewindTime <= 0)
2377  {
2378  float current = ComputeSecs(m_framesPlayed, true);
2379  float dest = current - seconds;
2380  if (dest < 0)
2381  {
2382  int64_t pos = TranslatePositionMsToFrame(seconds * 1000, false);
2383  if (CalcRWTime(pos) < 0)
2384  return true;
2385  dest = 0;
2386  }
2387  uint64_t target = FindFrame(dest, true);
2388  m_rewindTime = m_framesPlayed - target;
2389  }
2390  return (uint64_t)m_rewindTime >= m_framesPlayed;
2391 }
2392 
2393 bool MythPlayer::JumpToFrame(uint64_t frame)
2394 {
2395  if (!m_videoOutput)
2396  return false;
2397 
2398  bool ret = false;
2399  m_ffTime = m_rewindTime = 0;
2400  if (frame > m_framesPlayed)
2401  {
2402  m_ffTime = frame - m_framesPlayed;
2403  ret = m_ffTime > CalcMaxFFTime(m_ffTime, false);
2404  }
2405  else if (frame < m_framesPlayed)
2406  {
2407  m_rewindTime = m_framesPlayed - frame;
2408  ret = m_ffTime > CalcMaxFFTime(m_ffTime, false);
2409  }
2410  return ret;
2411 }
2412 
2413 
2414 void MythPlayer::JumpChapter(int chapter)
2415 {
2416  if (m_jumpChapter == 0)
2417  m_jumpChapter = chapter;
2418 }
2419 
2420 void MythPlayer::ResetPlaying(bool resetframes)
2421 {
2422  ClearAfterSeek();
2423  m_ffrewSkip = 1;
2424  if (resetframes)
2425  m_framesPlayed = 0;
2426  if (m_decoder)
2427  {
2428  m_decoder->Reset(true, true, true);
2429  if (m_decoder->IsErrored())
2430  SetErrored("Unable to reset video decoder");
2431  }
2432 }
2433 
2435 {
2436  bool last = !(m_playerCtx->m_tvchain->HasNext());
2437  SetWatchingRecording(last);
2438 }
2439 
2441 {
2442  if (!IsReallyNearEnd())
2443  return;
2444 
2445  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchToProgram - start");
2446  bool discontinuity = false;
2447  bool newtype = false;
2448  int newid = -1;
2450  discontinuity, newtype, newid);
2451  if (!pginfo)
2452  return;
2453 
2454  bool newIsDummy = m_playerCtx->m_tvchain->GetInputType(newid) == "DUMMY";
2455 
2456  SetPlayingInfo(*pginfo);
2457  Pause();
2458  ChangeSpeed();
2459 
2460  // Release all frames to ensure the current decoder resources are released
2461  DiscardVideoFrames(true, true);
2462 
2463  if (newIsDummy)
2464  {
2465  OpenDummy();
2466  ResetPlaying();
2468  delete pginfo;
2469  return;
2470  }
2471 
2473  {
2474  // Restore original ringbuffer
2475  auto *ic = dynamic_cast<MythInteractiveBuffer*>(m_playerCtx->m_buffer);
2476  if (ic)
2477  m_playerCtx->m_buffer = ic->TakeBuffer();
2478  delete ic;
2479  }
2480 
2483 
2484  if (!m_playerCtx->m_buffer->IsOpen())
2485  {
2486  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram's OpenFile failed " +
2487  QString("(input type: %1).")
2488  .arg(m_playerCtx->m_tvchain->GetInputType(newid)));
2489  LOG(VB_GENERAL, LOG_ERR, m_playerCtx->m_tvchain->toString());
2491  SetErrored(tr("Error opening switch program buffer"));
2492  delete pginfo;
2493  return;
2494  }
2495 
2496  if (GetEof() != kEofStateNone)
2497  {
2498  discontinuity = true;
2499  ResetCaptions();
2500  }
2501 
2502  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SwitchToProgram(void) "
2503  "discont: %1 newtype: %2 newid: %3 decoderEof: %4")
2504  .arg(discontinuity).arg(newtype).arg(newid).arg(GetEof()));
2505 
2506  if (discontinuity || newtype)
2507  {
2508  m_playerCtx->m_tvchain->SetProgram(*pginfo);
2509  if (m_decoder)
2510  m_decoder->SetProgramInfo(*pginfo);
2511 
2512  m_playerCtx->m_buffer->Reset(true);
2513  if (newtype)
2514  {
2515  if (OpenFile() < 0)
2516  SetErrored(tr("Error opening switch program file"));
2517  }
2518  else
2519  ResetPlaying();
2520  }
2521  else
2522  {
2524  if (m_decoder)
2525  {
2528  }
2529  }
2530  delete pginfo;
2531 
2532  if (IsErrored())
2533  {
2534  LOG(VB_GENERAL, LOG_ERR, LOC + "SwitchToProgram failed.");
2536  return;
2537  }
2538 
2540 
2541  // the bitrate is reset by m_playerCtx->m_buffer->OpenFile()...
2542  if (m_decoder)
2545 
2546  if (discontinuity || newtype)
2547  {
2548  CheckTVChain();
2549  m_forcePositionMapSync = true;
2550  }
2551 
2552  Play();
2553  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchToProgram - end");
2554 }
2555 
2556 // This is called from decoder thread. Set an indicator that will
2557 // be checked and actioned in the player thread.
2559 {
2560  LOG(VB_PLAYBACK, LOG_INFO, LOC + "FileChangedCallback");
2561  m_fileChanged = true;
2562 }
2563 
2564 // Called from the player thread.
2566 {
2567  m_fileChanged = false;
2568  LOG(VB_PLAYBACK, LOG_INFO, LOC + "FileChanged");
2569 
2570  Pause();
2571  ChangeSpeed();
2572  if (dynamic_cast<AvFormatDecoder *>(m_decoder))
2573  m_playerCtx->m_buffer->Reset(false, true);
2574  else
2575  m_playerCtx->m_buffer->Reset(false, true, true);
2577  Play();
2578 
2580 
2581  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
2583  if (m_decoder)
2585  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
2586 
2587  CheckTVChain();
2588  m_forcePositionMapSync = true;
2589 }
2590 
2591 
2592 
2593 
2595 {
2596  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToProgram - start");
2597  bool discontinuity = false;
2598  bool newtype = false;
2599  int newid = -1;
2600  long long nextpos = m_playerCtx->m_tvchain->GetJumpPos();
2602  discontinuity, newtype, newid);
2603  if (!pginfo)
2604  return;
2605 
2606  m_inJumpToProgramPause = true;
2607 
2608  bool newIsDummy = m_playerCtx->m_tvchain->GetInputType(newid) == "DUMMY";
2609  SetPlayingInfo(*pginfo);
2610 
2611  Pause();
2612  ChangeSpeed();
2613  ResetCaptions();
2614 
2615  // Release all frames to ensure the current decoder resources are released
2616  DiscardVideoFrames(true, true);
2617 
2618  m_playerCtx->m_tvchain->SetProgram(*pginfo);
2619  m_playerCtx->m_buffer->Reset(true);
2620 
2621  if (newIsDummy)
2622  {
2623  OpenDummy();
2624  ResetPlaying();
2626  delete pginfo;
2627  m_inJumpToProgramPause = false;
2628  return;
2629  }
2630 
2631  SendMythSystemPlayEvent("PLAY_CHANGED", pginfo);
2632 
2634  {
2635  // Restore original ringbuffer
2636  auto *ic = dynamic_cast<MythInteractiveBuffer*>(m_playerCtx->m_buffer);
2637  if (ic)
2638  m_playerCtx->m_buffer = ic->TakeBuffer();
2639  delete ic;
2640  }
2641 
2643  QString subfn = m_playerCtx->m_buffer->GetSubtitleFilename();
2644  TVState desiredState = m_playerCtx->GetState();
2645  bool isInProgress = (desiredState == kState_WatchingRecording ||
2646  desiredState == kState_WatchingLiveTV);
2647  if (GetSubReader())
2648  GetSubReader()->LoadExternalSubtitles(subfn, isInProgress &&
2649  !subfn.isEmpty());
2650 
2651  if (!m_playerCtx->m_buffer->IsOpen())
2652  {
2653  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram's OpenFile failed " +
2654  QString("(input type: %1).")
2655  .arg(m_playerCtx->m_tvchain->GetInputType(newid)));
2656  LOG(VB_GENERAL, LOG_ERR, m_playerCtx->m_tvchain->toString());
2658  SetErrored(tr("Error opening jump program file buffer"));
2659  delete pginfo;
2660  m_inJumpToProgramPause = false;
2661  return;
2662  }
2663 
2664  bool wasDummy = m_isDummy;
2665  if (newtype || wasDummy)
2666  {
2667  if (OpenFile() < 0)
2668  SetErrored(tr("Error opening jump program file"));
2669  }
2670  else
2671  ResetPlaying();
2672 
2673  if (IsErrored() || !m_decoder)
2674  {
2675  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToProgram failed.");
2676  if (!IsErrored())
2677  SetErrored(tr("Error reopening video decoder"));
2678  delete pginfo;
2679  m_inJumpToProgramPause = false;
2680  return;
2681  }
2682 
2684 
2685  // the bitrate is reset by m_playerCtx->m_buffer->OpenFile()...
2688 
2689  m_decoder->SetProgramInfo(*pginfo);
2690  delete pginfo;
2691 
2692  CheckTVChain();
2693  m_forcePositionMapSync = true;
2694  m_inJumpToProgramPause = false;
2695  Play();
2696  ChangeSpeed();
2697 
2698  // check that we aren't too close to the end of program.
2699  // and if so set it to 10s from the end if completed recordings
2700  // or 3s if live
2701  long long duration = m_playerCtx->m_tvchain->GetLengthAtCurPos();
2702  int maxpos = m_playerCtx->m_tvchain->HasNext() ? 10 : 3;
2703 
2704  if (nextpos > (duration - maxpos))
2705  {
2706  nextpos = duration - maxpos;
2707  if (nextpos < 0)
2708  {
2709  nextpos = 0;
2710  }
2711  }
2712  else if (nextpos < 0)
2713  {
2714  // it's a relative position to the end
2715  nextpos += duration;
2716  }
2717 
2718  // nextpos is the new position to use in seconds
2719  nextpos = TranslatePositionMsToFrame(nextpos * 1000, true);
2720 
2721  if (nextpos > 10)
2722  DoJumpToFrame(nextpos, kInaccuracyNone);
2723 
2725  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToProgram - end");
2726 }
2727 
2729 {
2730  if (OpenFile() < 0)
2731  {
2732  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to open video file.");
2733  return false;
2734  }
2735 
2736  m_framesPlayed = 0;
2737  m_rewindTime = m_ffTime = 0;
2739  m_jumpChapter = 0;
2742 
2743  if (!InitVideo())
2744  {
2745  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to initialize video.");
2747  return false;
2748  }
2749 
2750  bool seek = m_bookmarkSeek > 30;
2751  EventStart();
2752  DecoderStart(true);
2753  if (seek)
2754  InitialSeek();
2755  VideoStart();
2756 
2757  m_playerThread->setPriority(QThread::TimeCriticalPriority);
2758 #ifdef Q_OS_ANDROID
2759  setpriority(PRIO_PROCESS, m_playerThreadId, -20);
2760 #endif
2761  ProcessCallbacks();
2762  UnpauseDecoder();
2763  return !IsErrored();
2764 }
2765 
2767 {
2768  // TODO handle initial commskip and/or cutlist skip as well
2769  if (m_bookmarkSeek > 30)
2770  {
2773  SetBookmark(true);
2774  }
2775 }
2776 
2777 
2779 {
2780  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StopPlaying - begin"));
2781  m_playerThread->setPriority(QThread::NormalPriority);
2782 #ifdef Q_OS_ANDROID
2783  setpriority(PRIO_PROCESS, m_playerThreadId, 0);
2784 #endif
2785 
2786  ProcessCallbacks();
2787  DecoderEnd();
2788  VideoEnd();
2789  AudioEnd();
2790 
2791  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StopPlaying - end"));
2792 }
2793 
2795 {
2796  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
2797  {
2799  {
2800  // When initial playback gets underway, we override the ProgramInfo
2801  // flags such that future calls to GetBookmark() will consider only
2802  // an actual bookmark and not progstart or lastplaypos information.
2806  }
2807  }
2808  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
2810 }
2811 
2813 {
2814  // Handle decoder callbacks
2815  ProcessCallbacks();
2816 
2817  // Live TV program change
2818  if (m_fileChanged)
2819  FileChanged();
2820 
2821  // recreate the osd if a reinit was triggered by another thread
2822  if (m_reinitOsd)
2823  ReinitOSD();
2824 
2825  // reselect subtitle tracks if triggered by the decoder
2826  if (m_enableCaptions)
2827  SetCaptionsEnabled(true, false);
2828  if (m_disableCaptions)
2829  SetCaptionsEnabled(false, false);
2830 
2831  // enable/disable forced subtitles if signalled by the decoder
2836 
2837  // reset the scan (and hence deinterlacers) if triggered by the decoder
2838  if (m_resetScan != kScan_Ignore)
2840 
2841  // refresh the position map for an in-progress recording while editing
2843  {
2844  if (m_editUpdateTimer.hasExpired(2000))
2845  {
2846  // N.B. the positionmap update and osd refresh are asynchronous
2847  m_forcePositionMapSync = true;
2848  m_osdLock.lock();
2850  m_osdLock.unlock();
2851  m_editUpdateTimer.start();
2852  }
2853  }
2854 
2855  // Refresh the programinfo in use status
2856  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
2859  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
2860 
2861  // Disable timestretch if we are too close to the end of the buffer
2862  if (m_ffrewSkip == 1 && (m_playSpeed > 1.0F) && IsNearEnd())
2863  {
2864  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near end, Slowing down playback.");
2865  Play(1.0F, true, true);
2866  }
2867 
2869  {
2870  // Switch from the dummy recorder to the tuned program in livetv
2871  m_playerCtx->m_tvchain->JumpToNext(true, 0);
2872  JumpToProgram();
2873  }
2874  else if ((!m_allPaused || GetEof() != kEofStateNone) &&
2877  {
2878  // Switch to the next program in livetv
2880  SwitchToProgram();
2881  }
2882 
2883  // Jump to the next program in livetv
2885  {
2886  JumpToProgram();
2887  }
2888 
2889  // Change interactive stream if requested
2890  { QMutexLocker locker(&m_streamLock);
2891  if (!m_newStream.isEmpty())
2892  {
2893  QString stream = m_newStream;
2894  m_newStream.clear();
2895  locker.unlock();
2896  JumpToStream(stream);
2897  }}
2898 
2899  // Disable fastforward if we are too close to the end of the buffer
2900  if (m_ffrewSkip > 1 && (CalcMaxFFTime(100, false) < 100))
2901  {
2902  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near end, stopping fastforward.");
2903  Play(1.0F, true, true);
2904  }
2905 
2906  // Disable rewind if we are too close to the beginning of the buffer
2907  if (m_ffrewSkip < 0 && CalcRWTime(-m_ffrewSkip) >= 0 &&
2909  {
2910  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Near start, stopping rewind.");
2911  float stretch = (m_ffrewSkip > 0) ? 1.0F : m_audio.GetStretchFactor();
2912  Play(stretch, true, true);
2913  }
2914 
2915  // Check for error
2917  {
2918  LOG(VB_GENERAL, LOG_ERR, LOC +
2919  "Unknown recorder error, exiting decoder");
2920  if (!IsErrored())
2921  SetErrored(tr("Irrecoverable recorder error"));
2922  m_killDecoder = true;
2923  return;
2924  }
2925 
2926  // Handle speed change
2927  if (m_playSpeed != m_nextPlaySpeed &&
2928  (!m_playerCtx->m_tvchain ||
2930  {
2931  ChangeSpeed();
2932  return;
2933  }
2934 
2935  // Check if we got a communication error, and if so pause playback
2937  {
2938  Pause();
2940  }
2941 
2942  // Handle end of file
2943  EofState eof = GetEof();
2944  if (HasReachedEof())
2945  {
2946 #ifdef USING_MHEG
2948  {
2949  Pause();
2950  return;
2951  }
2952 #endif
2954  {
2955  LOG(VB_GENERAL, LOG_NOTICE, LOC + "LiveTV forcing JumpTo 1");
2956  m_playerCtx->m_tvchain->JumpToNext(true, 0);
2957  return;
2958  }
2959 
2960  bool videoDrained =
2962  bool audioDrained =
2963  !m_audio.GetAudioOutput() ||
2964  m_audio.IsPaused() ||
2966  if (eof != kEofStateDelayed || (videoDrained && audioDrained))
2967  {
2968  if (eof == kEofStateDelayed)
2969  {
2970  LOG(VB_PLAYBACK, LOG_INFO,
2971  QString("waiting for no video frames %1")
2973  }
2974  LOG(VB_PLAYBACK, LOG_INFO,
2975  QString("HasReachedEof() at framesPlayed=%1 totalFrames=%2")
2977  Pause();
2978  SetPlaying(false);
2979  return;
2980  }
2981  }
2982 
2983  // Handle rewind
2984  if (m_rewindTime > 0 && (m_ffrewSkip == 1 || m_ffrewSkip == 0))
2985  {
2987  if (m_rewindTime > 0)
2989  }
2990 
2991  // Handle fast forward
2992  if (m_ffTime > 0 && (m_ffrewSkip == 1 || m_ffrewSkip == 0))
2993  {
2995  if (m_ffTime > 0)
2996  {
2998  if (GetEof() != kEofStateNone)
2999  return;
3000  }
3001  }
3002 
3003  // Handle chapter jump
3004  if (m_jumpChapter != 0)
3006 
3007  // Handle commercial skipping
3008  if (m_commBreakMap.GetSkipCommercials() != 0 && (m_ffrewSkip == 1))
3009  {
3010  if (!m_commBreakMap.HasMap())
3011  {
3012  //: The commercials/adverts have not been flagged
3013  SetOSDStatus(tr("Not Flagged"), kOSDTimeout_Med);
3014  QString message = "COMMFLAG_REQUEST ";
3015  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3016  message += QString("%1").arg(m_playerCtx->m_playingInfo->GetChanID()) +
3018  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3019  gCoreContext->SendMessage(message);
3020  }
3021  else
3022  {
3023  QString msg;
3024  uint64_t jumpto = 0;
3025  uint64_t frameCount = GetCurrentFrameCount();
3026  // XXX CommBreakMap should use duration map not m_videoFrameRate
3027  bool jump = m_commBreakMap.DoSkipCommercials(jumpto, m_framesPlayed,
3029  frameCount, msg);
3030  if (!msg.isEmpty())
3032  if (jump)
3033  DoJumpToFrame(jumpto, kInaccuracyNone);
3034  }
3036  return;
3037  }
3038 
3039  // Handle automatic commercial skipping
3040  uint64_t jumpto = 0;
3041  if (m_deleteMap.IsEmpty() && (m_ffrewSkip == 1) &&
3044  {
3045  QString msg;
3046  uint64_t frameCount = GetCurrentFrameCount();
3047  // XXX CommBreakMap should use duration map not m_videoFrameRate
3048  bool jump = m_commBreakMap.AutoCommercialSkip(jumpto, m_framesPlayed,
3050  frameCount, msg);
3051  if (!msg.isEmpty())
3053  if (jump)
3054  DoJumpToFrame(jumpto, kInaccuracyNone);
3055  }
3056 
3057  // Handle cutlist skipping
3058  if (!m_allPaused && (m_ffrewSkip == 1) &&
3060  {
3061  if (jumpto == m_totalFrames)
3062  {
3063  if (!(m_endExitPrompt == 1 && !m_playerCtx->IsPIP() &&
3065  {
3067  }
3068  }
3069  else
3070  {
3071  DoJumpToFrame(jumpto, kInaccuracyNone);
3072  }
3073  }
3074 }
3075 
3077 {
3079 }
3080 
3087  void *Opaque1, void *Opaque2)
3088 {
3089  if (!Function)
3090  return;
3091 
3092  m_decoderCallbackLock.lock();
3093  QAtomicInt ready{0};
3094  QWaitCondition wait;
3095  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Queuing callback for %1").arg(Debug));
3096  m_decoderCallbacks.append(DecoderCallback(Debug, Function, &ready, &wait, Opaque1, Opaque2));
3097  int count = 0;
3098  while (!ready && !wait.wait(&m_decoderCallbackLock, 100) && (count += 100))
3099  LOG(VB_GENERAL, LOG_WARNING, QString("Waited %1ms for %2").arg(count).arg(Debug));
3100  m_decoderCallbackLock.unlock();
3101 }
3102 
3104 {
3105  m_decoderCallbackLock.lock();
3106  for (auto *it = m_decoderCallbacks.begin(); it != m_decoderCallbacks.end(); ++it)
3107  {
3108  if (it->m_function)
3109  {
3110  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Executing %1").arg(it->m_debug));
3111  it->m_function(it->m_opaque1, it->m_opaque2, it->m_opaque3);
3112  }
3113  if (it->m_ready)
3114  it->m_ready->ref();
3115  }
3116  m_decoderCallbacks.clear();
3117  m_decoderCallbackLock.unlock();
3118 }
3119 
3121 {
3122  m_decoderPauseLock.lock();
3124  {
3125  m_decoderPaused = true;
3126  m_decoderThreadPause.wakeAll();
3127  m_decoderPauseLock.unlock();
3128  return m_decoderPaused;
3129  }
3130 
3131  int tries = 0;
3132  m_pauseDecoder = true;
3133  while (m_decoderThread && !m_killDecoder && (tries++ < 100) &&
3135  {
3136  ProcessCallbacks();
3137  LOG(VB_GENERAL, LOG_WARNING, LOC + "Waited 100ms for decoder to pause");
3138  }
3139  m_pauseDecoder = false;
3140  m_decoderPauseLock.unlock();
3141  return m_decoderPaused;
3142 }
3143 
3145 {
3146  m_decoderPauseLock.lock();
3147 
3149  {
3150  m_decoderPaused = false;
3151  m_decoderThreadUnpause.wakeAll();
3152  m_decoderPauseLock.unlock();
3153  return;
3154  }
3155 
3156  if (!IsInStillFrame())
3157  {
3158  int tries = 0;
3159  m_unpauseDecoder = true;
3160  while (m_decoderThread && !m_killDecoder && (tries++ < 100) &&
3162  {
3163  ProcessCallbacks();
3164  LOG(VB_GENERAL, LOG_WARNING, LOC + "Waited 100ms for decoder to unpause");
3165  }
3166  m_unpauseDecoder = false;
3167  }
3168  m_decoderPauseLock.unlock();
3169 }
3170 
3171 void MythPlayer::DecoderStart(bool start_paused)
3172 {
3173  if (m_decoderThread)
3174  {
3175  if (m_decoderThread->isRunning())
3176  {
3177  LOG(VB_GENERAL, LOG_ERR, LOC + "Decoder thread already running");
3178  }
3179  delete m_decoderThread;
3180  }
3181 
3182  m_killDecoder = false;
3183  m_decoderPaused = start_paused;
3184  m_decoderThread = new DecoderThread(this, start_paused);
3185  if (m_decoderThread)
3187 }
3188 
3190 {
3191  PauseDecoder();
3192  SetPlaying(false);
3193  // Ensure any hardware frames are released (after pausing the decoder) to
3194  // allow the decoder to exit cleanly
3195  DiscardVideoFrames(true, true);
3196 
3197  m_killDecoder = true;
3198  int tries = 0;
3199  while (m_decoderThread && !m_decoderThread->wait(100) && (tries++ < 50))
3200  LOG(VB_PLAYBACK, LOG_INFO, LOC +
3201  "Waited 100ms for decoder loop to stop");
3202 
3204  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to stop decoder loop.");
3205  else
3206  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exited decoder loop.");
3207  SetDecoder(nullptr);
3208 }
3209 
3211 {
3213  {
3214  if (m_pauseDecoder)
3215  PauseDecoder();
3216  if (m_unpauseDecoder)
3217  UnpauseDecoder();
3218  }
3219 }
3220 
3223 {
3226 
3227  if (!m_decoderChangeLock.tryLock(50))
3228  return kEofStateNone;
3229 
3231  m_decoderChangeLock.unlock();
3232  return eof;
3233 }
3234 
3236 {
3238  {
3239  if (m_decoder)
3240  m_decoder->SetEofState(eof);
3241  return;
3242  }
3243 
3244  if (!m_decoderChangeLock.tryLock(50))
3245  return;
3246 
3247  if (m_decoder)
3248  m_decoder->SetEofState(eof);
3249  m_decoderChangeLock.unlock();
3250 }
3252 
3253 void MythPlayer::DecoderLoop(bool pause)
3254 {
3255  if (pause)
3256  PauseDecoder();
3257 
3258  while (!m_killDecoder && !IsErrored())
3259  {
3261 
3263  {
3264  usleep(1000);
3265  continue;
3266  }
3267 
3269  {
3270  if (!m_decoderChangeLock.tryLock(1))
3271  continue;
3272  if (m_decoder)
3273  {
3274  m_forcePositionMapSync = false;
3276  }
3277  m_decoderChangeLock.unlock();
3278  }
3279 
3280  if (m_decoderSeek >= 0)
3281  {
3282  if (!m_decoderChangeLock.tryLock(1))
3283  continue;
3284  if (m_decoder)
3285  {
3286  m_decoderSeekLock.lock();
3287  if (((uint64_t)m_decoderSeek < m_framesPlayed) && m_decoder)
3289  else if (m_decoder)
3291  m_decoderSeek = -1;
3292  m_decoderSeekLock.unlock();
3293  }
3294  m_decoderChangeLock.unlock();
3295  }
3296 
3297  bool obey_eof = (GetEof() != kEofStateNone) &&
3299  if (m_isDummy || ((m_decoderPaused || m_ffrewSkip == 0 || obey_eof) &&
3300  !m_decodeOneFrame))
3301  {
3302  usleep(1000);
3303  continue;
3304  }
3305 
3308 
3309  DecoderGetFrame(dt);
3310  m_decodeOneFrame = false;
3311  }
3312 
3313  // Clear any wait conditions
3315  m_decoderSeek = -1;
3316 }
3317 
3319 {
3320  if (!m_decoder)
3321  return false;
3322 
3323  if (m_ffrewSkip > 0)
3324  {
3325  long long delta = m_decoder->GetFramesRead() - m_framesPlayed;
3326  long long real_skip = CalcMaxFFTime(m_ffrewSkip - m_ffrewAdjust + delta) - delta;
3327  long long target_frame = m_decoder->GetFramesRead() + real_skip;
3328  if (real_skip >= 0)
3329  {
3330  m_decoder->DoFastForward(target_frame, false);
3331  }
3332  long long seek_frame = m_decoder->GetFramesRead();
3333  m_ffrewAdjust = seek_frame - target_frame;
3334  }
3335  else if (CalcRWTime(-m_ffrewSkip) >= 0)
3336  {
3338  }
3340 }
3341 
3343 {
3344  long long cur_frame = m_decoder->GetFramesPlayed();
3345  bool toBegin = -cur_frame > m_ffrewSkip + m_ffrewAdjust;
3346  long long real_skip = (toBegin) ? -cur_frame : m_ffrewSkip + m_ffrewAdjust;
3347  long long target_frame = cur_frame + real_skip;
3348  bool ret = m_decoder->DoRewind(target_frame, false);
3349  long long seek_frame = m_decoder->GetFramesPlayed();
3350  m_ffrewAdjust = target_frame - seek_frame;
3351  return ret;
3352 }
3353 
3354 bool MythPlayer::DecoderGetFrame(DecodeType decodetype, bool unsafe)
3355 {
3356  bool ret = false;
3357  if (!m_videoOutput)
3358  return false;
3359 
3360  // Wait for frames to be available for decoding onto
3361  int tries = 0;
3362  while (!unsafe &&
3363  (!m_videoOutput->EnoughFreeFrames() || GetAudio()->IsBufferAlmostFull()) )
3364  {
3366  return false;
3367 
3368  if (++tries > 10)
3369  {
3370  if (++m_videobufRetries >= 2000)
3371  {
3372  LOG(VB_GENERAL, LOG_ERR, LOC +
3373  "Decoder timed out waiting for free video buffers.");
3374  // We've tried for 20 seconds now, give up so that we don't
3375  // get stuck permanently in this state
3376  SetErrored("Decoder timed out waiting for free video buffers.");
3377  }
3378  return false;
3379  }
3380 
3381  usleep(1000);
3382  }
3383  m_videobufRetries = 0;
3384 
3385  if (!m_decoderChangeLock.tryLock(5))
3386  return false;
3388  {
3389  m_decoderChangeLock.unlock();
3390  return false;
3391  }
3392 
3393  if (m_ffrewSkip == 1 || m_decodeOneFrame)
3394  ret = DoGetFrame(decodetype);
3395  else if (m_ffrewSkip != 0)
3396  ret = DecoderGetFrameFFREW();
3397  m_decoderChangeLock.unlock();
3398  return ret;
3399 }
3400 
3415 {
3416  bool ret = false;
3417  QElapsedTimer timeout;
3418  timeout.start();
3419  bool retry = true;
3420  // retry for a maximum of 5 seconds
3421  while (retry && !m_pauseDecoder && !m_killDecoder && !timeout.hasExpired(5000))
3422  {
3423  retry = false;
3424  ret = m_decoder->GetFrame(Type, retry);
3425  if (retry)
3426  {
3427  m_decoderChangeLock.unlock();
3428  QThread::usleep(10000);
3429  m_decoderChangeLock.lock();
3430  }
3431  }
3432 
3433  if (timeout.hasExpired(5000))
3434  return false;
3435  return ret;
3436 }
3437 
3439 {
3440  m_transcoding = value;
3441 
3442  if (m_decoder)
3443  m_decoder->SetTranscoding(value);
3444 }
3445 
3447 {
3449  {
3450  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot add PiP from another thread");
3451  return false;
3452  }
3453 
3454  if (m_pipPlayers.contains(pip))
3455  {
3456  LOG(VB_GENERAL, LOG_ERR, LOC + "PiPMap already contains PiP.");
3457  return false;
3458  }
3459 
3460  QList<PIPLocation> locs = m_pipPlayers.values();
3461  if (locs.contains(loc))
3462  {
3463  LOG(VB_GENERAL, LOG_ERR, LOC +"Already have a PiP at that location.");
3464  return false;
3465  }
3466 
3467  m_pipPlayers.insert(pip, loc);
3468  return true;
3469 }
3470 
3472 {
3474  return false;
3475 
3476  if (!m_pipPlayers.contains(pip))
3477  return false;
3478 
3479  m_pipPlayers.remove(pip);
3480  if (m_videoOutput)
3481  m_videoOutput->RemovePIP(pip);
3482  return true;
3483 }
3484 
3486 {
3488  return kPIP_END;
3489 
3490  if (m_pipPlayers.isEmpty())
3491  return m_pipDefaultLoc;
3492 
3493  // order of preference, could be stored in db if we want it configurable
3494  PIPLocation ols[] =
3496 
3497  for (auto & ol : ols)
3498  {
3499  PIPMap::const_iterator it = m_pipPlayers.begin();
3500  for (; it != m_pipPlayers.end() && (*it != ol); ++it);
3501 
3502  if (it == m_pipPlayers.end())
3503  return ol;
3504  }
3505 
3506  return kPIP_END;
3507 }
3508 
3509 int64_t MythPlayer::AdjustAudioTimecodeOffset(int64_t v, int newsync)
3510 {
3511  if ((newsync >= -1000) && (newsync <= 1000))
3512  m_tcWrap[TC_AUDIO] = newsync;
3513  else
3514  m_tcWrap[TC_AUDIO] += v;
3515  return m_tcWrap[TC_AUDIO];
3516 }
3517 
3518 void MythPlayer::WrapTimecode(int64_t &timecode, TCTypes tc_type)
3519 {
3520  timecode += m_tcWrap[tc_type];
3521 }
3522 
3523 bool MythPlayer::PrepareAudioSample(int64_t &timecode)
3524 {
3525  WrapTimecode(timecode, TC_AUDIO);
3526  return false;
3527 }
3528 
3543 void MythPlayer::SetWatched(bool forceWatched)
3544 {
3545  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3546  if (!m_playerCtx->m_playingInfo)
3547  {
3548  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3549  return;
3550  }
3551 
3552  uint64_t numFrames = GetCurrentFrameCount();
3553 
3554  // For recordings we want to ignore the post-roll and account for
3555  // in-progress recordings where totalFrames doesn't represent
3556  // the full length of the recording. For videos we can only rely on
3557  // totalFrames as duration metadata can be wrong
3561  {
3562 
3563  // If the recording is stopped early we need to use the recording end
3564  // time, not the programme end time
3566  qint64 starttime = pi->GetRecordingStartTime().toSecsSinceEpoch();
3567  qint64 endactual = pi->GetRecordingEndTime().toSecsSinceEpoch();
3568  qint64 endsched = pi->GetScheduledEndTime().toSecsSinceEpoch();
3569  qint64 endtime = min(endactual, endsched);
3570  numFrames = (long long) ((endtime - starttime) * m_videoFrameRate);
3571  }
3572 
3573  int offset = (int) round(0.14 * (numFrames / m_videoFrameRate));
3574 
3575  if (offset < 240)
3576  offset = 240; // 4 Minutes Min
3577  else if (offset > 720)
3578  offset = 720; // 12 Minutes Max
3579 
3580  if (forceWatched || m_framesPlayed > numFrames - (offset * m_videoFrameRate))
3581  {
3583  LOG(VB_GENERAL, LOG_INFO, LOC +
3584  QString("Marking recording as watched using offset %1 minutes")
3585  .arg(offset/60));
3586  }
3587 
3588  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3589 }
3590 
3592 {
3593  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3596  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3597 }
3598 
3600 {
3601  uint64_t bookmark = 0;
3602 
3605  bookmark = 0;
3606  else
3607  {
3608  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3609  if (const ProgramInfo *pi = m_playerCtx->m_playingInfo)
3610  {
3611  bookmark = pi->QueryBookmark();
3612  // Disable progstart if the program has a cutlist.
3613  if (bookmark == 0 && !pi->HasCutlist())
3614  bookmark = pi->QueryProgStart();
3615  if (bookmark == 0)
3616  bookmark = pi->QueryLastPlayPos();
3617  }
3618  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3619  }
3620 
3621  return bookmark;
3622 }
3623 
3625 {
3626  bool skip_changed = false;
3627 
3628  float temp_speed = (m_playSpeed == 0.0F) ?
3630  if (m_playSpeed >= 0.0F && m_playSpeed <= 3.0F)
3631  {
3632  skip_changed = (m_ffrewSkip != 1);
3633  if (m_decoder)
3635  m_frameInterval = (int) (1000000.0 / m_videoFrameRate / static_cast<double>(temp_speed))
3636  / m_fpsMultiplier;
3637  m_ffrewSkip = static_cast<int>(m_playSpeed != 0.0F);
3638  }
3639  else
3640  {
3641  skip_changed = true;
3642  m_frameInterval = 200000;
3643  m_frameInterval = (fabs(m_playSpeed) >= 3.0F) ? 133466 : m_frameInterval;
3644  m_frameInterval = (fabs(m_playSpeed) >= 5.0F) ? 133466 : m_frameInterval;
3645  m_frameInterval = (fabs(m_playSpeed) >= 8.0F) ? 250250 : m_frameInterval;
3646  m_frameInterval = (fabs(m_playSpeed) >= 10.0F) ? 133466 : m_frameInterval;
3647  m_frameInterval = (fabs(m_playSpeed) >= 16.0F) ? 187687 : m_frameInterval;
3648  m_frameInterval = (fabs(m_playSpeed) >= 20.0F) ? 150150 : m_frameInterval;
3649  m_frameInterval = (fabs(m_playSpeed) >= 30.0F) ? 133466 : m_frameInterval;
3650  m_frameInterval = (fabs(m_playSpeed) >= 60.0F) ? 133466 : m_frameInterval;
3651  m_frameInterval = (fabs(m_playSpeed) >= 120.0F) ? 133466 : m_frameInterval;
3652  m_frameInterval = (fabs(m_playSpeed) >= 180.0F) ? 133466 : m_frameInterval;
3653  float ffw_fps = fabs(static_cast<double>(m_playSpeed)) * m_videoFrameRate;
3654  float dis_fps = 1000000.0F / m_frameInterval;
3655  m_ffrewSkip = (int)ceil(ffw_fps / dis_fps);
3657  m_ffrewAdjust = 0;
3658  }
3659 
3660  return skip_changed;
3661 }
3662 
3664 {
3665  float last_speed = m_playSpeed;
3668  m_rtcBase = 0;
3669 
3670  bool skip_changed = UpdateFFRewSkip();
3671 
3672  if (skip_changed && m_videoOutput)
3673  {
3675  if (m_playSpeed != 0.0F && !(last_speed == 0.0F && m_ffrewSkip == 1))
3677  }
3678 
3679  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Play speed: " +
3680  QString("rate: %1 speed: %2 skip: %3 => new interval %4")
3681  .arg(m_videoFrameRate).arg(static_cast<double>(m_playSpeed))
3683 
3684  if (m_videoOutput)
3685  m_videoOutput->SetVideoFrameRate(static_cast<float>(m_videoFrameRate));
3686 
3687  // ensure we re-check double rate support following a speed change
3688  m_scanInitialized = false;
3689  m_scanLocked = false;
3690 
3692  {
3695  }
3696 }
3697 
3698 bool MythPlayer::DoRewind(uint64_t frames, double inaccuracy)
3699 {
3701  return false;
3702 
3703  uint64_t number = frames + 1;
3704  uint64_t desiredFrame = (m_framesPlayed > number) ? m_framesPlayed - number : 0;
3705 
3706  m_limitKeyRepeat = false;
3707  if (desiredFrame < m_videoFrameRate)
3708  m_limitKeyRepeat = true;
3709 
3710  uint64_t seeksnap_wanted = UINT64_MAX;
3711  if (inaccuracy != kInaccuracyFull)
3712  seeksnap_wanted = frames * inaccuracy;
3713  ClearBeforeSeek(frames);
3714  WaitForSeek(desiredFrame, seeksnap_wanted);
3715  m_rewindTime = 0;
3716  ClearAfterSeek();
3717  return true;
3718 }
3719 
3720 bool MythPlayer::DoRewindSecs(float secs, double inaccuracy, bool use_cutlist)
3721 {
3722  float current = ComputeSecs(m_framesPlayed, use_cutlist);
3723  float target = current - secs;
3724  if (target < 0)
3725  target = 0;
3726  uint64_t targetFrame = FindFrame(target, use_cutlist);
3727  return DoRewind(m_framesPlayed - targetFrame, inaccuracy);
3728 }
3729 
3735 long long MythPlayer::CalcRWTime(long long rw) const
3736 {
3737  bool hasliveprev = (m_liveTV && m_playerCtx->m_tvchain &&
3739 
3740  if (!hasliveprev || ((int64_t)m_framesPlayed >= rw))
3741  {
3742  return rw;
3743  }
3744 
3745  m_playerCtx->m_tvchain->JumpToNext(false, ((int64_t)m_framesPlayed - rw) / m_videoFrameRate);
3746 
3747  return -1;
3748 }
3749 
3754 long long MythPlayer::CalcMaxFFTime(long long ffframes, bool setjump) const
3755 {
3756  float maxtime = 1.0;
3757  bool islivetvcur = (m_liveTV && m_playerCtx->m_tvchain &&
3759 
3760  if (m_liveTV || IsWatchingInprogress())
3761  maxtime = 3.0;
3762 
3763  long long ret = ffframes;
3764  float ff = ComputeSecs(ffframes, true);
3765  float secsPlayed = ComputeSecs(m_framesPlayed, true);
3766  float secsWritten = ComputeSecs(m_totalFrames, true);
3767 
3768  m_limitKeyRepeat = false;
3769 
3770  if (m_liveTV && !islivetvcur && m_playerCtx->m_tvchain)
3771  {
3772  // recording has completed, totalFrames will always be up to date
3773  if ((ffframes + m_framesPlayed > m_totalFrames) && setjump)
3774  {
3775  ret = -1;
3776  // Number of frames to be skipped is from the end of the current segment
3777  m_playerCtx->m_tvchain->JumpToNext(true, ((int64_t)m_totalFrames - (int64_t)m_framesPlayed - ffframes) / m_videoFrameRate);
3778  }
3779  }
3780  else if (islivetvcur || IsWatchingInprogress())
3781  {
3782  if ((ff + secsPlayed) > secsWritten)
3783  {
3784  // If we attempt to seek past the last known duration,
3785  // check for up to date data
3786  long long framesWritten = m_playerCtx->m_recorder->GetFramesWritten();
3787 
3788  secsWritten = ComputeSecs(framesWritten, true);
3789  }
3790 
3791  float behind = secsWritten - secsPlayed;
3792 
3793  if (behind < maxtime) // if we're close, do nothing
3794  ret = 0;
3795  else if (behind - ff <= maxtime)
3796  ret = TranslatePositionMsToFrame(1000 * (secsWritten - maxtime),
3797  true) - m_framesPlayed;
3798 
3799  if (behind < maxtime * 3)
3800  m_limitKeyRepeat = true;
3801  }
3802  else if (IsPaused())
3803  {
3804  uint64_t lastFrame =
3806  if (m_framesPlayed + ffframes >= lastFrame)
3807  ret = lastFrame - 1 - m_framesPlayed;
3808  }
3809  else
3810  {
3811  float secsMax = secsWritten - 2.F * maxtime;
3812  if (secsMax <= 0.F)
3813  ret = 0;
3814  else if (secsMax < secsPlayed + ff)
3815  ret = TranslatePositionMsToFrame(1000 * secsMax, true)
3816  - m_framesPlayed;
3817  }
3818 
3819  return ret;
3820 }
3821 
3829 {
3830  if (!m_videoOutput || !m_decoder)
3831  return false;
3832 
3833  return m_playerCtx->m_buffer->IsNearEnd(
3835 }
3836 
3840 {
3841  if (!m_playerCtx)
3842  return false;
3843 
3844  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
3846  !m_decoder)
3847  {
3848  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3849  return false;
3850  }
3851  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
3852 
3853  auto margin = (long long)(m_videoFrameRate * 2);
3854  margin = (long long) (margin * m_audio.GetStretchFactor());
3855  bool watchingTV = IsWatchingInprogress();
3856 
3857  uint64_t framesRead = m_framesPlayed;
3858  uint64_t framesLeft = 0;
3859 
3860  if (!m_playerCtx->IsPIP() &&
3862  {
3863  if (framesRead >= m_deleteMap.GetLastFrame())
3864  return true;
3865  uint64_t frameCount = GetCurrentFrameCount();
3866  framesLeft = (frameCount > framesRead) ? frameCount - framesRead : 0;
3867  return (framesLeft < (uint64_t)margin);
3868  }
3869 
3870  if (!m_liveTV && !watchingTV)
3871  return false;
3872 
3874  return false;
3875 
3876  if (m_playerCtx->m_recorder)
3877  {
3878  framesLeft =
3880 
3881  // if it looks like we are near end, get an updated GetFramesWritten()
3882  if (framesLeft < (uint64_t)margin)
3883  framesLeft = m_playerCtx->m_recorder->GetFramesWritten() - framesRead;
3884  }
3885 
3886  return (framesLeft < (uint64_t)margin);
3887 }
3888 
3889 bool MythPlayer::DoFastForward(uint64_t frames, double inaccuracy)
3890 {
3892  return false;
3893 
3894  uint64_t number = (frames ? frames - 1 : 0);
3895  uint64_t desiredFrame = m_framesPlayed + number;
3896 
3897  if (!m_deleteMap.IsEditing() && IsInDelete(desiredFrame))
3898  {
3899  uint64_t endcheck = m_deleteMap.GetLastFrame();
3900  if (desiredFrame > endcheck)
3901  desiredFrame = endcheck;
3902  }
3903 
3904  uint64_t seeksnap_wanted = UINT64_MAX;
3905  if (inaccuracy != kInaccuracyFull)
3906  seeksnap_wanted = frames * inaccuracy;
3907  ClearBeforeSeek(frames);
3908  WaitForSeek(desiredFrame, seeksnap_wanted);
3909  m_ffTime = 0;
3910  ClearAfterSeek(false);
3911  return true;
3912 }
3913 
3914 bool MythPlayer::DoFastForwardSecs(float secs, double inaccuracy,
3915  bool use_cutlist)
3916 {
3917  float current = ComputeSecs(m_framesPlayed, use_cutlist);
3918  float target = current + secs;
3919  uint64_t targetFrame = FindFrame(target, use_cutlist);
3920  return DoFastForward(targetFrame - m_framesPlayed, inaccuracy);
3921 }
3922 
3923 void MythPlayer::DoJumpToFrame(uint64_t frame, double inaccuracy)
3924 {
3925  if (frame > m_framesPlayed)
3926  DoFastForward(frame - m_framesPlayed, inaccuracy);
3927  else if (frame <= m_framesPlayed)
3928  DoRewind(m_framesPlayed - frame, inaccuracy);
3929 }
3930 
3931 void MythPlayer::WaitForSeek(uint64_t frame, uint64_t seeksnap_wanted)
3932 {
3933  if (!m_decoder)
3934  return;
3935 
3937  m_decoder->SetSeekSnap(seeksnap_wanted);
3938 
3939  bool islivetvcur = (m_liveTV && m_playerCtx->m_tvchain &&
3941 
3942  uint64_t max = GetCurrentFrameCount();
3943  if (islivetvcur || IsWatchingInprogress())
3944  {
3945  max = (uint64_t)m_playerCtx->m_recorder->GetFramesWritten();
3946  }
3947  if (frame >= max)
3948  frame = max - 1;
3949 
3950  m_decoderSeekLock.lock();
3951  m_decoderSeek = frame;
3952  m_decoderSeekLock.unlock();
3953 
3954  int count = 0;
3955  bool need_clear = false;
3956  while (m_decoderSeek >= 0)
3957  {
3958  // Waiting blocks the main UI thread but the decoder may
3959  // have initiated a callback into the UI thread to create
3960  // certain resources. Ensure the callback is processed.
3961  // Ideally MythPlayer should be fully event driven and these
3962  // calls wouldn't be necessary.
3963  ProcessCallbacks();
3964 
3965  usleep(50 * 1000);
3966 
3967  // provide some on screen feedback if seeking is slow
3968  count++;
3969  if (!(count % 3) && !m_hasFullPositionMap)
3970  {
3971  int num = count % 3;
3972  SetOSDMessage(tr("Searching") + QString().fill('.', num),
3975  need_clear = true;
3976  }
3977  }
3978  if (need_clear)
3979  {
3980  m_osdLock.lock();
3981  if (m_osd)
3982  m_osd->HideWindow("osd_message");
3983  m_osdLock.unlock();
3984  }
3985 }
3986 
3999 void MythPlayer::ClearAfterSeek(bool clearvideobuffers)
4000 {
4001  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("ClearAfterSeek(%1)")
4002  .arg(clearvideobuffers));
4003 
4004  if (clearvideobuffers && m_videoOutput)
4006 
4007  int64_t savedTC = m_tcWrap[TC_AUDIO];
4008 
4009  for (int j = 0; j < TCTYPESMAX; j++)
4010  m_tcWrap[j] = m_tcLastVal[j] = 0;
4011 
4012  m_tcWrap[TC_AUDIO] = savedTC;
4013 
4014  m_audio.Reset();
4015  // Reenable (or re-disable) subtitles, which ultimately does
4016  // nothing except to call ResetCaptions() to erase any captions
4017  // currently on-screen. The key is that the erasing is done in
4018  // the UI thread, not the decoder thread.
4023  m_needNewPauseFrame = true;
4024  ResetAVSync();
4025 }
4026 
4034 // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
4035 void MythPlayer::ClearBeforeSeek(uint64_t Frames)
4036 {
4037 #ifdef USING_MEDIACODEC
4038  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("ClearBeforeSeek: decoder %1 frames %2 recording %3 livetv %4")
4040 
4041  if ((Frames < 2) || !m_videoOutput /*|| !(m_liveTV || m_watchingRecording)*/)
4042  return;
4043 
4044  m_decoderChangeLock.lock();
4046  m_decoderChangeLock.unlock();
4047  if (codec_is_mediacodec(codec))
4048  m_videoOutput->DiscardFrames(true, true);
4049 #else
4050  Q_UNUSED(Frames);
4051 #endif
4052 }
4053 
4054 void MythPlayer::SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
4055 {
4057  m_tv = tv;
4058  m_parentWidget = widget;
4059  m_playerCtx = ctx;
4060  m_liveTV = ctx->m_tvchain;
4061 }
4062 
4064 {
4065  m_deleteMap.SetEditing(false);
4066 
4067  if (!m_hasFullPositionMap)
4068  {
4069  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot edit - no full position map");
4070  SetOSDStatus(tr("No Seektable"), kOSDTimeout_Med);
4071  return false;
4072  }
4073 
4074  if (m_deleteMap.IsFileEditing())
4075  return false;
4076 
4077  QMutexLocker locker(&m_osdLock);
4078  if (!m_osd)
4079  return false;
4080 
4082  int sample_rate = GetAudio()->GetSampleRate();
4083  m_audiograph.SetSampleRate(sample_rate);
4084  m_audiograph.SetSampleCount((unsigned)(sample_rate / m_videoFrameRate));
4086 
4088  m_tcWrap[TC_AUDIO] = 0;
4089 
4092  m_deleteMap.SetEditing(true);
4093  m_osd->DialogQuit();
4094  ResetCaptions();
4095  m_osd->HideAll();
4096 
4097  bool loadedAutoSave = m_deleteMap.LoadAutoSaveMap();
4098  if (loadedAutoSave)
4099  {
4100  SetOSDMessage(tr("Using previously auto-saved cuts"),
4102  }
4103 
4107  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
4110  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
4111  m_editUpdateTimer.start();
4112 
4113  return m_deleteMap.IsEditing();
4114 }
4115 
4123 void MythPlayer::DisableEdit(int howToSave)
4124 {
4125  QMutexLocker locker(&m_osdLock);
4126  if (!m_osd)
4127  return;
4128 
4129  m_deleteMap.SetEditing(false, m_osd);
4130  if (howToSave == 0)
4131  m_deleteMap.LoadMap();
4132  // Unconditionally save to remove temporary marks from the DB.
4133  if (howToSave >= 0)
4134  m_deleteMap.SaveMap();
4136  m_deleteMap.SetFileEditing(false);
4137  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
4140  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
4142  m_audiograph.Reset();
4145 
4146  if (!m_pausedBeforeEdit)
4148  else
4149  SetOSDStatus(tr("Paused"), kOSDTimeout_None);
4150 }
4151 
4152 bool MythPlayer::HandleProgramEditorActions(QStringList &actions)
4153 {
4154  bool handled = false;
4155  bool refresh = true;
4156  long long frame = GetFramesPlayed();
4157 
4158  for (int i = 0; i < actions.size() && !handled; i++)
4159  {
4160  QString action = actions[i];
4161  handled = true;
4162  float seekamount = m_deleteMap.GetSeekAmount();
4163  if (action == ACTION_LEFT)
4164  {
4165  if (seekamount == 0) // 1 frame
4167  else if (seekamount > 0)
4168  {
4169  // Use fully-accurate seeks for less than 1 second.
4170  DoRewindSecs(seekamount, seekamount < 1.0F ? kInaccuracyNone :
4171  kInaccuracyEditor, false);
4172  }
4173  else
4174  {
4175  HandleArbSeek(false);
4176  }
4177  }
4178  else if (action == ACTION_RIGHT)
4179  {
4180  if (seekamount == 0) // 1 frame
4182  else if (seekamount > 0)
4183  {
4184  // Use fully-accurate seeks for less than 1 second.
4185  DoFastForwardSecs(seekamount, seekamount < 1.0F ? kInaccuracyNone :
4186  kInaccuracyEditor, false);
4187  }
4188  else
4189  {
4190  HandleArbSeek(true);
4191  }
4192  }
4193  else if (action == ACTION_LOADCOMMSKIP)
4194  {
4195  if (m_commBreakMap.HasMap())
4196  {
4197  frm_dir_map_t map;
4198  m_commBreakMap.GetMap(map);
4200  }
4201  }
4202  else if (action == ACTION_PREVCUT)
4203  {
4204  float old_seekamount = m_deleteMap.GetSeekAmount();
4206  HandleArbSeek(false);
4207  m_deleteMap.SetSeekAmount(old_seekamount);
4208  }
4209  else if (action == ACTION_NEXTCUT)
4210  {
4211  float old_seekamount = m_deleteMap.GetSeekAmount();
4213  HandleArbSeek(true);
4214  m_deleteMap.SetSeekAmount(old_seekamount);
4215  }
4216 #define FFREW_MULTICOUNT 10.0F
4217  else if (action == ACTION_BIGJUMPREW)
4218  {
4219  if (seekamount == 0)
4221  else if (seekamount > 0)
4222  {
4223  DoRewindSecs(seekamount * FFREW_MULTICOUNT,
4224  kInaccuracyEditor, false);
4225  }
4226  else
4227  {
4229  kInaccuracyNone, false);
4230  }
4231  }
4232  else if (action == ACTION_BIGJUMPFWD)
4233  {
4234  if (seekamount == 0)
4236  else if (seekamount > 0)
4237  {
4238  DoFastForwardSecs(seekamount * FFREW_MULTICOUNT,
4239  kInaccuracyEditor, false);
4240  }
4241  else
4242  {
4244  kInaccuracyNone, false);
4245  }
4246  }
4247  else if (action == ACTION_SELECT)
4248  {
4249  m_deleteMap.NewCut(frame);
4250  SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
4251  refresh = true;
4252  }
4253  else if (action == "DELETE")
4254  {
4255  m_deleteMap.Delete(frame, tr("Delete"));
4256  refresh = true;
4257  }
4258  else if (action == "REVERT")
4259  {
4260  m_deleteMap.LoadMap(tr("Undo Changes"));
4261  refresh = true;
4262  }
4263  else if (action == "REVERTEXIT")
4264  {
4265  DisableEdit(0);
4266  refresh = false;
4267  }
4268  else if (action == ACTION_SAVEMAP)
4269  {
4270  m_deleteMap.SaveMap();
4271  refresh = true;
4272  }
4273  else if (action == "EDIT" || action == "SAVEEXIT")
4274  {
4275  DisableEdit(1);
4276  refresh = false;
4277  }
4278  else
4279  {
4280  QString undoMessage = m_deleteMap.GetUndoMessage();
4281  QString redoMessage = m_deleteMap.GetRedoMessage();
4282  handled = m_deleteMap.HandleAction(action, frame);
4283  if (handled && (action == "CUTTOBEGINNING" ||
4284  action == "CUTTOEND" || action == "NEWCUT"))
4285  {
4286  SetOSDMessage(tr("New cut added."), kOSDTimeout_Short);
4287  }
4288  else if (handled && action == "UNDO")
4289  {
4290  //: %1 is the undo message
4291  SetOSDMessage(tr("Undo - %1").arg(undoMessage),
4293  }
4294  else if (handled && action == "REDO")
4295  {
4296  //: %1 is the redo message
4297  SetOSDMessage(tr("Redo - %1").arg(redoMessage),
4299  }
4300  }
4301  }
4302 
4303  if (handled && refresh)
4304  {
4305  m_osdLock.lock();
4306  if (m_osd)
4307  {
4309  }
4310  m_osdLock.unlock();
4311  }
4312 
4313  return handled;
4314 }
4315 
4316 bool MythPlayer::IsInDelete(uint64_t frame)
4317 {
4318  return m_deleteMap.IsInDelete(frame);
4319 }
4320 
4321 uint64_t MythPlayer::GetNearestMark(uint64_t frame, bool right)
4322 {
4323  return m_deleteMap.GetNearestMark(frame, right);
4324 }
4325 
4326 bool MythPlayer::IsTemporaryMark(uint64_t frame)
4327 {
4328  return m_deleteMap.IsTemporaryMark(frame);
4329 }
4330 
4332 {
4333  return m_deleteMap.HasTemporaryMark();
4334 }
4335 
4337 {
4338  if (m_deleteMap.GetSeekAmount() == -2)
4339  {
4340  uint64_t framenum = m_deleteMap.GetNearestMark(m_framesPlayed, right);
4341  if (right && (framenum > m_framesPlayed))
4343  else if (!right && (m_framesPlayed > framenum))
4345  }
4346  else
4347  {
4348  if (right)
4350  else
4352  }
4353 }
4354 
4356 {
4357  if (m_videoOutput)
4358  return m_videoOutput->GetAspectOverride();
4359  return kAspect_Off;
4360 }
4361 
4363 {
4364  if (m_videoOutput)
4365  return m_videoOutput->GetAdjustFill();
4366  return kAdjustFill_Off;
4367 }
4368 
4370 {
4371  if (m_videoOutput)
4372  {
4373  m_videoOutput->ToggleAspectOverride(aspectMode);
4374  ReinitOSD();
4375  }
4376 }
4377 
4379 {
4380  if (m_videoOutput)
4381  {
4383  m_videoOutput->ToggleAdjustFill(adjustfillMode);
4384  ReinitOSD();
4385  }
4386 }
4387 
4389 {
4390  if (m_videoOutput)
4391  {
4392  m_videoOutput->Zoom(direction);
4393  ReinitOSD();
4394  }
4395 }
4396 
4398 {
4399  if (m_videoOutput)
4400  {
4402  ReinitOSD();
4403  }
4404 }
4405 
4407 {
4408  if (m_videoOutput)
4410 }
4411 
4413 {
4414  if (m_videoOutput)
4415  return m_videoOutput->IsEmbedding();
4416  return false;
4417 }
4418 
4420 {
4422 }
4423 
4439 char *MythPlayer::GetScreenGrab(int SecondsIn, int &BufferSize,
4440  int &FrameWidth, int &FrameHeight, float &AspectRatio)
4441 {
4442  auto frameNum = static_cast<uint64_t>(SecondsIn * m_videoFrameRate);
4443  return GetScreenGrabAtFrame(frameNum, false, BufferSize, FrameWidth, FrameHeight, AspectRatio);
4444 }
4445 
4462 char *MythPlayer::GetScreenGrabAtFrame(uint64_t FrameNum, bool Absolute,
4463  int &BufferSize, int &FrameWidth, int &FrameHeight,
4464  float &AspectRatio)
4465 {
4466  BufferSize = 0;
4467  FrameWidth = FrameHeight = 0;
4468  AspectRatio = 0;
4469 
4470  if (OpenFile(0) < 0)
4471  {
4472  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not open file for preview.");
4473  return nullptr;
4474  }
4475 
4476  if ((m_videoDim.width() <= 0) || (m_videoDim.height() <= 0))
4477  {
4478  LOG(VB_PLAYBACK, LOG_ERR, LOC +
4479  QString("Video Resolution invalid %1x%2")
4480  .arg(m_videoDim.width()).arg(m_videoDim.height()));
4481 
4482  // This is probably an audio file, just return a grey frame.
4483  FrameWidth = 640;
4484  FrameHeight = 480;
4485  AspectRatio = 4.0F / 3.0F;
4486 
4487  BufferSize = FrameWidth * FrameHeight * 4;
4488  char* result = new char[BufferSize];
4489  memset(result, 0x3f, static_cast<size_t>(BufferSize) * sizeof(char));
4490  return result;
4491  }
4492 
4493  if (!InitVideo())
4494  {
4495  LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to initialize video for screen grab.");
4496  return nullptr;
4497  }
4498 
4499  ClearAfterSeek();
4500  if (!m_decoderThread)
4501  DecoderStart(true /*start paused*/);
4502  uint64_t dummy = 0;
4503  SeekForScreenGrab(dummy, FrameNum, Absolute);
4504  int tries = 0;
4505  while (!m_videoOutput->ValidVideoFrames() && ((tries++) < 500))
4506  {
4507  m_decodeOneFrame = true;
4508  usleep(10000);
4509  if ((tries & 10) == 10)
4510  LOG(VB_PLAYBACK, LOG_INFO, LOC + "ScreenGrab: Waited 100ms for video frame");
4511  }
4512 
4513  VideoFrame *frame = nullptr;
4514  if (!(frame = m_videoOutput->GetLastDecodedFrame()))
4515  return nullptr;
4516  if (!frame->buf)
4517  return nullptr;
4518 
4519  if (frame->interlaced_frame)
4520  {
4521  // Use high quality - which is currently yadif
4522  frame->deinterlace_double = DEINT_NONE;
4524  MythDeinterlacer deinterlacer;
4525  deinterlacer.Filter(frame, kScan_Interlaced, nullptr, true);
4526  }
4527  unsigned char *result = CreateBuffer(FMT_RGB32, m_videoDim.width(), m_videoDim.height());
4528  MythAVCopy copyCtx;
4529  AVFrame retbuf;
4530  memset(&retbuf, 0, sizeof(AVFrame));
4531  copyCtx.Copy(&retbuf, frame, result, AV_PIX_FMT_RGB32);
4532  FrameWidth = m_videoDispDim.width();
4533  FrameHeight = m_videoDispDim.height();
4534  AspectRatio = frame->aspect;
4535 
4536  if (frame)
4537  DiscardVideoFrame(frame);
4538 
4539  return reinterpret_cast<char*>(result);
4540 }
4541 
4542 void MythPlayer::SeekForScreenGrab(uint64_t &number, uint64_t frameNum,
4543  bool absolute)
4544 {
4545  number = frameNum;
4546  if (number >= m_totalFrames)
4547  {
4548  LOG(VB_PLAYBACK, LOG_ERR, LOC +
4549  "Screen grab requested for frame number beyond end of file.");
4550  number = m_totalFrames / 2;
4551  }
4552 
4553  if (!absolute && m_hasFullPositionMap)
4554  {
4556  // Use the bookmark if we should, otherwise make sure we aren't
4557  // in the cutlist or a commercial break
4558  if (m_bookmarkSeek > 30)
4559  {
4560  number = m_bookmarkSeek;
4561  }
4562  else
4563  {
4564  uint64_t oldnumber = number;
4565  m_deleteMap.LoadMap();
4567 
4568  bool started_in_break_map = false;
4569  while (m_commBreakMap.IsInCommBreak(number) ||
4570  IsInDelete(number))
4571  {
4572  started_in_break_map = true;
4573  number += (uint64_t) (30 * m_videoFrameRate);
4574  if (number >= m_totalFrames)
4575  {
4576  number = oldnumber;
4577  break;
4578  }
4579  }
4580 
4581  // Advance a few seconds from the end of the break
4582  if (started_in_break_map)
4583  {
4584  oldnumber = number;
4585  number += (long long) (10 * m_videoFrameRate);
4586  if (number >= m_totalFrames)
4587  number = oldnumber;
4588  }
4589  }
4590  }
4591 
4593  DoJumpToFrame(number, kInaccuracyNone);
4594 }
4595 
4604 {
4605  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
4608  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
4609 
4610  if (!m_decoderThread)
4611  DecoderStart(false);
4612 
4613  if (frameNumber >= 0)
4614  {
4615  DoJumpToFrame(frameNumber, kInaccuracyNone);
4616  ClearAfterSeek();
4617  }
4618 
4619  int tries = 0;
4620  while (!m_videoOutput->ValidVideoFrames() && ((tries++) < 100))
4621  {
4622  m_decodeOneFrame = true;
4623  usleep(10000);
4624  if ((tries & 10) == 10)
4625  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waited 100ms for video frame");
4626  }
4627 
4629  return m_videoOutput->GetLastShownFrame();
4630 }
4631 
4632 QString MythPlayer::GetEncodingType(void) const
4633 {
4634  if (m_decoder)
4636  return QString();
4637 }
4638 
4640 {
4641  infoMap["audiocodec"] = ff_codec_id_string(m_audio.GetCodec());
4642  infoMap["audiochannels"] = QString::number(m_audio.GetOrigChannels());
4643 
4644  int width = m_videoDispDim.width();
4645  int height = m_videoDispDim.height();
4646  infoMap["videocodec"] = GetEncodingType();
4647  if (m_decoder)
4648  infoMap["videocodecdesc"] = m_decoder->GetRawEncodingType();
4649  infoMap["videowidth"] = QString::number(width);
4650  infoMap["videoheight"] = QString::number(height);
4651  infoMap["videoframerate"] = QString::number(m_videoFrameRate, 'f', 2);
4652  infoMap["deinterlacer"] = DeinterlacerName(m_lastDeinterlacer,
4654 
4655  if (width < 640)
4656  return;
4657 
4658  bool interlaced = is_interlaced(m_scan);
4659  if (height > 2100)
4660  infoMap["videodescrip"] = interlaced ? "UHD_4K_I" : "UHD_4K_P";
4661  else if (width == 1920 || height == 1080 || height == 1088)
4662  infoMap["videodescrip"] = interlaced ? "HD_1080_I" : "HD_1080_P";
4663  else if ((width == 1280 || height == 720) && !interlaced)
4664  infoMap["videodescrip"] = "HD_720_P";
4665  else if (height >= 720)
4666  infoMap["videodescrip"] = "HD";
4667  else
4668  infoMap["videodescrip"] = "SD";
4669 }
4670 
4672 {
4673  if (m_decoder)
4674  return m_decoder->GetRawAudioState();
4675  return false;
4676 }
4677 
4678 QString MythPlayer::GetXDS(const QString &key) const
4679 {
4680  if (!m_decoder)
4681  return QString();
4682  return m_decoder->GetXDS(key);
4683 }
4684 
4685 void MythPlayer::InitForTranscode(bool copyaudio, bool copyvideo)
4686 {
4687  // Are these really needed?
4688  SetPlaying(true);
4689  m_keyframeDist = 30;
4690 
4691  if (!InitVideo())
4692  {
4693  LOG(VB_GENERAL, LOG_ERR, LOC +
4694  "Unable to initialize video for transcode.");
4695  SetPlaying(false);
4696  return;
4697  }
4698 
4699  m_framesPlayed = 0;
4700  ClearAfterSeek();
4701 
4702  if (copyvideo && m_decoder)
4703  m_decoder->SetRawVideoState(true);
4704  if (copyaudio && m_decoder)
4705  m_decoder->SetRawAudioState(true);
4706 
4707  if (m_decoder)
4708  {
4709  m_decoder->SetSeekSnap(0);
4710  }
4711 }
4712 
4714  int &did_ff, bool &is_key, bool honorCutList)
4715 {
4716  m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
4719  m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
4720 
4721  int64_t lastDecodedFrameNumber =
4723 
4724  if ((lastDecodedFrameNumber == 0) && honorCutList)
4726 
4727  if (!m_decoderThread)
4728  DecoderStart(true/*start paused*/);
4729 
4730  if (!m_decoder)
4731  return false;
4732 
4733  {
4734  QMutexLocker decoderlocker(&m_decoderChangeLock);
4735  if (!DoGetFrame(kDecodeAV))
4736  return false;
4737  }
4738 
4739  if (GetEof() != kEofStateNone)
4740  return false;
4741 
4742  if (honorCutList && !m_deleteMap.IsEmpty())
4743  {
4744  if (m_totalFrames && lastDecodedFrameNumber >= (int64_t)m_totalFrames)
4745  return false;
4746 
4747  uint64_t jumpto = 0;
4748  if (m_deleteMap.TrackerWantsToJump(lastDecodedFrameNumber, jumpto))
4749  {
4750  LOG(VB_GENERAL, LOG_INFO, LOC +
4751  QString("Fast-Forwarding from %1 to %2")
4752  .arg(lastDecodedFrameNumber).arg(jumpto));
4753  if (jumpto >= m_totalFrames)
4754  {
4756  return false;
4757  }
4758 
4759  // For 0.25, move this to DoJumpToFrame(jumpto)
4760  WaitForSeek(jumpto, 0);
4762  ClearAfterSeek();
4763  m_decoderChangeLock.lock();
4765  m_decoderChangeLock.unlock();
4766  did_ff = 1;
4767  }
4768  }
4769  if (GetEof() != kEofStateNone)
4770  return false;
4771  is_key = m_decoder->IsLastFrameKey();
4772  return true;
4773 }
4774 
4775 long MythPlayer::UpdateStoredFrameNum(long curFrameNum)
4776 {
4777  if (m_decoder)
4778  return m_decoder->UpdateStoredFrameNum(curFrameNum);
4779  return 0;
4780 }
4781 
4782 void MythPlayer::SetCutList(const frm_dir_map_t &newCutList)
4783 {
4784  m_deleteMap.SetMap(newCutList);
4785 }
4786 
4788  bool Writevideo, long TimecodeOffset)
4789 {
4790  if (!m_decoder)
4791  return false;
4792  if (Writevideo && !m_decoder->GetRawVideoState())
4793  Writevideo = false;
4794  m_decoder->WriteStoredData(OutBuffer, Writevideo, TimecodeOffset);
4795  return Writevideo;
4796 }
4797 
4799 {
4801  m_forcePositionMapSync = true;
4802 }
4803 
4805 {
4806  double spos = 0.0;
4807 
4808  if (m_liveTV || IsWatchingInprogress())
4809  {
4810  spos = 1000.0 * m_framesPlayed / m_playerCtx->m_recorder->GetFramesWritten();
4811  }
4812  else if (m_totalFrames)
4813  {
4814  spos = 1000.0 * m_framesPlayed / m_totalFrames;
4815  }
4816 
4817  return((int)spos);
4818 }
4819 
4821 {
4822  QString samplerate = MythMediaBuffer::BitrateToString(m_audio.GetSampleRate(), true);
4823  infoMap.insert("samplerate", samplerate);
4824  infoMap.insert("filename", m_playerCtx->m_buffer->GetSafeFilename());
4825  infoMap.insert("decoderrate", m_playerCtx->m_buffer->GetDecoderRate());
4826  infoMap.insert("storagerate", m_playerCtx->m_buffer->GetStorageRate());
4827  infoMap.insert("bufferavail", m_playerCtx->m_buffer->GetAvailableBuffer());
4828  infoMap.insert("buffersize", QString::number(m_playerCtx->m_buffer->GetBufferSize() >> 20));
4829  int avsync = m_avsyncAvg / 1000;
4830  infoMap.insert("avsync", tr("%1 ms").arg(avsync));
4831 
4832  if (m_videoOutput)
4833  {
4834  QString frames = QString("%1/%2").arg(m_videoOutput->ValidVideoFrames())
4835  .arg(m_videoOutput->FreeVideoFrames());
4836  infoMap.insert("videoframes", frames);
4837  }
4838  if (m_decoder)
4839  infoMap["videodecoder"] = m_decoder->GetCodecDecoderName();
4840  if (m_outputJmeter)
4841  {
4842  infoMap["framerate"] = QString("%1%2%3")
4843  .arg(m_outputJmeter->GetLastFPS(), 0, 'f', 2)
4844  .arg(QChar(0xB1, 0))
4845  .arg(m_outputJmeter->GetLastSD(), 0, 'f', 2);
4846  infoMap["load"] = m_outputJmeter->GetLastCPUStats();
4847  }
4848  GetCodecDescription(infoMap);
4849 }
4850 
4851 int64_t MythPlayer::GetSecondsPlayed(bool honorCutList, int divisor)
4852 {
4853  int64_t pos = TranslatePositionFrameToMs(m_framesPlayed, honorCutList);
4854  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
4855  QString("GetSecondsPlayed: framesPlayed %1, honorCutList %2, divisor %3, pos %4")
4856  .arg(m_framesPlayed).arg(honorCutList).arg(divisor).arg(pos));
4857  return TranslatePositionFrameToMs(m_framesPlayed, honorCutList) / divisor;
4858 }
4859 
4860 int64_t MythPlayer::GetTotalSeconds(bool honorCutList, int divisor) const
4861 {
4862  uint64_t pos = m_totalFrames;
4863 
4864  if (IsWatchingInprogress())
4865  pos = UINT64_MAX;
4866 
4867  return TranslatePositionFrameToMs(pos, honorCutList) / divisor;
4868 }
4869 
4870 // Returns the total frame count, as totalFrames for a completed
4871 // recording, or the most recent frame count from the recorder for
4872 // live TV or an in-progress recording.
4874 {
4875  uint64_t result = m_totalFrames;
4876  if (IsWatchingInprogress())
4878  return result;
4879 }
4880 
4881 // Finds the frame number associated with the given time offset. A
4882 // positive offset or +0.0F indicate offset from the beginning. A
4883 // negative offset or -0.0F indicate offset from the end. Limit the
4884 // result to within bounds of the video.
4885 uint64_t MythPlayer::FindFrame(float offset, bool use_cutlist) const
4886 {
4887  bool islivetvcur = (m_liveTV && m_playerCtx->m_tvchain &&
4889  uint64_t length_ms = TranslatePositionFrameToMs(m_totalFrames, use_cutlist);
4890  uint64_t position_ms = 0;
4891 
4892  if (signbit(offset))
4893  {
4894  // Always get an updated totalFrame value for in progress recordings
4895  if (islivetvcur || IsWatchingInprogress())
4896  {
4897  uint64_t framesWritten = m_playerCtx->m_recorder->GetFramesWritten();
4898 
4899  if (m_totalFrames < framesWritten)
4900  {
4901  // Known duration is less than what the backend reported, use new value
4902  length_ms =
4903  TranslatePositionFrameToMs(framesWritten, use_cutlist);
4904  }
4905  }
4906  uint64_t offset_ms = llroundf(-offset * 1000);
4907  position_ms = (offset_ms > length_ms) ? 0 : length_ms - offset_ms;
4908  }
4909  else
4910  {
4911  position_ms = llroundf(offset * 1000);
4912 
4913  if (offset > length_ms)
4914  {
4915  // Make sure we have an updated totalFrames
4916  if ((islivetvcur || IsWatchingInprogress()) &&
4917  (length_ms < offset))
4918  {
4919  long long framesWritten =
4921 
4922  length_ms =
4923  TranslatePositionFrameToMs(framesWritten, use_cutlist);
4924  }
4925  position_ms = min(position_ms, length_ms);
4926  }
4927  }
4928  return TranslatePositionMsToFrame(position_ms, use_cutlist);
4929 }
4930 
4931 void MythPlayer::calcSliderPos(osdInfo &info, bool paddedFields)
4932 {
4933  if (!m_decoder)
4934  return;
4935 
4936  bool islive = false;
4937  info.text.insert("chapteridx", QString());
4938  info.text.insert("totalchapters", QString());
4939  info.text.insert("titleidx", QString());
4940  info.text.insert("totaltitles", QString());
4941  info.text.insert("angleidx", QString());
4942  info.text.insert("totalangles", QString());
4943  info.values.insert("position", 0);
4944  info.values.insert("progbefore", 0);
4945  info.values.insert("progafter", 0);
4946 
4947  int playbackLen = 0;
4948  bool fixed_playbacklen = false;
4949 
4950  if (m_decoder->GetCodecDecoderName() == "nuppel")
4951  {
4952  playbackLen = m_totalLength;
4953  fixed_playbacklen = true;
4954  }
4955 
4956  if (m_liveTV && m_playerCtx->m_tvchain)
4957  {
4958  info.values["progbefore"] = (int)m_playerCtx->m_tvchain->HasPrev();
4959  info.values["progafter"] = (int)m_playerCtx->m_tvchain->HasNext();
4960  playbackLen = m_playerCtx->m_tvchain->GetLengthAtCurPos();
4961  islive = true;
4962  fixed_playbacklen = true;
4963  }
4964  else if (IsWatchingInprogress())
4965  {
4966  islive = true;
4967  }
4968  else
4969  {
4970  int chapter = GetCurrentChapter();
4971  int chapters = GetNumChapters();
4972  if (chapter && chapters > 1)
4973  {
4974  info.text["chapteridx"] = QString::number(chapter + 1);
4975  info.text["totalchapters"] = QString::number(chapters);
4976  }
4977 
4978  int title = GetCurrentTitle();
4979  int titles = GetNumTitles();
4980  if (title && titles > 1)
4981  {
4982  info.text["titleidx"] = QString::number(title + 1);
4983  info.text["totaltitles"] = QString::number(titles);
4984  }
4985 
4986  int angle = GetCurrentAngle();
4987  int angles = GetNumAngles();
4988  if (angle && angles > 1)
4989  {
4990  info.text["angleidx"] = QString::number(angle + 1);
4991  info.text["totalangles"] = QString::number(angles);
4992  }
4993  }
4994 
4995  // Set the raw values, followed by the translated values.
4996  for (int i = 0; i < 2 ; ++i)
4997  {
4998  bool honorCutList = (i > 0);
4999  bool stillFrame = false;
5000  int pos = 0;
5001 
5002  QString relPrefix = (honorCutList ? "rel" : "");
5003  if (!fixed_playbacklen)
5004  playbackLen = GetTotalSeconds(honorCutList);
5005  int secsplayed = GetSecondsPlayed(honorCutList);
5006 
5007  stillFrame = (secsplayed < 0);
5008  playbackLen = max(playbackLen, 0);
5009  secsplayed = min(playbackLen, max(secsplayed, 0));
5010  int secsbehind = max((playbackLen - secsplayed), 0);
5011 
5012  if (playbackLen > 0)
5013  pos = (int)(1000.0F * (secsplayed / (float)playbackLen));
5014 
5015  info.values.insert(relPrefix + "secondsplayed", secsplayed);
5016  info.values.insert(relPrefix + "totalseconds", playbackLen);
5017  info.values[relPrefix + "position"] = pos;
5018 
5019  QString text1;
5020  QString text2;
5021  QString text3;
5022  if (paddedFields)
5023  {
5024  text1 = MythFormatTime(secsplayed, "HH:mm:ss");
5025  text2 = MythFormatTime(playbackLen, "HH:mm:ss");
5026  text3 = MythFormatTime(secsbehind, "HH:mm:ss");
5027  }
5028  else
5029  {
5030  QString fmt = (playbackLen >= ONEHOURINSEC) ? "H:mm:ss" : "m:ss";
5031  text1 = MythFormatTime(secsplayed, fmt);
5032  text2 = MythFormatTime(playbackLen, fmt);
5033 
5034  if (secsbehind >= ONEHOURINSEC)
5035  {
5036  text3 = MythFormatTime(secsbehind, "H:mm:ss");
5037  }
5038  else if (secsbehind >= ONEMININSEC)
5039  {
5040  text3 = MythFormatTime(secsbehind, "m:ss");
5041  }
5042  else
5043  {
5044  text3 = tr("%n second(s)", "", secsbehind);
5045  }
5046  }
5047 
5048  QString desc = stillFrame ? tr("Still Frame") :
5049  tr("%1 of %2").arg(text1).arg(text2);
5050 
5051  info.text[relPrefix + "description"] = desc;
5052  info.text[relPrefix + "playedtime"] = text1;
5053  info.text[relPrefix + "totaltime"] = text2;
5054  info.text[relPrefix + "remainingtime"] = islive ? QString() : text3;
5055  info.text[relPrefix + "behindtime"] = islive ? text3 : QString();
5056  }
5057 }
5058 
5059 // If position == -1, it signifies that we are computing the current
5060 // duration of an in-progress recording. In this case, we fetch the
5061 // current frame rate and frame count from the recorder.
5062 uint64_t MythPlayer::TranslatePositionFrameToMs(uint64_t position,
5063  bool use_cutlist) const
5064 {
5065  float frameRate = GetFrameRate();
5066  if (position == UINT64_MAX &&
5068  {
5069  float recorderFrameRate = m_playerCtx->m_recorder->GetFrameRate();
5070  if (recorderFrameRate > 0)
5071  frameRate = recorderFrameRate;
5072  position = m_playerCtx->m_recorder->GetFramesWritten();
5073  }
5074  return m_deleteMap.TranslatePositionFrameToMs(position, frameRate,
5075  use_cutlist);
5076 }
5077 
5079 {
5080  if (m_decoder)
5081  return m_decoder->GetNumChapters();
5082  return 0;
5083 }
5084 
5086 {
5087  if (m_decoder)
5089  return 0;
5090 }
5091 
5092 void MythPlayer::GetChapterTimes(QList<long long> &times)
5093 {
5094  if (m_decoder)
5095  return m_decoder->GetChapterTimes(times);
5096 }
5097 
5098 bool MythPlayer::DoJumpChapter(int chapter)
5099 {
5100  int64_t desiredFrame = -1;
5101  int total = GetNumChapters();
5102  int current = GetCurrentChapter();
5103 
5104  if (chapter < 0 || chapter > total)
5105  {
5106 
5107  if (chapter < 0)
5108  {
5109  chapter = current -1;
5110  if (chapter < 0) chapter = 0;
5111  }
5112  else if (chapter > total)
5113  {
5114  chapter = current + 1;
5115  if (chapter > total) chapter = total;
5116  }
5117  }
5118 
5119  desiredFrame = GetChapter(chapter);
5120  LOG(VB_PLAYBACK, LOG_INFO, LOC +
5121  QString("DoJumpChapter: current %1 want %2 (frame %3)")
5122  .arg(current).arg(chapter).arg(desiredFrame));
5123 
5124  if (desiredFrame < 0)
5125  {
5126  LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("DoJumpChapter failed."));
5127  m_jumpChapter = 0;
5128  return false;
5129  }
5130 
5131  DoJumpToFrame(desiredFrame, kInaccuracyNone);
5132  m_jumpChapter = 0;
5133  return true;
5134 }
5135 
5136 int64_t MythPlayer::GetChapter(int chapter)
5137 {
5138  if (m_decoder)
5139  return m_decoder->GetChapter(chapter);
5140  return 0;
5141 }
5142 
5144 {
5145 #ifdef USING_MHEG
5147  {
5148  MythMultiLocker locker({&m_osdLock, &m_itvLock});
5149  if (!m_interactiveTV && m_osd)
5150  m_interactiveTV = new InteractiveTV(this);
5151  }
5152 #endif // USING_MHEG
5153  return m_interactiveTV;
5154 }
5155 
5157 {
5158  bool result = false;
5159 
5160 #ifdef USING_MHEG
5161  if (!GetInteractiveTV())
5162  return result;
5163 
5164  QMutexLocker locker(&m_itvLock);
5165  result = m_interactiveTV->OfferKey(action);
5166 #else
5167  Q_UNUSED(action);
5168 #endif // USING_MHEG
5169 
5170  return result;
5171 }
5172 
5176 void MythPlayer::ITVRestart(uint chanid, uint cardid, bool isLiveTV)
5177 {
5178 #ifdef USING_MHEG
5179  if (!GetInteractiveTV())
5180  return;
5181 
5182  QMutexLocker locker(&m_itvLock);
5183  m_interactiveTV->Restart(chanid, cardid, isLiveTV);
5184  m_itvVisible = false;
5185 #else
5186  Q_UNUSED(chanid);
5187  Q_UNUSED(cardid);
5188  Q_UNUSED(isLiveTV);
5189 #endif // USING_MHEG
5190 }
5191 
5192 // Called from the interactiveTV (MHIContext) thread
5193 void MythPlayer::SetVideoResize(const QRect &videoRect)
5194 {
5195  QMutexLocker locker(&m_osdLock);
5196  if (m_videoOutput)
5197  m_videoOutput->SetVideoResize(videoRect);
5198 }
5199 
5203 // Called from the interactiveTV (MHIContext) thread
5205 {
5206  QMutexLocker locker(&m_decoderChangeLock);
5207  if (m_decoder)
5208  return m_decoder->SetAudioByComponentTag(tag);
5209  return false;
5210 }
5211 
5215 // Called from the interactiveTV (MHIContext) thread
5217 {
5218  QMutexLocker locker(&m_decoderChangeLock);
5219  if (m_decoder)
5220  return m_decoder->SetVideoByComponentTag(tag);
5221  return false;
5222 }
5223 
5224 static inline double SafeFPS(DecoderBase *m_decoder)
5225 {
5226  if (!m_decoder)
5227  return 25;
5228  double fps = m_decoder->GetFPS();
5229  return fps > 0 ? fps : 25.0;
5230 }
5231 
5232 // Called from the interactiveTV (MHIContext) thread
5233 bool MythPlayer::SetStream(const QString &stream)
5234 {
5235  // The stream name is empty if the stream is closing
5236  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetStream '%1'").arg(stream));
5237 
5238  QMutexLocker locker(&m_streamLock);
5239  m_newStream = stream;
5240  locker.unlock();
5241  // Stream will be changed by JumpToStream called from EventLoop
5242  // If successful will call m_interactiveTV->StreamStarted();
5243 
5244  if (stream.isEmpty() && m_playerCtx->m_tvchain &&
5246  {
5247  // Restore livetv
5249  m_playerCtx->m_tvchain->JumpToNext(false, 0);
5250  m_playerCtx->m_tvchain->JumpToNext(true, 0);
5251  }
5252 
5253  return !stream.isEmpty();
5254 }
5255 
5256 // Called from EventLoop pn the main application thread
5257 void MythPlayer::JumpToStream(const QString &stream)
5258 {
5259  LOG(VB_PLAYBACK, LOG_INFO, LOC + "JumpToStream - begin");
5260 
5261  if (stream.isEmpty())
5262  return; // Shouldn't happen
5263 
5264  Pause();
5265  ResetCaptions();
5266 
5267  ProgramInfo pginfo(stream);
5268  SetPlayingInfo(pginfo);
5269 
5272  else
5273  m_playerCtx->m_buffer->OpenFile(stream);
5274 
5275  if (!m_playerCtx->m_buffer->IsOpen())
5276  {
5277  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToStream buffer OpenFile failed");
5279  SetErrored(tr("Error opening remote stream buffer"));
5280  return;
5281  }
5282 
5283  m_watchingRecording = false;
5284  m_totalLength = 0;
5285  m_totalFrames = 0;
5286  m_totalDuration = 0;
5287 
5288  if (OpenFile(120) < 0) // 120 retries ~= 60 seconds
5289  {
5290  LOG(VB_GENERAL, LOG_ERR, LOC + "JumpToStream OpenFile failed.");
5292