五、transcode 函数
transcode 用于实现媒体文件转码的函数之一。转码是指将一个媒体文件从一种编码格式转换为另一种编码格式的过程。这可以包括视频编解码器、音频编解码器、容器格式或其他媒体属性的更改。
其主要包括以下两个核心函数:
- transcode_init()
- 初始化,打开所有输出流的编码器,打开所有输入流的解码器,写入所有输出文件的文件头。
- transcode_step()
- 于实现 FFmpeg 转码过程中的一个步骤的函数
1、transcode_init 函数
初始化工作:
- AVFormatContext *oc;//输出流的编解码器结构
- OutputStream *ost;//输出流
- InputStream *ist; //输入流
- init_input_stream
- init_output_stream
//transcode_init()函数是在转换前做准备工作的 static int transcode_init(void) { int ret = 0, i, j, k; AVFormatContext *oc;//输出流的编解码器结构 OutputStream *ost; //输出流 InputStream *ist; //输入流 char error[1024] = {0}; 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++) 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) 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; }
static int init_input_stream(int ist_index, char *error, int error_len) { int ret; InputStream *ist = input_streams[ist_index]; if (ist->decoding_needed) { AVCodec *codec = ist->dec; if (!codec) { snprintf(error, error_len, "Decoder (codec %s) not found for input stream #%d:%d", avcodec_get_name(ist->dec_ctx->codec_id), ist->file_index, ist->st->index); return AVERROR(EINVAL); } ist->dec_ctx->opaque = ist; ist->dec_ctx->get_format = get_format; ist->dec_ctx->get_buffer2 = get_buffer; ist->dec_ctx->thread_safe_callbacks = 1; av_opt_set_int(ist->dec_ctx, "refcounted_frames", 1, 0); if (ist->dec_ctx->codec_id == AV_CODEC_ID_DVB_SUBTITLE && (ist->decoding_needed & DECODING_FOR_OST)) { av_dict_set(&ist->decoder_opts, "compute_edt", "1", AV_DICT_DONT_OVERWRITE); if (ist->decoding_needed & DECODING_FOR_FILTER) av_log(NULL, AV_LOG_WARNING, "Warning using DVB subtitles for filtering and output at the same time is not fully supported, also see -compute_edt [0|1]\n"); } av_dict_set(&ist->decoder_opts, "sub_text_format", "ass", AV_DICT_DONT_OVERWRITE); /* Useful for subtitles retiming by lavf (FIXME), skipping samples in * audio, and video decoders such as cuvid or mediacodec */ ist->dec_ctx->pkt_timebase = ist->st->time_base; if (!av_dict_get(ist->decoder_opts, "threads", NULL, 0)) av_dict_set(&ist->decoder_opts, "threads", "auto", 0); /* Attached pics are sparse, therefore we would not want to delay their decoding till EOF. */ if (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC) av_dict_set(&ist->decoder_opts, "threads", "1", 0); ret = hw_device_setup_for_decode(ist); if (ret < 0) { snprintf(error, error_len, "Device setup failed for " "decoder on input stream #%d:%d : %s", ist->file_index, ist->st->index, av_err2str(ret)); return ret; } //打开解码器 if ((ret = avcodec_open2(ist->dec_ctx, codec, &ist->decoder_opts)) < 0) { if (ret == AVERROR_EXPERIMENTAL) abort_codec_experimental(codec, 0); snprintf(error, error_len, "Error while opening decoder for input stream " "#%d:%d : %s", ist->file_index, ist->st->index, av_err2str(ret)); return ret; } assert_avoptions(ist->decoder_opts); } ist->next_pts = AV_NOPTS_VALUE; ist->next_dts = AV_NOPTS_VALUE; return 0; }
2、transcode_step 函数
/** * Run a single step of transcoding. * * @return 0 for success, <0 for error */ /* 解码流程是: process_input() -> output_packet() -> decode_audio()/decode_video()/transcode_subtitles() 而decode_audio() 是调用 avcodec_decode_audio4() 来完成工作的。 decode_video() 则是通过调用 avcodec_decode_video2() 来完成的。 编码流程是: reap_filters() -> do_video_out()或 do_audio_out() -> avcodec_encode_video2() 或 avcodec_encode_audio2()。 */ static int transcode_step(void) { OutputStream *ost; InputStream *ist = NULL; int ret; //选择一个有效的输出流进行处理 ost = choose_output(); if (!ost) { if (got_eagain()) { reset_eagain(); av_usleep(10000); return 0; } av_log(NULL, AV_LOG_VERBOSE, "No more inputs to read from, finishing.\n"); return AVERROR_EOF; } //选择一个输入流 if (ost->filter && !ost->filter->graph->graph) { if (ifilter_has_all_input_formats(ost->filter->graph)) { ret = configure_filtergraph(ost->filter->graph); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n"); return ret; } } } if (ost->filter && ost->filter->graph->graph) { if (!ost->initialized) { char error[1024] = {0}; ret = init_output_stream(ost, error, sizeof(error)); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error initializing output stream %d:%d -- %s\n", ost->file_index, ost->index, error); exit_program(1); } } if ((ret = transcode_from_filter(ost->filter->graph, &ist)) < 0) return ret; if (!ist) return 0; } else if (ost->filter) { int i; for (i = 0; i < ost->filter->graph->nb_inputs; i++) { InputFilter *ifilter = ost->filter->graph->inputs[i]; if (!ifilter->ist->got_output && !input_files[ifilter->ist->file_index]->eof_reached) { ist = ifilter->ist; break; } } if (!ist) { ost->inputs_done = 1; return 0; } } else { av_assert0(ost->source_index >= 0); ist = input_streams[ost->source_index]; } //读取并处理每一个包 ret = process_input(ist->file_index); if (ret == AVERROR(EAGAIN)) { if (input_files[ist->file_index]->eagain) ost->unavailable = 1; return 0; } if (ret < 0) return ret == AVERROR_EOF ? 0 : ret; //根据滤波器做滤波处理,并把处理完的音视频输出到输出文件中 return reap_filters(0); }