diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/writeffmpeg.c | 346 | ||||
-rw-r--r-- | source/blender/imbuf/CMakeLists.txt | 6 | ||||
-rw-r--r-- | source/blender/imbuf/intern/IMB_anim.h | 2 | ||||
-rw-r--r-- | source/blender/imbuf/intern/anim_movie.c | 161 | ||||
-rw-r--r-- | source/blender/imbuf/intern/indexer.c | 241 | ||||
-rw-r--r-- | source/blender/imbuf/intern/util.c | 16 |
7 files changed, 372 insertions, 404 deletions
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 0944bbed2f3..acd9d54cfbe 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -592,10 +592,6 @@ if(WITH_CODEC_FFMPEG) ${FFMPEG_LIBRARIES} ) add_definitions(-DWITH_FFMPEG) - - remove_strict_c_flags_file( - intern/writeffmpeg.c - ) endif() if(WITH_PYTHON) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index e5550cee124..5d03278f3c1 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -56,6 +56,7 @@ # include <libavcodec/avcodec.h> # include <libavformat/avformat.h> # include <libavutil/imgutils.h> +# include <libavutil/opt.h> # include <libavutil/rational.h> # include <libavutil/samplefmt.h> # include <libswscale/swscale.h> @@ -80,6 +81,8 @@ typedef struct FFMpegContext { int ffmpeg_preset; /* see eFFMpegPreset */ AVFormatContext *outfile; + AVCodecContext *video_codec; + AVCodecContext *audio_codec; AVStream *video_stream; AVStream *audio_stream; AVFrame *current_frame; /* Image frame in output pixel format. */ @@ -91,10 +94,6 @@ typedef struct FFMpegContext { uint8_t *audio_input_buffer; uint8_t *audio_deinterleave_buffer; int audio_input_samples; -# ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - uint8_t *audio_output_buffer; - int audio_outbuf_size; -# endif double audio_time; bool audio_deinterleave; int audio_sample_size; @@ -141,33 +140,22 @@ static int request_float_audio_buffer(int codec_id) } # ifdef WITH_AUDASPACE + static int write_audio_frame(FFMpegContext *context) { - AVCodecContext *c = NULL; - AVPacket pkt; AVFrame *frame = NULL; - int got_output = 0; - - c = context->audio_stream->codec; - - av_init_packet(&pkt); - pkt.size = 0; - pkt.data = NULL; + AVCodecContext *c = context->audio_codec; AUD_Device_read( context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples); context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate; -# ifdef FFMPEG_HAVE_ENCODE_AUDIO2 frame = av_frame_alloc(); - av_frame_unref(frame); frame->pts = context->audio_time / av_q2d(c->time_base); frame->nb_samples = context->audio_input_samples; frame->format = c->sample_fmt; frame->channels = c->channels; -# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT frame->channel_layout = c->channel_layout; -# endif if (context->audio_deinterleave) { int channel, i; @@ -195,61 +183,49 @@ static int write_audio_frame(FFMpegContext *context) context->audio_input_samples * c->channels * context->audio_sample_size, 1); - if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) { - // XXX error("Error writing audio packet"); - return -1; - } + int success = 0; - if (!got_output) { - av_frame_free(&frame); - return 0; + int ret = avcodec_send_frame(c, frame); + if (ret < 0) { + /* Can't send frame to encoder. This shouldn't happen. */ + fprintf(stderr, "Can't send audio frame: %s\n", av_err2str(ret)); + success = -1; } -# else - pkt.size = avcodec_encode_audio(c, - context->audio_output_buffer, - context->audio_outbuf_size, - (short *)context->audio_input_buffer); - if (pkt.size < 0) { - // XXX error("Error writing audio packet"); - return -1; - } + AVPacket *pkt = av_packet_alloc(); - pkt.data = context->audio_output_buffer; - got_output = 1; -# endif + while (ret >= 0) { - if (got_output) { - if (pkt.pts != AV_NOPTS_VALUE) { - pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base); + ret = avcodec_receive_packet(c, pkt); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; } - if (pkt.dts != AV_NOPTS_VALUE) { - pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base); + if (ret < 0) { + fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret)); + success = -1; } - if (pkt.duration > 0) { - pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base); + + av_packet_rescale_ts(pkt, c->time_base, context->audio_stream->time_base); + if (pkt->duration > 0) { + pkt->duration = av_rescale_q(pkt->duration, c->time_base, context->audio_stream->time_base); } - pkt.stream_index = context->audio_stream->index; + pkt->stream_index = context->audio_stream->index; - pkt.flags |= AV_PKT_FLAG_KEY; + pkt->flags |= AV_PKT_FLAG_KEY; - if (av_interleaved_write_frame(context->outfile, &pkt) != 0) { - fprintf(stderr, "Error writing audio packet!\n"); - if (frame) { - av_frame_free(&frame); - } - return -1; + int write_ret = av_interleaved_write_frame(context->outfile, pkt); + if (write_ret != 0) { + fprintf(stderr, "Error writing audio packet: %s\n", av_err2str(write_ret)); + success = -1; + break; } - - av_free_packet(&pkt); } - if (frame) { - av_frame_free(&frame); - } + av_packet_free(&pkt); + av_frame_free(&frame); - return 0; + return success; } # endif /* #ifdef WITH_AUDASPACE */ @@ -265,14 +241,15 @@ static AVFrame *alloc_picture(int pix_fmt, int width, int height) if (!f) { return NULL; } - size = avpicture_get_size(pix_fmt, width, height); + size = av_image_get_buffer_size(pix_fmt, width, height, 1); /* allocate the actual picture buffer */ buf = MEM_mallocN(size, "AVFrame buffer"); if (!buf) { free(f); return NULL; } - avpicture_fill((AVPicture *)f, buf, pix_fmt, width, height); + + av_image_fill_arrays(f->data, f->linesize, buf, pix_fmt, width, height, 1); f->format = pix_fmt; f->width = width; f->height = height; @@ -342,58 +319,57 @@ static const char **get_file_extensions(int format) } /* Write a frame to the output file */ -static int write_video_frame( - FFMpegContext *context, const RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) +static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, ReportList *reports) { - int got_output; int ret, success = 1; - AVCodecContext *c = context->video_stream->codec; - AVPacket packet = {0}; + AVPacket *packet = av_packet_alloc(); - av_init_packet(&packet); + AVCodecContext *c = context->video_codec; frame->pts = cfra; - ret = avcodec_encode_video2(c, &packet, frame, &got_output); + ret = avcodec_send_frame(c, frame); + if (ret < 0) { + /* Can't send frame to encoder. This shouldn't happen. */ + fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret)); + success = -1; + } - if (ret >= 0 && got_output) { - if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base); - PRINT("Video Frame PTS: %d\n", (int)packet.pts); - } - else { - PRINT("Video Frame PTS: not set\n"); - } - if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base); - PRINT("Video Frame DTS: %d\n", (int)packet.dts); + while (ret >= 0) { + ret = avcodec_receive_packet(c, packet); + + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + /* No more packets available. */ + break; } - else { - PRINT("Video Frame DTS: not set\n"); + if (ret < 0) { + fprintf(stderr, "Error encoding frame: %s\n", av_err2str(ret)); + break; } - packet.stream_index = context->video_stream->index; - ret = av_interleaved_write_frame(context->outfile, &packet); - success = (ret == 0); - } - else if (ret < 0) { - success = 0; + packet->stream_index = context->video_stream->index; + av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); + if (av_interleaved_write_frame(context->outfile, packet) != 0) { + success = -1; + break; + } } if (!success) { BKE_report(reports, RPT_ERROR, "Error writing frame"); + PRINT("Error writing frame: %s\n", av_err2str(ret)); } + av_packet_free(&packet); + return success; } /* read and encode a frame of audio from the buffer */ -static AVFrame *generate_video_frame(FFMpegContext *context, - const uint8_t *pixels, - ReportList *reports) +static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels) { - AVCodecContext *c = context->video_stream->codec; - int height = c->height; + AVCodecParameters *codec = context->video_stream->codecpar; + int height = codec->height; AVFrame *rgb_frame; if (context->img_convert_frame != NULL) { @@ -438,7 +414,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context, (const uint8_t *const *)rgb_frame->data, rgb_frame->linesize, 0, - c->height, + codec->height, context->current_frame->data, context->current_frame->linesize); } @@ -446,9 +422,7 @@ static AVFrame *generate_video_frame(FFMpegContext *context, return context->current_frame; } -static void set_ffmpeg_property_option(AVCodecContext *c, - IDProperty *prop, - AVDictionary **dictionary) +static void set_ffmpeg_property_option(IDProperty *prop, AVDictionary **dictionary) { char name[128]; char *param; @@ -536,7 +510,7 @@ static void set_ffmpeg_properties(RenderData *rd, for (curr = prop->data.group.first; curr; curr = curr->next) { if (ffmpeg_proprty_valid(c, prop_name, curr)) { - set_ffmpeg_property_option(c, curr, dictionary); + set_ffmpeg_property_option(curr, dictionary); } } } @@ -553,7 +527,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, int error_size) { AVStream *st; - AVCodecContext *c; AVCodec *codec; AVDictionary *opts = NULL; @@ -567,7 +540,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context, /* Set up the codec context */ - c = st->codec; + context->video_codec = avcodec_alloc_context3(NULL); + AVCodecContext *c = context->video_codec; c->codec_id = codec_id; c->codec_type = AVMEDIA_TYPE_VIDEO; @@ -650,11 +624,9 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } - /* Deprecated and not doing anything since July 2015, deleted in recent ffmpeg */ - // c->me_method = ME_EPZS; - codec = avcodec_find_encoder(c->codec_id); if (!codec) { + avcodec_free_context(&c); return NULL; } @@ -714,7 +686,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context, if ((of->oformat->flags & AVFMT_GLOBALHEADER)) { PRINT("Using global header\n"); - c->flags |= CODEC_FLAG_GLOBAL_HEADER; + c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } /* xasp & yasp got float lately... */ @@ -742,6 +714,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context, if (avcodec_open2(c, codec, &opts) < 0) { BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); + avcodec_free_context(&c); return NULL; } av_dict_free(&opts); @@ -769,6 +742,8 @@ static AVStream *alloc_video_stream(FFMpegContext *context, NULL); } + avcodec_parameters_from_context(st->codecpar, c); + return st; } @@ -780,7 +755,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, int error_size) { AVStream *st; - AVCodecContext *c; AVCodec *codec; AVDictionary *opts = NULL; @@ -792,7 +766,8 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, } st->id = 1; - c = st->codec; + context->audio_codec = avcodec_alloc_context3(NULL); + AVCodecContext *c = context->audio_codec; c->thread_count = BLI_system_thread_count(); c->thread_type = FF_THREAD_SLICE; @@ -804,7 +779,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->sample_fmt = AV_SAMPLE_FMT_S16; c->channels = rd->ffcodecdata.audio_channels; -# ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT switch (rd->ffcodecdata.audio_channels) { case FFM_CHANNELS_MONO: c->channel_layout = AV_CH_LAYOUT_MONO; @@ -822,7 +796,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->channel_layout = AV_CH_LAYOUT_7POINT1; break; } -# endif if (request_float_audio_buffer(codec_id)) { /* mainly for AAC codec which is experimental */ @@ -833,6 +806,7 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, codec = avcodec_find_encoder(c->codec_id); if (!codec) { // XXX error("Couldn't find a valid audio codec"); + avcodec_free_context(&c); return NULL; } @@ -844,13 +818,13 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, * Float samples in particular are not always supported. */ const enum AVSampleFormat *p = codec->sample_fmts; for (; *p != -1; p++) { - if (*p == st->codec->sample_fmt) { + if (*p == c->sample_fmt) { break; } } if (*p == -1) { /* sample format incompatible with codec. Defaulting to a format known to work */ - st->codec->sample_fmt = codec->sample_fmts[0]; + c->sample_fmt = codec->sample_fmts[0]; } } @@ -859,18 +833,18 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, int best = 0; int best_dist = INT_MAX; for (; *p; p++) { - int dist = abs(st->codec->sample_rate - *p); + int dist = abs(c->sample_rate - *p); if (dist < best_dist) { best_dist = dist; best = *p; } } /* best is the closest supported sample rate (same as selected if best_dist == 0) */ - st->codec->sample_rate = best; + c->sample_rate = best; } if (of->oformat->flags & AVFMT_GLOBALHEADER) { - c->flags |= CODEC_FLAG_GLOBAL_HEADER; + c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } set_ffmpeg_properties(rd, c, "audio", &opts); @@ -879,32 +853,25 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, // XXX error("Couldn't initialize audio codec"); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); av_dict_free(&opts); + avcodec_free_context(&c); return NULL; } av_dict_free(&opts); /* need to prevent floating point exception when using vorbis audio codec, * initialize this value in the same way as it's done in FFmpeg itself (sergey) */ - st->codec->time_base.num = 1; - st->codec->time_base.den = st->codec->sample_rate; - -# ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - context->audio_outbuf_size = FF_MIN_BUFFER_SIZE; -# endif + c->time_base.num = 1; + c->time_base.den = c->sample_rate; if (c->frame_size == 0) { /* Used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD)) * not sure if that is needed anymore, so let's try out if there are any * complaints regarding some FFmpeg versions users might have. */ - context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels; + context->audio_input_samples = AV_INPUT_BUFFER_MIN_SIZE * 8 / c->bits_per_coded_sample / + c->channels; } else { context->audio_input_samples = c->frame_size; -# ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size) { - context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4; - } -# endif } context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt); @@ -913,10 +880,6 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, context->audio_input_buffer = (uint8_t *)av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size); -# ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - context->audio_output_buffer = (uint8_t *)av_malloc(context->audio_outbuf_size); -# endif - if (context->audio_deinterleave) { context->audio_deinterleave_buffer = (uint8_t *)av_malloc( context->audio_input_samples * c->channels * context->audio_sample_size); @@ -924,6 +887,8 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, context->audio_time = 0.0f; + avcodec_parameters_from_context(st->codecpar, c); + return st; } /* essential functions -- start, append, end */ @@ -949,7 +914,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va static void ffmpeg_add_metadata_callback(void *data, const char *propname, char *propvalue, - int len) + int UNUSED(len)) { AVDictionary **metadata = (AVDictionary **)data; av_dict_set(metadata, propname, propvalue, 0); @@ -1040,7 +1005,7 @@ static int start_ffmpeg_impl(FFMpegContext *context, fmt->audio_codec = context->ffmpeg_audio_codec; - BLI_strncpy(of->filename, name, sizeof(of->filename)); + of->url = av_strdup(name); /* set the codec to the user's selection */ switch (context->ffmpeg_type) { case FFMPEG_AVI: @@ -1105,9 +1070,11 @@ static int start_ffmpeg_impl(FFMpegContext *context, if (!context->video_stream) { if (error[0]) { BKE_report(reports, RPT_ERROR, error); + PRINT("Video stream error: %s\n", error); } else { BKE_report(reports, RPT_ERROR, "Error initializing video stream"); + PRINT("Error initializing video stream"); } goto fail; } @@ -1119,9 +1086,11 @@ static int start_ffmpeg_impl(FFMpegContext *context, if (!context->audio_stream) { if (error[0]) { BKE_report(reports, RPT_ERROR, error); + PRINT("Audio stream error: %s\n", error); } else { BKE_report(reports, RPT_ERROR, "Error initializing audio stream"); + PRINT("Error initializing audio stream"); } goto fail; } @@ -1129,6 +1098,7 @@ static int start_ffmpeg_impl(FFMpegContext *context, if (!(fmt->flags & AVFMT_NOFILE)) { if (avio_open(&of->pb, name, AVIO_FLAG_WRITE) < 0) { BKE_report(reports, RPT_ERROR, "Could not open file for writing"); + PRINT("Could not open file for writing\n"); goto fail; } } @@ -1138,10 +1108,12 @@ static int start_ffmpeg_impl(FFMpegContext *context, &of->metadata, context->stamp_data, ffmpeg_add_metadata_callback, false); } - if (avformat_write_header(of, NULL) < 0) { + int ret = avformat_write_header(of, NULL); + if (ret < 0) { BKE_report(reports, RPT_ERROR, "Could not initialize streams, probably unsupported codec combination"); + PRINT("Could not write media header: %s\n", av_err2str(ret)); goto fail; } @@ -1156,13 +1128,11 @@ fail: avio_close(of->pb); } - if (context->video_stream && context->video_stream->codec) { - avcodec_close(context->video_stream->codec); + if (context->video_stream) { context->video_stream = NULL; } - if (context->audio_stream && context->audio_stream->codec) { - avcodec_close(context->audio_stream->codec); + if (context->audio_stream) { context->audio_stream = NULL; } @@ -1190,46 +1160,36 @@ fail: */ static void flush_ffmpeg(FFMpegContext *context) { - int ret = 0; + AVCodecContext *c = context->video_codec; + AVPacket *packet = av_packet_alloc(); - AVCodecContext *c = context->video_stream->codec; - /* get the delayed frames */ - while (1) { - int got_output; - AVPacket packet = {0}; - av_init_packet(&packet); + avcodec_send_frame(c, NULL); - ret = avcodec_encode_video2(c, &packet, NULL, &got_output); - if (ret < 0) { - fprintf(stderr, "Error encoding delayed frame %d\n", ret); + /* Get the packets frames. */ + int ret = 1; + while (ret >= 0) { + ret = avcodec_receive_packet(c, packet); + + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + /* No more packets to flush. */ break; } - if (!got_output) { + if (ret < 0) { + fprintf(stderr, "Error encoding delayed frame: %s\n", av_err2str(ret)); break; } - if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base); - PRINT("Video Frame PTS: %d\n", (int)packet.pts); - } - else { - PRINT("Video Frame PTS: not set\n"); - } - if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base); - PRINT("Video Frame DTS: %d\n", (int)packet.dts); - } - else { - PRINT("Video Frame DTS: not set\n"); - } - packet.stream_index = context->video_stream->index; - ret = av_interleaved_write_frame(context->outfile, &packet); - if (ret != 0) { - fprintf(stderr, "Error writing delayed frame %d\n", ret); + packet->stream_index = context->video_stream->index; + av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base); + + int write_ret = av_interleaved_write_frame(context->outfile, packet); + if (write_ret != 0) { + fprintf(stderr, "Error writing delayed frame: %s\n", av_err2str(write_ret)); break; } } - avcodec_flush_buffers(context->video_stream->codec); + + av_packet_free(&packet); } /* ********************************************************************** @@ -1327,7 +1287,8 @@ int BKE_ffmpeg_start(void *context_v, success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports); # ifdef WITH_AUDASPACE if (context->audio_stream) { - AVCodecContext *c = context->audio_stream->codec; + AVCodecContext *c = context->audio_codec; + AUD_DeviceSpecs specs; specs.channels = c->channels; @@ -1354,10 +1315,6 @@ int BKE_ffmpeg_start(void *context_v, specs.rate = rd->ffcodecdata.audio_mixrate; context->audio_mixdown_device = BKE_sound_mixdown( scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume); -# ifdef FFMPEG_CODEC_TIME_BASE - c->time_base.den = specs.rate; - c->time_base.num = 1; -# endif } # endif return success; @@ -1398,8 +1355,8 @@ int BKE_ffmpeg_append(void *context_v, // write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base)); if (context->video_stream) { - avframe = generate_video_frame(context, (unsigned char *)pixels, reports); - success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports)); + avframe = generate_video_frame(context, (unsigned char *)pixels); + success = (avframe && write_video_frame(context, frame - start_frame, avframe, reports)); if (context->ffmpeg_autosplit) { if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) { @@ -1430,7 +1387,7 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) } # endif - if (context->video_stream && context->video_stream->codec) { + if (context->video_stream) { PRINT("Flushing delayed frames...\n"); flush_ffmpeg(context); } @@ -1441,14 +1398,12 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) /* Close the video codec */ - if (context->video_stream != NULL && context->video_stream->codec != NULL) { - avcodec_close(context->video_stream->codec); + if (context->video_stream != NULL) { PRINT("zero video stream %p\n", context->video_stream); context->video_stream = NULL; } - if (context->audio_stream != NULL && context->audio_stream->codec != NULL) { - avcodec_close(context->audio_stream->codec); + if (context->audio_stream != NULL) { context->audio_stream = NULL; } @@ -1467,6 +1422,16 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) avio_close(context->outfile->pb); } } + + if (context->video_codec != NULL) { + avcodec_free_context(&context->video_codec); + context->video_codec = NULL; + } + if (context->audio_codec != NULL) { + avcodec_free_context(&context->audio_codec); + context->audio_codec = NULL; + } + if (context->outfile != NULL) { avformat_free_context(context->outfile); context->outfile = NULL; @@ -1475,12 +1440,6 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) av_free(context->audio_input_buffer); context->audio_input_buffer = NULL; } -# ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (context->audio_output_buffer != NULL) { - av_free(context->audio_output_buffer); - context->audio_output_buffer = NULL; - } -# endif if (context->audio_deinterleave_buffer != NULL) { av_free(context->audio_deinterleave_buffer); @@ -1560,12 +1519,12 @@ static IDProperty *BKE_ffmpeg_property_add(RenderData *rd, switch (o->type) { case AV_OPT_TYPE_INT: case AV_OPT_TYPE_INT64: - val.i = FFMPEG_DEF_OPT_VAL_INT(o); + val.i = o->default_val.i64; idp_type = IDP_INT; break; case AV_OPT_TYPE_DOUBLE: case AV_OPT_TYPE_FLOAT: - val.f = FFMPEG_DEF_OPT_VAL_DOUBLE(o); + val.f = o->default_val.dbl; idp_type = IDP_FLOAT; break; case AV_OPT_TYPE_STRING: @@ -1707,16 +1666,9 @@ static void ffmpeg_set_expert_options(RenderData *rd) BKE_ffmpeg_property_add_string(rd, "video", "trellis:0"); BKE_ffmpeg_property_add_string(rd, "video", "weightb:1"); -# ifdef FFMPEG_HAVE_DEPRECATED_FLAGS2 - BKE_ffmpeg_property_add_string(rd, "video", "flags2:dct8x8"); - BKE_ffmpeg_property_add_string(rd, "video", "directpred:3"); - BKE_ffmpeg_property_add_string(rd, "video", "flags2:fastpskip"); - BKE_ffmpeg_property_add_string(rd, "video", "flags2:wpred"); -# else BKE_ffmpeg_property_add_string(rd, "video", "8x8dct:1"); BKE_ffmpeg_property_add_string(rd, "video", "fast-pskip:1"); BKE_ffmpeg_property_add_string(rd, "video", "wpredp:2"); -# endif } else if (codec_id == AV_CODEC_ID_DNXHD) { if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { @@ -1871,14 +1823,12 @@ bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd) { int codec = rd->ffcodecdata.codec; -# ifdef FFMPEG_FFV1_ALPHA_SUPPORTED - /* Visual Studio 2019 doesn't like #ifdef within ELEM(). */ - if (codec == AV_CODEC_ID_FFV1) { - return true; - } -# endif - - return ELEM(codec, AV_CODEC_ID_QTRLE, AV_CODEC_ID_PNG, AV_CODEC_ID_VP9, AV_CODEC_ID_HUFFYUV); + return ELEM(codec, + AV_CODEC_ID_FFV1, + AV_CODEC_ID_QTRLE, + AV_CODEC_ID_PNG, + AV_CODEC_ID_VP9, + AV_CODEC_ID_HUFFYUV); } void *BKE_ffmpeg_context_create(void) diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index 7ce795280a3..be0e364c85f 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -166,12 +166,6 @@ if(WITH_CODEC_FFMPEG) ${OPENJPEG_LIBRARIES} ) add_definitions(-DWITH_FFMPEG) - - remove_strict_c_flags_file( - intern/anim_movie.c - intern/indexer.c - intern/util.c - ) endif() if(WITH_IMAGE_DDS) diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index 1239d3881de..7d7864306a1 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -135,7 +135,7 @@ struct anim { struct ImBuf *last_frame; int64_t last_pts; int64_t next_pts; - AVPacket next_packet; + AVPacket *next_packet; #endif char index_dir[768]; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 432b62d172a..cdf6ca5c181 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -79,6 +79,7 @@ # include <libavcodec/avcodec.h> # include <libavformat/avformat.h> +# include <libavutil/imgutils.h> # include <libavutil/rational.h> # include <libswscale/swscale.h> @@ -519,12 +520,10 @@ static int startffmpeg(struct anim *anim) double frs_den; int streamcount; -# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT /* The following for color space determination */ int srcRange, dstRange, brightness, contrast, saturation; int *table; const int *inv_table; -# endif if (anim == NULL) { return (-1); @@ -547,7 +546,7 @@ static int startffmpeg(struct anim *anim) video_stream_index = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { - if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (streamcount > 0) { streamcount--; continue; @@ -563,16 +562,17 @@ static int startffmpeg(struct anim *anim) } video_stream = pFormatCtx->streams[video_stream_index]; - pCodecCtx = video_stream->codec; /* Find the decoder for the video stream */ - pCodec = avcodec_find_decoder(pCodecCtx->codec_id); + pCodec = avcodec_find_decoder(video_stream->codecpar->codec_id); if (pCodec == NULL) { avformat_close_input(&pFormatCtx); return -1; } - pCodecCtx->workaround_bugs = 1; + pCodecCtx = avcodec_alloc_context3(NULL); + avcodec_parameters_to_context(pCodecCtx, video_stream->codecpar); + pCodecCtx->workaround_bugs = FF_BUG_AUTODETECT; if (pCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { pCodecCtx->thread_count = 0; @@ -593,7 +593,7 @@ static int startffmpeg(struct anim *anim) return -1; } if (pCodecCtx->pix_fmt == AV_PIX_FMT_NONE) { - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&pFormatCtx); return -1; } @@ -639,7 +639,7 @@ static int startffmpeg(struct anim *anim) anim->params = 0; anim->x = pCodecCtx->width; - anim->y = av_get_cropped_height_from_codec(pCodecCtx); + anim->y = pCodecCtx->height; anim->pFormatCtx = pFormatCtx; anim->pCodecCtx = pCodecCtx; @@ -654,7 +654,8 @@ static int startffmpeg(struct anim *anim) anim->last_frame = 0; anim->last_pts = -1; anim->next_pts = -1; - anim->next_packet.stream_index = -1; + anim->next_packet = av_packet_alloc(); + anim->next_packet->stream_index = -1; anim->pFrame = av_frame_alloc(); anim->pFrameComplete = false; @@ -668,8 +669,9 @@ static int startffmpeg(struct anim *anim) if (av_frame_get_buffer(anim->pFrameRGB, 32) < 0) { fprintf(stderr, "Could not allocate frame data.\n"); - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->next_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -678,10 +680,11 @@ static int startffmpeg(struct anim *anim) } } - if (avpicture_get_size(AV_PIX_FMT_RGBA, anim->x, anim->y) != anim->x * anim->y * 4) { + if (av_image_get_buffer_size(AV_PIX_FMT_RGBA, anim->x, anim->y, 1) != anim->x * anim->y * 4) { fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n"); - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->next_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -690,14 +693,17 @@ static int startffmpeg(struct anim *anim) } if (anim->ib_flags & IB_animdeinterlace) { - avpicture_fill((AVPicture *)anim->pFrameDeinterlaced, - MEM_callocN(avpicture_get_size(anim->pCodecCtx->pix_fmt, - anim->pCodecCtx->width, - anim->pCodecCtx->height), - "ffmpeg deinterlace"), - anim->pCodecCtx->pix_fmt, - anim->pCodecCtx->width, - anim->pCodecCtx->height); + av_image_fill_arrays(anim->pFrameDeinterlaced->data, + anim->pFrameDeinterlaced->linesize, + MEM_callocN(av_image_get_buffer_size(anim->pCodecCtx->pix_fmt, + anim->pCodecCtx->width, + anim->pCodecCtx->height, + 1), + "ffmpeg deinterlace"), + anim->pCodecCtx->pix_fmt, + anim->pCodecCtx->width, + anim->pCodecCtx->height, + 1); } if (pCodecCtx->has_b_frames) { @@ -720,8 +726,9 @@ static int startffmpeg(struct anim *anim) if (!anim->img_convert_ctx) { fprintf(stderr, "Can't transform color space??? Bailing out...\n"); - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->next_packet); av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); av_frame_free(&anim->pFrame); @@ -729,7 +736,6 @@ static int startffmpeg(struct anim *anim) return -1; } -# ifdef FFMPEG_SWSCALE_COLOR_SPACE_SUPPORT /* Try do detect if input has 0-255 YCbCR range (JFIF Jpeg MotionJpeg) */ if (!sws_getColorspaceDetails(anim->img_convert_ctx, (int **)&inv_table, @@ -756,7 +762,6 @@ static int startffmpeg(struct anim *anim) else { fprintf(stderr, "Warning: Could not set libswscale colorspace details.\n"); } -# endif return 0; } @@ -795,11 +800,11 @@ static void ffmpeg_postprocess(struct anim *anim) input->data[3]); if (anim->ib_flags & IB_animdeinterlace) { - if (avpicture_deinterlace((AVPicture *)anim->pFrameDeinterlaced, - (const AVPicture *)anim->pFrame, - anim->pCodecCtx->pix_fmt, - anim->pCodecCtx->width, - anim->pCodecCtx->height) < 0) { + if (av_image_deinterlace(anim->pFrameDeinterlaced, + anim->pFrame, + anim->pCodecCtx->pix_fmt, + anim->pCodecCtx->width, + anim->pCodecCtx->height) < 0) { filter_y = true; } else { @@ -808,11 +813,13 @@ static void ffmpeg_postprocess(struct anim *anim) } if (!need_aligned_ffmpeg_buffer(anim)) { - avpicture_fill((AVPicture *)anim->pFrameRGB, - (unsigned char *)ibuf->rect, - AV_PIX_FMT_RGBA, - anim->x, - anim->y); + av_image_fill_arrays(anim->pFrameRGB->data, + anim->pFrameRGB->linesize, + (unsigned char *)ibuf->rect, + AV_PIX_FMT_RGBA, + anim->x, + anim->y, + 1); } # if defined(__x86_64__) || defined(_M_X64) @@ -903,82 +910,70 @@ static int ffmpeg_decode_video_frame(struct anim *anim) av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n"); - if (anim->next_packet.stream_index == anim->videoStream) { - av_free_packet(&anim->next_packet); - anim->next_packet.stream_index = -1; + if (anim->next_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->next_packet); + anim->next_packet->stream_index = -1; } - while ((rval = av_read_frame(anim->pFormatCtx, &anim->next_packet)) >= 0) { + while ((rval = av_read_frame(anim->pFormatCtx, anim->next_packet)) >= 0) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n", - (anim->next_packet.stream_index == anim->videoStream) ? "->" : " ", - anim->next_packet.stream_index, + (anim->next_packet->stream_index == anim->videoStream) ? "->" : " ", + anim->next_packet->stream_index, anim->videoStream, - (anim->next_packet.dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.dts, - (anim->next_packet.pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet.pts, - (anim->next_packet.flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); - if (anim->next_packet.stream_index == anim->videoStream) { + (anim->next_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->dts, + (anim->next_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->pts, + (anim->next_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); + if (anim->next_packet->stream_index == anim->videoStream) { anim->pFrameComplete = 0; - avcodec_decode_video2( - anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet); + avcodec_send_packet(anim->pCodecCtx, anim->next_packet); + anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; if (anim->pFrameComplete) { anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame); av_log(anim->pFormatCtx, AV_LOG_DEBUG, - " FRAME DONE: next_pts=%" PRId64 " pkt_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", + " FRAME DONE: next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts, (int64_t)anim->next_pts); break; } } - av_free_packet(&anim->next_packet); - anim->next_packet.stream_index = -1; + av_packet_unref(anim->next_packet); + anim->next_packet->stream_index = -1; } if (rval == AVERROR_EOF) { - /* this sets size and data fields to zero, - * which is necessary to decode the remaining data - * in the decoder engine after EOF. It also prevents a memory - * leak, since av_read_frame spills out a full size packet even - * on EOF... (and: it's safe to call on NULL packets) */ - - av_free_packet(&anim->next_packet); - - anim->next_packet.size = 0; - anim->next_packet.data = 0; - + /* Flush any remaining frames out of the decoder. */ anim->pFrameComplete = 0; - avcodec_decode_video2( - anim->pCodecCtx, anim->pFrame, &anim->pFrameComplete, &anim->next_packet); + avcodec_send_packet(anim->pCodecCtx, NULL); + anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; 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=%" PRId64 " pkt_pts=%" PRId64 - ", guessed_pts=%" PRId64 "\n", + " FRAME DONE (after EOF): next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n", (anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts, - (anim->pFrame->pkt_pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pkt_pts, (int64_t)anim->next_pts); rval = 0; } } if (rval < 0) { - anim->next_packet.stream_index = -1; + av_packet_unref(anim->next_packet); + anim->next_packet->stream_index = -1; av_log(anim->pFormatCtx, AV_LOG_ERROR, " DECODE READ FAILED: av_read_frame() " - "returned error: %d\n", - rval); + "returned error: %s\n", + av_err2str(rval)); } return (rval >= 0); @@ -1154,7 +1149,7 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_p /* Read first video stream packet. */ AVPacket read_packet = {0}; while (av_read_frame(anim->pFormatCtx, &read_packet) >= 0) { - if (anim->next_packet.stream_index == anim->videoStream) { + if (anim->next_packet->stream_index == anim->videoStream) { break; } } @@ -1246,9 +1241,9 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_ anim->next_pts = -1; - if (anim->next_packet.stream_index == anim->videoStream) { - av_free_packet(&anim->next_packet); - anim->next_packet.stream_index = -1; + if (anim->next_packet->stream_index == anim->videoStream) { + av_packet_unref(anim->next_packet); + anim->next_packet->stream_index = -1; } /* memset(anim->pFrame, ...) ?? */ @@ -1351,32 +1346,30 @@ static void free_anim_ffmpeg(struct anim *anim) } if (anim->pCodecCtx) { - avcodec_close(anim->pCodecCtx); + avcodec_free_context(&anim->pCodecCtx); avformat_close_input(&anim->pFormatCtx); + av_packet_free(&anim->next_packet); - /* Special case here: pFrame could share pointers with codec, - * so in order to avoid double-free we don't use av_frame_free() - * to free the frame. - * - * Could it be a bug in FFmpeg? - */ - av_free(anim->pFrame); + av_frame_free(&anim->pFrame); if (!need_aligned_ffmpeg_buffer(anim)) { /* If there's no need for own aligned buffer it means that FFmpeg's * frame shares the same buffer as temporary ImBuf. In this case we * should not free the buffer when freeing the FFmpeg buffer. */ - avpicture_fill((AVPicture *)anim->pFrameRGB, NULL, AV_PIX_FMT_RGBA, anim->x, anim->y); + av_image_fill_arrays(anim->pFrameRGB->data, + anim->pFrameRGB->linesize, + NULL, + AV_PIX_FMT_RGBA, + anim->x, + anim->y, + 1); } av_frame_free(&anim->pFrameRGB); av_frame_free(&anim->pFrameDeinterlaced); sws_freeContext(anim->img_convert_ctx); IMB_freeImBuf(anim->last_frame); - if (anim->next_packet.stream_index != -1) { - av_free_packet(&anim->next_packet); - } } anim->duration_in_frames = 0; } diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index ef9f6d861a3..11ce77e3091 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -48,6 +48,7 @@ #ifdef WITH_FFMPEG # include "ffmpeg_compat.h" +# include <libavutil/imgutils.h> #endif static const char magic[] = "BlenMIdx"; @@ -488,14 +489,14 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->of = avformat_alloc_context(); rv->of->oformat = av_guess_format("avi", NULL, NULL); - BLI_strncpy(rv->of->filename, fname, sizeof(rv->of->filename)); + rv->of->url = av_strdup(fname); - fprintf(stderr, "Starting work on proxy: %s\n", rv->of->filename); + fprintf(stderr, "Starting work on proxy: %s\n", rv->of->url); rv->st = avformat_new_stream(rv->of, NULL); rv->st->id = 0; - rv->c = rv->st->codec; + rv->c = avcodec_alloc_context3(NULL); rv->c->codec_type = AVMEDIA_TYPE_VIDEO; rv->c->codec_id = AV_CODEC_ID_H264; rv->c->width = width; @@ -513,7 +514,9 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( fprintf(stderr, "No ffmpeg encoder available? " "Proxy not built!\n"); - av_free(rv->of); + avcodec_free_context(&rv->c); + avformat_free_context(rv->of); + MEM_freeN(rv); return NULL; } @@ -524,7 +527,7 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->c->pix_fmt = AV_PIX_FMT_YUVJ420P; } - rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->codec->sample_aspect_ratio; + rv->c->sample_aspect_ratio = rv->st->sample_aspect_ratio = st->sample_aspect_ratio; rv->c->time_base.den = 25; rv->c->time_base.num = 1; @@ -557,34 +560,54 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( } if (rv->of->flags & AVFMT_GLOBALHEADER) { - rv->c->flags |= CODEC_FLAG_GLOBAL_HEADER; + rv->c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } - if (avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE) < 0) { + avcodec_parameters_from_context(rv->st->codecpar, rv->c); + + int ret = avio_open(&rv->of->pb, fname, AVIO_FLAG_WRITE); + + if (ret < 0) { fprintf(stderr, - "Couldn't open outputfile! " - "Proxy not built!\n"); - av_free(rv->of); - return 0; + "Couldn't open IO: %s\n" + "Proxy not built!\n", + av_err2str(ret)); + avcodec_free_context(&rv->c); + avformat_free_context(rv->of); + MEM_freeN(rv); + return NULL; } - avcodec_open2(rv->c, rv->codec, &codec_opts); + ret = avcodec_open2(rv->c, rv->codec, &codec_opts); + if (ret < 0) { + fprintf(stderr, + "Couldn't open codec: %s\n" + "Proxy not built!\n", + av_err2str(ret)); + avcodec_free_context(&rv->c); + avformat_free_context(rv->of); + MEM_freeN(rv); + return NULL; + } - rv->orig_height = av_get_cropped_height_from_codec(st->codec); + rv->orig_height = st->codecpar->height; - if (st->codec->width != width || st->codec->height != height || - st->codec->pix_fmt != rv->c->pix_fmt) { + if (st->codecpar->width != width || st->codecpar->height != height || + st->codecpar->format != rv->c->pix_fmt) { rv->frame = av_frame_alloc(); - avpicture_fill((AVPicture *)rv->frame, - MEM_mallocN(avpicture_get_size(rv->c->pix_fmt, round_up(width, 16), height), - "alloc proxy output frame"), - rv->c->pix_fmt, - round_up(width, 16), - height); - - rv->sws_ctx = sws_getContext(st->codec->width, + av_image_fill_arrays( + rv->frame->data, + rv->frame->linesize, + MEM_mallocN(av_image_get_buffer_size(rv->c->pix_fmt, round_up(width, 16), height, 1), + "alloc proxy output frame"), + rv->c->pix_fmt, + round_up(width, 16), + height, + 1); + + rv->sws_ctx = sws_getContext(st->codecpar->width, rv->orig_height, - st->codec->pix_fmt, + st->codecpar->format, width, height, rv->c->pix_fmt, @@ -594,26 +617,30 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( NULL); } - if (avformat_write_header(rv->of, NULL) < 0) { + ret = avformat_write_header(rv->of, NULL); + if (ret < 0) { fprintf(stderr, - "Couldn't set output parameters? " - "Proxy not built!\n"); - av_free(rv->of); - return 0; + "Couldn't write header: %s\n" + "Proxy not built!\n", + av_err2str(ret)); + + if (rv->frame) { + av_frame_free(&rv->frame); + } + + avcodec_free_context(&rv->c); + avformat_free_context(rv->of); + MEM_freeN(rv); + return NULL; } return rv; } -static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame) +static void add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *frame) { - AVPacket packet = {0}; - int ret, got_output; - - av_init_packet(&packet); - if (!ctx) { - return 0; + return; } if (ctx->sws_ctx && frame && @@ -633,35 +660,46 @@ static int add_to_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, AVFrame *fra frame->pts = ctx->cfra++; } - ret = avcodec_encode_video2(ctx->c, &packet, frame, &got_output); + int ret = avcodec_send_frame(ctx->c, frame); if (ret < 0) { - fprintf(stderr, "Error encoding proxy frame %d for '%s'\n", ctx->cfra - 1, ctx->of->filename); - return 0; + /* Can't send frame to encoder. This shouldn't happen. */ + fprintf(stderr, "Can't send video frame: %s\n", av_err2str(ret)); + return; } + AVPacket *packet = av_packet_alloc(); + + while (ret >= 0) { + ret = avcodec_receive_packet(ctx->c, packet); - if (got_output) { - if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, ctx->c->time_base, ctx->st->time_base); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + /* No more packets to flush. */ + break; } - if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, ctx->c->time_base, ctx->st->time_base); + if (ret < 0) { + fprintf(stderr, + "Error encoding proxy frame %d for '%s': %s\n", + ctx->cfra - 1, + ctx->of->url, + av_err2str(ret)); + break; } - packet.stream_index = ctx->st->index; + packet->stream_index = ctx->st->index; + av_packet_rescale_ts(packet, ctx->c->time_base, ctx->st->time_base); - if (av_interleaved_write_frame(ctx->of, &packet) != 0) { + int write_ret = av_interleaved_write_frame(ctx->of, packet); + if (write_ret != 0) { fprintf(stderr, "Error writing proxy frame %d " - "into '%s'\n", + "into '%s': %s\n", ctx->cfra - 1, - ctx->of->filename); - return 0; + ctx->of->url, + av_err2str(write_ret)); + break; } - - return 1; } - return 0; + av_packet_free(&packet); } static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) @@ -674,15 +712,15 @@ static void free_proxy_output_ffmpeg(struct proxy_output_ctx *ctx, int rollback) } if (!rollback) { - while (add_to_proxy_output_ffmpeg(ctx, NULL)) { - } + /* Flush the remaining packets. */ + add_to_proxy_output_ffmpeg(ctx, NULL); } avcodec_flush_buffers(ctx->c); av_write_trailer(ctx->of); - avcodec_close(ctx->c); + avcodec_free_context(&ctx->c); if (ctx->of->oformat) { if (!(ctx->of->oformat->flags & AVFMT_NOFILE)) { @@ -777,7 +815,7 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, /* 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 (context->iFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (streamcount > 0) { streamcount--; continue; @@ -794,9 +832,8 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, } context->iStream = context->iFormatCtx->streams[context->videoStream]; - context->iCodecCtx = context->iStream->codec; - context->iCodec = avcodec_find_decoder(context->iCodecCtx->codec_id); + context->iCodec = avcodec_find_decoder(context->iStream->codecpar->codec_id); if (context->iCodec == NULL) { avformat_close_input(&context->iFormatCtx); @@ -804,7 +841,9 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, return NULL; } - context->iCodecCtx->workaround_bugs = 1; + context->iCodecCtx = avcodec_alloc_context3(NULL); + avcodec_parameters_to_context(context->iCodecCtx, context->iStream->codecpar); + context->iCodecCtx->workaround_bugs = FF_BUG_AUTODETECT; if (context->iCodec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { context->iCodecCtx->thread_count = 0; @@ -822,19 +861,19 @@ static IndexBuildContext *index_ffmpeg_create_context(struct anim *anim, if (avcodec_open2(context->iCodecCtx, context->iCodec, NULL) < 0) { avformat_close_input(&context->iFormatCtx); + avcodec_free_context(&context->iCodecCtx); 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); + context->proxy_ctx[i] = alloc_proxy_output_ffmpeg(anim, + context->iStream, + proxy_sizes[i], + context->iCodecCtx->width * proxy_fac[i], + context->iCodecCtx->height * proxy_fac[i], + quality); if (!context->proxy_ctx[i]) { proxy_sizes_in_use &= ~proxy_sizes[i]; } @@ -873,7 +912,7 @@ static void index_rebuild_ffmpeg_finish(FFmpegIndexBuilderContext *context, int } } - avcodec_close(context->iCodecCtx); + avcodec_free_context(&context->iCodecCtx); avformat_close_input(&context->iFormatCtx); MEM_freeN(context); @@ -938,23 +977,18 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, short *do_update, float *progress) { - AVFrame *in_frame = 0; - AVPacket next_packet; + AVFrame *in_frame = av_frame_alloc(); + AVPacket *next_packet = av_packet_alloc(); uint64_t stream_size; - memset(&next_packet, 0, sizeof(AVPacket)); - - in_frame = av_frame_alloc(); - stream_size = avio_size(context->iFormatCtx->pb); context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL)); context->pts_time_base = av_q2d(context->iStream->time_base); - while (av_read_frame(context->iFormatCtx, &next_packet) >= 0) { - int frame_finished = 0; + while (av_read_frame(context->iFormatCtx, next_packet) >= 0) { float next_progress = - (float)((int)floor(((double)next_packet.pos) * 100 / ((double)stream_size) + 0.5)) / 100; + (float)((int)floor(((double)next_packet->pos) * 100 / ((double)stream_size) + 0.5)) / 100; if (*progress != next_progress) { *progress = next_progress; @@ -962,50 +996,59 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, } if (*stop) { - av_free_packet(&next_packet); break; } - if (next_packet.stream_index == context->videoStream) { - if (next_packet.flags & AV_PKT_FLAG_KEY) { + if (next_packet->stream_index == context->videoStream) { + if (next_packet->flags & AV_PKT_FLAG_KEY) { 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; + context->seek_pos = next_packet->pos; + context->seek_pos_dts = next_packet->dts; + context->seek_pos_pts = next_packet->pts; } - avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet); - } + int ret = avcodec_send_packet(context->iCodecCtx, next_packet); + while (ret >= 0) { + ret = avcodec_receive_frame(context->iCodecCtx, in_frame); - if (frame_finished) { - index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + /* No more frames to flush. */ + break; + } + if (ret < 0) { + fprintf(stderr, "Error decoding proxy frame: %s\n", av_err2str(ret)); + break; + } + index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame); + } } - av_free_packet(&next_packet); } /* process pictures still stuck in decoder engine after EOF - * according to ffmpeg docs using 0-size packets. + * according to ffmpeg docs using NULL packets. * * At least, if we haven't already stopped... */ - /* this creates the 0-size packet and prevents a memory leak. */ - av_free_packet(&next_packet); - if (!*stop) { - int frame_finished; - - do { - frame_finished = 0; + int ret = avcodec_send_packet(context->iCodecCtx, NULL); - avcodec_decode_video2(context->iCodecCtx, in_frame, &frame_finished, &next_packet); + while (ret >= 0) { + ret = avcodec_receive_frame(context->iCodecCtx, in_frame); - if (frame_finished) { - index_rebuild_ffmpeg_proc_decoded_frame(context, &next_packet, in_frame); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + /* No more frames to flush. */ + break; } - } while (frame_finished); + if (ret < 0) { + fprintf(stderr, "Error flushing proxy frame: %s\n", av_err2str(ret)); + break; + } + index_rebuild_ffmpeg_proc_decoded_frame(context, next_packet, in_frame); + } } + av_packet_free(&next_packet); av_free(in_frame); return 1; diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 64dad5de902..fabf6c5c3bd 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -245,7 +245,6 @@ static void ffmpeg_log_callback(void *ptr, int level, const char *format, va_lis void IMB_ffmpeg_init(void) { - av_register_all(); avdevice_register_all(); ffmpeg_last_error[0] = '\0'; @@ -269,7 +268,6 @@ static int isffmpeg(const char *filepath) unsigned int i; int videoStream; AVCodec *pCodec; - AVCodecContext *pCodecCtx; if (BLI_path_extension_check_n(filepath, ".swf", @@ -310,8 +308,8 @@ static int isffmpeg(const char *filepath) /* Find the first video stream */ videoStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { - if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codec && - (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)) { + if (pFormatCtx->streams[i] && pFormatCtx->streams[i]->codecpar && + (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) { videoStream = i; break; } @@ -322,21 +320,15 @@ static int isffmpeg(const char *filepath) return 0; } - pCodecCtx = pFormatCtx->streams[videoStream]->codec; + AVCodecParameters *codec_par = pFormatCtx->streams[videoStream]->codecpar; /* Find the decoder for the video stream */ - pCodec = avcodec_find_decoder(pCodecCtx->codec_id); + pCodec = avcodec_find_decoder(codec_par->codec_id); if (pCodec == NULL) { avformat_close_input(&pFormatCtx); return 0; } - if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { - avformat_close_input(&pFormatCtx); - return 0; - } - - avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 1; |