Whats my project .
voice calling .
Whats library in my project .
Asterisk server (version 11.0)
pjsip 2.5.1
siphon for UI
My achievement
One-to-One call working fine
My issues :-
I need to implement add new buddy feature so that we can do conference .
Whats My problems
I am unable to get conference Voice call . scenarios is A called to B voice working fine ,but when B add new buddy C then B and C communicate but A and C Unable to communicate .
This my code which i am using for calling One-toOne
if (([[_label text] length] > 0) &&
([phoneCallDelegate respondsToSelector:#selector(dialup:number:)]))
{
_lastNumber = [[NSString alloc] initWithString: [_label text]];
[_label setText:#""];
}
else
{
_lcd.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"lcd_top_simple.png"]];
[_label setText:_lastNumber];
[_lastNumber release];
}
}
Call.m file calling this below method .
status = pjsua_call_make_call(acc_id, &pj_uri, 0, NULL, NULL, call_id);
if (status != PJ_SUCCESS)
{
pjsua_perror(THIS_FILE, "Error making call", status);
}
There are missing some files Which is from "PJSIP" library. Please include the "PJSIP" library after compilation, or you may download the project from this link, this project have compiled "PJSIP" included.
static void on_call_media_state(pjsua_call_id call_id)
{
pjsua_call_info ci;
SiphonApplication *app = (SiphonApplication *)[SiphonApplication sharedApplication];
pjsua_call_get_info(call_id, &ci);
// PJ_LOG(3,(THIS_FILE,"on_call_media_state status %d count %d",
// ci.media_status
// pjmedia_conf_get_connect_count()));
/* FIXME: Stop ringback */
sip_ring_stop([app pjsipConfig]);
/* Connect ports appropriately when media status is ACTIVE or REMOTE HOLD,
* otherwise we should NOT connect the ports.
*/
if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE ||
ci.media_status == PJSUA_CALL_MEDIA_REMOTE_HOLD)
{
// When media is active, connect call to sound device.
pjsua_conf_connect(ci.conf_slot, 0);
pjsua_conf_connect(0, ci.conf_slot);
//pjsua_conf_adjust_rx_level(0, 3.0);
//pjsua_conf_adjust_tx_level(0, 5.0);
}
if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) { // When media is active, connect call to sound device.
pjsua_conf_port_id slotOne = ci.conf_slot;
// pjsua_conf_connect(slotOne, 0);
// pjsua_conf_connect(0, slotOne);
//mergeCalls=true;
int max=pjsua_call_get_count();
if (max==2) {
mergeCalls=true;
}
if (mergeCalls == true) {
pjsua_conf_port_id slotTwo = pjsua_call_get_conf_port(activeCallID);
pjsua_conf_connect(slotOne, slotTwo);
pjsua_conf_connect(slotTwo, slotOne);
// since the "activeCallID" is already talking, its conf_port is already connected to "0" (and vice versa) ...
} else {
activeCallID = call_id;
}
} else if (ci.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
// … callSuspended(callID);
}
}
Related
I am using PJSIP 2.0,Xcode 7.3 and ios 9.2.1,
Suppose we have 3 users A,B & C user.
When ever A call to B (its working fine ) but when A add member C in call (as conference call) then A & B can communicate with C but C never get Voice from B .(C & A communicate fine ).So how to manage communication between C & B .
This code working fine for me , only need to merge pjsua_conf_connect() slot number 0 1 and , which is our requirement.
static void on_call_media_state(pjsua_call_id call_id)
{
pjsua_call_info ci;
SiphonApplication *app = (SiphonApplication *)[SiphonApplication sharedApplication];
pjsua_call_get_info(call_id, &ci);
// PJ_LOG(3,(THIS_FILE,"on_call_media_state status %d count %d",
// ci.media_status
// pjmedia_conf_get_connect_count()));
/* FIXME: Stop ringback */
sip_ring_stop([app pjsipConfig]);
/* Connect ports appropriately when media status is ACTIVE or REMOTE HOLD,
* otherwise we should NOT connect the ports.
*/
pjsua_call_media_status slotOne = ci.media_status;
if (slotOne == PJSUA_CALL_MEDIA_ACTIVE ||
slotOne == PJSUA_CALL_MEDIA_REMOTE_HOLD)
{
// When media is active, connect call to sound device.
pjsua_conf_connect(ci.conf_slot, 0);
pjsua_conf_connect(0, ci.conf_slot);
pjsua_conf_adjust_rx_level(0, 3.0);
pjsua_conf_adjust_tx_level(0, 5.0);
}
[[NSUserDefaults standardUserDefaults] setObject: #"CallIsRunning"forKey:#"CallIsRunning"];
if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) { // When media is active, connect call to sound device.
pjsua_conf_port_id slotOne = ci.conf_slot;
// pjsua_conf_connect(slotOne, 0);
// pjsua_conf_connect(0, slotOne);
//mergeCalls=true;
mergeCalls=false;
int max=pjsua_call_get_count();
if (max==2) {
[[NSUserDefaults standardUserDefaults] setInteger:pjsua_call_get_count() forKey:#"callCountForCalling"];
mergeCalls=true;
}
NSString *grpID= [[NSUserDefaults standardUserDefaults] objectForKey:#"callerTypegroup"];
// if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"callerTypegroup"] length]>1)
if (mergeCalls == true && grpID.length==0) {
pjsua_conf_port_id slotTwo = pjsua_call_get_conf_port(activeCallID);
pjsua_conf_connect(slotOne, slotTwo);
pjsua_conf_connect(slotTwo, slotOne);
// since the "activeCallID" is already talking, its conf_port is already connected to "0" (and vice versa) ...
} else {
activeCallID = call_id;
}
} else if (ci.media_status == PJSUA_CALL_MEDIA_LOCAL_HOLD) {
// … callSuspended(callID);
}
}
You can use pjsip ios library 2.5 latest build and another call. Second is using webservice to originate call and redirect channels to confbridge for asterisk as sip server.
If you are using another media server then share the details as well.
I am trying to read an audio file (that is not supported by iOS) with ffmpeg and then play it using AVAudioPlayer. It took me a while to get ffmpeg built inside an iOS project, but I finally did using kewlbear/FFmpeg-iOS-build-script.
This is the snippet I have right now, after a lot of searching on the web, including stackoverflow. One of the best examples I found was here.
I believe this is all the relevant code. I added comments to let you know what I'm doing and where I need something clever to happen.
#import "FFmpegWrapper.h"
#import <AVFoundation/AVFoundation.h>
AVFormatContext *formatContext = NULL;
AVStream *audioStream = NULL;
av_register_all();
avformat_network_init();
avcodec_register_all();
// this is a file locacted on my NAS
int opened = avformat_open_input(&formatContext, #"http://192.168.1.70:50002/m/NDLNA/43729.flac", NULL, NULL);
// can't open file
if(opened == 1) {
avformat_close_input(&formatContext);
}
int streamInfoValue = avformat_find_stream_info(formatContext, NULL);
// can't open stream
if (streamInfoValue < 0)
{
avformat_close_input(&formatContext);
}
// number of streams available
int inputStreamCount = formatContext->nb_streams;
for(unsigned int i = 0; i<inputStreamCount; i++)
{
// I'm only interested in the audio stream
if(formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
// found audio stream
audioStream = formatContext->streams[i];
}
}
if(audioStream == NULL) {
// no audio stream
}
AVFrame* frame = av_frame_alloc();
AVCodecContext* codecContext = audioStream->codec;
codecContext->codec = avcodec_find_decoder(codecContext->codec_id);
if (codecContext->codec == NULL)
{
av_free(frame);
avformat_close_input(&formatContext);
// no proper codec found
}
else if (avcodec_open2(codecContext, codecContext->codec, NULL) != 0)
{
av_free(frame);
avformat_close_input(&formatContext);
// could not open the context with the decoder
}
// this is displaying: This stream has 2 channels and a sample rate of 44100Hz
// which makes sense
NSLog(#"This stream has %d channels and a sample rate of %dHz", codecContext->channels, codecContext->sample_rate);
AVPacket packet;
av_init_packet(&packet);
// this is where I try to store in the sound data
NSMutableData *soundData = [[NSMutableData alloc] init];
while (av_read_frame(formatContext, &packet) == 0)
{
if (packet.stream_index == audioStream->index)
{
// Try to decode the packet into a frame
int frameFinished = 0;
avcodec_decode_audio4(codecContext, frame, &frameFinished, &packet);
// Some frames rely on multiple packets, so we have to make sure the frame is finished before
// we can use it
if (frameFinished)
{
// this is where I think something clever needs to be done
// I need to store some bytes, but I can't figure out what exactly and what length?
// should the length be multiplied by the of the number of channels?
NSData *frameData = [[NSData alloc] initWithBytes:packet.buf->data length:packet.buf->size];
[soundData appendData: frameData];
}
}
// You *must* call av_free_packet() after each call to av_read_frame() or else you'll leak memory
av_free_packet(&packet);
}
// first try to write it to a file, see if that works
// this is indeed writing bytes, but it is unplayable
[soundData writeToFile:#"output.wav" atomically:YES];
NSError *error;
// this is my final goal, playing it with the AVAudioPlayer, but this is giving unclear errors
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:soundData error:&error];
if(player == nil) {
NSLog(error.description); // Domain=NSOSStatusErrorDomain Code=1954115647 "(null)"
} else {
[player prepareToPlay];
[player play];
}
// Some codecs will cause frames to be buffered up in the decoding process. If the CODEC_CAP_DELAY flag
// is set, there can be buffered up frames that need to be flushed, so we'll do that
if (codecContext->codec->capabilities & CODEC_CAP_DELAY)
{
av_init_packet(&packet);
// Decode all the remaining frames in the buffer, until the end is reached
int frameFinished = 0;
while (avcodec_decode_audio4(codecContext, frame, &frameFinished, &packet) >= 0 && frameFinished)
{
}
}
av_free(frame);
avcodec_close(codecContext);
avformat_close_input(&formatContext);
Not really found a solution to this specific problem, but ended up using ap4y/OrigamiEngine instead.
My main reason I wanted to use FFmpeg is to play unsupported audio files (FLAC/OGG) on iOS and tvOS and OrigamiEngine does the job just fine.
I am working on with pjsip for iOS, I have configured the pjsip and able to register and reregister for specific time interval, but there is a scenario where I want to change the pjsip account details at the time of re register with new details but I havent found anything on google which can guide how to change it.
If someone have a idea about this please guide me through how to change the pjsua_acc_config details at time of re registration, I get the method call at the time of re registration.
static void on_reg_state2(pjsua_acc_id acc_id, pjsua_reg_info *info) {
PJ_LOG(3,(__FILE__, "%s: Account %d Reason %.*s Status %d code %d CurrentOp %d",
__FUNCTION__, acc_id, info->cbparam->reason.slen, info->cbparam->reason.ptr,
info->cbparam->status,info->cbparam->code, info->cbparam->regc));
}
get account configuration for account id, and set the fields to whatever required in on_reg_state2 function.
if (pjsua_acc_is_valid(acc_id))
{
pjsua_acc_set_default(acc_id);
pjsua_acc_config acc_cfg;
pj_status_t status;
pjsua_acc_config_default(&acc_cfg);
acc_cfg.id = pj_str(id);
acc_cfg.reg_uri = pj_str(registrar);
acc_cfg.cred_count = 1;
acc_cfg.cred_info[0].scheme = pj_str("Digest");
acc_cfg.cred_info[0].realm = pj_str(realm);
acc_cfg.cred_info[0].username = pj_str(uname);
acc_cfg.cred_info[0].data_type = 0;
acc_cfg.cred_info[0].data = pj_str(passwd);
acc_cfg.publish_enabled = PJ_TRUE;
}
Its like same as re register.
remove current account and add new one
if (_sip_acc_id != PJSUA_INVALID_ID){
// pjsua_acc_info info;
// pjsua_acc_get_info(_sip_acc_id, &info);
//
// if (info.has_registration){
pj_status_t statusDelete = pjsua_acc_del(_sip_acc_id);
if (statusDelete != PJ_SUCCESS)
{
pjsua_perror(THIS_FILE, "Error removing new account", status);
[app displayParameterError: #"Error removing new account."];
}
// }
}
status = pjsua_acc_add(&acc_cfg, PJ_TRUE, &_sip_acc_id);
if (status != PJ_SUCCESS)
{
pjsua_perror(THIS_FILE, "Error adding new account", status);
[app displayParameterError: #"Error adding new account."];
}
This is guide for recomendationabout PJSIP You can click this PJSIP
I have been working on a SIP client for iOS using PJSIP. Everything works great on iPhone all the models. But on iPad and iPod Touch the audio is not heard from the speakers of the iPad or the iPod Touch. The code works great for iPhone speakers. Since iPad and iPod do not have earpiece no audio is coming out from the SIP call from the speakers. Anyone who can let me know what could be wrong with the iPad implementation ? Here is the code that might help:
// Start and register sip account
int startPjsip(char sipUser, char sipDomain, char* scheme, char* realm, char* username, int dataType, char* passwd, char* proxy, char* protocol)
{
pj_status_t status;
pj_log_set_level(5);
// Create pjsua first
status = pjsua_create();
if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);
// Init pjsua
{
// Init the config structure
pjsua_config cfg;
pjsua_config_default (&cfg);
cfg.cb.on_incoming_call = &on_incoming_call;
cfg.cb.on_call_media_state = &on_call_media_state;
cfg.cb.on_call_state = &on_call_state;
cfg.cb.on_reg_state = &on_reg_state;
// Init the logging config structure
pjsua_logging_config log_cfg;
pjsua_logging_config_default(&log_cfg);
log_cfg.console_level = 4;
// Init the pjsua
status = pjsua_init(&cfg, &log_cfg, NULL);
if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);
// If outbound proxy is required.
if (proxy) {
cfg.outbound_proxy_cnt = 1;
cfg.outbound_proxy[0] = pj_str(proxy);
}
}
if (strcasecmp(protocol, "UDP") == 0)
{
/* iOS version above iOS 4 do not support UDP in bakground. */
// Add UDP transport.
{
// Init transport config structure
pjsua_transport_config cfg;
pjsua_transport_config_default(&cfg);
cfg.port = 5060;
// Add UDP transport.
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, NULL);
if (status != PJ_SUCCESS) error_exit("Error creating transport", status);
}
}
if (strcasecmp(protocol, "TCP") == 0)
{
// Add TCP transport.
{
// Init transport config structure
pjsua_transport_config cfg;
pjsua_transport_config_default(&cfg);
cfg.port = 5060;
// Add TCP transport.
status = pjsua_transport_create(PJSIP_TRANSPORT_TCP, &cfg, NULL);
if (status != PJ_SUCCESS) error_exit("Error creating transport", status);
}
}
// Initialization is done, now start pjsua
status = pjsua_start();
if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);
// Register the account on local sip server
{
pjsua_acc_config cfg;
pjsua_acc_config_default(&cfg);
char sipId[MAX_SIP_ID_LENGTH];
sprintf(sipId, "sip:%s#%s", sipUser, sipDomain);
cfg.id = pj_str(sipId);
char regUri[MAX_SIP_REG_URI_LENGTH];
sprintf(regUri, "sip:%s", sipDomain);
cfg.reg_uri = pj_str(regUri);
// Add sip account credentials.
cfg.cred_count = 1;
cfg.cred_info[0].scheme = pj_str(scheme);
cfg.cred_info[0].realm = pj_str(realm);
cfg.cred_info[0].username = pj_str(username);
cfg.cred_info[0].data_type = dataType;
cfg.cred_info[0].data = pj_str(passwd);
status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
if (status != PJ_SUCCESS) error_exit("Error adding account", status);
}
return 0;
}
static void on_call_media_state(pjsua_call_id call_id)
{
pjsua_call_info ci;
pjsua_call_get_info(call_id, &ci);
if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
// When media is active, connect call to sound device.
pjsua_conf_connect(ci.conf_slot, 0);
pjsua_conf_connect(0, ci.conf_slot);
pjsua_conf_adjust_rx_level(0, 1);
pjsua_conf_adjust_tx_level(0, 1);
}
PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", call_id,
(int)ci.state_text.slen,
ci.state_text.ptr));
}
Here's how I initialize SDL:
//initialize engine and set main loop callback... kind of awkward is there a better way to do this?
extern "C"{
CBCore::Engine *engine;
void UpdateLoop(void *f)
{
engine->updateLoop();
}
int main(int argc, char *argv[])
{
engine = new CBCore::Engine();
engine->init();
Assert(engine->window(), "Window not set for iOS animation callback");
SDL_iPhoneSetAnimationCallback(engine->window(), 1, UpdateLoop, NULL);
return 0;
}
}
//init function of engine, initializes required SDL subsystems and creates window
void Engine::init()
{
SDL_Log("Initializing engine!");
/* initialize SDL */
if(SDL_Init(0) != 0){
Assert(0, "Could not initialize SDL");
}
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
Assert(0, "Could not initialize SDL Video");
}
if(SDL_InitSubSystem(SDL_INIT_TIMER) != 0){
Assert(0, "Could not initialize SDL Timer");
}
_window = SDL_CreateWindow(NULL, 0, 0, 480, 320, SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_BORDERLESS | SDL_WINDOW_OPENGL);
Assert(_window, "Could not create window");
_context = SDL_GL_CreateContext(pimpl->window);
Assert(_context, "Could not create context");
SDL_Log("Engine successfully initialized!");
}
//loop
void Engine::updateLoop()
{
SDL_Event event;
SDL_Log("Polling events");
while(SDL_PollEvent(&event)) {
SDL_Log("Polled event %d", event.type);
}
}
The loop gets called and I get 4 Events at startup: SDL_APP_DIDENTERFOREGROUND, SDL_WINDOWEVENT_SIZE_CHANGED, SDL_WINDOWEVENT_SHOWN and SDL_WINDOWEVENT_ENTER; I also got enter background messages and the likes. But I can click/touch on the simulator/device till the cows go home but no event is received... Am I missing something?
I was finally able to solve it by calling SDL_GL_SwapWindow, after that events get processed.