OpenMax概述

一、OpenMax简介

OpenMAX是一个多媒体应用程序的标准。由NVIDIA公司和Khronos™在2006年推出。

它是无授权费的、跨平台的C语言程序接口序列,这些接口对音频、视频、静态图片的常用操作进行封装。

它包括三层,分别是应用层(AI)、集成层(IL)和开发层(DL)。其中IL层已经成为了事实上的多媒体框架标准。嵌入式处理器或者多媒体编解码模块的硬件生产者,通常提供标准的OpenMax IL层的软件接口,这样软件的开发者就可以基于这个层次的标准化接口进行多媒体程序的开发。

二、OpenMAX/IL: OMX IL - 结构框架

OpenMAX IL 层 API 旨在为媒体组件提供跨平台的可移植能力。这些接口将系统的软硬件结构进行抽象化。每个组件及其相关的转换都被封装在组件接口的内部。OpenMAX IL API 允许用户去加载,控制,连接以及卸载各独立的组件。这种极具灵活性的内核结构使得 Intergration Layer 能够很容易的实现几乎所有的多媒体应用情形,并且能够很好的与现有的基于图像的多媒体框架相结合。

1.  主要的功能和优点

OpenMAX IL API 能够在应用程序、多媒体框架和编解码库,以及其支持的组件(比如,sources 和 sinks)之间建立统一的接口。对于用户来说,组件自身及其内部的软硬件结合情况都是完全透明的。其主要功能如下:

•  A flexible component-based API core;

•  Ability to easily plug in new components ;

•  Coverage of targeted domains (audio, video, and imaging) while remaining easily extensible by both the Khronos Group and individual vendors;

•  Capable of being implemented as either static or dynamic libraries;

•  Retention of key features and configuration options needed by parent software (such as media frameworks);

•  Ease of communication between the client and the components and between components themselves;

•  Standardized definition of key components so all implementations of such “standard components” expose the same external interface (i.e. same inputs, outputs, and controls).

2.  OpenMAX IL 软件结构

OpenMax概述

OpenMax的主要概念

客户端(Client):访问IL core或IL component的软件层,可能是位于GUI应用程序的下层,如GStreamer。IL client是一个典型的功能块,如filter graph multimedia framework, OpenMAX AL, 或application都可以调用它。IL client与OpenMAX IL core进行交互,利用IL core加载和卸载组件、在组件间建立直接通信以及获得组件方法的入口。

core:相关平台的代码,具有将IL component载入主存储器的功能,当应用程序不再需要某组件时,IL core将负责把该组件从存储器卸去。一般来说,组件一旦载入存储器,IL core将不在参与应用程序与组件之间的通信。

端口(Port):组件的输入输出接口

组件(Component):OpenMax IL的单元,每一个组件实现一种功能。组件按照端口可分类为Source(只有一个输出端口)、Sink(只有一个输入端口)和Host组件(一个输入端口和一个输出端口),此外有一个Accelerator组件,它具有一个输入端口,调用了硬件的编解码器,加速主要体现在这个环节上。

隧道化(Tunneled):让两个组件直接连接的方式。通过隧道化可以将不同的组件的一个输入端口和一个输出端口连接到一起,在这种情况下,两个组件的处理过程合并,共同处理。尤其对于单输入和单输出的组件,两个组件将作为类似一个使用。

3.  OpenMAX IL 接口

core API:负责动态加载和卸载组件,协助组件间通信。一旦加载组件,API 允许用户直接与组件进行通信,类似的,core API 允许用户在组件之间建立tunnel通信通道,一旦建立,core API 不再被需要,其通信直接发生在两个组件之间。

component API:在IL层,组件代表了独立的功能模块。一个组件可以是sources, sinks, codecs, filters, splitters, mixers, or any other data operator.各个组件的参数可以通过一组相关的数据结构,枚举类型和接口来设置或者获取。buffer状态,错误信息,以及其他的时间敏感信息会通过回调函数转发给应用程序。与组件进行数据的交换是通过端口(ports)完成的。类似哦,组件间的tunnel通道也是通过将一个组件的输出端口连接到另一个组件的输入端口来建立的。

