精华内容
下载资源
问答
  • Android OpenMax

    2014-03-08 21:28:00
    OpenMax是一个多媒体应用程序的框架标准...在Android中,OpenMax IL层(集成层)通常可以用于多媒体引擎的插件,Android多媒体引擎OpenCore和StageFright都可以使用OpenMax做为插件,主要用于编解码处理。 在And

    http://blog.csdn.net/bluedreamstart/article/details/6645964

    OpenMax是一个多媒体应用程序的框架标准。

    在Android中,OpenMax IL层(集成层)通常可以用于多媒体引擎的插件,Android多媒体引擎OpenCore和StageFright都可以使用OpenMax做为插件,主要用于编解码处理。

    在Android框架层中,定义了由Android封装的OpenMax接口,它使用了C++类型的接口和Android的Binder 的IPC机制。Android封装的OpenMax接口被StageFright使用,OpenCore并不使用这个接口,而是使用其他形式的接口对OpenMax IL层接口进行封装。

    OpenMax实际上分为三个层次,自上而下分别是,OpenMax DL(开发层),OpenMax IL(集成层)和OpenMax AL(应用层)

    第一层:OpenMax DL(开发层)

    OpenMax DL定义了一个API,它是音频、视频和图像功能的集合。

    第二层:OpenMax IL(集成层)

    OpenMax IL作为音频、视频和图像编解码器能与多媒体编解码器交互,并以统一的行为支持组件(例如,资源和皮肤)。这些编解码器或许是软硬件的混合体,对用户是透明的底层接口应用于嵌入式、移动设备。它提供了应用程序和媒体框架,透明的。S编解码器供应商必须写私有的或者封闭的接口,集成进移动设备。IL的主要目的是使用特征集合为编解码器提供一个系统抽象,为解决多个不同媒体系统之间轻便性的问题。

    第三层:OpenMax AL(Appliction Layer,应用层)

      OpenMax AL API在应用程序和多媒体中间件之间提供了一个标准化接口,多媒体中间件提供服务以实现被期待的API功能。



    展开全文
  • android openmax AL/IL

    2012-12-29 09:40:18
    openmax IL/AL框架的分析,android平台openmax多媒体引擎分析,一个很好的在android上移植openmax时的参考资料,同时包含介绍了Android_系统下Stagefright_Player框架,对我开发起到了一定的帮助!
  • android openmax1

    2013-06-25 15:29:00
    Android的框架层,也定义了由Android封装的OpenMax接口,和标准的接口概念基本相同,但是使用C++类型的接口,并且使用了Android的Binder IPC机制。Android封装OpenMax的接口被StageFright使用,OpenCore没有使用这...

    在Android的框架层,也定义了由Android封装的OpenMax接口,和标准的接口概念基本相同,但是使用C++类型的接口,并且使用了Android的Binder IPC机制。Android封装OpenMax的接口被StageFright使用,OpenCore没有使用这个接口,而是使用其他形式对OpenMax IL层接口进行封装。

    对于我们使用硬件来解码来说,主要是使用了openmax的il层,也即是集成层,就是由硬件厂商提供好了c的接口,供我们多媒体程序来调用,最终能够完成我们的解码和编码工作。具体的框架如:

    http://www.androidcn.com/news/20110329/00001449_2.html

    所写的那样。

    android只是封装了openmax IL层,主要利用起解码功能。

     

    主要参考了网页的内容:

    http://blog.chinaunix.net/uid-24406631-id-2609033.html

    函数介绍:

    http://www.360doc.com/content/10/0915/16/474846_53871489.shtml

     

    转载于:https://www.cnblogs.com/samyboy/p/3154789.html

    展开全文
  • Android OpenMax 调用顺序

    千次阅读 2014-09-22 16:19:33
    万一组件不是OpenMax全兼容或者OpenMax的特性不明确,以上获取的功能决定了OMX是否支持输入/输出端口“UseBufeer”和“AllocateBuffer”调用,以及OMX是否支持部分帧等等。 注意:如果OMX组件返回“OMX_...

    1 OMX 核心初始化 _OMX_MasterInit

    1)调用OMX_Init函数

    ->OsclInit::Init(error, &select); //init all Oscl layers except Oscl scheduler.
    ->_Try_OMX_Create(error, data); //create the OMX singleton
    ->OsclSingletonRegistry::registerInstanceAndUnlock(data, OSCL_SINGLETON_ID_OMX, error); //Release the singleton.
    ->_Try_OMX_Init(error, status); //If create succeeded, then init the OMX globals.

    2)PV框架列举所有OMX

    OMX_ComponentNameEnum //列举所有组件的名称
    ->OsclSingletonRegistry::getInstance(OSCL_SINGLETON_ID_OMX, error);
    ->oscl_strncpy(cComponentName, (data->ipRegTemplateList[Index])->ComponentName, nNameLength);

    OMX_GetRolesOfComponent // 通过组件名称找到组件,返回其角色(role)
    ->OsclSingletonRegistry::getInstance(OSCL_SINGLETON_ID_OMX, error);
    ->data->ipRegTemplateList[ii])->GetRolesOfComponent(RoleString)
    ->oscl_strncpy((OMX_STRING) roles[ii], (OMX_STRING)RoleString[ii], oscl_strlen((OMX_STRING)RoleString[ii]) + 1);

    2 OMX组件实例、功能及端口

    OMX核心初始化后,下一步为列举每个组件的功能和端口。
    1)调用OMX_GetHandle获取所需的OMX组件信息。

    2)调用及“PV_OMX_CAPABILITY_TYPE_INDEX”这个index去获取组件的功能。
    万一组件不是OpenMax全兼容或者OpenMax的特性不明确,以上获取的功能决定了OMX是否支持输入/输出端口“UseBufeer”和“AllocateBuffer”调用,以及OMX是否支持部分帧等等。

    注意:如果OMX组件返回“OMX_ErrorUnsupportedIndex”给index(或其他比如“OMX_ErrorNone”),PV框架将为组件功能赋默认值。

    3)调用OMX_GetParameter,针对视频组件再调用“”,针对音频组件则调用“OMX_IndexAudioIni”以获取可用的端口号。

    4)循环查找可用的端口号以找到输入端口。

    5)循环查找可用的端口号以找到输出端口。

    注意:
    Index “PV_OMX_CAPABILITY_TYPE_INDEX” is defined as:
    #define PV_OMX_COMPONENT_CAPABILITY_TYPE_INDEX 0xFF7A347
    The OMX_GetParameter call expects the following structure to be filled for this index:
    typedef struct PV_OMXComponentCapabilityFlagsType
    {
    // OMX COMPONENT CAPABILITY RELATED MEMBERS
    OMX_BOOL iIsOMXComponentMultiThreaded;
    OMX_BOOL iOMXComponentSupportsExternalOutputBufferAlloc;
    OMX_BOOL iOMXComponentSupportsExternalInputBufferAlloc;
    OMX_BOOL iOMXComponentSupportsMovableInputBuffers;
    OMX_BOOL iOMXComponentSupportsPartialFrames;
    OMX_BOOL iOMXComponentUsesNALStartCode;
    OMX_BOOL iOMXComponentCanHandleIncompleteFrames;
    OMX_BOOL iOMXComponentUsesFullAVCFrames;
    } PV_OMXComponentCapabilityFlagsType;

    功能参数的默认值:
    1)iIsOMXComponentMultiThreaded ——默认值OMX_TRUE。
    OMX组件一般运行与独立的线程(与PV框架线程不同),有可能将OMX组件集成进PV框架线程(e.g.,通过同步调用)。

    2)iOMXComponentSupportsExternalOutputBufferAlloc ——默认值OMX_TRUE。
    OMX规范要求OMX组件支持外部分配输出缓冲(就是输出缓冲的OMX_UseBuffer调用)。
    如果组件不支持,必须通知PV框架,以便其调用“OMX_AllocateBuffer”代替。

    3)iOMXComponentSupportsExternalInputBufferAlloc ——默认值OMX_TRUE。
    OMX规范要求OMX组件支持外部分配输入缓冲(就是输入缓冲的OMX_UseBuffer调用)。
    如果组件不支持,必须通知PV框架,以便其调用“OMX_AllocateBuffer”代替。

    4)iOMXComponentSupportsMovableInputBuffers ——默认值OMX_TRUE。
    如果OMX缓冲是外部分配的,为了提高稳定性和优化性能,可以分离OMX缓冲头部信息(OMX_BUFFERHEADERTYPE)与数据区(“pBuffer”)。换句话说,使OMX缓冲更有“移动性”,当传递一个输入缓冲到OMX组件时,“pBuffer”区域和头部信息不一定指向相同的缓冲区。因此,可以分配更多的数据缓冲在框架中循环工作,只有在需要把缓冲传递到OMX组件时才附加到头部信息去。如果OMX组件要求头部和数据一直指向相同的缓冲,则iOMXComponentSupportsMovableInputBuffers应该被置为OMX_FALSE。

    5)iOMXComponentSupportsPartialFrames ——默认值OMX_TRUE。
    OMX规范要求OMX组件支持将任意数据打包进OMX缓冲,包括独立单帧、NAL和被拆分到多个缓冲区的解码单元。PV框架支持“OMX_BUFFERFLAG_ENDOFFRAME”标记去组装部分帧。然而,如果OMX组件不支持组装部分帧,也就是OMX组件要求PV框架来组装部分帧并提供给OMX组件完整单帧或NAL,此时iOMXComponentSupportsPartialFrames需要被设置为OMX_FALSE。
    注意:设置为OMX_FALSE将影响性能。

    6)iOMXComponentUsesNALStartCode ——默认值OMX_FALSE。
    这个标志位将影响H264解码。PV框架提供所有信息以重建立H264 NALs(通过OMX缓冲的大小和OMX_BUFFERFLAG_ENDOFFRAME的大小)。因此没有必要由OMX组件解码输出流来获取0x0001的NAL初始代码。如果OMX的H264组件及解码单元需要NAL起始代码,可将iOMXComponentUsesNALStartCode置为OMX_TRUE。

    7)iOMXComponentCanHandleIncompleteFrames ——默认值OMX_TRUE。
    如果丢失部分数据流(比如数据包丢失),假定OMX组件及解码单元可以解决这个问题。如果不可以,则将iOMXComponentCanHandleIncompleteFrames 设置为OMX_FALSE。当“iOMXComponentSupportsPartialFrames”也被设置为OMX_FALSE时,这点相当重要,因为OMX组件不提供组装部分帧,也就是说PV框架必须提供组装好的frame/NALs,因此需要通知OMX组件不完整的帧数据是否可以传递进来。

    8)iOMXComponentUsesFullAVCFrames ——默认值OMX_FALSE。
    这个标志位决定AVC解码中使用NAL模式还是frame模式。AVC数据可由以上两种模式提供给OMX组件。默认为NAL模式(设置为OMX_FALSE),此模式下OpenCore框架可以为OMX输入缓冲同时提供完整或者部分AVC NAL。Frame模式下(OMX_TRUE),OpenCore框架积累NAL,并提供给OMX输入缓冲一帧完整的数据。
    NAL边界的问起前面讨论过了,此处省略。

    3 OMX组件输入输出缓冲协商

    在任何数据交换前,输入输出缓冲需要进行协商。PV框架需要做以下工作:、
    1)调用OMX_GetParameter获取输入端口缓冲参数(最小/实际缓冲数,缓冲区大小等等)
    2)检查有效的输入缓冲参数(修改一些参数,比如帧的长度和宽度,以及缓冲区的数目等等)
    3)调用OMX_SetParameter函数设置输入缓冲参数
    4)调用OMX_GetParameter获取输出端口缓冲参数(最小/实际缓冲数,缓冲区大小等等)
    5)检查有效的输出缓冲参数(修改一些参数,比如帧的长度和宽度,以及缓冲区的数目等等)
    6)调用OMX_SetParameter函数设置输出缓冲参数

    过程如图三(省略)

    一些设想:
    1)缓冲区尺寸的设想:
    对于编码器组件,最终分配的输出缓冲区尺寸可能小于profile/level/target比特率所要求的最大帧尺寸。出于内存消耗考虑,OMX编码器组件应当能够输出frame或NAL的比特流划分到多个输出缓冲区去。老调重弹,最后一个部分缓冲区使用“OMX_BUFFERFLAG_ENDOFFRAME”标记结尾。

    对于解码器组件,最终分配的输入缓冲区尺寸可能小于profile/level/target比特率所要求的最大帧尺寸。出于内存消耗考虑,PV框架在适当的时候提供采用“OMX_BUFFERFLAG_ENDOFFRAME”标记结尾的输入缓冲区。如果输入缓冲包含完整帧(H264也可能是NAL单元 )或多帧,“OMX_BUFFERFLAG_ENDOFFRAME”将会用来标记一帧的结尾。如果完整的frame/NAL不能放入一个输入缓冲区,则会被拆分放入多个缓冲区。包含部分信息的缓冲区,“OMX_BUFFERFLAG_ENDOFFRAME”标记位将会被置为0。一帧数据的最后一个缓冲区中“OMX_BUFFERFLAG_ENDOFFRAME”才会被置为1。通过这种做法,组件可以方便的重构一帧数据。

    如果OMX解码器组件不兼容组长部分帧,PV框架将负责做这件事情。

    2)输入/输出缓冲区数量。
    框架或许希望分配比OMX要求更多的的输入/输出缓冲区以提供更优的性能(比如解码器输出缓冲或编码器输入缓冲——缓冲更多的数据可以提高性能)。尽管性能提升了,但无法送到OMX组件去(不太理解,应该还是协商不好的意思)。

    4 OMX状态变换 加载->空闲 

    如果部分已分配的缓冲区进入空闲状态,缓冲区分配将挂起。在变化过程中,PV框架将“
    1)通过“OMX_SendCommand”调用发送命令给OMX组件,将状态由OMX_StateLoaded改变为OMX_StateIdle
    2)调用一系列“OMX_UseBuffer” 或者“OMX_AllocateBuffer”通知OMX组件。这些调用使用NumInputBuffer记录输入输入端口的数目,使用NumOutputBuffer记录输入输入端口的数目。
    3)等待OMX组件的EventHandler事件回调,通知框架状态变换完成(OMX_EventCmdComplete)

    一些设想与推荐参数:
    根据OMX规范,兼容的OMX组件必须同时支持OMX_UseBuffer 和 OMX_AllocateBuffer调用。然而,出于一些内在原因,完全实现是不可能的,组件应当通知PV框架其所具有的功能。

    推荐将INPUT缓冲分配在OMX组件外部,这样可以减少额外的内存拷贝(也就是说输入缓冲使用OMX_UseBuffer)。

    5 变换到“执行”状态与数据交换

    状态变化到执行时才开始真正处理数据。本步骤中,PV框架将:
    1)通过“OMX_SendCommand”调用发送命令给OMX组件,将状态由OMX_StateIdle改变为OMX_StateExecuting。
    2)等待OMX组件的EventHandler事件回调,通知框架状态变换完成(OMX_EventCmdComplete)。
    3)通过OMX_EmptyThisBuffer调用将输入缓冲发送给OMX组件,通过OMX_FillThisBuffer调用将输出缓冲发送给OMX组件,组件使用合适的回调函数返回缓冲区。

    过程如图5


    注意:
    1)在任意时刻OMX组件如果拥有所有输入缓冲区(也就是所有的输入缓冲都通过EmptyThisBuffer调用传给了OMX组件),此时将不能再传递任何输入缓冲区给OMX组件,知道OMX通过EmptyBufferDone释放一个缓冲。对于输出缓冲区同样。

    2)如果PV框架没有及时将缓冲区发给OMX框架,OMX组件不会多次返回同一个缓冲区(也就是一旦OMX框架返回一个缓冲区,只有PV框架再次调用这个缓冲,OMX组件才可使用)。这是OMX缓冲交换APSs所规定的基本规则。

    6 暂停

    PV框架常见的功能包括暂停和恢复,PV框架将:
    1)通过“OMX_SendCommand”调用发送命令给OMX组件,将状态由OMX_StateExecuting改变为OMX_StatePause。
    2)等待OMX组件的EventHandler事件回调,通知框架状态变换完成(OMX_EventCmdComplete)。

    PV框架向OMX组件发送暂停命令后,立刻停止发送输入输出缓冲。

    有暂停就有恢复,PV框架将:
    1)通过“OMX_SendCommand”调用发送命令给OMX组件,将状态由OMX_StatePause改变为OMX_StateExecuting。
    2)等待OMX组件的EventHandler事件回调,通知框架状态变换完成(OMX_EventCmdComplete)。

    PV框架的状态恢复到执行后,将恢复发送OMX输入输出缓冲。

    过程如图6

    7 端口刷新(如果可用)

    端口刷新调用一般用于重新配置解码器组件,这样会清空所有数据。在此情况下,PV框架将:
    1)通过“OMX_SendCommand”调用发送刷新全部端口指令。
    2)等待输入缓冲的输出缓冲的两个EventHandler事件回调,通知框架状态变换完成(OMX_EventCmdComplete)。

    刷新命令来了之后,PV框架立刻停止向OMX组件发送输入/输出缓冲。当组件接收到2个回调函数后,PV框架开始恢复发送输入/输出缓冲。

    端口刷新的顺序和通告并不相关。

    过程如图7。

    为了防止动态端口被重复配置,也可以先于OMX IL用户关闭OMX组件端口调用端口刷新指令。

    8 停止/变化到“IDLE”状态

    为了停止处理过程,PV框架将:
    1)通过“OMX_SendCommand”调用发送命令给OMX组件,将状态由OMX_StatePause改变为OMX_StateIdle。
    2)等待OMX组件的EventHandler事件回调,通知框架状态变换完成(OMX_EventCmdComplete)。

    过程见图8。

    一些设想:
    当命令完成“IDEL”态的回调后,PV框架假设所有输入输出缓冲按照OMX规范要求那样返回。

    9 OMX组件状态转换 IDLE->Loaded State和 De-initialization

    当PV框架结束与一个OMX组件的所有交互后,将:
    1)通过“OMX_SendCommand”调用发送命令给OMX组件,将状态由OMX_StateIdle改变为OMX_StateLoaded。
    2)向OMX组件发送一系列“OMX_FreeBuffer”调用。
    3)等待OMX组件的EventHandler事件回调,通知框架状态变换完成(OMX_EventCmdComplete)。使用NumInputBuffer记录输入输入端口的数目,使用NumOutputBuffer记录输入输入端口的数目。
    4)对OMX核心执行OMX_FreeHandle调用,释放组件句柄。

    流程图见图9

    注意:在向OMX组件发送卸载命令之前,PV框架一直在等待OMX组件返回的输入输出缓冲。由于回调的过程的不同步性,一些EmptyBufferDone/FillBufferDone的回调有可能在OMX组件状态由“executing” 到 “idle”之后才到达。

    10 OMX Core 卸载

    PV框架调用函数OMX_Deinit()。


    展开全文
  • android openmax&surfaceflinger

    千次阅读 2012-09-19 15:51:17
    int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { LOGV("SurfaceTextureClient::queueBuffer"); Mutex::Autolock lock(mMutex); int64_t timestamp; if (mTimestamp == ...

    ICS Overlay主要流程

     

    ICS Overlay主要流程

    只关注到Overlay的主要过程,对FB未做分析,待以后完善。

     

    Figure 1queueBuffer流程

    1. 在AWesomePlayer的Render中将解码后的Buf通过ANativeWindow接口通知SurfaceTextureClient

    struct AwesomeNativeWindowRenderer : public AwesomeRenderer {

    virtual void render(MediaBuffer *buffer) {

    int64_t timeUs;

    CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));

    native_window_set_buffers_timestamp(mNativeWindow.get(), timeUs * 1000);

    status_t err = mNativeWindow->queueBuffer(

    mNativeWindow.get(), buffer->graphicBuffer().get());

     

    }

    }

    进入到SurfaceTextureClient的queueBuffer(),这里的工作主要有:

    1) 更新timestamp,如果没有,则用系统时间

    2) 根据Buffer地址从slots中取出Buffer的序号

    3) 将序号以及时间戳、以及其它信息通过Binder通信发送给SurfaceTexture。

    int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {

    LOGV("SurfaceTextureClient::queueBuffer");

    Mutex::Autolock lock(mMutex);

    int64_t timestamp;

    if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) {

    timestamp = systemTime(SYSTEM_TIME_MONOTONIC);

    LOGV("SurfaceTextureClient::queueBuffer making up timestamp: %.2f ms",

    timestamp / 1000000.f);

    } else {

    timestamp= mTimestamp;

    }

    int i = getSlotFromBufferLocked(buffer);

    status_t err = mSurfaceTexture->queueBuffer(i,timestamp,

    &mDefaultWidth, &mDefaultHeight, &mTransformHint);

    return err;

    }

    2. 在SurfaceFlinger进程中,本地端的SurfaceTexture接收到Binder消息,并进入queueBuffer函数。主要做以下工作:

    1) 根据传入的int buf从slots中找出相应的Buffer,并更新此buffer的相关信息。

    2)发信号mDequeueCondition.signal(),用作通知等待者

    3) 通知监听者,onFrameAvailable有帧到达

    status_t SurfaceTexture::queueBuffer(intbuf, int64_t timestamp,

    uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {

    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);

     

    sp<FrameAvailableListener> listener;

     

    { // scope for the lock

    Mutex::Autolock lock(mMutex);

    //一些参数检查

     

    if (mSynchronousMode) {

    // In synchronous mode we queue all buffers in a FIFO.

    mQueue.push_back(buf);

     

    // Synchronous mode always signals that an additional frame should

    // be consumed.

    listener = mFrameAvailableListener;

    } else {

    // In asynchronous mode we only keep the most recent buffer.

    if (mQueue.empty()) {

    mQueue.push_back(buf);

     

    // Asynchronous mode only signals that a frame should be

    // consumed if no previous frame was pending. If a frame were

    // pending then the consumer would have already been notified.

    listener = mFrameAvailableListener;

    } else {

    Fifo::iterator front(mQueue.begin());

    // buffer currently queued is freed

    mSlots[*front].mBufferState = BufferSlot::FREE;

    // and we record the new buffer index in the queued list

    *front = buf;

    }

    }

     

    mSlots[buf].mBufferState = BufferSlot::QUEUED;

    mSlots[buf].mCrop = mNextCrop;

    mSlots[buf].mTransform = mNextTransform;

    mSlots[buf].mScalingMode = mNextScalingMode;

    mSlots[buf].mTimestamp = timestamp;

    mFrameCounter++;

    mSlots[buf].mFrameNumber = mFrameCounter;

     

    mDequeueCondition.signal();

     

    *outWidth = mDefaultWidth;

    *outHeight = mDefaultHeight;

    *outTransform = 0;

    } // scope for the lock

     

    // call back without lock held

    if (listener != 0) {

    listener->onFrameAvailable();

    }

    return OK;

    }

    在Layer中,第一次引用时会创建SurfaceTexture,并实现FrameAvailableListener

    void Layer::onFirstRef()

    {

    LayerBaseClient::onFirstRef();

     

    struct FrameQueuedListener : public SurfaceTexture::FrameAvailableListener {

    FrameQueuedListener(Layer* layer) : mLayer(layer) { }

    private:

    wp<Layer> mLayer;

    virtual void onFrameAvailable() {

    sp<Layer> that(mLayer.promote());

    if (that != 0) {

    that->onFrameQueued();

    }

    }

    };

    mSurfaceTexture = new SurfaceTextureLayer(mTextureName, this);

    mSurfaceTexture->setFrameAvailableListener(newFrameQueuedListener(this));

    mSurfaceTexture->setSynchronousMode(true);

    mSurfaceTexture->setBufferCountServer(2);

    }

    控制流进入Layer:onFrameQueued()函数,最后调用SurfaceFlinger的事件函数,通知其新事件。

    void Layer::onFrameQueued() {

    android_atomic_inc(&mQueuedFrames);

    mFlinger->signalEvent();

    }

    3. 在SurfaceFlinger中获得事件,进入主循环处理事件

    bool SurfaceFlinger::threadLoop()

    {

    waitForEvent();

    // post surfaces (if needed)

    handlePageFlip();//设置刚入队的Buffer为当前激活的Buffer

    handleRepaint();//将当前激活的Buffer地址送给硬件叠加层,进行叠加

     

    在lockPageFlip函数中遍历每一层设置当前激活的Buffer

    bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)

    {

    bool recomputeVisibleRegions = false;

    size_t count = currentLayers.size();

    sp<LayerBase> const* layers = currentLayers.array();

    for (size_t i=0 ; i<count ; i++) {

    const sp<LayerBase>& layer(layers[i]);

    layer->lockPageFlip(recomputeVisibleRegions);

    }

    return recomputeVisibleRegions;

    }

    对每个Layer将当前Buffer设置为激活Buffer

    void Layer::lockPageFlip(bool& recomputeVisibleRegions)

    {

    // update the active buffer

    mActiveBuffer= mSurfaceTexture->getCurrentBuffer();

     

    在handleRepaint()中调用setupHardwareComposer建立叠加层

    setupHardwareComposer(mDirtyRegion);

    void SurfaceFlinger::setupHardwareComposer(Region& dirtyInOut)

    {

    /*

    * update the per-frame h/w composer data for each layer

    * and build the transparent region of the FB

    */

    for (size_t i=0 ; i<count ; i++) {

    const sp<LayerBase>& layer(layers[i]);

    layer->setPerFrameData(&cur[i]);

    }

    }

     

    将当前激活的buffer设置到 handware composer中进行叠加

    void Layer::setPerFrameData(hwc_layer_t* hwcl) {

    const sp<GraphicBuffer>& buffer(mActiveBuffer);

    if (buffer == NULL) {

    // this can happen if the client never drew into this layer yet,

    // or if we ran out of memory. In that case, don't let

    // HWC handle it.

    hwcl->flags |= HWC_SKIP_LAYER;

    hwcl->handle = NULL;

    } else {

    hwcl->handle = buffer->handle;

    }

    }

     
     

    OpenMax图示

     

     

     

    Introduction to the Gstreamer System Architecture

    In my last post I wrote some different things about VLC, which I wanted to use for streaming purposes. I had to investigate what library it used. Actually, there is one library called "LIVE 555 streaming media" which the cone, VLC, is using.
    Really, it was very difficult for me to find information for getting started.

    For this reason, it was proposed to me, to investigate a bit about another low-level library called 
    Gstreamer and written in C, for streaming and playing multimedia, which is used in several applications.

    The first impression was really good. There is a 
    first manual in which you can learn the basic features of Gstreamer, how to initialize it, how to put it in different state, what is a pipeline, what is an element, and finally a OGG player, as a "Hello World" application to apply the concepts already learnt.

    As a result of my investigation, I'm going to summarize what I've learnt in this Gstreamer Manual.

    The main use of Gstreamer is to manage the video and audio stream to create, for example, a video player, or a streaming server.


    An element is the most important class of objects in Gstreamer. Usually, a functionality is achieved by linking elements.

    Normally, inside the elements, there are pads, which are inputs and outputs. The input pad is called "sink" and is from where data flows trough the element, for example, from a local file, or from another element. The output pad, is called "source".

    Here we can see an example of an elements with two pads. An input pad, and an output pad.

    This is an element called filter, with input and output pads.



    Besides elements, there is a kind of container called bin, which would be the parent of some elements. And then, there is a type of bin, calledpipeline.
    This is a hierarchical structure for one important reason. A medium-sized project with gstreamer will contain a lot of elements, imagine that you want to put the "play" state. You should tell to each element. This method allows you to tell the command to the pipeline and it will advise all its elements.

    There are also buses and message reporting functionalities which I'm not going to explain in this post because it will be too long.

    Finally, as an exemple of the above explained, we have the first "hello world" application, which is an audio player of the Vorbis encoded content in an OGG file.

    In the next posts I'm going to explain how it functions by putting snippets of code, and extending a little bit more the bus and message functionalities.

     

     

    gstreamer介绍




    Rainbow编码器是基于gstreamer实现的,webkit的video标签使用了gstreamer,gstreamer项目里还有一个rtsp的服务器,gstreamer的应用越来越多,而且它的确是一个不错的东西。

    最近有几次需要向人家解释gstreamer,发现这样一些观点:gstreamer比vlc差远了;下载gstreamer编译后就是一个播放器等等,这些都是对于gstreamer不理解造成的。为了理解gstreamer,我们先来看看directshow。

    DirectShow(有时缩写如 DS 或 DShow),开发代号 Quartz ,是一种由微软公司开发的能够让软件开发者对媒体文件执行各种不同处理的应用程序设计接口。它是微软公司对早先 Windows视频科技的一次更新。基于微软公司 Windows 组件对象模型(COM)框架,DirectShow为大部份微软公司程序设计语言提供了一个媒体的普遍接口,而且是一个可扩展的,能在使用者或开发者的命令下播放或记录媒体文件的,以Filter为基础的框架。DirectShow 开发工具及凭证被加入到微软公司 SDK 平台的一部份。Windows Media Player 这样的应用程序运用 DirectShow 或者它的各种衍生来播放来自文件或是互联网上的内容。DirectShow's 的最大的竞争对手是苹果计算机的QuickTime 框架。

    以上内容是从http://zh.wikipedia.org/zh/DirectShow贴过来的,也就是维基百科。directshow和gstreamer有什么关系吗?当然有,我们可以把gstreamer理解成linux下的directshow。

    在Wikimedia中,对于gstreamer是这样描述的:

    GStreamer is a pipeline-based multimedia framework written in the C programming language with the type system based on GObject.
    GStreamer是一个基于管道的多媒体框架,采用c语言开发,基于GObject。


    Gstreamer基于GObject,是跨平台的,包括Linux (x86, PowerPC and ARM), Solaris (Intel and SPARC) and OpenSolaris, FreeBSD, OpenBSD, NetBSD, Mac OS X, Microsoft Windows and OS/400。gstreamer已经多种语言绑定,包括Python,Perl,Ruby等。

    gstreamer通过把若干elements链接在一起构成pipeline实现对媒体内容的处理,element通过plug-in的方式提供。bin是一种特殊的element,是由多个其它elements组成的。

    element之间通过pad进行数据通讯,一个element的源pad可以链接到另一个element的sink pad。pad之间的数据类型通过capability来协商。pipeline有多种状态,当处于playing状态的时候,数据buffer就从源pad向sink pad传送。

    Gstreamer的核心实现了对plug-in的注册和加载等功能,plug-in是以动态库的形式存在的。当需要某个element的时候,Gstreamer就动态的加载对应的动态库。可以通过编写plug-in方式对gstreamer的功能进行扩展,包括编码方式,封装格式等各种功能。

    来看看这张来自《GStreamer Application Development Manual》的图片:





    命令行调试工具

    两个比较常用的工具是gst-inspect和gst-launch,用gst-inspect给出一个plugin或者element的信息,gst-launch用用于调试插件。

    也可以直接用gst-launch来完成很多任务,但是如果要实现一个产品还是不要直接采用gst-launch。比如你想录制一个通过组播发送的视频流,就可以通过如下命令实现:

    gst-launch udpsrc multicast-group=239.3.3.3 port=33333 ! filesink location=video.ts

    下面给出一些有用的命令:

    通过组播发送TS流


    基于gstreamer的应用

    首先是Webkit,Webkit在实现html5 video标签的支持的时候,采用了gstreamer。

    Totem播放器

    参考资料

    主站点 http://www.gstreamer.org/

    Wikipedia之Gstreamer词条 http://en.wikipedia.org/wiki/GStreamer

    理解Gstreamer架构

    本文给出了Gstreamer的总体设计。通过阅读本文可以了解Gstreamer的内部工作原理。本文编译自gstreamer源码中的文档,原文在源码中的位置是/gstreamer/docs/design/part-overview.txt。

    概述

    Gstreamer是一个libraries和plugins的集合,用于帮助实现各种类型的多媒体应用程序,比如播放器,转码工具,多媒体服务器等。

    利用Gstreamer编写多媒体应用程序,就是利用elements构建一个pipeline。element是一个对多媒体流进行处理的object,比如如下的处理:

    读取文件。

    不同格式的编解码。

    从硬件采集设备上采集数据。

    在硬件设备上播放多媒体。

    多个流的复用。


    elements的输入叫做sink pads,输出叫做source pads。应用程序通过pad把element连接起来构成pipeline,如下图所示,其中顺着流的方向为downstream,相反方向是upstream。



    应用程序会收到来自pipeline的消息和通知,比如EOS等。

    总体设计

    Gstreamer的设计目标如下:

    快速处理大规模数据。

    对多线程处理的完全支持。

    能处理各种格式的流媒体。

    不同数据流的同步。

    处理多种设备的能力。


    基于Gstreamer的应用程序能够具备的处理能力依赖于系统中安装的不同种类功能的elements的数量。

    Gstreamer核心不具备处理具体的media的功能,但是element处理media时需要具备的特性很多是由Gstreamer的核心提供的。

    elements

    element是pipeline的最小组成部分。element提供了多个pads,或者为sink,或者为source。一个element有四种可能的状态,分别是NULL,READY,PAUSED,PLAYING。NULL和READY状态下,element不对数据做任何处理,PLAYING状态对数据进行处理,PAUSE状态介于两者之间,对数据进行preroll。应用程序通过函数调用控制pipeline在不同状态之间进行转换。

    element的状态变换不能跳过中间状态,比如不能从READY状态直接变换到PLAYING状态,必须经过中间的PAUSE状态。

    element的状态转换成PAUSE会激活element的pad。首先是source pad被激活,然后是sink pad。pad被激活后会调用activate函数,有一些pad会启动一个Task。

    PAUSE状态下,pipeline会进行数据的preroll,目的是为后续的PLAYING状态准备好数据,使得PLAYING启动的速度更快。一些element需接收到足够的数据才能完成向PAUSE状态的转变,sink pad只有在接收到第一个数据才能实现向PAUSE的状态转变。

    通常情况下,element的状态转变需要协调一致。

    可对element进行如下分类:

    source,只提供数据源。

    sink,比如播放设备。

    transform

    demuxer

    muxer


    Bin

    bin是由多个element构成的特殊的element,用图来说明:

     

    Pipeline

    pipeline是具备如下特性的特殊的bin:

    选择并管理一个全局的时钟。

    基于选定的时钟管理running_time。running_time用于同步,指的是pipeline在PLAYING状态下花费的时间。

    管理pipeline的延迟。

    通过GstBus提供element与应用程序间的通讯方式。

    管理elements的全局状态,比如EOS,Error等。


    Dataflow and buffers

    Gstreamer支持两种类型的数据流,分别是push模式和pull模式。在push模式下,upstream的element通过调用downstream的sink pads的函数实现数据的传送。在pull模式下,downstream的element通过调用upstream的source pads的函数实现对数据的请求。

    push模式是常用的模式,pull模式一般用于demuxer或者低延迟的音频应用等。

    在pads之间传送的数据封装在Buffer里,Buffer中有一个指向实际数据的指针以及一些metadata。metadata的内容包括:

    timestamp

    offset

    duration

    media type

    其它


    在push模式下,element通过调用gst_pad_push()函数把buffer传送给对应的pad。在pull模式下,element通过调用gst_pad_pull_range()函数把pull过来。

    element在push buffer之前需要确认对应的element具备处理buffer中的数据类型的能力。在传说红之前首先查询对应的element能够处理的格式的种类,并从中选择合适的格式,通过gst_buffer_set_caps()函数对buffer进行设置,然后才传送数据。

    收到一个buffer后,element要首先对buffer进行检查以确认是否能够处理。

    可以调用gst_buffer_new()函数创建一个新的buffer,也可以调用gst_pad_alloc_buffer()函数申请一个可用的buffer。采用第二种方法接收数据的buffer可以设定接收其它类型的数据,这是通过对buffer的caps进行设定来实现的。

    选择媒体类型并对buffer进行设定的处理过程叫做caps negotianation。

    Caps

    Caps,也就是媒体类型,采用key/value对的列表来描述。key是一个字符串类型,value的类型可能是int/float/string类型的single/list/range。

    Data flow and events

    除了数据流,还有events流。与数据流不同,events的传送方向既有downstream的,也有upstream的。

    events用于传递EOS,flushing,seeking等消息。

    有的events必须和data flow一起进行serialized。serialized的events比如TAG,非serialized的events比如FLUSH。

    Pipeline construction

    gst_pipeline_create()函数用于创建一个pipeline,gst_bin_add()函数用于向pipeline中添加element,gst_bin_remove()函数用于从pipeline中移除element。gst_element_get_pad()函数用于检索pipeline中的element。gst_pad_link()函数用于把pads连接在一起。

    有的element会在数据流开始传送的时候创建新的pads,通过调用函数g_signal_connect()函数,能在新的pads被创建的时候接收到消息。

    由于处理的数据互相不兼容,有的elements是不能被连接到一起的。gst_pad_get_caps()函数查询element能够处理的数据类型。

    Pipeline clock

    Pipeline的一个重要功能是为pipeline中的所有elements选择一个全局时钟。

    时钟的作用是提供一个每秒为GST_SECOND的单调递增的时钟,单位是纳秒。element利用这个时钟时间来播放数据。

    在pipeline被设为PLAYING之前,pipeline查询每一个element是否能提供clock,并按照如下次序来选择clock:

    应用程序选择了一个clock。

    如果source element提供了clock。

    其它任何提供了clock的element。

    选择一个默认的系统clock。


    也有特殊的情况,比如存在音频sink提供了clock,那么就选择其提供的clock。

    Pipeline states

    完成了pads的链接和signals的链接,就可以设定pipeline为PAUSED状态启动数据流的处理。当bin(这里指的是pipeline)进行状态转换的时候要转换所有的children的状态,转换的次序是从sink element开始到source element结束,这样做的目的是为了确保upstream element提供数据的时候,downstream element已经准备好。

    Pipeline status

    Pipeline会通过bus向应用程序通报发生的events。bus是由pipeline提供的一个object,可以通过gst_pipeline_get_bus()函数取得。

    bus分布到加入pipeline的每一个element。element利用bus来发布messages。有各种不同类型的messages,比如ERRORS,WARNINGS,EOS,STATE_CHANGED等。

    pipeline以特殊的方式处理接收到的EOS message,只有当所有的sink element发送了EOS message的时候,pipeline才会把EOS发送给应用程序。

    也可以通过gst_element_query()函数获取pipeline status,比如获取当前的位置或者播放的时间。

    Pipeline EOS

    当source filter遇上了流结束,会沿着downstream的方向向下一个element发送一个EOS的event,这个event依次传送给每一个element,接收到EOS event的element不再接收数据。

    启动了线程的element发送了EOS event后就不再发送数据。

    EOS event最终会到达sink element。sink element会发送一个EOS消息,通告流结束。pipeline在接收到EOS消息以后,把消息发送给应用程序。只有在PLAYING状态下会把EOS的消息传送给应用程序。

    发送了EOS以后,pipeline保持PLAYING状态,等待应用程序把pipeline的状态置为PAUSE或者READY。应用程序也可以进行seek操作。

    Gstreamer的同步机制

    本文编译自gstreamer源代码中的文档,原文的路径是gstreamer/docs/design/part-synchronisation.txt。

    本文描述了Gstreamer的同步机制,Gstreamer中实现同步的组件如下:

    GstClock,是全局的,用于pipeline中的所有elements。

    GstBuffer的timestamps。

    buffers之前的NEW_SEGMENT event。


    GstClock

    GstClock是精确到纳秒的表示当前时间的一个计数。其值用absolute_time表示。这个计数的源的选择如下:

    系统时间,精度是微妙。

    音频设备。

    基于网络的数据包,比如RTP数据包。

    其它。


    在Gstreamer中任何element都可以提供GstClock,pipeline从所有可用的GstClock中选定一个并用于pipeline中所有的elements。时间计数单调递增,可以不是从0开始计数。

    Running time

    选定了clock之后pipeline会维护一个基于选定时钟的running_time。running_time指的是pipeline处于PLAYING状态下的时间总和,计算方法如下:

    如果pipeline处于NULL或者READY状态则running_time处于undefined。

    PAUSE状态下,running_time的值保持不变,如果处于刚开始启动时的PAUSE状态,running_time的值为0。

    flushing seek之后,running_time被置为0。这需要向所有被flush的elemnt指定一个新的base_time。


    上述的计算方法在pipeline的状态从PLAYING状态设定为PAUSE状态时记录running_time,当从PAUSE状态转变为PLAYING状态后基于absolute_time恢复running_time。针对PAUSE后继续计数的clock,比如system clock,以及PAUSE后不再计数的clock,比如audioclock都是适用的。

    running_time的计算方法如下:

    C.running_time = absolute_time - base_time

    Timestamps

    GstBuffer的timestamps以及NEW_SEGMENT event定义了 buffer timestamps 到 running_time的变换

    B: GstBuffer

    B.timestamp = buffer timestamp (GST_BUFFER_TIMESTAMP)


    NS:  NEWSEGMENT event preceeding the buffers.

    NS.start: start field in the NEWSEGMENT event

    NS.stop: stop field in the NEWSEGMENT event

    NS.rate: rate field of NEWSEGMENT event

    NS.abs_rate: absolute value of rate field of NEWSEGMENT event

    NS.time: time field in the NEWSEGMENT event

    NS.accum: total accumulated time of all previous NEWSEGMENT events. This field is kept in the GstSegment structure.


    符合同步要求的buffers其B.timestamp需在NS.start和NS.stop之间,B.timestamp不在这个范围内的buffers需要丢掉或者进行修正。

    对于running_time存在如下变换:

    if (NS.rate > 0.0)
          B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
    else
          B.running_time = (NS.stop - B.timestamp) / NS.abs_rate + NS.accum

    B.running_time由NEWSEGMENT event以及该segment的buffers得到。

    可显示的第一个buffer的running_time的值为0。

    对于 NS.rate > 1.0,timestamps的值缩小从而使得播放速度加快。

    For negative rates, timestamps are received stop NS.stop to NS.start so that the first buffer received will be transformed into B.running_time of 0 (B.timestamp == NS.stop and NS.accum == 0).

    Synchronisation

    对于running_time的计算方法如下:

    采用clock以及element的base_time:

    C.running_time = absolute_time - base_time

    采用buffer timestamp和其前面的NEWSEGMENT event,假定为正向播放:

    B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum

    这里的前缀C.和B.代表不同的计算方法。

    同步播放的目的就是确保running_time为B.running_time的buffer在C.running_time的时刻播放。

    这需要满足如下条件:

    B.running_time = C.running_time

    也就是:

    B.running_time = absolute_time - base_time


    或者

    absolute_time = B.running_time + base_time

    具有B.running_time的buffer应当被播放时的absolute_time记为B.sync_time,那么:

    B.sync_time = B.running_time + base_time

    这意味着等到clock到了B.sync_time的时候才播放buffer,对于多个流中具有相同的running_time的buffer应该同时播放。

    dumuxer必须确保向输出pads发出的NEWSEGMENT能为buffers生成一样的running_time,从而使之保持同步。通常向pads发出同样的NEWSEGMENT来确保同步的buffer具有一样的timestamp。

    Stream time

    stream time,也称作在流中的位置,是一个在0和媒体文件长度(时间)之间的值。具有如下用途:

    report the POSITION query in the pipeline

    the position used in seek events/queries

    the position used to synchronize controller values


    通过buffer和其前面的NEWSEGMENT event来计算stream time:

    stream_time = (B.timestamp - NS.start) * NS.abs_applied_rate + NS.time

    对于播放速度为负的情况,B.timestamp将从NS.stop 到 NS.start,使得stream time反向。在PLAYING 状态,也可以采用pipeline clock 计算当前的stream_time。 Give the two formulas above to match the clock times with buffer timestamps allows us to rewrite the above formula for stream_time (and for positive rates).

    C.running_time = absolute_time - base_time

    B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum

    =>

    (B.timestamp - NS.start) / NS.abs_rate + NS.accum = absolute_time - base_time;

    =>

    (B.timestamp - NS.start) / NS.abs_rate = absolute_time - base_time - NS.accum;

    =>

    (B.timestamp - NS.start) = (absolute_time - base_time - NS.accum) * NS.abs_rate

     filling (B.timestamp - NS.start) in the above formule for stream time

    =>

    stream_time = (absolute_time - base_time - NS.accum) * NS.abs_rate * NS.abs_applied_rate + NS.time

    最后的计算公式通常是sink用于report当前的position的准确和有效的方式。Note that the stream time is never used for synchronisation against the clock.

    gstreamer中的调度模式

    编译自 GStreamer Plugin Writer's Guide - Chapter 11

    操作系统需要进程调度的功能,人需要通过大脑进行复杂的调度工作一样,Gstreamer为了协调多个elements进行工作,也需要调度功能。在Gstreamer中,调度的任务就是调用pipeline中的每个element处理数据并为下一个element准备数据。

    在gstreamer中有三种调度模式。第一种通过_chain()函数实现调度,采用这种调度模式的elements的sink pad都被设置了一个chain函数,并向souce pads推送buffer。第二种是随机访问的模式,这种调度方式基于_getrange()函数。第三种是task-runner模式,这种模式下,相应的element就是pipeline的"引擎"。

    pad activation阶段

    GStreamer决定采用何种调度模式的阶段叫做pad activation阶段。在这个阶段,GStreamer会通过scheduling capabilities来查询每一个element所支持的调度模式,并决定采用何种调度模式,然后把指定的调度模式notify给每一个pad,让pipeline以指定的调度模式开始运行。

    Pads可被设定为三种调度模式之一,每一种模式对于pads有不同的要求。Pads需要实现一个通知函数,Gstreamer通过这个函数来设定Pads的调度模式。对于被设定为基于pull调度模式的sink pads还要启动自己的任务。

    如果所有的elements被设定为基于push的调度模式,意味着通过chain函数,从upstream把数据push到下一个element。这要求每个element都要调用gst_pad_set_chain_function()对每一个sinkpad设定chain函数

    可以通过设定pad工作在基于pull的调度模式,使之成为pipeline的"引擎"。

    可以设定pipeline中所有的pads为基于push的调度模式。


    Pads driving the pipeline

    Sinkpads设定为基于pull的模式,所有的sourcepads都基于非pull的模式,这样的element可以启动一个任务来驱动pipeline的数据流。所有相关elements have random access over all of their sinkpads,并把数据push到sourcepads。这样的调度适合如下类型的elements:

    Demuxer,parsers以及输入数据是unparsed的特定类型的decoders

    特定类型的audio输出,这种类型的audio输出需要对输入数据进行控制,比如Jack sound server。

    作为"引擎"任务需要在activation函数中创建:

    #include "filter.h"
    #include <string.h>

    static gboolean    gst_my_filter_activate (GstPad * pad);
    static gboolean    gst_my_filter_activate_pull (GstPad  * pad, gboolean active);
    static void gst_my_filter_loop (GstMyFilter * filter);

    GST_BOILERPLATE (GstMyFilter, gst_my_filter, GstElement, GST_TYPE_ELEMENT);

    static void
    gst_my_filter_init (GstMyFilter * filter)
    {
      ...

      gst_pad_set_activate_function (filter->sinkpad, gst_my_filter_activate);
      gst_pad_set_activatepull_function (filter->sinkpad, gst_my_filter_activate_pull);

      ...
    }

    ...

    static gboolean
    gst_my_filter_activate (GstPad * pad)
    {
      if (gst_pad_check_pull_range (pad)) {
        return gst_pad_activate_pull (pad, TRUE);
      } else {
        return FALSE;
      }
    }

    static gboolean
    gst_my_filter_activate_pull (GstPad  *pad,gboolean active)
    {
      GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));

      if (active) {
        filter->offset = 0;
        return gst_pad_start_task (pad,(GstTaskFunction) gst_my_filter_loop, filter);
      } else {
        return gst_pad_stop_task (pad);
      }
    }

    启动后作为"引擎"的任务对输入和输出有完全的控制。最简单的"引擎"就是读取输入并将其push到sourcepads。这样的调度模式和仅仅有chain函数驱动的调度模式相比更加灵活。

    #define BLOCKSIZE 2048

    static void
    gst_my_filter_loop (GstMyFilter * filter)
    {
      GstFlowReturn ret;
      guint64 len;
      GstFormat fmt = GST_FORMAT_BYTES;
      GstBuffer *buf = NULL;

      if (!gst_pad_query_duration (filter->sinkpad, &fmt, &len)) {
        GST_DEBUG_OBJECT (filter, "failed to query duration, pausing");
        goto stop;
      }

       if (filter->offset >= len) {
        GST_DEBUG_OBJECT (filter, "at end of input, sending EOS, pausing");
        gst_pad_push_event (filter->srcpad, gst_event_new_eos ());
        goto stop;
      }

     
      ret = gst_pad_pull_range (filter->sinkpad, filter->offset,BLOCKSIZE, &buf);

      if (ret != GST_FLOW_OK) {
        GST_DEBUG_OBJECT (filter, "pull_range failed: %s", gst_flow_get_name (ret));
        goto stop;
      }

     
      ret = gst_pad_push (filter->srcpad, buf);

      buf = NULL;

      if (ret != GST_FLOW_OK) {
        GST_DEBUG_OBJECT (filter, "pad_push failed: %s", gst_flow_get_name (ret));
        goto stop;
      }

     
      filter->offset += BLOCKSIZE;
      return;

    stop:
      GST_DEBUG_OBJECT (filter, "pausing task");
      gst_pad_pause_task (filter->sinkpad);
    }

    Providing random access

    启动一个自己的进程来作为pipeline引擎的element需能够通过sinkpads进行随机访问,这意味着所有相连的element都应该具备随机访问的功能。elements通过gst_pad_pull_range()函数实现随机访问。sourcepads采用gst_pad_set_getrange_function()设定一个_get_range()函数,在_get_range()函数中支持随机访问。可实现随机访问的elements有:

    Data sources,比如file sources,以极低的延迟访问文件中任意位置的数据。

    Filters

    Parsers


    下面的例子给出了_get_range()函数的实现:

    #include "filter.h"
    static GstFlowReturn
    gst_my_filter_get_range    (GstPad     * pad,
                 guint64      offset,
                 guint        length,
                 GstBuffer ** buf);

    GST_BOILERPLATE (GstMyFilter, gst_my_filter, GstElement, GST_TYPE_ELEMENT);

    static void
    gst_my_filter_init (GstMyFilter * filter)
    {
      GstElementClass *klass = GST_ELEMENT_GET_CLASS (filter);

      filter->srcpad = gst_pad_new_from_template (
            gst_element_class_get_pad_template (klass, "src"), "src");
      gst_pad_set_getrange_function (filter->srcpad,
          gst_my_filter_get_range);
      gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);

      ...
    }

    static gboolean
    gst_my_filter_get_range (GstPad     * pad,
                 guint64      offset,
                 guint        length,
                 GstBuffer ** buf)
    {

      GstMyFilter *filter = GST_MY_FILTER (GST_OBJECT_PARENT (pad));

      [.. here, you would fill *buf ..]

      return GST_FLOW_OK;
    }

    很多理论上可实现随机访问的elements在实际应用当中仅仅实现了基于push的调度模式,因为downstream elements都没有启动自己的任务。实际上应该实现_chain()和_get_range()两个函数,同时还要提供_activate_*()函数用于启动自己的任务,这样Gstreamer就可以选择最优的调度模式。

     

     

     

    RefBase,Bp,Wp解析

     

     

    展开全文
  • androidopenMax的实现

    2018-04-20 15:34:44
    1.android中用openmax来干啥? android中的 AwesomePlayer就是用openmax来做(code)编解码,其实在openmax接口设计中,他不光能用来当编解码。通过他的组件可以组成一个完整的播放器,包括source、demux、decode、...
  • OMX中OMXNodeInstance负责创建并维护不同的实例,这些实例是根据上面需求...1.android系统中只用openmax来做codec,所以android向上抽象了一层OMXCodec,提供给上层播放器用。播放器中音视频解码器mVideosource、...
  • android中的 AwesomePlayer就 是用openmax来做(code)编解码,其实在openmax接口设计中,他不光能用来当编解码。通过他的组件可以组成一个完整的播放器,包括 sourc、demux、decode、output。但是为什么android只用他...
  • Android平台OpenMax多媒体引擎架构

    千次阅读 2013-07-09 17:40:29
    1.android中用openmax来做什么工作?  android的播放器的上层api接口是MediaPlayer,通过JNI调用本地服务MediaPlayrService,本地服务再调用StagefrightPlayer,接着调用AwesomePlayer,而 AwesomePlayer就是用...
  • Android平台OpenMax多媒体引擎介绍

    千次阅读 2013-07-08 09:56:59
    OpenMax是一个多媒体应用程序的框架... 在Android中,OpenMax IL层,通常可以用于多媒体引擎的插件,Android的多媒体引擎OpenCore和StageFright都可以使用OpenMax作为插件,主要用于编解码(Codec)处理。  在Androi
  • 3.AndroidOpenMax的使用情况  Android系统的一些部分对OpenMax IL层进行使用,基本使用的是标准OpenMax IL层的接口,只是进行了简单的封装。标准的OpenMax IL实现很容易以插件的形式加入到Android系统中。 ...
  • OpenMax是一个多媒体应用程序的框架标准。其中,OpenMax IL(集成层)技术规格定义了媒体组件接口,以便在嵌入式器件的流媒体... 在Android中,OpenMax IL层,通常可以用于多媒体引擎的插件,Android的多媒体引擎OpenC
  • 也就是说OMXClient 是androidopenmax 的入口。 在创建音视频解码mVideoSource、mAudioSource的时候会把OMXClient中的sp<IOMX> mOMX的实例 传给mVideoSource、mAudioSource来共享使用这个OMX的入口。 也就是...
  • AndroidOpenMax的适配层

    千次阅读 2011-06-21 14:41:00
    Android中的OpenMax适配层的接口在frameworks/base/include/media/目录中的IOMX.h文件定义,其内容如下所示:class IOMX : public IInterface { public: DECLARE_META_INTERFACE(OMX); typedef void *buffer_...
  • androidopenMax的使用

    2013-04-03 16:50:07
    http://www.360doc.com/content/11/1019/09/11192_157350357.shtml
  • 转载Android Opencore OpenMAX学习(1) 1 OMX core methods 1)OMX_Init 2)OMX_Deinit 3)OMX_GetHandle 4)OMX_FreeHandle 5)OMX_ComponentNameEnum 6)OMX_GetComponentsOfRole 7)OMX_GetRolesOfComponent 8)OMX_...
  • Android-video openMAX详解

    2019-09-16 16:22:13
    本文参考: http://zhoujinjian.cc/2018/09/06/Android%20Video%20System%EF%BC%884%EF%BC%89%EF%BC%9AAndroid%20Multimedia%20-%20OpenMax%E5%AE%9E%E7%8E%B0%E5%88%86%E6%9E%90/index.html 1. 相关代码 /hardware...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 235
精华内容 94
关键字:

androidopenmax