{"action":"created","comment":{"url":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/comments/172723169","pull_request_review_id":101786085,"id":172723169,"diff_hunk":"@@ -0,0 +1,741 @@\n+//\n+//  ExampleAudioEngineDevice.m\n+//  AudioDeviceExample\n+//\n+//  Copyright © 2018 Twilio, Inc. All rights reserved.\n+//\n+\n+#import \"ExampleAudioEngineDevice.h\"\n+\n+// We want to get as close to 10 msec buffers as possible because this is what the media engine prefers.\n+static double const kPreferredIOBufferDuration = 0.01;\n+// We will use stereo playback where available. Some audio routes may be restricted to mono only.\n+static size_t const kPreferredNumberOfChannels = 1;\n+// An audio sample is a signed 16-bit integer.\n+static size_t const kAudioSampleSize = 2;\n+static uint32_t const kPreferredSampleRate = 48000;\n+\n+typedef struct RendererAudioContext {\n+    TVIAudioDeviceContext deviceContext;\n+    size_t maxFramesPerBuffer;\n+    AudioBufferList *bufferList;\n+    void *renderBlock; // AVAudioEngineManualRenderingBlock\n+} RendererAudioContext;\n+\n+typedef struct CapturerAudioContext {\n+    TVIAudioDeviceContext deviceContext;\n+    AudioBufferList *bufferList;\n+    AudioUnit audioUnit;\n+} CapturerAudioContext;\n+\n+// The RemoteIO audio unit uses bus 0 for ouptut, and bus 1 for input.\n+static int kOutputBus = 0;\n+static int kInputBus = 1;\n+\n+// This is the maximum slice size for RemoteIO (as observed in the field). We will double check at initialization time.\n+static size_t kMaximumFramesPerBuffer = 1156;\n+\n+@interface ExampleAudioEngineDevice()\n+\n+@property (nonatomic, assign, getter=isInterrupted) BOOL interrupted;\n+@property (nonatomic, assign) AudioUnit audioUnit;\n+@property (nonatomic, assign) AudioBufferList audioBufferList;\n+@property (nonatomic, assign, getter=isCapturerInitialized) BOOL capturerInitialized;\n+@property (nonatomic, assign, getter=isRendererInitialized) BOOL rendererInitialized;\n+\n+@property (nonatomic, strong, nullable) TVIAudioFormat *renderingFormat;\n+@property (nonatomic, strong, nullable) TVIAudioFormat *capturingFormat;\n+@property (atomic, assign) RendererAudioContext *renderingContext;\n+@property (nonatomic, assign) CapturerAudioContext *capturingContext;\n+\n+@property (nonatomic, strong) AVAudioEngine *engine;\n+@property (nonatomic, strong) AVAudioPlayerNode *player;\n+@property (nonatomic, strong) AVAudioUnitReverb *reverb;\n+\n+@end\n+\n+@implementation ExampleAudioEngineDevice\n+\n+#pragma mark - Init & Dealloc\n+\n+- (id)init {\n+    if (@available(iOS 11.0, *)) {\n+        self = [super init];\n+\n+        if (self) {\n+        }\n+\n+        return self;\n+    } else {\n+        self = nil;\n+        NSException *exception = [NSException exceptionWithName:@\"ExampleAudioEngineDeviceNotSupported\"\n+                                                         reason:@\"ExampleAudioEngineDevice requires iOS 11.0 or greater.\" userInfo:nil];\n+        [exception raise];\n+        return self;\n+    }\n+}\n+\n+- (void)dealloc {\n+    [self unregisterAVAudioSessionObservers];\n+}\n+\n++ (NSString *)description {\n+    return @\"AVAudioEngine Audio Mixing\";\n+}\n+\n+/*\n+ * Determine at runtime the maximum slice size used by RemoteIO. Setting the stream format and sample rate doesn't\n+ * appear to impact the maximum size so we prefer to read this value once at initialization time.\n+ */\n++ (void)initialize {\n+    AudioComponentDescription audioUnitDescription = [self audioUnitDescription];\n+    AudioComponent audioComponent = AudioComponentFindNext(NULL, &audioUnitDescription);\n+    AudioUnit audioUnit;\n+    OSStatus status = AudioComponentInstanceNew(audioComponent, &audioUnit);\n+    if (status != 0) {\n+        NSLog(@\"Could not find RemoteIO AudioComponent instance!\");\n+        return;\n+    }\n+\n+    UInt32 framesPerSlice = 0;\n+    UInt32 propertySize = sizeof(framesPerSlice);\n+    status = AudioUnitGetProperty(audioUnit, kAudioUnitProperty_MaximumFramesPerSlice,\n+                                  kAudioUnitScope_Global, kOutputBus,\n+                                  &framesPerSlice, &propertySize);\n+    if (status != 0) {\n+        NSLog(@\"Could not read RemoteIO AudioComponent instance!\");\n+        AudioComponentInstanceDispose(audioUnit);\n+        return;\n+    }\n+\n+    NSLog(@\"This device uses a maximum slice size of %d frames.\", (unsigned int)framesPerSlice);\n+    kMaximumFramesPerBuffer = (size_t)framesPerSlice;\n+    AudioComponentInstanceDispose(audioUnit);\n+}\n+\n+#pragma mark - AudioEngine\n+\n+- (void)setupAudioEngine {\n+    NSAssert(_engine == nil, @\"AVAudioEngine is already configured\");\n+\n+    _engine = [AVAudioEngine new];\n+    [_engine stop];\n+\n+    NSError *error = nil;\n+    const AudioStreamBasicDescription asbd = [self.renderingFormat streamDescription];\n+    AVAudioFormat *format = [[AVAudioFormat alloc] initWithStreamDescription:&asbd];\n+    if (@available(iOS 11.0, *)) {\n+        BOOL success = [_engine enableManualRenderingMode:AVAudioEngineManualRenderingModeRealtime\n+                                                   format:format\n+                                        maximumFrameCount:(uint32_t)kMaximumFramesPerBuffer\n+                                                    error:&error];\n+        if (!success) {\n+            NSLog(@\"Failed to setup manual rendering mode, error = %@\", error);\n+            return;\n+        }\n+\n+        [_engine connect:_engine.inputNode to:_engine.mainMixerNode format:format];\n+\n+        _renderingContext->renderBlock = (__bridge void *)(_engine.manualRenderingBlock);\n+\n+        RendererAudioContext *context = _renderingContext;\n+        success = [_engine.inputNode setManualRenderingInputPCMFormat:format\n+                                                           inputBlock: ^const AudioBufferList * _Nullable(AVAudioFrameCount inNumberOfFrames) {\n+\n+                                                               AudioBufferList *bufferList = context->bufferList;\n+                                                               int8_t *audioBuffer = (int8_t *)bufferList->mBuffers[0].mData;\n+                                                               UInt32 audioBufferSizeInBytes = bufferList->mBuffers[0].mDataByteSize;\n+\n+                                                               /*\n+                                                                * Pull decoded the all remote Participant's mixed audio data from the media\n+                                                                * engine into the AudioUnit's AudioBufferList.\n+                                                                */\n+                                                               TVIAudioDeviceReadRenderData(context->deviceContext, audioBuffer, audioBufferSizeInBytes);\n+\n+                                                               return bufferList;\n+                                                           }];\n+        if (!success) {\n+            NSLog(@\"Failed to set the manual rendering block\");\n+            return;\n+        }\n+\n+        success = [_engine startAndReturnError:&error];\n+        if (!success) {\n+            NSLog(@\"Failed to start AVAudioEngine, error = %@\", error);\n+        }\n+    }\n+}\n+\n+- (void)teardownAudioEngine {\n+    [_engine stop];\n+    _engine = nil;\n+}\n+\n+- (void)playMusic {\n+    if (!_engine) {\n+        NSLog(@\"Cannot play music. AudioEngine has not been created yet.\");\n+        return;\n+    }\n+\n+    if (_player.isPlaying) {\n+        [_player stop];\n+    }\n+\n+    NSString *fileName = [NSString stringWithFormat:@\"%@/%@\", [[NSBundle mainBundle] bundlePath], @\"mixLoop.caf\"];\n+    NSURL *url = [NSURL fileURLWithPath:fileName];\n+    AVAudioFile *file = [[AVAudioFile alloc] initForReading:url error:nil];\n+\n+    _player = [[AVAudioPlayerNode alloc] init];\n+    _reverb = [[AVAudioUnitReverb alloc] init];\n+\n+    [_reverb loadFactoryPreset:AVAudioUnitReverbPresetMediumHall];\n+    _reverb.wetDryMix = 50;\n+\n+    [_engine attachNode:_player];\n+    [_engine attachNode:_reverb];\n+\n+    [_engine connect:_player to:_reverb format:file.processingFormat];\n+    [_engine connect:_reverb to:_engine.mainMixerNode format:file.processingFormat];\n+\n+    [_player scheduleFile:file atTime:nil completionHandler:^{}];\n+    [_player play];\n+}\n+\n+#pragma mark - TVIAudioDeviceRenderer\n+\n+- (nullable TVIAudioFormat *)renderFormat {\n+    if (!_renderingFormat) {\n+\n+        if (!self.isCapturerInitialized) {\n+            [self setupAVAudioSession];\n+            _rendererInitialized = YES;\n+        }\n+\n+        /*\n+         * Assume that the AVAudioSession has already been configured and started and that the values\n+         * for sampleRate and IOBufferDuration are final.\n+         */\n+        _renderingFormat = [[self class] activeFormat];\n+    }\n+\n+    return _renderingFormat;\n+}\n+\n+- (BOOL)initializeRenderer {\n+    /*\n+     * In this example we don't need any fixed size buffers or other pre-allocated resources. We will simply write\n+     * directly to the AudioBufferList provided in the AudioUnit's rendering callback.\n+     */","path":"AudioDeviceExample/AudioDevices/ExampleAudioEngineDevice.m","position":228,"original_position":228,"commit_id":"8922d0dce7012d9f4a07cca66a128bee20d11457","original_commit_id":"8922d0dce7012d9f4a07cca66a128bee20d11457","user":{"login":"ceaglest","id":1302577,"avatar_url":"https://avatars2.githubusercontent.com/u/1302577?v=4","gravatar_id":"","url":"https://api.github.com/users/ceaglest","html_url":"https://github.com/ceaglest","followers_url":"https://api.github.com/users/ceaglest/followers","following_url":"https://api.github.com/users/ceaglest/following{/other_user}","gists_url":"https://api.github.com/users/ceaglest/gists{/gist_id}","starred_url":"https://api.github.com/users/ceaglest/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceaglest/subscriptions","organizations_url":"https://api.github.com/users/ceaglest/orgs","repos_url":"https://api.github.com/users/ceaglest/repos","events_url":"https://api.github.com/users/ceaglest/events{/privacy}","received_events_url":"https://api.github.com/users/ceaglest/received_events","type":"User","site_admin":false},"body":"Does this apply to the new class? It looked like you were providing a buffer.\r\n\r\nEdit: Looks like this is still true for rendering. 👍 ","created_at":"2018-03-07T02:14:56Z","updated_at":"2018-03-07T02:48:40Z","html_url":"https://github.com/twilio/video-quickstart-swift/pull/233#discussion_r172723169","pull_request_url":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/233","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/comments/172723169"},"html":{"href":"https://github.com/twilio/video-quickstart-swift/pull/233#discussion_r172723169"},"pull_request":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/233"}}},"pull_request":{"url":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/233","id":172654999,"html_url":"https://github.com/twilio/video-quickstart-swift/pull/233","diff_url":"https://github.com/twilio/video-quickstart-swift/pull/233.diff","patch_url":"https://github.com/twilio/video-quickstart-swift/pull/233.patch","issue_url":"https://api.github.com/repos/twilio/video-quickstart-swift/issues/233","number":233,"state":"open","locked":false,"title":"AVAudioEngine example","user":{"login":"piyushtank","id":17037593,"avatar_url":"https://avatars3.githubusercontent.com/u/17037593?v=4","gravatar_id":"","url":"https://api.github.com/users/piyushtank","html_url":"https://github.com/piyushtank","followers_url":"https://api.github.com/users/piyushtank/followers","following_url":"https://api.github.com/users/piyushtank/following{/other_user}","gists_url":"https://api.github.com/users/piyushtank/gists{/gist_id}","starred_url":"https://api.github.com/users/piyushtank/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piyushtank/subscriptions","organizations_url":"https://api.github.com/users/piyushtank/orgs","repos_url":"https://api.github.com/users/piyushtank/repos","events_url":"https://api.github.com/users/piyushtank/events{/privacy}","received_events_url":"https://api.github.com/users/piyushtank/received_events","type":"User","site_admin":false},"body":"> *NOTE*: This PR is still work in progress. In `AudioDeviceExample` sample app, we are planning to add UI to select audio device and make some more improvements over the next few days.\r\n\r\nThis PR adds a new `ExampleEngineAudioDevice` to the AudioDeviceExample sample app. The new audio device demonstrates the use of [AVAudioEngine](https://developer.apple.com/documentation/avfoundation/avaudioengine) and Core Audio with TwilioVideo's [TVIAudioDevice](https://media.twiliocdn.com/sdk/ios/video/releases/2.0.0-beta2/docs/Protocols/TVIAudioDevice.html). The `ExampleEngineAudioDevice` can capture the local audio from microphone, render the remote participant's audio into the speaker and it can also play music along with the remote participant's audio.\r\n\r\nThe audio device uses Core Audio to capture and render audio samples to the iPhone hardware. It sets up an AudioUnit with `kAudioUnitSubType_VoiceProcessingIO` for capturing the audio from the microphone and renders the data to the device speaker. The audio unit supports echo cancellation.\r\n\r\nThe ExampleEngineAudioDevice uses AVAudioEngine to mix the music and remote participant's audio.  The audio device sets up the AVAudioEngine in [real-time manual rendering mode](https://developer.apple.com/videos/play/wwdc2017/501/) (iOS 11+ only) to do so. The audio samples received from Twilio Video SDK gets supplied to the input node of the AVAudioEngine which goes to AVAudioEngine's main mixer. To play the music along with the remote participant's audio, the AVPlayerNode get set up which supplies the file audio to the main mixer of the AVAudioEngine. At the other side of the AVAudioEngine, at the output node, the mixed audio is received. The core audio receives the mixed audio which gets played on a speaker. \r\n\r\nFollowing diagram explains the audio sample flow in `ExampleEngineAudioDevice`  -\r\n\r\n<img width=\"936\" alt=\"screen shot 2018-03-02 at 11 16 57 pm\" src=\"https://user-images.githubusercontent.com/17037593/36931667-e1cacf84-1e6f-11e8-9bbd-0dffee627432.png\">\r\n\r\n## UI Flow \r\n\r\n1. By default, the Core Audio Device will be selected when you launch the application, a log message will appear on the top of the screen -\r\n\r\n<kbd><img width=\"300px\" src=\"https://user-images.githubusercontent.com/17037593/37048840-e95706a8-2123-11e8-86ca-367e2c53de29.PNG\"/>\r\n\r\n2. By clicking the `Select Audio Device` you can select an Audio Device -\r\n\r\n\r\n<kbd><img width=\"300px\" src=\"https://user-images.githubusercontent.com/17037593/37048762-b7b1d628-2123-11e8-8f0d-53cd1c979da2.PNG\"/>\r\n\r\n3. A log message will appear on the top of the screen once the new device is selected -\r\n\r\n<kbd><img width=\"300px\" src=\"https://user-images.githubusercontent.com/17037593/37048777-c3d6bebe-2123-11e8-83b1-0b2a3ee27d0b.PNG\"/>\r\n\r\n\r\n#### TODOs\r\n- [x] UI to select the audio device\r\n- [x] Readme update\r\n- [x] AVAudioEngine and AVPalyerNode's thread safety\r\n- [x] Do we need to refactor context classes?\r\n","created_at":"2018-03-03T07:22:14Z","updated_at":"2018-03-07T02:48:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"70d080a4f4268e7c66034f7d2d31bc20ca1e3df4","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/233/commits","review_comments_url":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/233/comments","review_comment_url":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/comments{/number}","comments_url":"https://api.github.com/repos/twilio/video-quickstart-swift/issues/233/comments","statuses_url":"https://api.github.com/repos/twilio/video-quickstart-swift/statuses/8922d0dce7012d9f4a07cca66a128bee20d11457","head":{"label":"piyushtank:audio-engine-example","ref":"audio-engine-example","sha":"8922d0dce7012d9f4a07cca66a128bee20d11457","user":{"login":"piyushtank","id":17037593,"avatar_url":"https://avatars3.githubusercontent.com/u/17037593?v=4","gravatar_id":"","url":"https://api.github.com/users/piyushtank","html_url":"https://github.com/piyushtank","followers_url":"https://api.github.com/users/piyushtank/followers","following_url":"https://api.github.com/users/piyushtank/following{/other_user}","gists_url":"https://api.github.com/users/piyushtank/gists{/gist_id}","starred_url":"https://api.github.com/users/piyushtank/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piyushtank/subscriptions","organizations_url":"https://api.github.com/users/piyushtank/orgs","repos_url":"https://api.github.com/users/piyushtank/repos","events_url":"https://api.github.com/users/piyushtank/events{/privacy}","received_events_url":"https://api.github.com/users/piyushtank/received_events","type":"User","site_admin":false},"repo":{"id":50969677,"name":"video-quickstart-swift","full_name":"piyushtank/video-quickstart-swift","owner":{"login":"piyushtank","id":17037593,"avatar_url":"https://avatars3.githubusercontent.com/u/17037593?v=4","gravatar_id":"","url":"https://api.github.com/users/piyushtank","html_url":"https://github.com/piyushtank","followers_url":"https://api.github.com/users/piyushtank/followers","following_url":"https://api.github.com/users/piyushtank/following{/other_user}","gists_url":"https://api.github.com/users/piyushtank/gists{/gist_id}","starred_url":"https://api.github.com/users/piyushtank/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/piyushtank/subscriptions","organizations_url":"https://api.github.com/users/piyushtank/orgs","repos_url":"https://api.github.com/users/piyushtank/repos","events_url":"https://api.github.com/users/piyushtank/events{/privacy}","received_events_url":"https://api.github.com/users/piyushtank/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/piyushtank/video-quickstart-swift","description":"Twilio Video (Conversations) Quickstart for iOS with Swift","fork":true,"url":"https://api.github.com/repos/piyushtank/video-quickstart-swift","forks_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/forks","keys_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/keys{/key_id}","collaborators_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/teams","hooks_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/hooks","issue_events_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/issues/events{/number}","events_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/events","assignees_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/assignees{/user}","branches_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/branches{/branch}","tags_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/tags","blobs_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/git/refs{/sha}","trees_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/git/trees{/sha}","statuses_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/statuses/{sha}","languages_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/languages","stargazers_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/stargazers","contributors_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/contributors","subscribers_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/subscribers","subscription_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/subscription","commits_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/commits{/sha}","git_commits_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/git/commits{/sha}","comments_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/comments{/number}","issue_comment_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/issues/comments{/number}","contents_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/contents/{+path}","compare_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/compare/{base}...{head}","merges_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/merges","archive_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/downloads","issues_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/issues{/number}","pulls_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/pulls{/number}","milestones_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/milestones{/number}","notifications_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/labels{/name}","releases_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/releases{/id}","deployments_url":"https://api.github.com/repos/piyushtank/video-quickstart-swift/deployments","created_at":"2016-02-03T02:59:25Z","updated_at":"2016-02-03T02:59:25Z","pushed_at":"2018-03-06T21:56:06Z","git_url":"git://github.com/piyushtank/video-quickstart-swift.git","ssh_url":"git@github.com:piyushtank/video-quickstart-swift.git","clone_url":"https://github.com/piyushtank/video-quickstart-swift.git","svn_url":"https://github.com/piyushtank/video-quickstart-swift","homepage":null,"size":10385,"stargazers_count":0,"watchers_count":0,"language":"Swift","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"twilio:master","ref":"master","sha":"3398a885cd048f7301b78fc762617174588aeeb4","user":{"login":"twilio","id":109142,"avatar_url":"https://avatars0.githubusercontent.com/u/109142?v=4","gravatar_id":"","url":"https://api.github.com/users/twilio","html_url":"https://github.com/twilio","followers_url":"https://api.github.com/users/twilio/followers","following_url":"https://api.github.com/users/twilio/following{/other_user}","gists_url":"https://api.github.com/users/twilio/gists{/gist_id}","starred_url":"https://api.github.com/users/twilio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/twilio/subscriptions","organizations_url":"https://api.github.com/users/twilio/orgs","repos_url":"https://api.github.com/users/twilio/repos","events_url":"https://api.github.com/users/twilio/events{/privacy}","received_events_url":"https://api.github.com/users/twilio/received_events","type":"Organization","site_admin":false},"repo":{"id":48068060,"name":"video-quickstart-swift","full_name":"twilio/video-quickstart-swift","owner":{"login":"twilio","id":109142,"avatar_url":"https://avatars0.githubusercontent.com/u/109142?v=4","gravatar_id":"","url":"https://api.github.com/users/twilio","html_url":"https://github.com/twilio","followers_url":"https://api.github.com/users/twilio/followers","following_url":"https://api.github.com/users/twilio/following{/other_user}","gists_url":"https://api.github.com/users/twilio/gists{/gist_id}","starred_url":"https://api.github.com/users/twilio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/twilio/subscriptions","organizations_url":"https://api.github.com/users/twilio/orgs","repos_url":"https://api.github.com/users/twilio/repos","events_url":"https://api.github.com/users/twilio/events{/privacy}","received_events_url":"https://api.github.com/users/twilio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/twilio/video-quickstart-swift","description":"Twilio Video Quickstart for iOS with Swift","fork":false,"url":"https://api.github.com/repos/twilio/video-quickstart-swift","forks_url":"https://api.github.com/repos/twilio/video-quickstart-swift/forks","keys_url":"https://api.github.com/repos/twilio/video-quickstart-swift/keys{/key_id}","collaborators_url":"https://api.github.com/repos/twilio/video-quickstart-swift/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/twilio/video-quickstart-swift/teams","hooks_url":"https://api.github.com/repos/twilio/video-quickstart-swift/hooks","issue_events_url":"https://api.github.com/repos/twilio/video-quickstart-swift/issues/events{/number}","events_url":"https://api.github.com/repos/twilio/video-quickstart-swift/events","assignees_url":"https://api.github.com/repos/twilio/video-quickstart-swift/assignees{/user}","branches_url":"https://api.github.com/repos/twilio/video-quickstart-swift/branches{/branch}","tags_url":"https://api.github.com/repos/twilio/video-quickstart-swift/tags","blobs_url":"https://api.github.com/repos/twilio/video-quickstart-swift/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/twilio/video-quickstart-swift/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/twilio/video-quickstart-swift/git/refs{/sha}","trees_url":"https://api.github.com/repos/twilio/video-quickstart-swift/git/trees{/sha}","statuses_url":"https://api.github.com/repos/twilio/video-quickstart-swift/statuses/{sha}","languages_url":"https://api.github.com/repos/twilio/video-quickstart-swift/languages","stargazers_url":"https://api.github.com/repos/twilio/video-quickstart-swift/stargazers","contributors_url":"https://api.github.com/repos/twilio/video-quickstart-swift/contributors","subscribers_url":"https://api.github.com/repos/twilio/video-quickstart-swift/subscribers","subscription_url":"https://api.github.com/repos/twilio/video-quickstart-swift/subscription","commits_url":"https://api.github.com/repos/twilio/video-quickstart-swift/commits{/sha}","git_commits_url":"https://api.github.com/repos/twilio/video-quickstart-swift/git/commits{/sha}","comments_url":"https://api.github.com/repos/twilio/video-quickstart-swift/comments{/number}","issue_comment_url":"https://api.github.com/repos/twilio/video-quickstart-swift/issues/comments{/number}","contents_url":"https://api.github.com/repos/twilio/video-quickstart-swift/contents/{+path}","compare_url":"https://api.github.com/repos/twilio/video-quickstart-swift/compare/{base}...{head}","merges_url":"https://api.github.com/repos/twilio/video-quickstart-swift/merges","archive_url":"https://api.github.com/repos/twilio/video-quickstart-swift/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/twilio/video-quickstart-swift/downloads","issues_url":"https://api.github.com/repos/twilio/video-quickstart-swift/issues{/number}","pulls_url":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls{/number}","milestones_url":"https://api.github.com/repos/twilio/video-quickstart-swift/milestones{/number}","notifications_url":"https://api.github.com/repos/twilio/video-quickstart-swift/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/twilio/video-quickstart-swift/labels{/name}","releases_url":"https://api.github.com/repos/twilio/video-quickstart-swift/releases{/id}","deployments_url":"https://api.github.com/repos/twilio/video-quickstart-swift/deployments","created_at":"2015-12-15T20:46:04Z","updated_at":"2018-03-05T18:43:18Z","pushed_at":"2018-03-06T21:56:07Z","git_url":"git://github.com/twilio/video-quickstart-swift.git","ssh_url":"git@github.com:twilio/video-quickstart-swift.git","clone_url":"https://github.com/twilio/video-quickstart-swift.git","svn_url":"https://github.com/twilio/video-quickstart-swift","homepage":"https://www.twilio.com/docs/api/video","size":8661,"stargazers_count":109,"watchers_count":109,"language":"Swift","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":42,"mirror_url":null,"archived":false,"open_issues_count":30,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit"},"forks":42,"open_issues":30,"watchers":109,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/233"},"html":{"href":"https://github.com/twilio/video-quickstart-swift/pull/233"},"issue":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/issues/233"},"comments":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/issues/233/comments"},"review_comments":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/233/comments"},"review_comment":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/pulls/233/commits"},"statuses":{"href":"https://api.github.com/repos/twilio/video-quickstart-swift/statuses/8922d0dce7012d9f4a07cca66a128bee20d11457"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"ceaglest"}}