Performing NMS native play and record

NMS native play and record enables applications to maintain the quality of audio data played and recorded over network interfaces while minimizing the encoding and decoding resources needed to process the audio data.

Applications can perform the following tasks with NMS native play and record:

CG boards support NMS native play and record functionality.

NMS native play and record advantages

When an application plays or records audio data over an IP network, typically the application must encode or decode the data. Audio data is often encoded in a compressed format such as G.711 or G.723.1. Encoding or decoding the audio stream can consume system resources and incrementally degrade the quality of the data.

When an application records audio data using native record, the audio is stored in the NMS EDTX (extended discontinuous transmission) format without encoding the data. The application can then either play back the audio data directly to a network interface or transfer the data to the PSTN interface through a decoder.

The following illustration shows the advantages of native play and record:

native_play_record_general.gif

Implementing NMS native play and record

NMS native play and record uses an NMS proprietary format called EDTX (extended discontinuous transmission) to store and play back codec frames. EDTX formatting incorporates an optional silence compression scheme that uses silence frames in the recorded stream to indicate periods of silence.

Native play and record supports the following encoding types:

For more information about supported vocoder types, refer to the Fusion vocoder readme.txt files.

Applications can implement native play and record in the following ways:

Implementation

Description

Native play

Application plays a stream of audio data from an ADI port to an RTP endpoint.

Native record without inband silence and DTMF detection

Application receives and records a stream of audio data from an RTP endpoint. No data decoding takes place, inband silence detection is not supported, and DTMF detection is supported only through Fusion RFC 2833 support.

Native record with inband silence and DTMF detection

Application receives and records a stream of audio data from an RTP endpoint, and in parallel, decodes the data from its network format (for example, G.711A or G.723.1). The application also performs silence detection, DTMF detection, or both with the data.

Native play

To implement native play functionality, the application performs the following tasks:

The following illustration shows an overview of the native play mechanism:

native_play.gif

Sample procedure

When implementing native play functionality, applications use functions from the following resources:

The following procedure shows functions used to implement a typical native play operation:

Step

Action

1

Invoke ctaCreateQueue to create a Natural Access event queue.

ctaCreateQueue (&queuehd)

2

Invoke ctaCreateContext to create a Natural Access context for the audio channel.

ctaCreateContext (queuehd, &ctahd)

3

Invoke ctaOpenServices to open the ADI and MSPP services on the context.

ctaOpenServices (ctahd, svclist, nsvcs)

4

Invoke adiStartProtocol to start the nocc protocol on the ADI port.

adiStartProtocol (ctahd, "nocc", NULL, startparms)

5

Invoke mspCreateEndpoint to create an audio MSPP service RTP endpoint. mspCreateEndpoint returns an MSPP service endpoint handle (ephd).

mspCreateEndpoint (ctahd, mspaddrstruct, mspparmstruct, &rtpephd)

6

Invoke mspGetFilterHandle to retrieve the runtime filter ID (fltID) associated with the RTP endpoint handle (ephd). The application uses the returned fltID as the destination for the audio stream played out from the ADI port.

mspGetFilterHandle (rtpephd, MSP_ENDPOINT_RTPFDX, &fltID)

7

Invoke adiSetNativeInfo to set NMS native play parameters, specifying both the context handle of the ADI port and the RTP endpoint fltID returned by mspGetFilterHandle.

adiSetNativeInfo (ctahd, NULL, fltID, fltID_parms)

8

Invoke adiPlayFromMemory to begin playing a message.

adiPlayFromMemory (ctahd, encoding, buffer, bufsize, parms)

9

Invoke adiStopPlaying to stop playing the message.

adiStopPlaying (ctahd)

Example

The following example shows how to perform a native play operation:

ret = ctaCreateQueue( NULL, 0, &hCtaQueueHd );

ret = ctaCreateContext( hCtaQueueHd, 0, "Play", &ctahd );

