void SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("SourceBuffer(%p)::Remove(aStart=%f, aEnd=%f)", this, aStart, aEnd); if (!IsAttached()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (IsNaN(mMediaSource->Duration()) || aStart < 0 || aStart > mMediaSource->Duration() || aEnd <= aStart || IsNaN(aEnd)) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } if (mUpdating || mMediaSource->ReadyState() != MediaSourceReadyState::Open) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } StartUpdating(); /// TODO: Run coded frame removal algorithm. // Run the final step of the coded frame removal algorithm asynchronously // to ensure the SourceBuffer's updating flag transition behaves as // required by the spec. nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating); NS_DispatchToMainThread(event); }
void SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("Remove(aStart=%f, aEnd=%f)", aStart, aEnd); if (!IsAttached()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (mUpdating) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (IsNaN(mMediaSource->Duration()) || aStart < 0 || aStart > mMediaSource->Duration() || aEnd <= aStart || IsNaN(aEnd)) { aRv.Throw(NS_ERROR_DOM_TYPE_ERR); return; } if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) { mMediaSource->SetReadyState(MediaSourceReadyState::Open); } RangeRemoval(aStart, aEnd); }
void SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("Remove(aStart=%f, aEnd=%f)", aStart, aEnd); if (!IsAttached()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (mUpdating) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (IsNaN(mMediaSource->Duration()) || aStart < 0 || aStart > mMediaSource->Duration() || aEnd <= aStart || IsNaN(aEnd)) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) { mMediaSource->SetReadyState(MediaSourceReadyState::Open); } StartUpdating(); nsCOMPtr<nsIRunnable> task = new RangeRemovalRunnable(this, aStart, aEnd); NS_DispatchToMainThread(task); }
media::TimeIntervals MediaSourceDecoder::GetSeekable() { MOZ_ASSERT(NS_IsMainThread()); if (!mMediaSource) { NS_WARNING("MediaSource element isn't attached"); return media::TimeIntervals::Invalid(); } media::TimeIntervals seekable; double duration = mMediaSource->Duration(); if (IsNaN(duration)) { // Return empty range. } else if (duration > 0 && mozilla::IsInfinite(duration)) { media::TimeIntervals buffered = GetBuffered(); if (buffered.Length()) { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), buffered.GetEnd()); } } else { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), media::TimeUnit::FromSeconds(duration)); } MSE_DEBUG("ranges=%s", DumpTimeRanges(seekable).get()); return seekable; }
bool MediaSourceDecoder::CanPlayThrough() { MOZ_ASSERT(NS_IsMainThread()); if (NextFrameBufferedStatus() == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE) { return false; } if (IsNaN(mMediaSource->Duration())) { // Don't have any data yet. return false; } TimeUnit duration = TimeUnit::FromSeconds(mMediaSource->Duration()); TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition()); if (duration.IsInfinite()) { // We can't make an informed decision and just assume that it's a live stream return true; } else if (duration <= currentPosition) { return true; } // If we have data up to the mediasource's duration or 30s ahead, we can // assume that we can play without interruption. TimeUnit timeAhead = std::min(duration, currentPosition + TimeUnit::FromSeconds(30)); TimeInterval interval(currentPosition, timeAhead, MediaSourceDemuxer::EOS_FUZZ); return GetBuffered().Contains(ClampIntervalToEnd(interval)); }
static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate) { // Normalize by RMS power size_t numberOfChannels = response->GetChannels(); float power = 0; for (size_t i = 0; i < numberOfChannels; ++i) { float channelPower = AudioBufferSumOfSquares(static_cast<const float*>(response->GetData(i)), aLength); power += channelPower; } power = sqrt(power / (numberOfChannels * aLength)); // Protect against accidental overload if (!IsFinite(power) || IsNaN(power) || power < MinPower) power = MinPower; float scale = 1 / power; scale *= GainCalibration; // calibrate to make perceived volume same as unprocessed // Scale depends on sample-rate. if (sampleRate) scale *= GainCalibrationSampleRate / sampleRate; // True-stereo compensation if (response->GetChannels() == 4) scale *= 0.5f; return scale; }
// For NaNs, etc. static bool IsCacheCorrect(float cached, float actual) { if (IsNaN(cached)) { // GL is allowed to do anything it wants for NaNs, so if we're shadowing // a NaN, then whatever `actual` is might be correct. return true; } return cached == actual; }
void CheckNaN( const Matrix44& matrix ) { for (int i = 0; i < 16; ++i) { if (IsNaN( matrix.m[ i ] )) { //std::cerr << "Matrix contains NaN" << std::endl; } } }
void SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("SourceBuffer(%p)::Remove(aStart=%f, aEnd=%f)", this, aStart, aEnd); if (IsNaN(mMediaSource->Duration()) || aStart < 0 || aStart > mMediaSource->Duration() || aEnd <= aStart || IsNaN(aEnd)) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } if (!IsAttached() || mUpdating || mMediaSource->ReadyState() != MediaSourceReadyState::Open) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } StartUpdating(); /// TODO: Run coded frame removal algorithm asynchronously (would call StopUpdating()). StopUpdating(); }
/* Test given float values */ void TestDouble(double a, double b) { DOUB_LONG param1, param2, result, check; param1.d = a; param2.d = b; check.d = param1.d + param2.d; result.l = DoubleAdd (param1.l, param2.l); #if ALWAYS_PRINT printf("\n"); Display(param1, param2, result); #endif if (result.l != check.l && !(IsNaN(result.l) && IsNaN(check.l)) && !(IsZero(result.l) && IsZero(check.l))) { printf("\nFailed! Answer should be %e (%08lx).\n", check.d, check.l); Display(param1, param2, result); printf("\n"); } }
bool Ray::Intersects(const Plane& plane, F32* distance) const { Vector3f planeNormal = plane.GetNormal(); Vector3f rayEnd = origin + direction * this->distance; XMFLOAT4 f4PlaneCoefs(planeNormal.x, planeNormal.y, planeNormal.z, plane.GetDistance()); XMFLOAT3 f3RayBegin(origin.v); XMFLOAT3 f3RayEnd(rayEnd.v); XMVECTOR vPlaneCoefs = XMLoadFloat4(&f4PlaneCoefs); XMVECTOR vRayBegin = XMLoadFloat3(&f3RayBegin); XMVECTOR vRayEnd = XMLoadFloat3(&f3RayEnd); XMVECTOR vIntersection = XMPlaneIntersectLine(vPlaneCoefs, vRayBegin, vRayEnd); XMFLOAT3 f3Intersection; XMStoreFloat3(&f3Intersection, vIntersection); if (IsNaN(f3Intersection.x) || IsNaN(f3Intersection.y) || IsNaN(f3Intersection.z)) { return false; } else { *distance = Vector3f::Distance(origin, Vector3f(f3Intersection.x, f3Intersection.y, f3Intersection.z)); return true; } }
/* ------------------------------------------------------------------------ * Function: find_minmax * ------------------------------------------------------------------------ */ void find_minmax(double *input, int M, double *minval, int *minind, double *maxval, int *maxind) { int i; for (i = 0; i < M; i++) { if (!IsNaN(input[i])) { // init min/max to first non-NAN element *minval = input[i]; *minind = i; *maxval = input[i]; *maxind = i; break; } } if (i == M) { // special case: if all NaNs *minval = GetNaN(); *minind = 0; *maxval = GetNaN(); *maxind = 0; } for (; i < M; i++) { // go through keeping track of min/max if ((input[i] < *minval) && (!IsNaN(input[i]))) {*minval = input[i]; *minind = i;} else if ((input[i] > *maxval) && (!IsNaN(input[i]))) {*maxval = input[i]; *maxind = i;} } }
void SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("SetAppendWindowEnd(aAppendWindowEnd=%f)", aAppendWindowEnd); if (!IsAttached() || mUpdating) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } if (IsNaN(aAppendWindowEnd) || aAppendWindowEnd <= mAppendWindowStart) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } mAppendWindowEnd = aAppendWindowEnd; }
char ASCIIShade(double x) { if(IsNaN(x)) return 'E'; if(IsInf(x)==1) return 'I'; else if(IsInf(x)==-1) return 'i'; int index = (int)Trunc(x*8) + 7; if(index < 0) index=0; if(index >= kNumAsciiShades) index=kNumAsciiShades-1; if(index == 7) { if(x > 0) return kAsciiShades[8]; else if(x < 0) return kAsciiShades[6]; else return kAsciiShades[7]; } return kAsciiShades[index]; }
int64_t SubBufferDecoder::ConvertToByteOffset(double aTime) { // Uses a conversion based on (aTime/duration) * length. For the // purposes of eviction this should be adequate since we have the // byte threshold as well to ensure data actually gets evicted and // we ensure we don't evict before the current playable point. double duration = mParentDecoder->GetMediaSourceDuration(); if (duration <= 0.0 || IsNaN(duration)) { return -1; } int64_t length = GetResource()->GetLength(); MOZ_ASSERT(length > 0); int64_t offset = (aTime / duration) * length; return offset; }
void MediaSourceDecoder::SetInitialDuration(int64_t aDuration) { // Only use the decoded duration if one wasn't already // set. ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); if (!mMediaSource || !IsNaN(mMediaSourceDuration)) { return; } double duration = aDuration; // A duration of -1 is +Infinity. if (aDuration >= 0) { duration /= USECS_PER_S; } SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP); }
void MediaSource::SetDuration(double aDuration, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("SetDuration(aDuration=%f, ErrorResult)", aDuration); if (aDuration < 0 || IsNaN(aDuration)) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } if (mReadyState != MediaSourceReadyState::Open || mSourceBuffers->AnyUpdating()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } SetDuration(aDuration, MSRangeRemovalAction::RUN); }
void MediaSource::SetDuration(double aDuration, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread()); MSE_API("MediaSource(%p)::SetDuration(aDuration=%f)", this, aDuration); if (aDuration < 0 || IsNaN(aDuration)) { aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR); return; } if (mReadyState != MediaSourceReadyState::Open || mSourceBuffers->AnyUpdating()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } DurationChange(aDuration, aRv); }
void MediaSourceDecoder::SetInitialDuration(int64_t aDuration) { MOZ_ASSERT(NS_IsMainThread()); // Only use the decoded duration if one wasn't already // set. if (!mMediaSource || !IsNaN(ExplicitDuration())) { return; } double duration = aDuration; // A duration of -1 is +Infinity. if (aDuration >= 0) { duration /= USECS_PER_S; } SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP); }
char* DoubleToBuffer(double value, char* buffer) { // DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all // platforms these days. Just in case some system exists where DBL_DIG // is significantly larger -- and risks overflowing our buffer -- we have // this assert. GOOGLE_COMPILE_ASSERT(DBL_DIG < 20, DBL_DIG_is_too_big); if (value == numeric_limits<double>::infinity()) { strcpy(buffer, "inf"); return buffer; } else if (value == -numeric_limits<double>::infinity()) { strcpy(buffer, "-inf"); return buffer; } else if (IsNaN(value)) { strcpy(buffer, "nan"); return buffer; } int snprintf_result = snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value); // The snprintf should never overflow because the buffer is significantly // larger than the precision we asked for. GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); // We need to make parsed_value volatile in order to force the compiler to // write it out to the stack. Otherwise, it may keep the value in a // register, and if it does that, it may keep it as a long double instead // of a double. This long double may have extra bits that make it compare // unequal to "value" even though it would be exactly equal if it were // truncated to a double. volatile double parsed_value = strtod(buffer, NULL); if (parsed_value != value) { int snprintf_result = snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value); // Should never overflow; see above. GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); } DelocalizeRadix(buffer); return buffer; }
media::TimeIntervals MediaSourceDecoder::GetSeekable() { MOZ_ASSERT(NS_IsMainThread()); if (!mMediaSource) { NS_WARNING("MediaSource element isn't attached"); return media::TimeIntervals::Invalid(); } media::TimeIntervals seekable; double duration = mMediaSource->Duration(); if (IsNaN(duration)) { // Return empty range. } else if (duration > 0 && mozilla::IsInfinite(duration)) { media::TimeIntervals buffered = GetBuffered(); // 1. If live seekable range is not empty: if (mMediaSource->HasLiveSeekableRange()) { // 1. Let union ranges be the union of live seekable range and the // HTMLMediaElement.buffered attribute. media::TimeIntervals unionRanges = buffered + mMediaSource->LiveSeekableRange(); // 2. Return a single range with a start time equal to the earliest start // time in union ranges and an end time equal to the highest end time in // union ranges and abort these steps. seekable += media::TimeInterval(unionRanges.GetStart(), unionRanges.GetEnd()); return seekable; } if (buffered.Length()) { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), buffered.GetEnd()); } } else { seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0), media::TimeUnit::FromSeconds(duration)); } MSE_DEBUG("ranges=%s", DumpTimeRanges(seekable).get()); return seekable; }
nsresult MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable) { MOZ_ASSERT(NS_IsMainThread()); if (!mMediaSource) { return NS_ERROR_FAILURE; } double duration = mMediaSource->Duration(); if (IsNaN(duration)) { // Return empty range. } else if (duration > 0 && mozilla::IsInfinite(duration)) { nsRefPtr<dom::TimeRanges> bufferedRanges = new dom::TimeRanges(); mMediaSource->GetBuffered(bufferedRanges); aSeekable->Add(bufferedRanges->GetStartTime(), bufferedRanges->GetEndTime()); } else { aSeekable->Add(0, duration); } MSE_DEBUG("MediaSourceDecoder(%p)::GetSeekable ranges=%s", this, DumpTimeRanges(aSeekable).get()); return NS_OK; }
int IsInf(double x) { #ifdef _MSC_VER //doesn't have isinf int cls = _fpclass(x); if(cls == _FPCLASS_PINF) return 1; else if(cls == _FPCLASS_NINF) return -1; else return 0; #elif HAVE_DECL_ISINF if(isinf(x)) { if(x > 0) return 1; else return -1; } else return 0; #elif HAVE_IEEE_COMPARISONS double y=x-x; if(IsNaN(y)) return (x>0?1:-1); else return 0; #else #error "IsInf: Neither Microsoft's _fpclass, isinf, or IEEE comparisons defined" return 0; #endif }
char* FloatToBuffer(float value, char* buffer) { // FLT_DIG is 6 for IEEE-754 floats, which are used on almost all // platforms these days. Just in case some system exists where FLT_DIG // is significantly larger -- and risks overflowing our buffer -- we have // this assert. GOOGLE_COMPILE_ASSERT(FLT_DIG < 10, FLT_DIG_is_too_big); if (value == numeric_limits<double>::infinity()) { strcpy(buffer, "inf"); return buffer; } else if (value == -numeric_limits<double>::infinity()) { strcpy(buffer, "-inf"); return buffer; } else if (IsNaN(value)) { strcpy(buffer, "nan"); return buffer; } int snprintf_result = snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value); // The snprintf should never overflow because the buffer is significantly // larger than the precision we asked for. GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); float parsed_value; if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) { int snprintf_result = snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value); // Should never overflow; see above. GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); } DelocalizeRadix(buffer); return buffer; }
TEST_F(TextFormatTest, ParseExotic) { unittest::TestAllTypes message; ASSERT_TRUE(TextFormat::ParseFromString( "repeated_int32: -1\n" "repeated_int32: -2147483648\n" "repeated_int64: -1\n" "repeated_int64: -9223372036854775808\n" "repeated_uint32: 4294967295\n" "repeated_uint32: 2147483648\n" "repeated_uint64: 18446744073709551615\n" "repeated_uint64: 9223372036854775808\n" "repeated_double: 123.0\n" "repeated_double: 123.5\n" "repeated_double: 0.125\n" "repeated_double: 1.23E17\n" "repeated_double: 1.235E+22\n" "repeated_double: 1.235e-18\n" "repeated_double: 123.456789\n" "repeated_double: inf\n" "repeated_double: Infinity\n" "repeated_double: -inf\n" "repeated_double: -Infinity\n" "repeated_double: nan\n" "repeated_double: NaN\n" "repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\"\n", &message)); ASSERT_EQ(2, message.repeated_int32_size()); EXPECT_EQ(-1, message.repeated_int32(0)); // Note: In C, a negative integer literal is actually the unary negation // operator being applied to a positive integer literal, and 2147483648 is // outside the range of int32. However, it is not outside the range of // uint32. Confusingly, this means that everything works if we make the // literal unsigned, even though we are negating it. EXPECT_EQ(-2147483648u, message.repeated_int32(1)); ASSERT_EQ(2, message.repeated_int64_size()); EXPECT_EQ(-1, message.repeated_int64(0)); // Note: In C, a negative integer literal is actually the unary negation // operator being applied to a positive integer literal, and // 9223372036854775808 is outside the range of int64. However, it is not // outside the range of uint64. Confusingly, this means that everything // works if we make the literal unsigned, even though we are negating it. EXPECT_EQ(-GOOGLE_ULONGLONG(9223372036854775808), message.repeated_int64(1)); ASSERT_EQ(2, message.repeated_uint32_size()); EXPECT_EQ(4294967295u, message.repeated_uint32(0)); EXPECT_EQ(2147483648u, message.repeated_uint32(1)); ASSERT_EQ(2, message.repeated_uint64_size()); EXPECT_EQ(GOOGLE_ULONGLONG(18446744073709551615), message.repeated_uint64(0)); EXPECT_EQ(GOOGLE_ULONGLONG(9223372036854775808), message.repeated_uint64(1)); ASSERT_EQ(13, message.repeated_double_size()); EXPECT_EQ(123.0 , message.repeated_double(0)); EXPECT_EQ(123.5 , message.repeated_double(1)); EXPECT_EQ(0.125 , message.repeated_double(2)); EXPECT_EQ(1.23E17 , message.repeated_double(3)); EXPECT_EQ(1.235E22 , message.repeated_double(4)); EXPECT_EQ(1.235E-18 , message.repeated_double(5)); EXPECT_EQ(123.456789, message.repeated_double(6)); EXPECT_EQ(message.repeated_double(7), numeric_limits<double>::infinity()); EXPECT_EQ(message.repeated_double(8), numeric_limits<double>::infinity()); EXPECT_EQ(message.repeated_double(9), -numeric_limits<double>::infinity()); EXPECT_EQ(message.repeated_double(10), -numeric_limits<double>::infinity()); EXPECT_TRUE(IsNaN(message.repeated_double(11))); EXPECT_TRUE(IsNaN(message.repeated_double(12))); // Note: Since these string literals have \0's in them, we must explicitly // pass their sizes to string's constructor. ASSERT_EQ(1, message.repeated_string_size()); EXPECT_EQ(string("\000\001\a\b\f\n\r\t\v\\\'\"", 12), message.repeated_string(0)); }
bool TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles, Compositor* aCompositor, ISurfaceAllocator* aAllocator) { if (mResolution != aTiles.resolution()) { Clear(); } MOZ_ASSERT(aAllocator); MOZ_ASSERT(aCompositor); if (!aAllocator || !aCompositor) { return false; } if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) { // There are divisions by mResolution so this protects the compositor process // against malicious content processes and fuzzing. return false; } TilesPlacement oldTiles = mTiles; TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(), aTiles.retainedWidth(), aTiles.retainedHeight()); const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles(); nsTArray<TileHost> oldRetainedTiles; mRetainedTiles.SwapElements(oldRetainedTiles); mRetainedTiles.SetLength(tileDescriptors.Length()); // Step 1, we need to unlock tiles that don't have an internal buffer after the // next frame where they are replaced. // Since we are about to replace the tiles' textures, we need to keep their locks // somewhere (in mPreviousSharedLock) until we composite the layer. for (size_t i = 0; i < oldRetainedTiles.Length(); ++i) { TileHost& tile = oldRetainedTiles[i]; // It can happen that we still have a previous lock at this point, // if we changed a tile's front buffer (causing mSharedLock to // go into mPreviousSharedLock, and then did not composite that tile until // the next transaction, either because the tile is offscreen or because the // two transactions happened with no composition in between (over-production). tile.ReadUnlockPrevious(); if (tile.mTextureHost && !tile.mTextureHost->HasInternalBuffer()) { MOZ_ASSERT(tile.mSharedLock); const TileIntPoint tilePosition = oldTiles.TilePosition(i); if (newTiles.HasTile(tilePosition)) { // This tile still exist in the new buffer tile.mPreviousSharedLock = tile.mSharedLock; tile.mSharedLock = nullptr; } else { // This tile does not exist anymore in the new buffer because the size // changed. tile.ReadUnlock(); } } // By now we should not have anything in mSharedLock. MOZ_ASSERT(!tile.mSharedLock); } // Step 2, move the tiles in mRetainedTiles at places that correspond to where // they should be with the new retained with and height rather than the // old one. for (size_t i = 0; i < tileDescriptors.Length(); i++) { const TileIntPoint tilePosition = newTiles.TilePosition(i); // First, get the already existing tiles to the right place in the array, // and use placeholders where there was no tiles. if (!oldTiles.HasTile(tilePosition)) { mRetainedTiles[i] = GetPlaceholderTile(); } else { mRetainedTiles[i] = oldRetainedTiles[oldTiles.TileIndex(tilePosition)]; // If we hit this assertion it means we probably mixed something up in the // logic that tries to reuse tiles on the compositor side. It is most likely // benign, but we are missing some fast paths so let's try to make it not happen. MOZ_ASSERT(tilePosition.x == mRetainedTiles[i].x && tilePosition.y == mRetainedTiles[i].y); } } // It is important to remove the duplicated reference to tiles before calling // TextureHost::PrepareTextureSource, etc. because depending on the textures // ref counts we may or may not get some of the fast paths. oldRetainedTiles.Clear(); // Step 3, handle the texture updates and release the copy-on-write locks. for (size_t i = 0; i < mRetainedTiles.Length(); i++) { const TileDescriptor& tileDesc = tileDescriptors[i]; TileHost& tile = mRetainedTiles[i]; switch (tileDesc.type()) { case TileDescriptor::TTexturedTileDescriptor: { const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); const TileLock& ipcLock = texturedDesc.sharedLock(); if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) { return false; } RefPtr<TextureHost> textureHost = TextureHost::AsTextureHost( texturedDesc.textureParent() ); RefPtr<TextureHost> textureOnWhite = nullptr; if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) { textureOnWhite = TextureHost::AsTextureHost( texturedDesc.textureOnWhite().get_PTextureParent() ); } UseTileTexture(tile.mTextureHost, tile.mTextureSource, texturedDesc.updateRect(), textureHost, aCompositor); if (textureOnWhite) { UseTileTexture(tile.mTextureHostOnWhite, tile.mTextureSourceOnWhite, texturedDesc.updateRect(), textureOnWhite, aCompositor); } else { // We could still have component alpha textures from a previous frame. tile.mTextureSourceOnWhite = nullptr; tile.mTextureHostOnWhite = nullptr; } if (textureHost->HasInternalBuffer()) { // Now that we did the texture upload (in UseTileTexture), we can release // the lock. tile.ReadUnlock(); } break; } default: NS_WARNING("Unrecognised tile descriptor type"); case TileDescriptor::TPlaceholderTileDescriptor: { if (tile.mTextureHost) { tile.mTextureHost->UnbindTextureSource(); tile.mTextureSource = nullptr; } if (tile.mTextureHostOnWhite) { tile.mTextureHostOnWhite->UnbindTextureSource(); tile.mTextureSourceOnWhite = nullptr; } // we may have a previous lock, and are about to loose our reference to it. // It is okay to unlock it because we just destroyed the texture source. tile.ReadUnlockPrevious(); tile = GetPlaceholderTile(); break; } } TileIntPoint tilePosition = newTiles.TilePosition(i); tile.x = tilePosition.x; tile.y = tilePosition.y; } mTiles = newTiles; mValidRegion = aTiles.validRegion(); mResolution = aTiles.resolution(); mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(), aTiles.frameYResolution()); return true; }
TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator, const SurfaceDescriptorTiles& aDescriptor, const nsIntRegion& aOldPaintedRegion, Compositor* aCompositor) { mIsValid = true; mHasDoubleBufferedTiles = false; mValidRegion = aDescriptor.validRegion(); mPaintedRegion = aDescriptor.paintedRegion(); mRetainedWidth = aDescriptor.retainedWidth(); mRetainedHeight = aDescriptor.retainedHeight(); mResolution = aDescriptor.resolution(); mFrameResolution = CSSToParentLayerScale(aDescriptor.frameResolution()); if (mResolution == 0 || IsNaN(mResolution)) { // There are divisions by mResolution so this protects the compositor process // against malicious content processes and fuzzing. mIsValid = false; return; } // Combine any valid content that wasn't already uploaded nsIntRegion oldPaintedRegion(aOldPaintedRegion); oldPaintedRegion.And(oldPaintedRegion, mValidRegion); mPaintedRegion.Or(mPaintedRegion, oldPaintedRegion); bool isSameProcess = aAllocator->IsSameProcess(); const InfallibleTArray<TileDescriptor>& tiles = aDescriptor.tiles(); for(size_t i = 0; i < tiles.Length(); i++) { CompositableTextureHostRef texture; CompositableTextureHostRef textureOnWhite; const TileDescriptor& tileDesc = tiles[i]; switch (tileDesc.type()) { case TileDescriptor::TTexturedTileDescriptor : { texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent()); MaybeTexture onWhite = tileDesc.get_TexturedTileDescriptor().textureOnWhite(); if (onWhite.type() == MaybeTexture::TPTextureParent) { textureOnWhite = TextureHost::AsTextureHost(onWhite.get_PTextureParent()); } const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock(); nsRefPtr<gfxSharedReadLock> sharedLock; if (ipcLock.type() == TileLock::TShmemSection) { sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection()); } else { if (!isSameProcess) { // Trying to use a memory based lock instead of a shmem based one in // the cross-process case is a bad security violation. NS_ERROR("A client process may be trying to peek at the host's address space!"); // This tells the TiledContentHost that deserialization failed so that // it can propagate the error. mIsValid = false; mRetainedTiles.Clear(); return; } sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t()); if (sharedLock) { // The corresponding AddRef is in TiledClient::GetTileDescriptor sharedLock.get()->Release(); } } CompositableTextureSourceRef textureSource; CompositableTextureSourceRef textureSourceOnWhite; if (texture) { texture->SetCompositor(aCompositor); texture->PrepareTextureSource(textureSource); } if (textureOnWhite) { textureOnWhite->SetCompositor(aCompositor); textureOnWhite->PrepareTextureSource(textureSourceOnWhite); } mRetainedTiles.AppendElement(TileHost(sharedLock, texture.get(), textureOnWhite.get(), textureSource.get(), textureSourceOnWhite.get())); break; } default: NS_WARNING("Unrecognised tile descriptor type"); // Fall through case TileDescriptor::TPlaceholderTileDescriptor : mRetainedTiles.AppendElement(GetPlaceholderTile()); break; } if (texture && !texture->HasInternalBuffer()) { mHasDoubleBufferedTiles = true; } } }
bool TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles, Compositor* aCompositor, ISurfaceAllocator* aAllocator) { if (mResolution != aTiles.resolution() || aTiles.tileSize() != mTileSize) { Clear(); } MOZ_ASSERT(aAllocator); MOZ_ASSERT(aCompositor); if (!aAllocator || !aCompositor) { return false; } if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) { // There are divisions by mResolution so this protects the compositor process // against malicious content processes and fuzzing. return false; } TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(), aTiles.retainedWidth(), aTiles.retainedHeight()); const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles(); // Step 1, unlock all the old tiles that haven't been unlocked yet. Any tiles that // exist in both the old and new sets will have been locked again by content, so this // doesn't result in the surface being writeable again. MarkTilesForUnlock(); TextureSourceRecycler oldRetainedTiles(Move(mRetainedTiles)); mRetainedTiles.SetLength(tileDescriptors.Length()); // Step 2, deserialize the incoming set of tiles into mRetainedTiles, and attempt // to recycle the TextureSource for any repeated tiles. // // Since we don't have any retained 'tile' object, we have to search for instances // of the same TextureHost in the old tile set. The cost of binding a TextureHost // to a TextureSource for gralloc (binding EGLImage to GL texture) can be really // high, so we avoid this whenever possible. for (size_t i = 0; i < tileDescriptors.Length(); i++) { const TileDescriptor& tileDesc = tileDescriptors[i]; TileHost& tile = mRetainedTiles[i]; if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) { NS_WARN_IF_FALSE(tileDesc.type() == TileDescriptor::TPlaceholderTileDescriptor, "Unrecognised tile descriptor type"); continue; } const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); const TileLock& ipcLock = texturedDesc.sharedLock(); if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) { return false; } tile.mTextureHost = TextureHost::AsTextureHost(texturedDesc.textureParent()); tile.mTextureHost->SetCompositor(aCompositor); if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) { tile.mTextureHostOnWhite = TextureHost::AsTextureHost(texturedDesc.textureOnWhite().get_PTextureParent()); } tile.mTilePosition = newTiles.TilePosition(i); // If this same tile texture existed in the old tile set then this will move the texture // source into our new tile. oldRetainedTiles.RecycleTextureSourceForTile(tile); } // Step 3, attempt to recycle unused texture sources from the old tile set into new tiles. // // For gralloc, binding a new TextureHost to the existing TextureSource is the fastest way // to ensure that any implicit locking on the old gralloc image is released. for (TileHost& tile : mRetainedTiles) { if (!tile.mTextureHost || tile.mTextureSource) { continue; } oldRetainedTiles.RecycleTextureSource(tile); } // Step 4, handle the texture uploads, texture source binding and release the // copy-on-write locks for textures with an internal buffer. for (size_t i = 0; i < mRetainedTiles.Length(); i++) { TileHost& tile = mRetainedTiles[i]; if (!tile.mTextureHost) { continue; } const TileDescriptor& tileDesc = tileDescriptors[i]; const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); UseTileTexture(tile.mTextureHost, tile.mTextureSource, texturedDesc.updateRect(), aCompositor); if (tile.mTextureHostOnWhite) { UseTileTexture(tile.mTextureHostOnWhite, tile.mTextureSourceOnWhite, texturedDesc.updateRect(), aCompositor); } if (tile.mTextureHost->HasInternalBuffer()) { // Now that we did the texture upload (in UseTileTexture), we can release // the lock. tile.ReadUnlock(); } } mTiles = newTiles; mTileSize = aTiles.tileSize(); mTileOrigin = aTiles.tileOrigin(); mValidRegion = aTiles.validRegion(); mResolution = aTiles.resolution(); mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(), aTiles.frameYResolution()); return true; }
void DynamicsCompressorKernel::process(float* sourceChannels[], float* destinationChannels[], unsigned numberOfChannels, unsigned framesToProcess, float dbThreshold, float dbKnee, float ratio, float attackTime, float releaseTime, float preDelayTime, float dbPostGain, float effectBlend, /* equal power crossfade */ float releaseZone1, float releaseZone2, float releaseZone3, float releaseZone4 ) { MOZ_ASSERT(m_preDelayBuffers.Length() == numberOfChannels); float sampleRate = this->sampleRate(); float dryMix = 1 - effectBlend; float wetMix = effectBlend; float k = updateStaticCurveParameters(dbThreshold, dbKnee, ratio); // Makeup gain. float fullRangeGain = saturate(1, k); float fullRangeMakeupGain = 1 / fullRangeGain; // Empirical/perceptual tuning. fullRangeMakeupGain = powf(fullRangeMakeupGain, 0.6f); float masterLinearGain = WebAudioUtils::ConvertDecibelsToLinear(dbPostGain) * fullRangeMakeupGain; // Attack parameters. attackTime = max(0.001f, attackTime); float attackFrames = attackTime * sampleRate; // Release parameters. float releaseFrames = sampleRate * releaseTime; // Detector release time. float satReleaseTime = 0.0025f; float satReleaseFrames = satReleaseTime * sampleRate; // Create a smooth function which passes through four points. // Polynomial of the form // y = a + b*x + c*x^2 + d*x^3 + e*x^4; float y1 = releaseFrames * releaseZone1; float y2 = releaseFrames * releaseZone2; float y3 = releaseFrames * releaseZone3; float y4 = releaseFrames * releaseZone4; // All of these coefficients were derived for 4th order polynomial curve fitting where the y values // match the evenly spaced x values as follows: (y1 : x == 0, y2 : x == 1, y3 : x == 2, y4 : x == 3) float kA = 0.9999999999999998f*y1 + 1.8432219684323923e-16f*y2 - 1.9373394351676423e-16f*y3 + 8.824516011816245e-18f*y4; float kB = -1.5788320352845888f*y1 + 2.3305837032074286f*y2 - 0.9141194204840429f*y3 + 0.1623677525612032f*y4; float kC = 0.5334142869106424f*y1 - 1.272736789213631f*y2 + 0.9258856042207512f*y3 - 0.18656310191776226f*y4; float kD = 0.08783463138207234f*y1 - 0.1694162967925622f*y2 + 0.08588057951595272f*y3 - 0.00429891410546283f*y4; float kE = -0.042416883008123074f*y1 + 0.1115693827987602f*y2 - 0.09764676325265872f*y3 + 0.028494263462021576f*y4; // x ranges from 0 -> 3 0 1 2 3 // -15 -10 -5 0db // y calculates adaptive release frames depending on the amount of compression. setPreDelayTime(preDelayTime); const int nDivisionFrames = 32; const int nDivisions = framesToProcess / nDivisionFrames; unsigned frameIndex = 0; for (int i = 0; i < nDivisions; ++i) { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Calculate desired gain // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Fix gremlins. if (IsNaN(m_detectorAverage)) m_detectorAverage = 1; if (IsInfinite(m_detectorAverage)) m_detectorAverage = 1; float desiredGain = m_detectorAverage; // Pre-warp so we get desiredGain after sin() warp below. float scaledDesiredGain = asinf(desiredGain) / (0.5f * M_PI); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Deal with envelopes // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // envelopeRate is the rate we slew from current compressor level to the desired level. // The exact rate depends on if we're attacking or releasing and by how much. float envelopeRate; bool isReleasing = scaledDesiredGain > m_compressorGain; // compressionDiffDb is the difference between current compression level and the desired level. float compressionDiffDb = WebAudioUtils::ConvertLinearToDecibels(m_compressorGain / scaledDesiredGain, -1000.0f); if (isReleasing) { // Release mode - compressionDiffDb should be negative dB m_maxAttackCompressionDiffDb = -1; // Fix gremlins. if (IsNaN(compressionDiffDb)) compressionDiffDb = -1; if (IsInfinite(compressionDiffDb)) compressionDiffDb = -1; // Adaptive release - higher compression (lower compressionDiffDb) releases faster. // Contain within range: -12 -> 0 then scale to go from 0 -> 3 float x = compressionDiffDb; x = max(-12.0f, x); x = min(0.0f, x); x = 0.25f * (x + 12); // Compute adaptive release curve using 4th order polynomial. // Normal values for the polynomial coefficients would create a monotonically increasing function. float x2 = x * x; float x3 = x2 * x; float x4 = x2 * x2; float releaseFrames = kA + kB * x + kC * x2 + kD * x3 + kE * x4; #define kSpacingDb 5 float dbPerFrame = kSpacingDb / releaseFrames; envelopeRate = WebAudioUtils::ConvertDecibelsToLinear(dbPerFrame); } else { // Attack mode - compressionDiffDb should be positive dB // Fix gremlins. if (IsNaN(compressionDiffDb)) compressionDiffDb = 1; if (IsInfinite(compressionDiffDb)) compressionDiffDb = 1; // As long as we're still in attack mode, use a rate based off // the largest compressionDiffDb we've encountered so far. if (m_maxAttackCompressionDiffDb == -1 || m_maxAttackCompressionDiffDb < compressionDiffDb) m_maxAttackCompressionDiffDb = compressionDiffDb; float effAttenDiffDb = max(0.5f, m_maxAttackCompressionDiffDb); float x = 0.25f / effAttenDiffDb; envelopeRate = 1 - powf(x, 1 / attackFrames); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Inner loop - calculate shaped power average - apply compression. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ { int preDelayReadIndex = m_preDelayReadIndex; int preDelayWriteIndex = m_preDelayWriteIndex; float detectorAverage = m_detectorAverage; float compressorGain = m_compressorGain; int loopFrames = nDivisionFrames; while (loopFrames--) { float compressorInput = 0; // Predelay signal, computing compression amount from un-delayed version. for (unsigned i = 0; i < numberOfChannels; ++i) { float* delayBuffer = m_preDelayBuffers[i].get(); float undelayedSource = sourceChannels[i][frameIndex]; delayBuffer[preDelayWriteIndex] = undelayedSource; float absUndelayedSource = undelayedSource > 0 ? undelayedSource : -undelayedSource; if (compressorInput < absUndelayedSource) compressorInput = absUndelayedSource; } // Calculate shaped power on undelayed input. float scaledInput = compressorInput; float absInput = scaledInput > 0 ? scaledInput : -scaledInput; // Put through shaping curve. // This is linear up to the threshold, then enters a "knee" portion followed by the "ratio" portion. // The transition from the threshold to the knee is smooth (1st derivative matched). // The transition from the knee to the ratio portion is smooth (1st derivative matched). float shapedInput = saturate(absInput, k); float attenuation = absInput <= 0.0001f ? 1 : shapedInput / absInput; float attenuationDb = -WebAudioUtils::ConvertLinearToDecibels(attenuation, -1000.0f); attenuationDb = max(2.0f, attenuationDb); float dbPerFrame = attenuationDb / satReleaseFrames; float satReleaseRate = WebAudioUtils::ConvertDecibelsToLinear(dbPerFrame) - 1; bool isRelease = (attenuation > detectorAverage); float rate = isRelease ? satReleaseRate : 1; detectorAverage += (attenuation - detectorAverage) * rate; detectorAverage = min(1.0f, detectorAverage); // Fix gremlins. if (IsNaN(detectorAverage)) detectorAverage = 1; if (IsInfinite(detectorAverage)) detectorAverage = 1; // Exponential approach to desired gain. if (envelopeRate < 1) { // Attack - reduce gain to desired. compressorGain += (scaledDesiredGain - compressorGain) * envelopeRate; } else { // Release - exponentially increase gain to 1.0 compressorGain *= envelopeRate; compressorGain = min(1.0f, compressorGain); } // Warp pre-compression gain to smooth out sharp exponential transition points. float postWarpCompressorGain = sinf(0.5f * M_PI * compressorGain); // Calculate total gain using master gain and effect blend. float totalGain = dryMix + wetMix * masterLinearGain * postWarpCompressorGain; // Calculate metering. float dbRealGain = 20 * log10(postWarpCompressorGain); if (dbRealGain < m_meteringGain) m_meteringGain = dbRealGain; else m_meteringGain += (dbRealGain - m_meteringGain) * m_meteringReleaseK; // Apply final gain. for (unsigned i = 0; i < numberOfChannels; ++i) { float* delayBuffer = m_preDelayBuffers[i].get(); destinationChannels[i][frameIndex] = delayBuffer[preDelayReadIndex] * totalGain; } frameIndex++; preDelayReadIndex = (preDelayReadIndex + 1) & MaxPreDelayFramesMask; preDelayWriteIndex = (preDelayWriteIndex + 1) & MaxPreDelayFramesMask; } // Locals back to member variables. m_preDelayReadIndex = preDelayReadIndex; m_preDelayWriteIndex = preDelayWriteIndex; m_detectorAverage = DenormalDisabler::flushDenormalFloatToZero(detectorAverage); m_compressorGain = DenormalDisabler::flushDenormalFloatToZero(compressorGain); } } }
static bool IsNaN(FloatConstant* value) { return IsNaN(value->Value(), value->GetType()->GetSubtype()); }