GStreamer Pad and Capabilities Negotiation

gstreamer笔记:capabilities negociation 规格协商 caps协商主要通过问询和事件机制来实现. 定义的衬垫都由自己可以处理的数据类型,规定了当前元素的功能和支持的参数,比如常见的音视频格式、比特率等。在元素连接的过程中,相邻的元素需要协商彼此可以衔接的数据格式,以便将对应的数据传给对方。 最常见的协商是当前的src pad通过GST_QUERY_CAPS询问下游元素sink pad支持的格式,从中选择一个共同支持的格式。 另外,下游也可以在规格需要更改的时候,发送重新配置的事件GST_EVENT_RECONFIGURE,之后会重新走一遍协商流程。 无论怎样, 阅读详情

Pad是什么

  • 首先pad是GStreamer Element必不可少的组成部分,你可以把它看作是element和外界(其它element)交换数据的端口。数据从element的一端流入,另一端留出(一般情形,但是,source element只有出没有进, sink只有进没有出),那么一个element就该有两个pad分别位于element的两端。从pad的角度来讲,根据数据的流入和流出方向,就该有source pad和sink pad 的区分了:

    这里写图片描述

    pad 属性之一: direction(方向)
    数据流入端叫sink pad, 数据流出端叫source pad.

  • 其次, 每个element上有几个pad并不是固定的。

    • 有些pad在element的整个生命周期都存在, 这些pad 称为静态pad,也叫always pad, 顾名思义就是常在的意思。
    • 还有一些pad则是根据需要由element自行创建,比如demuxer element需要根据多媒体文件包含的数据源(Stream)动态的创建新的路径:
      这里写图片描述
    • 还有一类pad是由程序员手动创建的,比如tee element可以根据需要拷贝多份数据到不同的分支, 比如, 在下图中ksvideosrc是一个window下的camera source 插件,假设我们需要一边在桌边上实时播放捕捉画面,另外又需要将捕捉到的视频编码保存到本地,这时候我们就需要用到tee element,在它的输出端手动创建两个source pad,分别用来预览和编码。 假如过一段时间,你又要添加捕捉帧到静态图像的功能, 那么你可能又要再手动创建第三个source pad了:
      这里写图片描述

    pad 属性之二: 根据pad创建的方式可以分为:
    静态pad(always pad )
    动态pad (sometimes pad)
    手动pad (on-request pad)


Capabilities of a pad

pad的能力, 这里的能力就是可以流经这个pad的数据流格式。
我们知道GStreamer pipeline是由一系列互相连接的element组成的数据流通道, 当数据从一个element流向另外一个element时,数据类型必须是双方都能够识别的,从这个角度来讲,数据类型当然是越简单越好。 但显然element的实现者们希望它们的 element 能够尽可能的处理更多的数据类型。 鉴于多媒体数据类型的庞杂, 因此需要一套管理element之间数据类型协商的机制。由于GStreamer element之间的连接是基于pad的, 因此处理数据的能力就定义在pad上,而不是element上。

GstPadTemplate

一个element的实现, 需要注册一个或多个GstPadTemplate,然后才能够通过这些template创建pad。 下面的代码是从GStreamer插件指南里面截取的一段代码,用来实现GstMyFilter element:

//GstPadTemplate for sink pad
static GstStaticPadTemplate sink_factory = 
   GST_STATIC_PAD_TEMPLATE( 
      "sink",        //GstPadTemplate的名字
      GST_PAD_SINK,  //可从该template创建的pad 方向属性 (sink, source) 
      GST_PAD_AWAYS, //可从该template创建的pad 创建方式属性(always, sometimes, on-request)
      GST_STATIC_CAPS(
         "audio/x-raw, "
         "format = (string) " GST_AUDIO_NE(S16) ", "
         "channels = (int) { 1, 2}, "
         "rate = (int) [ 8000, 96000 ]"  ) //可从该template创建的pad支持数据类型列表 
     );

//GstPadTemplate for src pad
static GstStaticPadTemplate src_factory = 
   GST_STATIC_PAD_TEMPLATE(...);