ServiceCount = 2;
ServDesc[0].name.svcname         = "ADI";
ServDesc[0].name.svcmgrname      = "ADIMGR";
ServDesc[0].mvipaddr.board       = board;
ServDesc[0].mvipaddr.mode        = 0;
ServDesc[1].name.svcname         = "MSP";
ServDesc[1].name.svcmgrname      = "MSPMGR";
ret = ctaOpenServices( ctahd, ServDesc, ServiceCount );
ret = WaitForSpecificEvent( CTAEVN_OPEN_SERVICES_DONE, &event );

ret = adiStartProtocol( ctahd, "nocc", NULL, NULL );
ret = WaitForSpecificEvent( ADIEVN_STARTPROTOCOL_DONE, &event );

// create mspp RTP endpoint
ret = mspCreateEndpoint( ctahd, &mspAddr, &mspParm, &ephd );
ret = WaitForSpecificEvent( MSPEVN_CREATE_ENDPOINT_DONE, &event );

// get cg6xxx board handle
ret = mspGetFilterHandle( msphd, MSP_FILTER_RTPFDX_EPH, rtp_play_filter_handle );
ret = adiSetNativeInfo( ctahd, NULL,  /* no ingress handle, as this is a play only */
rtp_play_filter_handle, &natpr_ctl );  /* RTP endpoint filter ID
specified as a destination for audio */
ret = adiPlayFromMemory( ctahd, ADI_ENCODE_EDTX_AMRNB,  /* audio play */
MemoryBuffer, RecordedBytes, NULL );
.
.
.
ret = adiStopPlaying ( ctahd );

Native record without inband silence and DTMF detection

To implement native record functionality without inband silence detection or DTMF detection, the application performs the following tasks:

Note: Applications can perform DTMF detection using Fusion RFC 2833 support, but silence detection is not supported. Refer to the Dialogic® NaturalAccess™ Fusion™ VoIP API Developer’s Manual for more information.

The following illustration shows an overview of the native record mechanism without voice decoding:

native_record_no_decod.gif

Sample procedure

Applications use functions from the following Natural Access resources to implement native record functionality without inband silence detection or DTMF detection:

The following procedure shows functions used to implement a typical native record operation without decoding:

Step

Action

1

Invoke ctaCreateQueue to create a Natural Access event queue.

ctaCreateQueue (&queuehd)

2

Invoke ctaCreateContext to create a Natural Access context for the audio channel.

ctaCreateContext (queuehd, &ctahd)

3

Invoke ctaOpenServices to open the ADI and MSPP services on the context.

ctaOpenServices (ctahd, svclist, nsvcs)

4

Invoke mspCreateEndpoint to create an audio RTP endpoint. mspCreateEndpoint returns an endpoint handle (ephd).

mspCreateEndpoint (ctahd, mspaddrstruct, mspparmstruct, &rtpephd)

5

Invoke mspCreateChannel to create a record channel.

mspCreateChannel (ctahd, chnladdr, chnlparms, &chanhd)

6

Invoke mspConnect to connect the record channel with the RTP endpoint. Specify MSP_NO_CONNECT instead of a DS0 endpoint handle.

mspConnect (MSP_NO_CONNECT, chanhd, rtpephd)

7

Invoke mspEnableChannel to enable the record channel to process data.

mspEnableChannel (msphd)

8

Invoke adiStartProtocol to start the nocc protocol on the audio channel.

adiStartProtocol (ctahd, "nocc", NULL, startparms)

9

Invoke mspGetFilterHandle to retrieve the filter identifier (fltID) associated with the MSPP record channel.

mspGetFilterHandle (chanhd, MSP_FILTER_JITTER, &fltID)

10

Invoke adiSetNativeInfo to set NMS native record parameters. Specify both the context handle of the ADI port and the fltID returned by mspGetFilterHandle.

adiSetNativeInfo (ctahd, fltID, NULL, natpr_parms)

11

Invoke ctaGetParms to return parameter values for the ADI_RECORD_PARMS structure.

