基于 FFMPEG 的跨平台视频播放器简明教程(二):基础知识和解封装(demux)

news/2024/7/10 19:26:29 标签: ffmpeg, 音视频, 人工智能

系列文章目录

  1. 基于 FFMPEG 的跨平台视频播放器简明教程(一):FFMPEG + Conan 环境集成

文章目录

  • 系列文章目录
  • 前言
  • 基础知识
    • 视频,你所看到的!
    • 音频 - 你所听到的声音
    • 编解码器 - 压缩数据
    • 容器 - 存放音频和视频的地方
  • 解封装
  • 使用 ffmpeg api 进行解封装
  • 总结
  • 参考


前言

前面一章中我们介绍了如何使用 conan 和 cmake 搭建 ffmpeg 运行环境,你做的还顺利吗?如果遇到任何问题,请在进行评论,我看到都会回复的。

从本章开始,将正式开始我们的 ffmpeg 播放器学习之旅。接下去的任务是:使用 ffmpeg 解码视频,并将解码后的视频帧保存在本地(就像对视频截图一样)。其中涉及到两个重要的知识点:解封装和视频解码。今天我们先聊解封装。此外,还会扩展 ffmpeg api 以及编解码相关的知识。

本文参考文章来自 An ffmpeg and SDL Tutorial - Tutorial 01: Making Screencaps。这个系列对新手较为友好,但 2015 后就不再更新了,以至于文章中的 ffmpeg api 已经被弃用了。幸运的是,有人对该教程的代码进行重写,使用了较新的 api,你可以在 rambodrahmani/ffmpeg-video-player 找到这些代码。

本文解封装的代码在 ffmpeg_video_player_tutorial-tutorial01。


基础知识

本章节翻译自 ffmpeg-libav-tutorial Intro 部分。

视频,你所看到的!

视频频是由一系列图像组成的,这些图像以一定的频率改变(比如说每秒24帧),从而产生运动的错觉。简而言之,这就是视频的基本原理:以一定的速率运行的一系列图片/帧。
在这里插入图片描述

音频 - 你所听到的声音

尽管无声视频可以表达各种感情,但添加声音会给体验带来更多的乐趣。声音是作为压力波传播的振动,通过空气或任何其他传输介质(如气体、液体或固体)。在数字音频系统中,麦克风将声音转换为模拟电信号,然后模拟-数字转换器(ADC)(通常使用脉冲编码调制(PCM))将模拟信号转换为数字信号。
在这里插入图片描述

编解码器 - 压缩数据

编解码器(CODEC)是一种电子电路或软件,用于压缩或解压缩数字音频/视频。它将原始(未压缩)的数字音频/视频转换为压缩格式,反之亦然。https://en.wikipedia.org/wiki/Video_codec

但是,如果我们选择将数百万个图像打包到一个文件中,并称之为电影,可能会得到一个巨大的文件。让我们做个计算:

假设我们正在创建一个分辨率为1080 x 1920(高 x 宽)的视频,每个像素的颜色编码需要3个字节(屏幕上的最小点)(或者称为24位颜色,提供了16,777,216种不同的颜色),这个视频以每秒24帧的速度运行,并持续30分钟。

toppf = 1080 * 1920 //每帧的像素总数
cpp = 3 //每个像素的成本
tis = 30 * 60 //时间长度(以秒为单位)
fps = 24 //每秒帧数

required_storage = tis * fps * toppf * cpp

这个视频将需要约250.28GB的存储空间或1.19 Gbps的带宽!这就是为什么我们需要使用编解码器。

容器 - 存放音频和视频的地方

容器或封装格式是一种元文件格式,其规范描述了不同数据和元数据在计算机文件中如何共存。https://en.wikipedia.org/wiki/Digital_container_format

容器是一个包含所有流(通常是音频和视频)的单个文件,并提供同步和通用元数据,如标题、分辨率等。
通常,我们可以通过查看文件的扩展名来推断其格式:例如,video.webm可能是使用 webm 容器的。
在这里插入图片描述
当我们谈到视频格式时,常提到的是封装格式,比如 MP4。一个 MP4 文件可以包含一个视频流和一个音频流,其中视频流通常使用 H.264 进行视频压缩,音频流则通常使用 AAC 进行压缩。因此,当我们提及 H.264 和 AAC 时,它们既可以视为视频和音频的编码格式,也可以视为用于视频和音频压缩的算法。然而,通常我们很少单独将一个视频描述为 H.264 格式,因为视频通常以封装格式的形式出现,封装格式包含了视频流和音频流。

