Chromium VideoCapture的实现

  • javascript中的用法
  • videocapture简图
  • LocalMedia核心数据的分析
    • LocalMedia最上层分析
    • WebCore层的数据结构
    • webkitURL.createObjectURL
    • 几个核心接口
      • webrtc::VideoRendererInterface 
      • webrtc::VideoSourceInterface
      • cricket::VideoCapturer
      • webrtc::VideoTrackInterface
      • 接口间的关系
    • 重点类介绍
      • 重点类及其继承关系
      • 与MediaStreamDescriptor的关系
  • LocalMedia管理层次分析
    • WebCore层的核心管理类
    • WebKit层与Content层对接口的实现
  • VideoCapture的管理分析
    • 重要的数据结构
      • content::MediaStreamDevice
      • content::StreamDeviceInfo
      • 和VideoCapture相关的两个接口
    • StreamCreate相关的消息管理
      • Render端StreamCreate的消息类图
        • 关键类及其作用
        • webkitGetUserMedia的过程
        • 获取Stream后的流程
      • Host端StreamCreate的消息类图
        • 3个主要接口
        • 4个主要类
        • 消息过程
    • capture的进程间的消息管理
      • Render进程capture消息管理类图
      • Host端的capture消息管理
        • 主要类介绍
        • 从Render->Host的消息处理流程
        • 从Host到Render的消息处理流程
    • Shared Buffer
      • Shared Buffer的对应关系
      • Shared Buffer的创建过程
  • video标签播放视频流分析
    • WebMediaPlayerMS类分析
      • 核心类结构图
      • 数据流图
    • 视频帧数据
      • 视频格式及转换
      • VideoFrame对象及数据流
  • Android对VideoCapture的实现
    • Android层接口类图
    • Android层实现的几个关键点

javascript中的用法

首先,打开videocapture对象,使用如下代码

navigator.webkitGetUserMedia({video:true}, gotStream, function() {});

参数依次是:localmedia的配置信息,得到流的回调函数,出现错误时的回调函数。

gotStream的实现是

 

function gotStream(stream) {
    video.src = webkitURL.createObjectURL(stream);
}

video是一个video标签。

stream是获取的stream对象。通过createObjectURL将其转换为url对象,传递给video,实现将流内容显示在video标签上。


videocapture简图

  1. 蓝色表示信令流
  2. 黑色标号部分,表示数据流

数据来自与android的Camera对象,获取的是front摄像头。 由于android的Camera必须设置一个Surface或者SurfaceTexture作为预览的目标,否则,将不能获取任何数据。因此,我在内部创建了一个隐藏的SurfaceTexture对象,唯一的目的是让Camera能够获取数据。

 

Camera获取的是NV21格式(YCrCb420SP),需要转换为YV12(YCrCb420P)。

 

在渲染时,数据格式被分为Y U V 3个面(Pane),每个面对应一个Texture,因此,共有3个Texture, 将这3个Texture混合在一起(通过YUV2RGB的混合矩阵),得到RGB数据,并被显示在GL的FrameBuffer中。

 

以上过程,仅仅将各个模块的关键点给出,省略了很多次要过程。

 

实际上,上述过程设计两个进程:Render和Host进程。 其中的CC(Component Composite) 在Render进程,而GPUHost则在Host进程,它们都在独立线程中运行。

事件通知使用了4种方式:

  1. 函数回调(包括从Java到C的回调)
  2. Chromium的异步回调(通过消息循环实现)
  3. 进程间的通讯(同步通讯)
  4. signal-slot机制

 

LocalMedia核心数据的分析

localmedia的实现是非常庞大而复杂的,线索众多并相互交织。因此,我们必须从上至下,一层一层的进行分析,才能很好的解析清楚。

LocalMedia最上层分析

这是LocalMedia的基本原理。

中间部分,是LocalMedia的核心管理部分。

它的基本流程是:

  1. 从输入设备中得到音频(Macrophone)和视频(camera)数据
  2. 这些数据被抽象为MediaSource对象,交给核心管理部分
  3. 核心管理部分,负责将数据流交给不同的Render
  4. Render和具体的显示或者播放设备连接。对于视频,是针对video标签。

WebCore层的数据结构

WebCore层有两个主要数据:

  • WebCore::MediaStreamDescriptor 该数据结构包含了Video和Audo Source对象
  • WebCore::MediaConstraints 这个是在navigator.webkitGetUserMedia({video:true}, gotStream, function() {}); 中 {video:true}参数代表的对象

