Now can recive RTSP stream.

Decode fragmented NAL
Understand sprop-parametr-sets
Don't check unsetted video format (zm_remote_camera_rtsp.cpp)
This commit is contained in:
POKKAHOH
2013-09-27 15:08:11 +04:00
parent dddec742e0
commit 33b6e5cd03
9 changed files with 160 additions and 10 deletions

View File

@@ -19,7 +19,7 @@
#ifndef ZM_FFMPEG_H
#define ZM_FFMPEG_H
#include <stdint.h>
#if HAVE_LIBAVCODEC
#ifdef __cplusplus
@@ -27,17 +27,35 @@ extern "C" {
#endif
#if HAVE_LIBAVUTIL_AVUTIL_H
#include <libavutil/avutil.h>
#include <libavutil/base64.h>
#elif HAVE_FFMPEG_AVUTIL_H
#include <ffmpeg/avutil.h>
#include <ffmpeg/base64.h>
#else
#error "No location for avutils.h found"
#endif
#if HAVE_LIBAVCODEC_AVCODEC_H
#include <libavcodec/avcodec.h>
#elif HAVE_FFMPEG_AVCODEC_H
#include <ffmpeg/avcodec.h>
#else
#error "No location for avcodec.h found"
#endif
#if HAVE_LIBAVFORMAT_AVFORMAT_H
#include <libavformat/avformat.h>
#elif HAVE_FFMPEG_AVFORMAT_H
#include <ffmpeg/avformat.h>
#else
#error "No location for avformat.h found"
#endif
#if HAVE_LIBSWSCALE
#if HAVE_LIBSWSCALE_SWSCALE_H
#include <libswscale/swscale.h>
#include <libavutil/mathematics.h> // this is a fix for error: av_rescale_q was not declared in this scope
#include <libavutil/mathematics.h> // this is a fix for error: тАШav_rescale_qтАЩ was not declared in this scope
#elif HAVE_FFMPEG_SWSCALE_H
#include <ffmpeg/swscale.h>
#else
#error "No location for swscale.h found"
#endif
#endif // HAVE_LIBSWSCALE
#ifdef __cplusplus
@@ -72,3 +90,4 @@ extern "C" {
#endif // HAVE_LIBAVCODEC
#endif // ZM_FFMPEG_H

View File

@@ -203,7 +203,7 @@ int RemoteCameraRtsp::PrimeCapture()
if( (unsigned int)pSize != imagesize) {
Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize);
}
/*
#if HAVE_LIBSWSCALE
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
@@ -216,7 +216,7 @@ int RemoteCameraRtsp::PrimeCapture()
#else // HAVE_LIBSWSCALE
Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" );
#endif // HAVE_LIBSWSCALE
*/
return( 0 );
}

View File

@@ -253,15 +253,57 @@ 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...
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
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) );
}
}
Hexdump( 4, mFrame.head(), 16 );
if ( rtpHeader->m )
if ( rtpHeader->m || fragmentEnd )
{
if ( mFrameGood )
{
@@ -297,10 +339,13 @@ bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen )
mFrameGood = false;
mFrame.clear();
}
if ( rtpHeader->m )
if ( rtpHeader->m || fragmentEnd )
{
mFrameGood = true;
prevM = true;
}
else
prevM = false;
updateJitter( rtpHeader );

View File

@@ -84,6 +84,7 @@ private:
Buffer mFrame;
int mFrameCount;
bool mFrameGood;
bool prevM;
ThreadData<bool> mFrameReady;
ThreadData<bool> mFrameProcessed;

View File

