media: Test flush before and after CSD for decoders

Bug: 13205664
Change-Id: Ifeafc7ea61fb67d752d9581e2560a11f75d4a411
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 0edbc61..34469b79 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -327,20 +327,42 @@
      * Queue some frames with an EOS on the last one.  Test that we have decoded as many
      * frames as we queued.  This tests the EOS handling of the codec to see if all queued
      * (and out-of-order) frames are actually decoded and returned.
+     *
+     * Also test flushing prior to sending CSD, and immediately after sending CSD.
      */
     class EarlyEosTest extends ActivityTest {
+        // using bitfields to create a directed state graph that terminates at FLUSH_NEVER
+        static final int FLUSH_BEFORE_CSD = (1 << 1);
+        static final int FLUSH_AFTER_CSD = (1 << 0);
+        static final int FLUSH_NEVER = 0;
+
         public boolean isValid(Codec c) {
             return getFormat(c) != null;
         }
         public void addTests(TestList tests, final Codec c) {
-            for (int i = NUM_FRAMES / 2; i > 0; i--) {
+            int state = FLUSH_BEFORE_CSD;
+            for (int i = NUM_FRAMES / 2; i > 0; --i, state >>= 1) {
                 final int queuedFrames = i;
+                final int earlyFlushMode = state;
                 tests.add(
                     new Step("testing early EOS at " + queuedFrames, this, c) {
                         public void run() {
                             Decoder decoder = new Decoder(c.name);
                             try {
-                                decoder.configureAndStart(stepFormat(), stepSurface());
+                                MediaFormat fmt = stepFormat();
+                                MediaFormat configFmt = fmt;
+                                if (earlyFlushMode == FLUSH_BEFORE_CSD) {
+                                    // flush before CSD requires not submitting CSD with configure
+                                    configFmt = Media.removeCSD(fmt);
+                                }
+                                decoder.configureAndStart(configFmt, stepSurface());
+                                if (earlyFlushMode != FLUSH_NEVER) {
+                                    decoder.flush();
+                                    // We must always queue CSD after a flush that is potentially
+                                    // before we receive output format has changed.  This should
+                                    // work even after we receive the format change.
+                                    decoder.queueCSD(fmt);
+                                }
                                 int decodedFrames = -decoder.queueInputBufferRange(
                                         stepMedia(),
                                         0 /* startFrame */,
@@ -817,6 +839,7 @@
     class Decoder implements MediaCodec.OnFrameRenderedListener {
         private final static String TAG = "AdaptiveDecoder";
         final long kTimeOutUs = 5000;
+        final long kCSDTimeOutUs = 1000000;
         MediaCodec mCodec;
         ByteBuffer[] mInputBuffers;
         ByteBuffer[] mOutputBuffers;
@@ -1029,6 +1052,31 @@
             return queueInputBufferRange(media,frameStartIx,frameEndIx,sendEosAtEnd,waitForEos,0);
         }
 
+        public void queueCSD(MediaFormat format) {
+            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+            for (int csdIx = 0; ; ++csdIx) {
+                ByteBuffer csdBuf = format.getByteBuffer("csd-" + csdIx);
+                if (csdBuf == null) {
+                    break;
+                }
+
+                int ix = mCodec.dequeueInputBuffer(kCSDTimeOutUs);
+                if (ix < 0) {
+                    fail("Could not dequeue input buffer for CSD #" + csdIx);
+                    return;
+                }
+
+                ByteBuffer buf = mInputBuffers[ix];
+                buf.clear();
+                buf.put((ByteBuffer)csdBuf.clear());
+                Log.v(TAG, "queue-CSD { [" + buf.position() + "]=" +
+                        byteBufferToString(buf, 0, 16) + "} => #" + ix);
+                mCodec.queueInputBuffer(
+                        ix, 0 /* offset */, buf.position(), 0 /* timeUs */,
+                        MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+            }
+        }
+
         public int queueInputBufferRange(
                 Media media, int frameStartIx, int frameEndIx, boolean sendEosAtEnd,
                 boolean waitForEos, long adjustTimeUs) {
@@ -1174,6 +1222,31 @@
         return mFormat;
     }
 
+    public static MediaFormat removeCSD(MediaFormat orig) {
+        MediaFormat copy = MediaFormat.createVideoFormat(
+                orig.getString(orig.KEY_MIME),
+                orig.getInteger(orig.KEY_WIDTH), orig.getInteger(orig.KEY_HEIGHT));
+        for (String k : new String[] {
+                orig.KEY_FRAME_RATE, orig.KEY_MAX_WIDTH, orig.KEY_MAX_HEIGHT,
+                orig.KEY_MAX_INPUT_SIZE
+        }) {
+            if (orig.containsKey(k)) {
+                try {
+                    copy.setInteger(k, orig.getInteger(k));
+                } catch (ClassCastException e) {
+                    try {
+                        copy.setFloat(k, orig.getFloat(k));
+                    } catch (ClassCastException e2) {
+                        // Could not copy value. Don't fail here, as having non-standard
+                        // value types for defined keys is permissible by the media API
+                        // for optional keys.
+                    }
+                }
+            }
+        }
+        return copy;
+    }
+
     public MediaFormat getAdaptiveFormat(int width, int height) {
         mAdaptiveFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, width);
         mAdaptiveFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, height);