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