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的主要概念
客户端(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 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)是一个关于组件连接方式的概念。通过隧道化可以将不同的组件的一个输入端口和一个输出端口连接到一起,在这种情况下,两个组件的处理过程合并,共同处理。尤其对于单输入和单输出的组件,两个组件将作为类似一个使用。
7. Client与component之间的通信
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中定义了Core的API函数,应用程序通过它可以进行初始化、处理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
1)OMX_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)(
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)(
OMX_IN OMX_HANDLETYPE hComponent,
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 常用函数介绍
1)OMXConfigParser ()The configuration parser API
函数原型
OMX_BOOL OMXConfigParser ( OMX_PTR aInputParameters,OMX_PTR aOutputParameters);
传递参数
aInputParameters 指向如下结构
typedef struct
{
OMX_U8* inPtr; //codec 配置头部指针
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;
2)OMX_SetParameter
设定某个参数对应的值
#define OMX_SetParameter( /
hComponent, /
nParamIndex, /
pComponentParameterStructure) /
((OMX_COMPONENTTYPE*)hComponent)->SetParameter( /
hComponent, /
nParamIndex, /
pComponentParameterStructure) /* Macro End */
3)OMX_SetConfig
设定某个config值
#define OMX_SetConfig( /
hComponent, /
nConfigIndex, /
pComponentConfigStructure) /
((OMX_COMPONENTTYPE*)hComponent)->SetConfig( /
hComponent, /
nConfigIndex, /
pComponentConfigStructure) /* Macro End */
注意有时候,需要先停止某个组件(的某些端口),才能设置config 成功
4)OMX_SendCommand
一般的命令有:
OMX_CommandStateSet 、OMX_CommandFlush、OMX_CommandPortDisable" 、 "OMX_CommandPortEnable、CommandMarkBuffer
#define OMX_SendCommand( /
hComponent, /
Cmd, /
nParam, /
pCmdData) /
((OMX_COMPONENTTYPE*)hComponent)->SendCommand( /
hComponent, /
Cmd, /
nParam, /
pCmdData) /* Macro End */
例子:OMXSAFE(OMX_SendCommand(vrenderer, OMX_CommandPortEnable, 1, 0)); // 停下1对应的端口
5)OMX_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 来停下。
6)OMX_GetState
#define OMX_GetState( /
hComponent, /
pState) /
((OMX_COMPONENTTYPE*)hComponent)->GetState( /
hComponent, /
pState) /* Macro End */
7)decOutputPortDef
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,指针用我们分配的。
OpenMax概述的更多相关文章
- 多媒体的框架 - OpenCore框架概述
OpenCore是一个多媒体的框架,从宏观上来看,它主要包含了两大方面的内容:PVPlayer:提供媒体播放器的功能,完成各种音频 (Audio).视频(Video)流的回放(Playback)功能. ...
- 什么是OpenMAX技术分析OpenMAX
什么是OpenMAX技术分析OpenMAX OpenMAX是统一的抽象层,它允许访问否则需要供应商特定API的硬件. Broadcom的MMAL(多媒体抽象层API). 因此,OpenMAX允许使用此 ...
- 【AR实验室】ARToolKit之概述篇
0x00 - 前言 我从去年就开始对AR(Augmented Reality)技术比较关注,但是去年AR行业一直处于偶尔发声的状态,丝毫没有其"异姓同名"的兄弟VR(Virtual ...
- Recurrent Neural Network系列1--RNN(循环神经网络)概述
作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 本文翻译自 RECURRENT NEURAL NETWORKS T ...
- Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)
本篇博客算是一个开头,接下来会持续更新使用Swift3.0开发服务端相关的博客.当然,我们使用目前使用Swift开发服务端较为成熟的框架Perfect来实现.Perfect框架是加拿大一个创业团队开发 ...
- .Net 大型分布式基础服务架构横向演变概述
一. 业务背景 构建具备高可用,高扩展性,高性能,能承载高并发,大流量的分布式电子商务平台,支持用户,订单,采购,物流,配送,财务等多个项目的协作,便于后续运营报表,分析,便于运维及监控. 二. 基础 ...
- [C#] 进阶 - LINQ 标准查询操作概述
LINQ 标准查询操作概述 序 “标准查询运算符”是组成语言集成查询 (LINQ) 模式的方法.大多数这些方法都在序列上运行,其中的序列是一个对象,其类型实现了IEnumerable<T> ...
- 【基于WinForm+Access局域网共享数据库的项目总结】之篇一:WinForm开发总体概述与技术实现
篇一:WinForm开发总体概述与技术实现 篇二:WinForm开发扇形图统计和Excel数据导出 篇三:Access远程连接数据库和窗体打包部署 [小记]:最近基于WinForm+Access数据库 ...
- Java消息队列--JMS概述
1.什么是JMS JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送 ...
随机推荐
- Android中利用C++处理Bitmap对象
相信有些Android&图像算法开发者和我一样,遇到过这样的状况:要对Bitmap对象做一些密集计算(例如逐像素的滤波),但是在java层写循环代码来逐像素操作明显是不现实的,因为Java代码 ...
- 基于PHP采集数据入库程序(二)
在上篇基于PHP采集数据入库程序(一) 中提到采集新闻信息页的列表数据,接下来讲讲关于采集新闻具体内容 这是上篇博客的最终数据表截图: 接下来要做的操作就是从数据库中读取所需要采集的URL,进行页面抓 ...
- Thinkphp5 Route用法
域名路由:domain 1.application/router.php 文件位置,吧一下代码放进去就可以了 use think\Route; Route::domain('app.tp5a.com' ...
- vim 编辑器常用命令
vi 常用命令行 1.vi 模式 a) 一般模式: vi 处理文件时,一进入该文件,就是一般模式了. b) 编辑模式:在一般模式下可以进行删除,复制,粘贴等操作,却无法进行编辑操作.等按下‘i,I,o ...
- 元素加了position:absolute则该元素的text-align:center居中失效的解决办法
position:absolute; top:50%; left:50%; -webkit-transform: translate(-50%,-50%); -moz-transform: trans ...
- springboot使用@ControllerAdvice(二)之深入理解
前言: 接口类项目开发时,为了便于后期查找问题,一般会拦截器或过滤器中记录每个接口请求的参数与响应值记录, 请求参数很容易从request中获取,但controller的返回值无法从response中 ...
- Java面试经典题:线程池专题
1.什么是线程池 线程池的基本思想是一种对象池,在程序启动时就开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池, ...
- 9 云计算系列之Cinder的安装与NFS作为cinder后端存储
preface 在前面我们知道了如何搭建Openstack的keystone,glance,nova,neutron,horizon这几个服务,然而在这几个服务中唯独缺少存储服务,那么下面我们就学习块 ...
- ABBYY FineReader 14助力2017,正式进入新纪元
ABBYY FineReader 12自2014年推出以来,已经给万千用户的工作带来了便捷,蝉联优秀殊荣这么久,相信不少用户早在期待新版本的到来了吧.这不,ABBYY FineReader 14问世了 ...
- 【动态规划】数字分组I
[动态规划]数字分组I 时间限制: 1 Sec 内存限制: 64 MB提交: 10 解决: 6[提交][状态][讨论版] 题目描述 给出一堆魔法石的重量,问如何分成两堆,使得它们质量和之差最小,求 ...