2013-03-17 07:45:21 +08:00
//
// 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
2016-12-26 23:23:16 +08:00
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2013-03-17 07:45:21 +08:00
//
# include "zm.h"
# if HAVE_LIBAVFORMAT
# include "zm_remote_camera_rtsp.h"
# include "zm_ffmpeg.h"
# include "zm_mem_utils.h"
# include <sys/types.h>
# include <sys/socket.h>
2015-08-21 23:29:54 +08:00
RemoteCameraRtsp : : RemoteCameraRtsp ( int p_id , const std : : string & p_method , const std : : string & p_host , const std : : string & p_port , const std : : string & p_path , 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 ) :
2016-04-04 22:11:48 +08:00
RemoteCamera ( p_id , " rtsp " , p_host , p_port , p_path , p_width , p_height , p_colours , p_brightness , p_contrast , p_hue , p_colour , p_capture ) ,
rtsp_describe ( p_rtsp_describe ) ,
rtspThread ( 0 )
2015-11-05 14:19:58 +08:00
2013-03-17 07:45:21 +08:00
{
2016-04-04 22:11:48 +08:00
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 ( ) , id ) ;
if ( capture )
{
Initialise ( ) ;
}
mFormatContext = NULL ;
mVideoStreamId = - 1 ;
mCodecContext = NULL ;
mCodec = NULL ;
mRawFrame = NULL ;
mFrame = NULL ;
frameCount = 0 ;
# if HAVE_LIBSWSCALE
mConvertContext = NULL ;
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
/* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */
if ( colours = = ZM_COLOUR_RGB32 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGBA ;
imagePixFormat = AV_PIX_FMT_RGBA ;
} else if ( colours = = ZM_COLOUR_RGB24 ) {
subpixelorder = ZM_SUBPIX_ORDER_RGB ;
imagePixFormat = AV_PIX_FMT_RGB24 ;
} else if ( colours = = ZM_COLOUR_GRAY8 ) {
subpixelorder = ZM_SUBPIX_ORDER_NONE ;
imagePixFormat = AV_PIX_FMT_GRAY8 ;
} else {
Panic ( " Unexpected colours: %d " , colours ) ;
}
2013-03-17 07:45:21 +08:00
}
RemoteCameraRtsp : : ~ RemoteCameraRtsp ( )
{
2015-11-04 12:30:14 +08:00
# if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
2016-04-04 22:11:48 +08:00
av_frame_free ( & mFrame ) ;
av_frame_free ( & mRawFrame ) ;
2015-11-04 12:30:14 +08:00
# else
2016-04-04 22:11:48 +08:00
av_freep ( & mFrame ) ;
av_freep ( & mRawFrame ) ;
2015-11-04 12:30:14 +08:00
# endif
2016-04-04 22:11:48 +08:00
2013-03-17 07:45:21 +08:00
# if HAVE_LIBSWSCALE
2016-04-04 22:11:48 +08:00
if ( mConvertContext )
{
sws_freeContext ( mConvertContext ) ;
mConvertContext = NULL ;
}
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
if ( mCodecContext )
{
avcodec_close ( mCodecContext ) ;
mCodecContext = NULL ; // Freed by avformat_free_context in the destructor of RtspThread class
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
if ( capture )
{
Terminate ( ) ;
}
2013-03-17 07:45:21 +08:00
}
void RemoteCameraRtsp : : Initialise ( )
{
2016-04-04 22:11:48 +08:00
RemoteCamera : : Initialise ( ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
int max_size = width * height * colours ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
buffer . size ( max_size ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
if ( logDebugging ( ) )
av_log_set_level ( AV_LOG_DEBUG ) ;
else
av_log_set_level ( AV_LOG_QUIET ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
av_register_all ( ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
Connect ( ) ;
2013-03-17 07:45:21 +08:00
}
void RemoteCameraRtsp : : Terminate ( )
{
2016-04-04 22:11:48 +08:00
Disconnect ( ) ;
2013-03-17 07:45:21 +08:00
}
int RemoteCameraRtsp : : Connect ( )
{
2016-04-04 22:11:48 +08:00
rtspThread = new RtspThread ( id , method , protocol , host , port , path , auth , rtsp_describe ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
rtspThread - > start ( ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
return ( 0 ) ;
2013-03-17 07:45:21 +08:00
}
int RemoteCameraRtsp : : Disconnect ( )
{
2016-04-04 22:11:48 +08:00
if ( rtspThread )
{
rtspThread - > stop ( ) ;
rtspThread - > join ( ) ;
delete rtspThread ;
rtspThread = 0 ;
}
return ( 0 ) ;
2013-03-17 07:45:21 +08:00
}
int RemoteCameraRtsp : : PrimeCapture ( )
{
2016-04-04 22:11:48 +08:00
Debug ( 2 , " Waiting for sources " ) ;
for ( int i = 0 ; i < 100 & & ! rtspThread - > hasSources ( ) ; i + + )
{
usleep ( 100000 ) ;
}
if ( ! rtspThread - > hasSources ( ) )
Fatal ( " No RTSP sources " ) ;
Debug ( 2 , " Got sources " ) ;
mFormatContext = rtspThread - > getFormatContext ( ) ;
// Find first video stream present
mVideoStreamId = - 1 ;
for ( unsigned int i = 0 ; i < mFormatContext - > nb_streams ; i + + )
2015-05-29 23:38:02 +08:00
# if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
2016-04-04 22:11:48 +08:00
if ( mFormatContext - > streams [ i ] - > codec - > codec_type = = AVMEDIA_TYPE_VIDEO )
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
if ( mFormatContext - > streams [ i ] - > codec - > codec_type = = CODEC_TYPE_VIDEO )
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
{
mVideoStreamId = i ;
break ;
}
if ( mVideoStreamId = = - 1 )
Fatal ( " Unable to locate video stream " ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
// Get a pointer to the codec context for the video stream
mCodecContext = mFormatContext - > streams [ mVideoStreamId ] - > codec ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
// Find the decoder for the video stream
mCodec = avcodec_find_decoder ( mCodecContext - > codec_id ) ;
if ( mCodec = = NULL )
Panic ( " Unable to locate codec %d decoder " , mCodecContext - > codec_id ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
// Open codec
2015-05-29 23:38:02 +08:00
# if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
2016-04-04 22:11:48 +08:00
if ( avcodec_open ( mCodecContext , mCodec ) < 0 )
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
if ( avcodec_open2 ( mCodecContext , mCodec , 0 ) < 0 )
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
Panic ( " Can't open codec " ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
// Allocate space for the native video frame
2015-05-29 23:38:02 +08:00
# if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
2016-04-04 22:11:48 +08:00
mRawFrame = av_frame_alloc ( ) ;
2015-05-29 23:38:02 +08:00
# else
2016-04-04 22:11:48 +08:00
mRawFrame = avcodec_alloc_frame ( ) ;
2015-05-29 23:38:02 +08:00
# endif
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
// Allocate space for the converted video frame
2015-05-29 23:38:02 +08:00
# if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
2016-04-04 22:11:48 +08:00
mFrame = av_frame_alloc ( ) ;
2015-05-29 23:38:02 +08:00
# else
2016-04-04 22:11:48 +08:00
mFrame = avcodec_alloc_frame ( ) ;
2015-05-29 23:38:02 +08:00
# endif
2016-04-04 22:11:48 +08:00
if ( mRawFrame = = NULL | | mFrame = = NULL )
Fatal ( " Unable to allocate frame(s) " ) ;
2016-04-29 19:27:28 +08:00
2016-03-02 22:03:55 +08:00
# if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
2016-04-29 19:27:28 +08:00
int pSize = av_image_get_buffer_size ( imagePixFormat , width , height , 1 ) ;
2016-03-02 22:03:55 +08:00
# else
2016-04-04 22:11:48 +08:00
int pSize = avpicture_get_size ( imagePixFormat , width , height ) ;
2016-03-02 22:03:55 +08:00
# endif
2016-04-04 22:11:48 +08:00
if ( ( unsigned int ) pSize ! = imagesize ) {
Fatal ( " Image size mismatch. Required: %d Available: %d " , pSize , imagesize ) ;
}
/*
2013-03-17 07:45:21 +08:00
# if HAVE_LIBSWSCALE
2016-04-04 22:11:48 +08:00
if ( ! sws_isSupportedInput ( mCodecContext - > pix_fmt ) ) {
Fatal ( " swscale does not support the codec format: %c%c%c%c " , ( mCodecContext - > pix_fmt ) & 0xff , ( ( mCodecContext - > pix_fmt > > 8 ) & 0xff ) , ( ( mCodecContext - > pix_fmt > > 16 ) & 0xff ) , ( ( mCodecContext - > pix_fmt > > 24 ) & 0xff ) ) ;
}
if ( ! sws_isSupportedOutput ( imagePixFormat ) ) {
Fatal ( " swscale does not support the target format: %c%c%c%c " , ( imagePixFormat ) & 0xff , ( ( imagePixFormat > > 8 ) & 0xff ) , ( ( imagePixFormat > > 16 ) & 0xff ) , ( ( imagePixFormat > > 24 ) & 0xff ) ) ;
}
2013-03-17 07:45:21 +08:00
# else // HAVE_LIBSWSCALE
2016-04-04 22:11:48 +08:00
Fatal ( " You must compile ffmpeg with the --enable-swscale option to use RTSP cameras " ) ;
2013-03-17 07:45:21 +08:00
# endif // HAVE_LIBSWSCALE
2013-09-27 19:08:11 +08:00
*/
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
return ( 0 ) ;
2013-03-17 07:45:21 +08:00
}
int RemoteCameraRtsp : : PreCapture ( )
{
2016-04-04 22:11:48 +08:00
if ( ! rtspThread - > isRunning ( ) )
return ( - 1 ) ;
if ( ! rtspThread - > hasSources ( ) )
{
Error ( " Cannot precapture, no RTP sources " ) ;
return ( - 1 ) ;
}
return ( 0 ) ;
2013-03-17 07:45:21 +08:00
}
int RemoteCameraRtsp : : Capture ( Image & image )
{
2016-04-04 22:11:48 +08:00
AVPacket packet ;
uint8_t * directbuffer ;
int frameComplete = false ;
/* Request a writeable buffer of the target image */
directbuffer = image . WriteBuffer ( width , height , colours , subpixelorder ) ;
if ( directbuffer = = NULL ) {
Error ( " Failed requesting writeable buffer for the captured image. " ) ;
return ( - 1 ) ;
}
while ( true )
{
buffer . clear ( ) ;
if ( ! rtspThread - > isRunning ( ) )
return ( - 1 ) ;
if ( rtspThread - > getFrame ( buffer ) )
2013-03-17 07:45:21 +08:00
{
2016-04-04 22:11:48 +08:00
Debug ( 3 , " Read frame %d bytes " , buffer . size ( ) ) ;
Debug ( 4 , " Address %p " , buffer . head ( ) ) ;
Hexdump ( 4 , buffer . head ( ) , 16 ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
if ( ! buffer . size ( ) )
return ( - 1 ) ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
if ( mCodecContext - > 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
if ( nalType = = 7 )
{
lastSps = buffer ;
continue ;
}
// PPS
else if ( nalType = = 8 )
2013-03-17 07:45:21 +08:00
{
2016-04-04 22:11:48 +08:00
lastPps = buffer ;
continue ;
}
// IDR
else if ( nalType = = 5 )
{
buffer + = lastSps ;
buffer + = lastPps ;
}
}
av_init_packet ( & packet ) ;
while ( ! frameComplete & & buffer . size ( ) > 0 )
{
packet . data = buffer . head ( ) ;
packet . size = buffer . size ( ) ;
2015-05-29 23:38:02 +08:00
# if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
2016-04-04 22:11:48 +08:00
int len = avcodec_decode_video2 ( mCodecContext , mRawFrame , & frameComplete , & packet ) ;
2014-04-26 04:12:58 +08:00
# else
2016-04-04 22:11:48 +08:00
int len = avcodec_decode_video ( mCodecContext , mRawFrame , & frameComplete , packet . data , packet . size ) ;
2014-04-26 04:12:58 +08:00
# endif
2016-04-04 22:11:48 +08:00
if ( len < 0 )
{
Error ( " Error while decoding frame %d " , frameCount ) ;
Hexdump ( Logger : : ERROR , buffer . head ( ) , buffer . size ( ) > 256 ? 256 : buffer . size ( ) ) ;
buffer . clear ( ) ;
continue ;
}
Debug ( 2 , " Frame: %d - %d/%d " , frameCount , len , buffer . size ( ) ) ;
//if ( buffer.size() < 400 )
//Hexdump( 0, buffer.head(), buffer.size() );
buffer - = len ;
}
if ( frameComplete ) {
Debug ( 3 , " Got frame %d " , frameCount ) ;
2016-04-29 19:27:28 +08:00
2016-03-02 22:03:55 +08:00
# if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
2016-04-29 19:27:28 +08:00
av_image_fill_arrays ( mFrame - > data , mFrame - > linesize ,
directbuffer , imagePixFormat , width , height , 1 ) ;
2016-03-02 22:03:55 +08:00
# else
2016-04-29 19:27:28 +08:00
avpicture_fill ( ( AVPicture * ) mFrame , directbuffer ,
imagePixFormat , width , height ) ;
2016-03-02 22:03:55 +08:00
# endif
2013-03-17 07:45:21 +08:00
# if HAVE_LIBSWSCALE
2016-04-04 22:11:48 +08:00
if ( mConvertContext = = NULL ) {
mConvertContext = sws_getContext ( mCodecContext - > width , mCodecContext - > height , mCodecContext - > pix_fmt , width , height , imagePixFormat , SWS_BICUBIC , NULL , NULL , NULL ) ;
if ( mConvertContext = = NULL )
Fatal ( " Unable to create conversion context " ) ;
}
if ( sws_scale ( mConvertContext , mRawFrame - > data , mRawFrame - > linesize , 0 , mCodecContext - > height , mFrame - > data , mFrame - > linesize ) < 0 )
Fatal ( " Unable to convert raw format %u to target format %u at frame %d " , mCodecContext - > pix_fmt , imagePixFormat , frameCount ) ;
2013-03-17 07:45:21 +08:00
# else // HAVE_LIBSWSCALE
2016-04-04 22:11:48 +08:00
Fatal ( " You must compile ffmpeg with the --enable-swscale option to use RTSP cameras " ) ;
2013-03-17 07:45:21 +08:00
# endif // HAVE_LIBSWSCALE
2016-04-04 22:11:48 +08:00
frameCount + + ;
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
} /* frame complete */
2015-11-04 12:30:14 +08:00
# if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
2016-04-04 22:11:48 +08:00
av_packet_unref ( & packet ) ;
2015-11-04 12:30:14 +08:00
# else
2016-04-04 22:11:48 +08:00
av_free_packet ( & packet ) ;
2015-11-04 12:30:14 +08:00
# endif
2016-04-04 22:11:48 +08:00
} /* getFrame() */
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
if ( frameComplete )
return ( 0 ) ;
}
return ( 0 ) ;
2013-03-17 07:45:21 +08:00
}
int RemoteCameraRtsp : : PostCapture ( )
{
2016-04-04 22:11:48 +08:00
return ( 0 ) ;
2013-03-17 07:45:21 +08:00
}
# endif // HAVE_LIBAVFORMAT