首页 > 技术文章 > FFMPEG学习----使用SDL构建音频播放器

lgh1992314 2016-09-01 11:48 原文

ffmpeg版本:ffmpeg-20160413-git-0efafc5

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswresample/swresample.h"
#include "SDL.h"
};


#define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio



Uint32  audio_len;//音频数据大小
Uint8  *audio_pos;//指向音频数据的指针

//回调函数
void  fill_audio(void *userdata, Uint8 *stream, int len)
{
	//SDL 2.0
	SDL_memset(stream, 0, len);
	if (audio_len == 0)
	{
		return;
	}

	len = (len > audio_len ? audio_len : len);

	SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
	audio_pos += len;
	audio_len -= len;
}


int main(int argc, char* argv[])
{
	//FFmpeg
	AVFormatContext		*pFormatCtx;
	AVCodecContext		*pCodecCtx;
	AVCodec				*pCodec;
	AVPacket			packet;
	AVFrame				*pFrame;
	struct SwrContext	*au_convert_ctx;
	int					got_picture;
	int					audioIndex;
	
	//SDL
	uint8_t				*out_buffer;
	SDL_AudioSpec		wanted_spec;
	int					index = 0;
	

	char filepath[1024] = "";
	printf("Usage: player.exe *.mp3\n");
	if (argc == 2)
	{
		strcpy(filepath, argv[1]);
	}
	else
	{
		printf("Could not find a audio file\n");
		return -1;
	}


	av_register_all();

	pFormatCtx = avformat_alloc_context();
	
	if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0)
	{
		printf("Couldn't open input stream.\n");
		return -1;
	}
	
	if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
	{
		printf("Couldn't find stream information.\n");
		return -1;
	}
	
	av_dump_format(pFormatCtx, -1, filepath, 0);

	
	audioIndex = -1;
	for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++)
	{
		//AVStream->codec不推荐使用
		if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audioIndex = i;
			break;
		}
	}
	if (audioIndex == -1)
	{
		printf("Didn't find a audio stream.\n");
		return -1;
	}

	//------------------------------------------------
	//新版本:分配、填充AVCodecContext
	pCodecCtx = avcodec_alloc_context3(NULL);
	if (pCodecCtx == NULL)
	{
		printf("Could not allocate AVCodecContext\n");
		return -1;
	}

	if (avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[audioIndex]->codecpar) < 0)
	{
		printf("Could not initialize AVCodecContext\n");
		return -1;
	}
	//------------------------------------------------

	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL)
	{
		printf("Codec not found.\n");
		return -1;
	}

	if (avcodec_open2(pCodecCtx, pCodec, NULL)<0)
	{
		printf("Could not open codec.\n");
		return -1;
	}



	//Out Audio Param
	uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
	//nb_samples: AAC-1024 MP3-1152
	int out_nb_samples = pCodecCtx->frame_size;
	AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
	int out_sample_rate = 44100;
	int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
	//Out Buffer Size
	int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);

	out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
	pFrame = av_frame_alloc();


	//Init
	if (SDL_Init(SDL_INIT_AUDIO)) 
	{
		printf("Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}
	//SDL_AudioSpec
	wanted_spec.freq = out_sample_rate;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.channels = out_channels;
	wanted_spec.silence = 0;
	wanted_spec.samples = out_nb_samples;
	wanted_spec.callback = fill_audio;
	wanted_spec.userdata = pCodecCtx;

	if (SDL_OpenAudio(&wanted_spec, NULL) < 0)
	{
		printf("can't open audio.\n");
		return -1;
	}


	au_convert_ctx = swr_alloc();
	au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,
		pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0, NULL);
	swr_init(au_convert_ctx);

	//Play
	SDL_PauseAudio(0);

	while (av_read_frame(pFormatCtx, &packet) >= 0)
	{
		if (packet.stream_index == audioIndex)
		{
			if (avcodec_decode_audio4(pCodecCtx, pFrame, &got_picture, &packet) < 0)
			{
				printf("Error in decoding audio frame.\n");
				return -1;
			}
			if (got_picture)
			{
				swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)pFrame->data, pFrame->nb_samples);
				printf("index:%5d\t pts:%lld\t packet size:%d\n", index, packet.pts, packet.size);
				index++;
			}


			while (audio_len > 0)//Wait until finish
			{
				SDL_Delay(1);
			}

			//Audio buffer length
			audio_len = out_buffer_size;
			audio_pos = (Uint8 *)out_buffer;

		}
		av_packet_unref(&packet);
	}

	swr_free(&au_convert_ctx);
	SDL_CloseAudio();//Close SDL
	SDL_Quit();
	av_free(out_buffer);
	avcodec_free_context(&pCodecCtx);
	avformat_close_input(&pFormatCtx);
	return 0;
}


推荐阅读