從零開始學習音視頻編程技術(十八) 錄屏軟件開發之編碼AAC

原文地址: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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章