From 24b0b0197a777c801e681beba5aff9a39f5283fc Mon Sep 17 00:00:00 2001
From: Richard <peper03@yahoo.com>
Date: Fri, 1 Mar 2013 22:13:21 +0100
Subject: [PATCH] DVD PTS discontinuities are now handled by 'flattening' the
timecodes of incoming packets. This prevents
AVFormatDecoder getting stuck buffering video frames when
the timecodes jump backwards.
---
mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp | 42 +++++++++++++
mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h | 4 ++
mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp | 71 ++++++++++++++++++----
mythtv/libs/libmythtv/DVD/dvdringbuffer.h | 18 ++++++
mythtv/libs/libmythtv/avformatdecoder.cpp | 7 ++-
mythtv/libs/libmythtv/avformatdecoder.h | 3 +
6 files changed, 133 insertions(+), 12 deletions(-)
diff --git a/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp b/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp
index a27dfe5..ce7731b 100644
|
a
|
b
|
bool AvFormatDecoderDVD::GetFrame(DecodeType decodetype)
|
| 32 | 32 | return AvFormatDecoder::GetFrame( kDecodeAV ); |
| 33 | 33 | } |
| 34 | 34 | |
| | 35 | int64_t AvFormatDecoderDVD::AdjustTimestamp(int64_t timestamp) |
| | 36 | { |
| | 37 | int64_t newTimestamp = timestamp; |
| | 38 | |
| | 39 | if (newTimestamp != AV_NOPTS_VALUE) |
| | 40 | { |
| | 41 | int64_t timediff = ringBuffer->DVD()->GetTimeDiff(); |
| | 42 | if (newTimestamp >= timediff) |
| | 43 | { |
| | 44 | newTimestamp -= timediff; |
| | 45 | } |
| | 46 | } |
| | 47 | |
| | 48 | return newTimestamp; |
| | 49 | } |
| | 50 | |
| | 51 | int AvFormatDecoderDVD::ReadPacket(AVFormatContext *ctx, AVPacket* pkt) |
| | 52 | { |
| | 53 | int result = av_read_frame(ctx, pkt); |
| | 54 | |
| | 55 | while (result == AVERROR_EOF && errno == EAGAIN) |
| | 56 | { |
| | 57 | if (ringBuffer->DVD()->IsReadingBlocked()) |
| | 58 | { |
| | 59 | ringBuffer->DVD()->UnblockReading(); |
| | 60 | result = av_read_frame(ctx, pkt); |
| | 61 | } |
| | 62 | else |
| | 63 | { |
| | 64 | break; |
| | 65 | } |
| | 66 | } |
| | 67 | |
| | 68 | if (result >= 0) |
| | 69 | { |
| | 70 | pkt->dts = AdjustTimestamp(pkt->dts); |
| | 71 | pkt->pts = AdjustTimestamp(pkt->pts); |
| | 72 | } |
| | 73 | |
| | 74 | return result; |
| | 75 | } |
| | 76 | |
| 35 | 77 | void AvFormatDecoderDVD::PostProcessTracks(void) |
| 36 | 78 | { |
| 37 | 79 | if (!ringBuffer) |
diff --git a/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h b/mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h
index 1b15425..bdb64e4 100644
|
a
|
b
|
class AvFormatDecoderDVD : public AvFormatDecoder
|
| 12 | 12 | virtual void UpdateFramesPlayed(void); |
| 13 | 13 | virtual bool GetFrame(DecodeType decodetype); // DecoderBase |
| 14 | 14 | |
| | 15 | protected: |
| | 16 | int64_t AdjustTimestamp(int64_t timestamp); |
| | 17 | virtual int ReadPacket(AVFormatContext *ctx, AVPacket *pkt); |
| | 18 | |
| 15 | 19 | private: |
| 16 | 20 | virtual bool DoRewindSeek(long long desiredFrame); |
| 17 | 21 | 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 b0fa1ae..2698142 100644
|
a
|
b
|
DVDRingBuffer::DVDRingBuffer(const QString &lfilename) :
|
| 89 | 89 | m_lastNav(NULL), m_part(0), m_lastPart(0), |
| 90 | 90 | m_title(0), m_lastTitle(0), m_playerWait(false), |
| 91 | 91 | m_titleParts(0), m_gotStop(false), m_currentAngle(0), |
| 92 | | m_currentTitleAngleCount(0), m_newSequence(false), |
| | 92 | m_currentTitleAngleCount(0), |
| | 93 | m_endPts(0), m_timeDiff(0), |
| | 94 | m_newSequence(false), |
| 93 | 95 | m_still(0), m_lastStill(0), |
| 94 | 96 | m_audioStreamsChanged(false), |
| 95 | 97 | m_dvdWaiting(false), |
| … |
… |
DVDRingBuffer::DVDRingBuffer(const QString &lfilename) :
|
| 109 | 111 | m_currentTime(0), |
| 110 | 112 | m_parent(NULL), |
| 111 | 113 | m_forcedAspect(-1.0f), |
| | 114 | m_processState(PROCESS_NORMAL), |
| | 115 | m_dvdStat(DVDNAV_STATUS_OK), |
| | 116 | m_dvdEvent(0), |
| | 117 | m_dvdEventSize(0), |
| 112 | 118 | |
| 113 | 119 | // Menu/buttons |
| 114 | 120 | m_inMenu(false), m_buttonVersion(1), m_buttonStreamID(0), |
| … |
… |
bool DVDRingBuffer::StartFromBeginning(void)
|
| 397 | 403 | m_audioStreamsChanged = true; |
| 398 | 404 | } |
| 399 | 405 | |
| | 406 | m_endPts = 0; |
| | 407 | m_timeDiff = 0; |
| | 408 | |
| 400 | 409 | return m_dvdnav; |
| 401 | 410 | } |
| 402 | 411 | |
| … |
… |
void DVDRingBuffer::WaitForPlayer(void)
|
| 486 | 495 | |
| 487 | 496 | int DVDRingBuffer::safe_read(void *data, uint sz) |
| 488 | 497 | { |
| 489 | | dvdnav_status_t dvdStat; |
| 490 | 498 | unsigned char *blockBuf = NULL; |
| 491 | 499 | uint tot = 0; |
| 492 | | int32_t dvdEvent = 0; |
| 493 | | int32_t dvdEventSize = 0; |
| 494 | 500 | int needed = sz; |
| 495 | 501 | char *dest = (char*) data; |
| 496 | 502 | int offset = 0; |
| | 503 | bool bReprocessing = false; |
| 497 | 504 | |
| 498 | 505 | if (m_gotStop) |
| 499 | 506 | { |
| … |
… |
int DVDRingBuffer::safe_read(void *data, uint sz)
|
| 505 | 512 | if (readaheadrunning) |
| 506 | 513 | LOG(VB_GENERAL, LOG_ERR, LOC + "read ahead thread running."); |
| 507 | 514 | |
| 508 | | while (needed) |
| | 515 | while ((m_processState != PROCESS_WAIT) && needed) |
| 509 | 516 | { |
| 510 | 517 | blockBuf = m_dvdBlockWriteBuf; |
| 511 | 518 | |
| 512 | | dvdStat = dvdnav_get_next_cache_block( |
| 513 | | m_dvdnav, &blockBuf, &dvdEvent, &dvdEventSize); |
| | 519 | if (m_processState == PROCESS_REPROCESS) |
| | 520 | { |
| | 521 | m_processState = PROCESS_NORMAL; |
| | 522 | bReprocessing = true; |
| | 523 | } |
| | 524 | else |
| | 525 | { |
| | 526 | m_dvdStat = dvdnav_get_next_cache_block( |
| | 527 | m_dvdnav, &blockBuf, &m_dvdEvent, &m_dvdEventSize); |
| 514 | 528 | |
| 515 | | if (dvdStat == DVDNAV_STATUS_ERR) |
| | 529 | bReprocessing = false; |
| | 530 | } |
| | 531 | |
| | 532 | if (m_dvdStat == DVDNAV_STATUS_ERR) |
| 516 | 533 | { |
| 517 | 534 | LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read block: %1") |
| 518 | 535 | .arg(dvdnav_err_to_string(m_dvdnav))); |
| … |
… |
int DVDRingBuffer::safe_read(void *data, uint sz)
|
| 520 | 537 | return -1; |
| 521 | 538 | } |
| 522 | 539 | |
| 523 | | switch (dvdEvent) |
| | 540 | switch (m_dvdEvent) |
| 524 | 541 | { |
| 525 | 542 | // Standard packet for decoding |
| 526 | 543 | case DVDNAV_BLOCK_OK: |
| … |
… |
int DVDRingBuffer::safe_read(void *data, uint sz)
|
| 729 | 746 | { |
| 730 | 747 | QMutexLocker lock(&m_seekLock); |
| 731 | 748 | |
| | 749 | pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav); |
| | 750 | |
| | 751 | // If the start PTS of this block is not the |
| | 752 | // same as the end PTS of the last block, |
| | 753 | // we've got a timestamp discontinuity |
| | 754 | int64_t diff = (int64_t)pci->pci_gi.vobu_s_ptm - m_endPts; |
| | 755 | if (diff != 0) |
| | 756 | { |
| | 757 | if (!bReprocessing && !m_skipstillorwait) |
| | 758 | { |
| | 759 | LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3") |
| | 760 | .arg(pci->pci_gi.vobu_s_ptm) |
| | 761 | .arg(m_endPts) |
| | 762 | .arg(diff)); |
| | 763 | |
| | 764 | m_processState = PROCESS_WAIT; |
| | 765 | break; |
| | 766 | } |
| | 767 | |
| | 768 | m_timeDiff += diff; |
| | 769 | } |
| | 770 | |
| | 771 | m_endPts = pci->pci_gi.vobu_e_ptm; |
| | 772 | |
| 732 | 773 | // get the latest nav |
| 733 | 774 | m_lastNav = (dvdnav_t *)blockBuf; |
| 734 | 775 | |
| … |
… |
int DVDRingBuffer::safe_read(void *data, uint sz)
|
| 941 | 982 | default: |
| 942 | 983 | { |
| 943 | 984 | LOG(VB_GENERAL, LOG_ERR, LOC + |
| 944 | | QString("Unknown DVD event: %1").arg(dvdEvent)); |
| | 985 | QString("Unknown DVD event: %1").arg(m_dvdEvent)); |
| 945 | 986 | } |
| 946 | 987 | break; |
| 947 | 988 | } |
| … |
… |
int DVDRingBuffer::safe_read(void *data, uint sz)
|
| 950 | 991 | offset = tot; |
| 951 | 992 | } |
| 952 | 993 | |
| 953 | | return tot; |
| | 994 | if (m_processState == PROCESS_WAIT) |
| | 995 | { |
| | 996 | errno = EAGAIN; |
| | 997 | return 0; |
| | 998 | } |
| | 999 | else |
| | 1000 | { |
| | 1001 | return tot; |
| | 1002 | } |
| 954 | 1003 | } |
| 955 | 1004 | |
| 956 | 1005 | bool DVDRingBuffer::playTrack(int track) |
diff --git a/mythtv/libs/libmythtv/DVD/dvdringbuffer.h b/mythtv/libs/libmythtv/DVD/dvdringbuffer.h
index 3a31c1b..5468db7 100644
|
a
|
b
|
class MTV_PUBLIC DVDRingBuffer : public RingBuffer
|
| 78 | 78 | bool IsWaiting(void) const { return m_dvdWaiting; } |
| 79 | 79 | int NumPartsInTitle(void) const { return m_titleParts; } |
| 80 | 80 | void GetMenuSPUPkt(uint8_t *buf, int len, int stream_id); |
| | 81 | int64_t GetTimeDiff(void) const { return m_timeDiff; } |
| 81 | 82 | |
| 82 | 83 | // Public menu/button stuff |
| 83 | 84 | AVSubtitle *GetMenuSubtitle(uint &version); |
| … |
… |
class MTV_PUBLIC DVDRingBuffer : public RingBuffer
|
| 119 | 120 | void SkipStillFrame(void); |
| 120 | 121 | void WaitSkip(void); |
| 121 | 122 | void SkipDVDWaitingForPlayer(void) { m_playerWait = false; } |
| | 123 | void UnblockReading(void) { m_processState = PROCESS_REPROCESS; } |
| | 124 | bool IsReadingBlocked(void) { return (m_processState == PROCESS_WAIT); } |
| 122 | 125 | bool GoToMenu(const QString str); |
| 123 | 126 | void GoToNextProgram(void); |
| 124 | 127 | void GoToPreviousProgram(void); |
| … |
… |
class MTV_PUBLIC DVDRingBuffer : public RingBuffer
|
| 137 | 140 | void SetParent(MythDVDPlayer *p) { m_parent = p; } |
| 138 | 141 | |
| 139 | 142 | protected: |
| | 143 | |
| | 144 | typedef enum |
| | 145 | { |
| | 146 | PROCESS_NORMAL, |
| | 147 | PROCESS_REPROCESS, |
| | 148 | PROCESS_WAIT |
| | 149 | }processState_t; |
| | 150 | |
| 140 | 151 | dvdnav_t *m_dvdnav; |
| 141 | 152 | unsigned char m_dvdBlockWriteBuf[DVD_BLOCK_SIZE]; |
| 142 | 153 | unsigned char *m_dvdBlockReadBuf; |
| … |
… |
class MTV_PUBLIC DVDRingBuffer : public RingBuffer
|
| 159 | 170 | bool m_gotStop; |
| 160 | 171 | int m_currentAngle; |
| 161 | 172 | int m_currentTitleAngleCount; |
| | 173 | int64_t m_endPts; |
| | 174 | int64_t m_timeDiff; |
| 162 | 175 | |
| 163 | 176 | bool m_newSequence; |
| 164 | 177 | int m_still; |
| … |
… |
class MTV_PUBLIC DVDRingBuffer : public RingBuffer
|
| 191 | 204 | MythDVDPlayer *m_parent; |
| 192 | 205 | float m_forcedAspect; |
| 193 | 206 | |
| | 207 | processState_t m_processState; |
| | 208 | dvdnav_status_t m_dvdStat; |
| | 209 | int32_t m_dvdEvent; |
| | 210 | int32_t m_dvdEventSize; |
| | 211 | |
| 194 | 212 | // Private menu/button stuff |
| 195 | 213 | void ActivateButton(void); |
| 196 | 214 | void MoveButtonLeft(void); |
diff --git a/mythtv/libs/libmythtv/avformatdecoder.cpp b/mythtv/libs/libmythtv/avformatdecoder.cpp
index 3e3ce96..d4f6013 100644
|
a
|
b
|
bool AvFormatDecoder::GetFrame(DecodeType decodetype)
|
| 4646 | 4646 | } |
| 4647 | 4647 | |
| 4648 | 4648 | int retval = 0; |
| 4649 | | if (!ic || ((retval = av_read_frame(ic, pkt)) < 0)) |
| | 4649 | if (!ic || ((retval = ReadPacket(ic, pkt)) < 0)) |
| 4650 | 4650 | { |
| 4651 | 4651 | if (retval == -EAGAIN) |
| 4652 | 4652 | continue; |
| … |
… |
bool AvFormatDecoder::GetFrame(DecodeType decodetype)
|
| 4821 | 4821 | return true; |
| 4822 | 4822 | } |
| 4823 | 4823 | |
| | 4824 | int AvFormatDecoder::ReadPacket(AVFormatContext *ctx, AVPacket *pkt) |
| | 4825 | { |
| | 4826 | return av_read_frame(ctx, pkt); |
| | 4827 | } |
| | 4828 | |
| 4824 | 4829 | bool AvFormatDecoder::HasVideo(const AVFormatContext *ic) |
| 4825 | 4830 | { |
| 4826 | 4831 | if (ic && ic->cur_pmt_sect) |
diff --git a/mythtv/libs/libmythtv/avformatdecoder.h b/mythtv/libs/libmythtv/avformatdecoder.h
index 379dc96..ea08fc9 100644
|
a
|
b
|
class AvFormatDecoder : public DecoderBase
|
| 264 | 264 | |
| 265 | 265 | int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, |
| 266 | 266 | AVPacket *pkt); |
| | 267 | |
| | 268 | virtual int ReadPacket(AVFormatContext *ctx, AVPacket* pkt); |
| | 269 | |
| 267 | 270 | PrivateDecoder *private_dec; |
| 268 | 271 | |
| 269 | 272 | bool is_db_ignored; |