您的位置:首页 > Web前端 > HTML5

多媒体

2016-07-12 23:56 1206 查看
本章将结合Web前端的发展历程和未来的发展前景详解现在HTML5中引入的多媒体技术

HTML5的多媒体支持

在HTML5规范出来之前,网页对视频和音频播放的支持基本上是靠Flash插件来实现,在HTML5之后,同文字和图片一样,音频和视频直接变成HTML一系列规范中第一等公民,首先是JavaScript接口的支持,开发者可以使用JavaScript接口来方便的控制音视频的播放,实现例如播放、停止和记录等功能,其次HTML5中多媒体与图片一样可以用其他技术来进行操作,例如使用CSS技术来修改它的样式,例如3D变形,Web开发者可以将视频同Canvas2D或者WebGL结合在一起,而Flash插件中的视频是不能做到的。在HTML5中,对于多媒体的支持大致可以包括一下几个部分:第一是HTML的元素“video”,他用于音视频的播放;第二是“audio”,它用于单纯的音频播放;第三是可以将多个声音合成处理的WebAudio技术;第四是将照相机、麦克分与视频、音频和通信结合起来使用的最新技术WebRTC(网络实时通信),这使得Web领域使用视频对话和视频网络会议成为了现实。

视频

HTML5视频

在HTML5规范定义中,Web开发者可以使用“video”元素来播放视频资源,其中视频涉及到视频编码格式,目前标准中包含了三种编码格式:Ogg、MPEG4和WebM,其中Ogg使用Theora作为视频编码格式和Vorbis作为音频编码格式,MPEG4使用H.264作为视频编码格式和AAC作为音频编码格式,WebM是有Google研发的标准,使用VP8作为视频编码格式和Vorbis作为音频编码格式。HTML提供了一些属性让开发者来使用JavaScript代码检查和操作视频,HTML5在“video”和“audio”元素之间抽象了一个基类元素“media”,结合它提供的能力,大致有一下几个方面的JavaScript编程接口,首先是资源加载和信息方面的接口,开发者可以通过特定接口检查游览器支持什么格式,如Metadata和海报(Poster)等,其次是缓冲(Buffering)处理,包括缓冲区域、进度等信息,然后是播放方面的状态,包括播放、暂停、终止等。再次是搜寻(Seeking)方面的信息,包括设置当前时间、“Timeupdate”事件,以及两个状态“Seeking”和“seeked”,最后是音量方面的设置,包括获取和设置音量、静音和音量变换等事件。

WebKit基础设施

WebKit提供了支持多媒体规范的基础框架,如音视频元素、JavaScript接口和视频播放等,根据WebKit的一般设计思想,它主要是提供标准的实现框架,而具体的实现有各个移植类来完成,因为音视频需要平台的支持,下图显示了各个类和它们之间的关系,也包括了Chromium移动的几个基础类:



首先WebKit是支持规范定义的编程接口,图中左侧的HTMLMediaElement和HTMLVideoElement类是DOM树中的节点类,包括众多的DOM接口,这些接口可以被JavaScript代码访问;其次是MediaPlayer和MediaPlayerClient两个类,MediaPlayer类是一个公共标准累,被HTMLMediaElement类使用来播放音频和视频,它本身支持提供抽象接口,具体实现依赖于不同的WebKit移植,同时一些播放器中的状态信息需要通知到HTMLMediaElement类,这里使用MediaPlayerClient类来定义这些有关状态信息的接口,HTMLMediaElement类需要继承MediaPlayerClient类并接收这些状态信息,根据前面的描述,规范要求将事件派发到JavaScript代码中,而这个实现在HTMLMediaElement类完成,然后是不同移植对MediaPlayer类的实现,其中包括MediaPlayerPrivateInterface类和WebMediaPlayerClientImpl类,前者是除了Chromium移植之外使用的标准接口也是一个抽象接口,由不同移植来实现,后者是Chromium移植的实现类,因为Chromium将WebKit复制出Blink之后就将MediaPlayerPrivateInterface类直接移除了,而在MediaPlayer类中直接调用它,WebMediaPlayerClientImpl类会使用Chromium移植自己定义的WebMediaPlayer接口类来作为实际的播放器,而真正的播放器则是在Chromium项目的代码中来实现,最后同渲染有关,这里就是之前介绍的RenderObject树和RenderLayer树,图中的RenderMedia类和RenderVideo类是RenderObject的子类,用于表示Media节点和Video节点。

Chromium视频机制

资源获取

