原文地址:http://blog.yundiantech.com/?log=blog&id=24
光陰似箭,日月如梭。 時間過的可真快。。。
一轉眼 大半年就過去了,差點就忘記學習了。。
學習貴在堅持和積累,然而就是很難做到堅持。。
不知不覺已經半年沒有更新了,對不住大家了,趕緊繼續更新。。
之前講到了使用ffmpeg讀取麥克風並保存成PCM文件。傳送門
獲取到了PCM之後,下一步當然是編碼生成AAC了。
與之前說過的YUV是視頻的原始數據類似,PCM是音頻的原始數據,因此它的大小也相對比較大,因此就有必要將PCM數據編碼。
同樣,音頻的編碼方式也有很多種,常見如MP3,AAC。我們以後使用比較多的就是AAC,因此本文只講解將pcm編碼成AAC。
編碼aac一樣還是直接使用ffmpeg,方法如下:
1.查找並打開AAC編碼器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
AVCodecContext* pCodecCtx; AVCodec* aCodec; uint8_t* frame_buf; AVFrame* frame; int ONEFrameSize; pCodecCtx = avcodec_alloc_context3(aCodec); pCodecCtx->codec_id = AV_CODEC_ID_AAC; pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO; pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; pCodecCtx->sample_rate= 44100; pCodecCtx->channel_layout=AV_CH_LAYOUT_STEREO; pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout); pCodecCtx->bit_rate = 64000; aCodec = avcodec_find_encoder(pCodecCtx->codec_id); if (!aCodec) { printf ("沒有找到合適的編碼器! "); return ; } if (avcodec_open2(pCodecCtx, aCodec,NULL) < 0) { printf ("編碼器打開失敗! "); return ; } |
上面設置的
1
2
3
|
pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16; pCodecCtx->sample_rate= 44100; pCodecCtx->channel_layout=AV_CH_LAYOUT_STEREO; |
這三個必須要和原始PCM數據的一致,否則編碼後的音頻播放會有問題。
本文提供的例子是直接用ffmpeg採集後得到的pcm,因此默認都是16bit、44100HZ、雙聲道的數據,至於ffmpeg採集的時候如果修改這個信息,我也沒有研究,以後有時間再去看了,這裏暫時不管他了。
2.編碼器成功打開之後就是進行編碼了,編碼使用的函數是:avcodec_encode_audio2
這裏需要注意的是:
傳遞給ffmpeg編碼的Pcm數據,每次的大小必須是下面這個值。
1
|
ONEFrameSize = av_samples_get_buffer_size(NULL, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1); |
至於不是這個會出現什麼問題,我也忘記了,很久以前寫的了,只記得這裏要是這個值。
有興趣的自己去試一試會是什麼結果吧。
由於ffmpeg採集音頻的時候,一次獲取到的pcm數據不一定是這個大小(實際上是肯定不是),因此需要手動處理一下,大致代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
AVPacket pkt; av_new_packet(&pkt,ONEFrameSize); FILE *aacFp = fopen ( "out.aac" , "wb" ); uint8_t * mAacBuffer = (uint8_t * ) malloc (4096*100); int mAacBufferIndex = 0; int mAacBufferSize = 0; while (1) { if (mPcmBufferList.isEmpty()) { msleep(10); continue ; } mMutex.lock(); FrameDataNode node = mPcmBufferList.takeFirst(); //取出1幀yuv數據 mMutex.unlock(); memcpy (mAacBuffer+mAacBufferSize,node.buffer,node.size); mAacBufferSize += node.size; free (node.buffer); /// 每次傳遞給編碼器的數據大小都要是 上面獲取到的 "ONEFrameSize" /// 因此需要下面的循環 while (1) { int size = mAacBufferSize - mAacBufferIndex; if (size < ONEFrameSize) //不夠編碼1幀了 { memcpy (mAacBuffer,mAacBuffer+mAacBufferIndex,size); mAacBufferIndex = 0; mAacBufferSize = size; break ; } frame->data[0] = mAacBuffer+mAacBufferIndex; //採樣信號 mAacBufferIndex += ONEFrameSize; int got_frame=0; //編碼 int ret = avcodec_encode_audio2(pCodecCtx, &pkt,frame, &got_frame); if (got_frame==1) { /// 編碼後的數據是帶ADTS頭的 因此寫入文件後 可以直接用播放器播放 fwrite (pkt.data,1,pkt.size,aacFp); av_free_packet(&pkt); } } } |
這個代碼相對比較簡單,具體的就不做解釋了,請下載完整工程查看吧。
完整工程下載地址:http://download.csdn.net/detail/qq214517703/9823988
音視頻技術交流討論歡迎加 QQ羣 121376426
原文地址:http://blog.yundiantech.com/?log=blog&id=24