FFmpeg+SDL2.0 音频解码播放(部分格式杂音处理)

还是参照雷大神的例子改写得,但是改写过程中碰到几个问题,纠结的比较久的问题是解码MP4,
avi文件,播放出来的音频不仅很多杂音而且断断续续。因为是刚接触这块,没什么音频方面得知识,
出了问题也找不到问题点,只能百度到处看别人的帖子。一开始我觉得是SDL播放的问题,于是用雷神的程序,把同样的源文件先解码保存成PCM纯文件,然后下载了一个工具去播放,发现效果和SDL代码实现一致,还是有杂音。由此把问题确定是在解码的过程中。网上帖子大部分是都是说是由于avcodec_decode_audio4解码得到的是plane格式,需要用swr_convert进行格式转化。( 最后确实发现是这个转化中采样率导致的
但是我代码中已经加入了这个转化,并且解码aac文件并没有问题。所以一直想不通,期间陆陆续续看了很多帖子,慢慢就稍稍了解了一些音频的基本知识。
swr_alloc_set_opts(struct SwrContext *s,
                                      int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
                                      int64_t  in_ch_layout, enum AVSampleFormat  in_sample_fmt, int  in_sample_rate,
                                      int log_offset, void *log_ctx);

如果out_sample_rate低于in_sample_rate,则会出现杂音,猜测是因为采样率低导致数据丢失得原因。


再具体的见代码

// Audio_Player_demon_1.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "swscale.lib")
#pragma comment(lib, "swresample.lib")

#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio

static  Uint8  *audio_chunk;
static  Uint32  audio_len;
static  Uint8  *audio_pos;

/* The audio function callback takes the following parameters:
* stream: A pointer to the audio buffer to be filled
* len: The length (in bytes) of the audio buffer
* 回调函数
*/
void  fill_audio(void *udata,Uint8 *stream,int len);

int _tmain(int argc, _TCHAR* argv[])
{
av_register_all();

AVFormatContext *pAvFmtCtx;
AVCodecContext *pAvCodCtx;

pAvFmtCtx = avformat_alloc_context();

//char fileName[] = "WavinFlag.aac";
char fileName[] = "test.avi";
avformat_open_input(&pAvFmtCtx, fileName, NULL, NULL);

av_find_stream_info(pAvFmtCtx);

for (int i=0; i<pAvFmtCtx->nb_streams; ++i)
{
if (AVMEDIA_TYPE_AUDIO == pAvFmtCtx->streams[i]->codec->codec_type)
{
pAvCodCtx = pAvFmtCtx->streams[i]->codec;
break;
}
}

AVCodec *pAvcode;
pAvcode = avcodec_find_decoder(pAvCodCtx->codec_id);
avcodec_open2(pAvCodCtx, pAvcode, NULL);
AVPacket *pAvPct;
AVFrame *pAvFrame;

pAvPct = (AVPacket *)malloc(sizeof(AVPacket));
av_init_packet(pAvPct);

//AVFrame结构体不能使用下面方式初始化,主要原因猜测应该是av_malloc只能初始化实例,不能初始化buff
//pAvFrame = (pAvFrame *)av_malloc(sizeof(pAvFrame));

pAvFrame = av_frame_alloc();

uint8_t *uBuff;
//Out Audio Param
uint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;
int out_nb_samples=1024;
AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;

       //雷神这边是固定44100,主要杂音问题就是在这里,因为有些文件是48000
int out_sample_rate=pAvCodCtx->sample_rate;
       
int out_channels=av_get_channel_layout_nb_channels(out_channel_layout);

int iBufSize;
iBufSize = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);

uBuff = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE*2);;

//SDL
SDL_Init(SDL_INIT_AUDIO|SDL_INIT_TIMER);
//SDL_AudioSpec *pOutSpec;

//SDL_AudioSpec
SDL_AudioSpec pOutSpec;
pOutSpec.freq = pAvCodCtx->sample_rate;
//pOutSpec.freq = 48000;
pOutSpec.format = AUDIO_S16SYS;
pOutSpec.channels = out_channels;
pOutSpec.silence = 0;
pOutSpec.samples = out_nb_samples;
pOutSpec.callback = fill_audio;
pOutSpec.userdata = pAvCodCtx;

SwrContext *pSwCtx;
pSwCtx = swr_alloc();
pSwCtx = swr_alloc_set_opts(pSwCtx, out_channel_layout, out_sample_fmt, out_sample_rate, pAvCodCtx->channel_layout, pAvCodCtx->sample_fmt, pAvCodCtx->sample_rate, 0, NULL);
swr_init(pSwCtx);

int iGetFrame;
SDL_OpenAudio(&pOutSpec, NULL);

while(av_read_frame(pAvFmtCtx, pAvPct)>= 0)
{

avcodec_decode_audio4(pAvCodCtx, pAvFrame, &iGetFrame, pAvPct);

if (iGetFrame)
{
int data_size;
data_size = av_samples_get_buffer_size(nullptr, pAvCodCtx->channels, pAvFrame->nb_samples, pAvCodCtx->sample_fmt, 1);
//assert(data_size <= buf_size);
memcpy(uBuff, pAvFrame->data[0], data_size);


int dst_nb_samples = av_rescale_rnd(swr_get_delay(pSwCtx, pAvFrame->sample_rate) + pAvFrame->nb_samples,
pOutSpec.freq, pOutSpec.format, AVRounding(1));

swr_convert(pSwCtx, &uBuff, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)pAvFrame->data, pAvFrame->nb_samples);

if(pOutSpec.samples!=pAvFrame->nb_samples)
{
SDL_CloseAudio();
out_nb_samples=pAvFrame->nb_samples;
iBufSize=av_samples_get_buffer_size(NULL,out_channels ,out_nb_samples,out_sample_fmt, 1);
pOutSpec.samples=out_nb_samples;
SDL_OpenAudio(&pOutSpec, NULL);


}

audio_chunk = (Uint8 *) uBuff;
//Audio buffer length
audio_len =iBufSize;

audio_pos = audio_chunk;
//Play
SDL_PauseAudio(0);
while(audio_len>0)//Wait until finish
SDL_Delay(1);

}