由于视频资源相对其他资源而言比较大,当用户播放视频的时候需要连续性播放以获得较好的体验,但是网络可能并不是移植都稳定和告诉,所以资源的获取对用户体验很重要,需要使用缓存机制或者其他机制来预先获取视频资源。下图是Chromium中的缓存资源类,BufferedDataSource类表示资源数据,它是一个简单的数据表示类,内存包含一个较小的内存空间(32K),实际的缓冲机制由BufferedResourceLoader类完成,在Chromium的设置中,最小的缓存空间是2M内存,最大的缓存空间是20M,并没有使用磁盘来缓存视频资源:



基础设施

下图是chromium中支持硬件加速机制的视频播放所需基础设施的总体架构图,基于Chromium的多进程结构:



根据多进程架构的设计原则,Chromium的媒体播放器的实现应该在Renderer进程,而对于资源的获取则是在Browser进程,其中WebKit基础设施需要每个移植的具体实现,因此WebKit的Chromium移植部分提供了桥接接口,并且实现则是在Chromium代码中来完成,Chromium支持媒体播放器的具体实现涉及到不同的操作系统,目前Chromium在不同操作系统上实现的媒体播放器也不一样,下图显示了Chromium的基础类:



上半部分是WebKit和WebKit的Chromium移植中的相关类,下半部分是Chromium中使用硬件加速机制来实现视频播放的基础设施类,从做到分开来看,左边部分是播放器的具体实现类,右边部分是支持视频在合成器中工作的相关类。

首先看下这些类和对象的创建过程,WebMediaPlayerClientImpl类是WebKit在创建HTMLMediaElement对象之后创建MediaPlayer对象的时候有MediaPlayer对象来创建的,当视频资源开始加载时,WebKit创建一个WebMediaPlayer对象,当然就是Chromium中的具体实现类WebMediaPlayerImpl对象,同时WebMediaPlayerClientImpl类也实现了WebMediaPlayerClient类,所以WebMediaPlayerImpl在播放视频的过程中需要向该WebMediaPlayerClient类更新各种状态,这些状态信息最终会传递到HTMLMediaElement类中,最终可能成为JavaScript事件,之后WebMediaPlayerImpl对象会创建一个WebLayerImpl对象,海湖同时创建VideoLayer对象,根据合成器的设计,Chromium还有一个LayerImpl树,在同步的时候,VideoLayer对象对应的VideoLayerImpl对象会被创建,之后Chromium需要创建VideoFrameProviderClientImpl对象,该对象将合成器的Video层同视频播放器联系起来并将合成器绘制一帧的请求转给提供视频内容的VideoFrameProvider类,这实际上是调用Chromium的媒体播放器WebMediaPlayerImpl,因为它是一个VideoFrameProvider类的实现子类,然后是Chromium如何使用这些类来生成和显示每一帧,当合成器调用每一层来绘制下一帧的时候,VideoFrameProviderClientImpl::AcquireLockAndCurrentFrame()函数会被调用,然后该函数调用WebMediaPlayerImpl类的GetCurrentFrame函数返回当前一帧的数据,VideoLayerImpl类根据需要会将这一帧数据上传到GPU的纹理对象中,当绘制完这一帧之后,VideoLayerImpl调用VidelFrameProviderClientImpl::PutCurrentFrame来通知播放器这一帧已绘制完成,并释放掉相应的资源,同时,媒体播放器也可以通知合成器有一些新帧生成,需要绘制出来,它会首先调用播放器的VideoFrameProvider::DidReceiveFrame()函数,该函数用来检查当前有没有一个VideoLayerImpl对象,如果有对象存在,需要设置它的SetNeedsRedraw标记位,这样合成器就知道需要重新生成新的一帧,最后是有关视频播放对象的销毁过程,有多种情况使Chromium需要销毁媒体播放器和相关的资源,如“video”元素被移除或者设置为隐藏等,这样视频元素对应的各种层对象以及WebKit和Chromium中的这些设施都会被销毁,WebMediaPalyerImpl类是多媒体播放器的具体实现类,在Chromium项目中,随着对Android系统的支持,Chromium既能支持左面系统也能支持移动系统,而这两者对视频和音频的支持很不一样,所以在不同系统上WebMediaPlayerImpl是如何实现和工作的也很不一样。

桌面系统

在桌面系统中,Chroimum使用了一套多媒体播放框架,而不是直接使用系统或者第三方库的完整解决方案,下图是Chromium在桌面系统上采用的多媒体播放引擎的工作模块和过程,这一框架称为多媒体管线化引擎,图中主要的模块四号多路分配器、音视频解码器、音视频渲染器,这些部分主要被WebMediaPlayerImpl类调用:



