content/media/mediasource/MediaSource.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "MediaSource.h"
michael@0 8
michael@0 9 #include "AsyncEventRunner.h"
michael@0 10 #include "DecoderTraits.h"
michael@0 11 #include "SourceBuffer.h"
michael@0 12 #include "SourceBufferList.h"
michael@0 13 #include "mozilla/ErrorResult.h"
michael@0 14 #include "mozilla/FloatingPoint.h"
michael@0 15 #include "mozilla/Preferences.h"
michael@0 16 #include "mozilla/dom/BindingDeclarations.h"
michael@0 17 #include "mozilla/dom/HTMLMediaElement.h"
michael@0 18 #include "mozilla/mozalloc.h"
michael@0 19 #include "nsContentTypeParser.h"
michael@0 20 #include "nsDebug.h"
michael@0 21 #include "nsError.h"
michael@0 22 #include "nsIEventTarget.h"
michael@0 23 #include "nsIRunnable.h"
michael@0 24 #include "nsPIDOMWindow.h"
michael@0 25 #include "nsStringGlue.h"
michael@0 26 #include "nsThreadUtils.h"
michael@0 27 #include "prlog.h"
michael@0 28
michael@0 29 struct JSContext;
michael@0 30 class JSObject;
michael@0 31
michael@0 32 #ifdef PR_LOGGING
michael@0 33 PRLogModuleInfo* gMediaSourceLog;
michael@0 34 #define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
michael@0 35 #else
michael@0 36 #define MSE_DEBUG(...)
michael@0 37 #endif
michael@0 38
michael@0 39 // Arbitrary limit.
michael@0 40 static const unsigned int MAX_SOURCE_BUFFERS = 16;
michael@0 41
michael@0 42 namespace mozilla {
michael@0 43
michael@0 44 static const char* const gMediaSourceTypes[6] = {
michael@0 45 "video/webm",
michael@0 46 "audio/webm",
michael@0 47 // XXX: Disabled other codecs temporarily to allow WebM testing. For now, set
michael@0 48 // the developer-only media.mediasource.ignore_codecs pref to true to test other
michael@0 49 // codecs, and expect things to be broken.
michael@0 50 #if 0
michael@0 51 "video/mp4",
michael@0 52 "audio/mp4",
michael@0 53 "audio/mpeg",
michael@0 54 #endif
michael@0 55 nullptr
michael@0 56 };
michael@0 57
michael@0 58 static nsresult
michael@0 59 IsTypeSupported(const nsAString& aType)
michael@0 60 {
michael@0 61 if (aType.IsEmpty()) {
michael@0 62 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
michael@0 63 }
michael@0 64 // TODO: Further restrict this to formats in the spec.
michael@0 65 nsContentTypeParser parser(aType);
michael@0 66 nsAutoString mimeType;
michael@0 67 nsresult rv = parser.GetType(mimeType);
michael@0 68 if (NS_FAILED(rv)) {
michael@0 69 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
michael@0 70 }
michael@0 71 bool found = false;
michael@0 72 for (uint32_t i = 0; gMediaSourceTypes[i]; ++i) {
michael@0 73 if (mimeType.EqualsASCII(gMediaSourceTypes[i])) {
michael@0 74 found = true;
michael@0 75 break;
michael@0 76 }
michael@0 77 }
michael@0 78 if (!found) {
michael@0 79 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
michael@0 80 }
michael@0 81 if (Preferences::GetBool("media.mediasource.ignore_codecs", false)) {
michael@0 82 return NS_OK;
michael@0 83 }
michael@0 84 // Check aType against HTMLMediaElement list of MIME types. Since we've
michael@0 85 // already restricted the container format, this acts as a specific check
michael@0 86 // of any specified "codecs" parameter of aType.
michael@0 87 if (dom::HTMLMediaElement::GetCanPlay(aType) == CANPLAY_NO) {
michael@0 88 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
michael@0 89 }
michael@0 90 return NS_OK;
michael@0 91 }
michael@0 92
michael@0 93 namespace dom {
michael@0 94
michael@0 95 /* static */ already_AddRefed<MediaSource>
michael@0 96 MediaSource::Constructor(const GlobalObject& aGlobal,
michael@0 97 ErrorResult& aRv)
michael@0 98 {
michael@0 99 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
michael@0 100 if (!window) {
michael@0 101 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 102 return nullptr;
michael@0 103 }
michael@0 104
michael@0 105 nsRefPtr<MediaSource> mediaSource = new MediaSource(window);
michael@0 106 return mediaSource.forget();
michael@0 107 }
michael@0 108
michael@0 109 SourceBufferList*
michael@0 110 MediaSource::SourceBuffers()
michael@0 111 {
michael@0 112 MOZ_ASSERT_IF(mReadyState == MediaSourceReadyState::Closed, mSourceBuffers->IsEmpty());
michael@0 113 return mSourceBuffers;
michael@0 114 }
michael@0 115
michael@0 116 SourceBufferList*
michael@0 117 MediaSource::ActiveSourceBuffers()
michael@0 118 {
michael@0 119 MOZ_ASSERT_IF(mReadyState == MediaSourceReadyState::Closed, mActiveSourceBuffers->IsEmpty());
michael@0 120 return mActiveSourceBuffers;
michael@0 121 }
michael@0 122
michael@0 123 MediaSourceReadyState
michael@0 124 MediaSource::ReadyState()
michael@0 125 {
michael@0 126 return mReadyState;
michael@0 127 }
michael@0 128
michael@0 129 double
michael@0 130 MediaSource::Duration()
michael@0 131 {
michael@0 132 if (mReadyState == MediaSourceReadyState::Closed) {
michael@0 133 return UnspecifiedNaN<double>();
michael@0 134 }
michael@0 135 return mDuration;
michael@0 136 }
michael@0 137
michael@0 138 void
michael@0 139 MediaSource::SetDuration(double aDuration, ErrorResult& aRv)
michael@0 140 {
michael@0 141 if (aDuration < 0 || IsNaN(aDuration)) {
michael@0 142 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 143 return;
michael@0 144 }
michael@0 145 if (mReadyState != MediaSourceReadyState::Open ||
michael@0 146 mSourceBuffers->AnyUpdating()) {
michael@0 147 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 148 return;
michael@0 149 }
michael@0 150 DurationChange(aDuration, aRv);
michael@0 151 }
michael@0 152
michael@0 153 already_AddRefed<SourceBuffer>
michael@0 154 MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
michael@0 155 {
michael@0 156 nsresult rv = mozilla::IsTypeSupported(aType);
michael@0 157 MSE_DEBUG("MediaSource::AddSourceBuffer(Type=%s) -> %x", NS_ConvertUTF16toUTF8(aType).get(), rv);
michael@0 158 if (NS_FAILED(rv)) {
michael@0 159 aRv.Throw(rv);
michael@0 160 return nullptr;
michael@0 161 }
michael@0 162 if (mSourceBuffers->Length() >= MAX_SOURCE_BUFFERS) {
michael@0 163 aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
michael@0 164 return nullptr;
michael@0 165 }
michael@0 166 if (mReadyState != MediaSourceReadyState::Open) {
michael@0 167 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 168 return nullptr;
michael@0 169 }
michael@0 170 nsContentTypeParser parser(aType);
michael@0 171 nsAutoString mimeType;
michael@0 172 rv = parser.GetType(mimeType);
michael@0 173 if (NS_FAILED(rv)) {
michael@0 174 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
michael@0 175 return nullptr;
michael@0 176 }
michael@0 177 nsRefPtr<SourceBuffer> sourceBuffer = SourceBuffer::Create(this, NS_ConvertUTF16toUTF8(mimeType));
michael@0 178 if (!sourceBuffer) {
michael@0 179 aRv.Throw(NS_ERROR_FAILURE); // XXX need a better error here
michael@0 180 return nullptr;
michael@0 181 }
michael@0 182 mSourceBuffers->Append(sourceBuffer);
michael@0 183 mActiveSourceBuffers->Append(sourceBuffer);
michael@0 184 MSE_DEBUG("%p AddSourceBuffer(Type=%s) -> %p", this,
michael@0 185 NS_ConvertUTF16toUTF8(mimeType).get(), sourceBuffer.get());
michael@0 186 return sourceBuffer.forget();
michael@0 187 }
michael@0 188
michael@0 189 void
michael@0 190 MediaSource::RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv)
michael@0 191 {
michael@0 192 SourceBuffer* sourceBuffer = &aSourceBuffer;
michael@0 193 MSE_DEBUG("%p RemoveSourceBuffer(Buffer=%p)", this, sourceBuffer);
michael@0 194 if (!mSourceBuffers->Contains(sourceBuffer)) {
michael@0 195 aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
michael@0 196 return;
michael@0 197 }
michael@0 198 if (sourceBuffer->Updating()) {
michael@0 199 // TODO:
michael@0 200 // abort stream append loop (if running)
michael@0 201 // set updating to false
michael@0 202 // fire "abort" at sourceBuffer
michael@0 203 // fire "updateend" at sourceBuffer
michael@0 204 }
michael@0 205 // TODO:
michael@0 206 // For all sourceBuffer audioTracks, videoTracks, textTracks:
michael@0 207 // set sourceBuffer to null
michael@0 208 // remove sourceBuffer video, audio, text Tracks from MediaElement tracks
michael@0 209 // remove sourceBuffer video, audio, text Tracks and fire "removetrack" at affected lists
michael@0 210 // fire "removetrack" at modified MediaElement track lists
michael@0 211 // If removed enabled/selected, fire "change" at affected MediaElement list.
michael@0 212 if (mActiveSourceBuffers->Contains(sourceBuffer)) {
michael@0 213 mActiveSourceBuffers->Remove(sourceBuffer);
michael@0 214 }
michael@0 215 mSourceBuffers->Remove(sourceBuffer);
michael@0 216 // TODO: Free all resources associated with sourceBuffer
michael@0 217 }
michael@0 218
michael@0 219 void
michael@0 220 MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv)
michael@0 221 {
michael@0 222 MSE_DEBUG("%p EndOfStream(Error=%u)", this, aError.WasPassed() ? uint32_t(aError.Value()) : 0);
michael@0 223 if (mReadyState != MediaSourceReadyState::Open ||
michael@0 224 mSourceBuffers->AnyUpdating()) {
michael@0 225 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 226 return;
michael@0 227 }
michael@0 228
michael@0 229 SetReadyState(MediaSourceReadyState::Ended);
michael@0 230 mSourceBuffers->Ended();
michael@0 231 if (!aError.WasPassed()) {
michael@0 232 // TODO:
michael@0 233 // Run duration change algorithm.
michael@0 234 // DurationChange(highestDurationOfSourceBuffers, aRv);
michael@0 235 // if (aRv.Failed()) {
michael@0 236 // return;
michael@0 237 // }
michael@0 238 // Notify media element that all data is now available.
michael@0 239 return;
michael@0 240 }
michael@0 241 switch (aError.Value()) {
michael@0 242 case MediaSourceEndOfStreamError::Network:
michael@0 243 // TODO: If media element has a readyState of:
michael@0 244 // HAVE_NOTHING -> run resource fetch algorithm
michael@0 245 // > HAVE_NOTHING -> run "interrupted" steps of resource fetch
michael@0 246 break;
michael@0 247 case MediaSourceEndOfStreamError::Decode:
michael@0 248 // TODO: If media element has a readyState of:
michael@0 249 // HAVE_NOTHING -> run "unsupported" steps of resource fetch
michael@0 250 // > HAVE_NOTHING -> run "corrupted" steps of resource fetch
michael@0 251 break;
michael@0 252 default:
michael@0 253 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
michael@0 254 }
michael@0 255 }
michael@0 256
michael@0 257 /* static */ bool
michael@0 258 MediaSource::IsTypeSupported(const GlobalObject&, const nsAString& aType)
michael@0 259 {
michael@0 260 #ifdef PR_LOGGING
michael@0 261 if (!gMediaSourceLog) {
michael@0 262 gMediaSourceLog = PR_NewLogModule("MediaSource");
michael@0 263 }
michael@0 264 #endif
michael@0 265 nsresult rv = mozilla::IsTypeSupported(aType);
michael@0 266 MSE_DEBUG("MediaSource::IsTypeSupported(Type=%s) -> %x", NS_ConvertUTF16toUTF8(aType).get(), rv);
michael@0 267 return NS_SUCCEEDED(rv);
michael@0 268 }
michael@0 269
michael@0 270 bool
michael@0 271 MediaSource::Attach(MediaSourceDecoder* aDecoder)
michael@0 272 {
michael@0 273 MSE_DEBUG("%p Attaching decoder %p owner %p", this, aDecoder, aDecoder->GetOwner());
michael@0 274 MOZ_ASSERT(aDecoder);
michael@0 275 if (mReadyState != MediaSourceReadyState::Closed) {
michael@0 276 return false;
michael@0 277 }
michael@0 278 mDecoder = aDecoder;
michael@0 279 mDecoder->AttachMediaSource(this);
michael@0 280 SetReadyState(MediaSourceReadyState::Open);
michael@0 281 return true;
michael@0 282 }
michael@0 283
michael@0 284 void
michael@0 285 MediaSource::Detach()
michael@0 286 {
michael@0 287 MSE_DEBUG("%p Detaching decoder %p owner %p", this, mDecoder.get(), mDecoder->GetOwner());
michael@0 288 MOZ_ASSERT(mDecoder);
michael@0 289 mDecoder->DetachMediaSource();
michael@0 290 mDecoder = nullptr;
michael@0 291 mDuration = UnspecifiedNaN<double>();
michael@0 292 mActiveSourceBuffers->Clear();
michael@0 293 mSourceBuffers->Clear();
michael@0 294 SetReadyState(MediaSourceReadyState::Closed);
michael@0 295 }
michael@0 296
michael@0 297 MediaSource::MediaSource(nsPIDOMWindow* aWindow)
michael@0 298 : DOMEventTargetHelper(aWindow)
michael@0 299 , mDuration(UnspecifiedNaN<double>())
michael@0 300 , mDecoder(nullptr)
michael@0 301 , mReadyState(MediaSourceReadyState::Closed)
michael@0 302 {
michael@0 303 mSourceBuffers = new SourceBufferList(this);
michael@0 304 mActiveSourceBuffers = new SourceBufferList(this);
michael@0 305
michael@0 306 #ifdef PR_LOGGING
michael@0 307 if (!gMediaSourceLog) {
michael@0 308 gMediaSourceLog = PR_NewLogModule("MediaSource");
michael@0 309 }
michael@0 310 #endif
michael@0 311 }
michael@0 312
michael@0 313 void
michael@0 314 MediaSource::SetReadyState(MediaSourceReadyState aState)
michael@0 315 {
michael@0 316 MOZ_ASSERT(aState != mReadyState);
michael@0 317 MSE_DEBUG("%p SetReadyState old=%d new=%d", this, mReadyState, aState);
michael@0 318
michael@0 319 MediaSourceReadyState oldState = mReadyState;
michael@0 320 mReadyState = aState;
michael@0 321
michael@0 322 if (mReadyState == MediaSourceReadyState::Open &&
michael@0 323 (oldState == MediaSourceReadyState::Closed ||
michael@0 324 oldState == MediaSourceReadyState::Ended)) {
michael@0 325 QueueAsyncSimpleEvent("sourceopen");
michael@0 326 return;
michael@0 327 }
michael@0 328
michael@0 329 if (mReadyState == MediaSourceReadyState::Ended &&
michael@0 330 oldState == MediaSourceReadyState::Open) {
michael@0 331 QueueAsyncSimpleEvent("sourceended");
michael@0 332 return;
michael@0 333 }
michael@0 334
michael@0 335 if (mReadyState == MediaSourceReadyState::Closed &&
michael@0 336 (oldState == MediaSourceReadyState::Open ||
michael@0 337 oldState == MediaSourceReadyState::Ended)) {
michael@0 338 QueueAsyncSimpleEvent("sourceclose");
michael@0 339 return;
michael@0 340 }
michael@0 341
michael@0 342 NS_WARNING("Invalid MediaSource readyState transition");
michael@0 343 }
michael@0 344
michael@0 345 void
michael@0 346 MediaSource::DispatchSimpleEvent(const char* aName)
michael@0 347 {
michael@0 348 MSE_DEBUG("%p Dispatching event %s to MediaSource", this, aName);
michael@0 349 DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
michael@0 350 }
michael@0 351
michael@0 352 void
michael@0 353 MediaSource::QueueAsyncSimpleEvent(const char* aName)
michael@0 354 {
michael@0 355 MSE_DEBUG("%p Queuing event %s to MediaSource", this, aName);
michael@0 356 nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
michael@0 357 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
michael@0 358 }
michael@0 359
michael@0 360 void
michael@0 361 MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
michael@0 362 {
michael@0 363 if (mDuration == aNewDuration) {
michael@0 364 return;
michael@0 365 }
michael@0 366 double oldDuration = mDuration;
michael@0 367 mDuration = aNewDuration;
michael@0 368 if (aNewDuration < oldDuration) {
michael@0 369 mSourceBuffers->Remove(aNewDuration, oldDuration, aRv);
michael@0 370 if (aRv.Failed()) {
michael@0 371 return;
michael@0 372 }
michael@0 373 }
michael@0 374 // TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
michael@0 375 // TODO: Update media element's duration and run element's duration change algorithm.
michael@0 376 }
michael@0 377
michael@0 378 nsPIDOMWindow*
michael@0 379 MediaSource::GetParentObject() const
michael@0 380 {
michael@0 381 return GetOwner();
michael@0 382 }
michael@0 383
michael@0 384 JSObject*
michael@0 385 MediaSource::WrapObject(JSContext* aCx)
michael@0 386 {
michael@0 387 return MediaSourceBinding::Wrap(aCx, this);
michael@0 388 }
michael@0 389
michael@0 390 void
michael@0 391 MediaSource::NotifyEvicted(double aStart, double aEnd)
michael@0 392 {
michael@0 393 // Cycle through all SourceBuffers and tell them to evict data in
michael@0 394 // the given range.
michael@0 395 mSourceBuffers->Evict(aStart, aEnd);
michael@0 396 }
michael@0 397
michael@0 398 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaSource, DOMEventTargetHelper,
michael@0 399 mSourceBuffers, mActiveSourceBuffers)
michael@0 400
michael@0 401 NS_IMPL_ADDREF_INHERITED(MediaSource, DOMEventTargetHelper)
michael@0 402 NS_IMPL_RELEASE_INHERITED(MediaSource, DOMEventTargetHelper)
michael@0 403
michael@0 404 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaSource)
michael@0 405 NS_INTERFACE_MAP_ENTRY(mozilla::dom::MediaSource)
michael@0 406 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
michael@0 407
michael@0 408 } // namespace dom
michael@0 409
michael@0 410 } // namespace mozilla

mercurial