michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "VideoUtils.h" michael@0: #include "MediaResource.h" michael@0: #include "mozilla/dom/TimeRanges.h" michael@0: #include "nsMathUtils.h" michael@0: #include "nsSize.h" michael@0: #include "VorbisUtils.h" michael@0: #include "ImageContainer.h" michael@0: michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: michael@0: using layers::PlanarYCbCrImage; michael@0: michael@0: // Converts from number of audio frames to microseconds, given the specified michael@0: // audio rate. michael@0: CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) { michael@0: return (CheckedInt64(aFrames) * USECS_PER_S) / aRate; michael@0: } michael@0: michael@0: // Converts from microseconds to number of audio frames, given the specified michael@0: // audio rate. michael@0: CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) { michael@0: return (CheckedInt64(aUsecs) * aRate) / USECS_PER_S; michael@0: } michael@0: michael@0: nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs) { michael@0: if (aSeconds * double(USECS_PER_S) > INT64_MAX) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: aOutUsecs = int64_t(aSeconds * double(USECS_PER_S)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static int32_t ConditionDimension(float aValue) michael@0: { michael@0: // This will exclude NaNs and too-big values. michael@0: if (aValue > 1.0 && aValue <= INT32_MAX) michael@0: return int32_t(NS_round(aValue)); michael@0: return 0; michael@0: } michael@0: michael@0: void ScaleDisplayByAspectRatio(nsIntSize& aDisplay, float aAspectRatio) michael@0: { michael@0: if (aAspectRatio > 1.0) { michael@0: // Increase the intrinsic width michael@0: aDisplay.width = ConditionDimension(aAspectRatio * aDisplay.width); michael@0: } else { michael@0: // Increase the intrinsic height michael@0: aDisplay.height = ConditionDimension(aDisplay.height / aAspectRatio); michael@0: } michael@0: } michael@0: michael@0: static int64_t BytesToTime(int64_t offset, int64_t length, int64_t durationUs) { michael@0: NS_ASSERTION(length > 0, "Must have positive length"); michael@0: double r = double(offset) / double(length); michael@0: if (r > 1.0) michael@0: r = 1.0; michael@0: return int64_t(double(durationUs) * r); michael@0: } michael@0: michael@0: void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream, michael@0: int64_t aDurationUsecs, michael@0: mozilla::dom::TimeRanges* aOutBuffered) michael@0: { michael@0: // Nothing to cache if the media takes 0us to play. michael@0: if (aDurationUsecs <= 0 || !aStream || !aOutBuffered) michael@0: return; michael@0: michael@0: // Special case completely cached files. This also handles local files. michael@0: if (aStream->IsDataCachedToEndOfResource(0)) { michael@0: aOutBuffered->Add(0, double(aDurationUsecs) / USECS_PER_S); michael@0: return; michael@0: } michael@0: michael@0: int64_t totalBytes = aStream->GetLength(); michael@0: michael@0: // If we can't determine the total size, pretend that we have nothing michael@0: // buffered. This will put us in a state of eternally-low-on-undecoded-data michael@0: // which is not great, but about the best we can do. michael@0: if (totalBytes <= 0) michael@0: return; michael@0: michael@0: int64_t startOffset = aStream->GetNextCachedData(0); michael@0: while (startOffset >= 0) { michael@0: int64_t endOffset = aStream->GetCachedDataEnd(startOffset); michael@0: // Bytes [startOffset..endOffset] are cached. michael@0: NS_ASSERTION(startOffset >= 0, "Integer underflow in GetBuffered"); michael@0: NS_ASSERTION(endOffset >= 0, "Integer underflow in GetBuffered"); michael@0: michael@0: int64_t startUs = BytesToTime(startOffset, totalBytes, aDurationUsecs); michael@0: int64_t endUs = BytesToTime(endOffset, totalBytes, aDurationUsecs); michael@0: if (startUs != endUs) { michael@0: aOutBuffered->Add(double(startUs) / USECS_PER_S, michael@0: double(endUs) / USECS_PER_S); michael@0: } michael@0: startOffset = aStream->GetNextCachedData(endOffset); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: int DownmixAudioToStereo(mozilla::AudioDataValue* buffer, michael@0: int channels, uint32_t frames) michael@0: { michael@0: int outChannels; michael@0: outChannels = 2; michael@0: #ifdef MOZ_SAMPLE_TYPE_FLOAT32 michael@0: // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8. michael@0: static const float dmatrix[6][8][2]= { michael@0: /*3*/{{0.5858f,0},{0.4142f,0.4142f},{0, 0.5858f}}, michael@0: /*4*/{{0.4226f,0},{0, 0.4226f},{0.366f,0.2114f},{0.2114f,0.366f}}, michael@0: /*5*/{{0.6510f,0},{0.4600f,0.4600f},{0, 0.6510f},{0.5636f,0.3254f},{0.3254f,0.5636f}}, michael@0: /*6*/{{0.5290f,0},{0.3741f,0.3741f},{0, 0.5290f},{0.4582f,0.2645f},{0.2645f,0.4582f},{0.3741f,0.3741f}}, michael@0: /*7*/{{0.4553f,0},{0.3220f,0.3220f},{0, 0.4553f},{0.3943f,0.2277f},{0.2277f,0.3943f},{0.2788f,0.2788f},{0.3220f,0.3220f}}, michael@0: /*8*/{{0.3886f,0},{0.2748f,0.2748f},{0, 0.3886f},{0.3366f,0.1943f},{0.1943f,0.3366f},{0.3366f,0.1943f},{0.1943f,0.3366f},{0.2748f,0.2748f}}, michael@0: }; michael@0: // Re-write the buffer with downmixed data michael@0: for (uint32_t i = 0; i < frames; i++) { michael@0: float sampL = 0.0; michael@0: float sampR = 0.0; michael@0: for (int j = 0; j < channels; j++) { michael@0: sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0]; michael@0: sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1]; michael@0: } michael@0: buffer[i*outChannels]=sampL; michael@0: buffer[i*outChannels+1]=sampR; michael@0: } michael@0: #else michael@0: // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8. michael@0: // Coefficients in Q14. michael@0: static const int16_t dmatrix[6][8][2]= { michael@0: /*3*/{{9598, 0},{6786,6786},{0, 9598}}, michael@0: /*4*/{{6925, 0},{0, 6925},{5997,3462},{3462,5997}}, michael@0: /*5*/{{10663,0},{7540,7540},{0, 10663},{9234,5331},{5331,9234}}, michael@0: /*6*/{{8668, 0},{6129,6129},{0, 8668},{7507,4335},{4335,7507},{6129,6129}}, michael@0: /*7*/{{7459, 0},{5275,5275},{0, 7459},{6460,3731},{3731,6460},{4568,4568},{5275,5275}}, michael@0: /*8*/{{6368, 0},{4502,4502},{0, 6368},{5514,3184},{3184,5514},{5514,3184},{3184,5514},{4502,4502}} michael@0: }; michael@0: // Re-write the buffer with downmixed data michael@0: for (uint32_t i = 0; i < frames; i++) { michael@0: int32_t sampL = 0; michael@0: int32_t sampR = 0; michael@0: for (int j = 0; j < channels; j++) { michael@0: sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0]; michael@0: sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1]; michael@0: } michael@0: sampL = (sampL + 8192)>>14; michael@0: buffer[i*outChannels] = static_cast(MOZ_CLIP_TO_15(sampL)); michael@0: sampR = (sampR + 8192)>>14; michael@0: buffer[i*outChannels+1] = static_cast(MOZ_CLIP_TO_15(sampR)); michael@0: } michael@0: #endif michael@0: return outChannels; michael@0: } michael@0: michael@0: bool michael@0: IsVideoContentType(const nsCString& aContentType) michael@0: { michael@0: NS_NAMED_LITERAL_CSTRING(video, "video"); michael@0: if (FindInReadable(video, aContentType)) { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture, michael@0: const nsIntSize& aDisplay) michael@0: { michael@0: return michael@0: aFrame.width <= PlanarYCbCrImage::MAX_DIMENSION && michael@0: aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION && michael@0: aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT && michael@0: aFrame.width * aFrame.height != 0 && michael@0: aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION && michael@0: aPicture.x < PlanarYCbCrImage::MAX_DIMENSION && michael@0: aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION && michael@0: aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION && michael@0: aPicture.y < PlanarYCbCrImage::MAX_DIMENSION && michael@0: aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION && michael@0: aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT && michael@0: aPicture.width * aPicture.height != 0 && michael@0: aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION && michael@0: aDisplay.height <= PlanarYCbCrImage::MAX_DIMENSION && michael@0: aDisplay.width * aDisplay.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT && michael@0: aDisplay.width * aDisplay.height != 0; michael@0: } michael@0: michael@0: } // end namespace mozilla