1 /* 2 * Copyright (C) 2018 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.decoder.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 24 import android.app.Instrumentation; 25 import android.content.res.AssetFileDescriptor; 26 import android.content.res.Resources; 27 import android.media.MediaCodec; 28 import android.media.MediaCodecInfo; 29 import android.media.MediaCodecList; 30 import android.media.MediaExtractor; 31 import android.media.MediaFormat; 32 import android.media.cts.TestUtils; 33 import android.media.decoder.cts.DecoderTest.AudioParameter; 34 import android.media.decoder.cts.DecoderTestAacDrc.DrcParams; 35 import android.os.Build; 36 import android.os.Bundle; 37 import android.platform.test.annotations.AppModeFull; 38 import android.util.Log; 39 40 import androidx.test.InstrumentationRegistry; 41 42 import com.android.compatibility.common.util.ApiLevelUtil; 43 import com.android.compatibility.common.util.MediaUtils; 44 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.junit.runners.JUnit4; 49 50 import java.io.IOException; 51 import java.nio.ByteBuffer; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.List; 55 56 @AppModeFull(reason = "DecoderTest is non-instant") 57 @RunWith(JUnit4.class) 58 public class DecoderTestXheAac { 59 private static final String TAG = "DecoderTestXheAac"; 60 61 private static final boolean sIsAndroidRAndAbove = 62 ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R); 63 64 private Resources mResources; 65 66 // list of all AAC decoders as enumerated through the MediaCodecList 67 // lazy initialization in setUp() 68 private static ArrayList<String> sAacDecoderNames; 69 private static String defaultAacDecoder = null; 70 @Before setUp()71 public void setUp() throws Exception { 72 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 73 assertNotNull(inst); 74 mResources = inst.getContext().getResources(); 75 // build a list of all AAC decoders on which to run the test 76 if (sAacDecoderNames == null) { 77 sAacDecoderNames = initAacDecoderNames(); 78 } 79 } 80 initAacDecoderNames()81 protected static ArrayList<String> initAacDecoderNames() throws IOException { 82 ArrayList<String> aacDecoderNames = new ArrayList<String>(1); 83 // Default aac decoder (the one that gets created when createDecoderByType with AAC mime 84 // is called) is expected to pass all DRC tests 85 if (defaultAacDecoder != null) { 86 aacDecoderNames.add(defaultAacDecoder); 87 } else { 88 MediaCodec decoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC); 89 aacDecoderNames.add(decoder.getName()); 90 defaultAacDecoder = decoder.getName(); 91 decoder.release(); 92 } 93 // Add all decoders that advertise support for AACObjectXHE profile as decoders that 94 // support xHE-AAC profile are expected to support DRC 95 MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 48000, 96 2); 97 // Set both KEY_AAC_PROFILE and KEY_PROFILE as some codecs may only recognize one of 98 // these two keys 99 format.setInteger(MediaFormat.KEY_AAC_PROFILE, 100 MediaCodecInfo.CodecProfileLevel.AACObjectXHE); 101 format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectXHE); 102 103 final MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); 104 final MediaCodecInfo[] mediaCodecInfos = mediaCodecList.getCodecInfos(); 105 for (MediaCodecInfo mediaCodecInfo : mediaCodecInfos) { 106 if (mediaCodecInfo.isAlias()) { 107 continue; 108 } 109 if (mediaCodecInfo.isEncoder()) { 110 continue; 111 } 112 final String codecName = mediaCodecInfo.getName(); 113 final String[] mimeTypes = mediaCodecInfo.getSupportedTypes(); 114 for (String mimeType : mimeTypes) { 115 if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mimeType)) { 116 MediaCodecInfo.CodecCapabilities caps = mediaCodecInfo.getCapabilitiesForType( 117 mimeType); 118 if (caps.isFormatSupported(format)) { 119 if (!aacDecoderNames.contains(codecName)) { 120 aacDecoderNames.add(codecName); 121 } 122 } 123 break; 124 } 125 } 126 } 127 return aacDecoderNames; 128 } 129 130 /** 131 * Verify the correct decoding of USAC bitstreams with different MPEG-D DRC effect types. 132 */ 133 @Test testDecodeUsacDrcEffectTypeM4a()134 public void testDecodeUsacDrcEffectTypeM4a() throws Exception { 135 Log.v(TAG, "START testDecodeUsacDrcEffectTypeM4a"); 136 137 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 138 139 for (String aacDecName : sAacDecoderNames) { 140 try { 141 runDecodeUsacDrcEffectTypeM4a(aacDecName); 142 } catch (Error err) { 143 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 144 } 145 } 146 } 147 runDecodeUsacDrcEffectTypeM4a(String aacDecName)148 private void runDecodeUsacDrcEffectTypeM4a(String aacDecName) throws Exception { 149 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a running for dec=" + aacDecName); 150 // test DRC effectTypeID 1 "NIGHT" 151 // L -3dB -> normalization factor = 1/(10^(-3/10)) = 0.5011f 152 // R +3dB -> normalization factor = 1/(10^( 3/10)) = 1.9952f 153 try { 154 checkUsacDrcEffectType(1, 0.5011f, 1.9952f, "Night", 2, 0, aacDecName); 155 } catch (Exception e) { 156 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/0 failed for dec=" + aacDecName); 157 throw new RuntimeException(e); 158 } 159 160 // test DRC effectTypeID 2 "NOISY" 161 // L +3dB -> normalization factor = 1/(10^( 3/10)) = 1.9952f 162 // R -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 163 try { 164 checkUsacDrcEffectType(2, 1.9952f, 0.2511f, "Noisy", 2, 0, aacDecName); 165 } catch (Exception e) { 166 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Noisy/2/0 failed for dec=" + aacDecName); 167 throw new RuntimeException(e); 168 } 169 170 // test DRC effectTypeID 3 "LIMITED" 171 // L -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 172 // R +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 173 try { 174 checkUsacDrcEffectType(3, 0.2511f, 3.9810f, "Limited", 2, 0, aacDecName); 175 } catch (Exception e) { 176 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/2/0 failed for dec=" 177 + aacDecName); 178 throw new RuntimeException(e); 179 } 180 181 // test DRC effectTypeID 6 "GENERAL" 182 // L +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 183 // R -3dB -> normalization factor = 1/(10^(-3/10)) = 0.5011f 184 try { 185 checkUsacDrcEffectType(6, 3.9810f, 0.5011f, "General", 2, 0, aacDecName); 186 } catch (Exception e) { 187 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/2/0 failed for dec=" 188 + aacDecName); 189 throw new RuntimeException(e); 190 } 191 192 // test DRC effectTypeID 1 "NIGHT" 193 // L -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 194 // R +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 195 // mono -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 196 try { 197 checkUsacDrcEffectType(1, 0.2511f, 3.9810f, "Night", 2, 1, aacDecName); 198 } catch (Exception e) { 199 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/1 for dec=" + aacDecName); 200 throw new RuntimeException(e); 201 } 202 try { 203 checkUsacDrcEffectType(1, 0.2511f, 0.0f, "Night", 1, 1, aacDecName); 204 } catch (Exception e) { 205 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/1/1 for dec=" + aacDecName); 206 throw new RuntimeException(e); 207 } 208 209 // test DRC effectTypeID 2 "NOISY" 210 // L +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 211 // R -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f 212 // mono +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 213 try { 214 checkUsacDrcEffectType(2, 3.9810f, 0.1258f, "Noisy", 2, 1, aacDecName); 215 } catch (Exception e) { 216 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Noisy/2/1 for dec=" + aacDecName); 217 throw new RuntimeException(e); 218 } 219 try { 220 checkUsacDrcEffectType(2, 3.9810f, 0.0f, "Noisy", 1, 1, aacDecName); 221 } catch (Exception e) { 222 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/1 for dec=" + aacDecName); 223 throw new RuntimeException(e); 224 } 225 226 // test DRC effectTypeID 3 "LIMITED" 227 // L -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f 228 // R +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f 229 // mono -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f 230 try { 231 checkUsacDrcEffectType(3, 0.1258f, 7.9432f, "Limited", 2, 1, aacDecName); 232 } catch (Exception e) { 233 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/2/1 for dec=" + aacDecName); 234 throw new RuntimeException(e); 235 } 236 try { 237 checkUsacDrcEffectType(3, 0.1258f, 0.0f, "Limited", 1, 1, aacDecName); 238 } catch (Exception e) { 239 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/1/1 for dec=" + aacDecName); 240 throw new RuntimeException(e); 241 } 242 243 // test DRC effectTypeID 6 "GENERAL" 244 // L +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f 245 // R -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 246 // mono +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f 247 try { 248 checkUsacDrcEffectType(6, 7.9432f, 0.2511f, "General", 2, 1, aacDecName); 249 } catch (Exception e) { 250 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/2/1 for dec=" + aacDecName); 251 throw new RuntimeException(e); 252 } 253 try { 254 checkUsacDrcEffectType(6, 7.9432f, 0.0f, "General", 1, 1, aacDecName); 255 } catch (Exception e) { 256 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/1/1 for dec=" + aacDecName); 257 throw new RuntimeException(e); 258 } 259 } 260 261 /** 262 * Verify the correct decoding of USAC bitstreams with album mode. 263 */ 264 @Test testDecodeUsacDrcAlbumModeM4a()265 public void testDecodeUsacDrcAlbumModeM4a() throws Exception { 266 Log.v(TAG, "START testDecodeUsacDrcAlbumModeM4a"); 267 268 // Album mode is R feature 269 if (!MediaUtils.check(sIsAndroidRAndAbove, "Album mode support requires Android R")) 270 return; 271 272 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 273 274 for (String aacDecName : sAacDecoderNames) { 275 try { 276 runDecodeUsacDrcAlbumModeM4a(aacDecName); 277 } catch (Error err) { 278 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 279 } 280 } 281 } 282 runDecodeUsacDrcAlbumModeM4a(String aacDecName)283 private void runDecodeUsacDrcAlbumModeM4a(String aacDecName) throws Exception { 284 // test DRC Album Mode 285 // Track loudness = -19dB 286 // Album Loudness = -21 dB 287 // Fading Gains = -6 dB 288 // Album Mode ON : Gains = -24 - (-21) = -3dB 289 // Album Mode OFF : Gains = (-24 -(-19)) + (-6) = -11 dB 290 try { 291 checkUsacDrcAlbumMode(R.raw.noise_2ch_48khz_tlou_19lufs_alou_21lufs_mp4, aacDecName); 292 } catch (Exception e) { 293 Log.v(TAG, "testDecodeUsacDrcAlbumModeM4a for decoder" + aacDecName); 294 throw new RuntimeException(e); 295 } 296 } 297 298 /** 299 * Verify the correct decoding of USAC bitstreams with config changes. 300 */ 301 @Test testDecodeUsacStreamSwitchingM4a()302 public void testDecodeUsacStreamSwitchingM4a() throws Exception { 303 Log.v(TAG, "START testDecodeUsacStreamSwitchingM4a"); 304 305 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 306 307 for (String aacDecName : sAacDecoderNames) { 308 try { 309 runDecodeUsacStreamSwitchingM4a(aacDecName); 310 } catch (Error err) { 311 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 312 } 313 } 314 } 315 runDecodeUsacStreamSwitchingM4a(String aacDecName)316 private void runDecodeUsacStreamSwitchingM4a(String aacDecName) throws Exception { 317 // Stereo 318 // switch between SBR ratios and stereo modes 319 try { 320 checkUsacStreamSwitching(2.5459829E12f, 2, 321 R.raw.noise_2ch_44_1khz_aot42_19_lufs_config_change_mp4, aacDecName); 322 } catch (Exception e) { 323 Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 2ch sbr/stereo switch for " 324 + aacDecName); 325 throw new RuntimeException(e); 326 } 327 328 // Mono 329 // switch between SBR ratios and stereo modes 330 try { 331 checkUsacStreamSwitching(2.24669126E12f, 1, 332 R.raw.noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4, aacDecName); 333 } catch (Exception e) { 334 Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 1ch sbr/stereo switch for " 335 + aacDecName); 336 throw new RuntimeException(e); 337 } 338 339 // Stereo 340 // switch between USAC modes 341 try { 342 checkUsacStreamSwitching(2.1E12f, 2, 343 R.raw.noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4, aacDecName); 344 } catch (Exception e) { 345 Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 2ch USAC mode switch for " 346 + aacDecName); 347 throw new RuntimeException(e); 348 } 349 350 // Mono 351 // switch between USAC modes 352 try { 353 checkUsacStreamSwitching(1.7E12f, 1, 354 R.raw.noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4, aacDecName); 355 } catch (Exception e) { 356 Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 1ch USAC mode switch for " 357 + aacDecName); 358 throw new RuntimeException(e); 359 } 360 361 } 362 363 /** 364 * Verify the correct decoding of USAC bitstreams with various sampling rates. 365 */ 366 @Test testDecodeUsacSamplingRatesM4a()367 public void testDecodeUsacSamplingRatesM4a() throws Exception { 368 Log.v(TAG, "START testDecodeUsacSamplingRatesM4a"); 369 370 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 371 372 for (String aacDecName : sAacDecoderNames) { 373 try { 374 runDecodeUsacSamplingRatesM4a(aacDecName); 375 } catch (Error err) { 376 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 377 } 378 } 379 } 380 runDecodeUsacSamplingRatesM4a(String aacDecName)381 private void runDecodeUsacSamplingRatesM4a(String aacDecName) throws Exception { 382 try { 383 checkUsacSamplingRate(R.raw.noise_2ch_08khz_aot42_19_lufs_mp4, aacDecName); 384 checkUsacSamplingRate(R.raw.noise_2ch_12khz_aot42_19_lufs_mp4, aacDecName); 385 checkUsacSamplingRate(R.raw.noise_2ch_22_05khz_aot42_19_lufs_mp4, aacDecName); 386 checkUsacSamplingRate(R.raw.noise_2ch_64khz_aot42_19_lufs_mp4, aacDecName); 387 checkUsacSamplingRate(R.raw.noise_2ch_88_2khz_aot42_19_lufs_mp4, aacDecName); 388 checkUsacSamplingRateWoLoudness(R.raw.noise_2ch_19_2khz_aot42_no_ludt_mp4, 389 aacDecName); 390 } catch (Exception e) { 391 Log.v(TAG, "testDecodeUsacSamplingRatesM4a for decoder" + aacDecName); 392 throw new RuntimeException(e); 393 } 394 } 395 396 /** 397 * Verify the correct decoding of USAC bitstreams with different boost and attenuation settings 398 */ 399 @Test testDecodeUsacDrcBoostAndAttenuationM4a()400 public void testDecodeUsacDrcBoostAndAttenuationM4a() throws Exception { 401 Log.v(TAG, "START testDecodeUsacDrcBoostAndAttenuationM4a"); 402 403 if (!MediaUtils.check(sIsAndroidRAndAbove, "Att/Boost corrected in Android R")) 404 return; 405 406 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 407 408 for (String aacDecName : sAacDecoderNames) { 409 try { 410 runDecodeUsacDrcBoostAndAttenuationM4a(aacDecName); 411 } catch (Error err) { 412 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 413 } 414 } 415 } 416 runDecodeUsacDrcBoostAndAttenuationM4a(String aacDecName)417 private void runDecodeUsacDrcBoostAndAttenuationM4a(String aacDecName) throws Exception { 418 Log.v(TAG, "testDecodeUsacDrcBoostAndAttenuationM4a running for dec=" + aacDecName); 419 // test drcBoost and drcAttenuation parameters 420 // DRC effectTypeID 6 "GENERAL" 421 // L +6dB -> normalization factor = 10^(6/10 * (1 - boostFactor:64/127)) = 1.9844f 422 // R -3dB -> normalization factor = 10^(-3/10 * (1 - attenuationFactor:127/127)) = 1.0f 423 try { 424 checkUsacDrcBoostAndAttenuation(1.9844f, 1.0f, 64, 127, 2, aacDecName); 425 } catch (Exception e) { 426 Log.v(TAG, "testDecodeUsacDrcBoostAndAttenuationM4a failed for dec=" + aacDecName); 427 throw new RuntimeException(e); 428 } 429 430 // test drcBoost and drcAttenuation parameters 431 // DRC effectTypeID 6 "GENERAL" 432 // L +6dB -> normalization factor = 10^(6/10 * (1 - boostFactor:127/127)) = 1.0f 433 // R -3dB -> normalization factor = 10^(-3/10 * (1 - attenuationFactor:64/127)) = 0.7099f 434 try { 435 checkUsacDrcBoostAndAttenuation(1.0f, 0.7099f, 127, 64, 2, aacDecName); 436 } catch (Exception e) { 437 Log.v(TAG, "testDecodeUsacDrcBoostAndAttenuationM4a failed for dec=" + aacDecName); 438 throw new RuntimeException(e); 439 } 440 441 // test drcBoost and drcAttenuation parameters 442 // DRC effectTypeID 6 "GENERAL" 443 // L +6dB -> normalization factor = 10^(6/10 * (1 - boostFactor:0/127)) = 3.9811f 444 // R -3dB -> normalization factor = 10^(-3/10 * (1 - attenuationFactor:127/127)) = 1.0f 445 try { 446 checkUsacDrcBoostAndAttenuation(3.9811f, 1.0f, 0, 127, 2, aacDecName); 447 } catch (Exception e) { 448 Log.v(TAG, "testDecodeUsacDrcBoostAndAttenuationM4a failed for dec=" + aacDecName); 449 throw new RuntimeException(e); 450 } 451 452 // test drcBoost and drcAttenuation parameters 453 // DRC effectTypeID 6 "GENERAL" 454 // L +6dB -> normalization factor = 10^(6/10 * (1 - boostFactor:127/127)) = 1.0f 455 // R -3dB -> normalization factor = 10^(-3/10 * (1 - attenuationFactor:0/127)) = 0.5012f 456 try { 457 checkUsacDrcBoostAndAttenuation(1.0f, 0.5012f, 127, 0, 2, aacDecName); 458 } catch (Exception e) { 459 Log.v(TAG, "testDecodeUsacDrcBoostAndAttenuationM4a failed for dec=" + aacDecName); 460 throw new RuntimeException(e); 461 } 462 } 463 464 /** 465 * verify the correct decoding of USAC bitstreams when different kinds of loudness values 466 * are present 467 */ 468 @Test testDecodeUsacDrcLoudnessPreferenceM4a()469 public void testDecodeUsacDrcLoudnessPreferenceM4a() throws Exception { 470 Log.v(TAG, "START testDecodeUsacDrcLoudnessPreferenceM4a"); 471 472 if (!MediaUtils.check(sIsAndroidRAndAbove, "Loudness preference in Android R")) 473 return; 474 475 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 476 477 for (String aacDecName : sAacDecoderNames) { 478 try { 479 runDecodeUsacDrcLoudnessPreferenceM4a(aacDecName); 480 } catch (Error err) { 481 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 482 } 483 } 484 } 485 runDecodeUsacDrcLoudnessPreferenceM4a(String aacDecName)486 private void runDecodeUsacDrcLoudnessPreferenceM4a(String aacDecName) throws Exception { 487 Log.v(TAG, "testDecodeUsacDrcLoudnessPreferenceM4a running for dec=" + aacDecName); 488 // test drc loudness preference 489 // anchor loudness (-17 LUFS) and program loudness (-19 LUFS) are present in one stream 490 // -> anchor loudness should be selected 491 // the bitstream is decoded with targetLoudnessLevel = -16 LUFS and 492 // checked against the energy of the decoded signal without loudness normalization 493 // normfactor = loudness of waveform - targetLoudnessLevel = -1dB = 0.7943 494 try { 495 checkUsacDrcLoudnessPreference( 496 R.raw.noise_2ch_48khz_tlou_19lufs_anchor_17lufs_mp4, 0.7943f, aacDecName); 497 } catch (Exception e) { 498 Log.v(TAG, "testDecodeUsacDrcLoudnessPreferenceM4a failed for dec=" + aacDecName); 499 throw new RuntimeException(e); 500 } 501 502 // test drc loudness preference 503 // expert loudness (-23 LUFS) and program loudness (-19 LUFS) are present in one stream 504 // -> expert loudness should be selected 505 // the bitstream is decoded with targetLoudnessLevel = -16 LUFS and 506 // checked against the energy of the decoded signal without loudness normalization 507 // normfactor = loudness of waveform - targetLoudnessLevel = -7dB = 0.1995 508 try { 509 checkUsacDrcLoudnessPreference( 510 R.raw.noise_2ch_48khz_tlou_19lufs_expert_23lufs_mp4, 0.1995f, aacDecName); 511 } catch (Exception e) { 512 Log.v(TAG, "testDecodeUsacDrcLoudnessPreferenceM4a failed for dec=" + aacDecName); 513 throw new RuntimeException(e); 514 } 515 } 516 517 /** 518 * Verify that the correct output loudness values are returned when decoding USAC bitstreams 519 */ 520 @Test testDecodeUsacDrcOutputLoudnessM4a()521 public void testDecodeUsacDrcOutputLoudnessM4a() throws Exception { 522 Log.v(TAG, "START testDecodeUsacDrcOutputLoudnessM4a"); 523 524 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 525 526 for (String aacDecName : sAacDecoderNames) { 527 try { 528 runDecodeUsacDrcOutputLoudnessM4a(aacDecName); 529 } catch (Error err) { 530 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 531 } 532 } 533 } 534 runDecodeUsacDrcOutputLoudnessM4a(String aacDecName)535 private void runDecodeUsacDrcOutputLoudnessM4a(String aacDecName) throws Exception { 536 Log.v(TAG, "testDecodeUsacDrcOutputLoudnessM4a running for dec=" + aacDecName); 537 // test drc output loudness 538 // testfile without loudness metadata and loudness normalization off -> expected value: -1 539 try { 540 checkUsacDrcOutputLoudness( 541 R.raw.noise_2ch_19_2khz_aot42_no_ludt_mp4, -1, -1, aacDecName); 542 } catch (Exception e) { 543 Log.v(TAG, "testDecodeUsacDrcOutputLoudnessM4a failed for dec=" + aacDecName); 544 throw new RuntimeException(e); 545 } 546 547 Log.v(TAG, "testDecodeUsacDrcOutputLoudnessM4a running for dec=" + aacDecName); 548 // test drc output loudness 549 // testfile without loudness metadata and loudness normalization on 550 // -> expected value: -1 551 try { 552 checkUsacDrcOutputLoudness( 553 R.raw.noise_2ch_19_2khz_aot42_no_ludt_mp4, 64, -1, aacDecName); 554 } catch (Exception e) { 555 Log.v(TAG, "testDecodeUsacDrcOutputLoudnessM4a failed for dec=" + aacDecName); 556 throw new RuntimeException(e); 557 } 558 559 // test drc output loudness 560 // testfile with MPEG-D DRC loudness metadata and loudness normalization off 561 // -> expected value: loudness metadata in bitstream (-19*-4 = 76) 562 try { 563 checkUsacDrcOutputLoudness( 564 R.raw.noise_2ch_08khz_aot42_19_lufs_mp4, -1, 76, aacDecName); 565 } catch (Exception e) { 566 Log.v(TAG, "testDecodeUsacDrcOutputLoudnessM4a failed for dec=" + aacDecName); 567 throw new RuntimeException(e); 568 } 569 570 // test drc output loudness 571 // testfile with MPEG-D DRC loudness metadata and loudness normalization off 572 // -> expected value: loudness metadata in bitstream (-22*-4 = 88) 573 try { 574 checkUsacDrcOutputLoudness( 575 R.raw.noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4, -1, 88, aacDecName); 576 } catch (Exception e) { 577 Log.v(TAG, "testDecodeUsacDrcOutputLoudnessM4a failed for dec=" + aacDecName); 578 throw new RuntimeException(e); 579 } 580 581 // test drc output loudness 582 // testfile with MPEG-D DRC loudness metadata and loudness normalization on 583 // -> expected value: target loudness value (92) 584 try { 585 checkUsacDrcOutputLoudness( 586 R.raw.noise_2ch_08khz_aot42_19_lufs_mp4, 92, 92, aacDecName); 587 } catch (Exception e) { 588 Log.v(TAG, "testDecodeUsacDrcOutputLoudnessM4a failed for dec=" + aacDecName); 589 throw new RuntimeException(e); 590 } 591 } 592 593 594 /** 595 * Verify that seeking works correctly for USAC. 596 * Sync samples have to be taken into consideration. 597 */ 598 @Test testDecodeUsacSyncSampleSeekingM4a()599 public void testDecodeUsacSyncSampleSeekingM4a() throws Exception { 600 Log.v(TAG, "START testDecodeUsacSyncSampleSeekingM4a"); 601 if(!sIsAndroidRAndAbove) { 602 // The fix for b/158471477 was released in mainline release 300802800 603 // See https://android-build.googleplex.com/builds/treetop/googleplex-android-review/11990700 604 final int MIN_VERSION = 300802800; 605 TestUtils.assumeMainlineModuleAtLeast("com.google.android.media.swcodec", MIN_VERSION); 606 TestUtils.assumeMainlineModuleAtLeast("com.google.android.media", MIN_VERSION); 607 } 608 609 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 610 611 for (String aacDecName : sAacDecoderNames) { 612 try { 613 runDecodeUsacSyncSampleSeekingM4a(aacDecName); 614 } catch (Error err) { 615 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 616 } 617 } 618 } 619 runDecodeUsacSyncSampleSeekingM4a(String aacDecName)620 private void runDecodeUsacSyncSampleSeekingM4a(String aacDecName) throws Exception { 621 Log.v(TAG, "testDecodeUsacSyncSampleSeekingM4a running for dec=" + aacDecName); 622 // test usac seeking 623 try { 624 checkUsacSyncSampleSeeking(R.raw.sine_2ch_48khz_aot42_seek_mp4, aacDecName); 625 } catch (Exception e) { 626 Log.v(TAG, "testDecodeUsacSyncSampleSeekingM4a failed for dec=" + aacDecName); 627 throw new RuntimeException(e); 628 } 629 Log.v(TAG, "testDecodeUsacSyncSampleSeekingM4a running for dec=" + aacDecName); 630 } 631 632 /** 633 * Internal utilities 634 */ 635 636 /** 637 * USAC test DRC Effect Type 638 */ checkUsacDrcEffectType(int effectTypeID, float normFactor_L, float normFactor_R, String effectTypeName, int nCh, int aggressiveDrc, String decoderName)639 private void checkUsacDrcEffectType(int effectTypeID, float normFactor_L, float normFactor_R, 640 String effectTypeName, int nCh, int aggressiveDrc, String decoderName) 641 throws Exception { 642 for (boolean runtimeChange : new boolean[] {false, true}) { 643 if (runtimeChange && !sIsAndroidRAndAbove) { 644 // changing decoder configuration after it has been initialized requires R and above 645 continue; 646 } 647 int testinput = -1; 648 AudioParameter decParams = new AudioParameter(); 649 DrcParams drcParams_def = new DrcParams(127, 127, 96, 0, -1); 650 DrcParams drcParams_test = new DrcParams(127, 127, 96, 0, effectTypeID); 651 652 if (aggressiveDrc == 0) { 653 testinput = R.raw.noise_2ch_32khz_aot42_19_lufs_drc_mp4; 654 } else { 655 if (nCh == 2) { 656 testinput = R.raw.noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4; 657 } else if (nCh == 1){ 658 testinput = R.raw.noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4; 659 } 660 } 661 662 short[] decSamples_def = decodeToMemory(decParams, testinput, 663 -1, null, drcParams_def, decoderName); 664 short[] decSamples_test = decodeToMemory(decParams, testinput, 665 -1, null, drcParams_test, decoderName, runtimeChange); 666 667 float[] nrg_def = checkEnergyUSAC(decSamples_def, decParams, nCh, 1, 0); 668 float[] nrg_test = checkEnergyUSAC(decSamples_test, decParams, nCh, 1, 1); 669 670 if (nCh == 2) { 671 float nrgRatio_L = (nrg_test[1]/nrg_def[1])/normFactor_L; 672 float nrgRatio_R = (nrg_test[2]/nrg_def[2])/normFactor_R; 673 if ((nrgRatio_R > 1.05f || nrgRatio_R < 0.95f) 674 || (nrgRatio_L > 1.05f || nrgRatio_L < 0.95f) ){ 675 throw new Exception("DRC Effect Type '" + effectTypeName + "' not as expected"); 676 } 677 } else if (nCh == 1){ 678 float nrgRatio_L = (nrg_test[0]/nrg_def[0])/normFactor_L; 679 if (nrgRatio_L > 1.05f || nrgRatio_L < 0.95f){ 680 throw new Exception("DRC Effect Type '" + effectTypeName + "' not as expected"); 681 } 682 } 683 } 684 } 685 686 /** 687 * USAC test stream switching 688 */ checkUsacStreamSwitching(float nrg_ref, int encNch, int testinput, String decoderName)689 private void checkUsacStreamSwitching(float nrg_ref, int encNch, int testinput, 690 String decoderName) throws Exception 691 { 692 AudioParameter decParams = new AudioParameter(); 693 DrcParams drcParams = new DrcParams(127, 127, 64, 0, -1); 694 695 // Check stereo stream switching 696 short[] decSamples = decodeToMemory(decParams, testinput, 697 -1, null, drcParams, decoderName); 698 float[] nrg = checkEnergyUSAC(decSamples, decParams, encNch, 1); 699 700 float nrgRatio = nrg[0] / nrg_ref; 701 702 // Check if energy levels are within 15% of the reference 703 // Energy drops within the decoded stream are checked by checkEnergyUSAC() within every 704 // 250ms interval 705 if (nrgRatio > 1.15f || nrgRatio < 0.85f ) { 706 throw new Exception("Config switching not as expected"); 707 } 708 } 709 710 /** 711 * USAC test sampling rate 712 */ checkUsacSamplingRate(int testinput, String decoderName)713 private void checkUsacSamplingRate(int testinput, String decoderName) throws Exception { 714 AudioParameter decParams = new AudioParameter(); 715 DrcParams drcParams_def = new DrcParams(127, 127, 64, 0, -1); 716 DrcParams drcParams_test = new DrcParams(127, 127, 96, 0, -1); 717 718 short[] decSamples_def = decodeToMemory(decParams, testinput, 719 -1, null, drcParams_def, decoderName); 720 short[] decSamples_test = decodeToMemory(decParams, testinput, 721 -1, null, drcParams_test, decoderName); 722 723 float[] nrg_def = checkEnergyUSAC(decSamples_def, decParams, 2, 1); 724 float[] nrg_test = checkEnergyUSAC(decSamples_test, decParams, 2, 1); 725 726 float nrgRatio = nrg_def[0]/nrg_test[0]; 727 728 // normFactor = 1/(10^(-8/10)) = 6.3f 729 nrgRatio = nrgRatio / 6.3f; 730 731 // Check whether behavior is as expected 732 if (nrgRatio > 1.05f || nrgRatio < 0.95f ){ 733 throw new Exception("Sampling rate not supported"); 734 } 735 } 736 737 /** 738 * USAC test sampling rate for streams without loudness application 739 */ checkUsacSamplingRateWoLoudness(int testinput, String decoderName)740 private void checkUsacSamplingRateWoLoudness(int testinput, String decoderName) throws Exception 741 { 742 AudioParameter decParams = new AudioParameter(); 743 DrcParams drcParams = new DrcParams(); 744 745 short[] decSamples = decodeToMemory(decParams, testinput, -1, null, drcParams, decoderName); 746 747 float[] nrg = checkEnergyUSAC(decSamples, decParams, 2, 1); 748 749 float nrg_ref = 3.15766394E12f; 750 float nrgRatio = nrg_ref/nrg[0]; 751 752 // Check whether behavior is as expected 753 if (nrgRatio > 1.05f || nrgRatio < 0.95f ){ 754 throw new Exception("Sampling rate not supported"); 755 } 756 } 757 758 /** 759 * USAC test DRC Album Mode 760 */ checkUsacDrcAlbumMode(int testinput, String decoderName)761 private void checkUsacDrcAlbumMode(int testinput, String decoderName) throws Exception { 762 for (boolean runtimeChange : new boolean[] {false, true}) { 763 AudioParameter decParams = new AudioParameter(); 764 DrcParams drcParams_album_off = new DrcParams(127, 127, 64, 0, 0, 0); 765 DrcParams drcParams_album_on = new DrcParams(127, 127, 64, 0, 0, 1); 766 767 short[] decSamples_album_off = decodeToMemory( 768 decParams, testinput, -1, null, drcParams_album_off, decoderName); 769 short[] decSamples_album_on = decodeToMemory( 770 decParams, testinput, -1, null, drcParams_album_on, decoderName, runtimeChange); 771 772 float[] nrg_album_off = checkEnergyUSAC(decSamples_album_off, decParams, 2, 1); 773 float[] nrg_album_on = checkEnergyUSAC(decSamples_album_on, decParams, 2, 1); 774 775 float normFactor = 6.3095f; 776 777 float nrgRatio = (nrg_album_on[0]/nrg_album_off[0])/normFactor; 778 float nrgRatio_L = (nrg_album_on[1]/nrg_album_off[1])/normFactor; 779 float nrgRatio_R = (nrg_album_on[2]/nrg_album_off[2])/normFactor; 780 781 if (nrgRatio > 1.05f || nrgRatio < 0.95f ){ 782 throw new Exception("DRC Album Mode not supported, energy ratio " + nrgRatio); 783 } 784 } 785 } 786 787 /** 788 * USAC test DRC Boost and Attenuation 789 */ checkUsacDrcBoostAndAttenuation(float normFactor_L, float normFactor_R, int boostFactor, int attenuationFactor, int nCh, String decoderName)790 private void checkUsacDrcBoostAndAttenuation(float normFactor_L, float normFactor_R, 791 int boostFactor, int attenuationFactor, 792 int nCh, String decoderName) throws Exception { 793 for (boolean runtimeChange : new boolean[] {false, true}) { 794 if (runtimeChange && !sIsAndroidRAndAbove) { 795 // changing decoder configuration after it has been initialized requires R and above 796 continue; 797 } 798 799 int testinput = R.raw.noise_2ch_32khz_aot42_19_lufs_drc_mp4; 800 801 AudioParameter decParams = new AudioParameter(); 802 DrcParams drcParams_def = new DrcParams(127, 127, 64, 0, 6); 803 DrcParams drcParams_test = new DrcParams(boostFactor, attenuationFactor, 64, 0, 6); 804 805 short[] decSamples_def = decodeToMemory(decParams, testinput, -1, null, 806 drcParams_def, decoderName); 807 short[] decSamples_test = decodeToMemory(decParams, testinput, -1, null, 808 drcParams_test, decoderName, runtimeChange); 809 810 float[] nrg_def = checkEnergyUSAC(decSamples_def, decParams, 2, 1); 811 float[] nrg_test = checkEnergyUSAC(decSamples_test, decParams, 2, 1); 812 813 float nrgRatioLeft = nrg_test[1] / nrg_def[1]; 814 float nrgRatioRight = nrg_test[2] / nrg_def[2]; 815 816 float testValueLeft = normFactor_L * nrgRatioLeft; 817 float testValueRight = normFactor_R * nrgRatioRight; 818 819 // Check whether loudness behavior is as expected 820 if (testValueLeft > 1.05f || testValueLeft < 0.95f) { 821 throw new Exception("DRC boost/attenuation behavior not as expected"); 822 } 823 if (testValueRight > 1.05f || testValueRight < 0.95f) { 824 throw new Exception("DRC boost/attenuation behavior not as expected"); 825 } 826 } 827 } 828 829 /** 830 * USAC test Loudness Preference 831 */ checkUsacDrcLoudnessPreference(int testInput, float normFactor, String decoderName)832 private void checkUsacDrcLoudnessPreference(int testInput, float normFactor, String decoderName) throws Exception { 833 834 AudioParameter decParams = new AudioParameter(); 835 DrcParams drcParams_def = new DrcParams(127, 127, -1, 0, 6); 836 DrcParams drcParams_test = new DrcParams(127, 127, 64, 0, 6); 837 838 // Check drc loudness preference 839 short[] decSamples_def = decodeToMemory(decParams, testInput, -1, null, drcParams_def, decoderName); 840 short[] decSamples_test = decodeToMemory(decParams, testInput, -1, null, drcParams_test, decoderName); 841 842 float[] nrg_def = checkEnergyUSAC(decSamples_def, decParams, 2, 1); 843 float[] nrg_test = checkEnergyUSAC(decSamples_test, decParams, 2, 1); 844 845 float nrgRatio = (nrg_test[0]/nrg_def[0]); 846 nrgRatio = nrgRatio * normFactor; 847 848 if (nrgRatio > 1.05f || nrgRatio < 0.95f ){ 849 throw new Exception("DRC Loudness preference not as expected"); 850 } 851 } 852 853 /** 854 * USAC test Output Loudness 855 */ checkUsacDrcOutputLoudness(int testInput, int decoderTargetLevel, int expectedOutputLoudness, String decoderName)856 private void checkUsacDrcOutputLoudness(int testInput, int decoderTargetLevel, 857 int expectedOutputLoudness, String decoderName) throws Exception { 858 for (boolean runtimeChange : new boolean[] {false, true}) { 859 AudioParameter decParams = new AudioParameter(); 860 DrcParams drcParams_test = new DrcParams(127, 127, decoderTargetLevel, 0, 6); 861 862 // Check drc loudness preference 863 short[] decSamples_test = decodeToMemory( 864 decParams, testInput, -1, null, drcParams_test, 865 decoderName, runtimeChange, expectedOutputLoudness); 866 } 867 } 868 checkUsacSyncSampleSeeking(int testInput, String decoderName)869 private void checkUsacSyncSampleSeeking(int testInput, String decoderName) throws Exception { 870 871 AudioParameter decParams = new AudioParameter(); 872 DrcParams drcParams_def = new DrcParams(); 873 874 short[] decSamples_seek_next_sync = decodeToMemory(decParams, testInput, -1, null, 875 drcParams_def, decoderName, false, -2, true, 1100000, 876 MediaExtractor.SEEK_TO_NEXT_SYNC); 877 float[] nrg_seek_next_sync = checkEnergyUSAC(decSamples_seek_next_sync, decParams, 2, 1); 878 } 879 880 /** 881 * Perform a segmented energy analysis on given audio signal samples and run several tests on 882 * the energy values. 883 * 884 * The main purpose is to verify whether a USAC decoder implementation applies Spectral Band 885 * Replication (SBR), Parametric Stereo (PS) and Dynamic Range Control (DRC) correctly. All 886 * tools are inherent parts to either the MPEG-D USAC audio codec or the MPEG-D DRC tool. 887 * 888 * In addition, this test can verify the correct decoding of multi-channel (e.g. 5.1 channel) 889 * streams or the creation of a downmixed signal. 890 * 891 * Note: This test procedure is not an MPEG Conformance Test and can not serve as a replacement. 892 * 893 * @param decSamples the decoded audio samples to be tested 894 * @param decParams the audio parameters of the given audio samples (decSamples) 895 * @param encNch the encoded number of audio channels (number of channels of the original 896 * input) 897 * @param drcContext indicate to use test criteria applicable for DRC testing 898 * @return array of energies, at index 0: accumulated energy of all channels, and 899 * index 1 and over contain the individual channel energies 900 * @throws RuntimeException 901 */ checkEnergyUSAC(short[] decSamples, AudioParameter decParams, int encNch, int drcContext)902 protected float[] checkEnergyUSAC(short[] decSamples, AudioParameter decParams, 903 int encNch, int drcContext) 904 { 905 final float[] nrg = checkEnergyUSAC(decSamples, decParams, encNch, drcContext, 0); 906 return nrg; 907 } 908 909 /** 910 * Same as {@link #checkEnergyUSAC(short[], AudioParameter, int, int)} but with DRC effect type 911 * @param decSamples 912 * @param decParams 913 * @param encNch 914 * @param drcContext 915 * @param drcApplied indicate if MPEG-D DRC Effect Type has been applied 916 * @return 917 * @throws RuntimeException 918 */ checkEnergyUSAC(short[] decSamples, AudioParameter decParams, int encNch, int drcContext, int drcApplied)919 private float[] checkEnergyUSAC(short[] decSamples, AudioParameter decParams, 920 int encNch, int drcContext, int drcApplied) 921 throws RuntimeException 922 { 923 String localTag = TAG + "#checkEnergyUSAC"; 924 925 // the number of segments per block 926 final int nSegPerBlk = 4; 927 // the number of input channels 928 final int nCh = encNch; 929 // length of one (LB/HB) block [samples] 930 final int nBlkSmp = decParams.getSamplingRate(); 931 // length of one segment [samples] 932 final int nSegSmp = nBlkSmp / nSegPerBlk; 933 // actual # samples per channel (total) 934 final int smplPerChan = decSamples.length / nCh; 935 // actual # samples per segment (all ch) 936 final int nSegSmpTot = nSegSmp * nCh; 937 // signal offset between chans [segments] 938 final int nSegChOffst = 2 * nSegPerBlk; 939 // // the number of channels to be analyzed 940 final int procNch = Math.min(nCh, encNch); 941 // all original configs with more than five channel have an LFE 942 final int encEffNch = (encNch > 5) ? encNch-1 : encNch; 943 // expected number of decoded audio samples 944 final int expSmplPerChan = Math.max(encEffNch, 2) * nSegChOffst * nSegSmp; 945 // flag telling that input is dmx signal 946 final boolean isDmx = nCh < encNch; 947 final float nrgRatioThresh = 0.0f; 948 // the num analyzed channels with signal 949 int effProcNch = procNch; 950 951 // get the signal offset by counting zero samples at the very beginning (over all channels) 952 // sample value threshold 4 signal search 953 final int zeroSigThresh = 1; 954 // receives the number of samples that are in front of the actual signal 955 int signalStart = smplPerChan; 956 // receives the number of null samples (per chan) at the very beginning 957 int noiseStart = signalStart; 958 959 for (int smpl = 0; smpl < decSamples.length; smpl++) { 960 int value = Math.abs(decSamples[smpl]); 961 962 if (value > 0 && noiseStart == signalStart) { 963 // store start of prepended noise (can be same as signalStart) 964 noiseStart = smpl / nCh; 965 } 966 967 if (value > zeroSigThresh) { 968 // store signal start offset [samples] 969 signalStart = smpl / nCh; 970 break; 971 } 972 } 973 974 signalStart = (signalStart > noiseStart+1) ? signalStart : noiseStart; 975 976 // check if the decoder reproduced a waveform or kept silent 977 assertTrue ("no signal found in any channel!", signalStart < smplPerChan); 978 979 // max num seg that fit into signal 980 final int totSeg = (smplPerChan - signalStart) / nSegSmp; 981 // max num relevant samples (per channel) 982 final int totSmp = nSegSmp * totSeg; 983 984 // check if the number of reproduced segments in the audio file is valid 985 assertTrue("no segments left to test after signal search", totSeg > 0); 986 987 // get the energies and the channel offsets by searching for the first segment above the 988 // energy threshold: 989 // ratio of zeroNrgThresh to the max nrg 990 final double zeroMaxNrgRatio = 0.001f; 991 // threshold to classify segment energies 992 double zeroNrgThresh = nSegSmp * nSegSmp; 993 // will store the max seg nrg over all ch 994 double totMaxNrg = 0.0f; 995 // array receiving the segment energies 996 double[][] nrg = new double[procNch][totSeg]; 997 // array for channel offsets 998 int[] offset = new int[procNch]; 999 // array receiving the segment energy status over all channels 1000 boolean[] sigSeg = new boolean[totSeg]; 1001 // energy per channel 1002 double[] nrgPerChannel = new double[procNch]; 1003 // return value: [0]: total energy of all channels 1004 // [1 ... procNch + 1]: energy of the individual channels 1005 float[] nrgTotal = new float[procNch + 1]; 1006 // mapping array to sort channels 1007 int[] chMap = new int[nCh]; 1008 1009 // calculate the segmental energy for each channel 1010 for (int ch = 0; ch < procNch; ch++) { 1011 offset[ch] = -1; 1012 1013 for (int seg = 0; seg < totSeg; seg++) { 1014 final int smpStart = (signalStart * nCh) + (seg * nSegSmpTot) + ch; 1015 final int smpStop = smpStart + nSegSmpTot; 1016 1017 for (int smpl = smpStart; smpl < smpStop; smpl += nCh) { 1018 // accumulate total energy per channel 1019 nrgPerChannel[ch] += decSamples[smpl] * decSamples[smpl]; 1020 // accumulate segment energy 1021 nrg[ch][seg] += nrgPerChannel[ch]; 1022 } 1023 1024 // store 1st segment (index) per channel which has energy above the threshold to get 1025 // the ch offsets 1026 if (nrg[ch][seg] > zeroNrgThresh && offset[ch] < 0) { 1027 offset[ch] = seg / nSegChOffst; 1028 } 1029 1030 // store the max segment nrg over all ch 1031 if (nrg[ch][seg] > totMaxNrg) { 1032 totMaxNrg = nrg[ch][seg]; 1033 } 1034 1035 // store whether the channel has energy in this segment 1036 sigSeg[seg] |= nrg[ch][seg] > zeroNrgThresh; 1037 } 1038 1039 // if one channel has no signal it is most probably the LFE the LFE is no 1040 // effective channel 1041 if (offset[ch] < 0) { 1042 effProcNch -= 1; 1043 offset[ch] = effProcNch; 1044 } 1045 1046 // recalculate the zero signal threshold based on the 1st channels max energy for 1047 // all subsequent checks 1048 if (ch == 0) { 1049 zeroNrgThresh = zeroMaxNrgRatio * totMaxNrg; 1050 } 1051 } 1052 1053 // check if the LFE is decoded properly 1054 assertTrue("more than one LFE detected", effProcNch >= procNch - 1); 1055 1056 // check if the amount of samples is valid 1057 assertTrue(String.format("less samples decoded than expected: %d < %d", 1058 decSamples.length - (signalStart * nCh), 1059 totSmp * effProcNch), 1060 decSamples.length - (signalStart * nCh) >= totSmp * effProcNch); 1061 1062 // for multi-channel signals the only valid front channel orders 1063 // are L, R, C or C, L, R (L=left, R=right, C=center) 1064 if (procNch >= 5) { 1065 final int[] frontChMap1 = {2, 0, 1}; 1066 final int[] frontChMap2 = {0, 1, 2}; 1067 1068 // check if the channel mapping is valid 1069 if (!(Arrays.equals(Arrays.copyOfRange(offset, 0, 3), frontChMap1) 1070 || Arrays.equals(Arrays.copyOfRange(offset, 0, 3), frontChMap2))) { 1071 fail("wrong front channel mapping"); 1072 } 1073 } 1074 1075 // create mapping table to address channels from front to back the LFE must be last 1076 if (drcContext == 0) { 1077 // check whether every channel occurs exactly once 1078 for (int ch = 0; ch < effProcNch; ch++) { 1079 int occurred = 0; 1080 1081 for (int idx = 0; idx < procNch; idx++) { 1082 if (offset[idx] == ch) { 1083 occurred += 1; 1084 chMap[ch] = idx; 1085 } 1086 } 1087 1088 // check if one channel is mapped more than one time 1089 assertTrue(String.format("channel %d occurs %d times in the mapping", ch, occurred), 1090 occurred == 1); 1091 } 1092 } else { 1093 for (int ch = 0; ch < procNch; ch++) { 1094 chMap[ch] = ch; 1095 } 1096 } 1097 1098 // reference min energy for the 1st ch; others will be compared against 1st 1099 double refMinNrg = zeroNrgThresh; 1100 1101 // calculate total energy, min and max energy 1102 for (int ch = 0; ch < procNch; ch++) { 1103 // resolve channel mapping 1104 int idx = chMap[ch]; 1105 // signal offset [segments] 1106 final int ofst = offset[idx] * nSegChOffst; 1107 1108 if (ch <= effProcNch && ofst < totSeg) { 1109 // the last segment that has energy 1110 int nrgSegEnd; 1111 // the number of segments with energy 1112 int nrgSeg; 1113 1114 if (drcContext == 0) { 1115 1116 // the first channel of a mono or stereo signal has full signal all others have 1117 // one LB + one HB block 1118 if ((encNch <= 2) && (ch == 0)) { 1119 nrgSeg = totSeg; 1120 } else { 1121 nrgSeg = Math.min(totSeg, (2 * nSegPerBlk) + ofst) - ofst; 1122 } 1123 } else { 1124 nrgSeg = totSeg; 1125 } 1126 1127 nrgSegEnd = ofst + nrgSeg; 1128 1129 // find min and max energy of all segments that should have signal 1130 double minNrg = nrg[idx][ofst]; // channels minimum segment energy 1131 double maxNrg = nrg[idx][ofst]; // channels maximum segment energy 1132 1133 // values of 1st segment already assigned 1134 for (int seg = ofst + 1; seg < nrgSegEnd; seg++) { 1135 if (nrg[idx][seg] < minNrg) { 1136 minNrg = nrg[idx][seg]; 1137 } 1138 1139 if (nrg[idx][seg] > maxNrg) { 1140 maxNrg = nrg[idx][seg]; 1141 } 1142 } 1143 1144 // check if the energy of this channel is > 0 1145 assertTrue(String.format("max energy of channel %d is zero", ch),maxNrg > 0.0f); 1146 1147 if (drcContext == 0) { 1148 // check the channels minimum energy >= refMinNrg 1149 assertTrue(String.format("channel %d has not enough energy", ch), 1150 minNrg >= refMinNrg); 1151 1152 if (ch == 0) { 1153 // use 85% of 1st channels min energy as reference the other chs must meet 1154 refMinNrg = minNrg * 0.85f; 1155 } else if (isDmx && (ch == 1)) { 1156 // in case of downmixed signal the energy can be lower depending on the 1157 refMinNrg *= 0.50f; 1158 } 1159 1160 // calculate and check the energy ratio 1161 final double nrgRatio = minNrg / maxNrg; 1162 1163 // check if the threshold is exceeded 1164 assertTrue(String.format("energy ratio of channel %d below threshold", ch), 1165 nrgRatio >= nrgRatioThresh); 1166 1167 if (!isDmx) { 1168 if (nrgSegEnd < totSeg) { 1169 // consider that some noise can extend into the subsequent segment 1170 // allow this to be at max 20% of the channels minimum energy 1171 assertTrue( 1172 String.format("min energy after noise above threshold (%.2f)", 1173 nrg[idx][nrgSegEnd]), 1174 nrg[idx][nrgSegEnd] < minNrg * 0.20f); 1175 nrgSegEnd += 1; 1176 } 1177 } else { 1178 // ignore all subsequent segments in case of a downmixed signal 1179 nrgSegEnd = totSeg; 1180 } 1181 1182 // zero-out the verified energies to simplify the subsequent check 1183 for (int seg = ofst; seg < nrgSegEnd; seg++) { 1184 nrg[idx][seg] = 0.0f; 1185 } 1186 1187 // check zero signal parts 1188 for (int seg = 0; seg < totSeg; seg++) { 1189 assertTrue(String.format("segment %d in channel %d has signal where should " 1190 + "be none (%.2f)", seg, ch, nrg[idx][seg]), 1191 nrg[idx][seg] < zeroNrgThresh); 1192 } 1193 } 1194 } 1195 1196 // test whether each segment has energy in at least one channel 1197 for (int seg = 0; seg < totSeg; seg++) { 1198 assertTrue(String.format("no channel has energy in segment %d", seg), sigSeg[seg]); 1199 } 1200 1201 nrgTotal[0] += (float)nrgPerChannel[ch]; 1202 nrgTotal[ch + 1] = (float)nrgPerChannel[ch]; 1203 } 1204 1205 float errorMargin = 0.0f; 1206 if (drcApplied == 0) { 1207 errorMargin = 0.25f; 1208 } else if (drcApplied == 1) { 1209 errorMargin = 0.40f; 1210 } 1211 1212 float totSegEnergy = 0.0f; 1213 float[] segEnergy = new float[totSeg]; 1214 for (int seg = totSeg - 1; seg >= 0; seg--) { 1215 if (seg != 0) { 1216 for (int ch = 0; ch < nCh; ch++) { 1217 segEnergy[seg] += nrg[ch][seg] - nrg[ch][seg - 1]; 1218 } 1219 totSegEnergy += segEnergy[seg]; 1220 } else { 1221 for (int ch = 0; ch < nCh; ch++) { 1222 segEnergy[seg] += nrg[ch][seg]; 1223 } 1224 } 1225 } 1226 float avgSegEnergy = totSegEnergy / (totSeg - 1); 1227 for (int seg = 1; seg < totSeg; seg++) { 1228 float energyRatio = segEnergy[seg] / avgSegEnergy; 1229 if ((energyRatio > (1.0f + errorMargin) ) || (energyRatio < (1.0f - errorMargin) )) { 1230 fail("Energy drop out"); 1231 } 1232 } 1233 1234 // return nrgTotal: [0]: accumulated energy of all channels, [1 ... ] channel energies 1235 return nrgTotal; 1236 } 1237 1238 /** 1239 * Decodes a compressed bitstream in the ISOBMFF into the RAM of the device. 1240 * 1241 * The decoder decodes compressed audio data as stored in the ISO Base Media File Format 1242 * (ISOBMFF) aka .mp4/.m4a. The decoder is not reproducing the waveform but stores the decoded 1243 * samples in the RAM of the device under test. 1244 * 1245 * @param audioParams the decoder parameter configuration 1246 * @param testinput the compressed audio stream 1247 * @param eossample the End-Of-Stream indicator 1248 * @param timestamps the time stamps to decode 1249 * @param drcParams the MPEG-D DRC decoder parameter configuration 1250 * @param decoderName if non null, the name of the decoder to use for the decoding, otherwise 1251 * the default decoder for the format will be used 1252 * @param runtimeChange defines whether the decoder is configured at runtime or configured 1253 * before starting to decode 1254 * @param expectedOutputLoudness value to check if the correct output loudness is returned 1255 * by the decoder 1256 * @param seek_enable defines whether there will be an initial seek 1257 * @param seek_duration seeking duration in microseconds 1258 * @param seek_mode seeking mode 1259 * 1260 * @throws RuntimeException 1261 */ 1262 public short[] decodeToMemory(AudioParameter audioParams, int testinput, int eossample, 1263 List<Long> timestamps, DrcParams drcParams, String decoderName, boolean runtimeChange, 1264 int expectedOutputLoudness, 1265 boolean seek_enable, int seek_duration, int seek_mode) throws IOException { 1266 // TODO: code is the same as in DecoderTest, differences are: 1267 // - addition of application of DRC parameters 1268 // - no need/use of resetMode, configMode 1269 // Split method so code can be shared 1270 1271 final String localTag = TAG + "#decodeToMemory"; 1272 short [] decoded = new short[0]; 1273 int decodedIdx = 0; 1274 1275 AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput); 1276 1277 MediaExtractor extractor; 1278 MediaCodec codec; 1279 ByteBuffer[] codecInputBuffers; 1280 ByteBuffer[] codecOutputBuffers; 1281 1282 extractor = new MediaExtractor(); 1283 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 1284 testFd.getLength()); 1285 testFd.close(); 1286 1287 assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); 1288 MediaFormat format = extractor.getTrackFormat(0); 1289 String mime = format.getString(MediaFormat.KEY_MIME); 1290 assertTrue("not an audio file", mime.startsWith("audio/")); 1291 1292 MediaFormat configFormat = format; 1293 if (decoderName == null) { 1294 codec = MediaCodec.createDecoderByType(mime); 1295 } else { 1296 codec = MediaCodec.createByCodecName(decoderName); 1297 } 1298 1299 // set DRC parameters 1300 if (drcParams != null) { 1301 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION, drcParams.mHeavy); 1302 if (!runtimeChange) { 1303 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_BOOST_FACTOR, drcParams.mBoost); 1304 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_ATTENUATION_FACTOR, drcParams.mCut); 1305 if (drcParams.mDecoderTargetLevel != 0) { 1306 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 1307 drcParams.mDecoderTargetLevel); 1308 } 1309 if (drcParams.mEffectType != 0){ 1310 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_EFFECT_TYPE, 1311 drcParams.mEffectType); 1312 } 1313 if (drcParams.mAlbumMode != 0) { 1314 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_ALBUM_MODE, 1315 drcParams.mAlbumMode); 1316 } 1317 } 1318 } 1319 1320 Log.v(localTag, "configuring with " + configFormat); 1321 codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */); 1322 1323 if (drcParams != null && sIsAndroidRAndAbove) { // querying output format requires R 1324 if(!runtimeChange) { 1325 if (drcParams.mAlbumMode != 0) { 1326 int albumModeFromCodec = DecoderTest.getOutputFormatInteger(codec, 1327 MediaFormat.KEY_AAC_DRC_ALBUM_MODE); 1328 if (albumModeFromCodec != drcParams.mAlbumMode) { 1329 fail("Drc AlbumMode received from MediaCodec is not the Album Mode set"); 1330 } 1331 } 1332 if (drcParams.mEffectType != 0) { 1333 final int effectTypeFromCodec = DecoderTest.getOutputFormatInteger(codec, 1334 MediaFormat.KEY_AAC_DRC_EFFECT_TYPE); 1335 if (effectTypeFromCodec != drcParams.mEffectType) { 1336 fail("Drc Effect Type received from MediaCodec is not the Effect Type set"); 1337 } 1338 } 1339 if (drcParams.mDecoderTargetLevel != 0) { 1340 final int targetLevelFromCodec = DecoderTest.getOutputFormatInteger(codec, 1341 MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL); 1342 if (targetLevelFromCodec != drcParams.mDecoderTargetLevel) { 1343 fail("Drc Target Reference Level received from MediaCodec is not the Target Reference Level set"); 1344 } 1345 } 1346 } 1347 } 1348 1349 codec.start(); 1350 codecInputBuffers = codec.getInputBuffers(); 1351 codecOutputBuffers = codec.getOutputBuffers(); 1352 1353 if (drcParams != null) { 1354 if (runtimeChange) { 1355 Bundle b = new Bundle(); 1356 b.putInt(MediaFormat.KEY_AAC_DRC_BOOST_FACTOR, drcParams.mBoost); 1357 b.putInt(MediaFormat.KEY_AAC_DRC_ATTENUATION_FACTOR, drcParams.mCut); 1358 if (drcParams.mEffectType != 0) { 1359 b.putInt(MediaFormat.KEY_AAC_DRC_EFFECT_TYPE, drcParams.mEffectType); 1360 } 1361 if (drcParams.mDecoderTargetLevel != 0) { 1362 b.putInt(MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 1363 drcParams.mDecoderTargetLevel); 1364 } 1365 if (drcParams.mAlbumMode != 0) { 1366 b.putInt(MediaFormat.KEY_AAC_DRC_ALBUM_MODE, drcParams.mAlbumMode); 1367 } 1368 codec.setParameters(b); 1369 } 1370 } 1371 1372 extractor.selectTrack(0); 1373 1374 // execute initial seeking if specified 1375 if (seek_enable) { 1376 codec.flush(); 1377 extractor.seekTo(seek_duration, seek_mode); 1378 } 1379 1380 // start decoding 1381 final long kTimeOutUs = 5000; 1382 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 1383 boolean sawInputEOS = false; 1384 boolean sawOutputEOS = false; 1385 int noOutputCounter = 0; 1386 int samplecounter = 0; 1387 1388 // main decoding loop 1389 while (!sawOutputEOS && noOutputCounter < 50) { 1390 noOutputCounter++; 1391 if (!sawInputEOS) { 1392 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 1393 1394 if (inputBufIndex >= 0) { 1395 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 1396 1397 int sampleSize = 1398 extractor.readSampleData(dstBuf, 0 /* offset */); 1399 1400 long presentationTimeUs = 0; 1401 1402 if (sampleSize < 0 && eossample > 0) { 1403 fail("test is broken: never reached eos sample"); 1404 } 1405 1406 if (sampleSize < 0) { 1407 Log.d(TAG, "saw input EOS."); 1408 sawInputEOS = true; 1409 sampleSize = 0; 1410 } else { 1411 if (samplecounter == eossample) { 1412 sawInputEOS = true; 1413 } 1414 samplecounter++; 1415 presentationTimeUs = extractor.getSampleTime(); 1416 } 1417 1418 codec.queueInputBuffer( 1419 inputBufIndex, 1420 0 /* offset */, 1421 sampleSize, 1422 presentationTimeUs, 1423 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 1424 1425 if (!sawInputEOS) { 1426 extractor.advance(); 1427 } 1428 } 1429 } 1430 1431 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 1432 1433 if (res >= 0) { 1434 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs); 1435 1436 if (info.size > 0) { 1437 noOutputCounter = 0; 1438 if (timestamps != null) { 1439 timestamps.add(info.presentationTimeUs); 1440 } 1441 } 1442 1443 int outputBufIndex = res; 1444 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 1445 1446 if (decodedIdx + (info.size / 2) >= decoded.length) { 1447 decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2)); 1448 } 1449 1450 buf.position(info.offset); 1451 for (int i = 0; i < info.size; i += 2) { 1452 decoded[decodedIdx++] = buf.getShort(); 1453 } 1454 1455 codec.releaseOutputBuffer(outputBufIndex, false /* render */); 1456 1457 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 1458 Log.d(TAG, "saw output EOS."); 1459 sawOutputEOS = true; 1460 } 1461 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 1462 codecOutputBuffers = codec.getOutputBuffers(); 1463 1464 Log.d(TAG, "output buffers have changed."); 1465 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 1466 MediaFormat oformat = codec.getOutputFormat(); 1467 audioParams.setNumChannels(oformat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); 1468 audioParams.setSamplingRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE)); 1469 Log.d(TAG, "output format has changed to " + oformat); 1470 } else { 1471 Log.d(TAG, "dequeueOutputBuffer returned " + res); 1472 } 1473 } 1474 1475 if (noOutputCounter >= 50) { 1476 fail("decoder stopped outputting data"); 1477 } 1478 1479 // check if MediaCodec gives back correct drc parameters 1480 if (drcParams != null && sIsAndroidRAndAbove) { 1481 if (drcParams.mAlbumMode != 0) { 1482 final int albumModeFromCodec = DecoderTest.getOutputFormatInteger(codec, 1483 MediaFormat.KEY_AAC_DRC_ALBUM_MODE); 1484 assertEquals("DRC AlbumMode received from MediaCodec is not the Album Mode set" 1485 + " runtime:" + runtimeChange, drcParams.mAlbumMode, albumModeFromCodec); 1486 } 1487 if (drcParams.mEffectType != 0) { 1488 final int effectTypeFromCodec = DecoderTest.getOutputFormatInteger(codec, 1489 MediaFormat.KEY_AAC_DRC_EFFECT_TYPE); 1490 assertEquals("DRC Effect Type received from MediaCodec is not the Effect Type set" 1491 + " runtime:" + runtimeChange, drcParams.mEffectType, effectTypeFromCodec); 1492 } 1493 if (drcParams.mDecoderTargetLevel != 0) { 1494 final int targetLevelFromCodec = DecoderTest.getOutputFormatInteger(codec, 1495 MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL); 1496 assertEquals("DRC Target Ref Level received from MediaCodec is not the level set" 1497 + " runtime:" + runtimeChange, 1498 drcParams.mDecoderTargetLevel, targetLevelFromCodec); 1499 } 1500 1501 final int cutFromCodec = DecoderTest.getOutputFormatInteger(codec, 1502 MediaFormat.KEY_AAC_DRC_ATTENUATION_FACTOR); 1503 assertEquals("Attenuation factor received from MediaCodec differs from set:", 1504 drcParams.mCut, cutFromCodec); 1505 final int boostFromCodec = DecoderTest.getOutputFormatInteger(codec, 1506 MediaFormat.KEY_AAC_DRC_BOOST_FACTOR); 1507 assertEquals("Boost factor received from MediaCodec differs from set:", 1508 drcParams.mBoost, boostFromCodec); 1509 } 1510 1511 // expectedOutputLoudness == -2 indicates that output loudness is not tested 1512 if (expectedOutputLoudness != -2 && sIsAndroidRAndAbove) { 1513 final int outputLoudnessFromCodec = DecoderTest.getOutputFormatInteger(codec, 1514 MediaFormat.KEY_AAC_DRC_OUTPUT_LOUDNESS); 1515 if (outputLoudnessFromCodec != expectedOutputLoudness) { 1516 fail("Received decoder output loudness is not the expected value"); 1517 } 1518 } 1519 1520 codec.stop(); 1521 codec.release(); 1522 return decoded; 1523 } 1524 1525 private short[] decodeToMemory(AudioParameter audioParams, int testinput, 1526 int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName) 1527 throws IOException 1528 { 1529 final short[] decoded = decodeToMemory(audioParams, testinput, eossample, timestamps, 1530 drcParams, decoderName, false, -2, false, 0, 0); 1531 return decoded; 1532 } 1533 1534 private short[] decodeToMemory(AudioParameter audioParams, int testinput, 1535 int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName, 1536 boolean runtimeChange) 1537 throws IOException 1538 { 1539 final short[] decoded = decodeToMemory(audioParams, testinput, eossample, timestamps, 1540 drcParams, decoderName, runtimeChange, -2, false, 0, 0); 1541 return decoded; 1542 } 1543 1544 private short[] decodeToMemory(AudioParameter audioParams, int testinput, 1545 int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName, 1546 boolean runtimeChange, int expectedOutputLoudness) 1547 throws IOException 1548 { 1549 short [] decoded = decodeToMemory(audioParams, testinput, eossample, timestamps, drcParams, 1550 decoderName, runtimeChange, expectedOutputLoudness, false, 0, 0); 1551 return decoded; 1552 } 1553 } 1554 1555