4.  System 组件

OpenMAX IL 定义了三种通信方式:

1)Non-tunneled:用于client 与 component 之间交换data buffers;

2)Tunneling:用于组件之间互相交换data buffers的标准机制;

3)Proprietary communication:用于两个组件之间直接数据交换的专属机制,并且可以作为tunneling的备选机制。

OpenMax概述

openMAX IL的客户端,通过调用四个OpenMAX IL组件,实现了一个功能。四个组件分别是Source组件、Host组件、Accelerator组件和Sink组件。

·         Source组件只有一个输出端口;

·         Host组件有一个输入端口和一个输出端口;

·         Accelerator组件具有一个输入端口,调用了硬件的编解码器,加速主要体现在这个环节上。Accelerator组件和Sink组件通过私有通讯方式在内部进行连接,没有经过明确的组件端口。

OpenMAL IL在使用的时候,其数据流也有不同的处理方式:既可以经由客户端,也可以不经由客户端。 图中,Source组件到Host组件的数据流就是经过客户端的; 而Host组件到Accelerator组件的数据流就没有经过客户端,使用了隧道化的方式; Accelerator组件和Sink组件甚至可以使用私有的通讯方式。

OpenMAL IL的组件是OpenMax IL实现的核心内容,一个组件以输入、输出端口为接口,端口可以被连接到另一个组件上。外部对组件可以发送命令,还进行设置/获取参数、配置等内容。组件的端口可以包含缓冲区(Buffer)的队列。 组件的处理的核心内容是通过输入端口消耗Buffer,通过输出端口填充Buffer,由此多组件相联接可以构成流式的处理。

5. Component 架构

OpenMAL IL中一个组件的结构如图

组件的功能和其定义的端口类型密切相关,通常情况下:

只有一个输出端口的,为Source组件; 只有一个输入端口的,为Sink组件; 有多个输入端口,一个输出端口的为Mux组件; 有一个输入端口,多个输出端口的为DeMux组件; 输入输出端口各一个组件的为中间处理环节,这是最常见的组件。

端口具体支持的数据也有不同的类型。例如,对于一个输入、输出端口各一个组件,其输入端口使用MP3格式的数据,输出端口使用PCM格式的数据,那么这个组件就是一个MP3解码组件。

隧道化(Tunneled)是一个关于组件连接方式的概念。通过隧道化可以将不同的组件的一个输入端口和一个输出端口连接到一起,在这种情况下,两个组件的处理过程合并,共同处理。尤其对于单输入和单输出的组件,两个组件将作为类似一个使用。

OpenMax概述

7.  Clientcomponent之间的通信

client通过OMX_EmptyThisBuffer来调用component输入端口

client通过OMX_FillThisBuffer来调用component输出端口

8.  Tunneled buffer allocation

对于tunnel的两个端口,supplier端口会调用UseBuffer函数来要求邻接的端口来处理buffers;non-supplier端口会接受UseBuffer调用。Component需要遵循以下规则:

1)supplier端口都要提供buffers;

2)在端口上可靠的传输buffer配置;

3)通过OMX_EmptyThisBuffer调用将buffer从输出端口传递到另一component的输入端口;

4)通过OMX_Fill_This_Buffer调用将buffer从输入端口返回给component的输出端口。

9. Buffer payload

一般情况下,buffer中可用数据的起始点和范围由定义在buffer头中的 pBuffer,nOffset 和 nFilledLen 三个参数来决定。pBuffer指向buffer的起始地址;nOffset代表了buffer起始地址与实际可用数据地址之间的偏移量;nFilledLen表示buffer中连续可用的数据的大小。因此,buffer中可用数据的起始范围分别为pBuffer + nOffset 和 pBuffer + nOffset + nFilledLen 。

在buffer中数据的存放方式有三种

1)每个buffer要么填满,要么部分填满;

2)每个buffer中存放的压缩数据都是以完整的帧为单位的;

3)每个buffer中只存放一帧的压缩数据。

前两种都要求解码器在解码的之前对每帧数据进行解析,第三种情况则不需要解析。

10. Buffer flags and timestamps