//这里注册GstPadTemplate  
static void gst_my_filter_class_init( GstMyFilterClass* klass)
{
     GstElementClass* element_class = GST_ELEMENT_CLASS(klass);
     ...
    gst_element_class_add_pad_template( element_class, gst_static_pad_template_get(&sink_factory));
    gst_element_class_add_pad_template( element_class, gst_static_pad_template_get(&src_factory));
}

这里可以看出GstMyFilter element注册了两个GstPadTemplate.

每个GstPadTemplate有4项构成,其中我们最关注的当然是最后一项,包含了从该template创建的pad所能够支持的数据类型列表。我们抽取这部分进行分析

 "audio/x-raw, "
 "format = (string) " GST_AUDIO_NE(S16) ", "
 "channels = (int) { 1, 2}, "
 "rate = (int) [ 8000, 96000 ]" 

这是列表里面的一项, 当然列表可能包含不仅仅一项,而有可能包含多项, 每项用一个字符串表示,每项之间用”;“隔开。 每项内部属性与属性之间则用”,“分隔。

  • "audio/x-raw": 支持未经压缩的raw audio输入
  • "format = (string) "GST_AUDIO_NE(S16) ", ": 整形16bit audio数据
  • "channels = (int) { 1, 2}, ": 单声道或者立体声道, {}: 列表
  • "rate = (int) [ 8000, 96000 ]": 支持范围在8000-96000bps之间的传输率, [ ]: 范围

另外创建方式是GST_PAD_AWAYS, 这就说明这两个pad是在GstMyFilter被创建的时候就已经存在并且在GstMyFilter实例的整个生命周期都将存在,因此在GstMyFilter的_init函数中:

static void gst_my_filter_init( GstMyFilter* filter)
{
    filter->sinkpad = gst_pad_new_from_static_template(&sink_factory , "sink");
    gst_pad_set_event_function(filter->sinkpad, gst_my_filter_sink_event);
    gst_pad_set_query_function(filter->sinkpad, gst_my_filter_sink_query);
    gst_element_add_pad( GST_ELEMENT(filter), filter->sinkpad);

    filter->srcpad = gst_pad_new_from_static_template(&src_factory, "src");
    gst_element_add_pad( GST_ELEMENT(filter), filter->srcpad);
}

_init函数相当于GstMyFilter的构造函数,对每个实例,从注册的template创建一个sink pad, 一个src pad.

到此为止, 我们通过 gst_element_class_add_pad_template 注册了GstPadTemplate, 再在构造函数里面通过这些template创建pad的实例。但事情还没完, 我们看到我们注册的template,它们可能支持多种媒体格式,对于每个格式某些属性的取值范围也不是固定的,那当我们将两个pad连接起来的时候,到底怎么确定最终的媒体格式和确定属性的最终值呢? 这就涉及到了GStreamer中另外一个重要概念: 能力协商(Caps Negotiation), 非常直观的表述,目的就是要确定最终的格式和取值。


Caps Negotiation

以上我们通过_init构造函数创建了GstMyFilter的两个pad,当然这只是创建pad的一种方式。给element 创建新的pad不一定要在element实现内部完成,在外面也同样可以为一个已经存在的element实例创建pad, 这里暂不关心pad 是怎么被创建出来的, 我们只要知道我们GStreamer Pipeline中的所有elements都链接好了,接下来该是Caps Negotiation粉墨登场了。

Caps Negotiation定义了一套element之间互相询问,回答与事件处理机制。 这里有两个概念:

  • 查询 Query
  • 事件 Event

查询

GStreamer内部的Query可以有很多种, 这里主要涉及到

  • GST_QUERY_CAPS
  • GST_QUERY_ACCEPT_CAPS

GST_QUERY_CAPS

GST_QUERY_CAPS 用来查询相邻pad所支持的caps:

这里写图片描述

   GstCaps * gst_pad_peer_query_caps (GstPad *pad,GstCaps *filter);

gst_pad_peer_query_caps(srcpad, filter)是srcpad向sinkpad发出的一个GST_QUERY_CAPS查询,旨在获取element2 sinkpad端所支持的所有符合条件的caps集合。 这里符合条件是指能够通过filter 过滤的caps, 但是如果filter = NULL, 则会返回所有支持的caps.

