diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 84c4d7c7e..71b4a940d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,12 +58,6 @@ set(ZM_BIN_SRC_FILES zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_nvsocket.cpp - zm_remote_camera_rtsp.cpp - zm_rtp.cpp - zm_rtp_ctrl.cpp - zm_rtp_data.cpp - zm_rtp_source.cpp - zm_rtsp.cpp zm_rtsp_auth.cpp zm_rtsp_server_stream_source.cpp zm_rtsp_server_stream_adts_source.cpp @@ -71,7 +65,6 @@ set(ZM_BIN_SRC_FILES zm_rtsp_server_stream_av1_source.cpp zm_rtsp_server_stream_audio_source.cpp zm_rtsp_server_stream_video_source.cpp - zm_sdp.cpp zm_server.cpp zm_signal.cpp zm_stream.cpp diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 84bc42cb9..7305eab90 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -29,7 +29,6 @@ #include "zm_remote_camera.h" #include "zm_remote_camera_http.h" #include "zm_remote_camera_nvsocket.h" -#include "zm_remote_camera_rtsp.h" #include "zm_signal.h" #include "zm_stream_socket.h" #include "zm_time.h" @@ -830,28 +829,14 @@ void Monitor::LoadCamera() { record_audio ); } else if (protocol == "rtsp") { - Warning("Monitor %u (%s): the Remote/RTSP capture method is deprecated as of 1.40 and " - "will be removed in 1.41. Change this monitor to Type 'Ffmpeg' with Source Path %s", - id, name.c_str(), - remove_authentication(RtspUrlFromRemote(host, port, path, user, pass)).c_str()); - camera = zm::make_unique(this, - method, - host, // Host - port, // Port - path, // Path - user, - pass, - camera_width, - camera_height, - rtsp_describe, - colours, - brightness, - contrast, - hue, - colour, - purpose == CAPTURE, - record_audio - ); + // The hand-written rtsp client this used is gone. ffmpeg speaks rtsp + // better than it did and is what every other rtsp monitor already uses, + // so there is nothing left for a second implementation to do. Name the + // exact Source Path to switch to rather than only saying no. + Error("Monitor %u (%s): the Remote/RTSP capture method has been removed. " + "Change this monitor to Type 'Ffmpeg' with Source Path %s", + id, name.c_str(), + remove_authentication(RtspUrlFromRemote(host, port, path, user, pass)).c_str()); } else { Error("Unexpected remote camera protocol '%s'", protocol.c_str()); } diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp deleted file mode 100644 index 41c4f769b..000000000 --- a/src/zm_remote_camera_rtsp.cpp +++ /dev/null @@ -1,293 +0,0 @@ -// -// ZoneMinder Remote Camera Class Implementation, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_remote_camera_rtsp.h" - -#include "zm_config.h" -#include "zm_monitor.h" -#include "zm_packet.h" -#include "zm_signal.h" - -RemoteCameraRtsp::RemoteCameraRtsp( - const Monitor *monitor, - const std::string &p_method, - const std::string &p_host, - const std::string &p_port, - const std::string &p_path, - const std::string &p_user, - const std::string &p_pass, - 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( - monitor, "rtsp", - p_host, p_port, p_path, - p_user, p_pass, - p_width, p_height, p_colours, - p_brightness, p_contrast, p_hue, p_colour, - p_capture, p_record_audio), - rtsp_describe(p_rtsp_describe), - user(p_user), - pass(p_pass), - frameCount(0) { - if ( p_method == "rtpUni" ) - method = RtspThread::RTP_UNICAST; - else if ( p_method == "rtpMulti" ) - method = RtspThread::RTP_MULTICAST; - else if ( p_method == "rtpRtsp" ) - method = RtspThread::RTP_RTSP; - else if ( p_method == "rtpRtspHttp" ) - method = RtspThread::RTP_RTSP_HTTP; - else - Fatal("Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), monitor->Id()); - - if ( capture ) { - Initialise(); - } - - /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ - if ( zm_is_rgb32(pixelFormat) ) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = AV_PIX_FMT_RGBA; - pixelFormat = AV_PIX_FMT_RGBA; - } else if ( zm_is_rgb24(pixelFormat) ) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = AV_PIX_FMT_RGB24; - pixelFormat = AV_PIX_FMT_RGB24; - } else if ( pixelFormat == AV_PIX_FMT_GRAY8 ) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = AV_PIX_FMT_GRAY8; - pixelFormat = AV_PIX_FMT_GRAY8; - } else { - Panic("Unexpected pixel format %d (%s); legacy colours=%d subpixelorder=%d", - pixelFormat, zm_get_pix_fmt_name(pixelFormat), colours, subpixelorder); - } -} // end RemoteCameraRtsp::RemoteCameraRtsp(...) - -RemoteCameraRtsp::~RemoteCameraRtsp() { - - if ( mVideoCodecContext ) { - //avcodec_close(mVideoCodecContext); - avcodec_free_context(&mVideoCodecContext); - mVideoCodecContext = nullptr; // Freed by avformat_free_context in the destructor of RtspThread class - } - // Is allocated in RTSPThread and is free there as well - mFormatContext = nullptr; - - if ( capture ) { - Terminate(); - } -} - -void RemoteCameraRtsp::Initialise() { - RemoteCamera::Initialise(); - - int max_size = width*height*colours; - - // This allocates a buffer able to hold a raw fframe, which is a little artbitrary. Might be nice to get some - // decent data on how large a buffer is really needed. I think in ffmpeg there are now some functions to do that. - buffer.size(max_size); - - FFMPEGInit(); -} - -void RemoteCameraRtsp::Terminate() { - Disconnect(); -} - -int RemoteCameraRtsp::Connect() { - rtspThread = zm::make_unique(monitor->Id(), method, protocol, host, port, path, user, pass, rtsp_describe); - - return 0; -} - -int RemoteCameraRtsp::Disconnect() { - if (rtspThread) { - rtspThread->Stop(); - rtspThread.reset(); - } - return 0; -} - -int RemoteCameraRtsp::PrimeCapture() { - if (rtspThread) Disconnect(); - Connect(); - - Debug(2, "Waiting for sources"); - for (int i = 100; i && !zm_terminate && !rtspThread->hasSources(); i--) { - std::this_thread::sleep_for(Microseconds(10000)); - } - - if (!rtspThread->hasSources()) { - Error("No RTSP sources"); - return -1; - } - - Debug(2, "Got sources"); - - mFormatContext = rtspThread->getFormatContext(); - - // Find first video stream present - mVideoStreamId = -1; - mAudioStreamId = -1; - - // Find the first video stream. - for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) { - if ( is_video_stream(mFormatContext->streams[i]) ) { - if ( mVideoStreamId == -1 ) { - mVideoStreamId = i; - mVideoStream = mFormatContext->streams[i]; - mVideoStream->time_base = AV_TIME_BASE_Q; - continue; - } else { - Debug(2, "Have another video stream."); - } -#if 0 - } else if ( is_audio_stream(mFormatContext->streams[i]) ) { - if ( mAudioStreamId == -1 ) { - mAudioStreamId = i; - mAudioStream = mFormatContext->streams[i]; - } else { - Debug(2, "Have another audio stream."); - } -#endif - } else { - Debug(1, "Have unknown codec type in stream %d", i); - } - } // end foreach stream - - if ( mVideoStreamId == -1 ) { - Error("Unable to locate video stream"); - return -1; - } - if ( mAudioStreamId == -1 ) - Debug(3, "Unable to locate audio stream"); - - // Get a pointer to the codec context for the video stream - mVideoCodecContext = avcodec_alloc_context3(nullptr); - avcodec_parameters_to_context(mVideoCodecContext, mFormatContext->streams[mVideoStreamId]->codecpar); - - // Find the decoder for the video stream - const AVCodec *codec = avcodec_find_decoder(mVideoCodecContext->codec_id); - if ( codec == nullptr ) { - Error("Unable to locate codec %d decoder", mVideoCodecContext->codec_id); - return -1; - } - - // Open codec - if ( avcodec_open2(mVideoCodecContext, codec, nullptr) < 0 ) { - Error("Can't open codec"); - return -1; - } - - int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1); - - if ( (unsigned int)pSize != imagesize ) { - Error("Image size mismatch. Required: %d Available: %llu", pSize, imagesize); - return -1; - } - - return 1; -} // end PrimeCapture - -int RemoteCameraRtsp::PreCapture() { - if (!rtspThread || rtspThread->IsStopped()) - return -1; - if ( !rtspThread->hasSources() ) { - Error("Cannot precapture, no RTP sources"); - return -1; - } - return 1; -} - -int RemoteCameraRtsp::Capture(std::shared_ptr &zm_packet) { - int frameComplete = false; - AVPacket *packet = zm_packet->packet.get(); - - while (!frameComplete) { - buffer.clear(); - if (!rtspThread || rtspThread->IsStopped() || zm_terminate) - return -1; - - if (rtspThread->getFrame(buffer)) { - Debug(3, "Read frame %d bytes", buffer.size()); - Hexdump(4, buffer.head(), 16); - - if ( !buffer.size() ) - return -1; - - if ( mVideoCodecContext->codec_id == AV_CODEC_ID_H264 ) { - // SPS and PPS frames should be saved and appended to IDR frames - int nalType = (buffer.head()[3] & 0x1f); - - // SPS The SPS NAL unit contains parameters that apply to a series of consecutive coded video pictures - if ( nalType == 1 ) { - } else if ( nalType == 7 ) { - lastSps = buffer; - continue; - } else if ( nalType == 8 ) { - // PPS The PPS NAL unit contains parameters that apply to the decoding of one or more individual pictures inside a coded video sequence - lastPps = buffer; - continue; - } else if ( nalType == 5 ) { - packet->flags |= AV_PKT_FLAG_KEY; - zm_packet->keyframe = 1; - // IDR - buffer += lastSps; - buffer += lastPps; - } else { - Debug(2, "Unknown nalType %d", nalType); - } - } else { - Debug(3, "Not an h264 packet"); - } - - //while ( (!frameComplete) && (buffer.size() > 0) ) { - if ( buffer.size() > 0 ) { - packet->data = (uint8_t*)av_malloc(buffer.size()); - memcpy(packet->data, buffer.head(), buffer.size()); - //packet->data = buffer.head(); - packet->size = buffer.size(); - bytes += packet->size; - buffer -= packet->size; - - struct timeval now; - gettimeofday(&now, nullptr); - packet->pts = packet->dts = now.tv_sec*1000000+now.tv_usec; - zm_packet->codec_type = mVideoCodecContext->codec_type; - zm_packet->stream = mVideoStream; - frameComplete = true; - Debug(2, "Frame: %d - %d/%d", frameCount, packet->size, buffer.size()); - } - } /* getFrame() */ - } // end while true - - return 1; -} // end int RemoteCameraRtsp::Capture(ZMPacket &packet) - -int RemoteCameraRtsp::PostCapture() { - return 1; -} diff --git a/src/zm_remote_camera_rtsp.h b/src/zm_remote_camera_rtsp.h deleted file mode 100644 index 9f0357af5..000000000 --- a/src/zm_remote_camera_rtsp.h +++ /dev/null @@ -1,89 +0,0 @@ -// -// ZoneMinder Remote RTSP Camera Class Interface, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#ifndef ZM_REMOTE_CAMERA_RTSP_H -#define ZM_REMOTE_CAMERA_RTSP_H - -#include "zm_ffmpeg.h" -#include "zm_remote_camera.h" -#include "zm_rtsp.h" - -// -// Class representing 'rtsp' cameras, i.e. those which are -// accessed over a network connection using rtsp protocol -// (Real Time Streaming Protocol) -// -class RemoteCameraRtsp : public RemoteCamera { - protected: - struct sockaddr_in rtsp_sa; - struct sockaddr_in rtcp_sa; - int rtsp_sd; - int rtp_sd; - int rtcp_sd; - bool rtsp_describe; - - const std::string user; - const std::string pass; - - Buffer buffer; - Buffer lastSps; - Buffer lastPps; - - RtspThread::RtspMethod method; - - std::unique_ptr rtspThread; - - int frameCount; - - _AVPIXELFORMAT imagePixFormat; - - public: - RemoteCameraRtsp( - const Monitor *monitor, - const std::string &method, - const std::string &host, - const std::string &port, - const std::string &path, - const std::string &user, - const std::string &pass, - 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(); - - void Initialise() override; - void Terminate() override; - int Connect() override; - int Disconnect() override; - - int PrimeCapture() override; - int PreCapture() override; - int Capture(std::shared_ptr &p) override; - int PostCapture() override; - int Close() override { return 0; }; -}; - -#endif // ZM_REMOTE_CAMERA_RTSP_H diff --git a/src/zm_rtp.cpp b/src/zm_rtp.cpp deleted file mode 100644 index 1b833f1bc..000000000 --- a/src/zm_rtp.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// ZoneMinder RTP/RTCP Class Implementation, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_rtp.h" - -// Blank diff --git a/src/zm_rtp.h b/src/zm_rtp.h deleted file mode 100644 index bcace6eb6..000000000 --- a/src/zm_rtp.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// ZoneMinder RTP/RTCP Class Interface, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#ifndef ZM_RTP_H -#define ZM_RTP_H - -#define RTP_VERSION 2 - -#endif // ZM_RTP_H diff --git a/src/zm_rtp_ctrl.cpp b/src/zm_rtp_ctrl.cpp deleted file mode 100644 index 232ff6a1d..000000000 --- a/src/zm_rtp_ctrl.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// -// ZoneMinder RTCP Class Implementation, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_rtp_ctrl.h" - -#include "zm_config.h" -#include "zm_rtp.h" -#include "zm_rtsp.h" - -RtpCtrlThread::RtpCtrlThread(RtspThread &rtspThread, RtpSource &rtpSource) - : mRtspThread(rtspThread), mRtpSource(rtpSource), mTerminate(false) { - mThread = std::thread(&RtpCtrlThread::Run, this); -} - -RtpCtrlThread::~RtpCtrlThread() { - Stop(); - if (mThread.joinable()) - mThread.join(); -} - -int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) { - const RtcpPacket *rtcpPacket; - rtcpPacket = (RtcpPacket *)packet; - - int consumed = 0; - - //printf( "C: " ); - //for ( int i = 0; i < packetLen; i++ ) - //printf( "%02x ", (unsigned char)packet[i] ); - //printf( "\n" ); - int ver = rtcpPacket->header.version; - int count = rtcpPacket->header.count; - int pt = rtcpPacket->header.pt; - int len = ntohs(rtcpPacket->header.lenN); - - Debug( 5, "RTCP Ver: %d Count: %d Pt: %d len: %d", ver, count, pt, len); - - 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() ) { - Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); - return( -1 ); - } - } else if ( ssrc ) { - mRtpSource.setSsrc( ssrc ); - } - - if ( len > 1 ) { - //printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts ); - uint32_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN); - uint32_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN); - //printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts ); - //printf( "Pkts:$sendpkts, Octs:$sendocts\n" ); - uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN); - - mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime ); - } - break; - } - case RTCP_SDES : { - ssize_t contentLen = packetLen - sizeof(rtcpPacket->header); - 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()) ) { - 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++ ) { - RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr; - Debug( 5, "RTCP Item length %d", item->len ); - switch( item->type ) { - case RTCP_SDES_CNAME : { - std::string cname( item->data, item->len ); - Debug( 5, "RTCP Got CNAME %s", cname.c_str() ); - break; - } - case RTCP_SDES_END : - case RTCP_SDES_NAME : - case RTCP_SDES_EMAIL : - case RTCP_SDES_PHONE : - case RTCP_SDES_LOC : - case RTCP_SDES_TOOL : - case RTCP_SDES_NOTE : - case RTCP_SDES_PRIV : - default : - Error( "Received unexpected SDES item type %d, ignoring", item->type ); - return -1; - } - int paddedLen = 4+2+item->len+1; // Add null byte - paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4 - Debug(5, "RTCP PL:%d", paddedLen); - sdesPtr += paddedLen; - contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0; - } - } // end whiel contentLen - break; - } - case RTCP_BYE : - Debug(5, "RTCP Got BYE"); - Stop(); - 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; - 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; -} - -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]); - int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; - - rtcpPacket->header.version = RTP_VERSION; - rtcpPacket->header.p = 0; - rtcpPacket->header.pt = RTCP_RR; - rtcpPacket->header.count = 1; - rtcpPacket->header.lenN = htons(wordLen-1); - - mRtpSource.updateRtcpStats(); - - 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()); - rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets(); - rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction(); - rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq()); - rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter()); - rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp()); - rtcpPacket->body.rr.rr[0].dlsrN = 0; - - return wordLen*sizeof(uint32_t); -} // end RtpCtrlThread::generateRr - -int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) { - RtcpPacket *rtcpPacket = (RtcpPacket *)packet; - - const std::string &cname = mRtpSource.getCname(); - - int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size(); - int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; - - rtcpPacket->header.version = RTP_VERSION; - rtcpPacket->header.p = 0; - rtcpPacket->header.pt = RTCP_SDES; - rtcpPacket->header.count = 1; - rtcpPacket->header.lenN = htons(wordLen-1); - - rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1); - rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME; - rtcpPacket->body.sdes.item[0].len = cname.size(); - memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() ); - - return wordLen*sizeof(uint32_t); -} // end RtpCtrlThread::generateSdes - -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]); - int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; - - rtcpPacket->header.version = RTP_VERSION; - rtcpPacket->header.p = 0; - rtcpPacket->header.pt = RTCP_BYE; - rtcpPacket->header.count = 1; - rtcpPacket->header.lenN = htons(wordLen-1); - - rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc()); - - return wordLen*sizeof(uint32_t); -} // end RtpCtrolThread::generateBye - -int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) { - unsigned char *bufferPtr = buffer; - - // u_int32 len; /* length of compound RTCP packet in words */ - // rtcp_t *r; /* RTCP header */ - // rtcp_t *end; /* end of compound RTCP packet */ - - // if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) { - // /* something wrong with packet format */ - // } - // end = (rtcp_t *)((u_int32 *)r + len); - - // do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1); - // while (r < end && r->common.version == 2); - - // if (r != end) { - // /* something wrong with packet format */ - // } - - while ( nBytes > 0 ) { - int consumed = recvPacket( bufferPtr, nBytes ); - if ( consumed <= 0 ) - break; - bufferPtr += consumed; - nBytes -= consumed; - } - return nBytes; -} - -void RtpCtrlThread::Run() { - if (mRtpSource.getLocalCtrlPort()<=1024) { - Debug(2, "Not starting control thread"); - return; - } - Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() ); - zm::SockAddrInet localAddr, remoteAddr; - - bool sendReports; - zm::UdpInetSocket rtpCtrlServer; - 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 { - 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() ); - if ( !rtpCtrlServer.connect( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ) ) - Fatal( "Failed to connect RTCP server" ); - Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ); - sendReports = true; - } - - // The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets. - // Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response - zm::Select select(Seconds(10)); - select.addReader( &rtpCtrlServer ); - - unsigned char buffer[ZM_NETWORK_BUFSIZ]; - - TimePoint last_receive = std::chrono::steady_clock::now(); - bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true. - - while (!mTerminate && (select.wait() >= 0)) { - TimePoint now = std::chrono::steady_clock::now(); - zm::Select::CommsList readable = select.getReadable(); - 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: %.2f s", - bufferPtr - buffer, rtpCtrlServer.getWriteDesc(), FPSeconds(now - last_receive).count()); - if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 ) - Error("Unable to send: %s", strerror(errno)); - timeout = true; - continue; - } else { - Debug(1, "RTCP timed out. Time since last receive: %.2f s", FPSeconds(now - last_receive).count()); - continue; - //break; - } - } else { - timeout = false; - last_receive = std::chrono::steady_clock::now(); - } - for (zm::Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) { - if ( zm::UdpInetSocket *socket = dynamic_cast(*iter) ) { - ssize_t nBytes = socket->recv( buffer, sizeof(buffer) ); - Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() ); - - if ( nBytes ) { - recvPackets( buffer, nBytes ); - - if ( sendReports ) { - unsigned char *bufferPtr = buffer; - bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); - bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); - Debug(3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc()); - if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) - Error("Unable to send: %s", strerror(errno)); - //Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() ); - } - } else { - // Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs. - Stop(); - break; - } - } else { - Panic("Barfed"); - } // end if socket - } // end foeach comms iterator - } - rtpCtrlServer.close(); - mRtspThread.Stop(); -} diff --git a/src/zm_rtp_ctrl.h b/src/zm_rtp_ctrl.h deleted file mode 100644 index 3d0fe45df..000000000 --- a/src/zm_rtp_ctrl.h +++ /dev/null @@ -1,145 +0,0 @@ -// -// ZoneMinder RTCP Class Interface, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#ifndef ZM_RTP_CTRL_H -#define ZM_RTP_CTRL_H - -#include -#include -#include - -// Defined in ffmpeg rtp.h -//#define RTP_MAX_SDES 255 // maximum text length for SDES - -// Big-endian mask for version, padding bit and packet type pair -#define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe) -#define RTCP_VALID_VALUE ((RTP_VERSION << 14) | RTCP_SR) - -class RtspThread; -class RtpSource; - -class RtpCtrlThread { - friend class RtspThread; - - private: - typedef enum { - RTCP_SR = 200, - RTCP_RR = 201, - RTCP_SDES = 202, - RTCP_BYE = 203, - RTCP_APP = 204 - } RtcpType; - - typedef enum { - RTCP_SDES_END = 0, - RTCP_SDES_CNAME = 1, - RTCP_SDES_NAME = 2, - RTCP_SDES_EMAIL = 3, - RTCP_SDES_PHONE = 4, - RTCP_SDES_LOC = 5, - RTCP_SDES_TOOL = 6, - RTCP_SDES_NOTE = 7, - RTCP_SDES_PRIV = 8 - } RtcpSdesType; - - struct RtcpCommonHeader { - uint8_t count:5; // varies by packet type - uint8_t p:1; // padding flag - uint8_t version:2; // protocol version - uint8_t pt; // RTCP packet type - uint16_t lenN; // pkt len in words, w/o this word, network order - }; - - // Reception report block - 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 - uint32_t lastSeqN; // extended last seq. no. received, network order - uint32_t jitterN; // interarrival jitter, network order - uint32_t lsrN; // last SR packet from this source, network order - uint32_t dlsrN; // delay since last SR packet, network order - }; - - // SDES item - 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 { - RtcpCommonHeader header; // common header - union { - // Sender Report (SR) - struct Sr { - uint32_t ssrcN; // sender generating this report, network order - uint32_t ntpSecN; // NTP timestamp, network order - uint32_t ntpFracN; - uint32_t rtpTsN; // RTP timestamp, network order - uint32_t pSentN; // packets sent, network order - uint32_t oSentN; // octets sent, network order - RtcpRr rr[]; // variable-length list - } sr; - - // Reception Report (RR) - struct Rr { - uint32_t ssrcN; // receiver generating this report - RtcpRr rr[]; // variable-length list - } rr; - - // source description (SDES) - struct Sdes { - uint32_t srcN; // first SSRC/CSRC - RtcpSdesItem item[]; // list of SDES items - } sdes; - - // BYE - struct { - uint32_t srcN[]; // list of sources - // can't express trailing text for reason (what does this mean? it's not even english!) - } bye; - } body; - }; - - private: - RtspThread &mRtspThread; - RtpSource &mRtpSource; - int mPort; - - std::atomic mTerminate; - std::thread mThread; - - private: - int recvPacket( const unsigned char *packet, ssize_t packetLen ); - int generateRr( const unsigned char *packet, ssize_t packetLen ); - int generateSdes( const unsigned char *packet, ssize_t packetLen ); - int generateBye( const unsigned char *packet, ssize_t packetLen ); - int recvPackets( unsigned char *buffer, ssize_t nBytes ); - void Run(); - - public: - RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ); - ~RtpCtrlThread(); - - void Stop() { mTerminate = true; } -}; - -#endif // ZM_RTP_CTRL_H diff --git a/src/zm_rtp_data.cpp b/src/zm_rtp_data.cpp deleted file mode 100644 index 90b9f38a4..000000000 --- a/src/zm_rtp_data.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// -// ZoneMinder RTP Data Class Implementation, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_rtp_data.h" - -#include "zm_config.h" -#include "zm_rtsp.h" -#include "zm_signal.h" - -RtpDataThread::RtpDataThread(RtspThread &rtspThread, RtpSource &rtpSource) : - mRtspThread(rtspThread), mRtpSource(rtpSource), mTerminate(false) { - mThread = std::thread(&RtpDataThread::Run, this); -} - -RtpDataThread::~RtpDataThread() { - Stop(); - if (mThread.joinable()) - mThread.join(); -} - -bool RtpDataThread::recvPacket(const unsigned char *packet, size_t packetLen) { - const RtpDataHeader *rtpHeader; - rtpHeader = (RtpDataHeader *)packet; - - Debug(5, "Ver: %d P: %d Pt: %d Mk: %d Seq: %d T/S: %x SSRC: %x", - rtpHeader->version, - rtpHeader->p, - rtpHeader->pt, - rtpHeader->m, - ntohs(rtpHeader->seqN), - ntohl(rtpHeader->timestampN), - ntohl(rtpHeader->ssrcN)); - - //unsigned short seq = ntohs(rtpHeader->seqN); - uint32_t ssrc = ntohl(rtpHeader->ssrcN); - - if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) { - Warning("Discarding packet for unrecognised ssrc %x", ssrc); - return false; - } - - return mRtpSource.handlePacket(packet, packetLen); -} - -void RtpDataThread::Run() { - if (mRtpSource.getLocalDataPort()<=1024) { - Debug(2, "Not starting data thread"); - return; - } - Debug(2, "Starting data thread %d on port %d", - mRtpSource.getSsrc(), mRtpSource.getLocalDataPort()); - - zm::SockAddrInet localAddr; - zm::UdpInetServer rtpDataSocket; - if ( mRtpSource.getLocalHost() != "" ) { - if ( !rtpDataSocket.bind(mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort()) ) - Fatal("Failed to bind RTP server at %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort()); - } else { - if ( !rtpDataSocket.bind( - mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", - mRtpSource.getLocalDataPort() ) ) - Fatal("Failed to bind RTP server at %s:%d", - (mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0"), mRtpSource.getLocalDataPort()); - } - Debug(3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort()); - - zm::Select select(Seconds(3)); - select.addReader(&rtpDataSocket); - - unsigned char buffer[ZM_NETWORK_BUFSIZ]; - while ( !zm_terminate && !mTerminate && (select.wait() >= 0) ) { - zm::Select::CommsList readable = select.getReadable(); - if ( readable.size() == 0 ) { - Error("RTP timed out"); - Stop(); - break; - } - for (zm::Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) { - if ( zm::UdpInetServer *socket = dynamic_cast(*iter) ) { - ssize_t nBytes = socket->recv(buffer, sizeof(buffer)); - Debug(4, "Got %zd bytes on sd %d", nBytes, socket->getReadDesc()); - if ( nBytes ) { - recvPacket(buffer, nBytes); - } else { - Stop(); - break; - } - } else { - Panic("Barfed"); - } - } // end foreach commsList - } - rtpDataSocket.close(); - mRtspThread.Stop(); -} diff --git a/src/zm_rtp_data.h b/src/zm_rtp_data.h deleted file mode 100644 index aff813077..000000000 --- a/src/zm_rtp_data.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// ZoneMinder RTP Data Class Interface, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#ifndef ZM_RTP_DATA_H -#define ZM_RTP_DATA_H - -#include "zm_define.h" -#include -#include - -class RtspThread; -class RtpSource; - -struct RtpDataHeader { - uint8_t cc:4; // CSRC count - uint8_t x:1; // header extension flag - uint8_t p:1; // padding flag - uint8_t version:2; // protocol version - uint8_t pt:7; // payload type - uint8_t m:1; // marker bit - uint16_t seqN; // sequence number, network order - uint32_t timestampN; // timestamp, network order - uint32_t ssrcN; // synchronization source, network order - uint32_t csrc[]; // optional CSRC list -}; - -class RtpDataThread { - friend class RtspThread; - - private: - RtspThread &mRtspThread; - RtpSource &mRtpSource; - - std::atomic mTerminate; - std::thread mThread; - - private: - bool recvPacket( const unsigned char *packet, size_t packetLen ); - void Run(); - - public: - RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ); - ~RtpDataThread(); - - void Stop() { mTerminate = true; } -}; - -#endif // ZM_RTP_DATA_H diff --git a/src/zm_rtp_source.cpp b/src/zm_rtp_source.cpp deleted file mode 100644 index 861bb4aeb..000000000 --- a/src/zm_rtp_source.cpp +++ /dev/null @@ -1,388 +0,0 @@ -// -// ZoneMinder RTP Source Class Implementation, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_rtp_source.h" - -#include "zm_time.h" -#include "zm_rtp_data.h" -#include "zm_utils.h" -#include -#include - -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), - prevM(false), - mFrameReady(false), - mFrameProcessed(false), - mTerminate(false) { - char hostname[256] = ""; - gethostname(hostname, sizeof(hostname)); - - mCname = stringtf("zm-%d@%s", mId, hostname); - Debug(3, "RTP CName = %s", mCname.c_str()); - - init(seq); - mMaxSeq = seq - 1; - mProbation = MIN_SEQUENTIAL; - - mLocalPortChans[0] = localPortBase; - mLocalPortChans[1] = localPortBase+1; - - mRemotePortChans[0] = remotePortBase; - mRemotePortChans[1] = remotePortBase+1; - - mRtpFactor = mRtpClock; - - mBaseTimeReal = std::chrono::system_clock::now(); - mBaseTimeNtp = {}; - mBaseTimeRtp = rtpTime; - - mLastSrTimeReal = {}; - mLastSrTimeNtp = {}; - mLastSrTimeRtp = 0; - - mLastSrTimeNtpSecs = 0; - mLastSrTimeNtpFrac = 0; - mExpectedPackets = 0; - mLostPackets = 0; - mLostFraction = 0; - - if ( mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4 ) - Warning("The device is using a codec (%d) that may not be supported. Do not be surprised if things don't work.", mCodecId); -} - -RtpSource::~RtpSource() { - mTerminate = true; - mFrameReadyCv.notify_all(); - mFrameProcessedCv.notify_all(); -} - -void RtpSource::init(uint16_t seq) { - Debug(3, "Initialising sequence"); - mBaseSeq = seq; - mMaxSeq = seq; - mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false - mCycles = 0; - mReceivedPackets = 0; - mReceivedPrior = 0; - mExpectedPrior = 0; - // other initialization - mJitter = 0; - mTransit = 0; -} - -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) { - // packet is in sequence - if ( seq == mMaxSeq + 1 ) { - Debug(3, "Sequence in probation %d, in sequence", mProbation); - mProbation--; - mMaxSeq = seq; - if ( mProbation == 0 ) { - init(seq); - mReceivedPackets++; - return true; - } - } 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 ) { - Debug(4, "Packet in sequence, gap %d", uDelta); - } else { - Warning("Packet in sequence, gap %d", uDelta); - } - - // in order, with permissible gap - if ( seq < mMaxSeq ) { - // Sequence number wrapped - count another 64K cycle. - mCycles += RTP_SEQ_MOD; - } - mMaxSeq = seq; - } 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 ) { - 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 { - mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); - return false; - } - } else { - Warning("Packet duplicate or reordered, gap %d", uDelta); - // duplicate or reordered packet - return false; - } - mReceivedPackets++; - return( uDelta==1?true:false ); -} - -void RtpSource::updateJitter( const RtpDataHeader *header ) { - if (mRtpFactor > 0) { - SystemTimePoint now = std::chrono::system_clock::now(); - FPSeconds time_diff = std::chrono::duration_cast(now - mBaseTimeReal); - - uint32_t localTimeRtp = mBaseTimeRtp + static_cast(time_diff.count() * mRtpFactor); - uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); - - Debug(5, - "Delta rtp = %.6f\n Local RTP time = %x Packet RTP time = %x Packet transit RTP time = %x", - time_diff.count(), - localTimeRtp, - ntohl(header->timestampN), - packetTransit); - - if ( mTransit > 0 ) { - // Jitter - int d = packetTransit - mTransit; - Debug(5, "Jitter D = %d", d); - if ( d < 0 ) - d = -d; - //mJitter += (1./16.) * ((double)d - mJitter); - mJitter += d - ((mJitter + 8) >> 4); - } - mTransit = packetTransit; - } else { - mJitter = 0; - } - Debug(5, "RTP Jitter: %d", mJitter); -} - -void RtpSource::updateRtcpData( - uint32_t ntpTimeSecs, - uint32_t ntpTimeFrac, - uint32_t rtpTime) { - timeval ntpTime = zm::chrono::duration_cast( - Seconds(ntpTimeSecs) + Microseconds((Microseconds::period::den * (ntpTimeFrac >> 16)) / (1 << 16))); - - Debug(5, "ntpTime: %jd.%06jd, rtpTime: %x", static_cast(ntpTime.tv_sec), - static_cast(ntpTime.tv_usec), rtpTime); - - if ( mBaseTimeNtp.tv_sec == 0 ) { - mBaseTimeReal = std::chrono::system_clock::now(); - mBaseTimeNtp = ntpTime; - mBaseTimeRtp = rtpTime; - } else if ( !mRtpClock ) { - Debug(5, "lastSrNtpTime: %jd.%06jd, rtpTime: %x" - "ntpTime: %jd.%06jd, rtpTime: %x", - static_cast(mLastSrTimeNtp.tv_sec), static_cast(mLastSrTimeNtp.tv_usec), rtpTime, - static_cast(ntpTime.tv_sec), static_cast(ntpTime.tv_usec), rtpTime); - - FPSeconds diffNtpTime = - zm::chrono::duration_cast(ntpTime) - zm::chrono::duration_cast(mBaseTimeNtp); - - uint32_t diffRtpTime = rtpTime - mBaseTimeRtp; - if (diffNtpTime.count() > 0.0) { - mRtpFactor = static_cast(diffRtpTime / diffNtpTime.count()); - } - - Debug( 5, "NTP-diff: %.6f RTP-diff: %d RTPfactor: %d", - diffNtpTime.count(), diffRtpTime, mRtpFactor); - } - mLastSrTimeNtpSecs = ntpTimeSecs; - mLastSrTimeNtpFrac = ntpTimeFrac; - mLastSrTimeNtp = ntpTime; - mLastSrTimeRtp = rtpTime; -} - -void RtpSource::updateRtcpStats() { - uint32_t extendedMax = mCycles + mMaxSeq; - mExpectedPackets = extendedMax - mBaseSeq + 1; - // The number of packets lost is defined to be the number of packets - // expected less the number of packets actually received: - mLostPackets = mExpectedPackets - mReceivedPackets; - uint32_t expectedInterval = mExpectedPackets - mExpectedPrior; - mExpectedPrior = mExpectedPackets; - uint32_t receivedInterval = mReceivedPackets - mReceivedPrior; - mReceivedPrior = mReceivedPackets; - int32_t lostInterval = expectedInterval - receivedInterval; - - if ( expectedInterval == 0 || lostInterval <= 0 ) - mLostFraction = 0; - else - mLostFraction = (lostInterval << 8) / expectedInterval; - - Debug(5, - "Expected packets = %d\n Lost packets = %d\n Expected interval = %d\n Received interval = %d\n Lost interval = %d\n Lost fraction = %d\n", - mExpectedPackets, - mLostPackets, - expectedInterval, - receivedInterval, - lostInterval, - mLostFraction); -} - -bool RtpSource::handlePacket(const unsigned char *packet, size_t packetLen) { - const RtpDataHeader *rtpHeader; - rtpHeader = (RtpDataHeader *)packet; - 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 == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40); - // M stands for Marker, it is the 8th bit - // The interpretation of the marker is defined by a profile. It is intended - // to allow significant events such as frame boundaries to be marked in the - // packet stream. A profile may define additional marker bits or specify - // 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)) ) { - Hexdump(4, packet+rtpHeaderSize, 16); - - if ( mFrameGood ) { - int extraHeader = 0; - - if ( mCodecId == AV_CODEC_ID_H264 ) { - int nalType = (packet[rtpHeaderSize] & 0x1f); - Debug(3, "Have H264 frame: nal type is %d", 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 ) { - // 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; - 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); - } - - Hexdump(4, mFrame.head(), 16); - - if ( thisM ) { - if ( mFrameGood ) { - Debug(3, "Got new frame %d, %d bytes", mFrameCount, mFrame.size()); - - { - std::lock_guard lck(mFrameReadyMutex); - mFrameReady = true; - } - mFrameReadyCv.notify_all(); - - { - std::unique_lock lck(mFrameProcessedMutex); - mFrameProcessedCv.wait(lck, [&] { return mFrameProcessed || mTerminate; }); - mFrameProcessed = false; - } - - if (mTerminate) - return false; - - mFrameCount++; - } else { - Warning("Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size()); - } - mFrame.clear(); - } - } else { - if ( mFrame.size() ) { - Warning("Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size()); - } else { - Warning("Discarding frame %d", mFrameCount); - } - mFrameGood = false; - mFrame.clear(); - } - if ( thisM ) { - mFrameGood = true; - prevM = true; - } else - prevM = false; - - updateJitter(rtpHeader); - - return true; -} - -bool RtpSource::getFrame(Buffer &buffer) { - { - std::unique_lock lck(mFrameReadyMutex); - mFrameReadyCv.wait(lck, [&] { return mFrameReady || mTerminate; }); - mFrameReady = false; - } - - if (mTerminate) - return false; - - buffer = mFrame; - { - std::lock_guard lck(mFrameProcessedMutex); - mFrameProcessed = true; - } - mFrameProcessedCv.notify_all(); - Debug(4, "Copied %d bytes", buffer.size()); - return true; -} diff --git a/src/zm_rtp_source.h b/src/zm_rtp_source.h deleted file mode 100644 index 577e1ba05..000000000 --- a/src/zm_rtp_source.h +++ /dev/null @@ -1,178 +0,0 @@ -// -// ZoneMinder RTP Source Class Interface, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#ifndef ZM_RTP_SOURCE_H -#define ZM_RTP_SOURCE_H - -#include "zm_buffer.h" -#include "zm_config.h" -#include "zm_define.h" -#include "zm_ffmpeg.h" -#include "zm_time.h" -#include -#include -#include -#include - -struct RtpDataHeader; - -class RtpSource { - public: - typedef enum { EMPTY, FILLING, READY } FrameState; - private: - static const int RTP_SEQ_MOD = 1<<16; - static const int MAX_DROPOUT = 3000; - static const int MAX_MISORDER = 100; - static const int MIN_SEQUENTIAL = 2; - - private: - // Identity - int mId; // General id (usually monitor id) - std::string mCname; // Canonical name, for SDES - - // RTP/RTCP fields - uint32_t mSsrc; - uint16_t mMaxSeq; // highest seq. number seen - uint32_t mCycles; // shifted count of seq. number cycles - uint32_t mBaseSeq; // base seq number - uint32_t mBadSeq; // last 'bad' seq number + 1 - uint32_t mProbation; // sequ. packets till source is valid - uint32_t mReceivedPackets; // packets received - uint32_t mExpectedPrior; // packet expected at last interval - uint32_t mReceivedPrior; // packet received at last interval - uint32_t mTransit; // relative trans time for prev pkt - uint32_t mJitter; // estimated jitter - - // Ports/Channels - std::string mLocalHost; - int mLocalPortChans[2]; - std::string mRemoteHost; - int mRemotePortChans[2]; - - // Time keys - uint32_t mRtpClock; - uint32_t mRtpFactor; - SystemTimePoint mBaseTimeReal; - struct timeval mBaseTimeNtp; - uint32_t mBaseTimeRtp; - - struct timeval mLastSrTimeReal; - uint32_t mLastSrTimeNtpSecs; - uint32_t mLastSrTimeNtpFrac; - struct timeval mLastSrTimeNtp; - uint32_t mLastSrTimeRtp; - - // Stats, intermittently updated - uint32_t mExpectedPackets; - uint32_t mLostPackets; - uint8_t mLostFraction; - - _AVCODECID mCodecId; - - Buffer mFrame; - int mFrameCount; - bool mFrameGood; - bool prevM; - - bool mFrameReady; - std::condition_variable mFrameReadyCv; - std::mutex mFrameReadyMutex; - - bool mFrameProcessed; - std::condition_variable mFrameProcessedCv; - std::mutex mFrameProcessedMutex; - bool mTerminate; - - 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, _AVCODECID codecId ); - ~RtpSource(); - - bool updateSeq( uint16_t seq ); - void updateJitter( const RtpDataHeader *header ); - void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ); - void updateRtcpStats(); - - bool handlePacket( const unsigned char *packet, size_t packetLen ); - - uint32_t getSsrc() const { - return( mSsrc ); - } - void setSsrc( uint32_t ssrc ) { - mSsrc = ssrc; - } - - bool getFrame( Buffer &buffer ); - - const std::string &getCname() const { - return( mCname ); - } - - const std::string &getLocalHost() const { - return( mLocalHost ); - } - - int getLocalDataPort() const { - return( mLocalPortChans[0] ); - } - - int getLocalCtrlPort() const { - return( mLocalPortChans[1] ); - } - - const std::string &getRemoteHost() const { - return( mRemoteHost ); - } - - int getRemoteDataPort() const { - return( mRemotePortChans[0] ); - } - - int getRemoteCtrlPort() const { - return( mRemotePortChans[1] ); - } - - uint32_t getMaxSeq() const { - return( mCycles + mMaxSeq ); - } - - uint32_t getExpectedPackets() const { - return( mExpectedPackets ); - } - - uint32_t getLostPackets() const { - return( mLostPackets ); - } - - uint8_t getLostFraction() const { - return( mLostFraction ); - } - - uint32_t getJitter() const { - return( mJitter >> 4 ); - } - - uint32_t getLastSrTimestamp() const { - return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) ); - } -}; - -#endif // ZM_RTP_SOURCE_H diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp deleted file mode 100644 index 57f25995c..000000000 --- a/src/zm_rtsp.cpp +++ /dev/null @@ -1,794 +0,0 @@ -// -// ZoneMinder RTSP Class Implementation, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_rtsp.h" - -#include "zm_config.h" -#include "zm_rtp_data.h" -#include "zm_rtp_ctrl.h" -#include "zm_db.h" - -#include - -int RtspThread::smMinDataPort = 0; -int RtspThread::smMaxDataPort = 0; -RtspThread::PortSet RtspThread::smAssignedPorts; - -bool RtspThread::sendCommand(std::string message) { - if ( mNeedAuth ) { - StringVector parts = Split(message, " "); - if ( parts.size() > 1 ) - message += mAuthenticator->getAuthHeader(parts[0], parts[1]); - } - message += stringtf("User-Agent: ZoneMinder/%s\r\n", ZM_VERSION); - message += stringtf("CSeq: %d\r\n\r\n", ++mSeq); - Debug(2, "Sending RTSP message: %s", message.c_str()); - if ( mMethod == RTP_RTSP_HTTP ) { - message = Base64Encode(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; - } - } 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 true; -} - -bool RtspThread::recvResponse(std::string &response) { - if ( mRtspSocket.recv(response) < 0 ) - Error("Recv failed; %s", strerror(errno)); - Debug(2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size()); - float respVer = 0; - respCode = -1; - char respText[ZM_NETWORK_BUFSIZ]; - static_assert(ZM_NETWORK_BUFSIZ == 32768, "respText field width must be ZM_NETWORK_BUFSIZ-1"); - if ( sscanf(response.c_str(), "RTSP/%f %3d %32767[^\r\n]\r\n", &respVer, &respCode, respText) != 3 ) { - if ( !response.empty() && isalnum(response[0]) ) { - Error("Response parse failure in '%s'", response.c_str()); - } else { - Error("Response parse failure, %zd bytes follow", response.size()); - if ( response.size() ) - Hexdump(Logger::ERROR, response.data(), std::min(int(response.size()), 16)); - } - 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; - } else if ( respCode != 200 ) { - Error("Unexpected response code %d, text is '%s'", respCode, respText); - return false; - } - return true; -} // end RtspThread::recvResponse - -int RtspThread::requestPorts() { - if ( !smMinDataPort ) { - //FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors - std::string sql = "SELECT `Id` FROM `Monitors` WHERE `Function` != 'None' AND `Type` = 'Remote' AND `Protocol` = 'rtsp' AND `Method` = 'rtpUni' ORDER BY `Id` ASC"; - - MYSQL_RES *result = zmDbFetch(sql); - - int nMonitors = 0; - int position = 0; - if (result) { - nMonitors = mysql_num_rows(result); - if ( nMonitors ) { - for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) { - int id = atoi(dbrow[0]); - if ( mId == id ) { - position = i; - break; - } - } - } - mysql_free_result(result); - } - if (!nMonitors) { - // Minor hack for testing when not strictly enabled - nMonitors = 1; - position = 0; - } - int portRange = int(((config.max_rtp_port-config.min_rtp_port)+1)/nMonitors); - smMinDataPort = config.min_rtp_port + (position * portRange); - smMaxDataPort = smMinDataPort + portRange - 1; - Debug(2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort); - } - for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) { - PortSet::const_iterator iter = smAssignedPorts.find(i); - if ( iter == smAssignedPorts.end() ) { - smAssignedPorts.insert(i); - return i; - } - } - Panic("Can assign RTP port, no ports left in pool"); - return -1; -} - -void RtspThread::releasePorts(int port) { - if ( port > 0 ) - 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 &user, - const std::string &pass, - bool rtsp_describe) : - mId(id), - mMethod(method), - mProtocol(protocol), - mHost(host), - mPort(port), - mPath(path), - mRtspDescribe(rtsp_describe), - mSessDesc(0), - mFormatContext(0), - mSeq(0), - mSession(0), - mSsrc(0), - mDist(UNDEFINED), - mRtpTime(0), - mTerminate(false) { - mUrl = mProtocol+"://"+mHost+":"+mPort; - if ( !mPath.empty() ) { - if ( mPath[0] == '/' ) - mUrl += mPath; - else - mUrl += '/'+mPath; - } - - mSsrc = rand(); - - Debug(2, "RTSP Local SSRC is %x, url is %s", mSsrc, mUrl.c_str()); - - if ( mMethod == RTP_RTSP_HTTP ) - mHttpSession = stringtf("%d", rand()); - - mNeedAuth = false; - if ( user.length() > 0 && pass.length() > 0 ) { - Debug(2, "# of auth parts 2"); - mAuthenticator = new zm::Authenticator(user, pass); - } else if( user.length() > 0 ) { - Debug(2, "# of auth parts 1"); - mAuthenticator = new zm::Authenticator(user, ""); - } else { - Debug(2, "# of auth parts 0"); - mAuthenticator = new zm::Authenticator("", ""); - } - - mThread = std::thread(&RtspThread::Run, this); -} - -RtspThread::~RtspThread() { - Stop(); - if (mThread.joinable()) - mThread.join(); - - if ( mFormatContext ) { - avformat_free_context(mFormatContext); - mFormatContext = nullptr; - } - if ( mSessDesc ) { - delete mSessDesc; - mSessDesc = nullptr; - } - delete mAuthenticator; - mAuthenticator = nullptr; -} - -void RtspThread::Run() { - std::string message; - std::string response; - - response.reserve(ZM_NETWORK_BUFSIZ); - - if ( !mRtspSocket.connect(mHost.c_str(), mPort.c_str()) ) - Fatal("Unable to connect RTSP socket"); - //Select select( 0.25 ); - //select.addReader( &mRtspSocket ); - //while ( select.wait() ) - //{ - //mRtspSocket.recv( response ); - //Debug( 4, "Drained %d bytes from RTSP socket", response.size() ); - //} - - bool authTried = false; - if ( mMethod == RTP_RTSP_HTTP ) { - if ( !mRtspSocket2.connect(mHost.c_str(), mPort.c_str()) ) - Fatal("Unable to connect auxiliary RTSP/HTTP socket"); - //Select select( 0.25 ); - //select.addReader( &mRtspSocket2 ); - //while ( select.wait() ) - //{ - //mRtspSocket2.recv( response ); - //Debug( 4, "Drained %d bytes from HTTP socket", response.size() ); - //} - - //possibly retry sending the message for authentication - int respCode = -1; - char respText[256]; - do { - message = "GET "+mPath+" HTTP/1.0\r\n"; - message += "X-SessionCookie: "+mHttpSession+"\r\n"; - if ( mNeedAuth ) { - message += mAuthenticator->getAuthHeader("GET", mPath); - authTried = true; - } - message += "Accept: application/x-rtsp-tunnelled\r\n\r\n"; - 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; - } - if ( mRtspSocket.recv(response) < 0 ) { - Error("Recv failed; %s", strerror(errno)); - return; - } - - Debug(2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size()); - float respVer = 0; - respCode = -1; - if ( sscanf(response.c_str(), "HTTP/%f %3d %255[^\r\n]\r\n", &respVer, &respCode, respText) != 3 ) { - if ( isalnum(response[0]) ) { - Error("Response parse failure in '%s'", response.c_str()); - } else { - Error("Response parse failure, %zd bytes follow", response.size()); - if ( response.size() ) - Hexdump(Logger::ERROR, response.data(), std::min(int(response.size()), 16)); - } - return; - } - // If Server requests authentication, check WWW-Authenticate header and fill required fields - // for requested authentication method - if ( respCode == 401 && !authTried ) { - mNeedAuth = true; - mAuthenticator->checkAuthResponse(response); - Debug(2, "Processed 401 response"); - mRtspSocket.close(); - if ( !mRtspSocket.connect(mHost.c_str(), mPort.c_str()) ) - Fatal("Unable to reconnect RTSP socket"); - Debug(2, "connection should be reopened now"); - } - - } while (respCode == 401 && !authTried); - - if ( respCode != 200 ) { - Error("Unexpected response code %d, text is '%s'", respCode, respText); - return; - } - - message = "POST "+mPath+" HTTP/1.0\r\n"; - message += "X-SessionCookie: "+mHttpSession+"\r\n"; - if ( mNeedAuth ) - message += mAuthenticator->getAuthHeader("POST", mPath); - message += "Content-Length: 32767\r\n"; - message += "Content-Type: application/x-rtsp-tunnelled\r\n"; - message += "\r\n"; - Debug(2, "Sending HTTP 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; - } - } // end if ( mMethod == RTP_RTSP_HTTP ) - - std::string localHost = ""; - int localPorts[2] = { 0, 0 }; - - // Request supported RTSP commands by the server - message = "OPTIONS "+mUrl+" RTSP/1.0\r\n"; - if ( !sendCommand(message) ) - return; - - // 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; - if ( !recvResponse(response) ) - return; - } else { - return; - } - } // end if failed response maybe due to auth - - char publicLine[256] = ""; - StringVector lines = Split(response, "\r\n"); - for ( size_t i = 0; i < lines.size(); i++ ) - sscanf(lines[i].c_str(), "Public: %255[^\r\n]\r\n", publicLine); - - // Check if the server supports the GET_PARAMETER command - // If yes, it is likely that the server will request this command as a keepalive message - bool sendKeepalive = false; - if ( publicLine[0] && strstr(publicLine, "GET_PARAMETER") ) - sendKeepalive = true; - - message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n"; - bool res; - do { - if ( mNeedAuth ) - authTried = true; - sendCommand(message); - // FIXME Why sleep 1? - std::this_thread::sleep_for(Microseconds(10)); - - res = recvResponse(response); - if ( !res && respCode==401 ) - mNeedAuth = true; - } while (!res && respCode==401 && !authTried); - - const std::string endOfHeaders = "\r\n\r\n"; - size_t sdpStart = response.find(endOfHeaders); - if ( sdpStart == std::string::npos ) - return; - - if ( mRtspDescribe ) { - std::string DescHeader = response.substr(0, sdpStart); - Debug(1, "Processing DESCRIBE response header '%s'", DescHeader.c_str()); - - lines = Split(DescHeader, "\r\n"); - for ( size_t i = 0; i < lines.size(); i++ ) { - // If the device sends us a url value for Content-Base in the response header, we should use that instead - if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) { - mUrl = TrimSpaces(lines[i].substr(13)); - 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(); - - std::string sdp = response.substr(sdpStart); - Debug(1, "Processing SDP '%s'", sdp.c_str()); - - try { - mSessDesc = new SessionDescriptor( mUrl, sdp ); - mFormatContext = mSessDesc->generateFormatContext(); - } catch ( const Exception &e ) { - Error("%s", e.getMessage().c_str()); - return; - } - -#if 0 - // New method using ffmpeg native functions - std::string authUrl = mUrl; - if ( !mAuth.empty() ) - authUrl.insert( authUrl.find( "://" )+3, mAuth+"@" ); - - if ( av_open_input_file( &mFormatContext, authUrl.c_str(), nullptr, 0, nullptr ) != 0 ) { - Error( "Unable to open input '%s'", authUrl.c_str() ); - return( -1 ); - } -#endif - - uint32_t rtpClock = 0; - std::string trackUrl = mUrl; - std::string controlUrl; - - _AVCODECID codecId = AV_CODEC_ID_NONE; - - if ( mFormatContext->nb_streams >= 1 ) { - for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) { - SessionDescriptor::MediaDescriptor *mediaDesc = mSessDesc->getStream(i); - if (mFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - // Check if control Url is absolute or relative - controlUrl = mediaDesc->getControlUrl(); - if (trackUrl != controlUrl) { - if ( *trackUrl.rbegin() != '/' ) { - trackUrl += "/" + controlUrl; - } else { - trackUrl += controlUrl; - } - } - rtpClock = mediaDesc->getClock(); - codecId = mFormatContext->streams[i]->codecpar->codec_id; - break; - } // end if is video - } // end foreach stream - } // end if have stream - - switch ( mMethod ) { - case RTP_UNICAST : - localPorts[0] = requestPorts(); - localPorts[1] = localPorts[0]+1; - - message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;unicast;client_port=" - +stringtf("%d", localPorts[0] )+"-"+stringtf( "%d", localPorts[1])+"\r\n"; - break; - case RTP_MULTICAST : - message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;multicast\r\n"; - break; - case RTP_RTSP : - case RTP_RTSP_HTTP : - message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP/TCP;unicast\r\n"; - break; - default: - Panic("Got unexpected method %d", mMethod); - break; - } - - if ( !sendCommand(message) ) - return; - if ( !recvResponse(response) ) - return; - - lines = Split(response, "\r\n"); - std::string session; - Seconds timeout = Seconds(0); - char transport[256] = ""; - - for ( size_t i = 0; i < lines.size(); i++ ) { - if ( ( lines[i].size() > 8 ) && ( lines[i].substr(0, 8) == "Session:" ) ) { - StringVector sessionLine = Split(lines[i].substr(9), ";"); - session = TrimSpaces(sessionLine[0]); - if ( sessionLine.size() == 2 ) { - int32 timeout_val = 0; - sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout_val); - timeout = Seconds(timeout_val); - } - } - sscanf(lines[i].c_str(), "Transport: %255s", transport); - } - - if ( session.empty() ) - Fatal("Unable to get session identifier from response '%s'", response.c_str()); - - Debug(2, "Got RTSP session %s, timeout %" PRIi64 " secs", - session.c_str(), - static_cast(Seconds(timeout).count())); - - if ( !transport[0] ) - Fatal("Unable to get transport details from response '%s'", response.c_str()); - - Debug(2, "Got RTSP transport %s", transport); - - std::string method = ""; - int remotePorts[2] = { 0, 0 }; - int remoteChannels[2] = { 0, 0 }; - std::string distribution = ""; - unsigned long ssrc = 0; - StringVector parts = Split(transport, ";"); - for ( size_t i = 0; i < parts.size(); i++ ) { - if ( parts[i] == "unicast" || parts[i] == "multicast" ) - distribution = parts[i]; - else if (StartsWith(parts[i], "server_port=") ) { - method = "RTP/UNICAST"; - StringVector subparts = Split(parts[i], "="); - StringVector ports = Split(subparts[1], "-"); - remotePorts[0] = strtol( ports[0].c_str(), nullptr, 10 ); - remotePorts[1] = strtol( ports[1].c_str(), nullptr, 10 ); - } else if (StartsWith(parts[i], "interleaved=") ) { - method = "RTP/RTSP"; - StringVector subparts = Split(parts[i], "="); - StringVector channels = Split(subparts[1], "-"); - remoteChannels[0] = strtol( channels[0].c_str(), nullptr, 10 ); - remoteChannels[1] = strtol( channels[1].c_str(), nullptr, 10 ); - } else if (StartsWith(parts[i], "port=") ) { - method = "RTP/MULTICAST"; - StringVector subparts = Split(parts[i], "="); - StringVector ports = Split(subparts[1], "-"); - localPorts[0] = strtol( ports[0].c_str(), nullptr, 10 ); - localPorts[1] = strtol( ports[1].c_str(), nullptr, 10 ); - } else if (StartsWith(parts[i], "destination=") ) { - StringVector subparts = Split(parts[i], "="); - localHost = subparts[1]; - } else if (StartsWith(parts[i], "ssrc=") ) { - StringVector subparts = Split(parts[i], "="); - ssrc = strtoll( subparts[1].c_str(), nullptr, 16 ); - } - } - - Debug( 2, "RTSP Method is %s", method.c_str() ); - Debug( 2, "RTSP Distribution is %s", distribution.c_str() ); - Debug( 2, "RTSP SSRC is %lx", ssrc ); - Debug( 2, "RTSP Local Host is %s", localHost.c_str() ); - Debug( 2, "RTSP Local Ports are %d/%d", localPorts[0], localPorts[1] ); - Debug( 2, "RTSP Remote Ports are %d/%d", remotePorts[0], remotePorts[1] ); - Debug( 2, "RTSP Remote Channels are %d/%d", remoteChannels[0], remoteChannels[1] ); - - message = "PLAY "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\nRange: npt=0.000-\r\n"; - if ( !sendCommand(message) ) - return; - if ( !recvResponse(response) ) - return; - - lines = Split(response, "\r\n"); - std::string rtpInfo; - for ( size_t i = 0; i < lines.size(); i++ ) { - if ( ( lines[i].size() > 9 ) && ( lines[i].substr(0, 9) == "RTP-Info:" ) ) - rtpInfo = TrimSpaces(lines[i].substr(9)); - // Check for a timeout again. Some rtsp devices don't send a timeout until after the PLAY command is sent - if ((lines[i].size() > 8) && (lines[i].substr(0, 8) == "Session:") && (timeout == Seconds(0))) { - StringVector sessionLine = Split(lines[i].substr(9), ";"); - if ( sessionLine.size() == 2 ) { - int32 timeout_val = 0; - sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout_val); - timeout = Seconds(timeout_val); - } - - if ( timeout > Seconds(0) ) { - Debug(2, "Got timeout %" PRIi64 " secs from PLAY command response", - static_cast(Seconds(timeout).count())); - } - } - } - - int seq = 0; - unsigned long rtpTime = 0; - StringVector streams; - if ( rtpInfo.empty() ) { - Debug( 1, "RTP Info Empty. Starting values for Sequence and Rtptime shall be zero."); - } else { - Debug( 2, "Got RTP Info %s", rtpInfo.c_str() ); - // More than one stream can be included in the RTP Info - streams = Split(rtpInfo, ","); - for ( size_t i = 0; i < streams.size(); i++ ) { - // We want the stream that matches the trackUrl we are using - if ( streams[i].find(controlUrl.c_str()) != std::string::npos ) { - // Parse the sequence and rtptime values - parts = Split(streams[i].c_str(), ";"); - for ( size_t j = 0; j < parts.size(); j++ ) { - if (StartsWith(parts[j], "seq=") ) { - StringVector subparts = Split(parts[j], "="); - seq = strtol( subparts[1].c_str(), nullptr, 10 ); - } else if (StartsWith(parts[j], "rtptime=") ) { - StringVector subparts = Split(parts[j], "="); - rtpTime = strtol( subparts[1].c_str(), nullptr, 10 ); - } - } - break; - } - } - } - - Debug( 2, "RTSP Seq is %d", seq ); - Debug( 2, "RTSP Rtptime is %ld", rtpTime ); - - TimePoint lastKeepalive = std::chrono::steady_clock::now(); - TimePoint now; - message = "GET_PARAMETER "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - - switch( mMethod ) { - case RTP_UNICAST : { - 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 ); - - while (!mTerminate) { - now = std::chrono::steady_clock::now(); - // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - Debug(5, "sendkeepalive %d, timeout %" PRIi64 " s, now: %" PRIi64 " s last: %" PRIi64 " s since: %" PRIi64 "s ", - sendKeepalive, - static_cast(Seconds(timeout).count()), - static_cast(std::chrono::duration_cast(now.time_since_epoch()).count()), - static_cast(std::chrono::duration_cast(lastKeepalive.time_since_epoch()).count()), - static_cast(std::chrono::duration_cast((now - lastKeepalive)).count())); - - if (sendKeepalive && (timeout > Seconds(0)) && ((now - lastKeepalive) > (timeout - Seconds(5)))) { - if (!sendCommand(message)) - return; - lastKeepalive = now; - } - std::this_thread::sleep_for(Microseconds(100)); - } -#if 0 - message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); -#endif - - message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return; - if ( !recvResponse( response ) ) - return; - - rtpDataThread.Stop(); - rtpCtrlThread.Stop(); - - //rtpDataThread.kill( SIGTERM ); - //rtpCtrlThread.kill( SIGTERM ); - - delete mSources[ssrc]; - mSources.clear(); - - releasePorts( localPorts[0] ); - - break; - } - case RTP_RTSP : - case RTP_RTSP_HTTP : { - 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 ); - RtpCtrlThread rtpCtrlThread( *this, *source ); - - zm::Select select(Milliseconds(config.http_timeout)); - select.addReader( &mRtspSocket ); - - Buffer buffer( ZM_NETWORK_BUFSIZ ); - std::string keepaliveMessage = "OPTIONS "+mUrl+" RTSP/1.0\r\n"; - std::string keepaliveResponse = "RTSP/1.0 200 OK\r\n"; - while (!mTerminate && select.wait() >= 0) { - zm::Select::CommsList readable = select.getReadable(); - if ( readable.size() == 0 ) { - Error( "RTSP timed out" ); - break; - } - - static char tempBuffer[ZM_NETWORK_BUFSIZ]; - ssize_t nBytes = mRtspSocket.recv( tempBuffer, sizeof(tempBuffer) ); - buffer.append( tempBuffer, nBytes ); - Debug( 4, "Read %zd bytes on sd %d, %d total", nBytes, mRtspSocket.getReadDesc(), buffer.size() ); - - while( buffer.size() > 0 ) { - 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 %d bytes, rereading", (len+4)-buffer.size() ); - break; - } - if ( channel == remoteChannels[0] ) { - Debug(4, "Got %d bytes on data channel %d, packet length is %d", buffer.size(), channel, len); - Hexdump(4, (char *)buffer, 16); - rtpDataThread.recvPacket(buffer+4, len); - } 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 { - 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 ) { - Debug( 4, "Got keepalive response '%s'", (char *)buffer ); - //buffer.consume( keepaliveResponse.size() ); - if ( const char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) { - int discardBytes = charPtr-(char *)buffer; - buffer -= discardBytes; - } else { - buffer.clear(); - } - } else { - if ( const 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 { - Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() ); - Hexdump( -1, (char *)buffer, 32 ); - buffer.clear(); - } - } - } - } - // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - // FIXME: Is this really necessary when using tcp ? - now = std::chrono::steady_clock::now(); - // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - Debug(5, "sendkeepalive %d, timeout %" PRIi64 " s, now: %" PRIi64 " s last: %" PRIi64 " s since: %" PRIi64 " s", - sendKeepalive, - static_cast(Seconds(timeout).count()), - static_cast(std::chrono::duration_cast(now.time_since_epoch()).count()), - static_cast(std::chrono::duration_cast(lastKeepalive.time_since_epoch()).count()), - static_cast(std::chrono::duration_cast((now - lastKeepalive)).count())); - - if (sendKeepalive && (timeout > Seconds(0)) && ((now - lastKeepalive) > (timeout - Seconds(5)))) { - if (!sendCommand(message)) { - return; - } - - lastKeepalive = now; - } - buffer.tidy(true); - } -#if 0 - message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); -#endif - // Send a teardown message but don't expect a response as this may not be implemented on the server when using TCP - message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return; - - delete mSources[ssrc]; - mSources.clear(); - - break; - } - case RTP_MULTICAST : { - 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 ); - - - while (!mTerminate) { - // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - if (sendKeepalive && (timeout > Seconds(0)) - && ((std::chrono::steady_clock::now() - lastKeepalive) > (timeout - Seconds(5)))) { - if (!sendCommand(message)) { - return; - } - lastKeepalive = std::chrono::steady_clock::now(); - } - std::this_thread::sleep_for(Microseconds(100)); - } -#if 0 - message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); -#endif - message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand(message) ) - return; - if ( !recvResponse(response) ) - return; - - rtpDataThread.Stop(); - rtpCtrlThread.Stop(); - - delete mSources[ssrc]; - mSources.clear(); - - releasePorts( localPorts[0] ); - break; - } - default: - Panic("Got unexpected method %d", mMethod); - break; - } - - return; -} diff --git a/src/zm_rtsp.h b/src/zm_rtsp.h deleted file mode 100644 index b19b95f64..000000000 --- a/src/zm_rtsp.h +++ /dev/null @@ -1,138 +0,0 @@ -// -// ZoneMinder RTSP Class Interface, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#ifndef ZM_RTSP_H -#define ZM_RTSP_H - -#include "zm_comms.h" -#include "zm_rtp_source.h" -#include "zm_rtsp_auth.h" -#include "zm_sdp.h" -#include -#include -#include -#include - -class RtspThread { - public: - typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod; - typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist; - - private: - typedef std::set PortSet; - typedef std::set SsrcSet; - typedef std::map SourceMap; - - private: - static int smMinDataPort; - static int smMaxDataPort; - static PortSet smLocalSsrcs; - static PortSet smAssignedPorts; - - private: - int mId; - - RtspMethod mMethod; - std::string mProtocol; - std::string mHost; - std::string mPort; - std::string mPath; - bool mRtspDescribe; - std::string mUrl; - - // Reworked authentication system - // First try without authentication, even if we have a username and password - // on receiving a 401 response, select authentication method (basic or digest) - // fill required fields and set needAuth - // subsequent requests can set the required authentication header. - bool mNeedAuth; - int respCode; - zm::Authenticator* mAuthenticator; - - - std::string mHttpSession; ///< Only for RTSP over HTTP sessions - - zm::TcpInetClient mRtspSocket; - zm::TcpInetClient mRtspSocket2; - - SourceMap mSources; - - SessionDescriptor *mSessDesc; - AVFormatContext *mFormatContext; - - uint16_t mSeq; - uint32_t mSession; - uint32_t mSsrc; - - int mRemotePorts[2]; - int mRemoteChannels[2]; - RtspDist mDist; - - unsigned long mRtpTime; - - std::thread mThread; - std::atomic mTerminate; - - private: - bool sendCommand( std::string message ); - bool recvResponse( std::string &response ); - void checkAuthResponse(std::string &response); - void Run(); - - public: - RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, - const std::string &port, const std::string &path, const std::string &user, const std::string &pass, - bool rtsp_describe ); - ~RtspThread(); - - public: - int requestPorts(); - void releasePorts( int port ); - - bool isValidSsrc( uint32_t ssrc ); - bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header ); - - uint32_t getSsrc() const { - return( mSsrc ); - } - - bool hasSources() const { - return( !mSources.empty() ); - } - - AVFormatContext *getFormatContext() { - return( mFormatContext ); - } - - bool getFrame( Buffer &frame ) { - SourceMap::iterator iter = mSources.begin(); - if ( iter == mSources.end() ) - return( false ); - return( iter->second->getFrame( frame ) ); - } - - void Stop() { mTerminate = true; } - bool IsStopped() const { return mTerminate; } - - int getAddressFamily () { - return mRtspSocket.getDomain(); - } -}; - -#endif // ZM_RTSP_H diff --git a/src/zm_sdp.cpp b/src/zm_sdp.cpp deleted file mode 100644 index 53bf43472..000000000 --- a/src/zm_sdp.cpp +++ /dev/null @@ -1,431 +0,0 @@ -// -// ZoneMinder SDP Class Implementation, $Date: 2009-04-14 21:20:02 +0100 (Tue, 14 Apr 2009) $, $Revision: 2850 $ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#include "zm_sdp.h" - -#include "zm_config.h" -#include "zm_exception.h" -#include "zm_logger.h" - -SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { - { 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 }, - { 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 }, - { 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 }, - { 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 }, - { 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 }, - { 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 }, - { 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 }, - { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 }, - { 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 }, - { 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 }, - { 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, - { 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 }, - { 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, - { 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 }, - { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 }, - { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 }, - { 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 }, - { 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 }, - { -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 } -}; - -SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { - { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, - { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, - { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, - { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB }, - { "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE } -}; - -SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) : - mTtl( 16 ), - mNoAddresses( 0 ) { - StringVector tokens = Split(connInfo, " "); - if ( tokens.size() < 3 ) - throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" ); - mNetworkType = tokens[0]; - if ( mNetworkType != "IN" ) - throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); - mAddressType = tokens[1]; - if ( mAddressType != "IP4" && mAddressType != "IP6" ) - throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" ); - StringVector addressTokens = Split(tokens[2], "/"); - if ( addressTokens.size() < 1 ) - throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" ); - mAddress = addressTokens[0]; - if ( addressTokens.size() >= 2 ) - mTtl = atoi(addressTokens[1].c_str()); - if ( addressTokens.size() >= 3 ) - mNoAddresses = atoi(addressTokens[2].c_str()); -} - -SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) : - mValue( 0 ) { - StringVector tokens = Split(bandInfo, ":"); - if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" ); - mType = tokens[0]; - //if ( mNetworkType != "IN" ) - //throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); - mValue = atoi(tokens[1].c_str()); -} - -SessionDescriptor::MediaDescriptor::MediaDescriptor( - const std::string &type, - int port, - int numPorts, - const std::string &transport, - int payloadType ) : - mType( type ), - mPort( port ), - mNumPorts( numPorts ), - mTransport( transport ), - mPayloadType( payloadType ), - mFrameRate( 0.0 ), - mClock( 0 ), - mWidth( 0 ), - mHeight( 0 ), - mSprops( "" ), - mConnInfo( nullptr ) { -} - -SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) : - mUrl( url ), - mConnInfo( nullptr ), - mBandInfo( nullptr ) { - MediaDescriptor *currMedia = nullptr; - - StringVector lines = Split(sdp, "\r\n"); - for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter ) { - std::string line = *iter; - if ( line.empty() ) - break; - - Debug(3, "Processing SDP line '%s'", line.c_str()); - const char sdpType = line[0]; - if ( line[1] != '=' ) - throw Exception("Invalid SDP format at '"+line+"'"); - - line.erase(0, 2); - switch( sdpType ) { - case 'v' : - mVersion = line; - break; - case 'o' : - mOwner = line; - break; - case 's' : - mName = line; - break; - case 'i' : - mInfo = line; - break; - case 'c' : - // This prevent a memory leak if the field appears more than one time - if ( mConnInfo ) - delete mConnInfo; - mConnInfo = new ConnInfo( line ); - break; - case 'b' : - // This prevent a memory leak if the field appears more than one time - if ( mBandInfo ) - delete mBandInfo; - mBandInfo = new BandInfo( line ); - break; - case 't' : - mTimeInfo = line; - break; - case 'a' : { - mAttributes.push_back( line ); - StringVector tokens = Split(line, ":", 2); - std::string attrName = tokens[0]; - 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" ) { - // a=rtpmap:96 MP4V-ES/90000 - if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP rtpmap 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() ) ); - 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" ) { - // 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())); - //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" ) { - // 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" ) { - // 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())); - //currMedia->setPayloadType( payloadType ); - if ( attrTokens.size() > 1 ) { - StringVector attr2Tokens = Split(attrTokens[1], "; "); - 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" ) { - } 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()); - } - } - } - } else if ( attrName == "mpeg4-iod" ) { - // a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA" - } 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()); - } - } else { - Debug(3, "Ignoring general SDP attribute '%s'", line.c_str()); - } - break; - } - case 'm' : { - StringVector tokens = Split(line, " "); - if ( tokens.size() < 4 ) - throw Exception("Can't parse SDP media description '"+line+"'"); - std::string mediaType = tokens[0]; - if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" ) - throw Exception("Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'"); - StringVector portTokens = Split(tokens[1], "/"); - int mediaPort = atoi(portTokens[0].c_str()); - int mediaNumPorts = 1; - if ( portTokens.size() > 1 ) - mediaNumPorts = atoi(portTokens[1].c_str()); - std::string mediaTransport = tokens[2]; - if ( mediaTransport != "RTP/AVP" ) - throw Exception("Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'"); - int payloadType = atoi(tokens[3].c_str()); - currMedia = new MediaDescriptor(mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType); - mMediaList.push_back(currMedia); - break; - } - } // end switch - } // end foreach line -} - -SessionDescriptor::~SessionDescriptor() { - if ( mConnInfo ) - delete mConnInfo; - if ( mBandInfo ) - delete mBandInfo; - for ( unsigned int i = 0; i < mMediaList.size(); i++ ) - delete mMediaList[i]; -} - -AVFormatContext *SessionDescriptor::generateFormatContext() const { - AVFormatContext *formatContext = avformat_alloc_context(); - if (!formatContext) { - Error("Failed to allocate AVFormatContext"); - return nullptr; - } - -#if (LIBAVFORMAT_VERSION_CHECK(58, 12, 0, 0, 100)) - formatContext->url = av_strdup(mUrl.c_str()); -#else - strncpy(formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) - 1); - formatContext->filename[sizeof(formatContext->filename) - 1] = '\0'; -#endif - /* - 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++ ) { - const MediaDescriptor *mediaDesc = mMediaList[i]; - AVStream *stream = avformat_new_stream(formatContext, nullptr); - if (!stream) { - Error("Failed to allocate AVStream for media %d", i); - continue; - } - stream->id = i; - - AVCodecContext *codec_context = avcodec_alloc_context3(nullptr); - if (!codec_context) { - Error("Failed to allocate AVCodecContext for media %d", i); - continue; - } - - 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 ( type == "video" ) - codec_context->codec_type = AVMEDIA_TYPE_VIDEO; - else if ( type == "audio" ) - codec_context->codec_type = AVMEDIA_TYPE_AUDIO; - else if ( type == "application" ) - codec_context->codec_type = AVMEDIA_TYPE_DATA; - else - Warning("Unknown media_type %s", type.c_str()); - - std::string codec_name; - if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC ) { - // Look in static table - for ( unsigned int j = 0; j < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); j++ ) { - if ( smStaticPayloads[j].payloadType == mediaDesc->getPayloadType() ) { - Debug( 1, "Got static payload type %d, %s", smStaticPayloads[j].payloadType, smStaticPayloads[j].payloadName ); - codec_name = std::string(smStaticPayloads[j].payloadName); - codec_context->codec_type = smStaticPayloads[j].codecType; - codec_context->codec_id = smStaticPayloads[j].codecId; - codec_context->sample_rate = smStaticPayloads[j].clockRate; - break; - } - } - } else { - // Look in dynamic table - for ( unsigned int j = 0; j < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); j++ ) { - if ( smDynamicPayloads[j].payloadName == mediaDesc->getPayloadDesc() ) { - Debug(1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[j].payloadName); - codec_name = std::string(smDynamicPayloads[j].payloadName); - codec_context->codec_type = smDynamicPayloads[j].codecType; - codec_context->codec_id = smDynamicPayloads[j].codecId; - codec_context->sample_rate = mediaDesc->getClock(); - break; - } - } - } /// end if static or dynamic - - - if (codec_name.empty()) { - Warning( "Can't find payload details for %s payload type %d, name %s", - mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); - } - if ( mediaDesc->getWidth() ) - codec_context->width = mediaDesc->getWidth(); - if ( mediaDesc->getHeight() ) - codec_context->height = mediaDesc->getHeight(); - if ( codec_context->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size()) { - uint8_t start_sequence[]= { 0, 0, 1 }; - codec_context->extradata_size= 0; - codec_context->extradata= nullptr; - char pvalue[1024], *value = pvalue; - - // sprop-parameter-sets comes from the (untrusted) camera SDP. A real - // H.264 SPS/PPS base64 blob is small; anything approaching the buffer - // size is malformed or hostile, so refuse it rather than overflow the - // stack with an unbounded strcpy. - if (mediaDesc->getSprops().size() >= sizeof(pvalue)) { - Warning("Ignoring oversized sprop-parameter-sets (%zu bytes) from SDP", - mediaDesc->getSprops().size()); - pvalue[0] = '\0'; - } else { - 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) < (long)(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) + codec_context->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - if ( dest ) { - if ( codec_context->extradata_size ) { - // av_realloc? - memcpy(dest, codec_context->extradata, codec_context->extradata_size); - av_free(codec_context->extradata); - } - - memcpy(dest+codec_context->extradata_size, start_sequence, sizeof(start_sequence)); - memcpy(dest+codec_context->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); - memset(dest+codec_context->extradata_size+sizeof(start_sequence)+ - packet_size, 0, - AV_INPUT_BUFFER_PADDING_SIZE - ); - - codec_context->extradata= dest; - codec_context->extradata_size+= sizeof(start_sequence)+packet_size; -// } else { -// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!"); -// return AVERROR(ENOMEM); - } - } - } - } - avcodec_parameters_from_context(stream->codecpar, codec_context); - avcodec_free_context(&codec_context); - } // end foreach mediaList - - return formatContext; -} diff --git a/src/zm_sdp.h b/src/zm_sdp.h deleted file mode 100644 index 82a199f8d..000000000 --- a/src/zm_sdp.h +++ /dev/null @@ -1,224 +0,0 @@ -// -// ZoneMinder SDP Class Interface, $Date: 2009-02-16 18:21:50 +0000 (Mon, 16 Feb 2009) $, $Revision: 2765 $ -// Copyright (C) 2001-2008 Philip Coombes -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// - -#ifndef ZM_SDP_H -#define ZM_SDP_H - -#include "zm_ffmpeg.h" -#include "zm_utils.h" -#include -#include - -class SessionDescriptor { - protected: - enum { PAYLOAD_TYPE_DYNAMIC=96 }; - - struct StaticPayloadDesc { - int payloadType; - const char payloadName[6]; - AVMediaType codecType; - _AVCODECID codecId; - int clockRate; - int autoChannels; - }; - - struct DynamicPayloadDesc { - const char payloadName[32]; - AVMediaType codecType; - _AVCODECID codecId; - - //int clockRate; - //int autoChannels; - }; - - public: - class ConnInfo { - protected: - std::string mNetworkType; - std::string mAddressType; - std::string mAddress; - int mTtl; - int mNoAddresses; - - public: - explicit ConnInfo( const std::string &connInfo ); - }; - - class BandInfo { - protected: - std::string mType; - int mValue; - - public: - explicit BandInfo( const std::string &bandInfo ); - }; - - class MediaDescriptor { - protected: - std::string mType; - int mPort; - int mNumPorts; - std::string mTransport; - int mPayloadType; - - std::string mPayloadDesc; - std::string mControlUrl; - double mFrameRate; - int mClock; - int mWidth; - int mHeight; - std::string mSprops; - - ConnInfo *mConnInfo; - - public: - MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ); - - const std::string &getType() const { - return( mType ); - } - int getPort() const { - return( mPort ); - } - int getNumPorts() const { - return( mNumPorts ); - } - const std::string &getTransport() const { - return( mTransport ); - } - int getPayloadType() const { - return( mPayloadType ); - } - - const std::string &getPayloadDesc() const { - return( mPayloadDesc ); - } - void setPayloadDesc( const std::string &payloadDesc ) { - mPayloadDesc = payloadDesc; - } - - const std::string &getControlUrl() const { - return( mControlUrl ); - } - void setControlUrl( const std::string &controlUrl ) { - mControlUrl = controlUrl; - } - - int getClock() const { - return( mClock ); - } - void setClock( int clock ) { - mClock = clock; - } - - void setFrameSize( int width, int height ) { - mWidth = width; - mHeight = height; - } - int getWidth() const { - return( mWidth ); - } - int getHeight() const { - return( mHeight ); - } - - void setSprops(const std::string &props) { - mSprops = props; - } - std::string getSprops() const { - return ( mSprops ); - } - double getFrameRate() const { - return( mFrameRate ); - } - void setFrameRate( double frameRate ) { - mFrameRate = frameRate; - } - }; - - typedef std::vector MediaList; - - protected: - static StaticPayloadDesc smStaticPayloads[]; - static DynamicPayloadDesc smDynamicPayloads[]; - - protected: - std::string mUrl; - - std::string mVersion; - std::string mOwner; - std::string mName; - std::string mInfo; - - ConnInfo *mConnInfo; - BandInfo *mBandInfo; - std::string mTimeInfo; - StringVector mAttributes; - - MediaList mMediaList; - - public: - SessionDescriptor( const std::string &url, const std::string &sdp ); - ~SessionDescriptor(); - - const std::string &getUrl() const { - return( mUrl ); - } - - int getNumStreams() const { - return( mMediaList.size() ); - } - MediaDescriptor *getStream( int index ) { - if ( index < 0 || (unsigned int)index >= mMediaList.size() ) - return nullptr; - return( mMediaList[index] ); - } - - AVFormatContext *generateFormatContext() const; -}; -#if 0 -v=0 - o=- 1239719297054659 1239719297054674 IN IP4 192.168.1.11 - s=Media Presentation - e=NONE - c=IN IP4 0.0.0.0 - b=AS:174 - t=0 0 - a=control: - * - a=range: - npt=now- - a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA" - m=video 0 RTP/AVP 96 - b=AS:110 - a=framerate:5.0 - a=control: - trackID=1 - a=rtpmap:96 MP4V-ES/90000 - a=fmtp:96 profile-level-id=247; -config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F - a=mpeg4-esid:201 - m=audio 0 RTP/AVP 0 - b=AS:64 - a=control: - trackID=2 - -#endif - -#endif // ZM_SDP_H