From d96f2fbf76fd2587d8f9aa984591439cf38bfb0b Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sat, 25 Mar 2023 04:36:28 +0100 Subject: [PATCH] fftools/ffmpeg: move initializing encoders to a new file This file will contain more encoding-related code in the future. --- fftools/Makefile | 1 + fftools/ffmpeg.c | 306 +------------------------------------- fftools/ffmpeg.h | 2 + fftools/ffmpeg_enc.c | 342 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 346 insertions(+), 305 deletions(-) create mode 100644 fftools/ffmpeg_enc.c diff --git a/fftools/Makefile b/fftools/Makefile index 8ac38e75d2..9939c7095c 100644 --- a/fftools/Makefile +++ b/fftools/Makefile @@ -11,6 +11,7 @@ ALLAVPROGS_G = $(AVBASENAMES:%=%$(PROGSSUF)_g$(EXESUF)) OBJS-ffmpeg += \ fftools/ffmpeg_demux.o \ + fftools/ffmpeg_enc.o \ fftools/ffmpeg_filter.o \ fftools/ffmpeg_hw.o \ fftools/ffmpeg_mux.o \ diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 42b2735fb4..c439f94de1 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -2967,316 +2967,12 @@ static int init_output_stream_streamcopy(OutputStream *ost) return 0; } -static void set_encoder_id(OutputFile *of, OutputStream *ost) -{ - const char *cname = ost->enc_ctx->codec->name; - uint8_t *encoder_string; - int encoder_string_len; - - if (av_dict_get(ost->st->metadata, "encoder", NULL, 0)) - return; - - encoder_string_len = sizeof(LIBAVCODEC_IDENT) + strlen(cname) + 2; - encoder_string = av_mallocz(encoder_string_len); - if (!encoder_string) - report_and_exit(AVERROR(ENOMEM)); - - if (!of->bitexact && !ost->bitexact) - av_strlcpy(encoder_string, LIBAVCODEC_IDENT " ", encoder_string_len); - else - av_strlcpy(encoder_string, "Lavc ", encoder_string_len); - av_strlcat(encoder_string, cname, encoder_string_len); - av_dict_set(&ost->st->metadata, "encoder", encoder_string, - AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE); -} - -static void init_encoder_time_base(OutputStream *ost, AVRational default_time_base) -{ - InputStream *ist = ost->ist; - AVCodecContext *enc_ctx = ost->enc_ctx; - - if (ost->enc_timebase.num > 0) { - enc_ctx->time_base = ost->enc_timebase; - return; - } - - if (ost->enc_timebase.num < 0) { - if (ist) { - enc_ctx->time_base = ist->st->time_base; - return; - } - - av_log(ost, AV_LOG_WARNING, - "Input stream data not available, using default time base\n"); - } - - enc_ctx->time_base = default_time_base; -} - -static int init_output_stream_encode(OutputStream *ost, AVFrame *frame) -{ - InputStream *ist = ost->ist; - AVCodecContext *enc_ctx = ost->enc_ctx; - AVCodecContext *dec_ctx = NULL; - const AVCodec *enc = enc_ctx->codec; - OutputFile *of = output_files[ost->file_index]; - int ret; - - set_encoder_id(output_files[ost->file_index], ost); - - if (ist) { - dec_ctx = ist->dec_ctx; - } - - if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { - if (!ost->frame_rate.num) - ost->frame_rate = av_buffersink_get_frame_rate(ost->filter->filter); - if (!ost->frame_rate.num && !ost->max_frame_rate.num) { - ost->frame_rate = (AVRational){25, 1}; - av_log(ost, AV_LOG_WARNING, - "No information " - "about the input framerate is available. Falling " - "back to a default value of 25fps. Use the -r option " - "if you want a different framerate.\n"); - } - - if (ost->max_frame_rate.num && - (av_q2d(ost->frame_rate) > av_q2d(ost->max_frame_rate) || - !ost->frame_rate.den)) - ost->frame_rate = ost->max_frame_rate; - - if (enc->supported_framerates && !ost->force_fps) { - int idx = av_find_nearest_q_idx(ost->frame_rate, enc->supported_framerates); - ost->frame_rate = enc->supported_framerates[idx]; - } - // reduce frame rate for mpeg4 to be within the spec limits - if (enc_ctx->codec_id == AV_CODEC_ID_MPEG4) { - av_reduce(&ost->frame_rate.num, &ost->frame_rate.den, - ost->frame_rate.num, ost->frame_rate.den, 65535); - } - } - - switch (enc_ctx->codec_type) { - case AVMEDIA_TYPE_AUDIO: - enc_ctx->sample_fmt = av_buffersink_get_format(ost->filter->filter); - enc_ctx->sample_rate = av_buffersink_get_sample_rate(ost->filter->filter); - ret = av_buffersink_get_ch_layout(ost->filter->filter, &enc_ctx->ch_layout); - if (ret < 0) - return ret; - - if (ost->bits_per_raw_sample) - enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; - else if (dec_ctx && ost->filter->graph->is_meta) - enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, - av_get_bytes_per_sample(enc_ctx->sample_fmt) << 3); - - init_encoder_time_base(ost, av_make_q(1, enc_ctx->sample_rate)); - break; - - case AVMEDIA_TYPE_VIDEO: - init_encoder_time_base(ost, av_inv_q(ost->frame_rate)); - - if (!(enc_ctx->time_base.num && enc_ctx->time_base.den)) - enc_ctx->time_base = av_buffersink_get_time_base(ost->filter->filter); - if ( av_q2d(enc_ctx->time_base) < 0.001 && ost->vsync_method != VSYNC_PASSTHROUGH - && (ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR || - (ost->vsync_method == VSYNC_AUTO && !(of->format->flags & AVFMT_VARIABLE_FPS)))){ - av_log(ost, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n" - "Please consider specifying a lower framerate, a different muxer or " - "setting vsync/fps_mode to vfr\n"); - } - - enc_ctx->width = av_buffersink_get_w(ost->filter->filter); - enc_ctx->height = av_buffersink_get_h(ost->filter->filter); - enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio = - ost->frame_aspect_ratio.num ? // overridden by the -aspect cli option - av_mul_q(ost->frame_aspect_ratio, (AVRational){ enc_ctx->height, enc_ctx->width }) : - av_buffersink_get_sample_aspect_ratio(ost->filter->filter); - - enc_ctx->pix_fmt = av_buffersink_get_format(ost->filter->filter); - - if (ost->bits_per_raw_sample) - enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; - else if (dec_ctx && ost->filter->graph->is_meta) - enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, - av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth); - - if (frame) { - enc_ctx->color_range = frame->color_range; - enc_ctx->color_primaries = frame->color_primaries; - enc_ctx->color_trc = frame->color_trc; - enc_ctx->colorspace = frame->colorspace; - enc_ctx->chroma_sample_location = frame->chroma_location; - } - - enc_ctx->framerate = ost->frame_rate; - - ost->st->avg_frame_rate = ost->frame_rate; - - // Field order: autodetection - if (frame) { - if (enc_ctx->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME) && - ost->top_field_first >= 0) - frame->top_field_first = !!ost->top_field_first; - - if (frame->interlaced_frame) { - if (enc->id == AV_CODEC_ID_MJPEG) - enc_ctx->field_order = frame->top_field_first ? AV_FIELD_TT:AV_FIELD_BB; - else - enc_ctx->field_order = frame->top_field_first ? AV_FIELD_TB:AV_FIELD_BT; - } else - enc_ctx->field_order = AV_FIELD_PROGRESSIVE; - } - - // Field order: override - if (ost->top_field_first == 0) { - enc_ctx->field_order = AV_FIELD_BB; - } else if (ost->top_field_first == 1) { - enc_ctx->field_order = AV_FIELD_TT; - } - - break; - case AVMEDIA_TYPE_SUBTITLE: - enc_ctx->time_base = AV_TIME_BASE_Q; - if (!enc_ctx->width) { - enc_ctx->width = ost->ist->par->width; - enc_ctx->height = ost->ist->par->height; - } - if (dec_ctx && dec_ctx->subtitle_header) { - /* ASS code assumes this buffer is null terminated so add extra byte. */ - enc_ctx->subtitle_header = av_mallocz(dec_ctx->subtitle_header_size + 1); - if (!enc_ctx->subtitle_header) - return AVERROR(ENOMEM); - memcpy(enc_ctx->subtitle_header, dec_ctx->subtitle_header, - dec_ctx->subtitle_header_size); - enc_ctx->subtitle_header_size = dec_ctx->subtitle_header_size; - } - if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && - enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - int input_props = 0, output_props = 0; - AVCodecDescriptor const *input_descriptor = - avcodec_descriptor_get(ist->dec->id); - AVCodecDescriptor const *output_descriptor = - avcodec_descriptor_get(enc_ctx->codec_id); - if (input_descriptor) - input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (output_descriptor) - output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (input_props && output_props && input_props != output_props) { - av_log(ost, AV_LOG_ERROR, - "Subtitle encoding currently only possible from text to text " - "or bitmap to bitmap"); - return AVERROR_INVALIDDATA; - } - } - - break; - default: - abort(); - break; - } - - if (ost->bitexact) - enc_ctx->flags |= AV_CODEC_FLAG_BITEXACT; - - if (!av_dict_get(ost->encoder_opts, "threads", NULL, 0)) - av_dict_set(&ost->encoder_opts, "threads", "auto", 0); - - if (enc->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE) { - ret = av_dict_set(&ost->encoder_opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); - if (ret < 0) - return ret; - } - - ret = hw_device_setup_for_encode(ost); - if (ret < 0) { - av_log(ost, AV_LOG_ERROR, - "Encoding hardware device setup failed: %s\n", av_err2str(ret)); - return ret; - } - - if ((ret = avcodec_open2(ost->enc_ctx, enc, &ost->encoder_opts)) < 0) { - if (ret == AVERROR_EXPERIMENTAL) - abort_codec_experimental(enc, 1); - av_log(ost, AV_LOG_ERROR, "Error while opening encoder - maybe " - "incorrect parameters such as bit_rate, rate, width or height.\n"); - return ret; - } - - if (ost->enc_ctx->frame_size) { - av_assert0(ost->sq_idx_encode >= 0); - sq_frame_samples(output_files[ost->file_index]->sq_encode, - ost->sq_idx_encode, ost->enc_ctx->frame_size); - } - - assert_avoptions(ost->encoder_opts); - if (ost->enc_ctx->bit_rate && ost->enc_ctx->bit_rate < 1000 && - ost->enc_ctx->codec_id != AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */) - av_log(ost, AV_LOG_WARNING, "The bitrate parameter is set too low." - " It takes bits/s as argument, not kbits/s\n"); - - ret = avcodec_parameters_from_context(ost->st->codecpar, ost->enc_ctx); - if (ret < 0) { - av_log(ost, AV_LOG_FATAL, - "Error initializing the output stream codec context.\n"); - exit_program(1); - } - - if (ost->enc_ctx->nb_coded_side_data) { - int i; - - for (i = 0; i < ost->enc_ctx->nb_coded_side_data; i++) { - const AVPacketSideData *sd_src = &ost->enc_ctx->coded_side_data[i]; - uint8_t *dst_data; - - dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); - if (!dst_data) - return AVERROR(ENOMEM); - memcpy(dst_data, sd_src->data, sd_src->size); - } - } - - /* - * Add global input side data. For now this is naive, and copies it - * from the input stream's global side data. All side data should - * really be funneled over AVFrame and libavfilter, then added back to - * packet side data, and then potentially using the first packet for - * global side data. - */ - if (ist) { - int i; - for (i = 0; i < ist->st->nb_side_data; i++) { - AVPacketSideData *sd = &ist->st->side_data[i]; - if (sd->type != AV_PKT_DATA_CPB_PROPERTIES) { - uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size); - if (!dst) - return AVERROR(ENOMEM); - memcpy(dst, sd->data, sd->size); - if (ist->autorotate && sd->type == AV_PKT_DATA_DISPLAYMATRIX) - av_display_rotation_set((int32_t *)dst, 0); - } - } - } - - // copy timebase while removing common factors - if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) - ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1}); - - // copy estimated duration as a hint to the muxer - if (ost->st->duration <= 0 && ist && ist->st->duration > 0) - ost->st->duration = av_rescale_q(ist->st->duration, ist->st->time_base, ost->st->time_base); - - ost->mux_timebase = enc_ctx->time_base; - - return 0; -} - static int init_output_stream(OutputStream *ost, AVFrame *frame) { int ret = 0; if (ost->enc_ctx) { - ret = init_output_stream_encode(ost, frame); + ret = enc_open(ost, frame); if (ret < 0) return ret; } else if (ost->ist) { diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 791deedc07..c1e2bbc300 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -810,6 +810,8 @@ AVBufferRef *hw_device_for_filter(void); int hwaccel_decode_init(AVCodecContext *avctx); +int enc_open(OutputStream *ost, AVFrame *frame); + /* * Initialize muxing state for the given stream, should be called * after the codec/streamcopy setup has been done. diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c new file mode 100644 index 0000000000..9db2ff5ef3 --- /dev/null +++ b/fftools/ffmpeg_enc.c @@ -0,0 +1,342 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "ffmpeg.h" + +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/avutil.h" +#include "libavutil/dict.h" +#include "libavutil/display.h" +#include "libavutil/eval.h" +#include "libavutil/frame.h" +#include "libavutil/log.h" +#include "libavutil/pixdesc.h" +#include "libavutil/rational.h" + +#include "libavfilter/buffersink.h" + +#include "libavcodec/avcodec.h" + +#include "libavformat/avformat.h" + +static void set_encoder_id(OutputFile *of, OutputStream *ost) +{ + const char *cname = ost->enc_ctx->codec->name; + uint8_t *encoder_string; + int encoder_string_len; + + if (av_dict_get(ost->st->metadata, "encoder", NULL, 0)) + return; + + encoder_string_len = sizeof(LIBAVCODEC_IDENT) + strlen(cname) + 2; + encoder_string = av_mallocz(encoder_string_len); + if (!encoder_string) + report_and_exit(AVERROR(ENOMEM)); + + if (!of->bitexact && !ost->bitexact) + av_strlcpy(encoder_string, LIBAVCODEC_IDENT " ", encoder_string_len); + else + av_strlcpy(encoder_string, "Lavc ", encoder_string_len); + av_strlcat(encoder_string, cname, encoder_string_len); + av_dict_set(&ost->st->metadata, "encoder", encoder_string, + AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE); +} + +static void init_encoder_time_base(OutputStream *ost, AVRational default_time_base) +{ + InputStream *ist = ost->ist; + AVCodecContext *enc_ctx = ost->enc_ctx; + + if (ost->enc_timebase.num > 0) { + enc_ctx->time_base = ost->enc_timebase; + return; + } + + if (ost->enc_timebase.num < 0) { + if (ist) { + enc_ctx->time_base = ist->st->time_base; + return; + } + + av_log(ost, AV_LOG_WARNING, + "Input stream data not available, using default time base\n"); + } + + enc_ctx->time_base = default_time_base; +} + +int enc_open(OutputStream *ost, AVFrame *frame) +{ + InputStream *ist = ost->ist; + AVCodecContext *enc_ctx = ost->enc_ctx; + AVCodecContext *dec_ctx = NULL; + const AVCodec *enc = enc_ctx->codec; + OutputFile *of = output_files[ost->file_index]; + int ret; + + set_encoder_id(output_files[ost->file_index], ost); + + if (ist) { + dec_ctx = ist->dec_ctx; + } + + if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!ost->frame_rate.num) + ost->frame_rate = av_buffersink_get_frame_rate(ost->filter->filter); + if (!ost->frame_rate.num && !ost->max_frame_rate.num) { + ost->frame_rate = (AVRational){25, 1}; + av_log(ost, AV_LOG_WARNING, + "No information " + "about the input framerate is available. Falling " + "back to a default value of 25fps. Use the -r option " + "if you want a different framerate.\n"); + } + + if (ost->max_frame_rate.num && + (av_q2d(ost->frame_rate) > av_q2d(ost->max_frame_rate) || + !ost->frame_rate.den)) + ost->frame_rate = ost->max_frame_rate; + + if (enc->supported_framerates && !ost->force_fps) { + int idx = av_find_nearest_q_idx(ost->frame_rate, enc->supported_framerates); + ost->frame_rate = enc->supported_framerates[idx]; + } + // reduce frame rate for mpeg4 to be within the spec limits + if (enc_ctx->codec_id == AV_CODEC_ID_MPEG4) { + av_reduce(&ost->frame_rate.num, &ost->frame_rate.den, + ost->frame_rate.num, ost->frame_rate.den, 65535); + } + } + + switch (enc_ctx->codec_type) { + case AVMEDIA_TYPE_AUDIO: + enc_ctx->sample_fmt = av_buffersink_get_format(ost->filter->filter); + enc_ctx->sample_rate = av_buffersink_get_sample_rate(ost->filter->filter); + ret = av_buffersink_get_ch_layout(ost->filter->filter, &enc_ctx->ch_layout); + if (ret < 0) + return ret; + + if (ost->bits_per_raw_sample) + enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; + else if (dec_ctx && ost->filter->graph->is_meta) + enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, + av_get_bytes_per_sample(enc_ctx->sample_fmt) << 3); + + init_encoder_time_base(ost, av_make_q(1, enc_ctx->sample_rate)); + break; + + case AVMEDIA_TYPE_VIDEO: + init_encoder_time_base(ost, av_inv_q(ost->frame_rate)); + + if (!(enc_ctx->time_base.num && enc_ctx->time_base.den)) + enc_ctx->time_base = av_buffersink_get_time_base(ost->filter->filter); + if ( av_q2d(enc_ctx->time_base) < 0.001 && ost->vsync_method != VSYNC_PASSTHROUGH + && (ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR || + (ost->vsync_method == VSYNC_AUTO && !(of->format->flags & AVFMT_VARIABLE_FPS)))){ + av_log(ost, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n" + "Please consider specifying a lower framerate, a different muxer or " + "setting vsync/fps_mode to vfr\n"); + } + + enc_ctx->width = av_buffersink_get_w(ost->filter->filter); + enc_ctx->height = av_buffersink_get_h(ost->filter->filter); + enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio = + ost->frame_aspect_ratio.num ? // overridden by the -aspect cli option + av_mul_q(ost->frame_aspect_ratio, (AVRational){ enc_ctx->height, enc_ctx->width }) : + av_buffersink_get_sample_aspect_ratio(ost->filter->filter); + + enc_ctx->pix_fmt = av_buffersink_get_format(ost->filter->filter); + + if (ost->bits_per_raw_sample) + enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; + else if (dec_ctx && ost->filter->graph->is_meta) + enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, + av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth); + + if (frame) { + enc_ctx->color_range = frame->color_range; + enc_ctx->color_primaries = frame->color_primaries; + enc_ctx->color_trc = frame->color_trc; + enc_ctx->colorspace = frame->colorspace; + enc_ctx->chroma_sample_location = frame->chroma_location; + } + + enc_ctx->framerate = ost->frame_rate; + + ost->st->avg_frame_rate = ost->frame_rate; + + // Field order: autodetection + if (frame) { + if (enc_ctx->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME) && + ost->top_field_first >= 0) + frame->top_field_first = !!ost->top_field_first; + + if (frame->interlaced_frame) { + if (enc->id == AV_CODEC_ID_MJPEG) + enc_ctx->field_order = frame->top_field_first ? AV_FIELD_TT:AV_FIELD_BB; + else + enc_ctx->field_order = frame->top_field_first ? AV_FIELD_TB:AV_FIELD_BT; + } else + enc_ctx->field_order = AV_FIELD_PROGRESSIVE; + } + + // Field order: override + if (ost->top_field_first == 0) { + enc_ctx->field_order = AV_FIELD_BB; + } else if (ost->top_field_first == 1) { + enc_ctx->field_order = AV_FIELD_TT; + } + + break; + case AVMEDIA_TYPE_SUBTITLE: + enc_ctx->time_base = AV_TIME_BASE_Q; + if (!enc_ctx->width) { + enc_ctx->width = ost->ist->par->width; + enc_ctx->height = ost->ist->par->height; + } + if (dec_ctx && dec_ctx->subtitle_header) { + /* ASS code assumes this buffer is null terminated so add extra byte. */ + enc_ctx->subtitle_header = av_mallocz(dec_ctx->subtitle_header_size + 1); + if (!enc_ctx->subtitle_header) + return AVERROR(ENOMEM); + memcpy(enc_ctx->subtitle_header, dec_ctx->subtitle_header, + dec_ctx->subtitle_header_size); + enc_ctx->subtitle_header_size = dec_ctx->subtitle_header_size; + } + if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && + enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { + int input_props = 0, output_props = 0; + AVCodecDescriptor const *input_descriptor = + avcodec_descriptor_get(ist->dec->id); + AVCodecDescriptor const *output_descriptor = + avcodec_descriptor_get(enc_ctx->codec_id); + if (input_descriptor) + input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); + if (output_descriptor) + output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); + if (input_props && output_props && input_props != output_props) { + av_log(ost, AV_LOG_ERROR, + "Subtitle encoding currently only possible from text to text " + "or bitmap to bitmap"); + return AVERROR_INVALIDDATA; + } + } + + break; + default: + abort(); + break; + } + + if (ost->bitexact) + enc_ctx->flags |= AV_CODEC_FLAG_BITEXACT; + + if (!av_dict_get(ost->encoder_opts, "threads", NULL, 0)) + av_dict_set(&ost->encoder_opts, "threads", "auto", 0); + + if (enc->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE) { + ret = av_dict_set(&ost->encoder_opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); + if (ret < 0) + return ret; + } + + ret = hw_device_setup_for_encode(ost); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, + "Encoding hardware device setup failed: %s\n", av_err2str(ret)); + return ret; + } + + if ((ret = avcodec_open2(ost->enc_ctx, enc, &ost->encoder_opts)) < 0) { + if (ret != AVERROR_EXPERIMENTAL) + av_log(ost, AV_LOG_ERROR, "Error while opening encoder - maybe " + "incorrect parameters such as bit_rate, rate, width or height.\n"); + return ret; + } + + if (ost->enc_ctx->frame_size) { + av_assert0(ost->sq_idx_encode >= 0); + sq_frame_samples(output_files[ost->file_index]->sq_encode, + ost->sq_idx_encode, ost->enc_ctx->frame_size); + } + + assert_avoptions(ost->encoder_opts); + if (ost->enc_ctx->bit_rate && ost->enc_ctx->bit_rate < 1000 && + ost->enc_ctx->codec_id != AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */) + av_log(ost, AV_LOG_WARNING, "The bitrate parameter is set too low." + " It takes bits/s as argument, not kbits/s\n"); + + ret = avcodec_parameters_from_context(ost->st->codecpar, ost->enc_ctx); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, + "Error initializing the output stream codec context.\n"); + exit_program(1); + } + + if (ost->enc_ctx->nb_coded_side_data) { + int i; + + for (i = 0; i < ost->enc_ctx->nb_coded_side_data; i++) { + const AVPacketSideData *sd_src = &ost->enc_ctx->coded_side_data[i]; + uint8_t *dst_data; + + dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); + if (!dst_data) + return AVERROR(ENOMEM); + memcpy(dst_data, sd_src->data, sd_src->size); + } + } + + /* + * Add global input side data. For now this is naive, and copies it + * from the input stream's global side data. All side data should + * really be funneled over AVFrame and libavfilter, then added back to + * packet side data, and then potentially using the first packet for + * global side data. + */ + if (ist) { + int i; + for (i = 0; i < ist->st->nb_side_data; i++) { + AVPacketSideData *sd = &ist->st->side_data[i]; + if (sd->type != AV_PKT_DATA_CPB_PROPERTIES) { + uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size); + if (!dst) + return AVERROR(ENOMEM); + memcpy(dst, sd->data, sd->size); + if (ist->autorotate && sd->type == AV_PKT_DATA_DISPLAYMATRIX) + av_display_rotation_set((int32_t *)dst, 0); + } + } + } + + // copy timebase while removing common factors + if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) + ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1}); + + // copy estimated duration as a hint to the muxer + if (ost->st->duration <= 0 && ist && ist->st->duration > 0) + ost->st->duration = av_rescale_q(ist->st->duration, ist->st->time_base, ost->st->time_base); + + ost->mux_timebase = enc_ctx->time_base; + + return 0; +}