完整的音频播放示例

使用最新的SDL2.0版本,对于http://dranger.com/ffmpeg教程中示例改进,更新API

// tutorial_1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/pixfmt.h>
#include <libavutil/error.h>
#include <libavutil/time.h>
#include <libswresample/swresample.h>
#include <SDL.h>
#include <SDL_thread.h>
#include "libavutil/audio_fifo.h"
}
#include <assert.h>

#ifdef __MINGW32__
#undef main /* Prevents SDL from overriding main() */
#endif

#define SDL_AUDIO_BUFFER_SIZE 1024
#define MAX_AUDIO_FRAME_SIZE 192000

typedef struct PacketQueue {
	AVPacketList *first_pkt, *last_pkt;
	int nb_packets;
	int size;
	SDL_mutex *mutex;
	SDL_cond *cond;
} PacketQueue;

typedef struct AudioParams {
	int freq;
	int channels;
	int64_t channel_layout;
	enum AVSampleFormat fmt;
	int frame_size;
	int nb_samples;
	int buffer_size;
} ST_AudioParams;

PacketQueue audioq;

SwrContext * g_swrCtx;

ST_AudioParams g_audio_param_dst;

//解码缓冲区
AVAudioFifo* audiofifo = NULL;//


int ct = 0;
int quit = 0;
DWORD start_time, end_time;
FILE *g_file;

//全局解码缓冲区
uint8_t audio_decode[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
Uint32  audio_decodeLen;//每音频帧的长度
Uint8  *audio_decode_pos;//目前接收播放位置
bool b_finish_packet = false;

void packet_queue_init(PacketQueue *q);
int packet_queue_put(PacketQueue *q, AVPacket *pkt);
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block);
int audio_decode_frame(AVCodecContext *aCodecCtx, AVPacket *pPacket, uint8_t *audio_buf, int buf_size);

void  audio_callback(void *udata, Uint8 *stream, int len);

static DWORD WINAPI Thread_Decode_Audio(LPVOID lpParam);

int main(int argc, char *argv[])
{
	AVFormatContext * pFormatCtx = NULL;
	int audioStream;
	AVCodecContext  * pAudioCodecCtx;
	AVCodec         * pAudioCodec = NULL;
	AVPacket        * packet;
	AVDictionary    * pAudioOptionsDict = NULL;
	uint8_t ** audio_data_buffer = NULL;//存储转换后的数据,再编码AAC   二维数组data[0],data[1]
	SDL_Event event;
	SDL_AudioSpec  wanted_spec, real_spec;
	if (argc < 2)
	{
		std::cout << "please provide a movie file" << std::endl;
		return -1;
	}

	g_file = fopen("1.pcm", "wb");
	if (g_file == NULL)
	{
		printf("create file failed.\n");
	}
	start_time = GetTickCount();
	//注册所有的文件格式和编码器
	av_register_all();

	//打开视频文件
	if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
	{
		return -1;
	}
	//检索获取流信息
	if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
	{
		return -1;
	}
	av_dump_format(pFormatCtx, 0, argv[1], 0);
	//寻找第一个音频流
	audioStream = -1;
	int i;
	for (i = 0; i < pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0)
		{
			audioStream = i;
		}
	}
	//未找到音频流
	if (audioStream == -1)
		return -1;
	pAudioCodecCtx = pFormatCtx->streams[audioStream]->codec;

	//打印一些信息:
	printf("音频流标识符:%d\n", pFormatCtx->streams[audioStream]->index);
	printf("音频流长度:%d微秒\n", pFormatCtx->streams[audioStream]->duration);
	printf("音频时长:%d微秒\n", pFormatCtx->streams[audioStream]->duration);
	printf("音频采样率:%d\n", pFormatCtx->streams[audioStream]->codecpar->sample_rate);
	printf("音频信道数目:%d\n", pFormatCtx->streams[audioStream]->codecpar->channels);

	//获取音频编码结构
	pAudioCodec = avcodec_find_decoder(pAudioCodecCtx->codec_id);
	if (pAudioCodec == NULL)
	{
		std::cout << "Unsupported codec!\n";
		return -1;
	}

	if (avcodec_open2(pAudioCodecCtx, pAudioCodec, &pAudioOptionsDict) < 0)
	{
		return -1;
	}

	packet = (AVPacket *)av_malloc(sizeof(AVPacket));
	av_init_packet(packet);

	//g_audio_param_dst为输出音频参数
	g_audio_param_dst.frame_size = SDL_AUDIO_BUFFER_SIZE;
	g_audio_param_dst.fmt = AV_SAMPLE_FMT_S16;
	g_audio_param_dst.freq = 44100;
	g_audio_param_dst.channel_layout = AV_CH_LAYOUT_STEREO;
	g_audio_param_dst.channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO); 
	g_audio_param_dst.nb_samples = 1024;
	g_audio_param_dst.buffer_size = av_samples_get_buffer_size(NULL, g_audio_param_dst.channels, g_audio_param_dst.nb_samples, g_audio_param_dst.fmt, 1);

	audiofifo = av_audio_fifo_alloc(g_audio_param_dst.fmt, g_audio_param_dst.channels, 1);
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
		exit(1);
	}

	wanted_spec.freq = g_audio_param_dst.freq;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.channels = g_audio_param_dst.channels;
	wanted_spec.silence = 0;
	wanted_spec.samples = g_audio_param_dst.nb_samples;
	wanted_spec.callback = audio_callback;
	wanted_spec.userdata = pAudioCodecCtx;
	if (SDL_OpenAudio(&wanted_spec, NULL) < 0) {
		fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
		return -1;
	}

	g_swrCtx = swr_alloc();
	g_swrCtx = swr_alloc_set_opts(
		g_swrCtx,
		g_audio_param_dst.channel_layout,
		g_audio_param_dst.fmt,
		g_audio_param_dst.freq,
		pAudioCodecCtx->channels,
		pAudioCodecCtx->sample_fmt,
		pAudioCodecCtx->sample_rate,
		0,
		nullptr
	);
	if (g_swrCtx == NULL || swr_init(g_swrCtx) < 0)
	{
		printf("Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n",
			pAudioCodecCtx->sample_rate, av_get_sample_fmt_name(pAudioCodecCtx->sample_fmt), pAudioCodecCtx->channels,
			g_audio_param_dst.freq, av_get_sample_fmt_name(g_audio_param_dst.fmt), g_audio_param_dst.channels);
		swr_free(&g_swrCtx);
		return -1;
	}
	SDL_PauseAudio(0);

	packet_queue_init(&audioq);

	AVFrame *pFrame = av_frame_alloc();
	AVFrame* audio_frame = av_frame_alloc();
	int out_buffer_size = 0; 

	while (av_read_frame(pFormatCtx, packet) >= 0)
	{
		
		if (packet->stream_index == audioStream) {
			packet_queue_put(&audioq, packet);
			printf("audioq size is %d\n", audioq.nb_packets);
		}
		else {
			av_packet_unref(packet);
		}
	}
	HANDLE hThread = CreateThread(NULL, 0, Thread_Decode_Audio, (LPVOID)pAudioCodecCtx, NULL, NULL);
	printf("test1..\n");
	WaitForSingleObject(hThread, INFINITE);
	printf("test2..\n");
	//getchar();

	avcodec_close(pAudioCodecCtx);

	avformat_close_input(&pFormatCtx);

	return 0;
}

