Permalink
Browse files

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.

Signed-off-by: Stuart Morgan <smorgan@mythtv.org>
  • Loading branch information...
Richard authored and stuartm committed Mar 1, 2013
1 parent e60e3d2 commit b6c0ec5529d8b921a846c38b5d6afeb06a4d5671
@@ -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)
@@ -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);
@@ -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,22 +517,32 @@ 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)));
errno = EIO;
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)
@@ -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);
@@ -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)
@@ -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;

0 comments on commit b6c0ec5

Please sign in to comment.