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.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.DecoderTest.AudioParameter; 33 import android.media.cts.DecoderTestAacDrc.DrcParams; 34 import android.media.cts.R; 35 import android.util.Log; 36 37 import androidx.test.InstrumentationRegistry; 38 39 import org.junit.Before; 40 import org.junit.Test; 41 42 import java.io.IOException; 43 import java.nio.ByteBuffer; 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.List; 47 48 public class DecoderTestXheAac { 49 private static final String TAG = "DecoderTestXheAac"; 50 51 private Resources mResources; 52 53 // list of all AAC decoders as enumerated through the MediaCodecList 54 // lazy initialization in setUp() 55 private static ArrayList<String> sAacDecoderNames; 56 57 @Before setUp()58 public void setUp() throws Exception { 59 final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); 60 assertNotNull(inst); 61 mResources = inst.getContext().getResources(); 62 // build a list of all AAC decoders on which to run the test 63 if (sAacDecoderNames == null) { 64 sAacDecoderNames = initAacDecoderNames(); 65 } 66 } 67 initAacDecoderNames()68 protected static ArrayList<String> initAacDecoderNames() { 69 // at least 1 AAC decoder expected 70 ArrayList<String> aacDecoderNames = new ArrayList<String>(1); 71 final MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); 72 final MediaCodecInfo[] mediaCodecInfos = mediaCodecList.getCodecInfos(); 73 for (MediaCodecInfo mediaCodecInfo : mediaCodecInfos) { 74 if (mediaCodecInfo.isAlias()) { 75 continue; 76 } 77 if (mediaCodecInfo.isEncoder()) { 78 continue; 79 } 80 final String[] mimeTypes = mediaCodecInfo.getSupportedTypes(); 81 for (String mimeType : mimeTypes) { 82 if (MediaFormat.MIMETYPE_AUDIO_AAC.equalsIgnoreCase(mimeType)) { 83 aacDecoderNames.add(mediaCodecInfo.getName()); 84 break; 85 } 86 } 87 } 88 return aacDecoderNames; 89 } 90 91 /** 92 * Verify the correct decoding of USAC bitstreams with different MPEG-D DRC effect types. 93 */ 94 @Test testDecodeUsacDrcEffectTypeM4a()95 public void testDecodeUsacDrcEffectTypeM4a() throws Exception { 96 Log.v(TAG, "START testDecodeUsacDrcEffectTypeM4a"); 97 98 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 99 100 for (String aacDecName : sAacDecoderNames) { 101 try { 102 runDecodeUsacDrcEffectTypeM4a(aacDecName); 103 } catch (Error err) { 104 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 105 } 106 } 107 } 108 runDecodeUsacDrcEffectTypeM4a(String aacDecName)109 private void runDecodeUsacDrcEffectTypeM4a(String aacDecName) throws Exception { 110 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a running for dec=" + aacDecName); 111 // test DRC effectTypeID 1 "NIGHT" 112 // L -3dB -> normalization factor = 1/(10^(-3/10)) = 0.5011f 113 // R +3dB -> normalization factor = 1/(10^( 3/10)) = 1.9952f 114 try { 115 checkUsacDrcEffectType(1, 0.5011f, 1.9952f, "Night", 2, 0, aacDecName); 116 } catch (Exception e) { 117 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/0 failed for dec=" + aacDecName); 118 throw new RuntimeException(e); 119 } 120 121 // test DRC effectTypeID 2 "NOISY" 122 // L +3dB -> normalization factor = 1/(10^( 3/10)) = 1.9952f 123 // R -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 124 try { 125 checkUsacDrcEffectType(2, 1.9952f, 0.2511f, "Noisy", 2, 0, aacDecName); 126 } catch (Exception e) { 127 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Noisy/2/0 failed for dec=" + aacDecName); 128 throw new RuntimeException(e); 129 } 130 131 // test DRC effectTypeID 3 "LIMITED" 132 // L -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 133 // R +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 134 try { 135 checkUsacDrcEffectType(3, 0.2511f, 3.9810f, "Limited", 2, 0, aacDecName); 136 } catch (Exception e) { 137 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/2/0 failed for dec=" 138 + aacDecName); 139 throw new RuntimeException(e); 140 } 141 142 // test DRC effectTypeID 6 "GENERAL" 143 // L +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 144 // R -3dB -> normalization factor = 1/(10^(-3/10)) = 0.5011f 145 try { 146 checkUsacDrcEffectType(6, 3.9810f, 0.5011f, "General", 2, 0, aacDecName); 147 } catch (Exception e) { 148 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/2/0 failed for dec=" 149 + aacDecName); 150 throw new RuntimeException(e); 151 } 152 153 // test DRC effectTypeID 1 "NIGHT" 154 // L -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 155 // R +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 156 // mono -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 157 try { 158 checkUsacDrcEffectType(1, 0.2511f, 3.9810f, "Night", 2, 1, aacDecName); 159 } catch (Exception e) { 160 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/1 for dec=" + aacDecName); 161 throw new RuntimeException(e); 162 } 163 try { 164 checkUsacDrcEffectType(1, 0.2511f, 0.0f, "Night", 1, 1, aacDecName); 165 } catch (Exception e) { 166 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/1/1 for dec=" + aacDecName); 167 throw new RuntimeException(e); 168 } 169 170 // test DRC effectTypeID 2 "NOISY" 171 // L +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 172 // R -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f 173 // mono +6dB -> normalization factor = 1/(10^( 6/10)) = 3.9810f 174 try { 175 checkUsacDrcEffectType(2, 3.9810f, 0.1258f, "Noisy", 2, 1, aacDecName); 176 } catch (Exception e) { 177 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Noisy/2/1 for dec=" + aacDecName); 178 throw new RuntimeException(e); 179 } 180 try { 181 checkUsacDrcEffectType(2, 3.9810f, 0.0f, "Noisy", 1, 1, aacDecName); 182 } catch (Exception e) { 183 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Night/2/1 for dec=" + aacDecName); 184 throw new RuntimeException(e); 185 } 186 187 // test DRC effectTypeID 3 "LIMITED" 188 // L -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f 189 // R +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f 190 // mono -9dB -> normalization factor = 1/(10^(-9/10)) = 0.1258f 191 try { 192 checkUsacDrcEffectType(3, 0.1258f, 7.9432f, "Limited", 2, 1, aacDecName); 193 } catch (Exception e) { 194 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/2/1 for dec=" + aacDecName); 195 throw new RuntimeException(e); 196 } 197 try { 198 checkUsacDrcEffectType(3, 0.1258f, 0.0f, "Limited", 1, 1, aacDecName); 199 } catch (Exception e) { 200 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a Limited/1/1 for dec=" + aacDecName); 201 throw new RuntimeException(e); 202 } 203 204 // test DRC effectTypeID 6 "GENERAL" 205 // L +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f 206 // R -6dB -> normalization factor = 1/(10^(-6/10)) = 0.2511f 207 // mono +9dB -> normalization factor = 1/(10^( 9/10)) = 7.9432f 208 try { 209 checkUsacDrcEffectType(6, 7.9432f, 0.2511f, "General", 2, 1, aacDecName); 210 } catch (Exception e) { 211 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/2/1 for dec=" + aacDecName); 212 throw new RuntimeException(e); 213 } 214 try { 215 checkUsacDrcEffectType(6, 7.9432f, 0.0f, "General", 1, 1, aacDecName); 216 } catch (Exception e) { 217 Log.v(TAG, "testDecodeUsacDrcEffectTypeM4a General/1/1 for dec=" + aacDecName); 218 throw new RuntimeException(e); 219 } 220 } 221 222 /** 223 * Verify the correct decoding of USAC bitstreams with config changes. 224 */ 225 @Test testDecodeUsacStreamSwitchingM4a()226 public void testDecodeUsacStreamSwitchingM4a() throws Exception { 227 Log.v(TAG, "START testDecodeUsacStreamSwitchingM4a"); 228 229 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 230 231 for (String aacDecName : sAacDecoderNames) { 232 try { 233 runDecodeUsacStreamSwitchingM4a(aacDecName); 234 } catch (Error err) { 235 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 236 } 237 } 238 } 239 runDecodeUsacStreamSwitchingM4a(String aacDecName)240 private void runDecodeUsacStreamSwitchingM4a(String aacDecName) throws Exception { 241 // Stereo 242 // switch between SBR ratios and stereo modes 243 try { 244 checkUsacStreamSwitching(2.5459829E12f, 2, 245 R.raw.noise_2ch_44_1khz_aot42_19_lufs_config_change_mp4, aacDecName); 246 } catch (Exception e) { 247 Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 2ch sbr/stereo switch for " 248 + aacDecName); 249 throw new RuntimeException(e); 250 } 251 252 // Mono 253 // switch between SBR ratios and stereo modes 254 try { 255 checkUsacStreamSwitching(2.24669126E12f, 1, 256 R.raw.noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4, aacDecName); 257 } catch (Exception e) { 258 Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 1ch sbr/stereo switch for " 259 + aacDecName); 260 throw new RuntimeException(e); 261 } 262 263 // Stereo 264 // switch between USAC modes 265 try { 266 checkUsacStreamSwitching(2.1E12f, 2, 267 R.raw.noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4, aacDecName); 268 } catch (Exception e) { 269 Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 2ch USAC mode switch for " 270 + aacDecName); 271 throw new RuntimeException(e); 272 } 273 274 // Mono 275 // switch between USAC modes 276 try { 277 checkUsacStreamSwitching(1.7E12f, 1, 278 R.raw.noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4, aacDecName); 279 } catch (Exception e) { 280 Log.v(TAG, "testDecodeUsacStreamSwitchingM4a failed 1ch USAC mode switch for " 281 + aacDecName); 282 throw new RuntimeException(e); 283 } 284 285 } 286 287 /** 288 * Verify the correct decoding of USAC bitstreams with various sampling rates. 289 */ 290 @Test testDecodeUsacSamplingRatesM4a()291 public void testDecodeUsacSamplingRatesM4a() throws Exception { 292 Log.v(TAG, "START testDecodeUsacSamplingRatesM4a"); 293 294 assertTrue("No AAC decoder found", sAacDecoderNames.size() > 0); 295 296 for (String aacDecName : sAacDecoderNames) { 297 try { 298 runDecodeUsacSamplingRatesM4a(aacDecName); 299 } catch (Error err) { 300 throw new Error(err.getMessage() + " [dec=" + aacDecName + "]" , err); 301 } 302 } 303 } 304 runDecodeUsacSamplingRatesM4a(String aacDecName)305 private void runDecodeUsacSamplingRatesM4a(String aacDecName) throws Exception { 306 try { 307 checkUsacSamplingRate(R.raw.noise_2ch_08khz_aot42_19_lufs_mp4, aacDecName); 308 checkUsacSamplingRate(R.raw.noise_2ch_12khz_aot42_19_lufs_mp4, aacDecName); 309 checkUsacSamplingRate(R.raw.noise_2ch_22_05khz_aot42_19_lufs_mp4, aacDecName); 310 checkUsacSamplingRate(R.raw.noise_2ch_64khz_aot42_19_lufs_mp4, aacDecName); 311 checkUsacSamplingRate(R.raw.noise_2ch_88_2khz_aot42_19_lufs_mp4, aacDecName); 312 checkUsacSamplingRateWoLoudness(R.raw.noise_2ch_19_2khz_aot42_no_ludt_mp4, 313 aacDecName); 314 } catch (Exception e) { 315 Log.v(TAG, "testDecodeUsacSamplingRatesM4a for decoder" + aacDecName); 316 throw new RuntimeException(e); 317 } 318 } 319 320 321 /** 322 * Internal utilities 323 */ 324 325 /** 326 * USAC test DRC Effect Type 327 */ checkUsacDrcEffectType(int effectTypeID, float normFactor_L, float normFactor_R, String effectTypeName, int nCh, int aggressiveDrc, String decoderName)328 private void checkUsacDrcEffectType(int effectTypeID, float normFactor_L, float normFactor_R, 329 String effectTypeName, int nCh, int aggressiveDrc, String decoderName) 330 throws Exception { 331 int testinput = -1; 332 AudioParameter decParams = new AudioParameter(); 333 DrcParams drcParams_def = new DrcParams(127, 127, 96, 0, -1); 334 DrcParams drcParams_test = new DrcParams(127, 127, 96, 0, effectTypeID); 335 336 if (aggressiveDrc == 0) { 337 testinput = R.raw.noise_2ch_32khz_aot42_19_lufs_drc_mp4; 338 } else { 339 if (nCh == 2) { 340 testinput = R.raw.noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4; 341 } else if (nCh == 1){ 342 testinput = R.raw.noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4; 343 } 344 } 345 346 short[] decSamples_def = decodeToMemory(decParams, testinput, 347 -1, null, drcParams_def, decoderName); 348 short[] decSamples_test = decodeToMemory(decParams, testinput, 349 -1, null, drcParams_test, decoderName); 350 351 float[] nrg_def = checkEnergyUSAC(decSamples_def, decParams, nCh, 1, 0); 352 float[] nrg_test = checkEnergyUSAC(decSamples_test, decParams, nCh, 1, 1); 353 354 if (nCh == 2) { 355 float nrgRatio_L = (nrg_test[1]/nrg_def[1])/normFactor_L; 356 float nrgRatio_R = (nrg_test[2]/nrg_def[2])/normFactor_R; 357 if ((nrgRatio_R > 1.05f || nrgRatio_R < 0.95f) 358 || (nrgRatio_L > 1.05f || nrgRatio_L < 0.95f) ){ 359 throw new Exception("DRC Effect Type '" + effectTypeName + "' not as expected"); 360 } 361 } else if (nCh == 1){ 362 float nrgRatio_L = (nrg_test[0]/nrg_def[0])/normFactor_L; 363 if (nrgRatio_L > 1.05f || nrgRatio_L < 0.95f){ 364 throw new Exception("DRC Effect Type '" + effectTypeName + "' not as expected"); 365 } 366 } 367 } 368 369 /** 370 * USAC test stream switching 371 */ checkUsacStreamSwitching(float nrg_ref, int encNch, int testinput, String decoderName)372 private void checkUsacStreamSwitching(float nrg_ref, int encNch, int testinput, 373 String decoderName) throws Exception 374 { 375 AudioParameter decParams = new AudioParameter(); 376 DrcParams drcParams = new DrcParams(127, 127, 64, 0, -1); 377 378 // Check stereo stream switching 379 short[] decSamples = decodeToMemory(decParams, testinput, 380 -1, null, drcParams, decoderName); 381 float[] nrg = checkEnergyUSAC(decSamples, decParams, encNch, 1); 382 383 float nrgRatio = nrg[0] / nrg_ref; 384 385 // Check if energy levels are within 15% of the reference 386 // Energy drops within the decoded stream are checked by checkEnergyUSAC() within every 387 // 250ms interval 388 if (nrgRatio > 1.15f || nrgRatio < 0.85f ) { 389 throw new Exception("Config switching not as expected"); 390 } 391 } 392 393 /** 394 * USAC test sampling rate 395 */ checkUsacSamplingRate(int testinput, String decoderName)396 private void checkUsacSamplingRate(int testinput, String decoderName) throws Exception { 397 AudioParameter decParams = new AudioParameter(); 398 DrcParams drcParams_def = new DrcParams(127, 127, 64, 0, -1); 399 DrcParams drcParams_test = new DrcParams(127, 127, 96, 0, -1); 400 401 short[] decSamples_def = decodeToMemory(decParams, testinput, 402 -1, null, drcParams_def, decoderName); 403 short[] decSamples_test = decodeToMemory(decParams, testinput, 404 -1, null, drcParams_test, decoderName); 405 406 float[] nrg_def = checkEnergyUSAC(decSamples_def, decParams, 2, 1); 407 float[] nrg_test = checkEnergyUSAC(decSamples_test, decParams, 2, 1); 408 409 float nrgRatio = nrg_def[0]/nrg_test[0]; 410 411 // normFactor = 1/(10^(-8/10)) = 6.3f 412 nrgRatio = nrgRatio / 6.3f; 413 414 // Check whether behavior is as expected 415 if (nrgRatio > 1.05f || nrgRatio < 0.95f ){ 416 throw new Exception("Sampling rate not supported"); 417 } 418 } 419 420 /** 421 * USAC test sampling rate for streams without loudness application 422 */ checkUsacSamplingRateWoLoudness(int testinput, String decoderName)423 private void checkUsacSamplingRateWoLoudness(int testinput, String decoderName) throws Exception 424 { 425 AudioParameter decParams = new AudioParameter(); 426 DrcParams drcParams = new DrcParams(); 427 428 short[] decSamples = decodeToMemory(decParams, testinput, -1, null, drcParams, decoderName); 429 430 float[] nrg = checkEnergyUSAC(decSamples, decParams, 2, 1); 431 432 float nrg_ref = 3.15766394E12f; 433 float nrgRatio = nrg_ref/nrg[0]; 434 435 // Check whether behavior is as expected 436 if (nrgRatio > 1.05f || nrgRatio < 0.95f ){ 437 throw new Exception("Sampling rate not supported"); 438 } 439 } 440 441 /** 442 * Perform a segmented energy analysis on given audio signal samples and run several tests on 443 * the energy values. 444 * 445 * The main purpose is to verify whether a USAC decoder implementation applies Spectral Band 446 * Replication (SBR), Parametric Stereo (PS) and Dynamic Range Control (DRC) correctly. All 447 * tools are inherent parts to either the MPEG-D USAC audio codec or the MPEG-D DRC tool. 448 * 449 * In addition, this test can verify the correct decoding of multi-channel (e.g. 5.1 channel) 450 * streams or the creation of a downmixed signal. 451 * 452 * Note: This test procedure is not an MPEG Conformance Test and can not serve as a replacement. 453 * 454 * @param decSamples the decoded audio samples to be tested 455 * @param decParams the audio parameters of the given audio samples (decSamples) 456 * @param encNch the encoded number of audio channels (number of channels of the original 457 * input) 458 * @param drcContext indicate to use test criteria applicable for DRC testing 459 * @return array of energies, at index 0: accumulated energy of all channels, and 460 * index 1 and over contain the individual channel energies 461 * @throws RuntimeException 462 */ checkEnergyUSAC(short[] decSamples, AudioParameter decParams, int encNch, int drcContext)463 protected float[] checkEnergyUSAC(short[] decSamples, AudioParameter decParams, 464 int encNch, int drcContext) 465 { 466 final float[] nrg = checkEnergyUSAC(decSamples, decParams, encNch, drcContext, 0); 467 return nrg; 468 } 469 470 /** 471 * Same as {@link #checkEnergyUSAC(short[], AudioParameter, int, int)} but with DRC effect type 472 * @param decSamples 473 * @param decParams 474 * @param encNch 475 * @param drcContext 476 * @param drcApplied indicate if MPEG-D DRC Effect Type has been applied 477 * @return 478 * @throws RuntimeException 479 */ checkEnergyUSAC(short[] decSamples, AudioParameter decParams, int encNch, int drcContext, int drcApplied)480 private float[] checkEnergyUSAC(short[] decSamples, AudioParameter decParams, 481 int encNch, int drcContext, int drcApplied) 482 throws RuntimeException 483 { 484 String localTag = TAG + "#checkEnergyUSAC"; 485 486 // the number of segments per block 487 final int nSegPerBlk = 4; 488 // the number of input channels 489 final int nCh = encNch; 490 // length of one (LB/HB) block [samples] 491 final int nBlkSmp = decParams.getSamplingRate(); 492 // length of one segment [samples] 493 final int nSegSmp = nBlkSmp / nSegPerBlk; 494 // actual # samples per channel (total) 495 final int smplPerChan = decSamples.length / nCh; 496 // actual # samples per segment (all ch) 497 final int nSegSmpTot = nSegSmp * nCh; 498 // signal offset between chans [segments] 499 final int nSegChOffst = 2 * nSegPerBlk; 500 // // the number of channels to be analyzed 501 final int procNch = Math.min(nCh, encNch); 502 // all original configs with more than five channel have an LFE 503 final int encEffNch = (encNch > 5) ? encNch-1 : encNch; 504 // expected number of decoded audio samples 505 final int expSmplPerChan = Math.max(encEffNch, 2) * nSegChOffst * nSegSmp; 506 // flag telling that input is dmx signal 507 final boolean isDmx = nCh < encNch; 508 final float nrgRatioThresh = 0.0f; 509 // the num analyzed channels with signal 510 int effProcNch = procNch; 511 512 // get the signal offset by counting zero samples at the very beginning (over all channels) 513 // sample value threshold 4 signal search 514 final int zeroSigThresh = 1; 515 // receives the number of samples that are in front of the actual signal 516 int signalStart = smplPerChan; 517 // receives the number of null samples (per chan) at the very beginning 518 int noiseStart = signalStart; 519 520 for (int smpl = 0; smpl < decSamples.length; smpl++) { 521 int value = Math.abs(decSamples[smpl]); 522 523 if (value > 0 && noiseStart == signalStart) { 524 // store start of prepended noise (can be same as signalStart) 525 noiseStart = smpl / nCh; 526 } 527 528 if (value > zeroSigThresh) { 529 // store signal start offset [samples] 530 signalStart = smpl / nCh; 531 break; 532 } 533 } 534 535 signalStart = (signalStart > noiseStart+1) ? signalStart : noiseStart; 536 537 // check if the decoder reproduced a waveform or kept silent 538 assertTrue ("no signal found in any channel!", signalStart < smplPerChan); 539 540 // max num seg that fit into signal 541 final int totSeg = (smplPerChan - signalStart) / nSegSmp; 542 // max num relevant samples (per channel) 543 final int totSmp = nSegSmp * totSeg; 544 545 // check if the number of reproduced segments in the audio file is valid 546 assertTrue("no segments left to test after signal search", totSeg > 0); 547 548 // get the energies and the channel offsets by searching for the first segment above the 549 // energy threshold: 550 // ratio of zeroNrgThresh to the max nrg 551 final double zeroMaxNrgRatio = 0.001f; 552 // threshold to classify segment energies 553 double zeroNrgThresh = nSegSmp * nSegSmp; 554 // will store the max seg nrg over all ch 555 double totMaxNrg = 0.0f; 556 // array receiving the segment energies 557 double[][] nrg = new double[procNch][totSeg]; 558 // array for channel offsets 559 int[] offset = new int[procNch]; 560 // array receiving the segment energy status over all channels 561 boolean[] sigSeg = new boolean[totSeg]; 562 // energy per channel 563 double[] nrgPerChannel = new double[procNch]; 564 // return value: [0]: total energy of all channels 565 // [1 ... procNch + 1]: energy of the individual channels 566 float[] nrgTotal = new float[procNch + 1]; 567 // mapping array to sort channels 568 int[] chMap = new int[nCh]; 569 570 // calculate the segmental energy for each channel 571 for (int ch = 0; ch < procNch; ch++) { 572 offset[ch] = -1; 573 574 for (int seg = 0; seg < totSeg; seg++) { 575 final int smpStart = (signalStart * nCh) + (seg * nSegSmpTot) + ch; 576 final int smpStop = smpStart + nSegSmpTot; 577 578 for (int smpl = smpStart; smpl < smpStop; smpl += nCh) { 579 // accumulate total energy per channel 580 nrgPerChannel[ch] += decSamples[smpl] * decSamples[smpl]; 581 // accumulate segment energy 582 nrg[ch][seg] += nrgPerChannel[ch]; 583 } 584 585 // store 1st segment (index) per channel which has energy above the threshold to get 586 // the ch offsets 587 if (nrg[ch][seg] > zeroNrgThresh && offset[ch] < 0) { 588 offset[ch] = seg / nSegChOffst; 589 } 590 591 // store the max segment nrg over all ch 592 if (nrg[ch][seg] > totMaxNrg) { 593 totMaxNrg = nrg[ch][seg]; 594 } 595 596 // store whether the channel has energy in this segment 597 sigSeg[seg] |= nrg[ch][seg] > zeroNrgThresh; 598 } 599 600 // if one channel has no signal it is most probably the LFE the LFE is no 601 // effective channel 602 if (offset[ch] < 0) { 603 effProcNch -= 1; 604 offset[ch] = effProcNch; 605 } 606 607 // recalculate the zero signal threshold based on the 1st channels max energy for 608 // all subsequent checks 609 if (ch == 0) { 610 zeroNrgThresh = zeroMaxNrgRatio * totMaxNrg; 611 } 612 } 613 614 // check if the LFE is decoded properly 615 assertTrue("more than one LFE detected", effProcNch >= procNch - 1); 616 617 // check if the amount of samples is valid 618 assertTrue(String.format("less samples decoded than expected: %d < %d", 619 decSamples.length - (signalStart * nCh), 620 totSmp * effProcNch), 621 decSamples.length - (signalStart * nCh) >= totSmp * effProcNch); 622 623 // for multi-channel signals the only valid front channel orders 624 // are L, R, C or C, L, R (L=left, R=right, C=center) 625 if (procNch >= 5) { 626 final int[] frontChMap1 = {2, 0, 1}; 627 final int[] frontChMap2 = {0, 1, 2}; 628 629 // check if the channel mapping is valid 630 if (!(Arrays.equals(Arrays.copyOfRange(offset, 0, 3), frontChMap1) 631 || Arrays.equals(Arrays.copyOfRange(offset, 0, 3), frontChMap2))) { 632 fail("wrong front channel mapping"); 633 } 634 } 635 636 // create mapping table to address channels from front to back the LFE must be last 637 if (drcContext == 0) { 638 // check whether every channel occurs exactly once 639 for (int ch = 0; ch < effProcNch; ch++) { 640 int occurred = 0; 641 642 for (int idx = 0; idx < procNch; idx++) { 643 if (offset[idx] == ch) { 644 occurred += 1; 645 chMap[ch] = idx; 646 } 647 } 648 649 // check if one channel is mapped more than one time 650 assertTrue(String.format("channel %d occurs %d times in the mapping", ch, occurred), 651 occurred == 1); 652 } 653 } else { 654 for (int ch = 0; ch < procNch; ch++) { 655 chMap[ch] = ch; 656 } 657 } 658 659 // reference min energy for the 1st ch; others will be compared against 1st 660 double refMinNrg = zeroNrgThresh; 661 662 // calculate total energy, min and max energy 663 for (int ch = 0; ch < procNch; ch++) { 664 // resolve channel mapping 665 int idx = chMap[ch]; 666 // signal offset [segments] 667 final int ofst = offset[idx] * nSegChOffst; 668 669 if (ch <= effProcNch && ofst < totSeg) { 670 // the last segment that has energy 671 int nrgSegEnd; 672 // the number of segments with energy 673 int nrgSeg; 674 675 if (drcContext == 0) { 676 677 // the first channel of a mono or stereo signal has full signal all others have 678 // one LB + one HB block 679 if ((encNch <= 2) && (ch == 0)) { 680 nrgSeg = totSeg; 681 } else { 682 nrgSeg = Math.min(totSeg, (2 * nSegPerBlk) + ofst) - ofst; 683 } 684 } else { 685 nrgSeg = totSeg; 686 } 687 688 nrgSegEnd = ofst + nrgSeg; 689 690 // find min and max energy of all segments that should have signal 691 double minNrg = nrg[idx][ofst]; // channels minimum segment energy 692 double maxNrg = nrg[idx][ofst]; // channels maximum segment energy 693 694 // values of 1st segment already assigned 695 for (int seg = ofst + 1; seg < nrgSegEnd; seg++) { 696 if (nrg[idx][seg] < minNrg) { 697 minNrg = nrg[idx][seg]; 698 } 699 700 if (nrg[idx][seg] > maxNrg) { 701 maxNrg = nrg[idx][seg]; 702 } 703 } 704 705 // check if the energy of this channel is > 0 706 assertTrue(String.format("max energy of channel %d is zero", ch),maxNrg > 0.0f); 707 708 if (drcContext == 0) { 709 // check the channels minimum energy >= refMinNrg 710 assertTrue(String.format("channel %d has not enough energy", ch), 711 minNrg >= refMinNrg); 712 713 if (ch == 0) { 714 // use 85% of 1st channels min energy as reference the other chs must meet 715 refMinNrg = minNrg * 0.85f; 716 } else if (isDmx && (ch == 1)) { 717 // in case of downmixed signal the energy can be lower depending on the 718 refMinNrg *= 0.50f; 719 } 720 721 // calculate and check the energy ratio 722 final double nrgRatio = minNrg / maxNrg; 723 724 // check if the threshold is exceeded 725 assertTrue(String.format("energy ratio of channel %d below threshold", ch), 726 nrgRatio >= nrgRatioThresh); 727 728 if (!isDmx) { 729 if (nrgSegEnd < totSeg) { 730 // consider that some noise can extend into the subsequent segment 731 // allow this to be at max 20% of the channels minimum energy 732 assertTrue( 733 String.format("min energy after noise above threshold (%.2f)", 734 nrg[idx][nrgSegEnd]), 735 nrg[idx][nrgSegEnd] < minNrg * 0.20f); 736 nrgSegEnd += 1; 737 } 738 } else { 739 // ignore all subsequent segments in case of a downmixed signal 740 nrgSegEnd = totSeg; 741 } 742 743 // zero-out the verified energies to simplify the subsequent check 744 for (int seg = ofst; seg < nrgSegEnd; seg++) { 745 nrg[idx][seg] = 0.0f; 746 } 747 748 // check zero signal parts 749 for (int seg = 0; seg < totSeg; seg++) { 750 assertTrue(String.format("segment %d in channel %d has signal where should " 751 + "be none (%.2f)", seg, ch, nrg[idx][seg]), 752 nrg[idx][seg] < zeroNrgThresh); 753 } 754 } 755 } 756 757 // test whether each segment has energy in at least one channel 758 for (int seg = 0; seg < totSeg; seg++) { 759 assertTrue(String.format("no channel has energy in segment %d", seg), sigSeg[seg]); 760 } 761 762 nrgTotal[0] += (float)nrgPerChannel[ch]; 763 nrgTotal[ch + 1] = (float)nrgPerChannel[ch]; 764 } 765 766 float errorMargin = 0.0f; 767 if (drcApplied == 0) { 768 errorMargin = 0.25f; 769 } else if (drcApplied == 1) { 770 errorMargin = 0.40f; 771 } 772 773 float totSegEnergy = 0.0f; 774 float[] segEnergy = new float[totSeg]; 775 for (int seg = totSeg - 1; seg >= 0; seg--) { 776 if (seg != 0) { 777 for (int ch = 0; ch < nCh; ch++) { 778 segEnergy[seg] += nrg[ch][seg] - nrg[ch][seg - 1]; 779 } 780 totSegEnergy += segEnergy[seg]; 781 } else { 782 for (int ch = 0; ch < nCh; ch++) { 783 segEnergy[seg] += nrg[ch][seg]; 784 } 785 } 786 } 787 float avgSegEnergy = totSegEnergy / (totSeg - 1); 788 for (int seg = 1; seg < totSeg; seg++) { 789 float energyRatio = segEnergy[seg] / avgSegEnergy; 790 if ((energyRatio > (1.0f + errorMargin) ) || (energyRatio < (1.0f - errorMargin) )) { 791 fail("Energy drop out"); 792 } 793 } 794 795 // return nrgTotal: [0]: accumulated energy of all channels, [1 ... ] channel energies 796 return nrgTotal; 797 } 798 799 /** 800 * Decodes a compressed bitstream in the ISOBMFF into the RAM of the device. 801 * 802 * The decoder decodes compressed audio data as stored in the ISO Base Media File Format 803 * (ISOBMFF) aka .mp4/.m4a. The decoder is not reproducing the waveform but stores the decoded 804 * samples in the RAM of the device under test. 805 * 806 * @param audioParams the decoder parameter configuration 807 * @param testinput the compressed audio stream 808 * @param eossample the End-Of-Stream indicator 809 * @param timestamps the time stamps to decode 810 * @param drcParams the MPEG-D DRC decoder parameter configuration 811 * @param decoderName if non null, the name of the decoder to use for the decoding, otherwise 812 * the default decoder for the format will be used 813 * @throws RuntimeException 814 */ 815 public short[] decodeToMemory(AudioParameter audioParams, int testinput, 816 int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName) 817 throws IOException 818 { 819 // TODO: code is the same as in DecoderTest, differences are: 820 // - addition of application of DRC parameters 821 // - no need/use of resetMode, configMode 822 // Split method so code can be shared 823 824 final String localTag = TAG + "#decodeToMemory"; 825 short [] decoded = new short[0]; 826 int decodedIdx = 0; 827 828 AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput); 829 830 MediaExtractor extractor; 831 MediaCodec codec; 832 ByteBuffer[] codecInputBuffers; 833 ByteBuffer[] codecOutputBuffers; 834 835 extractor = new MediaExtractor(); 836 extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(), 837 testFd.getLength()); 838 testFd.close(); 839 840 assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); 841 MediaFormat format = extractor.getTrackFormat(0); 842 String mime = format.getString(MediaFormat.KEY_MIME); 843 assertTrue("not an audio file", mime.startsWith("audio/")); 844 845 MediaFormat configFormat = format; 846 if (decoderName == null) { 847 codec = MediaCodec.createDecoderByType(mime); 848 } else { 849 codec = MediaCodec.createByCodecName(decoderName); 850 } 851 852 // set DRC parameters 853 if (drcParams != null) { 854 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_BOOST_FACTOR, drcParams.mBoost); 855 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_ATTENUATION_FACTOR, drcParams.mCut); 856 if (drcParams.mDecoderTargetLevel != 0) { 857 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL, 858 drcParams.mDecoderTargetLevel); 859 } 860 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION, drcParams.mHeavy); 861 if (drcParams.mEffectType != 0){ 862 configFormat.setInteger(MediaFormat.KEY_AAC_DRC_EFFECT_TYPE, 863 drcParams.mEffectType); 864 } 865 } 866 867 Log.v(localTag, "configuring with " + configFormat); 868 codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */); 869 870 codec.start(); 871 codecInputBuffers = codec.getInputBuffers(); 872 codecOutputBuffers = codec.getOutputBuffers(); 873 874 extractor.selectTrack(0); 875 876 // start decoding 877 final long kTimeOutUs = 5000; 878 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 879 boolean sawInputEOS = false; 880 boolean sawOutputEOS = false; 881 int noOutputCounter = 0; 882 int samplecounter = 0; 883 884 // main decoding loop 885 while (!sawOutputEOS && noOutputCounter < 50) { 886 noOutputCounter++; 887 if (!sawInputEOS) { 888 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs); 889 890 if (inputBufIndex >= 0) { 891 ByteBuffer dstBuf = codecInputBuffers[inputBufIndex]; 892 893 int sampleSize = 894 extractor.readSampleData(dstBuf, 0 /* offset */); 895 896 long presentationTimeUs = 0; 897 898 if (sampleSize < 0 && eossample > 0) { 899 fail("test is broken: never reached eos sample"); 900 } 901 902 if (sampleSize < 0) { 903 Log.d(TAG, "saw input EOS."); 904 sawInputEOS = true; 905 sampleSize = 0; 906 } else { 907 if (samplecounter == eossample) { 908 sawInputEOS = true; 909 } 910 samplecounter++; 911 presentationTimeUs = extractor.getSampleTime(); 912 } 913 914 codec.queueInputBuffer( 915 inputBufIndex, 916 0 /* offset */, 917 sampleSize, 918 presentationTimeUs, 919 sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); 920 921 if (!sawInputEOS) { 922 extractor.advance(); 923 } 924 } 925 } 926 927 int res = codec.dequeueOutputBuffer(info, kTimeOutUs); 928 929 if (res >= 0) { 930 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs); 931 932 if (info.size > 0) { 933 noOutputCounter = 0; 934 if (timestamps != null) { 935 timestamps.add(info.presentationTimeUs); 936 } 937 } 938 939 int outputBufIndex = res; 940 ByteBuffer buf = codecOutputBuffers[outputBufIndex]; 941 942 if (decodedIdx + (info.size / 2) >= decoded.length) { 943 decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2)); 944 } 945 946 buf.position(info.offset); 947 for (int i = 0; i < info.size; i += 2) { 948 decoded[decodedIdx++] = buf.getShort(); 949 } 950 951 codec.releaseOutputBuffer(outputBufIndex, false /* render */); 952 953 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 954 Log.d(TAG, "saw output EOS."); 955 sawOutputEOS = true; 956 } 957 } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 958 codecOutputBuffers = codec.getOutputBuffers(); 959 960 Log.d(TAG, "output buffers have changed."); 961 } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 962 MediaFormat oformat = codec.getOutputFormat(); 963 audioParams.setNumChannels(oformat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); 964 audioParams.setSamplingRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE)); 965 Log.d(TAG, "output format has changed to " + oformat); 966 } else { 967 Log.d(TAG, "dequeueOutputBuffer returned " + res); 968 } 969 } 970 971 if (noOutputCounter >= 50) { 972 fail("decoder stopped outputting data"); 973 } 974 975 codec.stop(); 976 codec.release(); 977 return decoded; 978 } 979 980 } 981 982