add ffmpeg decoding of a .mp4 to get the frames

pull/2077/head
Isaac Connor 2017-08-23 15:05:44 -04:00
parent 5c13ae26e3
commit a81ff85fbb
16 changed files with 228 additions and 264 deletions

View File

@ -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})

View File

@ -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

View File

@ -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;
}

View File

@ -21,11 +21,8 @@
#define ZM_FFMPEG_H
#include <stdint.h>
#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

View File

@ -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

View File

@ -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

View File

@ -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];

View File

@ -22,6 +22,7 @@
#include "zm_image.h"
#include "zm_utils.h"
#include "zm_rgb.h"
#include "zm_ffmpeg.h"
#include <sys/stat.h>
#include <errno.h>
@ -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();

View File

@ -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 <errno.h>
#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();

View File

@ -20,6 +20,8 @@
#include "zm_packet.h"
#include "zm_ffmpeg.h"
#include <sys/time.h>
using namespace std;
ZMPacket::ZMPacket( AVPacket *p ) {

View File

@ -19,12 +19,11 @@
#include "zm_packetqueue.h"
#include "zm_ffmpeg.h"
#include <sys/time.h>
#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<ZMPacket *>::reverse_iterator it;
std::list<ZMPacket *>::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<ZMPacket *>::reverse_iterator it;
std::list<ZMPacket *>::reverse_iterator it;
Debug(3, "Looking for keyframe after start recording stream id (%d)", mVideoStreamId );
for ( it = pktQueue.rbegin(); it != pktQueue.rend(); ++ it ) {

View File

@ -21,6 +21,7 @@
#include "zm_utils.h"
#include "zm_ffmpeg.h"
#include "zm_buffer.h"
#include "zm_swscale.h"
/*
#define HAVE_LIBX264 1

View File

@ -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_cast<AVCodec *>audio_in_ctx->codec);
avformat_new_stream(oc, reinterpret_cast<const AVCodec *>(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;

View File

@ -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();

View File

@ -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();

View File

@ -131,7 +131,7 @@ class Event {
} # end Event->delete
public function getStreamSrc( $args=array(), $querySep='&amp;' ) {
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'};
}