content/media/VideoUtils.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 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "VideoUtils.h"
michael@0 6 #include "MediaResource.h"
michael@0 7 #include "mozilla/dom/TimeRanges.h"
michael@0 8 #include "nsMathUtils.h"
michael@0 9 #include "nsSize.h"
michael@0 10 #include "VorbisUtils.h"
michael@0 11 #include "ImageContainer.h"
michael@0 12
michael@0 13 #include <stdint.h>
michael@0 14
michael@0 15 namespace mozilla {
michael@0 16
michael@0 17 using layers::PlanarYCbCrImage;
michael@0 18
michael@0 19 // Converts from number of audio frames to microseconds, given the specified
michael@0 20 // audio rate.
michael@0 21 CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) {
michael@0 22 return (CheckedInt64(aFrames) * USECS_PER_S) / aRate;
michael@0 23 }
michael@0 24
michael@0 25 // Converts from microseconds to number of audio frames, given the specified
michael@0 26 // audio rate.
michael@0 27 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate) {
michael@0 28 return (CheckedInt64(aUsecs) * aRate) / USECS_PER_S;
michael@0 29 }
michael@0 30
michael@0 31 nsresult SecondsToUsecs(double aSeconds, int64_t& aOutUsecs) {
michael@0 32 if (aSeconds * double(USECS_PER_S) > INT64_MAX) {
michael@0 33 return NS_ERROR_FAILURE;
michael@0 34 }
michael@0 35 aOutUsecs = int64_t(aSeconds * double(USECS_PER_S));
michael@0 36 return NS_OK;
michael@0 37 }
michael@0 38
michael@0 39 static int32_t ConditionDimension(float aValue)
michael@0 40 {
michael@0 41 // This will exclude NaNs and too-big values.
michael@0 42 if (aValue > 1.0 && aValue <= INT32_MAX)
michael@0 43 return int32_t(NS_round(aValue));
michael@0 44 return 0;
michael@0 45 }
michael@0 46
michael@0 47 void ScaleDisplayByAspectRatio(nsIntSize& aDisplay, float aAspectRatio)
michael@0 48 {
michael@0 49 if (aAspectRatio > 1.0) {
michael@0 50 // Increase the intrinsic width
michael@0 51 aDisplay.width = ConditionDimension(aAspectRatio * aDisplay.width);
michael@0 52 } else {
michael@0 53 // Increase the intrinsic height
michael@0 54 aDisplay.height = ConditionDimension(aDisplay.height / aAspectRatio);
michael@0 55 }
michael@0 56 }
michael@0 57
michael@0 58 static int64_t BytesToTime(int64_t offset, int64_t length, int64_t durationUs) {
michael@0 59 NS_ASSERTION(length > 0, "Must have positive length");
michael@0 60 double r = double(offset) / double(length);
michael@0 61 if (r > 1.0)
michael@0 62 r = 1.0;
michael@0 63 return int64_t(double(durationUs) * r);
michael@0 64 }
michael@0 65
michael@0 66 void GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStream,
michael@0 67 int64_t aDurationUsecs,
michael@0 68 mozilla::dom::TimeRanges* aOutBuffered)
michael@0 69 {
michael@0 70 // Nothing to cache if the media takes 0us to play.
michael@0 71 if (aDurationUsecs <= 0 || !aStream || !aOutBuffered)
michael@0 72 return;
michael@0 73
michael@0 74 // Special case completely cached files. This also handles local files.
michael@0 75 if (aStream->IsDataCachedToEndOfResource(0)) {
michael@0 76 aOutBuffered->Add(0, double(aDurationUsecs) / USECS_PER_S);
michael@0 77 return;
michael@0 78 }
michael@0 79
michael@0 80 int64_t totalBytes = aStream->GetLength();
michael@0 81
michael@0 82 // If we can't determine the total size, pretend that we have nothing
michael@0 83 // buffered. This will put us in a state of eternally-low-on-undecoded-data
michael@0 84 // which is not great, but about the best we can do.
michael@0 85 if (totalBytes <= 0)
michael@0 86 return;
michael@0 87
michael@0 88 int64_t startOffset = aStream->GetNextCachedData(0);
michael@0 89 while (startOffset >= 0) {
michael@0 90 int64_t endOffset = aStream->GetCachedDataEnd(startOffset);
michael@0 91 // Bytes [startOffset..endOffset] are cached.
michael@0 92 NS_ASSERTION(startOffset >= 0, "Integer underflow in GetBuffered");
michael@0 93 NS_ASSERTION(endOffset >= 0, "Integer underflow in GetBuffered");
michael@0 94
michael@0 95 int64_t startUs = BytesToTime(startOffset, totalBytes, aDurationUsecs);
michael@0 96 int64_t endUs = BytesToTime(endOffset, totalBytes, aDurationUsecs);
michael@0 97 if (startUs != endUs) {
michael@0 98 aOutBuffered->Add(double(startUs) / USECS_PER_S,
michael@0 99 double(endUs) / USECS_PER_S);
michael@0 100 }
michael@0 101 startOffset = aStream->GetNextCachedData(endOffset);
michael@0 102 }
michael@0 103 return;
michael@0 104 }
michael@0 105
michael@0 106 int DownmixAudioToStereo(mozilla::AudioDataValue* buffer,
michael@0 107 int channels, uint32_t frames)
michael@0 108 {
michael@0 109 int outChannels;
michael@0 110 outChannels = 2;
michael@0 111 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
michael@0 112 // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8.
michael@0 113 static const float dmatrix[6][8][2]= {
michael@0 114 /*3*/{{0.5858f,0},{0.4142f,0.4142f},{0, 0.5858f}},
michael@0 115 /*4*/{{0.4226f,0},{0, 0.4226f},{0.366f,0.2114f},{0.2114f,0.366f}},
michael@0 116 /*5*/{{0.6510f,0},{0.4600f,0.4600f},{0, 0.6510f},{0.5636f,0.3254f},{0.3254f,0.5636f}},
michael@0 117 /*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 118 /*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 119 /*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 120 };
michael@0 121 // Re-write the buffer with downmixed data
michael@0 122 for (uint32_t i = 0; i < frames; i++) {
michael@0 123 float sampL = 0.0;
michael@0 124 float sampR = 0.0;
michael@0 125 for (int j = 0; j < channels; j++) {
michael@0 126 sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0];
michael@0 127 sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1];
michael@0 128 }
michael@0 129 buffer[i*outChannels]=sampL;
michael@0 130 buffer[i*outChannels+1]=sampR;
michael@0 131 }
michael@0 132 #else
michael@0 133 // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8.
michael@0 134 // Coefficients in Q14.
michael@0 135 static const int16_t dmatrix[6][8][2]= {
michael@0 136 /*3*/{{9598, 0},{6786,6786},{0, 9598}},
michael@0 137 /*4*/{{6925, 0},{0, 6925},{5997,3462},{3462,5997}},
michael@0 138 /*5*/{{10663,0},{7540,7540},{0, 10663},{9234,5331},{5331,9234}},
michael@0 139 /*6*/{{8668, 0},{6129,6129},{0, 8668},{7507,4335},{4335,7507},{6129,6129}},
michael@0 140 /*7*/{{7459, 0},{5275,5275},{0, 7459},{6460,3731},{3731,6460},{4568,4568},{5275,5275}},
michael@0 141 /*8*/{{6368, 0},{4502,4502},{0, 6368},{5514,3184},{3184,5514},{5514,3184},{3184,5514},{4502,4502}}
michael@0 142 };
michael@0 143 // Re-write the buffer with downmixed data
michael@0 144 for (uint32_t i = 0; i < frames; i++) {
michael@0 145 int32_t sampL = 0;
michael@0 146 int32_t sampR = 0;
michael@0 147 for (int j = 0; j < channels; j++) {
michael@0 148 sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0];
michael@0 149 sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1];
michael@0 150 }
michael@0 151 sampL = (sampL + 8192)>>14;
michael@0 152 buffer[i*outChannels] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampL));
michael@0 153 sampR = (sampR + 8192)>>14;
michael@0 154 buffer[i*outChannels+1] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampR));
michael@0 155 }
michael@0 156 #endif
michael@0 157 return outChannels;
michael@0 158 }
michael@0 159
michael@0 160 bool
michael@0 161 IsVideoContentType(const nsCString& aContentType)
michael@0 162 {
michael@0 163 NS_NAMED_LITERAL_CSTRING(video, "video");
michael@0 164 if (FindInReadable(video, aContentType)) {
michael@0 165 return true;
michael@0 166 }
michael@0 167 return false;
michael@0 168 }
michael@0 169
michael@0 170 bool
michael@0 171 IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
michael@0 172 const nsIntSize& aDisplay)
michael@0 173 {
michael@0 174 return
michael@0 175 aFrame.width <= PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 176 aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 177 aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
michael@0 178 aFrame.width * aFrame.height != 0 &&
michael@0 179 aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 180 aPicture.x < PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 181 aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 182 aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 183 aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 184 aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 185 aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
michael@0 186 aPicture.width * aPicture.height != 0 &&
michael@0 187 aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 188 aDisplay.height <= PlanarYCbCrImage::MAX_DIMENSION &&
michael@0 189 aDisplay.width * aDisplay.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
michael@0 190 aDisplay.width * aDisplay.height != 0;
michael@0 191 }
michael@0 192
michael@0 193 } // end namespace mozilla

mercurial