在处理音视频的管线化过程中,需要解码器和渲染其来分别处理视频和音频数据,它们均采用一种叫做“拉”而不是“推”的方式进行,也就是说有视频或者音频渲染器根据声卡或者时钟控制器,按需求来请求解码器解码数据,然后解码器和渲染器又向前请求“拉”数据,直到请求从视频资源文件读入数据,根据之前的多进程架构和Chromium的安全机制,整个管线化引擎虽然在Render进程中,但是由于Render进程不能访问声卡,所以渲染器需要通过IPC将数据或者消息同Browser进程通信,由Browser进程来访问声卡。虽然FFmpeg多媒体库拥有上述管线化的能力,但Chromium并不是将其作为一个黑盒来使用,而是分别使用FFmpeg的不同模块来实现自己的管线化引擎,目的是由自身来控制这一整个过程。Chromum使用并行FFmpeg解码技术,也就是说FFmpeg能够在帧这个层面上并行解码,当然不是针对所有格式的视频文件,目前主要针对H.264这个格式的视频。

Android系统

Chromium使用的是Android系统所提供的android.media.MediaPlayer类,也就是使用系统提供的音视频的渲染框架,在减少了管线化引擎带来复杂性的同时,也引入了一些额外的复杂问题。Android中的Chromium彻底抛弃了FFmpeg,直接使用系统自带的多媒体功能,因而,Android系统支持什么样的音视频格式,Chromium就只能支持什么样的相应格式,同时由于Android多媒体框架的优点使得视频元素仍然能够同HTML5中的其他技术一起工作。

Ⅰ Android媒体播放框架

Android中使用一个名为“MediaService”的服务进程来为应用程序提供音频和视频的播放功能,对于每一个使用多媒体播发功能的应用程序来说,“MediaService”服务是透明的,因为Android系统提供了“MediaService”的封装接口,这些接口隐藏了“MediaService”服务内部的细节,应用程序只是使用了简单的播放接口。MediaService能够为多个播放器提供服务,对于播放器来说,它的主要设置为两个参数,其一是输入的URL,第二是输出结果的绘制目标,下图描述了Android的播放器类和相关类:



当应用程序使用播放器的时候,Chromium可以创建MediaPlayer类的对象,调用setDataSource函数来设置待播放视频文件,并调用setSurface来设置视频结果绘制的目标-SurfaceTexture对象,这是一个GL的纹理对象,实际的解码和绘制是在MediaService进程中完成,这需要该纹理对象能够被多个不同的GL上下文对象所访问,支持多个GL上下文对象访问的GL纹理对象的类型GL_TEXTURE_EXTERNAL_OES,由此可以看到Chromium使用Android系统提供的音视频播放功能,表示Chromium使用Android系统的音视频解码器,所以Chromium是依赖与Android系统支持的音视频编码格式,而不像Chromium桌面版独立与操作系统的音视频编码格式。

Ⅱ Chromium的视频解决方案

在Android系统上,因为Chromium使用系统的多媒体框架,所以没有自己的管线化引擎,主要的工作是将Chromium的架构同Android多媒体框架结合起来以完成对网页中视频和音频的播放。下图是Chromium的Android系统上支持音频和视频播放的播放器主要类,因为Chromium的多进程架构,所以这里面包括两大部分,首先是右侧的Render进程的相关类,根据前面Chromium的桌面版上支持多媒体的相关类,可以看到WebKit::WebMediaPlayer类和WebMediaPlayerClient类来自于WebKit的Chromium移植,这两个类在所有平台上的定义都是一样的:



