Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsError.h"
8 : #include "MediaDecoderStateMachine.h"
9 : #include "AbstractMediaDecoder.h"
10 : #include "MediaResource.h"
11 : #ifdef MOZ_AV1
12 : #include "AOMDecoder.h"
13 : #endif
14 : #include "OpusDecoder.h"
15 : #include "VPXDecoder.h"
16 : #include "WebMDemuxer.h"
17 : #include "WebMBufferedParser.h"
18 : #include "gfx2DGlue.h"
19 : #include "mozilla/Atomics.h"
20 : #include "mozilla/EndianUtils.h"
21 : #include "mozilla/SharedThreadPool.h"
22 : #include "MediaDataDemuxer.h"
23 : #include "nsAutoPtr.h"
24 : #include "nsAutoRef.h"
25 : #include "NesteggPacketHolder.h"
26 : #include "XiphExtradata.h"
27 : #include "prprf.h" // leaving it for PR_vsnprintf()
28 : #include "mozilla/IntegerPrintfMacros.h"
29 : #include "mozilla/SizePrintfMacros.h"
30 : #include "mozilla/Sprintf.h"
31 :
32 : #include <algorithm>
33 : #include <numeric>
34 : #include <stdint.h>
35 :
36 : #define WEBM_DEBUG(arg, ...) MOZ_LOG(gMediaDemuxerLog, mozilla::LogLevel::Debug, ("WebMDemuxer(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
37 : extern mozilla::LazyLogModule gMediaDemuxerLog;
38 :
39 : namespace mozilla {
40 :
41 : using namespace gfx;
42 : using media::TimeUnit;
43 :
44 : LazyLogModule gNesteggLog("Nestegg");
45 :
46 : // How far ahead will we look when searching future keyframe. In microseconds.
47 : // This value is based on what appears to be a reasonable value as most webm
48 : // files encountered appear to have keyframes located < 4s.
49 : #define MAX_LOOK_AHEAD 10000000
50 :
51 : static Atomic<uint32_t> sStreamSourceID(0u);
52 :
53 : // Functions for reading and seeking using WebMDemuxer required for
54 : // nestegg_io. The 'user data' passed to these functions is the
55 : // demuxer.
56 0 : static int webmdemux_read(void* aBuffer, size_t aLength, void* aUserData)
57 : {
58 0 : MOZ_ASSERT(aUserData);
59 0 : MOZ_ASSERT(aLength < UINT32_MAX);
60 : WebMDemuxer::NestEggContext* context =
61 0 : reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
62 0 : uint32_t count = aLength;
63 0 : if (context->IsMediaSource()) {
64 0 : int64_t length = context->GetEndDataOffset();
65 0 : int64_t position = context->GetResource()->Tell();
66 0 : MOZ_ASSERT(position <= context->GetResource()->GetLength());
67 0 : MOZ_ASSERT(position <= length);
68 0 : if (length >= 0 && count + position > length) {
69 0 : count = length - position;
70 : }
71 0 : MOZ_ASSERT(count <= aLength);
72 : }
73 0 : uint32_t bytes = 0;
74 : nsresult rv =
75 0 : context->GetResource()->Read(static_cast<char*>(aBuffer), count, &bytes);
76 0 : bool eof = bytes < aLength;
77 0 : return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
78 : }
79 :
80 0 : static int webmdemux_seek(int64_t aOffset, int aWhence, void* aUserData)
81 : {
82 0 : MOZ_ASSERT(aUserData);
83 : WebMDemuxer::NestEggContext* context =
84 0 : reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
85 0 : nsresult rv = context->GetResource()->Seek(aWhence, aOffset);
86 0 : return NS_SUCCEEDED(rv) ? 0 : -1;
87 : }
88 :
89 0 : static int64_t webmdemux_tell(void* aUserData)
90 : {
91 0 : MOZ_ASSERT(aUserData);
92 : WebMDemuxer::NestEggContext* context =
93 0 : reinterpret_cast<WebMDemuxer::NestEggContext*>(aUserData);
94 0 : return context->GetResource()->Tell();
95 : }
96 :
97 0 : static void webmdemux_log(nestegg* aContext,
98 : unsigned int aSeverity,
99 : char const* aFormat, ...)
100 : {
101 0 : if (!MOZ_LOG_TEST(gNesteggLog, LogLevel::Debug)) {
102 0 : return;
103 : }
104 :
105 : va_list args;
106 : char msg[256];
107 : const char* sevStr;
108 :
109 0 : switch(aSeverity) {
110 : case NESTEGG_LOG_DEBUG:
111 0 : sevStr = "DBG";
112 0 : break;
113 : case NESTEGG_LOG_INFO:
114 0 : sevStr = "INF";
115 0 : break;
116 : case NESTEGG_LOG_WARNING:
117 0 : sevStr = "WRN";
118 0 : break;
119 : case NESTEGG_LOG_ERROR:
120 0 : sevStr = "ERR";
121 0 : break;
122 : case NESTEGG_LOG_CRITICAL:
123 0 : sevStr = "CRT";
124 0 : break;
125 : default:
126 0 : sevStr = "UNK";
127 0 : break;
128 : }
129 :
130 0 : va_start(args, aFormat);
131 :
132 0 : SprintfLiteral(msg, "%p [Nestegg-%s] ", aContext, sevStr);
133 0 : PR_vsnprintf(msg+strlen(msg), sizeof(msg)-strlen(msg), aFormat, args);
134 0 : MOZ_LOG(gNesteggLog, LogLevel::Debug, ("%s", msg));
135 :
136 0 : va_end(args);
137 : }
138 :
139 0 : WebMDemuxer::NestEggContext::~NestEggContext()
140 : {
141 0 : if (mContext) {
142 0 : nestegg_destroy(mContext);
143 : }
144 0 : }
145 :
146 : int
147 0 : WebMDemuxer::NestEggContext::Init()
148 : {
149 : nestegg_io io;
150 0 : io.read = webmdemux_read;
151 0 : io.seek = webmdemux_seek;
152 0 : io.tell = webmdemux_tell;
153 0 : io.userdata = this;
154 :
155 : // While reading the metadata, we do not really care about which nestegg
156 : // context is being used so long that they are both initialised.
157 : // For reading the metadata however, we will use mVideoContext.
158 0 : return nestegg_init(&mContext, io, &webmdemux_log,
159 0 : mParent->IsMediaSource() ? mResource.GetLength() : -1);
160 : }
161 :
162 0 : WebMDemuxer::WebMDemuxer(MediaResource* aResource)
163 0 : : WebMDemuxer(aResource, false)
164 : {
165 0 : }
166 :
167 0 : WebMDemuxer::WebMDemuxer(MediaResource* aResource, bool aIsMediaSource)
168 : : mVideoContext(this, aResource)
169 : , mAudioContext(this, aResource)
170 : , mBufferedState(nullptr)
171 : , mInitData(nullptr)
172 : , mVideoTrack(0)
173 : , mAudioTrack(0)
174 : , mSeekPreroll(0)
175 : , mAudioCodec(-1)
176 : , mVideoCodec(-1)
177 : , mHasVideo(false)
178 : , mHasAudio(false)
179 : , mNeedReIndex(true)
180 : , mLastWebMBlockOffset(-1)
181 0 : , mIsMediaSource(aIsMediaSource)
182 : {
183 0 : }
184 :
185 0 : WebMDemuxer::~WebMDemuxer()
186 : {
187 0 : Reset(TrackInfo::kVideoTrack);
188 0 : Reset(TrackInfo::kAudioTrack);
189 0 : }
190 :
191 : RefPtr<WebMDemuxer::InitPromise>
192 0 : WebMDemuxer::Init()
193 : {
194 0 : InitBufferedState();
195 :
196 0 : if (NS_FAILED(ReadMetadata())) {
197 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
198 0 : __func__);
199 : }
200 :
201 0 : if (!GetNumberTracks(TrackInfo::kAudioTrack)
202 0 : && !GetNumberTracks(TrackInfo::kVideoTrack)) {
203 : return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_METADATA_ERR,
204 0 : __func__);
205 : }
206 :
207 0 : return InitPromise::CreateAndResolve(NS_OK, __func__);
208 : }
209 :
210 : void
211 0 : WebMDemuxer::InitBufferedState()
212 : {
213 0 : MOZ_ASSERT(!mBufferedState);
214 0 : mBufferedState = new WebMBufferedState;
215 0 : }
216 :
217 : bool
218 0 : WebMDemuxer::HasTrackType(TrackInfo::TrackType aType) const
219 : {
220 0 : return !!GetNumberTracks(aType);
221 : }
222 :
223 : uint32_t
224 0 : WebMDemuxer::GetNumberTracks(TrackInfo::TrackType aType) const
225 : {
226 0 : switch(aType) {
227 : case TrackInfo::kAudioTrack:
228 0 : return mHasAudio ? 1 : 0;
229 : case TrackInfo::kVideoTrack:
230 0 : return mHasVideo ? 1 : 0;
231 : default:
232 0 : return 0;
233 : }
234 : }
235 :
236 : UniquePtr<TrackInfo>
237 0 : WebMDemuxer::GetTrackInfo(TrackInfo::TrackType aType,
238 : size_t aTrackNumber) const
239 : {
240 0 : switch(aType) {
241 : case TrackInfo::kAudioTrack:
242 0 : return mInfo.mAudio.Clone();
243 : case TrackInfo::kVideoTrack:
244 0 : return mInfo.mVideo.Clone();
245 : default:
246 0 : return nullptr;
247 : }
248 : }
249 :
250 : already_AddRefed<MediaTrackDemuxer>
251 0 : WebMDemuxer::GetTrackDemuxer(TrackInfo::TrackType aType, uint32_t aTrackNumber)
252 : {
253 0 : if (GetNumberTracks(aType) <= aTrackNumber) {
254 0 : return nullptr;
255 : }
256 : RefPtr<WebMTrackDemuxer> e =
257 0 : new WebMTrackDemuxer(this, aType, aTrackNumber);
258 0 : mDemuxers.AppendElement(e);
259 :
260 0 : return e.forget();
261 : }
262 :
263 : nsresult
264 0 : WebMDemuxer::Reset(TrackInfo::TrackType aType)
265 : {
266 0 : if (aType == TrackInfo::kVideoTrack) {
267 0 : mVideoPackets.Reset();
268 : } else {
269 0 : mAudioPackets.Reset();
270 : }
271 0 : return NS_OK;
272 : }
273 :
274 : nsresult
275 0 : WebMDemuxer::ReadMetadata()
276 : {
277 0 : int r = mVideoContext.Init();
278 0 : if (r == -1) {
279 0 : return NS_ERROR_FAILURE;
280 : }
281 0 : if (mAudioContext.Init() == -1) {
282 0 : return NS_ERROR_FAILURE;
283 : }
284 :
285 : // For reading the metadata we can only use the video resource/context.
286 0 : MediaResourceIndex& resource = Resource(TrackInfo::kVideoTrack);
287 0 : nestegg* context = Context(TrackInfo::kVideoTrack);
288 :
289 : {
290 : // Check how much data nestegg read and force feed it to BufferedState.
291 0 : RefPtr<MediaByteBuffer> buffer = resource.MediaReadAt(0, resource.Tell());
292 0 : if (!buffer) {
293 0 : return NS_ERROR_FAILURE;
294 : }
295 0 : mBufferedState->NotifyDataArrived(buffer->Elements(), buffer->Length(), 0);
296 0 : if (mBufferedState->GetInitEndOffset() < 0) {
297 0 : return NS_ERROR_FAILURE;
298 : }
299 0 : MOZ_ASSERT(mBufferedState->GetInitEndOffset() <= resource.Tell());
300 : }
301 0 : mInitData = resource.MediaReadAt(0, mBufferedState->GetInitEndOffset());
302 0 : if (!mInitData
303 0 : || mInitData->Length() != size_t(mBufferedState->GetInitEndOffset())) {
304 0 : return NS_ERROR_FAILURE;
305 : }
306 :
307 0 : unsigned int ntracks = 0;
308 0 : r = nestegg_track_count(context, &ntracks);
309 0 : if (r == -1) {
310 0 : return NS_ERROR_FAILURE;
311 : }
312 :
313 0 : for (unsigned int track = 0; track < ntracks; ++track) {
314 0 : int id = nestegg_track_codec_id(context, track);
315 0 : if (id == -1) {
316 0 : return NS_ERROR_FAILURE;
317 : }
318 0 : int type = nestegg_track_type(context, track);
319 0 : if (type == NESTEGG_TRACK_VIDEO && !mHasVideo) {
320 : nestegg_video_params params;
321 0 : r = nestegg_track_video_params(context, track, ¶ms);
322 0 : if (r == -1) {
323 0 : return NS_ERROR_FAILURE;
324 : }
325 0 : mVideoCodec = nestegg_track_codec_id(context, track);
326 0 : switch(mVideoCodec) {
327 : case NESTEGG_CODEC_VP8:
328 0 : mInfo.mVideo.mMimeType = "video/webm; codecs=vp8";
329 0 : break;
330 : case NESTEGG_CODEC_VP9:
331 0 : mInfo.mVideo.mMimeType = "video/webm; codecs=vp9";
332 0 : break;
333 : case NESTEGG_CODEC_AV1:
334 0 : mInfo.mVideo.mMimeType = "video/webm; codecs=av1";
335 0 : break;
336 : default:
337 0 : NS_WARNING("Unknown WebM video codec");
338 0 : return NS_ERROR_FAILURE;
339 : }
340 : // Picture region, taking into account cropping, before scaling
341 : // to the display size.
342 0 : unsigned int cropH = params.crop_right + params.crop_left;
343 0 : unsigned int cropV = params.crop_bottom + params.crop_top;
344 : nsIntRect pictureRect(params.crop_left,
345 : params.crop_top,
346 0 : params.width - cropH,
347 0 : params.height - cropV);
348 :
349 : // If the cropping data appears invalid then use the frame data
350 0 : if (pictureRect.width <= 0
351 0 : || pictureRect.height <= 0
352 0 : || pictureRect.x < 0
353 0 : || pictureRect.y < 0) {
354 0 : pictureRect.x = 0;
355 0 : pictureRect.y = 0;
356 0 : pictureRect.width = params.width;
357 0 : pictureRect.height = params.height;
358 : }
359 :
360 : // Validate the container-reported frame and pictureRect sizes. This
361 : // ensures that our video frame creation code doesn't overflow.
362 0 : nsIntSize displaySize(params.display_width, params.display_height);
363 0 : nsIntSize frameSize(params.width, params.height);
364 0 : if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
365 : // Video track's frame sizes will overflow. Ignore the video track.
366 0 : continue;
367 : }
368 :
369 0 : mVideoTrack = track;
370 0 : mHasVideo = true;
371 :
372 0 : mInfo.mVideo.mDisplay = displaySize;
373 0 : mInfo.mVideo.mImage = frameSize;
374 0 : mInfo.mVideo.SetImageRect(pictureRect);
375 0 : mInfo.mVideo.SetAlpha(params.alpha_mode);
376 :
377 0 : switch (params.stereo_mode) {
378 : case NESTEGG_VIDEO_MONO:
379 0 : mInfo.mVideo.mStereoMode = StereoMode::MONO;
380 0 : break;
381 : case NESTEGG_VIDEO_STEREO_LEFT_RIGHT:
382 0 : mInfo.mVideo.mStereoMode = StereoMode::LEFT_RIGHT;
383 0 : break;
384 : case NESTEGG_VIDEO_STEREO_BOTTOM_TOP:
385 0 : mInfo.mVideo.mStereoMode = StereoMode::BOTTOM_TOP;
386 0 : break;
387 : case NESTEGG_VIDEO_STEREO_TOP_BOTTOM:
388 0 : mInfo.mVideo.mStereoMode = StereoMode::TOP_BOTTOM;
389 0 : break;
390 : case NESTEGG_VIDEO_STEREO_RIGHT_LEFT:
391 0 : mInfo.mVideo.mStereoMode = StereoMode::RIGHT_LEFT;
392 0 : break;
393 : }
394 0 : uint64_t duration = 0;
395 0 : r = nestegg_duration(context, &duration);
396 0 : if (!r) {
397 0 : mInfo.mVideo.mDuration = TimeUnit::FromNanoseconds(duration);
398 : }
399 0 : mInfo.mVideo.mCrypto = GetTrackCrypto(TrackInfo::kVideoTrack, track);
400 0 : if (mInfo.mVideo.mCrypto.mValid) {
401 0 : mCrypto.AddInitData(NS_LITERAL_STRING("webm"),
402 0 : mInfo.mVideo.mCrypto.mKeyId);
403 0 : }
404 0 : } else if (type == NESTEGG_TRACK_AUDIO && !mHasAudio) {
405 : nestegg_audio_params params;
406 0 : r = nestegg_track_audio_params(context, track, ¶ms);
407 0 : if (r == -1) {
408 0 : return NS_ERROR_FAILURE;
409 : }
410 :
411 0 : mAudioTrack = track;
412 0 : mHasAudio = true;
413 0 : mAudioCodec = nestegg_track_codec_id(context, track);
414 0 : if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
415 0 : mInfo.mAudio.mMimeType = "audio/vorbis";
416 0 : } else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
417 0 : mInfo.mAudio.mMimeType = "audio/opus";
418 0 : OpusDataDecoder::AppendCodecDelay(
419 : mInfo.mAudio.mCodecSpecificConfig,
420 0 : TimeUnit::FromNanoseconds(params.codec_delay).ToMicroseconds());
421 : }
422 0 : mSeekPreroll = params.seek_preroll;
423 0 : mInfo.mAudio.mRate = params.rate;
424 0 : mInfo.mAudio.mChannels = params.channels;
425 :
426 0 : unsigned int nheaders = 0;
427 0 : r = nestegg_track_codec_data_count(context, track, &nheaders);
428 0 : if (r == -1) {
429 0 : return NS_ERROR_FAILURE;
430 : }
431 :
432 0 : AutoTArray<const unsigned char*,4> headers;
433 0 : AutoTArray<size_t,4> headerLens;
434 0 : for (uint32_t header = 0; header < nheaders; ++header) {
435 0 : unsigned char* data = 0;
436 0 : size_t length = 0;
437 0 : r = nestegg_track_codec_data(context, track, header, &data, &length);
438 0 : if (r == -1) {
439 0 : return NS_ERROR_FAILURE;
440 : }
441 0 : headers.AppendElement(data);
442 0 : headerLens.AppendElement(length);
443 : }
444 :
445 : // Vorbis has 3 headers, convert to Xiph extradata format to send them to
446 : // the demuxer.
447 : // TODO: This is already the format WebM stores them in. Would be nice
448 : // to avoid having libnestegg split them only for us to pack them again,
449 : // but libnestegg does not give us an API to access this data directly.
450 0 : if (nheaders > 1) {
451 0 : if (!XiphHeadersToExtradata(mInfo.mAudio.mCodecSpecificConfig,
452 : headers, headerLens)) {
453 0 : return NS_ERROR_FAILURE;
454 : }
455 : }
456 : else {
457 0 : mInfo.mAudio.mCodecSpecificConfig->AppendElements(headers[0],
458 0 : headerLens[0]);
459 : }
460 0 : uint64_t duration = 0;
461 0 : r = nestegg_duration(context, &duration);
462 0 : if (!r) {
463 0 : mInfo.mAudio.mDuration = TimeUnit::FromNanoseconds(duration);
464 : }
465 0 : mInfo.mAudio.mCrypto = GetTrackCrypto(TrackInfo::kAudioTrack, track);
466 0 : if (mInfo.mAudio.mCrypto.mValid) {
467 0 : mCrypto.AddInitData(NS_LITERAL_STRING("webm"),
468 0 : mInfo.mAudio.mCrypto.mKeyId);
469 : }
470 : }
471 : }
472 0 : return NS_OK;
473 : }
474 :
475 : bool
476 0 : WebMDemuxer::IsSeekable() const
477 : {
478 0 : return Context(TrackInfo::kVideoTrack)
479 0 : && nestegg_has_cues(Context(TrackInfo::kVideoTrack));
480 : }
481 :
482 : bool
483 0 : WebMDemuxer::IsSeekableOnlyInBufferedRanges() const
484 : {
485 0 : return Context(TrackInfo::kVideoTrack)
486 0 : && !nestegg_has_cues(Context(TrackInfo::kVideoTrack));
487 : }
488 :
489 : void
490 0 : WebMDemuxer::EnsureUpToDateIndex()
491 : {
492 0 : if (!mNeedReIndex || !mInitData) {
493 0 : return;
494 : }
495 : AutoPinned<MediaResource> resource(
496 0 : Resource(TrackInfo::kVideoTrack).GetResource());
497 0 : MediaByteRangeSet byteRanges;
498 0 : nsresult rv = resource->GetCachedRanges(byteRanges);
499 0 : if (NS_FAILED(rv) || !byteRanges.Length()) {
500 0 : return;
501 : }
502 0 : mBufferedState->UpdateIndex(byteRanges, resource);
503 :
504 0 : mNeedReIndex = false;
505 :
506 0 : if (!mIsMediaSource) {
507 0 : return;
508 : }
509 0 : mLastWebMBlockOffset = mBufferedState->GetLastBlockOffset();
510 0 : MOZ_ASSERT(mLastWebMBlockOffset <= resource->GetLength());
511 : }
512 :
513 : void
514 0 : WebMDemuxer::NotifyDataArrived()
515 : {
516 0 : WEBM_DEBUG("");
517 0 : mNeedReIndex = true;
518 0 : }
519 :
520 : void
521 0 : WebMDemuxer::NotifyDataRemoved()
522 : {
523 0 : mBufferedState->Reset();
524 0 : if (mInitData) {
525 0 : mBufferedState->NotifyDataArrived(mInitData->Elements(),
526 0 : mInitData->Length(), 0);
527 : }
528 0 : mNeedReIndex = true;
529 0 : }
530 :
531 : UniquePtr<EncryptionInfo>
532 0 : WebMDemuxer::GetCrypto()
533 : {
534 0 : return mCrypto.IsEncrypted() ? MakeUnique<EncryptionInfo>(mCrypto) : nullptr;
535 : }
536 :
537 : CryptoTrack
538 0 : WebMDemuxer::GetTrackCrypto(TrackInfo::TrackType aType, size_t aTrackNumber)
539 : {
540 0 : const int WEBM_IV_SIZE = 16;
541 : const unsigned char * contentEncKeyId;
542 : size_t contentEncKeyIdLength;
543 0 : CryptoTrack crypto;
544 0 : nestegg* context = Context(aType);
545 :
546 0 : int r = nestegg_track_content_enc_key_id(
547 0 : context, aTrackNumber, &contentEncKeyId, &contentEncKeyIdLength);
548 :
549 0 : if (r == -1) {
550 0 : WEBM_DEBUG("nestegg_track_content_enc_key_id failed r=%d", r);
551 0 : return crypto;
552 : }
553 :
554 : uint32_t i;
555 0 : nsTArray<uint8_t> initData;
556 0 : for (i = 0; i < contentEncKeyIdLength; i++) {
557 0 : initData.AppendElement(contentEncKeyId[i]);
558 : }
559 :
560 0 : if (!initData.IsEmpty()) {
561 0 : crypto.mValid = true;
562 : // crypto.mMode is not used for WebMs
563 0 : crypto.mIVSize = WEBM_IV_SIZE;
564 0 : crypto.mKeyId = Move(initData);
565 : }
566 :
567 0 : return crypto;
568 : }
569 :
570 : nsresult
571 0 : WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType,
572 : MediaRawDataQueue *aSamples)
573 : {
574 0 : if (mIsMediaSource) {
575 : // To ensure mLastWebMBlockOffset is properly up to date.
576 0 : EnsureUpToDateIndex();
577 : }
578 :
579 0 : RefPtr<NesteggPacketHolder> holder;
580 0 : nsresult rv = NextPacket(aType, holder);
581 :
582 0 : if (NS_FAILED(rv)) {
583 0 : return rv;
584 : }
585 :
586 0 : int r = 0;
587 0 : unsigned int count = 0;
588 0 : r = nestegg_packet_count(holder->Packet(), &count);
589 0 : if (r == -1) {
590 0 : return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
591 : }
592 0 : int64_t tstamp = holder->Timestamp();
593 0 : int64_t duration = holder->Duration();
594 :
595 : // The end time of this frame is the start time of the next frame. Fetch
596 : // the timestamp of the next packet for this track. If we've reached the
597 : // end of the resource, use the file's duration as the end time of this
598 : // video frame.
599 0 : int64_t next_tstamp = INT64_MIN;
600 0 : if (aType == TrackInfo::kAudioTrack) {
601 0 : RefPtr<NesteggPacketHolder> next_holder;
602 0 : rv = NextPacket(aType, next_holder);
603 0 : if (NS_FAILED(rv) && rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
604 0 : return rv;
605 : }
606 0 : if (next_holder) {
607 0 : next_tstamp = next_holder->Timestamp();
608 0 : PushAudioPacket(next_holder);
609 0 : } else if (duration >= 0) {
610 0 : next_tstamp = tstamp + duration;
611 0 : } else if (!mIsMediaSource
612 0 : || (mIsMediaSource && mLastAudioFrameTime.isSome())) {
613 0 : next_tstamp = tstamp;
614 0 : next_tstamp += tstamp - mLastAudioFrameTime.refOr(0);
615 : } else {
616 0 : PushAudioPacket(holder);
617 : }
618 0 : mLastAudioFrameTime = Some(tstamp);
619 0 : } else if (aType == TrackInfo::kVideoTrack) {
620 0 : RefPtr<NesteggPacketHolder> next_holder;
621 0 : rv = NextPacket(aType, next_holder);
622 0 : if (NS_FAILED(rv) && rv != NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
623 0 : return rv;
624 : }
625 0 : if (next_holder) {
626 0 : next_tstamp = next_holder->Timestamp();
627 0 : PushVideoPacket(next_holder);
628 0 : } else if (duration >= 0) {
629 0 : next_tstamp = tstamp + duration;
630 0 : } else if (!mIsMediaSource
631 0 : || (mIsMediaSource && mLastVideoFrameTime.isSome())) {
632 0 : next_tstamp = tstamp;
633 0 : next_tstamp += tstamp - mLastVideoFrameTime.refOr(0);
634 : } else {
635 0 : PushVideoPacket(holder);
636 : }
637 0 : mLastVideoFrameTime = Some(tstamp);
638 : }
639 :
640 0 : if (mIsMediaSource && next_tstamp == INT64_MIN) {
641 0 : return NS_ERROR_DOM_MEDIA_END_OF_STREAM;
642 : }
643 :
644 0 : int64_t discardPadding = 0;
645 0 : if (aType == TrackInfo::kAudioTrack) {
646 0 : (void) nestegg_packet_discard_padding(holder->Packet(), &discardPadding);
647 : }
648 :
649 0 : int packetEncryption = nestegg_packet_encryption(holder->Packet());
650 :
651 0 : for (uint32_t i = 0; i < count; ++i) {
652 : unsigned char* data;
653 : size_t length;
654 0 : r = nestegg_packet_data(holder->Packet(), i, &data, &length);
655 0 : if (r == -1) {
656 0 : WEBM_DEBUG("nestegg_packet_data failed r=%d", r);
657 0 : return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
658 : }
659 : unsigned char* alphaData;
660 0 : size_t alphaLength = 0;
661 : // Check packets for alpha information if file has declared alpha frames
662 : // may be present.
663 0 : if (mInfo.mVideo.HasAlpha()) {
664 0 : r = nestegg_packet_additional_data(holder->Packet(),
665 : 1,
666 : &alphaData,
667 0 : &alphaLength);
668 0 : if (r == -1) {
669 0 : WEBM_DEBUG(
670 : "nestegg_packet_additional_data failed to retrieve alpha data r=%d",
671 : r);
672 : }
673 : }
674 0 : bool isKeyframe = false;
675 0 : if (aType == TrackInfo::kAudioTrack) {
676 0 : isKeyframe = true;
677 0 : } else if (aType == TrackInfo::kVideoTrack) {
678 0 : if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
679 : // Packet is encrypted, can't peek, use packet info
680 0 : isKeyframe = nestegg_packet_has_keyframe(holder->Packet())
681 : == NESTEGG_PACKET_HAS_KEYFRAME_TRUE;
682 : } else {
683 0 : auto sample = MakeSpan(data, length);
684 0 : switch (mVideoCodec) {
685 : case NESTEGG_CODEC_VP8:
686 0 : isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP8);
687 0 : break;
688 : case NESTEGG_CODEC_VP9:
689 0 : isKeyframe = VPXDecoder::IsKeyframe(sample, VPXDecoder::Codec::VP9);
690 0 : break;
691 : #ifdef MOZ_AV1
692 : case NESTEGG_CODEC_AV1:
693 0 : isKeyframe = AOMDecoder::IsKeyframe(sample);
694 0 : break;
695 : #endif
696 : default:
697 0 : NS_WARNING("Cannot detect keyframes in unknown WebM video codec");
698 0 : return NS_ERROR_FAILURE;
699 : }
700 0 : if (isKeyframe) {
701 : // For both VP8 and VP9, we only look for resolution changes
702 : // on keyframes. Other resolution changes are invalid.
703 0 : auto dimensions = nsIntSize(0, 0);
704 0 : switch (mVideoCodec) {
705 : case NESTEGG_CODEC_VP8:
706 0 : dimensions = VPXDecoder::GetFrameSize(sample, VPXDecoder::Codec::VP8);
707 0 : break;
708 : case NESTEGG_CODEC_VP9:
709 0 : dimensions = VPXDecoder::GetFrameSize(sample, VPXDecoder::Codec::VP9);
710 0 : break;
711 : #ifdef MOZ_AV1
712 : case NESTEGG_CODEC_AV1:
713 0 : dimensions = AOMDecoder::GetFrameSize(sample);
714 0 : break;
715 : #endif
716 : }
717 0 : if (mLastSeenFrameSize.isSome()
718 0 : && (dimensions != mLastSeenFrameSize.value())) {
719 0 : mInfo.mVideo.mDisplay = dimensions;
720 : mSharedVideoTrackInfo =
721 0 : new TrackInfoSharedPtr(mInfo.mVideo, ++sStreamSourceID);
722 : }
723 0 : mLastSeenFrameSize = Some(dimensions);
724 : }
725 : }
726 : }
727 :
728 0 : WEBM_DEBUG("push sample tstamp: %" PRId64 " next_tstamp: %" PRId64 " length: %" PRIuSIZE " kf: %d",
729 : tstamp, next_tstamp, length, isKeyframe);
730 0 : RefPtr<MediaRawData> sample;
731 0 : if (mInfo.mVideo.HasAlpha() && alphaLength != 0) {
732 0 : sample = new MediaRawData(data, length, alphaData, alphaLength);
733 0 : if ((length && !sample->Data()) || (alphaLength && !sample->AlphaData())) {
734 : // OOM.
735 0 : return NS_ERROR_OUT_OF_MEMORY;
736 : }
737 : } else {
738 0 : sample = new MediaRawData(data, length);
739 0 : if (length && !sample->Data()) {
740 : // OOM.
741 0 : return NS_ERROR_OUT_OF_MEMORY;
742 : }
743 : }
744 0 : sample->mTimecode = TimeUnit::FromMicroseconds(tstamp);
745 0 : sample->mTime = TimeUnit::FromMicroseconds(tstamp);
746 0 : sample->mDuration = TimeUnit::FromMicroseconds(next_tstamp - tstamp);
747 0 : sample->mOffset = holder->Offset();
748 0 : sample->mKeyframe = isKeyframe;
749 0 : if (discardPadding && i == count - 1) {
750 0 : CheckedInt64 discardFrames;
751 0 : if (discardPadding < 0) {
752 : // This is an invalid value as discard padding should never be negative.
753 : // Set to maximum value so that the decoder will reject it as it's
754 : // greater than the number of frames available.
755 0 : discardFrames = INT32_MAX;
756 0 : WEBM_DEBUG("Invalid negative discard padding");
757 : } else {
758 0 : discardFrames = TimeUnitToFrames(
759 0 : TimeUnit::FromNanoseconds(discardPadding), mInfo.mAudio.mRate);
760 : }
761 0 : if (discardFrames.isValid()) {
762 0 : sample->mDiscardPadding = discardFrames.value();
763 : }
764 : }
765 :
766 0 : if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED
767 0 : || packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED
768 0 : || packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
769 0 : nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
770 : unsigned char const* iv;
771 : size_t ivLength;
772 0 : nestegg_packet_iv(holder->Packet(), &iv, &ivLength);
773 0 : writer->mCrypto.mValid = true;
774 0 : writer->mCrypto.mIVSize = ivLength;
775 0 : if (ivLength == 0) {
776 : // Frame is not encrypted
777 0 : writer->mCrypto.mPlainSizes.AppendElement(length);
778 0 : writer->mCrypto.mEncryptedSizes.AppendElement(0);
779 : } else {
780 : // Frame is encrypted
781 0 : writer->mCrypto.mIV.AppendElements(iv, 8);
782 : // Iv from a sample is 64 bits, must be padded with 64 bits more 0s
783 : // in compliance with spec
784 0 : for (uint32_t i = 0; i < 8; i++) {
785 0 : writer->mCrypto.mIV.AppendElement(0);
786 : }
787 :
788 0 : if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
789 0 : writer->mCrypto.mPlainSizes.AppendElement(0);
790 0 : writer->mCrypto.mEncryptedSizes.AppendElement(length);
791 0 : } else if (packetEncryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_PARTITIONED) {
792 0 : uint8_t numPartitions = 0;
793 0 : const uint32_t* partitions = NULL;
794 0 : nestegg_packet_offsets(holder->Packet(), &partitions, &numPartitions);
795 :
796 : // WebM stores a list of 'partitions' in the data, which alternate
797 : // clear, encrypted. The data in the first partition is always clear.
798 : // So, and sample might look as follows:
799 : // 00|XXXX|000|XX, where | represents a partition, 0 a clear byte and
800 : // X an encrypted byte. If the first bytes in sample are unencrypted,
801 : // the first partition will be at zero |XXXX|000|XX.
802 : //
803 : // As GMP expects the lengths of the clear and encrypted chunks of
804 : // data, we calculate these from the difference between the last two
805 : // partitions.
806 0 : uint32_t lastOffset = 0;
807 0 : bool encrypted = false;
808 :
809 0 : for (uint8_t i = 0; i < numPartitions; i++) {
810 0 : uint32_t partition = partitions[i];
811 0 : uint32_t currentLength = partition - lastOffset;
812 :
813 0 : if (encrypted) {
814 0 : writer->mCrypto.mEncryptedSizes.AppendElement(currentLength);
815 : } else {
816 0 : writer->mCrypto.mPlainSizes.AppendElement(currentLength);
817 : }
818 :
819 0 : encrypted = !encrypted;
820 0 : lastOffset = partition;
821 :
822 0 : assert(lastOffset <= length);
823 : }
824 :
825 : // Add the data between the last offset and the end of the data.
826 : // 000|XXX|000
827 : // ^---^
828 0 : if (encrypted) {
829 0 : writer->mCrypto.mEncryptedSizes.AppendElement(length - lastOffset);
830 : } else {
831 0 : writer->mCrypto.mPlainSizes.AppendElement(length - lastOffset);
832 : }
833 :
834 : // Make sure we have an equal number of encrypted and plain sizes (GMP
835 : // expects this). This simple check is sufficient as there are two
836 : // possible cases at this point:
837 : // 1. The number of samples are even (so we don't need to do anything)
838 : // 2. There is one more clear sample than encrypted samples, so add a
839 : // zero length encrypted chunk.
840 : // There can never be more encrypted partitions than clear partitions
841 : // due to the alternating structure of the WebM samples and the
842 : // restriction that the first chunk is always clear.
843 0 : if (numPartitions % 2 == 0) {
844 0 : writer->mCrypto.mEncryptedSizes.AppendElement(0);
845 : }
846 :
847 : // Assert that the lengths of the encrypted and plain samples add to
848 : // the length of the data.
849 0 : assert(((size_t)(std::accumulate(writer->mCrypto.mPlainSizes.begin(), writer->mCrypto.mPlainSizes.end(), 0) \
850 : + std::accumulate(writer->mCrypto.mEncryptedSizes.begin(), writer->mCrypto.mEncryptedSizes.end(), 0)) \
851 0 : == length));
852 : }
853 : }
854 : }
855 0 : if (aType == TrackInfo::kVideoTrack) {
856 0 : sample->mTrackInfo = mSharedVideoTrackInfo;
857 : }
858 0 : aSamples->Push(sample);
859 : }
860 0 : return NS_OK;
861 : }
862 :
863 : nsresult
864 0 : WebMDemuxer::NextPacket(TrackInfo::TrackType aType,
865 : RefPtr<NesteggPacketHolder>& aPacket)
866 : {
867 0 : bool isVideo = aType == TrackInfo::kVideoTrack;
868 :
869 : // Flag to indicate that we do need to playback these types of
870 : // packets.
871 0 : bool hasType = isVideo ? mHasVideo : mHasAudio;
872 :
873 0 : if (!hasType) {
874 0 : return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
875 : }
876 :
877 : // The packet queue for the type that we are interested in.
878 0 : WebMPacketQueue &packets = isVideo ? mVideoPackets : mAudioPackets;
879 :
880 0 : if (packets.GetSize() > 0) {
881 0 : aPacket = packets.PopFront();
882 0 : return NS_OK;
883 : }
884 :
885 : // Track we are interested in
886 0 : uint32_t ourTrack = isVideo ? mVideoTrack : mAudioTrack;
887 :
888 : do {
889 0 : RefPtr<NesteggPacketHolder> holder;
890 0 : nsresult rv = DemuxPacket(aType, holder);
891 0 : if (NS_FAILED(rv)) {
892 0 : return rv;
893 : }
894 0 : if (!holder) {
895 0 : return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
896 : }
897 :
898 0 : if (ourTrack == holder->Track()) {
899 0 : aPacket = holder;
900 0 : return NS_OK;
901 0 : }
902 : } while (true);
903 : }
904 :
905 : nsresult
906 0 : WebMDemuxer::DemuxPacket(TrackInfo::TrackType aType,
907 : RefPtr<NesteggPacketHolder>& aPacket)
908 : {
909 : nestegg_packet* packet;
910 0 : int r = nestegg_read_packet(Context(aType), &packet);
911 0 : if (r == 0) {
912 0 : nestegg_read_reset(Context(aType));
913 0 : return NS_ERROR_DOM_MEDIA_END_OF_STREAM;
914 0 : } else if (r < 0) {
915 0 : return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
916 : }
917 :
918 0 : unsigned int track = 0;
919 0 : r = nestegg_packet_track(packet, &track);
920 0 : if (r == -1) {
921 0 : return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
922 : }
923 :
924 0 : int64_t offset = Resource(aType).Tell();
925 0 : RefPtr<NesteggPacketHolder> holder = new NesteggPacketHolder();
926 0 : if (!holder->Init(packet, offset, track, false)) {
927 0 : return NS_ERROR_DOM_MEDIA_DEMUXER_ERR;
928 : }
929 :
930 0 : aPacket = holder;
931 0 : return NS_OK;
932 : }
933 :
934 : void
935 0 : WebMDemuxer::PushAudioPacket(NesteggPacketHolder* aItem)
936 : {
937 0 : mAudioPackets.PushFront(aItem);
938 0 : }
939 :
940 : void
941 0 : WebMDemuxer::PushVideoPacket(NesteggPacketHolder* aItem)
942 : {
943 0 : mVideoPackets.PushFront(aItem);
944 0 : }
945 :
946 : nsresult
947 0 : WebMDemuxer::SeekInternal(TrackInfo::TrackType aType,
948 : const TimeUnit& aTarget)
949 : {
950 0 : EnsureUpToDateIndex();
951 0 : uint32_t trackToSeek = mHasVideo ? mVideoTrack : mAudioTrack;
952 0 : uint64_t target = aTarget.ToNanoseconds();
953 :
954 0 : if (NS_FAILED(Reset(aType))) {
955 0 : return NS_ERROR_FAILURE;
956 : }
957 :
958 0 : if (mSeekPreroll) {
959 0 : uint64_t startTime = 0;
960 0 : if (!mBufferedState->GetStartTime(&startTime)) {
961 0 : startTime = 0;
962 : }
963 0 : WEBM_DEBUG("Seek Target: %f",
964 : TimeUnit::FromNanoseconds(target).ToSeconds());
965 0 : if (target < mSeekPreroll || target - mSeekPreroll < startTime) {
966 0 : target = startTime;
967 : } else {
968 0 : target -= mSeekPreroll;
969 : }
970 0 : WEBM_DEBUG("SeekPreroll: %f StartTime: %f Adjusted Target: %f",
971 : TimeUnit::FromNanoseconds(mSeekPreroll).ToSeconds(),
972 : TimeUnit::FromNanoseconds(startTime).ToSeconds(),
973 : TimeUnit::FromNanoseconds(target).ToSeconds());
974 : }
975 0 : int r = nestegg_track_seek(Context(aType), trackToSeek, target);
976 0 : if (r == -1) {
977 0 : WEBM_DEBUG("track_seek for track %u to %f failed, r=%d", trackToSeek,
978 : TimeUnit::FromNanoseconds(target).ToSeconds(), r);
979 : // Try seeking directly based on cluster information in memory.
980 0 : int64_t offset = 0;
981 0 : bool rv = mBufferedState->GetOffsetForTime(target, &offset);
982 0 : if (!rv) {
983 0 : WEBM_DEBUG("mBufferedState->GetOffsetForTime failed too");
984 0 : return NS_ERROR_FAILURE;
985 : }
986 :
987 0 : r = nestegg_offset_seek(Context(aType), offset);
988 0 : if (r == -1) {
989 0 : WEBM_DEBUG("and nestegg_offset_seek to %" PRIu64 " failed", offset);
990 0 : return NS_ERROR_FAILURE;
991 : }
992 0 : WEBM_DEBUG("got offset from buffered state: %" PRIu64 "", offset);
993 : }
994 :
995 0 : if (aType == TrackInfo::kAudioTrack) {
996 0 : mLastAudioFrameTime.reset();
997 : } else {
998 0 : mLastVideoFrameTime.reset();
999 : }
1000 :
1001 0 : return NS_OK;
1002 : }
1003 :
1004 : media::TimeIntervals
1005 0 : WebMDemuxer::GetBuffered()
1006 : {
1007 0 : EnsureUpToDateIndex();
1008 : AutoPinned<MediaResource> resource(
1009 0 : Resource(TrackInfo::kVideoTrack).GetResource());
1010 :
1011 0 : media::TimeIntervals buffered;
1012 :
1013 0 : MediaByteRangeSet ranges;
1014 0 : nsresult rv = resource->GetCachedRanges(ranges);
1015 0 : if (NS_FAILED(rv)) {
1016 0 : return media::TimeIntervals();
1017 : }
1018 0 : uint64_t duration = 0;
1019 0 : uint64_t startOffset = 0;
1020 0 : if (!nestegg_duration(Context(TrackInfo::kVideoTrack), &duration)) {
1021 0 : if(mBufferedState->GetStartTime(&startOffset)) {
1022 0 : duration += startOffset;
1023 : }
1024 0 : WEBM_DEBUG("Duration: %f StartTime: %f",
1025 : TimeUnit::FromNanoseconds(duration).ToSeconds(),
1026 : TimeUnit::FromNanoseconds(startOffset).ToSeconds());
1027 : }
1028 0 : for (uint32_t index = 0; index < ranges.Length(); index++) {
1029 : uint64_t start, end;
1030 0 : bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
1031 0 : ranges[index].mEnd,
1032 0 : &start, &end);
1033 0 : if (rv) {
1034 0 : NS_ASSERTION(startOffset <= start,
1035 : "startOffset negative or larger than start time");
1036 :
1037 0 : if (duration && end > duration) {
1038 0 : WEBM_DEBUG("limit range to duration, end: %f duration: %f",
1039 : TimeUnit::FromNanoseconds(end).ToSeconds(),
1040 : TimeUnit::FromNanoseconds(duration).ToSeconds());
1041 0 : end = duration;
1042 : }
1043 0 : auto startTime = TimeUnit::FromNanoseconds(start);
1044 0 : auto endTime = TimeUnit::FromNanoseconds(end);
1045 0 : WEBM_DEBUG("add range %f-%f", startTime.ToSeconds(), endTime.ToSeconds());
1046 0 : buffered += media::TimeInterval(startTime, endTime);
1047 : }
1048 : }
1049 0 : return buffered;
1050 : }
1051 :
1052 0 : bool WebMDemuxer::GetOffsetForTime(uint64_t aTime, int64_t* aOffset)
1053 : {
1054 0 : EnsureUpToDateIndex();
1055 0 : return mBufferedState && mBufferedState->GetOffsetForTime(aTime, aOffset);
1056 : }
1057 :
1058 :
1059 : //WebMTrackDemuxer
1060 0 : WebMTrackDemuxer::WebMTrackDemuxer(WebMDemuxer* aParent,
1061 : TrackInfo::TrackType aType,
1062 0 : uint32_t aTrackNumber)
1063 : : mParent(aParent)
1064 : , mType(aType)
1065 0 : , mNeedKeyframe(true)
1066 : {
1067 0 : mInfo = mParent->GetTrackInfo(aType, aTrackNumber);
1068 0 : MOZ_ASSERT(mInfo);
1069 0 : }
1070 :
1071 0 : WebMTrackDemuxer::~WebMTrackDemuxer()
1072 : {
1073 0 : mSamples.Reset();
1074 0 : }
1075 :
1076 : UniquePtr<TrackInfo>
1077 0 : WebMTrackDemuxer::GetInfo() const
1078 : {
1079 0 : return mInfo->Clone();
1080 : }
1081 :
1082 : RefPtr<WebMTrackDemuxer::SeekPromise>
1083 0 : WebMTrackDemuxer::Seek(const TimeUnit& aTime)
1084 : {
1085 : // Seeks to aTime. Upon success, SeekPromise will be resolved with the
1086 : // actual time seeked to. Typically the random access point time
1087 :
1088 0 : auto seekTime = aTime;
1089 0 : mSamples.Reset();
1090 0 : mParent->SeekInternal(mType, aTime);
1091 0 : nsresult rv = mParent->GetNextPacket(mType, &mSamples);
1092 0 : if (NS_FAILED(rv)) {
1093 0 : if (rv == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
1094 : // Ignore the error for now, the next GetSample will be rejected with EOS.
1095 0 : return SeekPromise::CreateAndResolve(TimeUnit::Zero(), __func__);
1096 : }
1097 0 : return SeekPromise::CreateAndReject(rv, __func__);
1098 : }
1099 0 : mNeedKeyframe = true;
1100 :
1101 : // Check what time we actually seeked to.
1102 0 : if (mSamples.GetSize() > 0) {
1103 0 : const RefPtr<MediaRawData>& sample = mSamples.First();
1104 0 : seekTime = sample->mTime;
1105 : }
1106 0 : SetNextKeyFrameTime();
1107 :
1108 0 : return SeekPromise::CreateAndResolve(seekTime, __func__);
1109 : }
1110 :
1111 : nsresult
1112 0 : WebMTrackDemuxer::NextSample(RefPtr<MediaRawData>& aData)
1113 : {
1114 0 : nsresult rv = NS_ERROR_DOM_MEDIA_END_OF_STREAM;;
1115 0 : while (mSamples.GetSize() < 1 &&
1116 0 : NS_SUCCEEDED((rv = mParent->GetNextPacket(mType, &mSamples)))) {
1117 : }
1118 0 : if (mSamples.GetSize()) {
1119 0 : aData = mSamples.PopFront();
1120 0 : return NS_OK;
1121 : }
1122 0 : return rv;
1123 : }
1124 :
1125 : RefPtr<WebMTrackDemuxer::SamplesPromise>
1126 0 : WebMTrackDemuxer::GetSamples(int32_t aNumSamples)
1127 : {
1128 0 : RefPtr<SamplesHolder> samples = new SamplesHolder;
1129 0 : MOZ_ASSERT(aNumSamples);
1130 :
1131 0 : nsresult rv = NS_ERROR_DOM_MEDIA_END_OF_STREAM;
1132 :
1133 0 : while (aNumSamples) {
1134 0 : RefPtr<MediaRawData> sample;
1135 0 : rv = NextSample(sample);
1136 0 : if (NS_FAILED(rv)) {
1137 0 : break;
1138 : }
1139 0 : if (mNeedKeyframe && !sample->mKeyframe) {
1140 0 : continue;
1141 : }
1142 0 : mNeedKeyframe = false;
1143 0 : samples->mSamples.AppendElement(sample);
1144 0 : aNumSamples--;
1145 : }
1146 :
1147 0 : if (samples->mSamples.IsEmpty()) {
1148 0 : return SamplesPromise::CreateAndReject(rv, __func__);
1149 : } else {
1150 0 : UpdateSamples(samples->mSamples);
1151 0 : return SamplesPromise::CreateAndResolve(samples, __func__);
1152 : }
1153 : }
1154 :
1155 : void
1156 0 : WebMTrackDemuxer::SetNextKeyFrameTime()
1157 : {
1158 0 : if (mType != TrackInfo::kVideoTrack || mParent->IsMediaSource()) {
1159 0 : return;
1160 : }
1161 :
1162 0 : auto frameTime = TimeUnit::Invalid();
1163 :
1164 0 : mNextKeyframeTime.reset();
1165 :
1166 0 : MediaRawDataQueue skipSamplesQueue;
1167 0 : bool foundKeyframe = false;
1168 0 : while (!foundKeyframe && mSamples.GetSize()) {
1169 0 : RefPtr<MediaRawData> sample = mSamples.PopFront();
1170 0 : if (sample->mKeyframe) {
1171 0 : frameTime = sample->mTime;
1172 0 : foundKeyframe = true;
1173 : }
1174 0 : skipSamplesQueue.Push(sample.forget());
1175 : }
1176 0 : Maybe<int64_t> startTime;
1177 0 : if (skipSamplesQueue.GetSize()) {
1178 0 : const RefPtr<MediaRawData>& sample = skipSamplesQueue.First();
1179 0 : startTime.emplace(sample->mTimecode.ToMicroseconds());
1180 : }
1181 : // Demux and buffer frames until we find a keyframe.
1182 0 : RefPtr<MediaRawData> sample;
1183 0 : nsresult rv = NS_OK;
1184 0 : while (!foundKeyframe && NS_SUCCEEDED((rv = NextSample(sample)))) {
1185 0 : if (sample->mKeyframe) {
1186 0 : frameTime = sample->mTime;
1187 0 : foundKeyframe = true;
1188 : }
1189 0 : int64_t sampleTimecode = sample->mTimecode.ToMicroseconds();
1190 0 : skipSamplesQueue.Push(sample.forget());
1191 0 : if (!startTime) {
1192 0 : startTime.emplace(sampleTimecode);
1193 0 : } else if (!foundKeyframe
1194 0 : && sampleTimecode > startTime.ref() + MAX_LOOK_AHEAD) {
1195 0 : WEBM_DEBUG("Couldn't find keyframe in a reasonable time, aborting");
1196 0 : break;
1197 : }
1198 : }
1199 : // We may have demuxed more than intended, so ensure that all frames are kept
1200 : // in the right order.
1201 0 : mSamples.PushFront(Move(skipSamplesQueue));
1202 :
1203 0 : if (frameTime.IsValid()) {
1204 0 : mNextKeyframeTime.emplace(frameTime);
1205 0 : WEBM_DEBUG("Next Keyframe %f (%u queued %.02fs)",
1206 : mNextKeyframeTime.value().ToSeconds(),
1207 : uint32_t(mSamples.GetSize()),
1208 : (mSamples.Last()->mTimecode - mSamples.First()->mTimecode).ToSeconds());
1209 : } else {
1210 0 : WEBM_DEBUG("Couldn't determine next keyframe time (%u queued)",
1211 : uint32_t(mSamples.GetSize()));
1212 : }
1213 : }
1214 :
1215 : void
1216 0 : WebMTrackDemuxer::Reset()
1217 : {
1218 0 : mSamples.Reset();
1219 0 : media::TimeIntervals buffered = GetBuffered();
1220 0 : mNeedKeyframe = true;
1221 0 : if (buffered.Length()) {
1222 0 : WEBM_DEBUG("Seek to start point: %f", buffered.Start(0).ToSeconds());
1223 0 : mParent->SeekInternal(mType, buffered.Start(0));
1224 0 : SetNextKeyFrameTime();
1225 : } else {
1226 0 : mNextKeyframeTime.reset();
1227 : }
1228 0 : }
1229 :
1230 : void
1231 0 : WebMTrackDemuxer::UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples)
1232 : {
1233 0 : for (const auto& sample : aSamples) {
1234 0 : if (sample->mCrypto.mValid) {
1235 0 : nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
1236 0 : writer->mCrypto.mMode = mInfo->mCrypto.mMode;
1237 0 : writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
1238 0 : writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
1239 : }
1240 : }
1241 0 : if (mNextKeyframeTime.isNothing()
1242 0 : || aSamples.LastElement()->mTime >= mNextKeyframeTime.value()) {
1243 0 : SetNextKeyFrameTime();
1244 : }
1245 0 : }
1246 :
1247 : nsresult
1248 0 : WebMTrackDemuxer::GetNextRandomAccessPoint(TimeUnit* aTime)
1249 : {
1250 0 : if (mNextKeyframeTime.isNothing()) {
1251 : // There's no next key frame.
1252 0 : *aTime = TimeUnit::FromInfinity();
1253 : } else {
1254 0 : *aTime = mNextKeyframeTime.ref();
1255 : }
1256 0 : return NS_OK;
1257 : }
1258 :
1259 : RefPtr<WebMTrackDemuxer::SkipAccessPointPromise>
1260 0 : WebMTrackDemuxer::SkipToNextRandomAccessPoint(const TimeUnit& aTimeThreshold)
1261 : {
1262 0 : uint32_t parsed = 0;
1263 0 : bool found = false;
1264 0 : RefPtr<MediaRawData> sample;
1265 0 : nsresult rv = NS_OK;
1266 :
1267 0 : WEBM_DEBUG("TimeThreshold: %f", aTimeThreshold.ToSeconds());
1268 0 : while (!found && NS_SUCCEEDED((rv = NextSample(sample)))) {
1269 0 : parsed++;
1270 0 : if (sample->mKeyframe && sample->mTime >= aTimeThreshold) {
1271 0 : WEBM_DEBUG("next sample: %f (parsed: %d)",
1272 : sample->mTime.ToSeconds(), parsed);
1273 0 : found = true;
1274 0 : mSamples.Reset();
1275 0 : mSamples.PushFront(sample.forget());
1276 : }
1277 : }
1278 0 : if (NS_SUCCEEDED(rv)) {
1279 0 : SetNextKeyFrameTime();
1280 : }
1281 0 : if (found) {
1282 0 : return SkipAccessPointPromise::CreateAndResolve(parsed, __func__);
1283 : } else {
1284 0 : SkipFailureHolder failure(NS_ERROR_DOM_MEDIA_END_OF_STREAM, parsed);
1285 0 : return SkipAccessPointPromise::CreateAndReject(Move(failure), __func__);
1286 : }
1287 : }
1288 :
1289 : media::TimeIntervals
1290 0 : WebMTrackDemuxer::GetBuffered()
1291 : {
1292 0 : return mParent->GetBuffered();
1293 : }
1294 :
1295 : void
1296 0 : WebMTrackDemuxer::BreakCycles()
1297 : {
1298 0 : mParent = nullptr;
1299 0 : }
1300 :
1301 : int64_t
1302 0 : WebMTrackDemuxer::GetEvictionOffset(const TimeUnit& aTime)
1303 : {
1304 : int64_t offset;
1305 0 : if (!mParent->GetOffsetForTime(aTime.ToNanoseconds(), &offset)) {
1306 0 : return 0;
1307 : }
1308 :
1309 0 : return offset;
1310 : }
1311 :
1312 : #undef WEBM_DEBUG
1313 : } // namespace mozilla
|