音頻解碼和視頻解碼有很多是相似的,我們直接貼代碼
Java_com_example_ffmpegapplication_MyAudioPlayer_playaudio(JNIEnv *env, jobject instance,
jstring src_, jstring des_) {
const char *src = env->GetStringUTFChars(src_, 0);
const char *des = env->GetStringUTFChars(des_, 0);
avformat_network_init();
//總上下文
AVFormatContext *pContext = avformat_alloc_context();
//字典
AVDictionary *pDictionary = NULL;
av_dict_set(&pDictionary, "timeout", "3000000", 0);
// 打開(kāi)源文件輸入流
if(avformat_open_input(&pContext, src, NULL, &pDictionary) != 0) {
avformat_free_context(pContext);
return;
}
// 獲取視頻流信息齿穗,通知ffmpeg把流解析出來(lái)
if(avformat_find_stream_info(pContext,NULL) < 0){
LOGI("%s","無(wú)法獲取輸入文件信息");
return;
}
//音頻流索引
int audio_stream_index = -1;
//nb_streams:視頻里面流的數(shù)量讳侨,比如流0是視頻,流1是音頻姜胖,流2是字幕
for(int i = 0; i < pContext->nb_streams; i++) {
//codecpar:解碼器參數(shù)钦勘,舊版本的是codec, codec_type:解碼器類型棋傍。
if(pContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
//如果是音頻流救拉,保存索引
audio_stream_index = i;
break;
}
}
//音頻流的解碼參數(shù)
AVCodecParameters *pParameters = pContext->streams[audio_stream_index]->codecpar;
//通過(guò)解碼器id找到解碼器
AVCodec *pCodec = avcodec_find_decoder(pParameters->codec_id);
//創(chuàng)建解碼器上下文
AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
//將解碼器參數(shù)copy到解碼器上下文
avcodec_parameters_to_context(pCodecContext, pParameters);
//打開(kāi)解碼器
avcodec_open2(pCodecContext, pCodec, &pDictionary);
//音頻轉(zhuǎn)換上下文
SwrContext *pSwrContext = swr_alloc();
//輸入?yún)?shù)
//輸入采樣格式
AVSampleFormat in_sample = pCodecContext->sample_fmt;
//輸入采樣率
int in_sample_rate = pCodecContext->sample_rate;
//輸入聲道布局
uint64_t in_ch_layout = pCodecContext->channel_layout;
//輸出參數(shù) 固定
//輸出采樣格式
AVSampleFormat out_sample = AV_SAMPLE_FMT_S16;
//輸出采樣率
int out_sample_rate = 44100;
//輸出聲道布局 AV_CH_LAYOUT_STEREO 雙通道
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
//設(shè)置轉(zhuǎn)換參數(shù)
swr_alloc_set_opts(pSwrContext,out_ch_layout,out_sample,out_sample_rate
,in_ch_layout,in_sample,in_sample_rate,0,NULL);
//初始化轉(zhuǎn)換器
swr_init(pSwrContext);
//為輸出文件申請(qǐng)內(nèi)存
uint8_t *out_buffer = (uint8_t *)(av_malloc(2 * 44100));
FILE *fp_pcm = fopen(des, "wb");
//數(shù)據(jù)存儲(chǔ)在一個(gè)AVPacket中 舊版本需要用戶自己去malloc。新版本的可以理解為:待解碼隊(duì)列
AVPacket *pPacket = av_packet_alloc();
//解碼后的yuv數(shù)據(jù)容器
AVFrame *pFrame = av_frame_alloc();
while (av_read_frame(pContext, pPacket) >= 0) {
//AVCodecContext *avctx, const AVPacket *avpkt 從待解碼隊(duì)列中取出視頻流數(shù)據(jù)發(fā)送給解碼器
avcodec_send_packet(pCodecContext, pPacket);
//AVCodecContext *avctx, AVFrame *frame 從解碼器取出解碼后到數(shù)據(jù)(yuv)到frame
int ret = avcodec_receive_frame(pCodecContext, pFrame);
if (ret == AVERROR(EAGAIN)) {
continue;
} else if (ret < 0) {
LOGI("%s","解碼完成");
break;
}
//frame ---->統(tǒng)一的格式
swr_convert(pSwrContext, &out_buffer, 2 * 44100,
(const uint8_t **)pFrame->data, pFrame->nb_samples);
int out_channerl_nb= av_get_channel_layout_nb_channels(out_ch_layout);
//緩沖區(qū)的 大小
int out_buffer_size= av_samples_get_buffer_size(NULL, out_channerl_nb, pFrame->nb_samples, out_sample, 1);
fwrite(out_buffer,1, out_buffer_size, fp_pcm);
}
fclose(fp_pcm);
av_free(out_buffer);
swr_free(&pSwrContext);
//釋放容器
av_frame_free(&pFrame);
//釋放AVPacket
av_packet_free(&pPacket);
//釋放解碼器上下文
avcodec_free_context(&pCodecContext);
//關(guān)閉文件
avformat_close_input(&pContext);
//釋放總上下文
avformat_free_context(pContext);
env->ReleaseStringUTFChars(src_, src);
env->ReleaseStringUTFChars(des_, des);
}
我們來(lái)分析一下代碼瘫拣,相同的就不在看了
初始化網(wǎng)路
打開(kāi)文件流
初始化解碼器
轉(zhuǎn)換器的初始化和使用
- struct SwrContext *swr_alloc(void);
音頻轉(zhuǎn)換上下文
使用swr_free釋放 - struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
int log_offset, void *log_ctx);
設(shè)置轉(zhuǎn)換參數(shù)
音頻要播放出來(lái)亿絮,就需要把原始數(shù)據(jù)轉(zhuǎn)換成固定格式的數(shù)據(jù)。比如代碼中,不管原始數(shù)據(jù)的參數(shù)如何壹无。都轉(zhuǎn)成固定的輸出采樣格式葱绒、輸出采樣率、輸出聲道布局 - int swr_init(struct SwrContext *s);
初始化轉(zhuǎn)換器斗锭,必須調(diào)用 - int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
const uint8_t **in , int in_count);
轉(zhuǎn)換文件到固定格式
輸出文件
- uint8_t *out_buffer = (uint8_t *)(av_malloc(2 * 44100));
我們看下這個(gè)代碼里面地淀,申請(qǐng)的內(nèi)存大小是244100,這個(gè)大小一般就是:通道數(shù)采樣率 - int av_get_channel_layout_nb_channels(uint64_t channel_layout);
獲取通道數(shù) - int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
enum AVSampleFormat sample_fmt, int align);
獲取這一幀聲音的實(shí)際大小 - fwrite(out_buffer,1, out_buffer_size, fp_pcm);
這里面第二個(gè)參數(shù)是1岖是,是因?yàn)橐纛l的最小單位就是1個(gè)字節(jié)帮毁。如果是像素,就是4個(gè)字節(jié)
最終在輸出目錄下生成一個(gè)pcm文件