Gstreamer基础讲解_gstreamer

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

Gstreamer讲解

文章目录

基础

背景

​ 从历史的角度来看Linux在多媒体方面已经远远落后于其他的操作系统。Microsoft’s Windows 和Apple’s MacOS它们对多媒体设备、多媒体创作、播放和实时处理等方面已经有了很好的支持。另一方面Linux对多媒体应用的综合贡献比较少这也使得Linux很难在专业级别的软件上与MS Windows和MacOS去竞争。GStreamer正是为解决Linux多媒体方面当前问题而设计的

​ GStreamer 是一个非常强大而且通用流媒体应用程序框架。GStreamer并不受限于音频和视频处理, 它能够处理任意类型的数据流。主要的优点在于: 它的可插入组件能够很方便的接入到任意的流水线pipeline当中。这个优点使得利用GStreamer编写一个万能的可编辑音视频应用程序成为可能。

GStreamer框架是基于插件的。所有的插件都能够被链接到任意的已经定义了的数据流水线中。

官方网站https://gstreamer.freedesktop.org/

小结

Gstreamer的优点
1.结构清晰且威力强大
GStreamer提供一套清晰的接口无论是构建媒体管道的应用程序员还是插件程序员均可以方便的使用这些API。
2.面向对象的编程思想
GStreamer是依附于GLib 2.0对象模型的采用了信号与对象属性的机制。
3.灵活的可扩展性能
所有的GStreamer对象都可以采用GObject继承的方法进行扩展
所有的插件都可以被动态装载可以独立的扩展或升级。
4.核心库与插件(core/plugins)分离
所有的媒体处理功能都是由插件从外部提供给内核的并告诉内核如何去处理特定的媒体类型。

image-20220114212052396

元件Element

​ 元件(Element)是GStreamer中最重要的概念。

​ 可以通过创建一系列的元件并把它们连接起来,从而让数据流在这个被连接的各个元件之间传输。

​ 可以将若干个元件连接在一起,从而创建一个流水线(pipeline)来完成一个特殊的任务,例如,媒体播放或者录音。

​ 对程序员来说 GStreamer中最重要的一个概念就是GstElement对象。元件是构建一个媒体流水线的基本块。每一个元件都对应一个GstElement。任何一个解码器编码器、分离器、视频/音频输出部件实际上都是一个GstElement对象。

​ 下图为一个流水线的实例实现 文件->音视频解码->分别播放 的功能

image-20220114215215203

源元件source element

​ 源元件为管道产生数据比如从磁盘或者声卡读取数据。下图是形象化的源元件我们总是将**源衬垫(source pad)**画在元件的右端。

image-20220114220749263

​ 源元件不接收数据仅产生数据。你可从上图中明白这一点因为上图仅有一个源衬垫右端 。

过滤/类过滤元件filter/filter-like element

​ 过滤器(Filters)以及类过滤元件(Filter-like elements)都同时拥有输入和输出衬垫。他们对从输入衬垫得到的数据进行操作然后将数据提供给输出衬垫。音量元件(filter) 、视频转换器(convertor) 、Ogg分流器或者Vorbis解码器都是这种类型的元件。

​ 类过滤元件可以拥有任意个的源衬垫或者接收衬垫。像解码器只有一个源衬垫及一个接收衬垫。而视频分流器可能有一个接收衬垫以及多个源衬垫每个接收衬垫对应一种元数据流。

image-20220114224254934

接收元件sink element

​ 接收元件是媒体管道的末端它接收数据但不产生任何数据。写磁盘、利用声卡播放声音以及视频输出等都是由接收元件实现的。下图显示了接收元件。

image-20220114225023291

将元件链接(link)起来

​ 通过将一个源元件零个或多个类过滤元件和一个接收元件链接在一起你可以建立起一条媒体管道。数据将在这些元件间流过。这是 GStreamer中处理媒体的基本概念。

image-20220114225536564

衬垫(Pads)

​ 衬垫(Pads)在GStreamer中被用于多个元件的链接,从而让数据流能在这样的链接中流动。衬垫是元件对外的接口可以被看作是一个元件的插座或者端口,元件之间的链接就是依靠着衬垫。数据流从一个元件的源衬垫(source pad)到另一个元件的接收衬垫(sink pad)。衬垫的功能(capabilities)决定了一个元件所能处理的媒体类型。