上图中右侧的Render进程,从上向下首先是WebMediaPlayerAndroid类,它同之前的WebMediaPlayerImpl类相似,表示的是Android系统上网页中的播放器,同video元素是一一对应的,与桌面系统不一样的是,Android系统使用RendeerMediaPlayerManager类来管理所有的WebMediaPlayerAndroid对象,因为一个网页中可能包含多个播放器实例,而WebMediaPlayerProxyAndroid则是同Browser进程来通信的,因为真正的Android播放器是在Browser进程中,主要请求Browser进程创建实际的Android的MediaPlayer类并设置播放文件的信息,左侧则是实际的播放器,在JNI(Java Native Interface)之上的是Java类,该播放器就是使用Android系统的android.media.MediaPlayer及其相关类来工作的,从下向上看首先是BrowserMediaPlayerManager类,该类不仅负责同Render进程的播放器类进行通信,而且自身又是一个播放器的管理类,它包含当前全部网页中的所有播放器对象,因为可能会有多个Render进程,所以只能通过播放器的唯一标记来区分这些播放器,BrowserMediaPlayerManager类管理称为MediaPlayerAndroid类的多个对象,而MediaPlayerAndroid的子类MediaPlayerBridge则是具体实现类,该子类能够与Java层中相同名字类通过JNI调用来控制Android系统的播放器类,以上的基本过程就是如何在网页中创建一个播放器,从右向左直到android.media.MediaPlayer对象被创建,同时Chromium获取网页中设置的视频文件的URL字符串然后传递并设置该URL字符串到Android的媒体播放器作为输入,对于输出Chrome使用SurfaceTexture对象作为输出目标,当Chromium调用WebMediaPlayerAndroid类的play函数时,该函数发起请求从Render进程到Browser进程来创建输出目标,也就是SurfeceTexture对象,下图描述了这个过程中使用到的主要类和之间的关系:



右侧的Render进程,最上面的StreamTexureFactoryImpl是创建目标结果存储空间的类,它被WebMediaPlayerAndroid类使用来创建所需要的结果存储对象StreamTexture,由于实际的对象是在Browser进程中创建的,所以Render进程中的StreamTextureProxy类就是一个代理类,最后的请求是通过GPUChannelHost类传递给Browser进程。在Browser进程中,负责处理上述请求的是GPU线程,该线程有StreamTextureManagerAndroid类处理所有创建StreamTexture对象的请求,StreamTexture对象的直接使用者是GPU线程,Render进程需要区分和标识这些StreamTexture对象,具体的方法是使用整形标记符来表示Browser进程中的各个StreamTexture对象,StreamTexture和StreamTextureManager是基础抽象类,在Android系统上,StreamTextureAndroid和StreamTextureManagerAndroid是实际的实现类,StreamTextureAndroid表示的是C++端的桥接类,它包含一个SurfaceTexture对象,该对象会在Java端创建一个android.graphics.SurfaceTexture对象,Chromium设置该对象到MediaPlayer对象作为播放器的输出目标,当视频播放器将解码后的结果写入到SurfaceTexture中后,播放器需要告诉Chromium游览器这一信息,Chroimum游览器需要执行合成操作而合成器在Render进程中,同之前创建SurfaceTexture对象的调用过程正好相反,这里需要使用回调机制,这就是Java层SurfaceTextureListener类的作用,该回调类注册Java层的回调对象到创建好的SurfaceTexture对象,当该对象被写入新的帧的时候,Chromium首先从Browse进程中的Java层将这一回调动作通过JNI到C++层的SurfaceTextureListener类的FrameAvailable函数,该函数经过StreamTextureAndroid和StreamTextureManagerAndroid类最后发送到Render进程,Render进程的调用过程如下:



网页中的视频播放有两种模式,其一是嵌入式模式,其二是全屏模式,这两种模式在解码后结果的处理上是不一样的,下图描述了全屏模式创建视频结果的绘制目标的相关类和过程:



当播放器进入全屏模式的时候,Chromium使用ContentVideoView类来管理,该类会创建一个SurfaceView对象并将对象传递给C++端的ContentVideoView类,因为统一时刻只有一个播放器是全屏模式,而且BrowserMediaPlayerManager管理所有的MediaPlayer对象,该管理类能够知道哪个对象是全屏模式,并将该SurfaceView对象设置到相应的MediaPlayer对象中去。

字幕

视频需要字母的支持,W3C组织已经开始定义支持字幕的“trace”元素,而字幕文件采用的格式是WebVTT格式,该格式看起来比较直观,简单的例子就是时间戳区间加上相应的字母文字,一下是使用字母的视频元素,因为语言的问题,每个“video”元素可以有多个“trace”元素,每个“trace”元素可以用来表示一个语言:

<video controls="controls">
<source src="video.mp4" type="video/mp4">
<trace src="trace.vtt" kind="subtitles" srclang="en" label="English"></trace>
</video>


字幕文件的解释工作不依赖与各个WebKit移植,WebCore模块支持“track”元素解析、字幕文件解析等功能,下图是WebKit支持字幕功能的主要类:



