diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 496b7da9c..b35c66316 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY) # Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc) -set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.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_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp) +set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.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_sdp.cpp zm_signal.cpp zm_stream.cpp zm_swscale.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp zm_storage.cpp) # A fix for cmake recompiling the source files for every target. add_library(zm STATIC ${ZM_BIN_SRC_FILES}) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index a1159fb4f..594901ad5 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -206,6 +206,7 @@ bool EventStream::loadEventData( int event_id ) { exit( mysql_errno( &dbconn ) ); } + mysql_free_result( result ); //for ( int i = 0; i < 250; i++ ) //{ //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); @@ -215,13 +216,13 @@ bool EventStream::loadEventData( int event_id ) { char filepath[PATH_MAX]; snprintf( filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file ); ffmpeg_input = new FFmpeg_Input(); - if ( ! ffmpeg_input->Open( filepath ) ) { + if ( 0 > ffmpeg_input->Open( filepath ) ) { + Warning("Unable to open ffmpeg_input %s/%s", event_data->path, event_data->video_file ); delete ffmpeg_input; ffmpeg_input = NULL; } } - mysql_free_result( result ); if ( forceEventChange || mode == MODE_ALL_GAPLESS ) { if ( replay_rate > 0 ) @@ -232,7 +233,7 @@ bool EventStream::loadEventData( int event_id ) { Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); return( true ); -} +} // bool EventStream::loadEventData( int event_id ) void EventStream::processCommand( const CmdMsg *msg ) { Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); @@ -599,11 +600,12 @@ bool EventStream::sendFrame( int delta_us ) { } else if ( monitor->GetOptSaveJPEGs() & 2 ) { snprintf( filepath, sizeof(filepath), Event::analyse_file_format, event_data->path, curr_frame_id ); if ( stat( filepath, &filestat ) < 0 ) { - Debug(1, "%s not found, dalling back to capture"); + Debug(1, "analyze file %s not found will try to stream from other", filepath); snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); + filepath[0] = 0; } - } else { + } else if ( ! ffmpeg_input ) { Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet"); return false; } @@ -628,7 +630,7 @@ bool EventStream::sendFrame( int delta_us ) { int img_buffer_size = 0; uint8_t *img_buffer = temp_img_buffer; - bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); + bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0]; fprintf( stdout, "--ZoneMinderFrame\r\n" ); @@ -650,9 +652,27 @@ bool EventStream::sendFrame( int delta_us ) { img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); #endif } else { - Image image( filepath ); + Image *image = NULL; - Image *send_image = prepareImage( &image ); + if ( filepath[0] ) { + image = new Image( filepath ); + } else if ( ffmpeg_input ) { + // Get the frame from the mp4 input + Debug(1,"Getting frame from ffmpeg"); + AVFrame *frame = ffmpeg_input->get_frame( ffmpeg_input->get_video_stream_id() ); + if ( frame ) { + image = new Image( frame ); + av_frame_free(&frame); + } else { + Error("Failed getting a frame."); + return false; + } + } else { + Error("Unable to get a frame"); + return false; + } + + Image *send_image = prepareImage( image ); switch( type ) { case STREAM_JPEG : @@ -676,6 +696,8 @@ bool EventStream::sendFrame( int delta_us ) { Fatal( "Unexpected frame type %d", type ); break; } + delete image; + image = NULL; } switch( type ) { @@ -694,7 +716,7 @@ bool EventStream::sendFrame( int delta_us ) { } - if(send_raw) { + if ( send_raw ) { #if HAVE_SENDFILE fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); if ( zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size ) { @@ -714,6 +736,7 @@ bool EventStream::sendFrame( int delta_us ) { #endif fclose(fdj); /* Close the file handle */ } else { + Debug(3, "Content length: %d", img_buffer_size ); fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { Error( "Unable to send stream frame: %s", strerror(errno) ); @@ -730,11 +753,14 @@ bool EventStream::sendFrame( int delta_us ) { void EventStream::runStream() { Event::Initialise(); + Debug(3, "Initialized"); openComms(); + Debug(3, "Comms open"); checkInitialised(); + Debug(3, "frame rate is: (%f)", (double)event_data->frame_count/event_data->duration ); updateFrameRate( (double)event_data->frame_count/event_data->duration ); if ( type == STREAM_JPEG ) @@ -757,6 +783,7 @@ void EventStream::runStream() { if ( step != 0 ) curr_frame_id += step; + // Detects when we hit end of event and will load the next event or previous event checkEventLoaded(); // Get current frame data diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 34a32bd61..4ba88d900 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -130,181 +130,6 @@ int av_dict_parse_string(AVDictionary **pm, const char *str, #endif #endif // HAVE_LIBAVUTIL -#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL -SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) { - Debug(4,"SWScale object created"); - - /* Allocate AVFrame for the input */ -#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - input_avframe = av_frame_alloc(); -#else - input_avframe = avcodec_alloc_frame(); -#endif - if(input_avframe == NULL) { - Fatal("Failed allocating AVFrame for the input"); - } - - /* Allocate AVFrame for the output */ -#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) - output_avframe = av_frame_alloc(); -#else - output_avframe = avcodec_alloc_frame(); -#endif - if(output_avframe == NULL) { - Fatal("Failed allocating AVFrame for the output"); - } -} - -SWScale::~SWScale() { - - /* Free up everything */ - av_frame_free( &input_avframe ); - //input_avframe = NULL; - - av_frame_free( &output_avframe ); - //output_avframe = NULL; - - if(swscale_ctx) { - sws_freeContext(swscale_ctx); - swscale_ctx = NULL; - } - - Debug(4,"SWScale object destroyed"); -} - -int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { - - /* Assign the defaults */ - default_input_pf = in_pf; - default_output_pf = out_pf; - default_width = width; - default_height = height; - - gotdefaults = true; - - return 0; -} - -int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { - /* Parameter checking */ - if(in_buffer == NULL || out_buffer == NULL) { - Error("NULL Input or output buffer"); - return -1; - } - // if(in_pf == 0 || out_pf == 0) { - // Error("Invalid input or output pixel formats"); - // return -2; - // } - if (!width || !height) { - Error("Invalid width or height"); - return -3; - } - -#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) - /* Warn if the input or output pixelformat is not supported */ - if(!sws_isSupportedInput(in_pf)) { - Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff)); - } - if(!sws_isSupportedOutput(out_pf)) { - Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff)); - } -#endif - - /* Check the buffer sizes */ -#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - size_t insize = av_image_get_buffer_size(in_pf, width, height,1); -#else - size_t insize = avpicture_get_size(in_pf, width, height); -#endif - if(insize != in_buffer_size) { - Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); - return -4; - } -#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - size_t outsize = av_image_get_buffer_size(out_pf, width, height,1); -#else - size_t outsize = avpicture_get_size(out_pf, width, height); -#endif - - if(outsize < out_buffer_size) { - Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); - return -5; - } - - /* Get the context */ - swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL ); - if(swscale_ctx == NULL) { - Error("Failed getting swscale context"); - return -6; - } - - /* Fill in the buffers */ -#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - if (av_image_fill_arrays(input_avframe->data, input_avframe->linesize, - (uint8_t*) in_buffer, in_pf, width, height, 1) <= 0) { -#else - if (avpicture_fill((AVPicture*) input_avframe, (uint8_t*) in_buffer, - in_pf, width, height) <= 0) { -#endif - Error("Failed filling input frame with input buffer"); - return -7; - } -#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize, - out_buffer, out_pf, width, height, 1) <= 0) { -#else - if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, width, - height) <= 0) { -#endif - Error("Failed filling output frame with output buffer"); - return -8; - } - - /* Do the conversion */ - if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) { - Error("swscale conversion failed"); - return -10; - } - - return 0; -} - -int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { - if(img->Width() != width) { - Error("Source image width differs. Source: %d Output: %d",img->Width(), width); - return -12; - } - - if(img->Height() != height) { - Error("Source image height differs. Source: %d Output: %d",img->Height(), height); - return -13; - } - - return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height); -} - -int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) { - - if(!gotdefaults) { - Error("Defaults are not set"); - return -24; - } - - return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); -} - -int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) { - - if(!gotdefaults) { - Error("Defaults are not set"); - return -24; - } - - return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); -} -#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL - - #endif // HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE #if HAVE_LIBAVUTIL @@ -560,5 +385,3 @@ bool is_audio_stream( AVStream * stream ) { } return false; } - - diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index cd328b6eb..10ad5260c 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -21,11 +21,8 @@ #define ZM_FFMPEG_H #include #include "zm.h" -#include "zm_image.h" -#ifdef __cplusplus extern "C" { -#endif // AVUTIL #if HAVE_LIBAVUTIL_AVUTIL_H @@ -207,31 +204,6 @@ void FFMPEGInit(); enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder); #endif // HAVE_LIBAVUTIL - -/* SWScale wrapper class to make our life easier and reduce code reuse */ -#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL -class SWScale { -public: - SWScale(); - ~SWScale(); - int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); - int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); - int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); - int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); - int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); - -protected: - bool gotdefaults; - struct SwsContext* swscale_ctx; - AVFrame* input_avframe; - AVFrame* output_avframe; - enum _AVPIXELFORMAT default_input_pf; - enum _AVPIXELFORMAT default_output_pf; - unsigned int default_width; - unsigned int default_height; -}; -#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL - #if !LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100) #define AV_CODEC_ID_NONE CODEC_ID_NONE #define AV_CODEC_ID_PCM_MULAW CODEC_ID_PCM_MULAW diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 259d65fa6..3af1e5880 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -290,7 +290,7 @@ int FfmpegCamera::Capture( Image &image ) { /* Request a writeable buffer of the target image */ directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - if(directbuffer == NULL) { + if ( directbuffer == NULL ) { Error("Failed requesting writeable buffer for the captured image."); return (-1); } @@ -304,17 +304,6 @@ int FfmpegCamera::Capture( Image &image ) { #endif #if HAVE_LIBSWSCALE - if ( mConvertContext == NULL ) { - mConvertContext = sws_getContext(mVideoCodecContext->width, - mVideoCodecContext->height, - mVideoCodecContext->pix_fmt, - width, height, imagePixFormat, - SWS_BICUBIC, NULL, NULL, NULL); - - if ( mConvertContext == NULL ) - Fatal( "Unable to create conversion context for %s", mPath.c_str() ); - } - if ( sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0 ) Fatal("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount); #else // HAVE_LIBSWSCALE diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index b47e573b7..7469a1cfb 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -7,6 +7,9 @@ FFmpeg_Input::FFmpeg_Input() { input_format_context = NULL; video_stream_id = -1; audio_stream_id = -1; + av_register_all(); + avcodec_register_all(); + } FFmpeg_Input::~FFmpeg_Input() { } @@ -16,7 +19,7 @@ int FFmpeg_Input::Open( const char *filepath ) { int error; /** Open the input file to read from it. */ - if ((error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0) { + if ( (error = avformat_open_input( &input_format_context, filepath, NULL, NULL)) < 0 ) { Error("Could not open input file '%s' (error '%s')\n", filepath, av_make_error_string(error).c_str() ); @@ -25,7 +28,7 @@ int FFmpeg_Input::Open( const char *filepath ) { } /** Get information on the input file (number of streams etc.). */ - if ((error = avformat_find_stream_info( input_format_context, NULL)) < 0) { + if ( (error = avformat_find_stream_info(input_format_context, NULL)) < 0 ) { Error( "Could not open find stream info (error '%s')\n", av_make_error_string(error).c_str() ); avformat_close_input(&input_format_context); @@ -34,15 +37,14 @@ int FFmpeg_Input::Open( const char *filepath ) { for ( unsigned int i = 0; i < input_format_context->nb_streams; i += 1 ) { if ( is_video_stream( input_format_context->streams[i] ) ) { + zm_dump_stream_format(input_format_context, i, 0, 0); if ( video_stream_id == -1 ) { video_stream_id = i; // if we break, then we won't find the audio stream - continue; } else { Warning( "Have another video stream." ); } - } - if ( is_audio_stream( input_format_context->streams[i] ) ) { + } else if ( is_audio_stream( input_format_context->streams[i] ) ) { if ( audio_stream_id == -1 ) { audio_stream_id = i; } else { @@ -56,15 +58,16 @@ int FFmpeg_Input::Open( const char *filepath ) { #else streams[i].context = input_format_context->streams[i]->codec; #endif + streams[i].frame_count = 0; - /** Find a decoder for the audio stream. */ - if (!(streams[i].codec = avcodec_find_decoder(input_format_context->streams[i]->codecpar->codec_id))) { + if ( !(streams[i].codec = avcodec_find_decoder(input_format_context->streams[i]->codecpar->codec_id)) ) { Error( "Could not find input codec\n"); avformat_close_input(&input_format_context); return AVERROR_EXIT; + } else { + Debug(1, "Using codec (%s) for stream %d", streams[i].codec->name, i ); } - /** Open the decoder for the audio stream to use it later. */ if ((error = avcodec_open2( streams[i].context, streams[i].codec, NULL)) < 0) { Error( "Could not open input codec (error '%s')\n", av_make_error_string(error).c_str() ); @@ -72,13 +75,104 @@ int FFmpeg_Input::Open( const char *filepath ) { avformat_close_input(&input_format_context); return error; } - } // end foreach stream + if ( video_stream_id == -1 ) Error( "Unable to locate video stream in %s", filepath ); if ( audio_stream_id == -1 ) Debug( 3, "Unable to locate audio stream in %s", filepath ); return 0; -} // end int FFmpeg::Open( const char * filepath ) +} // end int FFmpeg_Input::Open( const char * filepath ) +AVFrame *FFmpeg_Input::get_frame( int stream_id ) { + Debug(1, "Getting frame from stream %d", stream_id ); + + int frameComplete = false; + AVPacket packet; + av_init_packet( &packet ); + AVFrame *frame = zm_av_frame_alloc(); + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + + while ( !frameComplete ) { + int ret = av_read_frame( input_format_context, &packet ); + if ( ret < 0 ) { + av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE); + if ( + // Check if EOF. + (ret == AVERROR_EOF || (input_format_context->pb && input_format_context->pb->eof_reached)) || + // Check for Connection failure. + (ret == -110) + ) { + Info( "av_read_frame returned %s.", errbuf ); + return NULL; + } + Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf ); + return NULL; + } + + if ( (stream_id < 0 ) || ( packet.stream_index == stream_id ) ) { + Debug(1,"Packet is for our stream (%d)", packet.stream_index ); + + AVCodecContext *context = streams[packet.stream_index].context; + +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + ret = avcodec_send_packet( context, &packet ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); + zm_av_packet_unref( &packet ); + continue; + } else { + Debug(1, "Success getting a packet"); + } + +#if HAVE_AVUTIL_HWCONTEXT_H + if ( hwaccel ) { + ret = avcodec_receive_frame( context, hwFrame ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to receive frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); + zm_av_packet_unref( &packet ); + continue; + } + ret = av_hwframe_transfer_data(frame, hwFrame, 0); + if (ret < 0) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to transfer frame at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); + zm_av_packet_unref( &packet ); + continue; + } + } else { +#endif + Debug(1,"Getting a frame?"); + ret = avcodec_receive_frame( context, frame ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to send packet at frame %d: %s, continuing", streams[packet.stream_index].frame_count, errbuf ); + zm_av_packet_unref( &packet ); + continue; + } + +#if HAVE_AVUTIL_HWCONTEXT_H + } +#endif + + frameComplete = 1; +# else + ret = zm_avcodec_decode_video( streams[packet.stream_index].context, frame, &frameComplete, &packet ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf ); + zm_av_packet_unref( &packet ); + continue; + } +#endif + } // end if it's the right stream + + zm_av_packet_unref( &packet ); + + } // end while ! frameComplete + return frame; + +} // end AVFrame *FFmpeg_Input::get_frame diff --git a/src/zm_ffmpeg_input.h b/src/zm_ffmpeg_input.h index 9a2147bd0..3e12f45cc 100644 --- a/src/zm_ffmpeg_input.h +++ b/src/zm_ffmpeg_input.h @@ -21,11 +21,19 @@ class FFmpeg_Input { int Open( const char *filename ); int Close(); + AVFrame *get_frame( int stream_id=-1 ); + int get_video_stream_id() { + return video_stream_id; + } + int get_audio_stream_id() { + return audio_stream_id; + } private: typedef struct { AVCodecContext *context; AVCodec *codec; + int frame_count; } stream; stream streams[2]; diff --git a/src/zm_image.cpp b/src/zm_image.cpp index cab8a225d..9ff23a217 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -22,6 +22,7 @@ #include "zm_image.h" #include "zm_utils.h" #include "zm_rgb.h" +#include "zm_ffmpeg.h" #include #include @@ -128,6 +129,46 @@ Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uin text[0] = '\0'; } +Image::Image( const AVFrame *frame ) { + AVFrame *dest_frame = zm_av_frame_alloc(); + + width = frame->width; + height = frame->height; + pixels = width*height; + colours = ZM_COLOUR_RGB32; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + size = pixels*colours; + buffer = 0; + holdbuffer = 0; + AllocImgBuffer(size); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(dest_frame->data, dest_frame->linesize, + buffer, AV_PIX_FMT_RGBA, width, height, 1); +#else + avpicture_fill( (AVPicture *)mFrame, buffer, + AV_PIX_FMT_RGBA, width, height); +#endif + +#if HAVE_LIBSWSCALE + struct SwsContext *mConvertContext = sws_getContext( + width, + height, + (AVPixelFormat)frame->format, + width, height, + AV_PIX_FMT_RGBA, SWS_BICUBIC, NULL, + NULL, NULL); + if ( mConvertContext == NULL ) + Fatal( "Unable to create conversion context" ); + + if ( sws_scale(mConvertContext, frame->data, frame->linesize, 0, frame->height, dest_frame->data, dest_frame->linesize) < 0 ) + Fatal("Unable to convert raw format %u to target format %u", frame->format, AV_PIX_FMT_RGBA); +#else // HAVE_LIBSWSCALE + Fatal("You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras"); +#endif // HAVE_LIBSWSCALE + av_frame_free( &dest_frame ); +} + Image::Image( const Image &p_image ) { if ( !initialised ) Initialise(); diff --git a/src/zm_image.h b/src/zm_image.h index e68e0ff70..934d2a682 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -21,8 +21,7 @@ #define ZM_IMAGE_H #include "zm.h" -extern "C" -{ +extern "C" { #include "zm_jpeg.h" } #include "zm_rgb.h" @@ -32,6 +31,9 @@ extern "C" #include "zm_mem_utils.h" #include "zm_utils.h" +class Image; +#include "zm_ffmpeg.h" + #include #if HAVE_ZLIB_H @@ -148,12 +150,12 @@ protected: int holdbuffer; /* Hold the buffer instead of replacing it with new one */ char text[1024]; - public: Image(); Image( const char *filename ); Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0); Image( const Image &p_image ); + Image( const AVFrame *frame ); ~Image(); static void Initialise(); static void Deinitialise(); diff --git a/src/zm_packet.cpp b/src/zm_packet.cpp index 8fbb65cb8..7c08d4158 100644 --- a/src/zm_packet.cpp +++ b/src/zm_packet.cpp @@ -20,6 +20,8 @@ #include "zm_packet.h" #include "zm_ffmpeg.h" +#include + using namespace std; ZMPacket::ZMPacket( AVPacket *p ) { diff --git a/src/zm_packetqueue.cpp b/src/zm_packetqueue.cpp index 497809342..f55a7a680 100644 --- a/src/zm_packetqueue.cpp +++ b/src/zm_packetqueue.cpp @@ -19,12 +19,11 @@ #include "zm_packetqueue.h" #include "zm_ffmpeg.h" +#include #define VIDEO_QUEUESIZE 200 #define AUDIO_QUEUESIZE 50 -using namespace std; - zm_packetqueue::zm_packetqueue(){ } @@ -68,7 +67,7 @@ unsigned int zm_packetqueue::clearQueue( unsigned int frames_to_keep, int stream return 0; } - list::reverse_iterator it; + std::list::reverse_iterator it; ZMPacket *packet = NULL; for ( it = pktQueue.rbegin(); it != pktQueue.rend() && frames_to_keep; ++it ) { @@ -121,7 +120,7 @@ void zm_packetqueue::clear_unwanted_packets( timeval *recording_started, int mVi // Step 1 - find keyframe < recording_started. // Step 2 - pop packets until we get to the packet in step 2 - list::reverse_iterator it; + std::list::reverse_iterator it; Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId ); for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) { diff --git a/src/zm_video.h b/src/zm_video.h index 936adfeb8..2b71f12df 100644 --- a/src/zm_video.h +++ b/src/zm_video.h @@ -21,6 +21,7 @@ #include "zm_utils.h" #include "zm_ffmpeg.h" #include "zm_buffer.h" +#include "zm_swscale.h" /* #define HAVE_LIBX264 1 diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index af5c187df..b2842e711 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -39,8 +39,8 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, audio_in_stream = p_audio_in_stream; #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - video_in_ctx = avcodec_alloc_ctx3(NULL); - avcodec_parameters_to_ctx(video_in_ctx, + video_in_ctx = avcodec_alloc_context3(NULL); + avcodec_parameters_to_context(video_in_ctx, video_in_stream->codecpar); // zm_dump_codecpar( video_in_stream->codecpar ); #else @@ -53,7 +53,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, Info("Opening video storage stream %s format: %s", filename, format); - ret = avformat_alloc_out_ctx2(&oc, NULL, NULL, filename); + ret = avformat_alloc_output_context2(&oc, NULL, NULL, filename); if (ret < 0) { Warning( "Could not create video storage stream %s as no out ctx" @@ -65,7 +65,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, // Couldn't deduce format from filename, trying from format name if (!oc) { - avformat_alloc_out_ctx2(&oc, NULL, format, filename); + avformat_alloc_output_context2(&oc, NULL, format, filename); if (!oc) { Fatal( "Could not create video storage stream %s as no out ctx" @@ -87,10 +87,10 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // Since we are not re-encoding, all we have to do is copy the parameters - video_out_ctx = avcodec_alloc_ctx3(NULL); + video_out_ctx = avcodec_alloc_context3(NULL); // Copy params from instream to ctx - ret = avcodec_parameters_to_ctx(video_out_ctx, + ret = avcodec_parameters_to_context(video_out_ctx, video_in_stream->codecpar); if (ret < 0) { Error("Could not initialize ctx parameteres"); @@ -113,7 +113,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, } // Now copy them to the out stream - ret = avcodec_parameters_from_ctx(video_out_stream->codecpar, + ret = avcodec_parameters_from_context(video_out_stream->codecpar, video_out_ctx); if (ret < 0) { Error("Could not initialize stream parameteres"); @@ -202,8 +202,8 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, Debug(3, "Have audio stream"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - audio_in_ctx = avcodec_alloc_ctx3(NULL); - ret = avcodec_parameters_to_ctx(audio_in_ctx, + audio_in_ctx = avcodec_alloc_context3(NULL); + ret = avcodec_parameters_to_context(audio_in_ctx, audio_in_stream->codecpar); #else audio_in_ctx = audio_in_stream->codec; @@ -222,7 +222,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, Debug(3, "Got AAC"); audio_out_stream = - avformat_new_stream(oc, reinterpret_castaudio_in_ctx->codec); + avformat_new_stream(oc, reinterpret_cast(audio_in_ctx->codec)); if (!audio_out_stream) { Error("Unable to create audio out stream\n"); audio_out_stream = NULL; @@ -230,15 +230,15 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, Debug(2, "setting parameters"); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - audio_out_ctx = avcodec_alloc_ctx3(audio_out_codec); + audio_out_ctx = avcodec_alloc_context3(audio_out_codec); // Copy params from instream to ctx - ret = avcodec_parameters_to_ctx(audio_out_ctx, + ret = avcodec_parameters_to_context(audio_out_ctx, audio_in_stream->codecpar); if (ret < 0) { Error("Unable to copy audio params to ctx %s\n", av_make_error_string(ret).c_str()); } - ret = avcodec_parameters_from_ctx(audio_out_stream->codecpar, + ret = avcodec_parameters_from_context(audio_out_stream->codecpar, audio_out_ctx); if (ret < 0) { Error("Unable to copy audio params to stream %s\n", @@ -430,7 +430,7 @@ VideoStore::~VideoStore() { } /* free the stream */ - avformat_free_ctx(oc); + avformat_free_context(oc); } bool VideoStore::setup_resampler() { @@ -460,7 +460,7 @@ bool VideoStore::setup_resampler() { Debug(2, "Have audio out codec"); // audio_out_ctx = audio_out_stream->codec; - audio_out_ctx = avcodec_alloc_ctx3(audio_out_codec); + audio_out_ctx = avcodec_alloc_context3(audio_out_codec); if (!audio_out_ctx) { Error("could not allocate codec ctx for AAC\n"); @@ -512,7 +512,7 @@ bool VideoStore::setup_resampler() { audio_out_stream = avformat_new_stream(oc, audio_out_codec); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) - ret = avcodec_parameters_from_ctx(audio_out_stream->codecpar, + ret = avcodec_parameters_from_context(audio_out_stream->codecpar, audio_out_ctx); if (ret < 0) { Error("Could not initialize stream parameteres"); @@ -554,7 +554,7 @@ bool VideoStore::setup_resampler() { } // Setup the audio resampler - resample_ctx = avresample_alloc_ctx(); + resample_ctx = avresample_alloc_context(); if (!resample_ctx) { Error("Could not allocate resample ctx\n"); return false; diff --git a/src/zm_videostore.h b/src/zm_videostore.h index c33b4c2ff..014731f40 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -76,7 +76,7 @@ public: const char *format_in, AVStream *video_in_stream, AVStream *audio_in_stream, - int64_t nStartTime,xi + int64_t nStartTime, Monitor * p_monitor); ~VideoStore(); diff --git a/src/zms.cpp b/src/zms.cpp index 3b1a6dfd1..2161a822e 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -52,7 +52,7 @@ int main( int argc, const char *argv[] ) { srand( getpid() * time( 0 ) ); - enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR; + enum { ZMS_UNKNOWN, ZMS_MONITOR, ZMS_EVENT } source = ZMS_UNKNOWN; enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG; char format[32] = ""; int monitor_id = 0; @@ -125,8 +125,10 @@ int main( int argc, const char *argv[] ) { event_time = atoi( value ); } else if ( !strcmp( name, "event" ) ) { event_id = strtoull( value, (char **)NULL, 10 ); + source = ZMS_EVENT; } else if ( !strcmp( name, "frame" ) ) { frame_id = strtoull( value, (char **)NULL, 10 ); + source = ZMS_EVENT; } else if ( !strcmp( name, "scale" ) ) { scale = atoi( value ); } else if ( !strcmp( name, "rate" ) ) { @@ -264,6 +266,7 @@ int main( int argc, const char *argv[] ) { if ( ! event_id ) { Fatal( "Can't view an event without specifying an event_id." ); } + Debug(3,"Doing event stream scale(%d)", scale ); EventStream stream; stream.setStreamScale( scale ); stream.setStreamReplayRate( rate ); @@ -273,6 +276,7 @@ int main( int argc, const char *argv[] ) { if ( monitor_id && event_time ) { stream.setStreamStart( monitor_id, event_time ); } else { + Debug(3, "Setting stream start to frame (%d)", frame_id); stream.setStreamStart( event_id, frame_id ); } if ( mode == ZMS_JPEG ) { @@ -291,6 +295,8 @@ int main( int argc, const char *argv[] ) { #endif // HAVE_LIBAVCODEC } // end if jpeg or mpeg stream.runStream(); + } else { + Error("Neither a monitor or event was specified."); } // end if monitor or event logTerm(); diff --git a/web/includes/Event.php b/web/includes/Event.php index 7bef5498f..a9102723f 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -131,7 +131,7 @@ class Event { } # end Event->delete public function getStreamSrc( $args=array(), $querySep='&' ) { - if ( $this->{'DefaultVideo'} ) { + if ( $this->{'DefaultVideo'} and $args['mode'] != 'jpeg' ) { return ( ZM_BASE_PATH != '/' ? ZM_BASE_PATH : '' ).'/index.php?view=view_video&eid='.$this->{'Id'}; }