diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index e3d1079d83..1fd9168971 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -392,6 +392,66 @@ if (is_ios || (is_mac && target_cpu != "x86")) {
"{{bundle_resources_dir}}/{{source_file_part}}",
]
}
+
+ rtc_static_library("ObjCNativeAPIDemo_lib") {
+ testonly = true
+ sources = [
+ "objcnativeapi/objc/NADAppDelegate.h",
+ "objcnativeapi/objc/NADAppDelegate.m",
+ "objcnativeapi/objc/NADViewController.h",
+ "objcnativeapi/objc/NADViewController.mm",
+ "objcnativeapi/objc/objccallclient.h",
+ "objcnativeapi/objc/objccallclient.mm",
+ ]
+
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+
+ deps = [
+ "../api:libjingle_peerconnection_api",
+ "../api/audio_codecs:builtin_audio_decoder_factory",
+ "../api/audio_codecs:builtin_audio_encoder_factory",
+ "../logging:rtc_event_log_impl_base",
+ "../media:rtc_audio_video",
+ "../modules/audio_processing:audio_processing",
+ "../pc:libjingle_peerconnection",
+ "../rtc_base:rtc_base",
+ "../sdk:default_codec_factory_objc",
+ "../sdk:framework_objc",
+ "../sdk:native_api",
+ "../sdk:ui_objc",
+ "../sdk:videotoolbox_objc",
+ "../system_wrappers:field_trial_default",
+ "../system_wrappers:metrics_default",
+ "../system_wrappers:runtime_enabled_features_default",
+ ]
+
+ if (current_cpu == "arm64") {
+ deps += [ "../sdk:metal_objc" ]
+ }
+ }
+
+ ios_app_bundle("ObjCNativeAPIDemo") {
+ testonly = true
+ sources = [
+ "objcnativeapi/objc/main.m",
+ ]
+
+ info_plist = "objcnativeapi/Info.plist"
+
+ configs += [ "..:common_config" ]
+ public_configs = [ "..:common_inherited_config" ]
+
+ deps = [
+ ":ObjCNativeAPIDemo_lib",
+ ]
+
+ if (target_cpu == "x86") {
+ deps += [ "//testing/iossim:iossim" ]
+ }
+ }
}
if (is_mac) {
diff --git a/examples/DEPS b/examples/DEPS
index acd6d82957..1570220097 100644
--- a/examples/DEPS
+++ b/examples/DEPS
@@ -8,5 +8,6 @@ include_rules = [
"+modules/audio_processing",
"+p2p",
"+pc",
+ "+sdk/objc/Framework/Native/api",
"+third_party/libyuv",
]
diff --git a/examples/objcnativeapi/Info.plist b/examples/objcnativeapi/Info.plist
new file mode 100644
index 0000000000..cbc9e5f9f3
--- /dev/null
+++ b/examples/objcnativeapi/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ com.google.ObjCNativeAPIDemo
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ ObjCNativeAPIDemo
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ NSCameraUsageDescription
+ Camera access needed for video calling
+ NSMicrophoneUsageDescription
+ Microphone access needed for video calling
+
+
diff --git a/examples/objcnativeapi/objc/NADAppDelegate.h b/examples/objcnativeapi/objc/NADAppDelegate.h
new file mode 100644
index 0000000000..d421120842
--- /dev/null
+++ b/examples/objcnativeapi/objc/NADAppDelegate.h
@@ -0,0 +1,17 @@
+/*
+ * 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
+
+@interface NADAppDelegate : UIResponder
+
+@property(strong, nonatomic) UIWindow* window;
+
+@end
diff --git a/examples/objcnativeapi/objc/NADAppDelegate.m b/examples/objcnativeapi/objc/NADAppDelegate.m
new file mode 100644
index 0000000000..254dd3be76
--- /dev/null
+++ b/examples/objcnativeapi/objc/NADAppDelegate.m
@@ -0,0 +1,63 @@
+/*
+ * 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 "NADAppDelegate.h"
+
+#import "NADViewController.h"
+
+@interface NADAppDelegate ()
+@end
+
+@implementation NADAppDelegate
+
+@synthesize window = _window;
+
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ [_window makeKeyAndVisible];
+
+ NADViewController *viewController = [[NADViewController alloc] init];
+ _window.rootViewController = viewController;
+
+ return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+ // Sent when the application is about to move from active to inactive state. This can occur for
+ // certain types of temporary interruptions (such as an incoming phone call or SMS message) or
+ // when the user quits the application and it begins the transition to the background state. Use
+ // this method to pause ongoing tasks, disable timers, and invalidate graphics rendering
+ // callbacks. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+ // Use this method to release shared resources, save user data, invalidate timers, and store
+ // enough application state information to restore your application to its current state in case
+ // it is terminated later. If your application supports background execution, this method is
+ // called instead of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+ // Called as part of the transition from the background to the active state; here you can undo
+ // many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+ // Restart any tasks that were paused (or not yet started) while the application was inactive. If
+ // the application was previously in the background, optionally refresh the user interface.
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+ // Called when the application is about to terminate. Save data if appropriate. See also
+ // applicationDidEnterBackground:.
+}
+
+@end
diff --git a/examples/objcnativeapi/objc/NADViewController.h b/examples/objcnativeapi/objc/NADViewController.h
new file mode 100644
index 0000000000..c43bebb52d
--- /dev/null
+++ b/examples/objcnativeapi/objc/NADViewController.h
@@ -0,0 +1,15 @@
+/*
+ * 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
+
+@interface NADViewController : UIViewController
+
+@end
diff --git a/examples/objcnativeapi/objc/NADViewController.mm b/examples/objcnativeapi/objc/NADViewController.mm
new file mode 100644
index 0000000000..0d5ac9a3b9
--- /dev/null
+++ b/examples/objcnativeapi/objc/NADViewController.mm
@@ -0,0 +1,157 @@
+/*
+ * 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 "NADViewController.h"
+
+#import
+#import
+#import
+#import
+#import
+#include
+
+#include "objccallclient.h"
+
+@interface NADViewController ()
+
+@property(nonatomic) RTCCameraVideoCapturer *capturer;
+@property(nonatomic) RTCCameraPreviewView *localVideoView;
+@property(nonatomic) __kindof UIView *remoteVideoView;
+@property(nonatomic) UIButton *callButton;
+@property(nonatomic) UIButton *hangUpButton;
+
+@end
+
+@implementation NADViewController {
+ std::unique_ptr _call_client;
+
+ UIView *_view;
+}
+
+@synthesize capturer = _capturer;
+@synthesize localVideoView = _localVideoView;
+@synthesize remoteVideoView = _remoteVideoView;
+@synthesize callButton = _callButton;
+@synthesize hangUpButton = _hangUpButton;
+
+#pragma mark - View controller lifecycle
+
+- (void)loadView {
+ _view = [[UIView alloc] initWithFrame:CGRectZero];
+
+#if defined(RTC_SUPPORTS_METAL)
+ _remoteVideoView = [[RTCMTLVideoView alloc] initWithFrame:CGRectZero];
+#else
+ _remoteVideoView = [[RTCEAGLVideoView alloc] initWithFrame:CGRectZero];
+#endif
+ _remoteVideoView.translatesAutoresizingMaskIntoConstraints = NO;
+ [_view addSubview:_remoteVideoView];
+
+ _localVideoView = [[RTCCameraPreviewView alloc] initWithFrame:CGRectZero];
+ _localVideoView.translatesAutoresizingMaskIntoConstraints = NO;
+ [_view addSubview:_localVideoView];
+
+ _callButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ _callButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [_callButton setTitle:@"Call" forState:UIControlStateNormal];
+ [_callButton addTarget:self action:@selector(call:) forControlEvents:UIControlEventTouchUpInside];
+ [_view addSubview:_callButton];
+
+ _hangUpButton = [UIButton buttonWithType:UIButtonTypeSystem];
+ _hangUpButton.translatesAutoresizingMaskIntoConstraints = NO;
+ [_hangUpButton setTitle:@"Hang up" forState:UIControlStateNormal];
+ [_hangUpButton addTarget:self
+ action:@selector(hangUp:)
+ forControlEvents:UIControlEventTouchUpInside];
+ [_view addSubview:_hangUpButton];
+
+ UILayoutGuide *margin = _view.layoutMarginsGuide;
+ [_remoteVideoView.leadingAnchor constraintEqualToAnchor:margin.leadingAnchor].active = YES;
+ [_remoteVideoView.topAnchor constraintEqualToAnchor:margin.topAnchor].active = YES;
+ [_remoteVideoView.trailingAnchor constraintEqualToAnchor:margin.trailingAnchor].active = YES;
+ [_remoteVideoView.bottomAnchor constraintEqualToAnchor:margin.bottomAnchor].active = YES;
+
+ [_localVideoView.leadingAnchor constraintEqualToAnchor:margin.leadingAnchor constant:8.0].active =
+ YES;
+ [_localVideoView.topAnchor constraintEqualToAnchor:margin.topAnchor constant:8.0].active = YES;
+ [_localVideoView.widthAnchor constraintEqualToConstant:60].active = YES;
+ [_localVideoView.heightAnchor constraintEqualToConstant:60].active = YES;
+
+ [_callButton.leadingAnchor constraintEqualToAnchor:margin.leadingAnchor constant:8.0].active =
+ YES;
+ [_callButton.bottomAnchor constraintEqualToAnchor:margin.bottomAnchor constant:8.0].active = YES;
+ [_callButton.widthAnchor constraintEqualToConstant:100].active = YES;
+ [_callButton.heightAnchor constraintEqualToConstant:40].active = YES;
+
+ [_hangUpButton.trailingAnchor constraintEqualToAnchor:margin.trailingAnchor constant:8.0].active =
+ YES;
+ [_hangUpButton.bottomAnchor constraintEqualToAnchor:margin.bottomAnchor constant:8.0].active =
+ YES;
+ [_hangUpButton.widthAnchor constraintEqualToConstant:100].active = YES;
+ [_hangUpButton.heightAnchor constraintEqualToConstant:40].active = YES;
+
+ self.view = _view;
+}
+
+- (void)viewDidLoad {
+ [super viewDidLoad];
+
+ self.capturer = [[RTCCameraVideoCapturer alloc] init];
+ self.localVideoView.captureSession = self.capturer.captureSession;
+
+ _call_client.reset(new webrtc_examples::ObjCCallClient());
+
+ // Start capturer.
+ AVCaptureDevice *selectedDevice = nil;
+ NSArray *captureDevices = [RTCCameraVideoCapturer captureDevices];
+ for (AVCaptureDevice *device in captureDevices) {
+ if (device.position == AVCaptureDevicePositionFront) {
+ selectedDevice = device;
+ break;
+ }
+ }
+
+ AVCaptureDeviceFormat *selectedFormat = nil;
+ int targetWidth = 640;
+ int targetHeight = 480;
+ int currentDiff = INT_MAX;
+ NSArray *formats =
+ [RTCCameraVideoCapturer supportedFormatsForDevice:selectedDevice];
+ for (AVCaptureDeviceFormat *format in formats) {
+ CMVideoDimensions dimension = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
+ FourCharCode pixelFormat = CMFormatDescriptionGetMediaSubType(format.formatDescription);
+ int diff = abs(targetWidth - dimension.width) + abs(targetHeight - dimension.height);
+ if (diff < currentDiff) {
+ selectedFormat = format;
+ currentDiff = diff;
+ } else if (diff == currentDiff && pixelFormat == [_capturer preferredOutputPixelFormat]) {
+ selectedFormat = format;
+ }
+ }
+
+ [self.capturer startCaptureWithDevice:selectedDevice format:selectedFormat fps:30];
+}
+
+- (void)didReceiveMemoryWarning {
+ [super didReceiveMemoryWarning];
+ // Dispose of any resources that can be recreated.
+}
+
+#pragma mark - Actions
+
+- (IBAction)call:(id)sender {
+ _call_client->Call(self.capturer, self.remoteVideoView);
+}
+
+- (IBAction)hangUp:(id)sender {
+ _call_client->Hangup();
+}
+
+@end
diff --git a/examples/objcnativeapi/objc/main.m b/examples/objcnativeapi/objc/main.m
new file mode 100644
index 0000000000..2c3b5fbbfb
--- /dev/null
+++ b/examples/objcnativeapi/objc/main.m
@@ -0,0 +1,18 @@
+/*
+ * 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
+#import "NADAppDelegate.h"
+
+int main(int argc, char* argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([NADAppDelegate class]));
+ }
+}
diff --git a/examples/objcnativeapi/objc/objccallclient.h b/examples/objcnativeapi/objc/objccallclient.h
new file mode 100644
index 0000000000..e48aae47e1
--- /dev/null
+++ b/examples/objcnativeapi/objc/objccallclient.h
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#ifndef EXAMPLES_OBJCNATIVEAPI_OBJCCALLCLIENT_H_
+#define EXAMPLES_OBJCNATIVEAPI_OBJCCALLCLIENT_H_
+
+#include
+#include
+
+#include "api/peerconnectioninterface.h"
+#include "rtc_base/criticalsection.h"
+#include "rtc_base/scoped_ref_ptr.h"
+#include "rtc_base/thread_checker.h"
+
+@class RTCVideoCapturer;
+@protocol RTCVideoRenderer;
+
+namespace webrtc_examples {
+
+class ObjCCallClient {
+ public:
+ ObjCCallClient();
+
+ void Call(RTCVideoCapturer* capturer, id remote_renderer);
+ void Hangup();
+
+ private:
+ class PCObserver : public webrtc::PeerConnectionObserver {
+ public:
+ explicit PCObserver(ObjCCallClient* client);
+
+ void OnSignalingChange(
+ webrtc::PeerConnectionInterface::SignalingState new_state) override;
+ void OnDataChannel(
+ rtc::scoped_refptr data_channel) override;
+ void OnRenegotiationNeeded() override;
+ void OnIceConnectionChange(
+ webrtc::PeerConnectionInterface::IceConnectionState new_state) override;
+ void OnIceGatheringChange(
+ webrtc::PeerConnectionInterface::IceGatheringState new_state) override;
+ void OnIceCandidate(
+ const webrtc::IceCandidateInterface* candidate) override;
+
+ private:
+ const ObjCCallClient* client_;
+ };
+
+ void CreatePeerConnectionFactory() RTC_RUN_ON(thread_checker_);
+ void CreatePeerConnection() RTC_RUN_ON(thread_checker_);
+ void Connect() RTC_RUN_ON(thread_checker_);
+
+ rtc::ThreadChecker thread_checker_;
+
+ bool call_started_ RTC_GUARDED_BY(thread_checker_);
+
+ const std::unique_ptr pc_observer_;
+
+ rtc::scoped_refptr pcf_
+ RTC_GUARDED_BY(thread_checker_);
+ std::unique_ptr network_thread_ RTC_GUARDED_BY(thread_checker_);
+ std::unique_ptr worker_thread_ RTC_GUARDED_BY(thread_checker_);
+ std::unique_ptr signaling_thread_
+ RTC_GUARDED_BY(thread_checker_);
+
+ std::unique_ptr> remote_sink_
+ RTC_GUARDED_BY(thread_checker_);
+ rtc::scoped_refptr video_source_
+ RTC_GUARDED_BY(thread_checker_);
+
+ rtc::CriticalSection pc_mutex_;
+ rtc::scoped_refptr pc_
+ RTC_GUARDED_BY(pc_mutex_);
+};
+
+} // namespace webrtc_examples
+
+#endif // EXAMPLES_OBJCNATIVEAPI_OBJCCALLCLIENT_H_
diff --git a/examples/objcnativeapi/objc/objccallclient.mm b/examples/objcnativeapi/objc/objccallclient.mm
new file mode 100644
index 0000000000..68c58e2621
--- /dev/null
+++ b/examples/objcnativeapi/objc/objccallclient.mm
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+
+#include "examples/objcnativeapi/objc/objccallclient.h"
+
+#include
+
+#import
+#import
+#import
+
+#include "api/audio_codecs/builtin_audio_decoder_factory.h"
+#include "api/audio_codecs/builtin_audio_encoder_factory.h"
+#include "api/peerconnectioninterface.h"
+#include "media/engine/webrtcmediaengine.h"
+#include "modules/audio_processing/include/audio_processing.h"
+#include "rtc_base/ptr_util.h"
+#include "sdk/objc/Framework/Native/api/video_capturer.h"
+#include "sdk/objc/Framework/Native/api/video_decoder_factory.h"
+#include "sdk/objc/Framework/Native/api/video_encoder_factory.h"
+#include "sdk/objc/Framework/Native/api/video_renderer.h"
+
+namespace webrtc_examples {
+
+namespace {
+
+class CreateOfferObserver : public webrtc::CreateSessionDescriptionObserver {
+ public:
+ explicit CreateOfferObserver(rtc::scoped_refptr pc);
+
+ void OnSuccess(webrtc::SessionDescriptionInterface* desc) override;
+ void OnFailure(const std::string& error) override;
+
+ private:
+ const rtc::scoped_refptr pc_;
+};
+
+class SetRemoteSessionDescriptionObserver : public webrtc::SetRemoteDescriptionObserverInterface {
+ public:
+ void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override;
+};
+
+class SetLocalSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver {
+ public:
+ void OnSuccess() override;
+ void OnFailure(const std::string& error) override;
+};
+
+} // namespace
+
+ObjCCallClient::ObjCCallClient()
+ : call_started_(false), pc_observer_(rtc::MakeUnique(this)) {
+ thread_checker_.DetachFromThread();
+ CreatePeerConnectionFactory();
+}
+
+void ObjCCallClient::Call(RTCVideoCapturer* capturer, id remote_renderer) {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+
+ rtc::CritScope lock(&pc_mutex_);
+ if (call_started_) {
+ RTC_LOG(LS_WARNING) << "Call already started.";
+ return;
+ }
+ call_started_ = true;
+
+ remote_sink_ = webrtc::ObjCToNativeVideoRenderer(remote_renderer);
+
+ video_source_ =
+ webrtc::ObjCToNativeVideoCapturer(capturer, signaling_thread_.get(), worker_thread_.get());
+
+ CreatePeerConnection();
+ Connect();
+}
+
+void ObjCCallClient::Hangup() {
+ RTC_DCHECK_RUN_ON(&thread_checker_);
+
+ call_started_ = false;
+
+ {
+ rtc::CritScope lock(&pc_mutex_);
+ if (pc_ != nullptr) {
+ pc_->Close();
+ pc_ = nullptr;
+ }
+ }
+
+ remote_sink_ = nullptr;
+ video_source_ = nullptr;
+}
+
+void ObjCCallClient::CreatePeerConnectionFactory() {
+ network_thread_ = rtc::Thread::CreateWithSocketServer();
+ network_thread_->SetName("network_thread", nullptr);
+ RTC_CHECK(network_thread_->Start()) << "Failed to start thread";
+
+ worker_thread_ = rtc::Thread::Create();
+ worker_thread_->SetName("worker_thread", nullptr);
+ RTC_CHECK(worker_thread_->Start()) << "Failed to start thread";
+
+ signaling_thread_ = rtc::Thread::Create();
+ signaling_thread_->SetName("signaling_thread", nullptr);
+ RTC_CHECK(signaling_thread_->Start()) << "Failed to start thread";
+
+ std::unique_ptr videoDecoderFactory =
+ webrtc::ObjCToNativeVideoDecoderFactory([[RTCDefaultVideoDecoderFactory alloc] init]);
+ std::unique_ptr videoEncoderFactory =
+ webrtc::ObjCToNativeVideoEncoderFactory([[RTCDefaultVideoEncoderFactory alloc] init]);
+
+ std::unique_ptr media_engine =
+ cricket::WebRtcMediaEngineFactory::Create(nullptr /* adm */,
+ webrtc::CreateBuiltinAudioEncoderFactory(),
+ webrtc::CreateBuiltinAudioDecoderFactory(),
+ std::move(videoEncoderFactory),
+ std::move(videoDecoderFactory),
+ nullptr /* audio_mixer */,
+ webrtc::AudioProcessingBuilder().Create());
+ RTC_LOG(LS_INFO) << "Media engine created: " << media_engine.get();
+
+ pcf_ = webrtc::CreateModularPeerConnectionFactory(network_thread_.get(),
+ worker_thread_.get(),
+ signaling_thread_.get(),
+ std::move(media_engine),
+ webrtc::CreateCallFactory(),
+ webrtc::CreateRtcEventLogFactory());
+ RTC_LOG(LS_INFO) << "PeerConnectionFactory created: " << pcf_;
+}
+
+void ObjCCallClient::CreatePeerConnection() {
+ rtc::CritScope lock(&pc_mutex_);
+ webrtc::PeerConnectionInterface::RTCConfiguration config;
+ config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
+ // DTLS SRTP has to be disabled for loopback to work.
+ config.enable_dtls_srtp = false;
+ pc_ = pcf_->CreatePeerConnection(
+ config, nullptr /* port_allocator */, nullptr /* cert_generator */, pc_observer_.get());
+ RTC_LOG(LS_INFO) << "PeerConnection created: " << pc_;
+
+ rtc::scoped_refptr local_video_track =
+ pcf_->CreateVideoTrack("video", video_source_);
+ pc_->AddTransceiver(local_video_track);
+ RTC_LOG(LS_INFO) << "Local video sink set up: " << local_video_track;
+
+ for (const rtc::scoped_refptr& tranceiver :
+ pc_->GetTransceivers()) {
+ rtc::scoped_refptr track = tranceiver->receiver()->track();
+ if (track && track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
+ static_cast(track.get())
+ ->AddOrUpdateSink(remote_sink_.get(), rtc::VideoSinkWants());
+ RTC_LOG(LS_INFO) << "Remote video sink set up: " << track;
+ break;
+ }
+ }
+}
+
+void ObjCCallClient::Connect() {
+ rtc::CritScope lock(&pc_mutex_);
+ pc_->CreateOffer(new rtc::RefCountedObject(pc_),
+ webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
+}
+
+ObjCCallClient::PCObserver::PCObserver(ObjCCallClient* client) : client_(client) {}
+
+void ObjCCallClient::PCObserver::OnSignalingChange(
+ webrtc::PeerConnectionInterface::SignalingState new_state) {
+ RTC_LOG(LS_INFO) << "OnSignalingChange: " << new_state;
+}
+
+void ObjCCallClient::PCObserver::OnDataChannel(
+ rtc::scoped_refptr data_channel) {
+ RTC_LOG(LS_INFO) << "OnDataChannel";
+}
+
+void ObjCCallClient::PCObserver::OnRenegotiationNeeded() {
+ RTC_LOG(LS_INFO) << "OnRenegotiationNeeded";
+}
+
+void ObjCCallClient::PCObserver::OnIceConnectionChange(
+ webrtc::PeerConnectionInterface::IceConnectionState new_state) {
+ RTC_LOG(LS_INFO) << "OnIceConnectionChange: " << new_state;
+}
+
+void ObjCCallClient::PCObserver::OnIceGatheringChange(
+ webrtc::PeerConnectionInterface::IceGatheringState new_state) {
+ RTC_LOG(LS_INFO) << "OnIceGatheringChange: " << new_state;
+}
+
+void ObjCCallClient::PCObserver::OnIceCandidate(const webrtc::IceCandidateInterface* candidate) {
+ RTC_LOG(LS_INFO) << "OnIceCandidate: " << candidate->server_url();
+ rtc::CritScope lock(&client_->pc_mutex_);
+ RTC_DCHECK(client_->pc_ != nullptr);
+ client_->pc_->AddIceCandidate(candidate);
+}
+
+CreateOfferObserver::CreateOfferObserver(rtc::scoped_refptr pc)
+ : pc_(pc) {}
+
+void CreateOfferObserver::OnSuccess(webrtc::SessionDescriptionInterface* desc) {
+ std::string sdp;
+ desc->ToString(&sdp);
+ RTC_LOG(LS_INFO) << "Created offer: " << sdp;
+
+ // Ownership of desc was transferred to us, now we transfer it forward.
+ pc_->SetLocalDescription(new rtc::RefCountedObject(), desc);
+
+ // Generate a fake answer.
+ std::unique_ptr answer(
+ webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp));
+ pc_->SetRemoteDescription(std::move(answer),
+ new rtc::RefCountedObject());
+}
+
+void CreateOfferObserver::OnFailure(const std::string& error) {
+ RTC_LOG(LS_INFO) << "Failed to create offer: " << error;
+}
+
+void SetRemoteSessionDescriptionObserver::OnSetRemoteDescriptionComplete(webrtc::RTCError error) {
+ RTC_LOG(LS_INFO) << "Set remote description: " << error.message();
+}
+
+void SetLocalSessionDescriptionObserver::OnSuccess() {
+ RTC_LOG(LS_INFO) << "Set local description success!";
+}
+
+void SetLocalSessionDescriptionObserver::OnFailure(const std::string& error) {
+ RTC_LOG(LS_INFO) << "Set local description failure: " << error;
+}
+
+} // namespace webrtc_examples
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index 1ef7de2744..e052edf1cd 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -296,13 +296,29 @@ if (is_ios || is_mac) {
}
}
+ rtc_static_library("videocapturebase_objc") {
+ visibility = [ "*" ]
+ sources = [
+ "objc/Framework/Classes/PeerConnection/RTCVideoCapturer.m",
+ "objc/Framework/Headers/WebRTC/RTCVideoCapturer.h",
+ ]
+ libs = [ "AVFoundation.framework" ]
+
+ configs += [ "..:common_objc" ]
+
+ public_configs = [ ":common_config_objc" ]
+
+ deps = [
+ ":common_objc",
+ ":videoframebuffer_objc",
+ ]
+ }
+
rtc_static_library("videocapture_objc") {
visibility = [ "*" ]
sources = [
"objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m",
- "objc/Framework/Classes/PeerConnection/RTCVideoCapturer.m",
"objc/Framework/Headers/WebRTC/RTCCameraVideoCapturer.h",
- "objc/Framework/Headers/WebRTC/RTCVideoCapturer.h",
]
if (is_ios) {
sources += [
@@ -325,6 +341,7 @@ if (is_ios || is_mac) {
deps = [
":common_objc",
":video_objc",
+ ":videocapturebase_objc",
":videoframebuffer_objc",
]
}
@@ -922,6 +939,8 @@ if (is_ios || is_mac) {
rtc_static_library("native_api") {
visibility = [ "*" ]
sources = [
+ "objc/Framework/Native/api/video_capturer.h",
+ "objc/Framework/Native/api/video_capturer.mm",
"objc/Framework/Native/api/video_decoder_factory.h",
"objc/Framework/Native/api/video_decoder_factory.mm",
"objc/Framework/Native/api/video_encoder_factory.h",
@@ -946,9 +965,11 @@ if (is_ios || is_mac) {
deps = [
":native_video",
+ ":videocapturebase_objc",
":videocodec_objc",
":videoframebuffer_objc",
":videorenderer_objc",
+ "../api:libjingle_peerconnection_api",
"../api:video_frame_api",
"../api/video_codecs:video_codecs_api",
"../common_video",
@@ -984,6 +1005,7 @@ if (is_ios || is_mac) {
deps = [
":common_objc",
+ ":videocapturebase_objc",
":videocodec_objc",
":videoframebuffer_objc",
":videorenderer_objc",
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m b/sdk/objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m
index 3f82cb7c96..527bc8a536 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCCameraVideoCapturer.m
@@ -46,6 +46,10 @@ const int64_t kNanosecondsPerSecond = 1000000000;
@synthesize frameQueue = _frameQueue;
@synthesize captureSession = _captureSession;
+- (instancetype)init {
+ return [self initWithDelegate:nil captureSession:[[AVCaptureSession alloc] init]];
+}
+
- (instancetype)initWithDelegate:(__weak id)delegate {
return [self initWithDelegate:delegate captureSession:[[AVCaptureSession alloc] init]];
}
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCVideoCapturer.m b/sdk/objc/Framework/Classes/PeerConnection/RTCVideoCapturer.m
index 9d4d99dd7c..3639fa75ca 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCVideoCapturer.m
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCVideoCapturer.m
@@ -15,7 +15,6 @@
@synthesize delegate = _delegate;
- (instancetype)initWithDelegate:(id)delegate {
- NSAssert(delegate != nil, @"delegate cannot be nil");
if (self = [super init]) {
_delegate = delegate;
}
diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCVideoCapturer.h b/sdk/objc/Framework/Headers/WebRTC/RTCVideoCapturer.h
index 3addec83b1..f0a2eaa14c 100644
--- a/sdk/objc/Framework/Headers/WebRTC/RTCVideoCapturer.h
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCVideoCapturer.h
@@ -22,7 +22,7 @@ RTC_EXPORT
RTC_EXPORT
@interface RTCVideoCapturer : NSObject
-@property(nonatomic, readonly, weak) id delegate;
+@property(nonatomic, weak) id delegate;
- (instancetype)initWithDelegate:(id)delegate;
diff --git a/sdk/objc/Framework/Native/api/video_capturer.h b/sdk/objc/Framework/Native/api/video_capturer.h
new file mode 100644
index 0000000000..f601f1840d
--- /dev/null
+++ b/sdk/objc/Framework/Native/api/video_capturer.h
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+#ifndef SDK_OBJC_FRAMEWORK_NATIVE_API_VIDEO_CAPTURER_H_
+#define SDK_OBJC_FRAMEWORK_NATIVE_API_VIDEO_CAPTURER_H_
+
+#import "WebRTC/RTCVideoCapturer.h"
+
+#include "api/mediastreaminterface.h"
+#include "rtc_base/scoped_ref_ptr.h"
+
+namespace webrtc {
+
+rtc::scoped_refptr ObjCToNativeVideoCapturer(
+ RTCVideoCapturer* objc_video_capturer,
+ rtc::Thread* signaling_thread,
+ rtc::Thread* worker_thread);
+
+} // namespace webrtc
+
+#endif // SDK_OBJC_FRAMEWORK_NATIVE_API_VIDEO_CAPTURER_H_
diff --git a/sdk/objc/Framework/Native/api/video_capturer.mm b/sdk/objc/Framework/Native/api/video_capturer.mm
new file mode 100644
index 0000000000..2d67f2d10d
--- /dev/null
+++ b/sdk/objc/Framework/Native/api/video_capturer.mm
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#include "sdk/objc/Framework/Native/api/video_capturer.h"
+
+#include "api/videosourceproxy.h"
+#include "rtc_base/ptr_util.h"
+#include "sdk/objc/Framework/Native/src/objc_video_track_source.h"
+
+namespace webrtc {
+
+rtc::scoped_refptr ObjCToNativeVideoCapturer(
+ RTCVideoCapturer *objc_video_capturer,
+ rtc::Thread *signaling_thread,
+ rtc::Thread *worker_thread) {
+ RTCObjCVideoSourceAdapter *adapter = [[RTCObjCVideoSourceAdapter alloc] init];
+ rtc::scoped_refptr objc_video_track_source(
+ new rtc::RefCountedObject(adapter));
+ rtc::scoped_refptr video_source =
+ webrtc::VideoTrackSourceProxy::Create(
+ signaling_thread, worker_thread, objc_video_track_source);
+
+ objc_video_capturer.delegate = adapter;
+
+ return video_source;
+}
+
+} // namespace webrtc
diff --git a/sdk/objc/Framework/Native/src/objc_video_track_source.h b/sdk/objc/Framework/Native/src/objc_video_track_source.h
index 8b0daebf66..1062e967e7 100644
--- a/sdk/objc/Framework/Native/src/objc_video_track_source.h
+++ b/sdk/objc/Framework/Native/src/objc_video_track_source.h
@@ -11,17 +11,23 @@
#ifndef SDK_OBJC_FRAMEWORK_CLASSES_VIDEO_OBJCVIDEOTRACKSOURCE_H_
#define SDK_OBJC_FRAMEWORK_CLASSES_VIDEO_OBJCVIDEOTRACKSOURCE_H_
+#import "WebRTC/RTCVideoCapturer.h"
+
#include "WebRTC/RTCMacros.h"
#include "media/base/adaptedvideotracksource.h"
#include "rtc_base/timestampaligner.h"
RTC_FWD_DECL_OBJC_CLASS(RTCVideoFrame);
+@interface RTCObjCVideoSourceAdapter : NSObject
+@end
+
namespace webrtc {
class ObjCVideoTrackSource : public rtc::AdaptedVideoTrackSource {
public:
ObjCVideoTrackSource();
+ explicit ObjCVideoTrackSource(RTCObjCVideoSourceAdapter* adapter);
// This class can not be used for implementing screen casting. Hopefully, this
// function will be removed before we add that to iOS/Mac.
@@ -36,13 +42,16 @@ class ObjCVideoTrackSource : public rtc::AdaptedVideoTrackSource {
bool remote() const override { return false; }
- // Called by RTCVideoSource.
void OnCapturedFrame(RTCVideoFrame* frame);
+
+ // Called by RTCVideoSource.
void OnOutputFormatRequest(int width, int height, int fps);
private:
rtc::VideoBroadcaster broadcaster_;
rtc::TimestampAligner timestamp_aligner_;
+
+ RTCObjCVideoSourceAdapter* adapter_;
};
} // namespace webrtc
diff --git a/sdk/objc/Framework/Native/src/objc_video_track_source.mm b/sdk/objc/Framework/Native/src/objc_video_track_source.mm
index 72ed1bb140..d3942dfd39 100644
--- a/sdk/objc/Framework/Native/src/objc_video_track_source.mm
+++ b/sdk/objc/Framework/Native/src/objc_video_track_source.mm
@@ -16,10 +16,28 @@
#include "api/video/i420_buffer.h"
#include "sdk/objc/Framework/Native/src/objc_frame_buffer.h"
+@interface RTCObjCVideoSourceAdapter ()
+@property(nonatomic) webrtc::ObjCVideoTrackSource *objCVideoTrackSource;
+@end
+
+@implementation RTCObjCVideoSourceAdapter
+
+@synthesize objCVideoTrackSource = _objCVideoTrackSource;
+
+- (void)capturer:(RTCVideoCapturer *)capturer didCaptureVideoFrame:(RTCVideoFrame *)frame {
+ _objCVideoTrackSource->OnCapturedFrame(frame);
+}
+
+@end
+
namespace webrtc {
ObjCVideoTrackSource::ObjCVideoTrackSource() {}
+ObjCVideoTrackSource::ObjCVideoTrackSource(RTCObjCVideoSourceAdapter *adapter) : adapter_(adapter) {
+ adapter_.objCVideoTrackSource = this;
+}
+
void ObjCVideoTrackSource::OnOutputFormatRequest(int width, int height, int fps) {
cricket::VideoFormat format(width, height, cricket::VideoFormat::FpsToInterval(fps), 0);
video_adapter()->OnOutputFormatRequest(format);