“track”本身是一个HTML元素,因此它在DOM中有相应的节点元素,这就是HTMLTrackElement类,根据规范,“track“元素有一个重要的属性”src“,该属性指定了字幕文件的URL,WebKit使用LoadableTextTrack类来负责解析字幕文件并使用TextTrack类来存储解析后的结果,目前WebKit只支持WebVTT格式的字幕,使用WebVTTParser解析器来解释它们。

下面一部分是提供接口,这里的接口是WebKit的Chromium移植所定义的接口不同额移植所定义的接口可能不一样,接口有两个类,WebInbandTextTrack和WebInbandTextTrackClient类,且是公开接口,WebInbandTextTrack类是有Chromium实现由WebKit调用,而WebInbandTextTrackClient则是有WebKit实现,实现类就是InbandTextTrackPrivateImpl,它实现WebInbandTextTrackClient的接口,然后后调用解析后的字幕并返回给Chromium。上述需要将一些消息传递给JavaScript代码,因为规范提供了JavaScript接口,开发者可以让JavaScript代码控制或者获取字幕信息,下面是Chromium中支持框架,下图描述了Chromium是如何将WebKit中的字幕信息桥接到多媒体管线化引擎中的:



在Chromium中,WebMediaPlayerImpl类创建继承类的对象,并设置WebInbandTextTrackClient对戏那个到该对象,该对象实际上是InTextTrack,它包含解析后的字幕内容,这样TextTrackImpl就可以获取字幕的内容,而TextTrack对象会被多媒体的管线化引擎多调用并渲染在视频的结果中。

音频

音频元素

音频支持不仅指对声音的播放,还包括对音频的编辑和合成以及对乐器数字接口等的支持。

HTML5 Audio元素

在HTML5中使用”audio“元素来表示,同视频类似,HTML5标准中定义了三种格式:Ogg、MP3和Wav,因为视频内容通常包含音频数据,所以不仅仅是”audio“元素才会使用音频播放,同时,音频的字幕同视频一样,”track“元素也可以用在”audio“元素的字母中,用来显示字幕。

基础设施

音频的支持方面还是从输入和输出两个方面着手,对于输入,同视频类似,WebKit使用资源加载器先加载音频文件,之后建立音频元素、管线话引擎相关类,如MediaPlayer类,HTMLAudioElement和WebMediaPlayer类等,同视频不一样的是,视频的输出是GPU中的纹理对象,而音频需要输出到声卡,因此需要打开声卡设备,由于Chromium的沙箱模型机制,所以只能靠Browser进程来打开和关闭声卡设备,下图描述了多进程中如何将音频从Render进程传输到Browser进程,以及WebKit和Chromium中相应的基础设施:



首先看Render进程,从上玩下介绍如下:

WebKit::WebAudioSourceProvider和WebKit::WebAudioSourceProviderClient:最上面两个类是WebKit的Chromium移植接口类,前者提供音频原数据,也就是音频文件中的数据,这里采用“拉”的方式,也就是在ResourceLoader加载数据之后,当且仅当渲染引擎需要新的数据的时候,主动从加载后的数据中拉出数据来进行解码,“provideInput”函数由Chromium实现,由WebKit引擎调用,WebKit::WebAudioSourceProviderClient提供“setFormat”函数,用于让Chromium的媒体播放器设置频道数量、采样率等信息,WebAudioSourceProviderImpl是WebKit::WebAudioSourceProvider的实现类

AudioRendererImpl:该类是音频渲染器的实现,并使用AudioRenderSink将音频解码的结果输出到音频设备

AudioRendererSink:一个抽象类,用于表示一个音频终端店,能够接收解码后的音频信息,典型的例子就是音频设备

AudioRendererMixer:渲染器中的调用类

AudioOutputDevice:音频的输出设备,当然只是一个桥接层,因为实际的调用请求是通过下面两个类传送给Browser进程的

AudioOutputIPCImpl和AudioMessageFilter:前者将数据和指令通过IPC发送给Browser进程,后者就是执行消息发送机制的类

下面是Browser进程,从下向上一次介绍:

AudioRendererHost:Browser进程端同Renderer进程通信并调度管理输出视频流的功能,对于每个输出流,有相应的AudioOutputStream对象对应,并且通过AudioOutputController类处理和优化输出

AudioOutputController:该类控制一个AudioOutputStream对象并提供数据给该对象,提供play、pause、stop等功能,因为它控制这音频的输出结果

AudiOutputStream和AudioOutputProxy:音频的输出流类和其子类,AudioOutputProxy是一个使用优化算法的类,它仅在Start()和Stop()函数之间打开音频设备,其他情况下音频设备都是关闭的,AudioOutputProxy使用AudioOutputDispatcher打开和关闭实际的物理音频设备

