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