FFmpeg_3.2.4+SDL_2.0.5学习(1)音视频解码帧及显示/播放数据

2018-03-08 14:37:15 浏览数 (1)

代码语言:javascript复制
int OpenAVFile(const char* szFileName)
{
	char errbuf[256] = { 0 };
	int iRes = 0;
	int vindex = -1;
	AVFormatContext* pFmtCtx = NULL;
	AVCodecContext* vCodecCtx = NULL;
	AVCodec* vCodec = NULL;
	AVPacket* pkt = NULL;
	AVFrame* pfe = NULL;
	AVFrame* YUV = NULL;
	uint8_t* buf = NULL;
	struct SwsContext* img_ctx = NULL;

	SDL_Window* window = NULL;
	SDL_Renderer* renderer = NULL;
	SDL_Texture* texture = NULL;
	SDL_Rect rect= { 0 };

	av_register_all();
	if(SDL_Init(SDL_INIT_VIDEO) != 0)
		return ERROR_INIT;

	pFmtCtx = avformat_alloc_context();
	if ((iRes = avformat_open_input(&pFmtCtx, szFileName, NULL, NULL)) != 0)
		return ERROR_OPEN;
	if (avformat_find_stream_info(pFmtCtx, NULL) < 0)
		return ERROR_FIND;
	av_dump_format(pFmtCtx, -1, szFileName, NULL);

	for (int i = 0; i < pFmtCtx->nb_streams;   i)
	{
		if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
			vindex = i;
	}
	if (vindex == -1)
		return ERROR_FIND;

	vCodecCtx = avcodec_alloc_context3(NULL);
	if(avcodec_parameters_to_context(vCodecCtx, pFmtCtx->streams[vindex]->codecpar) < 0)
		return ERROR_COPY;
	vCodec = avcodec_find_decoder(vCodecCtx->codec_id);
	if (!vCodec)
		return ERROR_FIND;
	if(avcodec_open2(vCodecCtx, vCodec, NULL) != 0)
		return ERROR_OPEN;

	window = SDL_CreateWindow("video", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 680, 540, SDL_WINDOW_OPENGL);
	renderer = SDL_CreateRenderer(window, -1, 0);
	texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, vCodecCtx->width, vCodecCtx->height);
	if (!window || !renderer || !texture)
		return ERROR_CREATE;
	rect.w = vCodecCtx->width;
	rect.h = vCodecCtx->height;

	img_ctx = sws_getContext(vCodecCtx->width, vCodecCtx->height, vCodecCtx->pix_fmt,
		vCodecCtx->width, vCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL,NULL);
	if (!img_ctx)
		return ERROR_GET;
	pkt = av_packet_alloc();
	pfe = av_frame_alloc();
	YUV = av_frame_alloc();
	buf = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height,1));
	av_image_fill_arrays(YUV->data, YUV->linesize, buf, AV_PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height, 1);
	while (av_read_frame(pFmtCtx, pkt) >= 0)
	{
		if (pkt->stream_index == vindex)
		{
			if ((iRes = avcodec_send_packet(vCodecCtx, pkt)) != 0)
				av_strerror(iRes, errbuf, 256);//return -5;
			if ((iRes=  avcodec_receive_frame(vCodecCtx, pfe)) != 0)
				av_strerror(iRes, errbuf, 256); //return -6;
			sws_scale(img_ctx, pfe->data, pfe->linesize, 0, vCodecCtx->height, YUV->data, YUV->linesize);
			
			SDL_UpdateTexture(texture, &rect, YUV->data[0], YUV->linesize[0]);
			SDL_RenderClear(renderer);
			SDL_RenderCopy(renderer, texture, NULL, NULL);
			SDL_RenderPresent(renderer);
		}
	}
	
	av_free(buf);
	av_frame_free(&YUV);
	av_frame_free(&pfe);
	av_packet_free(&pkt);
	sws_freeContext(img_ctx);
	SDL_DestroyTexture(texture);
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();
	avcodec_free_context(&vCodecCtx);
	avformat_close_input(&pFmtCtx);
	avformat_free_context(pFmtCtx);
	return 0;
}
代码语言:javascript复制
uint8_t* g_buf = NULL;
int g_MaxLen = 0;
int g_CurPos = 0;
void aCallback(void *userdata, Uint8 * stream, int len)
{
	SDL_memset(stream, 0, len);//important
	SDL_MixAudio(stream, g_buf, len, SDL_MIX_MAXVOLUME);//memcpy(stream, g_buf, len);
	g_CurPos  = len;
	g_MaxLen -= len;
}

