1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/VideoUtils.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,193 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "VideoUtils.h" 1.9 +#include "MediaResource.h" 1.10 +#include "mozilla/dom/TimeRanges.h" 1.11 +#include "nsMathUtils.h" 1.12 +#include "nsSize.h" 1.13 +#include "VorbisUtils.h" 1.14 +#include "ImageContainer.h" 1.15 + 1.16 +#include <stdint.h> 1.17 + 1.18 +namespace mozilla { 1.19 + 1.20 +using layers::PlanarYCbCrImage; 1.21 + 1.22 +// Converts from number of audio frames to microseconds, given the specified 1.23 +// audio rate. 1.24 +CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) { 1.25 + return (CheckedInt64(aFrames) * USECS_PER_S) / aRate; 1.26 +} 1.27 + 1.28 +// Converts from microseconds to number of audio frames, given the specified 1.29 +// audio rate. 1.30 +CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) { 1.31 + return (CheckedInt64(aUsecs) * aRate) / USECS_PER_S; 1.32 +} 1.33 + 1.34 +nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs) { 1.35 + if (aSeconds * double(USECS_PER_S) > INT64_MAX) { 1.36 + return NS_ERROR_FAILURE; 1.37 + } 1.38 + aOutUsecs = int64_t(aSeconds * double(USECS_PER_S)); 1.39 + return NS_OK; 1.40 +} 1.41 + 1.42 +static int32_t ConditionDimension(float aValue) 1.43 +{ 1.44 + // This will exclude NaNs and too-big values. 1.45 + if (aValue > 1.0 && aValue <= INT32_MAX) 1.46 + return int32_t(NS_round(aValue)); 1.47 + return 0; 1.48 +} 1.49 + 1.50 +void ScaleDisplayByAspectRatio(nsIntSize& aDisplay, float aAspectRatio) 1.51 +{ 1.52 + if (aAspectRatio > 1.0) { 1.53 + // Increase the intrinsic width 1.54 + aDisplay.width = ConditionDimension(aAspectRatio * aDisplay.width); 1.55 + } else { 1.56 + // Increase the intrinsic height 1.57 + aDisplay.height = ConditionDimension(aDisplay.height / aAspectRatio); 1.58 + } 1.59 +} 1.60 + 1.61 +static int64_t BytesToTime(int64_t offset, int64_t length, int64_t durationUs) { 1.62 + NS_ASSERTION(length > 0, "Must have positive length"); 1.63 + double r = double(offset) / double(length); 1.64 + if (r > 1.0) 1.65 + r = 1.0; 1.66 + return int64_t(double(durationUs) * r); 1.67 +} 1.68 + 1.69 +void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream, 1.70 + int64_t aDurationUsecs, 1.71 + mozilla::dom::TimeRanges* aOutBuffered) 1.72 +{ 1.73 + // Nothing to cache if the media takes 0us to play. 1.74 + if (aDurationUsecs <= 0 || !aStream || !aOutBuffered) 1.75 + return; 1.76 + 1.77 + // Special case completely cached files. This also handles local files. 1.78 + if (aStream->IsDataCachedToEndOfResource(0)) { 1.79 + aOutBuffered->Add(0, double(aDurationUsecs) / USECS_PER_S); 1.80 + return; 1.81 + } 1.82 + 1.83 + int64_t totalBytes = aStream->GetLength(); 1.84 + 1.85 + // If we can't determine the total size, pretend that we have nothing 1.86 + // buffered. This will put us in a state of eternally-low-on-undecoded-data 1.87 + // which is not great, but about the best we can do. 1.88 + if (totalBytes <= 0) 1.89 + return; 1.90 + 1.91 + int64_t startOffset = aStream->GetNextCachedData(0); 1.92 + while (startOffset >= 0) { 1.93 + int64_t endOffset = aStream->GetCachedDataEnd(startOffset); 1.94 + // Bytes [startOffset..endOffset] are cached. 1.95 + NS_ASSERTION(startOffset >= 0, "Integer underflow in GetBuffered"); 1.96 + NS_ASSERTION(endOffset >= 0, "Integer underflow in GetBuffered"); 1.97 + 1.98 + int64_t startUs = BytesToTime(startOffset, totalBytes, aDurationUsecs); 1.99 + int64_t endUs = BytesToTime(endOffset, totalBytes, aDurationUsecs); 1.100 + if (startUs != endUs) { 1.101 + aOutBuffered->Add(double(startUs) / USECS_PER_S, 1.102 + double(endUs) / USECS_PER_S); 1.103 + } 1.104 + startOffset = aStream->GetNextCachedData(endOffset); 1.105 + } 1.106 + return; 1.107 +} 1.108 + 1.109 +int DownmixAudioToStereo(mozilla::AudioDataValue* buffer, 1.110 + int channels, uint32_t frames) 1.111 +{ 1.112 + int outChannels; 1.113 + outChannels = 2; 1.114 +#ifdef MOZ_SAMPLE_TYPE_FLOAT32 1.115 + // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8. 1.116 + static const float dmatrix[6][8][2]= { 1.117 + /*3*/{{0.5858f,0},{0.4142f,0.4142f},{0, 0.5858f}}, 1.118 + /*4*/{{0.4226f,0},{0, 0.4226f},{0.366f,0.2114f},{0.2114f,0.366f}}, 1.119 + /*5*/{{0.6510f,0},{0.4600f,0.4600f},{0, 0.6510f},{0.5636f,0.3254f},{0.3254f,0.5636f}}, 1.120 + /*6*/{{0.5290f,0},{0.3741f,0.3741f},{0, 0.5290f},{0.4582f,0.2645f},{0.2645f,0.4582f},{0.3741f,0.3741f}}, 1.121 + /*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}}, 1.122 + /*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}}, 1.123 + }; 1.124 + // Re-write the buffer with downmixed data 1.125 + for (uint32_t i = 0; i < frames; i++) { 1.126 + float sampL = 0.0; 1.127 + float sampR = 0.0; 1.128 + for (int j = 0; j < channels; j++) { 1.129 + sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0]; 1.130 + sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1]; 1.131 + } 1.132 + buffer[i*outChannels]=sampL; 1.133 + buffer[i*outChannels+1]=sampR; 1.134 + } 1.135 +#else 1.136 + // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8. 1.137 + // Coefficients in Q14. 1.138 + static const int16_t dmatrix[6][8][2]= { 1.139 + /*3*/{{9598, 0},{6786,6786},{0, 9598}}, 1.140 + /*4*/{{6925, 0},{0, 6925},{5997,3462},{3462,5997}}, 1.141 + /*5*/{{10663,0},{7540,7540},{0, 10663},{9234,5331},{5331,9234}}, 1.142 + /*6*/{{8668, 0},{6129,6129},{0, 8668},{7507,4335},{4335,7507},{6129,6129}}, 1.143 + /*7*/{{7459, 0},{5275,5275},{0, 7459},{6460,3731},{3731,6460},{4568,4568},{5275,5275}}, 1.144 + /*8*/{{6368, 0},{4502,4502},{0, 6368},{5514,3184},{3184,5514},{5514,3184},{3184,5514},{4502,4502}} 1.145 + }; 1.146 + // Re-write the buffer with downmixed data 1.147 + for (uint32_t i = 0; i < frames; i++) { 1.148 + int32_t sampL = 0; 1.149 + int32_t sampR = 0; 1.150 + for (int j = 0; j < channels; j++) { 1.151 + sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0]; 1.152 + sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1]; 1.153 + } 1.154 + sampL = (sampL + 8192)>>14; 1.155 + buffer[i*outChannels] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampL)); 1.156 + sampR = (sampR + 8192)>>14; 1.157 + buffer[i*outChannels+1] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampR)); 1.158 + } 1.159 +#endif 1.160 + return outChannels; 1.161 +} 1.162 + 1.163 +bool 1.164 +IsVideoContentType(const nsCString& aContentType) 1.165 +{ 1.166 + NS_NAMED_LITERAL_CSTRING(video, "video"); 1.167 + if (FindInReadable(video, aContentType)) { 1.168 + return true; 1.169 + } 1.170 + return false; 1.171 +} 1.172 + 1.173 +bool 1.174 +IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture, 1.175 + const nsIntSize& aDisplay) 1.176 +{ 1.177 + return 1.178 + aFrame.width <= PlanarYCbCrImage::MAX_DIMENSION && 1.179 + aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION && 1.180 + aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT && 1.181 + aFrame.width * aFrame.height != 0 && 1.182 + aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION && 1.183 + aPicture.x < PlanarYCbCrImage::MAX_DIMENSION && 1.184 + aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION && 1.185 + aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION && 1.186 + aPicture.y < PlanarYCbCrImage::MAX_DIMENSION && 1.187 + aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION && 1.188 + aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT && 1.189 + aPicture.width * aPicture.height != 0 && 1.190 + aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION && 1.191 + aDisplay.height <= PlanarYCbCrImage::MAX_DIMENSION && 1.192 + aDisplay.width * aDisplay.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT && 1.193 + aDisplay.width * aDisplay.height != 0; 1.194 +} 1.195 + 1.196 +} // end namespace mozilla