• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.MediaExtractor;
29 import android.media.MediaFormat;
30 import android.media.decoder.cts.DecoderTest.AudioParameter;
31 import android.os.Build;
32 import android.os.Bundle;
33 import android.platform.test.annotations.AppModeFull;
34 import android.util.Log;
35 
36 import androidx.test.InstrumentationRegistry;
37 
38 import com.android.compatibility.common.util.ApiLevelUtil;
39 import com.android.compatibility.common.util.MediaUtils;
40 
41 import org.junit.Before;
42 import org.junit.Test;
43 
44 import java.io.IOException;
45 import java.nio.ByteBuffer;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.List;
49 
50 @AppModeFull(reason = "DecoderTest is non-instant")
51 public class DecoderTestAacDrc {
52     private static final String TAG = "DecoderTestAacDrc";
53 
54     private static final boolean sIsAndroidRAndAbove =
55             ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
56 
57     private Resources mResources;
58 
59     @Before
setUp()60     public void setUp() throws Exception {
61         final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
62         assertNotNull(inst);
63         mResources = inst.getContext().getResources();
64     }
65 
66     /**
67      * Verify correct decoding of MPEG-4 AAC with output level normalization to -23dBFS.
68      */
69     @Test
testDecodeAacDrcLevelM4a()70     public void testDecodeAacDrcLevelM4a() throws Exception {
71         AudioParameter decParams = new AudioParameter();
72         // full boost, full cut, target ref level: -23dBFS, heavy compression: no
73         DrcParams drcParams = new DrcParams(127, 127, 92, 0);
74         short[] decSamples = decodeToMemory(decParams, R.raw.sine_2ch_48khz_aot5_drclevel_mp4,
75                 -1, null, drcParams, null /*decoderName: use default decoder*/);
76         DecoderTest decTester = new DecoderTest();
77         decTester.checkEnergy(decSamples, decParams, 2, 0.70f);
78     }
79 
80     /**
81      * Verify correct decoding of MPEG-4 AAC with Dynamic Range Control (DRC) metadata.
82      * Fully apply light compression DRC (default settings).
83      */
84     @Test
testDecodeAacDrcFullM4a()85     public void testDecodeAacDrcFullM4a() throws Exception {
86         AudioParameter decParams = new AudioParameter();
87         short[] decSamples = decodeToMemory(decParams, R.raw.sine_2ch_48khz_aot5_drcfull_mp4,
88                 -1, null, null, null /*decoderName: use default decoder*/);
89         DecoderTest decTester = new DecoderTest();
90         decTester.checkEnergy(decSamples, decParams, 2, 0.80f);
91     }
92 
93     /**
94      * Verify correct decoding of MPEG-4 AAC with Dynamic Range Control (DRC) metadata.
95      * Apply only half of the light compression DRC and normalize to -20dBFS output level.
96      */
97     @Test
testDecodeAacDrcHalfM4a()98     public void testDecodeAacDrcHalfM4a() throws Exception {
99         AudioParameter decParams = new AudioParameter();
100         // half boost, half cut, target ref level: -20dBFS, heavy compression: no
101         DrcParams drcParams = new DrcParams(63, 63, 80, 0);
102         short[] decSamples = decodeToMemory(decParams, R.raw.sine_2ch_48khz_aot2_drchalf_mp4,
103                 -1, null, drcParams, null /*decoderName: use default decoder*/);
104         DecoderTest decTester = new DecoderTest();
105         decTester.checkEnergy(decSamples, decParams, 2, 0.80f);
106     }
107 
108     /**
109      * Verify correct decoding of MPEG-4 AAC with Dynamic Range Control (DRC) metadata.
110      * Disable light compression DRC to test if MediaFormat keys reach the decoder.
111      */
112     @Test
testDecodeAacDrcOffM4a()113     public void testDecodeAacDrcOffM4a() throws Exception {
114         AudioParameter decParams = new AudioParameter();
115         // no boost, no cut, target ref level: -16dBFS, heavy compression: no
116         DrcParams drcParams = new DrcParams(0, 0, 64, 0);       // normalize to -16dBFS
117         short[] decSamples = decodeToMemory(decParams, R.raw.sine_2ch_48khz_aot5_drcoff_mp4,
118                 -1, null, drcParams, null /*decoderName: use default decoder*/);
119         DecoderTest decTester = new DecoderTest();
120         decTester.checkEnergy(decSamples, decParams, 2, 0.80f);
121     }
122 
123     /**
124      * Verify correct decoding of MPEG-4 AAC with Dynamic Range Control (DRC) metadata.
125      * Apply heavy compression gains and normalize to -16dBFS output level.
126      */
127     @Test
testDecodeAacDrcHeavyM4a()128     public void testDecodeAacDrcHeavyM4a() throws Exception {
129         AudioParameter decParams = new AudioParameter();
130         // full boost, full cut, target ref level: -16dBFS, heavy compression: yes
131         DrcParams drcParams = new DrcParams(127, 127, 64, 1);
132         short[] decSamples = decodeToMemory(decParams, R.raw.sine_2ch_48khz_aot2_drcheavy_mp4,
133                 -1, null, drcParams, null /*decoderName: use default decoder*/);
134         DecoderTest decTester = new DecoderTest();
135         decTester.checkEnergy(decSamples, decParams, 2, 0.80f);
136     }
137 
138     /**
139      * Test signal limiting (without clipping) of MPEG-4 AAC decoder with the help of DRC metadata.
140      * Uses a two channel 248 Hz sine tone at 48 kHz sampling rate for input.
141      */
142     @Test
testDecodeAacDrcClipM4a()143     public void testDecodeAacDrcClipM4a() throws Exception {
144         AudioParameter decParams = new AudioParameter();
145         short[] decSamples = decodeToMemory(decParams, R.raw.sine_2ch_48khz_aot5_drcclip_mp4,
146                 -1, null, null, null /*decoderName: use default decoder*/);
147         checkClipping(decSamples, decParams, 248.0f /* Hz */);
148     }
149 
150     /**
151      * Test if there is decoder internal clipping of MPEG-4 AAC decoder.
152      * Uses a two channel 248 Hz sine tone at 48 kHz sampling rate for input.
153      */
154     @Test
testDecodeAacInternalClipM4a()155     public void testDecodeAacInternalClipM4a() throws Exception {
156         if (!MediaUtils.check(sIsAndroidRAndAbove, "Internal clipping fixed in Android R"))
157                 return;
158         AudioParameter decParams = new AudioParameter();
159         short[] decSamples = decodeToMemory(decParams, R.raw.sine_2ch_48khz_aot2_internalclip_mp4,
160                 -1, null, null, null /*decoderName: use default decoder*/);
161         checkClipping(decSamples, decParams, 248.0f /* Hz */);
162     }
163 
164     /**
165      * Default decoder target level.
166      * The actual default value used by the decoder can differ between platforms, or even devices,
167      * but tests will measure energy relative to this value.
168      */
169     public static final int DEFAULT_DECODER_TARGET_LEVEL = 64; // -16.0 dBFs
170 
171     /**
172      * Test USAC decoder with different target loudness levels
173      */
174     @Test
testDecodeUsacLoudnessM4a()175     public void testDecodeUsacLoudnessM4a() throws Exception {
176         Log.v(TAG, "START testDecodeUsacLoudnessM4a");
177 
178         ArrayList<String> aacDecoderNames = DecoderTestXheAac.initAacDecoderNames();
179         assertTrue("No AAC decoder found", aacDecoderNames.size() > 0);
180 
181         for (String aacDecName : aacDecoderNames) {
182             // test default loudness
183             // decoderTargetLevel = 64 --> target output level = -16.0 dBFs
184             try {
185                 checkUsacLoudness(DEFAULT_DECODER_TARGET_LEVEL, 1, 1.0f, aacDecName);
186             } catch (Exception e) {
187                 Log.v(TAG, "testDecodeUsacLoudnessM4a for default loudness failed for " +
188                         aacDecName);
189                 throw new RuntimeException(e);
190             }
191 
192             // test loudness boost
193             // decoderTargetLevel = 40 --> target output level = -10.0 dBFs
194             // normFactor = 1/(10^(-6/10)) = 3.98f
195             //   where "-6" is the difference between the default level (-16), and -10 for this test
196             try {
197                 checkUsacLoudness(40, 1, (float)(1.0f/Math.pow(10.0f, -6.0f/10.0f)), aacDecName);
198             } catch (Exception e) {
199                 Log.v(TAG, "testDecodeUsacLoudnessM4a for loudness boost failed for " + aacDecName);
200                 throw new RuntimeException(e);
201             }
202 
203             // test loudness attenuation
204             // decoderTargetLevel = 96 --> target output level = -24.0 dBFs
205             // normFactor = 1/(10^(8/10)) = 0.15f
206             //     where 8 is the difference between the default level (-16), and -24 for this test
207             try {
208                 checkUsacLoudness(96, 0, (float)(1.0f/Math.pow(10.0f, 8.0f/10.0f)), aacDecName);
209             } catch (Exception e) {
210                 Log.v(TAG, "testDecodeUsacLoudnessM4a for loudness attenuation failed for "
211                         + aacDecName);
212                 throw new RuntimeException(e);
213             }
214 
215             if (sIsAndroidRAndAbove) {
216                 // test loudness normalization off
217                 // decoderTargetLevel = -1 --> target output level = -19.0 dBFs (program loudness of
218                 // waveform)
219                 // normFactor = 1/(10^(3/10)) = 0.5f
220                 // where 3 is the difference between the default level (-16), and -19 for this test
221                 try {
222                     checkUsacLoudness(-1, 0, (float) (1.0f / Math.pow(10.0f, 3.0f / 10.0f)),
223                             aacDecName);
224                 } catch (Exception e) {
225                     Log.v(TAG, "testDecodeUsacLoudnessM4a for loudness attenuation failed for "
226                             + aacDecName);
227                     throw new RuntimeException(e);
228                 }
229             }
230         }
231     }
232 
233     /**
234      * Verify that the correct output loudness values are returned by the MPEG-4 AAC decoder
235      */
236     @Test
testDecodeAacDrcOutputLoudnessM4a()237     public void testDecodeAacDrcOutputLoudnessM4a() throws Exception {
238         Log.v(TAG, "START testDecodeAacDrcOutputLoudnessM4a");
239 
240         ArrayList<String> aacDecoderNames = DecoderTestXheAac.initAacDecoderNames();
241         assertTrue("No AAC decoder found", aacDecoderNames.size() > 0);
242 
243         for (String aacDecName : aacDecoderNames) {
244             // test drc output loudness
245             // testfile without loudness metadata and loudness normalization off
246             // -> expected value: -1
247             try {
248                 checkAacDrcOutputLoudness(
249                         R.raw.noise_1ch_24khz_aot5_dr_sbr_sig1_mp4, -1, -1, aacDecName);
250             } catch (Exception e) {
251                 Log.v(TAG, "testDecodeUsacLoudnessM4a for default loudness failed for " +
252                         aacDecName);
253                 throw new RuntimeException(e);
254             }
255             // test drc output loudness
256             // testfile without loudness metadata and loudness normalization on
257             // -> expected value: -1
258             try {
259                 checkAacDrcOutputLoudness(
260                         R.raw.noise_1ch_24khz_aot5_dr_sbr_sig1_mp4, 70, -1, aacDecName);
261             } catch (Exception e) {
262                 Log.v(TAG, "testDecodeUsacLoudnessM4a for default loudness failed for " +
263                         aacDecName);
264                 throw new RuntimeException(e);
265             }
266             // test drc output loudness
267             // testfile with MPEG-4 DRC loudness metadata and loudness normalization off
268             // -> expected value: loudness metadata in bitstream (-16*-4 = 64)
269             try {
270                 checkAacDrcOutputLoudness(
271                         R.raw.sine_2ch_48khz_aot2_drchalf_mp4, -1, 64, aacDecName);
272             } catch (Exception e) {
273                 Log.v(TAG, "testDecodeUsacLoudnessM4a for default loudness failed for " +
274                         aacDecName);
275                 throw new RuntimeException(e);
276             }
277             // test drc output loudness
278             // testfile with MPEG-4 DRC loudness metadata and loudness normalization off
279             // -> expected value: loudness metadata in bitstream (-31*-4 = 124)
280             try {
281                 checkAacDrcOutputLoudness(
282                         R.raw.sine_2ch_48khz_aot5_drcclip_mp4, -1, 124, aacDecName);
283             } catch (Exception e) {
284                 Log.v(TAG, "testDecodeUsacLoudnessM4a for default loudness failed for " +
285                         aacDecName);
286                 throw new RuntimeException(e);
287             }
288             // test drc output loudness
289             // testfile with MPEG-4 DRC loudness metadata and loudness normalization on
290             // -> expected value: target loudness value (85)
291             try {
292                 checkAacDrcOutputLoudness(
293                         R.raw.sine_2ch_48khz_aot5_drcclip_mp4, 85, 85, aacDecName);
294             } catch (Exception e) {
295                 Log.v(TAG, "testDecodeUsacLoudnessM4a for default loudness failed for " +
296                         aacDecName);
297                 throw new RuntimeException(e);
298             }
299         }
300     }
301 
302     /**
303      *  Internal utilities
304      */
305 
306     /**
307      * The test routine performs a THD+N (Total Harmonic Distortion + Noise) analysis on a given
308      * audio signal (decSamples). The THD+N value is defined here as harmonic distortion (+ noise)
309      * RMS over full signal RMS.
310      *
311      * After the energy measurement of the unprocessed signal the routine creates and applies a
312      * notch filter at the given frequency (sineFrequency). Afterwards the signal energy is
313      * measured again. Then the THD+N value is calculated as the ratio of the filtered and the full
314      * signal energy.
315      *
316      * The test passes if the THD+N value is lower than -60 dB. Otherwise it fails.
317      *
318      * @param decSamples the decoded audio samples to be tested
319      * @param decParams the audio parameters of the given audio samples (decSamples)
320      * @param sineFrequency frequency of the test signal tone used for testing
321      * @throws RuntimeException
322      */
checkClipping(short[] decSamples, AudioParameter decParams, float sineFrequency)323     private void checkClipping(short[] decSamples, AudioParameter decParams, float sineFrequency)
324             throws RuntimeException
325     {
326         final double threshold_clipping = -60.0; // dB
327         final int numChannels = decParams.getNumChannels();
328         final int startSample = 2 * 2048 * numChannels;          // exclude signal on- & offset to
329         final int stopSample = decSamples.length - startSample;  // ... measure only the stationary
330                                                                  // ... sine tone
331         // get full energy of signal (all channels)
332         double nrgFull = getEnergy(decSamples, startSample, stopSample);
333 
334         // create notch filter to suppress sine-tone at 248 Hz
335         Biquad filter = new Biquad(sineFrequency, decParams.getSamplingRate());
336         for (int channel = 0; channel < numChannels; channel++) {
337             // apply notch-filter on buffer for each channel to filter out the sine tone.
338             // only the harmonics (and noise) remain. */
339             filter.apply(decSamples, channel, numChannels);
340         }
341 
342         // get energy of harmonic distortion (signal without sine-tone)
343         double nrgHd = getEnergy(decSamples, startSample, stopSample);
344 
345         // Total Harmonic Distortion + Noise, defined here as harmonic distortion (+ noise) RMS
346         // over full signal RMS, given in dB
347         double THDplusN = 10 * Math.log10(nrgHd / nrgFull);
348         assertTrue("signal has clipping samples", THDplusN <= threshold_clipping);
349     }
350 
351     /**
352      * Measure the energy of a given signal over all channels within a given signal range.
353      * @param signal audio signal samples
354      * @param start start offset of the measuring range
355      * @param stop stop sample which is the last sample of the measuring range
356      * @return the signal energy in the given range
357      */
getEnergy(short[] signal, int start, int stop)358     private double getEnergy(short[] signal, int start, int stop) {
359         double nrg = 0.0;
360         for (int sample = start; sample < stop; sample++) {
361             double v = signal[sample];
362             nrg += v * v;
363         }
364         return nrg;
365     }
366 
367     // Notch filter implementation
368     private class Biquad {
369         // filter coefficients for biquad filter (2nd order IIR filter)
370         float[] a;
371         float[] b;
372         // filter states
373         float[] state_ff;
374         float[] state_fb;
375 
376         protected float alpha = 0.95f;
377 
Biquad(float f_notch, float f_s)378         public Biquad(float f_notch, float f_s) {
379             // Create filter coefficients of notch filter which suppresses a sine tone with f_notch
380             // Hz at sampling frequency f_s. Zeros placed at unit circle at f_notch, poles placed
381             // nearby the unit circle at f_notch.
382             state_ff = new float[2];
383             state_fb = new float[2];
384             state_ff[0] = state_ff[1] = state_fb[0] = state_fb[1] = 0.0f;
385 
386             a = new float[3];
387             b = new float[3];
388             double omega = 2.0 * Math.PI * f_notch / f_s;
389             a[0] = b[0] = b[2] = 1.0f;
390             a[1] = -2.0f * alpha * (float)Math.cos(omega);
391             a[2] = alpha * alpha;
392             b[1] = -2.0f * (float)Math.cos(omega);
393         }
394 
apply(short[] signal, int offset, int stride)395         public void apply(short[] signal, int offset, int stride) {
396             // reset states
397             state_ff[0] = state_ff[1] = 0.0f;
398             state_fb[0] = state_fb[1] = 0.0f;
399             // process 2nd order IIR filter in Direct Form I
400             float x_0, x_1, x_2, y_0, y_1, y_2;
401             x_2 = state_ff[0];  // x[n-2]
402             x_1 = state_ff[1];  // x[n-1]
403             y_2 = state_fb[0];  // y[n-2]
404             y_1 = state_fb[1];  // y[n-1]
405             for (int sample = offset; sample < signal.length; sample += stride) {
406                 x_0 = signal[sample];
407                 y_0 = b[0] * x_0 + b[1] * x_1 + b[2] * x_2
408                         - a[1] * y_1 - a[2] * y_2;
409                 x_2 = x_1;
410                 x_1 = x_0;
411                 y_2 = y_1;
412                 y_1 = y_0;
413                 signal[sample] = (short)y_0;
414             }
415             state_ff[0] = x_2;  // next x[n-2]
416             state_ff[1] = x_1;  // next x[n-1]
417             state_fb[0] = y_2;  // next y[n-2]
418             state_fb[1] = y_1;  // next y[n-1]
419         }
420     }
421 
422     /**
423      * USAC test DRC loudness
424      */
checkUsacLoudness(int decoderTargetLevel, int heavy, float normFactor, String decoderName)425     private void checkUsacLoudness(int decoderTargetLevel, int heavy, float normFactor,
426             String decoderName) throws Exception {
427         for (boolean runtimeChange : new boolean[] {false, true}) {
428             if (runtimeChange && !sIsAndroidRAndAbove) {
429                 // changing decoder configuration after it has been initialized requires R and above
430                 continue;
431             }
432             AudioParameter decParams = new AudioParameter();
433             DrcParams drcParams_def  = new DrcParams(127, 127, DEFAULT_DECODER_TARGET_LEVEL, 1);
434             DrcParams drcParams_test = new DrcParams(127, 127, decoderTargetLevel, heavy);
435 
436             short[] decSamples_def = decodeToMemory(decParams,
437                     R.raw.noise_2ch_48khz_aot42_19_lufs_mp4,
438                     -1, null, drcParams_def, decoderName);
439             short[] decSamples_test = decodeToMemory(decParams,
440                     R.raw.noise_2ch_48khz_aot42_19_lufs_mp4,
441                     -1, null, drcParams_test, decoderName, runtimeChange);
442 
443             DecoderTestXheAac decTesterXheAac = new DecoderTestXheAac();
444             float[] nrg_def  = decTesterXheAac.checkEnergyUSAC(decSamples_def, decParams, 2, 1);
445             float[] nrg_test = decTesterXheAac.checkEnergyUSAC(decSamples_test, decParams, 2, 1);
446 
447             float[] nrgThreshold = {2602510595620.0f, 2354652443657.0f};
448 
449             // Check default loudness behavior
450             if (nrg_def[0] > nrgThreshold[0] || nrg_def[0] < nrgThreshold[1]) {
451                 throw new Exception("Default loudness behavior not as expected");
452             }
453 
454             float nrgRatio = nrg_def[0]/nrg_test[0];
455 
456             // Check for loudness boost/attenuation if decoderTargetLevel deviates from default value
457             // used in these tests (note that the default target level can change from platform
458             // to platform, or device to device)
459             if (decoderTargetLevel != -1) {
460                 if ((decoderTargetLevel < DEFAULT_DECODER_TARGET_LEVEL) // boosted loudness
461                         && (nrg_def[0] > nrg_test[0])) {
462                     throw new Exception("Signal not attenuated");
463                 }
464                 if ((decoderTargetLevel > DEFAULT_DECODER_TARGET_LEVEL) // attenuated loudness
465                         && (nrg_def[0] < nrg_test[0])) {
466                     throw new Exception("Signal not boosted");
467                 }
468             }
469             nrgRatio = nrgRatio * normFactor;
470 
471             // Check whether loudness behavior is as expected
472             if (nrgRatio > 1.05f || nrgRatio < 0.95f ){
473                 throw new Exception("Loudness behavior not as expected");
474             }
475         }
476     }
477 
478     /**
479     * AAC test Output Loudness
480     */
checkAacDrcOutputLoudness(int testInput, int decoderTargetLevel, int expectedOutputLoudness, String decoderName)481     private void checkAacDrcOutputLoudness(int testInput, int decoderTargetLevel, int expectedOutputLoudness, String decoderName) throws Exception {
482         for (boolean runtimeChange : new boolean[] {false, true}) {
483             AudioParameter decParams = new AudioParameter();
484             DrcParams drcParams_test = new DrcParams(127, 127, decoderTargetLevel, 0, 6);
485 
486             // Check drc loudness preference
487             short[] decSamples_test = decodeToMemory(decParams, testInput, -1, null,
488                     drcParams_test, decoderName, runtimeChange, expectedOutputLoudness);
489         }
490     }
491 
492 
493     /**
494      *  Class handling all MPEG-4 and MPEG-D Dynamic Range Control (DRC) parameter relevant
495      *  for testing
496      */
497     protected static class DrcParams {
498         int mBoost;                          // scaling of boosting gains
499         int mCut;                            // scaling of compressing gains
500         int mDecoderTargetLevel;             // desired target output level (for normalization)
501         int mHeavy;                          // en-/disable heavy compression
502         int mEffectType;                     // MPEG-D DRC Effect Type
503         int mAlbumMode;                      // MPEG-D DRC Album Mode
504 
DrcParams()505         public DrcParams() {
506             mBoost = 127;               // no scaling
507             mCut   = 127;               // no scaling
508             mHeavy = 1;                 // enabled
509         }
510 
DrcParams(int boost, int cut, int decoderTargetLevel, int heavy)511         public DrcParams(int boost, int cut, int decoderTargetLevel, int heavy) {
512             mBoost = boost;
513             mCut = cut;
514             mDecoderTargetLevel = decoderTargetLevel;
515             mHeavy = heavy;
516         }
517 
DrcParams(int boost, int cut, int decoderTargetLevel, int heavy, int effectType)518         public DrcParams(int boost, int cut, int decoderTargetLevel, int heavy, int effectType) {
519             this(boost, cut, decoderTargetLevel, heavy);
520             mEffectType = effectType;
521         }
522 
DrcParams(int boost, int cut, int decoderTargetLevel, int heavy, int effectType, int albumMode)523         public DrcParams(int boost, int cut, int decoderTargetLevel, int heavy, int effectType,
524                 int albumMode) {
525             this(boost, cut, decoderTargetLevel, heavy, effectType);
526             mAlbumMode = albumMode;
527         }
528     }
529 
530 
531     // TODO: code is the same as in DecoderTest, differences are:
532     //          - addition of application of DRC parameters
533     //          - no need/use of resetMode, configMode
534     //       Split method so code can be shared
535 
decodeToMemory(AudioParameter audioParams, int testinput, int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName, boolean runtimeChange, int expectedOutputLoudness)536     private short[] decodeToMemory(AudioParameter audioParams, int testinput, int eossample,
537             List<Long> timestamps, DrcParams drcParams, String decoderName, boolean runtimeChange,
538             int expectedOutputLoudness)
539             throws IOException
540     {
541         String localTag = TAG + "#decodeToMemory";
542         short [] decoded = new short[0];
543         int decodedIdx = 0;
544 
545         AssetFileDescriptor testFd = mResources.openRawResourceFd(testinput);
546 
547         MediaExtractor extractor;
548         MediaCodec codec;
549         ByteBuffer[] codecInputBuffers;
550         ByteBuffer[] codecOutputBuffers;
551 
552         extractor = new MediaExtractor();
553         extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
554                 testFd.getLength());
555         testFd.close();
556 
557         assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
558         MediaFormat format = extractor.getTrackFormat(0);
559         String mime = format.getString(MediaFormat.KEY_MIME);
560         assertTrue("not an audio file", mime.startsWith("audio/"));
561 
562         MediaFormat configFormat = format;
563         if (decoderName == null) {
564             codec = MediaCodec.createDecoderByType(mime);
565         } else {
566             codec = MediaCodec.createByCodecName(decoderName);
567         }
568 
569         // set DRC parameters
570         if (drcParams != null) {
571             configFormat.setInteger(MediaFormat.KEY_AAC_DRC_BOOST_FACTOR, drcParams.mBoost);
572             configFormat.setInteger(MediaFormat.KEY_AAC_DRC_ATTENUATION_FACTOR, drcParams.mCut);
573             if (!runtimeChange) {
574                 if (drcParams.mDecoderTargetLevel != 0) {
575                     configFormat.setInteger(MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL,
576                             drcParams.mDecoderTargetLevel);
577                 }
578             }
579             configFormat.setInteger(MediaFormat.KEY_AAC_DRC_HEAVY_COMPRESSION, drcParams.mHeavy);
580         }
581         Log.v(localTag, "configuring with " + configFormat);
582         codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
583 
584         if (drcParams != null && sIsAndroidRAndAbove) { // querying output format requires R
585             if(!runtimeChange) {
586                 // check if MediaCodec gives back correct drc parameters
587                 if (drcParams.mDecoderTargetLevel != 0) {
588                     final int targetLevelFromCodec = DecoderTest.getOutputFormatInteger(codec,
589                             MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL);
590                     if (targetLevelFromCodec != drcParams.mDecoderTargetLevel) {
591                         fail("DRC Target Ref Level received from MediaCodec is not the level set");
592                     }
593                 }
594             }
595         }
596 
597         codec.start();
598         codecInputBuffers = codec.getInputBuffers();
599         codecOutputBuffers = codec.getOutputBuffers();
600 
601         if (drcParams != null) {
602             if (runtimeChange) {
603                 if (drcParams.mDecoderTargetLevel != 0) {
604                     Bundle b = new Bundle();
605                     b.putInt(MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL,
606                             drcParams.mDecoderTargetLevel);
607                     codec.setParameters(b);
608                 }
609             }
610         }
611 
612         extractor.selectTrack(0);
613 
614         // start decoding
615         final long kTimeOutUs = 5000;
616         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
617         boolean sawInputEOS = false;
618         boolean sawOutputEOS = false;
619         int noOutputCounter = 0;
620         int samplecounter = 0;
621         while (!sawOutputEOS && noOutputCounter < 50) {
622             noOutputCounter++;
623             if (!sawInputEOS) {
624                 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
625 
626                 if (inputBufIndex >= 0) {
627                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
628 
629                     int sampleSize =
630                         extractor.readSampleData(dstBuf, 0 /* offset */);
631 
632                     long presentationTimeUs = 0;
633 
634                     if (sampleSize < 0 && eossample > 0) {
635                         fail("test is broken: never reached eos sample");
636                     }
637                     if (sampleSize < 0) {
638                         Log.d(TAG, "saw input EOS.");
639                         sawInputEOS = true;
640                         sampleSize = 0;
641                     } else {
642                         if (samplecounter == eossample) {
643                             sawInputEOS = true;
644                         }
645                         samplecounter++;
646                         presentationTimeUs = extractor.getSampleTime();
647                     }
648                     codec.queueInputBuffer(
649                             inputBufIndex,
650                             0 /* offset */,
651                             sampleSize,
652                             presentationTimeUs,
653                             sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
654 
655                     if (!sawInputEOS) {
656                         extractor.advance();
657                     }
658                 }
659             }
660 
661             int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
662 
663             if (res >= 0) {
664                 //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs);
665 
666                 if (info.size > 0) {
667                     noOutputCounter = 0;
668                     if (timestamps != null) {
669                         timestamps.add(info.presentationTimeUs);
670                     }
671                 }
672 
673                 int outputBufIndex = res;
674                 ByteBuffer buf = codecOutputBuffers[outputBufIndex];
675 
676                 if (decodedIdx + (info.size / 2) >= decoded.length) {
677                     decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2));
678                 }
679 
680                 buf.position(info.offset);
681                 for (int i = 0; i < info.size; i += 2) {
682                     decoded[decodedIdx++] = buf.getShort();
683                 }
684 
685                 codec.releaseOutputBuffer(outputBufIndex, false /* render */);
686 
687                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
688                     Log.d(TAG, "saw output EOS.");
689                     sawOutputEOS = true;
690                 }
691             } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
692                 codecOutputBuffers = codec.getOutputBuffers();
693 
694                 Log.d(TAG, "output buffers have changed.");
695             } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
696                 MediaFormat oformat = codec.getOutputFormat();
697                 audioParams.setNumChannels(oformat.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
698                 audioParams.setSamplingRate(oformat.getInteger(MediaFormat.KEY_SAMPLE_RATE));
699                 Log.d(TAG, "output format has changed to " + oformat);
700             } else {
701                 Log.d(TAG, "dequeueOutputBuffer returned " + res);
702             }
703         }
704         if (noOutputCounter >= 50) {
705             fail("decoder stopped outputing data");
706         }
707 
708         // check if MediaCodec gives back correct drc parameters (R and above)
709         if (drcParams != null && sIsAndroidRAndAbove) {
710             if (drcParams.mDecoderTargetLevel != 0) {
711                 final int targetLevelFromCodec = DecoderTest.getOutputFormatInteger(codec,
712                         MediaFormat.KEY_AAC_DRC_TARGET_REFERENCE_LEVEL);
713                 if (targetLevelFromCodec != drcParams.mDecoderTargetLevel) {
714                     fail("DRC Target Ref Level received from MediaCodec is not the level set");
715                 }
716             }
717 
718             final int cutFromCodec = DecoderTest.getOutputFormatInteger(codec,
719                     MediaFormat.KEY_AAC_DRC_ATTENUATION_FACTOR);
720             assertEquals("Attenuation factor received from MediaCodec differs from set:",
721                     drcParams.mCut, cutFromCodec);
722             final int boostFromCodec = DecoderTest.getOutputFormatInteger(codec,
723                     MediaFormat.KEY_AAC_DRC_BOOST_FACTOR);
724             assertEquals("Boost factor received from MediaCodec differs from set:",
725                     drcParams.mBoost, boostFromCodec);
726         }
727 
728         // expectedOutputLoudness == -2 indicates that output loudness is not tested
729         if (expectedOutputLoudness != -2 && sIsAndroidRAndAbove) {
730             final int outputLoudnessFromCodec = DecoderTest.getOutputFormatInteger(codec,
731                     MediaFormat.KEY_AAC_DRC_OUTPUT_LOUDNESS);
732             if (outputLoudnessFromCodec != expectedOutputLoudness) {
733                 fail("Received decoder output loudness is not the expected value");
734             }
735         }
736 
737         codec.stop();
738         codec.release();
739         return decoded;
740     }
741 
decodeToMemory(AudioParameter audioParams, int testinput, int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName)742     private short[] decodeToMemory(AudioParameter audioParams, int testinput,
743             int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName)
744             throws IOException
745     {
746         final short[] decoded = decodeToMemory(audioParams, testinput, eossample, timestamps,
747                 drcParams, decoderName, false, -2);
748         return decoded;
749     }
750 
decodeToMemory(AudioParameter audioParams, int testinput, int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName, boolean runtimeChange)751     private short[] decodeToMemory(AudioParameter audioParams, int testinput,
752             int eossample, List<Long> timestamps, DrcParams drcParams, String decoderName,
753             boolean runtimeChange)
754             throws IOException
755     {
756         final short[] decoded = decodeToMemory(audioParams, testinput, eossample, timestamps,
757                 drcParams, decoderName, runtimeChange, -2);
758         return decoded;
759     }
760 
761 }
762 
763