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

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;
}
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的函数

/*{
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 条评论
登录 后参与评论

相关文章

来自专栏算法+

3D Lut 电影级调色算法 附完整C代码

长话短说,3d lut(全称 : 3D Lookup table )它是通过建立一个颜色映射表,对图像的色调进行重调的算法。

90310
来自专栏数据之美

Hive & Performance 学习笔记

注:本文来源于 Hortonworks 的 Adam Muise 在 July 23 2013 日的 Toronto Hadoop User Group 大会...

3705
来自专栏码匠的流水账

聊聊rocketmq的RequestTask

org/apache/rocketmq/remoting/netty/RequestTask.java

1562
来自专栏静默虚空的博客

二叉树

二叉树基本操作代码 #include "stdafx.h" #include "stdlib.h" #include "string.h" #define M...

1977
来自专栏PPV课数据科学社区

【学习】七天搞定SAS(三):基本模块调用

搞定基本的函数之后,开始鼓捣SAS里面的模型。也就是说,要开始写PROC了。说实话,越学SAS,越觉得SAS像Stata...无论是从输出的样式,还是语法。好不...

3135
来自专栏一个会写诗的程序员的博客

java.sql.BatchUpdateException: Can not issue SELECT via executeUpdate() or executeLargeUpdate().

java.sql.BatchUpdateException: Can not issue SELECT via executeUpdate() or execu...

983
来自专栏杨建荣的学习笔记

通过java来格式化sql语句(r4笔记第61天)

经常在抓取一些sql语句的时候,得到的sql文本有格式的问题,如果尝试得到执行计划,每次都会费一番周折。 比如下面的sql语句,基本包含了常见的格式问题。第3行...

3204
来自专栏叁金大数据

EmguCV学习——视频与图片互转

其实视频转图片在上篇文章中已经有些眉目了,其实就是按帧读取视频,然后把帧保存就ok。然后自己再加个进度条美化一下。。。这代码简单易懂,还是直接上代码吧。

1791
来自专栏听雨堂

想修改CSS

      下载了一个“通用”的CSS文件,本来想偷懒的,结果发现有问题,就是它用的颜色是变量定义的,无法识别。我又找不到在哪里可以定义。 BODY{     ...

24310
来自专栏james大数据架构

CSS好看的按钮

好看的按钮 <style> .btn { BORDER-RIGHT: #7b9ebd 1px solid; PADDING-RIGHT: 2px; BORDE...

2117

扫码关注云+社区