int OpenAVFile(const char* szFileName)
{
	char errbuf[256] = { 0 };
	int iRes = 0;
	int aindex = -1;
	AVFormatContext* pFmtCtx = NULL;
	AVCodecContext* aCodecCtx = NULL;
	AVCodec* aCodec = NULL;
	AVPacket* pkt = NULL;
	AVFrame* pfe = NULL;
	uint8_t* buf = NULL;
	struct SwrContext* audio_ctx = NULL;
	SDL_AudioSpec want = { 0 };
	SDL_AudioSpec recv = { 0 };

	av_register_all();
	if (SDL_Init(SDL_INIT_AUDIO) != 0)
		return ERROR_INIT;

	pFmtCtx = avformat_alloc_context();
	if ((iRes = avformat_open_input(&pFmtCtx, szFileName, NULL, NULL)) != 0)
		return ERROR_OPEN;
	if (avformat_find_stream_info(pFmtCtx, NULL) < 0)
		return ERROR_FIND;
	av_dump_format(pFmtCtx, -1, szFileName, NULL);

	for (int i = 0; i < pFmtCtx->nb_streams;   i)
	{
		if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
			aindex = i;
	}
	if (aindex == -1)
		return ERROR_FIND;

	aCodecCtx = avcodec_alloc_context3(NULL);
	if (avcodec_parameters_to_context(aCodecCtx, pFmtCtx->streams[aindex]->codecpar) < 0)
		return ERROR_COPY;
	aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
	if (!aCodec)
		return ERROR_FIND;
	if (avcodec_open2(aCodecCtx, aCodec, NULL) != 0)
		return ERROR_OPEN;

	want.callback = aCallback;
	//want.channels = 1;//设为单声道就不用装换pannel了
	want.channels = aCodecCtx->channels;
	want.format = AUDIO_S16SYS;//ffmpeg的fmt与SDL的不一样,但是可以自己计算转换
	want.freq = aCodecCtx->sample_rate;
	want.samples = aCodecCtx->frame_size;
	want.silence = 0;
	want.userdata = aCodecCtx;
	SDL_OpenAudio(&want, &recv);

	//audio_ctx = swr_alloc();
	audio_ctx = swr_alloc_set_opts(NULL, av_get_default_channel_layout(recv.channels), AV_SAMPLE_FMT_S16, recv.freq,
		av_get_default_channel_layout(aCodecCtx->channels), aCodecCtx->sample_fmt, aCodecCtx->sample_rate, 0, NULL);
	swr_init(audio_ctx);//必须要

	pkt = av_packet_alloc();
	pfe = av_frame_alloc();
	g_buf = (uint8_t*)av_malloc(av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1));
	while (av_read_frame(pFmtCtx, pkt) >= 0)
	{
		if (pkt->stream_index == aindex)
		{
			if ((iRes = avcodec_send_packet(aCodecCtx, pkt)) != 0)
				av_strerror(iRes, errbuf, 256);//return -5;
			if ((iRes = avcodec_receive_frame(aCodecCtx, pfe)) != 0)
				av_strerror(iRes, errbuf, 256); //return -6;

			//单声道
			//memset(g_buf, 0, 10240);
			//memcpy(g_buf, pfe->data[0], pfe->linesize[0]);
			//g_CurPos = 0;
			//g_MaxLen = pfe->linesize[0];

			swr_convert(audio_ctx, &g_buf, av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1), 
				(const uint8_t **)pfe->data, pfe->nb_samples);
			g_CurPos = 0;
			g_MaxLen = av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1);

			SDL_PauseAudio(0);
			while (g_MaxLen > 0)
				SDL_Delay(1);
		}
	}

	av_free(buf);
	av_frame_free(&pfe);
	av_packet_free(&pkt);
	swr_free(&audio_ctx);
	SDL_Quit();
	avcodec_free_context(&aCodecCtx);
	avformat_close_input(&pFmtCtx);
	avformat_free_context(pFmtCtx);
	return 0;
}