Buffer flags 是存放在buffer中的表示特定属性的数据,比如数据流结束;

Timestamps 是以微秒为单位的存放在buffer中的数据,用来在播放时确定各buffer的播放时刻。

三、接口与头文件

 1.  OpenMAX IL 层的接口定义是由若干个头文件的形式给出的,在头文件中定义了一些结构体和需要开发者实现的接口函数,包括:

·         OMX_Types.h:OpenMax Il的数据类型定义

·         OMX_Core.h:OpenMax IL核心的API

·         OMX_Component.h:OpenMax IL 组件相关的 API

·         OMX_Audio.h:音频相关的常量和数据结构

·         OMX_IVCommon.h:图像和视频公共的常量和数据结构

·         OMX_Image.h:图像相关的常量和数据结构

·         OMX_Video.h:视频相关的常量和数据结构

·         OMX_Other.h:其他数据结构(包括A/V 同步)

·         OMX_Index.h:OpenMax IL定义的数据结构索引

·         OMX_ContentPipe.h:内容的管道定义

提示:OpenMax标准只有头文件,没有标准的库,设置没有定义函数接口。对于实现者,需要实现的主要是包含函数指针的结构体

2.  OMX_Core.h中定义了CoreAPI函数,应用程序通过它可以进行初始化、处理handle等操作,具体内容如下:

OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void);         // 初始化OMX Core,且应该是OMX中第一个被调用的函数;

OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void);        // 反初始化OMX Core,且应该是OMX中最后一个被;

OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum(       // 列出系统中所有可用component的名称;

OMX_OUT OMX_STRING cComponentName,

OMX_IN  OMX_U32 nNameLength,

OMX_IN  OMX_U32 nIndex);

OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle(    // 根据名称查找component,并调用component的方法来实例化component;

OMX_OUT OMX_HANDLETYPE* pHandle,

OMX_IN  OMX_STRING cComponentName,

OMX_IN  OMX_PTR pAppData,

OMX_IN  OMX_CALLBACKTYPE* pCallBacks);

OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle(

OMX_IN  OMX_HANDLETYPE hComponent);

OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel(         // 在两个component之间建立tunnel连接

OMX_IN  OMX_HANDLETYPE hOutput,

OMX_IN  OMX_U32 nPortOutput,

OMX_IN  OMX_HANDLETYPE hInput,

OMX_IN  OMX_U32 nPortInput);

OMX_API OMX_ERRORTYPE   OMX_GetContentPipe(

OMX_OUT OMX_HANDLETYPE *hPipe,

OMX_IN OMX_STRING szURI);

OMX_API OMX_ERRORTYPE OMX_GetComponentsOfRole (

OMX_IN      OMX_STRING role,

OMX_INOUT   OMX_U32 *pNumComps,

OMX_INOUT   OMX_U8  **compNames);

OMX_API OMX_ERRORTYPE OMX_GetRolesOfComponent ( 

OMX_IN      OMX_STRING compName,

OMX_INOUT   OMX_U32 *pNumRoles,

OMX_OUT     OMX_U8 **roles);

当应用程序需要使用某个component的功能时,其首先需要调用OMX_Init()来对OMX Core进行初始化,然后通过OMX_GetHandle()来实例化component,取得相应的handle。handle实际上是一个指向component对象的void类型指针,其在OMX_Type.h中定义如下,

 typedef void* OMX_HANDLETYPE;

OMX_SetupTunnel()用来在两个component之间建立tunnel连接。

3.  OMX_Core.h中还定义了数据类型OMX_BUFFERHEADERTYPE,其对象存放在buffer内用来描述该buffer的特性。

具体内容及各字段注释如下:

typedef struct OMX_BUFFERHEADERTYPE