/*audio_chunk = (Uint8 *) uBuff;
//Audio buffer length
audio_len =iBufSize;

audio_pos = audio_chunk;
//Play
SDL_PauseAudio(0);
while(audio_len>0)//Wait until finish
SDL_Delay(1);

av_free_packet(pAvPct);*/
}

return 0;
}

void fill_audio(void *udata, Uint8 *stream, int len)
{
SDL_memset(stream, 0, len);
if (audio_len == 0)
{
printf("audio_len=0\n");
return;
}

len = (len>audio_len?audio_len:len);
SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);//对音频数据进行混音,感觉这边混音的用处就是为了填充缓存
//memcpy(stream, audio_pos, len);
audio_pos+=len;//更新播放位置
audio_len-=len;//更新数据长度
}










PS:整个代码基本流程如下
视频、音频解码过程都是一致的,首先
av_register_all()  注册所需要的函数
avformat_open_input打开文件
av_find_stream_info 取流文件,这时候只有文件的头部信息,比如文件长度、码率之类。
avcodec_find_decoder 通过流文件信息获取解码器信息
avcodec_open2 打开解码器
av_read_frame  取信息包。
最后解码。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用FFmpegSDL实现音频播放,您可以按照以下步骤进行操作: 1. 首先,确保您已经安装了FFmpegSDL库。 2. 创建一个C/C++源文件,并包含必要的头文件: ```c #include <stdio.h> #include <SDL2/SDL.h> #include <libavformat/avformat.h> #include <libavutil/opt.h> #include <libswresample/swresample.h> ``` 3. 初始化FFmpegSDL库: ```c av_register_all(); avformat_network_init(); SDL_Init(SDL_INIT_AUDIO); ``` 4. 打开音频文件并解码音频流: ```c AVFormatContext *formatCtx = NULL; if (avformat_open_input(&formatCtx, "audio.mp3", NULL, NULL) != 0) { // 处理打开文件失败的情况 return -1; } if (avformat_find_stream_info(formatCtx, NULL) < 0) { // 处理获取流信息失败的情况 return -1; } int audioStreamIndex = -1; for (int i = 0; i < formatCtx->nb_streams; i++) { if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audioStreamIndex = i; break; } } if (audioStreamIndex == -1) { // 处理没有找到音频流的情况 return -1; } AVCodecParameters *codecPar = formatCtx->streams[audioStreamIndex]->codecpar; AVCodec *codec = avcodec_find_decoder(codecPar->codec_id); if (codec == NULL) { // 处理找不到解码器的情况 return -1; } AVCodecContext *codecCtx = avcodec_alloc_context3(codec); if (avcodec_parameters_to_context(codecCtx, codecPar) < 0) { // 处理解码器参数设置失败的情况 return -1; } if (avcodec_open2(codecCtx, codec, NULL) < 0) { // 处理打开解码器失败的情况 return -1; } ``` 5. 配置SDL音频参数: ```c SDL_AudioSpec wantedSpec, obtainedSpec; wantedSpec.freq = codecCtx->sample_rate; wantedSpec.format = AUDIO_S16SYS; wantedSpec.channels = codecCtx->channels; wantedSpec.silence = 0; wantedSpec.samples = 1024;wantedSpec.callback = audio_callback; wantedSpec.userdata = codecCtx; SDL_OpenAudio(&wantedSpec, &obtainedSpec); SDL_PauseAudio(0); ``` 6. 实现SDL音频回调函数: ```c void audio_callback(void *userdata, Uint8 *stream, int len) { AVCodecContext *codecCtx = (AVCodecContext *)userdata; AVPacket packet; static uint8_t audio_buffer[(192000 * 3) / 2]; static unsigned int audio_buffer_size = 0; static unsigned int audio_buffer_index = 0; while (len > 0) { if (audio_buffer_index >= audio_buffer_size) { int ret = av_read_frame(formatCtx, &packet); if (ret < 0) { // 处理读取音频帧失败的情况 break; } if (packet.stream_index == audioStreamIndex) { ret = avcodec_send_packet(codecCtx, &packet); if (ret < 0) { // 处理发送音频帧给解码器失败的情况 break; } ret = avcodec_receive_frame(codecCtx, &frame); if (ret < 0) { // 处理接收解码后的音频帧失败的情况 break; } int data_size = av_samples_get_buffer_size(NULL, codecCtx->channels, frame->nb_samples, codecCtx->sample_fmt, 1); memcpy(audio_buffer, frame->data[0], data_size); audio_buffer_size = data_size; audio_buffer_index = 0; } av_packet_unref(&packet); } int remaining = audio_buffer_size - audio_buffer_index; int to_copy = len > remaining ? remaining : len; memcpy(stream, audio_buffer + audio_buffer_index, to_copy); len -= to_copy; stream += to_copy; audio_buffer_index += to_copy; } } ``` 7. 清理资源并关闭SDLFFmpeg库: ```c SDL_CloseAudio(); SDL_Quit(); avformat_close_input(&formatCtx); avcodec_free_context(&codecCtx); return 0; ``` 以上是一个简单的示例,演示了如何使用FFmpegSDL实现音频播放。您可以根据自己的需求进行调整和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值