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; |