FFMPEG音频封装编码-创新互联

FFMPEG 4.0 for Android 准备工作

让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:申请域名、网络空间、营销软件、网站建设、扎鲁特旗网站维护、网站推广。

FFMPEG4.0 音频解码解封装

下面的函数方法基于最新的FFMPEG 4.0(4.X):

本文主要讲如何从一个pcm文件中拿到原始数据,用原始数据生成一个我们需要的音频格式文件,结合上一篇的FFMPEG4.0 音频解码解封装,你将能够实现音频格式转换.

从PCM文件中读取数据生成MP3格式文件。
一、初始化输出

 AVFormatContext *fmt_ctx;
    int ret = avformat_alloc_output_context2(&fmt_ctx,NULL,NULL,out_file);
 ret = avio_open(&fmt_ctx->pb,out_file,AVIO_FLAG_WRITE);

下面的变量不是必须的,里面存了输出格式的信息,包含生成的音视频编码格式。

AVOutputFormat *ofmt;
ofmt = fmt_ctx->oformat;

二、准备编码器、流,设置编码参数

encodec = avcodec_find_encoder(AV_CODEC_ID_MP3);//可通过ofmt->audio_codec得到格式
st = avformat_new_stream(fmt_ctx,encodec);
encodec_ctx = avcodec_alloc_context3(encodec);

encodec_ctx->sample_rate = 44100;
encodec_ctx->bit_rate = 64000;
encodec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
encodec_ctx->channel_layout = AV_CH_LAYOUT_STEREO;
encodec_ctx->channels = av_get_channel_layout_nb_channels(encodec_ctx->channel_layout);

三、打开编码器,得到一帧数据的采样数

ret = avcodec_open2(encodec_ctx,encodec,NULL);
int encode_nb_sample = encodec_ctx->frame_size;

四、初始化frame与packet

frame = av_frame_alloc();
    pkt = av_packet_alloc();
    frame->nb_samples = encode_nb_sample;
    frame->format = encodec_ctx->sample_fmt;
    frame->channel_layout = encodec_ctx->channel_layout;

    //frame.data 需要申请的字节数
    int size = av_samples_get_buffer_size(NULL,encodec_ctx->channels,encode_nb_sample,encodec_ctx->sample_fmt,1);
    uint8_t *frame_buf = (uint8_t *) av_malloc(size);
avcodec_fill_audio_frame(frame,encodec_ctx->channels,encodec_ctx->sample_fmt,frame_buf,size,1);

上面的给frame内data分配内存的方法可以通过调用如下方法达到(sample内方法):
ret = av_frame_get_buffer(frame, 0);

重采样的数据从pcm文件中读取,这里根据生成的一帧数据的样本数计算得出转换一帧数据需要读取的样本数(pcm样本的采样率是44100)(网络上的示例这里都是错的!他们的例子在不改变采样率时没问题,一改变就有播放时间变化):
int in_nb_sample = av_rescale_rnd(frame->nb_samples,44100,encodec_ctx->sample_rate,AV_ROUND_UP);

计算转换需要的一帧数据buf大小:

int readSize = in_nb_sample * av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO) * av_get_bytes_per_sample(in_fmt);
char *read_buf = (char*)malloc(readSize);

五、复制参数、写头信息

avcodec_parameters_from_context(st->codecpar,encodec_ctx);
    avformat_write_header(fmt_ctx,NULL);

六、设置重采样参数

swc = swr_alloc();
    av_opt_set_int(swc,"in_channel_layout",AV_CH_LAYOUT_STEREO,0);
    av_opt_set_int(swc,"in_sample_rate",in_sample_rate,0);
    av_opt_set_sample_fmt(swc,"in_sample_fmt",in_fmt,0);

    av_opt_set_int(swc,"out_channel_layout",encodec_ctx->channel_layout,0);
    av_opt_set_int(swc,"out_sample_rate",encodec_ctx->sample_rate,0);
    av_opt_set_sample_fmt(swc,"out_sample_fmt",encodec_ctx->sample_fmt,0);
ret = swr_init(swc);

七、编码 (下面是一帧编码,实际编码过程应该是反复循环下面的行为,直到文件读完)
1.读取pcm文件,准备重采样的数组指针,有些做法是利用ffmpeg的接口生成frame,对frame进行data内存分配,实质都是一样:

 if (fread(read_buf, 1, readSize, infile) < 0) {
            printf("文件读取错误!\n");
            return -1;
        } else if (feof(infile)) {
            break;
        }
        //重采样源数据
        const uint8_t *indata[AV_NUM_DATA_POINTERS] = {0};
        indata[0] = (uint8_t *) read_buf;

2.重采样,设置pts

 int len = swr_convert(swc, frame->data, frame->nb_samples,indata, in_nb_sample);
        LOGV("len = %d\n",len);
        frame->pts = apts;
        apts += av_rescale_q(len,(AVRational){1,encodec_ctx->sample_rate},encodec_ctx->time_base);

3.编码(也许不用while循环。注意文件读完后还需呀send一次,frame传NULL,主要为了flush编码器)

ret = avcodec_send_frame(encodec_ctx, frame);

       while(ret >= 0) {
            LOGV("receiver\n");
            ret = avcodec_receive_packet(encodec_ctx, pkt);
            if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR, "%s,ret = %d\n", "avcodec_receive_packet!error ",ret);
                break;
            }
            pkt->stream_index = st->index;
            av_log(NULL, AV_LOG_DEBUG, "第%d帧\n", i);
            pkt->pts = av_rescale_q(pkt->pts, encodec_ctx->time_base, st->time_base);
            pkt->dts = av_rescale_q(pkt->dts, encodec_ctx->time_base, st->time_base);
            pkt->duration = av_rescale_q(pkt->duration, encodec_ctx->time_base, st->time_base);
            LOGV("duration = %d,dts=%d,pts=%d\n",pkt->duration,pkt->dts,pkt->pts);
            ret = av_write_frame(fmt_ctx, pkt);
            if (ret < 0) {
                av_log(NULL, AV_LOG_ERROR, "av_write_frame error!");
            }
            av_packet_unref(pkt);
        }

4.写结束符
av_write_trailer(fmt_ctx);

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


文章标题:FFMPEG音频封装编码-创新互联
当前URL:http://ybzwz.com/article/ddihpi.html