1 /* 2 * Copyright 2017 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import static org.junit.Assert.assertEquals; 14 import static org.junit.Assert.assertNotNull; 15 16 import androidx.annotation.Nullable; 17 import androidx.test.filters.SmallTest; 18 import java.util.ArrayList; 19 import java.util.Arrays; 20 import java.util.Collection; 21 import java.util.HashMap; 22 import java.util.List; 23 import java.util.concurrent.BlockingQueue; 24 import java.util.concurrent.LinkedBlockingQueue; 25 import java.util.concurrent.TimeUnit; 26 import org.junit.After; 27 import org.junit.Before; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.junit.runners.Parameterized; 31 import org.junit.runners.Parameterized.Parameters; 32 33 /** Unit tests for {@link AndroidVideoDecoder}. */ 34 @RunWith(Parameterized.class) 35 public final class AndroidVideoDecoderInstrumentationTest { 36 @Parameters(name = "{0};useEglContext={1}") parameters()37 public static Collection<Object[]> parameters() { 38 return Arrays.asList(new Object[] {/*codecName=*/"VP8", /*useEglContext=*/false}, 39 new Object[] {/*codecName=*/"VP8", /*useEglContext=*/true}, 40 new Object[] {/*codecName=*/"H264", /*useEglContext=*/false}, 41 new Object[] {/*codecName=*/"H264", /*useEglContext=*/true}); 42 } 43 44 private final VideoCodecInfo codecType; 45 private final boolean useEglContext; 46 AndroidVideoDecoderInstrumentationTest(String codecName, boolean useEglContext)47 public AndroidVideoDecoderInstrumentationTest(String codecName, boolean useEglContext) { 48 if (codecName.equals("H264")) { 49 this.codecType = H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC; 50 } else { 51 this.codecType = new VideoCodecInfo(codecName, new HashMap<>()); 52 } 53 this.useEglContext = useEglContext; 54 } 55 56 private static final String TAG = "AndroidVideoDecoderInstrumentationTest"; 57 58 private static final int TEST_FRAME_COUNT = 10; 59 private static final int TEST_FRAME_WIDTH = 640; 60 private static final int TEST_FRAME_HEIGHT = 360; 61 private VideoFrame.I420Buffer[] TEST_FRAMES; 62 63 private static final boolean ENABLE_INTEL_VP8_ENCODER = true; 64 private static final boolean ENABLE_H264_HIGH_PROFILE = true; 65 private static final VideoEncoder.Settings ENCODER_SETTINGS = new VideoEncoder.Settings( 66 1 /* core */, 67 getAlignedNumber(TEST_FRAME_WIDTH, HardwareVideoEncoderTest.getPixelAlignmentRequired()), 68 getAlignedNumber(TEST_FRAME_HEIGHT, HardwareVideoEncoderTest.getPixelAlignmentRequired()), 69 300 /* kbps */, 30 /* fps */, 1 /* numberOfSimulcastStreams */, true /* automaticResizeOn */, 70 /* capabilities= */ new VideoEncoder.Capabilities(false /* lossNotification */)); 71 72 private static final int DECODE_TIMEOUT_MS = 1000; 73 private static final VideoDecoder.Settings SETTINGS = new VideoDecoder.Settings(1 /* core */, 74 getAlignedNumber(TEST_FRAME_WIDTH, HardwareVideoEncoderTest.getPixelAlignmentRequired()), 75 getAlignedNumber(TEST_FRAME_HEIGHT, HardwareVideoEncoderTest.getPixelAlignmentRequired())); 76 77 private static class MockDecodeCallback implements VideoDecoder.Callback { 78 private BlockingQueue<VideoFrame> frameQueue = new LinkedBlockingQueue<>(); 79 80 @Override onDecodedFrame(VideoFrame frame, Integer decodeTimeMs, Integer qp)81 public void onDecodedFrame(VideoFrame frame, Integer decodeTimeMs, Integer qp) { 82 assertNotNull(frame); 83 frameQueue.offer(frame); 84 } 85 assertFrameDecoded(EncodedImage testImage, VideoFrame.I420Buffer testBuffer)86 public void assertFrameDecoded(EncodedImage testImage, VideoFrame.I420Buffer testBuffer) { 87 VideoFrame decodedFrame = poll(); 88 VideoFrame.Buffer decodedBuffer = decodedFrame.getBuffer(); 89 assertEquals(testImage.encodedWidth, decodedBuffer.getWidth()); 90 assertEquals(testImage.encodedHeight, decodedBuffer.getHeight()); 91 // TODO(sakal): Decoder looses the nanosecond precision. This is not a problem in practice 92 // because C++ EncodedImage stores the timestamp in milliseconds. 93 assertEquals(testImage.captureTimeNs / 1000, decodedFrame.getTimestampNs() / 1000); 94 assertEquals(testImage.rotation, decodedFrame.getRotation()); 95 } 96 poll()97 public VideoFrame poll() { 98 try { 99 VideoFrame frame = frameQueue.poll(DECODE_TIMEOUT_MS, TimeUnit.MILLISECONDS); 100 assertNotNull("Timed out waiting for the frame to be decoded.", frame); 101 return frame; 102 } catch (InterruptedException e) { 103 throw new RuntimeException(e); 104 } 105 } 106 } 107 generateTestFrames()108 private static VideoFrame.I420Buffer[] generateTestFrames() { 109 VideoFrame.I420Buffer[] result = new VideoFrame.I420Buffer[TEST_FRAME_COUNT]; 110 for (int i = 0; i < TEST_FRAME_COUNT; i++) { 111 result[i] = JavaI420Buffer.allocate( 112 getAlignedNumber(TEST_FRAME_WIDTH, HardwareVideoEncoderTest.getPixelAlignmentRequired()), 113 getAlignedNumber( 114 TEST_FRAME_HEIGHT, HardwareVideoEncoderTest.getPixelAlignmentRequired())); 115 // TODO(sakal): Generate content for the test frames. 116 } 117 return result; 118 } 119 120 private final EncodedImage[] encodedTestFrames = new EncodedImage[TEST_FRAME_COUNT]; 121 private EglBase14 eglBase; 122 createDecoderFactory(EglBase.Context eglContext)123 private VideoDecoderFactory createDecoderFactory(EglBase.Context eglContext) { 124 return new HardwareVideoDecoderFactory(eglContext); 125 } 126 createDecoder()127 private @Nullable VideoDecoder createDecoder() { 128 VideoDecoderFactory factory = 129 createDecoderFactory(useEglContext ? eglBase.getEglBaseContext() : null); 130 return factory.createDecoder(codecType); 131 } 132 encodeTestFrames()133 private void encodeTestFrames() { 134 VideoEncoderFactory encoderFactory = new HardwareVideoEncoderFactory( 135 eglBase.getEglBaseContext(), ENABLE_INTEL_VP8_ENCODER, ENABLE_H264_HIGH_PROFILE); 136 VideoEncoder encoder = encoderFactory.createEncoder(codecType); 137 HardwareVideoEncoderTest.MockEncoderCallback encodeCallback = 138 new HardwareVideoEncoderTest.MockEncoderCallback(); 139 assertEquals(VideoCodecStatus.OK, encoder.initEncode(ENCODER_SETTINGS, encodeCallback)); 140 141 long lastTimestampNs = 0; 142 for (int i = 0; i < TEST_FRAME_COUNT; i++) { 143 lastTimestampNs += TimeUnit.SECONDS.toNanos(1) / ENCODER_SETTINGS.maxFramerate; 144 VideoEncoder.EncodeInfo info = new VideoEncoder.EncodeInfo( 145 new EncodedImage.FrameType[] {EncodedImage.FrameType.VideoFrameDelta}); 146 HardwareVideoEncoderTest.testEncodeFrame( 147 encoder, new VideoFrame(TEST_FRAMES[i], 0 /* rotation */, lastTimestampNs), info); 148 encodedTestFrames[i] = encodeCallback.poll(); 149 } 150 151 assertEquals(VideoCodecStatus.OK, encoder.release()); 152 } 153 getAlignedNumber(int number, int alignment)154 private static int getAlignedNumber(int number, int alignment) { 155 return (number / alignment) * alignment; 156 } 157 158 @Before setUp()159 public void setUp() { 160 NativeLibrary.initialize(new NativeLibrary.DefaultLoader(), TestConstants.NATIVE_LIBRARY); 161 162 TEST_FRAMES = generateTestFrames(); 163 164 eglBase = EglBase.createEgl14(EglBase.CONFIG_PLAIN); 165 eglBase.createDummyPbufferSurface(); 166 eglBase.makeCurrent(); 167 168 encodeTestFrames(); 169 } 170 171 @After tearDown()172 public void tearDown() { 173 eglBase.release(); 174 } 175 176 @Test 177 @SmallTest testInitialize()178 public void testInitialize() { 179 VideoDecoder decoder = createDecoder(); 180 assertEquals(VideoCodecStatus.OK, decoder.initDecode(SETTINGS, null /* decodeCallback */)); 181 assertEquals(VideoCodecStatus.OK, decoder.release()); 182 } 183 184 @Test 185 @SmallTest testDecode()186 public void testDecode() { 187 VideoDecoder decoder = createDecoder(); 188 MockDecodeCallback callback = new MockDecodeCallback(); 189 assertEquals(VideoCodecStatus.OK, decoder.initDecode(SETTINGS, callback)); 190 191 for (int i = 0; i < TEST_FRAME_COUNT; i++) { 192 assertEquals(VideoCodecStatus.OK, 193 decoder.decode(encodedTestFrames[i], 194 new VideoDecoder.DecodeInfo(false /* isMissingFrames */, 0 /* renderTimeMs */))); 195 callback.assertFrameDecoded(encodedTestFrames[i], TEST_FRAMES[i]); 196 } 197 198 assertEquals(VideoCodecStatus.OK, decoder.release()); 199 } 200 } 201