mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)
mp4转flv后不清晰(超详细Mp4转Flv之timebase分析)video部分调用栈如下,这个调用栈是需要重新编码:2.查看对应输出编码格式1.首先解析命令行如直接copy和编码。如下解析结果:指定再次编码,并指定格式。如下解析结果:
阅读本文前,可以看看前面几篇文章。
超详细ffmpeg.c框架分析
超详细ffmpeg.c命令行分析
超详细Mp4转Flv之timebase分析
1.首先解析命令行
如直接copy和编码。如下解析结果:
指定再次编码,并指定格式。如下解析结果:
2.查看对应输出编码格式
video部分调用栈如下,这个调用栈是需要重新编码:
如果不需要重新编码,调用栈流程,就简单些。如图所示:
不需要重新编码,输出流的调用栈。
关于直接copy的时间基的转换,st->time_base转换为ost->mux_timebase。最后写到文件或发送网络流。详细部分,可以参考前面的文章。
一般那路流指定要重新编解码,那么这路流就需要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,这个在前面的文章有分析过。
执行reap_filters(0);如果不使用过滤器,就不会使用这个。
直接拷贝以前编码后的码流,并输出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”