WebCore::MediaConstraints由WebCore::MediaConstraintsImpl,这是一个简单的数据结构,它将传递的数据结构转换为一个hash表保存。

 

WebCore::MediaStreamDescriptor稍微复杂些,它的层次结构如下:

 

该数据结构中,重要的数据结构,实际上是MediaStreamSource:ExtraData和MediaStreamDescriptor::ExtraData.

因为,在WebCore这一层,完全不管理具体的音频、视频流,它仅仅管理了两个source,所有和真正Source实现部分,都是保存在MediaStreamSource::ExtraData中的。

而和Render相关的数据,则保存在MediaStreamDescriptor::ExtraData中。

webkitURL.createObjectURL

在js中,得到的stream对象,在C++层面,就是WebCore::MediaStreamDescriptor对象

由于video标签的src属性,只接收URL,因此,无法直接将WebCore::MediaStreamDescriptor对象传递给video标签,所以,使用createObjectURL。

createObjectURL的实现很简单,就是利用特定算法,将WebCore::MediaStreamDescriptor对象计算为一个唯一的字符串,以此字符串为Key,放在hash表中映射WebCore::MediaStreamDescriptor对象。

 

关于具体实现,可以查阅源代码。

 

几个核心接口

以下接口是核心部分(Audo部分的接口参考Video的接口)

  • webrtc::VideoRendererInterface 
  • webrtc::VideoSourceInterface
  • cricket::VideoCapturer
  • webrtc::VideoTrackInterface 

webrtc::VideoRendererInterface 

代码摘要:

// Interface for rendering VideoFrames from a VideoTrack
class VideoRendererInterface {
public:
virtual void SetSize(int width, int height) = 0;
virtual void RenderFrame(const cricket::VideoFrame* frame) = 0;

protected:
// The destructor is protected to prevent deletion via the interface.
// This is so that we allow reference counted classes, where the destructor
// should never be public, to implement the interface.
virtual ~VideoRendererInterface() {}
};

webrtc::VideoSourceInterface

代码摘要

// VideoSourceInterface is a reference counted source used for VideoTracks.
// The same source can be used in multiple VideoTracks.
// The methods are only supposed to be called by the PeerConnection
// implementation.
class VideoSourceInterface : public MediaSourceInterface {
public:
// Get access to the source implementation of cricket::VideoCapturer.
// This can be used for receiving frames and state notifications.
// But it should not be used for starting or stopping capturing.
virtual cricket::VideoCapturer* GetVideoCapturer() = 0;
// Adds |output| to the source to receive frames.
virtual void AddSink(cricket::VideoRenderer* output) = 0;
virtual void RemoveSink(cricket::VideoRenderer* output) = 0;

protected:
virtual ~VideoSourceInterface() {}
};

这里引入的cricket::VideoRenderer接口和webrtc::VideoRendererInterface接口实际上是一样的。

chromium在内部,将对VideoRenderer的调用,转换为对VideoRendererInterface的调用。

之所以提供两个不同的接口,是因为cricket::VideoRenderer是webrtc的第三方库提供的。

cricket::VideoCapturer

该类不是一个纯虚类,而是有部分纯虚函数。下面贴出其纯虚函数:

class VideoCapturer
: public sigslot::has_slots<>,
public talk_base::MessageHandler {
public:
// All signals are marshalled to |thread| or the creating thread if
// none is provided.
VideoCapturer();
explicit VideoCapturer(talk_base::Thread* thread);
virtual ~VideoCapturer() {}

......

virtual CaptureState Start(const VideoFormat& capture_format) = 0;

// Stop the video capturer.
virtual void Stop() = 0;
// Check if the video capturer is running.
virtual bool IsRunning() = 0;

....

};

webrtc::VideoTrackInterface

代码摘要

class VideoTrackInterface : public MediaStreamTrackInterface {
public:
// Register a renderer that will render all frames received on this track.
virtual void AddRenderer(VideoRendererInterface* renderer) = 0;
// Deregister a renderer.
virtual void RemoveRenderer(VideoRendererInterface* renderer) = 0;

// Gets a pointer to the frame input of this VideoTrack.
// The pointer is valid for the lifetime of this VideoTrack.
// VideoFrames rendered to the cricket::VideoRenderer will be rendered on all
// registered renderers.
virtual cricket::VideoRenderer* FrameInput() = 0;

virtual VideoSourceInterface* GetSource() const = 0;

protected:
virtual ~VideoTrackInterface() {}
};

