nsresult SkeletonState::GetDuration(const nsTArray<uint32_t>& aTracks, int64_t& aDuration) { if (!mActive || mVersion < SKELETON_VERSION(4,0) || !HasIndex() || aTracks.Length() == 0) { return NS_ERROR_FAILURE; } int64_t endTime = INT64_MIN; int64_t startTime = INT64_MAX; for (uint32_t i=0; i<aTracks.Length(); i++) { nsKeyFrameIndex* index = nullptr; mIndex.Get(aTracks[i], &index); if (!index) { // Can't get the timestamps for one of the required tracks, fail. return NS_ERROR_FAILURE; } if (index->mEndTime > endTime) { endTime = index->mEndTime; } if (index->mStartTime < startTime) { startTime = index->mStartTime; } } NS_ASSERTION(endTime > startTime, "Duration must be positive"); CheckedInt64 duration = CheckedInt64(endTime) - startTime; aDuration = duration.isValid() ? duration.value() : 0; return duration.isValid() ? NS_OK : NS_ERROR_FAILURE; }
int64_t OpusState::Time(int aPreSkip, int64_t aGranulepos) { if (aGranulepos < 0) return -1; // Ogg Opus always runs at a granule rate of 48 kHz. CheckedInt64 t = CheckedInt64(aGranulepos - aPreSkip) * USECS_PER_S; return t.isValid() ? t.value() / 48000 : -1; }
int64_t TheoraState::StartTime(int64_t granulepos) { if (granulepos < 0 || !mActive || mInfo.fps_numerator == 0) { return -1; } CheckedInt64 t = (CheckedInt64(th_granule_frame(mCtx, granulepos)) * USECS_PER_S) * mInfo.fps_denominator; if (!t.isValid()) return -1; return t.value() / mInfo.fps_numerator; }
int64_t VorbisState::Time(vorbis_info* aInfo, int64_t aGranulepos) { if (aGranulepos == -1 || aInfo->rate == 0) { return -1; } CheckedInt64 t = CheckedInt64(aGranulepos) * USECS_PER_S; if (!t.isValid()) t = 0; return t.value() / aInfo->rate; }
int64_t TheoraState::Time(th_info* aInfo, int64_t aGranulepos) { if (aGranulepos < 0 || aInfo->fps_numerator == 0) { return -1; } // Implementation of th_granule_frame inlined here to operate // on the th_info structure instead of the theora_state. int shift = aInfo->keyframe_granule_shift; ogg_int64_t iframe = aGranulepos >> shift; ogg_int64_t pframe = aGranulepos - (iframe << shift); int64_t frameno = iframe + pframe - TH_VERSION_CHECK(aInfo, 3, 2, 1); CheckedInt64 t = ((CheckedInt64(frameno) + 1) * USECS_PER_S) * aInfo->fps_denominator; if (!t.isValid()) return -1; t /= aInfo->fps_numerator; return t.isValid() ? t.value() : -1; }
PRInt64 nsTheoraState::MaxKeyframeOffset() { // Determine the maximum time in microseconds by which a key frame could // offset for the theora bitstream. Theora granulepos encode time as: // ((key_frame_number << granule_shift) + frame_offset). // Therefore the maximum possible time by which any frame could be offset // from a keyframe is the duration of (1 << granule_shift) - 1) frames. PRInt64 frameDuration; // Max number of frames keyframe could possibly be offset. PRInt64 keyframeDiff = (1 << mInfo.keyframe_granule_shift) - 1; // Length of frame in usecs. CheckedInt64 d = CheckedInt64(mInfo.fps_denominator) * USECS_PER_S; if (!d.valid()) d = 0; frameDuration = d.value() / mInfo.fps_numerator; // Total time in usecs keyframe can be offset from any given frame. return frameDuration * keyframeDiff; }
// Converts from microseconds to number of audio frames, given the specified // audio rate. CheckedInt64 UsecsToFrames(PRInt64 aUsecs, PRUint32 aRate) { return (CheckedInt64(aUsecs) * aRate) / USECS_PER_S; }
// Converts from number of audio frames to microseconds, given the specified // audio rate. CheckedInt64 FramesToUsecs(PRInt64 aFrames, PRUint32 aRate) { return (CheckedInt64(aFrames) * USECS_PER_S) / aRate; }
bool WaveReader::LoadAllChunks(nsAutoPtr<dom::HTMLMediaElement::MetadataTags> &aTags) { // Chunks are always word (two byte) aligned. MOZ_ASSERT(mDecoder->GetResource()->Tell() % 2 == 0, "LoadAllChunks called with unaligned resource"); bool loadFormatChunk = false; bool findDataOffset = false; for (;;) { static const unsigned int CHUNK_HEADER_SIZE = 8; char chunkHeader[CHUNK_HEADER_SIZE]; const char* p = chunkHeader; if (!ReadAll(chunkHeader, sizeof(chunkHeader))) { return false; } static_assert(sizeof(uint32_t) * 2 <= CHUNK_HEADER_SIZE, "Reads would overflow chunkHeader buffer."); uint32_t magic = ReadUint32BE(&p); uint32_t chunkSize = ReadUint32LE(&p); int64_t chunkStart = GetPosition(); switch (magic) { case FRMT_CHUNK_MAGIC: loadFormatChunk = LoadFormatChunk(chunkSize); if (!loadFormatChunk) { return false; } break; case LIST_CHUNK_MAGIC: if (!aTags) { LoadListChunk(chunkSize, aTags); } break; case DATA_CHUNK_MAGIC: findDataOffset = FindDataOffset(chunkSize); return loadFormatChunk && findDataOffset; default: break; } // RIFF chunks are two-byte aligned, so round up if necessary. chunkSize += chunkSize % 2; // Move forward to next chunk CheckedInt64 forward = CheckedInt64(chunkStart) + chunkSize - GetPosition(); if (!forward.isValid() || forward.value() < 0) { return false; } static const int64_t MAX_CHUNK_SIZE = 1 << 16; static_assert(uint64_t(MAX_CHUNK_SIZE) < UINT_MAX / sizeof(char), "MAX_CHUNK_SIZE too large for enumerator."); nsAutoArrayPtr<char> chunk(new char[MAX_CHUNK_SIZE]); while (forward.value() > 0) { int64_t size = std::min(forward.value(), MAX_CHUNK_SIZE); if (!ReadAll(chunk.get(), size)) { return false; } forward -= size; } } return false; }
// Format TimeUnit as number of frames at given rate. CheckedInt64 TimeUnitToFrames(const TimeUnit& aTime, uint32_t aRate) { return aTime.IsValid() ? UsecsToFrames(aTime.ToMicroseconds(), aRate) : CheckedInt64(INT64_MAX) + 1; }
// Converts from microseconds to number of audio frames, given the specified // audio rate. CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) { return (CheckedInt64(aUsecs) * aRate) / USECS_PER_S; }
// Converts from number of audio frames to microseconds, given the specified // audio rate. CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) { return (CheckedInt64(aFrames) * USECS_PER_S) / aRate; }
bool SkeletonState::DecodeIndex(ogg_packet* aPacket) { NS_ASSERTION(aPacket->bytes >= SKELETON_4_0_MIN_INDEX_LEN, "Index must be at least minimum size"); if (!mActive) { return false; } uint32_t serialno = LittleEndian::readUint32(aPacket->packet + INDEX_SERIALNO_OFFSET); int64_t numKeyPoints = LittleEndian::readInt64(aPacket->packet + INDEX_NUM_KEYPOINTS_OFFSET); int64_t endTime = 0, startTime = 0; const unsigned char* p = aPacket->packet; int64_t timeDenom = LittleEndian::readInt64(aPacket->packet + INDEX_TIME_DENOM_OFFSET); if (timeDenom == 0) { LOG(PR_LOG_DEBUG, ("Ogg Skeleton Index packet for stream %u has 0 " "timestamp denominator.", serialno)); return (mActive = false); } // Extract the start time. CheckedInt64 t = CheckedInt64(LittleEndian::readInt64(p + INDEX_FIRST_NUMER_OFFSET)) * USECS_PER_S; if (!t.isValid()) { return (mActive = false); } else { startTime = t.value() / timeDenom; } // Extract the end time. t = LittleEndian::readInt64(p + INDEX_LAST_NUMER_OFFSET) * USECS_PER_S; if (!t.isValid()) { return (mActive = false); } else { endTime = t.value() / timeDenom; } // Check the numKeyPoints value read, ensure we're not going to run out of // memory while trying to decode the index packet. CheckedInt64 minPacketSize = (CheckedInt64(numKeyPoints) * MIN_KEY_POINT_SIZE) + INDEX_KEYPOINT_OFFSET; if (!minPacketSize.isValid()) { return (mActive = false); } int64_t sizeofIndex = aPacket->bytes - INDEX_KEYPOINT_OFFSET; int64_t maxNumKeyPoints = sizeofIndex / MIN_KEY_POINT_SIZE; if (aPacket->bytes < minPacketSize.value() || numKeyPoints > maxNumKeyPoints || numKeyPoints < 0) { // Packet size is less than the theoretical minimum size, or the packet is // claiming to store more keypoints than it's capable of storing. This means // that the numKeyPoints field is too large or small for the packet to // possibly contain as many packets as it claims to, so the numKeyPoints // field is possibly malicious. Don't try decoding this index, we may run // out of memory. LOG(PR_LOG_DEBUG, ("Possibly malicious number of key points reported " "(%lld) in index packet for stream %u.", numKeyPoints, serialno)); return (mActive = false); } nsAutoPtr<nsKeyFrameIndex> keyPoints(new nsKeyFrameIndex(startTime, endTime)); p = aPacket->packet + INDEX_KEYPOINT_OFFSET; const unsigned char* limit = aPacket->packet + aPacket->bytes; int64_t numKeyPointsRead = 0; CheckedInt64 offset = 0; CheckedInt64 time = 0; while (p < limit && numKeyPointsRead < numKeyPoints) { int64_t delta = 0; p = ReadVariableLengthInt(p, limit, delta); offset += delta; if (p == limit || !offset.isValid() || offset.value() > mLength || offset.value() < 0) { return (mActive = false); } p = ReadVariableLengthInt(p, limit, delta); time += delta; if (!time.isValid() || time.value() > endTime || time.value() < startTime) { return (mActive = false); } CheckedInt64 timeUsecs = time * USECS_PER_S; if (!timeUsecs.isValid()) return mActive = false; timeUsecs /= timeDenom; keyPoints->Add(offset.value(), timeUsecs.value()); numKeyPointsRead++; } int32_t keyPointsRead = keyPoints->Length(); if (keyPointsRead > 0) { mIndex.Put(serialno, keyPoints.forget()); } LOG(PR_LOG_DEBUG, ("Loaded %d keypoints for Skeleton on stream %u", keyPointsRead, serialno)); return true; }