​ 衬垫有处理特殊数据的能力:一个衬垫能够限制数据流类型的通过。链接成功的条件是只有在两个衬垫允许通过的数据类型一致的时候才被建立。这个被称为协商negotiation

pad的信息通过gst-inspect-1.0即可查看

Pad Templates:
  SINK template: 'sink'                    ------>sink pad数据流入
    Availability: Always                   ------>pad时效性永久型
    Capabilities:                          ------>pad支持的caps
      video/quicktime
      video/mj2
      audio/x-m4a
      application/x-3gp

  SRC template: 'video_%u'                 ------>src pad数据流出
    Availability: Sometimes                ------>pad时效性随机型
    Capabilities:
      ANY

  SRC template: 'audio_%u'
    Availability: Sometimes
    Capabilities:
      ANY

  SRC template: 'subtitle_%u'
    Availability: Sometimes
    Capabilities:
      ANY

从上面可以看到每个pad都会有以下属性padname、direction、presence、caps。

  • padnamepad名称

  • directionpad的输入输出方向有src和sink两种

  • presencepad的时效性有永久型GST_PAD_ALWAYS、随机型GST_PAD_SOMETIMES、请求型GST_PAD_REQUEST请求型的仅在gst_element_request_pad()调用随机型的则是会根据不同的输入数据使用不同的pad。三种时效性的意义顾名思义: 永久型的衬垫一直会存在随机型的衬垫只在某种特定的条件下才存在(会随机消失的衬垫也属于随机型)请求型的衬垫只在应用程序明确发出请求时才出现。

  • capspad支持的功能

参考资料
https://blog.csdn.net/houxiaoni01/article/details/98509594

Gstreamer的面向对象

​ Gstreamer使用C语言来模拟面向对象的实现主要基于glib库里面的GObject库的数据类型来实现GObject是一个程序库它可以帮助我们使用C语言编写面向对象的程序。

​ 很多人被灌输了这样一种概念要写面向对象程序那么就需要学习一种面向对象编程语言例如C++、Java、C# 等等而 C 语言是用来编写结构化程序的。事实上面向对象只是一种编程思想不是一种编程语言。换句话说面向对象是一种游戏规则它不是游戏。GObject 告诉我们使用 C 语言编写程序时可以运用面向对象这种编程思想。

​ 在 GObject 世界里是两个结构体的组合一个是实例结构体另一个是类结构体。例如 MyObject 是实例结构体MyObjectClass 是类结构体它们合起来便可以称为 MyObject类。

#include <glib-object.h>

//实例结构体
typedef struct _MyObject{
        GObject parent_instance;
} MyObject;
//类结构体
typedef struct _MyObjectClass {
        GObjectClass parent_class;
} MyObjectClass;

//让GObjectx系统知道你定义了这个类
G_DEFINE_TYPE(MyObject, my_object, G_TYPE_OBJECT);

​ 在GObject中一个对象的产生遵循如下原则:

​ 如果产生的是该类的第一个实例那么先分配Class结构体再分配针对该实例的结构体。否则直接分配针对该实例的结构。也就是说在Class结构体中所有的内容是通过该类生成的实例所公有的。而实例化每个对象时为其单独分配专门的实例用结构体。

​ 也许你会注意到MyObject实例结构体的第一个成员是 GObject 结构体MyObject类结构体的第一个成员是 GObjectClass 结构体。其实GObject 结构体与 GObjectClass 结构体分别是 GObject类实例结构体类结构体当它们分别作为 MyObject实例结构体类结构体第一个成员时这意味着 MyObject继承自 GObject类

​ 每个类必须定义为两个结构体它的类结构体和它的实例结构体。所有的类结构体的第一个成员必须是一个GTypeClass结构所有的实例结构体的第一个成员必须是GTypeInstance结构。

Gstreamer的多线程

​ GStreamer 是一个支持多线程的框架而且是绝对安全的线程。

​ Gstreamer的多线程由元件queue来实现queue前后的两部分被分成两个线程执行。

image-20220119215443674

实用工具

​ gst-inspect 用于查看一个插件的信息在gstreamer1.0版本下为gst-inspect-1.0

​ gst-launch 用于启动一个流水线在gstreamer1.0版本下为gst-launch-1.0

