【项目教程】FFmpeg+SDL2实现视频播放器

一、前言

学习ffmpeg和sdl,并编写一个视频播放器,是一个很好的音视频开发项目。

虽然关于视频播放器的原理已经有很多人在博客中进行了讲解,但是很多人不提供视频和代码,这也是我写这篇博客的主要原因。

二、在视频播放器中,主要涉及以下几个基本原理:


2.1 视频文件解封装

视频文件通常将音频和视频数据进行封装,因此在处理视频文件时,首先需要进行解封装操作,将视频流和音频流的压缩编码数据分离。常见的封装格式有MP4、MKV、FLV、AVI、RMVB、TS等。例如,解封装FLV格式的文件可能会得到H.264编码的视频流和AAC编码的音频流。

在FFMPEG中,解封装的过程如下所示:

这一步最重要的是得到解封装器的上下文结构体"AVFormatContext *m_pFormatCtx", 以及接下来我们要解码的音视频流索引。

2.2 音视频解码

原始数据通常经过压缩编码,解码过程则是将H.264、AAC等压缩后的数据解码为非压缩的音频/视频原始数据,其中视频一般为YUV或RGB数据,音频一般为PCM采样数据。

解码的步骤如下:

2.3 使用SDL2播放视频数据

我们知道视频是由连续的一帧帧图像快速播放形成的动态效果,一般设置为每秒播放25帧图像。

在播放视频时,我们使用SDL2库。每个图像在SDL2中被表示为一个纹理,而纹理与SDL2的渲染器相关联。

在视频解码后,我们可以从avcodec_receive_frame函数中获取到一个AVFrame对象,该对象包含了一帧视频数据。我们的目标是将这一帧的数据渲染到SDL的渲染器中。总体流程如下所示:

首先,我们需要使用sws_scale函数对获取到的AVFrame数据进行大小和格式的转换。接下来,我们需要更新SDL2中的纹理(Texture)和渲染器(Render)。以下是相关的关键代码示例:

AVFrame *frame = m_videoFrameQueue.front();
m_videoFrameQueue.pop();AVFrame *frameYUV = av_frame_alloc();
int ret = av_image_alloc(frameYUV->data, frameYUV->linesize, m_sdlRect.w, m_sdlRect.h, AV_PIX_FMT_YUV420P, 1);
//Convert image
if (m_imgConvertCtx)
{
sws_scale(m_imgConvertCtx, frame->data, frame->linesize, 0, m_videoCodecParams.height, frameYUV->data, frameYUV->linesize);
SDL_UpdateYUVTexture(m_sdlTexture, NULL, frameYUV->data[0], frameYUV->linesize[0], frameYUV->data[1], frameYUV->linesize[1], frameYUV->data[2], frameYUV->linesize[2]);
SDL_RenderClear(m_sdlRender);
SDL_RenderCopy(m_sdlRender, m_sdlTexture, NULL, &m_sdlRect);// Present picture
SDL_RenderPresent(m_sdlRender);
}

2.4 使用SDL2播放音频数据

对于音频数据,在使用avcodec_receive_frame函数接收到AVFrame后,我们得到的是音频的PCM数据。

与视频数据不同,音频数据并不是以帧为单位表示的,它可能包含多个采样数据(samples)。 为了播放音频,我们同样需要对音频数据进行格式转换,以适应音频设备的播放要求。音频格式转换主要通过swr_convert函数来完成。转换后的音频数据可以存放在一个公共缓冲区中。

使用SDL_OpenAudio函数进行音频播放,该函数需要传入一个SDL_AudioSpec结构体来设置播放参数。其中需要设置一个回调函数(callback),用于在音频设备需要获取数据时执行。因此,我们需要在此回调函数中向音频设备提供数据,实现数据的"喂养":

