Expose setCodecPreferences/getCapabilities for android
Bug: webrtc:15177
Change-Id: If61ef9a87bc4f68d73cef6e681461682ca48f034
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/304880
Reviewed-by: Sergey Silkin <ssilkin@webrtc.org>
Commit-Queue: Sergey Silkin <ssilkin@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40071}
diff --git a/AUTHORS b/AUTHORS
index 83a968d..1e4161e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -154,6 +154,7 @@
Intel Corporation <*@intel.com>
LG Electronics, Inc. <*@lge.com>
Life On Air Inc. <*@lifeonair.com>
+LiveKit <*@livekit.io>
Meta Platforms, Inc. <*@meta.com>
Microsoft Corporation <*@microsoft.com>
MIPS Technologies <*@mips.com>
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index 5bf25ff..4c38644 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -287,6 +287,7 @@
"api/org/webrtc/RTCStatsCollectorCallback.java",
"api/org/webrtc/RTCStatsReport.java",
"api/org/webrtc/RtcCertificatePem.java",
+ "api/org/webrtc/RtpCapabilities.java",
"api/org/webrtc/RtpParameters.java",
"api/org/webrtc/RtpReceiver.java",
"api/org/webrtc/RtpSender.java",
@@ -734,6 +735,8 @@
"src/jni/pc/rtc_certificate.h",
"src/jni/pc/rtc_stats_collector_callback_wrapper.cc",
"src/jni/pc/rtc_stats_collector_callback_wrapper.h",
+ "src/jni/pc/rtp_capabilities.cc",
+ "src/jni/pc/rtp_capabilities.h",
"src/jni/pc/rtp_parameters.cc",
"src/jni/pc/rtp_parameters.h",
"src/jni/pc/rtp_receiver.cc",
@@ -1391,6 +1394,7 @@
"api/org/webrtc/RTCStatsCollectorCallback.java",
"api/org/webrtc/RTCStatsReport.java",
"api/org/webrtc/RtcCertificatePem.java",
+ "api/org/webrtc/RtpCapabilities.java",
"api/org/webrtc/RtpParameters.java",
"api/org/webrtc/RtpReceiver.java",
"api/org/webrtc/RtpSender.java",
@@ -1486,6 +1490,7 @@
"instrumentationtests/src/org/webrtc/PeerConnectionTest.java",
"instrumentationtests/src/org/webrtc/RendererCommonTest.java",
"instrumentationtests/src/org/webrtc/RtcCertificatePemTest.java",
+ "instrumentationtests/src/org/webrtc/RtpCapabilitiesTest.java",
"instrumentationtests/src/org/webrtc/RtpSenderTest.java",
"instrumentationtests/src/org/webrtc/RtpTransceiverTest.java",
"instrumentationtests/src/org/webrtc/SoftwareVideoDecoderFactoryTest.java",
diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
index ca67b3a..de89872 100644
--- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java
+++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
@@ -15,7 +15,9 @@
import androidx.annotation.Nullable;
import java.util.List;
import org.webrtc.Logging.Severity;
+import org.webrtc.MediaStreamTrack;
import org.webrtc.PeerConnection;
+import org.webrtc.RtpCapabilities;
import org.webrtc.audio.AudioDeviceModule;
import org.webrtc.audio.JavaAudioDeviceModule;
@@ -471,6 +473,16 @@
return new AudioTrack(nativeCreateAudioTrack(nativeFactory, id, source.getNativeAudioSource()));
}
+ public RtpCapabilities getRtpReceiverCapabilities(MediaStreamTrack.MediaType mediaType) {
+ checkPeerConnectionFactoryExists();
+ return nativeGetRtpReceiverCapabilities(nativeFactory, mediaType);
+ }
+
+ public RtpCapabilities getRtpSenderCapabilities(MediaStreamTrack.MediaType mediaType) {
+ checkPeerConnectionFactoryExists();
+ return nativeGetRtpSenderCapabilities(nativeFactory, mediaType);
+ }
+
// Starts recording an AEC dump. Ownership of the file is transfered to the
// native code. If an AEC dump is already in progress, it will be stopped and
// a new one will start using the provided file.
@@ -615,4 +627,8 @@
private static native void nativeInjectLoggable(JNILogging jniLogging, int severity);
private static native void nativeDeleteLoggable();
private static native void nativePrintStackTrace(int tid);
+ private static native RtpCapabilities nativeGetRtpSenderCapabilities(
+ long factory, MediaStreamTrack.MediaType mediaType);
+ private static native RtpCapabilities nativeGetRtpReceiverCapabilities(
+ long factory, MediaStreamTrack.MediaType mediaType);
}
diff --git a/sdk/android/api/org/webrtc/RtpCapabilities.java b/sdk/android/api/org/webrtc/RtpCapabilities.java
new file mode 100644
index 0000000..02d1704
--- /dev/null
+++ b/sdk/android/api/org/webrtc/RtpCapabilities.java
@@ -0,0 +1,125 @@
+/*
+ * 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 androidx.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+import org.webrtc.MediaStreamTrack;
+
+public class RtpCapabilities {
+ public static class CodecCapability {
+ public int preferredPayloadType;
+ // Name used to identify the codec. Equivalent to MIME subtype.
+ public String name;
+ // The media type of this codec. Equivalent to MIME top-level type.
+ public MediaStreamTrack.MediaType kind;
+ // Clock rate in Hertz.
+ public Integer clockRate;
+ // The number of audio channels used. Set to null for video codecs.
+ public Integer numChannels;
+ // The "format specific parameters" field from the "a=fmtp" line in the SDP
+ public Map<String, String> parameters;
+ // The MIME type of the codec. This is a convenience field.
+ public String mimeType;
+
+ public CodecCapability() {}
+
+ @CalledByNative("CodecCapability")
+ CodecCapability(int preferredPayloadType, String name, MediaStreamTrack.MediaType kind,
+ Integer clockRate, Integer numChannels, String mimeType, Map<String, String> parameters) {
+ this.preferredPayloadType = preferredPayloadType;
+ this.name = name;
+ this.kind = kind;
+ this.clockRate = clockRate;
+ this.numChannels = numChannels;
+ this.parameters = parameters;
+ this.mimeType = mimeType;
+ }
+
+ @CalledByNative("CodecCapability")
+ int getPreferredPayloadType() {
+ return preferredPayloadType;
+ }
+
+ @CalledByNative("CodecCapability")
+ String getName() {
+ return name;
+ }
+
+ @CalledByNative("CodecCapability")
+ MediaStreamTrack.MediaType getKind() {
+ return kind;
+ }
+
+ @CalledByNative("CodecCapability")
+ Integer getClockRate() {
+ return clockRate;
+ }
+
+ @CalledByNative("CodecCapability")
+ Integer getNumChannels() {
+ return numChannels;
+ }
+
+ @CalledByNative("CodecCapability")
+ Map getParameters() {
+ return parameters;
+ }
+ }
+
+ public static class HeaderExtensionCapability {
+ private final String uri;
+ private final int preferredId;
+ private final boolean preferredEncrypted;
+
+ @CalledByNative("HeaderExtensionCapability")
+ HeaderExtensionCapability(String uri, int preferredId, boolean preferredEncrypted) {
+ this.uri = uri;
+ this.preferredId = preferredId;
+ this.preferredEncrypted = preferredEncrypted;
+ }
+
+ @CalledByNative("HeaderExtensionCapability")
+ public String getUri() {
+ return uri;
+ }
+
+ @CalledByNative("HeaderExtensionCapability")
+ public int getPreferredId() {
+ return preferredId;
+ }
+
+ @CalledByNative("HeaderExtensionCapability")
+ public boolean getPreferredEncrypted() {
+ return preferredEncrypted;
+ }
+ }
+
+ public List<CodecCapability> codecs;
+ public List<HeaderExtensionCapability> headerExtensions;
+
+ @CalledByNative
+ RtpCapabilities(List<CodecCapability> codecs, List<HeaderExtensionCapability> headerExtensions) {
+ this.headerExtensions = headerExtensions;
+ this.codecs = codecs;
+ }
+
+ @CalledByNative
+ public List<HeaderExtensionCapability> getHeaderExtensions() {
+ return headerExtensions;
+ }
+
+ @CalledByNative
+ List<CodecCapability> getCodecs() {
+ return codecs;
+ }
+}
diff --git a/sdk/android/api/org/webrtc/RtpTransceiver.java b/sdk/android/api/org/webrtc/RtpTransceiver.java
index 1102bd7..d2ba733 100644
--- a/sdk/android/api/org/webrtc/RtpTransceiver.java
+++ b/sdk/android/api/org/webrtc/RtpTransceiver.java
@@ -215,6 +215,11 @@
nativeStopInternal(nativeRtpTransceiver);
}
+ public void setCodecPreferences(List<RtpCapabilities.CodecCapability> codecs) {
+ checkRtpTransceiverExists();
+ nativeSetCodecPreferences(nativeRtpTransceiver, codecs);
+ }
+
/**
* The StopInternal method stops the RtpTransceiver, like Stop, but goes
* immediately to Stopped state.
@@ -263,4 +268,6 @@
private static native void nativeStopStandard(long rtpTransceiver);
private static native boolean nativeSetDirection(
long rtpTransceiver, RtpTransceiverDirection rtpTransceiverDirection);
+ private static native void nativeSetCodecPreferences(
+ long rtpTransceiver, List<RtpCapabilities.CodecCapability> codecs);
}
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionEndToEndTest.java b/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionEndToEndTest.java
index e433fa4..64b1ac0 100644
--- a/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionEndToEndTest.java
+++ b/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionEndToEndTest.java
@@ -582,7 +582,7 @@
}
}
- private static class SdpObserverLatch implements SdpObserver {
+ static class SdpObserverLatch implements SdpObserver {
private boolean success;
private @Nullable SessionDescription sdp;
private @Nullable String error;
diff --git a/sdk/android/instrumentationtests/src/org/webrtc/RtpCapabilitiesTest.java b/sdk/android/instrumentationtests/src/org/webrtc/RtpCapabilitiesTest.java
new file mode 100644
index 0000000..961ecd5
--- /dev/null
+++ b/sdk/android/instrumentationtests/src/org/webrtc/RtpCapabilitiesTest.java
@@ -0,0 +1,217 @@
+/*
+ * 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;
+ }
+}
diff --git a/sdk/android/src/jni/pc/peer_connection_factory.cc b/sdk/android/src/jni/pc/peer_connection_factory.cc
index 4c68208..c6099c2 100644
--- a/sdk/android/src/jni/pc/peer_connection_factory.cc
+++ b/sdk/android/src/jni/pc/peer_connection_factory.cc
@@ -40,8 +40,10 @@
#include "sdk/android/src/jni/pc/android_network_monitor.h"
#include "sdk/android/src/jni/pc/audio.h"
#include "sdk/android/src/jni/pc/ice_candidate.h"
+#include "sdk/android/src/jni/pc/media_stream_track.h"
#include "sdk/android/src/jni/pc/owned_factory_and_threads.h"
#include "sdk/android/src/jni/pc/peer_connection.h"
+#include "sdk/android/src/jni/pc/rtp_capabilities.h"
#include "sdk/android/src/jni/pc/ssl_certificate_verifier_wrapper.h"
#include "sdk/android/src/jni/pc/video.h"
#include "system_wrappers/include/field_trial.h"
@@ -393,6 +395,27 @@
return jlongFromPointer(track.release());
}
+ScopedJavaLocalRef<jobject> JNI_PeerConnectionFactory_GetRtpSenderCapabilities(
+ JNIEnv* jni,
+ jlong native_factory,
+ const JavaParamRef<jobject>& media_type) {
+ auto factory = PeerConnectionFactoryFromJava(native_factory);
+ return NativeToJavaRtpCapabilities(
+ jni, factory->GetRtpSenderCapabilities(
+ JavaToNativeMediaType(jni, media_type)));
+}
+
+ScopedJavaLocalRef<jobject>
+JNI_PeerConnectionFactory_GetRtpReceiverCapabilities(
+ JNIEnv* jni,
+ jlong native_factory,
+ const JavaParamRef<jobject>& media_type) {
+ auto factory = PeerConnectionFactoryFromJava(native_factory);
+ return NativeToJavaRtpCapabilities(
+ jni, factory->GetRtpReceiverCapabilities(
+ JavaToNativeMediaType(jni, media_type)));
+}
+
static jboolean JNI_PeerConnectionFactory_StartAecDump(
JNIEnv* jni,
jlong native_factory,
diff --git a/sdk/android/src/jni/pc/rtp_capabilities.cc b/sdk/android/src/jni/pc/rtp_capabilities.cc
new file mode 100644
index 0000000..09157be
--- /dev/null
+++ b/sdk/android/src/jni/pc/rtp_capabilities.cc
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#include "sdk/android/src/jni/pc/rtp_capabilities.h"
+
+#include "sdk/android/generated_peerconnection_jni/RtpCapabilities_jni.h"
+#include "sdk/android/native_api/jni/java_types.h"
+#include "sdk/android/src/jni/jni_helpers.h"
+#include "sdk/android/src/jni/pc/media_stream_track.h"
+
+namespace webrtc {
+namespace jni {
+
+namespace {
+
+ScopedJavaLocalRef<jobject> NativeToJavaRtpCodecParameter(
+ JNIEnv* env,
+ const RtpCodecCapability& codec) {
+ return Java_CodecCapability_Constructor(
+ env, codec.preferred_payload_type.value(),
+ NativeToJavaString(env, codec.name),
+ NativeToJavaMediaType(env, codec.kind),
+ NativeToJavaInteger(env, codec.clock_rate),
+ NativeToJavaInteger(env, codec.num_channels),
+ NativeToJavaString(env, codec.mime_type()),
+ NativeToJavaStringMap(env, codec.parameters));
+}
+
+ScopedJavaLocalRef<jobject> NativeToJavaRtpHeaderExtensionParameter(
+ JNIEnv* env,
+ const RtpHeaderExtensionCapability& extension) {
+ return Java_HeaderExtensionCapability_Constructor(
+ env, NativeToJavaString(env, extension.uri),
+ extension.preferred_id.value(), extension.preferred_encrypt);
+}
+} // namespace
+
+RtpCapabilities JavaToNativeRtpCapabilities(
+ JNIEnv* jni,
+ const JavaRef<jobject>& j_capabilities) {
+ RtpCapabilities capabilities;
+
+ ScopedJavaLocalRef<jobject> j_header_extensions =
+ Java_RtpCapabilities_getHeaderExtensions(jni, j_capabilities);
+ for (const JavaRef<jobject>& j_header_extension :
+ Iterable(jni, j_header_extensions)) {
+ RtpHeaderExtensionCapability header_extension;
+ header_extension.uri = JavaToStdString(
+ jni, Java_HeaderExtensionCapability_getUri(jni, j_header_extension));
+ header_extension.preferred_id =
+ Java_HeaderExtensionCapability_getPreferredId(jni, j_header_extension);
+ header_extension.preferred_encrypt =
+ Java_HeaderExtensionCapability_getPreferredEncrypted(
+ jni, j_header_extension);
+ capabilities.header_extensions.push_back(header_extension);
+ }
+
+ // Convert codecs.
+ ScopedJavaLocalRef<jobject> j_codecs =
+ Java_RtpCapabilities_getCodecs(jni, j_capabilities);
+ for (const JavaRef<jobject>& j_codec : Iterable(jni, j_codecs)) {
+ RtpCodecCapability codec;
+ codec.preferred_payload_type =
+ Java_CodecCapability_getPreferredPayloadType(jni, j_codec);
+ codec.name =
+ JavaToStdString(jni, Java_CodecCapability_getName(jni, j_codec));
+ codec.kind =
+ JavaToNativeMediaType(jni, Java_CodecCapability_getKind(jni, j_codec));
+ codec.clock_rate = JavaToNativeOptionalInt(
+ jni, Java_CodecCapability_getClockRate(jni, j_codec));
+ codec.num_channels = JavaToNativeOptionalInt(
+ jni, Java_CodecCapability_getNumChannels(jni, j_codec));
+ auto parameters_map = JavaToNativeStringMap(
+ jni, Java_CodecCapability_getParameters(jni, j_codec));
+ codec.parameters.insert(parameters_map.begin(), parameters_map.end());
+ capabilities.codecs.push_back(codec);
+ }
+ return capabilities;
+}
+
+ScopedJavaLocalRef<jobject> NativeToJavaRtpCapabilities(
+ JNIEnv* env,
+ const RtpCapabilities& capabilities) {
+ return Java_RtpCapabilities_Constructor(
+ env,
+ NativeToJavaList(env, capabilities.codecs,
+ &NativeToJavaRtpCodecParameter),
+ NativeToJavaList(env, capabilities.header_extensions,
+ &NativeToJavaRtpHeaderExtensionParameter));
+}
+
+RtpCodecCapability JavaToNativeRtpCodecCapability(
+ JNIEnv* jni,
+ const JavaRef<jobject>& j_codec) {
+ RtpCodecCapability codec;
+ codec.preferred_payload_type =
+ Java_CodecCapability_getPreferredPayloadType(jni, j_codec);
+ codec.name = JavaToStdString(jni, Java_CodecCapability_getName(jni, j_codec));
+ codec.kind =
+ JavaToNativeMediaType(jni, Java_CodecCapability_getKind(jni, j_codec));
+ codec.clock_rate = JavaToNativeOptionalInt(
+ jni, Java_CodecCapability_getClockRate(jni, j_codec));
+ codec.num_channels = JavaToNativeOptionalInt(
+ jni, Java_CodecCapability_getNumChannels(jni, j_codec));
+ auto parameters_map = JavaToNativeStringMap(
+ jni, Java_CodecCapability_getParameters(jni, j_codec));
+ codec.parameters.insert(parameters_map.begin(), parameters_map.end());
+ return codec;
+}
+
+} // namespace jni
+} // namespace webrtc
diff --git a/sdk/android/src/jni/pc/rtp_capabilities.h b/sdk/android/src/jni/pc/rtp_capabilities.h
new file mode 100644
index 0000000..07be9f0
--- /dev/null
+++ b/sdk/android/src/jni/pc/rtp_capabilities.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef SDK_ANDROID_SRC_JNI_PC_RTP_CAPABILITIES_H_
+#define SDK_ANDROID_SRC_JNI_PC_RTP_CAPABILITIES_H_
+
+#include <jni.h>
+
+#include "api/rtp_parameters.h"
+#include "sdk/android/native_api/jni/scoped_java_ref.h"
+
+namespace webrtc {
+namespace jni {
+
+RtpCapabilities JavaToNativeRtpCapabilities(
+ JNIEnv* jni,
+ const JavaRef<jobject>& j_capabilities);
+
+ScopedJavaLocalRef<jobject> NativeToJavaRtpCapabilities(
+ JNIEnv* jni,
+ const RtpCapabilities& capabilities);
+
+RtpCodecCapability JavaToNativeRtpCodecCapability(
+ JNIEnv* jni,
+ const JavaRef<jobject>& j_codec_capability);
+
+} // namespace jni
+} // namespace webrtc
+
+#endif // SDK_ANDROID_SRC_JNI_PC_RTP_CAPABILITIES_H_
diff --git a/sdk/android/src/jni/pc/rtp_transceiver.cc b/sdk/android/src/jni/pc/rtp_transceiver.cc
index 1d46846..748dbb7 100644
--- a/sdk/android/src/jni/pc/rtp_transceiver.cc
+++ b/sdk/android/src/jni/pc/rtp_transceiver.cc
@@ -16,6 +16,7 @@
#include "sdk/android/native_api/jni/java_types.h"
#include "sdk/android/src/jni/jni_helpers.h"
#include "sdk/android/src/jni/pc/media_stream_track.h"
+#include "sdk/android/src/jni/pc/rtp_capabilities.h"
#include "sdk/android/src/jni/pc/rtp_parameters.h"
#include "sdk/android/src/jni/pc/rtp_receiver.h"
#include "sdk/android/src/jni/pc/rtp_sender.h"
@@ -139,6 +140,17 @@
: nullptr;
}
+void JNI_RtpTransceiver_SetCodecPreferences(
+ JNIEnv* jni,
+ jlong j_rtp_transceiver_pointer,
+ const JavaParamRef<jobject>& j_codecs) {
+ std::vector<RtpCodecCapability> codecs =
+ JavaListToNativeVector<RtpCodecCapability, jobject>(
+ jni, j_codecs, &JavaToNativeRtpCodecCapability);
+ reinterpret_cast<RtpTransceiverInterface*>(j_rtp_transceiver_pointer)
+ ->SetCodecPreferences(codecs);
+}
+
void JNI_RtpTransceiver_StopInternal(JNIEnv* jni,
jlong j_rtp_transceiver_pointer) {
reinterpret_cast<RtpTransceiverInterface*>(j_rtp_transceiver_pointer)