libff: Improved handling of EOF in the decoder threads

(Also modifies obs-ffmpeg to handle empty frames on EOF)

Previously the demuxer could hit EOF before the decoder threads are
finished, resulting in truncated output. In the worse case scenario the
demuxer could read small files before ff_decoder_refresh even has a chance
to start the clocks, resulting in no output at all.
This commit is contained in:
Richard Stanway 2016-04-28 23:53:57 +02:00
parent ea851b1351
commit 65fcd20242
No known key found for this signature in database
GPG key ID: AAC1E5265D71B3FD
5 changed files with 38 additions and 10 deletions

View file

@ -70,8 +70,14 @@ static int decode_frame(struct ff_decoder *decoder,
int ret;
while (true) {
ret = packet_queue_get(&decoder->packet_queue, packet, 1);
if (ret == FF_PACKET_FAIL) {
if (decoder->eof)
ret = packet_queue_get(&decoder->packet_queue, packet, 0);
else
ret = packet_queue_get(&decoder->packet_queue, packet, 1);
if (ret == FF_PACKET_EMPTY) {
return 0;
} else if (ret == FF_PACKET_FAIL) {
return -1;
}
@ -134,8 +140,10 @@ static bool queue_frame(struct ff_decoder *decoder, AVFrame *frame,
|| queue_frame->frame->sample_rate != codec->sample_rate
|| queue_frame->frame->format != codec->sample_fmt);
if (queue_frame->frame != NULL)
if (queue_frame->frame != NULL) {
//FIXME: this shouldn't happen any more!
av_frame_free(&queue_frame->frame);
}
queue_frame->frame = av_frame_clone(frame);
queue_frame->clock = ff_clock_retain(decoder->clock);
@ -157,10 +165,13 @@ void *ff_audio_decoder_thread(void *opaque_audio_decoder)
struct ff_packet packet = {0};
bool frame_complete;
AVFrame *frame = av_frame_alloc();
int ret;
while (!decoder->abort) {
if (decode_frame(decoder, &packet, frame, &frame_complete)
< 0) {
ret = decode_frame(decoder, &packet, frame, &frame_complete);
if (ret == 0) {
break;
} else if (ret < 0) {
av_free_packet(&packet.base);
continue;
}
@ -184,5 +195,8 @@ void *ff_audio_decoder_thread(void *opaque_audio_decoder)
ff_clock_release(&decoder->clock);
av_frame_free(&frame);
decoder->finished = true;
return NULL;
}

View file

@ -42,6 +42,7 @@ struct ff_decoder *ff_decoder_init(AVCodecContext *codec_context,
decoder->codec->opaque = decoder;
decoder->stream = stream;
decoder->abort = false;
decoder->finished = false;
decoder->packet_queue_size = packet_queue_size;
if (!packet_queue_init(&decoder->packet_queue))
@ -179,7 +180,7 @@ void ff_decoder_refresh(void *opaque)
if (decoder && decoder->stream) {
if (decoder->frame_queue.size == 0) {
if (!decoder->eof) {
if (!decoder->eof || !decoder->finished) {
// We expected a frame, but there were none
// available
@ -280,6 +281,8 @@ void ff_decoder_refresh(void *opaque)
(int)(delay_until_next_wake * 1000
+ 0.5L));
av_frame_free(&frame->frame);
ff_circular_queue_advance_read(&decoder->frame_queue);
}
} else {

View file

@ -56,6 +56,7 @@ struct ff_decoder {
bool first_frame;
bool eof;
bool abort;
bool finished;
};
typedef struct ff_decoder ff_decoder_t;

View file

@ -51,8 +51,11 @@ static bool queue_frame(struct ff_decoder *decoder, AVFrame *frame,
|| queue_frame->frame->height != codec->height
|| queue_frame->frame->format != codec->pix_fmt);
if (queue_frame->frame != NULL)
if (queue_frame->frame != NULL) {
// This shouldn't happen any more, the frames are freed in
// ff_decoder_refresh.
av_frame_free(&queue_frame->frame);
}
queue_frame->frame = av_frame_clone(frame);
queue_frame->clock = ff_clock_retain(decoder->clock);
@ -78,8 +81,12 @@ void *ff_video_decoder_thread(void *opaque_video_decoder)
bool key_frame;
while (!decoder->abort) {
ret = packet_queue_get(&decoder->packet_queue, &packet, 1);
if (ret == FF_PACKET_FAIL) {
if (decoder->eof)
ret = packet_queue_get(&decoder->packet_queue, &packet, 0);
else
ret = packet_queue_get(&decoder->packet_queue, &packet, 1);
if (ret == FF_PACKET_EMPTY || ret == FF_PACKET_FAIL) {
// should we just use abort here?
break;
}
@ -137,5 +144,8 @@ void *ff_video_decoder_thread(void *opaque_video_decoder)
ff_clock_release(&decoder->clock);
av_frame_free(&frame);
decoder->finished = true;
return NULL;
}

View file

@ -269,7 +269,7 @@ static bool audio_frame(struct ff_frame *frame, void *opaque)
uint64_t pts;
// Media ended
if (frame == NULL)
if (frame == NULL || frame->frame == NULL)
return true;
pts = (uint64_t)(frame->pts * 1000000000.0L);