ctaGetParms(ctahd, parmid, buffer, size)

12

Invoke adiRecordToMemory to begin recording audio data.

adiRecordToMemory (ctahd, buf, bufsize, rec_param)

13

Invoke adiStopRecording stop recording audio data.

adiStopRecording (ctahd)

Example

The following example shows how to perform a native record operation without decoding:

ret = ctaCreateQueue( NULL, 0, &hCtaQueueHd ) ;
ret = ctaCreateContext( hCtaQueueHd, 0, "Record", &ctahd );

ServiceCount = 2;
ServDesc[0].name.svcname       = "ADI";
ServDesc[0].name.svcmgrname    = "ADIMGR";
ServDesc[0].mvipaddr.mode      = ADI_VOICE_DUPLEX;
ServDesc[0].mvipaddr.stream    = 0;
ServDesc[0].mvipaddr.timeslot  = record_timeslot;
ServDesc[1].name.svcname       = "MSP";
ServDesc[1].name.svcmgrname    = "MSPMGR";

ret = ctaOpenServices( ctahd, ServDesc, ServiceCount );
ret = WaitForSpecificEvent( CTAEVN_OPEN_SERVICES_DONE, &Event );

// IP Channel Initialization
MSPHD     ds0_ephd = MSP_NO_CONNECT;
MSPHD     rtp_ephd;

