chromium系列:MediaSource和SourceBuffer

 使用 MSE API,请执行以下步骤
  1. 在页面的 HTML 部分中定义 HTML5 video 元素。
  2. 使用 JavaScript 创建 MediaSource 对象。
  3. 使用 createObjectURL 创建虚拟 URL,并将 MediaSource 对象作为源。
  4. 将虚拟 URL 分配到视频元素的 src 属性。
  5. 使用 addSourceBuffer 创建 SourceBuffer,包含你添加的 MIME 类型的视频。
  6. 从媒体文件联机获取视频初始化分段,并使用 appendBuffer 将其添加到 SourceBuffer 中。
  7. 从媒体文件获取视频数据的分段,并使用 appendBuffer 将其附加到 SourceBuffer 中。
  8. 在 video 元素上调用 play 方法。
  9. 重复步骤 7 直到完成。
  10. 清除。

网页中javascript是通过MediaSource object把media的数据 传入c++代码中的, MediaSource是被javascript创建,同时与一个HTMLMediaElement对象绑定。MediaSource对象是HTMLMediaElement对象的数据源。MediaSource会把media data添加在SourceBuffer对象中。

1.创建 MediaSource

     MediaSource的js和c++代码的接口定义在MediaSource.idl文件中,在这个文件中的接口,js都可以直接调用,接口对接c++代码。

     js代码中调用new MediaSource(),会生成一个MediaSource对象。 时序图如下:

    

             从上图可以看到js代码将关联object url和一个MediaSource对象,并将保存在m_mediaSource的set集合中。这样做是为了后面将一个MediaSource和一个HTMLMediaElement相关联。MediaSourceRegistry是HTMLMediaSource和MediaSource的桥梁,我们从最主要的代码就是从调用的MediaSourceRegistry::registry()开始

             在MediaSourceRegistry.cpp中,registry是静态成员函数:

       MediaSourceRegistry& MediaSourceRegistry::registry() {
         DEFINE_STATIC_LOCAL(MediaSourceRegistry, instance, ());
         return instance;
       }
             DEFINE_STATIC_LOCAL定义在base/macros.h中 :       

       #define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \
           static type& name = *new type arguments
             从上面可以看到是new了一个对象。

            所以在registry中调用了MediaSourceRegistry的构造函数,在构造函数中将该对象注册到HTMLMediaSource中

      MediaSourceRegistry::MediaSourceRegistry() {
         HTMLMediaSource::setRegistry(this);
      }
             而在MediaSourceRegistry::registerURL中将url和MediaSource保存在一个m_mediaSources集合中:

       void MediaSourceRegistry::registerURL(SecurityOrigin*, const KURL& url, URLRegistrable* registrable) {
          MediaSource* source = static_cast<MediaSource*>(registrable);
          ........
          m_mediaSources.set(url.string(), source);
       }
               HTMLMediaSource公有继承URLRegistrable,MediaSource公有继承HTMLMediaSource。

             因此HTMLMediaSource就可以通过MediaSourceRegistry拿到对应url的MediaSource了。

        2. 关联 MediaSource 和 HTMLMediaElement

            通过关联HTMLMediaElement, MediaSource就可以与HTMLMediaElement单方面“通信”了。

     

            在HTMLMediaElement.cpp文件中

       void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType, const String& keySystem) {
            .....
            m_mediaSource = HTMLMediaSource::lookup(url.string());
            if (m_mediaSource) {
              m_mediaSource->attachToElement(this);
              ....
            }
            ....
        }

          HTMLMediaSource::lookup在HTMLMediaSource.h文件中

       static HTMLMediaSource* lookup(const String& url) { 
          return s_registry ? static_cast<HTMLMediaSource*>(s_registry->lookup(url)) : 0; 
       }

         在Create MediaSource的时序图中可以看到在MediaSourceRegistry中调用HTMLMediaSource::setRegistry将MediaSourceRegistry注册到HTMLMediaSource中,因此s_registry就是MediaSourceRegistry对象,在MediaSourceRegistry.cpp中       

     URLRegistrable* MediaSourceRegistry::lookup(const String& url) {
        return m_mediaSources.get(url);
     }

        再回过头来看 attach到一个HTMLMediaElement对象,拿到一个MediaSource对象时,调用attachToElement方法

     bool MediaSource::attachToElement(HTMLMediaElement* element) {
       ......
       m_attachedElement = element;
       return true;
     }

 3.创建SourceBuffer  

          js调用addSourceBuffer时会把包含media的一些特征信息传下来,例如“video/webm; codecs="vp9"”或 “audio/mp4; codecs="mp4a.40.2"” ,在后面会调用isTypeSupported检测是否支持该类型的media。
           
         在isTypeSupported中会分别调用 HTMLMediaElement :: supportsType MIMETypeRegistry::isSupportedMediaSourceMIMEType检测是否支持此类型的视频播放。
   bool MediaSource::isTypeSupported(const String& type) {
      .....
      if (HTMLMediaElement::supportsType(contentType) == WebMimeRegistry::IsNotSupported){
        ...
      } 
      ...
      bool result = MIMETypeRegistry::isSupportedMediaSourceMIMEType(contentType.type(), codecs);
      return result;
  }
     这两次的检测的方法异曲同工,都是拿type和已经配置好的config比较,所谓的配置就是mimetype和对应的codec绑定在一起。下面我们拿 MIMETypeRegistry :: isSupportedMediaSourceMIMEType举例说明。
      
  static bool CheckTypeAndCodecs(const std::string& type,const std::vector<std::string>& codecs,const LogCB& log_cb,ParserFactoryFunction* factory_function,std::vector<CodecInfo::HistogramTag>* audio_codecs,std::vector<CodecInfo::HistogramTag>* video_codecs) {
     .....
     for(size_t i = 0; i < arraysize(kSupportedTypeInfo); ++i) {
        ...
        const SupportedTypeInfo& type_info = kSupportedTypeInfo[i];
        ...
     }
  }
    从上面的代码中看到kSupportedTypeInfo中应该保存有配置信息。
   static const SupportedTypeInfo kSupportedTypeInfo[] = {
     { "video/webm", &BuildWebMParser, kVideoWebMCodecs },
     { "audio/webm", &BuildWebMParser, kAudioWebMCodecs },
      ....
     };
     "video/webm"和“audio/webm”就是mimetype, BuildWebMParser是用来创建StreamParser对象的,kVideoWebMCodecs是已经配置好的codec。  
    static StreamParser* BuildWebMParser(const std::vector<std::string>& codecs,const LogCB& log_cb) {
       return new WebMStreamParser();
     }

    static const CodecInfo* kVideoWebMCodecs[] = {
     &kVP8CodecInfo,
     &kVP9CodecInfo,
     &kVorbisCodecInfo,
     &kOpusCodecInfo,
     NULL
    };

    static const CodecInfo kVP8CodecInfo = { "vp8", CodecInfo::VIDEO, NULL,CodecInfo::HISTOGRAM_VP8 };
     我们继续回到create sourcebuffer中,接下来会创建WebSourceBuffer对象并与Sourcebuffer关联。
 PassOwnPtr<WebSourceBuffer> MediaSource::createWebSourceBuffer(const String& type, const Vector<String>& codecs, ExceptionState& exceptionState) 
  {
    WebSourceBuffer* webSourceBuffer = 0;
    switch (m_webMediaSource->addSourceBuffer(type, codecs, &webSourceBuffer)) {
        ....
    }
    ....
  }

SourceBuffer* MediaSource::addSourceBuffer(const String& type, ExceptionState& exceptionState) 
{
  if (!isTypeSupported(type)) {
    ...
  }
  //Create a new SourceBuffer object and associated resources.
   ContentType contentType(type);
   Vector<String> codecs = contentType.codecs();
   OwnPtr<WebSourceBuffer> webSourceBuffer = createWebSourceBuffer(contentType.type(), codecs, exceptionState);
    ...
   SourceBuffer* buffer = SourceBuffer::create(webSourceBuffer.release(), this, m_asyncEventQueue.get());
    //Add the new object to sourceBuffers and fire a addsourcebuffer on that object.
   m_sourceBuffers->add(buffer);
}
以上就是MediaSource和SourceBuffer的创建过程,下篇文章将分析appendBuffer的过程。




     



     

猜你喜欢

转载自blog.csdn.net/u014269285/article/details/52693787