版權聲明:本文為衛(wèi)偉學習總結(jié)文章,轉(zhuǎn)載請注明出處错妖!
上幾篇文章詳解的介紹了使用MediaRecord和AudioRecord兩種對音頻進行采集的技術,以及FFmpeg包的編輯和裁剪,生成安卓使用的.so庫的技術派任。本文章將詳細介紹使用FFmpeg進行音頻解碼操作。
一虑灰、音頻解碼過程
音頻解碼過程如下圖所示:
- av_register_all():的作用是初始化所有組件吨瞎,只有調(diào)用了該函數(shù),才能使用復用器和編解碼器(源碼)
- avformat_open_input()/avformat_close_input(): 函數(shù)會讀文件頭穆咐,對 mp4 文件而言颤诀,它會解析所有的 box。但它知識把讀到的結(jié)果保存在對應的數(shù)據(jù)結(jié)構下对湃。這個時候崖叫,AVStream 中的很多字段都是空白的。
- av_dump_format(): 打印視音頻信息
- avformat_find_stream_info():讀取一部分視音頻數(shù)據(jù)并且獲得一些相關的信息拍柒,會檢測一些重要字段心傀,如果是空白的,就設法填充它們拆讯。因為我們解析文件頭的時候脂男,已經(jīng)掌握了大量的信息,avformat_find_stream_info 就是通過這些信息來填充自己的成員种呐,當重要的成員都填充完畢后宰翅,該函數(shù)就返回了。這中情況下爽室,該函數(shù)效率很高汁讼。但對于某些文件,單純的從文件頭中獲取信息是不夠的,比如 video 的 pix_fmt 是需要調(diào)用 h264_decode_frame 才可以獲取其pix_fmt的嘿架。
- av_find_best_stream(): 獲取音視頻及字幕的 stream_index , 以前沒有這個函數(shù)時瓶珊,我們一般都是寫的 for 循環(huán)。
- av_packet_free(): 首先將 AVPacket 指向的數(shù)據(jù)域的引用技術減1(數(shù)據(jù)域的引用技術減為0時會自動釋放) 接著耸彪,釋放為 AVPacket 分配的空間伞芹。
- av_packet_unref(): 減少數(shù)據(jù)域的引用技術,當引用技術減為0時搜囱,會自動釋放數(shù)據(jù)域所占用的空間丑瞧。
二、獲取音頻Meta信息
對于amr格式的音頻文件而言蜀肘,如何去獲取它的采樣率以及是否單聲道绊汹。如果用ffmpeg那很easy就能解決。但問題是不會扮宠,那么我們就只能用其他的第三方庫西乖。當然如果你了解其原理,甚至可以自己分析二進制文件坛增。
int audio_stream_idx;
AVStream *audio_stream;
av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
if(avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0)
{
if(LOG_DEBUG)
{
LOGE("can not open url :%s", url);
}
return;
}
if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
if(LOG_DEBUG)
{
LOGE("can not find streams from %s", url);
}
return;
}
// 獲取采樣率和通道
audio_stream_idx = av_find_best_stream(pFormatCtx, AVMediaType::AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
audio_stream = pFormatCtx->streams[audio_stream_idx];
LOGE("采樣率:%d, 通道數(shù): %d", audio_stream->codecpar->sample_rate, audio_stream->codecpar->channels);
二获雕、解碼音頻數(shù)據(jù)
關于解碼函數(shù) avcodec_decode_audio4 已經(jīng)過時了,取而代之的是 avcodec_send_packet 和 avcodec_receive_frame 收捣。
void WlFFmpeg::start() {
if(audio == NULL)
{
if(LOG_DEBUG)
{
LOGE("audio is null");
return;
}
}
int count = 0;
while(1)
{
AVPacket *avPacket = av_packet_alloc();
if(av_read_frame(pFormatCtx, avPacket) == 0)
{
if(avPacket->stream_index == audio->streamIndex) //音頻幀數(shù)據(jù)
{
//解碼操作
count++;
if(LOG_DEBUG)
{
LOGE("解碼第 %d 幀", count);
}
av_packet_free(&avPacket);
av_free(avPacket);
} else{
av_packet_free(&avPacket);
av_free(avPacket);
}
} else{
if(LOG_DEBUG)
{
LOGE("decode finished");
}
av_packet_free(&avPacket);
av_free(avPacket);
break;
}
}
三届案、子線程下進行解碼操作
WlFFmpeg::WlFFmpeg(WlCallJava *callJava, const char *url) {
this->callJava = callJava;
this->url = url;
}
void *decodeFFmpeg(void *data)
{
WlFFmpeg *wlFFmpeg = (WlFFmpeg *) data;
wlFFmpeg->decodeFFmpegThread();
pthread_exit(&wlFFmpeg->decodeThread);
}
void WlFFmpeg::parpared() {
pthread_create(&decodeThread, NULL, decodeFFmpeg, this);
}
[代碼](鏈接: https://pan.baidu.com/s/1RrqmBf2adXygilH5PEMnaQ)
密碼:v8ef