FFmpeg基础:SDL播放音频流数据

从视频文件中抽离出来的音频流,需要经过解析之后发送给声卡才能通过对应的播放设备来进行播放。这里介绍一下如何通过FFmpeg库和SDL库来实现音频流的播放。
使用FFmpeg库和SDL库播放音频流的流程如下图所示:
FFmpeg基础:SDL播放音频流数据_第1张图片
播放音频流的示例代码如下所示:
(使用mp4封装格式中的aac音频流验证通过)

#include 
#include 
extern "C"
{
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
}
#include "SDL.h"
#include "SDL_thread.h"
#include "SDL_syswm.h"
#include "SDL_render.h"
#include "SDL_audio.h"


typedef struct _AudioPacket
{
	AVPacketList* first, *last;
	int nb_packets, size;
	SDL_mutex* mutex;
	SDL_cond* cond;
} AudioPacket;

#define SDL_AUDIO_BUFFER_SIZE 1024
#define MAX_AUDIO_FRAME_SIZE 192000

static AVFrame wanted_frame;
static AudioPacket audioq;
static SDL_AudioSpec wantedSpec = { 0 }, audioSpec = { 0 };
struct SwrContext* swrCtx = NULL;

//从数据队列里面取数据
int getAudioPacket(AudioPacket* q, AVPacket* pkt, int block) {

	AVPacketList* pktl;
	int ret;

	SDL_LockMutex(q->mutex);

	while (1)
	{
		pktl = q->first;
		if (pktl)
		{
			q->first = pktl->next;
			if (!q->first)
				q->last = NULL;

			q->nb_packets--;
			q->size -= pktl->pkt.size;

			*pkt = pktl->pkt;
			av_free(pktl);
			ret = 1;
			break;
		}
		else if (!block)
		{
			ret = 0;
			break;
		}
		else
		{
			SDL_CondWait(q->cond, q->mutex);
		}
	}

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

//从音频流中解析数据包
int audio_decode_frame(AVCodecContext* aCodecCtx, uint8_t* audio_buf, int buf_size) {

	static AVPacket pkt;
	static uint8_t* audio_pkt_data = NULL;
	static int audio_pkt_size = 0;
	static AVFrame frame;

	int len1;
	int data_size = 0;
	SwrContext* swr_ctx = NULL;

	while (1)
	{
		//取到数据之后解析数据
		while (audio_pkt_size > 0)
		{
			int got_frame = 0;
			avcodec_send_packet(aCodecCtx, &pkt);
			avcodec_receive_frame(aCodecCtx, &frame);

			len1 = frame.pkt_size;
			if (len1 < 0)
			{
				audio_pkt_size = 0;
				break;
			}

			//拷贝音频数据
			audio_pkt_data += len1;
			audio_pkt_size -= len1;
			data_size = 0;

			if (got_frame)
			{
				int linesize = 1;
				data_size = av_samples_get_buffer_size(&linesize, aCodecCtx->channels, frame.nb_samples, aCodecCtx->sample_fmt, 1);
				assert(data_size <= buf_size);
				memcpy(audio_buf, frame.data[0], data_size);
			}

			//获取通道信息
			if (frame.channels > 0 && frame.channel_layout == 0)
				frame.channel_layout = av_get_default_channel_layout(frame.channels);
			else if (frame.channels == 0 && frame.channel_layout > 0)
				frame.channels = av_get_channel_layout_nb_channels(frame.channel_layout);

			if (swr_ctx)
			{
				swr_free(&swr_ctx);
				swr_ctx = NULL;
			}

			//对音频格式进行转换,重采样
			swr_ctx = swr_alloc_set_opts(NULL, wanted_frame.channel_layout, (AVSampleFormat)wanted_frame.format, wanted_frame.sample_rate,
				frame.channel_layout, (AVSampleFormat)frame.format, frame.sample_rate, 0, NULL);

			if (!swr_ctx || swr_init(swr_ctx) < 0)
			{
				printf("swr_init failed\n");
			}

			int dst_nb_samples = (int)av_rescale_rnd(swr_get_delay(swr_ctx, frame.sample_rate) + frame.nb_samples,
				wanted_frame.sample_rate, wanted_frame.format, AV_ROUND_INF);
			int len2 = swr_convert(swr_ctx, &audio_buf, dst_nb_samples,
				(const uint8_t**)frame.data, frame.nb_samples);
			if (len2 < 0)
			{
				printf("swr_convert failed\n");
			}
			av_packet_unref(&pkt);

			if (swr_ctx)
			{
				swr_free(&swr_ctx);
				swr_ctx = NULL;
			}
			//返回数据长度
			return wanted_frame.channels * len2 * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
		}

		//从队列里面取数据
		if (getAudioPacket(&audioq, &pkt, 1) < 0)
			return -1;
		audio_pkt_data = pkt.data;
		audio_pkt_size = pkt.size;
	}
}


//音频数据包的回调函数
void audio_callback(void* userdata, Uint8* stream, int len)
{
	AVCodecContext* aCodecCtx = (AVCodecContext*)userdata;
	int len1, audio_size;

	static uint8_t audio_buff[192000 * 3 / 2];
	static unsigned int audio_buf_size = 0;
	static unsigned int audio_buf_index = 0;

	SDL_memset(stream, 0, len);

	while (len > 0)
	{
		if (audio_buf_index >= audio_buf_size)
		{
			audio_size = audio_decode_frame(aCodecCtx, audio_buff, sizeof(audio_buff));
			if (audio_size < 0)
			{
				audio_buf_size = 1024;
				memset(audio_buff, 0, audio_buf_size);
			}
			else
				audio_buf_size = audio_size;

			audio_buf_index = 0;
		}

		//播放取到的音频数据
		len1 = audio_buf_size - audio_buf_index;
		if (len1 > len)
			len1 = len;

		SDL_MixAudio(stream, audio_buff + audio_buf_index, len, SDL_MIX_MAXVOLUME);
		len -= len1;
		stream += len1;
		audio_buf_index += len1;
	}
}

//初始化音频数据包
void init_audio_packet(AudioPacket* q)
{
	q->last = NULL;
	q->first = NULL;
	q->mutex = SDL_CreateMutex();
	q->cond = SDL_CreateCond();
}

//将数据包加入到队列中
int put_audio_packet(AVPacket* packet)
{
	AVPacketList* pktl;
	AVPacket* newPkt;
	newPkt = (AVPacket*)av_mallocz_array(1, sizeof(AVPacket));
	if (av_packet_ref(newPkt, packet) < 0)
		return -1;

	pktl = (AVPacketList*)av_malloc(sizeof(AVPacketList));
	if (!pktl)
		return -1;

	pktl->pkt = *newPkt;
	pktl->next = NULL;

	SDL_LockMutex(audioq.mutex);

	if (!audioq.last)
		audioq.first = pktl;
	else
		audioq.last->next = pktl;

	audioq.last = pktl;
	audioq.nb_packets++;
	audioq.size += newPkt->size;

	SDL_CondSignal(audioq.cond);
	SDL_UnlockMutex(audioq.mutex);
	return 0;
}


int main(int argc, char *argv[]) {

	int       ret = -1;
	int    audioStream;
	//媒体文件上下文
	AVFormatContext *pFormatCtx = NULL;
	//音频解码器上下文
	AVCodecContext  *pCodecAudioCtx = NULL;
	//音频解码器
	AVCodec         *pAudioCodec = NULL;

	AVFrame         *pFrame = NULL;
	AVPacket        packet;
	SDL_Event       event;

	if (argc < 2) {
		printf("Usage: command \n");
		return ret;
	}

	//初始化SDL
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
		printf("There is something wrong with your SDL Libs. Couldn't run");

	//打开音频驱动
#ifdef _WIN32
	SDL_AudioInit("directsound");
#endif

	//打开媒体文件,获取流信息
	if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0) {
		printf("Failed to open multi-media file\n");
		goto __FAIL;
	}
	if (avformat_find_stream_info(pFormatCtx, NULL)<0) {
		printf("could not find stream info\n");
		goto __FAIL;
	}

	//查找音频流
	audioStream = -1;
	for (int i = 0; i<(int)pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
			audioStream < 0) {
			audioStream = i;
		}
	}
	if (audioStream == -1) {
		goto __FAIL;
	}

	//获取音频解码器
	pAudioCodec = avcodec_find_decoder(pFormatCtx->streams[audioStream]->codecpar->codec_id);
	if (pAudioCodec == NULL)
	{
		goto __FAIL;
	}
	//创建解码器上下文并拷贝参数
	pCodecAudioCtx = avcodec_alloc_context3(pAudioCodec);
	if (pCodecAudioCtx == NULL)
	{
		goto __FAIL;
	}
	ret = avcodec_parameters_to_context(pCodecAudioCtx, pFormatCtx->streams[audioStream]->codecpar);
	if (ret < 0)
	{
		goto __FAIL;
	}
	//打开解码器
	ret = avcodec_open2(pCodecAudioCtx, pAudioCodec, NULL);

	if (ret < 0)
	{
		goto __FAIL;
	}

	//设置音频参数转换的上下文
	swrCtx = swr_alloc();
	if (swrCtx == NULL)
	{
		goto __FAIL;
	}

	//设置通道数,采样率,采样格式的输入输出格式
	av_opt_set_channel_layout(swrCtx, "in_channel_layout", pCodecAudioCtx->channel_layout, 0);
	av_opt_set_channel_layout(swrCtx, "out_channel_layout", pCodecAudioCtx->channel_layout, 0);
	av_opt_set_int(swrCtx, "in_sample_rate", pCodecAudioCtx->sample_rate, 0);
	av_opt_set_int(swrCtx, "out_sample_rate", pCodecAudioCtx->sample_rate, 0);
	av_opt_set_sample_fmt(swrCtx, "in_sample_fmt", pCodecAudioCtx->sample_fmt, 0);
	av_opt_set_sample_fmt(swrCtx, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);

	int res = swr_init(swrCtx);
	if (res != 0)
	{
		goto __FAIL;
	}

	//打开音响设备
	memset(&wantedSpec, 0, sizeof(wantedSpec));
	wantedSpec.channels = pCodecAudioCtx->channels;
	wantedSpec.freq = pCodecAudioCtx->sample_rate;
	wantedSpec.format = AUDIO_S16SYS;
	wantedSpec.silence = 0;
	wantedSpec.samples = SDL_AUDIO_BUFFER_SIZE;
	wantedSpec.userdata = pCodecAudioCtx;  //音频流的上下文
	wantedSpec.callback = audio_callback; //设置数据包的回调函数

	if (SDL_OpenAudio(&wantedSpec, &audioSpec) < 0)
	{
		printf("Failed to open audio");
		goto __FAIL;
	}
	wanted_frame.format = AV_SAMPLE_FMT_S16;
	wanted_frame.sample_rate = audioSpec.freq;
	wanted_frame.channel_layout = av_get_default_channel_layout(audioSpec.channels);
	wanted_frame.channels = audioSpec.channels;

	//初始化数据包
	init_audio_packet(&audioq);
	SDL_PauseAudio(0);

	pFrame = av_frame_alloc();

	//读取数据包
	while (av_read_frame(pFormatCtx, &packet) >= 0)
	{
		if (packet.stream_index == audioStream) {
			put_audio_packet(&packet);
		}
		else
		{
			SDL_Delay(1000 / 30);
		}
		SDL_PollEvent(&event);
		av_packet_unref(&packet);
	}
__QUIT:
	ret = 0;

__FAIL:
	//后处理清理数据
	if (pFrame) {
		av_frame_free(&pFrame);
	}
	if (pCodecAudioCtx)
	{
		avcodec_close(pCodecAudioCtx);
	}
	if (pFormatCtx) {
		avformat_close_input(&pFormatCtx);
	}
	SDL_Quit();
	return ret;
}

你可能感兴趣的:(音视频,ffmpeg,音视频,c++,编码解码,流媒体)