FFMPEG 4.0 for Android 準(zhǔn)備工作
FFMPEG4.0 音頻解碼解封裝
下面的函數(shù)方法基于最新的FFMPEG 4.0(4.X):
本文主要講如何從一個(gè)pcm文件中拿到原始數(shù)據(jù),用原始數(shù)據(jù)生成一個(gè)我們需要的音頻格式文件,結(jié)合上一篇的FFMPEG4.0 音頻解碼解封裝,你將能夠?qū)崿F(xiàn)音頻格式轉(zhuǎn)換.
從PCM文件中讀取數(shù)據(jù)生成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;
二、準(zhǔn)備編碼器、流,設(shè)置編碼參數(shù)
encodec = avcodec_find_encoder(AV_CODEC_ID_MP3);//可通過(guò)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);
三、打開(kāi)編碼器,得到一幀數(shù)據(jù)的采樣數(shù)
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 需要申請(qǐng)的字節(jié)數(shù)
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內(nèi)data分配內(nèi)存的方法可以通過(guò)調(diào)用如下方法達(dá)到(sample內(nèi)方法):ret = av_frame_get_buffer(frame, 0);
重采樣的數(shù)據(jù)從pcm文件中讀取,這里根據(jù)生成的一幀數(shù)據(jù)的樣本數(shù)計(jì)算得出轉(zhuǎn)換一幀數(shù)據(jù)需要讀取的樣本數(shù)(pcm樣本的采樣率是44100)(網(wǎng)絡(luò)上的示例這里都是錯(cuò)的!他們的例子在不改變采樣率時(shí)沒(méi)問(wèn)題,一改變就有播放時(shí)間變化):int in_nb_sample = av_rescale_rnd(frame->nb_samples,44100,encodec_ctx->sample_rate,AV_ROUND_UP);
計(jì)算轉(zhuǎn)換需要的一幀數(shù)據(jù)buf大?。?/p>
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);
五、復(fù)制參數(shù)、寫(xiě)頭信息
avcodec_parameters_from_context(st->codecpar,encodec_ctx);
avformat_write_header(fmt_ctx,NULL);
六、設(shè)置重采樣參數(shù)
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);
七、編碼 (下面是一幀編碼,實(shí)際編碼過(guò)程應(yīng)該是反復(fù)循環(huán)下面的行為,直到文件讀完)
1.讀取pcm文件,準(zhǔn)備重采樣的數(shù)組指針,有些做法是利用ffmpeg的接口生成frame,對(duì)frame進(jìn)行data內(nèi)存分配,實(shí)質(zhì)都是一樣:
if (fread(read_buf, 1, readSize, infile) < 0) {
printf("文件讀取錯(cuò)誤!\n");
return -1;
} else if (feof(infile)) {
break;
}
//重采樣源數(shù)據(jù)
const uint8_t *indata[AV_NUM_DATA_POINTERS] = {0};
indata[0] = (uint8_t *) read_buf;
2.重采樣,設(shè)置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循環(huán)。注意文件讀完后還需呀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.寫(xiě)結(jié)束符av_write_trailer(fmt_ctx);
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。