- 在页面的 HTML 部分中定义 HTML5 video 元素。
- 使用 JavaScript 创建 MediaSource 对象。
- 使用 createObjectURL 创建虚拟 URL,并将 MediaSource 对象作为源。
- 将虚拟 URL 分配到视频元素的 src 属性。
- 使用 addSourceBuffer 创建 SourceBuffer,包含你添加的 MIME 类型的视频。
- 从媒体文件联机获取视频初始化分段,并使用 appendBuffer 将其添加到 SourceBuffer 中。
- 从媒体文件获取视频数据的分段,并使用 appendBuffer 将其附加到 SourceBuffer 中。
- 在 video 元素上调用 play 方法。
- 重复步骤 7 直到完成。
- 清除。
网页中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
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 };
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的过程。