Updating benchmark library with Large audio frame API

 - Enables decoding using large audio frame APIs

Bug: 361349204
Flag: EXEMPT does not affect framework behaviour

Change-Id: I813f5ca9c3e5c55436b31e971e6d3979147fe0d5
diff --git a/media/tests/benchmark/MediaBenchmarkTest/Android.bp b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
index 1049d5e..8f9ee86 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/Android.bp
+++ b/media/tests/benchmark/MediaBenchmarkTest/Android.bp
@@ -69,6 +69,6 @@
 java_defaults {
     name: "MediaBenchmark-defaults",
 
-    min_sdk_version: "29",
-    target_sdk_version: "30",
+    min_sdk_version: "35",
+    target_sdk_version: "35",
 }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml b/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
index 28c2654..bc0c16f 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
+++ b/media/tests/benchmark/MediaBenchmarkTest/AndroidManifest.xml
@@ -26,7 +26,7 @@
         tools:ignore="AllowBackup,GoogleAppIndexingWarning,MissingApplicationIcon"
         tools:remove="android:appComponentFactory">
     </application>
-    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="31"/>
+    <uses-sdk android:minSdkVersion="35" android:targetSdkVersion="35"/>
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.media.benchmark"
             android:label="Benchmark Media Test"/>
diff --git a/media/tests/benchmark/MediaBenchmarkTest/build.gradle b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
index a2af701..87fc24c 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/build.gradle
+++ b/media/tests/benchmark/MediaBenchmarkTest/build.gradle
@@ -27,11 +27,11 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 30
+    compileSdkVersion 35
     defaultConfig {
         applicationId "com.android.media.benchmark"
         minSdkVersion 29
-        targetSdkVersion 30
+        targetSdkVersion 35
         versionCode 1
         versionName "1.0"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
index afd70a3..c68a990 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/DecoderTest.java
@@ -46,6 +46,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertEquals;
@@ -118,7 +119,7 @@
     }
 
     @Test(timeout = PER_TEST_TIMEOUT_MS)
