Implement mac version of AppRTCDemo.
- Refactored and moved AppRTCDemo to support sharing AppRTC connection code between iOS and mac counterparts. - Refactored OpenGL rendering code to be shared between iOS and mac counterparts. - iOS AppRTCDemo now respects video aspect ratio. BUG=2168 R=fischman@webrtc.org Review URL: https://webrtc-codereview.appspot.com/17589004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6291 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
9f8164c060
commit
acca675bcf
@ -45,6 +45,10 @@ Example of building & using the unittest & app:
|
|||||||
ninja -C out_mac/Debug libjingle_peerconnection_objc_test && \
|
ninja -C out_mac/Debug libjingle_peerconnection_objc_test && \
|
||||||
./out_mac/Debug/libjingle_peerconnection_objc_test.app/Contents/MacOS/libjingle_peerconnection_objc_test
|
./out_mac/Debug/libjingle_peerconnection_objc_test.app/Contents/MacOS/libjingle_peerconnection_objc_test
|
||||||
|
|
||||||
|
- To build & launch the sample app on OSX:
|
||||||
|
wrmac && gclient runhooks && ninja -C out_mac/Debug AppRTCDemo && \
|
||||||
|
./out_mac/Debug/AppRTCDemo.app/Contents/MacOS/AppRTCDemo
|
||||||
|
|
||||||
- To build & launch the sample app on the iOS simulator:
|
- To build & launch the sample app on the iOS simulator:
|
||||||
wrsim && gclient runhooks && ninja -C out_sim/Debug iossim AppRTCDemo && \
|
wrsim && gclient runhooks && ninja -C out_sim/Debug iossim AppRTCDemo && \
|
||||||
./out_sim/Debug/iossim out_sim/Debug/AppRTCDemo.app
|
./out_sim/Debug/iossim out_sim/Debug/AppRTCDemo.app
|
||||||
@ -66,14 +70,11 @@ Example of building & using the unittest & app:
|
|||||||
the Info.plist file to ensure that the Bundle Identifier matches
|
the Info.plist file to ensure that the Bundle Identifier matches
|
||||||
your phone provisioning profile, or use a development wildcard
|
your phone provisioning profile, or use a development wildcard
|
||||||
provisioning profile.)
|
provisioning profile.)
|
||||||
|
- Alternately, use ios-deploy:
|
||||||
|
ios-deploy -d -b out_ios/Debug-iphoneos/AppRTCDemo.app
|
||||||
|
|
||||||
- Once installed:
|
- Once installed:
|
||||||
- Tap AppRTCDemo on the iOS device's home screen (might have to scroll to find it).
|
- Tap AppRTCDemo on the iOS device's home screen (might have to scroll to find it).
|
||||||
- In desktop chrome, navigate to http://apprtc.appspot.com and note
|
- In desktop chrome, navigate to http://apprtc.appspot.com and note
|
||||||
the r=<NNN> room number in the resulting URL; enter that number
|
the r=<NNN> room number in the resulting URL; enter that number
|
||||||
into the text field on the phone.
|
into the text field on the phone.
|
||||||
- Alternatively, background the app and launch Safari. In Safari,
|
|
||||||
open the url apprtc://apprtc.appspot.com/?r=<NNN> where <NNN> is
|
|
||||||
the room name. Other options are to put the link in an email/chat
|
|
||||||
and send it to yourself. Clicking on it will launch AppRTCDemo
|
|
||||||
and navigate to the room.
|
|
||||||
|
@ -32,20 +32,21 @@
|
|||||||
#import "RTCEAGLVideoView+Internal.h"
|
#import "RTCEAGLVideoView+Internal.h"
|
||||||
|
|
||||||
#import <GLKit/GLKit.h>
|
#import <GLKit/GLKit.h>
|
||||||
#import <QuartzCore/QuartzCore.h>
|
|
||||||
|
|
||||||
#import "RTCEAGLVideoRenderer.h"
|
#import "RTCOpenGLVideoRenderer.h"
|
||||||
#import "RTCVideoRenderer.h"
|
#import "RTCVideoRenderer.h"
|
||||||
#import "RTCVideoTrack.h"
|
#import "RTCVideoTrack.h"
|
||||||
|
|
||||||
@interface RTCEAGLVideoView () <GLKViewDelegate>
|
@interface RTCEAGLVideoView () <GLKViewDelegate>
|
||||||
|
// |i420Frame| is set when we receive a frame from a worker thread and is read
|
||||||
|
// from the display link callback so atomicity is required.
|
||||||
@property(atomic, strong) RTCI420Frame* i420Frame;
|
@property(atomic, strong) RTCI420Frame* i420Frame;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation RTCEAGLVideoView {
|
@implementation RTCEAGLVideoView {
|
||||||
CADisplayLink* _displayLink;
|
CADisplayLink* _displayLink;
|
||||||
GLKView* _glkView;
|
GLKView* _glkView;
|
||||||
RTCEAGLVideoRenderer* _glRenderer;
|
RTCOpenGLVideoRenderer* _glRenderer;
|
||||||
RTCVideoRenderer* _videoRenderer;
|
RTCVideoRenderer* _videoRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +54,7 @@
|
|||||||
if (self = [super initWithFrame:frame]) {
|
if (self = [super initWithFrame:frame]) {
|
||||||
EAGLContext* glContext =
|
EAGLContext* glContext =
|
||||||
[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||||
_glRenderer = [[RTCEAGLVideoRenderer alloc] initWithContext:glContext];
|
_glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext];
|
||||||
|
|
||||||
// GLKView manages a framebuffer for us.
|
// GLKView manages a framebuffer for us.
|
||||||
_glkView = [[GLKView alloc] initWithFrame:CGRectZero
|
_glkView = [[GLKView alloc] initWithFrame:CGRectZero
|
||||||
@ -175,7 +176,9 @@
|
|||||||
// provide. This occurs on non-main thread.
|
// provide. This occurs on non-main thread.
|
||||||
- (void)renderer:(RTCVideoRenderer*)renderer
|
- (void)renderer:(RTCVideoRenderer*)renderer
|
||||||
didSetSize:(CGSize)size {
|
didSetSize:(CGSize)size {
|
||||||
// Size is checked in renderer as frames arrive, no need to do anything here.
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self.delegate videoView:self didChangeVideoSize:size];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)renderer:(RTCVideoRenderer*)renderer
|
- (void)renderer:(RTCVideoRenderer*)renderer
|
||||||
|
187
talk/app/webrtc/objc/RTCNSGLVideoView.m
Normal file
187
talk/app/webrtc/objc/RTCNSGLVideoView.m
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2014, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||||
|
#error "This file requires ARC support."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#import "RTCNSGLVideoView.h"
|
||||||
|
|
||||||
|
#import <CoreVideo/CVDisplayLink.h>
|
||||||
|
#import <OpenGL/gl3.h>
|
||||||
|
#import "RTCOpenGLVideoRenderer.h"
|
||||||
|
#import "RTCVideoRenderer.h"
|
||||||
|
|
||||||
|
@interface RTCNSGLVideoView () <RTCVideoRendererDelegate>
|
||||||
|
// |i420Frame| is set when we receive a frame from a worker thread and is read
|
||||||
|
// from the display link callback so atomicity is required.
|
||||||
|
@property(atomic, strong) RTCI420Frame* i420Frame;
|
||||||
|
@property(atomic, strong) RTCOpenGLVideoRenderer* glRenderer;
|
||||||
|
- (void)drawFrame;
|
||||||
|
@end
|
||||||
|
|
||||||
|
static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink,
|
||||||
|
const CVTimeStamp* now,
|
||||||
|
const CVTimeStamp* outputTime,
|
||||||
|
CVOptionFlags flagsIn,
|
||||||
|
CVOptionFlags* flagsOut,
|
||||||
|
void* displayLinkContext) {
|
||||||
|
RTCNSGLVideoView* view = (__bridge RTCNSGLVideoView*)displayLinkContext;
|
||||||
|
[view drawFrame];
|
||||||
|
return kCVReturnSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation RTCNSGLVideoView {
|
||||||
|
CVDisplayLinkRef _displayLink;
|
||||||
|
RTCVideoRenderer* _videoRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(NSRect)frame
|
||||||
|
pixelFormat:(NSOpenGLPixelFormat*)format {
|
||||||
|
if (self = [super initWithFrame:frame pixelFormat:format]) {
|
||||||
|
_videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:self];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
[self teardownDisplayLink];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawRect:(NSRect)rect {
|
||||||
|
[self drawFrame];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)reshape {
|
||||||
|
[super reshape];
|
||||||
|
NSRect frame = [self frame];
|
||||||
|
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||||
|
glViewport(0, 0, frame.size.width, frame.size.height);
|
||||||
|
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)lockFocus {
|
||||||
|
NSOpenGLContext* context = [self openGLContext];
|
||||||
|
[super lockFocus];
|
||||||
|
if ([context view] != self) {
|
||||||
|
[context setView:self];
|
||||||
|
}
|
||||||
|
[context makeCurrentContext];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)prepareOpenGL {
|
||||||
|
[super prepareOpenGL];
|
||||||
|
if (!self.glRenderer) {
|
||||||
|
self.glRenderer =
|
||||||
|
[[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]];
|
||||||
|
}
|
||||||
|
[self.glRenderer setupGL];
|
||||||
|
[self setupDisplayLink];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearGLContext {
|
||||||
|
[self.glRenderer teardownGL];
|
||||||
|
self.glRenderer = nil;
|
||||||
|
[super clearGLContext];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setVideoTrack:(RTCVideoTrack*)videoTrack {
|
||||||
|
if (_videoTrack == videoTrack) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_videoTrack) {
|
||||||
|
[_videoTrack removeRenderer:_videoRenderer];
|
||||||
|
CVDisplayLinkStop(_displayLink);
|
||||||
|
}
|
||||||
|
_videoTrack = videoTrack;
|
||||||
|
if (_videoTrack) {
|
||||||
|
[_videoTrack addRenderer:_videoRenderer];
|
||||||
|
CVDisplayLinkStart(_displayLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - RTCVideoRendererDelegate
|
||||||
|
|
||||||
|
// These methods are called when the video track has frame information to
|
||||||
|
// provide. This occurs on non-main thread.
|
||||||
|
- (void)renderer:(RTCVideoRenderer*)renderer
|
||||||
|
didSetSize:(CGSize)size {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self.delegate videoView:self didChangeVideoSize:size];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)renderer:(RTCVideoRenderer*)renderer
|
||||||
|
didReceiveFrame:(RTCI420Frame*)frame {
|
||||||
|
self.i420Frame = frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
|
- (void)drawFrame {
|
||||||
|
RTCI420Frame* i420Frame = self.i420Frame;
|
||||||
|
if (i420Frame && self.glRenderer.lastDrawnFrame != i420Frame) {
|
||||||
|
// This method may be called from CVDisplayLink callback which isn't on the
|
||||||
|
// main thread so we have to lock the GL context before drawing.
|
||||||
|
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||||
|
[self.glRenderer drawFrame:i420Frame];
|
||||||
|
CGLUnlockContext([[self openGLContext] CGLContextObj]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setupDisplayLink {
|
||||||
|
if (_displayLink) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Synchronize buffer swaps with vertical refresh rate.
|
||||||
|
GLint swapInt = 1;
|
||||||
|
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||||
|
|
||||||
|
// Create display link.
|
||||||
|
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
|
||||||
|
CVDisplayLinkSetOutputCallback(_displayLink,
|
||||||
|
&OnDisplayLinkFired,
|
||||||
|
(__bridge void*)self);
|
||||||
|
// Set the display link for the current renderer.
|
||||||
|
CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
|
||||||
|
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
||||||
|
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
|
||||||
|
_displayLink, cglContext, cglPixelFormat);
|
||||||
|
if (_videoTrack) {
|
||||||
|
CVDisplayLinkStart(_displayLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)teardownDisplayLink {
|
||||||
|
if (!_displayLink) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CVDisplayLinkRelease(_displayLink);
|
||||||
|
_displayLink = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -29,50 +29,72 @@
|
|||||||
#error "This file requires ARC support."
|
#error "This file requires ARC support."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#import "RTCEAGLVideoRenderer.h"
|
#import "RTCOpenGLVideoRenderer.h"
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
#import <OpenGLES/ES2/gl.h>
|
#import <OpenGLES/ES2/gl.h>
|
||||||
|
#else
|
||||||
|
#import <OpenGL/gl3.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#import "RTCI420Frame.h"
|
#import "RTCI420Frame.h"
|
||||||
|
|
||||||
// TODO(tkchin): check and log openGL errors. Methods here return BOOLs in
|
// TODO(tkchin): check and log openGL errors. Methods here return BOOLs in
|
||||||
// anticipation of that happening in the future.
|
// anticipation of that happening in the future.
|
||||||
|
|
||||||
// Convenience macro for writing shader code that converts a code snippet into
|
#if TARGET_OS_IPHONE
|
||||||
// a C string during the C preprocessor step.
|
#define RTC_PIXEL_FORMAT GL_LUMINANCE
|
||||||
#define RTC_STRINGIZE(...) #__VA_ARGS__
|
#define SHADER_VERSION
|
||||||
|
#define VERTEX_SHADER_IN "attribute"
|
||||||
|
#define VERTEX_SHADER_OUT "varying"
|
||||||
|
#define FRAGMENT_SHADER_IN "varying"
|
||||||
|
#define FRAGMENT_SHADER_OUT
|
||||||
|
#define FRAGMENT_SHADER_COLOR "gl_FragColor"
|
||||||
|
#define FRAGMENT_SHADER_TEXTURE "texture2D"
|
||||||
|
#else
|
||||||
|
#define RTC_PIXEL_FORMAT GL_RED
|
||||||
|
#define SHADER_VERSION "#version 150\n"
|
||||||
|
#define VERTEX_SHADER_IN "in"
|
||||||
|
#define VERTEX_SHADER_OUT "out"
|
||||||
|
#define FRAGMENT_SHADER_IN "in"
|
||||||
|
#define FRAGMENT_SHADER_OUT "out vec4 fragColor;\n"
|
||||||
|
#define FRAGMENT_SHADER_COLOR "fragColor"
|
||||||
|
#define FRAGMENT_SHADER_TEXTURE "texture"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Vertex shader doesn't do anything except pass coordinates through.
|
// Vertex shader doesn't do anything except pass coordinates through.
|
||||||
static const char kVertexShaderSource[] = RTC_STRINGIZE(
|
static const char kVertexShaderSource[] =
|
||||||
attribute vec2 position;
|
SHADER_VERSION
|
||||||
attribute vec2 texcoord;
|
VERTEX_SHADER_IN " vec2 position;\n"
|
||||||
varying vec2 v_texcoord;
|
VERTEX_SHADER_IN " vec2 texcoord;\n"
|
||||||
void main() {
|
VERTEX_SHADER_OUT " vec2 v_texcoord;\n"
|
||||||
gl_Position = vec4(position.x, position.y, 0.0, 1.0);
|
"void main() {\n"
|
||||||
v_texcoord = texcoord;
|
" gl_Position = vec4(position.x, position.y, 0.0, 1.0);\n"
|
||||||
}
|
" v_texcoord = texcoord;\n"
|
||||||
);
|
"}\n";
|
||||||
|
|
||||||
// Fragment shader converts YUV values from input textures into a final RGB
|
// Fragment shader converts YUV values from input textures into a final RGB
|
||||||
// pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php.
|
// pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php.
|
||||||
static const char kFragmentShaderSource[] = RTC_STRINGIZE(
|
static const char kFragmentShaderSource[] =
|
||||||
precision highp float;
|
SHADER_VERSION
|
||||||
varying vec2 v_texcoord;
|
"precision highp float;"
|
||||||
uniform lowp sampler2D s_textureY;
|
FRAGMENT_SHADER_IN " vec2 v_texcoord;\n"
|
||||||
uniform lowp sampler2D s_textureU;
|
"uniform lowp sampler2D s_textureY;\n"
|
||||||
uniform lowp sampler2D s_textureV;
|
"uniform lowp sampler2D s_textureU;\n"
|
||||||
void main() {
|
"uniform lowp sampler2D s_textureV;\n"
|
||||||
float y, u, v, r, g, b;
|
FRAGMENT_SHADER_OUT
|
||||||
y = texture2D(s_textureY, v_texcoord).r;
|
"void main() {\n"
|
||||||
u = texture2D(s_textureU, v_texcoord).r;
|
" float y, u, v, r, g, b;\n"
|
||||||
v = texture2D(s_textureV, v_texcoord).r;
|
" y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n"
|
||||||
u = u - 0.5;
|
" u = " FRAGMENT_SHADER_TEXTURE "(s_textureU, v_texcoord).r;\n"
|
||||||
v = v - 0.5;
|
" v = " FRAGMENT_SHADER_TEXTURE "(s_textureV, v_texcoord).r;\n"
|
||||||
r = y + 1.403 * v;
|
" u = u - 0.5;\n"
|
||||||
g = y - 0.344 * u - 0.714 * v;
|
" v = v - 0.5;\n"
|
||||||
b = y + 1.770 * u;
|
" r = y + 1.403 * v;\n"
|
||||||
gl_FragColor = vec4(r, g, b, 1.0);
|
" g = y - 0.344 * u - 0.714 * v;\n"
|
||||||
}
|
" b = y + 1.770 * u;\n"
|
||||||
);
|
" " FRAGMENT_SHADER_COLOR " = vec4(r, g, b, 1.0);\n"
|
||||||
|
" }\n";
|
||||||
|
|
||||||
// Compiles a shader of the given |type| with GLSL source |source| and returns
|
// Compiles a shader of the given |type| with GLSL source |source| and returns
|
||||||
// the shader handle or 0 on error.
|
// the shader handle or 0 on error.
|
||||||
@ -122,11 +144,11 @@ GLuint CreateProgram(GLuint vertexShader, GLuint fragmentShader) {
|
|||||||
// here because the incoming frame has origin in upper left hand corner but
|
// here because the incoming frame has origin in upper left hand corner but
|
||||||
// OpenGL expects origin in bottom left corner.
|
// OpenGL expects origin in bottom left corner.
|
||||||
const GLfloat gVertices[] = {
|
const GLfloat gVertices[] = {
|
||||||
// X, Y, U, V.
|
// X, Y, U, V.
|
||||||
-1, -1, 0, 1, // Bottom left.
|
-1, -1, 0, 1, // Bottom left.
|
||||||
1, -1, 1, 1, // Bottom right.
|
1, -1, 1, 1, // Bottom right.
|
||||||
1, 1, 1, 0, // Top right.
|
1, 1, 1, 0, // Top right.
|
||||||
-1, 1, 0, 0, // Top left.
|
-1, 1, 0, 0, // Top left.
|
||||||
};
|
};
|
||||||
|
|
||||||
// |kNumTextures| must not exceed 8, which is the limit in OpenGLES2. Two sets
|
// |kNumTextures| must not exceed 8, which is the limit in OpenGLES2. Two sets
|
||||||
@ -136,13 +158,20 @@ const GLfloat gVertices[] = {
|
|||||||
static const GLsizei kNumTextureSets = 2;
|
static const GLsizei kNumTextureSets = 2;
|
||||||
static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
||||||
|
|
||||||
@implementation RTCEAGLVideoRenderer {
|
@implementation RTCOpenGLVideoRenderer {
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
EAGLContext* _context;
|
EAGLContext* _context;
|
||||||
|
#else
|
||||||
|
NSOpenGLContext* _context;
|
||||||
|
#endif
|
||||||
BOOL _isInitialized;
|
BOOL _isInitialized;
|
||||||
NSUInteger _currentTextureSet;
|
NSUInteger _currentTextureSet;
|
||||||
// Handles for OpenGL constructs.
|
// Handles for OpenGL constructs.
|
||||||
GLuint _textures[kNumTextures];
|
GLuint _textures[kNumTextures];
|
||||||
GLuint _program;
|
GLuint _program;
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
|
GLuint _vertexArray;
|
||||||
|
#endif
|
||||||
GLuint _vertexBuffer;
|
GLuint _vertexBuffer;
|
||||||
GLint _position;
|
GLint _position;
|
||||||
GLint _texcoord;
|
GLint _texcoord;
|
||||||
@ -156,7 +185,11 @@ static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
|||||||
glDisable(GL_DITHER);
|
glDisable(GL_DITHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
- (instancetype)initWithContext:(EAGLContext*)context {
|
- (instancetype)initWithContext:(EAGLContext*)context {
|
||||||
|
#else
|
||||||
|
- (instancetype)initWithContext:(NSOpenGLContext*)context {
|
||||||
|
#endif
|
||||||
NSAssert(context != nil, @"context cannot be nil");
|
NSAssert(context != nil, @"context cannot be nil");
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_context = context;
|
_context = context;
|
||||||
@ -177,8 +210,14 @@ static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
|
glBindVertexArray(_vertexArray);
|
||||||
|
#endif
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
||||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
|
[_context flushBuffer];
|
||||||
|
#endif
|
||||||
_lastDrawnFrame = frame;
|
_lastDrawnFrame = frame;
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
@ -213,23 +252,34 @@ static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
|||||||
glDeleteTextures(kNumTextures, _textures);
|
glDeleteTextures(kNumTextures, _textures);
|
||||||
glDeleteBuffers(1, &_vertexBuffer);
|
glDeleteBuffers(1, &_vertexBuffer);
|
||||||
_vertexBuffer = 0;
|
_vertexBuffer = 0;
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
|
glDeleteVertexArrays(1, &_vertexArray);
|
||||||
|
#endif
|
||||||
_isInitialized = NO;
|
_isInitialized = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Private
|
#pragma mark - Private
|
||||||
|
|
||||||
- (void)ensureGLContext {
|
- (void)ensureGLContext {
|
||||||
|
NSAssert(_context, @"context shouldn't be nil");
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
if ([EAGLContext currentContext] != _context) {
|
if ([EAGLContext currentContext] != _context) {
|
||||||
NSAssert(_context, @"context shouldn't be nil");
|
|
||||||
[EAGLContext setCurrentContext:_context];
|
[EAGLContext setCurrentContext:_context];
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if ([NSOpenGLContext currentContext] != _context) {
|
||||||
|
[_context makeCurrentContext];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)setupProgram {
|
- (BOOL)setupProgram {
|
||||||
NSAssert(!_program, @"program already set up");
|
NSAssert(!_program, @"program already set up");
|
||||||
GLuint vertexShader = CreateShader(GL_VERTEX_SHADER, kVertexShaderSource);
|
GLuint vertexShader = CreateShader(GL_VERTEX_SHADER, kVertexShaderSource);
|
||||||
|
NSAssert(vertexShader, @"failed to create vertex shader");
|
||||||
GLuint fragmentShader =
|
GLuint fragmentShader =
|
||||||
CreateShader(GL_FRAGMENT_SHADER, kFragmentShaderSource);
|
CreateShader(GL_FRAGMENT_SHADER, kFragmentShaderSource);
|
||||||
|
NSAssert(fragmentShader, @"failed to create fragment shader");
|
||||||
_program = CreateProgram(vertexShader, fragmentShader);
|
_program = CreateProgram(vertexShader, fragmentShader);
|
||||||
// Shaders are created only to generate program.
|
// Shaders are created only to generate program.
|
||||||
if (vertexShader) {
|
if (vertexShader) {
|
||||||
@ -282,33 +332,31 @@ static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
|||||||
glActiveTexture(GL_TEXTURE0 + i * 3);
|
glActiveTexture(GL_TEXTURE0 + i * 3);
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
lumaWidth,
|
lumaWidth,
|
||||||
lumaHeight,
|
lumaHeight,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0 + i * 3 + 1);
|
glActiveTexture(GL_TEXTURE0 + i * 3 + 1);
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
chromaWidth,
|
chromaWidth,
|
||||||
chromaHeight,
|
chromaHeight,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0 + i * 3 + 2);
|
glActiveTexture(GL_TEXTURE0 + i * 3 + 2);
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
chromaWidth,
|
chromaWidth,
|
||||||
chromaHeight,
|
chromaHeight,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
@ -328,11 +376,11 @@ static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
|||||||
glUniform1i(_ySampler, textureOffset);
|
glUniform1i(_ySampler, textureOffset);
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
frame.width,
|
frame.width,
|
||||||
frame.height,
|
frame.height,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
frame.yPlane);
|
frame.yPlane);
|
||||||
|
|
||||||
@ -340,11 +388,11 @@ static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
|||||||
glUniform1i(_uSampler, textureOffset + 1);
|
glUniform1i(_uSampler, textureOffset + 1);
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
frame.chromaWidth,
|
frame.chromaWidth,
|
||||||
frame.chromaHeight,
|
frame.chromaHeight,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
frame.uPlane);
|
frame.uPlane);
|
||||||
|
|
||||||
@ -352,11 +400,11 @@ static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
|||||||
glUniform1i(_vSampler, textureOffset + 2);
|
glUniform1i(_vSampler, textureOffset + 2);
|
||||||
glTexImage2D(GL_TEXTURE_2D,
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
frame.chromaWidth,
|
frame.chromaWidth,
|
||||||
frame.chromaHeight,
|
frame.chromaHeight,
|
||||||
0,
|
0,
|
||||||
GL_LUMINANCE,
|
RTC_PIXEL_FORMAT,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
frame.vPlane);
|
frame.vPlane);
|
||||||
|
|
||||||
@ -365,9 +413,21 @@ static const GLsizei kNumTextures = 3 * kNumTextureSets;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)setupVertices {
|
- (BOOL)setupVertices {
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
|
NSAssert(!_vertexArray, @"vertex array already set up");
|
||||||
|
glGenVertexArrays(1, &_vertexArray);
|
||||||
|
if (!_vertexArray) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
glBindVertexArray(_vertexArray);
|
||||||
|
#endif
|
||||||
NSAssert(!_vertexBuffer, @"vertex buffer already set up");
|
NSAssert(!_vertexBuffer, @"vertex buffer already set up");
|
||||||
glGenBuffers(1, &_vertexBuffer);
|
glGenBuffers(1, &_vertexBuffer);
|
||||||
if (!_vertexBuffer) {
|
if (!_vertexBuffer) {
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
|
glDeleteVertexArrays(1, &_vertexArray);
|
||||||
|
_vertexArray = 0;
|
||||||
|
#endif
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
|
@ -28,7 +28,6 @@
|
|||||||
#import "RTCPeerConnection.h"
|
#import "RTCPeerConnection.h"
|
||||||
|
|
||||||
#import "RTCPeerConnectionDelegate.h"
|
#import "RTCPeerConnectionDelegate.h"
|
||||||
#import "RTCPeerConnectionObserver.h"
|
|
||||||
|
|
||||||
#include "talk/app/webrtc/peerconnectioninterface.h"
|
#include "talk/app/webrtc/peerconnectioninterface.h"
|
||||||
|
|
||||||
@ -37,8 +36,8 @@
|
|||||||
@property(nonatomic, assign, readonly)
|
@property(nonatomic, assign, readonly)
|
||||||
talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection;
|
talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection;
|
||||||
|
|
||||||
- (id)initWithPeerConnection:(
|
- (instancetype)initWithFactory:(webrtc::PeerConnectionFactoryInterface*)factory
|
||||||
talk_base::scoped_refptr<webrtc::PeerConnectionInterface>)peerConnection
|
iceServers:(const webrtc::PeerConnectionInterface::IceServers&)iceServers
|
||||||
observer:(webrtc::RTCPeerConnectionObserver *)observer;
|
constraints:(const webrtc::MediaConstraintsInterface*)constraints;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#import "RTCMediaConstraints+Internal.h"
|
#import "RTCMediaConstraints+Internal.h"
|
||||||
#import "RTCMediaStream+Internal.h"
|
#import "RTCMediaStream+Internal.h"
|
||||||
#import "RTCMediaStreamTrack+Internal.h"
|
#import "RTCMediaStreamTrack+Internal.h"
|
||||||
|
#import "RTCPeerConnectionObserver.h"
|
||||||
#import "RTCSessionDescription+Internal.h"
|
#import "RTCSessionDescription+Internal.h"
|
||||||
#import "RTCSessionDescriptionDelegate.h"
|
#import "RTCSessionDescriptionDelegate.h"
|
||||||
#import "RTCSessionDescription.h"
|
#import "RTCSessionDescription.h"
|
||||||
@ -273,19 +274,15 @@ class RTCStatsObserver : public StatsObserver {
|
|||||||
|
|
||||||
@implementation RTCPeerConnection (Internal)
|
@implementation RTCPeerConnection (Internal)
|
||||||
|
|
||||||
- (id)initWithPeerConnection:
|
- (instancetype)initWithFactory:(webrtc::PeerConnectionFactoryInterface*)factory
|
||||||
(talk_base::scoped_refptr<webrtc::PeerConnectionInterface>)
|
iceServers:(const webrtc::PeerConnectionInterface::IceServers&)iceServers
|
||||||
peerConnection
|
constraints:(const webrtc::MediaConstraintsInterface*)constraints {
|
||||||
observer:(webrtc::RTCPeerConnectionObserver*)observer {
|
NSParameterAssert(factory != NULL);
|
||||||
if (!peerConnection || !observer) {
|
if (self = [super init]) {
|
||||||
NSAssert(NO, @"nil arguments not allowed");
|
_observer.reset(new webrtc::RTCPeerConnectionObserver(self));
|
||||||
self = nil;
|
_peerConnection = factory->CreatePeerConnection(
|
||||||
return nil;
|
iceServers, constraints, NULL, NULL, _observer.get());
|
||||||
}
|
|
||||||
if ((self = [super init])) {
|
|
||||||
_peerConnection = peerConnection;
|
|
||||||
_localStreams = [[NSMutableArray alloc] init];
|
_localStreams = [[NSMutableArray alloc] init];
|
||||||
_observer.reset(observer);
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,6 @@
|
|||||||
#import "RTCMediaStreamTrack+Internal.h"
|
#import "RTCMediaStreamTrack+Internal.h"
|
||||||
#import "RTCPeerConnection+Internal.h"
|
#import "RTCPeerConnection+Internal.h"
|
||||||
#import "RTCPeerConnectionDelegate.h"
|
#import "RTCPeerConnectionDelegate.h"
|
||||||
#import "RTCPeerConnectionObserver.h"
|
|
||||||
#import "RTCVideoCapturer+Internal.h"
|
#import "RTCVideoCapturer+Internal.h"
|
||||||
#import "RTCVideoSource+Internal.h"
|
#import "RTCVideoSource+Internal.h"
|
||||||
#import "RTCVideoTrack+Internal.h"
|
#import "RTCVideoTrack+Internal.h"
|
||||||
@ -94,19 +93,11 @@
|
|||||||
for (RTCICEServer* server in servers) {
|
for (RTCICEServer* server in servers) {
|
||||||
iceServers.push_back(server.iceServer);
|
iceServers.push_back(server.iceServer);
|
||||||
}
|
}
|
||||||
webrtc::RTCPeerConnectionObserver* observer =
|
|
||||||
new webrtc::RTCPeerConnectionObserver(delegate);
|
|
||||||
webrtc::DTLSIdentityServiceInterface* dummy_dtls_identity_service = NULL;
|
|
||||||
talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection =
|
|
||||||
self.nativeFactory->CreatePeerConnection(iceServers,
|
|
||||||
constraints.constraints,
|
|
||||||
NULL,
|
|
||||||
dummy_dtls_identity_service,
|
|
||||||
observer);
|
|
||||||
RTCPeerConnection* pc =
|
RTCPeerConnection* pc =
|
||||||
[[RTCPeerConnection alloc] initWithPeerConnection:peerConnection
|
[[RTCPeerConnection alloc] initWithFactory:self.nativeFactory.get()
|
||||||
observer:observer];
|
iceServers:iceServers
|
||||||
observer->SetPeerConnection(pc);
|
constraints:constraints.constraints];
|
||||||
|
pc.delegate = delegate;
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,12 +38,9 @@ namespace webrtc {
|
|||||||
class RTCPeerConnectionObserver : public PeerConnectionObserver {
|
class RTCPeerConnectionObserver : public PeerConnectionObserver {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RTCPeerConnectionObserver(id<RTCPeerConnectionDelegate> delegate);
|
RTCPeerConnectionObserver(RTCPeerConnection* peerConnection);
|
||||||
virtual ~RTCPeerConnectionObserver();
|
virtual ~RTCPeerConnectionObserver();
|
||||||
|
|
||||||
// |peerConnection| owns |this|, so outlives it.
|
|
||||||
void SetPeerConnection(RTCPeerConnection *peerConnection);
|
|
||||||
|
|
||||||
virtual void OnError() OVERRIDE;
|
virtual void OnError() OVERRIDE;
|
||||||
|
|
||||||
// Triggered when the SignalingState changed.
|
// Triggered when the SignalingState changed.
|
||||||
@ -74,10 +71,7 @@ class RTCPeerConnectionObserver : public PeerConnectionObserver {
|
|||||||
virtual void OnIceCandidate(const IceCandidateInterface* candidate) OVERRIDE;
|
virtual void OnIceCandidate(const IceCandidateInterface* candidate) OVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
id<RTCPeerConnectionDelegate> _delegate;
|
__weak RTCPeerConnection* _peerConnection;
|
||||||
// __unsafe_unretained is in fact safe because |_peerConnection| owns |this|;
|
|
||||||
// see comment on SetPeerConnection() above.
|
|
||||||
__unsafe_unretained RTCPeerConnection *_peerConnection;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -39,70 +39,74 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
RTCPeerConnectionObserver::RTCPeerConnectionObserver(
|
RTCPeerConnectionObserver::RTCPeerConnectionObserver(
|
||||||
id<RTCPeerConnectionDelegate> delegate) {
|
|
||||||
_delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
RTCPeerConnectionObserver::~RTCPeerConnectionObserver() {}
|
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::SetPeerConnection(
|
|
||||||
RTCPeerConnection* peerConnection) {
|
RTCPeerConnection* peerConnection) {
|
||||||
_peerConnection = peerConnection;
|
_peerConnection = peerConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RTCPeerConnectionObserver::~RTCPeerConnectionObserver() {
|
||||||
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnError() {
|
void RTCPeerConnectionObserver::OnError() {
|
||||||
[_delegate peerConnectionOnError:_peerConnection];
|
[_peerConnection.delegate peerConnectionOnError:_peerConnection];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnSignalingChange(
|
void RTCPeerConnectionObserver::OnSignalingChange(
|
||||||
PeerConnectionInterface::SignalingState new_state) {
|
PeerConnectionInterface::SignalingState new_state) {
|
||||||
[_delegate peerConnection:_peerConnection
|
RTCSignalingState state =
|
||||||
signalingStateChanged:[RTCEnumConverter
|
[RTCEnumConverter convertSignalingStateToObjC:new_state];
|
||||||
convertSignalingStateToObjC:new_state]];
|
[_peerConnection.delegate peerConnection:_peerConnection
|
||||||
|
signalingStateChanged:state];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnAddStream(MediaStreamInterface* stream) {
|
void RTCPeerConnectionObserver::OnAddStream(MediaStreamInterface* stream) {
|
||||||
RTCMediaStream* mediaStream =
|
RTCMediaStream* mediaStream =
|
||||||
[[RTCMediaStream alloc] initWithMediaStream:stream];
|
[[RTCMediaStream alloc] initWithMediaStream:stream];
|
||||||
[_delegate peerConnection:_peerConnection addedStream:mediaStream];
|
[_peerConnection.delegate peerConnection:_peerConnection
|
||||||
|
addedStream:mediaStream];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnRemoveStream(MediaStreamInterface* stream) {
|
void RTCPeerConnectionObserver::OnRemoveStream(MediaStreamInterface* stream) {
|
||||||
RTCMediaStream* mediaStream =
|
RTCMediaStream* mediaStream =
|
||||||
[[RTCMediaStream alloc] initWithMediaStream:stream];
|
[[RTCMediaStream alloc] initWithMediaStream:stream];
|
||||||
[_delegate peerConnection:_peerConnection removedStream:mediaStream];
|
[_peerConnection.delegate peerConnection:_peerConnection
|
||||||
|
removedStream:mediaStream];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnDataChannel(
|
void RTCPeerConnectionObserver::OnDataChannel(
|
||||||
DataChannelInterface* data_channel) {
|
DataChannelInterface* data_channel) {
|
||||||
RTCDataChannel* dataChannel =
|
RTCDataChannel* dataChannel =
|
||||||
[[RTCDataChannel alloc] initWithDataChannel:data_channel];
|
[[RTCDataChannel alloc] initWithDataChannel:data_channel];
|
||||||
[_delegate peerConnection:_peerConnection didOpenDataChannel:dataChannel];
|
[_peerConnection.delegate peerConnection:_peerConnection
|
||||||
|
didOpenDataChannel:dataChannel];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnRenegotiationNeeded() {
|
void RTCPeerConnectionObserver::OnRenegotiationNeeded() {
|
||||||
[_delegate peerConnectionOnRenegotiationNeeded:_peerConnection];
|
id<RTCPeerConnectionDelegate> delegate = _peerConnection.delegate;
|
||||||
|
[delegate peerConnectionOnRenegotiationNeeded:_peerConnection];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnIceConnectionChange(
|
void RTCPeerConnectionObserver::OnIceConnectionChange(
|
||||||
PeerConnectionInterface::IceConnectionState new_state) {
|
PeerConnectionInterface::IceConnectionState new_state) {
|
||||||
[_delegate peerConnection:_peerConnection
|
RTCICEConnectionState state =
|
||||||
iceConnectionChanged:[RTCEnumConverter
|
[RTCEnumConverter convertIceConnectionStateToObjC:new_state];
|
||||||
convertIceConnectionStateToObjC:new_state]];
|
[_peerConnection.delegate peerConnection:_peerConnection
|
||||||
|
iceConnectionChanged:state];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnIceGatheringChange(
|
void RTCPeerConnectionObserver::OnIceGatheringChange(
|
||||||
PeerConnectionInterface::IceGatheringState new_state) {
|
PeerConnectionInterface::IceGatheringState new_state) {
|
||||||
[_delegate peerConnection:_peerConnection
|
RTCICEGatheringState state =
|
||||||
iceGatheringChanged:[RTCEnumConverter
|
[RTCEnumConverter convertIceGatheringStateToObjC:new_state];
|
||||||
convertIceGatheringStateToObjC:new_state]];
|
[_peerConnection.delegate peerConnection:_peerConnection
|
||||||
|
iceGatheringChanged:state];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTCPeerConnectionObserver::OnIceCandidate(
|
void RTCPeerConnectionObserver::OnIceCandidate(
|
||||||
const IceCandidateInterface* candidate) {
|
const IceCandidateInterface* candidate) {
|
||||||
RTCICECandidate* iceCandidate =
|
RTCICECandidate* iceCandidate =
|
||||||
[[RTCICECandidate alloc] initWithCandidate:candidate];
|
[[RTCICECandidate alloc] initWithCandidate:candidate];
|
||||||
[_delegate peerConnection:_peerConnection gotICECandidate:iceCandidate];
|
[_peerConnection.delegate peerConnection:_peerConnection
|
||||||
|
gotICECandidate:iceCandidate];
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -30,10 +30,18 @@
|
|||||||
|
|
||||||
#import "RTCVideoRenderer.h"
|
#import "RTCVideoRenderer.h"
|
||||||
|
|
||||||
|
@class RTCEAGLVideoView;
|
||||||
|
@protocol RTCEAGLVideoViewDelegate
|
||||||
|
|
||||||
|
- (void)videoView:(RTCEAGLVideoView*)videoView didChangeVideoSize:(CGSize)size;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@class RTCVideoTrack;
|
@class RTCVideoTrack;
|
||||||
// RTCEAGLVideoView renders |videoTrack| onto itself using OpenGLES.
|
// RTCEAGLVideoView renders |videoTrack| onto itself using OpenGLES.
|
||||||
@interface RTCEAGLVideoView : UIView
|
@interface RTCEAGLVideoView : UIView
|
||||||
|
|
||||||
@property(nonatomic, strong) RTCVideoTrack* videoTrack;
|
@property(nonatomic, strong) RTCVideoTrack* videoTrack;
|
||||||
|
@property(nonatomic, weak) id<RTCEAGLVideoViewDelegate> delegate;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
48
talk/app/webrtc/objc/public/RTCNSGLVideoView.h
Normal file
48
talk/app/webrtc/objc/public/RTCNSGLVideoView.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2014, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
#error "This file targets OSX."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#import <AppKit/NSOpenGLView.h>
|
||||||
|
|
||||||
|
#import "RTCVideoTrack.h"
|
||||||
|
|
||||||
|
@class RTCNSGLVideoView;
|
||||||
|
@protocol RTCNSGLVideoViewDelegate
|
||||||
|
|
||||||
|
- (void)videoView:(RTCNSGLVideoView*)videoView didChangeVideoSize:(CGSize)size;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface RTCNSGLVideoView : NSOpenGLView
|
||||||
|
|
||||||
|
@property(nonatomic, strong) RTCVideoTrack* videoTrack;
|
||||||
|
@property(nonatomic, weak) id<RTCNSGLVideoViewDelegate> delegate;
|
||||||
|
|
||||||
|
@end
|
@ -26,21 +26,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
#import <GLKit/GLKit.h>
|
#import <GLKit/GLKit.h>
|
||||||
|
#else
|
||||||
|
#import <AppKit/NSOpenGL.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
@class RTCI420Frame;
|
@class RTCI420Frame;
|
||||||
|
|
||||||
// RTCEAGLVideoRenderer issues appropriate EAGL commands to draw a frame to the
|
// RTCOpenGLVideoRenderer issues appropriate OpenGL commands to draw a frame to
|
||||||
// currently bound framebuffer. OpenGL framebuffer creation and management
|
// the currently bound framebuffer. Supports OpenGL 3.2 and OpenGLES 2.0. OpenGL
|
||||||
// should be handled elsewhere using the same context used to initialize this
|
// framebuffer creation and management should be handled elsewhere using the
|
||||||
// class.
|
// same context used to initialize this class.
|
||||||
@interface RTCEAGLVideoRenderer : NSObject
|
@interface RTCOpenGLVideoRenderer : NSObject
|
||||||
|
|
||||||
// The last successfully drawn frame. Used to avoid drawing frames unnecessarily
|
// The last successfully drawn frame. Used to avoid drawing frames unnecessarily
|
||||||
// hence saving battery life by reducing load.
|
// hence saving battery life by reducing load.
|
||||||
@property(nonatomic, readonly) RTCI420Frame* lastDrawnFrame;
|
@property(nonatomic, readonly) RTCI420Frame* lastDrawnFrame;
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
- (instancetype)initWithContext:(EAGLContext*)context;
|
- (instancetype)initWithContext:(EAGLContext*)context;
|
||||||
|
#else
|
||||||
|
- (instancetype)initWithContext:(NSOpenGLContext*)context;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Draws |frame| onto the currently bound OpenGL framebuffer. |setupGL| must be
|
// Draws |frame| onto the currently bound OpenGL framebuffer. |setupGL| must be
|
||||||
// called before this function will succeed.
|
// called before this function will succeed.
|
@ -45,6 +45,8 @@
|
|||||||
// http://www.w3.org/TR/mediacapture-streams/
|
// http://www.w3.org/TR/mediacapture-streams/
|
||||||
@interface RTCPeerConnection : NSObject
|
@interface RTCPeerConnection : NSObject
|
||||||
|
|
||||||
|
@property(nonatomic, weak) id<RTCPeerConnectionDelegate> delegate;
|
||||||
|
|
||||||
// Accessor methods to active local streams.
|
// Accessor methods to active local streams.
|
||||||
@property(nonatomic, strong, readonly) NSArray *localStreams;
|
@property(nonatomic, strong, readonly) NSArray *localStreams;
|
||||||
|
|
||||||
|
@ -1,150 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 "APPRTCViewController.h"
|
|
||||||
|
|
||||||
#import <AVFoundation/AVFoundation.h>
|
|
||||||
#import "RTCEAGLVideoView.h"
|
|
||||||
|
|
||||||
@interface APPRTCViewController ()
|
|
||||||
@property(nonatomic, assign) UIInterfaceOrientation statusBarOrientation;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation APPRTCViewController
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
self.statusBarOrientation =
|
|
||||||
[UIApplication sharedApplication].statusBarOrientation;
|
|
||||||
self.roomInput.delegate = self;
|
|
||||||
[self.roomInput becomeFirstResponder];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidLayoutSubviews {
|
|
||||||
if (self.statusBarOrientation !=
|
|
||||||
[UIApplication sharedApplication].statusBarOrientation) {
|
|
||||||
self.statusBarOrientation =
|
|
||||||
[UIApplication sharedApplication].statusBarOrientation;
|
|
||||||
[[NSNotificationCenter defaultCenter]
|
|
||||||
postNotificationName:@"StatusBarOrientationDidChange"
|
|
||||||
object:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)displayText:(NSString*)text {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
NSString* output =
|
|
||||||
[NSString stringWithFormat:@"%@\n%@", self.logView.text, text];
|
|
||||||
self.logView.text = output;
|
|
||||||
[self.logView
|
|
||||||
scrollRangeToVisible:NSMakeRange([self.logView.text length], 0)];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)resetUI {
|
|
||||||
[self.roomInput resignFirstResponder];
|
|
||||||
self.roomInput.text = nil;
|
|
||||||
self.roomInput.hidden = NO;
|
|
||||||
self.instructionsView.hidden = NO;
|
|
||||||
self.logView.hidden = YES;
|
|
||||||
self.logView.text = nil;
|
|
||||||
self.blackView.hidden = YES;
|
|
||||||
|
|
||||||
[self.remoteVideoView removeFromSuperview];
|
|
||||||
self.remoteVideoView = nil;
|
|
||||||
|
|
||||||
[self.localVideoView removeFromSuperview];
|
|
||||||
self.localVideoView = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(fischman): Use video dimensions from the incoming video stream
|
|
||||||
// and resize the Video View accordingly w.r.t. aspect ratio.
|
|
||||||
enum {
|
|
||||||
// Remote video view dimensions.
|
|
||||||
kRemoteVideoWidth = 640,
|
|
||||||
kRemoteVideoHeight = 480,
|
|
||||||
// Padding space for local video view with its parent.
|
|
||||||
kLocalViewPadding = 20
|
|
||||||
};
|
|
||||||
|
|
||||||
- (void)setupCaptureSession {
|
|
||||||
self.blackView.hidden = NO;
|
|
||||||
|
|
||||||
CGSize videoSize =
|
|
||||||
CGSizeMake(kRemoteVideoWidth, kRemoteVideoHeight);
|
|
||||||
CGRect remoteVideoFrame =
|
|
||||||
AVMakeRectWithAspectRatioInsideRect(videoSize,
|
|
||||||
self.blackView.bounds);
|
|
||||||
CGRect localVideoFrame = remoteVideoFrame;
|
|
||||||
// TODO(tkchin): use video dimensions from incoming video stream
|
|
||||||
// and handle rotation.
|
|
||||||
localVideoFrame.size.width = remoteVideoFrame.size.height / 4;
|
|
||||||
localVideoFrame.size.height = remoteVideoFrame.size.width / 4;
|
|
||||||
localVideoFrame.origin.x = CGRectGetMaxX(remoteVideoFrame)
|
|
||||||
- localVideoFrame.size.width - kLocalViewPadding;
|
|
||||||
localVideoFrame.origin.y = CGRectGetMaxY(remoteVideoFrame)
|
|
||||||
- localVideoFrame.size.height - kLocalViewPadding;
|
|
||||||
|
|
||||||
self.remoteVideoView =
|
|
||||||
[[RTCEAGLVideoView alloc] initWithFrame:remoteVideoFrame];
|
|
||||||
[self.blackView addSubview:self.remoteVideoView];
|
|
||||||
self.remoteVideoView.transform = CGAffineTransformMakeScale(-1, 1);
|
|
||||||
|
|
||||||
self.localVideoView =
|
|
||||||
[[RTCEAGLVideoView alloc] initWithFrame:localVideoFrame];
|
|
||||||
[self.blackView addSubview:self.localVideoView];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UITextFieldDelegate
|
|
||||||
|
|
||||||
- (void)textFieldDidEndEditing:(UITextField*)textField {
|
|
||||||
NSString* room = textField.text;
|
|
||||||
if ([room length] == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
textField.hidden = YES;
|
|
||||||
self.instructionsView.hidden = YES;
|
|
||||||
self.logView.hidden = NO;
|
|
||||||
// TODO(hughv): Instead of launching a URL with apprtc scheme, change to
|
|
||||||
// prepopulating the textField with a valid URL missing the room. This allows
|
|
||||||
// the user to have the simplicity of just entering the room or the ability to
|
|
||||||
// override to a custom appspot instance. Remove apprtc:// when this is done.
|
|
||||||
NSString* url =
|
|
||||||
[NSString stringWithFormat:@"apprtc://apprtc.appspot.com/?r=%@", room];
|
|
||||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{ [self setupCaptureSession]; });
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
|
|
||||||
// There is no other control that can take focus, so manually resign focus
|
|
||||||
// when return (Join) is pressed to trigger |textFieldDidEndEditing|.
|
|
||||||
[textField resignFirstResponder];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@ -1,3 +0,0 @@
|
|||||||
This directory contains an example iOS client for http://apprtc.appspot.com
|
|
||||||
|
|
||||||
See ../../app/webrtc/objc/README for information on how to use it.
|
|
@ -29,10 +29,13 @@
|
|||||||
|
|
||||||
#import "GAEChannelClient.h"
|
#import "GAEChannelClient.h"
|
||||||
|
|
||||||
// Called when there are RTCICEServers.
|
@class APPRTCAppClient;
|
||||||
@protocol ICEServerDelegate<NSObject>
|
@protocol APPRTCAppClientDelegate
|
||||||
|
|
||||||
- (void)onICEServers:(NSArray*)servers;
|
- (void)appClient:(APPRTCAppClient*)appClient
|
||||||
|
didErrorWithMessage:(NSString*)message;
|
||||||
|
- (void)appClient:(APPRTCAppClient*)appClient
|
||||||
|
didReceiveICEServers:(NSArray*)servers;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -47,13 +50,12 @@
|
|||||||
// for the registered handler to be called with received messages.
|
// for the registered handler to be called with received messages.
|
||||||
@interface APPRTCAppClient : NSObject<NSURLConnectionDataDelegate>
|
@interface APPRTCAppClient : NSObject<NSURLConnectionDataDelegate>
|
||||||
|
|
||||||
@property(nonatomic, weak, readonly) id<ICEServerDelegate> ICEServerDelegate;
|
@property(nonatomic) BOOL initiator;
|
||||||
@property(nonatomic, weak, readonly) id<GAEMessageHandler> messageHandler;
|
|
||||||
@property(nonatomic, assign) BOOL initiator;
|
|
||||||
@property(nonatomic, copy, readonly) RTCMediaConstraints* videoConstraints;
|
@property(nonatomic, copy, readonly) RTCMediaConstraints* videoConstraints;
|
||||||
|
@property(nonatomic, weak) id<APPRTCAppClientDelegate> delegate;
|
||||||
|
|
||||||
- (id)initWithICEServerDelegate:(id<ICEServerDelegate>)delegate
|
- (id)initWithDelegate:(id<APPRTCAppClientDelegate>)delegate
|
||||||
messageHandler:(id<GAEMessageHandler>)handler;
|
messageHandler:(id<GAEMessageHandler>)handler;
|
||||||
- (void)connectToRoom:(NSURL*)room;
|
- (void)connectToRoom:(NSURL*)room;
|
||||||
- (void)sendData:(NSData*)data;
|
- (void)sendData:(NSData*)data;
|
||||||
|
|
@ -25,18 +25,22 @@
|
|||||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||||
|
#error "This file requires ARC support."
|
||||||
|
#endif
|
||||||
|
|
||||||
#import "APPRTCAppClient.h"
|
#import "APPRTCAppClient.h"
|
||||||
|
|
||||||
#import <dispatch/dispatch.h>
|
#import <dispatch/dispatch.h>
|
||||||
|
|
||||||
#import "GAEChannelClient.h"
|
#import "GAEChannelClient.h"
|
||||||
#import "RTCICEServer.h"
|
#import "RTCICEServer.h"
|
||||||
#import "APPRTCAppDelegate.h"
|
|
||||||
#import "RTCMediaConstraints.h"
|
#import "RTCMediaConstraints.h"
|
||||||
|
#import "RTCPair.h"
|
||||||
|
|
||||||
@interface APPRTCAppClient ()
|
@interface APPRTCAppClient ()
|
||||||
|
|
||||||
@property(nonatomic, strong) dispatch_queue_t backgroundQueue;
|
@property(nonatomic, weak, readonly) id<GAEMessageHandler> messageHandler;
|
||||||
@property(nonatomic, copy) NSString* baseURL;
|
@property(nonatomic, copy) NSString* baseURL;
|
||||||
@property(nonatomic, strong) GAEChannelClient* gaeChannel;
|
@property(nonatomic, strong) GAEChannelClient* gaeChannel;
|
||||||
@property(nonatomic, copy) NSString* postMessageUrl;
|
@property(nonatomic, copy) NSString* postMessageUrl;
|
||||||
@ -49,12 +53,14 @@
|
|||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation APPRTCAppClient
|
@implementation APPRTCAppClient {
|
||||||
|
dispatch_queue_t _backgroundQueue;
|
||||||
|
}
|
||||||
|
|
||||||
- (id)initWithICEServerDelegate:(id<ICEServerDelegate>)delegate
|
- (id)initWithDelegate:(id<APPRTCAppClientDelegate>)delegate
|
||||||
messageHandler:(id<GAEMessageHandler>)handler {
|
messageHandler:(id<GAEMessageHandler>)handler {
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
_ICEServerDelegate = delegate;
|
_delegate = delegate;
|
||||||
_messageHandler = handler;
|
_messageHandler = handler;
|
||||||
_backgroundQueue = dispatch_queue_create("RTCBackgroundQueue",
|
_backgroundQueue = dispatch_queue_create("RTCBackgroundQueue",
|
||||||
DISPATCH_QUEUE_SERIAL);
|
DISPATCH_QUEUE_SERIAL);
|
||||||
@ -68,14 +74,15 @@
|
|||||||
#pragma mark - Public methods
|
#pragma mark - Public methods
|
||||||
|
|
||||||
- (void)connectToRoom:(NSURL*)url {
|
- (void)connectToRoom:(NSURL*)url {
|
||||||
NSURLRequest* request = [self getRequestFromUrl:url];
|
self.roomHtml = [NSMutableString stringWithCapacity:20000];
|
||||||
|
NSURLRequest* request = [NSURLRequest requestWithURL:url];
|
||||||
[NSURLConnection connectionWithRequest:request delegate:self];
|
[NSURLConnection connectionWithRequest:request delegate:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sendData:(NSData*)data {
|
- (void)sendData:(NSData*)data {
|
||||||
[self maybeLogMessage:@"Send message"];
|
[self maybeLogMessage:@"Send message"];
|
||||||
|
|
||||||
dispatch_async(self.backgroundQueue, ^{
|
dispatch_async(_backgroundQueue, ^{
|
||||||
[self.sendQueue addObject:[data copy]];
|
[self.sendQueue addObject:[data copy]];
|
||||||
|
|
||||||
if ([self.postMessageUrl length] < 1) {
|
if ([self.postMessageUrl length] < 1) {
|
||||||
@ -109,10 +116,10 @@
|
|||||||
NSArray* matches =
|
NSArray* matches =
|
||||||
[regexp matchesInString:self.roomHtml options:0 range:fullRange];
|
[regexp matchesInString:self.roomHtml options:0 range:fullRange];
|
||||||
if ([matches count] != 1) {
|
if ([matches count] != 1) {
|
||||||
[self showMessage:[NSString stringWithFormat:@"%d matches for %@ in %@",
|
NSString* format = @"%lu matches for %@ in %@";
|
||||||
[matches count],
|
NSString* message = [NSString stringWithFormat:format,
|
||||||
name,
|
(unsigned long)[matches count], name, self.roomHtml];
|
||||||
self.roomHtml]];
|
[self.delegate appClient:self didErrorWithMessage:message];
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
NSRange matchRange = [matches[0] rangeAtIndex:1];
|
NSRange matchRange = [matches[0] rangeAtIndex:1];
|
||||||
@ -130,15 +137,6 @@
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSURLRequest*)getRequestFromUrl:(NSURL*)url {
|
|
||||||
self.roomHtml = [NSMutableString stringWithCapacity:20000];
|
|
||||||
NSString* path =
|
|
||||||
[NSString stringWithFormat:@"https:%@", [url resourceSpecifier]];
|
|
||||||
NSURLRequest* request =
|
|
||||||
[NSURLRequest requestWithURL:[NSURL URLWithString:path]];
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)maybeLogMessage:(NSString*)message {
|
- (void)maybeLogMessage:(NSString*)message {
|
||||||
if (self.verboseLogging) {
|
if (self.verboseLogging) {
|
||||||
NSLog(@"%@", message);
|
NSLog(@"%@", message);
|
||||||
@ -164,23 +162,13 @@
|
|||||||
[NSString stringWithUTF8String:[responseData bytes]]);
|
[NSString stringWithUTF8String:[responseData bytes]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)showMessage:(NSString*)message {
|
|
||||||
NSLog(@"%@", message);
|
|
||||||
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:@"Unable to join"
|
|
||||||
message:message
|
|
||||||
delegate:nil
|
|
||||||
cancelButtonTitle:@"OK"
|
|
||||||
otherButtonTitles:nil];
|
|
||||||
[alertView show];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)updateICEServers:(NSMutableArray*)ICEServers
|
- (void)updateICEServers:(NSMutableArray*)ICEServers
|
||||||
withTurnServer:(NSString*)turnServerUrl {
|
withTurnServer:(NSString*)turnServerUrl {
|
||||||
if ([turnServerUrl length] < 1) {
|
if ([turnServerUrl length] < 1) {
|
||||||
[self.ICEServerDelegate onICEServers:ICEServers];
|
[self.delegate appClient:self didReceiveICEServers:ICEServers];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dispatch_async(self.backgroundQueue, ^(void) {
|
dispatch_async(_backgroundQueue, ^(void) {
|
||||||
NSMutableURLRequest* request = [NSMutableURLRequest
|
NSMutableURLRequest* request = [NSMutableURLRequest
|
||||||
requestWithURL:[NSURL URLWithString:turnServerUrl]];
|
requestWithURL:[NSURL URLWithString:turnServerUrl]];
|
||||||
[request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"];
|
[request addValue:@"Mozilla/5.0" forHTTPHeaderField:@"user-agent"];
|
||||||
@ -214,7 +202,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||||
[self.ICEServerDelegate onICEServers:ICEServers];
|
[self.delegate appClient:self didReceiveICEServers:ICEServers];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -223,8 +211,10 @@
|
|||||||
|
|
||||||
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
|
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
|
||||||
NSString* roomHtml = [NSString stringWithUTF8String:[data bytes]];
|
NSString* roomHtml = [NSString stringWithUTF8String:[data bytes]];
|
||||||
[self maybeLogMessage:[NSString stringWithFormat:@"Received %d chars",
|
NSString* message =
|
||||||
[roomHtml length]]];
|
[NSString stringWithFormat:@"Received %lu chars",
|
||||||
|
(unsigned long)[roomHtml length]];
|
||||||
|
[self maybeLogMessage:message];
|
||||||
[self.roomHtml appendString:roomHtml];
|
[self.roomHtml appendString:roomHtml];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,8 +233,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
|
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
|
||||||
[self maybeLogMessage:[NSString stringWithFormat:@"finished loading %d chars",
|
NSString* message =
|
||||||
[self.roomHtml length]]];
|
[NSString stringWithFormat:@"finished loading %lu chars",
|
||||||
|
(unsigned long)[self.roomHtml length]];
|
||||||
|
[self maybeLogMessage:message];
|
||||||
NSRegularExpression* fullRegex =
|
NSRegularExpression* fullRegex =
|
||||||
[NSRegularExpression regularExpressionWithPattern:@"room is full"
|
[NSRegularExpression regularExpressionWithPattern:@"room is full"
|
||||||
options:0
|
options:0
|
||||||
@ -253,10 +245,8 @@
|
|||||||
numberOfMatchesInString:self.roomHtml
|
numberOfMatchesInString:self.roomHtml
|
||||||
options:0
|
options:0
|
||||||
range:NSMakeRange(0, [self.roomHtml length])]) {
|
range:NSMakeRange(0, [self.roomHtml length])]) {
|
||||||
[self showMessage:@"Room full"];
|
NSString* message = @"Room full, dropping peerconnection.";
|
||||||
APPRTCAppDelegate* ad =
|
[self.delegate appClient:self didErrorWithMessage:message];
|
||||||
(APPRTCAppDelegate*)[[UIApplication sharedApplication] delegate];
|
|
||||||
[ad closeVideoUI];
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +321,22 @@
|
|||||||
json =
|
json =
|
||||||
[NSJSONSerialization JSONObjectWithData:mcData options:0 error:&error];
|
[NSJSONSerialization JSONObjectWithData:mcData options:0 error:&error];
|
||||||
NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
|
NSAssert(!error, @"Unable to parse. %@", error.localizedDescription);
|
||||||
if ([[json objectForKey:@"video"] boolValue]) {
|
id video = json[@"video"];
|
||||||
|
if ([video isKindOfClass:[NSDictionary class]]) {
|
||||||
|
NSDictionary* mandatory = video[@"mandatory"];
|
||||||
|
NSMutableArray* mandatoryContraints =
|
||||||
|
[NSMutableArray arrayWithCapacity:[mandatory count]];
|
||||||
|
[mandatory enumerateKeysAndObjectsUsingBlock:^(
|
||||||
|
id key, id obj, BOOL* stop) {
|
||||||
|
[mandatoryContraints addObject:[[RTCPair alloc] initWithKey:key
|
||||||
|
value:obj]];
|
||||||
|
}];
|
||||||
|
// TODO(tkchin): figure out json formats for optional constraints.
|
||||||
|
_videoConstraints =
|
||||||
|
[[RTCMediaConstraints alloc]
|
||||||
|
initWithMandatoryConstraints:mandatoryContraints
|
||||||
|
optionalConstraints:nil];
|
||||||
|
} else if ([video isKindOfClass:[NSNumber class]] && [video boolValue]) {
|
||||||
_videoConstraints = [[RTCMediaConstraints alloc] init];
|
_videoConstraints = [[RTCMediaConstraints alloc] init];
|
||||||
}
|
}
|
||||||
}
|
}
|
66
talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.h
Normal file
66
talk/examples/objc/AppRTCDemo/APPRTCConnectionManager.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2014, 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>
|
||||||
|
|
||||||
|
// Used to log messages to destination like UI.
|
||||||
|
@protocol APPRTCLogger<NSObject>
|
||||||
|
- (void)logMessage:(NSString*)message;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@class RTCVideoTrack;
|
||||||
|
@class APPRTCConnectionManager;
|
||||||
|
|
||||||
|
// Used to provide AppRTC connection information.
|
||||||
|
@protocol APPRTCConnectionManagerDelegate<NSObject>
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack;
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack;
|
||||||
|
|
||||||
|
- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager;
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didErrorWithMessage:(NSString*)errorMessage;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
// Abstracts the network connection aspect of AppRTC. The delegate will receive
|
||||||
|
// information about connection status as changes occur.
|
||||||
|
@interface APPRTCConnectionManager : NSObject
|
||||||
|
|
||||||
|
@property(nonatomic, weak) id<APPRTCConnectionManagerDelegate> delegate;
|
||||||
|
@property(nonatomic, weak) id<APPRTCLogger> logger;
|
||||||
|
|
||||||
|
- (instancetype)initWithDelegate:(id<APPRTCConnectionManagerDelegate>)delegate
|
||||||
|
logger:(id<APPRTCLogger>)logger;
|
||||||
|
- (BOOL)connectToRoomWithURL:(NSURL*)url;
|
||||||
|
- (void)disconnect;
|
||||||
|
|
||||||
|
@end
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* libjingle
|
* libjingle
|
||||||
* Copyright 2013, Google Inc.
|
* Copyright 2014, Google Inc.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
* modification, are permitted provided that the following conditions are met:
|
* modification, are permitted provided that the following conditions are met:
|
||||||
@ -25,14 +25,12 @@
|
|||||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#import "APPRTCConnectionManager.h"
|
||||||
|
|
||||||
#import <AVFoundation/AVFoundation.h>
|
#import <AVFoundation/AVFoundation.h>
|
||||||
|
#import "APPRTCAppClient.h"
|
||||||
#import "APPRTCAppDelegate.h"
|
#import "GAEChannelClient.h"
|
||||||
|
|
||||||
#import "APPRTCViewController.h"
|
|
||||||
#import "RTCEAGLVideoView.h"
|
|
||||||
#import "RTCICECandidate.h"
|
#import "RTCICECandidate.h"
|
||||||
#import "RTCICEServer.h"
|
|
||||||
#import "RTCMediaConstraints.h"
|
#import "RTCMediaConstraints.h"
|
||||||
#import "RTCMediaStream.h"
|
#import "RTCMediaStream.h"
|
||||||
#import "RTCPair.h"
|
#import "RTCPair.h"
|
||||||
@ -40,209 +38,83 @@
|
|||||||
#import "RTCPeerConnectionDelegate.h"
|
#import "RTCPeerConnectionDelegate.h"
|
||||||
#import "RTCPeerConnectionFactory.h"
|
#import "RTCPeerConnectionFactory.h"
|
||||||
#import "RTCSessionDescription.h"
|
#import "RTCSessionDescription.h"
|
||||||
|
#import "RTCSessionDescriptionDelegate.h"
|
||||||
#import "RTCStatsDelegate.h"
|
#import "RTCStatsDelegate.h"
|
||||||
#import "RTCVideoRenderer.h"
|
|
||||||
#import "RTCVideoCapturer.h"
|
#import "RTCVideoCapturer.h"
|
||||||
#import "RTCVideoTrack.h"
|
#import "RTCVideoSource.h"
|
||||||
|
|
||||||
@interface PCObserver : NSObject<RTCPeerConnectionDelegate>
|
@interface APPRTCConnectionManager ()
|
||||||
|
<APPRTCAppClientDelegate, GAEMessageHandler, RTCPeerConnectionDelegate,
|
||||||
- (id)initWithDelegate:(id<APPRTCSendMessage>)delegate;
|
RTCSessionDescriptionDelegate, RTCStatsDelegate>
|
||||||
|
|
||||||
@property(nonatomic, strong) RTCEAGLVideoView* videoView;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation PCObserver {
|
|
||||||
id<APPRTCSendMessage> _delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)initWithDelegate:(id<APPRTCSendMessage>)delegate {
|
|
||||||
if (self = [super init]) {
|
|
||||||
_delegate = delegate;
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - RTCPeerConnectionDelegate
|
|
||||||
|
|
||||||
- (void)peerConnectionOnError:(RTCPeerConnection*)peerConnection {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
NSLog(@"PCO onError.");
|
|
||||||
NSAssert(NO, @"PeerConnection failed.");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
signalingStateChanged:(RTCSignalingState)stateChanged {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
NSLog(@"PCO onSignalingStateChange: %d", stateChanged);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
addedStream:(RTCMediaStream*)stream {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
NSLog(@"PCO onAddStream.");
|
|
||||||
NSAssert([stream.audioTracks count] <= 1,
|
|
||||||
@"Expected at most 1 audio stream");
|
|
||||||
NSAssert([stream.videoTracks count] <= 1,
|
|
||||||
@"Expected at most 1 video stream");
|
|
||||||
if ([stream.videoTracks count] != 0) {
|
|
||||||
self.videoView.videoTrack = stream.videoTracks[0];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
removedStream:(RTCMediaStream*)stream {
|
|
||||||
dispatch_async(dispatch_get_main_queue(),
|
|
||||||
^(void) { NSLog(@"PCO onRemoveStream."); });
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection*)peerConnection {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
NSLog(@"PCO onRenegotiationNeeded - ignoring because AppRTC has a "
|
|
||||||
"predefined negotiation strategy");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
gotICECandidate:(RTCICECandidate*)candidate {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
NSLog(@"PCO onICECandidate.\n Mid[%@] Index[%d] Sdp[%@]",
|
|
||||||
candidate.sdpMid,
|
|
||||||
candidate.sdpMLineIndex,
|
|
||||||
candidate.sdp);
|
|
||||||
NSDictionary* json = @{
|
|
||||||
@"type" : @"candidate",
|
|
||||||
@"label" : [NSNumber numberWithInt:candidate.sdpMLineIndex],
|
|
||||||
@"id" : candidate.sdpMid,
|
|
||||||
@"candidate" : candidate.sdp
|
|
||||||
};
|
|
||||||
NSError* error;
|
|
||||||
NSData* data =
|
|
||||||
[NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
|
|
||||||
if (!error) {
|
|
||||||
[_delegate sendData:data];
|
|
||||||
} else {
|
|
||||||
NSAssert(NO,
|
|
||||||
@"Unable to serialize JSON object with error: %@",
|
|
||||||
error.localizedDescription);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
iceGatheringChanged:(RTCICEGatheringState)newState {
|
|
||||||
dispatch_async(dispatch_get_main_queue(),
|
|
||||||
^(void) { NSLog(@"PCO onIceGatheringChange. %d", newState); });
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
iceConnectionChanged:(RTCICEConnectionState)newState {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
NSLog(@"PCO onIceConnectionChange. %d", newState);
|
|
||||||
if (newState == RTCICEConnectionConnected)
|
|
||||||
[self displayLogMessage:@"ICE Connection Connected."];
|
|
||||||
NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
didOpenDataChannel:(RTCDataChannel*)dataChannel {
|
|
||||||
NSAssert(NO, @"AppRTC doesn't use DataChannels");
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Private
|
|
||||||
|
|
||||||
- (void)displayLogMessage:(NSString*)message {
|
|
||||||
[_delegate displayLogMessage:message];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface APPRTCAppDelegate () <RTCStatsDelegate>
|
|
||||||
|
|
||||||
@property(nonatomic, strong) APPRTCAppClient* client;
|
@property(nonatomic, strong) APPRTCAppClient* client;
|
||||||
@property(nonatomic, strong) PCObserver* pcObserver;
|
|
||||||
@property(nonatomic, strong) RTCPeerConnection* peerConnection;
|
@property(nonatomic, strong) RTCPeerConnection* peerConnection;
|
||||||
@property(nonatomic, strong) RTCPeerConnectionFactory* peerConnectionFactory;
|
@property(nonatomic, strong) RTCPeerConnectionFactory* peerConnectionFactory;
|
||||||
|
@property(nonatomic, strong) RTCVideoSource* videoSource;
|
||||||
@property(nonatomic, strong) NSMutableArray* queuedRemoteCandidates;
|
@property(nonatomic, strong) NSMutableArray* queuedRemoteCandidates;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation APPRTCAppDelegate {
|
@implementation APPRTCConnectionManager {
|
||||||
NSTimer* _statsTimer;
|
NSTimer* _statsTimer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - UIApplicationDelegate methods
|
- (instancetype)initWithDelegate:(id<APPRTCConnectionManagerDelegate>)delegate
|
||||||
|
logger:(id<APPRTCLogger>)logger {
|
||||||
- (BOOL)application:(UIApplication*)application
|
if (self = [super init]) {
|
||||||
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
self.delegate = delegate;
|
||||||
[RTCPeerConnectionFactory initializeSSL];
|
self.logger = logger;
|
||||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init];
|
||||||
self.viewController =
|
// TODO(tkchin): turn this into a button.
|
||||||
[[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController"
|
// Uncomment for stat logs.
|
||||||
bundle:nil];
|
// _statsTimer =
|
||||||
self.window.rootViewController = self.viewController;
|
// [NSTimer scheduledTimerWithTimeInterval:10
|
||||||
_statsTimer =
|
// target:self
|
||||||
[NSTimer scheduledTimerWithTimeInterval:10
|
// selector:@selector(didFireStatsTimer:)
|
||||||
target:self
|
// userInfo:nil
|
||||||
selector:@selector(didFireStatsTimer:)
|
// repeats:YES];
|
||||||
userInfo:nil
|
}
|
||||||
repeats:YES];
|
return self;
|
||||||
[self.window makeKeyAndVisible];
|
|
||||||
return YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationWillResignActive:(UIApplication*)application {
|
- (void)dealloc {
|
||||||
[self displayLogMessage:@"Application lost focus, connection broken."];
|
[self disconnect];
|
||||||
[self closeVideoUI];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)applicationDidEnterBackground:(UIApplication*)application {
|
- (BOOL)connectToRoomWithURL:(NSURL*)url {
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationWillEnterForeground:(UIApplication*)application {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationDidBecomeActive:(UIApplication*)application {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(UIApplication*)application {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication*)application
|
|
||||||
openURL:(NSURL*)url
|
|
||||||
sourceApplication:(NSString*)sourceApplication
|
|
||||||
annotation:(id)annotation {
|
|
||||||
if (self.client) {
|
if (self.client) {
|
||||||
|
// Already have a connection.
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
self.client = [[APPRTCAppClient alloc] initWithICEServerDelegate:self
|
self.client = [[APPRTCAppClient alloc] initWithDelegate:self
|
||||||
messageHandler:self];
|
messageHandler:self];
|
||||||
[self.client connectToRoom:url];
|
[self.client connectToRoom:url];
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)displayLogMessage:(NSString*)message {
|
- (void)disconnect {
|
||||||
NSAssert([NSThread isMainThread], @"Called off main thread!");
|
if (!self.client) {
|
||||||
NSLog(@"%@", message);
|
return;
|
||||||
[self.viewController displayText:message];
|
}
|
||||||
|
[self.client
|
||||||
|
sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
[self.peerConnection close];
|
||||||
|
self.peerConnection = nil;
|
||||||
|
self.client = nil;
|
||||||
|
self.queuedRemoteCandidates = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - RTCSendMessage method
|
#pragma mark - APPRTCAppClientDelegate
|
||||||
|
|
||||||
- (void)sendData:(NSData*)data {
|
- (void)appClient:(APPRTCAppClient*)appClient
|
||||||
[self.client sendData:data];
|
didErrorWithMessage:(NSString*)message {
|
||||||
|
[self.delegate connectionManager:self
|
||||||
|
didErrorWithMessage:message];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - ICEServerDelegate method
|
- (void)appClient:(APPRTCAppClient*)appClient
|
||||||
|
didReceiveICEServers:(NSArray*)servers {
|
||||||
- (void)onICEServers:(NSArray*)servers {
|
|
||||||
self.queuedRemoteCandidates = [NSMutableArray array];
|
self.queuedRemoteCandidates = [NSMutableArray array];
|
||||||
self.peerConnectionFactory = [[RTCPeerConnectionFactory alloc] init];
|
|
||||||
RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
|
RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
|
||||||
initWithMandatoryConstraints:
|
initWithMandatoryConstraints:
|
||||||
@[
|
@[
|
||||||
@ -256,11 +128,10 @@
|
|||||||
[[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement"
|
[[RTCPair alloc] initWithKey:@"DtlsSrtpKeyAgreement"
|
||||||
value:@"true"]
|
value:@"true"]
|
||||||
]];
|
]];
|
||||||
self.pcObserver = [[PCObserver alloc] initWithDelegate:self];
|
|
||||||
self.peerConnection =
|
self.peerConnection =
|
||||||
[self.peerConnectionFactory peerConnectionWithICEServers:servers
|
[self.peerConnectionFactory peerConnectionWithICEServers:servers
|
||||||
constraints:constraints
|
constraints:constraints
|
||||||
delegate:self.pcObserver];
|
delegate:self];
|
||||||
RTCMediaStream* lms =
|
RTCMediaStream* lms =
|
||||||
[self.peerConnectionFactory mediaStreamWithLabel:@"ARDAMS"];
|
[self.peerConnectionFactory mediaStreamWithLabel:@"ARDAMS"];
|
||||||
|
|
||||||
@ -268,7 +139,10 @@
|
|||||||
// support or emulation (http://goo.gl/rHAnC1) so don't bother
|
// support or emulation (http://goo.gl/rHAnC1) so don't bother
|
||||||
// trying to open a local stream.
|
// trying to open a local stream.
|
||||||
RTCVideoTrack* localVideoTrack;
|
RTCVideoTrack* localVideoTrack;
|
||||||
#if !TARGET_IPHONE_SIMULATOR
|
|
||||||
|
// TODO(tkchin): local video capture for OSX. See
|
||||||
|
// https://code.google.com/p/webrtc/issues/detail?id=3417.
|
||||||
|
#if !TARGET_IPHONE_SIMULATOR && TARGET_OS_IPHONE
|
||||||
NSString* cameraID = nil;
|
NSString* cameraID = nil;
|
||||||
for (AVCaptureDevice* captureDevice in
|
for (AVCaptureDevice* captureDevice in
|
||||||
[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
|
[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
|
||||||
@ -290,25 +164,23 @@
|
|||||||
if (localVideoTrack) {
|
if (localVideoTrack) {
|
||||||
[lms addVideoTrack:localVideoTrack];
|
[lms addVideoTrack:localVideoTrack];
|
||||||
}
|
}
|
||||||
self.viewController.localVideoView.videoTrack = localVideoTrack;
|
[self.delegate connectionManager:self
|
||||||
#else
|
didReceiveLocalVideoTrack:localVideoTrack];
|
||||||
self.viewController.localVideoView.hidden = YES;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
self.pcObserver.videoView = self.viewController.remoteVideoView;
|
|
||||||
[lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]];
|
[lms addAudioTrack:[self.peerConnectionFactory audioTrackWithID:@"ARDAMSa0"]];
|
||||||
[self.peerConnection addStream:lms constraints:constraints];
|
[self.peerConnection addStream:lms constraints:constraints];
|
||||||
[self displayLogMessage:@"onICEServers - added local stream."];
|
[self.logger logMessage:@"onICEServers - added local stream."];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - GAEMessageHandler methods
|
#pragma mark - GAEMessageHandler methods
|
||||||
|
|
||||||
- (void)onOpen {
|
- (void)onOpen {
|
||||||
if (!self.client.initiator) {
|
if (!self.client.initiator) {
|
||||||
[self displayLogMessage:@"Callee; waiting for remote offer"];
|
[self.logger logMessage:@"Callee; waiting for remote offer"];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[self displayLogMessage:@"GAE onOpen - create offer."];
|
[self.logger logMessage:@"GAE onOpen - create offer."];
|
||||||
RTCPair* audio =
|
RTCPair* audio =
|
||||||
[[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"];
|
[[RTCPair alloc] initWithKey:@"OfferToReceiveAudio" value:@"true"];
|
||||||
RTCPair* video =
|
RTCPair* video =
|
||||||
@ -318,14 +190,14 @@
|
|||||||
[[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory
|
[[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatory
|
||||||
optionalConstraints:nil];
|
optionalConstraints:nil];
|
||||||
[self.peerConnection createOfferWithDelegate:self constraints:constraints];
|
[self.peerConnection createOfferWithDelegate:self constraints:constraints];
|
||||||
[self displayLogMessage:@"PC - createOffer."];
|
[self.logger logMessage:@"PC - createOffer."];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)onMessage:(NSDictionary*)messageData {
|
- (void)onMessage:(NSDictionary*)messageData {
|
||||||
NSString* type = messageData[@"type"];
|
NSString* type = messageData[@"type"];
|
||||||
NSAssert(type, @"Missing type: %@", messageData);
|
NSAssert(type, @"Missing type: %@", messageData);
|
||||||
[self displayLogMessage:[NSString stringWithFormat:@"GAE onMessage type - %@",
|
[self.logger logMessage:[NSString stringWithFormat:@"GAE onMessage type - %@",
|
||||||
type]];
|
type]];
|
||||||
if ([type isEqualToString:@"candidate"]) {
|
if ([type isEqualToString:@"candidate"]) {
|
||||||
NSString* mid = messageData[@"id"];
|
NSString* mid = messageData[@"id"];
|
||||||
NSNumber* sdpLineIndex = messageData[@"label"];
|
NSNumber* sdpLineIndex = messageData[@"label"];
|
||||||
@ -344,36 +216,202 @@
|
|||||||
NSString* sdpString = messageData[@"sdp"];
|
NSString* sdpString = messageData[@"sdp"];
|
||||||
RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
|
RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
|
||||||
initWithType:type
|
initWithType:type
|
||||||
sdp:[APPRTCAppDelegate preferISAC:sdpString]];
|
sdp:[[self class] preferISAC:sdpString]];
|
||||||
[self.peerConnection setRemoteDescriptionWithDelegate:self
|
[self.peerConnection setRemoteDescriptionWithDelegate:self
|
||||||
sessionDescription:sdp];
|
sessionDescription:sdp];
|
||||||
[self displayLogMessage:@"PC - setRemoteDescription."];
|
[self.logger logMessage:@"PC - setRemoteDescription."];
|
||||||
} else if ([type isEqualToString:@"bye"]) {
|
} else if ([type isEqualToString:@"bye"]) {
|
||||||
[self closeVideoUI];
|
[self.delegate connectionManagerDidReceiveHangup:self];
|
||||||
UIAlertView* alertView =
|
|
||||||
[[UIAlertView alloc] initWithTitle:@"Remote end hung up"
|
|
||||||
message:@"dropping PeerConnection"
|
|
||||||
delegate:nil
|
|
||||||
cancelButtonTitle:@"OK"
|
|
||||||
otherButtonTitles:nil];
|
|
||||||
[alertView show];
|
|
||||||
} else {
|
} else {
|
||||||
NSAssert(NO, @"Invalid message: %@", messageData);
|
NSAssert(NO, @"Invalid message: %@", messageData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)onClose {
|
- (void)onClose {
|
||||||
[self displayLogMessage:@"GAE onClose."];
|
[self.logger logMessage:@"GAE onClose."];
|
||||||
[self closeVideoUI];
|
[self.delegate connectionManagerDidReceiveHangup:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)onError:(int)code withDescription:(NSString*)description {
|
- (void)onError:(int)code withDescription:(NSString*)description {
|
||||||
[self displayLogMessage:[NSString stringWithFormat:@"GAE onError: %d, %@",
|
NSString* message = [NSString stringWithFormat:@"GAE onError: %d, %@",
|
||||||
code, description]];
|
code, description];
|
||||||
[self closeVideoUI];
|
[self.logger logMessage:message];
|
||||||
|
[self.delegate connectionManager:self
|
||||||
|
didErrorWithMessage:message];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - RTCSessionDescriptionDelegate methods
|
#pragma mark - RTCPeerConnectionDelegate
|
||||||
|
|
||||||
|
- (void)peerConnectionOnError:(RTCPeerConnection*)peerConnection {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
NSString* message = @"PeerConnection error";
|
||||||
|
NSLog(@"%@", message);
|
||||||
|
NSAssert(NO, @"PeerConnection failed.");
|
||||||
|
[self.delegate connectionManager:self
|
||||||
|
didErrorWithMessage:message];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
signalingStateChanged:(RTCSignalingState)stateChanged {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
NSLog(@"PCO onSignalingStateChange: %d", stateChanged);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
addedStream:(RTCMediaStream*)stream {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
NSLog(@"PCO onAddStream.");
|
||||||
|
NSAssert([stream.audioTracks count] == 1 || [stream.videoTracks count] == 1,
|
||||||
|
@"Expected audio or video track");
|
||||||
|
NSAssert([stream.audioTracks count] <= 1,
|
||||||
|
@"Expected at most 1 audio stream");
|
||||||
|
NSAssert([stream.videoTracks count] <= 1,
|
||||||
|
@"Expected at most 1 video stream");
|
||||||
|
if ([stream.videoTracks count] != 0) {
|
||||||
|
[self.delegate connectionManager:self
|
||||||
|
didReceiveRemoteVideoTrack:stream.videoTracks[0]];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
removedStream:(RTCMediaStream*)stream {
|
||||||
|
dispatch_async(dispatch_get_main_queue(),
|
||||||
|
^{ NSLog(@"PCO onRemoveStream."); });
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnectionOnRenegotiationNeeded:(RTCPeerConnection*)peerConnection {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
NSLog(@"PCO onRenegotiationNeeded - ignoring because AppRTC has a "
|
||||||
|
"predefined negotiation strategy");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
gotICECandidate:(RTCICECandidate*)candidate {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
NSLog(@"PCO onICECandidate.\n Mid[%@] Index[%li] Sdp[%@]",
|
||||||
|
candidate.sdpMid,
|
||||||
|
(long)candidate.sdpMLineIndex,
|
||||||
|
candidate.sdp);
|
||||||
|
NSDictionary* json = @{
|
||||||
|
@"type" : @"candidate",
|
||||||
|
@"label" : @(candidate.sdpMLineIndex),
|
||||||
|
@"id" : candidate.sdpMid,
|
||||||
|
@"candidate" : candidate.sdp
|
||||||
|
};
|
||||||
|
NSError* error;
|
||||||
|
NSData* data =
|
||||||
|
[NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
|
||||||
|
if (!error) {
|
||||||
|
[self.client sendData:data];
|
||||||
|
} else {
|
||||||
|
NSAssert(NO,
|
||||||
|
@"Unable to serialize JSON object with error: %@",
|
||||||
|
error.localizedDescription);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
iceGatheringChanged:(RTCICEGatheringState)newState {
|
||||||
|
dispatch_async(dispatch_get_main_queue(),
|
||||||
|
^{ NSLog(@"PCO onIceGatheringChange. %d", newState); });
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
iceConnectionChanged:(RTCICEConnectionState)newState {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
NSLog(@"PCO onIceConnectionChange. %d", newState);
|
||||||
|
if (newState == RTCICEConnectionConnected)
|
||||||
|
[self.logger logMessage:@"ICE Connection Connected."];
|
||||||
|
NSAssert(newState != RTCICEConnectionFailed, @"ICE Connection failed!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
didOpenDataChannel:(RTCDataChannel*)dataChannel {
|
||||||
|
NSAssert(NO, @"AppRTC doesn't use DataChannels");
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - RTCSessionDescriptionDelegate
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
didCreateSessionDescription:(RTCSessionDescription*)origSdp
|
||||||
|
error:(NSError*)error {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if (error) {
|
||||||
|
[self.logger logMessage:@"SDP onFailure."];
|
||||||
|
NSAssert(NO, error.description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[self.logger logMessage:@"SDP onSuccess(SDP) - set local description."];
|
||||||
|
RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
|
||||||
|
initWithType:origSdp.type
|
||||||
|
sdp:[[self class] preferISAC:origSdp.description]];
|
||||||
|
[self.peerConnection setLocalDescriptionWithDelegate:self
|
||||||
|
sessionDescription:sdp];
|
||||||
|
[self.logger logMessage:@"PC setLocalDescription."];
|
||||||
|
NSDictionary* json = @{@"type" : sdp.type, @"sdp" : sdp.description};
|
||||||
|
NSError* jsonError;
|
||||||
|
NSData* data = [NSJSONSerialization dataWithJSONObject:json
|
||||||
|
options:0
|
||||||
|
error:&jsonError];
|
||||||
|
NSAssert(!jsonError, @"Error: %@", jsonError.description);
|
||||||
|
[self.client sendData:data];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
didSetSessionDescriptionWithError:(NSError*)error {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
if (error) {
|
||||||
|
[self.logger logMessage:@"SDP onFailure."];
|
||||||
|
NSAssert(NO, error.description);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[self.logger logMessage:@"SDP onSuccess() - possibly drain candidates"];
|
||||||
|
if (!self.client.initiator) {
|
||||||
|
if (self.peerConnection.remoteDescription &&
|
||||||
|
!self.peerConnection.localDescription) {
|
||||||
|
[self.logger logMessage:@"Callee, setRemoteDescription succeeded"];
|
||||||
|
RTCPair* audio = [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio"
|
||||||
|
value:@"true"];
|
||||||
|
RTCPair* video = [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo"
|
||||||
|
value:@"true"];
|
||||||
|
NSArray* mandatory = @[ audio, video ];
|
||||||
|
RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
|
||||||
|
initWithMandatoryConstraints:mandatory
|
||||||
|
optionalConstraints:nil];
|
||||||
|
[self.peerConnection createAnswerWithDelegate:self
|
||||||
|
constraints:constraints];
|
||||||
|
[self.logger logMessage:@"PC - createAnswer."];
|
||||||
|
} else {
|
||||||
|
[self.logger logMessage:@"SDP onSuccess - drain candidates"];
|
||||||
|
[self drainRemoteCandidates];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (self.peerConnection.remoteDescription) {
|
||||||
|
[self.logger logMessage:@"SDP onSuccess - drain candidates"];
|
||||||
|
[self drainRemoteCandidates];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - RTCStatsDelegate methods
|
||||||
|
|
||||||
|
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
||||||
|
didGetStats:(NSArray*)stats {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
NSString* message = [NSString stringWithFormat:@"Stats:\n %@", stats];
|
||||||
|
[self.logger logMessage:message];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
// Match |pattern| to |string| and return the first group of the first
|
// Match |pattern| to |string| and return the first group of the first
|
||||||
// match, or nil if no match was found.
|
// match, or nil if no match was found.
|
||||||
@ -438,96 +476,6 @@
|
|||||||
return [newLines componentsJoinedByString:@"\n"];
|
return [newLines componentsJoinedByString:@"\n"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
didCreateSessionDescription:(RTCSessionDescription*)origSdp
|
|
||||||
error:(NSError*)error {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
if (error) {
|
|
||||||
[self displayLogMessage:@"SDP onFailure."];
|
|
||||||
NSAssert(NO, error.description);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[self displayLogMessage:@"SDP onSuccess(SDP) - set local description."];
|
|
||||||
RTCSessionDescription* sdp = [[RTCSessionDescription alloc]
|
|
||||||
initWithType:origSdp.type
|
|
||||||
sdp:[APPRTCAppDelegate preferISAC:origSdp.description]];
|
|
||||||
[self.peerConnection setLocalDescriptionWithDelegate:self
|
|
||||||
sessionDescription:sdp];
|
|
||||||
|
|
||||||
[self displayLogMessage:@"PC setLocalDescription."];
|
|
||||||
NSDictionary* json = @{@"type" : sdp.type, @"sdp" : sdp.description};
|
|
||||||
NSError* error;
|
|
||||||
NSData* data =
|
|
||||||
[NSJSONSerialization dataWithJSONObject:json options:0 error:&error];
|
|
||||||
NSAssert(!error,
|
|
||||||
@"%@",
|
|
||||||
[NSString stringWithFormat:@"Error: %@", error.description]);
|
|
||||||
[self sendData:data];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
didSetSessionDescriptionWithError:(NSError*)error {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
if (error) {
|
|
||||||
[self displayLogMessage:@"SDP onFailure."];
|
|
||||||
NSAssert(NO, error.description);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self displayLogMessage:@"SDP onSuccess() - possibly drain candidates"];
|
|
||||||
if (!self.client.initiator) {
|
|
||||||
if (self.peerConnection.remoteDescription &&
|
|
||||||
!self.peerConnection.localDescription) {
|
|
||||||
[self displayLogMessage:@"Callee, setRemoteDescription succeeded"];
|
|
||||||
RTCPair* audio = [[RTCPair alloc] initWithKey:@"OfferToReceiveAudio"
|
|
||||||
value:@"true"];
|
|
||||||
RTCPair* video = [[RTCPair alloc] initWithKey:@"OfferToReceiveVideo"
|
|
||||||
value:@"true"];
|
|
||||||
NSArray* mandatory = @[ audio, video ];
|
|
||||||
RTCMediaConstraints* constraints = [[RTCMediaConstraints alloc]
|
|
||||||
initWithMandatoryConstraints:mandatory
|
|
||||||
optionalConstraints:nil];
|
|
||||||
[self.peerConnection createAnswerWithDelegate:self
|
|
||||||
constraints:constraints];
|
|
||||||
[self displayLogMessage:@"PC - createAnswer."];
|
|
||||||
} else {
|
|
||||||
[self displayLogMessage:@"SDP onSuccess - drain candidates"];
|
|
||||||
[self drainRemoteCandidates];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (self.peerConnection.remoteDescription) {
|
|
||||||
[self displayLogMessage:@"SDP onSuccess - drain candidates"];
|
|
||||||
[self drainRemoteCandidates];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - RTCStatsDelegate methods
|
|
||||||
|
|
||||||
- (void)peerConnection:(RTCPeerConnection*)peerConnection
|
|
||||||
didGetStats:(NSArray*)stats {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
NSString* message = [NSString stringWithFormat:@"Stats:\n %@", stats];
|
|
||||||
[self displayLogMessage:message];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - internal methods
|
|
||||||
|
|
||||||
- (void)disconnect {
|
|
||||||
[self.client
|
|
||||||
sendData:[@"{\"type\": \"bye\"}" dataUsingEncoding:NSUTF8StringEncoding]];
|
|
||||||
[self.peerConnection close];
|
|
||||||
self.peerConnection = nil;
|
|
||||||
self.pcObserver = nil;
|
|
||||||
self.client = nil;
|
|
||||||
self.videoSource = nil;
|
|
||||||
self.peerConnectionFactory = nil;
|
|
||||||
[RTCPeerConnectionFactory deinitializeSSL];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)drainRemoteCandidates {
|
- (void)drainRemoteCandidates {
|
||||||
for (RTCICECandidate* candidate in self.queuedRemoteCandidates) {
|
for (RTCICECandidate* candidate in self.queuedRemoteCandidates) {
|
||||||
[self.peerConnection addICECandidate:candidate];
|
[self.peerConnection addICECandidate:candidate];
|
||||||
@ -535,29 +483,7 @@
|
|||||||
self.queuedRemoteCandidates = nil;
|
self.queuedRemoteCandidates = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*)unHTMLifyString:(NSString*)base {
|
- (void)didFireStatsTimer:(NSTimer*)timer {
|
||||||
// TODO(hughv): Investigate why percent escapes are being added. Removing
|
|
||||||
// them isn't necessary on Android.
|
|
||||||
// convert HTML escaped characters to UTF8.
|
|
||||||
NSString* removePercent =
|
|
||||||
[base stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
// remove leading and trailing ".
|
|
||||||
NSRange range;
|
|
||||||
range.length = [removePercent length] - 2;
|
|
||||||
range.location = 1;
|
|
||||||
NSString* removeQuotes = [removePercent substringWithRange:range];
|
|
||||||
// convert \" to ".
|
|
||||||
NSString* removeEscapedQuotes =
|
|
||||||
[removeQuotes stringByReplacingOccurrencesOfString:@"\\\""
|
|
||||||
withString:@"\""];
|
|
||||||
// convert \\ to \.
|
|
||||||
NSString* removeBackslash =
|
|
||||||
[removeEscapedQuotes stringByReplacingOccurrencesOfString:@"\\\\"
|
|
||||||
withString:@"\\"];
|
|
||||||
return removeBackslash;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)didFireStatsTimer:(NSTimer *)timer {
|
|
||||||
if (self.peerConnection) {
|
if (self.peerConnection) {
|
||||||
[self.peerConnection getStatsWithDelegate:self
|
[self.peerConnection getStatsWithDelegate:self
|
||||||
mediaStreamTrack:nil
|
mediaStreamTrack:nil
|
||||||
@ -565,11 +491,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - public methods
|
|
||||||
|
|
||||||
- (void)closeVideoUI {
|
|
||||||
[self.viewController resetUI];
|
|
||||||
[self disconnect];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
@ -25,7 +25,7 @@
|
|||||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
// These methods will be called by the AppEngine chanel. The documentation
|
// These methods will be called by the AppEngine chanel. The documentation
|
||||||
// for these methods is found here. (Yes, it is a JS API.)
|
// for these methods is found here. (Yes, it is a JS API.)
|
||||||
@ -35,15 +35,15 @@
|
|||||||
- (void)onOpen;
|
- (void)onOpen;
|
||||||
- (void)onMessage:(NSDictionary*)data;
|
- (void)onMessage:(NSDictionary*)data;
|
||||||
- (void)onClose;
|
- (void)onClose;
|
||||||
- (void)onError:(int)code withDescription:(NSString *)description;
|
- (void)onError:(int)code withDescription:(NSString*)description;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
// Initialize with a token for an AppRTC data channel. This will load
|
// Initialize with a token for an AppRTC data channel. This will load
|
||||||
// ios_channel.html and use the token to establish a data channel between the
|
// ios_channel.html and use the token to establish a data channel between the
|
||||||
// application and AppEngine.
|
// application and AppEngine.
|
||||||
@interface GAEChannelClient : NSObject<UIWebViewDelegate>
|
@interface GAEChannelClient : NSObject
|
||||||
|
|
||||||
- (id)initWithToken:(NSString *)token delegate:(id<GAEMessageHandler>)delegate;
|
- (id)initWithToken:(NSString*)token delegate:(id<GAEMessageHandler>)delegate;
|
||||||
|
|
||||||
@end
|
@end
|
@ -29,10 +29,25 @@
|
|||||||
|
|
||||||
#import "RTCPeerConnectionFactory.h"
|
#import "RTCPeerConnectionFactory.h"
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface GAEChannelClient () <UIWebViewDelegate>
|
||||||
|
|
||||||
|
@property(nonatomic, strong) UIWebView* webView;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#import <WebKit/WebKit.h>
|
||||||
|
|
||||||
@interface GAEChannelClient ()
|
@interface GAEChannelClient ()
|
||||||
|
|
||||||
|
@property(nonatomic, strong) WebView* webView;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@property(nonatomic, assign) id<GAEMessageHandler> delegate;
|
@property(nonatomic, assign) id<GAEMessageHandler> delegate;
|
||||||
@property(nonatomic, strong) UIWebView* webView;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -41,47 +56,67 @@
|
|||||||
- (id)initWithToken:(NSString*)token delegate:(id<GAEMessageHandler>)delegate {
|
- (id)initWithToken:(NSString*)token delegate:(id<GAEMessageHandler>)delegate {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
_webView = [[UIWebView alloc] init];
|
_webView = [[UIWebView alloc] init];
|
||||||
_webView.delegate = self;
|
_webView.delegate = self;
|
||||||
|
#else
|
||||||
|
_webView = [[WebView alloc] init];
|
||||||
|
_webView.policyDelegate = self;
|
||||||
|
#endif
|
||||||
_delegate = delegate;
|
_delegate = delegate;
|
||||||
NSString* htmlPath =
|
NSString* htmlPath =
|
||||||
[[NSBundle mainBundle] pathForResource:@"ios_channel" ofType:@"html"];
|
[[NSBundle mainBundle] pathForResource:@"channel" ofType:@"html"];
|
||||||
NSURL* htmlUrl = [NSURL fileURLWithPath:htmlPath];
|
NSURL* htmlUrl = [NSURL fileURLWithPath:htmlPath];
|
||||||
NSString* path = [NSString
|
NSString* path = [NSString
|
||||||
stringWithFormat:@"%@?token=%@", [htmlUrl absoluteString], token];
|
stringWithFormat:@"%@?token=%@", [htmlUrl absoluteString], token];
|
||||||
|
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
[_webView
|
[_webView
|
||||||
|
#else
|
||||||
|
[[_webView mainFrame]
|
||||||
|
#endif
|
||||||
loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]];
|
loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
_webView.delegate = nil;
|
_webView.delegate = nil;
|
||||||
[_webView stopLoading];
|
[_webView stopLoading];
|
||||||
|
#else
|
||||||
|
_webView.policyDelegate = nil;
|
||||||
|
[[_webView mainFrame] stopLoading];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - UIWebViewDelegate method
|
#if TARGET_OS_IPHONE
|
||||||
|
#pragma mark - UIWebViewDelegate
|
||||||
+ (NSDictionary*)jsonStringToDictionary:(NSString*)str {
|
|
||||||
NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
NSError* error;
|
|
||||||
NSDictionary* dict =
|
|
||||||
[NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
|
||||||
NSAssert(!error, @"Invalid JSON? %@", str);
|
|
||||||
return dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)webView:(UIWebView*)webView
|
- (BOOL)webView:(UIWebView*)webView
|
||||||
shouldStartLoadWithRequest:(NSURLRequest*)request
|
shouldStartLoadWithRequest:(NSURLRequest*)request
|
||||||
navigationType:(UIWebViewNavigationType)navigationType {
|
navigationType:(UIWebViewNavigationType)navigationType {
|
||||||
|
#else
|
||||||
|
// WebPolicyDelegate is an informal delegate.
|
||||||
|
#pragma mark - WebPolicyDelegate
|
||||||
|
|
||||||
|
- (void)webView:(WebView*)webView
|
||||||
|
decidePolicyForNavigationAction:(NSDictionary*)actionInformation
|
||||||
|
request:(NSURLRequest*)request
|
||||||
|
frame:(WebFrame*)frame
|
||||||
|
decisionListener:(id<WebPolicyDecisionListener>)listener {
|
||||||
|
#endif
|
||||||
NSString* scheme = [request.URL scheme];
|
NSString* scheme = [request.URL scheme];
|
||||||
NSAssert(scheme, @"scheme is nil: %@", request);
|
NSAssert(scheme, @"scheme is nil: %@", request);
|
||||||
if (![scheme isEqualToString:@"js-frame"]) {
|
if (![scheme isEqualToString:@"js-frame"]) {
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
return YES;
|
return YES;
|
||||||
|
#else
|
||||||
|
[listener use];
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
|
||||||
NSString* queuedMessage = [webView
|
NSString* queuedMessage = [webView
|
||||||
stringByEvaluatingJavaScriptFromString:@"popQueuedMessage();"];
|
stringByEvaluatingJavaScriptFromString:@"popQueuedMessage();"];
|
||||||
NSAssert([queuedMessage length], @"Empty queued message from JS");
|
NSAssert([queuedMessage length], @"Empty queued message from JS");
|
||||||
@ -110,7 +145,23 @@
|
|||||||
NSAssert(NO, @"Invalid message sent from UIWebView: %@", queuedMessage);
|
NSAssert(NO, @"Invalid message sent from UIWebView: %@", queuedMessage);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
return NO;
|
return NO;
|
||||||
|
#else
|
||||||
|
[listener ignore];
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
|
+ (NSDictionary*)jsonStringToDictionary:(NSString*)str {
|
||||||
|
NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
NSError* error;
|
||||||
|
NSDictionary* dict =
|
||||||
|
[NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||||
|
NSAssert(!error, @"Invalid JSON? %@", str);
|
||||||
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
@ -27,34 +27,8 @@
|
|||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
#import "GAEChannelClient.h"
|
|
||||||
#import "APPRTCAppClient.h"
|
|
||||||
#import "RTCSessionDescriptionDelegate.h"
|
|
||||||
#import "RTCVideoSource.h"
|
|
||||||
// Used to send a message to an apprtc.appspot.com "room".
|
|
||||||
@protocol APPRTCSendMessage<NSObject>
|
|
||||||
|
|
||||||
- (void)sendData:(NSData*)data;
|
|
||||||
// Logging helper.
|
|
||||||
- (void)displayLogMessage:(NSString*)message;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@class APPRTCViewController;
|
|
||||||
@class RTCVideoTrack;
|
|
||||||
|
|
||||||
// The main application class of the AppRTCDemo iOS app demonstrating
|
// The main application class of the AppRTCDemo iOS app demonstrating
|
||||||
// interoperability between the Objective C implementation of PeerConnection
|
// interoperability between the Objective C implementation of PeerConnection
|
||||||
// and the apprtc.appspot.com demo webapp.
|
// and the apprtc.appspot.com demo webapp.
|
||||||
@interface APPRTCAppDelegate : UIResponder<ICEServerDelegate,
|
@interface APPRTCAppDelegate : NSObject<UIApplicationDelegate>
|
||||||
GAEMessageHandler,
|
|
||||||
APPRTCSendMessage,
|
|
||||||
RTCSessionDescriptionDelegate,
|
|
||||||
UIApplicationDelegate>
|
|
||||||
|
|
||||||
@property(strong, nonatomic) UIWindow* window;
|
|
||||||
@property(strong, nonatomic) APPRTCViewController* viewController;
|
|
||||||
@property (strong, nonatomic) RTCVideoSource* videoSource;
|
|
||||||
|
|
||||||
- (void)closeVideoUI;
|
|
||||||
|
|
||||||
@end
|
@end
|
65
talk/examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.m
Normal file
65
talk/examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.m
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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 "APPRTCAppDelegate.h"
|
||||||
|
|
||||||
|
#import "APPRTCViewController.h"
|
||||||
|
#import "RTCPeerConnectionFactory.h"
|
||||||
|
|
||||||
|
@implementation APPRTCAppDelegate {
|
||||||
|
UIWindow* _window;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - UIApplicationDelegate methods
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication*)application
|
||||||
|
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
||||||
|
[RTCPeerConnectionFactory initializeSSL];
|
||||||
|
_window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||||
|
APPRTCViewController* viewController =
|
||||||
|
[[APPRTCViewController alloc] initWithNibName:@"APPRTCViewController"
|
||||||
|
bundle:nil];
|
||||||
|
_window.rootViewController = viewController;
|
||||||
|
[_window makeKeyAndVisible];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationWillResignActive:(UIApplication*)application {
|
||||||
|
[[self appRTCViewController] applicationWillResignActive:application];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationWillTerminate:(UIApplication*)application {
|
||||||
|
[RTCPeerConnectionFactory deinitializeSSL];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
|
- (APPRTCViewController*)appRTCViewController {
|
||||||
|
return (APPRTCViewController*)_window.rootViewController;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -27,8 +27,6 @@
|
|||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@class RTCEAGLVideoView;
|
|
||||||
|
|
||||||
// The view controller that is displayed when AppRTCDemo is loaded.
|
// The view controller that is displayed when AppRTCDemo is loaded.
|
||||||
@interface APPRTCViewController : UIViewController<UITextFieldDelegate>
|
@interface APPRTCViewController : UIViewController<UITextFieldDelegate>
|
||||||
|
|
||||||
@ -37,10 +35,6 @@
|
|||||||
@property(weak, nonatomic) IBOutlet UITextView* logView;
|
@property(weak, nonatomic) IBOutlet UITextView* logView;
|
||||||
@property(weak, nonatomic) IBOutlet UIView* blackView;
|
@property(weak, nonatomic) IBOutlet UIView* blackView;
|
||||||
|
|
||||||
@property(nonatomic, strong) RTCEAGLVideoView* localVideoView;
|
- (void)applicationWillResignActive:(UIApplication*)application;
|
||||||
@property(nonatomic, strong) RTCEAGLVideoView* remoteVideoView;
|
|
||||||
|
|
||||||
- (void)displayText:(NSString*)text;
|
|
||||||
- (void)resetUI;
|
|
||||||
|
|
||||||
@end
|
@end
|
231
talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m
Normal file
231
talk/examples/objc/AppRTCDemo/ios/APPRTCViewController.m
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||||
|
#error "This file requires ARC support."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#import "APPRTCViewController.h"
|
||||||
|
|
||||||
|
#import <AVFoundation/AVFoundation.h>
|
||||||
|
#import "APPRTCConnectionManager.h"
|
||||||
|
#import "RTCEAGLVideoView.h"
|
||||||
|
|
||||||
|
// Padding space for local video view with its parent.
|
||||||
|
static CGFloat const kLocalViewPadding = 20;
|
||||||
|
|
||||||
|
@interface APPRTCViewController ()
|
||||||
|
<APPRTCConnectionManagerDelegate, APPRTCLogger, RTCEAGLVideoViewDelegate>
|
||||||
|
@property(nonatomic, assign) UIInterfaceOrientation statusBarOrientation;
|
||||||
|
@property(nonatomic, strong) RTCEAGLVideoView* localVideoView;
|
||||||
|
@property(nonatomic, strong) RTCEAGLVideoView* remoteVideoView;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation APPRTCViewController {
|
||||||
|
APPRTCConnectionManager* _connectionManager;
|
||||||
|
CGSize _localVideoSize;
|
||||||
|
CGSize _remoteVideoSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithNibName:(NSString*)nibName
|
||||||
|
bundle:(NSBundle*)bundle {
|
||||||
|
if (self = [super initWithNibName:nibName bundle:bundle]) {
|
||||||
|
_connectionManager =
|
||||||
|
[[APPRTCConnectionManager alloc] initWithDelegate:self
|
||||||
|
logger:self];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewDidLoad {
|
||||||
|
[super viewDidLoad];
|
||||||
|
self.statusBarOrientation =
|
||||||
|
[UIApplication sharedApplication].statusBarOrientation;
|
||||||
|
self.roomInput.delegate = self;
|
||||||
|
[self.roomInput becomeFirstResponder];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewDidLayoutSubviews {
|
||||||
|
if (self.statusBarOrientation !=
|
||||||
|
[UIApplication sharedApplication].statusBarOrientation) {
|
||||||
|
self.statusBarOrientation =
|
||||||
|
[UIApplication sharedApplication].statusBarOrientation;
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
postNotificationName:@"StatusBarOrientationDidChange"
|
||||||
|
object:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applicationWillResignActive:(UIApplication*)application {
|
||||||
|
[self logMessage:@"Application lost focus, connection broken."];
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - APPRTCConnectionManagerDelegate
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack {
|
||||||
|
self.localVideoView.hidden = NO;
|
||||||
|
self.localVideoView.videoTrack = localVideoTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack {
|
||||||
|
self.remoteVideoView.videoTrack = remoteVideoTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
|
||||||
|
[self showAlertWithMessage:@"Remote hung up."];
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didErrorWithMessage:(NSString*)message {
|
||||||
|
[self showAlertWithMessage:message];
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - APPRTCLogger
|
||||||
|
|
||||||
|
- (void)logMessage:(NSString*)message {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
NSString* output =
|
||||||
|
[NSString stringWithFormat:@"%@\n%@", self.logView.text, message];
|
||||||
|
self.logView.text = output;
|
||||||
|
[self.logView
|
||||||
|
scrollRangeToVisible:NSMakeRange([self.logView.text length], 0)];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - RTCEAGLVideoViewDelegate
|
||||||
|
|
||||||
|
- (void)videoView:(RTCEAGLVideoView*)videoView
|
||||||
|
didChangeVideoSize:(CGSize)size {
|
||||||
|
if (videoView == self.localVideoView) {
|
||||||
|
_localVideoSize = size;
|
||||||
|
} else if (videoView == self.remoteVideoView) {
|
||||||
|
_remoteVideoSize = size;
|
||||||
|
} else {
|
||||||
|
NSParameterAssert(NO);
|
||||||
|
}
|
||||||
|
[self updateVideoViewLayout];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - UITextFieldDelegate
|
||||||
|
|
||||||
|
- (void)textFieldDidEndEditing:(UITextField*)textField {
|
||||||
|
NSString* room = textField.text;
|
||||||
|
if ([room length] == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
textField.hidden = YES;
|
||||||
|
self.instructionsView.hidden = YES;
|
||||||
|
self.logView.hidden = NO;
|
||||||
|
NSString* url =
|
||||||
|
[NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", room];
|
||||||
|
[_connectionManager connectToRoomWithURL:[NSURL URLWithString:url]];
|
||||||
|
[self setupCaptureSession];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
|
||||||
|
// There is no other control that can take focus, so manually resign focus
|
||||||
|
// when return (Join) is pressed to trigger |textFieldDidEndEditing|.
|
||||||
|
[textField resignFirstResponder];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
|
- (void)disconnect {
|
||||||
|
[self resetUI];
|
||||||
|
[_connectionManager disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showAlertWithMessage:(NSString*)message {
|
||||||
|
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:nil
|
||||||
|
message:message
|
||||||
|
delegate:nil
|
||||||
|
cancelButtonTitle:@"OK"
|
||||||
|
otherButtonTitles:nil];
|
||||||
|
[alertView show];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)resetUI {
|
||||||
|
[self.roomInput resignFirstResponder];
|
||||||
|
self.roomInput.text = nil;
|
||||||
|
self.roomInput.hidden = NO;
|
||||||
|
self.instructionsView.hidden = NO;
|
||||||
|
self.logView.hidden = YES;
|
||||||
|
self.logView.text = nil;
|
||||||
|
self.blackView.hidden = YES;
|
||||||
|
[self.remoteVideoView removeFromSuperview];
|
||||||
|
self.remoteVideoView = nil;
|
||||||
|
[self.localVideoView removeFromSuperview];
|
||||||
|
self.localVideoView = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setupCaptureSession {
|
||||||
|
self.blackView.hidden = NO;
|
||||||
|
self.remoteVideoView =
|
||||||
|
[[RTCEAGLVideoView alloc] initWithFrame:self.blackView.bounds];
|
||||||
|
self.remoteVideoView.delegate = self;
|
||||||
|
self.remoteVideoView.transform = CGAffineTransformMakeScale(-1, 1);
|
||||||
|
[self.blackView addSubview:self.remoteVideoView];
|
||||||
|
|
||||||
|
self.localVideoView =
|
||||||
|
[[RTCEAGLVideoView alloc] initWithFrame:self.blackView.bounds];
|
||||||
|
self.localVideoView.delegate = self;
|
||||||
|
[self.blackView addSubview:self.localVideoView];
|
||||||
|
[self updateVideoViewLayout];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateVideoViewLayout {
|
||||||
|
// TODO(tkchin): handle rotation.
|
||||||
|
CGSize defaultAspectRatio = CGSizeMake(4, 3);
|
||||||
|
CGSize localAspectRatio = CGSizeEqualToSize(_localVideoSize, CGSizeZero) ?
|
||||||
|
defaultAspectRatio : _localVideoSize;
|
||||||
|
CGSize remoteAspectRatio = CGSizeEqualToSize(_remoteVideoSize, CGSizeZero) ?
|
||||||
|
defaultAspectRatio : _remoteVideoSize;
|
||||||
|
|
||||||
|
CGRect remoteVideoFrame =
|
||||||
|
AVMakeRectWithAspectRatioInsideRect(remoteAspectRatio,
|
||||||
|
self.blackView.bounds);
|
||||||
|
self.remoteVideoView.frame = remoteVideoFrame;
|
||||||
|
|
||||||
|
CGRect localVideoFrame =
|
||||||
|
AVMakeRectWithAspectRatioInsideRect(localAspectRatio,
|
||||||
|
self.blackView.bounds);
|
||||||
|
localVideoFrame.size.width = localVideoFrame.size.width / 3;
|
||||||
|
localVideoFrame.size.height = localVideoFrame.size.height / 3;
|
||||||
|
localVideoFrame.origin.x = CGRectGetMaxX(self.blackView.bounds)
|
||||||
|
- localVideoFrame.size.width - kLocalViewPadding;
|
||||||
|
localVideoFrame.origin.y = CGRectGetMaxY(self.blackView.bounds)
|
||||||
|
- localVideoFrame.size.height - kLocalViewPadding;
|
||||||
|
self.localVideoView.frame = localVideoFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
@ -38,19 +38,6 @@
|
|||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>com.google.apprtcdemo</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>apprtc</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1.0</string>
|
<string>1.0</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
@ -52,7 +52,7 @@
|
|||||||
<bool key="IBUIClipsSubviews">YES</bool>
|
<bool key="IBUIClipsSubviews">YES</bool>
|
||||||
<bool key="IBUIUserInteractionEnabled">NO</bool>
|
<bool key="IBUIUserInteractionEnabled">NO</bool>
|
||||||
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
|
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
|
||||||
<string key="IBUIText">Use Safari and open a URL with a scheme of apprtc to load the test app and connect. i.e. apprtc://apprtc.appspot.com/?r=12345678 Or just enter the room below to connect to apprtc.</string>
|
<string key="IBUIText">Enter the room below to connect to apprtc.</string>
|
||||||
<object class="IBUITextInputTraits" key="IBUITextInputTraits">
|
<object class="IBUITextInputTraits" key="IBUITextInputTraits">
|
||||||
<int key="IBUIAutocapitalizationType">2</int>
|
<int key="IBUIAutocapitalizationType">2</int>
|
||||||
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
|
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
|
31
talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.h
Normal file
31
talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2014, 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 <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface APPRTCAppDelegate : NSObject<NSApplicationDelegate>
|
||||||
|
@end
|
77
talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.m
Normal file
77
talk/examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.m
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2014, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(__has_feature) || !__has_feature(objc_arc)
|
||||||
|
#error "This file requires ARC support."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#import "APPRTCAppDelegate.h"
|
||||||
|
|
||||||
|
#import "APPRTCViewController.h"
|
||||||
|
#import "RTCPeerConnectionFactory.h"
|
||||||
|
|
||||||
|
@interface APPRTCAppDelegate () <NSWindowDelegate>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation APPRTCAppDelegate {
|
||||||
|
APPRTCViewController* _viewController;
|
||||||
|
NSWindow* _window;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSApplicationDelegate
|
||||||
|
|
||||||
|
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
|
||||||
|
[RTCPeerConnectionFactory initializeSSL];
|
||||||
|
NSScreen* screen = [NSScreen mainScreen];
|
||||||
|
NSRect visibleRect = [screen visibleFrame];
|
||||||
|
NSRect windowRect = NSMakeRect(NSMidX(visibleRect),
|
||||||
|
NSMidY(visibleRect),
|
||||||
|
1320,
|
||||||
|
1140);
|
||||||
|
NSUInteger styleMask = NSTitledWindowMask | NSClosableWindowMask;
|
||||||
|
_window = [[NSWindow alloc] initWithContentRect:windowRect
|
||||||
|
styleMask:styleMask
|
||||||
|
backing:NSBackingStoreBuffered
|
||||||
|
defer:NO];
|
||||||
|
_window.delegate = self;
|
||||||
|
[_window makeKeyAndOrderFront:self];
|
||||||
|
[_window makeMainWindow];
|
||||||
|
_viewController = [[APPRTCViewController alloc] initWithNibName:nil
|
||||||
|
bundle:nil];
|
||||||
|
[_window setContentView:[_viewController view]];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSWindow
|
||||||
|
|
||||||
|
- (void)windowWillClose:(NSNotification*)notification {
|
||||||
|
[_viewController windowWillClose:notification];
|
||||||
|
[RTCPeerConnectionFactory deinitializeSSL];
|
||||||
|
[NSApp terminate:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
34
talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.h
Normal file
34
talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2014, 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 <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface APPRTCViewController : NSViewController
|
||||||
|
|
||||||
|
- (void)windowWillClose:(NSNotification*)notification;
|
||||||
|
|
||||||
|
@end
|
312
talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
Normal file
312
talk/examples/objc/AppRTCDemo/mac/APPRTCViewController.m
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2014, 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 "APPRTCViewController.h"
|
||||||
|
|
||||||
|
#import <AVFoundation/AVFoundation.h>
|
||||||
|
#import "APPRTCConnectionManager.h"
|
||||||
|
#import "RTCNSGLVideoView.h"
|
||||||
|
|
||||||
|
static NSUInteger const kContentWidth = 1280;
|
||||||
|
static NSUInteger const kContentHeight = 720;
|
||||||
|
static NSUInteger const kRoomFieldWidth = 80;
|
||||||
|
static NSUInteger const kLogViewHeight = 280;
|
||||||
|
|
||||||
|
@class APPRTCMainView;
|
||||||
|
@protocol APPRTCMainViewDelegate
|
||||||
|
|
||||||
|
- (void)appRTCMainView:(APPRTCMainView*)mainView
|
||||||
|
didEnterRoomId:(NSString*)roomId;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface APPRTCMainView : NSView
|
||||||
|
|
||||||
|
@property(nonatomic, weak) id<APPRTCMainViewDelegate> delegate;
|
||||||
|
@property(nonatomic, readonly) RTCNSGLVideoView* localVideoView;
|
||||||
|
@property(nonatomic, readonly) RTCNSGLVideoView* remoteVideoView;
|
||||||
|
|
||||||
|
- (void)displayLogMessage:(NSString*)message;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface APPRTCMainView () <NSTextFieldDelegate, RTCNSGLVideoViewDelegate>
|
||||||
|
@end
|
||||||
|
@implementation APPRTCMainView {
|
||||||
|
NSScrollView* _scrollView;
|
||||||
|
NSTextField* _roomLabel;
|
||||||
|
NSTextField* _roomField;
|
||||||
|
NSTextView* _logView;
|
||||||
|
RTCNSGLVideoView* _localVideoView;
|
||||||
|
RTCNSGLVideoView* _remoteVideoView;
|
||||||
|
CGSize _localVideoSize;
|
||||||
|
CGSize _remoteVideoSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (BOOL)requiresConstraintBasedLayout {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(NSRect)frame {
|
||||||
|
if (self = [super initWithFrame:frame]) {
|
||||||
|
[self setupViews];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)updateConstraints {
|
||||||
|
NSParameterAssert(
|
||||||
|
_roomField != nil && _scrollView != nil && _remoteVideoView != nil);
|
||||||
|
[self removeConstraints:[self constraints]];
|
||||||
|
NSDictionary* viewsDictionary =
|
||||||
|
NSDictionaryOfVariableBindings(_roomLabel,
|
||||||
|
_roomField,
|
||||||
|
_scrollView,
|
||||||
|
_remoteVideoView);
|
||||||
|
|
||||||
|
NSSize remoteViewSize = [self remoteVideoViewSize];
|
||||||
|
NSDictionary* metrics = @{
|
||||||
|
@"kLogViewHeight" : @(kLogViewHeight),
|
||||||
|
@"kRoomFieldWidth" : @(kRoomFieldWidth),
|
||||||
|
@"remoteViewWidth" : @(remoteViewSize.width),
|
||||||
|
@"remoteViewHeight" : @(remoteViewSize.height),
|
||||||
|
};
|
||||||
|
// Declare this separately to avoid compiler warning about splitting string
|
||||||
|
// within an NSArray expression.
|
||||||
|
NSString* verticalConstraint =
|
||||||
|
@"V:|-[_roomLabel]-[_roomField]-[_scrollView(kLogViewHeight)]"
|
||||||
|
"-[_remoteVideoView(remoteViewHeight)]-|";
|
||||||
|
NSArray* constraintFormats = @[
|
||||||
|
verticalConstraint,
|
||||||
|
@"|-[_roomLabel]",
|
||||||
|
@"|-[_roomField(kRoomFieldWidth)]",
|
||||||
|
@"|-[_scrollView(remoteViewWidth)]-|",
|
||||||
|
@"|-[_remoteVideoView(remoteViewWidth)]-|",
|
||||||
|
];
|
||||||
|
for (NSString* constraintFormat in constraintFormats) {
|
||||||
|
NSArray* constraints =
|
||||||
|
[NSLayoutConstraint constraintsWithVisualFormat:constraintFormat
|
||||||
|
options:0
|
||||||
|
metrics:metrics
|
||||||
|
views:viewsDictionary];
|
||||||
|
for (NSLayoutConstraint* constraint in constraints) {
|
||||||
|
[self addConstraint:constraint];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[super updateConstraints];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)displayLogMessage:(NSString*)message {
|
||||||
|
_logView.string =
|
||||||
|
[NSString stringWithFormat:@"%@%@\n", _logView.string, message];
|
||||||
|
NSRange range = NSMakeRange([_logView.string length], 0);
|
||||||
|
[_logView scrollRangeToVisible:range];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSControl delegate
|
||||||
|
|
||||||
|
- (void)controlTextDidEndEditing:(NSNotification*)notification {
|
||||||
|
NSDictionary* userInfo = [notification userInfo];
|
||||||
|
NSInteger textMovement = [userInfo[@"NSTextMovement"] intValue];
|
||||||
|
if (textMovement == NSReturnTextMovement) {
|
||||||
|
[self.delegate appRTCMainView:self didEnterRoomId:_roomField.stringValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - RTCNSGLVideoViewDelegate
|
||||||
|
|
||||||
|
- (void)videoView:(RTCNSGLVideoView*)videoView
|
||||||
|
didChangeVideoSize:(NSSize)size {
|
||||||
|
if (videoView == _remoteVideoView) {
|
||||||
|
_remoteVideoSize = size;
|
||||||
|
} else if (videoView == _localVideoView) {
|
||||||
|
_localVideoSize = size;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[self setNeedsUpdateConstraints:YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
|
- (void)setupViews {
|
||||||
|
NSParameterAssert([[self subviews] count] == 0);
|
||||||
|
|
||||||
|
_roomLabel = [[NSTextField alloc] initWithFrame:NSZeroRect];
|
||||||
|
[_roomLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||||
|
[_roomLabel setBezeled:NO];
|
||||||
|
[_roomLabel setDrawsBackground:NO];
|
||||||
|
[_roomLabel setEditable:NO];
|
||||||
|
[_roomLabel setStringValue:@"Enter AppRTC room id:"];
|
||||||
|
[self addSubview:_roomLabel];
|
||||||
|
|
||||||
|
_roomField = [[NSTextField alloc] initWithFrame:NSZeroRect];
|
||||||
|
[_roomField setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||||
|
[self addSubview:_roomField];
|
||||||
|
[_roomField setEditable:YES];
|
||||||
|
[_roomField setDelegate:self];
|
||||||
|
|
||||||
|
_logView = [[NSTextView alloc] initWithFrame:NSZeroRect];
|
||||||
|
[_logView setMinSize:NSMakeSize(0, kLogViewHeight)];
|
||||||
|
[_logView setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
|
||||||
|
[_logView setVerticallyResizable:YES];
|
||||||
|
[_logView setAutoresizingMask:NSViewWidthSizable];
|
||||||
|
NSTextContainer* textContainer = [_logView textContainer];
|
||||||
|
NSSize containerSize = NSMakeSize(kContentWidth, FLT_MAX);
|
||||||
|
[textContainer setContainerSize:containerSize];
|
||||||
|
[textContainer setWidthTracksTextView:YES];
|
||||||
|
[_logView setEditable:NO];
|
||||||
|
|
||||||
|
_scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
|
||||||
|
[_scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||||
|
[_scrollView setHasVerticalScroller:YES];
|
||||||
|
[_scrollView setDocumentView:_logView];
|
||||||
|
[self addSubview:_scrollView];
|
||||||
|
|
||||||
|
NSOpenGLPixelFormatAttribute attributes[] = {
|
||||||
|
NSOpenGLPFADoubleBuffer,
|
||||||
|
NSOpenGLPFADepthSize, 24,
|
||||||
|
NSOpenGLPFAOpenGLProfile,
|
||||||
|
NSOpenGLProfileVersion3_2Core,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
NSOpenGLPixelFormat* pixelFormat =
|
||||||
|
[[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
|
||||||
|
_remoteVideoView = [[RTCNSGLVideoView alloc] initWithFrame:NSZeroRect
|
||||||
|
pixelFormat:pixelFormat];
|
||||||
|
[_remoteVideoView setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||||
|
_remoteVideoView.delegate = self;
|
||||||
|
[self addSubview:_remoteVideoView];
|
||||||
|
|
||||||
|
// TODO(tkchin): create local video view.
|
||||||
|
// https://code.google.com/p/webrtc/issues/detail?id=3417.
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSSize)remoteVideoViewSize {
|
||||||
|
if (_remoteVideoSize.width > 0 && _remoteVideoSize.height > 0) {
|
||||||
|
return _remoteVideoSize;
|
||||||
|
} else {
|
||||||
|
return NSMakeSize(kContentWidth, kContentHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSSize)localVideoViewSize {
|
||||||
|
return NSZeroSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface APPRTCViewController ()
|
||||||
|
<APPRTCConnectionManagerDelegate, APPRTCMainViewDelegate, APPRTCLogger>
|
||||||
|
@property(nonatomic, readonly) APPRTCMainView* mainView;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation APPRTCViewController {
|
||||||
|
APPRTCConnectionManager* _connectionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithNibName:(NSString*)nibName
|
||||||
|
bundle:(NSBundle*)bundle {
|
||||||
|
if (self = [super initWithNibName:nibName bundle:bundle]) {
|
||||||
|
_connectionManager =
|
||||||
|
[[APPRTCConnectionManager alloc] initWithDelegate:self
|
||||||
|
logger:self];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)loadView {
|
||||||
|
APPRTCMainView* view = [[APPRTCMainView alloc] initWithFrame:NSZeroRect];
|
||||||
|
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||||
|
view.delegate = self;
|
||||||
|
self.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)windowWillClose:(NSNotification*)notification {
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - APPRTCConnectionManagerDelegate
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didReceiveLocalVideoTrack:(RTCVideoTrack*)localVideoTrack {
|
||||||
|
self.mainView.localVideoView.videoTrack = localVideoTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didReceiveRemoteVideoTrack:(RTCVideoTrack*)remoteVideoTrack {
|
||||||
|
self.mainView.remoteVideoView.videoTrack = remoteVideoTrack;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectionManagerDidReceiveHangup:(APPRTCConnectionManager*)manager {
|
||||||
|
[self showAlertWithMessage:@"Remote closed connection"];
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectionManager:(APPRTCConnectionManager*)manager
|
||||||
|
didErrorWithMessage:(NSString*)message {
|
||||||
|
[self showAlertWithMessage:message];
|
||||||
|
[self disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - APPRTCLogger
|
||||||
|
|
||||||
|
- (void)logMessage:(NSString*)message {
|
||||||
|
[self.mainView displayLogMessage:message];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - APPRTCMainViewDelegate
|
||||||
|
|
||||||
|
- (void)appRTCMainView:(APPRTCMainView*)mainView
|
||||||
|
didEnterRoomId:(NSString*)roomId {
|
||||||
|
NSString* urlString =
|
||||||
|
[NSString stringWithFormat:@"https://apprtc.appspot.com/?r=%@", roomId];
|
||||||
|
[_connectionManager connectToRoomWithURL:[NSURL URLWithString:urlString]];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Private
|
||||||
|
|
||||||
|
- (APPRTCMainView*)mainView {
|
||||||
|
return (APPRTCMainView*)self.view;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)showAlertWithMessage:(NSString*)message {
|
||||||
|
NSAlert* alert = [[NSAlert alloc] init];
|
||||||
|
[alert setMessageText:message];
|
||||||
|
[alert runModal];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)disconnect {
|
||||||
|
self.mainView.remoteVideoView.videoTrack = nil;
|
||||||
|
[_connectionManager disconnect];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
29
talk/examples/objc/AppRTCDemo/mac/Info.plist
Normal file
29
talk/examples/objc/AppRTCDemo/mac/Info.plist
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple/DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.Google.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>${PRODUCT_NAME}</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||||
|
<key>NSPrincipalClass</key>
|
||||||
|
<string>NSApplication</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
39
talk/examples/objc/AppRTCDemo/mac/main.m
Normal file
39
talk/examples/objc/AppRTCDemo/mac/main.m
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2014, 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 <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
#import "APPRTCAppDelegate.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
@autoreleasepool {
|
||||||
|
[NSApplication sharedApplication];
|
||||||
|
APPRTCAppDelegate* delegate = [[APPRTCAppDelegate alloc] init];
|
||||||
|
[NSApp setDelegate:delegate];
|
||||||
|
[NSApp run];
|
||||||
|
}
|
||||||
|
}
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
3
talk/examples/objc/README
Normal file
3
talk/examples/objc/README
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
This directory contains sample iOS and mac clients for http://apprtc.appspot.com
|
||||||
|
|
||||||
|
See ../../app/webrtc/objc/README for information on how to use it.
|
@ -189,6 +189,7 @@
|
|||||||
'app/webrtc/objc/RTCMediaStream.mm',
|
'app/webrtc/objc/RTCMediaStream.mm',
|
||||||
'app/webrtc/objc/RTCMediaStreamTrack+Internal.h',
|
'app/webrtc/objc/RTCMediaStreamTrack+Internal.h',
|
||||||
'app/webrtc/objc/RTCMediaStreamTrack.mm',
|
'app/webrtc/objc/RTCMediaStreamTrack.mm',
|
||||||
|
'app/webrtc/objc/RTCOpenGLVideoRenderer.mm',
|
||||||
'app/webrtc/objc/RTCPair.m',
|
'app/webrtc/objc/RTCPair.m',
|
||||||
'app/webrtc/objc/RTCPeerConnection+Internal.h',
|
'app/webrtc/objc/RTCPeerConnection+Internal.h',
|
||||||
'app/webrtc/objc/RTCPeerConnection.mm',
|
'app/webrtc/objc/RTCPeerConnection.mm',
|
||||||
@ -217,6 +218,7 @@
|
|||||||
'app/webrtc/objc/public/RTCMediaSource.h',
|
'app/webrtc/objc/public/RTCMediaSource.h',
|
||||||
'app/webrtc/objc/public/RTCMediaStream.h',
|
'app/webrtc/objc/public/RTCMediaStream.h',
|
||||||
'app/webrtc/objc/public/RTCMediaStreamTrack.h',
|
'app/webrtc/objc/public/RTCMediaStreamTrack.h',
|
||||||
|
'app/webrtc/objc/public/RTCOpenGLVideoRenderer.h',
|
||||||
'app/webrtc/objc/public/RTCPair.h',
|
'app/webrtc/objc/public/RTCPair.h',
|
||||||
'app/webrtc/objc/public/RTCPeerConnection.h',
|
'app/webrtc/objc/public/RTCPeerConnection.h',
|
||||||
'app/webrtc/objc/public/RTCPeerConnectionDelegate.h',
|
'app/webrtc/objc/public/RTCPeerConnectionDelegate.h',
|
||||||
@ -255,10 +257,8 @@
|
|||||||
'conditions': [
|
'conditions': [
|
||||||
['OS=="ios"', {
|
['OS=="ios"', {
|
||||||
'sources': [
|
'sources': [
|
||||||
'app/webrtc/objc/RTCEAGLVideoRenderer.mm',
|
|
||||||
'app/webrtc/objc/RTCEAGLVideoView+Internal.h',
|
'app/webrtc/objc/RTCEAGLVideoView+Internal.h',
|
||||||
'app/webrtc/objc/RTCEAGLVideoView.m',
|
'app/webrtc/objc/RTCEAGLVideoView.m',
|
||||||
'app/webrtc/objc/public/RTCEAGLVideoRenderer.h',
|
|
||||||
'app/webrtc/objc/public/RTCEAGLVideoView.h',
|
'app/webrtc/objc/public/RTCEAGLVideoView.h',
|
||||||
],
|
],
|
||||||
'link_settings': {
|
'link_settings': {
|
||||||
@ -271,11 +271,22 @@
|
|||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
['OS=="mac"', {
|
['OS=="mac"', {
|
||||||
|
'sources': [
|
||||||
|
'app/webrtc/objc/RTCNSGLVideoView.m',
|
||||||
|
'app/webrtc/objc/public/RTCNSGLVideoView.h',
|
||||||
|
],
|
||||||
'xcode_settings': {
|
'xcode_settings': {
|
||||||
# Need to build against 10.7 framework for full ARC support
|
# Need to build against 10.7 framework for full ARC support
|
||||||
# on OSX.
|
# on OSX.
|
||||||
'MACOSX_DEPLOYMENT_TARGET' : '10.7',
|
'MACOSX_DEPLOYMENT_TARGET' : '10.7',
|
||||||
},
|
},
|
||||||
|
'link_settings': {
|
||||||
|
'xcode_settings': {
|
||||||
|
'OTHER_LDFLAGS': [
|
||||||
|
'-framework Cocoa',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
}, # target libjingle_peerconnection_objc
|
}, # target libjingle_peerconnection_objc
|
||||||
|
@ -218,7 +218,7 @@
|
|||||||
], # targets
|
], # targets
|
||||||
}], # OS=="linux" or OS=="win"
|
}], # OS=="linux" or OS=="win"
|
||||||
|
|
||||||
['OS=="ios"', {
|
['OS=="ios" or (OS=="mac" and mac_sdk>="10.8")', {
|
||||||
'targets': [
|
'targets': [
|
||||||
{
|
{
|
||||||
'target_name': 'AppRTCDemo',
|
'target_name': 'AppRTCDemo',
|
||||||
@ -226,40 +226,71 @@
|
|||||||
'product_name': 'AppRTCDemo',
|
'product_name': 'AppRTCDemo',
|
||||||
'mac_bundle': 1,
|
'mac_bundle': 1,
|
||||||
'mac_bundle_resources': [
|
'mac_bundle_resources': [
|
||||||
'examples/ios/AppRTCDemo/ResourceRules.plist',
|
'examples/objc/AppRTCDemo/channel.html',
|
||||||
'examples/ios/AppRTCDemo/en.lproj/APPRTCViewController.xib',
|
|
||||||
'examples/ios/AppRTCDemo/ios_channel.html',
|
|
||||||
'examples/ios/Icon.png',
|
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'libjingle.gyp:libjingle_peerconnection_objc',
|
'libjingle.gyp:libjingle_peerconnection_objc',
|
||||||
],
|
],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
|
['OS=="ios"', {
|
||||||
|
'mac_bundle_resources': [
|
||||||
|
'examples/objc/AppRTCDemo/ios/ResourceRules.plist',
|
||||||
|
'examples/objc/AppRTCDemo/ios/en.lproj/APPRTCViewController.xib',
|
||||||
|
'examples/objc/Icon.png',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.h',
|
||||||
|
'examples/objc/AppRTCDemo/ios/APPRTCAppDelegate.m',
|
||||||
|
'examples/objc/AppRTCDemo/ios/APPRTCViewController.h',
|
||||||
|
'examples/objc/AppRTCDemo/ios/APPRTCViewController.m',
|
||||||
|
'examples/objc/AppRTCDemo/ios/AppRTCDemo-Prefix.pch',
|
||||||
|
'examples/objc/AppRTCDemo/ios/main.m',
|
||||||
|
],
|
||||||
|
'xcode_settings': {
|
||||||
|
'INFOPLIST_FILE': 'examples/objc/AppRTCDemo/ios/Info.plist',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
['OS=="mac"', {
|
||||||
|
'sources': [
|
||||||
|
'examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.h',
|
||||||
|
'examples/objc/AppRTCDemo/mac/APPRTCAppDelegate.m',
|
||||||
|
'examples/objc/AppRTCDemo/mac/APPRTCViewController.h',
|
||||||
|
'examples/objc/AppRTCDemo/mac/APPRTCViewController.m',
|
||||||
|
'examples/objc/AppRTCDemo/mac/main.m',
|
||||||
|
],
|
||||||
|
'xcode_settings': {
|
||||||
|
'CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS': 'NO',
|
||||||
|
'INFOPLIST_FILE': 'examples/objc/AppRTCDemo/mac/Info.plist',
|
||||||
|
'MACOSX_DEPLOYMENT_TARGET' : '10.8',
|
||||||
|
'OTHER_LDFLAGS': [
|
||||||
|
'-framework AVFoundation',
|
||||||
|
'-framework WebKit',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}],
|
||||||
['target_arch=="ia32"', {
|
['target_arch=="ia32"', {
|
||||||
'dependencies' : [
|
'dependencies' : [
|
||||||
'<(DEPTH)/testing/iossim/iossim.gyp:iossim#host',
|
'<(DEPTH)/testing/iossim/iossim.gyp:iossim#host',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'examples/objc/APPRTCDemo',
|
||||||
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
'examples/ios/AppRTCDemo/APPRTCAppClient.h',
|
'examples/objc/AppRTCDemo/APPRTCAppClient.h',
|
||||||
'examples/ios/AppRTCDemo/APPRTCAppClient.m',
|
'examples/objc/AppRTCDemo/APPRTCAppClient.m',
|
||||||
'examples/ios/AppRTCDemo/APPRTCAppDelegate.h',
|
'examples/objc/AppRTCDemo/APPRTCConnectionManager.h',
|
||||||
'examples/ios/AppRTCDemo/APPRTCAppDelegate.m',
|
'examples/objc/AppRTCDemo/APPRTCConnectionManager.m',
|
||||||
'examples/ios/AppRTCDemo/APPRTCViewController.h',
|
'examples/objc/AppRTCDemo/GAEChannelClient.h',
|
||||||
'examples/ios/AppRTCDemo/APPRTCViewController.m',
|
'examples/objc/AppRTCDemo/GAEChannelClient.m',
|
||||||
'examples/ios/AppRTCDemo/AppRTCDemo-Prefix.pch',
|
|
||||||
'examples/ios/AppRTCDemo/GAEChannelClient.h',
|
|
||||||
'examples/ios/AppRTCDemo/GAEChannelClient.m',
|
|
||||||
'examples/ios/AppRTCDemo/main.m',
|
|
||||||
],
|
],
|
||||||
'xcode_settings': {
|
'xcode_settings': {
|
||||||
'CLANG_ENABLE_OBJC_ARC': 'YES',
|
'CLANG_ENABLE_OBJC_ARC': 'YES',
|
||||||
'INFOPLIST_FILE': 'examples/ios/AppRTCDemo/Info.plist',
|
|
||||||
},
|
},
|
||||||
}, # target AppRTCDemo
|
}, # target AppRTCDemo
|
||||||
], # targets
|
], # targets
|
||||||
}], # OS=="ios"
|
}], # OS=="ios" or (OS=="mac" and mac_sdk>="10.8")
|
||||||
|
|
||||||
['OS=="android"', {
|
['OS=="android"', {
|
||||||
'targets': [
|
'targets': [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user