VS2015 FFMPEG3.3.1+SDL2.0无损音频播放器

首先感谢雷神的无私奉献,没有雷神,ffmpeg这么好个框架感觉真是荒废了,连android都可以用jni层使用ffmpeg解码,以前试过,非常强悍,望雷神一路走好。附上雷神最全面强悍的老版本的教程:FFMPEG视音频编解码零基础学习方法,还有感谢我们的hell小姐姐,最新版音视频结合的ffmpeg3.2.2+sdl2.0,省去我从那恶心ffplay.c中提取了。hell小姐姐文章:FFMPEG-3.2.2 SDL-2.0.5(3)


首先我讲解下,对于目前网上出现的源码解码ape,flac格式解码遇到的问题,pAudioCodecCtxOrig->frame_size获取不到,ape,flac等格式为0,导致swr转换以及sdl无法播放。第二个是ffmpeg github上官方源码示例,decode_audio.c中的问题,即使用fread直接读文件导致header missing。最开始挺纳闷的,ffplay是没有问题的,所以我当时着手点是想去看ffplay里面的逻辑,最后发现其实可以不用那么复杂。


源码仅公开ffmpeg+sdl部分,其余内容不予公开


代码的解决思路,就是从frame中获取nb_samples来计算out_buffer分配大小,以及使用avformat_open_input读取文件头信息到pformat中,av_read_frame传入pformat读取信息到packet中

#include "stdafx.h"

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

//swr
struct SwrContext* au_convert_ctx;
AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
int out_buffer_size;
uint8_t* out_buffer;

//decode
int audioStream = -1;
AVFormatContext* pFormateCtx = NULL;
AVCodecParameters* audioCodecParameter;
AVCodecContext* pAudioCodecCtxOrig = NULL;
AVCodec* pAudioCodec = NULL;
AVFrame* pFrame = NULL;


#if USE_SDL
static Uint8* audio_chunk;
static Uint32 audio_len;
static Uint8* audio_pos;
SDL_AudioSpec wanted_spec;
#endif


void decode(AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame,
            FILE* outfile);
void fill_audio(void* udata, Uint8* stream, int len);

int main(int argc, char** argv)
{
	char filePath[256] = "D:\\123.flac";

	//初始化
	av_register_all();

	//读取文件头和存储信息到pFormateCtx中
	if (avformat_open_input(&pFormateCtx, filePath, NULL, 0) != 0)
	{
		printf_s("avformat_open_input failed\n");
		return -1;
	}

	if (avformat_find_stream_info(pFormateCtx, NULL) < 0)
	{
		printf_s("avformat_find_stream_info failed\n");
		return -1;
	}
	av_dump_format(pFormateCtx, 0, filePath, 0);

	for (unsigned i = 0; i < pFormateCtx->nb_streams; ++i)
	{
		if (AVMEDIA_TYPE_AUDIO == pFormateCtx->streams[i]->codecpar->codec_type)
		{
			audioStream = i;
			continue;
		}
	}

	if (-1 == audioStream)
	{
		printf_s("Can't find audio stream\n");
		return -1;
	}


	//找音频解码器
	audioCodecParameter = pFormateCtx->streams[audioStream]->codecpar;
	pAudioCodec = avcodec_find_decoder(audioCodecParameter->codec_id);
	if (NULL == pAudioCodec)
	{
		printf_s("avcodec_find_decoder failed audio\n");
		return -1;
	}
	
	pAudioCodecCtxOrig = avcodec_alloc_context3(pAudioCodec);


	if (avcodec_parameters_to_context(pAudioCodecCtxOrig, audioCodecParameter) < 0)
	{
		printf_s("avcodec_parameters_to_context failed\n");
		return -1;
	}

	avcodec_open2(pAudioCodecCtxOrig, pAudioCodec, NULL);
	
	uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
	int out_sample_rate = 44100;
	int in_channel_layout = av_get_default_channel_layout(pAudioCodecCtxOrig->channels);
	au_convert_ctx = swr_alloc();
	au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout, out_sample_fmt, out_sample_rate,
	                                    in_channel_layout, pAudioCodecCtxOrig->sample_fmt, pAudioCodecCtxOrig->sample_rate, 0, NULL);
	swr_init(au_convert_ctx);


	FILE* outfile = NULL;
