当前位置:首页 > 资讯 > info5 > 正文

Chromium为视频标签全屏播放的过程分析

发表于: 2016-08-29   作者:Luoshengyang   来源:转载   浏览:
摘要:     在Chromium中,标签有全屏和非全屏两种播放模式。在非全屏模式下,标签播放的视频嵌入在网页中显示,也就是视频画面作为网页的一部分显示。在全屏模式下,我们是看不到网页其它内容的,因此标签播放的视频可以在一个独立的全屏窗口中显示。这两种截然不同的播放模式,导致Chromium使用不同的方式渲染视频画面。本文接下来就详细分析标签全屏播放的过程。    从前面Chromium为视频标签渲染视频

       在Chromium中,       从前面Chromium为视频标签

       Surface有两种获取方式。第一种方式是通过SurfaceTexture构造一个新的Surface。第二种方式是从SurfaceView内部获得。在非全屏模式下,Chromium就是通过第一种方式构造一个Surface,然后设置给MediaPlayer的。在全屏模式下,Chromium将会直接创建一个全屏的SurfaceView,然后再从这个SurfaceView内部获得一个Surface,并且设置给MediaPlayer。

       在Android平台上,SurfaceView的本质是一个窗口。既然是窗口,那么它的UI就是由系统(SurfaceFlinger)合成在屏幕上显示的。它的UI就来源于它内部的Surface描述的GPU缓冲区队列。因此,当MediaPlayer将解码出来的视频画面写入到SurfaceView内部的Surface描述的GPU缓冲区队列去时,SurfaceFlinger就会从该GPU缓冲区队列中将新写入的视频画面提取出来,并且合成在屏幕上显示。关于SurfaceView的更多知识,可以参考前面Android视图SurfaceView的实现原理分析一文。

       Surface描述的GPU缓冲区队列,是一个生产者/消息者模型。在我们这个情景中,生产者便是MediaPlayer。如果Surface是通过SurfaceTexture构造的,那么SurfaceTexture的所有者,也就是Chromium,就是消费者。消费者有责任将视频画面从GPU缓冲区队列中提取出来,并且进行渲染。渲染完成后,再交给SurfaceFlinger合成显示在屏幕中。如果Surface是从SurfaceView内部获取的,那么SurfaceView就是消费者,然后再交给SurfaceFlinger合成显示在屏幕中。

       简单来说,在非全屏模式下,       Chromium支持Chromium为视频标签<video>全屏播放的过程分析_第1张图片

图1

     当      接下来,我们就结合源代码,从

从前面Chromium为视频标

void HTMLMediaElement::enterFullscreen()
{
    WTF_LOG(Media, "HTMLMediaElement::enterFullscreen");

    FullscreenElementStack::from(document()).requestFullScreenForElement(this, 0, FullscreenElementStack::ExemptIFrameAllowFullScreenRequirement);
}
       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp中。

 

       在WebKit中,网页的每一个标签都可以进入全屏模式。每一个网页都对应有一个FullscreenElementStack对象。这个FullscreenElementStack对象内部有一个栈,用来记录它对应的网页有哪些标签进入了全屏模式。

       HTMLMediaElement类的成员函数enterFullscreen首先调用成员函数document获得当前正在处理的

      FullscreenElementStack类的成员函数requestFullScreenForElement的实现如下所示:

 

