FFmpeg: Fix seeking so it's sample exact, using packet timestamps. Fixes noticeable issues when using CUE sheets with APE, for instance.

CQTexperiment
Christopher Snowhill 2021-05-30 14:22:20 -07:00
parent 297ba6398a
commit 8aa47584f1
2 changed files with 27 additions and 1 deletions

View File

@ -34,6 +34,7 @@
AVPacket *lastReadPacket;
int bytesConsumedFromDecodedFrame;
BOOL readNextPacket;
int64_t seekFrame;
BOOL endOfStream;
BOOL endOfAudio;
}

View File

@ -208,6 +208,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
av_new_packet(lastReadPacket, 0);
readNextPacket = YES;
bytesConsumedFromDecodedFrame = INT_MAX;
seekFrame = -1;
frequency = codecCtx->sample_rate;
channels = codecCtx->channels;
@ -246,7 +247,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
}
//totalFrames = codecCtx->sample_rate * ((float)formatCtx->duration/AV_TIME_BASE);
AVRational tb = (AVRational) { 1, codecCtx->sample_rate };
AVRational tb = {.num = 1, .den = codecCtx->sample_rate};
totalFrames = av_rescale_q(stream->duration, stream->time_base, tb);
bitrate = (int)((codecCtx->bit_rate) / 1000);
framesRead = 0;
@ -296,6 +297,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
int bytesToRead = frames * frameSize;
int bytesRead = 0;
int seekBytesSkip = 0;
int errcode;
@ -346,6 +348,24 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
}
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)
@ -384,6 +404,10 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
if ( dataSize < 0 )
dataSize = 0;
int minSkipped = FFMIN(dataSize, seekBytesSkip);
bytesConsumedFromDecodedFrame += minSkipped;
seekBytesSkip -= minSkipped;
}
int toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
@ -436,6 +460,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
readNextPacket = YES; // so we immediately read next packet
bytesConsumedFromDecodedFrame = INT_MAX; // so we immediately begin decoding next frame
framesRead = frame;
seekFrame = frame;
endOfStream = NO;
endOfAudio = NO;