基于 FFmpeg 的跨平台视频播放器简明教程(四):像素格式与格式转换

这篇具有很好参考价值的文章主要介绍了基于 FFmpeg 的跨平台视频播放器简明教程(四):像素格式与格式转换。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章目录

  1. 基于 FFmpeg 的跨平台视频播放器简明教程(一):FFMPEG + Conan 环境集成
  2. 基于 FFmpeg 的跨平台视频播放器简明教程(二):基础知识和解封装(demux)
  3. 基于 FFmpeg 的跨平台视频播放器简明教程(三):视频解码


前言

经过前面三章的学习,我们快要完成我们的目标任务了:使用 ffmpeg 解码视频,并将解码后的视频帧保存在本地(就像对视频截图一样)。

现在就差临门一脚,如何将解码后的视频帧保存到本地呢?这是今天要讨论的内容。

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

本文的代码在 ffmpeg_video_player_tutorial-tutorial01。

像素格式

在使用 FFmpeg 进行视频处理时,AVPacket 表示被算法压缩后的视频数据,对 AVPacket 进行解码后,我们可以得到 AVFrame,它是用来表示视频画面的一个数据结构,包含了该画面的宽度、高度、像素格式等信息。

像素格式(Pixel format)是一种用于表示和存储数字图像中每个像素数据的方式。它描述了每个像素中哪些颜色通道是可用的,以及这些通道的顺序、比特深度和其他属性。像素格式决定了图像数据在内存中的存储布局。常见的像素格式包括:

  • RGB:每个像素包含红、绿、蓝三种颜色通道。
  • RGBA:每个像素包含红、绿、蓝和透明度四种通道。
  • YUV:每个像素表示为亮度(Y),色差(U)和饱和度(V)三个通道。
  • Grayscale:图像只有一个颜色通道,通常表示为灰度值。

这些格式可能有不同的比特深度,比如8位、16位等,表示每个颜色通道的数据精度。不同的像素格式和比特深度将影响颜色的展示和图像的质量,同时也决定了文件大小和处理速度。在计算机图形、视频编码和图像处理中,选择合适的像素格式尤为重要。

在 FFmpeg 的 AVPixelFormat 枚举中有大几十种像素格式,你可能会发问:为什么要有这么多的像素格式?都使用 RGB 不行吗?

将所有图像都使用RGB像素格式的确是可能的,但实际上,不同的像素格式存在的原因在于它们各自适用于不同的场景和需求。这些场景可能需要考虑存储空间、颜色表现力和处理效率等方面的差异。以下是一些不同像素格式存在的原因:

  • 存储和带宽优化:一些像素格式,如YUV,可能需要比RGB更少的存储空间。因为YUV格式考虑到人眼对亮度敏感度高于色度,常常在色度分量上采样更低的分辨率,因此可以在保持相对较高的图像质量的同时减小文件大小。这在视频压缩和传输等场景下特别重要。

  • 颜色空间:RGB是一种基于颜色的光学叠加模型,对某些颜色计算并不直观。其他颜色空间,如HSB(色相、饱和度、亮度)或YUV,可能在特定的颜色操作或计算上更具优势。

  • 兼容性和专业领域需求:例如,在视频行业和电视广播中,YUV格式有更好的性能和兼容性。而在一些图形应用程序中,可能需要包含透明度通道的像素格式,例如RGBA,从而支持透明度相关的效果和操作。

  • 灰度图像:对于只需要单个颜色通道的应用,如文本扫描、医学成像或图像分析等,使用灰度格式可以大幅减少存储和处理资源的需求。

总之,不同的像素格式适用于不同的场景。RGB虽然是一种常用的颜色表示方法,但在某些情况下,使用其他像素格式可能会更加高效或更适合任务需求。

关于 YUV 格式,笔者之前写过一篇 YUV 文件读取、显示、缩放、裁剪等操作教程,有兴趣的读者可以参考参考。

AVFrame 的像素格式转换

经过前面三章内容,我们现在已经能够将 AVPacket 解码为 AVFrame,为了保存 AVFrame 中的图像数据到本地,需要对 AVFrame 做一次像素格式的转换,这样方便我们直接看到视频帧的内容。

为啥要做像素转换呢?这是因为多数解码后的 AVFrame 使用 YUV 作为像素格式(比 RGB 更少的存储空间),如果你想看 YUV 格式的图片,你需要一个类似 YUV Viewer 的软件来打开 YUV 图片。当然你可以选择用我开发的 simple_yuv_viewer。

