Skip to content

Commit

Permalink
DVD PTS discontinuities are now handled by 'flattening' the timecodes…
Browse files Browse the repository at this point in the history
… 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 13, 2013
1 parent e60e3d2 commit b6c0ec5
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 12 deletions.
42 changes: 42 additions & 0 deletions mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h
Expand Up @@ -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);
Expand Down
71 changes: 60 additions & 11 deletions mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp
Expand Up @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -402,6 +408,9 @@ bool DVDRingBuffer::StartFromBeginning(void)
m_audioStreamsChanged = true;
}

m_endPts = 0;
m_timeDiff = 0;

return m_dvdnav;
}

Expand Down Expand Up @@ -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)
{
Expand All @@ -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:
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
Expand All @@ -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)
Expand Down
18 changes: 18 additions & 0 deletions mythtv/libs/libmythtv/DVD/dvdringbuffer.h
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion mythtv/libs/libmythtv/avformatdecoder.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions mythtv/libs/libmythtv/avformatdecoder.h
Expand Up @@ -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;
Expand Down

0 comments on commit b6c0ec5

Please sign in to comment.