more work
This commit is contained in:
parent
24a8915699
commit
9fa48954db
|
@ -587,7 +587,9 @@ bool EventStream::sendFrame( int delta_us ) {
|
||||||
|
|
||||||
static char filepath[PATH_MAX];
|
static char filepath[PATH_MAX];
|
||||||
static struct stat filestat;
|
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.
|
// 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 ) {
|
if ( monitor->GetOptSaveJPEGs() & 1 ) {
|
||||||
|
@ -598,18 +600,56 @@ bool EventStream::sendFrame( int delta_us ) {
|
||||||
Debug(1, "%s not found, dalling back to capture");
|
Debug(1, "%s not found, dalling back to capture");
|
||||||
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
|
snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id );
|
||||||
}
|
}
|
||||||
|
} else if ( ! ffmpeg_input ) {
|
||||||
} else {
|
|
||||||
Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet");
|
Fatal("JPEGS not saved.zms is not capable of streaming jpegs from mp4 yet");
|
||||||
return false;
|
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 ) {
|
if ( type == STREAM_MPEG ) {
|
||||||
Image image( filepath );
|
#if HAVE_LIBAVCODEC
|
||||||
|
|
||||||
Image *send_image = prepareImage( &image );
|
|
||||||
|
|
||||||
if ( !vid_stream ) {
|
if ( !vid_stream ) {
|
||||||
vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() );
|
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() );
|
fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() );
|
||||||
|
@ -619,107 +659,46 @@ bool EventStream::sendFrame( int delta_us ) {
|
||||||
} else
|
} else
|
||||||
#endif // HAVE_LIBAVCODEC
|
#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;
|
int img_buffer_size = 0;
|
||||||
uint8_t *img_buffer = temp_img_buffer;
|
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 ) {
|
if ( send_raw ) {
|
||||||
fdj = fopen( filepath, "rb" );
|
if ( ! send_file( filepath ) ) {
|
||||||
if ( !fdj ) {
|
Error( "Can't send %s: %s", filepath, strerror(errno) );
|
||||||
Error( "Can't open %s: %s", filepath, strerror(errno) );
|
|
||||||
return( false );
|
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 {
|
} else {
|
||||||
Image image( filepath );
|
|
||||||
|
|
||||||
Image *send_image = prepareImage( &image );
|
|
||||||
|
|
||||||
switch( type ) {
|
switch( type ) {
|
||||||
case STREAM_JPEG :
|
case STREAM_JPEG :
|
||||||
send_image->EncodeJpeg( img_buffer, &img_buffer_size );
|
send_image->EncodeJpeg( img_buffer, &img_buffer_size );
|
||||||
|
fprintf( stdout, "Content-Type: image/jpeg\r\n" );
|
||||||
break;
|
break;
|
||||||
case STREAM_ZIP :
|
case STREAM_ZIP :
|
||||||
#if HAVE_ZLIB_H
|
|
||||||
unsigned long zip_buffer_size;
|
unsigned long zip_buffer_size;
|
||||||
send_image->Zip( img_buffer, &zip_buffer_size );
|
send_image->Zip( img_buffer, &zip_buffer_size );
|
||||||
img_buffer_size = zip_buffer_size;
|
img_buffer_size = zip_buffer_size;
|
||||||
|
fprintf( stdout, "Content-Type: image/x-rgbz\r\n" );
|
||||||
break;
|
break;
|
||||||
#else
|
|
||||||
Error("zlib is required for zipped images. Falling back to raw image");
|
|
||||||
type = STREAM_RAW;
|
|
||||||
#endif // HAVE_ZLIB_H
|
|
||||||
case STREAM_RAW :
|
case STREAM_RAW :
|
||||||
img_buffer = (uint8_t*)(send_image->Buffer());
|
img_buffer = (uint8_t*)(send_image->Buffer());
|
||||||
img_buffer_size = send_image->Size();
|
img_buffer_size = send_image->Size();
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Fatal( "Unexpected frame type %d", type );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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" );
|
fprintf( stdout, "Content-Type: image/x-rgb\r\n" );
|
||||||
break;
|
break;
|
||||||
case STREAM_ZIP :
|
|
||||||
fprintf( stdout, "Content-Type: image/x-rgbz\r\n" );
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
Fatal( "Unexpected frame type %d", type );
|
Fatal( "Unexpected frame type %d", type );
|
||||||
break;
|
break;
|
||||||
}
|
} // end switch type
|
||||||
|
send_buffer( img_buffer, img_buffer_size );
|
||||||
|
|
||||||
|
} // endif send_raw or not
|
||||||
if(send_raw) {
|
} // mpeg_output or jpeg
|
||||||
#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" );
|
fprintf( stdout, "\r\n\r\n" );
|
||||||
fflush( stdout );
|
fflush( stdout );
|
||||||
}
|
|
||||||
last_frame_sent = TV_2_FLOAT( now );
|
last_frame_sent = TV_2_FLOAT( now );
|
||||||
return( true );
|
return( true );
|
||||||
}
|
}
|
||||||
|
@ -839,4 +818,55 @@ void EventStream::runStream() {
|
||||||
#endif // HAVE_LIBAVCODEC
|
#endif // HAVE_LIBAVCODEC
|
||||||
|
|
||||||
closeComms();
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,8 @@ class EventStream : public StreamBase {
|
||||||
private:
|
private:
|
||||||
AVCodecContext *input_codec_context;
|
AVCodecContext *input_codec_context;
|
||||||
AVCodec *input_codec;
|
AVCodec *input_codec;
|
||||||
|
bool send_file( const char *file_path );
|
||||||
|
bool send_buffer( uint8_t * buffer, int size );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ZM_EVENTSTREAM_H
|
#endif // ZM_EVENTSTREAM_H
|
||||||
|
|
|
@ -8,6 +8,7 @@ FFmpeg_Input::FFmpeg_Input() {
|
||||||
video_stream_id = -1;
|
video_stream_id = -1;
|
||||||
audio_stream_id = -1;
|
audio_stream_id = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFmpeg_Input::~FFmpeg_Input() {
|
FFmpeg_Input::~FFmpeg_Input() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,3 +83,74 @@ int FFmpeg_Input::Open( const char *filepath ) {
|
||||||
return 0;
|
return 0;
|
||||||
} // end int FFmpeg::Open( const char * filepath )
|
} // 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;
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,11 @@ class FFmpeg_Input {
|
||||||
int Open( const char *filename );
|
int Open( const char *filename );
|
||||||
int Close();
|
int Close();
|
||||||
|
|
||||||
|
AVPacket * read_packet();
|
||||||
|
int read_packet( AVPacket *packet );
|
||||||
|
AVFrame * decode_packet( AVPacket *packet );
|
||||||
|
int decode_packet( AVPacket *packet, AVFrame *frame );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef struct {
|
typedef struct {
|
||||||
AVCodecContext *context;
|
AVCodecContext *context;
|
||||||
|
|
|
@ -145,6 +145,13 @@ public:
|
||||||
|
|
||||||
void setStreamType( StreamType p_type ) {
|
void setStreamType( StreamType p_type ) {
|
||||||
type = 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 ) {
|
void setStreamFormat( const char *p_format ) {
|
||||||
format = p_format;
|
format = p_format;
|
||||||
|
|
Loading…
Reference in New Issue