Posted: Sat May 10, 2008 8:30 am Post subject: [Asterisk-video] Configuration file for h324m stack (h324m.c
Hi alls,
This is a proposed patch to add a configuration file to the h324m stack.
I have added a configuration file to :
- Enable the debug traces (for 0 to 10)
- Configure the channel codec request for the h324m_gw_call function.
I have add a CLI command to reload the configuration (CLI> h324m reload).
The related modifications are marked with (#ifndef i6net_config and #endif).
Regards,
Tech from i6net
/*
* Asterisk -- An open source telephony toolkit.
*
* Sergio Garcia Murillo <sergio.garcia@ydilo.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
static char *name_h324m_loopback = "h324m_loopback";
static char *syn_h324m_loopback = "H324m loopback mode";
static char *des_h324m_loopback = " h324m_loopback([options]): Establish H.324M connection and loopback media.\n"
"\n"
"Note: By default this function loops audio and video. Looping audio can cause\n"
"acoustic feedback. To prevent this you can turn it off using the 'a' option.\n"
"\n"
"Available options:\n"
" 'a': deactivate loopback of audio\n"
" 'v': deactivate loopback of video\n"
"\n"
"Note: This function just loops the h324m audio and video frames. Thus, there \n"
"is no conversion from h324m frames into Asterisk internal frame format. If \n"
"you want to test the conversion from h324m frames to Asterisk frames and \n"
"vice versa too, do not use h324m_loopback() but use h324m_gw() and the Echo()\n"
"application.\n"
"\n"
"Examples:\n"
" h324m_loopback(): loopback audio and video\n"
" h324m_loopback(a): deactivate loopback of audio\n"
" h324m_loopback(v): deactivate loopback of video\n"
" h324m_loopback(av): deactivate loopback of audio and video\n";
static char *name_h324m_gw = "h324m_gw";
static char *syn_h324m_gw = "H324m gateway";
static char *des_h324m_gw = " h324m_gw(extension@context): Creates a pseudo channel for an incoming h324m call.\n"
"This function decodes the received H.324M data (usually via a ISDN connection\n"
"and extracts the video and voice frames into Asterisk's internal frame format\n"
"and vice versa.\n"
"A pseudo channel will be created to continue dialplan execution at another\n"
"extension/context.\n"
"\n"
"Examples:\n"
" [frompstn]\n"
" exten => 111,1,h324m_gw(britney@3gp_videos)\n"
" exten => 112,1,h324m_gw(justin@3gp_videos)\n"
" [3gp_videos]\n"
" exten => britney,1,h324m_gw_answer()\n"
" exten => britney,2,mp4_play(/var/videos/britney.3gp)\n"
" exten => justin,1,h324m_gw_answer()\n"
" exten => justin,2,mp4_play(/var/videos/justin.3gp)\n";
static char *name_h324m_call = "h324m_call";
static char *syn_h324m_call = "H324m call";
static char *des_h324m_call = " h324m_call(extension@context): Creates a pseudo channel for an outgoing h324m call.\n"
"This function encodes the video and voice frames from Asterisk's internal\n"
" frame format into H.324M data and vice versa.\n"
"A pseudo channel will be created to continue dialplan execution at another\n"
"extension/context.\n"
"\n"
"Examples:\n"
" [fromsip]\n"
" ;prefix 0 means PSTN with normal audio call\n"
" ;prefix 1 means PSTN with 3G video calls\n"
" exten => _0X.,1,Dial(Zap/${EXTEN:1}\n"
" exten => _1X.,1,h324m_call(0${EXTEN:1}@fromsip)\n";
static char *name_h324m_gw_answer = "h324m_gw_answer";
static char *syn_h324m_gw_answer = "H324m Answer incoming call";
static char *des_h324m_gw_answer = " h324m_gw_answer(): Answer an incomming call from h324m_gw and waits for 3G negotiation.\n";
static char *name_video_loopback = "video_loopback";
static char *syn_video_loopback = "video_loopback";
static char *des_video_loopback = " video_loopback(): Video loopback.\n"
"This function just loops the Asterisk video frames. Thus, it is similar\n"
"to the Echo() application but only loops video (the Echo application loops\n"
"audio and video). To use this function with H324M calls you first have to use\n"
"the h324m_gw() function.\n";
#ifndef i6net_config
/* Configuration file */
static int load_config(void)
{
struct ast_config *cfg;
struct ast_variable *var;
char *tmp;
int level;
cfg = (void *)ast_config_load(config);
if (!cfg)
{
ast_log(LOG_WARNING,
"Unable to load config for h324m : %s\n", config);
return -1;
}
var = (void *)ast_variable_browse(cfg, "general");
if (!var)
{
ast_log(LOG_WARNING, "Nothing configured for h324m.\n");
/* nothing configured */
return 0;
}
static struct ast_frame* create_ast_frame(void *frame, struct video_creator *vt)
{
int mark = 0;
unsigned int i = 0;
short j = 0;
int found = 0;
unsigned int len = 0;
struct ast_frame* send;
unsigned char* data = 0;
/* Set data */
send->data = (unsigned char*)send + AST_FRIENDLY_OFFSET + sizeof(struct ast_frame);
data = send->data;
/* Depending on the type */
switch(FrameGetType(frame))
{
case MEDIA_AUDIO:
/*Check it's AMR */
if (FrameGetCodec(frame)!=CODEC_AMR)
/* exit */
return NULL;
ast_log(LOG_DEBUG, "create_ast_frame: received AMR frame with %d bytes\n",framelength);
/* Set video type */
send->frametype = AST_FRAME_VOICE;
/* Set codec value */
send->subclass = AST_FORMAT_AMRNB;
/* Rest of values*/
send->src = "h324m";
send->samples = 160;
send->delivery.tv_usec = 0;
send->delivery.tv_sec = 0;
/* Don't free */
send->mallocd = 0;
/* Send */
return send;
case MEDIA_VIDEO:
/*Check it's H263 */
if (FrameGetCodec(frame)!=CODEC_H263)
/* exit */
return NULL;
/* Search from the begining */
i = 0;
found = 0;
/* Check length*/
if(framelength>3)
{
/* Try to find begining of frame */
while (!found && i<framelength-4)
{
/* Check if we found start code */
if (framedata[i] == 0 && framedata[i+1] == 0 && (framedata[i+2] & 0xFC) == 0x80)
/* We found it */
found = 1;
else
/* Increase pointer */
i++;
}
}
/* If still in the same frame */
if (found)
{
/* Send what was on the buffer plus the beggining of the packet*/
len = vt->bufferLength + i;
/* Last packet */
mark = 1;
/* Update recived ts */
vt->tvnext = ast_tvnow();
} else {
/* Send only what was on the buffer */
len = vt->bufferLength;
/* Not last packet */
mark = 0;
}
/* if its first pcaket of a frame */
if (vt->first)
{
/* If it's not empty */
if (vt->bufferLength)
{
/* Set data len */
send->datalen = vt->bufferLength;
/* Copy */
memcpy(data+2, vt->buffer+2, vt->bufferLength-2);
} else {
/* Only header part by bow */
send->datalen = 2;
}
/* Set header */
data[0] = 0x04;
data[1] = 0x00;
} else {
/* Set data len */
send->datalen = vt->bufferLength + 2 ;
/* If it's not empty */
if (vt->bufferLength)
/* Copy */
memcpy(data+2, vt->buffer, vt->bufferLength);
/* Set header */
data[0] = 0x00;
data[1] = 0x00;
}
/* Assertion test */
if (i>framelength)
{
/* Never should happen */
ast_log(LOG_ERROR, "Counter past of frame [%d,%d]\n",i,framelength);
/* Empty */
vt->bufferLength = 0;
/* If we have to send the begging of this frame */
} else if (i>0 && found) {
/* Copy the begining to the packet to send*/
memcpy(data+send->datalen,framedata,i);
/* Increase size */
send->datalen += i;
/* Copy the rest to the buffer */
memcpy(vt->buffer,framedata+i,framelength-i);
/* Set buffer length */
vt->bufferLength = framelength-i;
} else {
/* Copy the whole packet to the buffer */
memcpy(vt->buffer,framedata,framelength);
/* Set buffer length */
vt->bufferLength = framelength;
}
/* Set video type */
send->frametype = AST_FRAME_VIDEO;
/* Set codec value */
send->subclass = AST_FORMAT_H263_PLUS | mark;
/* Rest of values*/
send->src = "h324m";
send->samples = vt->samples;
send->delivery.tv_usec = 0;
send->delivery.tv_sec = 0;
/* Don't free */
send->mallocd = 0;
/* If the next packet is from a different frame */
if (mark)
{
/* Calculate ms */
int ms = 0;
/* If it's not first*/
if (!ast_tvzero(vt->tv))
/* Get the difference in ms */
ms = ast_tvdiff_ms(vt->tvnext,vt->tv);
/* Change tr */
vt->tv = vt->tvnext;
/* Update samles */
vt->samples = ms*90;
/* Set it's the first */
vt->first = 1;
} else {
/* Next it's not first */
vt->first = 0;
}
/* Send */
return send;
}
/* NOthing */
return NULL;
}
struct h324m_packetizer
{
unsigned char *framedata;
unsigned char *offset;
int framelength;
int num;
int max;
};
static int init_h324m_packetizer(struct h324m_packetizer *pak,struct ast_frame* f)
{
int i;
/* Empty data */
memset(pak,0,sizeof(struct h324m_packetizer));
/* Depending on the type */
switch (f->frametype)
{
case AST_FRAME_VOICE:
/* Check audio type */
if (!(f->subclass & AST_FORMAT_AMRNB))
/* exit */
return 0;
/* Get data & length */
pak->framedata = (unsigned char *)f->data;
pak->framelength = f->datalen;
/* Read toc until no mark found, skip first byte */
while ((++pak->max < pak->framelength) && (pak->framedata[pak->max] & 0x80)) {}
/* Check lenght */
if (pak->max >= pak->framelength)
/* Exit */
return 0;
ast_log(LOG_DEBUG, "init_h324m_packetizer: found %d AMR frames inside ast_frame\n",pak->max);
/* Set offset */
pak->offset = pak->framedata + pak->max + 1; /* +1 because of CMR octet */
/* Move toc to the beginning so we can overwrite the byte before the frame */
/* This overwrites the CMR but it is not needed at all */
for (i=0;i < pak->max;i++)
/* copy */
pak->framedata[i] = pak->framedata[i+1];
/* Good one */
return 1;
case AST_FRAME_VIDEO:
/* Depending on the codec */
if (f->subclass & AST_FORMAT_H263)
{
/* Get data & length without rfc 2190 (only A packets ) */
pak->framedata = (unsigned char *)f->data+4;
pak->framelength = f->datalen-4;
} else if (f->subclass & AST_FORMAT_H263_PLUS) {
/* Get initial data */
pak->framedata = (unsigned char *)f->data;
pak->framelength = f->datalen;
/* Get header */
unsigned char p = pak->framedata[0] & 0x04;
unsigned char v = pak->framedata[0] & 0x02;
unsigned char plen = ((pak->framedata[0] & 0x1 ) << 5 ) | (pak->framedata[1] >> 3);
unsigned char pebit = pak->framedata[0] & 0x7;
/* skip header*/
pak->framedata += 2+plen;
pak->framelength -= 2+plen;
/* Check */
if (v)
{
/* Increase ini */
pak->framedata++;
pak->framelength--;
}
/* Check p bit */
if (p)
{
/* Decrease ini */
pak->framedata -= 2;
pak->framelength += 2;
/* Append 0s */
pak->framedata[0] = 0;
pak->framedata[1] = 0;
}
} else
break;
/* Only 1 packet */
pak->max = 1;
/* Exit */
return 1;
default:
/* dummy statement to make compiler happy */
;
}
/* Nothing to do */
return 0;
}
static void* create_h324m_frame(struct h324m_packetizer *pak,struct ast_frame* f)
{
int i = 0;
/* if not more */
if (pak->num == pak->max) {
/* Exit */
return NULL;
}
pak->num++;
ast_log(LOG_DEBUG, "create_h324m_frame: processing AMR frame #%d inside ast_frame\n",pak->num);
/* Depending on the type */
switch (f->frametype)
{
case AST_FRAME_VOICE:
/* Check audio type */
if (!(f->subclass & AST_FORMAT_AMRNB))
/* exit */
break;
/* Convert to if2 */
/* Get header: pak->framedata starts with ToC as CMR was
overwritten in init_h324m_packetizer() */
unsigned char header = pak->framedata[pak->num-1];
/* Get mode */
unsigned char mode = (header >> 3 ) & 0x0f;
/* Get blockSize */
signed bs = blockSize[mode];
if (bs < 0) {
ast_log(LOG_DEBUG, "create_h324m_frame: error decoding AMR structure - AMR frame #%d has block size %d\n",
pak->num,bs);
/* exit */
break;
}
/*Get Stuffing bits*/
int stuf = if2stuffing[mode];
/* Check input paramaters */
if (strchr(data,'v'))
/* deactivate video loopback */
loop_video=0;
/* Create session */
void* id = H324MSessionCreate();
/* Init session */
H324MSessionInit(id);
/* Wait for data avaiable on channel */
while (ast_waitfor(chan, -1) > -1)
{
/* Read frame from channel */
f = ast_read(chan);
/* if it's null */
if (f == NULL)
break;
/* Check frame type */
if (f->frametype == AST_FRAME_VOICE)
{
/* read data */
H324MSessionRead(id, (unsigned char *)f->data, f->datalen);
/* Get frames */
while ((frame=H324MSessionGetFrame(id))!=NULL)
{
if (FrameGetType(frame)==MEDIA_VIDEO)
{
/* If video loopback is activated */
if (loop_video)
/* Send it back */
H324MSessionSendFrame(id,frame);
} else if (FrameGetType(frame)==MEDIA_AUDIO) {
/* If audio loopback is activated */
if (loop_audio)
/* Send it back. Note: this can cause loopback/echo problems */
H324MSessionSendFrame(id,frame);
}
/* Delete frame */
FrameDestroy(frame);
}
/* write data */
H324MSessionWrite(id, (unsigned char *)f->data, f->datalen);
/* deliver now */
f->delivery.tv_usec = 0;
f->delivery.tv_sec = 0;
/* write frame */
ast_write(chan, f);
}
}
/* Destroy session */
H324MSessionEnd(id);
/* Destroy session */
H324MSessionDestroy(id);
ast_log(LOG_DEBUG, "exit");
/* Unlock module*/
ast_module_user_remove(u);
//Exit
return 0;
}
static int app_h324m_gw(struct ast_channel *chan, void *data)
{
struct ast_frame *f;
struct ast_frame *send;
struct ast_module_user *u;
struct h324m_packetizer pak;
struct video_creator vt;
void* frame;
char* input;
char* src = 0;
int reason = 0;
int state = 0;
int ms;
struct ast_channel *channels[2];
struct ast_channel *pseudo;
struct ast_channel *where;
/* If somthing has gone wrong */
if (!pseudo)
/* goto end */
goto end;
/* Set caller id */
ast_set_callerid(pseudo, chan->cid.cid_num, chan->cid.cid_name, chan->cid.cid_num);
/* Place call */
if (ast_call(pseudo,data,0))
/* if fail goto clean */
goto clean_pseudo;
/* Set up array */
channels[0] = chan;
channels[1] = pseudo;
/* No timeout */
ms = -1;
/* while not setup */
while (pseudo->_state!=AST_STATE_UP)
{
/* Wait for data */
if ((where = ast_waitfor_n(channels, 2, &ms))<0)
/* error, timeout, or done */
break;
/* Read frame */
f = ast_read(where);
/* If not frame */
if (!f)
/* done */
break;
/* Check channel */
if (where==pseudo)
{
/* If it's a control frame */
if (f->frametype == AST_FRAME_CONTROL)
{
/* Dependinf on the event */
switch (f->subclass)
{
case AST_CONTROL_RINGING:
ast_indicate(chan, AST_CONTROL_RINGING);
break;
case AST_CONTROL_BUSY:
case AST_CONTROL_CONGESTION:
/* Delete frame */
ast_frfree(f);
/* Save cause */
reason = pseudo->hangupcause;
/* exit */
goto hangup_pseudo;
break;
case AST_CONTROL_ANSWER:
/* Set UP*/
reason = 0;
break;
}
}
} else {
/* If it's a control frame */
if (f->frametype == AST_FRAME_CONTROL)
{
/* Depending on the event */
switch (f->subclass)
{
case AST_CONTROL_HANGUP:
/* Delete frame */
ast_frfree(f);
/* Save cause */
reason = pseudo->hangupcause;
/* exit */
goto hangup_pseudo;
break;
}
}
}
/* Delete frame */
ast_frfree(f);
}
/* If no answer */
if (pseudo->_state != AST_STATE_UP)
/* goto end */
goto clean_pseudo;
/* Create session */
void* id = H324MSessionCreate();
/* Init session */
H324MSessionInit(id);
/* Answer call */
ast_answer(chan);
/* Wait for data avaiable on any channel */
while (!reason && (where = ast_waitfor_n(channels, 2, &ms)) != NULL)
{
/* Read frame from channel */
f = ast_read(where);
/* if it's null */
if (f == NULL)
break;
/* If it's on h324m channel */
if (where==chan)
{
/* Check frame type */
if ((f->frametype == AST_FRAME_DIGITAL) || (f->frametype == AST_FRAME_VOICE))
{
/* read data */
H324MSessionRead(id, (unsigned char *)f->data, f->datalen);
/* If state changed */
if (state!=H324MSessionGetState(id))
{
/* Update state */
state = H324MSessionGetState(id);
/* Log */
ast_log(LOG_DEBUG, "H324M changed state %d\n", state);
/* If connected */
if (state==CALLSTATE_STABLISHED)
{
/* Log */
ast_log(LOG_DEBUG, "Connected, sending VIDUPDATE\n");
/* Indicate Video Update */
ast_indicate(pseudo, AST_CONTROL_VIDUPDATE);
}
}
/* Get frames */
while ((frame=H324MSessionGetFrame(id))!=NULL)
{
/* Packetize outgoing frame */
if ((send=create_ast_frame(frame,&vt))!=NULL)
/* Send frame */
ast_write(pseudo,send);
/* Delete frame */
FrameDestroy(frame);
}
/* Get user input */
while((input=H324MSessionGetUserInput(id))!=NULL)
{
/* Send digit begin */
ast_senddigit_begin(pseudo,input[0]);
/* Send digit end */
ast_senddigit_end(pseudo,input[0],100);
/* free data */
free(input);
}
/* we only do support AMR as voice codec - thus make sure that
Asterisk's core transcode voice frames to/from AMR */
if (ast_set_read_format(chan, AST_FORMAT_AMRNB))
ast_log(LOG_WARNING, "app_h324m_call: Unable to set read format to AMR-NB!\n");
if (ast_set_write_format(chan, AST_FORMAT_AMRNB))
ast_log(LOG_WARNING, "app_h324m_call: Unable to set read format to AMR-NB!\n");
/* Request new channel */
/* sometimes Asterisk uses internally a differnt LAW then chan_zap/zaptel and
* performs ALAW/ULAW conversion. Is is deadly as we transmit digital data inside
* LAW-frames (we have to do this as Asterisk does not support digital ISDN calls).
*
* If you have problems on outgoing 3G calls please specify exactly the LAW used
* by your ISDN line. Usually in Europe you have ALAW, in USA ULAW.
*
* Example for Austria(Europe):
* pseudo = ast_request("Local", AST_FORMAT_ALAW , data, &reason);
*/
/* If somthing has gone wrong */
if (!pseudo)
/* goto end */
goto end;
/* Set caller id */
ast_set_callerid(pseudo, chan->cid.cid_num, chan->cid.cid_name, chan->cid.cid_num);
/* Place call */
if (ast_call(pseudo,data,0))
/* if fail goto clean */
goto clean_pseudo;
/* Set up array */
channels[0] = chan;
channels[1] = pseudo;
/* No timeout */
ms = -1;
/* while not setup */
while (pseudo->_state!=AST_STATE_UP)
{
/* Wait for data */
if ((where = ast_waitfor_n(channels, 2, &ms))<0)
/* error, timeout, or done */
break;
/* Read frame */
f = ast_read(where);
/* If not frame */
if (!f)
/* done */
break;
/* Check channel */
if (where==pseudo)
{
/* If it's a control frame */
if (f->frametype == AST_FRAME_CONTROL)
{
/* Depending on the event */
switch (f->subclass)
{
case AST_CONTROL_RINGING:
ast_indicate(chan, AST_CONTROL_RINGING);
break;
case AST_CONTROL_BUSY:
ast_log(LOG_DEBUG, "h324m_call: pseudo channel: BUSY\n");
ast_log(LOG_DEBUG, "h324m_call: pseudo channel: hangupcause=%d\n",pseudo->hangupcause);
/* Delete frame */
ast_frfree(f);
/* Save cause */
reason = AST_CAUSE_BUSY;
/* exit */
goto hangup_pseudo;
break;
case AST_CONTROL_CONGESTION:
ast_log(LOG_DEBUG, "h324m_call: pseudo channel: CONGESTION\n");
ast_log(LOG_DEBUG, "h324m_call: pseudo channel: hangupcause=%d\n",pseudo->hangupcause);
/* Delete frame */
ast_frfree(f);
/* Save cause */
reason = AST_CAUSE_CONGESTION;
/* exit */
goto hangup_pseudo;
break;
case AST_CONTROL_ANSWER:
/* Set UP*/
reason = 0;
break;
default:
reason = pseudo->hangupcause;
}
}
} else {
/* If it's a control frame */
if (f->frametype == AST_FRAME_CONTROL)
{
/* Depending on the event */
switch (f->subclass)
{
case AST_CONTROL_HANGUP:
/* Delete frame */
ast_frfree(f);
/* Save cause */
reason = pseudo->hangupcause;
/* exit */
goto hangup_pseudo;
break;
}
}
}
/* Delete frame */
ast_frfree(f);
}
/* If no answer */
if (pseudo->_state != AST_STATE_UP)
{ ast_log(LOG_DEBUG, "h324m_call: pseudo channel not up -> hangup\n");
/* goto end */
goto clean_pseudo;
}
/* Create session */
void* id = H324MSessionCreate();
/* Init session */
H324MSessionInit(id);
/* Create enpty packet */
send = (struct ast_frame *) malloc(sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET + 160 );
/* No data*/
send->data = (void*)send + AST_FRIENDLY_OFFSET;
send->datalen = 160;
/* Set DTMF type */
send->frametype = AST_FRAME_VOICE;
/* Set DTMF value */
send->subclass = pseudo->rawwriteformat;
/* Rest of values*/
send->src = 0;
send->samples = 160;
send->delivery.tv_usec = 0;
send->delivery.tv_sec = 0;
/* We will free the frame */
send->mallocd = 0;
/* Send */
ast_write(pseudo,send);
/* Wait for data avaiable on any channel */
while (!reason && (where = ast_waitfor_n(channels, 2, &ms)) != NULL)
{
/* Read frame from channel */
f = ast_read(where);
/* if it's null */
if (f == NULL)
break;
/* If it's on h324m channel */
if (where==pseudo)
{
/* Check frame type */
if (f->frametype == AST_FRAME_VOICE)
{
/* read data */
H324MSessionRead(id, (unsigned char *)f->data, f->datalen);
/* If state changed */
if (state!=H324MSessionGetState(id))
{
/* Update state */
state = H324MSessionGetState(id);
/* Log */
ast_log(LOG_DEBUG, "H324M changed state %d\n", state);
/* If connected */
if (state==CALLSTATE_STABLISHED)
{
/* Answer call if not done yet */
ast_answer(chan);
/* Log */
ast_log(LOG_DEBUG, "Connected, sending VIDUPDATE\n");
/* Indicate Video Update */
ast_indicate(pseudo, AST_CONTROL_VIDUPDATE);
}
}
/* Get frames */
while ((frame=H324MSessionGetFrame(id))!=NULL)
{
/* Packetize outgoing frame */
if ((send=create_ast_frame(frame,&vt))!=NULL)
/* Send frame */
ast_write(chan,send);
/* Delete frame */
FrameDestroy(frame);
}
/* Get user input */
while((input=H324MSessionGetUserInput(id))!=NULL)
{
/* Send digit begin */
ast_senddigit_begin(chan,input[0]);
/* Send digit end */
ast_senddigit_end(chan,input[0],100);
/* free data */
free(input);
}
res = ast_unregister_application(name_h324m_loopback);
res &= ast_unregister_application(name_h324m_gw);
res &= ast_unregister_application(name_h324m_call);
res &= ast_unregister_application(name_h324m_gw_answer);
res &= ast_unregister_application(name_video_loopback);
ast_module_user_hangup_all();
return res;
}
static int load_module(void)
{
int res;
res = ast_register_application(name_h324m_loopback, app_h324m_loopback, syn_h324m_loopback, des_h324m_loopback);
res &= ast_register_application(name_h324m_gw, app_h324m_gw, syn_h324m_gw, des_h324m_gw);
res &= ast_register_application(name_h324m_call, app_h324m_call, syn_h324m_call, des_h324m_call);
res &= ast_register_application(name_h324m_gw_answer, app_h324m_gw_answer, syn_h324m_gw_answer, des_h324m_gw_answer);
res &= ast_register_application(name_video_loopback, app_video_loopback, syn_video_loopback, des_video_loopback);
You can post new topics in this forum You can reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum