/mythtv/libs/libmythtv/mythplayer.cpp
C++ | 5631 lines | 4474 code | 724 blank | 433 comment | 1168 complexity | 7110c5f81af804cb5d2fabab6ecf7a6f MD5 | raw file
Possible License(s): CC-BY-SA-3.0, MIT, GPL-2.0, LGPL-2.0, BSD-3-Clause, GPL-3.0, LGPL-2.1, LGPL-3.0, MPL-2.0-no-copyleft-exception
Large files files are truncated, but you can click here to view the full file
- // -*- Mode: c++ -*-
- #undef HAVE_AV_CONFIG_H
- // C++ headers
- #include <algorithm>
- #include <cassert>
- #include <cmath> // for fabs, ceil, round, signbit
- #include <cstdint>
- #include <cstdio>
- #include <cstdlib>
- #include <unistd.h>
- using namespace std;
- // Qt headers
- #include <QCoreApplication>
- #include <QDir>
- #include <QHash> // for QHash
- #include <QMap> // for QMap<>::iterator, etc
- #include <QThread> // for QThread, etc
- #include <QtCore/qnumeric.h> // for qIsNaN
- #include <utility>
- // MythTV headers
- #include "mthread.h"
- #include "mythconfig.h"
- #include "mythplayer.h"
- #include "DetectLetterbox.h"
- #include "audioplayer.h"
- #include "interactivescreen.h"
- #include "programinfo.h"
- #include "mythcorecontext.h"
- #include "livetvchain.h"
- #include "decoderbase.h"
- #include "nuppeldecoder.h"
- #include "avformatdecoder.h"
- #include "dummydecoder.h"
- #include "tv_play.h"
- #include "interactivetv.h"
- #include "mythsystemevent.h"
- #include "mythlogging.h"
- #include "mythmiscutil.h"
- #include "io/mythinteractivebuffer.h"
- #include "audiooutput.h"
- #include "cardutil.h"
- #include "mythavutil.h"
- #include "jitterometer.h"
- #include "mythtimer.h"
- #include "mythuiactions.h"
- #include "io/mythmediabuffer.h"
- #include "tv_actions.h"
- #include "mythcodeccontext.h"
- // MythUI headers
- #include <mythmainwindow.h>
- extern "C" {
- #include "libavcodec/avcodec.h"
- }
- #include "remoteencoder.h"
- #if ! HAVE_ROUND
- #define round(x) ((int) ((x) + 0.5))
- #endif
- static unsigned dbg_ident(const MythPlayer* /*player*/);
- #define LOC QString("Player(%1): ").arg(dbg_ident(this),0,36)
- #define LOC_DEC QString("Player(%1): ").arg(dbg_ident(m_mp),0,36)
- const int MythPlayer::kNightModeBrightenssAdjustment = 10;
- const int MythPlayer::kNightModeContrastAdjustment = 10;
- // Exact frame seeking, no inaccuracy allowed.
- const double MythPlayer::kInaccuracyNone = 0;
- // By default, when seeking, snap to a keyframe if the keyframe's
- // distance from the target frame is less than 10% of the total seek
- // distance.
- const double MythPlayer::kInaccuracyDefault = 0.1;
- // Allow greater inaccuracy (50%) in the cutlist editor (unless the
- // editor seek distance is set to 1 frame or 1 keyframe).
- const double MythPlayer::kInaccuracyEditor = 0.5;
- // Any negative value means completely inexact, i.e. seek to the
- // keyframe that is closest to the target.
- const double MythPlayer::kInaccuracyFull = -1.0;
- void DecoderThread::run(void)
- {
- RunProlog();
- LOG(VB_PLAYBACK, LOG_INFO, LOC_DEC + "Decoder thread starting.");
- if (m_mp)
- m_mp->DecoderLoop(m_startPaused);
- LOG(VB_PLAYBACK, LOG_INFO, LOC_DEC + "Decoder thread exiting.");
- RunEpilog();
- }
- static int toCaptionType(int type)
- {
- if (kTrackTypeCC608 == type) return kDisplayCC608;
- if (kTrackTypeCC708 == type) return kDisplayCC708;
- if (kTrackTypeSubtitle == type) return kDisplayAVSubtitle;
- if (kTrackTypeTeletextCaptions == type) return kDisplayTeletextCaptions;
- if (kTrackTypeTextSubtitle == type) return kDisplayTextSubtitle;
- if (kTrackTypeRawText == type) return kDisplayRawTextSubtitle;
- return 0;
- }
- static int toTrackType(int type)
- {
- if (kDisplayCC608 == type) return kTrackTypeCC608;
- if (kDisplayCC708 == type) return kTrackTypeCC708;
- if (kDisplayAVSubtitle == type) return kTrackTypeSubtitle;
- if (kDisplayTeletextCaptions == type) return kTrackTypeTeletextCaptions;
- if (kDisplayTextSubtitle == type) return kTrackTypeTextSubtitle;
- if (kDisplayRawTextSubtitle == type) return kTrackTypeRawText;
- return kTrackTypeUnknown;
- }
- MythMultiLocker::MythMultiLocker(std::initializer_list<QMutex*> Locks)
- : m_locks(Locks)
- {
- Relock();
- }
- MythMultiLocker::~MythMultiLocker()
- {
- Unlock();
- }
- void MythMultiLocker::Unlock(void)
- {
- for (QVector<QMutex*>::const_reverse_iterator it = m_locks.crbegin(); it != m_locks.crend(); ++it)
- if (*it)
- (*it)->unlock();
- }
- void MythMultiLocker::Relock(void)
- {
- foreach (auto lock, m_locks)
- if (lock)
- lock->lock();
- }
- MythPlayer::MythPlayer(PlayerFlags flags)
- : m_playerFlags(flags),
- m_display((flags & kVideoIsNull) ? nullptr : MythDisplay::AcquireRelease()),
- // CC608/708
- m_cc608(this), m_cc708(this),
- // Audio
- m_audio(this, (flags & kAudioMuted) != 0),
- // Debugging variables
- m_outputJmeter(new Jitterometer(LOC))
- {
- m_playerThread = QThread::currentThread();
- #ifdef Q_OS_ANDROID
- m_playerThreadId = gettid();
- #endif
- // Playback (output) zoom control
- m_detectLetterBox = new DetectLetterbox(this);
- m_vbiMode = VBIMode::Parse(gCoreContext->GetSetting("VbiFormat"));
- m_captionsEnabledbyDefault = gCoreContext->GetBoolSetting("DefaultCCMode");
- m_itvEnabled = gCoreContext->GetBoolSetting("EnableMHEG", false);
- m_clearSavedPosition = gCoreContext->GetNumSetting("ClearSavedPosition", 1);
- m_endExitPrompt = gCoreContext->GetNumSetting("EndOfRecordingExitPrompt");
- m_pipDefaultLoc = (PIPLocation)gCoreContext->GetNumSetting("PIPLocation", kPIPTopLeft);
- // Get VBI page number
- QString mypage = gCoreContext->GetSetting("VBIpageNr", "888");
- bool valid = false;
- uint tmp = mypage.toInt(&valid, 16);
- m_ttPageNum = (valid) ? tmp : m_ttPageNum;
- m_cc608.SetTTPageNum(m_ttPageNum);
- m_avTimer.start();
- }
- MythPlayer::~MythPlayer(void)
- {
- // NB the interactiveTV thread is a client of OSD so must be deleted
- // before locking and deleting the OSD
- {
- QMutexLocker lk0(&m_itvLock);
- delete m_interactiveTV;
- m_interactiveTV = nullptr;
- }
- MythMultiLocker locker({&m_osdLock, &m_vidExitLock});
- delete m_osd;
- m_osd = nullptr;
- SetDecoder(nullptr);
- delete m_decoderThread;
- m_decoderThread = nullptr;
- delete m_videoOutput;
- m_videoOutput = nullptr;
- delete m_outputJmeter;
- m_outputJmeter = nullptr;
- delete m_detectLetterBox;
- m_detectLetterBox = nullptr;
- if (m_display)
- MythDisplay::AcquireRelease(false);
- }
- void MythPlayer::SetWatchingRecording(bool mode)
- {
- m_watchingRecording = mode;
- if (m_decoder)
- m_decoder->SetWatchingRecording(mode);
- }
- bool MythPlayer::IsWatchingInprogress(void) const
- {
- return m_watchingRecording && m_playerCtx->m_recorder &&
- m_playerCtx->m_recorder->IsValidRecorder();
- }
- void MythPlayer::PauseBuffer(void)
- {
- m_bufferPauseLock.lock();
- if (m_playerCtx->m_buffer)
- {
- m_playerCtx->m_buffer->Pause();
- m_playerCtx->m_buffer->WaitForPause();
- }
- m_bufferPaused = true;
- m_bufferPauseLock.unlock();
- }
- void MythPlayer::UnpauseBuffer(void)
- {
- m_bufferPauseLock.lock();
- if (m_playerCtx->m_buffer)
- m_playerCtx->m_buffer->Unpause();
- m_bufferPaused = false;
- m_bufferPauseLock.unlock();
- }
- bool MythPlayer::Pause(void)
- {
- while (!m_pauseLock.tryLock(100))
- {
- LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waited 100ms to get pause lock.");
- DecoderPauseCheck();
- }
- bool already_paused = m_allPaused;
- if (already_paused)
- {
- m_pauseLock.unlock();
- return already_paused;
- }
- m_nextPlaySpeed = 0.0;
- m_nextNormalSpeed = false;
- PauseVideo();
- m_audio.Pause(true);
- PauseDecoder();
- PauseBuffer();
- if (!m_decoderPaused)
- PauseDecoder(); // Retry in case audio only stream
- m_allPaused = m_decoderPaused && m_videoPaused && m_bufferPaused;
- {
- if (FlagIsSet(kVideoIsNull) && m_decoder)
- m_decoder->UpdateFramesPlayed();
- else if (m_videoOutput && !FlagIsSet(kVideoIsNull))
- m_framesPlayed = m_videoOutput->GetFramesPlayed();
- }
- m_pauseLock.unlock();
- return already_paused;
- }
- bool MythPlayer::Play(float speed, bool normal, bool unpauseaudio)
- {
- m_pauseLock.lock();
- LOG(VB_PLAYBACK, LOG_INFO, LOC +
- QString("Play(%1, normal %2, unpause audio %3)")
- .arg(speed,5,'f',1).arg(normal).arg(unpauseaudio));
- if (m_deleteMap.IsEditing())
- {
- LOG(VB_GENERAL, LOG_ERR, LOC + "Ignoring Play(), in edit mode.");
- m_pauseLock.unlock();
- return false;
- }
- m_rtcBase = 0;
- m_priorAudioTimecode = 0;
- m_priorVideoTimecode = 0;
- m_lastFix = 0.0;
- SetEof(kEofStateNone);
- UnpauseBuffer();
- UnpauseDecoder();
- if (unpauseaudio)
- m_audio.Pause(false);
- UnpauseVideo();
- m_allPaused = false;
- m_nextPlaySpeed = speed;
- m_nextNormalSpeed = normal;
- m_pauseLock.unlock();
- return true;
- }
- void MythPlayer::PauseVideo(void)
- {
- m_videoPauseLock.lock();
- m_needNewPauseFrame = true;
- m_videoPaused = true;
- m_videoPauseLock.unlock();
- }
- void MythPlayer::UnpauseVideo(void)
- {
- m_videoPauseLock.lock();
- m_videoPaused = false;
- m_videoPauseLock.unlock();
- }
- void MythPlayer::SetPlayingInfo(const ProgramInfo &pginfo)
- {
- assert(m_playerCtx);
- if (!m_playerCtx)
- return;
- m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
- m_playerCtx->SetPlayingInfo(&pginfo);
- m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
- }
- void MythPlayer::SetPlaying(bool is_playing)
- {
- QMutexLocker locker(&m_playingLock);
- m_playing = is_playing;
- m_playingWaitCond.wakeAll();
- }
- bool MythPlayer::IsPlaying(uint wait_in_msec, bool wait_for) const
- {
- QMutexLocker locker(&m_playingLock);
- if (!wait_in_msec)
- return m_playing;
- MythTimer t;
- t.start();
- while ((wait_for != m_playing) && ((uint)t.elapsed() < wait_in_msec))
- {
- m_playingWaitCond.wait(
- &m_playingLock, max(0,(int)wait_in_msec - t.elapsed()));
- }
- return m_playing;
- }
- bool MythPlayer::InitVideo(void)
- {
- if (!m_playerCtx)
- return false;
- PIPState pipState = m_playerCtx->GetPIPState();
- if (!m_decoder)
- {
- LOG(VB_GENERAL, LOG_ERR, LOC +
- "Cannot create a video renderer without a decoder.");
- return false;
- }
- m_videoOutput = MythVideoOutput::Create(
- m_decoder->GetCodecDecoderName(),
- m_decoder->GetVideoCodecID(),
- pipState, m_videoDim, m_videoDispDim, m_videoAspect,
- m_parentWidget, m_embedRect,
- m_videoFrameRate, (uint)m_playerFlags, m_codecName, m_maxReferenceFrames);
- if (!m_videoOutput)
- {
- LOG(VB_GENERAL, LOG_ERR, LOC +
- "Couldn't create VideoOutput instance. Exiting..");
- SetErrored(tr("Failed to initialize video output"));
- return false;
- }
- if (m_embedding && pipState == kPIPOff)
- m_videoOutput->EmbedInWidget(m_embedRect);
- return true;
- }
- void MythPlayer::ReinitOSD(void)
- {
- if (m_videoOutput && !FlagIsSet(kVideoIsNull))
- {
- m_osdLock.lock();
- if (!is_current_thread(m_playerThread))
- {
- m_reinitOsd = true;
- m_osdLock.unlock();
- return;
- }
- QRect visible;
- QRect total;
- float aspect = NAN;
- float scaling = NAN;
- m_videoOutput->GetOSDBounds(total, visible, aspect,
- scaling, 1.0F);
- if (m_osd)
- {
- m_osd->SetPainter(m_videoOutput->GetOSDPainter());
- int stretch = lroundf(aspect * 100);
- if ((m_osd->Bounds() != visible) ||
- (m_osd->GetFontStretch() != stretch))
- {
- uint old = m_textDisplayMode;
- ToggleCaptions(old);
- m_osd->Reinit(visible, aspect);
- EnableCaptions(old, false);
- if (m_deleteMap.IsEditing())
- {
- bool const changed = m_deleteMap.IsChanged();
- m_deleteMap.SetChanged(true);
- m_deleteMap.UpdateOSD(m_framesPlayed, m_videoFrameRate, m_osd);
- m_deleteMap.SetChanged(changed);
- }
- }
- }
- #ifdef USING_MHEG
- if (GetInteractiveTV())
- {
- QMutexLocker locker(&m_itvLock);
- m_interactiveTV->Reinit(total, visible, aspect);
- m_itvVisible = false;
- }
- #endif // USING_MHEG
- m_reinitOsd = false;
- m_osdLock.unlock();
- }
- }
- void MythPlayer::ReinitVideo(bool ForceUpdate)
- {
- bool aspect_only = false;
- {
- MythMultiLocker locker({&m_osdLock, &m_vidExitLock});
- m_videoOutput->SetVideoFrameRate(static_cast<float>(m_videoFrameRate));
- float aspect = (m_forcedVideoAspect > 0) ? m_forcedVideoAspect : m_videoAspect;
- if (!m_videoOutput->InputChanged(m_videoDim, m_videoDispDim, aspect,
- m_decoder->GetVideoCodecID(), aspect_only, &locker,
- m_maxReferenceFrames, ForceUpdate))
- {
- LOG(VB_GENERAL, LOG_ERR, LOC +
- "Failed to Reinitialize Video. Exiting..");
- SetErrored(tr("Failed to reinitialize video output"));
- return;
- }
- if (m_osd)
- m_osd->SetPainter(m_videoOutput->GetOSDPainter());
- ReinitOSD();
- }
- if (!aspect_only)
- ClearAfterSeek();
- if (m_textDisplayMode)
- EnableSubtitles(true);
- AutoVisualise();
- }
- static inline QString toQString(FrameScanType scan) {
- switch (scan) {
- case kScan_Ignore: return QString("Ignore Scan");
- case kScan_Detect: return QString("Detect Scan");
- case kScan_Interlaced: return QString("Interlaced Scan");
- case kScan_Progressive: return QString("Progressive Scan");
- default: return QString("Unknown Scan");
- }
- }
- FrameScanType MythPlayer::detectInterlace(FrameScanType newScan,
- FrameScanType scan,
- float fps, int video_height) const
- {
- QString dbg = QString("detectInterlace(") + toQString(newScan) +
- QString(", ") + toQString(scan) + QString(", ") +
- QString("%1").arg(static_cast<double>(fps)) + QString(", ") +
- QString("%1").arg(video_height) + QString(") ->");
- if (kScan_Ignore != newScan || kScan_Detect == scan)
- {
- // The scanning mode should be decoded from the stream, but if it
- // isn't, we have to guess.
- scan = kScan_Interlaced; // default to interlaced
- if ((720 == video_height) || // ATSC 720p
- (fps > 45)) // software deinterlacing
- scan = kScan_Progressive;
- if (kScan_Detect != newScan)
- scan = newScan;
- };
- LOG(VB_PLAYBACK, LOG_INFO, LOC + dbg +toQString(scan));
- return scan;
- }
- void MythPlayer::SetKeyframeDistance(int keyframedistance)
- {
- m_keyframeDist = (keyframedistance > 0) ? static_cast<uint>(keyframedistance) : m_keyframeDist;
- }
- /*! \brief Check whether deinterlacing should be enabled
- *
- * If the user has triggered an override, this will always be used (until 'detect'
- * is requested to turn it off again).
- *
- * For H264 material, the decoder will signal when the current frame is on a new
- * GOP boundary and if the frame's interlaced flag does not match the current
- * scan type, the scan type is unlocked. This works well for all test clips
- * with mixed progressive/interlaced sequences.
- *
- * For all other material, we lock the scan type to interlaced when interlaced
- * frames are seen - and do not unlock if we see progressive frames. This is
- * primarily targetted at MPEG2 material where there is a lot of content where
- * the scan type changes frequently - and for no obvious reason. This will result
- * in 'false positives' in some cases but there is no clear approach that works
- * for all cases. The previous behaviour is preserved (i.e. lock to interlaced
- * if interlaced frames are seen) which results in less erratic playback (as the
- * deinterlacers are not continually switched on and off) and correctly deinterlaces
- * material that is not otherwise flagged correctly.
- */
- void MythPlayer::AutoDeint(VideoFrame *frame, bool allow_lock)
- {
- if (!frame)
- return;
- if ((m_scanOverride > kScan_Detect) && (m_scan != m_scanOverride))
- {
- LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Locking scan override to '%1'")
- .arg(ScanTypeToString(m_scanOverride, true)));
- SetScanType(m_scanOverride);
- }
- // This is currently only signalled for H264 content
- if (frame->new_gop)
- {
- if (m_scanOverride < kScan_Interlaced &&
- ((frame->interlaced_frame && !is_interlaced(m_scan)) ||
- (!frame->interlaced_frame && is_interlaced(m_scan))))
- {
- LOG(VB_PLAYBACK, LOG_INFO, LOC + "Unlocking frame scan");
- m_scanLocked = false;
- }
- }
- if (m_scanLocked)
- return;
- if (frame->interlaced_frame)
- {
- if (m_scanTracker < 0)
- {
- LOG(VB_PLAYBACK, LOG_INFO, LOC +
- QString("Interlaced frame seen after %1 progressive frames")
- .arg(abs(m_scanTracker)));
- m_scanTracker = 2;
- if (allow_lock)
- {
- LOG(VB_PLAYBACK, LOG_INFO, LOC + "Locking scan to Interlaced.");
- SetScanType(kScan_Interlaced);
- return;
- }
- }
- m_scanTracker++;
- }
- else
- {
- if (m_scanTracker > 0)
- {
- LOG(VB_PLAYBACK, LOG_INFO, LOC +
- QString("Progressive frame seen after %1 interlaced frames")
- .arg(m_scanTracker));
- m_scanTracker = 0;
- }
- m_scanTracker--;
- }
- int min_count = !allow_lock ? 0 : 2;
- if (abs(m_scanTracker) <= min_count)
- return;
- SetScanType((m_scanTracker > min_count) ? kScan_Interlaced : kScan_Progressive);
- m_scanLocked = false;
- }
- FrameScanType MythPlayer::NextScanOverride(void)
- {
- int next = m_scanOverride + 1;
- if (next > kScan_Progressive)
- next = kScan_Detect;
- return static_cast<FrameScanType>(next);
- }
- void MythPlayer::SetScanOverride(FrameScanType Scan)
- {
- if (m_scanOverride == Scan)
- return;
- m_scanOverride = Scan;
- if (m_scanOverride == kScan_Detect)
- {
- m_scanLocked = false;
- m_scanInitialized = false;
- LOG(VB_PLAYBACK, LOG_INFO, LOC + "Reverting to auto detection of scan");
- }
- }
- FrameScanType MythPlayer::GetScanType(void) const
- {
- if (m_scanOverride > kScan_Detect)
- return m_scanOverride;
- return m_scan;
- }
- void MythPlayer::SetScanType(FrameScanType Scan)
- {
- if (!is_current_thread(m_playerThread))
- {
- m_resetScan = Scan;
- return;
- }
- if (!m_videoOutput)
- return;
- m_resetScan = kScan_Ignore;
- if (m_scanInitialized && m_scan == Scan && m_frameIntervalPrev == m_frameInterval)
- return;
- m_scanLocked = (Scan != kScan_Detect);
- m_scanInitialized = true;
- m_frameIntervalPrev = m_frameInterval;
- if (is_interlaced(Scan))
- {
- MythDeintType forced = m_playerCtx->IsPiPOrSecondaryPBP() ? (DEINT_CPU | DEINT_MEDIUM) : DEINT_NONE;
- bool normal = m_playSpeed > 0.99F && m_playSpeed < 1.01F && m_normalSpeed;
- m_doubleFramerate = CanSupportDoubleRate() && normal && !forced;
- m_videoOutput->SetDeinterlacing(true, m_doubleFramerate, forced);
- }
- else if (kScan_Progressive == Scan)
- {
- m_doubleFramerate = false;
- m_videoOutput->SetDeinterlacing(false, false);
- }
- m_scan = Scan;
- }
- void MythPlayer::SetVideoParams(int width, int height, double fps,
- float aspect, bool ForceUpdate,
- int ReferenceFrames, FrameScanType scan, const QString& codecName)
- {
- bool paramsChanged = ForceUpdate;
- if (width >= 0 && height >= 0)
- {
- paramsChanged = true;
- m_videoDim = m_videoDispDim = QSize(width, height);
- m_videoAspect = aspect > 0.0F ? aspect : static_cast<float>(width) / height;
- }
- if (!qIsNaN(fps) && fps > 0.0 && fps < 121.0)
- {
- paramsChanged = true;
- m_videoFrameRate = fps;
- if (m_ffrewSkip != 0 && m_ffrewSkip != 1)
- {
- UpdateFFRewSkip();
- }
- else
- {
- float temp_speed = (m_playSpeed == 0.0F) ?
- m_audio.GetStretchFactor() : m_playSpeed;
- SetFrameInterval(kScan_Progressive,
- 1.0 / (m_videoFrameRate * static_cast<double>(temp_speed)));
- }
- }
- if (!codecName.isEmpty())
- {
- m_codecName = codecName;
- paramsChanged = true;
- }
- if (ReferenceFrames > 0)
- {
- m_maxReferenceFrames = ReferenceFrames;
- paramsChanged = true;
- }
- if (!paramsChanged)
- return;
- if (m_videoOutput)
- ReinitVideo(ForceUpdate);
- if (IsErrored())
- return;
- // ensure deinterlacers are correctly reset after a change
- m_scanInitialized = false;
- SetScanType(detectInterlace(scan, m_scan, static_cast<float>(m_videoFrameRate),
- m_videoDispDim.height()));
- m_scanLocked = false;
- m_scanTracker = (m_scan == kScan_Interlaced) ? 2 : 0;
- }
- void MythPlayer::SetFrameRate(double fps)
- {
- m_videoFrameRate = fps;
- float temp_speed = (m_playSpeed == 0.0F) ?
- m_audio.GetStretchFactor() : m_playSpeed;
- SetFrameInterval(kScan_Progressive,
- 1.0 / (m_videoFrameRate * static_cast<double>(temp_speed)));
- }
- void MythPlayer::SetFileLength(int total, int frames)
- {
- m_totalLength = total;
- m_totalFrames = frames;
- }
- void MythPlayer::SetDuration(int duration)
- {
- m_totalDuration = duration;
- }
- void MythPlayer::OpenDummy(void)
- {
- m_isDummy = true;
- if (!m_videoOutput)
- {
- SetKeyframeDistance(15);
- SetVideoParams(720, 576, 25.00, 1.25F, false, 2);
- }
- m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
- auto *dec = new DummyDecoder(this, *(m_playerCtx->m_playingInfo));
- m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
- SetDecoder(dec);
- }
- void MythPlayer::CreateDecoder(char *TestBuffer, int TestSize)
- {
- if (AvFormatDecoder::CanHandle(TestBuffer, m_playerCtx->m_buffer->GetFilename(), TestSize))
- {
- SetDecoder(new AvFormatDecoder(this, *m_playerCtx->m_playingInfo, m_playerFlags));
- return;
- }
- if (NuppelDecoder::CanHandle(TestBuffer, TestSize))
- SetDecoder(new NuppelDecoder(this, *m_playerCtx->m_playingInfo));
- }
- int MythPlayer::OpenFile(int Retries)
- {
- // Sanity check
- if (!m_playerCtx || (m_playerCtx && !m_playerCtx->m_buffer))
- return -1;
- LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opening '%1'")
- .arg(m_playerCtx->m_buffer->GetSafeFilename()));
- // Disable hardware acceleration for second PBP
- if (m_playerCtx && (m_playerCtx->IsPBP() && !m_playerCtx->IsPrimaryPBP()) &&
- FlagIsSet(kDecodeAllowGPU))
- {
- m_playerFlags = static_cast<PlayerFlags>(m_playerFlags - kDecodeAllowGPU);
- }
- m_isDummy = false;
- m_liveTV = m_playerCtx->m_tvchain && m_playerCtx->m_buffer->LiveMode();
- // Dummy setup for livetv transtions. Can we get rid of this?
- if (m_playerCtx->m_tvchain)
- {
- int currentposition = m_playerCtx->m_tvchain->GetCurPos();
- if (m_playerCtx->m_tvchain->GetInputType(currentposition) == "DUMMY")
- {
- OpenDummy();
- return 0;
- }
- }
- // Start the RingBuffer read ahead thread
- m_playerCtx->m_buffer->Start();
- /// OSX has a small stack, so we put this buffer on the heap instead.
- char *testbuf = new char[kDecoderProbeBufferSize];
- UnpauseBuffer();
- // delete any pre-existing recorder
- SetDecoder(nullptr);
- int testreadsize = 2048;
- // Test the incoming buffer and create a suitable decoder
- MythTimer bigTimer;
- bigTimer.start();
- int timeout = max((Retries + 1) * 500, 30000);
- while (testreadsize <= kDecoderProbeBufferSize)
- {
- MythTimer peekTimer;
- peekTimer.start();
- while (m_playerCtx->m_buffer->Peek(testbuf, testreadsize) != testreadsize)
- {
- // NB need to allow for streams encountering network congestion
- if (peekTimer.elapsed() > 30000 || bigTimer.elapsed() > timeout
- || m_playerCtx->m_buffer->GetStopReads())
- {
- LOG(VB_GENERAL, LOG_ERR, LOC +
- QString("OpenFile(): Could not read first %1 bytes of '%2'")
- .arg(testreadsize)
- .arg(m_playerCtx->m_buffer->GetFilename()));
- delete[] testbuf;
- SetErrored(tr("Could not read first %1 bytes").arg(testreadsize));
- return -1;
- }
- LOG(VB_GENERAL, LOG_WARNING, LOC + "OpenFile() waiting on data");
- usleep(50 * 1000);
- }
- m_playerCtx->LockPlayingInfo(__FILE__, __LINE__);
- CreateDecoder(testbuf, testreadsize);
- m_playerCtx->UnlockPlayingInfo(__FILE__, __LINE__);
- if (m_decoder || (bigTimer.elapsed() > timeout))
- break;
- testreadsize <<= 1;
- }
- // Fail
- if (!m_decoder)
- {
- LOG(VB_GENERAL, LOG_ERR, LOC +
- QString("Couldn't find an A/V decoder for: '%1'")
- .arg(m_playerCtx->m_buffer->GetFilename()));
- SetErrored(tr("Could not find an A/V decoder"));
- delete[] testbuf;
- return -1;
- }
- if (m_decoder->IsErrored())
- {
- LOG(VB_GENERAL, LOG_ERR, LOC + "Could not initialize A/V decoder.");
- SetDecoder(nullptr);
- SetErrored(tr("Could not initialize A/V decoder"));
- delete[] testbuf;
- return -1;
- }
- // Pre-init the decoder
- m_decoder->SetSeekSnap(0);
- m_decoder->SetLiveTVMode(m_liveTV);
- m_decoder->SetWatchingRecording(m_watchingRecording);
- m_decoder->SetTranscoding(m_transcoding);
- // Open the decoder
- int result = m_decoder->OpenFile(m_playerCtx->m_buffer, false, testbuf, testreadsize);
- delete[] testbuf;
- if (result < 0)
- {
- LOG(VB_GENERAL, LOG_ERR, QString("Couldn't open decoder for: %1")
- .arg(m_playerCtx->m_buffer->GetFilename()));
- SetErrored(tr("Could not open decoder"));
- return -1;
- }
- // Disable audio if necessary
- m_audio.CheckFormat();
- // Livetv, recording or in-progress
- if (result > 0)
- {
- m_hasFullPositionMap = true;
- m_deleteMap.LoadMap();
- m_deleteMap.TrackerReset(0);
- }
- // Determine the initial bookmark and update it for the cutlist
- m_bookmarkSeek = GetBookmark();
- m_deleteMap.TrackerReset(m_bookmarkSeek);
- m_deleteMap.TrackerWantsToJump(m_bookmarkSeek, m_bookmarkSeek);
- if (!gCoreContext->IsDatabaseIgnored() &&
- m_playerCtx->m_playingInfo->QueryAutoExpire() == kLiveTVAutoExpire)
- {
- gCoreContext->SaveSetting("DefaultChanid",
- static_cast<int>(m_playerCtx->m_playingInfo->GetChanID()));
- QString callsign = m_playerCtx->m_playingInfo->GetChannelSchedulingID();
- QString channum = m_playerCtx->m_playingInfo->GetChanNum();
- gCoreContext->SaveSetting("DefaultChanKeys", callsign + "[]:[]" + channum);
- if (m_playerCtx->m_recorder && m_playerCtx->m_recorder->IsValidRecorder())
- {
- uint cardid = static_cast<uint>(m_playerCtx->m_recorder->GetRecorderNumber());
- CardUtil::SetStartChannel(cardid, channum);
- }
- }
- return IsErrored() ? -1 : 0;
- }
- void MythPlayer::SetFramesPlayed(uint64_t played)
- {
- m_framesPlayed = played;
- if (m_videoOutput)
- m_videoOutput->SetFramesPlayed(played);
- }
- /** \fn MythPlayer::GetFreeVideoFrames(void)
- * \brief Returns the number of frames available for decoding onto.
- */
- int MythPlayer::GetFreeVideoFrames(void) const
- {
- if (m_videoOutput)
- return m_videoOutput->FreeVideoFrames();
- return 0;
- }
- /// \brief Return a list of frame types that can be rendered directly.
- VideoFrameType* MythPlayer::DirectRenderFormats(void)
- {
- static VideoFrameType s_defaultFormats[] = { FMT_YV12, FMT_NONE };
- if (m_videoOutput)
- return m_videoOutput->DirectRenderFormats();
- return &s_defaultFormats[0];
- }
- /**
- * \brief Removes a frame from the available queue for decoding onto.
- *
- * This places the frame in the limbo queue, from which frames are
- * removed if they are added to another queue. Normally a frame is
- * freed from limbo either by a ReleaseNextVideoFrame() or
- * DiscardVideoFrame() call; but limboed frames are also freed
- * during a seek reset.
- */
- VideoFrame *MythPlayer::GetNextVideoFrame(void)
- {
- if (m_videoOutput)
- return m_videoOutput->GetNextFreeFrame();
- return nullptr;
- }
- /** \fn MythPlayer::ReleaseNextVideoFrame(VideoFrame*, int64_t)
- * \brief Places frame on the queue of frames ready for display.
- */
- void MythPlayer::ReleaseNextVideoFrame(VideoFrame *buffer,
- int64_t timecode,
- bool wrap)
- {
- if (wrap)
- WrapTimecode(timecode, TC_VIDEO);
- buffer->timecode = timecode;
- m_latestVideoTimecode = timecode;
- if (m_videoOutput)
- m_videoOutput->ReleaseFrame(buffer);
- m_detectLetterBox->Detect(buffer);
- if (m_allPaused)
- CheckAspectRatio(buffer);
- }
- /** \fn MythPlayer::DiscardVideoFrame(VideoFrame*)
- * \brief Places frame in the available frames queue.
- */
- void MythPlayer::DiscardVideoFrame(VideoFrame *buffer)
- {
- if (m_videoOutput)
- m_videoOutput->DiscardFrame(buffer);
- }
- /** \fn MythPlayer::DiscardVideoFrames(bool)
- * \brief Places frames in the available frames queue.
- *
- * If called with 'Keyframe' set to false then all frames
- * not in use by the decoder are made available for decoding. Otherwise,
- * all frames are made available for decoding; this is only safe if
- * the next frame is a keyframe.
- *
- * \param Keyframe if this is true all frames are placed
- * in the available queue.
- * \param Flushed indicates that the decoder has been flushed AND the decoder
- * requires ALL frames to be released (used for VAAPI and VDPAU pause frames)
- */
- void MythPlayer::DiscardVideoFrames(bool KeyFrame, bool Flushed)
- {
- if (m_videoOutput)
- m_videoOutput->DiscardFrames(KeyFrame, Flushed);
- }
- bool MythPlayer::HasReachedEof(void) const
- {
- EofState eof = GetEof();
- if (eof != kEofStateNone && !m_allPaused)
- return true;
- if (GetEditMode())
- return false;
- if (m_liveTV)
- return false;
- if (!m_deleteMap.IsEmpty() && m_framesPlayed >= m_deleteMap.GetLastFrame())
- return true;
- return false;
- }
- VideoFrame *MythPlayer::GetCurrentFrame(int &w, int &h)
- {
- w = m_videoDim.width();
- h = m_videoDim.height();
- VideoFrame *retval = nullptr;
- m_vidExitLock.lock();
- if (m_videoOutput)
- retval = m_videoOutput->GetLastShownFrame();
- if (!retval)
- m_vidExitLock.unlock();
- return retval;
- }
- void MythPlayer::DeLimboFrame(VideoFrame *frame)
- {
- if (m_videoOutput)
- m_videoOutput->DeLimboFrame(frame);
- }
- void MythPlayer::ReleaseCurrentFrame(VideoFrame *frame)
- {
- if (frame)
- m_vidExitLock.unlock();
- }
- void MythPlayer::EmbedInWidget(QRect rect)
- {
- if (m_videoOutput)
- m_videoOutput->EmbedInWidget(rect);
- else
- {
- m_embedRect = rect;
- m_embedding = true;
- }
- }
- void MythPlayer::StopEmbedding(void)
- {
- if (m_videoOutput)
- {
- m_videoOutput->StopEmbedding();
- ReinitOSD();
- }
- else
- {
- m_embedRect = QRect();
- m_embedding = false;
- }
- }
- void MythPlayer::WindowResized(const QSize &new_size)
- {
- if (m_videoOutput)
- m_videoOutput->WindowResized(new_size);
- ReinitOSD();
- }
- void MythPlayer::EnableTeletext(int page)
- {
- QMutexLocker locker(&m_osdLock);
- if (!m_osd)
- return;
- m_osd->EnableTeletext(true, page);
- m_prevTextDisplayMode = m_textDisplayMode;
- m_textDisplayMode = kDisplayTeletextMenu;
- }
- void MythPlayer::DisableTeletext(void)
- {
- QMutexLocker locker(&m_osdLock);
- if (!m_osd)
- return;
- m_osd->EnableTeletext(false, 0);
- m_textDisplayMode = kDisplayNone;
- /* If subtitles are enabled before the teletext menu was displayed,
- re-enabled them. */
- if (m_prevTextDisplayMode & kDisplayAllCaptions)
- EnableCaptions(m_prevTextDisplayMode, false);
- }
- void MythPlayer::ResetTeletext(void)
- {
- QMutexLocker locker(&m_osdLock);
- if (!m_osd)
- return;
- m_osd->TeletextReset();
- }
- /** \fn MythPlayer::SetTeletextPage(uint)
- * \brief Set Teletext NUV Caption page
- */
- void MythPlayer::SetTeletextPage(uint page)
- {
- m_osdLock.lock();
- DisableCaptions(m_textDisplayMode);
- m_ttPageNum = page;
- m_cc608.SetTTPageNum(m_ttPageNum);
- m_textDisplayMode &= ~kDisplayAllCaptions;
- m_textDisplayMode |= kDisplayNUVTeletextCaptions;
- m_osdLock.unlock();
- }
- bool MythPlayer::HandleTeletextAction(const QString &action)
- {
- if (!(m_textDisplayMode & kDisplayTeletextMenu) || !m_osd)
- return false;
- bool handled = true;
- m_osdLock.lock();
- if (action == "MENU" || action == ACTION_TOGGLETT || action == "ESCAPE")
- DisableTeletext();
- else if (m_osd)
- handled = m_osd->TeletextAction(action);
- m_osdLock.unlock();
- return handled;
- }
- void MythPlayer::ResetCaptions(void)
- {
- QMutexLocker locker(&m_osdLock);
- if (!m_osd)
- return;
- if (((m_textDisplayMode & kDisplayAVSubtitle) ||
- (m_textDisplayMode & kDisplayTextSubtitle) ||
- (m_textDisplayMode & kDisplayRawTextSubtitle) ||
- (m_textDisplayMode & kDisplayDVDButton) ||
- (m_textDisplayMode & kDisplayCC608) ||
- (m_textDisplayMode & kDisplayCC708)))
- {
- m_osd->ClearSubtitles();
- }
- else if ((m_textDisplayMode & kDisplayTeletextCaptions) ||
- (m_textDisplayMode & kDisplayNUVTeletextCaptions))
- {
- m_osd->TeletextClear();
- }
- }
- void MythPlayer::DisableCaptions(uint mode, bool osd_msg)
- {
- if (m_textDisplayMode)
- m_prevNonzeroTextDisplayMode = m_textDisplayMode;
- m_textDisplayMode &= ~mode;
- ResetCaptions();
- QMutexLocker locker(&m_osdLock);
- bool newTextDesired = (m_textDisplayMode & kDisplayAllTextCaptions) != 0U;
- // Only turn off textDesired if the Operator requested it.
- if (osd_msg || newTextDesired)
- m_textDesired = newTextDesired;
- QString msg = "";
- if (kDisplayNUVTeletextCaptions & mode)
- msg += tr("TXT CAP");
- if (kDisplayTeletextCaptions & mode)
- {
- if (m_decoder != nullptr)
- msg += m_decoder->GetTrackDesc(kTrackTypeTeletextCaptions,
- GetTrack(kTrackTypeTeletextCaptions));
- DisableTeletext();
- }
- int preserve = m_textDisplayMode & (kDisplayCC608 | kDisplayTextSubtitle |
- kDisplayAVSubtitle | kDisplayCC708 |
- kDisplayRawTextSubtitle);
- if ((kDisplayCC608 & mode) || (kDisplayCC708 & mode) ||
- (kDisplayAVSubtitle & mode) || (kDisplayRawTextSubtitle & mode))
- {
- int type = toTrackType(mode);
- if (m_decoder != nullptr)
- msg += m_decoder->GetTrackDesc(type, GetTrack(type));
- if (m_osd)
- m_osd->EnableSubtitles(preserve);
- }
- if (kDisplayTextSubtitle & mode)
- {
- msg += tr("Text subtitles");
- if (m_osd)
- m_osd->EnableSubtitles(preserve);
- }
- if (!msg.isEmpty() && osd_msg)
- {
- msg += " " + tr("Off");
- SetOSDMessage(msg, kOSDTimeout_Med);
- }
- }
- void MythPlayer::EnableCaptions(uint mode, bool osd_msg)
- {
- QMutexLocker locker(&m_osdLock);
- bool newTextDesired = (mode & kDisplayAllTextCaptions) != 0U;
- // Only turn off textDesired if the Operator requested it.
- if (osd_msg || newTextDesired)
- m_textDesired = newTextDesired;
- QString msg = "";
- if ((kDisplayCC608 & mode) || (kDisplayCC708 & mode) ||
- (kDisplayAVSubtitle & mode) || kDisplayRawTextSubtitle & mode)
- {
- int type = toTrackType(mode);
- if (m_decoder != nullptr)
- msg += m_decoder->GetTrackDesc(type, GetTrack(type));
- if (m_osd)
- m_osd->EnableSubtitles(mode);
- }
- if (kDisplayTextSubtitle & mode)
- {
- if (m_osd)
- m_osd->EnableSubtitles(kDisplayTextSubtitle);
- msg += tr("Text subtitles");
- }
- if (kDisplayNUVTeletextCaptions & mode)
- msg += tr("TXT %1").arg(m_ttPageNum, 3, 16);
- if ((kDisplayTeletextCaptions & mode) && (m_decoder != nullptr))
- {
- msg += m_decoder->GetTrackDesc(kTrackTypeTeletextCaptions,
- GetTrack(kTrackTypeTeletextCaptions));
- int page = m_decoder->GetTrackLanguageIndex(
- kTrackTypeTeletextCaptions,
- GetTrack(kTrackTypeTeletextCaptions));
- EnableTeletext(page);
- m_textDisplayMode = kDisplayTeletextCaptions;
- }
- msg += " " + tr("On");
- LOG(VB_PLAYBACK, LOG_INFO, QString("EnableCaptions(%1) msg: %2")
- .arg(mode).arg(msg));
- m_textDisplayMode = mode;
- if (m_textDisplayMode)
- m_prevNonzeroTextDisplayMode = m_textDisplayMode;
- if (osd_msg)
- SetOSDMessage(msg, kOSDTimeout_Med);
- }
- bool MythPlayer::ToggleCaptions(void)
- {
- SetCaptionsEnabled(!((bool)m_textDisplayMode));
- return m_textDisplayMode != 0U;
- }
- bool MythPlayer::ToggleCaptions(uint type)
- {
- QMutexLocker locker(&m_osdLock);
- uint mode = toCaptionType(type);
- uint origMode = m_textDisplayMode;
- if (m_textDisplayMode)
- DisableCaptions(m_textDisplayMode, (origMode & mode) != 0U);
- if (origMode & mode)
- return m_textDisplayMode != 0U;
- if (mode)
- EnableCaptions(mode);
- return m_textDisplayMode != 0U;
- }
- void MythPlayer::SetCaptionsEnabled(bool enable, bool osd_msg)
- {
- QMutexLocker locker(&m_osdLock);
- m_enableCaptions = m_disableCaptions = false;
- uint origMode = m_textDisplayMode;
- // Only turn off textDesired if the Operator requested it.
- if (osd_msg || enable)
- m_textDesired = enable;
- if (!enable)
- {
- DisableCaptions(origMode, osd_msg);
- return;
- }
- int mode = HasCaptionTrack(m_prevNonzeroTextDisplayMode) ?
- m_prevNonzeroTextDisplayMode : NextCaptionTrack(kDisplayNone);
- if (origMode != (uint)mode)
- {
- DisableCaptions(origMode, false);
- if (kDisplayNone == mode)
- {
- if (osd_msg)
- {
- SetOSDMessage(tr("No captions",
- "CC/Teletext/Subtitle text not available"),
- kOSDTimeout_Med);
- }
- LOG(VB_PLAYBACK, LOG_INFO,
- "No captions available yet to enable.");
- }
- else if (mode)
- {
- EnableCaptions(mode, osd_msg);
- }
- }
- ResetCaptions();
- }
- bool MythPlayer::GetCaptionsEnabled(void) const
- {
- return (kDisplayNUVTeletextCaptions == m_textDisplayMode) ||
- (kDisplayTeletextCaptions == m_textDisplayMode) ||
- (kDisplayAVSubtitle == m_textDisplayMode) ||
- (kDisplayCC608 == m_textDisplayMode) ||
- (kDisplayCC708 == m_textDisplayMode) ||
- (kDisplayTextSubtitle == m_textDisplayMode) ||
- (kDisplayRawTextSubtitle == m_textDisplayMode) ||
- (kDisplayTeletextMenu == m_textDisplayMode);
- }
- QStringList MythPlayer::GetTracks(uint type)
- {
- if (m_decoder)
- return m_decoder->GetTracks(type);
- return QStringList();
- }
- uint MythPlayer::GetTrackCount(uint type)
- {
- if (m_decoder)
- return m_decoder->GetTrackCount(type);
- return 0;
- }
- int MythPlayer::SetTrack(uint type, int trackNo)
- {
- int ret = -1;
- if (!m_decoder)
- return ret;
- ret = m_decoder->SetTrack(type, trackNo);
- if (kTrackTypeAudio == type)
- {
- QString msg = "";
- if (m_decoder)
- SetOSDMessage(m_decoder->GetTrackDesc(type, GetTrack(type)),
- kOSDTimeout_Med);
- return ret;
- }
- uint subtype = toCaptionType(type);
- if (subtype)
- {
- DisableCaptions(m_textDisplayMode, false);
- EnableCaptions(subtype, true);
- if ((kDisplayCC708 == subtype || kDisplayCC608 == subtype) && m_decoder)
- {
- int sid = m_decoder->GetTrackInfo(type, trackNo).m_stream_id;
- if (sid >= 0)
- {
- (kDisplayCC708 == subtype) ? m_cc708.SetCurrentService(sid) :
- m_cc608.SetMode(sid);
- }
- }
- }
- return ret;
- }
- /** \fn MythPlayer::TracksChanged(uint)
- * \brief This tries to re-enable captions/subtitles if the user
- * wants them and one of the captions/subtitles tracks has
- * changed.
- */
- void MythPlayer::TracksChanged(uint trackType)
- {
- if (trackType >= kTrackTypeSubtitle &&
- trackType <= kTrackTypeTeletextCaptions && m_textDesired)
- {
- m_enableCaptions = true;
- }
- }
- void MythPlayer::EnableSubtitles(bool enable)
- {
- if (enable)
- m_enableCaptions = true;
- else
- m_disableCaptions = true;
- }
- void MythPlayer::EnableForcedSubtitles(bool enable)
- {
- if (enable)
- m_enableForcedSubtitles = true;
- else
- m_disableForcedSubtitles = true;
- }
- void MythPlayer::SetAllowForcedSubtitles(bool allow)
- {
- m_allowForcedSubtitles = allow;
- SetOSDMessage(m_allowForcedSubtitles ?
- tr("Forced Subtitles On") :
- tr("Forced Subtitles Off"),
- kOSDTimeout_Med);
- }
- void MythPlayer::DoDisableForcedSubtitles(void)
- {
- m_disableForcedSubtitles = false;
- m_osdLock.lock();
- if (m_osd)
- m_osd->DisableForcedSubtitles();
- m_osdLock.unlock();
- }
- void MythPlayer::DoEnableForcedSubtitles(void)
- {
- m_enableForcedSubtitles = false;
- if (!m_allowForcedSubtitles)
- return;
- m_osdLock.lock();
- if (m_osd)
- m_osd->EnableSubtitles(kDisplayAVSubtitle, true /*forced only*/);
- m_osdLock.unlock();
- }
- int MythPlayer::GetTrack(uint type)
- {
- if (m_decoder)
- return m_decoder->GetTrack(type);
- return -1;
- }
- int MythPlayer::ChangeTrack(uint type, int dir)
- {
- if (!m_decoder)
- return -1;
- int retval = m_decoder->ChangeTrack(type, dir);
- if (retval >= 0)
- {
- SetOSDMessage(m_decoder->GetTrackDesc(type, GetTrack(type)),
- kOSDTimeout_Med);
- return retval;
- }
- return -1;
- }
- void MythPlayer::ChangeCaptionTrack(int dir)
- {
- if (!m_decoder || (dir < 0))
- return;
- if (!((m_textDisplayMode == kDisplayTextSubtitle) ||
- (m_textDisplayMode == kDisplayNUVTeletextCaptions) ||
- (m_textDisplayMode == kDisplayNone)))
- {
- int tracktype = toTrackType(m_textDisplayMode);
- if (GetTrack(tracktype) < m_decoder->NextTrack(tracktype))
- {
- SetTrack(tracktype, m_decoder->NextTrack(tracktype));
- return;
- }
- }
- int nextmode = NextCaptionTrack(m_textDisplayMode);
- if ((nextmode == kDisplayTextSubtitle) ||
- (nextmode == kDisplayNUVTeletextCaptions) ||
- (nextmode == kDisplayNone))
- {
- DisableCaptions(m_textDisplayMode, true);
- if (nextmode != kDisplayNone)
- EnableCaptions(nextmode, true);
- }
- else
- {
- int tracktype = toTrackType(nextmode);
- int tracks = m_decoder->GetTrackCount(tracktype);
- if (tracks)
- {
- DisableCaptions(m_textDisplayMode, true);
- SetTrack(tracktype, 0);
- }
- }
- }
- bool MythPlayer::HasCaptionTrack(int mode)
- {
- if (mode == kDisplayNone)
- return false;
- if (((mode == kDisplayTextSubtitle) && HasTextSubtitles()) ||
- (mode == kDisplayNUVTeletextCaptions))
- {
- return true;
- }
- if (!(mode == kDisplayTextSubtitle) &&
- m_decoder->GetTrackCount(toTrackType(mode)))
- {
- return true;
- }
- return false;
- }
- int MythPlayer::NextCaptionTrack(int mode)
- {
- // Text->TextStream->708->608->AVSubs->Teletext->NUV->None
- // NUV only offerred if PAL
- bool pal = (m_vbiMode == VBIMode::PAL_TT);
- int nextmode = kDisplayNone;
- if (kDisplayTextSubtitle == mode)
- nextmode = kDisplayRawTextSubtitle;
- else if (kDisplayRawTextSubtitle == mode)
- nextmode = kDisplayCC708;
- else if (kDisplayCC708 == mode)
- nextmode = kDisplayCC608;
- else if (kDisplayCC608 == mode)
- nextmode = kDisplayAVSubtitle;
- else if (kDisplayAVSubtitle == mode)
- nextmode = kDisplayTeletextCaptions;
- else if (kDisplayTeletextCaptions == mode)
- nextmode = pal ? kDisplayNUVTeletextCaptions : kDisplayNone;
- else if ((kDisplayNUVTeletextCaptions == mode) && pal)
- nextmode = kDisplayNone;
- else if (kDisplayNone == mode)
- nextmode = kDisplayTextSubtitle;
- if (nextmode == kDisplayNone || HasCaptionTrack(nextmode))
- return nextmode;
- return NextCaptionTrack(nextmode);
- }
- void MythPlayer::SetFrameInterval(FrameScanType scan, double frame_period)
- {
- if (m_decoder)
- m_fpsMultiplier = m_decoder->GetfpsMultiplier();
- m_frameInterval = static_cast<int>(lround(1000000.0 * frame_period) / m_fpsMultiplier);
- LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SetFrameInterval Interval:%1 Speed:%2 Scan:%3 (Multiplier: %4)")
- .arg(m_frameInterval).arg(static_cast<double>(m_playSpeed)).arg(toQString(scan)).arg(m_fpsMultiplier));
- }
- void MythPlayer::ResetAVSync(void)
- {
- m_avsyncAvg = 0;
- m_prevTc = 0;
- m_rtcBase = 0;
- m_priorAudioTimecode = 0;
- m_priorVideoTimecode = 0;
- m_lastFix = 0.0;
- LOG(VB_PLAYBACK | VB_TIMESTAMP, LOG_INFO, LOC + "A/V sync reset");
- }
- void MythPlayer::InitAVSync(void)
- {
- m_rtcBase = 0;
- m_priorAudioTimecode = 0;
- m_priorVideoTimecode = 0;
- m_lastFix = 0.0;
- if (!FlagIsSet(kVideoIsNull))
- {
- LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Display Refresh Rate: %1 Video Frame Rate: %2")
- .arg(1000000.0 / m_display->GetRefreshInterval(m_frameInterval), 0, 'f', 3)
- .arg(1000000.0 / m_frameInterval, 0, 'f', 3));
- SetFrameInterval(m_scan, 1.0 / (m_videoFrameRate * static_cast<double>(m_playSpeed)));
- // try to get preferential scheduling, but ignore if we fail to.
- myth_nice(-19);
- }
- }
- void MythPlayer::WaitForTime(int64_t framedue)
- {
- int64_t unow = m_avTimer.nsecsElapsed() / 1000;
- int64_t delay = framedue - unow;
- if (delay > 0)
- QThread::usleep(static_cast<unsigned long>(delay));
- }
- /*! \brief Keep PiP frame rate in sync with master framerate
- *
- * This is a simple frame rate tracker. If a frame is not due, then just keep
- * the last displayed frame. Otherwise discard frame(s) that are too old.
- */
- bool MythPlayer::PipSync(void)
- {
- int maxtries = 6;
- int64_t timenow = m_avTimer.nsecsElapsed() / 1000;
- auto playspeed1000 = static_cast<int64_t>(1000.0F / m_playSpeed);
- while (maxtries--)
- {
- if (!m_videoOutput->ValidVideoFrames())
- return false;
- m_videoOutput->StartDisplayingFrame();
- VideoFrame *last = m_videoOutput->GetLastShownFrame();
- if (!last)
- return false;
- m_videoOutput->ProcessFrame(last, nullptr, m_pipPlayers, m_scan);
- int64_t videotimecode = last->timecode & 0x0000ffffffffffff;
- if (videotimecode != last->timecode)
- videotimecode = m_maxTcVal;
- if (videotimecode == 0)
- {
- m_videoOutput->DoneDisplayingFrame(last);
- return true;
- }
- m_maxTcVal = videotimecode;
- if (m_rtcBase == 0)
- m_rtcBase = timenow - (videotimecode * playspeed1000);
- int64_t framedue = m_rtcBase + (videotimecode * playspeed1000);
- if (framedue > timenow)
- return true;
- m_videoOutput->DoneDisplayingFrame(last);
- }
- return true;
- }
- #define AVSYNC_MAX_LATE 10000000
- void MythPlayer::AVSync(VideoFrame *buffer)
- {
- if (m_videoOutput->IsErrored())
- {
- LOG(VB_GENERAL, LOG_ERR, LOC +
- "AVSync: Unknown error in videoOutput, aborting playback.");
- SetErrored(tr("Failed to initialize A/V Sync"));
- return;
- }
- int64_t videotimecode = 0;
- bool dropframe = false;
- bool pause_audio = false;
- int64_t framedue = 0;
- int64_t audio_adjustment = 0;
- int64_t unow = 0;
- int64_t lateness = 0;
- auto playspeed1000 = static_cast<int64_t>(1000.0F / m_playSpeed);
- bool reset = false;
- // controller gain
- static float const s_av_control_gain = 0.4F;
- // time weighted exponential filter coefficient
- static float const s_sync_fc = 0.9F;
- while (framedue == 0)
- {
- if (buffer)
- {
- videotimecode = buffer->timecode & 0x0000ffffffffffff;
- // Detect bogus timecodes from DVD and ignore them.
- if (videotimecode != buffer->timecode)
- videotimecode = m_maxTcVal;
- }
- unow = m_avTimer.nsecsElapsed() / 1000;
- if (!m_normalSpeed || FlagIsSet(kMusicChoice))
- {
- framedue = unow + m_frameInterval;
- break;
- }
- // first time or after a seek - setup of m_rtcBase
- if (m_rtcBase == 0)
- {
- // cater for DVB radio
- if (videotimecode == 0)
- videotimecode = m_audio.GetAudioTime();;
- // cater for data only streams (i.e. MHEG)
- bool dataonly = !m_audio.HasAudioIn() && m_videoDim.isEmpty();
- // On first frame we get nothing, so exit out.…
Large files files are truncated, but you can click here to view the full file