blob: 961ecd5c7085ba4c197256fd3f3da50ddab92635 [file] [log] [blame]
/*
* Copyright 2023 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.webrtc.PeerConnectionEndToEndTest.SdpObserverLatch;
import org.webrtc.RtpParameters.Encoding;
import org.webrtc.RtpTransceiver.RtpTransceiverInit;
/**
* Unit-tests for {@link RtpCapabilities}.
*/
public class RtpCapabilitiesTest {
private PeerConnectionFactory factory;
private PeerConnection pc;
static class CustomHardwareVideoEncoderFactory implements VideoEncoderFactory {
private final List<VideoCodecInfo> supportedCodecs;
public CustomHardwareVideoEncoderFactory(List<VideoCodecInfo> supportedCodecs) {
this.supportedCodecs = supportedCodecs;
}
@Override
public @Nullable VideoEncoder createEncoder(VideoCodecInfo info) {
return null;
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
return supportedCodecs.toArray(new VideoCodecInfo[0]);
}
}
static class CustomHardwareVideoDecoderFactory implements VideoDecoderFactory {
private final List<VideoCodecInfo> supportedCodecs;
public CustomHardwareVideoDecoderFactory(List<VideoCodecInfo> supportedCodecs) {
this.supportedCodecs = supportedCodecs;
}
@Nullable
@Override
public VideoDecoder createDecoder(VideoCodecInfo videoCodecInfo) {
return null;
}
@Override
public VideoCodecInfo[] getSupportedCodecs() {
return supportedCodecs.toArray(new VideoCodecInfo[0]);
}
}
@Before
public void setUp() {
PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions
.builder(InstrumentationRegistry.getTargetContext())
.setNativeLibraryName(TestConstants.NATIVE_LIBRARY)
.createInitializationOptions());
VideoCodecInfo vp8Codec = new VideoCodecInfo("VP8", new HashMap<>());
VideoCodecInfo h264Codec = new VideoCodecInfo("H264", new HashMap<>());
List<VideoCodecInfo> supportedCodecs = new ArrayList<>();
supportedCodecs.add(vp8Codec);
supportedCodecs.add(h264Codec);
factory = PeerConnectionFactory.builder()
.setVideoEncoderFactory(new CustomHardwareVideoEncoderFactory(supportedCodecs))
.setVideoDecoderFactory(new CustomHardwareVideoDecoderFactory(supportedCodecs))
.createPeerConnectionFactory();
PeerConnection.RTCConfiguration config =
new PeerConnection.RTCConfiguration(Collections.emptyList());
// RtpTranceiver is part of new unified plan semantics.
config.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
pc = factory.createPeerConnection(config, mock(PeerConnection.Observer.class));
}
/**
* Test that getRtpSenderCapabilities returns the codecs supported by the video encoder factory.
*/
@Test
@SmallTest
public void testGetSenderCapabilities() {
RtpCapabilities capabilities =
factory.getRtpSenderCapabilities(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO);
List<String> codecNames = new ArrayList<>();
for (RtpCapabilities.CodecCapability codec : capabilities.getCodecs()) {
codecNames.add(codec.name);
}
assertTrue(codecNames.containsAll(Arrays.asList("VP8", "H264")));
}
/**
* Test that getRtpReceiverCapabilities returns the codecs supported by the video decoder factory.
*/
@Test
@SmallTest
public void testGetReceiverCapabilities() {
RtpCapabilities capabilities =
factory.getRtpReceiverCapabilities(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO);
List<String> codecNames = new ArrayList<>();
for (RtpCapabilities.CodecCapability codec : capabilities.getCodecs()) {
codecNames.add(codec.name);
}
assertTrue(codecNames.containsAll(Arrays.asList("VP8", "H264")));
}
/**
* Test that a created offer reflects the codec capabilities passed into setCodecPreferences.
*/
@Test
@SmallTest
public void testSetCodecPreferences() {
List<Encoding> encodings = new ArrayList<>();
encodings.add(new Encoding("1", true, null));
RtpTransceiverInit init = new RtpTransceiverInit(
RtpTransceiver.RtpTransceiverDirection.SEND_ONLY, Collections.emptyList(), encodings);
RtpTransceiver transceiver =
pc.addTransceiver(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO, init);
RtpCapabilities capabilities =
factory.getRtpSenderCapabilities(MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO);
RtpCapabilities.CodecCapability targetCodec = null;
for (RtpCapabilities.CodecCapability codec : capabilities.getCodecs()) {
if (codec.name.equals("VP8")) {
targetCodec = codec;
break;
}
}
assertNotNull(targetCodec);
transceiver.setCodecPreferences(Arrays.asList(targetCodec));
SdpObserverLatch sdpLatch = new SdpObserverLatch();
pc.createOffer(sdpLatch, new MediaConstraints());
assertTrue(sdpLatch.await());
SessionDescription offerSdp = sdpLatch.getSdp();
assertNotNull(offerSdp);
String[] sdpDescriptionLines = offerSdp.description.split("\r\n");
List<String> mediaDescriptions = getMediaDescriptions(sdpDescriptionLines);
assertEquals(1, mediaDescriptions.size());
List<String> payloads = getMediaPayloads(mediaDescriptions.get(0));
assertEquals(1, payloads.size());
String targetPayload = payloads.get(0);
List<String> rtpMaps = getRtpMaps(sdpDescriptionLines);
assertEquals(1, rtpMaps.size());
String rtpMap = rtpMaps.get(0);
String expected =
"a=rtpmap:" + targetPayload + " " + targetCodec.name + "/" + targetCodec.getClockRate();
assertEquals(expected, rtpMap);
}
private List<String> getMediaDescriptions(String[] sdpDescriptionLines) {
List<String> mediaDescriptions = new ArrayList<>();
for (String line : sdpDescriptionLines) {
if (line.charAt(0) == 'm') {
mediaDescriptions.add(line);
}
}
return mediaDescriptions;
}
private List<String> getMediaPayloads(String mediaDescription) {
String[] fields = mediaDescription.split(" ");
assertTrue(fields.length >= 4);
// Media formats are described from the fourth field onwards.
return Arrays.asList(Arrays.copyOfRange(fields, 3, fields.length));
}
private List<String> getRtpMaps(String[] sdpDescriptionLines) {
List<String> rtpMaps = new ArrayList<>();
for (String line : sdpDescriptionLines) {
if (line.startsWith("a=rtpmap")) {
rtpMaps.add(line);
}
}
return rtpMaps;
}
}