SDL2 播放视频文件(MP4)

1.简介

这里引入FFmpeg库,获取视频流数据,然后通过FFmpeg将视频流解码成YUV原始数据,再将YUV数据送入到SDL库中实现视频播放。

2.FFmpeg的操作流程

  • 注册API:av_register_all()
  • 构建输入AVFormatContext上下文:avformat_open_input()
  • 查找音视频流信息:avformat_find_stream_info()
  • 查找解码器:avcodec_find_decoder()
  • 打开解码器:avcodec_open2()
  • 然后通过while循环,不停的读取数据:av_read_frame()
  • 帧解码:avcodec_send_packet()和avcodec_receive_frame()

3.SDL显示视频流程

  • 初始化SDL:SDL_Init();
  • 创建窗口:SDL_CreateWindow();
  • 创建渲染器:SDL_CreateRenderer();
  • 创建纹理:SDL_CreateTexture();
  • 设置纹理数据:SDL_UpdateTexture();
  • 纹理复制给渲染目标:使用SDL_RenderCopy()将纹理数据复制给渲染目标。在使用SDL_RenderCopy()之前,可以使用SDL_RenderClear()先使用清空渲染目标。
  • 显示界面:SDL_RenderPresent();

4.示例

#include <stdio.h>
#include <SDL.h>

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
};

#define __STDC_CONSTANT_MACROS
//Refresh Event
#define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)
#define SFM_BREAK_EVENT  (SDL_USEREVENT + 2)

int thread_exit = 0;
int thread_pause = 0;

int sfp_refresh_thread(void *opaque) 
{
	while (!thread_exit) 
	{
		if (!thread_pause) 
		{
			SDL_Event event;
			event.type = SFM_REFRESH_EVENT;
			SDL_PushEvent(&event);
		}
		SDL_Delay(40);
	}

	//Break
	SDL_Event event;
	event.type = SFM_BREAK_EVENT;
	SDL_PushEvent(&event);

	return 0;
}

AVFrame *recv(AVCodecContext *codecCtx)
{
	if (!codecCtx)
	{
		return NULL;
	}

	AVFrame *frame = av_frame_alloc();
	int ret = avcodec_receive_frame(codecCtx, frame);

	if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
	{
		av_frame_free(&frame);
		return NULL;
	}
	else if (ret < 0)
	{
		av_frame_free(&frame);
		return NULL;
	}

	return frame;
}


#undef main
int main(int argc, char* argv[])
{
	///ffmpeg
	avformat_network_init();

	AVFormatContext* pFormatCtx = NULL;
	const char* inputUrl = "./3.mp4";

	///打开输入的流
	int ret = avformat_open_input(&pFormatCtx, inputUrl, NULL, NULL);
	if (ret != 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;
	}

	//找到视频流索引
	int video_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	AVStream* st = pFormatCtx->streams[video_index];
	AVCodec* codec = nullptr;
	//找到解码器
	codec = avcodec_find_decoder(st->codecpar->codec_id);
	if (!codec)
	{
		fprintf(stderr, "Codec not found\n");
		return -1;
	}

	//申请AVCodecContext
	AVCodecContext* pCodecCtx = avcodec_alloc_context3(codec);
	if (!pCodecCtx)
	{
		return -1;
	}

	avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[video_index]->codecpar);

	//打开解码器
	if ((ret = avcodec_open2(pCodecCtx, codec, NULL) < 0))
	{
		return -1;
	}

	AVFrame *pFrameYUV = av_frame_alloc();
	AVPacket* pkt = av_packet_alloc();

	unsigned char *out_buffer = (unsigned char *)av_malloc(av_image_get_buffer_size(
		AV_PIX_FMT_YUV420P, 
		pCodecCtx->width, 
		pCodecCtx->height,
		1)
	);

	av_image_fill_arrays(pFrameYUV->data, 
		pFrameYUV->linesize, 
		out_buffer,
		AV_PIX_FMT_YUV420P,
		pCodecCtx->width, 
		pCodecCtx->height,
		1
	);

	//------------SDL----------------
	int screen_w, screen_h;
	SDL_Rect sdlRect;
	
	struct SwsContext *img_convert_ctx;
	char filepath[] = "Titanic.ts";


	//Output Info-----------------------------
	printf("---------------- File Information ---------------\n");
	av_dump_format(pFormatCtx, 0, filepath, 0);
	printf("-------------------------------------------------\n");

	img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
		pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);


	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		printf("Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}

	//SDL 2.0 Support for multiple windows
	screen_w = pCodecCtx->width;
	screen_h = pCodecCtx->height;
	SDL_Window *screen = SDL_CreateWindow("SDL Play Video", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		screen_w, screen_h, SDL_WINDOW_OPENGL);

	if (!screen) 
	{
		printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
		return -1;
	}

	SDL_Renderer *sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
	//IYUV: Y + U + V  (3 planes)
	//YV12: Y + V + U  (3 planes)
	SDL_Texture *sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
	sdlRect.x = 0;
	sdlRect.y = 0;
	sdlRect.w = screen_w;
	sdlRect.h = screen_h;
	SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
	//------------SDL End------------
	//Event Loop
	SDL_Event event;
	for (;;) 
	{
		//Wait
		SDL_WaitEvent(&event);
		if (event.type == SFM_REFRESH_EVENT) 
		{
			//不断的读取一帧数据
			while (1) 
			{
				if (av_read_frame(pFormatCtx, pkt) < 0)
					thread_exit = 1;

				if (pkt->stream_index == video_index)
					break;
			}

			//一次send 多次recv
			int ret = avcodec_send_packet(pCodecCtx, pkt);
			if (ret < 0)
				continue;

			//释放资源
			av_packet_unref(pkt);

			while (1)
			{
				AVFrame *pFrame = recv(pCodecCtx);
				if (!pFrame)
					break;

				sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0,
								pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
				//SDL---------------------------
				SDL_UpdateTexture(sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0]);
				SDL_RenderClear(sdlRenderer);
				//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );  
				SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
				SDL_RenderPresent(sdlRenderer);
				//SDL End-----------------------

				av_frame_free(&pFrame);
			}
		}
		else if (event.type == SDL_KEYDOWN) 
		{
			//空格键暂停播放
			if (event.key.keysym.sym == SDLK_SPACE)
				thread_pause = !thread_pause;
		}
		else if (event.type == SDL_QUIT) 
		{
			thread_exit = 1;
		}
		else if (event.type == SFM_BREAK_EVENT) 
		{
			break;
		}
	}

	SDL_Quit();
	//--------------
	sws_freeContext(img_convert_ctx);
	avcodec_close(pCodecCtx);
	avcodec_free_context(&pCodecCtx);
	avformat_close_input(&pFormatCtx);
	av_frame_free(&pFrameYUV);
	av_packet_free(&pkt);

	return 0;
}