void packet_queue_init(PacketQueue *q) {
	memset(q, 0, sizeof(PacketQueue));
	q->mutex = SDL_CreateMutex();
	q->cond = SDL_CreateCond();
}


int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

	AVPacketList *pkt1;
	//if (av_dup_packet(pkt) < 0) {
	//	return -1;
	//}
	pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
	if (!pkt1)
		return -1;

	if (av_packet_ref(&pkt1->pkt, pkt) < 0)
		return -1;

	//pkt1->pkt = *pkt;
	pkt1->next = NULL;


	SDL_LockMutex(q->mutex);

	if (!q->last_pkt)
		q->first_pkt = pkt1;
	else
		q->last_pkt->next = pkt1;
	q->last_pkt = pkt1;
	q->nb_packets++;
	q->size += pkt1->pkt.size;
	SDL_CondSignal(q->cond);

	SDL_UnlockMutex(q->mutex);
	return 0;
}

static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
{
	AVPacketList *pkt1;
	int ret;

	SDL_LockMutex(q->mutex);

	for (;;) {

		if (quit) {
			ret = -1;
			break;
		}

		pkt1 = q->first_pkt;
		if (pkt1) {
			q->first_pkt = pkt1->next;
			if (!q->first_pkt)
				q->last_pkt = NULL;
			q->nb_packets--;
			q->size -= pkt1->pkt.size;
			//*pkt = pkt1->pkt;
			if (av_packet_ref(pkt, &pkt1->pkt) < 0)
			{
				ret = 0;
				break;
			}
			av_free(pkt1);
			ret = 1;
			break;
		}
		else if (!block) {
			ret = 0;
			break;
		}
		else {
			SDL_CondWait(q->cond, q->mutex);
		}
	}
	SDL_UnlockMutex(q->mutex);
	return ret;
}