在下文中,我们会提及视频文件、封装格式、容器这三个术语,通常它们是指同一个意思。


解封装

回到今天的任务:使用 ffmpeg 将视频解封装。

在大多数情况下,你下载到的视频文件是一个容器,一个视频文件包含多个流(stream),通常包括视频流和音频流。流(stream)是指一系列随时间可用的数据元素。每个流使用不同的编解码器进行编码,编解码器定义了实际数据的编码和解码方式,因此被称为编解码器(CODEC),例如MP3、H.264等。从流中可以读取数据包(packet),数据包包含经过编码器压缩后的数据。将数据包传递给解码器后,我们可以获取到所需的视频帧数据。在FFmpeg中,存放视频帧数据的数据结构被称为帧(frame)。

为了从这个容器中找到视频并将其解码,第一步需要做的是解封装(demux)。前面提到了容器,它就好像一个盒子,你可以往里头装不同的物品,例如视频、音频、字幕等等。解封装就相当于打开这盒子,按需的取出里头的各类物品,以便能够在播放器或者设备上进行播放、编辑或者其他处理。

现在假设我们要从视频文件中获取到视频数据,进行解封装流程为:

  1. 打开 video.mp4 文件
  2. 从 streams 中找到视频流
  3. 从视频流中读取 packet

ffmpeg_api__75">使用 ffmpeg api 进行解封装

在这里插入图片描述
如果站在 ffmpeg api 使用者的角度来看解封装的流程:

  1. 你首先需要将视频文件加载到一个叫 AVFormatContext 的组件中。实际上,它并不完全加载整个文件,通常只读取文件的头部信息。
  2. 加载了视频文件的最小头部信息后,我们就可以访问文件的流 ,在 ffmpeg 中使用 AVStream 来保存这些流的信息。
  3. 假设我们有两个流:一个使用 AAC 编码器进行编码的音频流,另一个是使用 h264 编码器进行编码的视频流。从每个流中,我们可以读取名为 packet 的数据片段,这些数据片段在 ffmepg 使用 AVPacket 来保存。

话不多说,让我们上代码。解封装的代码你可以在 ffmpeg_video_player_tutorial-tutorial01 找到。解下来是代码的详细解释,我们会跳过一些细节,但没有关系,你可以在源码中找到你想要的。

首先我们声明一个 AVFormatContext 组件,它里头存放着关于容器的关键信息,主要用于封装(muxing)和解封装(demuxing)媒体文件。

AVFormatContext * pFormatCtx = NULL;

接下来,我们将打开文件并读取其头部信息,并填充AVFormatContext结构体,提供有关格式的最小信息(注意,通常不会打开编解码器)。用于执行此操作的函数是avformat_open_input。它需要一个AVFormatContext、一个文件名和两个可选参数:AVInputFormat(如果传递NULL,FFmpeg将猜测格式)和AVDictionary(这些是解封装器的选项)。

int ret = avformat_open_input(&pFormatCtx, argv[1], NULL, NULL);

如果梳理的话,你可以打印文件格式和视频时长:

printf("Format %s, duration %lld us", pFormatCtx->iformat->long_name, pFormatCtx->duration);

avformat_open_input 只是读取了最小头部信息,接下去我们需要找到文件中流的信息,avformat_find_stream_info 用于执行次操作:

ret = avformat_find_stream_info(pFormatCtx, NULL);

现在,pFormatCtx->nb_streams将保存流的数量,而pFormatCtx->streams[i]将给出第i个流(一个AVStream)。你可以通过循环查看所有流:

for (int i = 0; i < pFormatContext->nb_streams; i++)
{
  //
}

由于我们目前只对视频感兴趣,因此在遍历 streams 时我们可以纪录视频流的下标,它在后面是有用的:

for (i = 0; i < pFormatCtx->nb_streams; i++)
{
	if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
	{
		videoStream = i;
		break;
	}
}

每个 AVStream 里头有一个类型为 AVCodecParameters 成员变量叫 codecpar,它描述了这个流中编解码相关的信息,例如 codec_id 是啥。关于编解码的信息非常重要,在后面的「视频解码」章节中将使用到这些信息。这里先暂时跳过。

接下来,根据 ffmpeg_video_player_tutorial-tutorial01 中代码,你会看到一系列和编解码相关的操作,例如通过 codec_id 找到编解码。但是先等等,让我们直接跳到读取 packet 的部分中,编解码内容的讲解将放到下一篇博客中。

