diff --git a/AUTHORS b/AUTHORS index 83a968d326..1e4161e339 100644 --- a/AUTHORS +++ b/AUTHORS @@ -154,6 +154,7 @@ HyperConnect Inc. <*@hpcnt.com> 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 5bf25ffc6d..4c386444a0 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -287,6 +287,7 @@ if (is_android) { "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 @@ if (current_os == "linux" || is_android) { "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 @@ if (current_os == "linux" || is_android) { "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 @@ if (is_android) { "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 ca67b3afc1..de898727a5 100644 --- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java +++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java @@ -15,7 +15,9 @@ import android.os.Process; 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 @@ public class PeerConnectionFactory { 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 @@ public class PeerConnectionFactory { 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 0000000000..02d1704243 --- /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 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 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 codecs; + public List headerExtensions; + + @CalledByNative + RtpCapabilities(List codecs, List headerExtensions) { + this.headerExtensions = headerExtensions; + this.codecs = codecs; + } + + @CalledByNative + public List getHeaderExtensions() { + return headerExtensions; + } + + @CalledByNative + List getCodecs() { + return codecs; + } +} diff --git a/sdk/android/api/org/webrtc/RtpTransceiver.java b/sdk/android/api/org/webrtc/RtpTransceiver.java index 1102bd7eb1..d2ba733db0 100644 --- a/sdk/android/api/org/webrtc/RtpTransceiver.java +++ b/sdk/android/api/org/webrtc/RtpTransceiver.java @@ -215,6 +215,11 @@ public class RtpTransceiver { nativeStopInternal(nativeRtpTransceiver); } + public void setCodecPreferences(List codecs) { + checkRtpTransceiverExists(); + nativeSetCodecPreferences(nativeRtpTransceiver, codecs); + } + /** * The StopInternal method stops the RtpTransceiver, like Stop, but goes * immediately to Stopped state. @@ -263,4 +268,6 @@ public class RtpTransceiver { private static native void nativeStopStandard(long rtpTransceiver); private static native boolean nativeSetDirection( long rtpTransceiver, RtpTransceiverDirection rtpTransceiverDirection); + private static native void nativeSetCodecPreferences( + long rtpTransceiver, List codecs); } diff --git a/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionEndToEndTest.java b/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionEndToEndTest.java index e433fa4208..64b1ac0573 100644 --- a/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionEndToEndTest.java +++ b/sdk/android/instrumentationtests/src/org/webrtc/PeerConnectionEndToEndTest.java @@ -582,7 +582,7 @@ public class PeerConnectionEndToEndTest { } } - 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 0000000000..961ecd5c70 --- /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 supportedCodecs; + + public CustomHardwareVideoEncoderFactory(List 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 supportedCodecs; + + public CustomHardwareVideoDecoderFactory(List 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 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 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 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 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 mediaDescriptions = getMediaDescriptions(sdpDescriptionLines); + assertEquals(1, mediaDescriptions.size()); + + List payloads = getMediaPayloads(mediaDescriptions.get(0)); + assertEquals(1, payloads.size()); + + String targetPayload = payloads.get(0); + List 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 getMediaDescriptions(String[] sdpDescriptionLines) { + List mediaDescriptions = new ArrayList<>(); + for (String line : sdpDescriptionLines) { + if (line.charAt(0) == 'm') { + mediaDescriptions.add(line); + } + } + + return mediaDescriptions; + } + + private List 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 getRtpMaps(String[] sdpDescriptionLines) { + List 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 4c682089db..c6099c2736 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 @@ jlong JNI_PeerConnectionFactory_CreateAudioTrack( return jlongFromPointer(track.release()); } +ScopedJavaLocalRef JNI_PeerConnectionFactory_GetRtpSenderCapabilities( + JNIEnv* jni, + jlong native_factory, + const JavaParamRef& media_type) { + auto factory = PeerConnectionFactoryFromJava(native_factory); + return NativeToJavaRtpCapabilities( + jni, factory->GetRtpSenderCapabilities( + JavaToNativeMediaType(jni, media_type))); +} + +ScopedJavaLocalRef +JNI_PeerConnectionFactory_GetRtpReceiverCapabilities( + JNIEnv* jni, + jlong native_factory, + const JavaParamRef& 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 0000000000..09157be287 --- /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 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 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& j_capabilities) { + RtpCapabilities capabilities; + + ScopedJavaLocalRef j_header_extensions = + Java_RtpCapabilities_getHeaderExtensions(jni, j_capabilities); + for (const JavaRef& 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 j_codecs = + Java_RtpCapabilities_getCodecs(jni, j_capabilities); + for (const JavaRef& 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 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& 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 0000000000..07be9f0d25 --- /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 + +#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& j_capabilities); + +ScopedJavaLocalRef NativeToJavaRtpCapabilities( + JNIEnv* jni, + const RtpCapabilities& capabilities); + +RtpCodecCapability JavaToNativeRtpCodecCapability( + JNIEnv* jni, + const JavaRef& 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 1d468461f1..748dbb7f10 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 @@ ScopedJavaLocalRef JNI_RtpTransceiver_CurrentDirection( : nullptr; } +void JNI_RtpTransceiver_SetCodecPreferences( + JNIEnv* jni, + jlong j_rtp_transceiver_pointer, + const JavaParamRef& j_codecs) { + std::vector codecs = + JavaListToNativeVector( + jni, j_codecs, &JavaToNativeRtpCodecCapability); + reinterpret_cast(j_rtp_transceiver_pointer) + ->SetCodecPreferences(codecs); +} + void JNI_RtpTransceiver_StopInternal(JNIEnv* jni, jlong j_rtp_transceiver_pointer) { reinterpret_cast(j_rtp_transceiver_pointer)