2013-03-17 07:45:21 +08:00
|
|
|
//
|
|
|
|
// ZoneMinder SDP Class Implementation, $Date: 2009-04-14 21:20:02 +0100 (Tue, 14 Apr 2009) $, $Revision: 2850 $
|
|
|
|
// Copyright (C) 2001-2008 Philip Coombes
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License
|
|
|
|
// as published by the Free Software Foundation; either version 2
|
|
|
|
// of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software
|
2016-12-26 23:23:16 +08:00
|
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2013-03-17 07:45:21 +08:00
|
|
|
//
|
|
|
|
|
2021-02-04 11:47:28 +08:00
|
|
|
#include "zm_sdp.h"
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2021-02-04 11:47:28 +08:00
|
|
|
#include "zm_config.h"
|
2021-03-04 06:48:02 +08:00
|
|
|
#include "zm_exception.h"
|
2021-02-04 11:47:28 +08:00
|
|
|
#include "zm_logger.h"
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2021-02-04 11:47:28 +08:00
|
|
|
#if HAVE_LIBAVFORMAT
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2015-05-29 23:38:02 +08:00
|
|
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
2013-03-17 07:45:21 +08:00
|
|
|
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 },
|
|
|
|
{ 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 },
|
|
|
|
{ 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 },
|
|
|
|
{ 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 },
|
|
|
|
{ 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 },
|
|
|
|
{ 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 },
|
|
|
|
{ 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 },
|
|
|
|
{ 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
|
|
|
{ 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 },
|
|
|
|
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
|
|
|
{ 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
|
|
|
{ 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 },
|
|
|
|
{ 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 }
|
2013-03-17 07:45:21 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
2019-04-29 00:05:32 +08:00
|
|
|
{ "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 },
|
|
|
|
{ "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC },
|
|
|
|
{ "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
|
|
|
|
{ "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE }
|
2013-03-17 07:45:21 +08:00
|
|
|
};
|
|
|
|
#else
|
|
|
|
SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = {
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 },
|
|
|
|
{ 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 },
|
|
|
|
{ 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 },
|
|
|
|
{ 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 },
|
|
|
|
{ 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 },
|
|
|
|
{ 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 },
|
|
|
|
{ 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 },
|
|
|
|
{ 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 },
|
|
|
|
{ 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
|
|
|
{ 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 },
|
2019-04-29 00:05:32 +08:00
|
|
|
{ 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 },
|
2016-04-04 22:11:48 +08:00
|
|
|
{ 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 },
|
|
|
|
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 },
|
|
|
|
{ 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 },
|
|
|
|
{ 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 },
|
|
|
|
{ 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 },
|
|
|
|
{ -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 }
|
2013-03-17 07:45:21 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = {
|
2016-04-04 22:11:48 +08:00
|
|
|
{ "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 },
|
|
|
|
{ "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC },
|
|
|
|
{ "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 },
|
|
|
|
{ "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB },
|
|
|
|
{ "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE }
|
2013-03-17 07:45:21 +08:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) :
|
2016-04-04 22:11:48 +08:00
|
|
|
mTtl( 16 ),
|
|
|
|
mNoAddresses( 0 )
|
2013-03-17 07:45:21 +08:00
|
|
|
{
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector tokens = Split(connInfo, " ");
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( tokens.size() < 3 )
|
|
|
|
throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" );
|
|
|
|
mNetworkType = tokens[0];
|
|
|
|
if ( mNetworkType != "IN" )
|
|
|
|
throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
|
|
|
mAddressType = tokens[1];
|
2016-05-06 20:32:21 +08:00
|
|
|
if ( mAddressType != "IP4" && mAddressType != "IP6" )
|
2016-04-04 22:11:48 +08:00
|
|
|
throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" );
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector addressTokens = Split(tokens[2], "/");
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( addressTokens.size() < 1 )
|
|
|
|
throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" );
|
|
|
|
mAddress = addressTokens[0];
|
|
|
|
if ( addressTokens.size() >= 2 )
|
|
|
|
mTtl = atoi(addressTokens[1].c_str());
|
|
|
|
if ( addressTokens.size() >= 3 )
|
|
|
|
mNoAddresses = atoi(addressTokens[2].c_str());
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
|
2016-04-04 22:11:48 +08:00
|
|
|
mValue( 0 )
|
2013-03-17 07:45:21 +08:00
|
|
|
{
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector tokens = Split(bandInfo, ":");
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( tokens.size() < 2 )
|
|
|
|
throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" );
|
|
|
|
mType = tokens[0];
|
|
|
|
//if ( mNetworkType != "IN" )
|
|
|
|
//throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" );
|
|
|
|
mValue = atoi(tokens[1].c_str());
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
SessionDescriptor::MediaDescriptor::MediaDescriptor(
|
|
|
|
const std::string &type,
|
|
|
|
int port,
|
|
|
|
int numPorts,
|
|
|
|
const std::string &transport,
|
|
|
|
int payloadType ) :
|
2016-04-04 22:11:48 +08:00
|
|
|
mType( type ),
|
|
|
|
mPort( port ),
|
|
|
|
mNumPorts( numPorts ),
|
|
|
|
mTransport( transport ),
|
|
|
|
mPayloadType( payloadType ),
|
|
|
|
mFrameRate( 0.0 ),
|
|
|
|
mClock( 0 ),
|
|
|
|
mWidth( 0 ),
|
|
|
|
mHeight( 0 ),
|
|
|
|
mSprops( "" ),
|
2020-08-26 07:45:48 +08:00
|
|
|
mConnInfo( nullptr )
|
2013-03-17 07:45:21 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) :
|
2016-04-04 22:11:48 +08:00
|
|
|
mUrl( url ),
|
2020-08-26 07:45:48 +08:00
|
|
|
mConnInfo( nullptr ),
|
|
|
|
mBandInfo( nullptr )
|
2013-03-17 07:45:21 +08:00
|
|
|
{
|
2020-08-26 07:45:48 +08:00
|
|
|
MediaDescriptor *currMedia = nullptr;
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector lines = Split(sdp, "\r\n");
|
2017-11-19 05:00:10 +08:00
|
|
|
for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
std::string line = *iter;
|
|
|
|
if ( line.empty() )
|
|
|
|
break;
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
Debug(3, "Processing SDP line '%s'", line.c_str());
|
2016-04-04 22:11:48 +08:00
|
|
|
const char sdpType = line[0];
|
|
|
|
if ( line[1] != '=' )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception("Invalid SDP format at '"+line+"'");
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
line.erase(0, 2);
|
|
|
|
switch( sdpType ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
case 'v' :
|
|
|
|
mVersion = line;
|
|
|
|
break;
|
|
|
|
case 'o' :
|
|
|
|
mOwner = line;
|
|
|
|
break;
|
|
|
|
case 's' :
|
|
|
|
mName = line;
|
|
|
|
break;
|
|
|
|
case 'i' :
|
|
|
|
mInfo = line;
|
|
|
|
break;
|
|
|
|
case 'c' :
|
|
|
|
// This prevent a memory leak if the field appears more than one time
|
|
|
|
if ( mConnInfo )
|
|
|
|
delete mConnInfo;
|
|
|
|
mConnInfo = new ConnInfo( line );
|
|
|
|
break;
|
|
|
|
case 'b' :
|
|
|
|
// This prevent a memory leak if the field appears more than one time
|
|
|
|
if ( mBandInfo )
|
|
|
|
delete mBandInfo;
|
|
|
|
mBandInfo = new BandInfo( line );
|
|
|
|
break;
|
|
|
|
case 't' :
|
|
|
|
mTimeInfo = line;
|
|
|
|
break;
|
|
|
|
case 'a' :
|
|
|
|
{
|
|
|
|
mAttributes.push_back( line );
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector tokens = Split(line, ":", 2);
|
2016-04-04 22:11:48 +08:00
|
|
|
std::string attrName = tokens[0];
|
2019-04-29 00:05:32 +08:00
|
|
|
if ( currMedia ) {
|
|
|
|
if ( attrName == "control" ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( tokens.size() < 2 )
|
|
|
|
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
|
|
|
currMedia->setControlUrl( tokens[1] );
|
2019-04-29 00:05:32 +08:00
|
|
|
} else if ( attrName == "range" ) {
|
|
|
|
} else if ( attrName == "rtpmap" ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
// a=rtpmap:96 MP4V-ES/90000
|
|
|
|
if ( tokens.size() < 2 )
|
|
|
|
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector attrTokens = Split(tokens[1], " ");
|
2016-04-04 22:11:48 +08:00
|
|
|
int payloadType = atoi(attrTokens[0].c_str());
|
|
|
|
if ( payloadType != currMedia->getPayloadType() )
|
|
|
|
throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) );
|
|
|
|
std::string payloadDesc = attrTokens[1];
|
|
|
|
//currMedia->setPayloadType( payloadType );
|
2019-04-29 00:05:32 +08:00
|
|
|
if ( attrTokens.size() > 1 ) {
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector payloadTokens = Split(attrTokens[1], "/");
|
2016-04-04 22:11:48 +08:00
|
|
|
std::string payloadDesc = payloadTokens[0];
|
|
|
|
int payloadClock = atoi(payloadTokens[1].c_str());
|
|
|
|
currMedia->setPayloadDesc( payloadDesc );
|
|
|
|
currMedia->setClock( payloadClock );
|
|
|
|
}
|
2019-04-29 00:05:32 +08:00
|
|
|
} else if ( attrName == "framesize" ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
// a=framesize:96 320-240
|
|
|
|
if ( tokens.size() < 2 )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception("Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'");
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector attrTokens = Split(tokens[1], " ");
|
2016-04-04 22:11:48 +08:00
|
|
|
int payloadType = atoi(attrTokens[0].c_str());
|
|
|
|
if ( payloadType != currMedia->getPayloadType() )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception( stringtf("Payload type mismatch, expected %d, got %d in '%s'",
|
|
|
|
currMedia->getPayloadType(), payloadType, line.c_str()));
|
2016-04-04 22:11:48 +08:00
|
|
|
//currMedia->setPayloadType( payloadType );
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector sizeTokens = Split(attrTokens[1], "-");
|
2016-04-04 22:11:48 +08:00
|
|
|
int width = atoi(sizeTokens[0].c_str());
|
|
|
|
int height = atoi(sizeTokens[1].c_str());
|
2019-04-29 00:05:32 +08:00
|
|
|
currMedia->setFrameSize(width, height);
|
|
|
|
} else if ( attrName == "framerate" ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
// a=framerate:5.0
|
|
|
|
if ( tokens.size() < 2 )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception("Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'");
|
2016-04-04 22:11:48 +08:00
|
|
|
double frameRate = atof(tokens[1].c_str());
|
2019-04-29 00:05:32 +08:00
|
|
|
currMedia->setFrameRate(frameRate);
|
|
|
|
} else if ( attrName == "fmtp" ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
|
|
|
|
if ( tokens.size() < 2 )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception("Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'");
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector attrTokens = Split(tokens[1], " ", 2);
|
2016-04-04 22:11:48 +08:00
|
|
|
int payloadType = atoi(attrTokens[0].c_str());
|
|
|
|
if ( payloadType != currMedia->getPayloadType() )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception(stringtf("Payload type mismatch, expected %d, got %d in '%s'",
|
|
|
|
currMedia->getPayloadType(), payloadType, line.c_str()));
|
2016-04-04 22:11:48 +08:00
|
|
|
//currMedia->setPayloadType( payloadType );
|
2019-04-29 00:05:32 +08:00
|
|
|
if ( attrTokens.size() > 1 ) {
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector attr2Tokens = Split(attrTokens[1], "; ");
|
2019-04-29 00:05:32 +08:00
|
|
|
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) {
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector attr3Tokens = Split(attr2Tokens[i], "=");
|
2016-04-04 22:11:48 +08:00
|
|
|
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
|
2017-11-19 05:00:10 +08:00
|
|
|
if ( attr3Tokens[0] == "profile-level-id" ) {
|
|
|
|
} else if ( attr3Tokens[0] == "config" ) {
|
|
|
|
} else if ( attr3Tokens[0] == "sprop-parameter-sets" ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
size_t t = attr2Tokens[i].find("=");
|
|
|
|
char *c = (char *)attr2Tokens[i].c_str() + t + 1;
|
|
|
|
Debug(4, "sprop-parameter-sets value %s", c);
|
|
|
|
currMedia->setSprops(std::string(c));
|
2017-11-19 05:00:10 +08:00
|
|
|
} else {
|
2016-04-04 22:11:48 +08:00
|
|
|
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
|
|
|
|
}
|
|
|
|
}
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
2017-11-19 05:00:10 +08:00
|
|
|
} else if ( attrName == "mpeg4-iod" ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
// a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA"
|
2017-11-19 05:00:10 +08:00
|
|
|
} else if ( attrName == "mpeg4-esid" ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
// a=mpeg4-esid:201
|
2017-11-19 05:00:10 +08:00
|
|
|
} else {
|
2019-04-29 00:05:32 +08:00
|
|
|
Debug(3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str());
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
2017-11-19 05:00:10 +08:00
|
|
|
} else {
|
2019-04-29 00:05:32 +08:00
|
|
|
Debug(3, "Ignoring general SDP attribute '%s'", line.c_str());
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'm' :
|
|
|
|
{
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector tokens = Split(line, " ");
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( tokens.size() < 4 )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception("Can't parse SDP media description '"+line+"'");
|
2016-04-04 22:11:48 +08:00
|
|
|
std::string mediaType = tokens[0];
|
|
|
|
if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception("Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'");
|
2021-04-04 06:30:18 +08:00
|
|
|
StringVector portTokens = Split(tokens[1], "/");
|
2016-04-04 22:11:48 +08:00
|
|
|
int mediaPort = atoi(portTokens[0].c_str());
|
|
|
|
int mediaNumPorts = 1;
|
|
|
|
if ( portTokens.size() > 1 )
|
|
|
|
mediaNumPorts = atoi(portTokens[1].c_str());
|
|
|
|
std::string mediaTransport = tokens[2];
|
|
|
|
if ( mediaTransport != "RTP/AVP" )
|
2019-04-29 00:05:32 +08:00
|
|
|
throw Exception("Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'");
|
2016-04-04 22:11:48 +08:00
|
|
|
int payloadType = atoi(tokens[3].c_str());
|
2019-04-29 00:05:32 +08:00
|
|
|
currMedia = new MediaDescriptor(mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType);
|
|
|
|
mMediaList.push_back(currMedia);
|
2016-04-04 22:11:48 +08:00
|
|
|
break;
|
|
|
|
}
|
2019-04-29 00:05:32 +08:00
|
|
|
} // end switch
|
|
|
|
} // end foreach line
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
SessionDescriptor::~SessionDescriptor() {
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( mConnInfo )
|
|
|
|
delete mConnInfo;
|
|
|
|
if ( mBandInfo )
|
|
|
|
delete mBandInfo;
|
|
|
|
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
|
|
|
|
delete mMediaList[i];
|
2014-11-15 05:31:03 +08:00
|
|
|
}
|
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
AVFormatContext *SessionDescriptor::generateFormatContext() const {
|
2016-04-04 22:11:48 +08:00
|
|
|
AVFormatContext *formatContext = avformat_alloc_context();
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-01-14 23:47:53 +08:00
|
|
|
#if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100))
|
|
|
|
formatContext->url = av_strdup(mUrl.c_str());
|
|
|
|
#else
|
|
|
|
strncpy(formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename));
|
|
|
|
#endif
|
2013-09-27 20:51:33 +08:00
|
|
|
/*
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( mName.length() )
|
|
|
|
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
|
|
|
|
if ( mInfo.length() )
|
|
|
|
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
|
2013-09-27 20:51:33 +08:00
|
|
|
*/
|
2016-04-04 22:11:48 +08:00
|
|
|
//formatContext->nb_streams = mMediaList.size();
|
2017-11-19 05:00:10 +08:00
|
|
|
for ( unsigned int i = 0; i < mMediaList.size(); i++ ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
const MediaDescriptor *mediaDesc = mMediaList[i];
|
2015-05-29 23:38:02 +08:00
|
|
|
#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
|
2019-04-29 00:05:32 +08:00
|
|
|
AVStream *stream = av_new_stream(formatContext, i);
|
2013-03-17 07:45:21 +08:00
|
|
|
#else
|
2020-08-26 07:45:48 +08:00
|
|
|
AVStream *stream = avformat_new_stream(formatContext, nullptr);
|
2016-04-04 22:11:48 +08:00
|
|
|
stream->id = i;
|
2013-03-17 07:45:21 +08:00
|
|
|
#endif
|
|
|
|
|
2017-11-08 10:21:51 +08:00
|
|
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
2020-08-26 07:45:48 +08:00
|
|
|
AVCodecContext *codec_context = avcodec_alloc_context3(nullptr);
|
2017-11-08 10:21:51 +08:00
|
|
|
#else
|
2017-11-19 05:00:10 +08:00
|
|
|
AVCodecContext *codec_context = stream->codec;
|
2017-11-08 10:21:51 +08:00
|
|
|
#endif
|
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
std::string type = mediaDesc->getType();
|
|
|
|
Debug(1, "Looking for codec for %s payload type %d / %s",
|
|
|
|
type.c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str());
|
2015-05-29 23:38:02 +08:00
|
|
|
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
|
2019-04-29 00:05:32 +08:00
|
|
|
if ( type == "video" )
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
|
2019-04-29 00:05:32 +08:00
|
|
|
else if ( type == "audio" )
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
|
2019-04-29 00:05:32 +08:00
|
|
|
else if ( type == "application" )
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->codec_type = AVMEDIA_TYPE_DATA;
|
2013-03-17 07:45:21 +08:00
|
|
|
#else
|
2019-04-29 00:05:32 +08:00
|
|
|
if ( type == "video" )
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->codec_type = CODEC_TYPE_VIDEO;
|
2019-04-29 00:05:32 +08:00
|
|
|
else if ( type == "audio" )
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->codec_type = CODEC_TYPE_AUDIO;
|
2019-04-29 00:05:32 +08:00
|
|
|
else if ( type == "application" )
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->codec_type = CODEC_TYPE_DATA;
|
2013-03-17 07:45:21 +08:00
|
|
|
#endif
|
2019-04-29 00:05:32 +08:00
|
|
|
else
|
|
|
|
Warning("Unknown media_type %s", type.c_str());
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2015-05-29 23:38:02 +08:00
|
|
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
2016-04-04 22:11:48 +08:00
|
|
|
std::string codec_name;
|
2015-05-29 23:38:02 +08:00
|
|
|
#endif
|
2017-11-19 05:00:10 +08:00
|
|
|
if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
// Look in static table
|
2017-11-19 05:00:10 +08:00
|
|
|
for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ ) {
|
|
|
|
if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName );
|
2015-05-29 23:38:02 +08:00
|
|
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
2019-04-29 00:05:32 +08:00
|
|
|
codec_name = std::string(smStaticPayloads[i].payloadName);
|
2015-05-29 23:38:02 +08:00
|
|
|
#else
|
2019-04-29 00:05:32 +08:00
|
|
|
strncpy(codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name));
|
2015-05-29 23:38:02 +08:00
|
|
|
#endif
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->codec_type = smStaticPayloads[i].codecType;
|
|
|
|
codec_context->codec_id = smStaticPayloads[i].codecId;
|
|
|
|
codec_context->sample_rate = smStaticPayloads[i].clockRate;
|
2016-04-04 22:11:48 +08:00
|
|
|
break;
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
2017-11-08 10:21:51 +08:00
|
|
|
} else {
|
2016-04-04 22:11:48 +08:00
|
|
|
// Look in dynamic table
|
2017-11-08 10:21:51 +08:00
|
|
|
for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) {
|
|
|
|
if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) {
|
2019-04-29 00:05:32 +08:00
|
|
|
Debug(1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName);
|
2015-05-29 23:38:02 +08:00
|
|
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
2019-04-29 00:05:32 +08:00
|
|
|
codec_name = std::string(smStaticPayloads[i].payloadName);
|
2015-05-29 23:38:02 +08:00
|
|
|
#else
|
2019-04-29 00:05:32 +08:00
|
|
|
strncpy(codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name));
|
2015-05-29 23:38:02 +08:00
|
|
|
#endif
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->codec_type = smDynamicPayloads[i].codecType;
|
|
|
|
codec_context->codec_id = smDynamicPayloads[i].codecId;
|
|
|
|
codec_context->sample_rate = mediaDesc->getClock();
|
2016-04-04 22:11:48 +08:00
|
|
|
break;
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
2019-04-29 00:05:32 +08:00
|
|
|
} /// end if static or dynamic
|
2015-05-29 23:38:02 +08:00
|
|
|
|
2020-12-28 01:04:17 +08:00
|
|
|
|
2015-05-29 23:38:02 +08:00
|
|
|
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( codec_name.empty() )
|
2015-05-29 23:38:02 +08:00
|
|
|
#else
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( !stream->codec->codec_name[0] )
|
2015-05-29 23:38:02 +08:00
|
|
|
#endif
|
2016-04-04 22:11:48 +08:00
|
|
|
{
|
2019-04-29 00:05:32 +08:00
|
|
|
Warning( "Can't find payload details for %s payload type %d, name %s",
|
|
|
|
mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
|
|
|
if ( mediaDesc->getWidth() )
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->width = mediaDesc->getWidth();
|
2016-04-04 22:11:48 +08:00
|
|
|
if ( mediaDesc->getHeight() )
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->height = mediaDesc->getHeight();
|
|
|
|
if ( codec_context->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size()) {
|
2016-04-04 22:11:48 +08:00
|
|
|
uint8_t start_sequence[]= { 0, 0, 1 };
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->extradata_size= 0;
|
2020-08-26 07:45:48 +08:00
|
|
|
codec_context->extradata= nullptr;
|
2016-04-04 22:11:48 +08:00
|
|
|
char pvalue[1024], *value = pvalue;
|
|
|
|
|
|
|
|
strcpy(pvalue, mediaDesc->getSprops().c_str());
|
|
|
|
|
2020-12-28 01:04:17 +08:00
|
|
|
while ( *value ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
char base64packet[1024];
|
|
|
|
uint8_t decoded_packet[1024];
|
|
|
|
uint32_t packet_size;
|
|
|
|
char *dst = base64packet;
|
2013-09-27 19:08:11 +08:00
|
|
|
|
2016-04-04 22:11:48 +08:00
|
|
|
while (*value && *value != ','
|
|
|
|
&& (dst - base64packet) < (long)(sizeof(base64packet)) - 1) {
|
2019-04-29 00:05:32 +08:00
|
|
|
*dst++ = *value++;
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
|
|
|
*dst++ = '\0';
|
2013-09-27 19:08:11 +08:00
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
if ( *value == ',' )
|
2016-04-04 22:11:48 +08:00
|
|
|
value++;
|
2013-09-27 19:08:11 +08:00
|
|
|
|
2020-12-28 01:04:17 +08:00
|
|
|
packet_size = av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
|
2016-04-04 22:11:48 +08:00
|
|
|
Hexdump(4, (char *)decoded_packet, packet_size);
|
2020-12-28 01:04:17 +08:00
|
|
|
if ( packet_size ) {
|
2016-04-04 22:11:48 +08:00
|
|
|
uint8_t *dest =
|
|
|
|
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->extradata_size +
|
|
|
|
#if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0)
|
|
|
|
AV_INPUT_BUFFER_PADDING_SIZE
|
|
|
|
#else
|
|
|
|
FF_INPUT_BUFFER_PADDING_SIZE
|
|
|
|
#endif
|
|
|
|
);
|
2019-04-29 00:05:32 +08:00
|
|
|
if ( dest ) {
|
|
|
|
if ( codec_context->extradata_size ) {
|
|
|
|
// av_realloc?
|
2017-11-08 10:21:51 +08:00
|
|
|
memcpy(dest, codec_context->extradata, codec_context->extradata_size);
|
2019-04-29 00:05:32 +08:00
|
|
|
av_free(codec_context->extradata);
|
|
|
|
}
|
2013-09-27 19:08:11 +08:00
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence));
|
|
|
|
memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
|
|
|
|
memset(dest+codec_context->extradata_size+sizeof(start_sequence)+
|
|
|
|
packet_size, 0,
|
2017-11-08 10:21:51 +08:00
|
|
|
#if LIBAVCODEC_VERSION_CHECK(57, 0, 0, 0, 0)
|
2019-04-29 00:05:32 +08:00
|
|
|
AV_INPUT_BUFFER_PADDING_SIZE
|
2017-11-08 10:21:51 +08:00
|
|
|
#else
|
2019-04-29 00:05:32 +08:00
|
|
|
FF_INPUT_BUFFER_PADDING_SIZE
|
2017-11-08 10:21:51 +08:00
|
|
|
#endif
|
2019-04-29 00:05:32 +08:00
|
|
|
);
|
2013-09-27 19:08:11 +08:00
|
|
|
|
2017-11-08 10:21:51 +08:00
|
|
|
codec_context->extradata= dest;
|
|
|
|
codec_context->extradata_size+= sizeof(start_sequence)+packet_size;
|
2016-04-04 22:11:48 +08:00
|
|
|
// } else {
|
|
|
|
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
|
|
|
|
// return AVERROR(ENOMEM);
|
|
|
|
}
|
2013-09-27 19:08:11 +08:00
|
|
|
}
|
2016-04-04 22:11:48 +08:00
|
|
|
}
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
2020-12-28 01:04:17 +08:00
|
|
|
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
|
|
|
|
avcodec_parameters_from_context(stream->codecpar, codec_context);
|
|
|
|
#endif
|
|
|
|
} // end foreach mediaList
|
2013-03-17 07:45:21 +08:00
|
|
|
|
2019-04-29 00:05:32 +08:00
|
|
|
return formatContext;
|
2013-03-17 07:45:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif // HAVE_LIBAVFORMAT
|