接下来,我们将从流中读取 packet,首先要做的是先申请一个 AVPacket:

AVPacket * pPacket = av_packet_alloc();

接着使用 av_read_frame 从文件中读取一个 packet:

while (av_read_frame(pFormatContext, pPacket) >= 0) {
  //...
}

读取的到的这个 packet,它可能来自视频流,也可能来自其他流。由于我们只对视频数据感兴趣,如果当前的 packet 来自其他流,那么直接忽略处理即可:

if (pPacket->stream_index == videoStream)
{
	// do something on video data
}

看!这里对 packet 来源的进行不同的处理,就是所谓的解封装,就这么简单!


总结

本文介绍了视频、音频、编解码器和容器的基本概念,介绍了什么是解封装以及使用 ffmpeg api 进行解封装的基本流程。所有代码可以在 ffmpeg_video_player_tutorial-tutorial01 中找到。

参考

  • An ffmpeg and SDL Tutorial - Tutorial 01: Making Screencaps
  • rambodrahmani/ffmpeg-video-player
  • ffmpeg-libav-tutorial
  • ffmpeg_video_player_tutorial-tutorial01

http://www.niftyadmin.cn/n/404245.html

相关文章

springboot+vue多维的知识分类管理系统

随着国内市场经济这几十年来的蓬勃发展&#xff0c;突然遇到了从国外传入国内的互联网技术&#xff0c;互联网产业从开始的群众不信任&#xff0c;到现在的离不开&#xff0c;中间经历了很多挫折。本次开发的多维分类的知识管理系统有管理员和用户两个角色。管理员可以管理用户…

分布式数据库-事务一致性

version: v-2023060601 author: 路__ 一、什么是“强一致性” 分布式数据库的“强一致性”应该包含两个方面&#xff1a;serializability&#xff08;串行&#xff09; and linearizability&#xff08;线性一致&#xff09;&#xff0c;上述图为“Highly Available Transact…

【学习笔记】NOI 模拟赛 t2 momo

点这里看题目 我考场上做这道题的时候真的可以说是脑子里一片浆糊。看来还是太菜了。 能把这题搞懂&#xff08;&#xff1f;&#xff09;还是归功于学长的博客。 先考虑树的情形。但是我是真没想到能推广到图上。不过还是应该相信出题人吧&#xff0c;毕竟有树的部分分。 不…

Vue3新特性解析:响应式API、Composition API与Teleport

Vue3是Vue.js框架的最新版本,带来了一系列令人期待的新特性,包括响应式API、Composition API和Teleport。本文将深入探讨这些新特性的原理、性能、示例和应用场景,帮助开发者更好地理解和运用Vue3,从而提高应用程序的性能和可维护性。 Vue3是Vue.js框架的最新版本,它带来…

【博客641】Prometheus Subqueries in VictoriaMetrics

Prometheus Subqueries in VictoriaMetrics 没有PromQL Subqueries时 以前不支持Subqueries时&#xff0c;类似以下这种联合查询是不被允许的&#xff1a; max_over_time(rate(my_counter_total[5m])[1h]) predict_linear(rate(my_counter_total[5m])[1d], 3600)以前执行联合…

STM32 实现简单定时任务调度器,动态创建任务,两种思路实现跑马灯

代码实现和硬件没关系&#xff0c;所以并不限于STM32&#xff0c;Arduino 之类的其他地方也能用&#xff0c;只要有一个能获取时间的函数就行&#xff0c;或者说&#xff0c;只要有一个会随着时间自动增加的变量就行&#xff0c;时间单位无所谓&#xff0c;所以确实想的话&…

SpringBoot+MyBatisplus搭建校园新闻平台——已开源

概述 开发背景 校园新闻平台是以新闻宣传机构的在线信息发布需求为基础&#xff0c;随着数字化和信息化的快速发展&#xff0c;校园新闻在校园内的传播和沟通中变得越来越重要。学校需要一个有效的管理系统来整合、发布和传播校园新闻&#xff0c;以满足师生、校友和其他利益…

mac m1/m2 安装 ps 2023 插件无法显示扩展界面

碎碎念&#xff1a;一直在踩坑的路上&#xff0c;甚至想休息时间玩一会儿 ps 都能踩坑 问题描述 新的 m2 芯片 mac 安装了色环插件后&#xff0c;在窗口界面中没有找到扩展&#xff0c;且在首选项->增效工具的旧版扩展也是灰色的 题外话&#xff1a;记录一下 mac 的 photo…