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