void FullscreenElementStack::requestFullScreenForElement(Element* element, unsigned short flags, FullScreenCheckType checkType)
{
    ......

    // The Mozilla Full Screen API  has different requirements
    // for full screen mode, and do not have the concept of a full screen element stack.
    bool inLegacyMozillaMode = (flags & Element::LEGACY_MOZILLA_REQUEST);

    do {
        ......

        // 1. If any of the following conditions are true, terminate these steps and queue a task to fire
        // an event named fullscreenerror with its bubbles attribute set to true on the context object's
        // node document:
        ......

        // The context object's node document fullscreen element stack is not empty and its top element
        // is not an ancestor of the context object. (NOTE: Ignore this requirement if the request was
        // made via the legacy Mozilla-style API.)
        if (!m_fullScreenElementStack.isEmpty() && !inLegacyMozillaMode) {
            Element* lastElementOnStack = m_fullScreenElementStack.last().get();
            if (lastElementOnStack == element || !lastElementOnStack->contains(element))
                break;
        }

        // A descendant browsing context's document has a non-empty fullscreen element stack.
        bool descendentHasNonEmptyStack = false;
        for (Frame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) {
            ......
            if (fullscreenElementFrom(*toLocalFrame(descendant)->document())) {
                descendentHasNonEmptyStack = true;
                break;
            }
        }
        if (descendentHasNonEmptyStack && !inLegacyMozillaMode)
            break;

        ......


        // 2. Let doc be element's node document. (i.e. "this")
        Document* currentDoc = document();

        // 3. Let docs be all doc's ancestor browsing context's documents (if any) and doc.
        Deque docs;

        do {
            docs.prepend(currentDoc);
            currentDoc = currentDoc->ownerElement() ? ¤tDoc->ownerElement()->document() : 0;
        } while (currentDoc);

        // 4. For each document in docs, run these substeps:
        Deque::iterator current = docs.begin(), following = docs.begin();

        do {
            ++following;

            // 1. Let following document be the document after document in docs, or null if there is no
            // such document.
            Document* currentDoc = *current;
            Document* followingDoc = following != docs.end() ? *following : 0;

            // 2. If following document is null, push context object on document's fullscreen element
            // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
            // set to true on the document.
            if (!followingDoc) {
                from(*currentDoc).pushFullscreenElementStack(element);
                addDocumentToFullScreenChangeEventQueue(currentDoc);
                continue;
            }

            // 3. Otherwise, if document's fullscreen element stack is either empty or its top element
            // is not following document's browsing context container,
            Element* topElement = fullscreenElementFrom(*currentDoc);
            if (!topElement || topElement != followingDoc->ownerElement()) {
                // ...push following document's browsing context container on document's fullscreen element
                // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute
                // set to true on document.
                from(*currentDoc).pushFullscreenElementStack(followingDoc->ownerElement());
                addDocumentToFullScreenChangeEventQueue(currentDoc);
                continue;
            }

            // 4. Otherwise, do nothing for this document. It stays the same.
        } while (++current != docs.end());

        // 5. Return, and run the remaining steps asynchronously.
        // 6. Optionally, perform some animation.
        ......
        document()->frameHost()->chrome().client().enterFullScreenForElement(element);

        // 7. Optionally, display a message indicating how the user can exit displaying the context object fullscreen.
        return;
    } while (0);

    ......
}

       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/dom/FullscreenElementStack.cpp中。

       FullscreenElementStack类的成员函数requestFullScreenForElement主要是用来为网页中的每一个Document建立一个Stack。这个Stack记录了Document中所有请求设置为全屏模式的标签。我们通过图2所示的例子说明FullscreenElementStack类的成员函数requestFullScreenForElement的实现:

Chromium为视频标签<video>全屏播放的过程分析_第2张图片

图2 Fullscreen Stack for Document

       图2所示的网页包含了两个Document:Doc1和Doc2。其中,Doc1通过

 

Chromium为视频标签全屏播放的过程分析

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
CC Layer Tree绘制完成后,会同步到一个新的CC Pending Layer Tree去。同步过程由Compositor线程执
CC Layer Tree绘制完成后,会同步到一个新的CC Pending Layer Tree去。同步过程由Compositor线程执
网页分块的光栅化操作完成后,CC Pending Layer Tree就会激活为CC Active Layer Tree。CC Active La
Chromium在Browser进程中为网页创建了一个Frame Tree之后,会将网页的URL发送给Render进程进行加载
Chromium在加载一个网页之前,需要在Browser进程创建一个Frame Tree。Browser进程为网页创建了Frame
在Chromium中,Render进程是通过Browser进程下载网页内容的,后者又是通过共享内存将下载回来的网页
在配置多进程的情况下,Chromium的网页渲染和JS执行在一个单独的进程中进行。这个进程称为Render进
Chromium除了有Browser进程和Render进程,还有GPU进程。GPU进程负责Chromium的GPU操作,例如Render
前面我们分析了Chromium的Render进程和GPU进程的启动过程,它们都是由Browser进程启动的。在Chromiu
在Chromium中,Render端和WebGL端绘制出来的UI最终是通过Browser端显示在屏幕上的。换句话说,就是B
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号