{

OMX_U32 nSize;              /**< size of the structure in bytes */

OMX_VERSIONTYPE nVersion;   /**< OMX specification version information */

OMX_U8* pBuffer;            /**< Pointer to actual block of memory  that is acting as the buffer */

OMX_U32 nAllocLen;          /**< size of the buffer allocated, in bytes */

OMX_U32 nFilledLen;         /**< number of bytes currently in the  buffer */

OMX_U32 nOffset;             /**< start offset of valid data in bytes from the start of the buffer */

OMX_PTR pAppPrivate;        /**< pointer to any data the application  wants to associate with this buffer */

OMX_PTR pPlatformPrivate;   /**< pointer to any data the platform wants to associate with this buffer */

OMX_PTR pInputPortPrivate;  /**< pointer to any data the input port  wants to associate with this buffer */

OMX_PTR pOutputPortPrivate; /**< pointer to any data the output port  wants to associate with this buffer */

OMX_HANDLETYPE hMarkTargetComponent; /**< The component that will generate a  mark event upon processing this buffer. */

OMX_PTR pMarkData;          /**< Application specific data associated with

the mark sent on a mark event to disambiguate   this mark from others. */

OMX_U32 nTickCount;         /**< Optional entry that the component and

application can update with a tick count

when they access the component.  This

value should be in microseconds.  Since

this is a value relative to an arbitrary

starting point, this value cannot be used

to determine absolute time.  This is an

optional entry and not all components

will update it.*/

OMX_TICKS nTimeStamp;          /**< Timestamp corresponding to the sample

starting at the first logical sample

boundary in the buffer. Timestamps of

successive samples within the buffer may

be inferred by adding the duration of the

of the preceding buffer to the timestamp

of the preceding buffer.*/

OMX_U32     nFlags;           /**< buffer specific flags */

OMX_U32 nOutputPortIndex;     /**< The index of the output port (if any) using

this buffer */

OMX_U32 nInputPortIndex;      /**< The index of the input port (if any) using

this buffer */

} OMX_BUFFERHEADERTYPE;

4.  OMX_Component.h

1OMX_COMPONENTTYPE类型数据结构OMX IL 用它来描述一个component,其中包含了可供调用的函数方法。

[cpp] view plain copy

typedef struct OMX_COMPONENTTYPE

{

OMX_U32 nSize;

OMX_VERSIONTYPE nVersion;

OMX_PTR pComponentPrivate;

OMX_PTR pApplicationPrivate;

OMX_ERRORTYPE (*GetComponentVersion)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_OUT OMX_STRING pComponentName,

OMX_OUT OMX_VERSIONTYPE* pComponentVersion,

OMX_OUT OMX_VERSIONTYPE* pSpecVersion,

OMX_OUT OMX_UUIDTYPE* pComponentUUID);

OMX_ERRORTYPE (*SendCommand)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_COMMANDTYPE Cmd,

OMX_IN  OMX_U32 nParam1,

OMX_IN  OMX_PTR pCmdData);

OMX_ERRORTYPE (*GetParameter)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_INDEXTYPE nParamIndex,

OMX_INOUT OMX_PTR pComponentParameterStructure);

OMX_ERRORTYPE (*SetParameter)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_INDEXTYPE nIndex,

OMX_IN  OMX_PTR pComponentParameterStructure);

OMX_ERRORTYPE (*GetConfig)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_INDEXTYPE nIndex,

OMX_INOUT OMX_PTR pComponentConfigStructure);

OMX_ERRORTYPE (*SetConfig)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_INDEXTYPE nIndex,

OMX_IN  OMX_PTR pComponentConfigStructure);

OMX_ERRORTYPE (*GetExtensionIndex)(网址:yii666.com

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_STRING cParameterName,

OMX_OUT OMX_INDEXTYPE* pIndexType);

OMX_ERRORTYPE (*GetState)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_OUT OMX_STATETYPE* pState);

OMX_ERRORTYPE (*ComponentTunnelRequest)(

OMX_IN  OMX_HANDLETYPE hComp,

OMX_IN  OMX_U32 nPort,

OMX_IN  OMX_HANDLETYPE hTunneledComp,

OMX_IN  OMX_U32 nTunneledPort,

OMX_INOUT  OMX_TUNNELSETUPTYPE* pTunnelSetup);

OMX_ERRORTYPE (*UseBuffer)(文章来源地址:https://www.yii666.com/article/756013.html

OMX_IN OMX_HANDLETYPE hComponent,文章地址https://www.yii666.com/article/756013.html

OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,

OMX_IN OMX_U32 nPortIndex,

OMX_IN OMX_PTR pAppPrivate,

OMX_IN OMX_U32 nSizeBytes,

OMX_IN OMX_U8* pBuffer);