SDL_AudioSpec m_sdlAudioSpec;
auto audioCtx = m_audioDecoder.GetCodecContext();m_sdlAudioSpec.freq = audioCtx->sample_rate; //根据你录制的PCM采样率决定
m_sdlAudioSpec.format = AUDIO_S16SYS;
m_sdlAudioSpec.channels = audioCtx->channels;
m_sdlAudioSpec.silence = 0;
m_sdlAudioSpec.samples = SDL_AUDIO_BUFFER_SIZE;
m_sdlAudioSpec.callback = &SDLVideoPlayer::ReadAudioData;
m_sdlAudioSpec.userdata = NULL;int re = SDL_OpenAudio(&m_sdlAudioSpec, NULL);
if (re < 0)
{std::cout << "can't open audio: " << GetErrorInfo(re);
}
else
{//Start play audioSDL_PauseAudio(0);
}void SDLVideoPlayer::ReadAudioData(void *udata, Uint8 *stream, int len) {SDL_memset(stream, 0, len);//需要向stream中填充len长度的音频数据...SDL_MixAudio(stream, m_audioPcmDataBuf, len, g_volum);
}

2.5 音视频同步的设计

为了实现音视频同步,我们使用了两个线程分别播放音频和视频。音频可以直接通过设置回调函数来传递数据,而视频则需要我们自己控制播放速度,这涉及到统一两者播放速度的问题。 音视频同步的基本方式是确定一个主时钟作为同步基准。在播放过程中,我们不断检查当前流的播放时间与主时钟的差异,以调节自身的播放速度。根据不同类型的主时钟,可以分为以下几种方式:

  1. 音频同步到视频:使用视频时钟作为主时钟。
  2. 视频同步到音频:使用音频时钟作为主时钟。
  3. 音视频都同步到外部时钟。 由于音频播放通常会将大量数据发送到设备缓存中,并且音频对人的敏感度更高,因此以音频时钟作为主时钟是比较合理且简单的方法。具体实现如下:
  4. 在每次传递音频数据时,记录送入数据的起始pts时间戳,表示当前音频的播放进度。
  5. 每次刷新视频帧时,记录当前图片帧的pts时间戳。
  6. 在记录当前音频pts的同时,根据记录的图片pts,计算两者之间的延迟。
  7. 在刷新视频帧时,根据延迟值判断,如果当前视频比音频快,那么调整视频等待时间为正常两帧之间的间隔加上音视频之间的延迟,并将延迟值置为0;如果音频比视频快,那么直接丢弃当前的视频帧,直到音频和视频时间一致。

2.6 快进和快退

快进和快退,以及通过拖动进度条来实现播放跳转,其实现思路都是一样的,即通过使用av_seek_frame函数来实现:

 av_seek_frame(m_pFormatCtx, -1, pts * AV_TIME_BASE, AVSEEK_FLAG_BACKWARD);

因此关键就是获取要跳转的时间戳,这个在做音视频同步处理后,这个时间戳就很容易拿到。

2.7 SDL事件处理

对于窗口大小更改、暂停、快进快退等操作,都需要与用户进行交互,而这可以通过SDL的事件机制来实现。 监听事件:

SDL_Event event;
SDL_WaitEvent(&event);
if (event.type == SDL_WINDOWEVENT) {...
}
...

除了预定义的事件,比如窗口事件、鼠标事件、按键事件等,你也可以自己触发或定义新的事件:

SDL_Event event;
event.type = SFM_REFRESH_PIC_EVENT;
SDL_PushEvent(&event);

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【组合数学 放球问题 虚拟点 小于等于转小于】1621. 大小为 K 的不重叠线段的数目

本文涉及知识点 放球问题 组合数学汇总 本题难道分&#xff1a;2198 LeetCode1621. 大小为 K 的不重叠线段的数目 给你一维空间的 n 个点&#xff0c;其中第 i 个点&#xff08;编号从 0 到 n-1&#xff09;位于 x i 处&#xff0c;请你找到 恰好 k 个不重叠 线段且每个线段…

零拷贝(Zero Copy)

目录 零拷贝&#xff08;Zero Copy&#xff09; 1.什么是Zero Copy? 2.物理内存和虚拟内存 3.内核空间和用户空间 4.Linux的I/O读写方式 4.1 I/O中断原理 4.2 DMA传输原理 5.传统I/O方式 5.1传统读操作 5.2传统写操作 6.零拷贝 6.1.用户态直接IO 6.2.mmapwrite …

免费使用知网下载文献

第一步&#xff1a;输入网址&#xff1a;https://digi.library.hb.cn:8443/#/&#xff08;或搜索湖北省图书馆&#xff09; 第二步&#xff1a;点击登录按钮。 第三步&#xff1a;使用手机 支付宝 扫描页面左侧二维码。 第四步&#xff1a;手机点击“电子读者证注册”。&…

抖音 v27.8.0 内置增强模块,自动播放、无水印下载(可登录,助手增强版)

介绍 抖音应用作为全球领先的短视频平台&#xff0c;其内置功能允许用户将喜欢的内容保存至本地设备&#xff0c;但默认情况下&#xff0c;这些视频会带有抖音的水印。为了解决这一限制&#xff0c;该版本使用户能够直接保存不带水印的视频到手机中&#xff0c;无需使用任何第…

R25 型双极型晶体管 433功率放大器,集电极电流可达100mA

R25 型硅基微波双极型晶体管是一种常见的晶体管&#xff0c;主要用于高频电子放大线路中。常被用作放大器、开关、变频器等电子电路中的核心元件。在放大电路中&#xff0c;它可以将微弱的信号放大到足以驱动输出负载&#xff1b;在开关电路中&#xff0c;它可以实现电路的打开…

易查分小程序 学生成绩管理小程序

亲爱的老师们&#xff0c;是不是每次成绩公布后&#xff0c;家长们的连环夺命call让你头大&#xff1f;担心孩子们的成绩信息安全&#xff0c;又想快速分享给家长&#xff0c;这可咋整&#xff1f;别急&#xff0c;易查分小程序来帮忙啦&#xff01; 安全有保障 智能验证码&a…

基于tcp实现自定义应用层协议

认识协议 协议&#xff08;Protocol&#xff09; 是一种通信规则或标准&#xff0c;用于定义通信双方或多方之间如何交互和传输数据。在计算机网络和通信系统中&#xff0c;协议规定了通信实体之间信息交换的格式、顺序、定时以及有关同步等事宜的约定。简易来说协议就是通信…

【Linux】Linux下centos更换国内yum源

&#x1f331;博客主页&#xff1a;青竹雾色间 &#x1f331;系列专栏&#xff1a;Linux &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 目录 1. 备份旧的 YUM 源文件2. 下载国内的 YUM 源文件阿里云&#xff1a;网易&#xff1a; 3. 清理 YUM 缓存4. 更新…

scp问题:Permission denied, please try again.

我把scp归纳三种情况&#xff1a; 源端root——》目标端root 源端root——》目标端mysql&#xff08;任意&#xff09;用户 源端&#xff08;任意用户&#xff09;——》目标端root用户 在scp传输文件的时候需要指导目标端的用户密码&#xff0c;如root用户密码、mysql用户…

选择海外代理IP需要注意什么?

跨境电商近年来的兴起与发展&#xff0c;越来越多的跨境从业者从事该行业&#xff0c;但在从事跨境贸易中则需要海外IP代理来突破地域限制、提升访问速度和稳定性、防止账号关联以及保护隐私和安全。这些功能都有助于跨境电商企业在全球范围内拓展业务&#xff0c;提升竞争力&a…

一文搞定jdk8升级到jdk11

