OpenTok iOS API:Stream in iPad more then two - ios

I want to make live streaming for more then two users on different devices and get api from opentok i had download demo app from ( https://github.com/opentok/OpenTok-iOS-Hello-World) and this is not webrtc, i had run application with key, session and token with disables of pear to pear,
And its working fine for two live streaming but while i tray to connect third stream i am not able to getting that,
I found staring in demo app that (On iPad 2 / 3 / 4, the limit is four streams. An app can have up to four simultaneous subscribers, or one publisher and up to three subscribers.)
with this i am testing with three iPads and got just two on screen
so how to make this more then two stream at a time in three iPads

The project you linked (OpenTok-iOS-Hello-World) is built to just subscribe to one stream. Just as a proof of concept, you can get two subscribers on screen pretty simply by just modifying a few methods and adding an instance variable in ViewController.m
Create a variable that tracks the number of subscribers:
#implementation ViewController {
OTSession* _session;
OTPublisher* _publisher;
OTSubscriber* _subscriber;
int _numSubscribers; // **NEW**
}
Initialize the variable in the initialization method:
- (void)viewDidLoad
{
[super viewDidLoad];
_session = [[OTSession alloc] initWithSessionId:kSessionId
delegate:self];
_numSubscribers = 0; // **NEW**
[self doConnect];
}
Make sure we aren't subscribing to our own stream:
static bool subscribeToSelf = NO;
Modify stop caring about whether there is already a subscriber in this session delegate method:
- (void)session:(OTSession*)mySession didReceiveStream:(OTStream*)stream
{
NSLog(#"session didReceiveStream (%#)", stream.streamId);
// See the declaration of subscribeToSelf above.
if ( (subscribeToSelf && [stream.connection.connectionId isEqualToString: _session.connection.connectionId])
||
(!subscribeToSelf && ![stream.connection.connectionId isEqualToString: _session.connection.connectionId])
) {
// ** Changing if statement **
if (_numSubscribers < 2) {
_subscriber = [[OTSubscriber alloc] initWithStream:stream delegate:self];
_numSubscribers++;
}
}
}
Place the subscribers next to one another, taking up a little less width:
- (void)subscriberDidConnectToStream:(OTSubscriber*)subscriber
{
NSLog(#"subscriberDidConnectToStream (%#)", subscriber.stream.connection.connectionId);
// ** Calculate the frame **
CGRect subFrame = CGRectMake(0, widgetHeight, widgetWidth / 2, widgetHeight)
if (_numSubscribers == 2) subFrame = CGRectOffset(subFrame, widgetWidth / 2, 0);
[subscriber.view setFrame:subFrame];
[self.view addSubview:subscriber.view];
}
NOTE: This solution doesn't result in a stable App. It should get you to a point where you can see both subscribers as long as you don't disconnect any of the iPads in between. To finish this off, you will need to store the OTSubscribers created in session:didRecieveStream: in a collection like NSArray, handle removing the right subscriber(s) and decrementing the _numSubscribers in session:didDropStream:, and thinking about how you want the updateSubscriber method to work instead.

If you look at the source code for hello world in the viewcontroller file line 93, you will see that it is only creating one subscriber. To have multiple subscribers simple create an array or hash object to store multiple subscribers.

Related

How to calculate payload size and timeout length for network connectivity test in iOS?

My app offers the option to download 3430 high resolution images from our server, each image of size 50k - 600k bytes.
The original approach was to just download all of them - but we realized that gave a lot of NSURLErrorTimedOut errors and crashed our program. We've now implemented it such that we download all of the images, but in batches of 100 images at a time.
- (void)batchDownloadImagesFromServer:(BOOL)downloadHiResImages
{
[UIApplication sharedApplication].idleTimerDisabled = YES;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self generateImageURLList:YES];
[leafletImageLoaderQueue removeAllObjects];
numberOfThumbnailLeft = [uncachedThumbnailArray count];
numberOfHiResImageLeft = [uncachedHiResImageArray count];
NSLog(#"DEBUG: In batchDownloadImagesFromServer numberOfThumbnailLeft %ul , numberOfHiResImageLeft %ul ", numberOfThumbnailLeft, numberOfHiResImageLeft);
numberOfImagesToDownload = numberOfThumbnailLeft;
if (downloadHiResImages)
{
numberOfImagesToDownload += numberOfHiResImageLeft;
}
if (numberTotalToDownload < 0) {
numberTotalToDownload = numberOfHiResImageLeft;
}
int midBatchCt = 0;
// start where we stopped
NSArray *subArray;
NSRange batchRange;
batchRange.location = 0;//uncachedHiResIndex;
NSInteger uncachedNumber = [uncachedHiResImageArray count];
NSLog(#"uncachedHiResIndex and numberTotalToDownload: %d %d", uncachedHiResIndex, numberTotalToDownload);
if (uncachedHiResIndex >= numberTotalToDownload || batchRange.location >= uncachedNumber) {
// we have reached the end of the uncached hires images
NSLog(#" END of download total to download=%ld , uncachedNumber=%ld, # not downloaded is %ld", (long)numberTotalToDownload, uncachedNumber, (long)numberFailedToDownload);
return;
}
if (batchRange.location+100 > uncachedNumber) {
NSInteger imagesUntilEnd = uncachedNumber -1;
batchRange.length = imagesUntilEnd;
NSLog(#"this is uncached number: %d this is uncachedhiresindex:%d and this images until end:%d ", uncachedNumber, uncachedHiResIndex, imagesUntilEnd);
} else {
batchRange.length = 100;
}
NSLog(#" NEW RANGE is from %lul to %lul ", (unsigned long)batchRange.location, batchRange.length);
subArray = [uncachedHiResImageArray subarrayWithRange:batchRange];
if (downloadHiResImages)
{
for ( LeafletURL* aLeafletURL in subArray )
{
LeafletImageLoader* hiResImageLoader = [[LeafletImageLoader alloc] initWithDelegate:self];
[leafletImageLoaderQueue addObject:hiResImageLoader]; // do this before making connection!! //
[hiResImageLoader loadImage:aLeafletURL isThumbnail:NO isBatchDownload:YES];
//// Adding object to array already retains it, so it's safe to release it here. ////
[hiResImageLoader release];
midBatchCt++;
uncachedHiResIndex++;
if (midBatchCt == 10) {
NSLog(#" Waiting for queued images to download...");
NSLog(#" REMOVED from QUEUE %lul , UncachedIndex %lul", numberRemovedFromQueue, uncachedHiResIndex);
break;
}
}
}
if ( [leafletImageLoaderQueue count] == 0 && numberRemovedFromQueue == numberTotalToDownload) {
if([delegate respondsToSelector:#selector(requestDidBatchDownloadImages:)])
{
[delegate requestDidBatchDownloadImages:self];
}
}
}
This has resolved most of our issues. However, we would like to test for network connectivity before even beginning batch downloading. I found a low level ping library that gives accurate round-trip timing results. Using the demo code from GBPing as a reference, I wrote my own code to ping our server before we call batchDownloadImagesFromServer.
- (IBAction)preloadAll:(id)sender
{
self.ping = [GBPing new];
self.ping.host = kDefaultDataServer;
self.ping.delegate = self;
self.ping.timeout = 1;
self.ping.pingPeriod = 0.9;
// setup the ping object (this resolves addresses etc)
[self.ping setupWithBlock:^(BOOL success, NSError *error) {
if (success) {
// start pinging
[self.ping startPinging];
// stop it after 5 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"stop it");
[self.ping stop];
self.ping = nil;
});
} else {
UIAlertController * alert = [UIAlertController alertControllerWithTitle:#"Internet Connection"
message:#"Not strong enough internet connection"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* OKButton = [UIAlertAction
actionWithTitle:#"Ok"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
[downloadManager batchDownloadImagesFromServer:YES];
}];
[alert addAction:OKButton];
[self presentViewController:alert animated:NO completion:nil];
}
}];
}
I am completely new to networking. How do I determine the payload size and timeout length for my test considering the batch size and the size of the images?
Timeout length is per request. It is just the time the networking code will wait for a reply before it gives up. This shouldn't be too short, but for most system API, timeout length is something around a minute or more, which is probably too long.
Also, note that you will still get time out errors if connectivity is bad, so whatever caused your crashes needs to be fixed. You have to be able to recover from time-outs.
You're not giving much information about your crash (what kind of crash is it? What backtrace do you get?), but I can see three obvious things that may be happening:
You did your downloading in a tight loop without an #autoreleasepool {} block inside it. This means that all your downloaded file data accumulated in RAM and blew your app's memory limit. Be sure to put autorelease pools in long-running loops.
You were doing these downloads on the main thread. The main thread is for the UI and short actions. If your main thread code does anything that takes longer than a few seconds, UIApplication will not get to process touch events from the user (and other occurrences) and the operating system will shoot it down as being unresponsive. Offload longer operations onto a dispatch queue (or use some other way to move the actions off the main thread, e.g. by using asynchronous API).
You are flooding your server with requests, and some DDoS-protection inside it decides to just ignore requests from you for a few minutes as a form of self-protection. Many servers have limits on how many requests they will accept from a client within a given period of time, or how many open connections a client may have at the same time.
I think you would be much better served by showing the code that performs the actual download. You should not need to get accurate ping timing information to download a bunch of image files.
Assuming one or more of the above possibilities are true, I'd suggest you implement your download code like this:
Create a list of all file URLs that need to be downloaded.
Write your code so that it downloads these URLs sequentially. I.e. do not let it start downloading a file until the previous one has finished (or failed and you decided to skip it for now).
Use NSURLSession's support for downloading an individual file to a folder, don't use the code to get an NSData and save the file yourself. That way, your application doesn't need to be running while the download finishes.
Ensure that you can tell whether a file has already been downloaded or not, in case your download gets interrupted, or the phone is restarted in mid-download. You can e.g. do this by comparing their names (if they are unique enough), or saving a note to a plist that lets you match a downloaded file to the URL where it came from, or whatever constitutes an identifying characteristic in your case.
At startup, check whether all files are there. If not, put the missing ones in above download list and download them sequentially, as in #2.
Before you start downloading anything (and that includes downloading the next file after the previous download has finished or failed), do a reachability check using the Reachability API from Apple's SystemConfiguration.framework. That will tell you whether the user has a connection at all, and whether you're on WiFi or cellular (in general, you do not want to download a large number of files via cellular, most cellular connections are metered).
If your images are stored on separate servers, or they are comparatively small and there is more overhead setting up the connection than actually downloading the data, you could modify the code to download several images at once, but usually if you're downloading more than 4 images from a server at the same time, you'll likely not see a performance benefit, as every additional image will just reduce the amount of bandwidth available for the others.

How to make audio/video calls and get the call type in on_incoming_call function in PJSIP in iOS?

I built PJSIP library with PJSUA_HAS_VIDEO as 1. I want to make an option to make audio only calls. I tried
pjsua_call_setting opt;
pjsua_call_setting_default(&opt);
opt.flag = PJSUA_CALL_INCLUDE_DISABLED_MEDIA;
opt.vid_cnt = 0;
opt.aud_cnt = 1;
pj_status_t status = pjsua_call_make_call((pjsua_acc_id)[self identifier], &uri, &opt, NULL, NULL, &callIdentifier);
At the receiving end, in on_incoming_call() function, I tried
if (callInfo.rem_offerer && callInfo.rem_vid_cnt == 1)
{
call.hasVideo = YES;
} else {
call.hasVideo = NO;
}
But rem_vid_cnt is always giving 1.
How can I set the call type while making call and receive it correctly at receiving end? I want to set the setHasVideo field of CallKit also at receiving end.
Thanks in advance.
At the app end your code is correct.
You need to disable video from server side also.
This is a two way communication. You can do this from the caller side set the rem_vid_cnt = 0 when you initiate a call, and at the receiver side you will get this as "0".
Hope this will help you :)
/** Number of video streams offered by remote */
unsigned rem_vid_cnt;

Call Directory Extension for Objective-C app is not identifying callers

I'm trying to add a Call Directory Extension to an existing Objective-C iOS app.
Creating the extension was straight-forward using Xcode -> File -> New -> Target, selecting Call Directory Extension and configuring it for Objective-C and the existing project. A new scheme for the extension has also successfully been created.
The extension's CallDirectoryHandler.h and CallDirectoryHandler.m files have been created automatically and currently I just want to test the general concept by hardcoding a phone number from a real device into it.
That real device has a number with a format like this:
+49 176 12345678
So far I've only commented out the automatically generated code referring to blocking numbers (which I don't want to do) and changed the phone numbers and strings in the code for identifying numbers.
The code looks like this:
- (void)addAllIdentificationPhoneNumbersToContext:(CXCallDirectoryExtensionContext *)context {
CXCallDirectoryPhoneNumber allPhoneNumbers[] = { 4917612345678, 4917612345679 };
NSArray<NSString *> *labels = #[ #"XYZ Dev-Test", #"Test Number" ];
NSUInteger count = (sizeof(allPhoneNumbers) / sizeof(CXCallDirectoryPhoneNumber));
for (NSUInteger i = 0; i < count; i += 1) {
CXCallDirectoryPhoneNumber phoneNumber = allPhoneNumbers[i];
NSString *label = labels[i];
[context addIdentificationEntryWithNextSequentialPhoneNumber:phoneNumber label:label];
}
}
- (void)addOrRemoveIncrementalIdentificationPhoneNumbersToContext:(CXCallDirectoryExtensionContext *)context {
CXCallDirectoryPhoneNumber phoneNumbersToAdd[] = { 4917612345678 };
NSArray<NSString *> *labelsToAdd = #[ #"XYZ Dev-Test Inc" ];
NSUInteger countOfPhoneNumbersToAdd = (sizeof(phoneNumbersToAdd) / sizeof(CXCallDirectoryPhoneNumber));
for (NSUInteger i = 0; i < countOfPhoneNumbersToAdd; i += 1) {
CXCallDirectoryPhoneNumber phoneNumber = phoneNumbersToAdd[i];
NSString *label = labelsToAdd[i];
[context addIdentificationEntryWithNextSequentialPhoneNumber:phoneNumber label:label];
}
}
Again, this is automatically generated code and I only changed the phone numbers and strings in it.
I have enabled the extension under Settings -> Phone -> Call blocking & identification and have toggled the switch many times already.
However when I call the phone from that number, it is never identified. I've already tried the following number formats in the code:
+4917612345678
4917612345678
017612345678
17612345678
I noticed that the iPhone 6 plus with iOS 11.1 that I use for testing displays the incoming call's number as 0176 12345678.
I also tried the following:
reset all settings on the phone
delete the app from the device
run the app from Xcode with the scheme of the main app
run the app again with the scheme of the extension
select my main app in the dialog asking to choose an app
Nonetheless the number identification doesn't work. I'm not even sure the app extension is executed, because I can't debug it with break points and the NSLog entries I've added for testing didn't appear in the console.
Is there an important step that I am missing here?
Phone numbers supplied via the extension must always be prefixed by the country's dialing code (49 in your case). And if the number incoming to the phone is 0176 12345678, you probably should try using 49017612345678 (note the zero).

How to update a UILabel in realtime when receiving Serial.print statements

I am using a Bluno microcontroller to send / receive data from an iPhone, and everything is working as it should, but I would like to update the text of a UILabel with the real time data that is being printed from the Serial.print(numTicks); statement. If I stop the flowmeter the UILabel gets updated with the most current value, but I would like to update this label in realtime. I am not sure if this is a C / Arduino question or more of a iOS / Objective-C question. The sketch I'm loading on my Bluno looks like the following, https://github.com/ipatch/KegCop/blob/master/KegCop-Bluno-sketch.c
And the method in question inside that sketch looks like the following,
// flowmeter stuff
bool getFlow4() {
// call the countdown function for pouring beer
// Serial.println(flowmeterPin);
flowmeterPinState = digitalRead(flowmeterPin);
// Serial.println(flowmeterPinStatePinState);
volatile unsigned long currentMillis = millis();
// if the predefined interval has passed
if (millis() - lastmillis >= 250) { // Update every 1/4 second
// disconnect flow meter from interrupt
detachInterrupt(0); // Disable interrupt when calculating
// Serial.print("Ticks:");
Serial.print(numTicks);
// numTicks = 0; // Restart the counter.
lastmillis = millis(); // Update lastmillis
attachInterrupt(0, count, FALLING); // enable interrupt
}
if(numTicks >= 475 || valveClosed == 1) {
close_valve();
numTicks = 0; // Restart the counter.
valveClosed = 0;
return 0;
}
}
On the iOS / Objective-C side of things I'm doing the following,
- (void)didReceiveData:(NSData *)data Device:(DFBlunoDevice *)dev {
// setup label to update
_ticks = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[_tickAmount setText:[NSString stringWithFormat:#"Ticks:%#",_ticks]];
[_tickAmount setNeedsDisplay];
NSLog(#"ticks = %#",_ticks);
}
Basically I would like to update the value of the UILabel while the flowmeter is working.
UPDATE
I just tested the functionality again with the serial monitor within the Arduino IDE, and I got the same if not similar results as to what I got via Xcode and the NSLog statements. So this leads me to believe something in the sketch is preventing the label from updating in real time. :/ Sorry for the confusion.

play multiple SpeakHere audio files

By recording multiple snippets using filenames, I have attempted to record multiple separate short voice snippets in SpeakHere, I want to play them serially, separated by a set fixed interval of time between the starts of each snippet. I want the series of snippets to play in a loop forever, or until the user stops play.
My question is how do I alter SpeakHere to do so?
(I say "attempted" because I have not been able yet to run SpeakHere on my Mac Mini iPhone simulator. That is the subject of another question and because another question on the subject of multiple files has not been answered, either.)
In SpeakHereController.mm is the following method definition for playing a recorded file. Notice the final else clause calls player->StartQueue(false)
- (IBAction)play:(id)sender
{
if (player->IsRunning())
{ [snip]
}
else
{
OSStatus result = player->StartQueue(false);
if (result == noErr)
[[NSNotificationCenter defaultCenter] postNotificationName:#"playbackQueueResumed" object:self];
}
}
Below is an excerpt from SpeakHere AQPlayer.mm
OSStatus AQPlayer::StartQueue(BOOL inResume)
{
// if we have a file but no queue, create one now
if ((mQueue == NULL) && (mFilePath != NULL)) CreateQueueForFile(mFilePath);
mIsDone = false;
// if we are not resuming, we also should restart the file read index
if (!inResume) {
mCurrentPacket = 0;
// prime the queue with some data before starting
for (int i = 0; i < kNumberBuffers; ++i) {
AQBufferCallback (this, mQueue, mBuffers[i]);
}
}
return AudioQueueStart(mQueue, NULL);
}
So, can the method play and AQPlayer::StartQueue be used to play the multiple files, how can the intervals be enforced, and how can the loop be repeated?
My adaptation of the code for the method 'record` is as follows, so you can see how the multiple files are being created.
- (IBAction)record:(id)sender
{
if (recorder->IsRunning()) // If we are currently recording, stop and save the file.
{
[self stopRecord];
}
else // If we're not recording, start.
{
self.counter = self.counter + 1 ; //Added *****
btn_play.enabled = NO;
// Set the button's state to "stop"
btn_record.title = #"Stop";
// Start the recorder
NSString *filename = [[NSString alloc] initWithFormat:#"recordedFile%d.caf",self.counter];
// recorder->StartRecord(CFSTR("recordedFile.caf"));
recorder->StartRecord((CFStringRef)filename);
[self setFileDescriptionForFormat:recorder->DataFormat() withName:#"Recorded File"];
// Hook the level meter up to the Audio Queue for the recorder
[lvlMeter_in setAq: recorder->Queue()];
}
}
Having spoken with a local "meetup" group on iOS I have learned that the easy solution to my question is to avoid AudioQueues and to instead use the "higher level" AVAudioRecorder and AVAudioPlayer from AVFoundation.
I also found how to partially test my app on the simulator with my Mac Mini: by plugging in an Olympus audio recorder with USB to my Mini as an input "voice". This works as an alternative to the iSight which does not produce an input audio on the Mini.

Resources