FFmpeg基础知识之音视频编码&封装api接口


FFmpeg的封装模块涉及到对视频数据的封装,也设计到对音频数据的封装,将已经编码好的音频流和视频流封装到一个文件中去,对音视频数据进行封装有很多优点,最重要的优点我认为是方便音视频数据的同步,ffmpeg 的封装模块涉及到了音视频的编码,相对于前面的编码和解码来说,要更加的复杂一些。

音视频封装

  • 从输入文件中读取未编码的视频数据和音频数据
  • 创建编码视频数据和音频数据所需的AVFrame、AVPacket、AVCodecContext等结构体
  • 写入文件头部信息
  • 将未编码的音频数据和视频数据送到编码器编码
  • 根据已经编码的音视频数据时间戳,将音视频数据写入输出文件中
  • 写入文件尾部信息
    avformat_alloc_output_context2(&oc, NULL, NULL, filename); //1、创建输出文件格式上下文,主要用于处理输入输出文件

    if (fmt->video_codec != AV_CODEC_ID_NONE) {
        add_stream(&video_st, oc, &video_codec, fmt->video_codec); //2、创建视频编码初始化流程,类似于前面文章的编码流程
    }
    
    if (fmt->audio_codec != AV_CODEC_ID_NONE) {
        add_stream(&audio_st, oc, &audio_codec, fmt->audio_codec);//2、创建音频编码初始化流程,类似于前面上面的视频编码流程
    }

    open_video(oc, video_codec, &video_st, opt);// 2、初始化视频编码器上下文,拷贝编码器参数到封装器

    open_audio(oc, audio_codec, &audio_st, opt);// 2、同上

    av_dump_format(oc, 0, filename, 1);//打印关于输入文件详细的信息
    
    ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);//创建AVIOContext,并挂在到pb上面,通过AVIOContext可以直接操作输出文件
    
    ret = avformat_write_header(oc, &opt);//3、向输出文件中写入封装格式的信息头结构
    
	av_compare_ts(video_st.next_pts, video_st.enc->time_base,audio_st.next_pts, audio_st.enc->time_base) <= 0)) {//比较音频数据和视频数据时间搓,先写音频数据还是视频数据
            encode_video = !write_video_frame(oc, &video_st);//4&5、编码并写入视频数据到输出文件
        } else {
            encode_audio = !write_audio_frame(oc, &audio_st);//4&5、编码并写入音频数据到输出文件
        }
        
    av_write_trailer(oc);//6、写封装格式文件尾部信息

    close_stream(oc, &video_st);//关闭视频流

    close_stream(oc, &audio_st);//关闭音频流

    avio_closep(&oc->pb);//关闭文件IO操作

    avformat_free_context(oc);//释放格式上下文

上面这些代码音视频封装有了一个框架上的讲解,就是将音视频数据读起来,然后编码,再根据时间戳写到输出文件中去,就这么简单,但是实际上可能还涉及到音视频同步,封装格式首尾信息写入等。

下面还是详细解释一下每个模块具体调用了什么API接口

add_stream

    /* find the encoder */
    *codec = avcodec_find_encoder(codec_id);// 找到编码器

    ost->st = avformat_new_stream(oc, NULL);//创建媒体流结构AVStream,主要用于输出文件中

    c = avcodec_alloc_context3(*codec);//创建编码器上下文结构

    case AVMEDIA_TYPE_AUDIO://设置音频相关参数
        c->sample_fmt  = (*codec)->sample_fmts ?
            (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
        c->bit_rate    = 64000;
        c->sample_rate = 44100;
        c->sample_rate = (*codec)->supported_samplerates[0];
        c->channels        = av_get_channel_layout_nb_channels(c->channel_layout);
        c->channel_layout = AV_CH_LAYOUT_STEREO;
        c->channel_layout = (*codec)->channel_layouts[0];
        c->channels        = av_get_channel_layout_nb_channels(c->channel_layout);
        ost->st->time_base = (AVRational){ 1, c->sample_rate };
        break;

    case AVMEDIA_TYPE_VIDEO://设置视频相关参数
        c->codec_id = codec_id;
        c->bit_rate = 400000;
        c->width    = 352;
        c->height   = 288;
        ost->st->time_base = (AVRational){ 1, STREAM_FRAME_RATE };
        c->time_base       = ost->st->time_base;
        c->gop_size      = 12; /* emit one intra frame every twelve frames at most */
        c->pix_fmt       = STREAM_PIX_FMT;
        c->max_b_frames = 2;
        c->mb_decision = 2;
        }

