add MediaCodecTest with both video and audio encoding

bug: 10361518
Change-Id: Ie13f8b2e7cb751aa8e334f3475986ea8893aae17
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index afa34e8..d2c39f4 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -20,11 +20,15 @@
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
 import android.opengl.GLES20;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import android.view.Surface;
 
+import java.nio.ByteBuffer;
+
 
 /**
  * General MediaCodec tests.
@@ -35,13 +39,26 @@
     private static final String TAG = "MediaCodecTest";
     private static final boolean VERBOSE = false;           // lots of logging
 
-    // parameters for the encoder
+    // parameters for the video encoder
     private static final String MIME_TYPE = "video/avc";    // H.264 Advanced Video Coding
     private static final int BIT_RATE = 2000000;            // 2Mbps
     private static final int FRAME_RATE = 15;               // 15fps
     private static final int IFRAME_INTERVAL = 10;          // 10 seconds between I-frames
     private static final int WIDTH = 1280;
     private static final int HEIGHT = 720;
+    // parameters for the audio encoder
+    private static final String MIME_TYPE_AUDIO = "audio/mp4a-latm";
+    private static final int AUDIO_SAMPLE_RATE = 44100;
+    private static final int AUDIO_AAC_PROFILE = 2; /* OMX_AUDIO_AACObjectLC */
+    private static final int AUDIO_CHANNEL_COUNT = 2; // mono
+    private static final int AUDIO_BIT_RATE = 128000;
+
+    private static final int TIMEOUT_USEC = 100000;
+    private static final int TIMEOUT_USEC_SHORT = 100;
+
+    private boolean mVideoEncoderHadError = false;
+    private boolean mAudioEncoderHadError = false;
+    private volatile boolean mVideoEncodingOngoing = false;
 
     /**
      * Tests:
@@ -60,7 +77,7 @@
         format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
 
         try {
-            encoder = MediaCodec.createByCodecName(codecInfo.getName());;
+            encoder = MediaCodec.createByCodecName(codecInfo.getName());
             try {
                 surface = encoder.createInputSurface();
                 fail("createInputSurface should not work pre-configure");
@@ -222,6 +239,221 @@
         }
     }
 
+    public void testConcurrentAudioVideoEncodings() throws InterruptedException {
+        final int VIDEO_NUM_SWAPS = 100;
+        // audio only checks this and stop
+        mVideoEncodingOngoing = true;
+        final CodecInfo info = getAvcSupportedFormatInfo();
+        long start = System.currentTimeMillis();
+        Thread videoEncodingThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                runVideoEncoding(VIDEO_NUM_SWAPS, info);
+            }
+        });
+        Thread audioEncodingThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                runAudioEncoding();
+            }
+        });
+        videoEncodingThread.start();
+        audioEncodingThread.start();
+        videoEncodingThread.join();
+        mVideoEncodingOngoing = false;
+        audioEncodingThread.join();
+        assertFalse("Video encoding error. Chekc logcat", mVideoEncoderHadError);
+        assertFalse("Audio encoding error. Chekc logcat", mAudioEncoderHadError);
+        long end = System.currentTimeMillis();
+        Log.w(TAG, "Concurrent AV encoding took " + (end - start) + " ms for " + VIDEO_NUM_SWAPS +
+                " video frames");
+    }
+
+    private static class CodecInfo {
+        public int mMaxW;
+        public int mMaxH;
+        public int mFps;
+        public int mBitRate;
+    };
+
+    private static CodecInfo getAvcSupportedFormatInfo() {
+        MediaCodecInfo mediaCodecInfo = selectCodec(MIME_TYPE);
+        CodecCapabilities cap = mediaCodecInfo.getCapabilitiesForType(MIME_TYPE);
+        if (cap == null) { // not supported
+            return null;
+        }
+        CodecInfo info = new CodecInfo();
+        int highestLevel = 0;
+        for (CodecProfileLevel lvl : cap.profileLevels) {
+            if (lvl.level > highestLevel) {
+                highestLevel = lvl.level;
+            }
+        }
+        int maxW = 0;
+        int maxH = 0;
+        int bitRate = 0;
+        int fps = 0; // frame rate for the max resolution
+        switch(highestLevel) {
+            // Do not support Level 1 to 2.
+            case CodecProfileLevel.AVCLevel1:
+            case CodecProfileLevel.AVCLevel11:
+            case CodecProfileLevel.AVCLevel12:
+            case CodecProfileLevel.AVCLevel13:
+            case CodecProfileLevel.AVCLevel1b:
+            case CodecProfileLevel.AVCLevel2:
+                return null;
+            case CodecProfileLevel.AVCLevel21:
+                maxW = 352;
+                maxH = 576;
+                bitRate = 4000000;
+                fps = 25;
+                break;
+            case CodecProfileLevel.AVCLevel22:
+                maxW = 720;
+                maxH = 480;
+                bitRate = 4000000;
+                fps = 15;
+                break;
+            case CodecProfileLevel.AVCLevel3:
+                maxW = 720;
+                maxH = 480;
+                bitRate = 10000000;
+                fps = 30;
+                break;
+            case CodecProfileLevel.AVCLevel31:
+                maxW = 1280;
+                maxH = 720;
+                bitRate = 14000000;
+                fps = 30;
+                break;
+            case CodecProfileLevel.AVCLevel32:
+                maxW = 1280;
+                maxH = 720;
+                bitRate = 20000000;
+                fps = 60;
+                break;
+            case CodecProfileLevel.AVCLevel4: // only try up to 1080p
+            default:
+                maxW = 1920;
+                maxH = 1080;
+                bitRate = 20000000;
+                fps = 30;
+                break;
+        }
+        info.mMaxW = maxW;
+        info.mMaxH = maxH;
+        info.mFps = fps;
+        info.mBitRate = bitRate;
+        Log.i(TAG, "AVC Level 0x" + Integer.toHexString(highestLevel) + " bit rate " + bitRate +
+                " fps " + info.mFps + " w " + maxW + " h " + maxH);
+
+        return info;
+    }
+
+    private void runVideoEncoding(int numSwap, CodecInfo info) {
+        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, info.mMaxW, info.mMaxH);
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, info.mBitRate);
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, info.mFps);
+        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+        MediaCodec encoder = null;
+        InputSurface inputSurface = null;
+        mVideoEncoderHadError = false;
+        try {
+            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+            inputSurface = new InputSurface(encoder.createInputSurface());
+            inputSurface.makeCurrent();
+            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+            encoder.start();
+            for (int i = 0; i < numSwap; i++) {
+                GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
+                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+                inputSurface.swapBuffers();
+                // dequeue buffers until not available
+                int index = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
+                while (index >= 0) {
+                    encoder.releaseOutputBuffer(index, false);
+                    // just throw away output
+                    // allow shorter wait for 2nd round to move on quickly.
+                    index = encoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC_SHORT);
+                }
+            }
+            encoder.signalEndOfInputStream();
+        } catch (Throwable e) {
+            Log.w(TAG, "runVideoEncoding got error: " + e);
+            mVideoEncoderHadError = true;
+        } finally {
+            if (encoder != null) {
+                encoder.stop();
+                encoder.release();
+            }
+            if (inputSurface != null) {
+                inputSurface.release();
+            }
+        }
+    }
+
+    private void runAudioEncoding() {
+        MediaFormat format = MediaFormat.createAudioFormat(MIME_TYPE_AUDIO, AUDIO_SAMPLE_RATE,
+                AUDIO_CHANNEL_COUNT);
+        format.setInteger(MediaFormat.KEY_AAC_PROFILE, AUDIO_AAC_PROFILE);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, AUDIO_BIT_RATE);
+        MediaCodec encoder = null;
+        mAudioEncoderHadError = false;
+        try {
+            encoder = MediaCodec.createEncoderByType(MIME_TYPE_AUDIO);
+            encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+            encoder.start();
+            ByteBuffer[] inputBuffers = encoder.getInputBuffers();
+            ByteBuffer source = ByteBuffer.allocate(inputBuffers[0].capacity());
+            for (int i = 0; i < source.capacity()/2; i++) {
+                source.putShort((short)i);
+            }
+            source.rewind();
+            int currentInputBufferIndex = 0;
+            long encodingLatencySum = 0;
+            int totalEncoded = 0;
+            int numRepeat = 0;
+            while (mVideoEncodingOngoing) {
+                numRepeat++;
+                int inputIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
+                while (inputIndex == -1) {
+                    inputIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
+                }
+                ByteBuffer inputBuffer = inputBuffers[inputIndex];
+                inputBuffer.rewind();
+                inputBuffer.put(source);
+                long start = System.currentTimeMillis();
+                totalEncoded += inputBuffers[inputIndex].limit();
+                encoder.queueInputBuffer(inputIndex, 0, inputBuffer.limit(), 0, 0);
+                source.rewind();
+                int index = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC);
+                long end = System.currentTimeMillis();
+                encodingLatencySum += (end - start);
+                while (index >= 0) {
+                    encoder.releaseOutputBuffer(index, false);
+                    // just throw away output
+                    // allow shorter wait for 2nd round to move on quickly.
+                    index = encoder.dequeueOutputBuffer(info, TIMEOUT_USEC_SHORT);
+                }
+            }
+            Log.w(TAG, "Audio encoding average latency " + encodingLatencySum / numRepeat +
+                    " ms for average write size " + totalEncoded / numRepeat +
+                    " total latency " + encodingLatencySum + " ms for total bytes " + totalEncoded);
+        } catch (Throwable e) {
+            Log.w(TAG, "runAudioEncoding got error: " + e);
+            mAudioEncoderHadError = true;
+        } finally {
+            if (encoder != null) {
+                encoder.stop();
+                encoder.release();
+            }
+        }
+    }
+
     /**
      * Creates a MediaFormat with the basic set of values.
      */