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

相关文章

来自专栏happyJared

爬虫进阶:Scrapy抓取科技平台Zealer

  这次的目标网站也是本人一直以来有在关注的科技平台:Zealer,爬取的信息包括全部的科技资讯以及相应的评论。默认配置下运行,大概跑了半个多小时,最终抓取了5...

442
来自专栏大数据架构

Spark SQL / Catalyst 内部原理 与 RBO

从上图可见,无论是直接使用 SQL 语句还是使用 DataFrame,都会经过如下步骤转换成 DAG 对 RDD 的操作

986
来自专栏https://www.cnblogs.com/L

【Spark篇】---SparkSQL初始和创建DataFrame的几种方式

          Hive是Shark的前身,Shark是SparkSQL的前身,SparkSQL产生的根本原因是其完全脱离了Hive的限制。

501
来自专栏大内老A

SQLXML初体验:用XML代替T-SQL来操作数据库

随着Internet的飞速发展,W3C成员意识到必须找到一种办法将数据和Web的表现方式分离出来,于是XML诞生了。当今的XML已经成为IT领域各个数据(特别是...

3206
来自专栏沃趣科技

会话和锁信息查询视图 | 全方位认识 sys 系统库

在上一篇《等待事件统计视图 | 全方位认识 sys 系统库》中,我们介绍了sys 系统库中的等待事件统计视图,本期的内容先给大家介绍会话信息和锁等待信息查询视图...

690
来自专栏乐沙弥的世界

收缩表段(shrink space)

--==================== -- 收缩表段(shrink space) --==================== 一、表的增长方式  ...

601
来自专栏c#开发者

ado.net data services开发框架学习

ado.net data services开发框架学习 什么是ado.net data services Ado.net 数据服务可以很方便的将企业内部数据发...

2546
来自专栏乐沙弥的世界

检查及设置合理的undo表空间

      UNDO是用于实现并发控制以及构建一致性读,也就是在数据变更之前产生前镜像,以保证用户能够回滚或撤销对数据库所作的修改。是Oracle数据库完整性的...

612
来自专栏Vamei实验室

安卓第七夜 雅典学院

我之前只使用了一种持续保存数据的方法,即SharedPreferences。然而,SharedPreferences只能存储少量松散的数据,并不适合大量数据的存...

1768
来自专栏函数式编程语言及工具

SDP(11):MongoDB-Engine功能实现

  根据上篇关于MongoDB-Engine的功能设计方案,我们将在这篇讨论里进行功能实现和测试。下面是具体的功能实现代码:基本上是直接调用Mongo-scal...

2783

扫描关注云+社区