Home | History | Annotate | Download | only in cts
      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
     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 
     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
     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 
    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
    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 
    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
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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