那么element2收到GST_QUERY_CAPS查询是怎么返回caps的呢? 不知道大家有没有注意到上面我们在GstMyFilter _init函数里面有这么一行: 

 gst_pad_set_query_function(filter->sinkpad, gst_my_filter_sink_query);

这里注册一个query回调函数 gst_my_filter_sink_query, element2可以在这个函数里面处理所有对element2的查询:

static gboolean gst_my_filter_sink_query( GstPad *pad, GstObject *parent, GstQuery *query)
{   
    ...
    GstMyFilter* filter = GST_MY_FILTER(parent);

    switch( GST_QUERY_TYPE( query ) ) 
    {
        case GST_QUERY_CAPS:
         //这里是处理的地方 ...
         //并将查询转发给下游 
         GstCaps* filter;
         gst_query_parse_caps( query, &filter);
         gst_pad_peer_query_caps(filter->srcpad, filter);
         break;
        case GST_QUERY_ACCEPT_CAPS:
         //处理GST_QUERY_ACCEPT_CAPS ...
         break;
        default:
         gst_pad_query_default( pad, parent, query );
         break;
    }
    ...
}

处理的时候要注意以下几点:

  1. element2在创建返回caps列表的时候应该考虑相邻元素,最理想的状态是返回的caps能够被pipeline上所有的element支持。
  2. 假设输入的filter不为NULL,则只能返回与filter匹配的caps
  3. 假设输入的filter包含多个caps, 则 caps 的先后次序反应查询者预期的caps 优先级, 在创建返回列表之时, 应将优先级高的caps放在列表前端。

GST_QUERY_ACCEPT_CAPS

看完了GST_QUERY_CAPS 查询, 再来看看GST_QUERY_ACCEPT_CAPS又是怎么回事呢?

这里写图片描述

当element2返回caps列表之后, element1 需要遍历列表并选出最优选线(a fixed caps), 并再次向element2发出查询,以下代码来自element1:

GstCaps* elem1_src_caps = gst_pad_query_caps( srcpad ); //首先获取srcpad自身caps
GstCaps* elem2_sink_candidates = gst_pad_peer_query_caps(srcpad, elem1_src_caps);

foreach( GstCaps* candidate, elem2_sink_candidates )
{ 
    //这里将candidate内部属性取值固定
    GstCaps* fixed_caps = gst_pad_fixate_caps( srcpad, candidate );

   //再次发送GST_QUERY_ACCEPT_CAPS
   if( gst_pad_peer_query_accept_caps(srcpad, fixed_caps) )
   {
       ...
   }

}

element2 对于 GST_QUERY_ACCEPT_CAPS查询的处理可以不必转发给下游,而是根据自身情况决定返回值。因为此时上游发过来的fixed caps已经是从element2返回的caps内部筛选出的结果。


事件

这里也同样涉及两个事件:

  • GST_EVENT_CAPS
  • GST_EVENT_RECONFIGURE

GST_EVENT_CAPS

这是上游element通知下游element,一个fixed caps 已经被选定,各位应该做好相应的准备,等着接受由上游传递的buffer来处理吧 ,buffer的格式则由这个指定的fixed caps限定了。

从element1发送GST_EVENT_CAPS:

   if( gst_pad_peer_query_accept_caps(srcpad, fixed_caps) )
   {
      gst_pad_push_event( srcpad, gst_event_new_caps( fixed_caps ));
   }

element2的处理则放在_init 注册的事件处理函数中:

    gst_pad_set_event_function(filter->sinkpad, gst_my_filter_sink_event);

    static gboolean gst_my_filter_sink_event( GstPad *pad, GstObject *parent, GstEvent *event)
    {
        ...
        GstMyFilter* filter = GST_MY_FILTER(parent);

        switch( GST_EVENT_TYPE(event))
        {
           case GST_EVENT_CAPS:
             //这里做好处理
             //并将事件按转发给下游 
             gst_pad_push_event( filter->srcpad, event );
             break;
           default:
             gst_pad_event_default( pad, parent, event);
             break;
        }
        ...
    }

貌似此时我们的能力协商工作已经完成了, 下图总结了此过程:

这里写图片描述


GST_EVENT_RECONFIGURE

