Skip to content

Commit

Permalink
ios,android: setCodecPreferences and getCapabilities
Browse files Browse the repository at this point in the history
  • Loading branch information
davidliu authored and saghul committed Nov 27, 2023
1 parent c6df5da commit 314607f
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 108 deletions.
39 changes: 34 additions & 5 deletions android/src/main/java/com/oney/WebRTCModule/SerializeUtils.java
Expand Up @@ -170,11 +170,7 @@ public static ReadableMap serializeRtpParameters(RtpParameters params) {
}
// Serializing sdpFmptLine.
if (!codec.parameters.isEmpty()) {
final String sdpFmptLineParams = codec.parameters.keySet()
.stream()
.map(key -> key + "=" + codec.parameters.get(key))
.collect(Collectors.joining(";"));
codecMap.putString("sdpFmtpLine", sdpFmptLineParams);
codecMap.putString("sdpFmtpLine", serializeSdpParameters(codec.parameters));
}

codecs.pushMap(codecMap);
Expand All @@ -192,6 +188,39 @@ public static ReadableMap serializeRtpParameters(RtpParameters params) {
return result;
}

public static ReadableMap serializeRtpCapabilities(RtpCapabilities capabilities) {
WritableMap result = Arguments.createMap();
WritableArray codecs = Arguments.createArray();

capabilities.codecs.forEach(codec -> codecs.pushMap(serializeRtpCapabilitiesCodec(codec)));

result.putArray("codecs", codecs);
return result;
}

public static ReadableMap serializeRtpCapabilitiesCodec(RtpCapabilities.CodecCapability codec) {
WritableMap codecMap = Arguments.createMap();
codecMap.putInt("payloadType", codec.preferredPayloadType);
codecMap.putString("mimeType", codec.mimeType);
codecMap.putInt("clockRate", codec.clockRate);
if (codec.numChannels != null) {
codecMap.putInt("channels", codec.numChannels);
}
if (!codec.parameters.isEmpty()) {
codecMap.putString("sdpFmtpLine", serializeSdpParameters(codec.parameters));
}

return codecMap;
}

// For serializing sdpFmptLine.
public static String serializeSdpParameters(Map<String, String> parameters) {
return parameters.keySet()
.stream()
.map(key -> key + "=" + parameters.get(key))
.collect(Collectors.joining(";"));
}

/**
* Parsing APIs
*/
Expand Down
101 changes: 85 additions & 16 deletions android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java
@@ -1,6 +1,7 @@
package com.oney.WebRTCModule;

import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;

import androidx.annotation.NonNull;
Expand Down Expand Up @@ -709,6 +710,68 @@ public void transceiverSetDirection(int id, String senderId, String direction, P
});
}