VideoTrackInterface是一个内部使用的接口,这个接口主要负责将其他接口链接起来。

接口间的关系

可以用下图来描述(仅表示其逻辑关系,不代表代码真实情况。这种逻辑关系,由他们的实现类体现出来)

核心管理的两个接口是:

  • VideoTrackInterface
  • VideoSourceInterface

其中VideoSourceInterface负责管理Capturer和Renderer,将两者的数据贯穿起来;

而VideoTrackInterface的作用,是将VideoRenderererInterface转换为VideoRenderer接口。而且,将对一个VideoRenderer的调用,转换为对多个VideoRendererInterface的调用。

 

重点类介绍

本节介绍几个重点类。这些类是上节提到的几个接口的实现类,以及他们和MediaStreamDescriptor如何组合在一起的。这种关系,是实现localmedia的核心所在。

重点类及其继承关系

图例说明:

  1. 绿色部分表示重要的接口
  2. 蓝色是实现这些接口的类
  3. 蓝色虚线箭头,表示类直接的包含关系。与普通的包含不同,蓝色虚线表示它们通过包含类的接口指针来包含这个对象。

与MediaStreamDescriptor的关系

  1. WebKit::WebMediaStreamDescriptor::ExtraData对应的是WebCore::MediaStreamDescriptor::ExtraData, 同样的,WebKit::WebMediaStreamSource::ExtraData对应的是WebCore::MediaStreamSource::ExtraData。 chromium为了防止直接使用WebCore命名空间的内容,因此,增加了一个WebKit的命名空间,对应的类,都加上了Web前缀。 这样,WebCore::MediaStreamDescriptor实际上通过ExtraData掌握了Source和Render两种数据
  2. 具体的实现,是在content空间内
  3. 为了同时掌握audio和video,它增加了一个webrtc::LocalMediaStreamInterface。(另外有一个webrtc::RemoteMediaStreamInterface,用于处理远程render)

 

LocalMedia管理层次分析

LocalMedia的层次大约有WebCore、WebKit和content三个层次,其中,content又分为render和host两个进程。

WebCore层次主要提供面向javascript和标签的接口;content主要提供具体实现;WebKit层其实是给WebCore层穿了个马甲,隔离WebCore和content层。

 

我们重点分析的是WebCore和content层。

WebCore层的核心管理类

整个核心,有3个类:

  • WebCore::UserMediaController,它提供了接口 requestUserMedia,提供查询和打开设备的接口。UserMediaController和UserMediaClient是一回事。UserMediaController的所有实现,委托给UserMediaClient来实现。
  • UserMediaRequest 提供两个重要函数:
    • start:它调用UserMediaController的requestUserMedia函数,实现对设备查询和打开;
    • succeed: 这是接收requestUserMedia的查询结果的(异步),该函数将收到一个MediaStreamDescriptor类,并将调用js层提供的 gotStream函数。
  • MediaStreamCenter是WebCore对外通讯的接口,该类由外部实现,并负责将结果回调给UserMediaRequest。

 UserMediaClient和MediaStreamCenter是必须由外部提供和实现的。

WebKit层与Content层对接口的实现


该图按照3个层次划分了各个类。其中最重要的是以下3个类

  • content::MediaStreamImpl,它实际上,承担的是WebCore::MediaUserClient的任务,它的任务是
    • 实现requestUserMedia函数,负责创建和打开音频和视频设备,并捕获它们的流;
    • 它捕获设备打开的结果,并通过WebKit层,调用WebCore::UserMediaRequest的successed和failed函数,告诉js层成功或者失败。它通过content::UserMediaRequestInfo,将MediaStreamDescription和UserMediaRequest这两个对象关联在一起,一同调用;
    • 它担负起对音频设备和视频设备的管理职责(VideoCaptureManager)
  • MediaStreamCenter:它主要担负起对VideoSourceInterface和VideoTrackInterface的创建工作。并实现了WebCore:MediaStreamCenter的接口实现。
  • content::MediaStreamDependencyFactory 这是整个创建Render, Source, Track的类,是整个创建过程的核心中的核心。该类虽然庞大,但是功能单一,就是作为各种类的创建工厂存在的。

 

VideoCapture的管理分析

对于真实设备的管理,是整个Localmedia的核心。在这一节中,我们重点讨论对真实的Video设备的管理。这些全部的内容,都在content层实现的。

 

