add ffmpeg decoding of a .mp4 to get the frames
parent
5c13ae26e3
commit
a81ff85fbb
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "zm_packet.h"
|
||||
#include "zm_ffmpeg.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ZMPacket::ZMPacket( AVPacket *p ) {
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "zm_utils.h"
|
||||
#include "zm_ffmpeg.h"
|
||||
#include "zm_buffer.h"
|
||||
#include "zm_swscale.h"
|
||||
|
||||
/*
|
||||
#define HAVE_LIBX264 1
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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'};
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue