static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, int quality) { FFmpegIndexBuilderContext *context = MEM_callocN(sizeof(FFmpegIndexBuilderContext), "FFmpeg index builder context"); int num_proxy_sizes = IMB_PROXY_MAX_SLOT; int num_indexers = IMB_TC_MAX_SLOT; int i, streamcount; context->tcs_in_use = tcs_in_use; context->proxy_sizes_in_use = proxy_sizes_in_use; context->num_proxy_sizes = IMB_PROXY_MAX_SLOT; context->num_indexers = IMB_TC_MAX_SLOT; memset(context->proxy_ctx, 0, sizeof(context->proxy_ctx)); memset(context->indexer, 0, sizeof(context->indexer)); if (avformat_open_input(&context->iFormatCtx, anim->name, NULL, NULL) != 0) { MEM_freeN(context); return NULL; } if (avformat_find_stream_info(context->iFormatCtx, NULL) < 0) { avformat_close_input(&context->iFormatCtx); MEM_freeN(context); return NULL; } streamcount = anim->streamindex; /* Find the video stream */ context->videoStream = -1; for (i = 0; i < context->iFormatCtx->nb_streams; i++) if (context->iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { if (streamcount > 0) { streamcount--; continue; } context->videoStream = i; break; } if (context->videoStream == -1) { avformat_close_input(&context->iFormatCtx); MEM_freeN(context); return NULL; } context->iStream = context->iFormatCtx->streams[context->videoStream]; context->iCodecCtx = context->iStream->codec; context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id); if (context->iCodec == NULL) { avformat_close_input(&context->iFormatCtx); MEM_freeN(context); return NULL; } context->iCodecCtx->workaround_bugs = 1; if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) { avformat_close_input(&context->iFormatCtx); MEM_freeN(context); return NULL; } for (i = 0; i < num_proxy_sizes; i++) { if (proxy_sizes_in_use & proxy_sizes[i]) { context->proxy_ctx[i] = alloc_proxy_output_ffmpeg( anim, context->iStream, proxy_sizes[i], context->iCodecCtx->width * proxy_fac[i], av_get_cropped_height_from_codec( context->iCodecCtx) * proxy_fac[i], quality); if (!context->proxy_ctx[i]) { proxy_sizes_in_use &= ~proxy_sizes[i]; } } } for (i = 0; i < num_indexers; i++) { if (tcs_in_use & tc_types[i]) { char fname[FILE_MAX]; get_tc_filename(anim, tc_types[i], fname); context->indexer[i] = IMB_index_builder_create(fname); if (!context->indexer[i]) { tcs_in_use &= ~tc_types[i]; } } } return (IndexBuildContext *)context; }
static int index_rebuild_ffmpeg(struct anim * anim, IMB_Timecode_Type tcs_in_use, IMB_Proxy_Size proxy_sizes_in_use, int quality, short *stop, short *do_update, float *progress) { int i, videoStream; 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; int start_pts_set = FALSE; AVFormatContext *iFormatCtx; AVCodecContext *iCodecCtx; AVCodec *iCodec; AVStream *iStream; AVFrame* in_frame = 0; AVPacket next_packet; int streamcount; struct proxy_output_ctx * proxy_ctx[IMB_PROXY_MAX_SLOT]; anim_index_builder * indexer [IMB_TC_MAX_SLOT]; int num_proxy_sizes = IMB_PROXY_MAX_SLOT; int num_indexers = IMB_TC_MAX_SLOT; uint64_t stream_size; memset(proxy_ctx, 0, sizeof(proxy_ctx)); memset(indexer, 0, sizeof(indexer)); if(av_open_input_file(&iFormatCtx, anim->name, NULL, 0, NULL) != 0) { return 0; } if (av_find_stream_info(iFormatCtx) < 0) { av_close_input_file(iFormatCtx); return 0; } streamcount = anim->streamindex; /* Find the video stream */ videoStream = -1; for (i = 0; i < iFormatCtx->nb_streams; i++) if(iFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { if (streamcount > 0) { streamcount--; continue; } videoStream = i; break; } if (videoStream == -1) { av_close_input_file(iFormatCtx); return 0; } iStream = iFormatCtx->streams[videoStream]; iCodecCtx = iStream->codec; iCodec = avcodec_find_decoder(iCodecCtx->codec_id); if (iCodec == NULL) { av_close_input_file(iFormatCtx); return 0; } iCodecCtx->workaround_bugs = 1; if (avcodec_open(iCodecCtx, iCodec) < 0) { av_close_input_file(iFormatCtx); return 0; } in_frame = avcodec_alloc_frame(); stream_size = avio_size(iFormatCtx->pb); for (i = 0; i < num_proxy_sizes; i++) { if (proxy_sizes_in_use & proxy_sizes[i]) { proxy_ctx[i] = alloc_proxy_output_ffmpeg( anim, iStream, proxy_sizes[i], iCodecCtx->width * proxy_fac[i], iCodecCtx->height * proxy_fac[i], quality); if (!proxy_ctx[i]) { proxy_sizes_in_use &= ~proxy_sizes[i]; } } } for (i = 0; i < num_indexers; i++) { if (tcs_in_use & tc_types[i]) { char fname[FILE_MAXDIR+FILE_MAXFILE]; get_tc_filename(anim, tc_types[i], fname); indexer[i] = IMB_index_builder_create(fname); if (!indexer[i]) { tcs_in_use &= ~tc_types[i]; } } } frame_rate = av_q2d(iStream->r_frame_rate); pts_time_base = av_q2d(iStream->time_base); while(av_read_frame(iFormatCtx, &next_packet) >= 0) { int frame_finished = 0; float next_progress = ((int)floor(((double) next_packet.pos) * 100 / ((double) stream_size)+0.5)) / 100; if (*progress != next_progress) { *progress = next_progress; *do_update = 1; } if (*stop) { av_free_packet(&next_packet); break; } if (next_packet.stream_index == 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; } avcodec_decode_video2( iCodecCtx, in_frame, &frame_finished, &next_packet); } 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(iFormatCtx, in_frame); for (i = 0; i < num_proxy_sizes; i++) { add_to_proxy_output_ffmpeg( proxy_ctx[i], in_frame); } if (!start_pts_set) { start_pts = pts; start_pts_set = TRUE; } frameno = (pts - start_pts) * pts_time_base * frame_rate; /* 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 < seek_pos_pts) { s_pos = last_seek_pos; s_dts = last_seek_pos_dts; } for (i = 0; i < num_indexers; i++) { if (tcs_in_use & tc_types[i]) { IMB_index_builder_proc_frame( indexer[i], next_packet.data, next_packet.size, frameno, s_pos, s_dts, pts); } } } av_free_packet(&next_packet); } for (i = 0; i < num_indexers; i++) { if (tcs_in_use & tc_types[i]) { IMB_index_builder_finish(indexer[i], *stop); } } for (i = 0; i < num_proxy_sizes; i++) { if (proxy_sizes_in_use & proxy_sizes[i]) { free_proxy_output_ffmpeg(proxy_ctx[i], *stop); } } av_free(in_frame); return 1; }