ffmpeg之G711解析成pcm
阿新 • • 發佈:2019-02-06
ffmpeg在碼流轉換上面實在是強大,今天實驗了一下把G711音訊專成PCM的音訊,並最終實驗成功。
第一步:尋找解碼器,若格式不支援,則無法轉碼
codec = avcodec_find_decoder(AV_CODEC_ID_PCM_ALAW);
if (!codec) {
fprintf(stderr, "Codec not found\n");
return false;
}
提醒:G711A的解碼ID為AV_CODEC_ID_PCM_ALAW
第二步:建立解碼上下文,主要保持解碼相關的資訊
c = avcodec_alloc_context3(codec); if (!c) { fprintf(stderr, "Could not allocate audio codec context\n"); return false; } c->sample_fmt = sample_fmt; c->sample_rate = sample_rate; c->channels = channels;
需要設定3個引數,不然後面開啟解碼器會失敗
sample_fmt:取樣格式,標識含義8位,16位等取樣
sample_rate:取樣頻率
channels:取樣通道數
第三步:開啟解碼器
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
return false;
}
第四步:輸入碼流
ret = avcodec_send_packet(c, packet);
第五步:輸出解碼結果
int ret = avcodec_receive_frame(c, frame);
整個過程大約分為這5個步驟。
另外通過研究,有幾個重要的關於記憶體的發現:
由於輸入解碼的資料格式是AVPacket,而我拿到的資料確是位元組流,需要把位元組流轉換成AVPacket型別。
這裡作了如下處理:
packet->size = iSize; packet->data = (uint8_t *)av_malloc(packet->size); memcpy(packet->data, pData, iSize); ret = av_packet_from_data(packet, packet->data, packet->size); if (ret <0) { fprintf(stderr, "av_packet_from_data error \n"); av_free(packet->data); return; } ret = avcodec_send_packet(c, packet); av_packet_unref(packet);
通過函式av_packet_from_data生產了AVPacket型別資料,但是需要注意在使用完之後,需要呼叫av_packet_unref進行釋放記憶體,不然會存在記憶體洩漏,有人會問上面的記憶體是通過av_malcoc生成的,我們還需要釋放嗎?我通過實驗發現呼叫av_packet_unref之後,就不需要再呼叫av_free來進行釋放了,ffmpeg好像自己已經釋放了。
另外關於解碼出來的音訊,需要注意他的存放再uint8_t *data[AV_NUM_DATA_POINTERS];裡面的,這是一個數組,每個通道一維。由於pcm保持多通道根據取樣率進行順序保持,所以保持位pcm需要處理一下:
int iCopyPos = 0;
for(int i = 0; i < frame->nb_samples; i++)
{
for (int ch = 0; ch < c->channels; ch++)
{
memcpy(m_pOutData + iCopyPos,frame->data[ch] + data_size * i, data_size);
iCopyPos = iCopyPos + data_size;
}
}
最後,為了便於大家學習和交流,我採用vs2017開發,上傳示例如下連線: