• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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