但如果我们将 AVFrame 转为 RGB,然后将 RGB 数据保存在 PPM 格式文件中,那么你基本可以使用任意图片预览软件就能打开。方便不少。

PPM 文件

首先需要介绍写 PPM 文件,PPM(Portable Pixmap)文件是一种简单的图像文件格式,用于存储彩色图像。PPM文件格式通常存储未压缩的RGB图像数据,因此文件大小相对较大。由于其简易的文件结构和易于解析的特点,该格式常用于学习和测试图像处理算法。

PPM文件包含以下部分:

  1. 文件头,包括:
    • 魔数(Magic number):文件头的开始,表明文件类型。对于二进制PPM文件,常用"P6";对于ASCII PPM文件,常用"P3"。
    • 图像宽度和高度:以像素为单位的图像尺寸。
    • 最大颜色值:通常为255,代表每个颜色通道的最大值。
  2. 图像数据:字节序列表示的图像像素数据。二进制格式的PPM文件存储连续的RGB值(范围为0到最大颜色值),每个像素由3个字节表示,分别为红、绿、蓝颜色通道的值。

尽管PPM文件易于处理,但它不适合于大型或复杂图像,因为它占用较大的存储空间且不支持数据压缩。在实际应用中,我们通常会使用其他压缩格式,如JPEG、PNG等。

实际代码中,将一个 RGB 格式的 AVFrame 保存到本地非常简单:

void saveFrame(AVFrame *avFrame, int width, int height, const char* output_name)
{
  FILE *pFile;

  // Open file
  pFile = fopen(output_name, "wb");
  if (pFile == NULL) {
    return;
  }

  // Write header
  fprintf(pFile, "P6\n%d %d\n255\n", width, height);

  // Write pixel data
  const int kBytesPerPixel = 3; // R(8) G(8) B(8)
  for (int y = 0; y < height; y++) {
    uint8_t *img_row = avFrame->data[0] + y * avFrame->linesize[0];
    fwrite(img_row, 1, width * kBytesPerPixel, pFile);
  }

  // Close file
  fclose(pFile);
}

Linesize 是什么?

在解释上面代码逻辑之前,需要对 linesize 的概念进行说明。linesize,有时候它也叫 stride,或者 pitch,名字不同但含义相同。

linesize是指图像一行数据所占用的字节数。在处理图像时,图像的像素数据是以一行一行的方式存储的。由于一行像素数据不一定和图像宽度相等,因此需要用linesize来表示每行像素数据所占用的字节数。通常情况下,一个像素占用的字节数是已知的,因此可以通过图像宽度和像素占用的字节数计算出每行数据的字节数(linesize)。

当图像数据按行存储时,linesize可能大于实际像素宽度乘以每个像素所需的字节数。例如,如果一幅图像有宽度W、高度H、RGB格式的像素, 并且使用32位对齐存储,则linesize大于3*W。两行相邻像素之间的多余空间可能用于存储其他信息,或者是因为对齐的原因而保留。

了解linesize是有必要的,因为在执行图像处理任务时,你可能需要使用它来遍历图像中每行的所有像素。逐行遍历图像时,需要利用行跨距来确定每一行数据在内存中的起始位置,从而能够正确地访问和处理像素数据。

假设你正在处理一张100×100像素的彩色RGB图像,每个像素通常需要3个字节(24位)来存储红、绿和蓝颜色通道的数据。要进行8位字节对齐,我们首先找出紧密排列时每一行所需的字节数:

  1. 紧密排列的字节数 = 图像宽度 × 每个像素字节数 = 100 × 3 = 300
  2. 然后根据8字节对齐要求,计算出距离下一个最近的8字节对齐位置,即:
  3. linesize = (紧密排列的字节数 + 7) // 8 * 8 = (300 + 7) // 8 * 8 = 307 // 8 * 8 = 38 * 8 = 304

所以,在采用8字节对齐存储的情况下,linesize为 304 字节。这意味着在每行末尾有4个字节的填充空间以符合8字节对齐规则。

因此,存放一张 100 * 100 的 RGB 图片,实际使用的内存大小是:100 * 304 = 30400,而不是 100 * 300 = 30000。

保存 PPM 文件的代码解释