在以上的能力协商中,发起查询,发送事件的都是上游element, 下游element则一直回复和处理这些请求或事件。 最终fixed caps 也是由上游确定的,那么下游element 有没有办法发起一次能力协商或者有自己的意志去选择合适的数据类型呢? 这就是GST_EVENT_RECONFIGURE事件的作用, 它是由下游发送给上游。

一个GST_EVENT_RECONFIGURE事件的发起首先由下游确定一个fixed caps, 然后通过GST_QUERY_ACCEPT_CAPS查询上游是否支持,如果支持的话再发送GST_EVENT_RECONFIGURE事件,上游的处理这是发起一次新的能力协商,在上游查询GstCaps列表时,这次下游这只返回选定的fixed caps.

这里写图片描述

【GStreamer】GstPad的简单分析 GstPad用于在pipeline中连接不同的元素(GstElement),可以将pad理解为一个元素的连接点或者端口,数据通过这些连接点在元素之间流动。pad的核心作用是,数据流动的接口,可以是某一个元素的数据接收端口,也可以是某一个元素的数据发送端口。使用capabilities(Caps)描述pad可以处理的数据类型(如视频、音频、具体的编码格式等),必须确保两个pad的caps可以兼容。 阅读详情

相关推荐

【GStreamer】GstPad详解:SRC、SINK、GstCaps、GstStructure

【GStreamer】GstPad详解:SRC、SINK、GstCaps、GstStructure

郭老二 922

gstreamer query 查询caps流程