#if WRITE_PCM
	fopen_s(&outfile, "D:\\output.pcm", "wb");
#endif

	AVPacket packet;
	while (av_read_frame(pFormateCtx, &packet) >= 0)
	{
		if (!pFrame)
		{
			if (!(pFrame = av_frame_alloc()))
			{
				fprintf(stderr, "Could not allocate audio frame\n");
				exit(1);
			}
		}
		if (packet.size)
			decode(pAudioCodecCtxOrig, &packet, pFrame, outfile);

		av_frame_free(&pFrame);
		av_packet_unref(&packet);
	}
#if USE_SDL 
	SDL_CloseAudio();//Close SDL  
	SDL_Quit();
#endif 

#if WRITE_PCM
	fclose(outfile);
#endif
	
	avcodec_parameters_free(&audioCodecParameter);
	avcodec_free_context(&pAudioCodecCtxOrig);
	av_free(pFrame);
	swr_free(&au_convert_ctx);
	av_free(pAudioCodecCtxOrig);
	av_free(out_buffer);
	
	return 0;
}

#if USE_SDL
void fill_audio(void* udata, Uint8* stream, int len)
{
	//SDL 2.0  
	SDL_memset(stream, 0, len);
	if (audio_len == 0)
		return;

	len = (len > audio_len ? audio_len : len); /*  Mix  as  much  data  as  possible  */

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

bool init = false;

void decode(AVCodecContext* dec_ctx, AVPacket* pkt, AVFrame* frame,
            FILE* outfile)
{
	int i, ch;
	int ret, data_size;
	/* send the packet with the compressed data to the decoder */
	ret = avcodec_send_packet(dec_ctx, pkt);
	if (ret < 0)
	{
		fprintf(stderr, "Error submitting the packet to the decoder\n");
		exit(1);
	}
	/* read all the output frames (in general there may be any number of them */
	while (ret >= 0)
	{
		ret = avcodec_receive_frame(dec_ctx, frame);

		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			return;
		else if (ret < 0)
		{
			fprintf(stderr, "Error during decoding\n");
			exit(1);
		}
		data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
		if (data_size < 0)
		{
			/* This should not occur, checking just for paranoia */
			fprintf(stderr, "Failed to calculate data size\n");
			exit(1);
		}

		if (!init)
		{
			//ape 4608 flac 4096 mp3 1152
			//nb_samples only can get by frame like flac,ape....but mp3 or etc. can get by pcodectx
			int out_nb_samples = frame->nb_samples;
			out_buffer_size = av_samples_get_buffer_size(NULL, pAudioCodecCtxOrig->channels, out_nb_samples, out_sample_fmt, 1);
			out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
#if USE_SDL
			//Init  
			if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER))
			{
				printf("Could not initialize SDL - %s\n", SDL_GetError());
				break;
			}
			//SDL_AudioSpec  
			wanted_spec.freq = 44100;
			wanted_spec.format = AUDIO_S16SYS ;
			wanted_spec.channels = pAudioCodecCtxOrig->channels;
			wanted_spec.silence = 0;
			wanted_spec.samples = frame->nb_samples;
			wanted_spec.callback = fill_audio;
			wanted_spec.userdata = pAudioCodecCtxOrig;

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

			SDL_PauseAudio(0);
#endif 
			init = true;
		}
		swr_convert(au_convert_ctx, &out_buffer, out_buffer_size,
		            (const uint8_t**)frame->data, frame->nb_samples);
#if WRITE_PCM
		//write data without swr ,some source data put only in data[0]
		//for (int i = 0; ichannels; i++)//it may differ channel put on same data
		//{
		//	if (frame->data[i] == 0)
		//	{
		//		dec_ctx->channels--;
		//		frame->nb_samples *= 2;
		//	}
		//}
		//for (i = 0; i < frame->nb_samples; i++) {
		//	for (ch = 0; ch < dec_ctx->channels; ch++)
		//	{
		//			fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
		//	}
		//}

		fwrite(out_buffer, out_buffer_size, 1, outfile);
#endif
#if USE_SDL 

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

		//Set audio buffer (PCM data)  
		audio_chunk = (Uint8 *)out_buffer;
		//Audio buffer length  
		audio_len = out_buffer_size;
		audio_pos = audio_chunk;

#endif 
	}
}


你可能感兴趣的:(C/C++)