AudioOutputDispatcher和AudioOutputDispatcherImpl:控制音频设备的接口类和实际实现类

由此可以得出当WebKit和Chromium需要输出解码后的音频数据是,通过从侧自上向下、左侧自下向上的过程,然后使用共享内存的方式将解码后的数据输出到实际的物理设备中。

Web Audio

Audio元素能够用来播放各种格式的音频,但是HTML5还拥有更强大的能力来处理声音,这就是Web Audio,该规范提供了搞层次的JavaScript接口,用来处理和合成声音,整个思路就是提供一张图,giant图中的每个节点称为AudioNode,这些节点构成处理的整个过程,虽然实际的处理是使用C/C++来完成,但是Web Audio也提供了一些接口来让Web前端开发者使用JavaScript代码来调用C/C++的实现,WebAudio对于很多Web应用很有帮助,例如它呢能够帮助开发者设计和实时合成出各种音效,根据W3C的Web Audio规范的定义,整个处理过程可以看成一个拓扑图,该图有一个或多个输入源,称为Source节点,中间的所有点都可以看成各种处理过程,它们组成复杂的网,图中有一个最终节点称为“Destination”,它可以表示实际的音频设备,每个图只能有一个该类型的节点,上述图中的所有节点都是工作在一个上下文中,称为AudioContext:



对于Source1节点,它没有输入节点,hi有输出节点,对于中间的这些节点,它们既包含输入节点也包含输出节点,而对应Destination节点,它只有输入节点,没有输出节点,图中的其他节点都是可以任意定义的,这些即诶但每一个都可以代表一种处理算法,开发者可以根据需要设置不同的节点以处理出不同效果的音频输出,中间这些节点有很多类型,它们的作用也不一样,这些节点的实现通常由C或者C++代码来完成以达到高性能,当然这里提供的接口都是JavaScript接口。Web Audio的绝大多数处理都是在WebKi中完成的,而不需要Chromium过多的参与,除了输入源和输出结果到实际设备,其他同前面的多媒体数据源是一致的。下图的上半部分主要是支持规范中的标准接口,例如AudioBufferSourceNode、AudioContext、DestinationNode和OscillatorNode等类,它们对应上图中规范定义的接口,还有众多类没有绘出,以下重点关注OsicllatorNode类,它需要对音频数据进行大量计算,包括向量的加法、乘法等,同时该节点类需要使用PeriodicWave来计算周期性波形,这里面需要使用到FFT(快速傅立叶变换)算法,因为音频的及时性,网页对性能有非常高的要求,对于Chromium移植,不同平台采用不同的加速算法,在Windows和Linux中使用FFMpeg中的高性能算法,在Android上使用OpenMax DL提供的接口来加速,而在Mac上又是不同的算法:



MIDI和Web MIDI

MIDI是一个通信标准,它是电子乐器之间,以及电子乐器与电脑之间的统一交流协议,用以确定电脑音乐程序、合成器和其他电子音响设备互相交换信息与控制信息的方法,同其他的声音格式不同,MIDI不是记录采样信息,而是记录乐器的演奏指令。音频也可以以MIDI格式来存储,但是该格式不是HTML5的标准,所以游览器并没有内置支持它们,为了让MIDI格式的音乐播放出来,可以使用JavaScript代码,这就是MIDI.js,它使用上面提到的WebAudio技术和Audio元素来实现音乐的播放。Web MIDI规范中定义了输入和输出的MIDI设备,如MIDIInput和MIDIOutput,通过MIDIAccess接口分那会到所有枚举的输入和输出设备,MIDIInput主要包含一个接收指令的函数“onMessage”,而MIDIOutput包含一个发送指令的函数“send”,而发送的数据指令就是MIDIEvent,该指令包含一个时间戳和数据,WebKIt和Chromium对于Web MIDI的支持主要包括三个部分,第一是加入JavaScript的绑定,第二是将对MIDI接口的支持从Redner进程桥接到Browser进程,第三是Chromium的具体实现。

WebRTC

WebRTC(Web Real Time Communication)技术,中文全称为Web实时通信技术,是一种提供实时视频通信的规范,目前是W3C推荐的规范。

原理和规范

构建网络视频通信需要三种类型的技术,其一是视频,其二是音频,其三是网络传输

音视频输入和输出设备:同音视频播放不同,因为它们只是需要输出设备,这里需要输入和输出设备,同时输入使用getUserMedia技术,而输出基本上可以采用音视频播放的基本框架

