diff --git a/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp b/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp index a27dfe50ffe..ce7731b2e4a 100644 --- a/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp +++ b/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp @@ -32,6 +32,48 @@ bool AvFormatDecoderDVD::GetFrame(DecodeType decodetype) return AvFormatDecoder::GetFrame( kDecodeAV ); } +int64_t AvFormatDecoderDVD::AdjustTimestamp(int64_t timestamp) +{ + int64_t newTimestamp = timestamp; + + if (newTimestamp != AV_NOPTS_VALUE) + { + int64_t timediff = ringBuffer->DVD()->GetTimeDiff(); + if (newTimestamp >= timediff) + { + newTimestamp -= timediff; + } + } + + return newTimestamp; +} + +int AvFormatDecoderDVD::ReadPacket(AVFormatContext *ctx, AVPacket* pkt) +{ + int result = av_read_frame(ctx, pkt); + + while (result == AVERROR_EOF && errno == EAGAIN) + { + if (ringBuffer->DVD()->IsReadingBlocked()) + { + ringBuffer->DVD()->UnblockReading(); + result = av_read_frame(ctx, pkt); + } + else + { + break; + } + } + + if (result >= 0) + { + pkt->dts = AdjustTimestamp(pkt->dts); + pkt->pts = AdjustTimestamp(pkt->pts); + } + + return result; +} + void AvFormatDecoderDVD::PostProcessTracks(void) { if (!ringBuffer) diff --git a/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h b/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h index 1b154258254..bdb64e40442 100644 --- a/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h +++ b/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h @@ -12,6 +12,10 @@ class AvFormatDecoderDVD : public AvFormatDecoder virtual void UpdateFramesPlayed(void); virtual bool GetFrame(DecodeType decodetype); // DecoderBase + protected: + int64_t AdjustTimestamp(int64_t timestamp); + virtual int ReadPacket(AVFormatContext *ctx, AVPacket *pkt); + private: virtual bool DoRewindSeek(long long desiredFrame); virtual void DoFastForwardSeek(long long desiredFrame, bool &needflush); diff --git a/mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp b/mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp index 9f8ab82f6e6..eec83ccf75f 100644 --- a/mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp +++ b/mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp @@ -89,7 +89,9 @@ DVDRingBuffer::DVDRingBuffer(const QString &lfilename) : m_lastNav(NULL), m_part(0), m_lastPart(0), m_title(0), m_lastTitle(0), m_playerWait(false), m_titleParts(0), m_gotStop(false), m_currentAngle(0), - m_currentTitleAngleCount(0), m_newSequence(false), + m_currentTitleAngleCount(0), + m_endPts(0), m_timeDiff(0), + m_newSequence(false), m_still(0), m_lastStill(0), m_audioStreamsChanged(false), m_dvdWaiting(false), @@ -109,6 +111,10 @@ DVDRingBuffer::DVDRingBuffer(const QString &lfilename) : m_currentTime(0), m_parent(NULL), m_forcedAspect(-1.0f), + m_processState(PROCESS_NORMAL), + m_dvdStat(DVDNAV_STATUS_OK), + m_dvdEvent(0), + m_dvdEventSize(0), // Menu/buttons m_inMenu(false), m_buttonVersion(1), m_buttonStreamID(0), @@ -402,6 +408,9 @@ bool DVDRingBuffer::StartFromBeginning(void) m_audioStreamsChanged = true; } + m_endPts = 0; + m_timeDiff = 0; + return m_dvdnav; } @@ -491,14 +500,12 @@ void DVDRingBuffer::WaitForPlayer(void) int DVDRingBuffer::safe_read(void *data, uint sz) { - dvdnav_status_t dvdStat; unsigned char *blockBuf = NULL; uint tot = 0; - int32_t dvdEvent = 0; - int32_t dvdEventSize = 0; int needed = sz; char *dest = (char*) data; int offset = 0; + bool bReprocessing = false; if (m_gotStop) { @@ -510,14 +517,24 @@ int DVDRingBuffer::safe_read(void *data, uint sz) if (readaheadrunning) LOG(VB_GENERAL, LOG_ERR, LOC + "read ahead thread running."); - while (needed) + while ((m_processState != PROCESS_WAIT) && needed) { blockBuf = m_dvdBlockWriteBuf; - dvdStat = dvdnav_get_next_cache_block( - m_dvdnav, &blockBuf, &dvdEvent, &dvdEventSize); + if (m_processState == PROCESS_REPROCESS) + { + m_processState = PROCESS_NORMAL; + bReprocessing = true; + } + else + { + m_dvdStat = dvdnav_get_next_cache_block( + m_dvdnav, &blockBuf, &m_dvdEvent, &m_dvdEventSize); - if (dvdStat == DVDNAV_STATUS_ERR) + bReprocessing = false; + } + + if (m_dvdStat == DVDNAV_STATUS_ERR) { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read block: %1") .arg(dvdnav_err_to_string(m_dvdnav))); @@ -525,7 +542,7 @@ int DVDRingBuffer::safe_read(void *data, uint sz) return -1; } - switch (dvdEvent) + switch (m_dvdEvent) { // Standard packet for decoding case DVDNAV_BLOCK_OK: @@ -726,6 +743,30 @@ int DVDRingBuffer::safe_read(void *data, uint sz) { QMutexLocker lock(&m_seekLock); + pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav); + + // If the start PTS of this block is not the + // same as the end PTS of the last block, + // we've got a timestamp discontinuity + int64_t diff = (int64_t)pci->pci_gi.vobu_s_ptm - m_endPts; + if (diff != 0) + { + if (!bReprocessing && !m_skipstillorwait) + { + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3") + .arg(pci->pci_gi.vobu_s_ptm) + .arg(m_endPts) + .arg(diff)); + + m_processState = PROCESS_WAIT; + break; + } + + m_timeDiff += diff; + } + + m_endPts = pci->pci_gi.vobu_e_ptm; + // get the latest nav m_lastNav = (dvdnav_t *)blockBuf; @@ -932,7 +973,7 @@ int DVDRingBuffer::safe_read(void *data, uint sz) default: { LOG(VB_GENERAL, LOG_ERR, LOC + - QString("Unknown DVD event: %1").arg(dvdEvent)); + QString("Unknown DVD event: %1").arg(m_dvdEvent)); } break; } @@ -941,7 +982,15 @@ int DVDRingBuffer::safe_read(void *data, uint sz) offset = tot; } - return tot; + if (m_processState == PROCESS_WAIT) + { + errno = EAGAIN; + return 0; + } + else + { + return tot; + } } bool DVDRingBuffer::playTrack(int track) diff --git a/mythtv/libs/libmythtv/DVD/dvdringbuffer.h b/mythtv/libs/libmythtv/DVD/dvdringbuffer.h index 3a31c1bd791..5468db75f9a 100644 --- a/mythtv/libs/libmythtv/DVD/dvdringbuffer.h +++ b/mythtv/libs/libmythtv/DVD/dvdringbuffer.h @@ -78,6 +78,7 @@ class MTV_PUBLIC DVDRingBuffer : public RingBuffer bool IsWaiting(void) const { return m_dvdWaiting; } int NumPartsInTitle(void) const { return m_titleParts; } void GetMenuSPUPkt(uint8_t *buf, int len, int stream_id); + int64_t GetTimeDiff(void) const { return m_timeDiff; } // Public menu/button stuff AVSubtitle *GetMenuSubtitle(uint &version); @@ -119,6 +120,8 @@ class MTV_PUBLIC DVDRingBuffer : public RingBuffer void SkipStillFrame(void); void WaitSkip(void); void SkipDVDWaitingForPlayer(void) { m_playerWait = false; } + void UnblockReading(void) { m_processState = PROCESS_REPROCESS; } + bool IsReadingBlocked(void) { return (m_processState == PROCESS_WAIT); } bool GoToMenu(const QString str); void GoToNextProgram(void); void GoToPreviousProgram(void); @@ -137,6 +140,14 @@ class MTV_PUBLIC DVDRingBuffer : public RingBuffer void SetParent(MythDVDPlayer *p) { m_parent = p; } protected: + + typedef enum + { + PROCESS_NORMAL, + PROCESS_REPROCESS, + PROCESS_WAIT + }processState_t; + dvdnav_t *m_dvdnav; unsigned char m_dvdBlockWriteBuf[DVD_BLOCK_SIZE]; unsigned char *m_dvdBlockReadBuf; @@ -159,6 +170,8 @@ class MTV_PUBLIC DVDRingBuffer : public RingBuffer bool m_gotStop; int m_currentAngle; int m_currentTitleAngleCount; + int64_t m_endPts; + int64_t m_timeDiff; bool m_newSequence; int m_still; @@ -191,6 +204,11 @@ class MTV_PUBLIC DVDRingBuffer : public RingBuffer MythDVDPlayer *m_parent; float m_forcedAspect; + processState_t m_processState; + dvdnav_status_t m_dvdStat; + int32_t m_dvdEvent; + int32_t m_dvdEventSize; + // Private menu/button stuff void ActivateButton(void); void MoveButtonLeft(void); diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp index 78b66889147..3eb42c73338 100644 --- a/mythtv/libs/libmythtv/avformatdecoder.cpp +++ b/mythtv/libs/libmythtv/avformatdecoder.cpp @@ -4637,7 +4637,7 @@ bool AvFormatDecoder::GetFrame(DecodeType decodetype) } int retval = 0; - if (!ic || ((retval = av_read_frame(ic, pkt)) < 0)) + if (!ic || ((retval = ReadPacket(ic, pkt)) < 0)) { if (retval == -EAGAIN) continue; @@ -4812,6 +4812,11 @@ bool AvFormatDecoder::GetFrame(DecodeType decodetype) return true; } +int AvFormatDecoder::ReadPacket(AVFormatContext *ctx, AVPacket *pkt) +{ + return av_read_frame(ctx, pkt); +} + bool AvFormatDecoder::HasVideo(const AVFormatContext *ic) { if (ic && ic->cur_pmt_sect) diff --git a/mythtv/libs/libmythtv/avformatdecoder.h b/mythtv/libs/libmythtv/avformatdecoder.h index 379dc96218b..ea08fc94d7e 100644 --- a/mythtv/libs/libmythtv/avformatdecoder.h +++ b/mythtv/libs/libmythtv/avformatdecoder.h @@ -264,6 +264,9 @@ class AvFormatDecoder : public DecoderBase int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, AVPacket *pkt); + + virtual int ReadPacket(AVFormatContext *ctx, AVPacket* pkt); + PrivateDecoder *private_dec; bool is_db_ignored;