diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 9d037b46a..297ba1af4 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -587,7 +587,9 @@ bool EventStream::sendFrame( int delta_us ) { static char filepath[PATH_MAX]; static struct stat filestat; - FILE *fdj = NULL; + AVPacket *packet = NULL; // If loading from a container format, then this is the packet + Image *image = NULL; // Decoded image if not doing passthrough + Image *send_image = NULL; //Output converted image // This needs to be abstracted. If we are saving jpgs, then load the capture file. If we are only saving analysis frames, then send that. if ( monitor->GetOptSaveJPEGs() & 1 ) { @@ -598,18 +600,56 @@ bool EventStream::sendFrame( int delta_us ) { Debug(1, "%s not found, dalling back to capture"); snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); } - - } else { + } else if ( ! ffmpeg_input ) { Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet"); return false; } -#if HAVE_LIBAVCODEC + // Whether we need to decode, scale, etc. + bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); + // Or if input type != output type + if ( type != STREAM_JPEG ) + send_raw = false; + + if ( ffmpeg_input ) { + packet = ffmpeg_input->read_packet(); + + /* If output is jpeg, then must keep going until we get a video frame. + */ + if ( packet ) { + AVFrame *frame = ffmpeg_input->decode_packet( packet ); + if ( frame ) { + uint8_t* directbuffer; + image = new Image(); + + /* Request a writeable buffer of the target image */ + directbuffer = image->WriteBuffer( frame->width, frame->height, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); + if ( directbuffer == NULL ) { + Error("Failed requesting writeable buffer for the captured image."); + zm_av_packet_unref( packet ); + return false; + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(frame->data, frame->linesize, directbuffer, (AVPixelFormat)frame->format, frame->width, frame->height, 1); +#else + avpicture_fill( (AVPicture *)frame, directbuffer, frame->format, frame->width, frame->height); +#endif + + } // end if frame + } // end if packet + send_raw = false; + } else { + image = new Image( filepath ); + } + // Does conversions, scaling, etc. + if ( ! send_raw ) { + send_image = prepareImage( image ); + } else { + send_image = image; + } + if ( type == STREAM_MPEG ) { - Image image( filepath ); - - Image *send_image = prepareImage( &image ); - +#if HAVE_LIBAVCODEC if ( !vid_stream ) { vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); @@ -619,107 +659,46 @@ bool EventStream::sendFrame( int delta_us ) { } else #endif // HAVE_LIBAVCODEC { - static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; int img_buffer_size = 0; uint8_t *img_buffer = temp_img_buffer; - bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - - if ( type != STREAM_JPEG ) - send_raw = false; - if ( send_raw ) { - fdj = fopen( filepath, "rb" ); - if ( !fdj ) { - Error( "Can't open %s: %s", filepath, strerror(errno) ); + if ( ! send_file( filepath ) ) { + Error( "Can't send %s: %s", filepath, strerror(errno) ); return( false ); } -#if HAVE_SENDFILE - if( fstat(fileno(fdj),&filestat) < 0 ) { - Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); - return( false ); - } -#else - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); -#endif } else { - Image image( filepath ); - - Image *send_image = prepareImage( &image ); switch( type ) { case STREAM_JPEG : send_image->EncodeJpeg( img_buffer, &img_buffer_size ); + fprintf( stdout, "Content-Type: image/jpeg\r\n" ); break; case STREAM_ZIP : -#if HAVE_ZLIB_H unsigned long zip_buffer_size; send_image->Zip( img_buffer, &zip_buffer_size ); img_buffer_size = zip_buffer_size; + fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); break; -#else - Error("zlib is required for zipped images. Falling back to raw image"); - type = STREAM_RAW; -#endif // HAVE_ZLIB_H case STREAM_RAW : img_buffer = (uint8_t*)(send_image->Buffer()); img_buffer_size = send_image->Size(); + fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); break; default: Fatal( "Unexpected frame type %d", type ); break; - } - } + } // end switch type + send_buffer( img_buffer, img_buffer_size ); - switch( type ) { - case STREAM_JPEG : - fprintf( stdout, "Content-Type: image/jpeg\r\n" ); - break; - case STREAM_RAW : - fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); - break; - case STREAM_ZIP : - fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); - break; - default : - Fatal( "Unexpected frame type %d", type ); - break; - } + } // endif send_raw or not + } // mpeg_output or jpeg - - 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 ) { - /* sendfile() failed, use standard way instead */ - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { - Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); - } - } -#else - 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 raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); - } -#endif - fclose(fdj); /* Close the file handle */ - } else { - 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) ); - return( false ); - } - } - - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); - } + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); last_frame_sent = TV_2_FLOAT( now ); return( true ); } @@ -839,4 +818,55 @@ void EventStream::runStream() { #endif // HAVE_LIBAVCODEC closeComms(); +} // end void EventStream::runStream() + +bool EventStream::send_file( const char * filepath ) { + static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; + + int img_buffer_size = 0; + uint8_t *img_buffer = temp_img_buffer; + + FILE *fdj = NULL; + fdj = fopen( filepath, "rb" ); + if ( !fdj ) { + Error( "Can't open %s: %s", filepath, strerror(errno) ); + return false; + } + bool sent = false; + bool size_size = false; + +#if HAVE_SENDFILE + static struct stat filestat; + if( fstat(fileno(fdj),&filestat) < 0 ) { + Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); + return false; + } + fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); + size_sent = true; + + if ( ! zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size ) { + sent = true; + } +#endif + if ( ! sent ) { + img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); + if ( ! size_sent ) + 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 raw frame %u: %s",curr_frame_id,strerror(errno)); + return false; + } + } + fclose(fdj); /* Close the file handle */ + + return true; +} + +bool EventStream::send_buffer( uint8_t* buffer, int size ) { + fprintf( stdout, "Content-Length: %d\r\n\r\n", size ); + if ( fwrite( buffer, size, 1, stdout ) != 1 ) { + Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno)); + return false; + } + return true; } diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 07e161214..66f9cde64 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -122,6 +122,8 @@ class EventStream : public StreamBase { private: AVCodecContext *input_codec_context; AVCodec *input_codec; + bool send_file( const char *file_path ); + bool send_buffer( uint8_t * buffer, int size ); }; #endif // ZM_EVENTSTREAM_H diff --git a/src/zm_ffmpeg_input.cpp b/src/zm_ffmpeg_input.cpp index b47e573b7..6f8c050e4 100644 --- a/src/zm_ffmpeg_input.cpp +++ b/src/zm_ffmpeg_input.cpp @@ -8,6 +8,7 @@ FFmpeg_Input::FFmpeg_Input() { video_stream_id = -1; audio_stream_id = -1; } + FFmpeg_Input::~FFmpeg_Input() { } @@ -82,3 +83,74 @@ int FFmpeg_Input::Open( const char *filepath ) { return 0; } // end int FFmpeg::Open( const char * filepath ) +AVPacket * FFmpeg_Input::read_packet() { + AVPacket *packet = new AVPacket(); + if ( 0 > read_packet( packet ) ) { + delete packet; + packet = NULL; + } + return packet; +} + +/* I am reserving a 0 return value to mean no error, but also no success */ + +int FFmpeg_Input::read_packet( AVPacket *packet ) { + int avResult = av_read_frame( input_format_context, packet ); + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + if ( avResult < 0 ) { + av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE); + if ( + // Check if EOF. + (avResult == AVERROR_EOF || (input_format_context->pb && input_format_context->pb->eof_reached)) || + // Check for Connection failure. + (avResult == -110) + ) { + Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf ); + //ReopenFfmpeg(); + } + + Error( "Unable to read packet from stream %d: error %d \"%s\".", packet->stream_index, avResult, errbuf ); + return( -1 ); + } + Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet->stream_index, packet->pts, packet->dts ); + return 1; +} + +AVFrame *FFmpeg_Input::decode_packet( AVPacket *packet ) { + AVFrame *frame = new AVFrame(); + if ( 0 >= decode_packet( packet, frame ) ) { + delete frame; + frame = NULL; + } + return frame; +} + +int FFmpeg_Input::decode_packet( AVPacket *packet, AVFrame *frame ) { +/* Decoding may take multiple packets. So a return value of 0 means no error, but no frame yet. */ + char errbuf[AV_ERROR_MAX_STRING_SIZE]; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) + int ret = avcodec_send_packet( streams[packet->stream_index].context, packet ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to send packet at frame: %s, continuing", errbuf ); + zm_av_packet_unref( packet ); + return ret; + } + ret = avcodec_receive_frame( streams[packet->stream_index].context, frame ); + if ( ret < 0 ) { + av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE ); + Error( "Unable to send packet at frame: %s, continuing", errbuf ); + return 0; + } +# else + int frameComplete; + 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: %s, continuing", errbuf ); + zm_av_packet_unref( packet ); + return 0; + } +#endif + return 1; +} diff --git a/src/zm_ffmpeg_input.h b/src/zm_ffmpeg_input.h index 9a2147bd0..9beceb61a 100644 --- a/src/zm_ffmpeg_input.h +++ b/src/zm_ffmpeg_input.h @@ -22,6 +22,11 @@ class FFmpeg_Input { int Open( const char *filename ); int Close(); + AVPacket * read_packet(); + int read_packet( AVPacket *packet ); + AVFrame * decode_packet( AVPacket *packet ); + int decode_packet( AVPacket *packet, AVFrame *frame ); + private: typedef struct { AVCodecContext *context; diff --git a/src/zm_stream.h b/src/zm_stream.h index aad9bbfaa..5c149cbeb 100644 --- a/src/zm_stream.h +++ b/src/zm_stream.h @@ -145,6 +145,13 @@ public: void setStreamType( StreamType p_type ) { type = p_type; +#if ! HAVE_ZLIB_H + if ( type == STREAM_ZIP ) { + Error("zlib is required for zipped images. Falling back to raw image"); + type = STREAM_RAW; + } +#endif + } void setStreamFormat( const char *p_format ) { format = p_format;