content/media/webm/WebMReader.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 #include "nsError.h"
michael@0 7 #include "MediaDecoderStateMachine.h"
michael@0 8 #include "AbstractMediaDecoder.h"
michael@0 9 #include "MediaResource.h"
michael@0 10 #include "WebMReader.h"
michael@0 11 #include "WebMBufferedParser.h"
michael@0 12 #include "mozilla/dom/TimeRanges.h"
michael@0 13 #include "VorbisUtils.h"
michael@0 14 #include "gfx2DGlue.h"
michael@0 15
michael@0 16 #include <algorithm>
michael@0 17
michael@0 18 #define VPX_DONT_DEFINE_STDINT_TYPES
michael@0 19 #include "vpx/vp8dx.h"
michael@0 20 #include "vpx/vpx_decoder.h"
michael@0 21
michael@0 22 #include "OggReader.h"
michael@0 23
michael@0 24 using mozilla::NesteggPacketHolder;
michael@0 25
michael@0 26 template <>
michael@0 27 class nsAutoRefTraits<NesteggPacketHolder> : public nsPointerRefTraits<NesteggPacketHolder>
michael@0 28 {
michael@0 29 public:
michael@0 30 static void Release(NesteggPacketHolder* aHolder) { delete aHolder; }
michael@0 31 };
michael@0 32
michael@0 33 namespace mozilla {
michael@0 34
michael@0 35 using namespace gfx;
michael@0 36 using namespace layers;
michael@0 37
michael@0 38 // Un-comment to enable logging of seek bisections.
michael@0 39 //#define SEEK_LOGGING
michael@0 40
michael@0 41 #ifdef PR_LOGGING
michael@0 42 extern PRLogModuleInfo* gMediaDecoderLog;
michael@0 43 PRLogModuleInfo* gNesteggLog;
michael@0 44 #define LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
michael@0 45 #ifdef SEEK_LOGGING
michael@0 46 #define SEEK_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
michael@0 47 #else
michael@0 48 #define SEEK_LOG(type, msg)
michael@0 49 #endif
michael@0 50 #else
michael@0 51 #define LOG(type, msg)
michael@0 52 #define SEEK_LOG(type, msg)
michael@0 53 #endif
michael@0 54
michael@0 55 static const unsigned NS_PER_USEC = 1000;
michael@0 56 static const double NS_PER_S = 1e9;
michael@0 57
michael@0 58 // Functions for reading and seeking using MediaResource required for
michael@0 59 // nestegg_io. The 'user data' passed to these functions is the
michael@0 60 // decoder from which the media resource is obtained.
michael@0 61 static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
michael@0 62 {
michael@0 63 NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
michael@0 64 AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
michael@0 65 MediaResource* resource = decoder->GetResource();
michael@0 66 NS_ASSERTION(resource, "Decoder has no media resource");
michael@0 67
michael@0 68 nsresult rv = NS_OK;
michael@0 69 bool eof = false;
michael@0 70
michael@0 71 char *p = static_cast<char *>(aBuffer);
michael@0 72 while (NS_SUCCEEDED(rv) && aLength > 0) {
michael@0 73 uint32_t bytes = 0;
michael@0 74 rv = resource->Read(p, aLength, &bytes);
michael@0 75 if (bytes == 0) {
michael@0 76 eof = true;
michael@0 77 break;
michael@0 78 }
michael@0 79 aLength -= bytes;
michael@0 80 p += bytes;
michael@0 81 }
michael@0 82
michael@0 83 return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
michael@0 84 }
michael@0 85
michael@0 86 static int webm_seek(int64_t aOffset, int aWhence, void *aUserData)
michael@0 87 {
michael@0 88 NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
michael@0 89 AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
michael@0 90 MediaResource* resource = decoder->GetResource();
michael@0 91 NS_ASSERTION(resource, "Decoder has no media resource");
michael@0 92 nsresult rv = resource->Seek(aWhence, aOffset);
michael@0 93 return NS_SUCCEEDED(rv) ? 0 : -1;
michael@0 94 }
michael@0 95
michael@0 96 static int64_t webm_tell(void *aUserData)
michael@0 97 {
michael@0 98 NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
michael@0 99 AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
michael@0 100 MediaResource* resource = decoder->GetResource();
michael@0 101 NS_ASSERTION(resource, "Decoder has no media resource");
michael@0 102 return resource->Tell();
michael@0 103 }
michael@0 104
michael@0 105 static void webm_log(nestegg * context,
michael@0 106 unsigned int severity,
michael@0 107 char const * format, ...)
michael@0 108 {
michael@0 109 #ifdef PR_LOGGING
michael@0 110 va_list args;
michael@0 111 char msg[256];
michael@0 112 const char * sevStr;
michael@0 113
michael@0 114 switch(severity) {
michael@0 115 case NESTEGG_LOG_DEBUG:
michael@0 116 sevStr = "DBG";
michael@0 117 break;
michael@0 118 case NESTEGG_LOG_INFO:
michael@0 119 sevStr = "INF";
michael@0 120 break;
michael@0 121 case NESTEGG_LOG_WARNING:
michael@0 122 sevStr = "WRN";
michael@0 123 break;
michael@0 124 case NESTEGG_LOG_ERROR:
michael@0 125 sevStr = "ERR";
michael@0 126 break;
michael@0 127 case NESTEGG_LOG_CRITICAL:
michael@0 128 sevStr = "CRT";
michael@0 129 break;
michael@0 130 default:
michael@0 131 sevStr = "UNK";
michael@0 132 break;
michael@0 133 }
michael@0 134
michael@0 135 va_start(args, format);
michael@0 136
michael@0 137 PR_snprintf(msg, sizeof(msg), "%p [Nestegg-%s] ", context, sevStr);
michael@0 138 PR_vsnprintf(msg+strlen(msg), sizeof(msg)-strlen(msg), format, args);
michael@0 139 PR_LOG(gNesteggLog, PR_LOG_DEBUG, (msg));
michael@0 140
michael@0 141 va_end(args);
michael@0 142 #endif
michael@0 143 }
michael@0 144
michael@0 145 WebMReader::WebMReader(AbstractMediaDecoder* aDecoder)
michael@0 146 : MediaDecoderReader(aDecoder),
michael@0 147 mContext(nullptr),
michael@0 148 mPacketCount(0),
michael@0 149 mChannels(0),
michael@0 150 #ifdef MOZ_OPUS
michael@0 151 mOpusParser(nullptr),
michael@0 152 mOpusDecoder(nullptr),
michael@0 153 mSkip(0),
michael@0 154 mSeekPreroll(0),
michael@0 155 #endif
michael@0 156 mVideoTrack(0),
michael@0 157 mAudioTrack(0),
michael@0 158 mAudioStartUsec(-1),
michael@0 159 mAudioFrames(0),
michael@0 160 mAudioCodec(-1),
michael@0 161 mVideoCodec(-1),
michael@0 162 mHasVideo(false),
michael@0 163 mHasAudio(false)
michael@0 164 {
michael@0 165 MOZ_COUNT_CTOR(WebMReader);
michael@0 166 #ifdef PR_LOGGING
michael@0 167 if (!gNesteggLog) {
michael@0 168 gNesteggLog = PR_NewLogModule("Nestegg");
michael@0 169 }
michael@0 170 #endif
michael@0 171 // Zero these member vars to avoid crashes in VP8 destroy and Vorbis clear
michael@0 172 // functions when destructor is called before |Init|.
michael@0 173 memset(&mVPX, 0, sizeof(vpx_codec_ctx_t));
michael@0 174 memset(&mVorbisBlock, 0, sizeof(vorbis_block));
michael@0 175 memset(&mVorbisDsp, 0, sizeof(vorbis_dsp_state));
michael@0 176 memset(&mVorbisInfo, 0, sizeof(vorbis_info));
michael@0 177 memset(&mVorbisComment, 0, sizeof(vorbis_comment));
michael@0 178 }
michael@0 179
michael@0 180 WebMReader::~WebMReader()
michael@0 181 {
michael@0 182 Cleanup();
michael@0 183
michael@0 184 mVideoPackets.Reset();
michael@0 185 mAudioPackets.Reset();
michael@0 186
michael@0 187 vpx_codec_destroy(&mVPX);
michael@0 188
michael@0 189 vorbis_block_clear(&mVorbisBlock);
michael@0 190 vorbis_dsp_clear(&mVorbisDsp);
michael@0 191 vorbis_info_clear(&mVorbisInfo);
michael@0 192 vorbis_comment_clear(&mVorbisComment);
michael@0 193
michael@0 194 if (mOpusDecoder) {
michael@0 195 opus_multistream_decoder_destroy(mOpusDecoder);
michael@0 196 mOpusDecoder = nullptr;
michael@0 197 }
michael@0 198
michael@0 199 MOZ_COUNT_DTOR(WebMReader);
michael@0 200 }
michael@0 201
michael@0 202 nsresult WebMReader::Init(MediaDecoderReader* aCloneDonor)
michael@0 203 {
michael@0 204
michael@0 205 vorbis_info_init(&mVorbisInfo);
michael@0 206 vorbis_comment_init(&mVorbisComment);
michael@0 207 memset(&mVorbisDsp, 0, sizeof(vorbis_dsp_state));
michael@0 208 memset(&mVorbisBlock, 0, sizeof(vorbis_block));
michael@0 209
michael@0 210 if (aCloneDonor) {
michael@0 211 mBufferedState = static_cast<WebMReader*>(aCloneDonor)->mBufferedState;
michael@0 212 } else {
michael@0 213 mBufferedState = new WebMBufferedState;
michael@0 214 }
michael@0 215
michael@0 216 return NS_OK;
michael@0 217 }
michael@0 218
michael@0 219 nsresult WebMReader::ResetDecode()
michael@0 220 {
michael@0 221 mAudioFrames = 0;
michael@0 222 mAudioStartUsec = -1;
michael@0 223 nsresult res = NS_OK;
michael@0 224 if (NS_FAILED(MediaDecoderReader::ResetDecode())) {
michael@0 225 res = NS_ERROR_FAILURE;
michael@0 226 }
michael@0 227
michael@0 228 if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
michael@0 229 // Ignore failed results from vorbis_synthesis_restart. They
michael@0 230 // aren't fatal and it fails when ResetDecode is called at a
michael@0 231 // time when no vorbis data has been read.
michael@0 232 vorbis_synthesis_restart(&mVorbisDsp);
michael@0 233 #ifdef MOZ_OPUS
michael@0 234 } else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
michael@0 235 if (mOpusDecoder) {
michael@0 236 // Reset the decoder.
michael@0 237 opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
michael@0 238 mSkip = mOpusParser->mPreSkip;
michael@0 239 }
michael@0 240 #endif
michael@0 241 }
michael@0 242
michael@0 243 mVideoPackets.Reset();
michael@0 244 mAudioPackets.Reset();
michael@0 245
michael@0 246 return res;
michael@0 247 }
michael@0 248
michael@0 249 void WebMReader::Cleanup()
michael@0 250 {
michael@0 251 if (mContext) {
michael@0 252 nestegg_destroy(mContext);
michael@0 253 mContext = nullptr;
michael@0 254 }
michael@0 255 }
michael@0 256
michael@0 257 nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
michael@0 258 MetadataTags** aTags)
michael@0 259 {
michael@0 260 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
michael@0 261
michael@0 262 nestegg_io io;
michael@0 263 io.read = webm_read;
michael@0 264 io.seek = webm_seek;
michael@0 265 io.tell = webm_tell;
michael@0 266 io.userdata = mDecoder;
michael@0 267 int64_t maxOffset = -1;
michael@0 268 int r = nestegg_init(&mContext, io, &webm_log, maxOffset);
michael@0 269 if (r == -1) {
michael@0 270 return NS_ERROR_FAILURE;
michael@0 271 }
michael@0 272
michael@0 273 uint64_t duration = 0;
michael@0 274 r = nestegg_duration(mContext, &duration);
michael@0 275 if (r == 0) {
michael@0 276 ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
michael@0 277 mDecoder->SetMediaDuration(duration / NS_PER_USEC);
michael@0 278 }
michael@0 279
michael@0 280 unsigned int ntracks = 0;
michael@0 281 r = nestegg_track_count(mContext, &ntracks);
michael@0 282 if (r == -1) {
michael@0 283 Cleanup();
michael@0 284 return NS_ERROR_FAILURE;
michael@0 285 }
michael@0 286
michael@0 287 for (uint32_t track = 0; track < ntracks; ++track) {
michael@0 288 int id = nestegg_track_codec_id(mContext, track);
michael@0 289 if (id == -1) {
michael@0 290 Cleanup();
michael@0 291 return NS_ERROR_FAILURE;
michael@0 292 }
michael@0 293 int type = nestegg_track_type(mContext, track);
michael@0 294 if (!mHasVideo && type == NESTEGG_TRACK_VIDEO) {
michael@0 295 nestegg_video_params params;
michael@0 296 r = nestegg_track_video_params(mContext, track, &params);
michael@0 297 if (r == -1) {
michael@0 298 Cleanup();
michael@0 299 return NS_ERROR_FAILURE;
michael@0 300 }
michael@0 301
michael@0 302 vpx_codec_iface_t* dx = nullptr;
michael@0 303 mVideoCodec = nestegg_track_codec_id(mContext, track);
michael@0 304 if (mVideoCodec == NESTEGG_CODEC_VP8) {
michael@0 305 dx = vpx_codec_vp8_dx();
michael@0 306 } else if (mVideoCodec == NESTEGG_CODEC_VP9) {
michael@0 307 dx = vpx_codec_vp9_dx();
michael@0 308 }
michael@0 309 if (!dx || vpx_codec_dec_init(&mVPX, dx, nullptr, 0)) {
michael@0 310 Cleanup();
michael@0 311 return NS_ERROR_FAILURE;
michael@0 312 }
michael@0 313
michael@0 314 // Picture region, taking into account cropping, before scaling
michael@0 315 // to the display size.
michael@0 316 nsIntRect pictureRect(params.crop_left,
michael@0 317 params.crop_top,
michael@0 318 params.width - (params.crop_right + params.crop_left),
michael@0 319 params.height - (params.crop_bottom + params.crop_top));
michael@0 320
michael@0 321 // If the cropping data appears invalid then use the frame data
michael@0 322 if (pictureRect.width <= 0 ||
michael@0 323 pictureRect.height <= 0 ||
michael@0 324 pictureRect.x < 0 ||
michael@0 325 pictureRect.y < 0)
michael@0 326 {
michael@0 327 pictureRect.x = 0;
michael@0 328 pictureRect.y = 0;
michael@0 329 pictureRect.width = params.width;
michael@0 330 pictureRect.height = params.height;
michael@0 331 }
michael@0 332
michael@0 333 // Validate the container-reported frame and pictureRect sizes. This ensures
michael@0 334 // that our video frame creation code doesn't overflow.
michael@0 335 nsIntSize displaySize(params.display_width, params.display_height);
michael@0 336 nsIntSize frameSize(params.width, params.height);
michael@0 337 if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
michael@0 338 // Video track's frame sizes will overflow. Ignore the video track.
michael@0 339 continue;
michael@0 340 }
michael@0 341
michael@0 342 mVideoTrack = track;
michael@0 343 mHasVideo = true;
michael@0 344 mInfo.mVideo.mHasVideo = true;
michael@0 345
michael@0 346 mInfo.mVideo.mDisplay = displaySize;
michael@0 347 mPicture = pictureRect;
michael@0 348 mInitialFrame = frameSize;
michael@0 349
michael@0 350 switch (params.stereo_mode) {
michael@0 351 case NESTEGG_VIDEO_MONO:
michael@0 352 mInfo.mVideo.mStereoMode = StereoMode::MONO;
michael@0 353 break;
michael@0 354 case NESTEGG_VIDEO_STEREO_LEFT_RIGHT:
michael@0 355 mInfo.mVideo.mStereoMode = StereoMode::LEFT_RIGHT;
michael@0 356 break;
michael@0 357 case NESTEGG_VIDEO_STEREO_BOTTOM_TOP:
michael@0 358 mInfo.mVideo.mStereoMode = StereoMode::BOTTOM_TOP;
michael@0 359 break;
michael@0 360 case NESTEGG_VIDEO_STEREO_TOP_BOTTOM:
michael@0 361 mInfo.mVideo.mStereoMode = StereoMode::TOP_BOTTOM;
michael@0 362 break;
michael@0 363 case NESTEGG_VIDEO_STEREO_RIGHT_LEFT:
michael@0 364 mInfo.mVideo.mStereoMode = StereoMode::RIGHT_LEFT;
michael@0 365 break;
michael@0 366 }
michael@0 367 }
michael@0 368 else if (!mHasAudio && type == NESTEGG_TRACK_AUDIO) {
michael@0 369 nestegg_audio_params params;
michael@0 370 r = nestegg_track_audio_params(mContext, track, &params);
michael@0 371 if (r == -1) {
michael@0 372 Cleanup();
michael@0 373 return NS_ERROR_FAILURE;
michael@0 374 }
michael@0 375
michael@0 376 mAudioTrack = track;
michael@0 377 mHasAudio = true;
michael@0 378 mInfo.mAudio.mHasAudio = true;
michael@0 379 mAudioCodec = nestegg_track_codec_id(mContext, track);
michael@0 380 mCodecDelay = params.codec_delay / NS_PER_USEC;
michael@0 381
michael@0 382 if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
michael@0 383 // Get the Vorbis header data
michael@0 384 unsigned int nheaders = 0;
michael@0 385 r = nestegg_track_codec_data_count(mContext, track, &nheaders);
michael@0 386 if (r == -1 || nheaders != 3) {
michael@0 387 Cleanup();
michael@0 388 return NS_ERROR_FAILURE;
michael@0 389 }
michael@0 390
michael@0 391 for (uint32_t header = 0; header < nheaders; ++header) {
michael@0 392 unsigned char* data = 0;
michael@0 393 size_t length = 0;
michael@0 394
michael@0 395 r = nestegg_track_codec_data(mContext, track, header, &data, &length);
michael@0 396 if (r == -1) {
michael@0 397 Cleanup();
michael@0 398 return NS_ERROR_FAILURE;
michael@0 399 }
michael@0 400 ogg_packet opacket = InitOggPacket(data, length, header == 0, false, 0);
michael@0 401
michael@0 402 r = vorbis_synthesis_headerin(&mVorbisInfo,
michael@0 403 &mVorbisComment,
michael@0 404 &opacket);
michael@0 405 if (r != 0) {
michael@0 406 Cleanup();
michael@0 407 return NS_ERROR_FAILURE;
michael@0 408 }
michael@0 409 }
michael@0 410
michael@0 411 r = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
michael@0 412 if (r != 0) {
michael@0 413 Cleanup();
michael@0 414 return NS_ERROR_FAILURE;
michael@0 415 }
michael@0 416
michael@0 417 r = vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
michael@0 418 if (r != 0) {
michael@0 419 Cleanup();
michael@0 420 return NS_ERROR_FAILURE;
michael@0 421 }
michael@0 422
michael@0 423 mInfo.mAudio.mRate = mVorbisDsp.vi->rate;
michael@0 424 mInfo.mAudio.mChannels = mVorbisDsp.vi->channels;
michael@0 425 mChannels = mInfo.mAudio.mChannels;
michael@0 426 #ifdef MOZ_OPUS
michael@0 427 } else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
michael@0 428 unsigned char* data = 0;
michael@0 429 size_t length = 0;
michael@0 430 r = nestegg_track_codec_data(mContext, track, 0, &data, &length);
michael@0 431 if (r == -1) {
michael@0 432 Cleanup();
michael@0 433 return NS_ERROR_FAILURE;
michael@0 434 }
michael@0 435
michael@0 436 mOpusParser = new OpusParser;
michael@0 437 if (!mOpusParser->DecodeHeader(data, length)) {
michael@0 438 Cleanup();
michael@0 439 return NS_ERROR_FAILURE;
michael@0 440 }
michael@0 441
michael@0 442 if (!InitOpusDecoder()) {
michael@0 443 Cleanup();
michael@0 444 return NS_ERROR_FAILURE;
michael@0 445 }
michael@0 446
michael@0 447 if (static_cast<int64_t>(mCodecDelay) != FramesToUsecs(mOpusParser->mPreSkip, mOpusParser->mRate).value()) {
michael@0 448 LOG(PR_LOG_WARNING,
michael@0 449 ("Invalid Opus header: CodecDelay and pre-skip do not match!\n"));
michael@0 450 Cleanup();
michael@0 451 return NS_ERROR_FAILURE;
michael@0 452 }
michael@0 453
michael@0 454 mInfo.mAudio.mRate = mOpusParser->mRate;
michael@0 455
michael@0 456 mInfo.mAudio.mChannels = mOpusParser->mChannels;
michael@0 457 mChannels = mInfo.mAudio.mChannels;
michael@0 458 mSeekPreroll = params.seek_preroll;
michael@0 459 #endif
michael@0 460 } else {
michael@0 461 Cleanup();
michael@0 462 return NS_ERROR_FAILURE;
michael@0 463 }
michael@0 464 }
michael@0 465 }
michael@0 466
michael@0 467 // We can't seek in buffered regions if we have no cues.
michael@0 468 mDecoder->SetMediaSeekable(nestegg_has_cues(mContext) == 1);
michael@0 469
michael@0 470 *aInfo = mInfo;
michael@0 471
michael@0 472 *aTags = nullptr;
michael@0 473
michael@0 474 return NS_OK;
michael@0 475 }
michael@0 476
michael@0 477 #ifdef MOZ_OPUS
michael@0 478 bool WebMReader::InitOpusDecoder()
michael@0 479 {
michael@0 480 int r;
michael@0 481
michael@0 482 NS_ASSERTION(mOpusDecoder == nullptr, "leaking OpusDecoder");
michael@0 483
michael@0 484 mOpusDecoder = opus_multistream_decoder_create(mOpusParser->mRate,
michael@0 485 mOpusParser->mChannels,
michael@0 486 mOpusParser->mStreams,
michael@0 487 mOpusParser->mCoupledStreams,
michael@0 488 mOpusParser->mMappingTable,
michael@0 489 &r);
michael@0 490 mSkip = mOpusParser->mPreSkip;
michael@0 491
michael@0 492 return r == OPUS_OK;
michael@0 493 }
michael@0 494 #endif
michael@0 495
michael@0 496 ogg_packet WebMReader::InitOggPacket(unsigned char* aData,
michael@0 497 size_t aLength,
michael@0 498 bool aBOS,
michael@0 499 bool aEOS,
michael@0 500 int64_t aGranulepos)
michael@0 501 {
michael@0 502 ogg_packet packet;
michael@0 503 packet.packet = aData;
michael@0 504 packet.bytes = aLength;
michael@0 505 packet.b_o_s = aBOS;
michael@0 506 packet.e_o_s = aEOS;
michael@0 507 packet.granulepos = aGranulepos;
michael@0 508 packet.packetno = mPacketCount++;
michael@0 509 return packet;
michael@0 510 }
michael@0 511
michael@0 512 bool WebMReader::DecodeAudioPacket(nestegg_packet* aPacket, int64_t aOffset)
michael@0 513 {
michael@0 514 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
michael@0 515
michael@0 516 int r = 0;
michael@0 517 unsigned int count = 0;
michael@0 518 r = nestegg_packet_count(aPacket, &count);
michael@0 519 if (r == -1) {
michael@0 520 return false;
michael@0 521 }
michael@0 522
michael@0 523 uint64_t tstamp = 0;
michael@0 524 r = nestegg_packet_tstamp(aPacket, &tstamp);
michael@0 525 if (r == -1) {
michael@0 526 return false;
michael@0 527 }
michael@0 528
michael@0 529 const uint32_t rate = mInfo.mAudio.mRate;
michael@0 530 uint64_t tstamp_usecs = tstamp / NS_PER_USEC;
michael@0 531 if (mAudioStartUsec == -1) {
michael@0 532 // This is the first audio chunk. Assume the start time of our decode
michael@0 533 // is the start of this chunk.
michael@0 534 mAudioStartUsec = tstamp_usecs;
michael@0 535 }
michael@0 536 // If there's a gap between the start of this audio chunk and the end of
michael@0 537 // the previous audio chunk, we need to increment the packet count so that
michael@0 538 // the vorbis decode doesn't use data from before the gap to help decode
michael@0 539 // from after the gap.
michael@0 540 CheckedInt64 tstamp_frames = UsecsToFrames(tstamp_usecs, rate);
michael@0 541 CheckedInt64 decoded_frames = UsecsToFrames(mAudioStartUsec, rate);
michael@0 542 if (!tstamp_frames.isValid() || !decoded_frames.isValid()) {
michael@0 543 NS_WARNING("Int overflow converting WebM times to frames");
michael@0 544 return false;
michael@0 545 }
michael@0 546 decoded_frames += mAudioFrames;
michael@0 547 if (!decoded_frames.isValid()) {
michael@0 548 NS_WARNING("Int overflow adding decoded_frames");
michael@0 549 return false;
michael@0 550 }
michael@0 551 if (tstamp_frames.value() > decoded_frames.value()) {
michael@0 552 #ifdef DEBUG
michael@0 553 CheckedInt64 usecs = FramesToUsecs(tstamp_frames.value() - decoded_frames.value(), rate);
michael@0 554 LOG(PR_LOG_DEBUG, ("WebMReader detected gap of %lld, %lld frames, in audio stream\n",
michael@0 555 usecs.isValid() ? usecs.value() : -1,
michael@0 556 tstamp_frames.value() - decoded_frames.value()));
michael@0 557 #endif
michael@0 558 mPacketCount++;
michael@0 559 mAudioStartUsec = tstamp_usecs;
michael@0 560 mAudioFrames = 0;
michael@0 561 }
michael@0 562
michael@0 563 int32_t total_frames = 0;
michael@0 564 for (uint32_t i = 0; i < count; ++i) {
michael@0 565 unsigned char* data;
michael@0 566 size_t length;
michael@0 567 r = nestegg_packet_data(aPacket, i, &data, &length);
michael@0 568 if (r == -1) {
michael@0 569 return false;
michael@0 570 }
michael@0 571 if (mAudioCodec == NESTEGG_CODEC_VORBIS) {
michael@0 572 ogg_packet opacket = InitOggPacket(data, length, false, false, -1);
michael@0 573
michael@0 574 if (vorbis_synthesis(&mVorbisBlock, &opacket) != 0) {
michael@0 575 return false;
michael@0 576 }
michael@0 577
michael@0 578 if (vorbis_synthesis_blockin(&mVorbisDsp,
michael@0 579 &mVorbisBlock) != 0) {
michael@0 580 return false;
michael@0 581 }
michael@0 582
michael@0 583 VorbisPCMValue** pcm = 0;
michael@0 584 int32_t frames = 0;
michael@0 585 while ((frames = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm)) > 0) {
michael@0 586 nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * mChannels]);
michael@0 587 for (uint32_t j = 0; j < mChannels; ++j) {
michael@0 588 VorbisPCMValue* channel = pcm[j];
michael@0 589 for (uint32_t i = 0; i < uint32_t(frames); ++i) {
michael@0 590 buffer[i*mChannels + j] = MOZ_CONVERT_VORBIS_SAMPLE(channel[i]);
michael@0 591 }
michael@0 592 }
michael@0 593
michael@0 594 CheckedInt64 duration = FramesToUsecs(frames, rate);
michael@0 595 if (!duration.isValid()) {
michael@0 596 NS_WARNING("Int overflow converting WebM audio duration");
michael@0 597 return false;
michael@0 598 }
michael@0 599 CheckedInt64 total_duration = FramesToUsecs(total_frames, rate);
michael@0 600 if (!total_duration.isValid()) {
michael@0 601 NS_WARNING("Int overflow converting WebM audio total_duration");
michael@0 602 return false;
michael@0 603 }
michael@0 604
michael@0 605 CheckedInt64 time = total_duration + tstamp_usecs;
michael@0 606 if (!time.isValid()) {
michael@0 607 NS_WARNING("Int overflow adding total_duration and tstamp_usecs");
michael@0 608 return false;
michael@0 609 };
michael@0 610
michael@0 611 total_frames += frames;
michael@0 612 AudioQueue().Push(new AudioData(aOffset,
michael@0 613 time.value(),
michael@0 614 duration.value(),
michael@0 615 frames,
michael@0 616 buffer.forget(),
michael@0 617 mChannels));
michael@0 618 mAudioFrames += frames;
michael@0 619 if (vorbis_synthesis_read(&mVorbisDsp, frames) != 0) {
michael@0 620 return false;
michael@0 621 }
michael@0 622 }
michael@0 623 } else if (mAudioCodec == NESTEGG_CODEC_OPUS) {
michael@0 624 #ifdef MOZ_OPUS
michael@0 625 uint32_t channels = mOpusParser->mChannels;
michael@0 626
michael@0 627 // Maximum value is 63*2880, so there's no chance of overflow.
michael@0 628 int32_t frames_number = opus_packet_get_nb_frames(data, length);
michael@0 629
michael@0 630 if (frames_number <= 0)
michael@0 631 return false; // Invalid packet header.
michael@0 632 int32_t samples = opus_packet_get_samples_per_frame(data,
michael@0 633 (opus_int32) rate);
michael@0 634 int32_t frames = frames_number*samples;
michael@0 635
michael@0 636 // A valid Opus packet must be between 2.5 and 120 ms long.
michael@0 637 if (frames < 120 || frames > 5760)
michael@0 638 return false;
michael@0 639 nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frames * channels]);
michael@0 640
michael@0 641 // Decode to the appropriate sample type.
michael@0 642 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
michael@0 643 int ret = opus_multistream_decode_float(mOpusDecoder,
michael@0 644 data, length,
michael@0 645 buffer, frames, false);
michael@0 646 #else
michael@0 647 int ret = opus_multistream_decode(mOpusDecoder,
michael@0 648 data, length,
michael@0 649 buffer, frames, false);
michael@0 650 #endif
michael@0 651 if (ret < 0)
michael@0 652 return false;
michael@0 653 NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
michael@0 654 CheckedInt64 startTime = tstamp_usecs;
michael@0 655
michael@0 656 // Trim the initial frames while the decoder is settling.
michael@0 657 if (mSkip > 0) {
michael@0 658 int32_t skipFrames = std::min(mSkip, frames);
michael@0 659 if (skipFrames == frames) {
michael@0 660 // discard the whole packet
michael@0 661 mSkip -= frames;
michael@0 662 LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames"
michael@0 663 " (whole packet)", frames));
michael@0 664 return true;
michael@0 665 }
michael@0 666 int32_t keepFrames = frames - skipFrames;
michael@0 667 if (keepFrames < 0) {
michael@0 668 NS_WARNING("Int overflow in keepFrames");
michael@0 669 return false;
michael@0 670 }
michael@0 671 int samples = keepFrames * channels;
michael@0 672 if (samples < 0) {
michael@0 673 NS_WARNING("Int overflow in samples");
michael@0 674 return false;
michael@0 675 }
michael@0 676 nsAutoArrayPtr<AudioDataValue> trimBuffer(new AudioDataValue[samples]);
michael@0 677 for (int i = 0; i < samples; i++)
michael@0 678 trimBuffer[i] = buffer[skipFrames*channels + i];
michael@0 679 startTime = startTime + FramesToUsecs(skipFrames, rate);
michael@0 680 frames = keepFrames;
michael@0 681 buffer = trimBuffer;
michael@0 682
michael@0 683 mSkip -= skipFrames;
michael@0 684 LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames", skipFrames));
michael@0 685 }
michael@0 686
michael@0 687 int64_t discardPadding = 0;
michael@0 688 r = nestegg_packet_discard_padding(aPacket, &discardPadding);
michael@0 689 if (discardPadding > 0) {
michael@0 690 CheckedInt64 discardFrames = UsecsToFrames(discardPadding * NS_PER_USEC, rate);
michael@0 691 if (!discardFrames.isValid()) {
michael@0 692 NS_WARNING("Int overflow in DiscardPadding");
michael@0 693 return false;
michael@0 694 }
michael@0 695 int32_t keepFrames = frames - discardFrames.value();
michael@0 696 if (keepFrames > 0) {
michael@0 697 int samples = keepFrames * channels;
michael@0 698 if (samples < 0) {
michael@0 699 NS_WARNING("Int overflow in samples");
michael@0 700 return false;
michael@0 701 }
michael@0 702 nsAutoArrayPtr<AudioDataValue> trimBuffer(new AudioDataValue[samples]);
michael@0 703 for (int i = 0; i < samples; i++)
michael@0 704 trimBuffer[i] = buffer[i];
michael@0 705 frames = keepFrames;
michael@0 706 buffer = trimBuffer;
michael@0 707 } else {
michael@0 708 LOG(PR_LOG_DEBUG, ("Opus decoder discarding whole packet"
michael@0 709 " ( %d frames) as padding", frames));
michael@0 710 return true;
michael@0 711 }
michael@0 712 }
michael@0 713
michael@0 714 // Apply the header gain if one was specified.
michael@0 715 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
michael@0 716 if (mOpusParser->mGain != 1.0f) {
michael@0 717 float gain = mOpusParser->mGain;
michael@0 718 int samples = frames * channels;
michael@0 719 for (int i = 0; i < samples; i++) {
michael@0 720 buffer[i] *= gain;
michael@0 721 }
michael@0 722 }
michael@0 723 #else
michael@0 724 if (mOpusParser->mGain_Q16 != 65536) {
michael@0 725 int64_t gain_Q16 = mOpusParser->mGain_Q16;
michael@0 726 int samples = frames * channels;
michael@0 727 for (int i = 0; i < samples; i++) {
michael@0 728 int32_t val = static_cast<int32_t>((gain_Q16*buffer[i] + 32768)>>16);
michael@0 729 buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
michael@0 730 }
michael@0 731 }
michael@0 732 #endif
michael@0 733
michael@0 734 // No channel mapping for more than 8 channels.
michael@0 735 if (channels > 8) {
michael@0 736 return false;
michael@0 737 }
michael@0 738
michael@0 739 CheckedInt64 duration = FramesToUsecs(frames, rate);
michael@0 740 if (!duration.isValid()) {
michael@0 741 NS_WARNING("Int overflow converting WebM audio duration");
michael@0 742 return false;
michael@0 743 }
michael@0 744 CheckedInt64 time = startTime - mCodecDelay;
michael@0 745 if (!time.isValid()) {
michael@0 746 NS_WARNING("Int overflow shifting tstamp by codec delay");
michael@0 747 return false;
michael@0 748 };
michael@0 749 AudioQueue().Push(new AudioData(mDecoder->GetResource()->Tell(),
michael@0 750 time.value(),
michael@0 751 duration.value(),
michael@0 752 frames,
michael@0 753 buffer.forget(),
michael@0 754 mChannels));
michael@0 755
michael@0 756 mAudioFrames += frames;
michael@0 757 #else
michael@0 758 return false;
michael@0 759 #endif /* MOZ_OPUS */
michael@0 760 }
michael@0 761 }
michael@0 762
michael@0 763 return true;
michael@0 764 }
michael@0 765
michael@0 766 nsReturnRef<NesteggPacketHolder> WebMReader::NextPacket(TrackType aTrackType)
michael@0 767 {
michael@0 768 // The packet queue that packets will be pushed on if they
michael@0 769 // are not the type we are interested in.
michael@0 770 WebMPacketQueue& otherPackets =
michael@0 771 aTrackType == VIDEO ? mAudioPackets : mVideoPackets;
michael@0 772
michael@0 773 // The packet queue for the type that we are interested in.
michael@0 774 WebMPacketQueue &packets =
michael@0 775 aTrackType == VIDEO ? mVideoPackets : mAudioPackets;
michael@0 776
michael@0 777 // Flag to indicate that we do need to playback these types of
michael@0 778 // packets.
michael@0 779 bool hasType = aTrackType == VIDEO ? mHasVideo : mHasAudio;
michael@0 780
michael@0 781 // Flag to indicate that we do need to playback the other type
michael@0 782 // of track.
michael@0 783 bool hasOtherType = aTrackType == VIDEO ? mHasAudio : mHasVideo;
michael@0 784
michael@0 785 // Track we are interested in
michael@0 786 uint32_t ourTrack = aTrackType == VIDEO ? mVideoTrack : mAudioTrack;
michael@0 787
michael@0 788 // Value of other track
michael@0 789 uint32_t otherTrack = aTrackType == VIDEO ? mAudioTrack : mVideoTrack;
michael@0 790
michael@0 791 nsAutoRef<NesteggPacketHolder> holder;
michael@0 792
michael@0 793 if (packets.GetSize() > 0) {
michael@0 794 holder.own(packets.PopFront());
michael@0 795 } else {
michael@0 796 // Keep reading packets until we find a packet
michael@0 797 // for the track we want.
michael@0 798 do {
michael@0 799 nestegg_packet* packet;
michael@0 800 int r = nestegg_read_packet(mContext, &packet);
michael@0 801 if (r <= 0) {
michael@0 802 return nsReturnRef<NesteggPacketHolder>();
michael@0 803 }
michael@0 804 int64_t offset = mDecoder->GetResource()->Tell();
michael@0 805 holder.own(new NesteggPacketHolder(packet, offset));
michael@0 806
michael@0 807 unsigned int track = 0;
michael@0 808 r = nestegg_packet_track(packet, &track);
michael@0 809 if (r == -1) {
michael@0 810 return nsReturnRef<NesteggPacketHolder>();
michael@0 811 }
michael@0 812
michael@0 813 if (hasOtherType && otherTrack == track) {
michael@0 814 // Save the packet for when we want these packets
michael@0 815 otherPackets.Push(holder.disown());
michael@0 816 continue;
michael@0 817 }
michael@0 818
michael@0 819 // The packet is for the track we want to play
michael@0 820 if (hasType && ourTrack == track) {
michael@0 821 break;
michael@0 822 }
michael@0 823 } while (true);
michael@0 824 }
michael@0 825
michael@0 826 return holder.out();
michael@0 827 }
michael@0 828
michael@0 829 bool WebMReader::DecodeAudioData()
michael@0 830 {
michael@0 831 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
michael@0 832
michael@0 833 nsAutoRef<NesteggPacketHolder> holder(NextPacket(AUDIO));
michael@0 834 if (!holder) {
michael@0 835 return false;
michael@0 836 }
michael@0 837
michael@0 838 return DecodeAudioPacket(holder->mPacket, holder->mOffset);
michael@0 839 }
michael@0 840
michael@0 841 bool WebMReader::DecodeVideoFrame(bool &aKeyframeSkip,
michael@0 842 int64_t aTimeThreshold)
michael@0 843 {
michael@0 844 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
michael@0 845
michael@0 846 // Record number of frames decoded and parsed. Automatically update the
michael@0 847 // stats counters using the AutoNotifyDecoded stack-based class.
michael@0 848 uint32_t parsed = 0, decoded = 0;
michael@0 849 AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
michael@0 850
michael@0 851 nsAutoRef<NesteggPacketHolder> holder(NextPacket(VIDEO));
michael@0 852 if (!holder) {
michael@0 853 return false;
michael@0 854 }
michael@0 855
michael@0 856 nestegg_packet* packet = holder->mPacket;
michael@0 857 unsigned int track = 0;
michael@0 858 int r = nestegg_packet_track(packet, &track);
michael@0 859 if (r == -1) {
michael@0 860 return false;
michael@0 861 }
michael@0 862
michael@0 863 unsigned int count = 0;
michael@0 864 r = nestegg_packet_count(packet, &count);
michael@0 865 if (r == -1) {
michael@0 866 return false;
michael@0 867 }
michael@0 868
michael@0 869 uint64_t tstamp = 0;
michael@0 870 r = nestegg_packet_tstamp(packet, &tstamp);
michael@0 871 if (r == -1) {
michael@0 872 return false;
michael@0 873 }
michael@0 874
michael@0 875 // The end time of this frame is the start time of the next frame. Fetch
michael@0 876 // the timestamp of the next packet for this track. If we've reached the
michael@0 877 // end of the resource, use the file's duration as the end time of this
michael@0 878 // video frame.
michael@0 879 uint64_t next_tstamp = 0;
michael@0 880 nsAutoRef<NesteggPacketHolder> next_holder(NextPacket(VIDEO));
michael@0 881 if (next_holder) {
michael@0 882 r = nestegg_packet_tstamp(next_holder->mPacket, &next_tstamp);
michael@0 883 if (r == -1) {
michael@0 884 return false;
michael@0 885 }
michael@0 886 PushVideoPacket(next_holder.disown());
michael@0 887 } else {
michael@0 888 ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
michael@0 889 int64_t endTime = mDecoder->GetEndMediaTime();
michael@0 890 if (endTime == -1) {
michael@0 891 return false;
michael@0 892 }
michael@0 893 next_tstamp = endTime * NS_PER_USEC;
michael@0 894 }
michael@0 895
michael@0 896 int64_t tstamp_usecs = tstamp / NS_PER_USEC;
michael@0 897 for (uint32_t i = 0; i < count; ++i) {
michael@0 898 unsigned char* data;
michael@0 899 size_t length;
michael@0 900 r = nestegg_packet_data(packet, i, &data, &length);
michael@0 901 if (r == -1) {
michael@0 902 return false;
michael@0 903 }
michael@0 904
michael@0 905 vpx_codec_stream_info_t si;
michael@0 906 memset(&si, 0, sizeof(si));
michael@0 907 si.sz = sizeof(si);
michael@0 908 if (mVideoCodec == NESTEGG_CODEC_VP8) {
michael@0 909 vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);
michael@0 910 } else if (mVideoCodec == NESTEGG_CODEC_VP9) {
michael@0 911 vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), data, length, &si);
michael@0 912 }
michael@0 913 if (aKeyframeSkip && (!si.is_kf || tstamp_usecs < aTimeThreshold)) {
michael@0 914 // Skipping to next keyframe...
michael@0 915 parsed++; // Assume 1 frame per chunk.
michael@0 916 continue;
michael@0 917 }
michael@0 918
michael@0 919 if (aKeyframeSkip && si.is_kf) {
michael@0 920 aKeyframeSkip = false;
michael@0 921 }
michael@0 922
michael@0 923 if (vpx_codec_decode(&mVPX, data, length, nullptr, 0)) {
michael@0 924 return false;
michael@0 925 }
michael@0 926
michael@0 927 // If the timestamp of the video frame is less than
michael@0 928 // the time threshold required then it is not added
michael@0 929 // to the video queue and won't be displayed.
michael@0 930 if (tstamp_usecs < aTimeThreshold) {
michael@0 931 parsed++; // Assume 1 frame per chunk.
michael@0 932 continue;
michael@0 933 }
michael@0 934
michael@0 935 vpx_codec_iter_t iter = nullptr;
michael@0 936 vpx_image_t *img;
michael@0 937
michael@0 938 while ((img = vpx_codec_get_frame(&mVPX, &iter))) {
michael@0 939 NS_ASSERTION(img->fmt == IMG_FMT_I420, "WebM image format is not I420");
michael@0 940
michael@0 941 // Chroma shifts are rounded down as per the decoding examples in the VP8 SDK
michael@0 942 VideoData::YCbCrBuffer b;
michael@0 943 b.mPlanes[0].mData = img->planes[0];
michael@0 944 b.mPlanes[0].mStride = img->stride[0];
michael@0 945 b.mPlanes[0].mHeight = img->d_h;
michael@0 946 b.mPlanes[0].mWidth = img->d_w;
michael@0 947 b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
michael@0 948
michael@0 949 b.mPlanes[1].mData = img->planes[1];
michael@0 950 b.mPlanes[1].mStride = img->stride[1];
michael@0 951 b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
michael@0 952 b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
michael@0 953 b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
michael@0 954
michael@0 955 b.mPlanes[2].mData = img->planes[2];
michael@0 956 b.mPlanes[2].mStride = img->stride[2];
michael@0 957 b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
michael@0 958 b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
michael@0 959 b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
michael@0 960
michael@0 961 IntRect picture = ToIntRect(mPicture);
michael@0 962 if (img->d_w != static_cast<uint32_t>(mInitialFrame.width) ||
michael@0 963 img->d_h != static_cast<uint32_t>(mInitialFrame.height)) {
michael@0 964 // Frame size is different from what the container reports. This is legal
michael@0 965 // in WebM, and we will preserve the ratio of the crop rectangle as it
michael@0 966 // was reported relative to the picture size reported by the container.
michael@0 967 picture.x = (mPicture.x * img->d_w) / mInitialFrame.width;
michael@0 968 picture.y = (mPicture.y * img->d_h) / mInitialFrame.height;
michael@0 969 picture.width = (img->d_w * mPicture.width) / mInitialFrame.width;
michael@0 970 picture.height = (img->d_h * mPicture.height) / mInitialFrame.height;
michael@0 971 }
michael@0 972
michael@0 973 VideoData *v = VideoData::Create(mInfo.mVideo,
michael@0 974 mDecoder->GetImageContainer(),
michael@0 975 holder->mOffset,
michael@0 976 tstamp_usecs,
michael@0 977 (next_tstamp / NS_PER_USEC) - tstamp_usecs,
michael@0 978 b,
michael@0 979 si.is_kf,
michael@0 980 -1,
michael@0 981 picture);
michael@0 982 if (!v) {
michael@0 983 return false;
michael@0 984 }
michael@0 985 parsed++;
michael@0 986 decoded++;
michael@0 987 NS_ASSERTION(decoded <= parsed,
michael@0 988 "Expect only 1 frame per chunk per packet in WebM...");
michael@0 989 VideoQueue().Push(v);
michael@0 990 }
michael@0 991 }
michael@0 992
michael@0 993 return true;
michael@0 994 }
michael@0 995
michael@0 996 void
michael@0 997 WebMReader::PushVideoPacket(NesteggPacketHolder* aItem)
michael@0 998 {
michael@0 999 mVideoPackets.PushFront(aItem);
michael@0 1000 }
michael@0 1001
michael@0 1002 nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
michael@0 1003 int64_t aCurrentTime)
michael@0 1004 {
michael@0 1005 NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
michael@0 1006
michael@0 1007 LOG(PR_LOG_DEBUG, ("Reader [%p] for Decoder [%p]: About to seek to %fs",
michael@0 1008 this, mDecoder, aTarget/1000000.0));
michael@0 1009 if (NS_FAILED(ResetDecode())) {
michael@0 1010 return NS_ERROR_FAILURE;
michael@0 1011 }
michael@0 1012 uint32_t trackToSeek = mHasVideo ? mVideoTrack : mAudioTrack;
michael@0 1013 uint64_t target = aTarget * NS_PER_USEC;
michael@0 1014 if (mSeekPreroll) {
michael@0 1015 target = std::max(static_cast<uint64_t>(aStartTime * NS_PER_USEC), target - mSeekPreroll);
michael@0 1016 }
michael@0 1017 int r = nestegg_track_seek(mContext, trackToSeek, target);
michael@0 1018 if (r != 0) {
michael@0 1019 // Try seeking directly based on cluster information in memory.
michael@0 1020 int64_t offset = 0;
michael@0 1021 bool rv = mBufferedState->GetOffsetForTime((aTarget - aStartTime)/NS_PER_USEC, &offset);
michael@0 1022 if (!rv) {
michael@0 1023 return NS_ERROR_FAILURE;
michael@0 1024 }
michael@0 1025
michael@0 1026 r = nestegg_offset_seek(mContext, offset);
michael@0 1027 if (r != 0) {
michael@0 1028 return NS_ERROR_FAILURE;
michael@0 1029 }
michael@0 1030 }
michael@0 1031 return NS_OK;
michael@0 1032 }
michael@0 1033
michael@0 1034 nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
michael@0 1035 {
michael@0 1036 MediaResource* resource = mDecoder->GetResource();
michael@0 1037
michael@0 1038 uint64_t timecodeScale;
michael@0 1039 if (!mContext || nestegg_tstamp_scale(mContext, &timecodeScale) == -1) {
michael@0 1040 return NS_OK;
michael@0 1041 }
michael@0 1042
michael@0 1043 // Special case completely cached files. This also handles local files.
michael@0 1044 bool isFullyCached = resource->IsDataCachedToEndOfResource(0);
michael@0 1045 if (isFullyCached) {
michael@0 1046 uint64_t duration = 0;
michael@0 1047 if (nestegg_duration(mContext, &duration) == 0) {
michael@0 1048 aBuffered->Add(0, duration / NS_PER_S);
michael@0 1049 }
michael@0 1050 }
michael@0 1051
michael@0 1052 uint32_t bufferedLength = 0;
michael@0 1053 aBuffered->GetLength(&bufferedLength);
michael@0 1054
michael@0 1055 // Either we the file is not fully cached, or we couldn't find a duration in
michael@0 1056 // the WebM bitstream.
michael@0 1057 if (!isFullyCached || !bufferedLength) {
michael@0 1058 MediaResource* resource = mDecoder->GetResource();
michael@0 1059 nsTArray<MediaByteRange> ranges;
michael@0 1060 nsresult res = resource->GetCachedRanges(ranges);
michael@0 1061 NS_ENSURE_SUCCESS(res, res);
michael@0 1062
michael@0 1063 for (uint32_t index = 0; index < ranges.Length(); index++) {
michael@0 1064 uint64_t start, end;
michael@0 1065 bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart,
michael@0 1066 ranges[index].mEnd,
michael@0 1067 &start, &end);
michael@0 1068 if (rv) {
michael@0 1069 double startTime = start * timecodeScale / NS_PER_S - aStartTime;
michael@0 1070 double endTime = end * timecodeScale / NS_PER_S - aStartTime;
michael@0 1071 // If this range extends to the end of the file, the true end time
michael@0 1072 // is the file's duration.
michael@0 1073 if (resource->IsDataCachedToEndOfResource(ranges[index].mStart)) {
michael@0 1074 uint64_t duration = 0;
michael@0 1075 if (nestegg_duration(mContext, &duration) == 0) {
michael@0 1076 endTime = duration / NS_PER_S;
michael@0 1077 }
michael@0 1078 }
michael@0 1079
michael@0 1080 aBuffered->Add(startTime, endTime);
michael@0 1081 }
michael@0 1082 }
michael@0 1083 }
michael@0 1084
michael@0 1085 return NS_OK;
michael@0 1086 }
michael@0 1087
michael@0 1088 void WebMReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
michael@0 1089 {
michael@0 1090 mBufferedState->NotifyDataArrived(aBuffer, aLength, aOffset);
michael@0 1091 }
michael@0 1092
michael@0 1093 } // namespace mozilla

mercurial