open_video

	av_dict_copy(&opt, opt_arg, 0);// 拷贝输入命令时设置的字典key和value(常用于设置编码器的相关参数)
    /* open the codec */
    ret = avcodec_open2(c, codec, &opt);//初始化编码器上下文结构,申请编码器上下文数据成员空间
    av_dict_free(&opt);//释放字典
    /* allocate and init a re-usable frame */
    av_frame_get_buffer(picture, 32);//申请编码所必须的buffer空间

    /* copy the stream parameters to the muxer */
    ret = avcodec_parameters_from_context(ost->st->codecpar, c);//将编码器的相关参数信息拷贝到输出流中

open_audio

    av_dict_copy(&opt, opt_arg, 0); // 拷贝输入命令时设置的字典key和value(常用于设置编码器的相关参数)
    ret = avcodec_open2(c, codec, &opt);// 初始化编码器上下文结构,申请编码器上下文数据成员空间
    av_dict_free(&opt);//释放字典数据
    /* init signal generator */
    ost->t     = 0;//初始化相关参数
    ost->tincr = 2 * M_PI * 110.0 / c->sample_rate;
    /* increment frequency by 110 Hz per second */
    ost->tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;
	nb_samples = 10000;
    ost->frame     = alloc_audio_frame(c->sample_fmt, c->channel_layout,c->sample_rate, nb_samples);//申请音频编码所必须的frame buffer空间
    ost->tmp_frame = alloc_audio_frame(AV_SAMPLE_FMT_S16, c->channel_layout,c->sample_rate, nb_samples);

    /* copy the stream parameters to the muxer */
    ret = avcodec_parameters_from_context(ost->st->codecpar, c);//将编码器的相关参数信息拷贝到输出流中
    ost->swr_ctx = swr_alloc();//申请重采样上下文结构
    /* set options *///设置重采样参数
    av_opt_set_int       (ost->swr_ctx, "in_channel_count",   c->channels,       0);
    av_opt_set_int       (ost->swr_ctx, "in_sample_rate",     c->sample_rate,    0);
    av_opt_set_sample_fmt(ost->swr_ctx, "in_sample_fmt",      AV_SAMPLE_FMT_S16, 0);
    av_opt_set_int       (ost->swr_ctx, "out_channel_count",  c->channels,       0);
    av_opt_set_int       (ost->swr_ctx, "out_sample_rate",    c->sample_rate,    0);
    av_opt_set_sample_fmt(ost->swr_ctx, "out_sample_fmt",     c->sample_fmt,     0);
    /* initialize the resampling context */
    if ((ret = swr_init(ost->swr_ctx)) < 0) //初始化重采样上下文
 

write_video_frame

    frame = get_video_frame(ost);//通过AVStream拿到AVFrame结构,
    av_init_packet(&pkt);//初始化AVPacket结构 用于没有alloc packet
    frame = get_video_frame(ost); //拿到未解码视频数据
    av_init_packet(&pkt); //通过默认的值初始化packet模块
    /* encode the image */
    ret = avcodec_encode_video2(c, &pkt, frame, &got_packet); //得到编码后的视频数据
    ret = write_frame(oc, &c->time_base, ost->st, &pkt);//将视频数据写入到输出文件

write_audio_frame

		av_init_packet(&pkt); // 初始化AVacket结构
		   
		frame = get_audio_frame(ost);//通过AVStream得到AVFrame结构
		   
	    dst_nb_samples = av_rescale_rnd(swr_get_delay(ost->swr_ctx, c->sample_rate) + frame->nb_samples,c->sample_rate, c->sample_rate, AV_ROUND_UP);//对音频采样率进行基准转换,输入采样率转换为输出采样率
		           
        ret = av_frame_make_writable(ost->frame);//确保frame是可写的状态
        /* convert to destination format */
        ret = swr_convert(ost->swr_ctx,ost->frame->data, dst_nb_samples, (const uint8_t **)frame->data, frame->nb_samples); //进行采样率的转换

        frame = ost->frame;

        frame->pts = av_rescale_q(ost->samples_count, (AVRational){1, c->sample_rate}, c->time_base);//计算显示时间错
        ost->samples_count += dst_nb_samples;

        ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);//对音频数据进行编码
    
        ret = write_frame(oc, &c->time_base, ost->st, &pkt);//将编码后的音频数据写到输出文件中

总结

ffmpeg中音视频数据的编码和封装比较的复杂,不仅设计到音视频的编码部分,还涉及到音视频编码数据的时间基转换和音视频数据的时间戳对比,但是设计到时间戳相关的计算也是封装格式的一个重要组成部分。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值