// Create and init RTP endpoint
&
mspCreateEndpoint
( ctaHd, &rtpaddr, &rtp_params, rtp_ephd );
if (! WaitForSpecificEvent(MSPEVN_CREATE_ENDPOINT_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_CREATE_ENDPOINT_DONE (RTP)");
return FAILURE;
}

chanaddr.nBoard         = Board;
chanaddr.channelType    = G711RecordChannel;
chanaddr.FilterAttribs  = MSP_FCN_ATTRIB_RFC2833;
chan_params.size        = sizeof( MSP_CHANNEL_PARAMETER );
chan_params.channelType = G711RecordChannel;
chan_params.ChannelParms.VoiceParms.size = sizeof( MSP_VOICE_CHANNEL_PARMS );

// Create channel
mspCreateChannel
( ctaHd, &chanaddr, &chan_params, &msphd );
CTA_EVENT CtaEvent;
if (! WaitForSpecificEvent(MSPEVN_CREATE_CHANNEL_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_CREATE_CHANNEL_DONE");
return FAILURE;
}

// connect mspp endpoints
ret = mspConnect( ds0_ephd, msphd, rtp_ephd );
if (! WaitForSpecificEvent(MSPEVN_CONNECT_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_CONNECT_DONE");
return FAILURE;
}

// connect mspp enable channel
mspEnableChannel
( msphd );
if (! WaitForSpecificEvent(MSPEVN_ENABLE_CHANNEL_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_ENABLE_CHANNEL_DONE");
return FAILURE;
}

//adiStartProtocol
adiStartProtocol
( ctahd, "nocc", NULL, NULL );
if (! WaitForSpecificEvent (ADIEVN_STARTPROTOCOL_DONE, &Event, 5000))
{
printf("Failed to receive ADIEVN_STARTPROTOCOL_DONE event");
return FAILURE;
}

// get cg6xxx board handle
ret = mspGetFilterHandle( msphd, MSP_FILTER_JITTER, &cg6xxx_board_filter_handle );

ADI_NATIVE_CONTROL parms = {0};     /*   Native parameters                    */
parms.frameFormat        = 0;
parms.include2833        = 0;
parms.vadFlag            = 0;
parms.nsPayload          = 0;
parms.mode               = ADI_NATIVE;
parms.rec_encoding       = ADI_ENCODE_EDTX_MU_LAW;
parms.payloadID          = 0;
ret = adiSetNativeInfo( ctahd, cg6xxx_board_filter_handle,
NULL,       /* this is record only so no egress handle */
&parms);

// get default adi record parms
ret = ctaGetParms( ctahd, ADI_RECORD_PARMID, &recparms, sizeof(ADI_RECORD_PARMS) );
ret = adiRecordToMemory( ctahd, ADI_ENCODE_EDTX_MU_LAW,           /* audio rec */
MemoryBuffer, RecordedBytes, &recparms );
.
.
.
adiStopRecording
( ctahd );

Native record with inband silence and DTMF detection

On CG boards, use the following procedure to implement native record with inband silence detection or DTMF detection.

To implement native record functionality with inband silence detection or DTMF detection, the application performs the following tasks:

The following illustration shows an overview of the native record mechanism with voice decoding enabled:

native_record_decod.gif

Sample procedure

Applications use functions from the following Natural Access resources to implement native record functionality with inband silence detection or DTMF detection:

The following procedure shows functions used to implement a typical native record operation with decoding on CG boards:

Step

Action

1

Invoke ctaCreateQueue to create a Natural Access event queue.

ctaCreateQueue (&queuehd)

2

Invoke ctaCreateContext to create a Natural Access context for the audio channel.

ctaCreateContext (queuehd, &ctahd)

3

Invoke ctaOpenServices to open the ADI service on the context.

When using ctaOpenServices, the application must specify the following:

  • Set the svclist.mvipaddr.mode parameter to ADI_VOICE_DUPLEX to allocate DSP resources for the channel on the CG board.

  • Set the svclist.mvipaddr.stream parameter to 0 and the svclist.mvipaddr.timeslot parameter to a unique and valid entry. For more information, refer to the Dialogic® NaturalAccess™ Software Developer’s Manual.

ctaOpenServices (ctahd, svclist, nsvcs)

4

Invoke adiStartProtocol to start the nocc protocol on the audio channel and enable silence detection on the audio channel.

adiStartProtocol (ctahd, "nocc")

5

Invoke swiOpenSwitch to open a switching device for the context. swiOpenSwitch returns a switch handle (swihd).

swiOpenSwitch (ctahd, "cg6ksw", board, 0x0, &swihd)

6

Invoke ctaCreateContext to create a Natural Access context for the MSPP channel.

ctaCreateContext (queuehd, &msphd)

7

Invoke ctaOpenServices to open the MSPP service on the context.

ctaOpenServices (msphd, svclist, nsvcs)

8

Invoke mspCreateEndpoint to create an audio RTP endpoint. mspCreateEndpoint returns an endpoint handle (&rtpephd).

mspCreateEndpoint (msphd, mspaddrstruct, mspparmstruct, &rtpephd)

9

Invoke mspCreateEndpoint to create an audio DS0 endpoint. mspCreateEndpoint returns an endpoint handle (&ds0ephd).

mspCreateEndpoint (msphd, mspaddrstruct, mspparmstruct, &ds0ephd)

10

Invoke mspCreateChannel to create a full duplex or voice decoding channel.

mspCreateChannel (msphd, chnladdr, chnlparms, &chanhd)

11

Invoke mspConnect to connect the DS0 and RTP endpoints with the voice channel.

mspConnect (ds0ephd, chanhd, rtpephd)

12

Invoke mspEnableChannel to enable the record channel to process data.

mspEnableChannel (msphd)

13

Invoke swiMakeConnection with the swihd returned by swiOpenSwitch to connect the MSPP DS0 output to the ADI audio channel input and vice versa. When using swiMakeConnection, the application specifies the stream and timeslot used to create the ADI port and the stream and timeslot used to create the DS0 endpoint.

swiMakeConnection (swihd, adi_ds0, ds0ephd, 2)

14

Invoke mspGetFilterHandle to retrieve the filter identifier (fltID) associated with the MSPP record channel.

mspGetFilterHandle (chanhd, MSP_FILTER_JITTER, &fltID)

15

Invoke adiSetNativeInfo to set NMS native record parameters. Specify both the context handle of the ADI port and the fltID returned by mspGetFilterHandle.

adiSetNativeInfo (ctahd, fltID, NULL, natpr_parms)

16

Invoke ctaGetParms to return parameter values for the ADI_RECORD_PARMS structure.

ctaGetParms(ctahd, parmid, buffer, size)

17

Invoke adiRecordToMemory to begin recording a message.

adiRecordToMemory (ctahd, buf, bufsize, rec_param)

18

Invoke adiStopRecording stop recording the audio portion of the message.

adiStopRecording (ctahd)

Example

The following example shows how to perform a native record operation that supports ADI silence and DTMF detection on CG boards:

ret = ctaCreateQueue( NULL, 0, &hCtaQueueHd );
// create context for ADI port
ret = ctaCreateContext( hCtaQueueHd, 0, "Record", &ctahd );

ServiceCount = 1;
ServDesc[0].name.svcname       = "ADI";
ServDesc[0].name.svcmgrname    = "ADIMGR";
ServDesc[0].mvipaddr.mode      = ADI_VOICE_DUPLEX;
ServDesc[0].mvipaddr.stream    = 0;
ServDesc[0].mvipaddr.timeslot  = record_timeslot;

ret = ctaOpenServices( ctahd, ServDesc, ServiceCount );
ret = WaitForSpecificEvent( CTAEVN_OPEN_SERVICES_DONE, &Event );
{
printf("Failed to receive CTAEVN_OPEN_SERVICES_DONE event");
return FAILURE;
}

//adiStartProtocol
adiStartProtocol
( ctahd, "nocc", NULL, NULL );
if (! WaitForSpecificEvent (ADIEVN_STARTPROTOCOL_DONE, &Event, 5000))
{
printf("Failed to receive ADIEVN_STARTPROTOCOL_DONE event");
return FAILURE;
}

ret = swiOpenSwitch(ctahd, "agsw", Board, 0, &swihd);
if (ret != SUCCESS)
{
printf("Makeconnections: Failed to open board %d\n",Board);
return FAILURE;
}

// create context for MSPP channel
ret = ctaCreateContext( hCtaQueueHd, 0, "MSPP", &ipHd );

ServiceCount = 2;
ServDesc[0].name.svcname       = "ADI";
ServDesc[0].name.svcmgrname    = "ADIMGR";
ServDesc[0].mvipaddr.mode      = ADI_VOICE_DUPLEX;
ServDesc[0].mvipaddr.stream    = 0;
ServDesc[0].mvipaddr.timeslot  = fusion_timeslot;
ServDesc[1].name.svcname       = "MSP";
ServDesc[1].name.svcmgrname    = "MSPMGR";

ret = ctaOpenServices( iphd, ServDesc, ServiceCount );
ret = WaitForSpecificEvent( CTAEVN_OPEN_SERVICES_DONE, &Event );
{
printf("Failed to receive CTAEVN_OPEN_SERVICES_DONE event");
return FAILURE;
}
// IP Channel Initialization
MSPHD     ds0_ephd;
MSPHD     rtp_ephd;

// Create and init RTP endpoint
MSP_ENDPOINT_ADDR      rtpaddr    = {0};
MSP_ENDPOINT_PARAMETER rtp_params = {0};

rtpaddr.size    = sizeof(MSP_ENDPOINT_ADDR);
rtpaddr.eEpType = MSP_ENDPOINT_RTPFDX;
rtpaddr.nBoard  = Board;
...
mspCreateEndpoint
( ipHd, &rtpaddr, &rtp_params, &rtp_ephd );
if (! WaitForSpecificEvent(MSPEVN_CREATE_ENDPOINT_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_CREATE_ENDPOINT_DONE (RTP)");
return FAILURE;
}

// create mspp DS0 endpoint
MSP_ENDPOINT_ADDR     ds0addr      = {0};
ds0addr.eEpType          = MSP_ENDPOINT_DS0;
ds0addr.nBoard           = Board;
ds0addr.size             = sizeof(MSP_ENDPOINT_DS0);
ds0addr.EP.DS0.nTimeslot = fusion_timeslot;
MSP_ENDPOINT_PARAMETER ds0parms    = {0};
ds0parms.size            = sizeof(DS0_ENDPOINT_PARMS);
ds0parms.eParmType       = MSP_ENDPOINT_DS0;
ds0parms.EP.DS0.media    = MSP_VOICE;
mspCreateEndpoint
( ipHd, &ds0addr, &ds0parms, &ds0_ephd );
if (! WaitForSpecificEvent(MSPEVN_CREATE_ENDPOINT_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_CREATE_ENDPOINT_DONE (DS0)");
return FAILURE;
}

// create mspp Channel
MSP_CHANNEL_ADDR      chanaddr    = {0};
MSP_CHANNEL_PARAMETER chan_params = {0};

chanaddr.nBoard         = Board;
chanaddr.channelType    = G711FullDuplex;
chanaddr.FilterAttribs  = MSP_FCN_ATTRIB_RFC2833;
chan_params.size        = sizeof( MSP_CHANNEL_PARAMETER );
chan_params.channelType = G711FullDuplex;
chan_params.ChannelParms.VoiceParms.size = sizeof( MSP_VOICE_CHANNEL_PARMS );
...
// Create channel
mspCreateChannel
( ipHd, &chanaddr, &chan_params, &msphd );
CTA_EVENT CtaEvent;
if (! WaitForSpecificEvent(MSPEVN_CREATE_CHANNEL_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_CREATE_CHANNEL_DONE");
return FAILURE;
}

// connect mspp endpoints
ret = mspConnect(ds0_ephd , msphd, rtp_ephd);
if (! WaitForSpecificEvent(MSPEVN_CONNECT_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_CONNECT_DONE");
return FAILURE;
}

// connect mspp enable channel
mspEnableChannel
(msphd);
if (! WaitForSpecificEvent(MSPEVN_ENABLE_CHANNEL_DONE, &Event, 5000))
{
printf("Failed waiting for MSPEVN_ENABLE_CHANNEL_DONE");
return FAILURE;
}

// connect Fusion and ADI timeslots to allow Silence and DTMF detection
SWI_TERMINUS input[2];
SWI_TERMINUS output[2];

output[0].bus = MVIP95_LOCAL_BUS;
output[0].stream = BoardStream;
output[0].timeslot = record_timeslot;
output[1].bus = MVIP95_LOCAL_BUS;
output[1].stream = BoardStream;
output[1].timeslot = fusion_timeslot;
input[0].bus = MVIP95_MVIP_BUS;
input[0].stream = BoardStream+1;
input[0].timeslot = fusion_timeslot;
input[1].bus = MVIP95_MVIP_BUS;
input[1].stream = BoardStream+1;
input[1].timeslot = record_timeslot;
swiMakeConnection
(swihd, input, output, 2)

// get cg6xxx board handle
ret = mspGetFilterHandle( msphd, MSP_FILTER_JITTER, &cg6xxx_board_filter_handle );

ADI_NATIVE_CONTROL parms = {0};     /*   Native parameters                    */
parms.frameFormat   = 0;
parms.include2833   = 0;
parms.vadFlag       = 0;
parms.nsPayload     = 0;
parms.mode          = ADI_NATIVE;
parms.rec_encoding = ADI_ENCODE_EDTX_MU_LAW;
parms.payloadID     = 0;
ret = adiSetNativeInfo( ctahd, cg6xxx_board_filter_handle,
NULL,       /* this is record only so no egress handle */
&parms );

// get default adi record parms
ret = ctaGetParms( ctahd, ADI_RECORD_PARMID, &recparms, sizeof(ADI_RECORD_PARMS) );
ret = adiRecordToMemory( ctahd, ADI_ENCODE_EDTX_MU_LAW,           /* audio rec */
MemoryBuffer, RecordedBytes, &recparms );
.
.
.
adiStopRecording
(ctahd);