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);
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。