Further improved the FFmpeg plug-in, including cleaning up after a memory leak.

CQTexperiment
Chris Moeller 2016-07-15 21:41:23 -07:00
parent 0c830b92f8
commit aaf516fb00
2 changed files with 68 additions and 58 deletions

View File

@ -33,9 +33,9 @@
AVFrame *lastDecodedFrame; AVFrame *lastDecodedFrame;
AVPacket *lastReadPacket; AVPacket *lastReadPacket;
int bytesConsumedFromDecodedFrame; int bytesConsumedFromDecodedFrame;
int bytesReadFromPacket;
BOOL readNextPacket; BOOL readNextPacket;
BOOL endOfStream; BOOL endOfStream;
BOOL endOfAudio;
} }
@end @end

View File

@ -29,7 +29,7 @@ int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size)
int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence)
{ {
id source = (__bridge id) opaque; id source = (__bridge id) opaque;
return [source seek:offset whence:whence] ? [source tell] : -1; return [source seekable] ? ([source seek:offset whence:whence] ? [source tell] : -1) : -1;
} }
@implementation FFMPEGDecoder @implementation FFMPEGDecoder
@ -228,10 +228,13 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
return NO; return NO;
} }
totalFrames = codecCtx->sample_rate * ((float)formatCtx->duration/AV_TIME_BASE); //totalFrames = codecCtx->sample_rate * ((float)formatCtx->duration/AV_TIME_BASE);
AVRational tb = (AVRational) { 1, codecCtx->sample_rate };
totalFrames = av_rescale_q(formatCtx->streams[streamIndex]->duration, formatCtx->streams[streamIndex]->time_base, tb);
bitrate = (int)((codecCtx->bit_rate) / 1000); bitrate = (int)((codecCtx->bit_rate) / 1000);
framesRead = 0; framesRead = 0;
endOfStream = NO; endOfStream = NO;
endOfAudio = NO;
if ( totalFrames < 0 ) if ( totalFrames < 0 )
totalFrames = 0; totalFrames = 0;
@ -256,7 +259,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
if (formatCtx) { avformat_close_input(&(formatCtx)); formatCtx = NULL; } if (formatCtx) { avformat_close_input(&(formatCtx)); formatCtx = NULL; }
if (ioCtx) { av_free(ioCtx); ioCtx = NULL; buffer = NULL; } if (ioCtx) { buffer = ioCtx->buffer; av_free(ioCtx); ioCtx = NULL; }
if (buffer) { av_free(buffer); buffer = NULL; } if (buffer) { av_free(buffer); buffer = NULL; }
} }
@ -272,7 +275,6 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
return 0; return 0;
int frameSize = channels * (bitsPerSample / 8); int frameSize = channels * (bitsPerSample / 8);
int gotFrame = 0;
int dataSize = 0; int dataSize = 0;
int bytesToRead = frames * frameSize; int bytesToRead = frames * frameSize;
@ -296,69 +298,75 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
if ( dataSize < 0 ) if ( dataSize < 0 )
dataSize = 0; dataSize = 0;
while(readNextPacket && !endOfStream) while(readNextPacket && !endOfAudio)
{ {
// consume next chunk of encoded data from input stream // consume next chunk of encoded data from input stream
av_packet_unref(lastReadPacket); if (!endOfStream)
if((errcode = av_read_frame(formatCtx, lastReadPacket)) < 0) {
av_packet_unref(lastReadPacket);
if((errcode = av_read_frame(formatCtx, lastReadPacket)) < 0)
{
if (errcode == AVERROR_EOF)
{
DLog(@"End of stream");
endOfStream = YES;
}
if (formatCtx->pb && formatCtx->pb->error) break;
}
if (lastReadPacket->stream_index != streamIndex)
continue;
}
if ((errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : lastReadPacket)) < 0)
{
if (errcode != AVERROR(EAGAIN))
{
char errDescr[4096];
av_strerror(errcode, errDescr, 4096);
ALog(@"Error sending packet to codec, errcode = %d, error = %s", errcode, errDescr);
return 0;
}
}
readNextPacket = NO; // we probably won't need to consume another chunk
}
if (dataSize <= bytesConsumedFromDecodedFrame)
{
if (endOfStream && endOfAudio)
break;
bytesConsumedFromDecodedFrame = 0;
if ((errcode = avcodec_receive_frame(codecCtx, lastDecodedFrame)) < 0)
{ {
if (errcode == AVERROR_EOF) if (errcode == AVERROR_EOF)
{ {
DLog(@"End of stream"); endOfAudio = YES;
endOfStream = YES; break;
} }
if (formatCtx->pb && formatCtx->pb->error) break; else if (errcode == AVERROR(EAGAIN))
}
if (lastReadPacket->stream_index != streamIndex)
continue;
readNextPacket = NO; // we probably won't need to consume another chunk
bytesReadFromPacket = 0; // until this one is fully decoded
}
if (dataSize <= bytesConsumedFromDecodedFrame)
{
if (endOfStream)
break;
// consumed all decoded samples - decode more
av_frame_unref(lastDecodedFrame);
bytesConsumedFromDecodedFrame = 0;
int len;
do {
len = avcodec_decode_audio4(codecCtx, lastDecodedFrame, &gotFrame, lastReadPacket);
if (len > 0)
{ {
if (len >= lastReadPacket->size) { // Read another packet
lastReadPacket->data -= bytesReadFromPacket; readNextPacket = YES;
lastReadPacket->size += bytesReadFromPacket; continue;
readNextPacket = YES; }
break; else
} {
bytesReadFromPacket += len; char errDescr[4096];
lastReadPacket->data += len; av_strerror(errcode, errDescr, 4096);
lastReadPacket->size -= len; ALog(@"Error receiving frame, errcode = %d, error = %s", errcode, errDescr);
return 0;
} }
} while (!gotFrame && len > 0);
if (len < 0)
{
char errDescr[4096];
av_strerror(len, errDescr, 4096);
ALog(@"Error decoding packet, gotFrame = %d, errcode = %d, error = %s", gotFrame, len, errDescr);
dataSize = 0;
readNextPacket = YES;
} }
else
{ // Something has been successfully decoded
// Something has been successfully decoded dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels,
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels, lastDecodedFrame->nb_samples,
lastDecodedFrame->nb_samples, codecCtx->sample_fmt, 1);
codecCtx->sample_fmt, 1);
if ( dataSize < 0 ) if ( dataSize < 0 )
dataSize = 0; dataSize = 0;
}
} }
int toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead)); int toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
@ -402,6 +410,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
{ {
framesRead = totalFrames; framesRead = totalFrames;
endOfStream = YES; endOfStream = YES;
endOfAudio = YES;
return -1; return -1;
} }
int64_t ts = frame * (formatCtx->duration) / totalFrames; int64_t ts = frame * (formatCtx->duration) / totalFrames;
@ -411,6 +420,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
bytesConsumedFromDecodedFrame = INT_MAX; // so we immediately begin decoding next frame bytesConsumedFromDecodedFrame = INT_MAX; // so we immediately begin decoding next frame
framesRead = frame; framesRead = frame;
endOfStream = NO; endOfStream = NO;
endOfAudio = NO;
return frame; return frame;
} }