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;
AVPacket *lastReadPacket;
int bytesConsumedFromDecodedFrame;
int bytesReadFromPacket;
BOOL readNextPacket;
BOOL endOfStream;
BOOL endOfAudio;
}
@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)
{
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
@ -228,10 +228,13 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
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);
framesRead = 0;
endOfStream = NO;
endOfAudio = NO;
if ( 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 (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; }
}
@ -272,7 +275,6 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
return 0;
int frameSize = channels * (bitsPerSample / 8);
int gotFrame = 0;
int dataSize = 0;
int bytesToRead = frames * frameSize;
@ -296,69 +298,75 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
if ( dataSize < 0 )
dataSize = 0;
while(readNextPacket && !endOfStream)
while(readNextPacket && !endOfAudio)
{
// consume next chunk of encoded data from input stream
av_packet_unref(lastReadPacket);
if((errcode = av_read_frame(formatCtx, lastReadPacket)) < 0)
if (!endOfStream)
{
if (errcode == AVERROR_EOF)
av_packet_unref(lastReadPacket);
if((errcode = av_read_frame(formatCtx, lastReadPacket)) < 0)
{
DLog(@"End of stream");
endOfStream = YES;
if (errcode == AVERROR_EOF)
{
DLog(@"End of stream");
endOfStream = YES;
}
if (formatCtx->pb && formatCtx->pb->error) break;
}
if (formatCtx->pb && formatCtx->pb->error) break;
if (lastReadPacket->stream_index != streamIndex)
continue;
}
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
bytesReadFromPacket = 0; // until this one is fully decoded
}
if (dataSize <= bytesConsumedFromDecodedFrame)
{
if (endOfStream)
if (endOfStream && endOfAudio)
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) {
lastReadPacket->data -= bytesReadFromPacket;
lastReadPacket->size += bytesReadFromPacket;
readNextPacket = YES;
break;
}
bytesReadFromPacket += len;
lastReadPacket->data += len;
lastReadPacket->size -= len;
}
} 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
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels,
lastDecodedFrame->nb_samples,
codecCtx->sample_fmt, 1);
if ( dataSize < 0 )
dataSize = 0;
if ((errcode = avcodec_receive_frame(codecCtx, lastDecodedFrame)) < 0)
{
if (errcode == AVERROR_EOF)
{
endOfAudio = YES;
break;
}
else if (errcode == AVERROR(EAGAIN))
{
// Read another packet
readNextPacket = YES;
continue;
}
else
{
char errDescr[4096];
av_strerror(errcode, errDescr, 4096);
ALog(@"Error receiving frame, errcode = %d, error = %s", errcode, errDescr);
return 0;
}
}
// Something has been successfully decoded
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels,
lastDecodedFrame->nb_samples,
codecCtx->sample_fmt, 1);
if ( dataSize < 0 )
dataSize = 0;
}
int toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
@ -402,6 +410,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
{
framesRead = totalFrames;
endOfStream = YES;
endOfAudio = YES;
return -1;
}
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
framesRead = frame;
endOfStream = NO;
endOfAudio = NO;
return frame;
}