1 /*
2 * Copyright 2013, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "VideoFormats"
19 #include <utils/Log.h>
20
21 #include "VideoFormats.h"
22
23 #include <media/stagefright/foundation/ADebug.h>
24
25 namespace android {
26
27 // static
28 const VideoFormats::config_t VideoFormats::mResolutionTable[][32] = {
29 {
30 // CEA Resolutions
31 { 640, 480, 60, false, 0, 0},
32 { 720, 480, 60, false, 0, 0},
33 { 720, 480, 60, true, 0, 0},
34 { 720, 576, 50, false, 0, 0},
35 { 720, 576, 50, true, 0, 0},
36 { 1280, 720, 30, false, 0, 0},
37 { 1280, 720, 60, false, 0, 0},
38 { 1920, 1080, 30, false, 0, 0},
39 { 1920, 1080, 60, false, 0, 0},
40 { 1920, 1080, 60, true, 0, 0},
41 { 1280, 720, 25, false, 0, 0},
42 { 1280, 720, 50, false, 0, 0},
43 { 1920, 1080, 25, false, 0, 0},
44 { 1920, 1080, 50, false, 0, 0},
45 { 1920, 1080, 50, true, 0, 0},
46 { 1280, 720, 24, false, 0, 0},
47 { 1920, 1080, 24, false, 0, 0},
48 { 0, 0, 0, false, 0, 0},
49 { 0, 0, 0, false, 0, 0},
50 { 0, 0, 0, false, 0, 0},
51 { 0, 0, 0, false, 0, 0},
52 { 0, 0, 0, false, 0, 0},
53 { 0, 0, 0, false, 0, 0},
54 { 0, 0, 0, false, 0, 0},
55 { 0, 0, 0, false, 0, 0},
56 { 0, 0, 0, false, 0, 0},
57 { 0, 0, 0, false, 0, 0},
58 { 0, 0, 0, false, 0, 0},
59 { 0, 0, 0, false, 0, 0},
60 { 0, 0, 0, false, 0, 0},
61 { 0, 0, 0, false, 0, 0},
62 { 0, 0, 0, false, 0, 0},
63 },
64 {
65 // VESA Resolutions
66 { 800, 600, 30, false, 0, 0},
67 { 800, 600, 60, false, 0, 0},
68 { 1024, 768, 30, false, 0, 0},
69 { 1024, 768, 60, false, 0, 0},
70 { 1152, 864, 30, false, 0, 0},
71 { 1152, 864, 60, false, 0, 0},
72 { 1280, 768, 30, false, 0, 0},
73 { 1280, 768, 60, false, 0, 0},
74 { 1280, 800, 30, false, 0, 0},
75 { 1280, 800, 60, false, 0, 0},
76 { 1360, 768, 30, false, 0, 0},
77 { 1360, 768, 60, false, 0, 0},
78 { 1366, 768, 30, false, 0, 0},
79 { 1366, 768, 60, false, 0, 0},
80 { 1280, 1024, 30, false, 0, 0},
81 { 1280, 1024, 60, false, 0, 0},
82 { 1400, 1050, 30, false, 0, 0},
83 { 1400, 1050, 60, false, 0, 0},
84 { 1440, 900, 30, false, 0, 0},
85 { 1440, 900, 60, false, 0, 0},
86 { 1600, 900, 30, false, 0, 0},
87 { 1600, 900, 60, false, 0, 0},
88 { 1600, 1200, 30, false, 0, 0},
89 { 1600, 1200, 60, false, 0, 0},
90 { 1680, 1024, 30, false, 0, 0},
91 { 1680, 1024, 60, false, 0, 0},
92 { 1680, 1050, 30, false, 0, 0},
93 { 1680, 1050, 60, false, 0, 0},
94 { 1920, 1200, 30, false, 0, 0},
95 { 1920, 1200, 60, false, 0, 0},
96 { 0, 0, 0, false, 0, 0},
97 { 0, 0, 0, false, 0, 0},
98 },
99 {
100 // HH Resolutions
101 { 800, 480, 30, false, 0, 0},
102 { 800, 480, 60, false, 0, 0},
103 { 854, 480, 30, false, 0, 0},
104 { 854, 480, 60, false, 0, 0},
105 { 864, 480, 30, false, 0, 0},
106 { 864, 480, 60, false, 0, 0},
107 { 640, 360, 30, false, 0, 0},
108 { 640, 360, 60, false, 0, 0},
109 { 960, 540, 30, false, 0, 0},
110 { 960, 540, 60, false, 0, 0},
111 { 848, 480, 30, false, 0, 0},
112 { 848, 480, 60, false, 0, 0},
113 { 0, 0, 0, false, 0, 0},
114 { 0, 0, 0, false, 0, 0},
115 { 0, 0, 0, false, 0, 0},
116 { 0, 0, 0, false, 0, 0},
117 { 0, 0, 0, false, 0, 0},
118 { 0, 0, 0, false, 0, 0},
119 { 0, 0, 0, false, 0, 0},
120 { 0, 0, 0, false, 0, 0},
121 { 0, 0, 0, false, 0, 0},
122 { 0, 0, 0, false, 0, 0},
123 { 0, 0, 0, false, 0, 0},
124 { 0, 0, 0, false, 0, 0},
125 { 0, 0, 0, false, 0, 0},
126 { 0, 0, 0, false, 0, 0},
127 { 0, 0, 0, false, 0, 0},
128 { 0, 0, 0, false, 0, 0},
129 { 0, 0, 0, false, 0, 0},
130 { 0, 0, 0, false, 0, 0},
131 { 0, 0, 0, false, 0, 0},
132 { 0, 0, 0, false, 0, 0},
133 }
134 };
135
VideoFormats()136 VideoFormats::VideoFormats() {
137 memcpy(mConfigs, mResolutionTable, sizeof(mConfigs));
138
139 for (size_t i = 0; i < kNumResolutionTypes; ++i) {
140 mResolutionEnabled[i] = 0;
141 }
142
143 setNativeResolution(RESOLUTION_CEA, 0); // default to 640x480 p60
144 }
145
setNativeResolution(ResolutionType type,size_t index)146 void VideoFormats::setNativeResolution(ResolutionType type, size_t index) {
147 CHECK_LT(type, kNumResolutionTypes);
148 CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
149
150 mNativeType = type;
151 mNativeIndex = index;
152
153 setResolutionEnabled(type, index);
154 }
155
getNativeResolution(ResolutionType * type,size_t * index) const156 void VideoFormats::getNativeResolution(
157 ResolutionType *type, size_t *index) const {
158 *type = mNativeType;
159 *index = mNativeIndex;
160 }
161
disableAll()162 void VideoFormats::disableAll() {
163 for (size_t i = 0; i < kNumResolutionTypes; ++i) {
164 mResolutionEnabled[i] = 0;
165 for (size_t j = 0; j < 32; j++) {
166 mConfigs[i][j].profile = mConfigs[i][j].level = 0;
167 }
168 }
169 }
170
enableAll()171 void VideoFormats::enableAll() {
172 for (size_t i = 0; i < kNumResolutionTypes; ++i) {
173 mResolutionEnabled[i] = 0xffffffff;
174 for (size_t j = 0; j < 32; j++) {
175 mConfigs[i][j].profile = (1ul << PROFILE_CBP);
176 mConfigs[i][j].level = (1ul << LEVEL_31);
177 }
178 }
179 }
180
enableResolutionUpto(ResolutionType type,size_t index,ProfileType profile,LevelType level)181 void VideoFormats::enableResolutionUpto(
182 ResolutionType type, size_t index,
183 ProfileType profile, LevelType level) {
184 size_t width, height, fps, score;
185 bool interlaced;
186 if (!GetConfiguration(type, index, &width, &height,
187 &fps, &interlaced)) {
188 ALOGE("Maximum resolution not found!");
189 return;
190 }
191 score = width * height * fps * (!interlaced + 1);
192 for (size_t i = 0; i < kNumResolutionTypes; ++i) {
193 for (size_t j = 0; j < 32; j++) {
194 if (GetConfiguration((ResolutionType)i, j,
195 &width, &height, &fps, &interlaced)
196 && score >= width * height * fps * (!interlaced + 1)) {
197 setResolutionEnabled((ResolutionType)i, j);
198 setProfileLevel((ResolutionType)i, j, profile, level);
199 }
200 }
201 }
202 }
203
setResolutionEnabled(ResolutionType type,size_t index,bool enabled)204 void VideoFormats::setResolutionEnabled(
205 ResolutionType type, size_t index, bool enabled) {
206 CHECK_LT(type, kNumResolutionTypes);
207 CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
208
209 if (enabled) {
210 mResolutionEnabled[type] |= (1ul << index);
211 mConfigs[type][index].profile = (1ul << PROFILE_CBP);
212 mConfigs[type][index].level = (1ul << LEVEL_31);
213 } else {
214 mResolutionEnabled[type] &= ~(1ul << index);
215 mConfigs[type][index].profile = 0;
216 mConfigs[type][index].level = 0;
217 }
218 }
219
setProfileLevel(ResolutionType type,size_t index,ProfileType profile,LevelType level)220 void VideoFormats::setProfileLevel(
221 ResolutionType type, size_t index,
222 ProfileType profile, LevelType level) {
223 CHECK_LT(type, kNumResolutionTypes);
224 CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
225
226 mConfigs[type][index].profile = (1ul << profile);
227 mConfigs[type][index].level = (1ul << level);
228 }
229
getProfileLevel(ResolutionType type,size_t index,ProfileType * profile,LevelType * level) const230 void VideoFormats::getProfileLevel(
231 ResolutionType type, size_t index,
232 ProfileType *profile, LevelType *level) const{
233 CHECK_LT(type, kNumResolutionTypes);
234 CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
235
236 int i, bestProfile = -1, bestLevel = -1;
237
238 for (i = 0; i < kNumProfileTypes; ++i) {
239 if (mConfigs[type][index].profile & (1ul << i)) {
240 bestProfile = i;
241 }
242 }
243
244 for (i = 0; i < kNumLevelTypes; ++i) {
245 if (mConfigs[type][index].level & (1ul << i)) {
246 bestLevel = i;
247 }
248 }
249
250 if (bestProfile == -1 || bestLevel == -1) {
251 ALOGE("Profile or level not set for resolution type %d, index %zu",
252 type, index);
253 bestProfile = PROFILE_CBP;
254 bestLevel = LEVEL_31;
255 }
256
257 *profile = (ProfileType) bestProfile;
258 *level = (LevelType) bestLevel;
259 }
260
isResolutionEnabled(ResolutionType type,size_t index) const261 bool VideoFormats::isResolutionEnabled(
262 ResolutionType type, size_t index) const {
263 CHECK_LT(type, kNumResolutionTypes);
264 CHECK(GetConfiguration(type, index, NULL, NULL, NULL, NULL));
265
266 return mResolutionEnabled[type] & (1ul << index);
267 }
268
269 // static
GetConfiguration(ResolutionType type,size_t index,size_t * width,size_t * height,size_t * framesPerSecond,bool * interlaced)270 bool VideoFormats::GetConfiguration(
271 ResolutionType type,
272 size_t index,
273 size_t *width, size_t *height, size_t *framesPerSecond,
274 bool *interlaced) {
275 CHECK_LT(type, kNumResolutionTypes);
276
277 if (index >= 32) {
278 return false;
279 }
280
281 const config_t *config = &mResolutionTable[type][index];
282
283 if (config->width == 0) {
284 return false;
285 }
286
287 if (width) {
288 *width = config->width;
289 }
290
291 if (height) {
292 *height = config->height;
293 }
294
295 if (framesPerSecond) {
296 *framesPerSecond = config->framesPerSecond;
297 }
298
299 if (interlaced) {
300 *interlaced = config->interlaced;
301 }
302
303 return true;
304 }
305
parseH264Codec(const char * spec)306 bool VideoFormats::parseH264Codec(const char *spec) {
307 unsigned profile, level, res[3];
308
309 if (sscanf(
310 spec,
311 "%02x %02x %08X %08X %08X",
312 &profile,
313 &level,
314 &res[0],
315 &res[1],
316 &res[2]) != 5) {
317 return false;
318 }
319
320 for (size_t i = 0; i < kNumResolutionTypes; ++i) {
321 for (size_t j = 0; j < 32; ++j) {
322 if (res[i] & (1ul << j)){
323 mResolutionEnabled[i] |= (1ul << j);
324 if (profile > mConfigs[i][j].profile) {
325 // prefer higher profile (even if level is lower)
326 mConfigs[i][j].profile = profile;
327 mConfigs[i][j].level = level;
328 } else if (profile == mConfigs[i][j].profile &&
329 level > mConfigs[i][j].level) {
330 mConfigs[i][j].level = level;
331 }
332 }
333 }
334 }
335
336 return true;
337 }
338
339 // static
GetProfileLevel(ProfileType profile,LevelType level,unsigned * profileIdc,unsigned * levelIdc,unsigned * constraintSet)340 bool VideoFormats::GetProfileLevel(
341 ProfileType profile, LevelType level, unsigned *profileIdc,
342 unsigned *levelIdc, unsigned *constraintSet) {
343 CHECK_LT(profile, kNumProfileTypes);
344 CHECK_LT(level, kNumLevelTypes);
345
346 static const unsigned kProfileIDC[kNumProfileTypes] = {
347 66, // PROFILE_CBP
348 100, // PROFILE_CHP
349 };
350
351 static const unsigned kLevelIDC[kNumLevelTypes] = {
352 31, // LEVEL_31
353 32, // LEVEL_32
354 40, // LEVEL_40
355 41, // LEVEL_41
356 42, // LEVEL_42
357 };
358
359 static const unsigned kConstraintSet[kNumProfileTypes] = {
360 0xc0, // PROFILE_CBP
361 0x0c, // PROFILE_CHP
362 };
363
364 if (profileIdc) {
365 *profileIdc = kProfileIDC[profile];
366 }
367
368 if (levelIdc) {
369 *levelIdc = kLevelIDC[level];
370 }
371
372 if (constraintSet) {
373 *constraintSet = kConstraintSet[profile];
374 }
375
376 return true;
377 }
378
parseFormatSpec(const char * spec)379 bool VideoFormats::parseFormatSpec(const char *spec) {
380 CHECK_EQ(kNumResolutionTypes, 3);
381
382 disableAll();
383
384 unsigned native, dummy;
385 size_t size = strlen(spec);
386 size_t offset = 0;
387
388 if (sscanf(spec, "%02x %02x ", &native, &dummy) != 2) {
389 return false;
390 }
391
392 offset += 6; // skip native and preferred-display-mode-supported
393 CHECK_LE(offset + 58, size);
394 while (offset < size) {
395 parseH264Codec(spec + offset);
396 offset += 60; // skip H.264-codec + ", "
397 }
398
399 mNativeIndex = native >> 3;
400 mNativeType = (ResolutionType)(native & 7);
401
402 bool success;
403 if (mNativeType >= kNumResolutionTypes) {
404 success = false;
405 } else {
406 success = GetConfiguration(
407 mNativeType, mNativeIndex, NULL, NULL, NULL, NULL);
408 }
409
410 if (!success) {
411 ALOGW("sink advertised an illegal native resolution, fortunately "
412 "this value is ignored for the time being...");
413 }
414
415 return true;
416 }
417
getFormatSpec(bool forM4Message) const418 AString VideoFormats::getFormatSpec(bool forM4Message) const {
419 CHECK_EQ(kNumResolutionTypes, 3);
420
421 // wfd_video_formats:
422 // 1 byte "native"
423 // 1 byte "preferred-display-mode-supported" 0 or 1
424 // one or more avc codec structures
425 // 1 byte profile
426 // 1 byte level
427 // 4 byte CEA mask
428 // 4 byte VESA mask
429 // 4 byte HH mask
430 // 1 byte latency
431 // 2 byte min-slice-slice
432 // 2 byte slice-enc-params
433 // 1 byte framerate-control-support
434 // max-hres (none or 2 byte)
435 // max-vres (none or 2 byte)
436
437 return AStringPrintf(
438 "%02x 00 %02x %02x %08x %08x %08x 00 0000 0000 00 none none",
439 forM4Message ? 0x00 : ((mNativeIndex << 3) | mNativeType),
440 mConfigs[mNativeType][mNativeIndex].profile,
441 mConfigs[mNativeType][mNativeIndex].level,
442 mResolutionEnabled[0],
443 mResolutionEnabled[1],
444 mResolutionEnabled[2]);
445 }
446
447 // static
PickBestFormat(const VideoFormats & sinkSupported,const VideoFormats & sourceSupported,ResolutionType * chosenType,size_t * chosenIndex,ProfileType * chosenProfile,LevelType * chosenLevel)448 bool VideoFormats::PickBestFormat(
449 const VideoFormats &sinkSupported,
450 const VideoFormats &sourceSupported,
451 ResolutionType *chosenType,
452 size_t *chosenIndex,
453 ProfileType *chosenProfile,
454 LevelType *chosenLevel) {
455 #if 0
456 // Support for the native format is a great idea, the spec includes
457 // these features, but nobody supports it and the tests don't validate it.
458
459 ResolutionType nativeType;
460 size_t nativeIndex;
461 sinkSupported.getNativeResolution(&nativeType, &nativeIndex);
462 if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
463 if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
464 ALOGI("Choosing sink's native resolution");
465 *chosenType = nativeType;
466 *chosenIndex = nativeIndex;
467 return true;
468 }
469 } else {
470 ALOGW("Sink advertised native resolution that it doesn't "
471 "actually support... ignoring");
472 }
473
474 sourceSupported.getNativeResolution(&nativeType, &nativeIndex);
475 if (sourceSupported.isResolutionEnabled(nativeType, nativeIndex)) {
476 if (sinkSupported.isResolutionEnabled(nativeType, nativeIndex)) {
477 ALOGI("Choosing source's native resolution");
478 *chosenType = nativeType;
479 *chosenIndex = nativeIndex;
480 return true;
481 }
482 } else {
483 ALOGW("Source advertised native resolution that it doesn't "
484 "actually support... ignoring");
485 }
486 #endif
487
488 bool first = true;
489 uint32_t bestScore = 0;
490 size_t bestType = 0;
491 size_t bestIndex = 0;
492 for (size_t i = 0; i < kNumResolutionTypes; ++i) {
493 for (size_t j = 0; j < 32; ++j) {
494 size_t width, height, framesPerSecond;
495 bool interlaced;
496 if (!GetConfiguration(
497 (ResolutionType)i,
498 j,
499 &width, &height, &framesPerSecond, &interlaced)) {
500 break;
501 }
502
503 if (!sinkSupported.isResolutionEnabled((ResolutionType)i, j)
504 || !sourceSupported.isResolutionEnabled(
505 (ResolutionType)i, j)) {
506 continue;
507 }
508
509 ALOGV("type %zu, index %zu, %zu x %zu %c%zu supported",
510 i, j, width, height, interlaced ? 'i' : 'p', framesPerSecond);
511
512 uint32_t score = width * height * framesPerSecond;
513 if (!interlaced) {
514 score *= 2;
515 }
516
517 if (first || score > bestScore) {
518 bestScore = score;
519 bestType = i;
520 bestIndex = j;
521
522 first = false;
523 }
524 }
525 }
526
527 if (first) {
528 return false;
529 }
530
531 *chosenType = (ResolutionType)bestType;
532 *chosenIndex = bestIndex;
533
534 // Pick the best profile/level supported by both sink and source.
535 ProfileType srcProfile, sinkProfile;
536 LevelType srcLevel, sinkLevel;
537 sourceSupported.getProfileLevel(
538 (ResolutionType)bestType, bestIndex,
539 &srcProfile, &srcLevel);
540 sinkSupported.getProfileLevel(
541 (ResolutionType)bestType, bestIndex,
542 &sinkProfile, &sinkLevel);
543 *chosenProfile = srcProfile < sinkProfile ? srcProfile : sinkProfile;
544 *chosenLevel = srcLevel < sinkLevel ? srcLevel : sinkLevel;
545
546 return true;
547 }
548
549 } // namespace android
550
551