Building the Open-Source Conferencing App for iOS

{% include_relative nav.html selected="ios" %}

{% capture prerequisites%}

{% capture build_instructions%}

Build Instructions

Building the code is as simple as:

  1. Launch Xcode
  2. Open the project ios/MesiboConference from the folder where you have downloaded the code using the menu File -> Open
  3. Build using menu Product -> Build
  4. It may take a while to build the project for the first time.
  5. Once the build is over, run on the device using the menu Product -> Run
  6. That's it, you should see the welcome screen like below and then the login screen.

If you receive linker errors

If you receive linker errors, it means that your download was incomplete. One of the likely reasons is that you forgot to issue git lfs install. There are two ways to fix it:

  • Delete the entire repository and download again (do not forget git lfs install, as instructed above). Note that enabling git lfs install after partial download does not seem to work as expected.
  • OR, run fetch_broken_download.sh script
./fetch_broken_download.sh

{% endcapture %}

{% capture login %} Refer to the source code hereopen_in_new for an example on invoking the login REST API. {% highlight objc %} -(void) login:(NSString *)name email:(NSString *)email code:(NSString *)code handler:(SampleAPI_onResponse) handler { NSMutableDictionary *post = [[NSMutableDictionary alloc] init]; [post setValue:@"login" forKey:@"op"]; [post setValue:name forKey:@"name"]; [post setValue:email forKey:@"email"]; if(nil != code) { [post setValue:code forKey:@"code"]; }

NSString *packageName = [[NSBundle mainBundle] bundleIdentifier];
[post setValue:packageName forKey:@"appid"];

[self invokeApi:post filePath:nil handler:handler];

} {% endhighlight %} If login is successful, your server will generate an access token using mesibo User Management API and send it to you in the response. You need to use this access token later while initializing the mesibo real-time API. Refer to the example source code hereopen_in_new.

{% highlight objc %} if([op isEqualToString:@"login"]) { mToken = (NSString *)[returnedDict objectForKeyOrNil:@"token"];

if(![SampleAPI isEmpty:mToken]) { [self save];

[MesiboInstance reset];

}

} {% endhighlight %} {% endcapture %}

{% capture create_room %} Refer to the source code hereopen_in_new for an example on invoking the setgroup REST API.

{% highlight objc %} -(void) createRoom:(NSString *)name resolutoon:(int)resolution handler:(SampleAPI_onResponse) handler { NSMutableDictionary *post = [[NSMutableDictionary alloc] init]; [post setValue:@"setgroup" forKey:@"op"]; [post setValue:[self getToken] forKey:@"token"]; [post setValue:name forKey:@"name"];

NSString *r = [NSString stringWithFormat:@"%d", resolution];
[post setValue:r forKey:@"resolution"];

[self invokeApi:post filePath:nil handler:handler];

} {% endhighlight %} {% endcapture %}

{% capture create_room_pin %} If a room is created successfully using setgroup, you will receive the room pin and spin in the response. Refer to the source code hereopen_in_new

{% highlight objc %}

  • (IBAction)onCreateRoom:(id)sender { if(_mRoom.text.length < 3) { [self showError:@"missing or too short room Name"]; return;; }

    Resolution *r = [mResolutions objectAtIndex:mSelectedResolution];

    [SampleAPIInstance createRoom:_mRoom.text resolutoon:r.resolution handler:^(int result, NSDictionary *response) {

      if(SAMPLEAPP_RESULT_OK != result) {
          [self showError:@"Create Failed"];
          return;
      }
    
      NSInteger gid = [[response objectForKey:@"gid"] longValue];
    
    
      [self dismissViewControllerAnimated:NO completion:^{
          [mParent groupCallUi:gid video:YES publish:YES];
      }];
    

    }]; } {% endhighlight %}

You can share the room-id and room-pin(either pin for active participants or spin for subscribe-only participants) with other users who you want to join the conference. {% endcapture %}

{% capture join_room %} Refer to source code hereopen_in_new for an example on invoking the joingroup REST API. {% highlight objc %} -(void) joinRoom:(NSString *)gid pin:(NSString *)pin handler:(SampleAPI_onResponse) handler { NSMutableDictionary *post = [[NSMutableDictionary alloc] init]; [post setValue:@"joingroup" forKey:@"op"]; [post setValue:[self getToken] forKey:@"token"]; [post setValue:gid forKey:@"gid"]; [post setValue:pin forKey:@"pin"];

[self invokeApi:post filePath:nil handler:handler];

} {% endhighlight %}

f joingroup is successful, you can join a group call. Refer to source code JoinRoomViewController.mopen_in_new {% highlight objc %} -(void) joinRoom:(NSString *)gid pin:(NSString *)pin {

[SampleAPIInstance joinRoom:gid pin:pin handler:^(int result, NSDictionary *response) {
    
    if(SAMPLEAPP_RESULT_OK != result) {
        [self showError:@"Joining Room Failed"];
        return;
    }
    
    [self groupCallUi:gid video:YES publish:YES];
    
}];

} {% endhighlight %} {% endcapture %}

{% capture launch_group_call %} The app launches MesiboGroupCallControlleropen_in_new to show the group call screen. This, in turn, loads GroupCallViewopen_in_new and starts the group call.

Refer to JoinRoomViewController.mopen_in_new {% highlight objc %} -(BOOL) groupCallUi:(uint32_t)gid video:(BOOL)video publish:(BOOL)publish { MesiboGroupCallController *vc = [[MesiboGroupCallController alloc] initWithGid:gid]; vc.modalPresentationStyle = UIModalPresentationFullScreen;

UIViewController *me = self;
[MesiboInstance runInThread:YES handler:^{
    [me presentViewController:vc animated:YES completion:nil];
}];

return YES;

} {% endhighlight%} {% endcapture %}