OMX_ERRORTYPE (*AllocateBuffer)(

OMX_IN OMX_HANDLETYPE hComponent,

OMX_INOUT OMX_BUFFERHEADERTYPE** ppBuffer,

OMX_IN OMX_U32 nPortIndex,

OMX_IN OMX_PTR pAppPrivate,

OMX_IN OMX_U32 nSizeBytes);

OMX_ERRORTYPE (*FreeBuffer)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_U32 nPortIndex,

OMX_IN  OMX_BUFFERHEADERTYPE* pBuffer);

OMX_ERRORTYPE (*FillThisBuffer)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_BUFFERHEADERTYPE* pBuffer);

OMX_ERRORTYPE (*SetCallbacks)(

OMX_IN  OMX_HANDLETYPE hComponent,

OMX_IN  OMX_CALLBACKTYPE* pCallbacks,

OMX_IN  OMX_PTR pAppData);

OMX_ERRORTYPE (*ComponentDeInit)(

OMX_IN  OMX_HANDLETYPE hComponent);

OMX_ERRORTYPE (*UseEGLImage)(

OMX_IN OMX_HANDLETYPE hComponent,

OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr,

OMX_IN OMX_U32 nPortIndex,

OMX_IN OMX_PTR pAppPrivate,

OMX_IN void* eglImage);

OMX_ERRORTYPE (*ComponentRoleEnum)(

OMX_IN OMX_HANDLETYPE hComponent,

OMX_OUT OMX_U8 *cRole,

OMX_IN OMX_U32 nIndex);

} OMX_COMPONENTTYPE;

EmptyThisBuffer和FillThisBuffer是驱动组件运行的基本的机制,前者表示让组件消耗缓冲区,表示对应组件输入的内容;后者表示让组件填充缓冲区,表示对应组件输出的内容。

UseBuffer,AllocateBuffer,FreeBuffer为和端口相关的缓冲区管理函数,对于组件的端口有些可以自己分配缓冲区,有些可以使用外部的缓冲区,因此有不同的接口对其进行操作。

SendCommand表示向组件发送控制类的命令。

GetParameter,SetParameter,GetConfig,SetConfig几个接口用于辅助的参数和配置的设置和获取。

ComponentTunnelRequest用于组件之间的隧道化连接,其中需要制定两个组件及其相连的端口。

ComponentDeInit用于组件的反初始化。

提示:OpenMax函数的参数中,经常包含OMX_IN和OMX_OUT等宏,它们的实际内容为空,只是为了标记参数的方向是输入还是输出。

四、Openmax 常用函数介绍

1OMXConfigParser ()The configuration parser API

函数原型

OMX_BOOL OMXConfigParser ( OMX_PTR aInputParameters,OMX_PTR aOutputParameters);

传递参数

aInputParameters 指向如下结构

typedef struct

{

OMX_U8* inPtr;    //codec 配置头部指针文章来源地址https://www.yii666.com/article/756013.html

OMX_U32 inBytes;   //codec 配置头部长度

OMX_STRING cComponentRole; //OMX codec类型 eg "video_decoder.mpeg4"

OMX_STRING cComponentName; //OMX 组件名称

} OMXConfigParserInputs;

返回值

OMX_FALSE : 处理codec配置头部错误或不支持该格式

OMX_TURE : 正确处理codec配置头部

函数作用

填充aOutputParameters,有两种选择:audio coded or vedio codec

typedef struct

{

OMX_U16 Channels;   //通道:单声道、立体声、5.1

OMX_U16 BitsPerSample;   //位宽(eg16)

OMX_U32 SamplesPerSec;   //采样率

} AudioOMXConfigParserOutputs;

typedef struct

{

OMX_U32 width;    //检测到的视频剪辑宽度

OMX_U32 height;    //检测到的视频剪辑高度

OMX_U32 profile;   //参数

OMX_U32 level;    //级别?

} VideoOMXConfigParserOutputs;

