Formatting done via: git ls-files | grep -E '.*\.m$' | xargs clang-format -i After applying the command, I manually excluded Matlab .m files that I will handle separately. No-Iwyu: Includes didn't change and it isn't related to formatting Bug: webrtc:42225392 Change-Id: I40d11fd6b650ee0d90d92cbd6fc6aa6c78e1fea3 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/373887 Commit-Queue: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43706}
785 lines
28 KiB
Objective-C
785 lines
28 KiB
Objective-C
/*
|
|
* Copyright 2018 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.
|
|
*/
|
|
|
|
#import "base/RTCVideoDecoderFactory.h"
|
|
#import "base/RTCVideoEncoderFactory.h"
|
|
|
|
#import "api/peerconnection/RTCAudioSource.h"
|
|
#import "api/peerconnection/RTCConfiguration.h"
|
|
#import "api/peerconnection/RTCDataChannel.h"
|
|
#import "api/peerconnection/RTCDataChannelConfiguration.h"
|
|
#import "api/peerconnection/RTCMediaConstraints.h"
|
|
#import "api/peerconnection/RTCMediaStreamTrack.h"
|
|
#import "api/peerconnection/RTCPeerConnection.h"
|
|
#import "api/peerconnection/RTCPeerConnectionFactory.h"
|
|
#import "api/peerconnection/RTCRtpCapabilities.h"
|
|
#import "api/peerconnection/RTCRtpCodecCapability.h"
|
|
#import "api/peerconnection/RTCRtpHeaderExtensionCapability.h"
|
|
#import "api/peerconnection/RTCRtpReceiver.h"
|
|
#import "api/peerconnection/RTCRtpSender.h"
|
|
#import "api/peerconnection/RTCRtpTransceiver.h"
|
|
#import "api/peerconnection/RTCSessionDescription.h"
|
|
#import "api/peerconnection/RTCVideoSource.h"
|
|
#import "rtc_base/system/unused.h"
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
@interface MockVideoEncoderDecoderFactory
|
|
: NSObject <RTC_OBJC_TYPE (RTCVideoEncoderFactory),
|
|
RTC_OBJC_TYPE (RTCVideoDecoderFactory)>
|
|
- (instancetype)initWithSupportedCodecs:
|
|
(nonnull NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs;
|
|
@end
|
|
|
|
@implementation MockVideoEncoderDecoderFactory {
|
|
NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *_supportedCodecs;
|
|
}
|
|
|
|
- (instancetype)initWithSupportedCodecs:
|
|
(nonnull NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs {
|
|
self = [super init];
|
|
if (self) {
|
|
_supportedCodecs = supportedCodecs;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (nullable id<RTC_OBJC_TYPE(RTCVideoEncoder)>)createEncoder:
|
|
(nonnull RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info {
|
|
return nil;
|
|
}
|
|
|
|
- (nullable id<RTC_OBJC_TYPE(RTCVideoDecoder)>)createDecoder:
|
|
(nonnull RTC_OBJC_TYPE(RTCVideoCodecInfo) *)info {
|
|
return nil;
|
|
}
|
|
|
|
- (nonnull NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)supportedCodecs {
|
|
return _supportedCodecs;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface RTCPeerConnectionFactoryTests : XCTestCase
|
|
@end
|
|
|
|
@implementation RTCPeerConnectionFactoryTests
|
|
|
|
- (void)testPeerConnectionLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
|
|
initWithMandatoryConstraints:@{}
|
|
optionalConstraints:nil];
|
|
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
peerConnection = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
[peerConnection close];
|
|
factory = nil;
|
|
}
|
|
peerConnection = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, @"Expect test does not crash");
|
|
}
|
|
|
|
- (void)testMediaStreamLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCMediaStream) * mediaStream;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
mediaStream = [factory mediaStreamWithStreamId:@"mediaStream"];
|
|
factory = nil;
|
|
}
|
|
mediaStream = nil;
|
|
RTC_UNUSED(mediaStream);
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testDataChannelLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
|
|
initWithMandatoryConstraints:@{}
|
|
optionalConstraints:nil];
|
|
RTC_OBJC_TYPE(RTCDataChannelConfiguration) *dataChannelConfig =
|
|
[[RTC_OBJC_TYPE(RTCDataChannelConfiguration) alloc] init];
|
|
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection;
|
|
RTC_OBJC_TYPE(RTCDataChannel) * dataChannel;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
peerConnection = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
dataChannel = [peerConnection dataChannelForLabel:@"test_channel"
|
|
configuration:dataChannelConfig];
|
|
XCTAssertNotNil(dataChannel);
|
|
[peerConnection close];
|
|
peerConnection = nil;
|
|
factory = nil;
|
|
}
|
|
dataChannel = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testRTCRtpTransceiverLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
config.sdpSemantics = RTCSdpSemanticsUnifiedPlan;
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *contraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
|
|
initWithMandatoryConstraints:@{}
|
|
optionalConstraints:nil];
|
|
RTC_OBJC_TYPE(RTCRtpTransceiverInit) *init =
|
|
[[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init];
|
|
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection;
|
|
RTC_OBJC_TYPE(RTCRtpTransceiver) * tranceiver;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
peerConnection = [factory peerConnectionWithConfiguration:config
|
|
constraints:contraints
|
|
delegate:nil];
|
|
tranceiver = [peerConnection addTransceiverOfType:RTCRtpMediaTypeAudio
|
|
init:init];
|
|
XCTAssertNotNil(tranceiver);
|
|
[peerConnection close];
|
|
peerConnection = nil;
|
|
factory = nil;
|
|
}
|
|
tranceiver = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testRTCRtpSenderLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
config.sdpSemantics = RTCSdpSemanticsPlanB;
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
|
|
initWithMandatoryConstraints:@{}
|
|
optionalConstraints:nil];
|
|
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection;
|
|
RTC_OBJC_TYPE(RTCRtpSender) * sender;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
peerConnection = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
sender = [peerConnection senderWithKind:kRTCMediaStreamTrackKindVideo
|
|
streamId:@"stream"];
|
|
XCTAssertNotNil(sender);
|
|
[peerConnection close];
|
|
peerConnection = nil;
|
|
factory = nil;
|
|
}
|
|
sender = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testRTCRtpReceiverLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
config.sdpSemantics = RTCSdpSemanticsPlanB;
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
|
|
initWithMandatoryConstraints:@{}
|
|
optionalConstraints:nil];
|
|
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * pc1;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * pc2;
|
|
|
|
NSArray<RTC_OBJC_TYPE(RTCRtpReceiver) *> *receivers1;
|
|
NSArray<RTC_OBJC_TYPE(RTCRtpReceiver) *> *receivers2;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
pc1 = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
[pc1 senderWithKind:kRTCMediaStreamTrackKindAudio streamId:@"stream"];
|
|
|
|
pc2 = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
[pc2 senderWithKind:kRTCMediaStreamTrackKindAudio streamId:@"stream"];
|
|
|
|
NSTimeInterval negotiationTimeout = 15;
|
|
XCTAssertTrue([self negotiatePeerConnection:pc1
|
|
withPeerConnection:pc2
|
|
negotiationTimeout:negotiationTimeout]);
|
|
|
|
XCTAssertEqual(pc1.signalingState, RTCSignalingStateStable);
|
|
XCTAssertEqual(pc2.signalingState, RTCSignalingStateStable);
|
|
|
|
receivers1 = pc1.receivers;
|
|
receivers2 = pc2.receivers;
|
|
XCTAssertTrue(receivers1.count > 0);
|
|
XCTAssertTrue(receivers2.count > 0);
|
|
[pc1 close];
|
|
[pc2 close];
|
|
pc1 = nil;
|
|
pc2 = nil;
|
|
factory = nil;
|
|
}
|
|
receivers1 = nil;
|
|
receivers2 = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testAudioSourceLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCAudioSource) * audioSource;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
audioSource = [factory audioSourceWithConstraints:nil];
|
|
XCTAssertNotNil(audioSource);
|
|
factory = nil;
|
|
}
|
|
audioSource = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testVideoSourceLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCVideoSource) * videoSource;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
videoSource = [factory videoSource];
|
|
XCTAssertNotNil(videoSource);
|
|
factory = nil;
|
|
}
|
|
videoSource = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testAudioTrackLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCAudioTrack) * audioTrack;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
audioTrack = [factory audioTrackWithTrackId:@"audioTrack"];
|
|
XCTAssertNotNil(audioTrack);
|
|
factory = nil;
|
|
}
|
|
audioTrack = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testVideoTrackLifetime {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCVideoTrack) * videoTrack;
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
videoTrack = [factory videoTrackWithSource:[factory videoSource]
|
|
trackId:@"videoTrack"];
|
|
XCTAssertNotNil(videoTrack);
|
|
factory = nil;
|
|
}
|
|
videoTrack = nil;
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
|
|
- (void)testRollback {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
config.sdpSemantics = RTCSdpSemanticsUnifiedPlan;
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *constraints = [[RTC_OBJC_TYPE(
|
|
RTCMediaConstraints) alloc] initWithMandatoryConstraints:@{
|
|
kRTCMediaConstraintsOfferToReceiveAudio : kRTCMediaConstraintsValueTrue
|
|
}
|
|
optionalConstraints:nil];
|
|
|
|
__block RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
__block RTC_OBJC_TYPE(RTCPeerConnection) * pc1;
|
|
RTC_OBJC_TYPE(RTCSessionDescription) *rollback =
|
|
[[RTC_OBJC_TYPE(RTCSessionDescription) alloc]
|
|
initWithType:RTCSdpTypeRollback
|
|
sdp:@""];
|
|
|
|
@autoreleasepool {
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
pc1 = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
dispatch_semaphore_t negotiatedSem = dispatch_semaphore_create(0);
|
|
[pc1 offerForConstraints:constraints
|
|
completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * offer,
|
|
NSError * error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertNotNil(offer);
|
|
|
|
__weak RTC_OBJC_TYPE(RTCPeerConnection) *weakPC1 = pc1;
|
|
[pc1 setLocalDescription:offer
|
|
completionHandler:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[weakPC1 setLocalDescription:rollback
|
|
completionHandler:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
}];
|
|
}];
|
|
NSTimeInterval negotiationTimeout = 15;
|
|
dispatch_semaphore_wait(
|
|
negotiatedSem,
|
|
dispatch_time(DISPATCH_TIME_NOW,
|
|
(int64_t)(negotiationTimeout * NSEC_PER_SEC)));
|
|
|
|
XCTAssertEqual(pc1.signalingState, RTCSignalingStateStable);
|
|
|
|
[pc1 close];
|
|
pc1 = nil;
|
|
factory = nil;
|
|
}];
|
|
}
|
|
|
|
XCTAssertTrue(true, "Expect test does not crash");
|
|
}
|
|
}
|
|
|
|
- (void)testSenderCapabilities {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
MockVideoEncoderDecoderFactory *encoder;
|
|
MockVideoEncoderDecoderFactory *decoder;
|
|
|
|
NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *supportedCodecs = @[
|
|
[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:@"VP8"],
|
|
[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:@"H264"]
|
|
];
|
|
|
|
encoder = [[MockVideoEncoderDecoderFactory alloc]
|
|
initWithSupportedCodecs:supportedCodecs];
|
|
decoder = [[MockVideoEncoderDecoderFactory alloc]
|
|
initWithSupportedCodecs:supportedCodecs];
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc]
|
|
initWithEncoderFactory:encoder
|
|
decoderFactory:decoder];
|
|
|
|
RTC_OBJC_TYPE(RTCRtpCapabilities) *capabilities =
|
|
[factory rtpSenderCapabilitiesForKind:kRTCMediaStreamTrackKindVideo];
|
|
NSMutableArray<NSString *> *codecNames = [NSMutableArray new];
|
|
for (RTC_OBJC_TYPE(RTCRtpCodecCapability) * codec in capabilities.codecs) {
|
|
[codecNames addObject:codec.name];
|
|
}
|
|
|
|
XCTAssertTrue([codecNames containsObject:@"VP8"]);
|
|
XCTAssertTrue([codecNames containsObject:@"H264"]);
|
|
factory = nil;
|
|
}
|
|
}
|
|
|
|
- (void)testReceiverCapabilities {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
MockVideoEncoderDecoderFactory *encoder;
|
|
MockVideoEncoderDecoderFactory *decoder;
|
|
|
|
NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *supportedCodecs = @[
|
|
[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:@"VP8"],
|
|
[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:@"H264"]
|
|
];
|
|
|
|
encoder = [[MockVideoEncoderDecoderFactory alloc]
|
|
initWithSupportedCodecs:supportedCodecs];
|
|
decoder = [[MockVideoEncoderDecoderFactory alloc]
|
|
initWithSupportedCodecs:supportedCodecs];
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc]
|
|
initWithEncoderFactory:encoder
|
|
decoderFactory:decoder];
|
|
|
|
RTC_OBJC_TYPE(RTCRtpCapabilities) *capabilities =
|
|
[factory rtpReceiverCapabilitiesForKind:kRTCMediaStreamTrackKindVideo];
|
|
NSMutableArray<NSString *> *codecNames = [NSMutableArray new];
|
|
for (RTC_OBJC_TYPE(RTCRtpCodecCapability) * codec in capabilities.codecs) {
|
|
[codecNames addObject:codec.name];
|
|
}
|
|
|
|
XCTAssertTrue([codecNames containsObject:@"VP8"]);
|
|
XCTAssertTrue([codecNames containsObject:@"H264"]);
|
|
factory = nil;
|
|
}
|
|
}
|
|
|
|
- (void)testSetCodecPreferences {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
|
|
initWithMandatoryConstraints:nil
|
|
optionalConstraints:nil];
|
|
RTC_OBJC_TYPE(RTCRtpTransceiverInit) *init =
|
|
[[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init];
|
|
|
|
NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *supportedCodecs = @[
|
|
[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:@"VP8"],
|
|
[[RTC_OBJC_TYPE(RTCVideoCodecInfo) alloc] initWithName:@"H264"]
|
|
];
|
|
|
|
MockVideoEncoderDecoderFactory *encoder =
|
|
[[MockVideoEncoderDecoderFactory alloc]
|
|
initWithSupportedCodecs:supportedCodecs];
|
|
MockVideoEncoderDecoderFactory *decoder =
|
|
[[MockVideoEncoderDecoderFactory alloc]
|
|
initWithSupportedCodecs:supportedCodecs];
|
|
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection;
|
|
RTC_OBJC_TYPE(RTCRtpTransceiver) * tranceiver;
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc]
|
|
initWithEncoderFactory:encoder
|
|
decoderFactory:decoder];
|
|
|
|
peerConnection = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
tranceiver = [peerConnection addTransceiverOfType:RTCRtpMediaTypeVideo
|
|
init:init];
|
|
XCTAssertNotNil(tranceiver);
|
|
|
|
RTC_OBJC_TYPE(RTCRtpCapabilities) *capabilities =
|
|
[factory rtpReceiverCapabilitiesForKind:kRTCMediaStreamTrackKindVideo];
|
|
|
|
RTC_OBJC_TYPE(RTCRtpCodecCapability) * targetCodec;
|
|
for (RTC_OBJC_TYPE(RTCRtpCodecCapability) * codec in capabilities.codecs) {
|
|
if ([codec.name isEqual:@"VP8"]) {
|
|
targetCodec = codec;
|
|
break;
|
|
}
|
|
}
|
|
XCTAssertNotNil(targetCodec);
|
|
|
|
NSError *error = nil;
|
|
BOOL result = [tranceiver setCodecPreferences:@[ targetCodec ]
|
|
error:&error];
|
|
XCTAssertTrue(result);
|
|
XCTAssertNil(error);
|
|
|
|
@autoreleasepool {
|
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
|
|
__block BOOL completed = NO;
|
|
[peerConnection
|
|
offerForConstraints:constraints
|
|
completionHandler:^(
|
|
RTC_OBJC_TYPE(RTCSessionDescription) *_Nullable sdp,
|
|
NSError *_Nullable error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertNotNil(sdp);
|
|
|
|
NSArray<NSString *> *rtpMaps = [self rtpMapsFromSDP:sdp.sdp];
|
|
XCTAssertEqual(1, rtpMaps.count);
|
|
|
|
XCTAssertNotNil(targetCodec.preferredPayloadType);
|
|
XCTAssertNotNil(targetCodec.clockRate);
|
|
|
|
NSString *expected = [NSString
|
|
stringWithFormat:@"a=rtpmap:%i VP8/%i",
|
|
targetCodec.preferredPayloadType.intValue,
|
|
targetCodec.clockRate.intValue];
|
|
|
|
XCTAssertTrue([expected isEqualToString:rtpMaps[0]]);
|
|
|
|
completed = YES;
|
|
dispatch_semaphore_signal(semaphore);
|
|
}];
|
|
|
|
[peerConnection close];
|
|
peerConnection = nil;
|
|
factory = nil;
|
|
tranceiver = nil;
|
|
|
|
dispatch_semaphore_wait(
|
|
semaphore, dispatch_time(DISPATCH_TIME_NOW, 15.0 * NSEC_PER_SEC));
|
|
XCTAssertTrue(completed);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)testSetHeaderExtensionsToNegotiate {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
|
|
initWithMandatoryConstraints:nil
|
|
optionalConstraints:nil];
|
|
RTC_OBJC_TYPE(RTCRtpTransceiverInit) *init =
|
|
[[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init];
|
|
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection;
|
|
RTC_OBJC_TYPE(RTCRtpTransceiver) * tranceiver;
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
|
|
peerConnection = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
tranceiver = [peerConnection addTransceiverOfType:RTCRtpMediaTypeVideo
|
|
init:init];
|
|
XCTAssertNotNil(tranceiver);
|
|
|
|
NSArray<RTC_OBJC_TYPE(RTCRtpHeaderExtensionCapability) *>
|
|
*headerExtensionsToNegotiate = tranceiver.headerExtensionsToNegotiate;
|
|
|
|
__block RTC_OBJC_TYPE(RTCRtpHeaderExtensionCapability) *targetExtension =
|
|
nil;
|
|
[headerExtensionsToNegotiate
|
|
enumerateObjectsUsingBlock:^(
|
|
RTC_OBJC_TYPE(RTCRtpHeaderExtensionCapability) * extension,
|
|
NSUInteger idx,
|
|
BOOL * stop) {
|
|
if ([extension.uri
|
|
isEqualToString:@"urn:ietf:params:rtp-hdrext:sdes:mid"]) {
|
|
targetExtension = extension;
|
|
} else {
|
|
extension.direction = RTCRtpTransceiverDirectionStopped;
|
|
}
|
|
}];
|
|
|
|
NSError *error = nil;
|
|
BOOL isOK =
|
|
[tranceiver setHeaderExtensionsToNegotiate:headerExtensionsToNegotiate
|
|
error:&error];
|
|
XCTAssertNil(error);
|
|
XCTAssertTrue(isOK);
|
|
|
|
@autoreleasepool {
|
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
|
|
__block BOOL completed = NO;
|
|
[peerConnection
|
|
offerForConstraints:constraints
|
|
completionHandler:^(
|
|
RTC_OBJC_TYPE(RTCSessionDescription) *_Nullable sdp,
|
|
NSError *_Nullable error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertNotNil(sdp);
|
|
|
|
NSArray<NSString *> *extMaps = [self extMapsFromSDP:sdp.sdp];
|
|
XCTAssertEqual(1, extMaps.count);
|
|
|
|
XCTAssertNotNil(targetExtension);
|
|
XCTAssertNotNil(targetExtension.preferredId);
|
|
|
|
NSString *expected = [NSString
|
|
stringWithFormat:@"a=extmap:%i %@",
|
|
targetExtension.preferredId.intValue,
|
|
targetExtension.uri];
|
|
|
|
XCTAssertTrue([expected isEqualToString:extMaps[0]]);
|
|
|
|
completed = YES;
|
|
dispatch_semaphore_signal(semaphore);
|
|
}];
|
|
|
|
[peerConnection close];
|
|
peerConnection = nil;
|
|
factory = nil;
|
|
tranceiver = nil;
|
|
|
|
dispatch_semaphore_wait(
|
|
semaphore, dispatch_time(DISPATCH_TIME_NOW, 15.0 * NSEC_PER_SEC));
|
|
XCTAssertTrue(completed);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)testSetHeaderExtensionsToNegotiateError {
|
|
@autoreleasepool {
|
|
RTC_OBJC_TYPE(RTCConfiguration) *config =
|
|
[[RTC_OBJC_TYPE(RTCConfiguration) alloc] init];
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *constraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints) alloc]
|
|
initWithMandatoryConstraints:nil
|
|
optionalConstraints:nil];
|
|
RTC_OBJC_TYPE(RTCRtpTransceiverInit) *init =
|
|
[[RTC_OBJC_TYPE(RTCRtpTransceiverInit) alloc] init];
|
|
|
|
RTC_OBJC_TYPE(RTCPeerConnectionFactory) * factory;
|
|
RTC_OBJC_TYPE(RTCPeerConnection) * peerConnection;
|
|
RTC_OBJC_TYPE(RTCRtpTransceiver) * tranceiver;
|
|
factory = [[RTC_OBJC_TYPE(RTCPeerConnectionFactory) alloc] init];
|
|
|
|
peerConnection = [factory peerConnectionWithConfiguration:config
|
|
constraints:constraints
|
|
delegate:nil];
|
|
tranceiver = [peerConnection addTransceiverOfType:RTCRtpMediaTypeVideo
|
|
init:init];
|
|
XCTAssertNotNil(tranceiver);
|
|
|
|
NSArray<RTC_OBJC_TYPE(RTCRtpHeaderExtensionCapability) *>
|
|
*headerExtensionsToNegotiate = tranceiver.headerExtensionsToNegotiate;
|
|
|
|
[headerExtensionsToNegotiate
|
|
enumerateObjectsUsingBlock:^(
|
|
RTC_OBJC_TYPE(RTCRtpHeaderExtensionCapability) * extension,
|
|
NSUInteger idx,
|
|
BOOL * stop) {
|
|
if ([extension.uri
|
|
isEqualToString:@"urn:ietf:params:rtp-hdrext:sdes:mid"]) {
|
|
extension.direction = RTCRtpTransceiverDirectionStopped;
|
|
}
|
|
}];
|
|
|
|
// Stopping a mandatory extension should yield an error
|
|
NSError *error = nil;
|
|
BOOL isOK =
|
|
[tranceiver setHeaderExtensionsToNegotiate:headerExtensionsToNegotiate
|
|
error:&error];
|
|
XCTAssertNotNil(error);
|
|
XCTAssertFalse(isOK);
|
|
|
|
[peerConnection close];
|
|
peerConnection = nil;
|
|
factory = nil;
|
|
tranceiver = nil;
|
|
}
|
|
}
|
|
|
|
- (bool)negotiatePeerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)pc1
|
|
withPeerConnection:(RTC_OBJC_TYPE(RTCPeerConnection) *)pc2
|
|
negotiationTimeout:(NSTimeInterval)timeout {
|
|
__weak RTC_OBJC_TYPE(RTCPeerConnection) *weakPC1 = pc1;
|
|
__weak RTC_OBJC_TYPE(RTCPeerConnection) *weakPC2 = pc2;
|
|
RTC_OBJC_TYPE(RTCMediaConstraints) *sdpConstraints =
|
|
[[RTC_OBJC_TYPE(RTCMediaConstraints)
|
|
alloc] initWithMandatoryConstraints:@{
|
|
kRTCMediaConstraintsOfferToReceiveAudio : kRTCMediaConstraintsValueTrue
|
|
}
|
|
optionalConstraints:nil];
|
|
|
|
dispatch_semaphore_t negotiatedSem = dispatch_semaphore_create(0);
|
|
[weakPC1
|
|
offerForConstraints:sdpConstraints
|
|
completionHandler:^(RTC_OBJC_TYPE(RTCSessionDescription) * offer,
|
|
NSError * error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertNotNil(offer);
|
|
[weakPC1
|
|
setLocalDescription:offer
|
|
completionHandler:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[weakPC2
|
|
setRemoteDescription:offer
|
|
completionHandler:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[weakPC2
|
|
answerForConstraints:sdpConstraints
|
|
completionHandler:^(
|
|
RTC_OBJC_TYPE(RTCSessionDescription) *
|
|
answer,
|
|
NSError * error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertNotNil(answer);
|
|
[weakPC2
|
|
setLocalDescription:answer
|
|
completionHandler:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[weakPC1
|
|
setRemoteDescription:answer
|
|
completionHandler:^(
|
|
NSError *error) {
|
|
XCTAssertNil(error);
|
|
dispatch_semaphore_signal(
|
|
negotiatedSem);
|
|
}];
|
|
}];
|
|
}];
|
|
}];
|
|
}];
|
|
}];
|
|
|
|
return 0 ==
|
|
dispatch_semaphore_wait(negotiatedSem,
|
|
dispatch_time(DISPATCH_TIME_NOW,
|
|
(int64_t)(timeout * NSEC_PER_SEC)));
|
|
}
|
|
|
|
- (NSArray<NSString *> *)rtpMapsFromSDP:(NSString *)sdp {
|
|
NSMutableArray<NSString *> *rtpMaps = [NSMutableArray new];
|
|
NSArray *sdpLines =
|
|
[sdp componentsSeparatedByCharactersInSet:[NSCharacterSet
|
|
newlineCharacterSet]];
|
|
for (NSString *line in sdpLines) {
|
|
if ([line hasPrefix:@"a=rtpmap"]) {
|
|
[rtpMaps addObject:line];
|
|
}
|
|
}
|
|
return rtpMaps;
|
|
}
|
|
|
|
- (NSArray<NSString *> *)extMapsFromSDP:(NSString *)sdp {
|
|
NSMutableArray<NSString *> *extMaps = [NSMutableArray new];
|
|
NSArray *sdpLines =
|
|
[sdp componentsSeparatedByCharactersInSet:[NSCharacterSet
|
|
newlineCharacterSet]];
|
|
for (NSString *line in sdpLines) {
|
|
if ([line hasPrefix:@"a=extmap:"]) {
|
|
[extMaps addObject:line];
|
|
}
|
|
}
|
|
return extMaps;
|
|
}
|
|
|
|
@end
|