回到之前的代码中来,对保存 AVFrrame 到 PPM 文件的逻辑进行一些说明:

  1. avFrame->data[0] 指针指向一片内存,该内存存放着整张图片的 RGB 数据。注意:这片内存包含着用于填充 linesize 的无用数据。
  2. avFrame->data[0] 按行存放 RGB 数据,大致如下图,其中灰色表示无用数据。
    基于 FFmpeg 的跨平台视频播放器简明教程(四):像素格式与格式转换,ffmpeg,音视频
  3. fprintf(pFile, "P6\n%d %d\n255\n", width, height); 这是往 PPM 文件中写入头部信息,其中 “255” 表示 RGB 分量最大值为 255
  4. PPM 文件按行存储连续的 RGB 值,因此我们需要按行将 RGB 像素写入文件。 for 中循环写入每一行数据,其中 avFrame->data[0] + y * avFrame->linesize[0]; 定位到每一行的开始的位置;fwrite(img_row, 1, width * kBytesPerPixel, pFile); 写入每一行中的所有 RGB 数据。

使用 sws_scale 进行像素格式转换

正如前面所说,视频中一般使用 YUV 作为像素格式,为了保存为 PPM 文件,需要将 YUV 转换 RGB。在 FFmpeg 中,已经提供了方便的工具来做像素格式转换的工作,它就是 sws_scale 函数。

sws_scale 函数是 FFmpeg 中的一个关键功能,用于实现视频图像缩放和格式转换。它是由 libswscale 库提供的,这是一个专门用于处理各种像素格式以及実现高效缩放和颜色空间/格式转换的库。

sws_scale 函数的作用具体包括以下几点:

  1. 缩放:根据源图像和目标图像的尺寸,调整图像大小。例如,把 1920x1080 的图像缩放到 1280x720。
  2. 格式转换:在不同的像素格式之间转换图像数据。例如,将 YUV420 转换为 RGB24,或者将 NV12 转为 YUV420P。
  3. 颜色空间/范围转换:将图像的颜色空间与范围调整为不同的类型,例如从 BT.709 转为 BT.601,或者将全范围 YUV 转为有限范围 YUV。
  4. 效率:利用 CPU 的 SIMD 指令集(如 MMX、SSE 和 AVX)进行优化,以提高缩放和格式转换的速度。

那么如何使用 sws_scale 呢?步骤如下:

  1. 创建 SwsContext 结构体
  2. 创建一个新的 AVFrame,以便存放转换后的数据
  3. 使用 sws_scale 函数进行转换即可

让我们来看每个步骤具体的实现。这部分代码参考 ffmpeg_image_converter.h 中的实现

首先,使用 sws_getContext 创建 SwsContext。你需要填很多信息,包括源视频的宽高、像素格式,以及目标宽高、目标像素格式等

sws_ctx = sws_getContext(srcW, srcH, srcFormat, dstW, dstH, dstFormat,
                             flags, srcFilter, dstFilter, param);

创建一个 AVFrame 用于存放转换后的数据,其中 av_frame_get_buffer, 函数是用于为 AVFrame 结构体分配内存空间的函数。它会根据 AVFrame 的格式和大小信息,为 AVFrame 的数据指针分配内存并设置行大小,以准备存储原始数据。

frame = av_frame_alloc();
frame->width = dstW;
frame->height = dstH;
frame->format = dstFormat;
frame->format = (int)dstFormat;
frame->width = dstW;
frame->height = dstH;
frame->channels = 0;
frame->channel_layout = 0;
frame->nb_samples = 0;
av_frame_get_buffer(frame, 16);

最后,使用 sws_scale 进行转换

frame->pict_type = in_frame->pict_type;
frame->pts = in_frame->pts;
frame->pkt_dts = in_frame->pkt_dts;
frame->key_frame = in_frame->key_frame;
frame->coded_picture_number = in_frame->coded_picture_number;
frame->display_picture_number = in_frame->display_picture_number;
int output_height = sws_scale(
    sws_ctx, (uint8_t const *const *)in_frame->data, in_frame->linesize, 0,
    in_frame->height, frame->data, frame->linesize);

总结

本文讲述了如何将一帧视频保存到本地 PPM 文件,以便浏览。介绍了关于像素格式、PPM、Linesize、FFmpeg 中 sws_scale 等知识点。
结合前面三章内容,我们终于完成了第一个任务,对应 An ffmpeg and SDL Tutorial - Tutorial 01: Making Screencaps。接下来我们将继续 ffmpeg 学习路程。文章来源地址https://www.toymoban.com/news/detail-521879.html

参考

  • 图像处理、显示中的行宽(linesize)、步长(stride)、间距(pitch)

