Skip to content

Commit

Permalink
Make sure any stored packets are flushed when the DVDRingBuffer needs…
Browse files Browse the repository at this point in the history
… to wait for the player.

This fixes playback issues with still frames when using VDPAU (or when the 'Extra audio buffering' option is enabled) due to the still frame getting stuck in the decoder's buffers and the player running out of frames.
  • Loading branch information
peper03 committed Jul 23, 2013
1 parent 119c9d9 commit 0d49388
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 62 deletions.
84 changes: 63 additions & 21 deletions mythtv/libs/libmythtv/DVD/avformatdecoderdvd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ AvFormatDecoderDVD::AvFormatDecoderDVD(
, m_lbaLastVideoPkt(INVALID_LBA)
, m_framesReq(0)
, m_returnContext(NULL)
, m_oldLowBuffers(lowbuffers)
{
}

Expand Down Expand Up @@ -57,6 +58,14 @@ void AvFormatDecoderDVD::Reset(bool reset_video_data, bool seek_reset, bool rese
SyncPositionMap();
}

void AvFormatDecoderDVD::SetLowBuffers(bool low)
{
if (lowbuffers == m_oldLowBuffers)
DecoderBase::SetLowBuffers(low);

m_oldLowBuffers = low;
}

void AvFormatDecoderDVD::UpdateFramesPlayed(void)
{
if (!ringBuffer->IsDVD())
Expand All @@ -73,7 +82,7 @@ bool AvFormatDecoderDVD::GetFrame(DecodeType decodetype)
return AvFormatDecoder::GetFrame( kDecodeAV );
}

int AvFormatDecoderDVD::ReadPacket(AVFormatContext *ctx, AVPacket* pkt)
int AvFormatDecoderDVD::ReadPacket(AVFormatContext *ctx, AVPacket* pkt, bool& storePacket)
{
int result = 0;

Expand Down Expand Up @@ -106,33 +115,66 @@ int AvFormatDecoderDVD::ReadPacket(AVFormatContext *ctx, AVPacket* pkt)
{
gotPacket = true;

result = av_read_frame(ctx, pkt);

while (result == AVERROR_EOF && errno == EAGAIN)
do
{
if (ringBuffer->DVD()->IsReadingBlocked())
{
if (ringBuffer->DVD()->GetLastEvent() == DVDNAV_HOP_CHANNEL)
switch(ringBuffer->DVD()->GetLastEvent())
{
// Non-seamless jump - clear all buffers
m_framesReq = 0;
ReleaseContext(m_curContext);

while (m_contextList.size() > 0)
m_contextList.takeFirst()->DecrRef();

Reset(true, false, false);
m_audio->Reset();
m_parent->DiscardVideoFrames(false);
case DVDNAV_HOP_CHANNEL:
// Non-seamless jump - clear all buffers
m_framesReq = 0;
ReleaseContext(m_curContext);

while (m_contextList.size() > 0)
m_contextList.takeFirst()->DecrRef();

Reset(true, false, false);
m_audio->Reset();
m_parent->DiscardVideoFrames(false);
break;

case DVDNAV_WAIT:
case DVDNAV_STILL_FRAME:
if (storedPackets.count() > 0)
{
// Ringbuffer is waiting for the player
// to empty its buffers but we have one or
// more frames in our buffer that have not
// yet been sent to the player.
// Make sure no more frames will be buffered
// for the time being and start emptying our
// buffer.
m_oldLowBuffers = lowbuffers;
lowbuffers = false;

// Force AvFormatDecoder to stop buffering frames
storePacket = false;

// Return the first buffered packet
AVPacket *storedPkt = storedPackets.takeFirst();
av_copy_packet(pkt, storedPkt);
delete storedPkt;

return 0;
}
else
{
// Our buffers are empty, frames may be
// buffered again if necessary.
lowbuffers = m_oldLowBuffers;
}
break;

default:
break;
}

ringBuffer->DVD()->UnblockReading();
result = av_read_frame(ctx, pkt);
}
else
{
break;
}
}

result = av_read_frame(ctx, pkt);
}while (result == AVERROR_EOF && errno == EAGAIN);

if (result >= 0)
{
Expand Down
4 changes: 3 additions & 1 deletion mythtv/libs/libmythtv/DVD/avformatdecoderdvd.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ class AvFormatDecoderDVD : public AvFormatDecoder
virtual void Reset(bool reset_video_data, bool seek_reset, bool reset_file);
virtual void UpdateFramesPlayed(void);
virtual bool GetFrame(DecodeType decodetype); // DecoderBase
virtual void SetLowBuffers(bool low);

protected:
virtual int ReadPacket(AVFormatContext *ctx, AVPacket *pkt);
virtual int ReadPacket(AVFormatContext *ctx, AVPacket *pkt, bool &storePacket);
virtual bool ProcessVideoPacket(AVStream *stream, AVPacket *pkt);
virtual bool ProcessVideoFrame(AVStream *stream, AVFrame *mpa_pic);
virtual bool ProcessDataPacket(AVStream *curstream, AVPacket *pkt,
Expand All @@ -43,6 +44,7 @@ class AvFormatDecoderDVD : public AvFormatDecoder
uint32_t m_lbaLastVideoPkt;
int m_framesReq;
MythDVDContext* m_returnContext;
bool m_oldLowBuffers;
};

#endif // AVFORMATDECODERDVD_H
94 changes: 58 additions & 36 deletions mythtv/libs/libmythtv/DVD/dvdringbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ int DVDRingBuffer::safe_read(void *data, uint sz)
int offset = 0;
bool bReprocessing = false;
bool stillSeen = false;
bool waiting = false;

if (m_gotStop)
{
Expand Down Expand Up @@ -1146,55 +1147,76 @@ int DVDRingBuffer::safe_read(void *data, uint sz)

m_still = still->length;

// pause a little as the dvdnav VM will continue to return
// this event until it has been skipped
rwlock.unlock();
usleep(10000);
rwlock.lockForWrite();

// when scanning the file or exiting playback, skip immediately
// otherwise update the timeout in the player
if (m_skipstillorwait)
SkipStillFrame();
else if (m_parent)
if (!bReprocessing && !m_skipstillorwait && !waiting)
{
m_parent->SetStillFrameTimeout(m_still);
}

// debug
if (!stillSeen)
{
LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1)")
LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1) - waiting")
.arg(m_still));
stillSeen = true;
m_processState = PROCESS_WAIT;
}
else
{
waiting = true;

// release buffer
if (blockBuf != m_dvdBlockWriteBuf)
dvdnav_free_cache_block(m_dvdnav, blockBuf);
// pause a little as the dvdnav VM will continue to return
// this event until it has been skipped
rwlock.unlock();
usleep(10000);
rwlock.lockForWrite();

// when scanning the file or exiting playback, skip immediately
// otherwise update the timeout in the player
if (m_skipstillorwait)
SkipStillFrame();
else if (m_parent)
{
m_parent->SetStillFrameTimeout(m_still);
}

// debug
if (!stillSeen)
{
LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1)")
.arg(m_still));
stillSeen = true;
}

