天天看点

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

最近忙于使用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;
}