mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-03-16 12:58:00 -04:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ private:
|
||||
Buffer mFrame;
|
||||
int mFrameCount;
|
||||
bool mFrameGood;
|
||||
bool prevM;
|
||||
ThreadData<bool> mFrameReady;
|
||||
ThreadData<bool> mFrameProcessed;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -27,6 +27,13 @@
|
||||
|
||||
StreamBase::~StreamBase()
|
||||
{
|
||||
#if HAVE_LIBAVCODEC
|
||||
if ( vid_stream )
|
||||
{
|
||||
delete vid_stream;
|
||||
vid_stream = NULL;
|
||||
}
|
||||
#endif
|
||||
closeComms();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user