这部分管理分为两部分:

  • 针对Capture对象的创建和销毁的管理
  • Capture消息本身的管理

这两类消息是通过不同的方法实现的。

重要的数据结构

content::MediaStreamDevice

它有3个重要的数据结构

  • MediaStreamType type; 表示这是个音频还是视频设备
  •  std::string id; 设备的唯一ID,通过这个ID可以打开或者关闭设备
  • std::string name; 设备的名称,是提供给人看的

content::StreamDeviceInfo

它有两个数据结构,其中一个,是MediaStreamDevice;另外一个,是 int session_id。这个session_id,代表了一个具体设备,是用于和Host端通讯使用的。

和VideoCapture相关的两个接口

接口cricket::VideoCapturer和media::VideoCapture(注意,一个后有"r",一个没有)。这两个对象,实际上是一回事,但是针对的不同的层次。cricket::VideoCapturer主要针对webrtc和上层关系,而media::VideoCapture主要针对Host,和Host进行通讯。

它们的关系很密切,如下图:

  • content::RtcVideoCapturer和content::VideoCaptureImpl是两个实现接口的对象
  • content::RtcVideoCaptureDelegate将两个对象连接在一起
  • content::VideoCaptureImplManager负担有创建VideoCaptureImpl、管理和host消息通讯的职责
  • media::VideoCapture::EventHandler将接收VideoCaptureImpl提供的事件回调,最主要的是收到数据状态变化
  • RtcVideoCaptureDelegate收到消息后,将通过回调,调用到RtcVideoCapturer中的成员函数:OnFrameCapturedOnStateChanged中。

StreamCreate相关的消息管理

Render端StreamCreate的消息类图

关键类及其作用
  • MediaStreamImpl 这个类是实现UserMediaControlClient的类,响应requestuserMedia调用
  • MediaStreamDispater,这个类是实现和Host端通讯的类
  • RenderViewImpl 它是MediaStreamImpl和MediaStreamDispater的工厂类,负责创建它们。

MediaStreamImpl直接包含了MediaStreamDispatcher类。 MediaStreamDispatcher类通过MediaStreamDispatcherEventHandler将Host的回馈消息发送给MediaStreamImpl。

MediaStreamImpl通过UserMediaRequestInfo保存对UserMediaRequest的引用。

webkitGetUserMedia的过程

当javascript代码调用webkitGetUserMedia的时候,

  1. WebCore::UserMediaRequest的didCompleteQuery被调用(通过MediaStreamCenter实现的)
  2. content::MediaStreamImpl的requestUserMedia将会被调用(通过WebCore::UserMediaControl的UserMediaControlClient实现的,中间透过了WebKit层)
  3. 调用MediaStreamDispatcher的GenerateStream
  4. MediaStreamDispatcher向Host发送MediaStreamHostMsg_GenerateStream消息。

 

获取Stream后的流程
  1.  Host发送MediaStreamMsg_StreamGenerated,由MediaStreamDispatcher接收
  2. MediaStreamDispatcher调用MediaStreamImpl的OnStreamGenerated
  3. MediaStreamImpl将创建Webkit::WebMediaStreamDescriptor及对应的WebMediaStreamSource对象
  4. 通过UserMediaRquestInfo,最终调用到UserMediaRquest的succeed函数,(中间经过若干函数调用,而且为异步调用)
  5. succeed函数最终会调用javascript提供的gotStream函数,其中,stream就是 WebMediaRequestDescriptor对象。

Host端StreamCreate的消息类图

3个主要接口
  • content::MediaStreamRequester :用于接收设备和流创建情况的接口,被MediaStreamDispachterHost所实现
  • content::MediaStreamProviderListener:用于接收音频和视频设备创建成功与否与打开成功与否的接口
  • content::SettingsRequester:用于接收用户反馈结果的接口
4个主要类
  • MediaStreamDispatcherHost : 和Render端通讯类,负责发送和接收消息
  • MediaStreamManager:中心类,负责查询、创建、询问的类
  • VideoCaptureManager:负责查询和创建视频设备的类
  • MediaStreamUIController:负责和用户打交道,询问用户是否启用本地设备的类

media::VideoCaptureDevice是代表一个视频设备的类。

 

消息过程

