使用ffmpeg解码音频文件到PCM格式

时间:2023-02-01 19:43:07

最近忙于使用ffmpeg播放音乐文件的项目,现将开发经验总结如下:

一、解码音乐文件的大致流程如下:

1,打开音乐文件,调用av_open_input_file()

2,查找audio stream,调用av_find_stream_info()

3,查找对应的decoder,调用avcodec_find_decoder()

4,打开decoder,调用avcodec_open()

5,读取一桢数据包,调用av_read_frame()

6,解码数据包,调用avcodec_decode_audio3()

7,将解码后的数据返回

这样,得到解码后的PCM数据之后,我们可以播放、也可以encode成其他格式。

 

二、相关代码:

1,打开文件

// return 0: OK
// return -1: arguments are wrong
int internal_open(const char* file)
{
int i = 0;
int res = 0;
LOGD("enter internal_open(), file name:%s", file);

if (file == 0 || strlen(file) == 0)
{
LOGE("%s, argument file is wrong!", __FUNCTION__);
return -1;
}

// try open file
if ((res = av_open_input_file(&m_format_ctx, file, NULL, 0, NULL)) != 0)
{
LOGE("%s, av_open_input_file() return failed!", __FUNCTION__);
return res;
}

// find streams information
if ((res = av_find_stream_info(m_format_ctx) < 0))
{
LOGE("%s, av_find_stream_info() could not find codec parameters!", __FUNCTION__);
return res;
}

av_dump_format(m_format_ctx, 0, file, 0);

for(i = 0; i < m_format_ctx->nb_streams; i++)
{
// get audio stream id
if(m_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
m_audio_stream = i;
break;
}
}
if(m_audio_stream == -1)
{
LOGE("%s, find audioStream failed!", __FUNCTION__);
return -4;
}

// get audio stream codec
m_codec_ctx = m_format_ctx->streams[m_audio_stream]->codec;

// find the mpeg audio decoder
m_codec = avcodec_find_decoder(m_codec_ctx->codec_id);
if (!m_codec)
{
LOGE("%s, codec not found!", __FUNCTION__);
return -5;
}

// open it
if (avcodec_open(m_codec_ctx, m_codec) < 0)
{
LOGE("%s, could not open codec!", __FUNCTION__);
return -7;
}

LOGD("exit internal_open()");
return 0;
}

2,解码数据

int internal_getaudio(unsigned char buff[], long num_samples)
{
//AVPacket avpkt;
long copied_len = 0;
int frame_size = 0;
int decoded_len = 0;
int real_copied = 0;
int decoded_audio_len = 0;
unsigned char* valid_data_pointer = 0;

LOGD("enter internal_getaudio()");

// copy the backup (has not copied in last time) data at first!
if (m_audio_buff_backup_len > 0)
{
LOGI("%s, has last not copied data in backup buff, length:%d", __FUNCTION__, m_audio_buff_backup_len);
if (m_audio_buff_backup_len >= num_samples)
{
memcpy(buff, m_audio_buff_backup, num_samples);
copied_len += num_samples;
LOGI("%s, only copy data from backup buff, return 0", __FUNCTION__);
}
else
{
memcpy(buff, m_audio_buff_backup, m_audio_buff_backup_len);
copied_len += m_audio_buff_backup_len;
LOGI("%s, copy %d length data from backup buff", __FUNCTION__, m_audio_buff_backup_len);
}
}

// decode new data
while (copied_len < num_samples && av_read_frame(m_format_ctx, &m_avpkt) >= 0)
{
LOGI("%s, av_read_frame() return successful!", __FUNCTION__);
if (m_avpkt.stream_index == m_audio_stream)
{
while (m_avpkt.size > 0)
{
LOGI("%s, current packet size:%d", __FUNCTION__, m_avpkt.size);
frame_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
memset(m_audio_buff, 0, AVCODEC_MAX_AUDIO_FRAME_SIZE);
decoded_len = avcodec_decode_audio3(m_codec_ctx, (short *)m_audio_buff, &frame_size, &m_avpkt);
LOGI("%s, current decoded size:%d", __FUNCTION__, decoded_len);
if (decoded_len > 0)
{
m_avpkt.size -= decoded_len;
m_avpkt.data += decoded_len;

// copy audio data to output buff
if ((num_samples-copied_len) > decoded_audio_len)
{
memcpy(buff+copied_len, m_audio_buff, frame_size);
copied_len += frame_size;
LOGI("%s, copy1, %d bytes has copied to output buff, total:%d!", __FUNCTION__, frame_size, copied_len);
}
else
{
real_copied = (num_samples-copied_len);
memcpy(buff+copied_len, m_audio_buff, real_copied);
copied_len += real_copied;
LOGI("%s, copy2, %d bytes has copied to output buff, total:%d!", __FUNCTION__, real_copied, copied_len);

m_audio_buff_backup_len = frame_size - real_copied;
memcpy(m_audio_buff_backup, m_audio_buff+real_copied, m_audio_buff_backup_len);
LOGI("%s, copy2, %d bytes has copied to backup buff!", __FUNCTION__, m_audio_buff_backup_len);
break;
}
}
else
{
LOGE("%s, decoded size is error, returned!", __FUNCTION__);
break;
}
}
}
}

LOGD("exit internal_getaudio(), decoded length: %d", copied_len);
return copied_len;
}

3,关闭和释放资源

int internal_close()
{
if (m_format_ctx)
{
av_close_input_file(m_format_ctx);
m_format_ctx = 0;
}

return 0;
}
int internal_release()
{
if (m_avpkt.data)
{
av_free_packet(&m_avpkt);
}
if (!m_codec_ctx)
{
avcodec_close(m_codec_ctx);
m_codec_ctx = 0;
}

return 0;
}