The following code fragments show how to create an SDP description with mandatory fields.
Create the CSdpDocument
object. This includes all
SDP fields in structural format:
CSdpDocument* sdpDocument = CSdpDocument::NewLC();
Define the session name field for the SDP description:
sdpDocument->SetSessionNameL(_L8("SipSession"));
Create an instance of the Origin field class and set into the SDP description:
TInt64 sessionId( TUint( 2890844526 ) );
TInt64 sessionVersion( TUint( 2890842807 ) );
TInetAddr address;
const TUint32 KInetAddr = INET_ADDR(10,47,16,5);
address.SetAddress( KInetAddr );
CSdpOriginField* originfield = CSdpOriginField::NewL(
_L8("username"), sessionId, sessionversion, address);
sdpDocument->SetOriginField(originfield);
You must open the String Pool before you can use it. All predefined SDP constants are in the string table, which can be accessed through the string pool. Once you have finished with the SDP description, the String Pool must be closed.
SdpCodecStringPool::OpenL();
RStringPool pool = SdpCodecStringPool::StringPoolL();
Fetch the predefined network type and the address type for the connection field from the String Pool:
RStringF netType = pool.StringF( SdpCodecStringConstants::ENetType, SdpCodecStringConstants::Table );
RStringF addressTypeIP4 = pool.StringF( SdpCodecStringConstants::EAddressTypeIP4, SdpCodecStringConstants::Table );
Create the connection field and set into the SDP description:
_LIT8( KAddress, "10.47.16.5" );
CSdpConnectionField* connectionfield = CSdpConnectionField::NewL( netType, addressTypeIP4, KAddress );
sdpDocument->SetConnectionField(connectionfield);
Define the session part attribute for the SDP description. Because the direction-attribute is not one of the predefined attributes in the string table, it must be defined dynamically to the String Pool.
_LIT8(KSDPAttributeDirectionBoth,"both");
RStringF modifier = pool.OpenFStringL(_L8("direction"));
CleanupClosePushL(modifier);
CSdpAttributeField* attrField = CSdpAttributeField::NewL(modifier, KSDPAttributeDirectionBoth);
User::LeaveIfError((sdpDocument->AttributeFields()).Append(attrField));
CleanupStack::PopAndDestroy();
Create the first media field:
RStringF mediaVideo = pool.StringF( SdpCodecStringConstants::EMediaVideo, SdpCodecStringPool::StringTableL());
RStringF protocol = pool.StringF(SdpCodecStringConstants::EProtocolRtpAvp, SdpCodecStringPool::StringTableL());
CSdpMediaField* media1 = CSdpMediaField::NewLC(mediaVideo, 49152, protocol, _L8("96"));
Add the sendonly attribute (“a=sendonly”) to the first media field:
RStringF sendonlyAttr = iPool.StringF( SdpCodecStringConstants:: EAttributeSendonly, SdpCodecStringPool::StringTableL() );
CSdpAttributeField* attr1 = CSdpAttributeField::NewLC(sendonlyAttr, _L8(""));
User::LeaveIfError((media1->AttributeFields()).Append(attr1));
CleanupStack::Pop(); //attr1
Add the rtpmap attribute (“a=rtpmap:96 H263-2000/90000”) to the first media field and then add the rtpmap specific attribute (“a=ptime:15”):
_LIT8( KEncodingName, "H263-2000" );
_LIT8( KClockRate, "90000" );
_LIT8( KEncodingParam, "" );
TSdpRtpmapValue rtpmapValue( KEncodingName(), KClockRate(), KEncodingParam() );
HBufC8* rtpmapBuf = rtpmapValue.EncodeL();
CleanupStack::PushL(rtpmapBuf);
RStringF rtpmapStr = pool.StringF( SdpCodecStringConstants::EAttributeRtpmap, SdpCodecStringPool::StringTableL() );
CSdpFmtAttributeField* rtpmapAttribute = CSdpFmtAttributeField::NewLC(rtpmapStr, _L8("96"), *rtpmapBuf);
User::LeaveIfError(media1->FormatAttributeFields().Append (rtpmapAttribute));
CleanupStack::Pop(); // rtpmapAttribute
CleanupStack::PopAndDestroy(); // rtpmapBuf
// add a rtpmap specific attribute
RStringF ptimeAttrStr = iPool.StringF( SdpCodecStringConstants:: EAttributePtime, SdpCodecStringPool::StringTableL() );
CSdpAttributeField* ptimeAttr = CSdpAttributeField::NewLC(ptimeAttrStr, _L8("15"));
ptimeAttr->AssignTo(*(media1->FormatAttributeFields())[0]);
User::LeaveIfError(media1->AttributeFields().Append(ptimeAttr));
CleanupStack::Pop(); //ptimeAttr
Create the second media field:
RStringF mediaAudio = pool.StringF( SdpCodecStringConstants:: EMediaAudio, SdpCodecStringPool::StringTableL());
CSdpMediaField* media2 = CSdpMediaField::NewLC(mediaAudio, 57344, protocol, _L8("97"));
Create the sendonly attribute for the second media field:
CSdpAttributeField* attr2 = CSdpAttributeField::NewLC(sendonlyAttr,
_L8(""));
User::LeaveIfError((media2->AttributeFields()).Append(attr2));
CleanupStack::Pop();
Create the rtpmap attribute (“a=rtpmap:97 AMR/8000”) for the second media field:
_LIT8( KEncodingName1, "AMR" );
_LIT8( KClockRate1, "8000" );
TSdpRtpmapValue rtpmapValue1( KEncodingName1(), KClockRate1(), KEncodingParam() );
HBufC8* rtpmapBuf1 = rtpmapValue1.EncodeL();
CleanupStack::PushL(rtpmapBuf1);
CSdpFmtAttributeField* rtpmapAttribute1 = CSdpFmtAttributeField::NewLC(rtpmapStr, _L8("97"), *rtpmapBuf1);
User::LeaveIfError(media2->FormatAttributeFields().Append (rtpmapAttribute1));
CleanupStack::Pop(); // rtpmapAttribute1
CleanupStack::PopAndDestroy(); //rtpmapBuf1
Assign the media fields to the CSdpDocument
object
and then remove the buffers from the cleanup stack:
User::LeaveIfError(doc->MediaFields().Append(media1));
User::LeaveIfError(doc->MediaFields().Append(media2));
CleanupStack::Pop(); //media2
CleanupStack::Pop(); //media1
Close the String Pool, remove and delete the
CSdpDocument
object from the cleanup stack:
SdpCodecStringPool::Close();
CleanupStack::PopAndDestroy(); //sdpDocument
The SDP description created in the example above has the following fields:
v=0
o=username 2890844526 2890842807 IN IP4 10.47.16.5
s=SipSession
c=IN IP4 10.47.16.5
t=0 0
a=direction:both
m=video 49152 RTP/AVP 96
a=sendonly
a=rtpmap:96 H263-2000/90000
m=audio 57344 RTP/AVP 97
a=sendonly
a=rtpmap:97 AMR/8000