您好,登錄后才能下訂單哦!
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);
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。