Fix Remote RTSP Method on newer ffmpeg

This commit is contained in:
Isaac Connor 2019-04-28 12:05:32 -04:00
parent a0dbb70af6
commit 28269eccc3
7 changed files with 350 additions and 445 deletions

View File

@ -28,7 +28,22 @@
#include <sys/types.h>
#include <sys/socket.h>
RemoteCameraRtsp::RemoteCameraRtsp( unsigned int p_monitor_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
RemoteCameraRtsp::RemoteCameraRtsp(
unsigned int p_monitor_id,
const std::string &p_method,
const std::string &p_host,
const std::string &p_port,
const std::string &p_path,
int p_width,
int p_height,
bool p_rtsp_describe,
int p_colours,
int p_brightness,
int p_contrast,
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio ) :
RemoteCamera( p_monitor_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
rtsp_describe( p_rtsp_describe ),
rtspThread( 0 )
@ -169,7 +184,7 @@ int RemoteCameraRtsp::PrimeCapture() {
} else {
Debug(2, "Have another video stream." );
}
}
} else
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
#else
@ -181,6 +196,8 @@ int RemoteCameraRtsp::PrimeCapture() {
} else {
Debug(2, "Have another audio stream." );
}
} else {
Debug(1, "Have unknown codec type in stream %d : %d", i, mFormatContext->streams[i]->codec->codec_type);
}
} // end foreach stream

View File