5.相关推荐

[总结]FFMPEG视音频编解码零基础学习方法_零基础ffmpeg 雷霄骅-CSDN博客 

SDL2 播放视频数据(YUV420P)-CSDN博客

SDL2 消息循环和事件响应-CSDN博客

FFmpeg 视频解码(秒懂)-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/146606.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vscode+python开发之虚拟环境和解释器切换

需求情景&#xff1a; 现在我们要开发多个项目比如&#xff1a;项目A&#xff0c;项目B、项目C&#xff0c;他们每个项目需要依赖不同的库。每个项目依赖的解释器也不一样怎么办&#xff1f; 项目A&#xff1a;需要在python3.7环境运行 依赖aadd3.2库 项目B、需要在python3.11…

取消Element UI响应式设计——打造固定布局的菜单

引言 在当今的Web开发中&#xff0c;响应式设计已经成为了一个不可或缺的部分。然而&#xff0c;有时候我们可能需要取消这种响应式特性&#xff0c;尤其是对于一些特定的界面元素&#xff0c;如导航菜单。在Element UI框架中&#xff0c;导航菜单&#xff08;el-menu&#xff…

arcgis--填充面域空洞

方法一&#xff1a;使用【编辑器】-【合并工具】进行填充。首选需要在相同图层中构造一个填充空洞的面域&#xff0c;然后利用【合并】工具进行最后填充。 打开一幅含有空洞的矢量数据&#xff0c;如下&#xff1a; 打开【开始编辑】-【构造工具】-【面】进行覆盖空洞的面域的…

基于鸟群算法优化概率神经网络PNN的分类预测 - 附代码

基于鸟群算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于鸟群算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于鸟群优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

机器视觉系统中工业光源选型避坑指南

光源的作用&#xff1a; 照亮目标&#xff0c;提高目标亮度 形成有利于图像处理的效果&#xff0c;提升对比度 克服环境光干扰&#xff0c;保证图像的稳定性 光源的选型思路&#xff1a; ①颜色 ②外形  ③打光方式  ④亮度 选颜色 通过选择合适颜色的光源&#xff0c;…

腾讯云CVM服务器5年可选2核4G和4核8G配置

腾讯云服务器网整理五年云服务器优惠活动 txyfwq.com/go/txy 配置可选2核4G和4核8G&#xff0c;公网带宽可选1M、3M或5M&#xff0c;系统盘为50G高性能云硬盘&#xff0c;标准型S5实例CPU采用主频2.5GHz的Intel Xeon Cascade Lake或者Intel Xeon Cooper Lake处理器&#xff0c;…

Android JNI静态和动态注入方法

作者&#xff1a;MiniCode Android调用C/C的代码目前比较流行的方式之一便是通过JNI&#xff0c;其中按本地方法的实现有两种方式&#xff1a;静态和动态 创建一个C项目或者C的Module&#xff1a; 创建成功之后会生成如下文件&#xff08;CMakeLists.txt、nativelib.cpp&#…

Centos7下mbr主引导记录演示

linux mbr主引导记录演示 dd if/dev/sda ofmbr.bin bs446 count1 dd if/dev/sda ofmbr.bin bs446 count1hexdump -C mbr.bin[rootlocalhost ~]# cd /boot/grub2 [rootlocalhost grub2]# ls [rootlocalhost grub2]# grub2-editenv list #默认引导内核查看 [rootlocalhost g…

