1 /* 2 * Copyright (C) 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 package android.media.codec.cts; 18 19 import static org.junit.Assert.assertEquals; 20 21 import android.media.MediaCodec; 22 import android.media.MediaCodecList; 23 import android.media.MediaFormat; 24 import android.media.cts.MediaHeavyPresubmitTest; 25 import android.media.cts.TestArgs; 26 import android.platform.test.annotations.AppModeFull; 27 import android.util.Log; 28 29 import com.android.compatibility.common.util.ApiTest; 30 import com.android.compatibility.common.util.MediaUtils; 31 32 import org.junit.Assume; 33 import org.junit.Test; 34 import org.junit.runner.RunWith; 35 import org.junit.runners.Parameterized; 36 37 import java.io.File; 38 import java.nio.ByteBuffer; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collection; 42 import java.util.List; 43 44 /** 45 * Verification test for video encoder and decoder. 46 * 47 * A raw yv12 stream is encoded at various settings and written to an IVF 48 * file. Encoded stream bitrate and key frame interval are checked against target values. 49 * The stream is later decoded by video decoder to verify frames are decodable and to 50 * calculate PSNR values for various bitrates. 51 */ 52 @MediaHeavyPresubmitTest 53 @AppModeFull(reason = "TODO: evaluate and port to instant") 54 @RunWith(Parameterized.class) 55 public class VideoCodecTest extends VideoCodecTestBase { 56 57 private static final String ENCODED_IVF_BASE = "football"; 58 private static final String INPUT_YUV = null; 59 private static final String OUTPUT_YUV = SDCARD_DIR + File.separator + 60 ENCODED_IVF_BASE + "_out.yuv"; 61 62 // YUV stream properties. 63 private static final int WIDTH = 320; 64 private static final int HEIGHT = 240; 65 private static final int FPS = 30; 66 // Default encoding bitrate. 67 private static final int BITRATE = 400000; 68 // List of bitrates used in quality and basic bitrate tests. 69 private static final int[] TEST_BITRATES_SET = { 300000, 500000, 700000, 900000 }; 70 // Maximum allowed bitrate variation from the target value. 71 // Keep in sync with the variation at libmediandkjni/native_media_utils.h 72 // used in some tests along with BITRATE 73 private static final double MAX_BITRATE_VARIATION = 0.2; 74 // The tolerance varies by the bitrate, because lower bitrates interact with 75 // video quality standards introduced in Android 12. 76 private static final double[] MAX_CBR_BITRATE_VARIATIONS = { 0.20, 0.20, 0.20, 0.20 }; 77 private static final double[] MAX_VBR_BITRATE_VARIATIONS = { 0.50, 0.30, 0.30, 0.30 }; 78 // Average PSNR values for reference Google Video codec for the above bitrates. 79 private static final double[] REFERENCE_AVERAGE_PSNR = { 33.1, 35.2, 36.6, 37.8 }; 80 // Minimum PSNR values for reference Google Video codec for the above bitrates. 81 private static final double[] REFERENCE_MINIMUM_PSNR = { 25.9, 27.5, 28.4, 30.3 }; 82 // Maximum allowed average PSNR difference of encoder comparing to reference Google encoder. 83 private static final double MAX_AVERAGE_PSNR_DIFFERENCE = 2; 84 // Maximum allowed minimum PSNR difference of encoder comparing to reference Google encoder. 85 private static final double MAX_MINIMUM_PSNR_DIFFERENCE = 4; 86 // Maximum allowed average PSNR difference of the encoder running in a looper thread with 0 ms 87 // buffer dequeue timeout comparing to the encoder running in a callee's thread with 100 ms 88 // buffer dequeue timeout. 89 private static final double MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE = 1.5; 90 // Maximum allowed minimum PSNR difference of the encoder running in a looper thread 91 // comparing to the encoder running in a callee's thread. 92 private static final double MAX_ASYNC_MINIMUM_PSNR_DIFFERENCE = 2; 93 // Maximum allowed average key frame interval variation from the target value. 94 private static final int MAX_AVERAGE_KEYFRAME_INTERVAL_VARIATION = 1; 95 // Maximum allowed key frame interval variation from the target value. 96 private static final int MAX_KEYFRAME_INTERVAL_VARIATION = 3; 97 98 @Parameterized.Parameter(0) 99 public String mCodecName; 100 101 @Parameterized.Parameter(1) 102 public String mCodecMimeType; 103 104 @Parameterized.Parameter(2) 105 public int mBitRateMode; 106 prepareParamList(List<Object[]> exhaustiveArgsList)107 static private List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) { 108 final List<Object[]> argsList = new ArrayList<>(); 109 int argLength = exhaustiveArgsList.get(0).length; 110 for (Object[] arg : exhaustiveArgsList) { 111 String mediaType = (String)arg[0]; 112 if (TestArgs.shouldSkipMediaType(mediaType)) { 113 continue; 114 } 115 String[] encodersForMime = MediaUtils.getEncoderNamesForMime(mediaType); 116 for (String encoder : encodersForMime) { 117 if (TestArgs.shouldSkipCodec(encoder)) { 118 continue; 119 } 120 Object[] testArgs = new Object[argLength + 1]; 121 testArgs[0] = encoder; 122 System.arraycopy(arg, 0, testArgs, 1, argLength); 123 argsList.add(testArgs); 124 } 125 } 126 return argsList; 127 } 128 129 @Parameterized.Parameters(name = "{index}_{0}_{1}_{2}") input()130 public static Collection<Object[]> input() { 131 final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ 132 {VP8_MIME, VIDEO_ControlRateConstant}, 133 {VP8_MIME, VIDEO_ControlRateVariable}, 134 {VP9_MIME, VIDEO_ControlRateConstant}, 135 {VP9_MIME, VIDEO_ControlRateVariable}, 136 {AVC_MIME, VIDEO_ControlRateConstant}, 137 {AVC_MIME, VIDEO_ControlRateVariable}, 138 {HEVC_MIME, VIDEO_ControlRateConstant}, 139 {HEVC_MIME, VIDEO_ControlRateVariable}, 140 {AV1_MIME, VIDEO_ControlRateConstant}, 141 {AV1_MIME, VIDEO_ControlRateVariable}, 142 }); 143 return prepareParamList(exhaustiveArgsList); 144 } 145 146 /** 147 * A basic test for Video encoder. 148 * 149 * Encodes 9 seconds of raw stream with default configuration options, 150 * and then decodes it to verify the bitstream. 151 * Verifies the average bitrate is within allowed MAX_BITRATE_VARIATIONS[] of 152 * the target value. 153 */ internalTestBasic(String codecName, String codecMimeType, int bitRateMode)154 private void internalTestBasic(String codecName, String codecMimeType, int bitRateMode) 155 throws Exception { 156 int encodeSeconds = 9; 157 boolean skipped = true; 158 159 for (int i = 0; i < TEST_BITRATES_SET.length; i++) { 160 int targetBitrate = TEST_BITRATES_SET[i]; 161 162 EncoderOutputStreamParameters params = getDefaultEncodingParameters( 163 INPUT_YUV, 164 ENCODED_IVF_BASE, 165 codecName, 166 codecMimeType, 167 encodeSeconds, 168 WIDTH, 169 HEIGHT, 170 FPS, 171 bitRateMode, 172 targetBitrate, 173 true); 174 ArrayList<ByteBuffer> codecConfigs = new ArrayList<>(); 175 VideoEncodeOutput videoEncodeOutput = encode(params, codecConfigs); 176 ArrayList<MediaCodec.BufferInfo> bufInfo = videoEncodeOutput.bufferInfo; 177 if (bufInfo == null) { 178 continue; 179 } 180 skipped = false; 181 182 VideoEncodingStatistics statistics = computeEncodingStatistics(bufInfo); 183 184 if (params.bitrateType == VIDEO_ControlRateConstant) { 185 /* Constant bitrate -- variation applies to both over/under */ 186 double allowedVariance = MAX_CBR_BITRATE_VARIATIONS[i]; 187 assertEquals("Stream bitrate " + statistics.mAverageBitrate + 188 " differs from the target " + targetBitrate 189 + " by more than " + allowedVariance * targetBitrate, 190 targetBitrate, statistics.mAverageBitrate, 191 allowedVariance * targetBitrate); 192 } else if (params.bitrateType == VIDEO_ControlRateVariable 193 && statistics.mAverageBitrate > targetBitrate) { 194 /* VIDEO_ControlRateVariable mode only checks over-run */ 195 double allowedVariance = MAX_VBR_BITRATE_VARIATIONS[i]; 196 assertEquals("Stream bitrate " + statistics.mAverageBitrate 197 + " above target " + targetBitrate 198 + " by more than " + allowedVariance * targetBitrate, 199 targetBitrate, statistics.mAverageBitrate, 200 allowedVariance * targetBitrate); 201 } 202 203 decode(params.outputIvfFilename, null, codecMimeType, FPS, codecConfigs); 204 } 205 206 if (skipped) { 207 Log.i(TAG, "SKIPPING testBasic(): codec is not supported"); 208 } 209 } 210 211 /** 212 * Asynchronous encoding test for Video encoder. 213 * 214 * Encodes 9 seconds of raw stream using synchronous and asynchronous calls. 215 * Checks the PSNR difference between the encoded and decoded output and reference yuv input 216 * does not change much for two different ways of the encoder call. 217 */ internalTestAsyncEncoding(String codecName, String codecMimeType, int bitRateMode)218 private void internalTestAsyncEncoding(String codecName, String codecMimeType, int bitRateMode) 219 throws Exception { 220 int encodeSeconds = 9; 221 222 // First test the encoder running in a looper thread with buffer callbacks enabled. 223 boolean syncEncoding = false; 224 EncoderOutputStreamParameters params = getDefaultEncodingParameters( 225 INPUT_YUV, 226 ENCODED_IVF_BASE, 227 codecName, 228 codecMimeType, 229 encodeSeconds, 230 WIDTH, 231 HEIGHT, 232 FPS, 233 bitRateMode, 234 BITRATE, 235 syncEncoding); 236 ArrayList<ByteBuffer> codecConfigs = new ArrayList<>(); 237 VideoEncodeOutput videoEncodeOutput = encodeAsync(params, codecConfigs); 238 ArrayList<MediaCodec.BufferInfo> bufInfos = videoEncodeOutput.bufferInfo; 239 if (bufInfos == null) { 240 Log.i(TAG, "SKIPPING testAsyncEncoding(): no suitable encoder found"); 241 return; 242 } 243 computeEncodingStatistics(bufInfos); 244 decode(params.outputIvfFilename, OUTPUT_YUV, codecMimeType, FPS, codecConfigs); 245 VideoDecodingStatistics statisticsAsync = computeDecodingStatistics( 246 params.inputYuvFilename, "football_qvga.yuv", OUTPUT_YUV, 247 params.frameWidth, params.frameHeight); 248 249 250 // Test the encoder running in a callee's thread. 251 syncEncoding = true; 252 params = getDefaultEncodingParameters( 253 INPUT_YUV, 254 ENCODED_IVF_BASE, 255 codecName, 256 codecMimeType, 257 encodeSeconds, 258 WIDTH, 259 HEIGHT, 260 FPS, 261 bitRateMode, 262 BITRATE, 263 syncEncoding); 264 codecConfigs.clear(); 265 videoEncodeOutput = encode(params, codecConfigs); 266 bufInfos = videoEncodeOutput.bufferInfo; 267 if (bufInfos == null) { 268 Log.i(TAG, "SKIPPING testAsyncEncoding(): no suitable encoder found"); 269 return; 270 } 271 computeEncodingStatistics(bufInfos); 272 decode(params.outputIvfFilename, OUTPUT_YUV, codecMimeType, FPS, codecConfigs); 273 VideoDecodingStatistics statisticsSync = computeDecodingStatistics( 274 params.inputYuvFilename, "football_qvga.yuv", OUTPUT_YUV, 275 params.frameWidth, params.frameHeight); 276 277 // Check PSNR difference. 278 Log.d(TAG, "PSNR Average: Async: " + statisticsAsync.mAveragePSNR + 279 ". Sync: " + statisticsSync.mAveragePSNR); 280 Log.d(TAG, "PSNR Minimum: Async: " + statisticsAsync.mMinimumPSNR + 281 ". Sync: " + statisticsSync.mMinimumPSNR); 282 if ((Math.abs(statisticsAsync.mAveragePSNR - statisticsSync.mAveragePSNR) > 283 MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE) || 284 (Math.abs(statisticsAsync.mMinimumPSNR - statisticsSync.mMinimumPSNR) > 285 MAX_ASYNC_MINIMUM_PSNR_DIFFERENCE)) { 286 throw new RuntimeException("Difference between PSNRs for async and sync encoders"); 287 } 288 } 289 290 /** 291 * Check if MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME is honored. 292 * 293 * Encodes 9 seconds of raw stream and requests a sync frame every second (30 frames). 294 * The test does not verify the output stream. 295 */ internalTestSyncFrame(String codecName, String codecMimeType, int bitRateMode, boolean useNdk)296 private void internalTestSyncFrame(String codecName, String codecMimeType, int bitRateMode, 297 boolean useNdk) throws Exception { 298 int encodeSeconds = 9; 299 300 EncoderOutputStreamParameters params = getDefaultEncodingParameters( 301 INPUT_YUV, 302 ENCODED_IVF_BASE, 303 codecName, 304 codecMimeType, 305 encodeSeconds, 306 WIDTH, 307 HEIGHT, 308 FPS, 309 bitRateMode, 310 BITRATE, 311 true); 312 params.syncFrameInterval = encodeSeconds * FPS; 313 params.syncForceFrameInterval = FPS; 314 params.useNdk = useNdk; 315 VideoEncodeOutput videoEncodeOutput = encode(params); 316 ArrayList<MediaCodec.BufferInfo> bufInfo = videoEncodeOutput.bufferInfo; 317 if (bufInfo == null) { 318 Log.i(TAG, "SKIPPING testSyncFrame(): no suitable encoder found"); 319 return; 320 } 321 322 VideoEncodingStatistics statistics = computeEncodingStatistics(bufInfo); 323 324 // First check if we got expected number of key frames. 325 int actualKeyFrames = statistics.mKeyFrames.size(); 326 if (actualKeyFrames != encodeSeconds) { 327 throw new RuntimeException("Number of key frames " + actualKeyFrames + 328 " is different from the expected " + encodeSeconds); 329 } 330 331 // Check key frame intervals: 332 // Average value should be within +/- 1 frame of the target value, 333 // maximum value should not be greater than target value + 3, 334 // and minimum value should not be less that target value - 3. 335 if (Math.abs(statistics.mAverageKeyFrameInterval - FPS) > 336 MAX_AVERAGE_KEYFRAME_INTERVAL_VARIATION || 337 (statistics.mMaximumKeyFrameInterval - FPS > MAX_KEYFRAME_INTERVAL_VARIATION) || 338 (FPS - statistics.mMinimumKeyFrameInterval > MAX_KEYFRAME_INTERVAL_VARIATION)) { 339 throw new RuntimeException( 340 "Key frame intervals are different from the expected " + FPS); 341 } 342 } 343 344 /** 345 * Check if MediaCodec.PARAMETER_KEY_VIDEO_BITRATE is honored. 346 * 347 * Run the the encoder for 12 seconds. Request changes to the 348 * bitrate after 6 seconds and ensure the encoder responds. 349 */ internalTestDynamicBitrateChange(String codecName, String codecMimeType, int bitRateMode, boolean useNdk)350 private void internalTestDynamicBitrateChange(String codecName, String codecMimeType, 351 int bitRateMode, boolean useNdk) throws Exception { 352 int encodeSeconds = 12; // Encoding sequence duration in seconds. 353 int[] bitrateTargetValues = { 400000, 800000 }; // List of bitrates to test. 354 355 EncoderOutputStreamParameters params = getDefaultEncodingParameters( 356 INPUT_YUV, 357 ENCODED_IVF_BASE, 358 codecName, 359 codecMimeType, 360 encodeSeconds, 361 WIDTH, 362 HEIGHT, 363 FPS, 364 bitRateMode, 365 bitrateTargetValues[0], 366 true); 367 368 // Number of seconds for each bitrate 369 int stepSeconds = encodeSeconds / bitrateTargetValues.length; 370 // Fill the bitrates values. 371 params.bitrateSet = new int[encodeSeconds * FPS]; 372 for (int i = 0; i < bitrateTargetValues.length ; i++) { 373 Arrays.fill(params.bitrateSet, 374 i * encodeSeconds * FPS / bitrateTargetValues.length, 375 (i + 1) * encodeSeconds * FPS / bitrateTargetValues.length, 376 bitrateTargetValues[i]); 377 } 378 379 params.useNdk = useNdk; 380 VideoEncodeOutput videoEncodeOutput = encode(params); 381 ArrayList<MediaCodec.BufferInfo> bufInfo = videoEncodeOutput.bufferInfo; 382 if (bufInfo == null) { 383 Log.i(TAG, "SKIPPING testDynamicBitrateChange(): no suitable encoder found"); 384 return; 385 } 386 387 VideoEncodingStatistics statistics = computeEncodingStatistics(bufInfo); 388 389 // Calculate actual average bitrates for every [stepSeconds] second. 390 int[] bitrateActualValues = new int[bitrateTargetValues.length]; 391 for (int i = 0; i < bitrateTargetValues.length ; i++) { 392 bitrateActualValues[i] = 0; 393 for (int j = i * stepSeconds; j < (i + 1) * stepSeconds; j++) { 394 bitrateActualValues[i] += statistics.mBitrates.get(j); 395 } 396 bitrateActualValues[i] /= stepSeconds; 397 Log.d(TAG, "Actual bitrate for interval #" + i + " : " + bitrateActualValues[i] + 398 ". Target: " + bitrateTargetValues[i]); 399 400 // Compare actual bitrate values to make sure at least same increasing/decreasing 401 // order as the target bitrate values. 402 for (int j = 0; j < i; j++) { 403 long differenceTarget = bitrateTargetValues[i] - bitrateTargetValues[j]; 404 long differenceActual = bitrateActualValues[i] - bitrateActualValues[j]; 405 if (differenceTarget * differenceActual < 0) { 406 throw new RuntimeException("Target bitrates: " + 407 bitrateTargetValues[j] + " , " + bitrateTargetValues[i] + 408 ". Actual bitrates: " 409 + bitrateActualValues[j] + " , " + bitrateActualValues[i]); 410 } 411 } 412 } 413 } 414 415 /** 416 * Check if encoder and decoder can run simultaneously on different threads. 417 * 418 * Encodes and decodes 9 seconds of raw stream sequentially in CBR mode, 419 * and then run parallel encoding and decoding of the same streams. 420 * Compares average bitrate and PSNR for sequential and parallel runs. 421 */ internalTestParallelEncodingAndDecoding(String codecName, String codecMimeType)422 private void internalTestParallelEncodingAndDecoding(String codecName, String codecMimeType) 423 throws Exception { 424 // check for encoder up front, as by the time we detect lack of 425 // encoder support, we may have already started decoding. 426 MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS); 427 MediaFormat format = MediaFormat.createVideoFormat(codecMimeType, WIDTH, HEIGHT); 428 if (mcl.findEncoderForFormat(format) == null) { 429 Log.i(TAG, "SKIPPING testParallelEncodingAndDecoding(): no suitable encoder found"); 430 return; 431 } 432 433 int encodeSeconds = 9; 434 final int[] bitrate = new int[1]; 435 final double[] psnr = new double[1]; 436 final Exception[] exceptionEncoder = new Exception[1]; 437 final Exception[] exceptionDecoder = new Exception[1]; 438 final EncoderOutputStreamParameters params = getDefaultEncodingParameters( 439 INPUT_YUV, 440 ENCODED_IVF_BASE, 441 codecName, 442 codecMimeType, 443 encodeSeconds, 444 WIDTH, 445 HEIGHT, 446 FPS, 447 VIDEO_ControlRateConstant, 448 BITRATE, 449 true); 450 final String inputIvfFilename = params.outputIvfFilename; 451 final ArrayList<ByteBuffer> codecConfigs = new ArrayList<>(); 452 453 Runnable runEncoder = new Runnable() { 454 public void run() { 455 try { 456 ArrayList<MediaCodec.BufferInfo> bufInfo; 457 if (codecConfigs.isEmpty()) { 458 VideoEncodeOutput videoEncodeOutput = encode(params, codecConfigs); 459 bufInfo = videoEncodeOutput.bufferInfo; 460 } else { 461 VideoEncodeOutput videoEncodeOutput = encode(params); 462 bufInfo = videoEncodeOutput.bufferInfo; 463 } 464 VideoEncodingStatistics statistics = computeEncodingStatistics(bufInfo); 465 bitrate[0] = statistics.mAverageBitrate; 466 } catch (Exception e) { 467 Log.e(TAG, "Encoder error: " + e.toString()); 468 exceptionEncoder[0] = e; 469 } 470 } 471 }; 472 Runnable runDecoder = new Runnable() { 473 public void run() { 474 try { 475 decode(inputIvfFilename, OUTPUT_YUV, codecMimeType, FPS, codecConfigs); 476 VideoDecodingStatistics statistics = computeDecodingStatistics( 477 params.inputYuvFilename, "football_qvga.yuv", OUTPUT_YUV, 478 params.frameWidth, params.frameHeight); 479 psnr[0] = statistics.mAveragePSNR; 480 } catch (Exception e) { 481 Log.e(TAG, "Decoder error: " + e.toString()); 482 exceptionDecoder[0] = e; 483 } 484 } 485 }; 486 487 // Sequential encoding and decoding. 488 runEncoder.run(); 489 if (exceptionEncoder[0] != null) { 490 throw exceptionEncoder[0]; 491 } 492 int referenceBitrate = bitrate[0]; 493 runDecoder.run(); 494 if (exceptionDecoder[0] != null) { 495 throw exceptionDecoder[0]; 496 } 497 double referencePsnr = psnr[0]; 498 499 // Parallel encoding and decoding. 500 params.outputIvfFilename = SDCARD_DIR + File.separator + ENCODED_IVF_BASE + "_copy.ivf"; 501 Thread threadEncoder = new Thread(runEncoder); 502 Thread threadDecoder = new Thread(runDecoder); 503 threadEncoder.start(); 504 threadDecoder.start(); 505 threadEncoder.join(); 506 threadDecoder.join(); 507 if (exceptionEncoder[0] != null) { 508 throw exceptionEncoder[0]; 509 } 510 if (exceptionDecoder[0] != null) { 511 throw exceptionDecoder[0]; 512 } 513 514 // Compare bitrates and PSNRs for sequential and parallel cases. 515 Log.d(TAG, "Sequential bitrate: " + referenceBitrate + ". PSNR: " + referencePsnr); 516 Log.d(TAG, "Parallel bitrate: " + bitrate[0] + ". PSNR: " + psnr[0]); 517 assertEquals("Bitrate for sequenatial encoding" + referenceBitrate + 518 " is different from parallel encoding " + bitrate[0], 519 referenceBitrate, bitrate[0], MAX_BITRATE_VARIATION * referenceBitrate); 520 assertEquals("PSNR for sequenatial encoding" + referencePsnr + 521 " is different from parallel encoding " + psnr[0], 522 referencePsnr, psnr[0], MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE); 523 } 524 525 526 /** 527 * Check the encoder quality for various bitrates by calculating PSNR 528 * 529 * Run the the encoder for 9 seconds for each bitrate and calculate PSNR 530 * for each encoded stream. 531 * Video streams with higher bitrates should have higher PSNRs. 532 * Also compares average and minimum PSNR of codec with PSNR values of reference Google codec. 533 */ internalTestEncoderQuality(String codecName, String codecMimeType, int bitRateMode)534 private void internalTestEncoderQuality(String codecName, String codecMimeType, int bitRateMode) 535 throws Exception { 536 int encodeSeconds = 9; // Encoding sequence duration in seconds for each bitrate. 537 double[] psnrPlatformCodecAverage = new double[TEST_BITRATES_SET.length]; 538 double[] psnrPlatformCodecMin = new double[TEST_BITRATES_SET.length]; 539 boolean[] completed = new boolean[TEST_BITRATES_SET.length]; 540 boolean skipped = true; 541 542 // Run platform specific encoder for different bitrates 543 // and compare PSNR of codec with PSNR of reference Google codec. 544 for (int i = 0; i < TEST_BITRATES_SET.length; i++) { 545 EncoderOutputStreamParameters params = getDefaultEncodingParameters( 546 INPUT_YUV, 547 ENCODED_IVF_BASE, 548 codecName, 549 codecMimeType, 550 encodeSeconds, 551 WIDTH, 552 HEIGHT, 553 FPS, 554 bitRateMode, 555 TEST_BITRATES_SET[i], 556 true); 557 ArrayList<ByteBuffer> codecConfigs = new ArrayList<>(); 558 if (encode(params, codecConfigs) == null) { 559 // parameters not supported, try other bitrates 560 completed[i] = false; 561 continue; 562 } 563 completed[i] = true; 564 skipped = false; 565 566 decode(params.outputIvfFilename, OUTPUT_YUV, codecMimeType, FPS, codecConfigs); 567 VideoDecodingStatistics statistics = computeDecodingStatistics( 568 params.inputYuvFilename, "football_qvga.yuv", OUTPUT_YUV, 569 params.frameWidth, params.frameHeight); 570 psnrPlatformCodecAverage[i] = statistics.mAveragePSNR; 571 psnrPlatformCodecMin[i] = statistics.mMinimumPSNR; 572 } 573 574 if (skipped) { 575 Log.i(TAG, "SKIPPING testEncoderQuality(): no bitrates supported"); 576 return; 577 } 578 579 // First do an initial check - higher bitrates should results in higher PSNR. 580 for (int i = 1; i < TEST_BITRATES_SET.length ; i++) { 581 if (!completed[i]) { 582 continue; 583 } 584 for (int j = 0; j < i; j++) { 585 if (!completed[j]) { 586 continue; 587 } 588 double differenceBitrate = TEST_BITRATES_SET[i] - TEST_BITRATES_SET[j]; 589 double differencePSNR = psnrPlatformCodecAverage[i] - psnrPlatformCodecAverage[j]; 590 if (differenceBitrate * differencePSNR < 0) { 591 throw new RuntimeException("Target bitrates: " + 592 TEST_BITRATES_SET[j] + ", " + TEST_BITRATES_SET[i] + 593 ". Actual PSNRs: " 594 + psnrPlatformCodecAverage[j] + ", " + psnrPlatformCodecAverage[i]); 595 } 596 } 597 } 598 599 // Then compare average and minimum PSNR of platform codec with reference Google codec - 600 // average PSNR for platform codec should be no more than 2 dB less than reference PSNR 601 // and minumum PSNR - no more than 4 dB less than reference minimum PSNR. 602 // These PSNR difference numbers are arbitrary for now, will need further estimation 603 // when more devices with HW video codec will appear. 604 for (int i = 0; i < TEST_BITRATES_SET.length ; i++) { 605 if (!completed[i]) { 606 continue; 607 } 608 609 Log.d(TAG, "Bitrate " + TEST_BITRATES_SET[i]); 610 Log.d(TAG, "Reference: Average: " + REFERENCE_AVERAGE_PSNR[i] + ". Minimum: " + 611 REFERENCE_MINIMUM_PSNR[i]); 612 Log.d(TAG, "Platform: Average: " + psnrPlatformCodecAverage[i] + ". Minimum: " + 613 psnrPlatformCodecMin[i]); 614 if (psnrPlatformCodecAverage[i] < REFERENCE_AVERAGE_PSNR[i] - 615 MAX_AVERAGE_PSNR_DIFFERENCE) { 616 throw new RuntimeException("Low average PSNR " + psnrPlatformCodecAverage[i] + 617 " comparing to reference PSNR " + REFERENCE_AVERAGE_PSNR[i] + 618 " for bitrate " + TEST_BITRATES_SET[i]); 619 } 620 if (psnrPlatformCodecMin[i] < REFERENCE_MINIMUM_PSNR[i] - 621 MAX_MINIMUM_PSNR_DIFFERENCE) { 622 throw new RuntimeException("Low minimum PSNR " + psnrPlatformCodecMin[i] + 623 " comparing to reference PSNR " + REFERENCE_MINIMUM_PSNR[i] + 624 " for bitrate " + TEST_BITRATES_SET[i]); 625 } 626 } 627 } 628 629 @ApiTest(apis = {"android.media.MediaFormat#KEY_BITRATE_MODE", 630 "android.media.MediaFormat#KEY_BIT_RATE", 631 "android.media.MediaFormat#KEY_COLOR_FORMAT", 632 "android.media.MediaFormat#KEY_FRAME_RATE", 633 "android.media.MediaFormat#KEY_I_FRAME_INTERVAL"}) 634 @Test testBasic()635 public void testBasic() throws Exception { 636 internalTestBasic(mCodecName, mCodecMimeType, mBitRateMode); 637 } 638 639 @ApiTest(apis = {"android.media.MediaFormat#KEY_BITRATE_MODE", 640 "android.media.MediaFormat#KEY_BIT_RATE", 641 "android.media.MediaFormat#KEY_COLOR_FORMAT", 642 "android.media.MediaFormat#KEY_FRAME_RATE", 643 "android.media.MediaFormat#KEY_I_FRAME_INTERVAL"}) 644 @Test testAsyncEncode()645 public void testAsyncEncode() throws Exception { 646 internalTestAsyncEncoding(mCodecName, mCodecMimeType, mBitRateMode); 647 } 648 649 @ApiTest(apis = "android.media.MediaCodec#PARAMETER_KEY_REQUEST_SYNC_FRAME") 650 @Test testSyncFrame()651 public void testSyncFrame() throws Exception { 652 internalTestSyncFrame(mCodecName, mCodecMimeType, mBitRateMode, false); 653 } 654 655 @ApiTest(apis = "android.media.MediaCodec#PARAMETER_KEY_REQUEST_SYNC_FRAME") 656 @Test testSyncFrameNdk()657 public void testSyncFrameNdk() throws Exception { 658 internalTestSyncFrame(mCodecName, mCodecMimeType, mBitRateMode, true); 659 } 660 661 @ApiTest(apis = "android.media.MediaCodec#PARAMETER_KEY_VIDEO_BITRATE") 662 @Test testDynamicBitrateChange()663 public void testDynamicBitrateChange() throws Exception { 664 internalTestDynamicBitrateChange(mCodecName, mCodecMimeType, mBitRateMode, false); 665 } 666 667 @ApiTest(apis = "android.media.MediaCodec#PARAMETER_KEY_VIDEO_BITRATE") 668 @Test testDynamicBitrateChangeNdk()669 public void testDynamicBitrateChangeNdk() throws Exception { 670 internalTestDynamicBitrateChange(mCodecName, mCodecMimeType, mBitRateMode, true); 671 } 672 673 @ApiTest(apis = {"android.media.MediaFormat#KEY_BITRATE_MODE", 674 "android.media.MediaFormat#KEY_BIT_RATE", 675 "android.media.MediaFormat#KEY_COLOR_FORMAT", 676 "android.media.MediaFormat#KEY_FRAME_RATE", 677 "android.media.MediaFormat#KEY_I_FRAME_INTERVAL"}) 678 @Test testEncoderQuality()679 public void testEncoderQuality() throws Exception { 680 internalTestEncoderQuality(mCodecName, mCodecMimeType, mBitRateMode); 681 } 682 683 @ApiTest(apis = {"android.media.MediaFormat#KEY_BITRATE_MODE", 684 "android.media.MediaFormat#KEY_BIT_RATE", 685 "android.media.MediaFormat#KEY_COLOR_FORMAT", 686 "android.media.MediaFormat#KEY_FRAME_RATE", 687 "android.media.MediaFormat#KEY_I_FRAME_INTERVAL"}) 688 @Test testParallelEncodingAndDecoding()689 public void testParallelEncodingAndDecoding() throws Exception { 690 Assume.assumeTrue("Parallel Encode Decode test is run only for VBR mode", 691 mBitRateMode == VIDEO_ControlRateVariable); 692 internalTestParallelEncodingAndDecoding(mCodecName, mCodecMimeType); 693 } 694 } 695 696