到了这里,关于基于 FFmpeg 的跨平台视频播放器简明教程(四):像素格式与格式转换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

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

    基于 FFmpeg 的跨平台视频播放器简明教程(一):FFMPEG + Conan 环境集成 前面一章中我们介绍了如何使用 conan 和 cmake 搭建 ffmpeg 运行环境,你做的还顺利吗?如果遇到任何问题,请在进行评论,我看到都会回复的。 从本章开始,将正式开始我们的 ffmpeg 播放器学习之旅。接下去

    2024年02月08日
    浏览(189)
  • QtAV:基于Qt和FFmpeg的跨平台高性能音视频播放框架

    目录 一.简介 1.特性 2.支持的平台 3.简单易用的接口 二.编译 1.下载依赖包 2.开始编译 2.1克隆 2.2修改配置文件 2.3编译 三.试用 官网地址:http://www.qtav.org/ Github地址:https://github.com/wang-bin/QtAV ●支持大部分播放功能 ●播放、暂停、播放速度、快进快退、字幕、音量、声道、音

    2024年01月22日
    浏览(161)
  • 开源的跨平台的音视频处理工具FFmpeg

    FFmpeg是一个开源的跨平台的音视频处理工具,可以对音频、视频进行转码、裁剪、调节音量、添加水印等操作。 广泛的格式支持。 FFmpeg能够解码、编码、转码、复用、分离、流式传输、过滤和播放几乎人类和机器所创造的任何内容。它支持最古老且晦涩难懂的格式,也支持

    2024年02月15日
    浏览(68)
  • FFmpeg——开源的开源的跨平台音视频处理框架简介

    引言:         FFmpeg是一个开源的跨平台音视频处理框架,可以处理多种音视频格式。它由Fabrice Bellard于2000年创建,最初是一个只包括解码器的项目。后来,很多开发者参与其中,为FFmpeg增加了多种新的功能,例如编码器、过滤器、muxer、demuxer等等,使它成为了一个完整

    2024年03月23日
    浏览(71)
  • 基于FFmpeg的视频播放器之三:拉取rtsp流

    拉取网络流和打开本地文件流程差不多,详见:基于FFmpeg的视频播放器之二:解复用,下面是不同地方。 方法有很多,最方便的应该是用VLC串流了,具体步骤如下:https://blog.csdn.net/m0_61353061/article/details/120388230 当然也可以使用live555作为rtsp服务器,可参考:https://blog.csdn.net

    2023年04月25日
    浏览(50)
  • 音视频项目—基于FFmpeg和SDL的音视频播放器解析(三)

    介绍 在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本文,可参考我的前一篇文章音视频项目—基于FFmpeg和SDL的音视

    2024年02月05日
    浏览(71)
  • 音视频项目—基于FFmpeg和SDL的音视频播放器解析(二十一)

    介绍 在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本文,可参考我的前一篇文章音视频项目—基于FFmpeg和SDL的音视

    2024年02月02日
    浏览(74)
  • QT软件开发-基于FFMPEG设计视频播放器-软解图像(一)

    QT软件开发-基于FFMPEG设计视频播放器-CPU软解视频(一) https://xiaolong.blog.csdn.net/article/details/126832537 QT软件开发-基于FFMPEG设计视频播放器-GPU硬解视频(二) https://xiaolong.blog.csdn.net/article/details/126833434 QT软件开发-基于FFMPEG设计视频播放器-解码音频(三) https://xiaolong.blog.csdn.

    2023年04月08日
    浏览(51)
  • ffmpeg跨平台arm编译-ubuntu

    32位系统: 64位系统: 关键选项: –arch=arm:指定ARM平台 –target-os=linux:指定Linux系统 –enable-cross-compile :指定交叉编译 –cross-prefix=arm-linux-gnueabihf-:指定交叉编译链 如果是64位系统: –arch=aarch64:指定ARM平台 –cross-prefix=aarch64-linux-gnu-:指定交叉编译链

    2024年02月08日
    浏览(61)
  • 论文精讲 | 基于昇思MindSpore打造首个深度学习开源视频压缩算法库OpenDMC,支持跨平台和多种评价指标

    论文标题 OpenDMC: An Open-Source Library and Performance Evaluation for Deep-learning-based Multi-frame Compression 论文来源 ACM MultiMedia 论文链接 https://www.acmmm2023.org/open-source-program/ 代码链接 https://openi.pcl.ac.cn/OpenDMC/OpenDMC 昇思MindSpore作为开源的AI框架,为产学研和开发人员带来端边云全场景协同、

    2024年02月02日
    浏览(67)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包