@ -34,8 +34,7 @@
// accessed over a network connection using rtsp protocol
// (Real Time Streaming Protocol)
//
class RemoteCameraRtsp : public RemoteCamera
{
class RemoteCameraRtsp : public RemoteCamera {
protected:
struct sockaddr_in rtsp_sa;
struct sockaddr_in rtcp_sa;

View File

@ -28,12 +28,12 @@
#include <errno.h>
RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false )
RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource )
: mRtspThread( rtspThread ), mRtpSource( rtpSource ), mStop( false )
{
}
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
{
int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) {
const RtcpPacket *rtcpPacket;
rtcpPacket = (RtcpPacket *)packet;
@ -48,33 +48,24 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
int pt = rtcpPacket->header.pt;
int len = ntohs(rtcpPacket->header.lenN);
Debug( 5, "RTCP Ver: %d", ver );
Debug( 5, "RTCP Count: %d", count );
Debug( 5, "RTCP Pt: %d", pt );
Debug( 5, "RTCP len: %d", len );
Debug( 5, "RTCP Ver: %d Count: %d Pt: %d len: %d", ver, count, pt, len);
switch( pt )
{
switch( pt ) {
case RTCP_SR :
{
uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN);
Debug( 5, "RTCP Got SR (%x)", ssrc );
if ( mRtpSource.getSsrc() )
{
if ( ssrc != mRtpSource.getSsrc() )
{
if ( mRtpSource.getSsrc() ) {
if ( ssrc != mRtpSource.getSsrc() ) {
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 );
}
}
else if ( ssrc )
{
} else if ( ssrc ) {
mRtpSource.setSsrc( ssrc );
}
if ( len > 1 )
{
if ( len > 1 ) {
//printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts );
uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN);
uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN);
@ -89,25 +80,21 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
case RTCP_SDES :
{
ssize_t contentLen = packetLen - sizeof(rtcpPacket->header);
while ( contentLen )
{
while ( contentLen ) {
Debug( 5, "RTCP CL: %zd", contentLen );
uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN);
Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count );
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) )
{
if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) {
Warning( "Discarding packet for unrecognised ssrc %x", ssrc );
return( -1 );
}
unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item;
for ( int i = 0; i < count; i++ )
{
for ( int i = 0; i < count; i++ ) {
RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr;
Debug( 5, "RTCP Item length %d", item->len );
switch( item->type )
{
switch( item->type ) {
case RTCP_SDES_CNAME :
{
std::string cname( item->data, item->len );
@ -123,10 +110,8 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
case RTCP_SDES_NOTE :
case RTCP_SDES_PRIV :
default :
{
Error( "Received unexpected SDES item type %d, ignoring", item->type );
return( -1 );
}
return -1;
}
int paddedLen = 4+2+item->len+1; // Add null byte
paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4
@ -134,39 +119,30 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
sdesPtr += paddedLen;
contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0;
}
}
} // end whiel contentLen
break;
}
case RTCP_BYE :
{
Debug(5, "RTCP Got BYE");
mStop = true;
break;
}
case RTCP_APP :
{
// Ignoring as per RFC 3550
Debug(5, "Received RTCP_APP packet, ignoring.");
break;
}
case RTCP_RR :
{
Error("Received RTCP_RR packet.");
return( -1 );
}
return -1;
default :
{
// Ignore unknown packet types. Some cameras do this by design.
Debug(5, "Received unexpected packet type %d, ignoring", pt);
break;
}
}
consumed = sizeof(uint32_t)*(len+1);
return( consumed );
return consumed;
}
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
{
int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]);
@ -180,11 +156,13 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
mRtpSource.updateRtcpStats();
Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 );
Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() );
Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() );
Debug( 5, "Jitter = %d", mRtpSource.getJitter() );
Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() );
Debug(5, "Ssrc = %d Ssrc_1 = %d Last Seq = %d Jitter = %d Last SR = %d",
mRtspThread.getSsrc()+1,
mRtpSource.getSsrc(),
mRtpSource.getMaxSeq(),
mRtpSource.getJitter(),
mRtpSource.getLastSrTimestamp()
);
rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1);
rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc());
@ -195,11 +173,10 @@ int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen )
rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp());
rtcpPacket->body.rr.rr[0].dlsrN = 0;
return( wordLen*sizeof(uint32_t) );
}
return wordLen*sizeof(uint32_t);
} // end RtpCtrlThread::generateRr
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen )
{
int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
const std::string &cname = mRtpSource.getCname();
@ -218,11 +195,10 @@ int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen
rtcpPacket->body.sdes.item[0].len = cname.size();
memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() );
return( wordLen*sizeof(uint32_t) );
}
return wordLen*sizeof(uint32_t);
} // end RtpCtrlThread::generateSdes
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
{
int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) {
RtcpPacket *rtcpPacket = (RtcpPacket *)packet;
int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]);
@ -236,11 +212,10 @@ int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen )
rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc());
return( wordLen*sizeof(uint32_t) );
}
return wordLen*sizeof(uint32_t);
} // end RtpCtrolThread::generateBye
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
{
int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) {
unsigned char *bufferPtr = buffer;
// u_int32 len; /* length of compound RTCP packet in words */
@ -259,33 +234,28 @@ int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes )
// /* something wrong with packet format */
// }
while ( nBytes > 0 )
{
while ( nBytes > 0 ) {
int consumed = recvPacket( bufferPtr, nBytes );
if ( consumed <= 0 )
break;
bufferPtr += consumed;
nBytes -= consumed;
}
return( nBytes );
return nBytes;
}
int RtpCtrlThread::run()
{
int RtpCtrlThread::run() {
Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() );
SockAddrInet localAddr, remoteAddr;
bool sendReports;
UdpInetSocket rtpCtrlServer;
if ( mRtpSource.getLocalHost() != "" )
{
if ( mRtpSource.getLocalHost() != "" ) {
if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) )
Fatal( "Failed to bind RTCP server" );
sendReports = false;
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
}
else
{
} else {
if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) )
Fatal( "Failed to bind RTCP server" );
Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() );
@ -309,15 +279,15 @@ int RtpCtrlThread::run()
time_t now = time(NULL);
Select::CommsList readable = select.getReadable();
if ( readable.size() == 0 )
{
if ( readable.size() == 0 ) {
if ( ! timeout ) {
// With this code here, we will send an SDES and RR packet every 10 seconds
ssize_t nBytes;
unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d",
bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) );
if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 )
Error("Unable to send: %s", strerror(errno));
timeout = true;
@ -332,19 +302,15 @@ int RtpCtrlThread::run()
timeout = false;
last_receive = time(NULL);
}
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter )
{
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) )
{
for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) {
if ( UdpInetSocket *socket = dynamic_cast<UdpInetSocket *>(*iter) ) {
ssize_t nBytes = socket->recv( buffer, sizeof(buffer) );
Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() );
if ( nBytes )
{
if ( nBytes ) {
recvPackets( buffer, nBytes );
if ( sendReports )
{
if ( sendReports ) {
unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
@ -358,16 +324,14 @@ int RtpCtrlThread::run()
mStop = true;
break;
}
}
else
{
} else {
Panic("Barfed");
}
}
} // end if socket
} // end foeach comms iterator
}
rtpCtrlServer.close();
mRtspThread.stop();
return( 0 );
return 0;
}
#endif // HAVE_LIBAVFORMAT