网络连接的建立:因为视频通信需要不停的传送大量数据,所以需要建立一种可靠的网络连接来让各个参与方传输数据

数据捕获、比那吗和发送:当用户打开设备之后,需要捕获这些数据并对它们进行编码,因为原始数据的数据量太大,然后需要将编码后的数据通过连接传输出去

数据接收、解码和显示:接收来自其他方的数据流并进行解码,然后显示出来,这个跟播放媒体文件的需求比较类似

下图结合主要组成部分构建一个比较完整的音视频通信过程:



下面了解一下规范中如何针对上面的描述来定义相应的JavaScript接口,根据W3C推荐的规范草案,主要包括一下几个部分:

Media Capture and Streams 规范和WebRTC对它的扩展,这个主要是依赖摄像头和麦克风来捕获多媒体流,WebRTC对它进行扩展,使得多媒体流可以满足网络传输用途,也就是“video”元素可以来源与多媒体流而不仅仅是资源文件

点到点的连接,也就是规范中的RTCPeerConnection接口,它能够建立端到端的连接,两者直接通过某种方式传输控制信息,至于方式并没有进行规定

RTCDataChannel接口,通过该接口,通信双方可以发哦是那个任何类型的信息,例如文本或者二进制数据,这个不是必须的,不过这个功能极大的方便了开发者,其主要思想来源与WebSocket

WebKit和Chromium的实现

首先了解下WebRTC技术的内部框架和功能模块,下图主要包括三大方面,即语言、视频和传输,它们三个构成WebRTC的主要组成部分,其中iSAC(internet Speech Audio Codec)和iLBC(internet Low Bitrate Codec)是两种不同的音频编码格式,是为了适应互联网的语言传输要求而存在的,前者是针对带宽比较大的情况,后者是针对带宽较小的情况,其中VP8同样是Google提供免费视频格式,传输部分主要是加入了对前面协议的支持模块,在会话管理中,主要使用一个开源项目libjingle来管理,下面部分主要是WebRTC工作时依赖的下层功能的接口,在Chromium游览器中,它会提供相应接口和功能给WebRTC使用:



上图是WebRTC开源项目的架构图,在Chromium中,通常使用WebRTC项目来完成WebRTC规范的功能,并使用libjingle项目来建立点到点的连接,所有Chromium主要的目的是将WebRTC和libjingle的能力桥接到游览器中来,先看WebRTC规范中建立连接所需要的相关的技术设备,下图是WebKit、Chromium及Chromium中使用libjingle的类的层次图:



基础设备分成三个层次,首先是WebKit,该部分最上面的类是RTCPeerConnection,该类是对WebRTC连接的接口类,实际上它是从规范中定义的RTCPeerConnection接口文件生成的基本框架,实际真正和JavaScript引擎打交道还需要一个桥接类,该桥接类包含一个实际实现的连接类句柄m_peerHandler,它是这个连接所包含的本地多媒体流和远端对方的多媒体流,场景大致是首先WebKit需要将本地的多媒体流收集起来,通过连接传输给对方,本地可以选择是否通过“video”播放,同时需要接收从对方传输过来的多媒体流,接下来是WebKit的实现类,该类能够满足RTCPeerConnection的功能要求,但是它需要通过不同移植的实现才能完成,因为本身WebKit的WebCore并没有这样的能力,在WebKit的Chromium中同样定义了两个类WebRTCPeerConnectionHandler和WebRTCPeerCOnnectionHandlerClient,根据WebKit的类名定义方式,前者是需要Chroimum来实现,后者则由Chromium调用,并由WebKit来实现,这里主要是应用连接事件的监听函数,所以WebKit能够将它们传递给JavaScript引擎,之后是Chromium的实现类,RTCPeerCOnnectionHandler类继承自WebKit的Chromium移植的接口类,并做了具体的实现,也就是content::RTCPeerConnectionHandler,它同时集成自PeerConnectionHandleBase类,而该类拥有了支持建立连接所需的能力,当然它是依赖于libjingle项目提供的连接能力,libjingle提供了建立和管理连接的能力,支持透过NAT和防火墙设备、代理等建立连接,libjingle不仅支持点到点的连接,也支持多用户连接,同时还包含了连接所使用的MediaStream接口,这是因为Chromium本身不直接使用WebRTC项目提供的接口,而是调用libjingle来建立连接,并使用libjingle提供的MediaStream接口,而libjingle本身则会使用WebRTC项目的音视频处理引擎。

下面总结下多媒体流,先看下WebKit是怎样支持getUserMedia这个接口的,下图描述了WebKit,以及WebKit的Chroimum移植中所定义的接口:



最上层的是WebKit支持多媒体流编程接口提供的具体实现类,如NavigatorMediaStream类,而直接同V8JavaScript引擎桥接的类是V8NavigatorUserMediaSuccessCallback,它是一个绑定类,因为getUserMedia接口主要是返回一个MediaStream对象,而MediaStream类可以提供众多访问数据流的接口,而连接的目的就是需要将MediaStream对应的多媒体流传输出去,UserMediaRequest类负责请求创建一个MediaStream对象,在WebKit的Chromium移植中,定义WebUserMediaClient为一个接口类,Chromium需要新建子类来实现这一功能,这就是Chromium中的MediaStreamImpl类。WebKit使用MediaStreamRegistry类来注册和管理对应的类,管理类根据ID信息来识别各个多媒体数据流,在接口层中,Chromium移植使用WebMediaStream类来表示多媒体流,使用WebMediaStreamRegistry类来表示注册管理类,下面的问题是MediaStream接口需要提供各种事件给网页,因为很多实际的工作是在Chromium中来完成的,所以MediaStreamImpl会将这些事件从Chromium传递给WebKit,同时因为Chromium的多进程和沙箱模型,一些工作需要在Browser进程中完成,下图是所描述的跨进程的基础设施:



IPC左侧部分是Browser进程中的两个主要类,分别是消息处理类和MediaSrteam的管理类,该管理类知道MediaStream对应的网页是什么,并将事件(如创建和销毁等)传回Render进程,右侧是消息派发类,主要帮助MediaStreamImpl类来完成与Browser进程相关的MediaStream消息的传递,MediaStream可以表示本地的多媒体流也可以表示远端的多媒体流,对于本地的多媒体流,需要音频和视频的捕获机制,同时使用上面建立的连接传输给远端,对于远端的多媒体流,需要使用连接来接收数据,并使用到音频和视频的解码能力,下面分成四个部分来分别介绍,首先是音频的捕获机制,下图描述了该机制使用的主要类,当我那工业需要创建多媒体流的时候,MediaStreamImpl会创建音频捕获类WebRtcAudioCapturer类,因为捕获音频需要音频输入设备,所以使用AudioDeviceFactory工厂类创建一个逻辑上的AudioInputDevice对象,另外一个重要的类是WebRtcAudioDeviceImpl,用来表示音频的设备,该类继承自WebRtcAudioDeviceNotImpl类,这其实是继承自libjingle和WebRTC项目中的抽象接口的一个桥接类,用来表示它们需要的音频设备,包括输入鼠辈,同样因为Render进程不能访问音频输入设备,所以需要IPC来完成一个功能,Browser进程的AudioInputController会控制和访问设备,而AudioInputDeviceManager可以管理和控制所有输入设备:



其次是处理远程多媒体流中的音频解码和播放功能,下图是Chromium处理远端音频流所需要的一些主要类及关系图,这里不涉及连接如何接收传输的数据,因为Chromium是使用libjingle和WebRTC项目来完成连接的功能,Chromium使用WebRtcAudioRender类来完成音频渲染,该桥接类会被WebMediaPlayer作为渲染音频的实现类,其作用主要是将MediaStream的数据同实际的音频渲染类结合起来:



再次是从视频输入设备请求捕获本地视频流,下图是该功能依赖的一些主要类:



实线右侧Render进程中的设施,同样是MediaStreamImpl类发起,有辅助工厂类MediaStreamDependencyFactory帮助创建一个RtcVideoCapaturer,用来获取视频,该类有两个作用,其一是是吸纳linjingle和WebRTC项目中的接口类,因为需要薯片输入的实现,这个同音频部分类似,另外就是将调用请求交割一个代理类来完成(RtcVideoCaptureDelegate类),分别是管理类VideoCaptureImplManager和视频捕获类VideoCaptureImpl,并包括一个发送消息到Browser进程的辅助类,在Browser进程使用控制类VideoCaptureController来获取VideoCaptureDevice,该类会使用摄像头等视频输入设备。最后是处理远端多媒体流中的视频解码和播放功能,当MediaStreamImpl对象接收到远端的多媒体流之后,它会使用WebRTC来会视频数据进行解码,因为可以使用硬件来解码,提供了处理的性能:



把WebRTC整个过程综合起来分析如下图,可以有一种更为整体和直观的感受:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息