// release buffer
if (blockBuf != m_dvdBlockWriteBuf)
dvdnav_free_cache_block(m_dvdnav, blockBuf);
}
}
break;

// wait for the player
case DVDNAV_WAIT:
{
//debug
LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT");

// skip if required, otherwise wait (and loop)
if (m_skipstillorwait)
WaitSkip();
else
if (!bReprocessing && !m_skipstillorwait && !waiting)
{
m_dvdWaiting = true;
rwlock.unlock();
usleep(10000);
rwlock.lockForWrite();
LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT - waiting");
m_processState = PROCESS_WAIT;
}
else
{
waiting = true;

// release buffer
if (blockBuf != m_dvdBlockWriteBuf)
dvdnav_free_cache_block(m_dvdnav, blockBuf);
//debug
LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT");

// skip if required, otherwise wait (and loop)
if (m_skipstillorwait)
WaitSkip();
else
{
m_dvdWaiting = true;
rwlock.unlock();
usleep(10000);
rwlock.lockForWrite();
}

// release buffer
if (blockBuf != m_dvdBlockWriteBuf)
dvdnav_free_cache_block(m_dvdnav, blockBuf);
}
}
break;

Expand Down
4 changes: 2 additions & 2 deletions mythtv/libs/libmythtv/avformatdecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4661,7 +4661,7 @@ bool AvFormatDecoder::GetFrame(DecodeType decodetype)
}

int retval = 0;
if (!ic || ((retval = ReadPacket(ic, pkt)) < 0))
if (!ic || ((retval = ReadPacket(ic, pkt, storevideoframes)) < 0))
{
if (retval == -EAGAIN)
continue;
Expand Down Expand Up @@ -4836,7 +4836,7 @@ bool AvFormatDecoder::GetFrame(DecodeType decodetype)
return true;
}

int AvFormatDecoder::ReadPacket(AVFormatContext *ctx, AVPacket *pkt)
int AvFormatDecoder::ReadPacket(AVFormatContext *ctx, AVPacket *pkt, bool &/*storePacket*/)
{
return av_read_frame(ctx, pkt);
}
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/avformatdecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ class AvFormatDecoder : public DecoderBase
int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size,
AVPacket *pkt);

virtual int ReadPacket(AVFormatContext *ctx, AVPacket* pkt);
virtual int ReadPacket(AVFormatContext *ctx, AVPacket *pkt, bool &storePacket);

PrivateDecoder *private_dec;

Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/decoderbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ class DecoderBase
// Must be done while player is paused.
void SetProgramInfo(const ProgramInfo &pginfo);

void SetLowBuffers(bool low) { lowbuffers = low; }
virtual void SetLowBuffers(bool low) { lowbuffers = low; }
/// Disables AC3/DTS pass through
virtual void SetDisablePassThrough(bool disable) { (void)disable; }

Expand Down

0 comments on commit 0d49388

Please sign in to comment.