Merge pull request #42 from bnnm/ffmpeg-fixes

FFmpeg looping fix (sample discard was not working in some cases)
This commit is contained in:
Christopher Snowhill 2016-11-20 11:35:34 -08:00 committed by GitHub
commit e60cc12983
2 changed files with 37 additions and 6 deletions

View File

@ -117,6 +117,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
endOfStream = data->endOfStream;
endOfAudio = data->endOfAudio;
/* keep reading and decoding packets until the requested number of samples (in bytes) */
while (bytesRead < bytesToRead) {
int planeSize;
int planar = av_sample_fmt_is_planar(codecCtx->sample_fmt);
@ -127,6 +128,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
if (dataSize < 0)
dataSize = 0;
/* read packet */
while (readNextPacket && !endOfAudio) {
if (!endOfStream) {
av_packet_unref(lastReadPacket);
@ -138,7 +140,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
break;
}
if (lastReadPacket->stream_index != streamIndex)
continue;
continue; /* ignore non audio streams */
}
if ((errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : lastReadPacket)) < 0) {
@ -150,6 +152,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
readNextPacket = 0;
}
/* decode packet */
if (dataSize <= bytesConsumedFromDecodedFrame) {
if (endOfStream && endOfAudio)
break;
@ -178,21 +181,32 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
/* discard packet if needed (fully or partially) */
if (data->samplesToDiscard) {
int samplesToConsume;
int bytesPerFrame = ((data->bitsPerSample / 8) * channels);
int samplesToConsume = toConsume / bytesPerFrame;
if (data->samplesToDiscard >= samplesToConsume) {
/* discard all if there are more samples to do than the packet's samples */
if (data->samplesToDiscard >= dataSize / bytesPerFrame) {
samplesToConsume = dataSize / bytesPerFrame;
}
else {
samplesToConsume = toConsume / bytesPerFrame;
}
if (data->samplesToDiscard >= samplesToConsume) { /* full discard: skip to next */
data->samplesToDiscard -= samplesToConsume;
bytesConsumedFromDecodedFrame = dataSize;
continue;
}
else {
else { /* partial discard: copy below */
bytesConsumedFromDecodedFrame += data->samplesToDiscard * bytesPerFrame;
toConsume -= data->samplesToDiscard * bytesPerFrame;
data->samplesToDiscard = 0;
}
}
/* copy packet to buffer (mux channels if needed) */
if (!planar || channels == 1) {
memmove(targetBuf + bytesRead, (lastDecodedFrame->data[0] + bytesConsumedFromDecodedFrame), toConsume);
}
@ -210,6 +224,7 @@ void decode_ffmpeg(VGMSTREAM *vgmstream,
}
}
/* consume */
bytesConsumedFromDecodedFrame += toConsume;
bytesRead += toConsume;
}

View File

@ -1791,6 +1791,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
if (vgmstream->coding_type==coding_FFmpeg) {
ffmpeg_codec_data *data = (ffmpeg_codec_data *)(vgmstream->codec_data);
int64_t ts;
/* Seek to loop start by timestamp (closest frame) + adjust skipping some samples */
/* FFmpeg seeks by ts by design (since not all containers can accurately skip to a frame). */
/* TODO: this seems to be off by +-1 frames in some cases */
ts = vgmstream->loop_start_sample;
if (ts >= data->sampleRate * 2) {
data->samplesToDiscard = data->sampleRate * 2;
@ -1802,14 +1806,26 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
}
data->framesRead = (int)ts;
ts = data->framesRead * (data->formatCtx->duration) / data->totalFrames;
#ifdef VGM_USE_FFMPEG_ACCURATE_LOOPING
/* Start from 0 and discard samples until loop_start for accurate looping (slower but not too noticeable) */
/* We could also seek by offset (AVSEEK_FLAG_BYTE) to the frame closest to the loop then discard
* some samples, which is fast but would need calculations per format / when frame size is not constant */
data->samplesToDiscard = vgmstream->loop_start_sample;
data->framesRead = 0;
ts = 0;
#endif /* VGM_USE_FFMPEG_ACCURATE_LOOPING */
avformat_seek_file(data->formatCtx, -1, ts - 1000, ts, ts, AVSEEK_FLAG_ANY);
avcodec_flush_buffers(data->codecCtx);
data->readNextPacket = 1;
data->bytesConsumedFromDecodedFrame = INT_MAX;
data->endOfStream = 0;
data->endOfAudio = 0;
}
#endif
#endif /* VGM_USE_FFMPEG */
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = (mp4_aac_codec_data *)(vgmstream->codec_data);