4、调用函数内部用switch区分,我们这里调用gst_pad_query_caps_default (pad, query)接下来看一个插件gstvideodecoder.c中设置pad的query的函数,一个是sink pad,一个是src pad。6、先判断当前pad的caps是不是fixed_caps,如果是用下面方式获取caps。7、如果在有模板的情况下,先获取caps template模板,并从模板中获取caps。5、解析query`,这里是为了获取到caps中的filter。

weixin_43360707的博客 978

用MCP扩展Cursor的5种神奇用法:从文件操作到网页抓取实战

本文深入探讨了如何利用MCP(Model Context Protocol)扩展Cursor AI编程助手,解锁五种高效实用场景。从文件系统操作、网页内容抓取,到数据库查询、Figma设计稿对接,乃至构建自定义MCP服务器,文章通过实战案例展示了如何将Cursor从代码编辑器升级为能理解并操作工作环境的智能助手,显著提升开发效率与自动化水平。

weixin_29231027的博客 315

GStreamer学习七(GstPad)

一、GstPad简介 通过GstPad轻量级的连接,从而使元素间能互相通信。GstPad有个类型GstPadDirection 区分,GST_PAD_SRC 表示源类型生产数据,GST_PAD_SINK表示下沉类型消费数据。 1、使用步骤: 1.1、GstPad创建 典型的创建方式是通过GstPadTemplate的gst_pad_new_from_template()创建的,然后添加到GstElement中(这通常在创...

cai742925624的专栏 2807

【Unity3D优化】优化内置shader的内存占用

在打AB包的过程中,并不会把这些Built-in的shader单独打包,并与材质球和预设体做好依赖,因此打AB包时,Unity会自行在材质球和预制体的AB包内每一个都包含进去用到的Built-in的Shader,而且运行时,由于加载的是不同的AB包,shader也是不同的实例,占用的也是多份内存。结合项目特点可以分析,由于项目里使用了大量的粒子特效,一些特殊的效果,会给特效师写定制的shader,但是这些常见的效果,特效师直接使用了Unity Built-in的shader。继续优化内存占用。

a310989583的专栏 2311

运行 ui5 serve 命令行报错的处理方法

如果大家运行 ui5 serve 命令行,报如下的错误消息: ui5 不是内部或外部命令,也不是可运行的程序: 只需要执行命令行,全局安装 ui5 cli(command line interface)这个命令行工具即可: npm install --global @ui5/cli 这个 ui5 cli 的入口执行文件,实际安装在了我 Node.js 的安装目录下: 更多Jerry的原创文章,尽在:“汪子熙”: ...

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1340

Gstreamer 内存分配协商机制

在两个衬垫的caps协商完成之后,元件之间需要确认如何分配buffer。本文梳理GStremer内存协商机制,比如当某元件不能自己分配内存时,如何使用其他元件的分配器。

Aero's WorkSpace. 2070

PTC Creo软件二次开发:使用C++进行Creo二次开发实战

C++是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。由Bjarne Stroustrup在贝尔实验室开发,最初作为C语言的增强版出现,后来发展成为一种独立的语言。C++不仅保留了C语言的高效性和灵活性,还引入了类、模板、命名空间等高级特性,使其成为开发复杂系统和软件的理想选择。

kkchenjj的博客 4038

三、GstPad与GstCap

1 简介   两个元素直接连接是通过Pad,数据流从一个元件的src到另一元件的sink。 2 可用性Availability 2.1 永久性 Always   一直存在。 2.2 请求型 request   需要用户请求去创建pad。 pad = gst_element_get_request_pad (tee, "src%d"); name = gst_pad_get_name (pad); g_print ("A new pad %s was created\n", name); g_

Creationyang的博客 1176

【AI大模型应用开发】【LangChain系列】实战案例6:利用大模型进行文本总结的方法探索,文本Token超限怎么办?

假设有一组文档(PDF、Notion页面、客户问题等),你想要总结内容。可以利用大模型来帮你。今天来系统看下利用大模型来对文本进行总结的方法,以LangChain的使用为例。

同学小张的博客 3749

51单片机+L298驱动步进电机+L297测速+LCD1602显示+按键调试+Proteus仿真

51单片机+L298驱动步进电机+L297测速+LCD1602显示+按键调试+Proteus仿真 *Proteus仿真 L298N是双H桥式驱动器。 L297是步进电机控制集成芯片(包括环形分配器),可产生四相驱动信号,应用于微处理机控制两相双极性和四相单极性步进电机。电动机可由半阶梯、正常和斩波驱动模式驱动,同时设于晶片内的PWM斩波线路容许以开关形式控制线路的电流。此器件只需要时钟、方向和模式输入信号。相位是由内部产生的,因此可减轻微处理机和程序设计的负担。 实例代码 /************

perseverance51博客 4584

Gstreamer学习笔记(5):GStreamer Pad and Capabilities Negotiation

转载自:https://blog.youkuaiyun.com/techx/article/details/44134911 Pad是什么 首先pad是GStreamer Element必不可少的组成部分,你可以把它看作是element和外界(其它element)交换数据的端口。数据从element的一端流入,另一端流出(一般情形,但是,source element只有出没有进, sink只有进没有...

houxiaoni01的博客 1818

[内网基础] 内网基础知识 —— Windows 工作组

在一个大型单位里,可能有成百上千台计算机互相连接组成局域网,如果不对这些计算机进行分组,网络的混乱程度是可想而知的。Windows 为了解决这一问题,就产生了工作组(Work Group)这个概念。Windows 工作组尝试将不通的计算机按功能(或部门)分别列入不同的工作组,例如技术部的计算机都列入 “技术部” 工作组、行政部的计算机都列入 “行政部” 工作组。要想访问某个部门的资源,只要在 “网络” 里双击该部门的工作组名,就可以看到该部门里的所有计算机了。

Blue17 の 小窝 1602

GStreamer编程实例6: Media formats and Pad Capabilities

Pads如之前介绍的一般,Pads允许信息进出elements。Pad的Capabilities(简称为Caps)指定了Pad能够传递什么类型的信息。例如,“320x200分辨率,30FPS的RGB视频”,或是“16位音频样本,5.1通道,采样率44100Hz”,或者是mp3和h264这类的压缩格式。

浩瀚之水的专栏 313

UI自动化控制PC端微信获取当前群聊的群友列表

在一个月前,某个群友在获取整个群的群友信息时遇到了一点问题: 对整个群进行群友数据获取经测试确实有点棘手,下面我将我的解决过程公布给大家。 基础教程详见: https://blog.youkuaiyun.com/as604049322/article/details/121391639 人工打开要抓取的群聊窗口后,首先获取微信窗口并点击聊天信息按钮: import uiautomation as auto auto.uiautomation.SetGlobalSearchTimeout(1) wechatWind

小小明-代码实体的专栏 3万+

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

GStreamer基础教程06——媒体格式和pad的Capabilities

原文:https://gstreamer.freedesktop.org/documentation/tutorials/basic/index.html 译文原文:http://blog.youkuaiyun.com/sakulafly/article/details/21299519 原码:git clone git://anongit.freedesktop.org/gstreamer/gst-do

hwmstudent的博客 679

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

[GStreamer] 插件编写

Tips: A plugin registry is used to store the details of the plugins in a binary registry file. This way, a program using GStreamer does not have to load all plugins to determine which are needed. Plugins are only loaded when their provided elements are..

ykun089的博客 1437

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Gstreamer插件教程3.2—高级概念(Advanced Concepts):Cap协商(Caps negotiation)

cap协商是找到一种两个element都能够处理的媒体格式(即GstCaps)的一种行为。Gstreamer中的这种处理,对于整个pipeline,大多数情况下都可以找到最优的解决方案。在这个章节,我们解释了它是怎样工作的。 Caps negotiation basics 在Gstreamer中,媒体格式的协商总是遵循如下的简单规则: 一个下游的element在它的sinkpad上建

hwmstudent的博客 1799

Gstreamer之Caps协商

GStreamer一个多媒体框架,用来做多媒体播放器以及一些与之相关的应用都是一个不错的选择,当然这里指的多是Linux平台。如果你的平台是windows那不防可以考虑一下DirectShow等其它一些框架。 好吧,关于框架我先说到这里,先点到为止吧。如果后期时间允许我会单独的说说这些东西的。 开始说说gstreamer的caps协商过程吧。 说到gstreamer其实主要有

天好蓝 8971

GStreamer学习4----写一个插件

就比如最近欧洲杯里讨论的比较火的广告内容区域化转换处理,就可以在gst_my_filter_chain里进行。注意,这样生成的自定义的element的名字使用起来应该是my_filter,比如数据格式是RGB,获取到长宽后,可以进行颜色转换,画面镜像处理等等,例子的的方法来获取buffer中的内存数据,来进行操作,使用里面的工具自动生成一个插件程序,比如MyFilter。在gst_my_filter_chain方法中,画一下图,就比较容易理解生成的代码结构。element的定义,init。

aaajj的专栏 959

gstreamer协商

实际的func是gst_base_transform_default_query->gst_base_transform_query_caps, 然后又调用gst_pad_peer_query(casfilter的src_pad),参考上面的分析,又调到gst_pad_peer_query(videobox的src_pad), 最终调用的是gst_base_sink_sink_query(fakesink的sink);以一个例子来看gstreamer的协商。做做笔记,仅供参考。

evsqiezi 477

Gstreamer- 协商(Negotiation)

协商 Capabilities 协商是为 GStreamer pipeline内的数据流决定适当格式的过程。理想情况下,协商(也称为“capsnego”)将信息从pipeline中具有信息的那些部分传输到pipeline的那些易扩展的部分,受pipeline中不易扩展部分的约束。 基本规则 必须遵循这些简单的规则: 下游建议格式 上游决定格式 caps协商中使用了 4 种 查询/事件(queries/events ): GST_QUERY_CAPS:获取可能的格式 GST_QUERY_ACCEPT_C

zk5950886的博客 1736

gstreamer学习笔记---pad定义、连接、流动

  pad相当于element的接口,各个element就是通过pad连接进行传输数据,同时pad会通过caps限制特定的数据类型通过,只有当两个pad的caps数据类型一致时才可以建立连接。那么pad在element又是怎么创建以及使用的呢,下面一起来分析一下。 一、pad定义   在理解pad的定义之前,我们先来看看,pad都有那些信息。 Pad Templates: SINK ...

chengwei_peng 1万+

pad连接与数据流动

gst_pad_push_data函数分析 gst_pad_push_data{ gst_pad_chain_data_unchecke(GstPad * pad, gboolean is_buffer, void *data, GstPadPushCache * cache){ caps = gst_pad_data_get_caps (is_buffer, da...

evsqiezi 2445
上一篇: 直接Stream Video到GPU Texture 内存
下一篇: 命令行编译运行Java程序
小马识图
博客等级 码龄18年 8粉丝 30原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值