​ gst-launch主要用于临时启用或调试一个流水线如果一个已经定型的流水线需要使用gstreamer提供的C函数来形成一个流水线。

​ 为了使得我们自己的插件能够成功被gstreamer检测到gstreamer支持添加GST_PLUGIN_PATH环境变量来增加插件的搜索路径。

​ 在gst-launch启动前会先扫描需要使用的各插件对应的so库当扫描到了对应的库但是无法载入成功时则会把插件加入黑名单如果需要查看黑名单只需要gst-inspect-1.0 -b即可。

​ 损坏的插件在第一次扫描到时会报出加入黑名单的原因后续再扫描时将不会报出原因如果需要查看加入黑名单的原因需要删除相应的缓存再执行。

rm ~/.cache/gstreamer-1.0/registry.x86_64.bin
gst-inspect-1.0 -b

​ gstreamer调试等级通过设置环境变量GST_PLUGIN来设置gstreamer运行时的调试等级不同等级会打印出不同等级的信息。

总共有六个等级[05]
0 什么都不打印
1 打印GST_ERROR ()的信息
2 打印GST_ERROR () GST_WARNING () 的信息
3 打印GST_ERROR () GST_WARNING () GST_INFO () 的信息
4 打印GST_ERROR () GST_WARNING () GST_INFO () GST_DEBUG () 的信息
5 打印GST_ERROR () GST_WARNING () GST_INFO () GST_DEBUG () GST_LOG () 的信息

Gstreamer常用插件介绍

Gstreamer有一些常用的用于搭建流水线的插件这里做一个介绍。

v4l2srcVideo for Linux 2 source

Video for Linux 2是内核提供给应用程序访问音、视频驱动的统一接口v4l2src则是其提供给gstreamer的一个插件element它属于插件video4linux2动态链接库文件名为libgstvideo4linux2.so。

image-20220118223403315

v4l2src属性里常用的有

device用于指定Linux 设备文件例如/dev/video0

num-buffers: 用于指定从设备中读入多少帧。

v4l2src可以从外部指定caps类型。

由gst-inspect-1.0 v4l2src可发现

image-20220118224209893

v4l2src实例

gst-launch-1.0 v4l2src devce=/dev/video0 num-buffers=60 ! video/x-raw,width=1280,height=720 ! fakesink

其中fakesink是一个万能结束元件用于在调试中结束一条流水线。

官方文档

https://gstreamer.freedesktop.org/documentation/video4linux2/v4l2src.html?gi-language=c

内容与直接执行gst-inspect-1.0 v4l2src差不多

filesrc

filesrc是gstreamer的核心插件之一用于从文件系统中读取某一文件。

常用属性

location: 文件位置

num-buffers: 用于指定从设备中读入多少帧。

filesink

用于存放视频流至文件中

常用属性

location: 文件位置

qtdemux(QuickTime Demuxer)

用于将文件中的音频和视频分开一般读取文件后应接该元件不过有的视频文件不需要这个与文件本身有关。

实例gst-launch-1.0 filesrc location=flower_groundtruth.mp4 ! qtdemux ! h264parse ! omxh264dec ! omxh264enc filesink location=output.mp4

功能读文件->提取视频->解析h264视频流->h264解码->h264编码->写入文件

videotestsrc

gstreamer自带的测试视频源

常用属性

pattern测试视频显示图案

image-20220119150355022

实例gst-launch-1.0 videotestsrc pattern=2 ! ximagesink

其中ximagesink是指窗口显示

fakesrc 与fakesink

gstreamer自带的万能src和sink

videocrop

裁剪视频与v4l2src一样在外部指定caps

常用属性

image-20220119151006293

videoscale

改变视频尺寸与v4l2src一样在外部指定caps

常用属性

method插值方法

n-threads线程数

videoconvert

改变视频格式通常用于两个插件格式不匹配时作为中间插件协调两个插件的格式与v4l2src一样在外部指定caps。

kmssink

用于将视频发送至HDMI输出。

常用属性

sync: 同步当为True时当处理速度过慢时会采用丢弃一些帧的策略来处理最新帧当为false时不会放弃任何帧内部有buffer存放还没处理完的帧。

bus-id: 填写HDMI输出所对应的设备。例如a0007000.v_mix具体信息查看需要在dmesg里查看。

image-20220119154355851

jpegdec

jpeg解码当使用USB摄像头时送入的是JPEG图像此时需要进行解码才能正常处理。

其他实用插件可参考gstreamer官方文档。

gstreamer工程记录

​ 如果想把自定义的硬件集成在gstreamer框架下需要编写gstreamer插件。

关于Gstreamer的x86环境与aarch64交叉编译环境的搭建

​ 对于x86的Ubuntu环境

apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

​ 对于aarch64的交叉编译环境

​ 首先按照交叉编译器工具链这个除了gcc之外还包括了其他必要的工具

sudo apt-get install gcc-aarch64-linux-gnu

image-20220116184832612

	其次准备好必要的依赖库。库依赖关系

image-20220115164854318

​ 如果想准备gst-plugins-base和gstreamer core的交叉编译环境则需要按照依赖关系准备好前述编译环境。

注意事项交叉编译环境的版本应与开发板的gstreamer版本保持一致这是为了保证头文件和连接的依赖库保持一致。特别地gstreamer 1.12之前和1.14以后的版本不能通用两个版本的插件也不能通用。

  • 安装orc 0.4.27支持库

    如果安装了gtk-doc-tools和libgtk2.0-doc后提示gtkdoc-mktmpl: command not found将./autogen.sh --prefix=/usr改为下面的命令

./configure --prefix=/home/rongyitong/aarch64 --host=aarch64-linux-gnu
make 
sudo make install
  • zlib 1.2.11交叉编译
export CC=arm-linux-gnu-gcc   #它的configure不支持用CC变量来指定交叉工具链
./configure --prefix=/home/rongyitong/aarch64  #最后生成的库、头文件和man文件都在该目录下
make && make install
  • libffi 3.4.2交叉编译

    export CC=aarch64-linux-gnu-gcc
    ./configure --prefix=/home/rongyitong/aarch64 --host=aarch64-linux-gnu
    make && sudo make install
    
  • glib 2.45.3 交叉编译
    glib根目录下新建glib.cache里面写入

glib_cv_long_long_format=ll
glib_cv_stack_grows=no
glib_cv_have_strlcpy=no
glib_cv_have_qsort_r=yes
glib_cv_va_val_copy=yes
glib_cv_uscore=no
glib_cv_rtldglobal_broken=no
ac_cv_func_posix_getpwuid_r=yes
ac_cv_func_posix_getgrgid_r=yes
./autogen.sh --prefix=/home/rongyitong/aarch64 --host=aarch64-linux-gnu CC=aarch64-linux-gnu-gcc  LIBFFI_CFLAGS="-I/home/rongyitong/aarch64/lib/libffi-3.0.13/include" LIBFFI_LIBS="-L/home/rongyitong/aarch64/lib -lffi" -cache-file=glib.cache  --disable-selinux  --disable-xattr --disable-libelf ZLIB_CFLAGS="-I/home/rongyitong/aarch64/include" ZLIB_LIBS="-lz -L/home/rongyitong/aarch64/lib"

如果遇到

image-20220103153940015

这是编译器版本问题在 glib/gdate.c前面加上#pragma GCC diagnostic ignored “-Wformat-nonliteral”

  • gstreamer 1.12交叉编译因为开发板是1.12版本因此需要保持一致

    注意在之前添加环境变量PKG_CONFIG_PATH用于./configure里用pkg-config检测glib是否存在如果x86版本的glib干扰可以将/usr/local/lib临时改名

 ./configure --prefix=/home/rongyitong/aarch64 --host=aarch64-linux-gnu GLIB_LIBS=`-lglib-2.0 -L/home/rongyitong/aarch64/lib` GLIB_CFLAGS=`-I /home/rongyitong/aarch64/include/glib-2.0 -I /home/rongyitong/aarch64/lib/glib-2.0/include` GIO_LIBS=`-lgio-2.0 -lgobject-2.0 -lglib-2.0 -L/home/rongyitong/aarch64/lib` GIO_CFLAGS=`-pthread -I/home/rongyitong/aarch64/include/glib-2.0 -I/home/rongyitong/aarch64/lib/glib-2.0/include` LIBS=`-lzlib -L=/home/rongyitong/aarch64/lib`
  • 编译gst-plugin-base库
./configure --prefix=/home/rongyitong/aarch64 --host=aarch64-linux-gnu --disable-ogg --disable-vorbis

这里已经配好了pkg-config路径如果没有配好需要像gstreamer一样配置好环境变量

关于动态链接库(shared object library)的补充知识

​ gstreamer插件库是以动态链接库的形式出现的因此了解动态链接库的知识是非常必要的。

​ 软件库一般分为静态链接库动态链接库其中静态链接库扩展名为.alinux或.libwindows动态链接库扩展名为.soLinux或.dllWindows。

​ 动态链接的基本思想是把程序按照模块拆分成各个相对独立的部分在程序运行时才将它们链接在一起形成一个完整的程序而不是像静态链接一样把所有的程序模块都链接成一个单独的可执行文件。

​ Linux下的动态链接库通常为libxxx.so格式在gcc中链接需要的动态库只需简写为-lxxx。

​ 静态链接库会在链接过程合并至目标程序中而动态链接库只会在目标程序运行时需要这个函数时才会去对应的so文件中寻找该函数因此动态链接库经常出现依赖问题。

​ 动态链接库的依赖问题通常有两种

  1. 找不到对应的.so文件或对应的.so文件格式与目标程序不匹配。此错误通常在链接时就会报错。

  2. 在对应的.so文件中找不到需要的函数。此错误在可以正常链接但是在目标程序执行时才会报错。
    错误类型通常为符号未定义问题例如undefined symbol: pthread_create

    动态和静态链接库中函数和变量统称为符号symbol函数名或变量名称为符号名Symbol Name每个库中都有一个符号表symbol table

    动态链接库的实用工具

    readelf

    常见用法

    readelf -h libxxx.so

    选项 -h(elf header)显示elf文件开始的文件头信息。

    image-20220118141843116

    readelf -s libxxx.so

    选项 -s, 显示符号表段中的项如果Name显示不全可以再加个-W参数表示完整显示

    image-20220118151931033

    image-20220118152014689

    image-20220118151427682

    ​ Ndx表示该符号所在的段UND表示未定义表示在该文件里没有定义该符号只引用了该符号符号定义在其他文件里。

    lddlist dynamic dependencies工具

    ​ 用于查看一个库或可执行文件所依赖的其他库。

    image-20220118153659677

有时候使用ldd工具提示

image-20220118154609043

这种情况可能是由于该文件的架构和执行命令的平台不匹配比如aarch64平台在x86平台运行。解决方法可以使用aarch64版本的ldd工具并移植到aarch64平台进行。

ldd工具本质上只是个shell脚本因此修改代码即可。

1.注意保证#!/bin/bash与平台解释器一致
2.修改变量RTLDLIST值。将其修改为aarch64平台下的链接动态库(ld-linux-xx.so)多数位于/lib目录下;

image-20220118154942345

从模板开始编写一个插件

git clone https://gitlab.freedesktop.org/gstreamer/gst-template.git

进入git目录需要把branch切换到1.18版本master版本的模板有一些问题。利用make_element工具替换名字后

以videocrop插件为例基于实际插件重构

插件继承关系

GObject
    ╰──GInitiallyUnowned
        ╰──GstObject
            ╰──GstElement
                ╰──GstBaseTransform
                    ╰──GstVideoFilter
                        ╰──videocrop

插件重构方式gstvideofilter.c与gstbasetransform.c分别集成至了libgstvideo.so和libgstreamer-1.0.so中如无必要不要修改两个的源码直接链接相应的动态库即可。如果直接修改源码则两个c文件会依赖一系列头文件及其他依赖库其中有些头文件还是在编译gstreamer核心库时产生的这个头文件环境相当复杂因此不建议采用这种方法。

关于图像在内存中的存放方式

​ 当视频图像存储在内存时图像的每一行末尾也许包含一些扩展的内容这些扩展的内容只影响图像如何存储在内存中但是不影响图像如何显示出来

​ Stride 就是这些扩展内容的名称Stride 也被称作 Pitch如果图像的每一行像素末尾拥有扩展内容Stride 的值一定大于图像的宽度值就像下图所示
这里写图片描述

​ 两个缓冲区包含同样大小宽度和高度的视频帧却不一定拥有同样的 Stride 值如果你处理一个视频帧你必须在计算的时候把 Stride 考虑进去

​ 另外一张图像在内存中有两种不同的存储序列arranged对于一个从上而下存储Top-Down 的图像最顶行的像素保存在内存中最开头的部分对于一张从下而上存储Bottom-Up的图像最后一行的像素保存在内存中最开头的部分下面图示展示了这两种情况

这里写图片描述

​ YUV 图像永远都是从上而下表示的RGB 图像保存在系统内存时通常是从下而上

关于YUV的补充知识

YUV(YCrCb)是指将亮度参量Y和色度参量U/V分开表示的像素格式主要用于优化彩色视频信号的传输。

由于我们眼睛的视网膜杆细胞多于视网膜的锥细胞而视网膜的杆细胞是识别亮度的锥细胞是识别色度的所以我们的眼睛对于明暗的分辨要比对颜色的分辨要精细也就是我们眼睛对于亮度的敏感程度要大于色度的敏感程度。那么我们在存储图像信息时为了节约空间就没有必要将所有的色度信息全部存储下来了。

YUV像素格式来源于RGB像素格式通过公式运算YUV 三分量可以还原出 RGB。

YUV的存储格式

YUV格式有两大类planar和packed。

  • 对于planar的YUV格式先连续存储所有像素点的Y紧接着存储所有像素点的U随后是所有像素点的V。
  • 对于packed的YUV格式每个像素点的Y,U,V是连续交叉存储的。

YUV的采样格式

YUV码流的存储格式其实与其采样的方式密切相关主流的采样方式有三种YUV4:4:4YUV4:2:2YUV4:2:0

用三个图来直观地表示采集的方式吧以黑点表示采样该像素点的Y分量以空心圆圈表示采用该像素点的UV分量。

这里写图片描述

  • YUV 4:4:4采样每一个Y对应一组UV分量。
  • YUV 4:2:2采样每两个Y共用一组UV分量。
  • YUV 4:2:0采样每四个Y共用一组UV分量。

存储方式实例

<1>YUV422存储类型

<1-1>YUYV格式采样格式为YUV422存储格式为packed

img

YUYV是YUV422采样的存储格式的一种相邻的两个Y公用其相邻的两个Cb(U)、Cr(V)。对于像素点Y’00、Y’01而言其Cb、Cr的值均为Cb00、Cr00其他的像素点的YUV取值依次类推。

<1-2>UYVY格式采样格式为YUV422存储格式为packed

img

<1-3>YUV422P采样格式为YUV422存储格式为planar

img

​ YUV422P是一种Plane模式即planar模式并不是像上面YUV数据交错存储而是先存储所有的Y分量然后存储所有的UCb分量最后存储所有的VCr分量。其每一个像素点的YUV值提取方法也是遵循YUV422格式的最基本提取方法即两个Y共用一个UV。比如对于像素点Y’00、Y’01而言其Cb、Cr的值均为Cb00、Cr00。

<2>YUV420存储类型

基于 YUV 4:2:0 采样的格式主要有 YUV 420P 和 YUV 420SP 两种类型每个类型又对应其他具体格式。

  • YUV 420P 类型
    • YU12 格式
    • YV12 格式
  • YUV 420SP 类型
    • NV12 格式

    • NV21 格式

YUV 420P 和 YUV 420SP 都是基于 Planar 平面格式 进行存储的先存储所有的 Y 分量后 YUV420P 类型就会先存储所有的 U 分量或者 V 分量而 YUV420SP 则是按照 UV 或者 VU 的交替顺序进行存储了

<2-1>YUV420sp采样格式为YUV420存储格式为planar分Y-planner和UV-planar其中UV平面为packed

img

img

NV21、NV12都属于YUV420格式是一种two-plane模式即Y和UV分为两个Plane但是UVCbCr为交错存储而不是分为三个plane。其提取方式与上面一种类似即Y’00、Y’01、Y’10、Y’11共用Cr00、Cb00。

<2-2>YUV420p采样格式为YUV420存储格式为planar分Y-planner和U-planar和V-planar

img

img

YU12又称I420和YV12属于YUV420格式也是一种Plane模式将Y、U、V分量分别打包依次存储。其没一个像素点的YUV数据提取都遵循YUV420格式的提取方式即4个Y分量共用一组UV。如上图中Y’00、Y’01、Y’10、Y’11共用Cr00、Cb00其他以此类推。

注意YU12与YV12的区别在于是先存U还是先存V。对于YU12来说存储顺序是YUV即YCbCr对于YV12来说存储顺序是YVU即YCrCb。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6