整个过程分为两个部分:

  • 查询设备,从步骤4~7 蓝色部分 ,
    • 第4步骤,在MediaStreamManager中的调用过程是:HandleRequest > StartEnumeration > StartMonitoring
  • 打开设备,从步骤8~18红色部分
    • 第8步骤,MediaStreamManager通过PostRequestToUI调用
    • 第8~12步骤,主要是请求用户是否同意打开设备(目前的实现,直接内部同意了)
    • 第13~16步骤,是调用VideoCaptureManager来创建设备。 注意第16步骤由VideoCaptureManager::PostOnOpened调用

中间有若干部分都是异步的。

 

capture的进程间的消息管理

Render进程的消息,有两个关键类来管理 content::VideoCaptureImpl和content::VideoCaptureMessageFilter。这两个类,其中VideoCaptureImpl负责发送消息,而VideoCaptureMessageFilter负责接收消息。

Render进程capture消息管理类图

  • content::VideoCaptureImplManager是个类工厂。它负责创建Thread, filter和capture对象。  对VideoCaptureImpl对象,它使用SessionID为key,管理多个capture对象。
  • VideoCaptureMessageFilter负责接收来自Host的消息
  • VideoCaptureImpl负责向Host发送消息
  • VideoCaptureMessageFilter通过Delegate将收到的消息传递给VideoCaptureImpl

 

消息的传递和接收,必须严格在IO线程内部。而对capture的所有处理,应该在capture线程

IO线程来自与ChildProcess,而对应的message_loop_proxy就是 io_message_loop_proxy_

Capture线程来自 VideoCaptureManagerImpl的thread_,对应的message_loop_proxy就是 capture_message_loop_proxy_


VideoCaptureImplManager对象隶属于RenderThreadImpl类,并将VideoCaptureMessageFilter添加到RenderThreadImpl的Filter当中。只有添加到RenderThreadImpl的filter中,VideoCaptureMessageFilter才能接收到来自Host的消息

Host端的capture消息管理

主要类介绍
  • content::VideoCaptureHost 这是负责接收和发送消息的,和VideoCaptureImpl类对应
  • content::VideoCaptureManager,Capture的管理类,担负两项职责:
    • 作为类工厂,创建和管理 media::VideoCaptureDevice对象和VideoCaptureController对象。
    • 作为中转类,负责隔离其他类对VideoCaptureDevice类的调用
  • content::VideoCaptureController,这个类的主要职责和获取的数据相关:
    • 维护和VideoCaptureImpl的共享内存
    • 将从VideoCaptureDevice获取的数据转换为YV12格式
