diff options
author | Peter Schlaile <peter@schlaile.de> | 2012-09-08 01:41:38 +0400 |
---|---|---|
committer | Peter Schlaile <peter@schlaile.de> | 2012-09-08 01:41:38 +0400 |
commit | c95d4f68b863c851d0b8bddef8092fdc0932610e (patch) | |
tree | c3d0647274aa50776a32707dd50c53636ee7112b /source/blender | |
parent | d6a68ea850974a271dfd4c83a64d36ba763babf8 (diff) |
== FFMPEG ==
This fixes [#32399] VSE doesn't show last 3 frames of Quicktime movie.
Some decoders store frames internally until EOF.
So one has to feed the decoding engine with empty packets after EOF
until all frames could be extracted properly.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/imbuf/intern/anim_movie.c | 28 | ||||
-rw-r--r-- | source/blender/imbuf/intern/indexer.c | 152 |
2 files changed, 121 insertions, 59 deletions
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 9092d59c351..16dbd0823da 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -801,6 +801,34 @@ static int ffmpeg_decode_video_frame(struct anim *anim) anim->next_packet.stream_index = -1; } + if (rval == AVERROR_EOF) { + anim->next_packet.size = 0; + anim->next_packet.data = 0; + + anim->pFrameComplete = 0; + + avcodec_decode_video2( + anim->pCodecCtx, + anim->pFrame, &anim->pFrameComplete, + &anim->next_packet); + + if (anim->pFrameComplete) { + anim->next_pts = av_get_pts_from_frame( + anim->pFormatCtx, anim->pFrame); + + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + " FRAME DONE (after EOF): next_pts=%lld " + "pkt_pts=%lld, guessed_pts=%lld\n", + (anim->pFrame->pts == AV_NOPTS_VALUE) ? + -1 : (long long int)anim->pFrame->pts, + (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? + -1 : (long long int)anim->pFrame->pkt_pts, + (long long int)anim->next_pts); + rval = 0; + } + } + if (rval < 0) { anim->next_packet.stream_index = -1; diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 458f0198aed..ea493e277f3 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -723,6 +723,17 @@ typedef struct FFmpegIndexBuilderContext { IMB_Timecode_Type tcs_in_use; IMB_Proxy_Size proxy_sizes_in_use; + + unsigned long long seek_pos; + unsigned long long last_seek_pos; + unsigned long long seek_pos_dts; + unsigned long long seek_pos_pts; + unsigned long long last_seek_pos_dts; + unsigned long long start_pts; + double frame_rate; + double pts_time_base; + int frameno, frameno_gapless; + int start_pts_set; } FFmpegIndexBuilderContext; static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, @@ -839,20 +850,63 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int MEM_freeN(context); } +static void index_rebuild_ffmpeg_proc_decoded_frame( + FFmpegIndexBuilderContext *context, + AVPacket * curr_packet, + AVFrame *in_frame) +{ + int i; + unsigned long long s_pos = context->seek_pos; + unsigned long long s_dts = context->seek_pos_dts; + unsigned long long pts = av_get_pts_from_frame(context->iFormatCtx, in_frame); + + for (i = 0; i < context->num_proxy_sizes; i++) { + add_to_proxy_output_ffmpeg(context->proxy_ctx[i], in_frame); + } + + if (!context->start_pts_set) { + context->start_pts = pts; + context->start_pts_set = TRUE; + } + + context->frameno = floor((pts - context->start_pts) + * context->pts_time_base + * context->frame_rate + 0.5f); + + /* decoding starts *always* on I-Frames, + * so: P-Frames won't work, even if all the + * information is in place, when we seek + * to the I-Frame presented *after* the P-Frame, + * but located before the P-Frame within + * the stream */ + + if (pts < context->seek_pos_pts) { + s_pos = context->last_seek_pos; + s_dts = context->last_seek_pos_dts; + } + + for (i = 0; i < context->num_indexers; i++) { + if (context->tcs_in_use & tc_types[i]) { + int tc_frameno = context->frameno; + + if (tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS) + tc_frameno = context->frameno_gapless; + + IMB_index_builder_proc_frame( + context->indexer[i], + curr_packet->data, + curr_packet->size, + tc_frameno, + s_pos, s_dts, pts); + } + } + + context->frameno_gapless++; +} + static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, short *stop, short *do_update, float *progress) { - int i; - unsigned long long seek_pos = 0; - unsigned long long last_seek_pos = 0; - unsigned long long seek_pos_dts = 0; - unsigned long long seek_pos_pts = 0; - unsigned long long last_seek_pos_dts = 0; - unsigned long long start_pts = 0; - double frame_rate; - double pts_time_base; - int frameno = 0, frameno_gapless = 0; - int start_pts_set = FALSE; AVFrame *in_frame = 0; AVPacket next_packet; uint64_t stream_size; @@ -861,8 +915,8 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, stream_size = avio_size(context->iFormatCtx->pb); - frame_rate = av_q2d(context->iStream->r_frame_rate); - pts_time_base = av_q2d(context->iStream->time_base); + context->frame_rate = av_q2d(context->iStream->r_frame_rate); + context->pts_time_base = av_q2d(context->iStream->time_base); while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) { int frame_finished = 0; @@ -881,11 +935,11 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, if (next_packet.stream_index == context->videoStream) { if (next_packet.flags & AV_PKT_FLAG_KEY) { - last_seek_pos = seek_pos; - last_seek_pos_dts = seek_pos_dts; - seek_pos = next_packet.pos; - seek_pos_dts = next_packet.dts; - seek_pos_pts = next_packet.pts; + context->last_seek_pos = context->seek_pos; + context->last_seek_pos_dts = context->seek_pos_dts; + context->seek_pos = next_packet.pos; + context->seek_pos_dts = next_packet.dts; + context->seek_pos_pts = next_packet.pts; } avcodec_decode_video2( @@ -894,54 +948,34 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, } if (frame_finished) { - unsigned long long s_pos = seek_pos; - unsigned long long s_dts = seek_pos_dts; - unsigned long long pts = av_get_pts_from_frame(context->iFormatCtx, in_frame); - - for (i = 0; i < context->num_proxy_sizes; i++) { - add_to_proxy_output_ffmpeg( - context->proxy_ctx[i], in_frame); - } - - if (!start_pts_set) { - start_pts = pts; - start_pts_set = TRUE; - } + index_rebuild_ffmpeg_proc_decoded_frame( + context, &next_packet, in_frame); + } + av_free_packet(&next_packet); + } - frameno = floor((pts - start_pts) * - pts_time_base * frame_rate + 0.5f); + /* process pictures still stuck in decoder engine after EOF + according to ffmpeg docs using 0-size packets. - /* decoding starts *always* on I-Frames, - * so: P-Frames won't work, even if all the - * information is in place, when we seek - * to the I-Frame presented *after* the P-Frame, - * but located before the P-Frame within - * the stream */ + At least, if we haven't already stopped... */ + if (!*stop) { + int frame_finished; - if (pts < seek_pos_pts) { - s_pos = last_seek_pos; - s_dts = last_seek_pos_dts; - } + next_packet.size = 0; + next_packet.data = 0; - for (i = 0; i < context->num_indexers; i++) { - if (context->tcs_in_use & tc_types[i]) { - int tc_frameno = frameno; + do { + frame_finished = 0; - if (tc_types[i] == IMB_TC_RECORD_RUN_NO_GAPS) - tc_frameno = frameno_gapless; + avcodec_decode_video2( + context->iCodecCtx, in_frame, &frame_finished, + &next_packet); - IMB_index_builder_proc_frame( - context->indexer[i], - next_packet.data, - next_packet.size, - tc_frameno, - s_pos, s_dts, pts); - } + if (frame_finished) { + index_rebuild_ffmpeg_proc_decoded_frame( + context, &next_packet, in_frame); } - - frameno_gapless++; - } - av_free_packet(&next_packet); + } while (frame_finished); } av_free(in_frame); |