场景 当前的audio.mp2文件,保存了海康NVR点播时候的音频数据,音频基本信息:单通道,采样频率16000HZ FFmpeg读取文件解码播放 int PlayMP2Audio(){char *file = "audio.mp2";AVCodecContext *pCodecCtx = NU
场景
当前的audio.mp2文件,保存了海康NVR点播时候的音频数据,音频基本信息:单通道,采样频率16000HZ
FFmpeg读取文件解码播放
int PlayMP2Audio()
{
char *file = "audio.mp2";
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVFrame *pFrame = NULL;
AVPacket *pPacket;
uint8_t *pOutputAudioBuffer;
struct SwrContext *pAudioConvertContext;
AVFormatContext *pFormatCtx = NULL;
if (avformat_open_input(&pFormatCtx, file, NULL, NULL) != 0)
{
std::cout << "打开文件失败" << std::endl;
return -1;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
std::cout << "探测文件格式失败" << std::endl;
return -1;
}
int nAudioStreamIndex = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if (nAudioStreamIndex == -1)
{
std::cout << "寻找音频流失败" << std::endl;
return -1;
}
AVCodecParameters *pCodecParameters = pFormatCtx->streams[nAudioStreamIndex]->codecpar;
pCodec = avcodec_find_decoder(pCodecParameters->codec_id);
if (pCodec == NULL)
{
std::cout << "不支持该音频格式" << std::endl;
return -1;
}
pCodecCtx = avcodec_alloc_context3(pCodec);
if (avcodec_parameters_to_context(pCodecCtx, pCodecParameters) != 0)
{
std::cout << "复制音频信息失败" << std::endl;
return -1;
}
// pCodecCtx->sample_rate = 16000;
// pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16P;
// pCodecCtx->channels = 1;
// pCodecCtx->channel_layout = av_get_default_channel_layout(1);
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
std::cout << "打开解码器失败" << std::endl;
return -1;
}
//打开解码器以后pCodecCtx->sample_fmt格式从AV_SAMPLE_FMT_FLTP变化为AV_SAMPLE_FMT_S16P
pPacket = (AVPacket *)av_malloc(sizeof(AVPacket));
av_init_packet(pPacket);
pFrame = av_frame_alloc();
uint64_t nOutputChannelLayout = AV_CH_LAYOUT_MONO;//输出声道
int nNBSample = 1152;//MP2默认的音频帧采样个数
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;//输出格式S16
int nOutputSampleRate = pCodecCtx->sample_rate;
int nOutputChannels = av_get_channel_layout_nb_channels(nOutputChannelLayout);
int out_buffer_size = av_samples_get_buffer_size(NULL, nOutputChannels, nNBSample, out_sample_fmt, 1);
pOutputAudioBuffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER))
{
std::cout << "初始化SDL失败,错误码:" << SDL_GetError() << std::endl;
return -1;
}
int nInputChannelLayout = av_get_default_channel_layout(pCodecCtx->channels);
pAudioConvertContext = swr_alloc();
pAudioConvertContext = swr_alloc_set_opts(pAudioConvertContext, nOutputChannelLayout, out_sample_fmt, nOutputSampleRate,
nInputChannelLayout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
swr_init(pAudioConvertContext);
bool bInitAudioDevice = false;
while (av_read_frame(pFormatCtx, pPacket) >= 0)
{
if (pPacket->stream_index == 0)
{
avcodec_send_packet(pCodecCtx, pPacket);
while (avcodec_receive_frame(pCodecCtx, pFrame) == 0)
{
if (!bInitAudioDevice)
{
bInitAudioDevice = true;
SDL_AudioSpec spec;
spec.freq = nOutputSampleRate;
spec.format = AUDIO_S16SYS;
spec.channels = nOutputChannels;
spec.silence = 0;
spec.samples = pFrame->nb_samples;//这里不一定是2的幂指数次方,MP2默认是1152,否则播放声音异常
spec.callback = read_audio_data;
spec.userdata = pCodecCtx;
if (SDL_OpenAudio(&spec, NULL) < 0)
{
std::cout << "打开音频设备失败" << std::endl;
return -1;
}
SDL_PauseAudio(0);//开始播放
}
swr_convert(pAudioConvertContext, &pOutputAudioBuffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)pFrame->data,
pFrame->nb_samples); // 转换音频
}
audio_chunk = (Uint8 *)pOutputAudioBuffer;
audio_len = out_buffer_size;
audio_pos = audio_chunk;
while (audio_len > 0)
{
SDL_Delay(1);//等待回调函数读取完缓存中的音频数据
}
}
av_packet_unref(pPacket);
}
swr_free(&pAudioConvertContext);
av_free(pOutputAudioBuffer);
SDL_Quit();
return 0;
}
备注
对于单声道的音频数据,不需要调用swr进行重采样
手动打开文件播放
指定每次读取的缓存大小是1152,否则会提示[mp2 @ 03eee300] Header missing,并且出现杂音或者破音的情况,无法听清声音
int nHasReadDataLen = 0;
int nMP2FrameBufferSize = 1152;
char *pMP2Buffer = (char *)calloc(nMP2FrameBufferSize, sizeof(char));
std::ofstream ofstreamHanle;
ofstreamHanle.open("F:/audio.mp2", std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
while (1)
{
memset(pMP2Buffer, 0x00, nMP2FrameBufferSize);
if (fread(pMP2Buffer, 1, nMP2FrameBufferSize, fp) != nMP2FrameBufferSize)
{
//循环读取
fseek(fp, 0, SEEK_SET);
fread(pMP2Buffer, 1, nMP2FrameBufferSize, fp);
nHasReadDataLen = 0;
break;
}
printf("Now Playing %10d Bytes data.\n", nHasReadDataLen);
nHasReadDataLen += nMP2FrameBufferSize;
//解码
//播放
}