1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/mediasource/SourceBuffer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,531 @@ 1.4 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "SourceBuffer.h" 1.10 + 1.11 +#include "AsyncEventRunner.h" 1.12 +#include "DecoderTraits.h" 1.13 +#include "MediaDecoder.h" 1.14 +#include "MediaSourceDecoder.h" 1.15 +#include "SourceBufferResource.h" 1.16 +#include "mozilla/ErrorResult.h" 1.17 +#include "mozilla/FloatingPoint.h" 1.18 +#include "mozilla/dom/MediaSourceBinding.h" 1.19 +#include "mozilla/dom/TimeRanges.h" 1.20 +#include "nsError.h" 1.21 +#include "nsIEventTarget.h" 1.22 +#include "nsIRunnable.h" 1.23 +#include "nsThreadUtils.h" 1.24 +#include "prlog.h" 1.25 +#include "SubBufferDecoder.h" 1.26 + 1.27 +struct JSContext; 1.28 +class JSObject; 1.29 + 1.30 +#ifdef PR_LOGGING 1.31 +extern PRLogModuleInfo* gMediaSourceLog; 1.32 +#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__)) 1.33 +#else 1.34 +#define MSE_DEBUG(...) 1.35 +#endif 1.36 + 1.37 +namespace mozilla { 1.38 + 1.39 +class MediaResource; 1.40 +class ReentrantMonitor; 1.41 + 1.42 +namespace layers { 1.43 + 1.44 +class ImageContainer; 1.45 + 1.46 +} // namespace layers 1.47 + 1.48 +ReentrantMonitor& 1.49 +SubBufferDecoder::GetReentrantMonitor() 1.50 +{ 1.51 + return mParentDecoder->GetReentrantMonitor(); 1.52 +} 1.53 + 1.54 +bool 1.55 +SubBufferDecoder::OnStateMachineThread() const 1.56 +{ 1.57 + return mParentDecoder->OnStateMachineThread(); 1.58 +} 1.59 + 1.60 +bool 1.61 +SubBufferDecoder::OnDecodeThread() const 1.62 +{ 1.63 + return mParentDecoder->OnDecodeThread(); 1.64 +} 1.65 + 1.66 +SourceBufferResource* 1.67 +SubBufferDecoder::GetResource() const 1.68 +{ 1.69 + return static_cast<SourceBufferResource*>(mResource.get()); 1.70 +} 1.71 + 1.72 +void 1.73 +SubBufferDecoder::SetMediaDuration(int64_t aDuration) 1.74 +{ 1.75 + mMediaDuration = aDuration; 1.76 +} 1.77 + 1.78 +void 1.79 +SubBufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration) 1.80 +{ 1.81 + //mParentDecoder->UpdateEstimatedMediaDuration(aDuration); 1.82 +} 1.83 + 1.84 +void 1.85 +SubBufferDecoder::SetMediaSeekable(bool aMediaSeekable) 1.86 +{ 1.87 + //mParentDecoder->SetMediaSeekable(aMediaSeekable); 1.88 +} 1.89 + 1.90 +void 1.91 +SubBufferDecoder::SetTransportSeekable(bool aTransportSeekable) 1.92 +{ 1.93 + //mParentDecoder->SetTransportSeekable(aTransportSeekable); 1.94 +} 1.95 + 1.96 +layers::ImageContainer* 1.97 +SubBufferDecoder::GetImageContainer() 1.98 +{ 1.99 + return mParentDecoder->GetImageContainer(); 1.100 +} 1.101 + 1.102 +MediaDecoderOwner* 1.103 +SubBufferDecoder::GetOwner() 1.104 +{ 1.105 + return mParentDecoder->GetOwner(); 1.106 +} 1.107 + 1.108 +int64_t 1.109 +SubBufferDecoder::ConvertToByteOffset(double aTime) 1.110 +{ 1.111 + // Uses a conversion based on (aTime/duration) * length. For the 1.112 + // purposes of eviction this should be adequate since we have the 1.113 + // byte threshold as well to ensure data actually gets evicted and 1.114 + // we ensure we don't evict before the current playable point. 1.115 + if (mMediaDuration == -1) { 1.116 + return -1; 1.117 + } 1.118 + int64_t length = GetResource()->GetLength(); 1.119 + MOZ_ASSERT(length > 0); 1.120 + int64_t offset = (aTime / (double(mMediaDuration) / USECS_PER_S)) * length; 1.121 + return offset; 1.122 +} 1.123 + 1.124 +class ContainerParser { 1.125 +public: 1.126 + virtual ~ContainerParser() {} 1.127 + 1.128 + virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength) 1.129 + { 1.130 + return false; 1.131 + } 1.132 +}; 1.133 + 1.134 +class WebMContainerParser : public ContainerParser { 1.135 +public: 1.136 + bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength) 1.137 + { 1.138 + // XXX: This is overly primitive, needs to collect data as it's appended 1.139 + // to the SB and handle, rather than assuming everything is present in a 1.140 + // single aData segment. 1.141 + // 0x1a45dfa3 // EBML 1.142 + // ... 1.143 + // DocType == "webm" 1.144 + // ... 1.145 + // 0x18538067 // Segment (must be "unknown" size) 1.146 + // 0x1549a966 // -> Segment Info 1.147 + // 0x1654ae6b // -> One or more Tracks 1.148 + if (aLength >= 4 && 1.149 + aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) { 1.150 + return true; 1.151 + } 1.152 + return false; 1.153 + } 1.154 +}; 1.155 + 1.156 +namespace dom { 1.157 + 1.158 +void 1.159 +SourceBuffer::SetMode(SourceBufferAppendMode aMode, ErrorResult& aRv) 1.160 +{ 1.161 + if (!IsAttached() || mUpdating) { 1.162 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.163 + return; 1.164 + } 1.165 + MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed); 1.166 + if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) { 1.167 + mMediaSource->SetReadyState(MediaSourceReadyState::Open); 1.168 + } 1.169 + // TODO: Test append state. 1.170 + // TODO: If aMode is "sequence", set sequence start time. 1.171 + mAppendMode = aMode; 1.172 +} 1.173 + 1.174 +void 1.175 +SourceBuffer::SetTimestampOffset(double aTimestampOffset, ErrorResult& aRv) 1.176 +{ 1.177 + if (!IsAttached() || mUpdating) { 1.178 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.179 + return; 1.180 + } 1.181 + MOZ_ASSERT(mMediaSource->ReadyState() != MediaSourceReadyState::Closed); 1.182 + if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) { 1.183 + mMediaSource->SetReadyState(MediaSourceReadyState::Open); 1.184 + } 1.185 + // TODO: Test append state. 1.186 + // TODO: If aMode is "sequence", set sequence start time. 1.187 + mTimestampOffset = aTimestampOffset; 1.188 +} 1.189 + 1.190 +already_AddRefed<TimeRanges> 1.191 +SourceBuffer::GetBuffered(ErrorResult& aRv) 1.192 +{ 1.193 + if (!IsAttached()) { 1.194 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.195 + return nullptr; 1.196 + } 1.197 + nsRefPtr<TimeRanges> ranges = new TimeRanges(); 1.198 + for (uint32_t i = 0; i < mDecoders.Length(); ++i) { 1.199 + nsRefPtr<TimeRanges> r = new TimeRanges(); 1.200 + mDecoders[i]->GetBuffered(r); 1.201 + if (r->Length() > 0) { 1.202 + MSE_DEBUG("%p GetBuffered decoder=%u Length=%u Start=%f End=%f", this, i, r->Length(), 1.203 + r->GetStartTime(), r->GetEndTime()); 1.204 + ranges->Add(r->GetStartTime(), r->GetEndTime()); 1.205 + } else { 1.206 + MSE_DEBUG("%p GetBuffered decoder=%u Length=%u", this, i, r->Length()); 1.207 + } 1.208 + } 1.209 + ranges->Normalize(); 1.210 + MSE_DEBUG("%p GetBuffered Length=%u Start=%f End=%f", this, ranges->Length(), 1.211 + ranges->GetStartTime(), ranges->GetEndTime()); 1.212 + return ranges.forget(); 1.213 +} 1.214 + 1.215 +void 1.216 +SourceBuffer::SetAppendWindowStart(double aAppendWindowStart, ErrorResult& aRv) 1.217 +{ 1.218 + if (!IsAttached() || mUpdating) { 1.219 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.220 + return; 1.221 + } 1.222 + if (aAppendWindowStart < 0 || aAppendWindowStart >= mAppendWindowEnd) { 1.223 + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); 1.224 + return; 1.225 + } 1.226 + mAppendWindowStart = aAppendWindowStart; 1.227 +} 1.228 + 1.229 +void 1.230 +SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv) 1.231 +{ 1.232 + if (!IsAttached() || mUpdating) { 1.233 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.234 + return; 1.235 + } 1.236 + if (IsNaN(aAppendWindowEnd) || 1.237 + aAppendWindowEnd <= mAppendWindowStart) { 1.238 + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); 1.239 + return; 1.240 + } 1.241 + mAppendWindowEnd = aAppendWindowEnd; 1.242 +} 1.243 + 1.244 +void 1.245 +SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv) 1.246 +{ 1.247 + aData.ComputeLengthAndData(); 1.248 + 1.249 + AppendData(aData.Data(), aData.Length(), aRv); 1.250 +} 1.251 + 1.252 +void 1.253 +SourceBuffer::AppendBuffer(const ArrayBufferView& aData, ErrorResult& aRv) 1.254 +{ 1.255 + aData.ComputeLengthAndData(); 1.256 + 1.257 + AppendData(aData.Data(), aData.Length(), aRv); 1.258 +} 1.259 + 1.260 +void 1.261 +SourceBuffer::Abort(ErrorResult& aRv) 1.262 +{ 1.263 + MSE_DEBUG("%p Abort()", this); 1.264 + if (!IsAttached()) { 1.265 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.266 + return; 1.267 + } 1.268 + if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) { 1.269 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.270 + return; 1.271 + } 1.272 + if (mUpdating) { 1.273 + // TODO: Abort segment parser loop, buffer append, and stream append loop algorithms. 1.274 + AbortUpdating(); 1.275 + } 1.276 + // TODO: Run reset parser algorithm. 1.277 + mAppendWindowStart = 0; 1.278 + mAppendWindowEnd = PositiveInfinity<double>(); 1.279 + 1.280 + MSE_DEBUG("%p Abort: Discarding decoders.", this); 1.281 + if (mCurrentDecoder) { 1.282 + mCurrentDecoder->GetResource()->Ended(); 1.283 + mCurrentDecoder = nullptr; 1.284 + } 1.285 +} 1.286 + 1.287 +void 1.288 +SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv) 1.289 +{ 1.290 + MSE_DEBUG("%p Remove(Start=%f End=%f)", this, aStart, aEnd); 1.291 + if (!IsAttached() || mUpdating || 1.292 + mMediaSource->ReadyState() != MediaSourceReadyState::Open) { 1.293 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.294 + return; 1.295 + } 1.296 + if (aStart < 0 || aStart > mMediaSource->Duration() || 1.297 + aEnd <= aStart) { 1.298 + aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); 1.299 + return; 1.300 + } 1.301 + StartUpdating(); 1.302 + /// TODO: Run coded frame removal algorithm asynchronously (would call StopUpdating()). 1.303 + StopUpdating(); 1.304 +} 1.305 + 1.306 +void 1.307 +SourceBuffer::Detach() 1.308 +{ 1.309 + Ended(); 1.310 + mDecoders.Clear(); 1.311 + mCurrentDecoder = nullptr; 1.312 + mMediaSource = nullptr; 1.313 +} 1.314 + 1.315 +void 1.316 +SourceBuffer::Ended() 1.317 +{ 1.318 + for (uint32_t i = 0; i < mDecoders.Length(); ++i) { 1.319 + mDecoders[i]->GetResource()->Ended(); 1.320 + } 1.321 +} 1.322 + 1.323 +SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType) 1.324 + : DOMEventTargetHelper(aMediaSource->GetParentObject()) 1.325 + , mMediaSource(aMediaSource) 1.326 + , mType(aType) 1.327 + , mAppendWindowStart(0) 1.328 + , mAppendWindowEnd(PositiveInfinity<double>()) 1.329 + , mTimestampOffset(0) 1.330 + , mAppendMode(SourceBufferAppendMode::Segments) 1.331 + , mUpdating(false) 1.332 +{ 1.333 + MOZ_ASSERT(aMediaSource); 1.334 + if (mType.EqualsIgnoreCase("video/webm") || mType.EqualsIgnoreCase("audio/webm")) { 1.335 + mParser = new WebMContainerParser(); 1.336 + } else { 1.337 + // XXX: Plug in parsers for MPEG4, etc. here. 1.338 + mParser = new ContainerParser(); 1.339 + } 1.340 +} 1.341 + 1.342 +already_AddRefed<SourceBuffer> 1.343 +SourceBuffer::Create(MediaSource* aMediaSource, const nsACString& aType) 1.344 +{ 1.345 + nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(aMediaSource, aType); 1.346 + return sourceBuffer.forget(); 1.347 +} 1.348 + 1.349 +SourceBuffer::~SourceBuffer() 1.350 +{ 1.351 + for (uint32_t i = 0; i < mDecoders.Length(); ++i) { 1.352 + mDecoders[i]->GetResource()->Ended(); 1.353 + } 1.354 +} 1.355 + 1.356 +MediaSource* 1.357 +SourceBuffer::GetParentObject() const 1.358 +{ 1.359 + return mMediaSource; 1.360 +} 1.361 + 1.362 +JSObject* 1.363 +SourceBuffer::WrapObject(JSContext* aCx) 1.364 +{ 1.365 + return SourceBufferBinding::Wrap(aCx, this); 1.366 +} 1.367 + 1.368 +void 1.369 +SourceBuffer::DispatchSimpleEvent(const char* aName) 1.370 +{ 1.371 + MSE_DEBUG("%p Dispatching event %s to SourceBuffer", this, aName); 1.372 + DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName)); 1.373 +} 1.374 + 1.375 +void 1.376 +SourceBuffer::QueueAsyncSimpleEvent(const char* aName) 1.377 +{ 1.378 + MSE_DEBUG("%p Queuing event %s to SourceBuffer", this, aName); 1.379 + nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName); 1.380 + NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 1.381 +} 1.382 + 1.383 +bool 1.384 +SourceBuffer::InitNewDecoder() 1.385 +{ 1.386 + MediaSourceDecoder* parentDecoder = mMediaSource->GetDecoder(); 1.387 + nsRefPtr<SubBufferDecoder> decoder = parentDecoder->CreateSubDecoder(mType); 1.388 + if (!decoder) { 1.389 + return false; 1.390 + } 1.391 + mDecoders.AppendElement(decoder); 1.392 + // XXX: At this point, we really want to push through any remaining 1.393 + // processing for the old decoder and discard it, rather than hanging on 1.394 + // to all of them in mDecoders. 1.395 + mCurrentDecoder = decoder; 1.396 + return true; 1.397 +} 1.398 + 1.399 +void 1.400 +SourceBuffer::StartUpdating() 1.401 +{ 1.402 + MOZ_ASSERT(!mUpdating); 1.403 + mUpdating = true; 1.404 + QueueAsyncSimpleEvent("updatestart"); 1.405 +} 1.406 + 1.407 +void 1.408 +SourceBuffer::StopUpdating() 1.409 +{ 1.410 + MOZ_ASSERT(mUpdating); 1.411 + mUpdating = false; 1.412 + QueueAsyncSimpleEvent("update"); 1.413 + QueueAsyncSimpleEvent("updateend"); 1.414 +} 1.415 + 1.416 +void 1.417 +SourceBuffer::AbortUpdating() 1.418 +{ 1.419 + MOZ_ASSERT(mUpdating); 1.420 + mUpdating = false; 1.421 + QueueAsyncSimpleEvent("abort"); 1.422 + QueueAsyncSimpleEvent("updateend"); 1.423 +} 1.424 + 1.425 +void 1.426 +SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv) 1.427 +{ 1.428 + MSE_DEBUG("%p AppendBuffer(Data=%u bytes)", this, aLength); 1.429 + if (!IsAttached() || mUpdating) { 1.430 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.431 + return; 1.432 + } 1.433 + if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) { 1.434 + mMediaSource->SetReadyState(MediaSourceReadyState::Open); 1.435 + } 1.436 + // TODO: Run coded frame eviction algorithm. 1.437 + // TODO: Test buffer full flag. 1.438 + StartUpdating(); 1.439 + // TODO: Run buffer append algorithm asynchronously (would call StopUpdating()). 1.440 + if (mParser->IsInitSegmentPresent(aData, aLength) || !mCurrentDecoder) { 1.441 + MSE_DEBUG("%p AppendBuffer: New initialization segment, switching decoders.", this); 1.442 + if (mCurrentDecoder) { 1.443 + mCurrentDecoder->GetResource()->Ended(); 1.444 + } 1.445 + if (!InitNewDecoder()) { 1.446 + aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling. 1.447 + return; 1.448 + } 1.449 + } 1.450 + // XXX: For future reference: NDA call must run on the main thread. 1.451 + mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData), 1.452 + aLength, 1.453 + mCurrentDecoder->GetResource()->GetLength()); 1.454 + mCurrentDecoder->GetResource()->AppendData(aData, aLength); 1.455 + 1.456 + // Eviction uses a byte threshold. If the buffer is greater than the 1.457 + // number of bytes then data is evicted. The time range for this 1.458 + // eviction is reported back to the media source. It will then 1.459 + // evict data before that range across all SourceBuffer's it knows 1.460 + // about. 1.461 + const int evict_threshold = 1000000; 1.462 + bool evicted = mCurrentDecoder->GetResource()->EvictData(evict_threshold); 1.463 + if (evicted) { 1.464 + double start = 0.0; 1.465 + double end = 0.0; 1.466 + GetBufferedStartEndTime(&start, &end); 1.467 + 1.468 + // We notify that we've evicted from the time range 0 through to 1.469 + // the current start point. 1.470 + mMediaSource->NotifyEvicted(0.0, start); 1.471 + } 1.472 + StopUpdating(); 1.473 + 1.474 + // Schedule the state machine thread to ensure playback starts 1.475 + // if required when data is appended. 1.476 + mMediaSource->GetDecoder()->ScheduleStateMachineThread(); 1.477 +} 1.478 + 1.479 +void 1.480 +SourceBuffer::GetBufferedStartEndTime(double* aStart, double* aEnd) 1.481 +{ 1.482 + ErrorResult dummy; 1.483 + nsRefPtr<TimeRanges> ranges = GetBuffered(dummy); 1.484 + if (!ranges || ranges->Length() == 0) { 1.485 + *aStart = *aEnd = 0.0; 1.486 + return; 1.487 + } 1.488 + *aStart = ranges->Start(0, dummy); 1.489 + *aEnd = ranges->End(ranges->Length() - 1, dummy); 1.490 +} 1.491 + 1.492 +void 1.493 +SourceBuffer::Evict(double aStart, double aEnd) 1.494 +{ 1.495 + for (uint32_t i = 0; i < mDecoders.Length(); ++i) { 1.496 + // Need to map time to byte offset then evict 1.497 + int64_t end = mDecoders[i]->ConvertToByteOffset(aEnd); 1.498 + if (end <= 0) { 1.499 + NS_WARNING("SourceBuffer::Evict failed"); 1.500 + continue; 1.501 + } 1.502 + mDecoders[i]->GetResource()->EvictBefore(end); 1.503 + } 1.504 +} 1.505 + 1.506 +bool 1.507 +SourceBuffer::ContainsTime(double aTime) 1.508 +{ 1.509 + ErrorResult dummy; 1.510 + nsRefPtr<TimeRanges> ranges = GetBuffered(dummy); 1.511 + if (!ranges || ranges->Length() == 0) { 1.512 + return false; 1.513 + } 1.514 + for (uint32_t i = 0; i < ranges->Length(); ++i) { 1.515 + if (aTime >= ranges->Start(i, dummy) && 1.516 + aTime <= ranges->End(i, dummy)) { 1.517 + return true; 1.518 + } 1.519 + } 1.520 + return false; 1.521 +} 1.522 + 1.523 +NS_IMPL_CYCLE_COLLECTION_INHERITED(SourceBuffer, DOMEventTargetHelper, 1.524 + mMediaSource) 1.525 + 1.526 +NS_IMPL_ADDREF_INHERITED(SourceBuffer, DOMEventTargetHelper) 1.527 +NS_IMPL_RELEASE_INHERITED(SourceBuffer, DOMEventTargetHelper) 1.528 + 1.529 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SourceBuffer) 1.530 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.531 + 1.532 +} // namespace dom 1.533 + 1.534 +} // namespace mozilla