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