From 1aa2bdc76fa2f70183d3fe616a1fbeb4ef99e5ba Mon Sep 17 00:00:00 2001 From: stan Date: Mon, 8 Mar 2004 10:33:19 +0000 Subject: [PATCH] Added timestamping to video generation. git-svn-id: http://svn.zoneminder.com/svn/zm/trunk@930 e3e1d417-86f3-4887-817a-d78f3d33393f --- src/zm_event.cpp | 77 ++++++++++++++++++++++++++++++++-------------- src/zm_monitor.cpp | 13 ++++++-- src/zm_mpeg.cpp | 10 ++++-- src/zm_mpeg.h | 2 +- 4 files changed, 74 insertions(+), 28 deletions(-) diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 1064046ee..1cdcfafe3 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -487,7 +487,8 @@ void Event::StreamMpeg( int event_id, const char *format, int bitrate, int rate, static char sql[BUFSIZ]; static char eventpath[PATH_MAX]; - sprintf( sql, "select M.Id, M.Name,max(F.Delta)-min(F.Delta) as Duration, count(F.Id) as Frames from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on F.EventId = E.Id where E.Id = %d group by F.EventId", event_id ); + //sprintf( sql, "select M.Id, M.Name,max(F.Delta)-min(F.Delta) as Duration, count(F.Id) as Frames from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on F.EventId = E.Id where E.Id = %d group by F.EventId", event_id ); + sprintf( sql, "select M.Id, M.Name, E.Length, E.Frames from Events as E inner join Monitors as M on E.MonitorId = M.Id where E.Id = %d", event_id ); if ( mysql_query( &dbconn, sql ) ) { Error(( "Can't run query: %s", mysql_error( &dbconn ) )); @@ -509,19 +510,23 @@ void Event::StreamMpeg( int event_id, const char *format, int bitrate, int rate, } sprintf( eventpath, "%s/%s/%s/%d", ZM_PATH_WEB, (const char *)config.Item( ZM_DIR_EVENTS ), dbrow[1], event_id ); - int fps = ((atoi(dbrow[3])/atoi(dbrow[2]))*rate)/ZM_RATE_SCALE; - if ( rate ) + int duration = atoi(dbrow[2]); + int frames = atoi(dbrow[3]); + + int min_fps = 1; + int max_fps = 30; + int base_fps = frames/duration; + int effective_fps = (base_fps*rate)/ZM_RATE_SCALE; + + int frame_mod = 1; + // Min frame repeat? + while( effective_fps > max_fps ) { - if ( fps <= 0 ) - fps = 1; - else if ( fps > 30 ) - fps = 30; + effective_fps /= 2; + frame_mod *= 2; } - else - { - fps = 30; - } - Info(( "Duration:%d, Frames:%d, FPS:%d", atoi(dbrow[2]), atoi(dbrow[3]), fps )); + + Info(( "Duration:%d, Frames:%d, BFPS:%d, EFPS:%d, FM:%d", atoi(dbrow[2]), atoi(dbrow[3]), base_fps, effective_fps, frame_mod )); mysql_free_result( result ); @@ -542,23 +547,49 @@ void Event::StreamMpeg( int event_id, const char *format, int bitrate, int rate, fprintf( stdout, "Content-type: video/x-ms-asf\r\n\r\n"); VideoStream *vid_stream = 0; - for( int i = 0; dbrow = mysql_fetch_row( result ); i++ ) + int id = 1, last_id = 0; + double base_delta, last_delta = 0.0L; + unsigned int delta_ms =0; + while( (id <= frames) && (dbrow = mysql_fetch_row( result )) ) { - static char filepath[PATH_MAX]; - sprintf( filepath, "%s/%03d-capture.jpg", eventpath, atoi(dbrow[0]) ); - - Image image( filepath ); - - if ( !vid_stream ) + if ( id == 1 ) { - vid_stream = new VideoStream( "pipe:", format, bitrate, fps, image.Colours(), (image.Width()*scale)/ZM_SCALE_SCALE, (image.Height()*scale)/ZM_SCALE_SCALE ); + base_delta = last_delta = atof(dbrow[2]); } - if ( scale != 100 ) + int db_id = atoi( dbrow[0] ); + double db_delta = atof( dbrow[2] )-base_delta; + while( db_id >= id ) { - image.Scale( scale ); + if ( (frame_mod == 1) || (((id-1)%frame_mod) == 0) ) + { + static char filepath[PATH_MAX]; + sprintf( filepath, "%s/%03d-capture.jpg", eventpath, id ); + + Image image( filepath ); + + if ( !vid_stream ) + { + vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), (image.Width()*scale)/ZM_SCALE_SCALE, (image.Height()*scale)/ZM_SCALE_SCALE ); + } + + if ( scale != 100 ) + { + image.Scale( scale ); + } + + double temp_delta = ((id-last_id)*(db_delta-last_delta))/(db_id-last_id); + delta_ms = (unsigned int)((last_delta+temp_delta)*1000); + if ( rate != ZM_RATE_SCALE ) + delta_ms = (delta_ms*ZM_RATE_SCALE)/rate; + double pts = vid_stream->EncodeFrame( image.Buffer(), image.Size(), true, delta_ms ); + + //Info(( "I:%d, DI:%d, LI:%d, DD:%lf, LD:%lf, TD:%lf, DM:%d, PTS:%lf", id, db_id, last_id, db_delta, last_delta, temp_delta, delta_ms, pts )); + } + id++; } - double pts = vid_stream->EncodeFrame( image.Buffer(), image.Size() ); + last_id = db_id; + last_delta = db_delta; } if ( mysql_errno( &dbconn ) ) { diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index ac65f1391..a6414627d 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1203,7 +1203,9 @@ void Monitor::StreamMpeg( const char *format, int bitrate, int scale, int buffer } } - int frame_count; + int frame_count = 0; + struct timeval base_time; + struct DeltaTimeval delta_time; while ( true ) { if ( feof( stdout ) || ferror( stdout ) ) @@ -1239,8 +1241,15 @@ void Monitor::StreamMpeg( const char *format, int bitrate, int scale, int buffer snap_image = &scaled_image; } - double pts = vid_stream.EncodeFrame( snap_image->Buffer(), snap_image->Size() ); + if ( !frame_count ) + { + base_time = *(snap->timestamp); + } + DELTA_TIMEVAL( delta_time, *(snap->timestamp), base_time, DT_PREC_3 ); + double pts = vid_stream.EncodeFrame( snap_image->Buffer(), snap_image->Size(), true, delta_time.delta ); + //Info(( "DTD:%d, PTS:%lf", delta_time.delta, pts )); } + frame_count++; usleep( 10000 ); } } diff --git a/src/zm_mpeg.cpp b/src/zm_mpeg.cpp index c7ab46055..3aa63f260 100644 --- a/src/zm_mpeg.cpp +++ b/src/zm_mpeg.cpp @@ -238,14 +238,16 @@ VideoStream::~VideoStream() av_free(ofc); } -double VideoStream::EncodeFrame( uint8_t *buffer, int buffer_size ) +double VideoStream::EncodeFrame( uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) { double pts = 0.0; /* compute current video time */ if (ost) + { pts = (double)ost->pts.val * ofc->pts_num / ofc->pts_den; - + //Info(( "PTS:%lf, PTSV:%lld, PTSN:%d(%lld), PTSD:%d(%lld)", pts, ost->pts.val, ofc->pts_num, ost->pts.num, ofc->pts_den, ost->pts.den )); + } //if (!ost || pts >= STREAM_DURATION) //break; @@ -277,6 +279,9 @@ double VideoStream::EncodeFrame( uint8_t *buffer, int buffer_size ) } else { + // Add a timestamp if supplied + if ( add_timestamp ) + ost->pts.val = timestamp; /* encode the image */ int out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, opicture_ptr); /* if zero size, it means the image was buffered */ @@ -291,6 +296,7 @@ double VideoStream::EncodeFrame( uint8_t *buffer, int buffer_size ) { Fatal(( "Error while writing video frame" )); } + return( pts ); } #endif // HAVE_LIBAVCODEC diff --git a/src/zm_mpeg.h b/src/zm_mpeg.h index 15d333ef6..e7a447bae 100644 --- a/src/zm_mpeg.h +++ b/src/zm_mpeg.h @@ -54,7 +54,7 @@ protected: public: VideoStream( const char *filename, const char *format, int bitrate, int frame_rate, int colours, int width, int height ); ~VideoStream(); - double EncodeFrame( uint8_t *buffer, int buffer_size ); + double EncodeFrame( uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); }; #endif // ZM_MPEG_H