一、背景 为什么要升级JDK11 性能 JDK11的G1的GC性能高很多&#xff0c;对比JDK8无论是性能还是内存占比都有很大的提升&#xff0c;业内各项数据指标也都表明JDK11的G1在应对突发流量的下的效果惊人&#xff1b; 版本兼容 Spring Boot 2.7.x及以后的版本将不再支持Java 8作为…

【IC】partial good

假设单core良率80%&#xff0c;core pass 数量分布呈二项分布。 16个core全pass的概率为&#xff1a; 有n个core pass的概率为&#xff1a; 分布如下&#xff1a; 当np>5且nq>5时&#xff0c;二项分布近似服从正态分布

python fstring教程(f-string教程)(python3.6+格式化字符串方法)

文章目录 Python F-String 教程&#xff1a;深度探究与实用指南引言基础用法什么是F-String?表达式嵌入 格式化选项小数点精度宽度与对齐数字格式化 高级用法复杂表达式调用函数多行F-String嵌套格式化 总结 Python F-String 教程&#xff1a;深度探究与实用指南 引言 在Pyt…

中间件是什么?信创中间件有哪些牌子?哪家好用?

当今社会&#xff0c;中间件的重要性日益凸显&#xff0c;尤其是在信创背景下&#xff0c;选择适合的中间件产品对于推动企业数字化转型和升级具有重要意义。今天我们就来聊聊中间件是什么&#xff1f;信创中间件有哪些牌子&#xff1f;哪家好用&#xff1f;仅供参考哈&#xf…

山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(二十二)- 微服务(2)

目录 4. Ribbon负载均衡 4.1 负载均衡流程 4.2 负载均衡策略 4.3 Ribbon饥饿加载 5. Nacos注册中心 5.1 服务注册到nacos 5.2 nacos服务分级存储模型 5.3 根据权重负载均衡 5.4 环境隔离--namespace 4. Ribbon负载均衡 4.1 负载均衡流程 4.2 负载均衡策略 默认实现是…

cesium绘制区域编辑

npm 安装也是可以的 #默认安装最新的 yarn add cesium#卸载插件 yarn remove cesium#安装指定版本的 yarn add cesium1.96.0#安装指定版本到测试环境 yarn add cesium1.96.0 -D yarn install turf/turf <template><div id"cesiumContainer"></div&…

大学理科生搜题软件?分享四个软件和公众号,来对比看看吧 #笔记#知识分享

在快节奏的大学生活中&#xff0c;合理利用这些日常学习工具&#xff0c;能够让你事半功倍&#xff0c;提高学习效率。 1.福昕翻译 可以一键翻译文档内容&#xff0c;并提供还原排版的译文&#xff0c;对经常看外文文献的朋友来说&#xff0c;绝对是福音 福昕翻译是一流专业…

设计模式基础——设计原则介绍

1.概述 ​ 对于面向对象软件系统的设计而言&#xff0c;如何同时提高一个软件系统的可维护性、可复用性、可拓展性是面向对象设计需要解决的核心问题之一。面向对象设计原则应运而生&#xff0c;这些原则你会在设计模式中找到它们的影子&#xff0c;也是设计模式的基础。往往判…

HTML大雪纷飞

目录 写在前面 HTML简介 完整代码 代码分析 运行结果 系列文章 写在后面 写在前面 小编又又又出现啦&#xff01;这次小编给大家带来大雪纷飞HTML版&#xff0c;不需要任何的环境&#xff0c;只要有一个浏览器&#xff0c;就可以随时随地下一场大雪哦&#xff01; HTM…

Diffusion Model, Stable Diffusion, Stable Diffusion XL 详解

文章目录 Diffusion Model生成模型DDPM概述向前扩散过程前向扩散的逐步过程前向扩散的整体过程 反向去噪过程网络结构训练和推理过程训练过程推理过程优化目标 详细数学推导数学基础向前扩散过程反向去噪过程 Stable Diffusion组成结构运行流程网络结构变分自编码器 (VAE)文本编…