/*
*
*第一个参数 aCodecCtx,用于解码获取帧数据
*第二个参数 audio_buf,用户存储解码后的音频数据
*第三个参数 buf_size是audio_buf的大小
*/
int audio_decode_frame(AVCodecContext *aCodecCtx, AVPacket *pPacket, uint8_t *audio_buf, int buf_size) {
	static int ct = 0;
	AVPacket pkt;
	uint8_t *audio_data = NULL;
	
	AVFrame * pFrame = av_frame_alloc();
	AVFrame* audio_frame = av_frame_alloc();
	int len1, data_size = 0;
	bool need_newPacket = false;
	int ret = 0;
	int res = 0;
	int nb_samples = 0;             // 重采样输出样本数
	uint8_t ** audio_data_buffer = NULL;
	while (audioq.nb_packets) {
		need_newPacket = false;
		ret = avcodec_receive_frame(aCodecCtx, pFrame);
		if (ret != 0)
		{
			if (ret == AVERROR_EOF)
			{
				printf("audio avcodec_receive_frame(): the decoder has been fully flushed\n");
				res = 0;
				goto exit;
			}
			else if (ret == AVERROR(EAGAIN))
			{
				need_newPacket = true;
				res = 0;
			}
			else if (ret == AVERROR(EINVAL))
			{
				printf("audio avcodec_receive_frame(): codec not opened, or it is an encoder\n");
				res = -1;
				goto exit;
			}
			else
			{
				printf("audio avcodec_receive_frame(): legitimate decoding errors\n");
				res = -1;
				goto exit;
			}
		}
		else
		{
			av_samples_alloc_array_and_samples(
				&audio_data_buffer,// 此时data[0],data[1]分别指向audio_data_buffer数组起始、中间地址 
				NULL,
				g_audio_param_dst.channels,
				pFrame->nb_samples,
				g_audio_param_dst.fmt,
				1
			);
			int convert_size = swr_convert(
				g_swrCtx,// 转换数据,令各自声道的音频数据存储在不同的数组(分别由不同指针指向)
				audio_data_buffer,
				pFrame->nb_samples,
				(const uint8_t**)pFrame->data,
				pFrame->nb_samples
			);
			ret = av_audio_fifo_realloc(audiofifo, av_audio_fifo_size(audiofifo) + convert_size);
			if (ret < 0) {
				printf("av_audio_fifo_realloc error\n");
				return -1;
			}
			if (av_audio_fifo_write(audiofifo, (void **)audio_data_buffer, convert_size) < convert_size) {
				printf("av_audio_fifo_write error\n");
				return -1;
			}
			while (av_audio_fifo_size(audiofifo) >= g_audio_param_dst.frame_size) {
				int frame_size = 0;
				frame_size = FFMIN(av_audio_fifo_size(audiofifo), g_audio_param_dst.frame_size);

				//申请AVFrame成员的 buffer空间,并设置属性
				audio_frame->nb_samples = frame_size;
				audio_frame->channel_layout = g_audio_param_dst.channel_layout;
				audio_frame->format = g_audio_param_dst.fmt;
				audio_frame->sample_rate = g_audio_param_dst.freq;
				av_frame_get_buffer(audio_frame, 0);

				if (av_audio_fifo_read(audiofifo, (void **)audio_frame->data, frame_size) < frame_size) {
					printf("av_audio_fifo_read error\n");
					return -1;
				}
				//同步回调函数,等待回调函数取完数据,audio_decodeLen全局变量
				while (audio_decodeLen > 0)//Wait until finish
					SDL_Delay(1);
				//audio_decode_pos保存每一帧的数据
				audio_decode_pos = *audio_frame->data;
				//audio_decodeLen每一帧的数据字节数
				audio_decodeLen = g_audio_param_dst.buffer_size;

				res = audio_decodeLen;

				if (++ct == 30)
				{
					ct = 0;
					end_time = GetTickCount();
					printf("音频帧率 %f \n", 30000.0 / (end_time - start_time));
					start_time = end_time;
				}
			}
		}
		if (need_newPacket)
		{
			if (packet_queue_get(&audioq, pPacket, 1) <= 0)
			{
				av_packet_unref(pPacket);

				res = -1;
				goto exit;
			}
			else
			{
				printf("解码 packet.size %d\n", audioq.nb_packets);
			}
			ret = avcodec_send_packet(aCodecCtx, pPacket);
			if (ret != 0)
			{
				printf("avcodec_send_packet() failed %d\n", ret);
				av_packet_unref(pPacket);
				res = -1;
				goto exit;
			}
		}

	}


exit:
	av_frame_unref(pFrame);
	av_frame_unref(audio_frame);
	av_packet_unref(pPacket);
	b_finish_packet = true;
	return res;
}


static DWORD WINAPI Thread_Decode_Audio(LPVOID lpParam)
{
	DWORD dwRet = 0;
	AVCodecContext *aCodecCtx = (AVCodecContext *)lpParam;
	AVPacket * pPacket = NULL;
	pPacket = (AVPacket *)av_malloc(sizeof(AVPacket));
	//取packet
	av_init_packet(pPacket);

	//解码
	audio_decode_frame(aCodecCtx, pPacket, audio_decode, sizeof(audio_decode));

	return dwRet;
}
/*
*
第一个参数userdata是AVCodecContext,为了获取AVPacket传入AVCodecContext结构体,用于解码

第二个参数stream指向需要填充的音频缓冲区

第三个参数len,表示音频缓存区的大小
*
*/
void  audio_callback(void *udata, Uint8 *stream, int len) {

	SDL_memset(stream, 0, len);//初始化缓存区
	if (audio_decodeLen == 0)
		return;
	len = (len > audio_decodeLen ? audio_decodeLen : len);
	SDL_MixAudio(stream, audio_decode_pos, len, SDL_MIX_MAXVOLUME);
	//输出pcm文件
	fwrite((uint8_t *)audio_decode_pos, 1, len, g_file);
	audio_decode_pos += len;
	audio_decodeLen -= len;
}


Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