webrtc_m130/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm
perkj d6c395441b Refactor VideoTracks to forward all sinks to its source
This remove the use of VideoTrackRenderers within VideoTrack and instead all its sinks are passed to VideoSource.
That means that the source will handle all sinks and can (if the source implement it) handle different SinkWants for each sink.
The VideoBroadcaster is updated to produce black frames instead of as is today the deprecated VideoTrackRenderers.

BUG=webrtc:5426
R=nisse@webrtc.org, pthatcher@webrtc.org

Review URL: https://codereview.webrtc.org/1779063003 .

Cr-Commit-Position: refs/heads/master@{#12028}
2016-03-17 09:35:53 +00:00

351 lines
16 KiB
Plaintext

/*
* libjingle
* Copyright 2013 Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import <Foundation/Foundation.h>
#import "RTCICEServer.h"
#import "RTCMediaConstraints.h"
#import "RTCMediaStream.h"
#import "RTCPair.h"
#import "RTCPeerConnection.h"
#import "RTCPeerConnectionFactory.h"
#import "RTCPeerConnectionSyncObserver.h"
#import "RTCSessionDescription.h"
#import "RTCSessionDescriptionSyncObserver.h"
#import "RTCVideoRenderer.h"
#import "RTCVideoTrack.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/ssladapter.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
const NSTimeInterval kRTCPeerConnectionTestTimeout = 20;
@interface RTCFakeRenderer : NSObject <RTCVideoRenderer>
@end
@implementation RTCFakeRenderer
- (void)setSize:(CGSize)size {}
- (void)renderFrame:(RTCI420Frame*)frame {}
@end
@interface RTCPeerConnectionTest : NSObject
// Returns whether the two sessions are of the same type.
+ (BOOL)isSession:(RTCSessionDescription*)session1
ofSameTypeAsSession:(RTCSessionDescription*)session2;
// Create and add tracks to pc, with the given source, label, and IDs
- (RTCMediaStream*)addTracksToPeerConnection:(RTCPeerConnection*)pc
withFactory:(RTCPeerConnectionFactory*)factory
videoSource:(RTCVideoSource*)videoSource
streamLabel:(NSString*)streamLabel
videoTrackID:(NSString*)videoTrackID
audioTrackID:(NSString*)audioTrackID;
- (void)testCompleteSessionWithFactory:(RTCPeerConnectionFactory*)factory;
@end
@implementation RTCPeerConnectionTest
+ (BOOL)isSession:(RTCSessionDescription*)session1
ofSameTypeAsSession:(RTCSessionDescription*)session2 {
return [session1.type isEqual:session2.type];
}
- (RTCMediaStream*)addTracksToPeerConnection:(RTCPeerConnection*)pc
withFactory:(RTCPeerConnectionFactory*)factory
videoSource:(RTCVideoSource*)videoSource
streamLabel:(NSString*)streamLabel
videoTrackID:(NSString*)videoTrackID
audioTrackID:(NSString*)audioTrackID {
RTCMediaStream* localMediaStream = [factory mediaStreamWithLabel:streamLabel];
// TODO(zeke): Fix this test to create a fake video capturer so that a track
// can be created.
if (videoSource) {
RTCVideoTrack* videoTrack =
[factory videoTrackWithID:videoTrackID source:videoSource];
RTCFakeRenderer* videoRenderer = [[RTCFakeRenderer alloc] init];
[videoTrack addRenderer:videoRenderer];
[localMediaStream addVideoTrack:videoTrack];
// Test that removal/re-add works.
[localMediaStream removeVideoTrack:videoTrack];
[localMediaStream addVideoTrack:videoTrack];
}
RTCAudioTrack* audioTrack = [factory audioTrackWithID:audioTrackID];
[localMediaStream addAudioTrack:audioTrack];
[pc addStream:localMediaStream];
return localMediaStream;
}
- (void)testCompleteSessionWithFactory:(RTCPeerConnectionFactory*)factory {
NSArray* mandatory = @[
[[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement" value:@"true"],
[[RTCPair alloc] initWithKey:@"internalSctpDataChannels" value:@"true"],
];
RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc] init];
RTCMediaConstraints* pcConstraints =
[[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory
optionalConstraints:nil];
RTCPeerConnectionSyncObserver* offeringExpectations =
[[RTCPeerConnectionSyncObserver alloc] init];
RTCPeerConnection* pcOffer =
[factory peerConnectionWithICEServers:nil
constraints:pcConstraints
delegate:offeringExpectations];
RTCPeerConnectionSyncObserver* answeringExpectations =
[[RTCPeerConnectionSyncObserver alloc] init];
RTCPeerConnection* pcAnswer =
[factory peerConnectionWithICEServers:nil
constraints:pcConstraints
delegate:answeringExpectations];
// TODO(hughv): Create video capturer
RTCVideoCapturer* capturer = nil;
RTCVideoSource* videoSource =
[factory videoSourceWithCapturer:capturer constraints:constraints];
// Here and below, "oLMS" refers to offerer's local media stream, and "aLMS"
// refers to the answerer's local media stream, with suffixes of "a0" and "v0"
// for audio and video tracks, resp. These mirror chrome historical naming.
RTCMediaStream* oLMSUnused = [self addTracksToPeerConnection:pcOffer
withFactory:factory
videoSource:videoSource
streamLabel:@"oLMS"
videoTrackID:@"oLMSv0"
audioTrackID:@"oLMSa0"];
RTCDataChannel* offerDC =
[pcOffer createDataChannelWithLabel:@"offerDC"
config:[[RTCDataChannelInit alloc] init]];
EXPECT_TRUE([offerDC.label isEqual:@"offerDC"]);
offerDC.delegate = offeringExpectations;
offeringExpectations.dataChannel = offerDC;
RTCSessionDescriptionSyncObserver* sdpObserver =
[[RTCSessionDescriptionSyncObserver alloc] init];
[pcOffer createOfferWithDelegate:sdpObserver constraints:constraints];
[sdpObserver wait];
EXPECT_TRUE(sdpObserver.success);
RTCSessionDescription* offerSDP = sdpObserver.sessionDescription;
EXPECT_EQ([@"offer" compare:offerSDP.type options:NSCaseInsensitiveSearch],
NSOrderedSame);
EXPECT_GT([offerSDP.description length], 0);
sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init];
[answeringExpectations expectSignalingChange:RTCSignalingHaveRemoteOffer];
[answeringExpectations expectAddStream:@"oLMS"];
[pcAnswer setRemoteDescriptionWithDelegate:sdpObserver
sessionDescription:offerSDP];
[sdpObserver wait];
RTCMediaStream* aLMSUnused = [self addTracksToPeerConnection:pcAnswer
withFactory:factory
videoSource:videoSource
streamLabel:@"aLMS"
videoTrackID:@"aLMSv0"
audioTrackID:@"aLMSa0"];
sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init];
[pcAnswer createAnswerWithDelegate:sdpObserver constraints:constraints];
[sdpObserver wait];
EXPECT_TRUE(sdpObserver.success);
RTCSessionDescription* answerSDP = sdpObserver.sessionDescription;
EXPECT_EQ([@"answer" compare:answerSDP.type options:NSCaseInsensitiveSearch],
NSOrderedSame);
EXPECT_GT([answerSDP.description length], 0);
[offeringExpectations expectICECandidates:2];
// It's possible to only have 1 ICE candidate for the answerer, since we use
// BUNDLE and rtcp-mux by default, and don't provide any ICE servers in this
// test.
[answeringExpectations expectICECandidates:1];
sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init];
[answeringExpectations expectSignalingChange:RTCSignalingStable];
[pcAnswer setLocalDescriptionWithDelegate:sdpObserver
sessionDescription:answerSDP];
[sdpObserver wait];
EXPECT_TRUE(sdpObserver.sessionDescription == NULL);
sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init];
[offeringExpectations expectSignalingChange:RTCSignalingHaveLocalOffer];
[pcOffer setLocalDescriptionWithDelegate:sdpObserver
sessionDescription:offerSDP];
[sdpObserver wait];
EXPECT_TRUE(sdpObserver.sessionDescription == NULL);
[offeringExpectations expectICEConnectionChange:RTCICEConnectionChecking];
[offeringExpectations expectICEConnectionChange:RTCICEConnectionConnected];
// TODO(fischman): figure out why this is flaky and re-introduce (and remove
// special-casing from the observer!).
// [offeringExpectations expectICEConnectionChange:RTCICEConnectionCompleted];
[answeringExpectations expectICEConnectionChange:RTCICEConnectionChecking];
[answeringExpectations expectICEConnectionChange:RTCICEConnectionConnected];
[offeringExpectations expectStateChange:kRTCDataChannelStateOpen];
[answeringExpectations expectDataChannel:@"offerDC"];
[answeringExpectations expectStateChange:kRTCDataChannelStateOpen];
[offeringExpectations expectICEGatheringChange:RTCICEGatheringComplete];
[answeringExpectations expectICEGatheringChange:RTCICEGatheringComplete];
sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init];
[offeringExpectations expectSignalingChange:RTCSignalingStable];
[offeringExpectations expectAddStream:@"aLMS"];
[pcOffer setRemoteDescriptionWithDelegate:sdpObserver
sessionDescription:answerSDP];
[sdpObserver wait];
EXPECT_TRUE(sdpObserver.sessionDescription == NULL);
EXPECT_TRUE([offerSDP.type isEqual:pcOffer.localDescription.type]);
EXPECT_TRUE([answerSDP.type isEqual:pcOffer.remoteDescription.type]);
EXPECT_TRUE([offerSDP.type isEqual:pcAnswer.remoteDescription.type]);
EXPECT_TRUE([answerSDP.type isEqual:pcAnswer.localDescription.type]);
for (RTCICECandidate* candidate in offeringExpectations
.releaseReceivedICECandidates) {
[pcAnswer addICECandidate:candidate];
}
for (RTCICECandidate* candidate in answeringExpectations
.releaseReceivedICECandidates) {
[pcOffer addICECandidate:candidate];
}
EXPECT_TRUE(
[offeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout:
kRTCPeerConnectionTestTimeout]);
EXPECT_TRUE(
[answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout:
kRTCPeerConnectionTestTimeout]);
EXPECT_EQ(pcOffer.signalingState, RTCSignalingStable);
EXPECT_EQ(pcAnswer.signalingState, RTCSignalingStable);
// Test send and receive UTF-8 text
NSString* text = @"你好";
NSData* textData = [text dataUsingEncoding:NSUTF8StringEncoding];
RTCDataBuffer* buffer =
[[RTCDataBuffer alloc] initWithData:textData isBinary:NO];
[answeringExpectations expectMessage:[textData copy] isBinary:NO];
EXPECT_TRUE([offeringExpectations.dataChannel sendData:buffer]);
EXPECT_TRUE(
[answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout:
kRTCPeerConnectionTestTimeout]);
// Test send and receive binary data
const size_t byteLength = 5;
char bytes[byteLength] = {1, 2, 3, 4, 5};
NSData* byteData = [NSData dataWithBytes:bytes length:byteLength];
buffer = [[RTCDataBuffer alloc] initWithData:byteData isBinary:YES];
[answeringExpectations expectMessage:[byteData copy] isBinary:YES];
EXPECT_TRUE([offeringExpectations.dataChannel sendData:buffer]);
EXPECT_TRUE(
[answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout:
kRTCPeerConnectionTestTimeout]);
[offeringExpectations expectStateChange:kRTCDataChannelStateClosing];
[answeringExpectations expectStateChange:kRTCDataChannelStateClosing];
[offeringExpectations expectStateChange:kRTCDataChannelStateClosed];
[answeringExpectations expectStateChange:kRTCDataChannelStateClosed];
[answeringExpectations.dataChannel close];
[offeringExpectations.dataChannel close];
EXPECT_TRUE(
[offeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout:
kRTCPeerConnectionTestTimeout]);
EXPECT_TRUE(
[answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout:
kRTCPeerConnectionTestTimeout]);
// Don't need to listen to further state changes.
// TODO(tkchin): figure out why Closed->Closing without this.
offeringExpectations.dataChannel.delegate = nil;
answeringExpectations.dataChannel.delegate = nil;
// Let the audio feedback run for 2s to allow human testing and to ensure
// things stabilize. TODO(fischman): replace seconds with # of video frames,
// when we have video flowing.
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
[offeringExpectations expectICEConnectionChange:RTCICEConnectionClosed];
[answeringExpectations expectICEConnectionChange:RTCICEConnectionClosed];
[offeringExpectations expectSignalingChange:RTCSignalingClosed];
[answeringExpectations expectSignalingChange:RTCSignalingClosed];
[pcOffer close];
[pcAnswer close];
EXPECT_TRUE(
[offeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout:
kRTCPeerConnectionTestTimeout]);
EXPECT_TRUE(
[answeringExpectations waitForAllExpectationsToBeSatisfiedWithTimeout:
kRTCPeerConnectionTestTimeout]);
capturer = nil;
videoSource = nil;
pcOffer = nil;
pcAnswer = nil;
// TODO(fischman): be stricter about shutdown checks; ensure thread
// counts return to where they were before the test kicked off, and
// that all objects have in fact shut down.
}
@end
// TODO(fischman): move {Initialize,Cleanup}SSL into alloc/dealloc of
// RTCPeerConnectionTest and avoid the appearance of RTCPeerConnectionTest being
// a TestBase since it's not.
TEST(RTCPeerConnectionTest, SessionTest) {
@autoreleasepool {
rtc::InitializeSSL();
// Since |factory| will own the signaling & worker threads, it's important
// that it outlive the created PeerConnections since they self-delete on the
// signaling thread, and if |factory| is freed first then a last refcount on
// the factory will expire during this teardown, causing the signaling
// thread to try to Join() with itself. This is a hack to ensure that the
// factory outlives RTCPeerConnection:dealloc.
// See https://code.google.com/p/webrtc/issues/detail?id=3100.
RTCPeerConnectionFactory* factory = [[RTCPeerConnectionFactory alloc] init];
@autoreleasepool {
RTCPeerConnectionTest* pcTest = [[RTCPeerConnectionTest alloc] init];
[pcTest testCompleteSessionWithFactory:factory];
}
rtc::CleanupSSL();
}
}