@@ -576,13 +576,15 @@ int RtspThread::run()
{
if ( buffer[0] == '$' )
{
if ( buffer.size() < 4 )
break;
unsigned char channel = buffer[1];
unsigned short len = ntohs( *((unsigned short *)(buffer+2)) );
Debug( 4, "Got %d bytes left, expecting %d byte packet on channel %d", buffer.size(), len, channel );
if ( (unsigned short)buffer.size() < (len+4) )
{
Debug( 4, "Missing %zd bytes, rereading", (len+4)-nBytes );
Debug( 4, "Missing %d bytes, rereading", (len+4)-buffer.size() );
break;
}
if ( channel == remoteChannels[0] )
@@ -594,8 +596,10 @@ int RtspThread::run()
}
else if ( channel == remoteChannels[1] )
{
len = ntohs( *((unsigned short *)(buffer+2)) );
Debug( 4, "Got %zd bytes on control channel %d", nBytes, channel );
// 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

View File

@@ -144,6 +144,7 @@ SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, in
mClock( 0 ),
mWidth( 0 ),
mHeight( 0 ),
mSprops( "" ),
mConnInfo( 0 )
{
}
@@ -273,6 +274,13 @@ SessionDescriptor::SessionDescriptor( const std::string &url, const std::string
else if ( attr3Tokens[0] == "config" )
{
}
else if ( attr3Tokens[0] == "sprop-parameter-sets" )
{
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));
}
else
{
Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() )
@@ -330,6 +338,11 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) );
if ( mName.length() )
strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) );
if ( mInfo.length() )
strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) );
//formatContext->nb_streams = mMediaList.size();
for ( unsigned int i = 0; i < mMediaList.size(); i++ )
{
@@ -395,6 +408,58 @@ AVFormatContext *SessionDescriptor::generateFormatContext() const
stream->codec->width = mediaDesc->getWidth();
if ( mediaDesc->getHeight() )
stream->codec->height = mediaDesc->getHeight();
if ( stream->codec->codec_id == CODEC_ID_H264 && mediaDesc->getSprops().size())
{
uint8_t start_sequence[]= { 0, 0, 1 };
stream->codec->extradata_size= 0;
stream->codec->extradata= NULL;
char pvalue[1024], *value = pvalue;
strcpy(pvalue, mediaDesc->getSprops().c_str());
while (*value) {
char base64packet[1024];
uint8_t decoded_packet[1024];
uint32_t packet_size;
char *dst = base64packet;
while (*value && *value != ','
&& (dst - base64packet) < sizeof(base64packet) - 1) {
*dst++ = *value++;
}
*dst++ = '\0';
if (*value == ',')
value++;
packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet));
Hexdump(4, (char *)decoded_packet, packet_size);
if (packet_size) {
uint8_t *dest =
(uint8_t *)av_malloc(packet_size + sizeof(start_sequence) +
stream->codec->extradata_size +
FF_INPUT_BUFFER_PADDING_SIZE);
if(dest) {
if(stream->codec->extradata_size) {
// av_realloc?
memcpy(dest, stream->codec->extradata, stream->codec->extradata_size);
av_free(stream->codec->extradata);
}
memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence));
memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+
packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
stream->codec->extradata= dest;
stream->codec->extradata_size+= sizeof(start_sequence)+packet_size;
// } else {
// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!");
// return AVERROR(ENOMEM);
}
}
}
}
}
return( formatContext );

View File

@@ -103,6 +103,7 @@ public:
int mClock;
int mWidth;
int mHeight;
std::string mSprops;
ConnInfo *mConnInfo;
@@ -171,6 +172,14 @@ public:
return( mHeight );
}
void setSprops(const std::string props)
{
mSprops = props;
}
const std::string getSprops() const
{
return ( mSprops );
}
const double getFrameRate() const
{
return( mFrameRate );

View File

@@ -27,6 +27,13 @@
StreamBase::~StreamBase()
{
#if HAVE_LIBAVCODEC
if ( vid_stream )
{
delete vid_stream;
vid_stream = NULL;
}
#endif
closeComms();
}

View File

@@ -57,7 +57,7 @@ protected:
} DataMsg;
typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType;
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_QUERY=99 } MsgCommand;
typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUERY=99 } MsgCommand;
protected:
Monitor *monitor;