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