快捷搜索:  汽车  科技

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)video部分调用栈如下,这个调用栈是需要重新编码:2.查看对应输出编码格式1.首先解析命令行如直接copy和编码。如下解析结果:指定再次编码,并指定格式。如下解析结果:

阅读本文前,可以看看前面几篇文章。

超详细ffmpeg.c框架分析

超详细ffmpeg.c命令行分析

超详细Mp4转Flv之timebase分析

1.首先解析命令行

如直接copy和编码。如下解析结果:

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(1)

指定再次编码,并指定格式。如下解析结果:

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(2)

2.查看对应输出编码格式

video部分调用栈如下,这个调用栈是需要重新编码:

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(3)

如果不需要重新编码,调用栈流程,就简单些。如图所示:

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(4)

不需要重新编码,输出流的调用栈。

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(5)

关于直接copy的时间基的转换,st->time_base转换为ost->mux_timebase。最后写到文件或发送网络流。详细部分,可以参考前面的文章。

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(6)

一般那路流指定要重新编解码,那么这路流就需要filter。

注意:如果用mp4封装h265编码的视频文件,转码为flv,会出现问题,flv默认是不支持h265封装。

3.重点函数源码分析

这里讲解的主要是输出文件。

static int open_files(OptionGroupList *l const char *inout int (*open_file)(OptionsContext* const char*)) { int i ret; for (i = 0; i < l->nb_groups; i ) { OptionGroup *g = &l->groups[i]; OptionsContext o; // 保存命令行被解析后的信息,对应一个输入或者输出文件 init_options(&o); // 从OptionGroup解析出来的 o.g = g; ret = parse_optgroup(&o g); // 解析参数 从OptionGroup解析出来的 if (ret < 0) { av_log(NULL AV_LOG_ERROR "Error parsing options for %s file " "%s.\n" inout g->arg); return ret; } av_log(NULL AV_LOG_DEBUG "Opening an %s file: %s.\n" inout g->arg); ret = open_file(&o g->arg); // 一次只打开一路码流? uninit_options(&o); if (ret < 0) { av_log(NULL AV_LOG_ERROR "Error opening %s file %s.\n" inout g->arg); return ret; } av_log(NULL AV_LOG_DEBUG "Successfully opened the file.\n"); } return 0; }

int parse_optgroup(void *optctx OptionGroup *g) { int i ret; av_log(NULL AV_LOG_DEBUG "\nParsing a group of options: %s %s.\n" g->group_def->name g->arg); for (i = 0; i < g->nb_opts; i ) { Option *o = &g->opts[i]; // 读取全局参数 if (g->group_def->flags && !(g->group_def->flags & o->opt->flags)) { av_log(NULL AV_LOG_ERROR "Option %s (%s) cannot be applied to " "%s %s -- you are trying to apply an input option to an " "output file or vice versa. Move this option before the " "file it belongs to.\n" o->key o->opt->help g->group_def->name g->arg); return AVERROR(EINVAL); } // 打印参数 av_log(NULL AV_LOG_DEBUG "Applying option %s (%s) with argument %s.\n" o->key o->opt->help o->val); ret = write_option(optctx o->opt o->key o->val); if (ret < 0) return ret; } av_log(NULL AV_LOG_DEBUG "Successfully parsed a group of options.\n\n"); return 0; }

static int choose_encoder(OptionsContext *o AVFormatContext *s OutputStream *ost) { enum AVMediaType type = ost->st->codecpar->codec_type; char *codec_name = NULL; if (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO || type == AVMEDIA_TYPE_SUBTITLE) { // MATCH_PER_STREAM_OPT(codec_names str codec_name s ost->st); // 展开MATCH_PER_STREAM_OPT int i ret; for (i = 0; i < o->nb_codec_names; i ) { char *spec = o->codec_names[i].specifier; //检查video或audio流 if ((ret = check_stream_specifier(s ost->st spec)) > 0) codec_name = o->codec_names[i].u.str; else if (ret < 0) exit_program(1); } if (!codec_name) { ost->st->codecpar->codec_id = av_guess_codec(s->oformat NULL s->url NULL ost->st->codecpar->codec_type); ost->enc = avcodec_find_encoder(ost->st->codecpar->codec_id);// 具体还要看存不存在 if (!ost->enc) { av_log(NULL AV_LOG_FATAL "Automatic encoder selection failed for " "output stream #%d:%d. Default encoder for format %s (codec %s) is " "probably disabled. Please choose an encoder manually.\n" ost->file_index ost->index s->oformat->name avcodec_get_name(ost->st->codecpar->codec_id)); return AVERROR_ENCODER_NOT_FOUND; } } else if (!strcmp(codec_name "copy")) ost->stream_copy = 1; else { ost->enc = find_codec_or_die(codec_name ost->st->codecpar->codec_type 1); ost->st->codecpar->codec_id = ost->enc->id; } ost->encoding_needed = !ost->stream_copy; // 需要编码 } else { /* no encoding supported for other media types */ ost->stream_copy = 1; // 不需要重新编码 ost->encoding_needed = 0; } return 0; }

// audio/video共用 static OutputStream *new_output_stream(OptionsContext *o AVFormatContext *oc enum AVMediaType type int source_index) { OutputStream *ost; AVStream *st = avformat_new_stream(oc NULL); int idx = oc->nb_streams - 1 ret = 0; const char *bsfs = NULL *time_base = NULL; char *next *codec_tag = NULL; double qscale = -1; int i; if (!st) { av_log(NULL AV_LOG_FATAL "Could not alloc stream.\n"); exit_program(1); } if (oc->nb_streams - 1 < o->nb_streamid_map) st->id = o->streamid_map[oc->nb_streams - 1]; GROW_ARRAY(output_streams nb_output_streams); //realloc if (!(ost = av_mallocz(sizeof(*ost)))) exit_program(1); output_streams[nb_output_streams - 1] = ost; ost->file_index = nb_output_files - 1; // 对应的输出文件是哪个 ost->index = idx; ost->st = st; ost->forced_kf_ref_pts = AV_NOPTS_VALUE; st->codecpar->codec_type = type; ret = choose_encoder(o oc ost); if (ret < 0) { av_log(NULL AV_LOG_FATAL "Error selecting an encoder for stream " "%d:%d\n" ost->file_index ost->index); exit_program(1); } ost->enc_ctx = avcodec_alloc_context3(ost->enc); if (!ost->enc_ctx) { av_log(NULL AV_LOG_ERROR "Error allocating the encoding context.\n"); exit_program(1); } ost->enc_ctx->codec_type = type; ost->ref_par = avcodec_parameters_alloc(); if (!ost->ref_par) { av_log(NULL AV_LOG_ERROR "Error allocating the encoding parameters.\n"); exit_program(1); } if (ost->enc) { AVIOContext *s = NULL; char *buf = NULL *arg = NULL *preset = NULL; ost->encoder_opts = filter_codec_opts(o->g->codec_opts ost->enc->id oc st ost->enc); MATCH_PER_STREAM_OPT(presets str preset oc st); if (preset && (!(ret = get_preset_file_2(preset ost->enc->name &s)))) { do { buf = get_line(s); if (!buf[0] || buf[0] == '#') { av_free(buf); continue; } if (!(arg = strchr(buf '='))) { av_log(NULL AV_LOG_FATAL "Invalid line found in the preset file.\n"); exit_program(1); } *arg = 0; av_dict_set(&ost->encoder_opts buf arg AV_DICT_DONT_OVERWRITE); av_free(buf); } while (!s->eof_reached); avio_closep(&s); } if (ret) { av_log(NULL AV_LOG_FATAL "Preset %s specified for stream %d:%d but could not be opened.\n" preset ost->file_index ost->index); exit_program(1); } } else { ost->encoder_opts = filter_codec_opts(o->g->codec_opts AV_CODEC_ID_NONE oc st NULL); } if (o->bitexact) ost->enc_ctx->flags |= AV_CODEC_FLAG_BITEXACT; MATCH_PER_STREAM_OPT(time_bases str time_base oc st); if (time_base) { AVRational q; if (av_parse_ratio(&q time_base INT_MAX 0 NULL) < 0 || q.num <= 0 || q.den <= 0) { av_log(NULL AV_LOG_FATAL "Invalid time base: %s\n" time_base); exit_program(1); } st->time_base = q; } MATCH_PER_STREAM_OPT(enc_time_bases str time_base oc st); if (time_base) { AVRational q; if (av_parse_ratio(&q time_base INT_MAX 0 NULL) < 0 || q.den <= 0) { av_log(NULL AV_LOG_FATAL "Invalid time base: %s\n" time_base); exit_program(1); } ost->enc_timebase = q; } ost->max_frames = INT64_MAX; MATCH_PER_STREAM_OPT(max_frames i64 ost->max_frames oc st); for (i = 0; i<o->nb_max_frames; i ) { char *p = o->max_frames[i].specifier; if (!*p && type != AVMEDIA_TYPE_VIDEO) { av_log(NULL AV_LOG_WARNING "Applying unspecific -frames to non video streams maybe you meant -vframes ?\n"); break; } } ost->copy_prior_start = -1; MATCH_PER_STREAM_OPT(copy_prior_start i ost->copy_prior_start oc st); MATCH_PER_STREAM_OPT(bitstream_filters str bsfs oc st); while (bsfs && *bsfs) { const AVBitStreamFilter *filter; char *bsf *bsf_options_str *bsf_name; bsf = av_get_token(&bsfs " "); if (!bsf) exit_program(1); bsf_name = av_strtok(bsf "=" &bsf_options_str); if (!bsf_name) exit_program(1); filter = av_bsf_get_by_name(bsf_name); if (!filter) { av_log(NULL AV_LOG_FATAL "Unknown bitstream filter %s\n" bsf_name); exit_program(1); } ost->bsf_ctx = av_realloc_array(ost->bsf_ctx ost->nb_bitstream_filters 1 sizeof(*ost->bsf_ctx)); if (!ost->bsf_ctx) exit_program(1); ret = av_bsf_alloc(filter &ost->bsf_ctx[ost->nb_bitstream_filters]); if (ret < 0) { av_log(NULL AV_LOG_ERROR "Error allocating a bitstream filter context\n"); exit_program(1); } ost->nb_bitstream_filters ; if (bsf_options_str && filter->priv_class) { const AVOption *opt = av_opt_next(ost->bsf_ctx[ost->nb_bitstream_filters-1]->priv_data NULL); const char * shorthand[2] = {NULL}; if (opt) shorthand[0] = opt->name; ret = av_opt_set_from_string(ost->bsf_ctx[ost->nb_bitstream_filters-1]->priv_data bsf_options_str shorthand "=" ":"); if (ret < 0) { av_log(NULL AV_LOG_ERROR "Error parsing options for bitstream filter %s\n" bsf_name); exit_program(1); } } av_freep(&bsf); if (*bsfs) bsfs ; } MATCH_PER_STREAM_OPT(codec_tags str codec_tag oc st); if (codec_tag) { uint32_t tag = strtol(codec_tag &next 0); if (*next) tag = AV_RL32(codec_tag); ost->st->codecpar->codec_tag = ost->enc_ctx->codec_tag = tag; } MATCH_PER_STREAM_OPT(qscale dbl qscale oc st); if (qscale >= 0) { ost->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE; ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale; } MATCH_PER_STREAM_OPT(disposition str ost->disposition oc st); ost->disposition = av_strdup(ost->disposition); ost->max_muxing_queue_size = 128; MATCH_PER_STREAM_OPT(max_muxing_queue_size i ost->max_muxing_queue_size oc st); ost->max_muxing_queue_size *= sizeof(AVPacket); if (oc->oformat->flags & AVFMT_GLOBALHEADER) ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; av_dict_copy(&ost->sws_dict o->g->sws_dict 0); av_dict_copy(&ost->swr_opts o->g->swr_opts 0); if (ost->enc && av_get_exact_bits_per_sample(ost->enc->id) == 24) av_dict_set(&ost->swr_opts "output_sample_bits" "24" 0); av_dict_copy(&ost->resample_opts o->g->resample_opts 0); ost->source_index = source_index; if (source_index >= 0) { ost->sync_ist = input_streams[source_index]; input_streams[source_index]->discard = 0; input_streams[source_index]->st->discard = input_streams[source_index]->user_set_discard; } ost->last_mux_dts = AV_NOPTS_VALUE; ost->muxing_queue = av_fifo_alloc(8 * sizeof(AVPacket)); // 用来写入输出我觉得时候用得上 if (!ost->muxing_queue) exit_program(1); return ost; }

无论是否要重新编解码,都需要走transcode_init。

static int transcode_init(void) { int ret = 0 i j k; AVFormatContext *oc; // 输出流的编解码器结构 OutputStream *ost; // 输出流 InputStream *ist; // 输入流 char error[1024] = {0}; // 初始化filter for (i = 0; i < nb_filtergraphs; i ) { FilterGraph *fg = filtergraphs[i]; for (j = 0; j < fg->nb_outputs; j ) { OutputFilter *ofilter = fg->outputs[j]; if (!ofilter->ost || ofilter->ost->source_index >= 0) continue; if (fg->nb_inputs != 1) continue; for (k = nb_input_streams-1; k >= 0 ; k--) if (fg->inputs[0]->ist == input_streams[k]) break; ofilter->ost->source_index = k; } } // 初始化输入文件 文件打开 /* init framerate emulation */ for (i = 0; i < nb_input_files; i ) { InputFile *ifile = input_files[i]; // 查找对应的文件 if (ifile->rate_emu) // 初始化帧率仿真,主要是实时推流的时候使用,可以按播放速度去推流 for (j = 0; j < ifile->nb_streams; j ) {// 初始化start时间 // ifile->ist_index为 该file在input_streams数组的起始索引 input_streams[j ifile->ist_index]->start = av_gettime_relative(); // 记录起始时间 } } // 初始化输入流 /* init input streams */ for (i = 0; i < nb_input_streams; i ) // 每个流的都是独立处理 if ((ret = init_input_stream(i error sizeof(error))) < 0) { for (i = 0; i < nb_output_streams; i ) { ost = output_streams[i]; avcodec_close(ost->enc_ctx); } goto dump_format; } // 初始化输出流,根据对应的输入流,设置其编码器的参数 /* open each encoder */ for (i = 0; i < nb_output_streams; i ) { // skip streams fed from filtergraphs until we have a frame for them if (output_streams[i]->filter) // filter先暂且不理会 continue; // 初始化每个流 ret = init_output_stream(output_streams[i] error sizeof(error)); if (ret < 0) goto dump_format; } /* discard unused programs */ for (i = 0; i < nb_input_files; i ) { InputFile *ifile = input_files[i]; for (j = 0; j < ifile->ctx->nb_programs; j ) { AVProgram *p = ifile->ctx->programs[j]; int discard = AVDISCARD_ALL; for (k = 0; k < p->nb_stream_indexes; k ) if (!input_streams[ifile->ist_index p->stream_index[k]]->discard) { discard = AVDISCARD_DEFAULT; break; } p->discard = discard; } } /* write headers for files with no streams */ for (i = 0; i < nb_output_files; i ) { oc = output_files[i]->ctx; if (oc->oformat->flags & AVFMT_NOSTREAMS && oc->nb_streams == 0) { ret = check_init_output_file(output_files[i] i); if (ret < 0) goto dump_format; } } dump_format: /* dump the stream mapping */ av_log(NULL AV_LOG_INFO "Stream mapping:\n"); for (i = 0; i < nb_input_streams; i ) { ist = input_streams[i]; for (j = 0; j < ist->nb_filters; j ) { if (!filtergraph_is_simple(ist->filters[j]->graph)) { av_log(NULL AV_LOG_INFO " Stream #%d:%d (%s) -> %s" ist->file_index ist->st->index ist->dec ? ist->dec->name : "?" ist->filters[j]->name); if (nb_filtergraphs > 1) av_log(NULL AV_LOG_INFO " (graph %d)" ist->filters[j]->graph->index); av_log(NULL AV_LOG_INFO "\n"); } } } for (i = 0; i < nb_output_streams; i ) { ost = output_streams[i]; if (ost->attachment_filename) { /* an attached file */ av_log(NULL AV_LOG_INFO " File %s -> Stream #%d:%d\n" ost->attachment_filename ost->file_index ost->index); continue; } if (ost->filter && !filtergraph_is_simple(ost->filter->graph)) { /* output from a complex graph */ av_log(NULL AV_LOG_INFO " %s" ost->filter->name); if (nb_filtergraphs > 1) av_log(NULL AV_LOG_INFO " (graph %d)" ost->filter->graph->index); av_log(NULL AV_LOG_INFO " -> Stream #%d:%d (%s)\n" ost->file_index ost->index ost->enc ? ost->enc->name : "?"); continue; } av_log(NULL AV_LOG_INFO " Stream #%d:%d -> #%d:%d" input_streams[ost->source_index]->file_index input_streams[ost->source_index]->st->index ost->file_index ost->index); if (ost->sync_ist != input_streams[ost->source_index]) av_log(NULL AV_LOG_INFO " [sync #%d:%d]" ost->sync_ist->file_index ost->sync_ist->st->index); if (ost->stream_copy) av_log(NULL AV_LOG_INFO " (copy)"); else { const AVCodec *in_codec = input_streams[ost->source_index]->dec; const AVCodec *out_codec = ost->enc; const char *decoder_name = "?"; const char *in_codec_name = "?"; const char *encoder_name = "?"; const char *out_codec_name = "?"; const AVCodecDescriptor *desc; if (in_codec) { decoder_name = in_codec->name; desc = avcodec_descriptor_get(in_codec->id); if (desc) in_codec_name = desc->name; if (!strcmp(decoder_name in_codec_name)) decoder_name = "native"; } if (out_codec) { encoder_name = out_codec->name; desc = avcodec_descriptor_get(out_codec->id); if (desc) out_codec_name = desc->name; if (!strcmp(encoder_name out_codec_name)) encoder_name = "native"; } av_log(NULL AV_LOG_INFO " (%s (%s) -> %s (%s))" in_codec_name decoder_name out_codec_name encoder_name); } av_log(NULL AV_LOG_INFO "\n"); } if (ret) { av_log(NULL AV_LOG_ERROR "%s\n" error); return ret; } atomic_store(&transcode_init_done 1); return 0; }

初始化每个编码器。如果是不重新编解码,那就直接走init_output_stream_streamcopy(ost);

// 初始化输出stream,主要是初始化编码器 static int init_output_stream(OutputStream *ost char *error int error_len) { int ret = 0; if (ost->encoding_needed) { // 如果需要重新编码 AVCodec *codec = ost->enc; AVCodecContext *dec = NULL; InputStream *ist; // 对应输入stream的编码信息 ret = init_output_stream_encode(ost); // if (ret < 0) return ret; if ((ist = get_input_stream(ost))) dec = ist->dec_ctx; if (dec && dec->subtitle_header) { /* ASS code assumes this buffer is null terminated so add extra byte. */ ost->enc_ctx->subtitle_header = av_mallocz(dec->subtitle_header_size 1); if (!ost->enc_ctx->subtitle_header) return AVERROR(ENOMEM); memcpy(ost->enc_ctx->subtitle_header dec->subtitle_header dec->subtitle_header_size); ost->enc_ctx->subtitle_header_size = dec->subtitle_header_size; } if (!av_dict_get(ost->encoder_opts "threads" NULL 0)) av_dict_set(&ost->encoder_opts "threads" "auto" 0); if (ost->enc->type == AVMEDIA_TYPE_AUDIO && !codec->defaults && !av_dict_get(ost->encoder_opts "b" NULL 0) && !av_dict_get(ost->encoder_opts "ab" NULL 0)) av_dict_set(&ost->encoder_opts "b" "128000" 0);//默认128kbps if (ost->filter && av_buffersink_get_hw_frames_ctx(ost->filter->filter) && ((AVHWFramesContext*)av_buffersink_get_hw_frames_ctx(ost->filter->filter)->data)->format == av_buffersink_get_format(ost->filter->filter)) { ost->enc_ctx->hw_frames_ctx = av_buffer_ref(av_buffersink_get_hw_frames_ctx(ost->filter->filter)); if (!ost->enc_ctx->hw_frames_ctx) return AVERROR(ENOMEM); } else { ret = hw_device_setup_for_encode(ost); if (ret < 0) { snprintf(error error_len "Device setup failed for " "encoder on output stream #%d:%d : %s" ost->file_index ost->index av_err2str(ret)); return ret; } } if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && ost->enc->type == AVMEDIA_TYPE_SUBTITLE) { int input_props = 0 output_props = 0; AVCodecDescriptor const *input_descriptor = avcodec_descriptor_get(dec->codec_id); AVCodecDescriptor const *output_descriptor = avcodec_descriptor_get(ost->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) { snprintf(error error_len "Subtitle encoding currently only possible from text to text " "or bitmap to bitmap"); return AVERROR_INVALIDDATA; } } // 根据设置的参数打开编码, encoder_opts的信息主要来自命令行的设置 if ((ret = avcodec_open2(ost->enc_ctx codec &ost->encoder_opts)) < 0) { if (ret == AVERROR_EXPERIMENTAL) abort_codec_experimental(codec 1); snprintf(error error_len "Error while opening encoder for output stream #%d:%d - " "maybe incorrect parameters such as bit_rate rate width or height" ost->file_index ost->index); return ret; } if (ost->enc->type == AVMEDIA_TYPE_AUDIO && !(ost->enc->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) av_buffersink_set_frame_size(ost->filter->filter 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(NULL AV_LOG_WARNING "The bitrate parameter is set too low." " It takes bits/s as argument not kbits/s\n"); // 将编码器的信息拷贝到stream的codecpar,以便写复用的时候使用,拷贝到输出流 ret = avcodec_parameters_from_context(ost->st->codecpar ost->enc_ctx); if (ret < 0) { av_log(NULL AV_LOG_FATAL "Error initializing the output stream codec context.\n"); exit_program(1); } /* * FIXME: ost->st->codec should't be needed here anymore. */ ret = avcodec_copy_context(ost->st->codec ost->enc_ctx); if (ret < 0) return ret; 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]; 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((uint32_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->st->codec->codec= ost->enc_ctx->codec; } else if (ost->stream_copy) { //不重新编解码 ret = init_output_stream_streamcopy(ost); if (ret < 0) return ret; } // parse user provided disposition and update stream values if (ost->disposition) { static const AVOption opts[] = { { "disposition" NULL 0 AV_OPT_TYPE_FLAGS { .i64 = 0 } INT64_MIN INT64_MAX .unit = "flags" } { "default" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_DEFAULT } .unit = "flags" } { "dub" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_DUB } .unit = "flags" } { "original" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_ORIGINAL } .unit = "flags" } { "comment" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_COMMENT } .unit = "flags" } { "lyrics" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_LYRICS } .unit = "flags" } { "karaoke" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_KARAOKE } .unit = "flags" } { "forced" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_FORCED } .unit = "flags" } { "hearing_impaired" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_HEARING_IMPAIRED } .unit = "flags" } { "visual_impaired" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_VISUAL_IMPAIRED } .unit = "flags" } { "clean_effects" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_CLEAN_EFFECTS } .unit = "flags" } { "attached_pic" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_ATTACHED_PIC } .unit = "flags" } { "captions" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_CAPTIONS } .unit = "flags" } { "descriptions" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_DESCRIPTIONS } .unit = "flags" } { "dependent" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_DEPENDENT } .unit = "flags" } { "metadata" NULL 0 AV_OPT_TYPE_CONST { .i64 = AV_DISPOSITION_METADATA } .unit = "flags" } { NULL } }; static const AVClass class = { .class_name = "" .item_name = av_default_item_name .option = opts .version = LIBAVUTIL_VERSION_INT }; const AVClass *pclass = &class; ret = av_opt_eval_flags(&pclass &opts[0] ost->disposition &ost->st->disposition); if (ret < 0) return ret; } ret = init_output_bsfs(ost); if (ret < 0) return ret; ost->initialized = 1; // 流被初始化 ret = check_init_output_file(output_files[ost->file_index] ost->file_index); if (ret < 0) return ret; return ret; }

不重新编解码,直接使用以前编码的数据,仅仅换封装。这里会有些缺省的代码,可以参考源码。

static int init_output_stream_streamcopy(OutputStream *ost) { OutputFile *of = output_files[ost->file_index]; InputStream *ist = get_input_stream(ost); AVCodecParameters *par_dst = ost->st->codecpar; AVCodecParameters *par_src = ost->ref_par; AVRational sar; int i ret; uint32_t codec_tag = par_dst->codec_tag; av_assert0(ist && !ost->filter); ret = avcodec_parameters_to_context(ost->enc_ctx ist->st->codecpar); if (ret >= 0) ret = av_opt_set_dict(ost->enc_ctx &ost->encoder_opts); if (ret < 0) { av_log(NULL AV_LOG_FATAL "Error setting up codec context options.\n"); return ret; } //从编码器中拷贝上下文参数 ret = avcodec_parameters_from_context(par_src ost->enc_ctx); 。。。。。。。。。。。。。。 ret = avcodec_parameters_copy(par_dst par_src); if (ret < 0) return ret; par_dst->codec_tag = codec_tag; if (!ost->frame_rate.num) ost->frame_rate = ist->framerate; ost->st->avg_frame_rate = ost->frame_rate; ret = avformat_transfer_internal_stream_timing_info(of->ctx->oformat ost->st ist->st copy_tb); if (ret < 0) return ret; // 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(av_stream_get_codec_timebase(ost->st) (AVRational){0 1}); // copy estimated duration as a hint to the muxer if (ost->st->duration <= 0 && ist->st->duration > 0) ost->st->duration = av_rescale_q(ist->st->duration ist->st->time_base ost->st->time_base); // copy disposition ost->st->disposition = ist->st->disposition; if (ist->st->nb_side_data) { for (i = 0; i < ist->st->nb_side_data; i ) { const AVPacketSideData *sd_src = &ist->st->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); } } if (ost->rotate_overridden) { uint8_t *sd = av_stream_new_side_data(ost->st AV_PKT_DATA_DISPLAYMATRIX sizeof(int32_t) * 9); if (sd) av_display_rotation_set((int32_t *)sd -ost->rotate_override_value); } switch (par_dst->codec_type) { case AVMEDIA_TYPE_AUDIO: if (audio_volume != 256) { av_log(NULL AV_LOG_FATAL "-acodec copy and -vol are incompatible (frames are not decoded)\n"); exit_program(1); } if((par_dst->block_align == 1 || par_dst->block_align == 1152 || par_dst->block_align == 576) && par_dst->codec_id == AV_CODEC_ID_MP3) par_dst->block_align= 0; if(par_dst->codec_id == AV_CODEC_ID_AC3) par_dst->block_align= 0; break; case AVMEDIA_TYPE_VIDEO: if (ost->frame_aspect_ratio.num) { // overridden by the -aspect cli option sar = av_mul_q(ost->frame_aspect_ratio (AVRational){ par_dst->height par_dst->width }); av_log(NULL AV_LOG_WARNING "Overriding aspect ratio " "with stream copy may produce invalid files\n"); } else if (ist->st->sample_aspect_ratio.num) sar = ist->st->sample_aspect_ratio; else sar = par_src->sample_aspect_ratio; ost->st->sample_aspect_ratio = par_dst->sample_aspect_ratio = sar; ost->st->avg_frame_rate = ist->st->avg_frame_rate; ost->st->r_frame_rate = ist->st->r_frame_rate; break; } ost->mux_timebase = ist->st->time_base; return 0; }

transcode_init执行完后,就执行transcode_step,这个在前面的文章有分析过。

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(7)

执行reap_filters(0);如果不使用过滤器,就不会使用这个。

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(8)

直接拷贝以前编码后的码流,并输出packet。

static void do_streamcopy(InputStream *ist OutputStream *ost const AVPacket *pkt) { OutputFile *of = output_files[ost->file_index]; InputFile *f = input_files [ist->file_index]; int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time; int64_t ost_tb_start_time = av_rescale_q(start_time AV_TIME_BASE_Q ost->mux_timebase); AVPacket opkt = { 0 }; av_init_packet(&opkt); // EOF: flush output bitstream filters. if (!pkt) { output_packet(of &opkt ost 1); return; } if ((!ost->frame_number && !(pkt->flags & AV_PKT_FLAG_KEY)) && !ost->copy_initial_nonkeyframes) return; if (!ost->frame_number && !ost->copy_prior_start) { int64_t comp_start = start_time; if (copy_ts && f->start_time != AV_NOPTS_VALUE) comp_start = FFMAX(start_time f->start_time f->ts_offset); if (pkt->pts == AV_NOPTS_VALUE ? ist->pts < comp_start : // 由微妙转成AVStream time_base pkt->pts < av_rescale_q(comp_start AV_TIME_BASE_Q ist->st->time_base)) return; } if (of->recording_time != INT64_MAX && ist->pts >= of->recording_time start_time) { close_output_stream(ost); return; } if (f->recording_time != INT64_MAX) { start_time = f->ctx->start_time; if (f->start_time != AV_NOPTS_VALUE && copy_ts) start_time = f->start_time; if (ist->pts >= f->recording_time start_time) { close_output_stream(ost); return; } } /* force the input stream PTS */ if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) ost->sync_opts ; if (pkt->pts != AV_NOPTS_VALUE) opkt.pts = av_rescale_q(pkt->pts ist->st->time_base ost->mux_timebase) - ost_tb_start_time; else opkt.pts = AV_NOPTS_VALUE; if (pkt->dts == AV_NOPTS_VALUE) opkt.dts = av_rescale_q(ist->dts AV_TIME_BASE_Q ost->mux_timebase); else //时间基转换,转换为ost->mux_timebase opkt.dts = av_rescale_q(pkt->dts ist->st->time_base ost->mux_timebase); opkt.dts -= ost_tb_start_time; if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && pkt->dts != AV_NOPTS_VALUE) { int duration = av_get_audio_frame_duration(ist->dec_ctx pkt->size); if(!duration) duration = ist->dec_ctx->frame_size; opkt.dts = opkt.pts = av_rescale_delta(ist->st->time_base pkt->dts (AVRational){1 ist->dec_ctx->sample_rate} duration &ist->filter_in_rescale_delta_last ost->mux_timebase) - ost_tb_start_time; } opkt.duration = av_rescale_q(pkt->duration ist->st->time_base ost->mux_timebase); opkt.flags = pkt->flags; if (pkt->buf) { opkt.buf = av_buffer_ref(pkt->buf); if (!opkt.buf) exit_program(1); } opkt.data = pkt->data; opkt.size = pkt->size; av_copy_packet_side_data(&opkt pkt); //输出packet output_packet(of &opkt ost 0); }

本篇关于码流重新编解码源码分析的文章就分析到这里,欢迎,关注,点赞,转发,收藏。

想了解更多知识,可以关注微信公众号 “记录世界 from antonio”

mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)(9)

猜您喜欢: