草庐IT

Gstreamer基础讲解

YRong_ 2023-06-03 原文

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)分离
所有的媒体处理功能都是由插件从外部提供给内核的,并告诉内核如何去处理特定的媒体类型。

元件(Element)

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

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

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

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

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

源元件(source element)

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

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

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

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

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

接收元件(sink element)

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

将元件链接(link)起来

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

衬垫(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。

  • padname:pad名称

  • direction:pad的输入输出方向,有src和sink两种

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

  • caps:pad支持的功能

参考资料:
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前后的两部分被分成两个线程执行。

实用工具

​ 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运行时的调试等级,不同等级会打印出不同等级的信息。

总共有六个等级[0,5]
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有一些常用的用于搭建流水线的插件,这里做一个介绍。

v4l2src(Video for Linux 2 source)

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

v4l2src属性里,常用的有

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

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

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

由gst-inspect-1.0 v4l2src可发现

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:测试视频显示图案

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

其中ximagesink是指窗口显示

fakesrc 与fakesink

gstreamer自带的万能src和sink

videocrop

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

常用属性:

videoscale

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

常用属性:

method:插值方法

n-threads:线程数

videoconvert

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

kmssink

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

常用属性:

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

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

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

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

​ 如果想准备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"

如果遇到

这是编译器版本问题,在 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插件库是以动态链接库的形式出现的,因此了解动态链接库的知识是非常必要的。

​ 软件库一般分为静态链接库动态链接库,其中静态链接库扩展名为.a(linux)或.lib(windows),动态链接库扩展名为.so(Linux)或.dll(Windows)。

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

​ 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文件开始的文件头信息。

    readelf -s libxxx.so

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

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

    ldd(list dynamic dependencies)工具

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

有时候使用ldd工具,提示

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

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

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

从模板开始编写一个插件

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:4,YUV4:2:2,YUV4: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)

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

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

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

​ YUV422P是一种Plane模式,即planar模式,并不是像上面YUV数据交错存储,而是先存储所有的Y分量,然后存储所有的U(Cb)分量,最后存储所有的V(Cr)分量。其每一个像素点的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)

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

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

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。

有关Gstreamer基础讲解的更多相关文章

  1. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  2. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  3. 软件测试基础 - 2

    Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功

  4. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  5. 【网络】-- 网络基础 - 2

    (本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展        是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。    如:有三个人,每个人做的不同的事物,但是是需要协作的完成。    而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据

  6. 100个python算法超详细讲解:画直线 - 2

    1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva

  7. 【Elasticsearch基础】Elasticsearch索引、文档以及映射操作详解 - 2

    文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就

  8. c++基础-运算符 - 2

    目录1关系运算符2运算符优先级3关系表达式的书写代码实例:下面是面试中可能遇到的问题:1关系运算符C++中有6个关系运算符,用于比较两个值的大小关系,它们分别是:运算符描述==等于!=不等于小于>大于小于等于>=大于等于这些运算符返回一个布尔值,即true或false。例如,当x等于y时,x==y的结果为true,否则结果为false。2运算符优先级在C++中,关系运算符的优先级高于赋值运算符,但低于算术运算符。以下是关系运算符的优先级,从高到低排列:运算符描述>,,>=,关系运算符==,!=相等性运算符&&逻辑与`如果在表达式中有多个运算符,则按照优先级顺序依次进行运算。3关系表达式的书写在

  9. 计算机必读基础书籍 - 2

    一.计算机组成原理    这本书利用组合逻辑、同步时序逻辑电路设计的相关知识,从逻辑门开始逐步构建运算器、存储器、数据通路和控制器,最终集成为完整的CU原型系统,使读者从设计者的角度理解计算机部件构成及运行的基本原理,掌握软硬件协同的概念。    全书共9章,主要内容包括计算机系统概述、数据信息的表示、运算方法与运算器、存储系统、指令系统、中央处理器、指令流水线、总线系统、输入输出系统。1.计算机系统概述1.1计算机发展历程    计算机是一种能够按照事先存储的程序,自动、高速、准确地对相关信息进行处理的电子设备。1946年2月,世界上第一台电子数字计算机ENIAC(ElectronicNum

  10. 0基础学习软件测试有哪些建议 - 2

    其实现在基础的资料和视频到处都是,就是看你有没有认真的去找学习资源了,去哪里学习都是要看你个人靠谱不靠谱,再好的教程和老师,你自己学习不进去也是白搭在正式选择之前,大可以在各种学习网站里面找找学习资源先自己学习一下为什么选择学软件测试?同学们理由众多!大概分这几类:①不受开发语言、行业产品变化限制;②入门更简单,对零基础、女生都友好;③软件项目都需要测试人员,职业生涯稳;④学习周期短,但薪资并不低。要想“肩扛”一条线?需掌握三大技能:技能1:掌握测试流程,熟悉系统框架能提前与开发人员一起制定测试计划,通过测试左移,推动代码评审,代码审计,单元测试,自动化冒烟测试,来保证研发阶段的质量。技能2:

随机推荐