2OMX_SetParameter

设定某个参数对应的值

#define OMX_SetParameter(                                   /

hComponent,                                         /

nParamIndex,                                        /

pComponentParameterStructure)                        /

((OMX_COMPONENTTYPE*)hComponent)->SetParameter(         /

hComponent,                                         /

nParamIndex,                                        /

pComponentParameterStructure)    /* Macro End */

3OMX_SetConfig

设定某个config值

#define OMX_SetConfig(                                      /

hComponent,                                         /

nConfigIndex,                                       /

pComponentConfigStructure)                           /

((OMX_COMPONENTTYPE*)hComponent)->SetConfig(            /

hComponent,                                         /

nConfigIndex,                                       /

pComponentConfigStructure)       /* Macro End */

注意有时候,需要先停止某个组件(的某些端口),才能设置config 成功

4OMX_SendCommand

一般的命令有:

OMX_CommandStateSet 、OMX_CommandFlush、OMX_CommandPortDisable" 、 "OMX_CommandPortEnable、CommandMarkBuffer

#define OMX_SendCommand(                                    /

hComponent,                                        /

Cmd,                                               /网址:yii666.com<

nParam,                                            /

pCmdData)                                          /

((OMX_COMPONENTTYPE*)hComponent)->SendCommand(         /

hComponent,                                        /

Cmd,                                               /

nParam,                                            /

pCmdData)                          /* Macro End */

例子:OMXSAFE(OMX_SendCommand(vrenderer, OMX_CommandPortEnable, 1, 0)); // 停下1对应的端口

5OMX_SetupTunnel

将两个组件连接起来,实际会引起调用每个组件的ComponentTunnelRequest

OMX_API OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel(

OMX_IN  OMX_HANDLETYPE hOutput,

OMX_IN  OMX_U32 nPortOutput,

OMX_IN  OMX_HANDLETYPE hInput,

OMX_IN  OMX_U32 nPortInput);

例子:

OMXSAFE(OMX_SetupTunnel(reader,   0, vdecoder,  0)); // reader的0端口为出,vdecoder的0端口为入,连接成一个Tunnel

准备好后,就可以设置OMX_StateExecuting,来让这个流程活动起来了。再以后,就可以通过OMX_StateIdle 来停下。

6OMX_GetState

#define OMX_GetState(                                       /

hComponent,                                         /

pState)                                             /

((OMX_COMPONENTTYPE*)hComponent)->GetState(             /

hComponent,                                         /

pState)                         /* Macro End */

7decOutputPortDef

OMX_PARAM_PORTDEFINITIONTYPE decOutputPortDef;

INIT_PARAM(decOutputPortDef);

decOutputPortDef.nPortIndex = 0;

err = OMX_GetParameter(pCtx->hReaderComp,

OMX_IndexParamPortDefinition,

&decOutputPortDef); // 利用IndexParamPortDefinition来得到组件的输出端口的属性

videoWidth = decOutputPortDef.format.video.nFrameWidth;

videoHeight = decOutputPortDef.format.video.nFrameHeight;

8 *pOmxBufferHeader

OMX_BUFFERHEADERTYPE *pOmxBufferHeader ;

// tell decoder output port that it will be using our buffer

err = OMX_UseBuffer(hDecodeComp,

&pOmxBufferHeader,  //out

OMX_DECODE_OUTPUT_PORT,

NULL,

outSize,

(NvU8*)pOut);

将分配好的pOut指针和他的大小outSize,配成一个OMX_BUF, 并给pOmxBufferHeader,这样就通过OMX_UseBuffer,来得到一个以后能给他用的Buffer,指针用我们分配的。

版权声明:本文内容来源于网络,版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。文本页已经标记具体来源原文地址,请点击原文查看来源网址,站内文章以及资源内容站长不承诺其正确性,如侵犯了您的权益,请联系站长如有侵权请联系站长,将立刻删除

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

支付宝扫一扫打赏

微信图片_20190322181744_03.jpg

微信扫一扫打赏

请作者喝杯咖啡吧~

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

二维码1

zhifubaohongbao.png

二维码2

zhifubaohongbao2.png