Merge pull request #259 from ebarnard/rtsp-fix

Regression:  Fix RTSP decoding errors in 1.26.4 fixes #221
This commit is contained in:
Kyle Johnson 2013-12-13 10:12:50 -08:00
commit 45ad839f96
6 changed files with 120 additions and 50 deletions

View File

@ -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 )

View File

@ -41,6 +41,8 @@ protected:
int rtcp_sd;
Buffer buffer;
Buffer lastSps;
Buffer lastPps;
RtspThread::RtspMethod method;

View File

@ -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 );

View File

@ -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

View File

@ -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

View File

@ -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 );