{% capture start_group_call %} See GroupCallView.mopen_in_new {% highlight objc %}

  • (void)startCall { /* create a group call instance and join the room */ mGc = [MesiboCallInstance groupCall:self groupid:mGid]; [mGc join:self];

    /* publish streams */ MesiboParticipant *p = [mGc createPublisher:0]; [p setVideoSource:MESIBOCALL_VIDEOSOURCE_CAMERAFRONT index:0]; [mPublishers addObject:p]; [p call:YES video:YES listener:self];

} {% endhighlight%} {% endcapture %}

{% capture viewing_others %} In this app, GroupCallView.mopen_in_new implements GroupCallListener.

When someone joins the room and starts publishing, you will receive their Participant object through MesiboGroupCall_OnPublisheropen_in_new.

{% highlight objc %}

  • (void)MesiboGroupcall_OnPublisher:(MesiboParticipant * _Nonnull)p joined:(BOOL)joined {

    /* we got a new publisher, subscribe to them*/ if (joined) { int pos = [self getParticipantPosition:mPublishers p:p]; if(pos >= 0) { [mPublishers removeObjectAtIndex:pos]; } [mPublishers addObject:p]; [p call:YES video:(YES) listener:self];;

    } else { [self removeParticipant:p]; [self setStreams]; }

} {% endhighlight%} {% endcapture %}

{% capture listeners %}

{% endcapture %}

{% capture display_streams %} When you receive the video stream from a participant MesiboGroupcall_OnVideoopen_in_new will be called. You can then use, setVideoViewopen_in_new to display them.

Sorting Streams (Optional)

You can display the videos of participants in a grid or whatever fashion you like. Mesibo also provides a utility function to sort videos in a grid using the aspect ratio. To use this, you need to call the sort function with a list of streams and implement MesiboParticipantSortListeneropen_in_new which will be called with the video coordinates. Note, this is an optional utility. You can always use your own method of sorting participants and displaying videos.

Here, each video is displayed in a ParticipantViewHolderopen_in_new containing a MesiboVideoView. So, in a grid, we will have a list of views. We will sort these views, based on the aspect ratio every time the grid is updated (when participant video added or removed, when video aspect ratio changes, etc). Refer to the source code hereopen_in_new.

{% highlight objc %} -(void) setStreams { ...

NSArray *sorted = [mGc sortStreams:self views:mStreams width:mWidth height:mHeight start:0 size:mStreams.count params:nil];

... } {% endhighlight%}

The position for each video in the grid is set in ParticipantSort_onSetCoordinatesopen_in_new

{% highlight objc %}

  • (void)ParticipantSort_onSetCoordinates:(id _Nonnull)o position:(int)position x:(float)x y:(float)y width:(float)width height:(float)height {

    ParticipantViewHolder *pv = (ParticipantViewHolder *)o; [pv setCoordinates:position x:x y:y width:width height:height]; } {% endhighlight%}

{% endcapture %}

{% capture conf_ops %} {% endcapture %}

{% capture mute %} MesiboGroupcall_OnMuteopen_in_new is called when the publisher mutes audio or video. {% highlight objc %} -(void) MesiboGroupcall_OnMute:(MesiboParticipant *)participant audio:(BOOL)audioMuted video:(BOOL)videoMuted {

if(remote){
// Remote participant has muted
}

// Check mute status
if(audioMuted){
    // Audio Muted
}

if(videoMuted){
    // Video Muted
}

} {% endhighlight%}

You can toggle the audio using toggleAudioMute and toggle the video using toggleVideoMute

{% endcapture %}

{% capture talk_detection %} MesiboGroupcall_OnTalking is called when the participant starts or stops talking. {% highlight objc %} -(void) MesiboGroupcall_OnTalking:(MesiboParticipant *)participant talking:(BOOL)talking { if(talking){ // If talking is true, participant started talking // if it is false, participant stopped talking // Handle talking. For example, Show a talking icon } } {% endhighlight %}

{% endcapture %}

{% capture screen_sharing %} {% endcapture %}

{% capture play_sound %} You can use the utility function playInCallSound to play sound in the background in a group call.

{% highlight objc %} NSURL *sound = [NSURL fileURLWithPath:@"file:///path/to/audiofile/"]; [GroupCallInstance playInCallSound:sound] {% endhighlight %}

{% endcapture %}

{% capture chat %} For implementing chat, you can use the Mesibo Messaging UI Helper.

For group chat, simply set the group profile and use launchMessageView to launch the chat UI for the group. Similarly, for one-to-one chat, create a user profile and call launchMessageView.

For example,

{% highlight objc %}

  • (IBAction)onLaunchMessagingUIModule:(id)sender { //requires pod mesibo-ui [MesiboUI launchMessageViewController:self profile:mProfile]; } {% endhighlight%}

Refer to the documentation Launching Messaging UI in iOS learn more. {% endcapture %}

{% capture hang_up%} Whenever a publisher hangs up, MesiboGroupcall_OnHangup will be called.

To, stop viewing a participant you need to call hang up on their participant object.

{% highlight objc %} [participant hangup]; {%endhighlight%}

{% endcapture %}

{% capture leave_room%} {% highlight objc %} -(void) onHangup:(id)sender { [mGc leave]; [mController dismiss]; } {% endhighlight%}

In this app, we call leave when the local hangup button is pressedopen_in_new. {% endcapture %}

{% capture conclusion %} In the next part, we will explore the Javascript(Web) app.

On to Part 4 >> {% endcapture %}

{% include_relative conf-app.md %}