Refactor HardwareVideoDecoderTest.

Bug: webrtc:7760
Change-Id: I25d5bd83b5ca9b109b9064486caa8238d7ecd613
Reviewed-on: https://webrtc-review.googlesource.com/6841
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Commit-Queue: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20268}
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java
index 58b8d44..340d184 100644
--- a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java
+++ b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java
@@ -14,220 +14,174 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import android.annotation.TargetApi;
-import android.graphics.Matrix;
-import android.support.test.filters.MediumTest;
-import android.util.Log;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicReference;
-import org.chromium.base.test.BaseJUnit4ClassRunner;
+import android.support.test.filters.SmallTest;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import org.chromium.base.test.params.BaseJUnit4RunnerDelegate;
+import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
+import org.chromium.base.test.params.ParameterAnnotations.UseRunnerDelegate;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 /** Unit tests for {@link HardwareVideoDecoder}. */
-@TargetApi(16)
-@RunWith(BaseJUnit4ClassRunner.class)
+@RunWith(ParameterizedRunner.class)
+@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
 public final class HardwareVideoDecoderTest {
+  @ClassParameter private static List<ParameterSet> CLASS_PARAMS = new ArrayList<>();
+
+  static {
+    CLASS_PARAMS.add(new ParameterSet()
+                         .value("VP8" /* codecType */, false /* useEglContext */)
+                         .name("VP8WithoutEglContext"));
+    CLASS_PARAMS.add(new ParameterSet()
+                         .value("VP8" /* codecType */, true /* useEglContext */)
+                         .name("VP8WithEglContext"));
+  }
+
+  private final String codecType;
+  private final boolean useEglContext;
+
+  public HardwareVideoDecoderTest(String codecType, boolean useEglContext) {
+    this.codecType = codecType;
+    this.useEglContext = useEglContext;
+  }
+
   private static final String TAG = "HardwareVideoDecoderTest";
 
+  private static final int TEST_FRAME_COUNT = 10;
+  private static final int TEST_FRAME_WIDTH = 640;
+  private static final int TEST_FRAME_HEIGHT = 360;
+  private static final VideoFrame.I420Buffer[] TEST_FRAMES = generateTestFrames();
+
   private static final boolean ENABLE_INTEL_VP8_ENCODER = true;
   private static final boolean ENABLE_H264_HIGH_PROFILE = true;
+  private static final VideoEncoder.Settings ENCODER_SETTINGS =
+      new VideoEncoder.Settings(1 /* core */, 640 /* width */, 480 /* height */, 300 /* kbps */,
+          30 /* fps */, true /* automaticResizeOn */);
+
+  private static final int DECODE_TIMEOUT_MS = 1000;
   private static final VideoDecoder.Settings SETTINGS =
       new VideoDecoder.Settings(1 /* core */, 640 /* width */, 480 /* height */);
 
+  private static class MockDecodeCallback implements VideoDecoder.Callback {
+    private BlockingQueue<VideoFrame> frameQueue = new LinkedBlockingQueue<>();
+
+    @Override
+    public void onDecodedFrame(VideoFrame frame, Integer decodeTimeMs, Integer qp) {
+      assertNotNull(frame);
+      frameQueue.offer(frame);
+    }
+
+    public void assertFrameDecoded(EncodedImage testImage, VideoFrame.I420Buffer testBuffer) {
+      VideoFrame decodedFrame = poll();
+      VideoFrame.Buffer decodedBuffer = decodedFrame.getBuffer();
+      assertEquals(testImage.encodedWidth, decodedBuffer.getWidth());
+      assertEquals(testImage.encodedHeight, decodedBuffer.getHeight());
+      // TODO(sakal): Decoder looses the nanosecond precision. This is not a problem in practice
+      // because C++ EncodedImage stores the timestamp in milliseconds.
+      assertEquals(testImage.captureTimeNs / 1000, decodedFrame.getTimestampNs() / 1000);
+      assertEquals(testImage.rotation, decodedFrame.getRotation());
+    }
+
+    public VideoFrame poll() {
+      try {
+        VideoFrame frame = frameQueue.poll(DECODE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        assertNotNull("Timed out waiting for the frame to be decoded.", frame);
+        return frame;
+      } catch (InterruptedException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  private static VideoFrame.I420Buffer[] generateTestFrames() {
+    VideoFrame.I420Buffer[] result = new VideoFrame.I420Buffer[TEST_FRAME_COUNT];
+    for (int i = 0; i < TEST_FRAME_COUNT; i++) {
+      result[i] = JavaI420Buffer.allocate(TEST_FRAME_WIDTH, TEST_FRAME_HEIGHT);
+      // TODO(sakal): Generate content for the test frames.
+    }
+    return result;
+  }
+
+  private final EncodedImage[] encodedTestFrames = new EncodedImage[TEST_FRAME_COUNT];
+  private EglBase14 eglBase;
+
+  private VideoDecoderFactory createDecoderFactory(EglBase.Context eglContext) {
+    return new HardwareVideoDecoderFactory(eglContext);
+  }
+
+  private VideoDecoder createDecoder() {
+    VideoDecoderFactory factory =
+        createDecoderFactory(useEglContext ? eglBase.getEglBaseContext() : null);
+    return factory.createDecoder(codecType);
+  }
+
+  private void encodeTestFrames() {
+    VideoEncoderFactory encoderFactory = new HardwareVideoEncoderFactory(
+        eglBase.getEglBaseContext(), ENABLE_INTEL_VP8_ENCODER, ENABLE_H264_HIGH_PROFILE);
+    VideoEncoder encoder = encoderFactory.createEncoder(
+        new VideoCodecInfo(0 /* payload*/, codecType, new HashMap<>()));
+    HardwareVideoEncoderTest.MockEncoderCallback encodeCallback =
+        new HardwareVideoEncoderTest.MockEncoderCallback();
+    assertEquals(VideoCodecStatus.OK, encoder.initEncode(ENCODER_SETTINGS, encodeCallback));
+
+    long lastTimestampNs = 0;
+    for (int i = 0; i < TEST_FRAME_COUNT; i++) {
+      lastTimestampNs += TimeUnit.SECONDS.toNanos(1) / ENCODER_SETTINGS.maxFramerate;
+      VideoEncoder.EncodeInfo info = new VideoEncoder.EncodeInfo(
+          new EncodedImage.FrameType[] {EncodedImage.FrameType.VideoFrameDelta});
+      HardwareVideoEncoderTest.testEncodeFrame(
+          encoder, new VideoFrame(TEST_FRAMES[i], 0 /* rotation */, lastTimestampNs), info);
+      encodedTestFrames[i] = encodeCallback.poll();
+    }
+
+    assertEquals(VideoCodecStatus.OK, encoder.release());
+  }
+
+  @Before
+  public void setUp() {
+    eglBase = new EglBase14(null, EglBase.CONFIG_PLAIN);
+    eglBase.createDummyPbufferSurface();
+    eglBase.makeCurrent();
+
+    encodeTestFrames();
+  }
+
+  @After
+  public void tearDown() {
+    eglBase.release();
+  }
+
   @Test
-  @MediumTest
+  @SmallTest
   public void testInitialize() {
-    HardwareVideoEncoderFactory encoderFactory =
-        new HardwareVideoEncoderFactory(ENABLE_INTEL_VP8_ENCODER, ENABLE_H264_HIGH_PROFILE);
-    VideoCodecInfo[] supportedCodecs = encoderFactory.getSupportedCodecs();
-    if (supportedCodecs.length == 0) {
-      Log.i(TAG, "No hardware encoding support, skipping testInitialize");
-      return;
-    }
-
-    HardwareVideoDecoderFactory decoderFactory = new HardwareVideoDecoderFactory(null);
-
-    VideoDecoder decoder = decoderFactory.createDecoder(supportedCodecs[0].name);
-    assertEquals(decoder.initDecode(SETTINGS, null), VideoCodecStatus.OK);
-    assertEquals(decoder.release(), VideoCodecStatus.OK);
+    VideoDecoder decoder = createDecoder();
+    assertEquals(VideoCodecStatus.OK, decoder.initDecode(SETTINGS, null /* decodeCallback */));
+    assertEquals(VideoCodecStatus.OK, decoder.release());
   }
 
   @Test
-  @MediumTest
-  public void testInitializeUsingTextures() {
-    HardwareVideoEncoderFactory encoderFactory =
-        new HardwareVideoEncoderFactory(ENABLE_INTEL_VP8_ENCODER, ENABLE_H264_HIGH_PROFILE);
-    VideoCodecInfo[] supportedCodecs = encoderFactory.getSupportedCodecs();
-    if (supportedCodecs.length == 0) {
-      Log.i(TAG, "No hardware encoding support, skipping testInitialize");
-      return;
+  @SmallTest
+  public void testDecode() {
+    VideoDecoder decoder = createDecoder();
+    MockDecodeCallback callback = new MockDecodeCallback();
+    assertEquals(VideoCodecStatus.OK, decoder.initDecode(SETTINGS, callback));
+
+    for (int i = 0; i < TEST_FRAME_COUNT; i++) {
+      assertEquals(VideoCodecStatus.OK,
+          decoder.decode(encodedTestFrames[i],
+              new VideoDecoder.DecodeInfo(false /* isMissingFrames */, 0 /* renderTimeMs */)));
+      callback.assertFrameDecoded(encodedTestFrames[i], TEST_FRAMES[i]);
     }
 
-    EglBase14 eglBase = new EglBase14(null, EglBase.CONFIG_PLAIN);
-    HardwareVideoDecoderFactory decoderFactory =
-        new HardwareVideoDecoderFactory(eglBase.getEglBaseContext());
-
-    VideoDecoder decoder = decoderFactory.createDecoder(supportedCodecs[0].name);
-    assertEquals(decoder.initDecode(SETTINGS, null), VideoCodecStatus.OK);
-    assertEquals(decoder.release(), VideoCodecStatus.OK);
-
-    eglBase.release();
-  }
-
-  @Test
-  @MediumTest
-  public void testDecode() throws InterruptedException {
-    HardwareVideoEncoderFactory encoderFactory =
-        new HardwareVideoEncoderFactory(ENABLE_INTEL_VP8_ENCODER, ENABLE_H264_HIGH_PROFILE);
-    VideoCodecInfo[] supportedCodecs = encoderFactory.getSupportedCodecs();
-    if (supportedCodecs.length == 0) {
-      Log.i(TAG, "No hardware encoding support, skipping testEncodeYuvBuffer");
-      return;
-    }
-
-    // Set up the decoder.
-    HardwareVideoDecoderFactory decoderFactory = new HardwareVideoDecoderFactory(null);
-    VideoDecoder decoder = decoderFactory.createDecoder(supportedCodecs[0].name);
-
-    final long presentationTimestampUs = 20000;
-    final int rotation = 270;
-
-    final CountDownLatch decodeDone = new CountDownLatch(1);
-    final AtomicReference<VideoFrame> decoded = new AtomicReference<>();
-    VideoDecoder.Callback decodeCallback = new VideoDecoder.Callback() {
-      @Override
-      public void onDecodedFrame(VideoFrame frame, Integer decodeTimeMs, Integer qp) {
-        frame.retain();
-        decoded.set(frame);
-        decodeDone.countDown();
-      }
-    };
-    assertEquals(decoder.initDecode(SETTINGS, decodeCallback), VideoCodecStatus.OK);
-
-    // Set up an encoder to produce a valid encoded frame.
-    VideoEncoder encoder = encoderFactory.createEncoder(supportedCodecs[0]);
-    final CountDownLatch encodeDone = new CountDownLatch(1);
-    final AtomicReference<EncodedImage> encoded = new AtomicReference<>();
-    VideoEncoder.Callback encodeCallback = new VideoEncoder.Callback() {
-      @Override
-      public void onEncodedFrame(EncodedImage image, VideoEncoder.CodecSpecificInfo info) {
-        encoded.set(image);
-        encodeDone.countDown();
-      }
-    };
-    assertEquals(encoder.initEncode(new VideoEncoder.Settings(1, SETTINGS.width, SETTINGS.height,
-                                        300, 30, true /* automaticResizeOn */),
-                     encodeCallback),
-        VideoCodecStatus.OK);
-
-    // First, encode a frame.
-    VideoFrame.I420Buffer buffer = JavaI420Buffer.allocate(SETTINGS.width, SETTINGS.height);
-    VideoFrame frame = new VideoFrame(buffer, rotation, presentationTimestampUs * 1000);
-    VideoEncoder.EncodeInfo info = new VideoEncoder.EncodeInfo(
-        new EncodedImage.FrameType[] {EncodedImage.FrameType.VideoFrameKey});
-
-    assertEquals(encoder.encode(frame, info), VideoCodecStatus.OK);
-
-    ThreadUtils.awaitUninterruptibly(encodeDone);
-
-    // Now decode the frame.
-    assertEquals(
-        decoder.decode(encoded.get(), new VideoDecoder.DecodeInfo(false, 0)), VideoCodecStatus.OK);
-
-    ThreadUtils.awaitUninterruptibly(decodeDone);
-
-    frame = decoded.get();
-    assertEquals(frame.getRotation(), rotation);
-    assertEquals(frame.getTimestampNs(), presentationTimestampUs * 1000);
-    assertEquals(frame.getBuffer().getWidth(), SETTINGS.width);
-    assertEquals(frame.getBuffer().getHeight(), SETTINGS.height);
-
-    frame.release();
-    assertEquals(decoder.release(), VideoCodecStatus.OK);
-    assertEquals(encoder.release(), VideoCodecStatus.OK);
-  }
-
-  @Test
-  @MediumTest
-  public void testDecodeUsingTextures() throws InterruptedException {
-    HardwareVideoEncoderFactory encoderFactory =
-        new HardwareVideoEncoderFactory(ENABLE_INTEL_VP8_ENCODER, ENABLE_H264_HIGH_PROFILE);
-    VideoCodecInfo[] supportedCodecs = encoderFactory.getSupportedCodecs();
-    if (supportedCodecs.length == 0) {
-      Log.i(TAG, "No hardware encoding support, skipping testEncodeYuvBuffer");
-      return;
-    }
-
-    // Set up the decoder.
-    EglBase14 eglBase = new EglBase14(null, EglBase.CONFIG_PLAIN);
-    HardwareVideoDecoderFactory decoderFactory =
-        new HardwareVideoDecoderFactory(eglBase.getEglBaseContext());
-    VideoDecoder decoder = decoderFactory.createDecoder(supportedCodecs[0].name);
-
-    final long presentationTimestampUs = 20000;
-    final int rotation = 270;
-
-    final CountDownLatch decodeDone = new CountDownLatch(1);
-    final AtomicReference<VideoFrame> decoded = new AtomicReference<>();
-    VideoDecoder.Callback decodeCallback = new VideoDecoder.Callback() {
-      @Override
-      public void onDecodedFrame(VideoFrame frame, Integer decodeTimeMs, Integer qp) {
-        frame.retain();
-        decoded.set(frame);
-        decodeDone.countDown();
-      }
-    };
-    assertEquals(decoder.initDecode(SETTINGS, decodeCallback), VideoCodecStatus.OK);
-
-    // Set up an encoder to produce a valid encoded frame.
-    VideoEncoder encoder = encoderFactory.createEncoder(supportedCodecs[0]);
-    final CountDownLatch encodeDone = new CountDownLatch(1);
-    final AtomicReference<EncodedImage> encoded = new AtomicReference<>();
-    VideoEncoder.Callback encodeCallback = new VideoEncoder.Callback() {
-      @Override
-      public void onEncodedFrame(EncodedImage image, VideoEncoder.CodecSpecificInfo info) {
-        encoded.set(image);
-        encodeDone.countDown();
-      }
-    };
-    assertEquals(encoder.initEncode(new VideoEncoder.Settings(1, SETTINGS.width, SETTINGS.height,
-                                        300, 30, true /* automaticResizeOn */),
-                     encodeCallback),
-        VideoCodecStatus.OK);
-
-    // First, encode a frame.
-    VideoFrame.I420Buffer buffer = JavaI420Buffer.allocate(SETTINGS.width, SETTINGS.height);
-    VideoFrame frame = new VideoFrame(buffer, rotation, presentationTimestampUs * 1000);
-    VideoEncoder.EncodeInfo info = new VideoEncoder.EncodeInfo(
-        new EncodedImage.FrameType[] {EncodedImage.FrameType.VideoFrameKey});
-
-    assertEquals(encoder.encode(frame, info), VideoCodecStatus.OK);
-
-    ThreadUtils.awaitUninterruptibly(encodeDone);
-
-    // Now decode the frame.
-    assertEquals(
-        decoder.decode(encoded.get(), new VideoDecoder.DecodeInfo(false, 0)), VideoCodecStatus.OK);
-
-    ThreadUtils.awaitUninterruptibly(decodeDone);
-
-    frame = decoded.get();
-    assertEquals(frame.getRotation(), rotation);
-    assertEquals(frame.getTimestampNs(), presentationTimestampUs * 1000);
-
-    assertTrue(frame.getBuffer() instanceof VideoFrame.TextureBuffer);
-    VideoFrame.TextureBuffer textureBuffer = (VideoFrame.TextureBuffer) frame.getBuffer();
-    // TODO(mellem):  Compare the matrix to whatever we expect to get back?
-    assertNotNull(textureBuffer.getTransformMatrix());
-    assertEquals(textureBuffer.getWidth(), SETTINGS.width);
-    assertEquals(textureBuffer.getHeight(), SETTINGS.height);
-    assertEquals(textureBuffer.getType(), VideoFrame.TextureBuffer.Type.OES);
-
-    assertEquals(decoder.release(), VideoCodecStatus.OK);
-    assertEquals(encoder.release(), VideoCodecStatus.OK);
-
-    frame.release();
-    eglBase.release();
+    assertEquals(VideoCodecStatus.OK, decoder.release());
   }
 }
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java
index 7e3c5d0..e117ce6 100644
--- a/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java
+++ b/sdk/android/instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java
@@ -78,9 +78,9 @@
   // # Mock classes
   /**
    * Mock encoder callback that allows easy verification of the general properties of the encoded
-   * frame such as width and height.
+   * frame such as width and height. Also used from HardwareVideoDecoderTest.
    */
-  private static class MockEncoderCallback implements VideoEncoder.Callback {
+  static class MockEncoderCallback implements VideoEncoder.Callback {
     private BlockingQueue<EncodedImage> frameQueue = new LinkedBlockingQueue<>();
 
     public void onEncodedFrame(EncodedImage frame, VideoEncoder.CodecSpecificInfo info) {
@@ -294,7 +294,7 @@
     return useTextures ? generateTextureFrame(width, height) : generateI420Frame(width, height);
   }
 
-  private void testEncodeFrame(
+  static void testEncodeFrame(
       VideoEncoder encoder, VideoFrame frame, VideoEncoder.EncodeInfo info) {
     int numTries = 0;