FFmpeg Decoder: Fix seeking in files with preroll that happens to make the decoder return EAGAIN error, so they don't inadvertently skip actual audio data unnecessarily. Fixes seeking to the start of USAC files with preroll packets.

CQTexperiment
Christopher Snowhill 2021-12-29 22:55:31 -08:00
parent cd8b728ca6
commit fa20465271
1 changed files with 24 additions and 19 deletions

View File

@ -251,6 +251,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
totalFrames = av_rescale_q(stream->duration, stream->time_base, tb); totalFrames = av_rescale_q(stream->duration, stream->time_base, tb);
bitrate = (int)((codecCtx->bit_rate) / 1000); bitrate = (int)((codecCtx->bit_rate) / 1000);
framesRead = 0; framesRead = 0;
seekFrame = 0; // Skip preroll if necessary
endOfStream = NO; endOfStream = NO;
endOfAudio = NO; endOfAudio = NO;
@ -348,24 +349,6 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
} }
readNextPacket = NO; // we probably won't need to consume another chunk readNextPacket = NO; // we probably won't need to consume another chunk
// FFmpeg seeking by packet is usually inexact, so skip up to
// target sample using packet timestamp
if (seekFrame >= 0 && errcode >= 0) {
DLog(@"Seeking to frame %lld", seekFrame);
AVRational tb = {.num = 1, .den = codecCtx->sample_rate};
int64_t packetBeginFrame = av_rescale_q(
lastReadPacket->dts,
formatCtx->streams[streamIndex]->time_base,
tb
);
if (packetBeginFrame < seekFrame) {
seekBytesSkip += (int)((seekFrame - packetBeginFrame) * frameSize);
}
seekFrame = -1;
}
} }
if (dataSize <= bytesConsumedFromDecodedFrame) if (dataSize <= bytesConsumedFromDecodedFrame)
@ -385,6 +368,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
else if (errcode == AVERROR(EAGAIN)) else if (errcode == AVERROR(EAGAIN))
{ {
// Read another packet // Read another packet
DLog(@"Decoded samples: %d", lastDecodedFrame->nb_samples);
readNextPacket = YES; readNextPacket = YES;
continue; continue;
} }
@ -405,6 +389,27 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
if ( dataSize < 0 ) if ( dataSize < 0 )
dataSize = 0; dataSize = 0;
// FFmpeg seeking by packet is usually inexact, so skip up to
// target sample using packet timestamp
// New: Moved here, because sometimes preroll packets also
// trigger EAGAIN above, so ask for the next packet's timestamp
// instead
if (seekFrame >= 0 && errcode >= 0) {
DLog(@"Seeking to frame %lld", seekFrame);
AVRational tb = {.num = 1, .den = codecCtx->sample_rate};
int64_t packetBeginFrame = av_rescale_q(
lastReadPacket->dts,
formatCtx->streams[streamIndex]->time_base,
tb
);
if (packetBeginFrame < seekFrame) {
seekBytesSkip += (int)((seekFrame - packetBeginFrame) * frameSize);
}
seekFrame = -1;
}
int minSkipped = FFMIN(dataSize, seekBytesSkip); int minSkipped = FFMIN(dataSize, seekBytesSkip);
bytesConsumedFromDecodedFrame += minSkipped; bytesConsumedFromDecodedFrame += minSkipped;
seekBytesSkip -= minSkipped; seekBytesSkip -= minSkipped;
@ -456,7 +461,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
} }
AVRational tb = {.num = 1, .den = codecCtx->sample_rate}; AVRational tb = {.num = 1, .den = codecCtx->sample_rate};
int64_t ts = av_rescale_q(frame, tb, formatCtx->streams[streamIndex]->time_base); int64_t ts = av_rescale_q(frame, tb, formatCtx->streams[streamIndex]->time_base);
avformat_seek_file(formatCtx, streamIndex, ts - 1000, ts, ts, AVSEEK_FLAG_ANY); avformat_seek_file(formatCtx, streamIndex, ts - 1000, ts, ts, 0);
avcodec_flush_buffers(codecCtx); avcodec_flush_buffers(codecCtx);
readNextPacket = YES; // so we immediately read next packet readNextPacket = YES; // so we immediately read next packet
bytesConsumedFromDecodedFrame = INT_MAX; // so we immediately begin decoding next frame bytesConsumedFromDecodedFrame = INT_MAX; // so we immediately begin decoding next frame