最近在调试网上下载的android ffmpeg播放器的源代码。因为编译ffmpeg过程中,觉得很麻烦,因此就没有继续编译SDL库了。打算在Java层使用Bitmap显示视频帧,在C层使用OpenSLES播放音频帧。
将网上的相关源代码整合到一块后,发现音视频解码,都没有错误。并且视频可以播放出来,但是声音声调不对,主要还有杂音。试了网上的一些方法也不行。最后自己尝试性地改了一个变量。杂音就消失了,但是声调还不对。
网上的解码音频数据包,然后将解码后的原始数据push到音频缓冲区的源代码中有以下几行代码
//音频数据解码
len = avcodec_decode_audio4(aCodecCtx, frame, &got_frame, &packet);
//得到解码音频数据的大小
data_size = av_samples_get_buffer_size( NULL, aCodecCtx->channels,
frame->nb_samples,aCodecCtx->sample_fmt, 1);
//将解码的音频数据push到音频缓冲区
memcpy(stream, (uint8_t*)(frame->data[0]), data_size]);
我将 memcpy(stream, (uint8_t*)(frame->data[0]), data_size]);
改为 memcpy(stream, (uint8_t*)(frame->data[0]), frame->linesize[0]); 后杂音就消失了。但是声音明显比正常的要快。
这个时候,感觉再去网上找方法也没什么用了。必须老老实实地理解AVFrame结构体中,各个字段的具体意思和PCM的相关知识点。
后来知道了杂音和声调不对的原因了。
杂音是因为avcodec_decode_audio4(aCodecCtx, frame, &got_frame, &packet)解码后的两个声道的音频数据分别存放在frame->data[0]和frame->data[1]中,而 frame->linesize[0]和 frame->linesize[1] 分别是前两者的大小。(我用来调试的视频文件,音频部分是双声道的)。而data_size表示的是两个声道的音频数据的总和。 因此memcpy(stream, (uint8_t*)(frame->data[0]), data_size])将一大段错误的数据push到缓冲区了。
声音明显比正常的要快的原因是,memcpy(stream, (uint8_t*)(frame->data[0]), frame->linesize[0])只是push了一个声道的音频数据,而创建OpenSLES的播放器是却设置成了双 声道。format_pcm.numChannels = aCodecCtx->channels(实际上为2)。两个通道播放一个通道的数据,自然变快了。为尽快播放出正常的声音,将播放器声道数设为1,此时听到了正常的音乐。
当然最正确的方法是正确地将编码后的音频数据,转换为正确的PCM格式。这样就可以双声道播放了。
小结一下:遇到不是程序本身存在的问题时。还需要从原理入手,学习相关的知识,这样才能够真正理解一段代码的工作流程和原理。