Merge pull request #259 from ebarnard/rtsp-fix
Regression: Fix RTSP decoding errors in 1.26.4 fixes #221
This commit is contained in:
commit
45ad839f96
|
@ -261,6 +261,31 @@ int RemoteCameraRtsp::Capture( Image &image )
|
|||
if ( !buffer.size() )
|
||||
return( -1 );
|
||||
|
||||
if(mCodecContext->codec_id == CODEC_ID_H264)
|
||||
{
|
||||
// SPS and PPS frames should be saved and appended to IDR frames
|
||||
int nalType = (buffer.head()[3] & 0x1f);
|
||||
|
||||
// SPS
|
||||
if(nalType == 7)
|
||||
{
|
||||
lastSps = buffer;
|
||||
continue;
|
||||
}
|
||||
// PPS
|
||||
else if(nalType == 8)
|
||||
{
|
||||
lastPps = buffer;
|
||||
continue;
|
||||
}
|
||||
// IDR
|
||||
else if(nalType == 5)
|
||||
{
|
||||
buffer += lastSps;
|
||||
buffer += lastPps;
|
||||
}
|
||||
}
|
||||
|
||||
av_init_packet( &packet );
|
||||
|
||||
while ( !frameComplete && buffer.size() > 0 )
|
||||
|
|
|
@ -41,6 +41,8 @@ protected:
|
|||
int rtcp_sd;
|
||||
|
||||
Buffer buffer;
|
||||
Buffer lastSps;
|
||||
Buffer lastPps;
|
||||
|
||||
RtspThread::RtspMethod method;
|
||||
|
||||
|
|
|
@ -143,8 +143,13 @@ int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen )
|
|||
mStop = true;
|
||||
break;
|
||||
}
|
||||
case RTCP_RR :
|
||||
case RTCP_APP :
|
||||
{
|
||||
// Ignoring as per RFC 3550
|
||||
Debug( 5, "Received RTCP_APP packet, ignoring.");
|
||||
break;
|
||||
}
|
||||
case RTCP_RR :
|
||||
default :
|
||||
{
|
||||
Error( "Received unexpected packet type %d, ignoring", pt );
|
||||
|
|
|
@ -24,12 +24,19 @@
|
|||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
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 ) :
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0)
|
||||
#define _AVCODECID AVCodecID
|
||||
#else
|
||||
#define _AVCODECID CodecID
|
||||
#endif
|
||||
|
||||
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 ),
|
||||
mRemoteHost( remoteHost ),
|
||||
mRtpClock( rtpClock ),
|
||||
mCodecId( codecId ),
|
||||
mFrame( 65536 ),
|
||||
mFrameCount( 0 ),
|
||||
mFrameGood( true ),
|
||||
|
@ -61,6 +68,9 @@ RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, c
|
|||
mLastSrTimeReal = tvZero();
|
||||
mLastSrTimeNtp = tvZero();
|
||||
mLastSrTimeRtp = 0;
|
||||
|
||||
if(mCodecId != CODEC_ID_H264 && mCodecId != CODEC_ID_MPEG4)
|
||||
Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." );
|
||||
}
|
||||
|
||||
void RtpSource::init( uint16_t seq )
|
||||
|
@ -253,57 +263,63 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
|||
{
|
||||
const RtpDataHeader *rtpHeader;
|
||||
rtpHeader = (RtpDataHeader *)packet;
|
||||
bool fragmentEnd = false;
|
||||
|
||||
// Each RTP packet delivers only one NAL. It can be either the Single NAL
|
||||
// ( in that case it must be in one packet ) or the Fragmentation NALs
|
||||
// that delivers large single NAL...
|
||||
int rtpHeaderSize = 12 + rtpHeader->cc * 4;
|
||||
// No need to check for nal type as non fragmented packets already have 001 start sequence appended
|
||||
bool h264FragmentEnd = (mCodecId == CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40);
|
||||
bool thisM = rtpHeader->m || h264FragmentEnd;
|
||||
|
||||
if ( updateSeq( ntohs(rtpHeader->seqN) ) )
|
||||
{
|
||||
Hexdump( 4, packet+sizeof(RtpDataHeader), 16 );
|
||||
if ( ((packet[sizeof(RtpDataHeader)] & 0x1f) == 28 &&
|
||||
(packet[sizeof(RtpDataHeader)+1] & 0x80)) ||
|
||||
((packet[sizeof(RtpDataHeader)] & 0x1f) != 28 &&
|
||||
prevM && rtpHeader->m) )
|
||||
mFrameGood = true; // This means that if packet is in sequence
|
||||
// and is single NAL with mark set (and prev packet
|
||||
// was NAL with mark set or it is Fragmentation NAL with
|
||||
// Start bit set then we assume that sequence
|
||||
// was restored and we can handle packet
|
||||
Hexdump( 4, packet+rtpHeaderSize, 16 );
|
||||
|
||||
if ( mFrameGood )
|
||||
{
|
||||
// check if there fragmentation NAL
|
||||
if ( (packet[sizeof(RtpDataHeader)] & 0x1f) == 28 )
|
||||
{
|
||||
// is this NAL the first NAL in fragmentation sequence
|
||||
if ( packet[sizeof(RtpDataHeader)+1] & 0x80 )
|
||||
{
|
||||
// if there is any data in frame then we must
|
||||
// discard it because that frame was incomplete
|
||||
if ( mFrame.size() )
|
||||
mFrame.clear();
|
||||
// Now we will form new header of frame
|
||||
mFrame.append("\x0\x0\x1\x0",4);
|
||||
*(mFrame+3) = (packet[sizeof(RtpDataHeader)+1] & 0x1f) |
|
||||
(packet[sizeof(RtpDataHeader)] & 0x60);
|
||||
}
|
||||
else
|
||||
if ( packet[sizeof(RtpDataHeader)+1] & 0x40 )
|
||||
fragmentEnd = true;
|
||||
mFrame.append(packet+sizeof(RtpDataHeader)+2, packetLen-sizeof(RtpDataHeader)-2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// mframe.clear();
|
||||
if ( !mFrame.size() )
|
||||
mFrame.append("\x0\x0\x1",3);
|
||||
mFrame.append( packet+sizeof(RtpDataHeader), packetLen-sizeof(RtpDataHeader) );
|
||||
}
|
||||
int extraHeader = 0;
|
||||
|
||||
if( mCodecId == CODEC_ID_H264 )
|
||||
{
|
||||
int nalType = (packet[rtpHeaderSize] & 0x1f);
|
||||
|
||||
switch (nalType)
|
||||
{
|
||||
case 24:
|
||||
{
|
||||
extraHeader = 2;
|
||||
break;
|
||||
}
|
||||
case 25: case 26: case 27:
|
||||
{
|
||||
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 )
|
||||
{
|
||||
// Now we will form new header of frame
|
||||
mFrame.append( "\x0\x0\x1\x0", 4 );
|
||||
// Reconstruct NAL header from FU headers
|
||||
*(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) |
|
||||
(packet[rtpHeaderSize] & 0xe0);
|
||||
}
|
||||
|
||||
extraHeader = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Append NAL frame start code
|
||||
if ( !mFrame.size() )
|
||||
mFrame.append( "\x0\x0\x1", 3 );
|
||||
}
|
||||
mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader );
|
||||
}
|
||||
|
||||
Hexdump( 4, mFrame.head(), 16 );
|
||||
|
||||
if ( rtpHeader->m || fragmentEnd )
|
||||
if ( thisM )
|
||||
{
|
||||
if ( mFrameGood )
|
||||
{
|
||||
|
@ -339,7 +355,7 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
|
|||
mFrameGood = false;
|
||||
mFrame.clear();
|
||||
}
|
||||
if ( rtpHeader->m || fragmentEnd )
|
||||
if ( thisM )
|
||||
{
|
||||
mFrameGood = true;
|
||||
prevM = true;
|
||||
|
@ -368,3 +384,5 @@ bool RtpSource::getFrame( Buffer &buffer )
|
|||
Debug( 3, "Copied %d bytes", buffer.size() );
|
||||
return( true );
|
||||
}
|
||||
|
||||
#undef _AVCODECID
|
|
@ -21,12 +21,19 @@
|
|||
#define ZM_RTP_SOURCE_H
|
||||
|
||||
#include "zm_buffer.h"
|
||||
#include "zm_ffmpeg.h"
|
||||
#include "zm_thread.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0)
|
||||
#define _AVCODECID AVCodecID
|
||||
#else
|
||||
#define _AVCODECID CodecID
|
||||
#endif
|
||||
|
||||
struct RtpDataHeader;
|
||||
|
||||
class RtpSource
|
||||
|
@ -81,6 +88,8 @@ private:
|
|||
uint32_t mLostPackets;
|
||||
uint8_t mLostFraction;
|
||||
|
||||
_AVCODECID mCodecId;
|
||||
|
||||
Buffer mFrame;
|
||||
int mFrameCount;
|
||||
bool mFrameGood;
|
||||
|
@ -92,7 +101,8 @@ private:
|
|||
void init( uint16_t seq );
|
||||
|
||||
public:
|
||||
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 );
|
||||
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 );
|
||||
|
||||
bool updateSeq( uint16_t seq );
|
||||
void updateJitter( const RtpDataHeader *header );
|
||||
void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime );
|
||||
|
@ -177,4 +187,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
#undef _AVCODECID
|
||||
|
||||
#endif // ZM_RTP_SOURCE_H
|
||||
|
|
|
@ -330,6 +330,13 @@ int RtspThread::run()
|
|||
|
||||
uint32_t rtpClock = 0;
|
||||
std::string trackUrl = mUrl;
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0)
|
||||
enum AVCodecID codecId;
|
||||
#else
|
||||
enum CodecID codecId;
|
||||
#endif
|
||||
|
||||
if ( mFormatContext->nb_streams >= 1 )
|
||||
{
|
||||
for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ )
|
||||
|
@ -343,6 +350,7 @@ int RtspThread::run()
|
|||
{
|
||||
trackUrl += "/"+mediaDesc->getControlUrl();
|
||||
rtpClock = mediaDesc->getClock();
|
||||
codecId = mFormatContext->streams[i]->codec->codec_id;
|
||||
// Hackery pokery
|
||||
//rtpClock = mFormatContext->streams[i]->codec->sample_rate;
|
||||
break;
|
||||
|
@ -500,7 +508,7 @@ int RtspThread::run()
|
|||
{
|
||||
case RTP_UNICAST :
|
||||
{
|
||||
RtpSource *source = new RtpSource( mId, "", localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime );
|
||||
RtpSource *source = new RtpSource( mId, "", localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId );
|
||||
mSources[ssrc] = source;
|
||||
RtpDataThread rtpDataThread( *this, *source );
|
||||
RtpCtrlThread rtpCtrlThread( *this, *source );
|
||||
|
@ -545,7 +553,7 @@ int RtspThread::run()
|
|||
case RTP_RTSP :
|
||||
case RTP_RTSP_HTTP :
|
||||
{
|
||||
RtpSource *source = new RtpSource( mId, "", remoteChannels[0], mHost, remoteChannels[0], ssrc, seq, rtpClock, rtpTime );
|
||||
RtpSource *source = new RtpSource( mId, "", remoteChannels[0], mHost, remoteChannels[0], ssrc, seq, rtpClock, rtpTime, codecId );
|
||||
mSources[ssrc] = source;
|
||||
// These never actually run
|
||||
RtpDataThread rtpDataThread( *this, *source );
|
||||
|
@ -673,7 +681,7 @@ int RtspThread::run()
|
|||
}
|
||||
case RTP_MULTICAST :
|
||||
{
|
||||
RtpSource *source = new RtpSource( mId, localHost, localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime );
|
||||
RtpSource *source = new RtpSource( mId, localHost, localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId );
|
||||
mSources[ssrc] = source;
|
||||
RtpDataThread rtpDataThread( *this, *source );
|
||||
RtpCtrlThread rtpCtrlThread( *this, *source );
|
||||
|
|
Loading…
Reference in New Issue