Building the Open-Source Conferencing App for iOS
{% include_relative nav.html selected="ios" %}
{% capture prerequisites%}
Gone through the Quick Start with Mesibo Conferencing API - iOS and have a basic familiarity with mesibo conferencing APIs..
XCode Installed
An iPhone Device to run the app {% endcapture %}
{% capture build_instructions%}
Build Instructions
Building the code is as simple as:
- Launch Xcode
- Open the project ios/MesiboConference from the folder where you have downloaded the code using the menu
File -> Open
- Build using menu
Product -> Build
- It may take a while to build the project for the first time.
- Once the build is over, run on the device using the menu
Product -> Run
- 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 enablinggit 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 %}
When you make a call using the
call
method and you are connected, MesiboGroupcall_OnConnectedopen_in_new will be called.Once you are connected, you will be notified when you receive the video stream through the listener MesiboGroupcall_OnVideoopen_in_new or the audio stream through MesiboGroupcall_OnAudioopen_in_new
When a publisher mutes audio or video, you will be notified through MesiboGroupcall_OnMuteopen_in_new
When a publisher starts talking, MesiboGroupcall_OnTalkingopen_in_new will be called (Talk Detection).
When a publisher leaves the conference, MesiboGroupcall_OnHangupopen_in_new will be called
{% 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 %}