生产环境中的面试问题,实时链路中的Kafka数据发现某字段值错误,怎么办?...

大家好呀&#xff0c;今天分享的是一个生产环境中遇到的问题。也是群友遇到的一个面试问题。 原问题是&#xff1a; 早晨8点之后发现kafka的record中某个字段的值出现了错误&#xff0c;现在已经10点了&#xff0c;需要对kafka进行数据订正&#xff0c;怎么样定位和解决这个问题…

降低城市内涝风险,万宾科技内涝积水监测仪的作用

频繁的内涝会削弱和损坏城市的关键基础设施&#xff0c;包括道路、桥梁和公用设施。城市内涝风险降低可以减少交通中断事件&#xff0c;也可以保护居民安全并降低路面维修等成本&#xff0c;进一步确保城市基本服务继续发挥作用。对城市可持续发展来讲有效减少内涝的风险是重要…

根据数组数组,实现上一页下一页功能

<span click"prePage"><i class"el-icon-back"></i></span><span click"nextPage"><i class"el-icon-right"></i></span> this.typeList&#xff1a;最终显示页面的数组 this.typeNe…

C#中.NET Framework4.8 Windows窗体应用通过EF访问数据库并对数据库追加、删除记录

目录 一、应用程序设计 二、应用程序源码 三、生成效果 前文作者发布了在.NET Framework4.8 控制台应用中通过EF访问已有数据库&#xff0c;事实上在.NET Framework4.8 Windows窗体应用中通过EF访问已有数据库也是一样的。操作方法基本一样&#xff0c;数据库EF模型和上下文…

MySQL时间戳2038年灾难:你的数据还能撑过去吗?

点击上方蓝字关注我 Timestamp 类型在MySQL中通常用于存储日期和时间。然而&#xff0c;Timestamp类型的一个限制是其存储范围&#xff0c;它使用4字节&#xff08;32位&#xff09;整数来表示秒数&#xff0c;从而导致在2038年01月19日03:14:07之后无法正确存储时间戳。这是因…

Android设计模式--工厂模式

一&#xff0c;定义 工厂模式与Android 设计模式--单例模式-CSDN博客&#xff0c;Android设计模式--Builder建造者模式-CSDN博客&#xff0c;Android设计模式--原型模式-CSDN博客 一样&#xff0c;都是创建型设计模式。 工厂模式就是定义一个用于创建对象的接口&#xff0c;让…

[当人工智能遇上安全] 10.威胁情报实体识别 (1)基于BiLSTM-CRF的实体识别万字详解

您或许知道&#xff0c;作者后续分享网络安全的文章会越来越少。但如果您想学习人工智能和安全结合的应用&#xff0c;您就有福利了&#xff0c;作者将重新打造一个《当人工智能遇上安全》系列博客&#xff0c;详细介绍人工智能与安全相关的论文、实践&#xff0c;并分享各种案…

又卷又累,救救一个将被随机拖死的程序员!

前两天在小红书上看到有人吐槽&#xff1a;“国内做程序员性价比不高&#xff0c;又卷又累&#xff0c;个人时间都被拖死了。” 现在普遍来讲&#xff0c;“卷”都是打工人的现状。 而至于国内程序员性价比不高的话&#xff0c;确实是肉少僧多。工作强度一加持&#xff0c;累自…

Java主流分布式解决方案多场景设计与实战

Java的主流分布式解决方案的设计和实战涉及到多个场景&#xff0c;包括但不限于以下几点&#xff1a; 分布式缓存&#xff1a;在Java的分布式系统中&#xff0c;缓存是非常重要的一部分。常用的分布式缓存技术包括Redis、EhCache等。这些缓存技术可以用来提高系统的性能和响应…

数据分析法宝,一个 SQL 语句查询多个异构数据源

随着企业数据量呈现出爆炸式增长&#xff0c;跨部门、跨应用、跨平台的数据交互需求越来越频繁&#xff0c;传统的数据查询方式已经难以满足这些需求。同时&#xff0c;不同数据库系统之间的数据格式、查询语言等都存在差异&#xff0c;直接进行跨库查询十分困难。 原生跨库查…

批量重命名软件推荐 A Better Finder Rename 12最新 for mac

A Better Finder Rename的大量重命名选项被组织成15个直观的类别&#xff0c;涵盖了一个伟大的文件重命名器所期望的所有文本&#xff0c;字符&#xff0c;位置&#xff0c;转换和截断功能。 除此之外&#xff0c;A Better Finder Rename提供了更多高级功能&#xff0c;可以满…

spring cloud微服务中多线程下,子线程通过feign调用其它服务,请求头token等丢失

在线程池中&#xff0c;子线程调用其他服务&#xff0c;请求头丢失&#xff0c;token为空的情况 看了很多篇文章的处理方法和在自己亲测的情况下做出说明&#xff1a; 第一种&#xff1a; 这种方式只支持在主线程情况下&#xff0c;能够处理&#xff0c;在多线程情况下&#…
最新文章