从Render->Host的消息处理流程
  1. VideoCaptureHost得到消息,调用VideoCaptureManager处理(这里单例模式
  2. VideoCaptureManager负责VideoCaptureController和VideoCaptureDevice
  3. 当VideoCaptureManager创建了必须的对象后,VideoCaptureHost调用VideoCaptureController进行进一步的处理
  4. VideoCaptureController负责准备好必须的数据,包括共享内存,等
  5. 一旦准备好后,VideoCaptureController调用VideoCaptureManager的相关接口
  6. VideoCaptureManager将这种调用,转换为对VideoCaptureDevice的调用
  7. VideoCaptureDevice和OS的设备进行交互。

 

从Host到Render的消息处理流程
  1. VideoCaptureDevice从设备获取到数据后,交给VideoCaptureController(实现了VideoCaptureDevice::EventHandler接口)
  2. VideoCaptureController将进行数据拷贝和转换,在Android中,将NV21转换为YV12,并存入共享内存中
  3. VideoCaptureController发送BufferReady消息给VideoCaptureHost (实现了VideoCaptureControllerEventHandler接口)
  4. VideoCaptureHost将消息发送给VideoCaptureImpl

 

Host和Render在传送消息时,通过SessionID(有的类里称为DeviceID,或者是DeviceControllerID等)来标示一个唯一的设备。


Shared Buffer

这一节,我们将探讨Shred Buffer的创建和使用的相关细节。

Shared Buffer的对应关系

在Render端,Shared Buffer由VideoCaptureImpl管理;在Host端,SharedBuffer由VideoCaptureController管理。

VideoCaptureImpl:: DIBBuffer对象和VideoCaptureController::SharedDIB 两个类的定义完全一样。

核心是对象base::SharedMemory对象,它负责将创建并管理共享内存。

Shared Buffer的创建过程

SharedBuffer的创建过程是从VideoCaptureDevice对象的Allocate方法开始的。

Allocate方法,是要求VideoCapture创建设备,并为止分配足够的空间。


  • 在DoFrameOnIoThread中,创建SharedDIB,同时创建了共享内存。
  • 在VideoCaptureControl的OnBufferCreated中,创建了DIBBuffer,并和SharedDIB共享了内存 

 

video标签播放视频流分析

WebMediaPlayerMS类分析

WebMediaPlayerMS派生自WebKit::WebMediaPlayer,它给HTMLVideoElement类提供一个Player对象。

 

WebMediaPlayerMS在函数RenderViewImpl::createMediaPlayer中被创建。

 

WebMediaPlayerMS表示获取并播放流对象。WindowMediaPlayer有几个重要的函数:

  • load 这是根据URL创建流对象的函数
  • OnFrameAvailable 该函数作为回调,提供给流对象,让流对象发送数据给WebMediaPlayerMS
  • getCurrentFrame : 这是被CC调用的函数,提供给VideolayerImpl对象,其中包含了视频数据,即YV12格式。

核心类结构图

 

 

RTCVideoRenderer一方面实现了接口VideoRendererInterface,从而能够接收来自本地/远程的视频流,另外一方面,实现了VideoFrameProvider接口,能够将这种数据传递给WebMediaPlayerMS。

 

RTCVideoRender通过回调函数WebMediaPlayerMS::VideoFrameProvider

 

数据流图

WebMediaPlayerMS在得到数据后,调用Repaint,该函数最终会引起CC层的重新调度。在调度时,VideoLayerImpl就会调用getCurrentFrame获取当前需要绘制的视频帧。

在CC层的处理下,数据最终被绘制到了屏幕上。

VideoLayerImpl和一个video标签是对应的,因此,它能够获得video标签的位置并绘制。

 

视频帧数据

视频格式及转换

视频数据是YV12格式(详细参阅 YUV http://zh.wikipedia.org/wiki/YUV)。从 android的获得的NV21。

YV12实际上YUV420P,而NV21则是YUV420SP。 ‘S’的含义是交错格式。即U分量和V分量交错存储。P为Plane,表示一个平面。YUV有3个Plane,分别是Y、U(Cb)、V(Cr)

YUV 也可以写为YCbCr

所谓420,含义4个Y,共有一个UV。 即, Y00, Y10, Y01, Y11 共有U00, V00。

 

YV12的示意图

YU12和YV12属于YUV420格式,也是一种Plane模式,将Y、U、V分量分别打包,依次存储。其每一个像素点的YUV数据提取遵循YUV420格式的提取方式,即4个Y分量共用一组UV。注意,上图中,Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00,其他依次类推。

NV21的示意图

NV12和NV21属于YUV420格式,是一种two-plane模式,即Y和UV分为两个Plane,但是UV(CbCr)为交错存储,而不是分为三个plane。其提取方式与上一种类似,即Y'00、Y'01、Y'10、Y'11共用Cr00、Cb00

VideoFrame对象及数据流

表示YUV数据的对象,是VideoFrame对象,但是,在不同的阶段,相同的数据,数据对象类却是不一样的。为了防止造成误解,我把从VideoCapture到MediaPlayer这个过程中,涉及的数据格式和类,用图表表示出来,如下:

左边为设备管理对象,虚线表示函数调用;右边为数据对象,箭头线表示数据转换的方向和方法。

其中,

  • VideoCaptureImpl到RtcVideoCapturer中间通过了RtcVideoCaptureDelegate的回调实现。可以查阅RtcVideoCapturer::Start函数,回调函数在此安装;
  • RtcVideoCapturer到VideoCapturer的调用,是通过signal-slot完成的。
  • 从VideoCapturer到CaptureRenderAdapter的OnVideoFrame调用,也是通过signal-slot完成。
  • 从RTCVideoRenderer到WebMediaPlayerMS的调用,是通过回调函数完成的,参阅函数安装点:WebMediaPlayerMS::load函数。

 

Android对VideoCapture的实现

Chromium暴露给OS层的接口,并要求OS层实现的接口,仅有media::VideoCaptureDevice。只要实现该接口,就能实现对视频设备的支持。

Android层接口类图

  • 黄色部分:为java的C++包装类
  • 蓝色部分:为java对应的类

Android层实现的几个关键点

  1. java的VideoCaptureAndroid类中,预览android.hardware.Camera时,必须创建一个SurfaceTexture否则不能启动预览;
  2. media::VideoCaptureDeviceAndroid的函数Start,等必须在UI线程中调用,否则会出现崩溃。

 

 

你可能感兴趣的:(Chromium VideoCapture的实现)