视频的重要处理: sws_getContext,获得转换上下文

av_image_get_buffer_size,获得(转换后)图片大小

av_image_fill_arrays,将自定义内存块绑定到输出的AVFrame中

sws_scale,转换

音频的重要处理:

av_get_default_channel_layout,根据声道数获取默认的声道布局

swr_alloc_set_opts,获取转换上下文

swr_init,获取到上下文后必须初始化

av_samples_get_buffer_size,计算(参数格式的)音频数据的大小

swr_convert,转换 对于音频的相关参数,FFmpeg和SDL中的channels,sample_rate...等等这些"数值"都是可以直接互相赋值的。但是format,FFmpeg和SDL就各自有自己的定义。

下面是我自己做的由AVSampleFormat转SDL_AudioFormat的函数

代码语言:javascript复制
/*{
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
AV_SAMPLE_FMT_S16,         ///< signed 16 bits
AV_SAMPLE_FMT_S32,         ///< signed 32 bits
AV_SAMPLE_FMT_FLT,         ///< float
AV_SAMPLE_FMT_DBL,         ///< double

AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP,        ///< float, planar
AV_SAMPLE_FMT_DBLP,        ///< double, planar
AV_SAMPLE_FMT_S64,         ///< signed 64 bits
AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar

AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
}*/
/*  verbatim
  ---------------------- - sample is signed if set
||
||   ---------- - sample is bigendian if set
|| ||
|| ||   -- - sample is float if set
|| || ||
|| || ||  -- - sample bit size-- -  
|| || || | |
15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
endverbatim
*
*  There are macros in SDL 2.0 and later to query these bits.
*/
SDL_AudioFormat GetFmt(AVSampleFormat avfmt)
{
	SDL_AudioFormat res = 0;
	bool isp = false;
	bool iss = false;
	bool isf = false;
	bool isd = false;
	int bits = 0;
	switch (avfmt)
	{
	case AV_SAMPLE_FMT_NONE://// = -1,
		break;
	case	AV_SAMPLE_FMT_U8:         ///< unsigned 8 bits
		bits = 8;
		break;
	case	AV_SAMPLE_FMT_S16:         ///< signed 16 bits
		bits = 16;
		iss = true;
		break;
	case AV_SAMPLE_FMT_S32:         ///< signed 32 bits
		bits = 32;
		iss = true;
		break;
	case AV_SAMPLE_FMT_FLT:         ///< float
		isf = true;
		break;
	case	AV_SAMPLE_FMT_DBL:         ///< double
		isd = true;
		break;
	case	AV_SAMPLE_FMT_U8P:         ///< unsigned 8 bits, planar
		bits = 8;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_S16P:        ///< signed 16 bits, planar
		bits = 16;
		iss = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_S32P:        ///< signed 32 bits, planar
		bits = 32;
		iss = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_FLTP:        ///< float, planar
		isf = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_DBLP:       ///< double, planar
		isd = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_S64:        ///< signed 64 bits
		bits = 64;
		iss = true;
		break;
	case	AV_SAMPLE_FMT_S64P:       ///< signed 64 bits, planar
		bits = 64;
		iss = true;
		isp = true;
		break;
	case	AV_SAMPLE_FMT_NB:           ///< Number of sample formats. DO NOT USE if linking dynamically
		break;
	}
	if (iss)
		res |= 1 << 15;
	if (isf)
		res |= 1 << 8;
	bits &= 255;
	res |= bits;

	return res;
}

例子代码中为了简便,SDL播放音频的format就用AUDIO_S16SYS,对应FFmpeg就是AV_SAMPLE_FMT_S16,都标识signed 16bit。注意SDL中音频数据没有planner。

代码下载

0 人点赞