muxing.c 解读
------------------------------------------------------------
很好的演示代码,生成媒体文件.是音频编码,视频编码,复用的组合体.
代码略有名称修改,方面阅读
1. 首先,创建一个avf_ctx,
avformat_alloc_output_context2(&avf_ctx, NULL, NULL, filename);
关联了filename.
于是可得到它的输出格式AVOutputFormat* o_fmt;
o_fmt = avf_ctx->oformat;
2. 添加一个视频流, 音频流
add_stream(&video_st, avf_ctx, &video_codec, o_fmt->video_codec);
这个add_stream 是自定义函数, 输入avf_ctx, o_fmt->video_codec为codec_id
输出:video_st, 自定义结构, 将一些指针收集起来方便使用.
输出:video_codec, AVCodec 结构
具体创建过程可继续分解为:
//创建一个stream
ost->st = avformat_new_stream(avf_ctx, NULL);
ost->st->id = avf_ctx->nb_streams-1; // stream->id
//由codec_id 可以找到codec
codec = avcodec_find_encoder(codec_id);
//由codec 可创建codec_ctx
c = avcodec_alloc_context3(codec);
//context 保存
ost->enc = c;
//对codec_ctx 进行初始化,进行填充,例如对于视频
c->codec_id = codec_id;
c->bit_rate = 400000;
c->width = 352;
c->height = 288;
/* timebase: This is the fundamental unit of time (in seconds) in terms
* of which frame timestamps are represented. For fixed-fps content,
* timebase should be 1/framerate and timestamp increments should be
* identical to 1. */
ost->st->time_base = (AVRational){ 1, 25 }; //25帧
c->time_base = ost->st->time_base;
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
c->pix_fmt = AV_PIX_FMT_YUV420P ;
//对于音频,进行另外的一套初始化, 简化一下写法,
c->bit_rate = 64000;
c->sample_fmt = AV_SAMPLE_FMT_FLTP;
c->sample_rate = 44100;
c->channel_layout = AV_CH_LAYOUT_STEREO;
c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
ost->st->time_base = (AVRational){ 1, c->sample_rate };
//填充stream 的codepar
ret = avcodec_parameters_from_context(ost->st->codecpar, c);
音频考虑上重采样,还有
ost->swr_ctx = swr_alloc(); , av_opt_set, swr_init(ost->swr_ctx)
这样初始化过后, 就算打开了video 及 audio
3. 然后可以打印一下状态:
av_dump_format(avf_ctx, 0, filename, 1);
4. 如果需要,打开输出文件, 有可能已经自动打开了.
/* open the output file, if needed */
if (!(o_fmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&avf_ctx->pb, filename, AVIO_FLAG_WRITE);
}
/* Write the stream header, if any. 一些选项就在这里设定了 */
ret = avformat_write_header(avf_ctx, &opt);
5. 根据pts, 开始编码视频frame, 音频frame
5.1: 比较pts,
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b)
其意义是 ts_a * tb_a.num/tb_a.den 数值与 ts_b *tb_b.num/db_b.den 数值比较
就是演播时间的比较, 当然计算上会有一些优化.
(gdb) info args
ts_a = 1 // 视频时间戳
tb_a = {
num = 1, // 视频时基
den = 25
}
ts_b = 1152 // 音频时间戳
tb_b = {
num = 1,
den = 44100 // 音频时基
}
下一个视频是1/25=0.04, 下一个音频是1152/44100=0.026, 音频小,先编音频.
5.2: get_frame, (video_frame or audio_frame)
get_video_frame, 实际是:
fill_yuv_image(ost->frame, ost->next_pts, c->width, c->height);
get_audio_frame 实际是:
int16_t *q = (int16_t*)frame->data[0];
for (j = 0; j <frame->nb_samples; j++) { // 填充1152个数据.
v = (int)(sin(ost->t) * 10000);
for (i = 0; i < ost->enc->channels; i++)
*q++ = v;
ost->t += ost->tincr;
ost->tincr += ost->tincr2;
}
frame->pts = ost->next_pts;
ost->next_pts += frame->nb_samples;
5.3: write_frame
ret = avcodec_send_frame(c, frame);
ret = avcodec_receive_packet(c, &pkt); // 完成数据压缩
av_packet_rescale_ts(&pkt, c->time_base, st->time_base); //调整一下时间戳
pkt.stream_index = st->index;
/* Write the compressed frame to the media file. */
ret = av_interleaved_write_frame(fmt_ctx, &pkt); //写到输出文件