@ReactMethod(isBlockingSynchronousMethod = true)
public void transceiverSetCodecPreferences(int id, String senderId, ReadableArray codecPreferences) {
ThreadUtils.runOnExecutor(() -> {
WritableMap identifier = Arguments.createMap();
WritableMap params = Arguments.createMap();
identifier.putInt("peerConnectionId", id);
identifier.putString("transceiverId", senderId);
try {
PeerConnectionObserver pco = mPeerConnectionObservers.get(id);
if (pco == null) {
Log.d(TAG, "transceiverSetDirection() peerConnectionObserver is null");
return;
}
RtpTransceiver transceiver = pco.getTransceiver(senderId);
if (transceiver == null) {
Log.d(TAG, "transceiverSetDirection() transceiver is null");
return;
}

// Convert JSON codec capabilities to the actual objects.
RtpTransceiver.RtpTransceiverDirection direction = transceiver.getDirection();
List<Pair<Map<String, Object>, RtpCapabilities.CodecCapability>> availableCodecs = new ArrayList<>();

if (direction.equals(RtpTransceiver.RtpTransceiverDirection.SEND_RECV)
|| direction.equals(RtpTransceiver.RtpTransceiverDirection.SEND_ONLY)) {
RtpCapabilities capabilities = mFactory.getRtpSenderCapabilities(transceiver.getMediaType());
for (RtpCapabilities.CodecCapability codec : capabilities.codecs) {
Map<String, Object> codecDict = SerializeUtils.serializeRtpCapabilitiesCodec(codec).toHashMap();
availableCodecs.add(new Pair<>(codecDict, codec));
}
}

if (direction.equals(RtpTransceiver.RtpTransceiverDirection.SEND_RECV)
|| direction.equals(RtpTransceiver.RtpTransceiverDirection.RECV_ONLY)) {
RtpCapabilities capabilities = mFactory.getRtpReceiverCapabilities(transceiver.getMediaType());
for (RtpCapabilities.CodecCapability codec : capabilities.codecs) {
Map<String, Object> codecDict = SerializeUtils.serializeRtpCapabilitiesCodec(codec).toHashMap();
availableCodecs.add(new Pair<>(codecDict, codec));
}
}

// Codec preferences is order sensitive.
List<RtpCapabilities.CodecCapability> codecsToSet = new ArrayList<>();

for (int i = 0; i < codecPreferences.size(); i++) {
Map<String, Object> codecPref = codecPreferences.getMap(i).toHashMap();
for (Pair<Map<String, Object>, RtpCapabilities.CodecCapability> pair : availableCodecs) {
Map<String, Object> availableCodecDict = pair.first;
if (codecPref.equals(availableCodecDict)) {
codecsToSet.add(pair.second);
break;
}
}
}

transceiver.setCodecPreferences(codecsToSet);
} catch (Exception e) {
Log.d(TAG, "transceiverSetCodecPreferences(): " + e.getMessage());
}
});
}