View File

@ -34,13 +34,11 @@
class RtspThread;
class RtpSource;
class RtpCtrlThread : public Thread
{
class RtpCtrlThread : public Thread {
friend class RtspThread;
private:
typedef enum
{
typedef enum {
RTCP_SR = 200,
RTCP_RR = 201,
RTCP_SDES = 202,
@ -48,8 +46,7 @@ private:
RTCP_APP = 204
} RtcpType;
typedef enum
{
typedef enum {
RTCP_SDES_END = 0,
RTCP_SDES_CNAME = 1,
RTCP_SDES_NAME = 2,
@ -61,8 +58,7 @@ private:
RTCP_SDES_PRIV = 8
} RtcpSdesType;
struct RtcpCommonHeader
{
struct RtcpCommonHeader {
uint8_t count:5; // varies by packet type
uint8_t p:1; // padding flag
uint8_t version:2; // protocol version
@ -71,8 +67,7 @@ private:
};
// Reception report block
struct RtcpRr
{
struct RtcpRr {
uint32_t ssrcN; // data source being reported
int32_t lost:24; // cumul. no. pkts lost (signed!)
uint32_t fraction:8; // fraction lost since last SR/RR
@ -83,22 +78,18 @@ private:
};
// SDES item
struct RtcpSdesItem
{
struct RtcpSdesItem {
uint8_t type; // type of item (rtcp_sdes_type_t)
uint8_t len; // length of item (in octets)
char data[]; // text, not null-terminated
};
// RTCP packet
struct RtcpPacket
{
struct RtcpPacket {
RtcpCommonHeader header; // common header
union
{
union {
// Sender Report (SR)
struct Sr
{
struct Sr {
uint32_t ssrcN; // sender generating this report, network order
uint32_t ntpSecN; // NTP timestamp, network order
uint32_t ntpFracN;
@ -109,22 +100,19 @@ private:
} sr;
// Reception Report (RR)
struct Rr
{
struct Rr {
uint32_t ssrcN; // receiver generating this report
RtcpRr rr[]; // variable-length list
} rr;
// source description (SDES)
struct Sdes
{
struct Sdes {
uint32_t srcN; // first SSRC/CSRC
RtcpSdesItem item[]; // list of SDES items
} sdes;
// BYE
struct
{
struct {
uint32_t srcN[]; // list of sources
// can't express trailing text for reason (what does this mean? it's not even english!)
} bye;
@ -148,8 +136,7 @@ private:
public:
RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource );
void stop()
{
void stop() {
mStop = true;
}
};

View File

@ -26,7 +26,17 @@
#if HAVE_LIBAVCODEC
RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) :
RtpSource::RtpSource(
int id,
const std::string &localHost,
int localPortBase,
const std::string &remoteHost,
int remotePortBase,
uint32_t ssrc,
uint16_t seq,
uint32_t rtpClock,
uint32_t rtpTime,
_AVCODECID codecId ) :
mId( id ),
mSsrc( ssrc ),
mLocalHost( localHost ),
@ -66,11 +76,10 @@ RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, c
mLastSrTimeRtp = 0;
if ( mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4 )
Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
Warning("The device is using a codec (%d) that may not be supported. Do not be surprised if things don't work.", mCodecId);
}
void RtpSource::init( uint16_t seq )
{
void RtpSource::init( uint16_t seq ) {
Debug(3, "Initialising sequence");
mBaseSeq = seq;
mMaxSeq = seq;
@ -84,77 +93,58 @@ void RtpSource::init( uint16_t seq )
mTransit = 0;
}
bool RtpSource::updateSeq( uint16_t seq )
{
bool RtpSource::updateSeq( uint16_t seq ) {
uint16_t uDelta = seq - mMaxSeq;
// Source is not valid until MIN_SEQUENTIAL packets with
// sequential sequence numbers have been received.
Debug( 5, "Seq: %d", seq );
if ( mProbation)
{
if ( mProbation) {
// packet is in sequence
if ( seq == mMaxSeq + 1)
{
if ( seq == mMaxSeq + 1) {
Debug( 3, "Sequence in probation %d, in sequence", mProbation );
mProbation--;
mMaxSeq = seq;
if ( mProbation == 0 )
{
if ( mProbation == 0 ) {
init( seq );
mReceivedPackets++;
return( true );
}
}
else
{
} else {
Warning( "Sequence in probation %d, out of sequence", mProbation );
mProbation = MIN_SEQUENTIAL - 1;
mMaxSeq = seq;
return( false );
}
return( true );
}
else if ( uDelta < MAX_DROPOUT )
{
if ( uDelta == 1 )
{
} else if ( uDelta < MAX_DROPOUT ) {
if ( uDelta == 1 ) {
Debug( 4, "Packet in sequence, gap %d", uDelta );
}
else
{
} else {
Warning( "Packet in sequence, gap %d", uDelta );
}
// in order, with permissible gap
if ( seq < mMaxSeq )
{
if ( seq < mMaxSeq ) {
// Sequence number wrapped - count another 64K cycle.
mCycles += RTP_SEQ_MOD;
}
mMaxSeq = seq;
}
else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER )
{
} else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) {
Warning( "Packet out of sequence, gap %d", uDelta );
// the sequence number made a very large jump
if ( seq == mBadSeq )
{
if ( seq == mBadSeq ) {
Debug( 3, "Restarting sequence" );
// Two sequential packets -- assume that the other side
// restarted without telling us so just re-sync
// (i.e., pretend this was the first packet).
init( seq );
}
else
{
} else {
mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1);
return( false );
}
}
else
{
} else {
Warning( "Packet duplicate or reordered, gap %d", uDelta );
// duplicate or reordered packet
return( false );
@ -163,10 +153,8 @@ bool RtpSource::updateSeq( uint16_t seq )
return( uDelta==1?true:false );
}
void RtpSource::updateJitter( const RtpDataHeader *header )
{
if ( mRtpFactor > 0 )
{
void RtpSource::updateJitter( const RtpDataHeader *header ) {
if ( mRtpFactor > 0 ) {
Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) );
uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor );
Debug( 5, "Local RTP time = %x", localTimeRtp );
@ -174,8 +162,7 @@ void RtpSource::updateJitter( const RtpDataHeader *header )
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
Debug( 5, "Packet transit RTP time = %x", packetTransit );
if ( mTransit > 0 )
{
if ( mTransit > 0 ) {
// Jitter
int d = packetTransit - mTransit;
Debug( 5, "Jitter D = %d", d );
@ -185,28 +172,22 @@ void RtpSource::updateJitter( const RtpDataHeader *header )
mJitter += d - ((mJitter + 8) >> 4);
}
mTransit = packetTransit;
}
else
{
} else {
mJitter = 0;
}
Debug( 5, "RTP Jitter: %d", mJitter );
}
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime )
{
void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) {
struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) );
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
if ( mBaseTimeNtp.tv_sec == 0 )
{
if ( mBaseTimeNtp.tv_sec == 0 ) {
mBaseTimeReal = tvNow();
mBaseTimeNtp = ntpTime;
mBaseTimeRtp = rtpTime;
}
else if ( !mRtpClock )
{
} else if ( !mRtpClock ) {
Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime );
Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime );
@ -227,8 +208,7 @@ void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint
mLastSrTimeRtp = rtpTime;
}
void RtpSource::updateRtcpStats()
{
void RtpSource::updateRtcpStats() {
uint32_t extendedMax = mCycles + mMaxSeq;
mExpectedPackets = extendedMax - mBaseSeq + 1;
@ -255,8 +235,7 @@ void RtpSource::updateRtcpStats()
Debug( 5, "Lost fraction = %d", mLostFraction );
}
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
{
bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) {
const RtpDataHeader *rtpHeader;
rtpHeader = (RtpDataHeader *)packet;
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
@ -269,39 +248,29 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
// that there is no marker bit by changing the number of bits in the payload type field.
bool thisM = rtpHeader->m || h264FragmentEnd;
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
{
if ( updateSeq( ntohs(rtpHeader->seqN) ) ) {
Hexdump( 4, packet+rtpHeaderSize, 16 );
if ( mFrameGood )
{
if ( mFrameGood ) {
int extraHeader = 0;
if( mCodecId == AV_CODEC_ID_H264 )
{
if ( mCodecId == AV_CODEC_ID_H264 ) {
int nalType = (packet[rtpHeaderSize] & 0x1f);
Debug( 3, "Have H264 frame: nal type is %d", nalType );
switch (nalType)
{
switch (nalType) {
case 24: // STAP-A
{
extraHeader = 2;
break;
}
case 25: // STAP-B
case 26: // MTAP-16
case 27: // MTAP-24
{
extraHeader = 3;
break;
}
// FU-A and FU-B
case 28: case 29:
{
// Is this NAL the first NAL in fragmentation sequence
if ( packet[rtpHeaderSize+1] & 0x80 )
{
if ( packet[rtpHeaderSize+1] & 0x80 ) {
// Now we will form new header of frame
mFrame.append( "\x0\x0\x1\x0", 4 );
// Reconstruct NAL header from FU headers
@ -311,17 +280,14 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
extraHeader = 2;
break;
}
default:
{
Debug(3, "Unhandled nalType %d", nalType );
}
}
// Append NAL frame start code
if ( !mFrame.size() )
mFrame.append( "\x0\x0\x1", 3 );
}
} // end if H264
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
} else {
Debug( 3, "NOT H264 frame: type is %d", mCodecId );
@ -329,16 +295,13 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
Hexdump( 4, mFrame.head(), 16 );
if ( thisM )
{
if ( mFrameGood )
{
if ( thisM ) {
if ( mFrameGood ) {
Debug( 3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() );
mFrameProcessed.setValueImmediate( false );
mFrameReady.updateValueSignal( true );
if ( !mFrameProcessed.getValueImmediate() )
{
if ( !mFrameProcessed.getValueImmediate() ) {
// What is the point of this for loop? Is it just me, or will it call getUpdatedValue once or twice? Could it not be better written as
// if ( ! mFrameProcessed.getUpdatedValue( 1 ) && mFrameProcessed.getUpdatedValue( 1 ) ) return false;
@ -347,45 +310,34 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
return( false );
}
mFrameCount++;
}
else
{
} else {
Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() );
}
mFrame.clear();
}
}
else
{
if ( mFrame.size() )
{
} else {
if ( mFrame.size() ) {
Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() );
}
else
{
} else {
Warning( "Discarding frame %d", mFrameCount );
}
mFrameGood = false;
mFrame.clear();
}
if ( thisM )
{
if ( thisM ) {
mFrameGood = true;
prevM = true;
}
else
} else
prevM = false;
updateJitter( rtpHeader );
return( true );
return true;
}
bool RtpSource::getFrame( Buffer &buffer )
{
bool RtpSource::getFrame( Buffer &buffer ) {
Debug( 3, "Getting frame" );
if ( !mFrameReady.getValueImmediate() )
{
if ( !mFrameReady.getValueImmediate() ) {
// Allow for a couple of spurious returns
for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ )
if ( count > 1 )
@ -395,7 +347,7 @@ bool RtpSource::getFrame( Buffer &buffer )
mFrameReady.setValueImmediate( false );
mFrameProcessed.updateValueSignal( true );
Debug( 4, "Copied %d bytes", buffer.size() );
return( true );
return true;
}
#endif // HAVE_LIBAVCODEC

View File

@ -50,15 +50,15 @@ bool RtspThread::sendCommand( std::string message ) {
Debug(2, "Sending encoded RTSP message: %s", message.c_str());
if ( mRtspSocket2.send(message.c_str(), message.size()) != (int)message.length() ) {
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
return( false );
return false;
}
} else {
if ( mRtspSocket.send(message.c_str(), message.size()) != (int)message.length() ) {
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
return( false );
return false;
}
}
return( true );
return true;
}
bool RtspThread::recvResponse( std::string &response ) {
@ -76,23 +76,24 @@ bool RtspThread::recvResponse( std::string &response ) {
if ( response.size() )
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
}
return( false );
return false;
}
if ( respCode == 401 ) {
Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry");
mAuthenticator->checkAuthResponse(response);
mNeedAuth = true;
return( false );
return false;
} else if ( respCode != 200 ) {
Error("Unexpected response code %d, text is '%s'", respCode, respText);
return( false );
}
return( true );
return false;
}
return true;
} // end RtspThread::recResponse
int RtspThread::requestPorts() {
if ( !smMinDataPort ) {
char sql[ZM_SQL_SML_BUFSIZ];
//FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors
strncpy( sql, "select Id from Monitors where Function != 'None' and Type = 'Remote' and Protocol = 'rtsp' and Method = 'rtpUni' order by Id asc", sizeof(sql) );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
@ -129,11 +130,11 @@ int RtspThread::requestPorts() {
PortSet::const_iterator iter = smAssignedPorts.find(i);
if ( iter == smAssignedPorts.end() ) {
smAssignedPorts.insert(i);
return( i );
return i;
}
}
Panic("Can assign RTP port, no ports left in pool");
return( -1 );
return -1;
}
void RtspThread::releasePorts( int port ) {
@ -141,7 +142,15 @@ void RtspThread::releasePorts( int port ) {
smAssignedPorts.erase(port);
}
RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe) :
RtspThread::RtspThread(
int id,
RtspMethod method,
const std::string &protocol,
const std::string &host,
const std::string &port,
const std::string &path,
const std::string &auth,
bool rtsp_describe) :
mId( id ),
mMethod( method ),
mProtocol( protocol ),
@ -241,11 +250,11 @@ int RtspThread::run() {
Debug( 2, "Sending HTTP message: %s", message.c_str() );
if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) {
Error("Unable to send message '%s': %s", message.c_str(), strerror(errno));
return( -1 );
return -1;
}
if ( mRtspSocket.recv( response ) < 0 ) {
Error("Recv failed; %s", strerror(errno));
return( -1 );
return -1;
}
Debug(2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size());
@ -259,7 +268,7 @@ int RtspThread::run() {
if ( response.size() )
Hexdump( Logger::ERROR, response.data(), min(response.size(),16) );
}
return( -1 );
return -1;
}
// If Server requests authentication, check WWW-Authenticate header and fill required fields
// for requested authentication method
@ -277,7 +286,7 @@ int RtspThread::run() {
if ( respCode != 200 ) {
Error("Unexpected response code %d, text is '%s'", respCode, respText);
return( -1 );
return -1;
}
message = "POST "+mPath+" HTTP/1.0\r\n";
@ -300,18 +309,18 @@ int RtspThread::run() {
// Request supported RTSP commands by the server
message = "OPTIONS "+mUrl+" RTSP/1.0\r\n";
if ( !sendCommand( message ) )
return( -1 );
return -1;
// A negative return here may indicate auth failure, but we will have setup the auth mechanisms so we need to retry.
if ( !recvResponse(response) ) {
if ( mNeedAuth ) {
Debug( 2, "Resending OPTIONS due to possible auth requirement" );
if ( !sendCommand(message) )
return( -1 );
return -1;
if ( !recvResponse(response) )
return( -1 );
return -1;
} else {
return( -1 );
return -1;
}
} // end if failed response maybe due to auth
@ -332,6 +341,7 @@ int RtspThread::run() {
if (mNeedAuth)
authTried = true;
sendCommand(message);
// FIXME WHy sleep 1?
sleep(1);
res = recvResponse(response);
if ( !res && respCode==401 )
@ -341,7 +351,7 @@ int RtspThread::run() {
const std::string endOfHeaders = "\r\n\r\n";
size_t sdpStart = response.find(endOfHeaders);
if ( sdpStart == std::string::npos )
return( -1 );
return -1;
if ( mRtspDescribe ) {
std::string DescHeader = response.substr(0, sdpStart);
@ -355,8 +365,8 @@ int RtspThread::run() {
Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() );
break;
}
}
}
} // end foreach line
} // end if mRtspDescribe
sdpStart += endOfHeaders.length();
@ -368,7 +378,7 @@ int RtspThread::run() {
mFormatContext = mSessDesc->generateFormatContext();
} catch( const Exception &e ) {
Error( e.getMessage().c_str() );
return( -1 );
return -1;
}
#if 0
@ -672,51 +682,36 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
Hexdump( 4, (char *)buffer, 16 );
rtpDataThread.recvPacket( buffer+4, len );
Debug( 4, "Received" );
}
else if ( channel == remoteChannels[1] )
{
} else if ( channel == remoteChannels[1] ) {
// len = ntohs( *((unsigned short *)(buffer+2)) );
// Debug( 4, "Got %d bytes on control channel %d", nBytes, channel );
Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len );
Hexdump( 4, (char *)buffer, 16 );
rtpCtrlThread.recvPackets( buffer+4, len );
}
else
{
} else {
Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] );
buffer.clear();
break;
}
buffer.consume( len+4 );
nBytes -= len+4;
}
else
{
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 )
{
} else {
if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) {
Debug( 4, "Got keepalive response '%s'", (char *)buffer );
//buffer.consume( keepaliveResponse.size() );
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
{
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) {
int discardBytes = charPtr-(char *)buffer;
buffer -= discardBytes;
}
else
{
} else {
buffer.clear();
}
}
else
{
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) )
{
} else {
if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) {
int discardBytes = charPtr-(char *)buffer;
Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes );
Hexdump( -1, (char *)buffer, discardBytes );
buffer -= discardBytes;
}
else
{
} else {
Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() );
Hexdump( -1, (char *)buffer, 32 );
buffer.clear();
@ -764,13 +759,11 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
rtpDataThread.start();
rtpCtrlThread.start();
while( !mStop )
{
while ( !mStop ) {
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) )
{
if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) {
if ( !sendCommand( message ) )
return( -1 );
return -1;
lastKeepalive = time(NULL);
}
usleep(100000);
@ -784,9 +777,9 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
#endif
message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
if ( !sendCommand(message) )
return( -1 );
return -1;
if ( !recvResponse(response) )
return( -1 );
return -1;
rtpDataThread.stop();
rtpCtrlThread.stop();
@ -801,13 +794,11 @@ Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepali
break;
}
default:
{
Panic("Got unexpected method %d", mMethod);
break;
}
}
return( 0 );
return 0;
}
#endif // HAVE_LIBAVFORMAT

View File

@ -136,7 +136,12 @@ SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) :
mValue = atoi(tokens[1].c_str());
}
SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) :
SessionDescriptor::MediaDescriptor::MediaDescriptor(
const std::string &type,
int port,
int numPorts,
const std::string &transport,
int payloadType ) :
mType( type ),
mPort( port ),
mNumPorts( numPorts ),
@ -170,8 +175,7 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
throw Exception("Invalid SDP format at '"+line+"'");
line.erase(0, 2);
switch( sdpType )
{
switch( sdpType ) {
case 'v' :
mVersion = line;
break;
@ -204,19 +208,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
mAttributes.push_back( line );
StringVector tokens = split( line, ":", 2 );
std::string attrName = tokens[0];
if ( currMedia )
{
if ( attrName == "control" )
{
if ( currMedia ) {
if ( attrName == "control" ) {
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" );
currMedia->setControlUrl( tokens[1] );
}
else if ( attrName == "range" )
{
}
else if ( attrName == "rtpmap" )
{
} else if ( attrName == "range" ) {
} else if ( attrName == "rtpmap" ) {
// a=rtpmap:96 MP4V-ES/90000
if ( tokens.size() < 2 )
throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" );
@ -226,53 +224,46 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
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 );
if ( attrTokens.size() > 1 )
{
if ( attrTokens.size() > 1 ) {
StringVector payloadTokens = split( attrTokens[1], "/" );
std::string payloadDesc = payloadTokens[0];
int payloadClock = atoi(payloadTokens[1].c_str());
currMedia->setPayloadDesc( payloadDesc );
currMedia->setClock( payloadClock );
}
}
else if ( attrName == "framesize" )
{
} else if ( attrName == "framesize" ) {
// a=framesize:96 320-240
if ( tokens.size() < 2 )
throw Exception("Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'");
StringVector attrTokens = split(tokens[1], " ");
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() ) );
throw Exception( stringtf("Payload type mismatch, expected %d, got %d in '%s'",
currMedia->getPayloadType(), payloadType, line.c_str()));
//currMedia->setPayloadType( payloadType );
StringVector sizeTokens = split(attrTokens[1], "-");
int width = atoi(sizeTokens[0].c_str());
int height = atoi(sizeTokens[1].c_str());
currMedia->setFrameSize(width, height);
}
else if ( attrName == "framerate" )
{
} else if ( attrName == "framerate" ) {
// a=framerate:5.0
if ( tokens.size() < 2 )
throw Exception("Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'");
double frameRate = atof(tokens[1].c_str());
currMedia->setFrameRate(frameRate);
}
else if ( attrName == "fmtp" )
{
} else if ( attrName == "fmtp" ) {
// a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F
if ( tokens.size() < 2 )
throw Exception("Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'");
StringVector attrTokens = split(tokens[1], " ", 2);
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() ) );
throw Exception(stringtf("Payload type mismatch, expected %d, got %d in '%s'",
currMedia->getPayloadType(), payloadType, line.c_str()));
//currMedia->setPayloadType( payloadType );
if ( attrTokens.size() > 1 )
{
if ( attrTokens.size() > 1 ) {
StringVector attr2Tokens = split( attrTokens[1], "; " );
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ )
{
for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) {
StringVector attr3Tokens = split( attr2Tokens[i], "=" );
//Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() );
if ( attr3Tokens[0] == "profile-level-id" ) {
@ -292,7 +283,7 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
} else if ( attrName == "mpeg4-esid" ) {
// a=mpeg4-esid:201
} else {
Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() )
Debug(3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str());
}
} else {
Debug(3, "Ignoring general SDP attribute '%s'", line.c_str());
@ -320,12 +311,11 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
mMediaList.push_back(currMedia);
break;
}
}
}
} // end switch
} // end foreach line
}
SessionDescriptor::~SessionDescriptor()
{
SessionDescriptor::~SessionDescriptor() {
if ( mConnInfo )
delete mConnInfo;
if ( mBandInfo )
@ -334,8 +324,7 @@ SessionDescriptor::~SessionDescriptor()
delete mMediaList[i];
}
AVFormatContext *SessionDescriptor::generateFormatContext() const
{
AVFormatContext *SessionDescriptor::generateFormatContext() const {
AVFormatContext *formatContext = avformat_alloc_context();
#if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100))
@ -362,26 +351,31 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
AVCodecContext *codec_context = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(codec_context, stream->codecpar);
stream->codec = codec_context;
#else
AVCodecContext *codec_context = stream->codec;
#endif
Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
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());
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mediaDesc->getType() == "video" )
if ( type == "video" )
codec_context->codec_type = AVMEDIA_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" )
else if ( type == "audio" )
codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" )
else if ( type == "application" )
codec_context->codec_type = AVMEDIA_TYPE_DATA;
#else
if ( mediaDesc->getType() == "video" )
if ( type == "video" )
codec_context->codec_type = CODEC_TYPE_VIDEO;
else if ( mediaDesc->getType() == "audio" )
else if ( type == "audio" )
codec_context->codec_type = CODEC_TYPE_AUDIO;
else if ( mediaDesc->getType() == "application" )
else if ( type == "application" )
codec_context->codec_type = CODEC_TYPE_DATA;
#endif
else
Warning("Unknown media_type %s", type.c_str());
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
std::string codec_name;
@ -394,7 +388,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string(smStaticPayloads[i].payloadName);
#else
strncpy( codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name) );;
strncpy(codec_context->codec_name, smStaticPayloads[i].payloadName, sizeof(codec_context->codec_name));
#endif
codec_context->codec_type = smStaticPayloads[i].codecType;
codec_context->codec_id = smStaticPayloads[i].codecId;
@ -410,7 +404,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
codec_name = std::string(smStaticPayloads[i].payloadName);
#else
strncpy( codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name) );;
strncpy(codec_context->codec_name, smDynamicPayloads[i].payloadName, sizeof(codec_context->codec_name));
#endif
codec_context->codec_type = smDynamicPayloads[i].codecType;
codec_context->codec_id = smDynamicPayloads[i].codecId;
@ -418,7 +412,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
break;
}
}
}
} /// end if static or dynamic
#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103)
if ( codec_name.empty() )
@ -426,7 +420,8 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
if ( !stream->codec->codec_name[0] )
#endif
{
Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
Warning( "Can't find payload details for %s payload type %d, name %s",
mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() );
//return( 0 );
}
if ( mediaDesc->getWidth() )
@ -497,7 +492,7 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
}
}
return( formatContext );
return formatContext;
}
#endif // HAVE_LIBAVFORMAT