-    public void testDecoder() throws IOException {
+    public void testDecoder() throws IOException, InterruptedException {
         File inputFile = new File(mInputFilePath + mInputFile);
         assertTrue("Cannot find " + mInputFile + " in directory " + mInputFilePath,
                 inputFile.exists());
@@ -133,7 +134,7 @@
             extractor.selectExtractorTrack(currentTrack);
             MediaFormat format = extractor.getFormat(currentTrack);
             String mime = format.getString(MediaFormat.KEY_MIME);
-            ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+            List<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
             assertTrue("No suitable codecs found for file: " + mInputFile + " track : " +
                     currentTrack + " mime: " + mime, (mediaCodecs.size() > 0));
 
@@ -205,7 +206,7 @@
             extractor.selectExtractorTrack(currentTrack);
             MediaFormat format = extractor.getFormat(currentTrack);
             String mime = format.getString(MediaFormat.KEY_MIME);
-            ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
+            List<String> mediaCodecs = CodecUtils.selectCodecs(mime, false);
             for (String codecName : mediaCodecs) {
                 Log.i("Test: %s\n", mInputFile);
                 Native nativeDecoder = new Native();
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
index 4202732..4ce5214 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/androidTest/java/com/android/media/benchmark/tests/EncoderTest.java
@@ -50,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertEquals;
@@ -150,7 +151,7 @@
     }
 
     @BeforeClass
-    public static void prepareInput() throws IOException {
+    public static void prepareInput() throws IOException, InterruptedException {
 
         mDecodedFileFullHd = new File(mFileDirPath + DECODE_FULLHD_UNPACKED);
         int status = decodeFile(mInputFilePath + DECODE_FULLHD_INPUT, mDecodedFileFullHd);
@@ -165,7 +166,8 @@
         assertEquals("Decoder returned error " + status, 0, status);
     }
 
-    private static int decodeFile(String inputFileName, File outputDecodeFile) throws IOException {
+    private static int decodeFile(String inputFileName, File outputDecodeFile)
+            throws IOException, InterruptedException {
         int status = -1;
         File inputFile = new File(inputFileName);
         assertTrue("Cannot open input file " + inputFileName, inputFile.exists());
@@ -220,7 +222,7 @@
         int status;
         int frameSize;
 
-        ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mMime, true);
+        List<String> mediaCodecs = CodecUtils.selectCodecs(mMime, true);
         assertTrue("No suitable codecs found for mimetype: " + mMime, (mediaCodecs.size() > 0));
         Boolean[] encodeMode = {true, false};
         // Encoding the decoded input file
@@ -297,7 +299,7 @@
 
     @Test(timeout = PER_TEST_TIMEOUT_MS)
     public void testNativeEncoder() {
-        ArrayList<String> mediaCodecs = CodecUtils.selectCodecs(mMime, true);
+        List<String> mediaCodecs = CodecUtils.selectCodecs(mMime, true);
         assertTrue("No suitable codecs found for mimetype: " + mMime, (mediaCodecs.size() > 0));
         for (String codecName : mediaCodecs) {
             Native nativeEncoder = new Native();
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
index 1e10b37..f223242 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/CodecUtils.java
@@ -5,6 +5,7 @@
 import android.media.MediaFormat;
 import android.os.Build;
 import java.util.ArrayList;
+import java.util.List;
 
 public class CodecUtils {
     private CodecUtils() {}
@@ -15,7 +16,7 @@
      * @param isEncoder Specifies encoder or decoder
      * @return ArrayList of codec names
      */
-    public static ArrayList<String> selectCodecs(String mimeType, boolean isEncoder) {
+    public static List<String> selectCodecs(String mimeType, boolean isEncoder) {
         MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
         MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
         ArrayList<String> supportedCodecs = new ArrayList<>();
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
index e947ef6..e9b337d 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Decoder.java
@@ -28,7 +28,10 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 
 import com.android.media.benchmark.library.IBufferXfer;
 
@@ -37,28 +40,28 @@
     private static final boolean DEBUG = false;
     private static final int kQueueDequeueTimeoutUs = 1000;
 
-    private final Object mLock = new Object();
-    private MediaCodec mCodec;
-    private Surface mSurface = null;
-    private boolean mRender = false;
-    private ArrayList<BufferInfo> mInputBufferInfo;
-    private Stats mStats;
-    private String mMime;
+    protected final Object mLock = new Object();
+    protected MediaCodec mCodec;
+    protected Surface mSurface = null;
+    protected boolean mRender = false;
+    protected ArrayList<BufferInfo> mInputBufferInfo;
+    protected Stats mStats;
+    protected String mMime;
 
-    private boolean mSawInputEOS;
-    private boolean mSawOutputEOS;
-    private boolean mSignalledError;
+    protected boolean mSawInputEOS;
+    protected boolean mSawOutputEOS;
+    protected boolean mSignalledError;
 
-    private int mNumInFramesProvided;
-    private int mNumInFramesRequired;
+    protected int mNumInFramesProvided;
+    protected int mNumInFramesRequired;
 
-    private int mNumOutputFrame;
-    private int mIndex;
+    protected int mNumOutputFrame;
+    protected int mIndex;
 
-    private ArrayList<ByteBuffer> mInputBuffer;
-    private FileOutputStream mOutputStream;
-    private FrameReleaseQueue mFrameReleaseQueue = null;
-    private IBufferXfer.ISendBuffer mIBufferSend = null;
+    protected ArrayList<ByteBuffer> mInputBuffer;
+    protected FileOutputStream mOutputStream;
+    protected FrameReleaseQueue mFrameReleaseQueue = null;
+    protected IBufferXfer.ISendBuffer mIBufferSend = null;
 
     /* success for decoder */
     public static final int DECODE_SUCCESS = 0;
@@ -71,7 +74,9 @@
     @Override
     public boolean receiveBuffer(IBufferXfer.BufferXferInfo info) {
         MediaCodec codec = (MediaCodec)info.obj;
-        codec.releaseOutputBuffer(info.idx, mRender);
+        if (info.isComplete) {
+            codec.releaseOutputBuffer(info.idx, mRender);
+        }
         return true;
     }
     @Override
@@ -133,6 +138,49 @@
         }
     }
 
+    protected void setCallback(MediaCodec codec) {
+        codec.setCallback(new MediaCodec.Callback() {
+        @Override
+        public void onInputBufferAvailable(
+                @NonNull MediaCodec mediaCodec, int inputBufferId) {
+            try {
+                mStats.addInputTime();
+                onInputAvailable(inputBufferId, mediaCodec);
+            } catch (Exception e) {
+                e.printStackTrace();
+                Log.e(TAG, e.toString());
+            }
+        }
+
+        @Override
+        public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec,
+                int outputBufferId, @NonNull MediaCodec.BufferInfo bufferInfo) {
+            mStats.addOutputTime();
+            onOutputAvailable(mediaCodec, outputBufferId, bufferInfo);
+            if (mSawOutputEOS) {
+                synchronized (mLock) { mLock.notify(); }
+            }
+        }
+
+        @Override
+        public void onOutputFormatChanged(
+                @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
+            Log.i(TAG, "Output format changed. Format: " + format.toString());
+        }
+
+        @Override
+        public void onError(
+                @NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
+            mSignalledError = true;
+            Log.e(TAG, "Codec Error: " + e.toString());
+            e.printStackTrace();
+            synchronized (mLock) { mLock.notify(); }
+        }
+    });
+
+
+    }
+
     /**
      * Decodes the given input buffer,
      * provided valid list of buffer info and format are passed as inputs.
@@ -146,9 +194,10 @@
      *         DECODE_CREATE_ERROR for decoder not created
      * @throws IOException if the codec cannot be created.
      */
-    public int decode(@NonNull ArrayList<ByteBuffer> inputBuffer,
-            @NonNull ArrayList<BufferInfo> inputBufferInfo, final boolean asyncMode,
-            @NonNull MediaFormat format, String codecName) throws IOException {
+    public int decode(@NonNull List<ByteBuffer> inputBuffer,
+            @NonNull List<BufferInfo> inputBufferInfo, final boolean asyncMode,
+            @NonNull MediaFormat format, String codecName)
+            throws IOException, InterruptedException {
         mInputBuffer = new ArrayList<>(inputBuffer.size());
         mInputBuffer.addAll(inputBuffer);
         mInputBufferInfo = new ArrayList<>(inputBufferInfo.size());
@@ -170,64 +219,34 @@
             mFrameReleaseQueue.setMediaCodec(mCodec);
             mFrameReleaseQueue.setMime(mMime);
         }
+
         if (asyncMode) {
-            mCodec.setCallback(new MediaCodec.Callback() {
-                @Override
-                public void onInputBufferAvailable(
-                        @NonNull MediaCodec mediaCodec, int inputBufferId) {
-                    try {
-                        mStats.addInputTime();
-                        onInputAvailable(inputBufferId, mediaCodec);
-                    } catch (Exception e) {
-                        e.printStackTrace();
-                        Log.e(TAG, e.toString());
-                    }
-                }
-
-                @Override
-                public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec,
-                        int outputBufferId, @NonNull MediaCodec.BufferInfo bufferInfo) {
-                    mStats.addOutputTime();
-                    onOutputAvailable(mediaCodec, outputBufferId, bufferInfo);
-                    if (mSawOutputEOS) {
-                        synchronized (mLock) { mLock.notify(); }
-                    }
-                }
-
-                @Override
-                public void onOutputFormatChanged(
-                        @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
-                    Log.i(TAG, "Output format changed. Format: " + format.toString());
-                }
-
-                @Override
-                public void onError(
-                        @NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
-                    mSignalledError = true;
-                    Log.e(TAG, "Codec Error: " + e.toString());
-                    e.printStackTrace();
-                    synchronized (mLock) { mLock.notify(); }
-                }
-            });
+            setCallback(mCodec);
         }
         int isEncoder = 0;
         if (DEBUG) {
             Log.d(TAG, "Media Format : " + format.toString());
         }
         mCodec.configure(format, mSurface, null, isEncoder);
+
         mCodec.start();
-        Log.i(TAG, "Codec started ");
+        Log.i(TAG, "Codec started async mode ?  " + asyncMode);
         long eTime = mStats.getCurTime();
         mStats.setInitTime(mStats.getTimeDiff(sTime, eTime));
         mStats.setStartTime();
         if (asyncMode) {
             try {
-                synchronized (mLock) { mLock.wait(); }
-                if (mSignalledError) {
-                    return DECODE_DECODER_ERROR;
+                synchronized (mLock) {
+                    while (!mSawOutputEOS && !mSignalledError) {
+                        mLock.wait();
+                    }
+                    if (mSignalledError) {
+                        return DECODE_DECODER_ERROR;
+                    }
                 }
             } catch (InterruptedException e) {
-                e.printStackTrace();
+                Log.e(TAG, "Error in waiting");
+                throw e;
             }
         } else {
             while (!mSawOutputEOS && !mSignalledError) {
@@ -319,7 +338,7 @@
         return mCodec.getOutputFormat();
     }
 
-    private void onInputAvailable(int inputBufferId, MediaCodec mediaCodec) {
+    protected void onInputAvailable(int inputBufferId, MediaCodec mediaCodec) {
         if (inputBufferId >= 0) {
             ByteBuffer inputCodecBuffer = mediaCodec.getInputBuffer(inputBufferId);
             BufferInfo bufInfo;
@@ -351,7 +370,7 @@
         }
     }
 
-    private void onOutputAvailable(
+    protected void onOutputAvailable(
             MediaCodec mediaCodec, int outputBufferId, BufferInfo outputBufferInfo) {
         if (mSawOutputEOS || outputBufferId < 0) {
             return;
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
index 63d17ee..3aa38d1 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Encoder.java
@@ -200,7 +200,8 @@
      * @throws IOException If the codec cannot be created.
      */
     public int encode(String codecName, MediaFormat encodeFormat, String mime, int frameRate,
-                      int sampleRate, int frameSize, boolean asyncMode) throws IOException {
+            int sampleRate, int frameSize, boolean asyncMode)
+            throws IOException, InterruptedException {
         mInputBufferSize = (mInputStream != null) ? mInputStream.getChannel().size() : 0;
         mOffset = 0;
         mFrameRate = frameRate;
@@ -275,12 +276,16 @@
         mStats.setStartTime();
         if (asyncMode) {
             try {
-                synchronized (mLock) { mLock.wait(); }
-                if (mSignalledError) {
-                    return ENCODE_ENCODER_ERROR;
+                synchronized (mLock) {
+                    while (!mSawOutputEOS && !mSignalledError) {
+                        mLock.wait();
+                    }
+                    if (mSignalledError) {
+                        return ENCODE_ENCODER_ERROR;
+                    }
                 }
             } catch (InterruptedException e) {
-                e.printStackTrace();
+                throw e;
             }
         } else {
             while (!mSawOutputEOS && !mSignalledError) {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
index f3024e7..1c0f810 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Extractor.java
@@ -76,6 +76,21 @@
     public MediaCodec.BufferInfo getBufferInfo() { return this.mBufferInfo; }
 
     /**
+     * Returns the maximum sample size for the selected track
+     * @return max sample size in the given track
+     */
+    public int getMaxSampleSize() {
+        int size = 0;
+        int maxSampleSize = 0;
+        while ((size = (int) mExtractor.getSampleSize()) != -1) {
+            maxSampleSize = Math.max(maxSampleSize, size);
+            mExtractor.advance();
+        }
+        mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+        return maxSampleSize;
+    }
+
+    /**
      * Returns the duration of the sample
      */
     public long getClipDuration() { return this.mDurationUs; }
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
index 90731ed..20a2573 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/FrameReleaseQueue.java
@@ -185,7 +185,7 @@
                 try {
                     mCodec.releaseOutputBuffer(curFrameInfo.bufferId, actualRender);
                 } catch (IllegalStateException e) {
-                    e.printStackTrace();
+                    throw(e);
                 }
             });
 
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
index a75962c..c97a35c 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXfer.java
@@ -28,6 +28,7 @@
       public Object obj;
       int flag;
       int bytesRead;
+      boolean isComplete = true;
       long presentationTimeUs;
   }
 
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
index ab55df5..3e6cee1 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/IBufferXferImpl.java
@@ -16,9 +16,9 @@
 
 package com.android.media.benchmark.library;
 
-/**
+/*
  * Class that manages the buffer senders
-*/
+ */
 import com.android.media.benchmark.library.IBufferXfer;
 import java.util.ArrayDeque;
 import android.util.Log;
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
new file mode 100644
index 0000000..cb92f06
--- /dev/null
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/MultiAccessUnitDecoder.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.media.benchmark.library;
+
+import android.view.Surface;
+
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import com.android.media.benchmark.library.IBufferXfer;
+import com.android.media.benchmark.library.Decoder;
+
+public class MultiAccessUnitDecoder extends Decoder {
+    private static final String TAG = "MultiAccessUnitDecoder";
+    private static final boolean DEBUG = false;
+    private final ArrayDeque<BufferInfo> mInputInfos = new ArrayDeque<>();
+
+    @Override
+    public void setCallback(MediaCodec codec) {
+        mCodec.setCallback(new MediaCodec.Callback() {
+            boolean isUsingLargeFrameMode = false;
+
+            @Override
+            public void onInputBufferAvailable(
+                    @NonNull MediaCodec mediaCodec, int inputBufferId) {
+                try {
+                    mStats.addInputTime();
+                    if (isUsingLargeFrameMode) {
+                        onInputsAvailable(inputBufferId, mediaCodec);
+                    } else {
+                        onInputAvailable(inputBufferId, mediaCodec);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    Log.e(TAG, e.toString());
+                }
+            }
+
+            @Override
+            public void onOutputBufferAvailable(@NonNull MediaCodec mediaCodec,
+                    int outputBufferId, @NonNull MediaCodec.BufferInfo bufferInfo) {
+                mStats.addOutputTime();
+                onOutputAvailable(mediaCodec, outputBufferId, bufferInfo);
+                if (mSawOutputEOS) {
+                    synchronized (mLock) { mLock.notify(); }
+                }
+            }
+
+            @Override
+            public void onOutputBuffersAvailable(
+                    @NonNull MediaCodec mediaCodec,
+                            int outputBufferId, @NonNull ArrayDeque<BufferInfo> infos) {
+                int i = 0;
+                while(i++ < infos.size()) {
+                    mStats.addOutputTime();
+                }
+                onOutputsAvailable(mediaCodec, outputBufferId, infos);
+                if (mSawOutputEOS) {
+                    synchronized (mLock) { mLock.notify(); }
+                }
+            }
+
+            @Override
+            public void onOutputFormatChanged(
+                    @NonNull MediaCodec mediaCodec, @NonNull MediaFormat format) {
+                Log.i(TAG, "Output format changed. Format: " + format.toString());
+                final int maxOutputSize = format.getNumber(
+                        MediaFormat.KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, 0).intValue();
+                isUsingLargeFrameMode = (maxOutputSize > 0);
+            }
+
+            @Override
+            public void onError(
+                    @NonNull MediaCodec mediaCodec, @NonNull MediaCodec.CodecException e) {
+                mSignalledError = true;
+                Log.e(TAG, "Codec Error: " + e.toString());
+                e.printStackTrace();
+                synchronized (mLock) { mLock.notify(); }
+            }
+        });
+
+    }
+    /**
+     * Decodes the given input buffer,
+     * provided valid list of buffer info and format are passed as inputs.
+     *
+     * @param inputBuffer     Decode the provided list of ByteBuffers
+     * @param inputBufferInfo List of buffer info corresponding to provided input buffers
+     * @param asyncMode       Will run on async implementation if true
+     * @param format          For creating the decoder if codec name is empty and configuring it
+     * @param codecName       Will create the decoder with codecName
+     * @return DECODE_SUCCESS if decode was successful, DECODE_DECODER_ERROR for fail,
+     *         DECODE_CREATE_ERROR for decoder not created
+     * @throws IOException if the codec cannot be created.
+     */
+    @Override
+    public int decode(@NonNull List<ByteBuffer> inputBuffer,
+            @NonNull List<BufferInfo> inputBufferInfo, final boolean asyncMode,
+            @NonNull MediaFormat format, String codecName)
+            throws IOException, InterruptedException {
+        return super.decode(inputBuffer, inputBufferInfo, asyncMode, format, codecName);
+    }
+
+    private void onInputsAvailable(int inputBufferId, MediaCodec mediaCodec) {
+        if (inputBufferId >= 0) {
+            ByteBuffer inputCodecBuffer = mediaCodec.getInputBuffer(inputBufferId);
+            BufferInfo bufInfo;
+            mInputInfos.clear();
+            int offset = 0;
+            while (mNumInFramesProvided < mNumInFramesRequired) {
+                bufInfo = mInputBufferInfo.get(mIndex);
+                mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+                if (inputCodecBuffer.remaining() < bufInfo.size) {
+                    if (mInputInfos.size() == 0) {
+                        Log.d(TAG, "SampleSize " + inputCodecBuffer.remaining()
+                                + "greater than MediaCodec Buffer size " + bufInfo.size);
+                    }
+                    break;
+                }
+                inputCodecBuffer.put(mInputBuffer.get(mIndex).array());
+                bufInfo.offset = offset; offset += bufInfo.size;
+                mInputInfos.add(bufInfo);
+                mNumInFramesProvided++;
+                mIndex = mNumInFramesProvided % (mInputBufferInfo.size() - 1);
+            }
+            if (mNumInFramesProvided >= mNumInFramesRequired) {
+                mIndex = mInputBufferInfo.size() - 1;
+                bufInfo = mInputBufferInfo.get(mIndex);
+                if (inputCodecBuffer.remaining() > bufInfo.size) {
+                    if ((bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == 0) {
+                        Log.e(TAG, "Error in EOS flag for Decoder");
+                    }
+                    mSawInputEOS = (bufInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+                    inputCodecBuffer.put(mInputBuffer.get(mIndex).array());
+                    bufInfo.offset = offset; offset += bufInfo.size;
+                    mInputInfos.add(bufInfo);
+                    mNumInFramesProvided++;
+                }
+            }
+            if (mInputInfos.size() == 0) {
+                Log.d(TAG, " No inputs to queue");
+            } else {
+                mStats.addFrameSize(offset);
+                mediaCodec.queueInputBuffers(inputBufferId, mInputInfos);
+            }
+        }
+    }
+
+    private void onOutputsAvailable(MediaCodec mc, int outputBufferId,
+            ArrayDeque<BufferInfo> infos) {
+        if (mSawOutputEOS || outputBufferId < 0) {
+            return;
+        }
+        Iterator<BufferInfo> iter = infos.iterator();
+        while (iter.hasNext()) {
+            BufferInfo bufferInfo = iter.next();
+            mNumOutputFrame++;
+            if (DEBUG) {
+                Log.d(TAG,
+                        "In OutputBufferAvailable ,"
+                                + " output frame number = " + mNumOutputFrame
+                                + " timestamp = " + bufferInfo.presentationTimeUs
+                                + " size = " + bufferInfo.size);
+            }
+            if (mIBufferSend != null) {
+                IBufferXfer.BufferXferInfo info = new IBufferXfer.BufferXferInfo();
+                info.buf = mc.getOutputBuffer(outputBufferId);
+                info.idx = outputBufferId;
+                info.obj = mc;
+                info.bytesRead = bufferInfo.size;
+                info.presentationTimeUs = bufferInfo.presentationTimeUs;
+                info.flag = bufferInfo.flags;
+                info.isComplete = iter.hasNext() ? false : true;
+                mIBufferSend.sendBuffer(this, info);
+            }
+            mSawOutputEOS |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+        }
+        if (mOutputStream != null) {
+            try {
+                ByteBuffer outputBuffer = mc.getOutputBuffer(outputBufferId);
+                byte[] bytesOutput = new byte[outputBuffer.remaining()];
+                outputBuffer.get(bytesOutput);
+                mOutputStream.write(bytesOutput);
+            } catch (IOException e) {
+                e.printStackTrace();
+                Log.d(TAG, "Error Dumping File: Exception " + e.toString());
+            }
+        }
+        if (mIBufferSend == null) {
+            mc.releaseOutputBuffer(outputBufferId, mRender);
+        }
+        if (mSawOutputEOS) {
+            Log.i(TAG, "Large frame - saw output EOS");
+        }
+        // we don't support frame release queue for large audio frame
+    }
+}
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
index 340b539..786290d 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Muxer.java
@@ -23,6 +23,7 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.List;
 
 public class Muxer {
     private Stats mStats;
@@ -61,8 +62,8 @@
      * @param inputBufferInfo      Buffer information related to these samples
      * @return Returns Status as 0 if write operation is successful, -1 otherwise
      */
-    public int mux(int trackIndex, ArrayList<ByteBuffer> inputExtractedBuffer,
-                   ArrayList<MediaCodec.BufferInfo> inputBufferInfo) {
+    public int mux(int trackIndex, List<ByteBuffer> inputExtractedBuffer,
+                   List<MediaCodec.BufferInfo> inputBufferInfo) {
         mStats.setStartTime();
         for (int sampleCount = 0; sampleCount < inputExtractedBuffer.size(); sampleCount++) {
             try {
diff --git a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
index 0ebf798..17de1e7 100644
--- a/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
+++ b/media/tests/benchmark/MediaBenchmarkTest/src/main/java/com/android/media/benchmark/library/Stats.java
@@ -23,6 +23,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Measures Performance.
@@ -88,9 +89,9 @@
 
     public long getStartTime() { return mStartTimeNs; }
 
-    public ArrayList<Long> getOutputTimers() { return mOutputTimer; }
+    public List<Long> getOutputTimers() { return mOutputTimer; }
 
-    public ArrayList<Long> getInputTimers() { return mInputTimer; }
+    public List<Long> getInputTimers() { return mInputTimer; }
 
     public long getTimeDiff(long sTime, long eTime) { return (eTime - sTime); }