@ReactMethod
public void getDisplayMedia(Promise promise) {
ThreadUtils.runOnExecutor(() -> getUserMediaImpl.getDisplayMedia(promise));
Expand Down Expand Up @@ -1129,18 +1192,21 @@ public void onSetFailure(String s) {
}

@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap receiverGetCapabilities() {
public WritableMap receiverGetCapabilities(String kind) {
try {
return (WritableMap) ThreadUtils
.submitToExecutor((Callable<Object>) () -> {
VideoCodecInfo[] videoCodecInfos = mVideoDecoderFactory.getSupportedCodecs();
WritableMap params = Arguments.createMap();
WritableArray codecs = Arguments.createArray();
for (VideoCodecInfo codecInfo : videoCodecInfos) {
codecs.pushMap(SerializeUtils.serializeVideoCodecInfo(codecInfo));
MediaStreamTrack.MediaType mediaType;
if (kind.equals("audio")) {
mediaType = MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO;
} else if (kind.equals("video")) {
mediaType = MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO;
} else {
return Arguments.createMap();
}
params.putArray("codecs", codecs);
return params;

RtpCapabilities capabilities = mFactory.getRtpReceiverCapabilities(mediaType);
return SerializeUtils.serializeRtpCapabilities(capabilities);
})
.get();
} catch (ExecutionException | InterruptedException e) {
Expand All @@ -1150,18 +1216,21 @@ public WritableMap receiverGetCapabilities() {
}

@ReactMethod(isBlockingSynchronousMethod = true)
public WritableMap senderGetCapabilities() {
public WritableMap senderGetCapabilities(String kind) {
try {
return (WritableMap) ThreadUtils
.submitToExecutor((Callable<Object>) () -> {
VideoCodecInfo[] videoCodecInfos = mVideoEncoderFactory.getSupportedCodecs();
WritableMap params = Arguments.createMap();
WritableArray codecs = Arguments.createArray();
for (VideoCodecInfo codecInfo : videoCodecInfos) {
codecs.pushMap(SerializeUtils.serializeVideoCodecInfo(codecInfo));
MediaStreamTrack.MediaType mediaType;
if (kind.equals("audio")) {
mediaType = MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO;
} else if (kind.equals("video")) {
mediaType = MediaStreamTrack.MediaType.MEDIA_TYPE_VIDEO;
} else {
return Arguments.createMap();
}
params.putArray("codecs", codecs);
return params;

RtpCapabilities capabilities = mFactory.getRtpSenderCapabilities(mediaType);
return SerializeUtils.serializeRtpCapabilities(capabilities);
})
.get();
} catch (ExecutionException | InterruptedException e) {
Expand Down
3 changes: 3 additions & 0 deletions ios/RCTWebRTC/SerializeUtils.h
@@ -1,4 +1,5 @@
#import <WebRTC/RTCMediaStreamTrack.h>
#import <WebRTC/RTCPeerConnectionFactory.h>
#import <WebRTC/RTCRtpReceiver.h>
#import <WebRTC/RTCRtpTransceiver.h>
#import <WebRTC/RTCVideoCodecInfo.h>
Expand All @@ -13,6 +14,8 @@
receiver:(RTCRtpReceiver *_Nonnull)receiver;
+ (NSDictionary *_Nonnull)trackToJSONWithPeerConnectionId:(nonnull NSNumber *)id
track:(RTCMediaStreamTrack *_Nonnull)track;
+ (NSDictionary *_Nonnull)capabilitiesToJSON:(RTCRtpCapabilities *_Nonnull)capabilities;
+ (NSDictionary *_Nonnull)codecCapabilityToJSON:(RTCRtpCodecCapability *_Nonnull)codec;
+ (NSString *_Nonnull)serializeDirection:(RTCRtpTransceiverDirection)direction;
+ (RTCRtpTransceiverDirection)parseDirection:(NSString *_Nonnull)direction;
+ (RTCRtpTransceiverInit *_Nonnull)parseTransceiverOptions:(NSDictionary *_Nonnull)parameters;
Expand Down
49 changes: 42 additions & 7 deletions ios/RCTWebRTC/SerializeUtils.m
Expand Up @@ -130,13 +130,7 @@ + (NSDictionary *)parametersToJSON:(RTCRtpParameters *)params {
}

if (codec.parameters.count) {
NSMutableArray *parts = [NSMutableArray arrayWithCapacity:codec.parameters.count];
[codec.parameters
enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL *_Nonnull stop) {
[parts addObject:[NSString stringWithFormat:@"%@=%@", key, value]];
}];

codecDictionary[@"sdpFmtpLine"] = [parts componentsJoinedByString:@";"];
codecDictionary[@"sdpFmtpLine"] = [self serializeSdpParameters:codec.parameters];
}

[codecs addObject:codecDictionary];
Expand Down Expand Up @@ -176,6 +170,47 @@ + (NSDictionary *)trackToJSONWithPeerConnectionId:(NSNumber *)id track:(RTCMedia
};
}

+ (NSDictionary *)capabilitiesToJSON:(RTCRtpCapabilities *)capabilities {
NSMutableArray *codecs = [NSMutableArray new];

for (RTCRtpCodecCapability *codec in capabilities.codecs) {
[codecs addObject:[self codecCapabilityToJSON:codec]];
}

return @{@"codecs" : codecs};
}

+ (NSDictionary *)codecCapabilityToJSON:(RTCRtpCodecCapability *)codec {
NSMutableDictionary *codecDictionary = [NSMutableDictionary new];

codecDictionary[@"payloadType"] = codec.preferredPayloadType;
codecDictionary[@"mimeType"] = codec.mimeType;
codecDictionary[@"clockRate"] = codec.clockRate;

if (codec.numChannels) {
codecDictionary[@"channels"] = codec.numChannels;
}

if (codec.parameters.count) {
codecDictionary[@"sdpFmtpLine"] = [self serializeSdpParameters:codec.parameters];
}

return codecDictionary;
}

+ (NSString *)serializeSdpParameters:(NSDictionary *)parameters {
if (parameters == nil || parameters.count == 0) {
return nil;
}

NSMutableArray *parts = [NSMutableArray arrayWithCapacity:parameters.count];
[parameters enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull value, BOOL *_Nonnull stop) {
[parts addObject:[NSString stringWithFormat:@"%@=%@", key, value]];
}];

return [parts componentsJoinedByString:@";"];
}

+ (NSString *)serializeDirection:(RTCRtpTransceiverDirection)direction {
if (direction == RTCRtpTransceiverDirectionInactive) {
return @"inactive";
Expand Down

0 comments on commit 314607f

Please sign in to comment.