Merge branch 'feature-h264-videostorage'
This commit is contained in:
commit
eef5e5cd90
19
README.md
19
README.md
|
@ -1,7 +1,20 @@
|
||||||
ZoneMinder
|
ZoneMinder H264 Patch
|
||||||
==========
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
[![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png?branch=feature-h264-videostorage)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received)
|
||||||
|
|
||||||
|
##Feature-h264-videostorage Branch Details
|
||||||
|
This branch supports direct recording of h264 cameras into MP4 format uisng the h264 Passthrough option, but only with FFMPEG Monitors currently. It also provides h264 encoding for any other monitor type. If you encounter any issues, please open an issue on GitHub and attach it to the h264 milestone. But do remember this is bleeding edge so it will have problems.
|
||||||
|
Thanks to @chriswiggins and @mastertheknife for their work, @SteveGilvarry is now maintaining this branch and welcomes any assistance.
|
||||||
|
|
||||||
|
**The following SQL changes are required, these will be merged to zmupdate once we are ready to merge this branch to master.**
|
||||||
|
```
|
||||||
|
ALTER TABLE `Monitors` ADD `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' AFTER `Deinterlacing` ,
|
||||||
|
ADD `VideoWriter` TINYINT NOT NULL DEFAULT '0' AFTER `SaveJPEGs` ,
|
||||||
|
ADD `EncoderParameters` TEXT NOT NULL AFTER `VideoWriter` ,
|
||||||
|
ADD `RecordAudio` TINYINT NOT NULL DEFAULT '0' AFTER `EncoderParameters` ;
|
||||||
|
|
||||||
|
ALTER TABLE `Events` ADD `DefaultVideo` VARCHAR( 64 ) NOT NULL AFTER `AlarmFrames` ;
|
||||||
|
```
|
||||||
|
|
||||||
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org
|
||||||
|
|
||||||
|
|
|
@ -420,6 +420,35 @@ sub generateVideo
|
||||||
return( 1 );
|
return( 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Returns an image absolute path for given event and frame
|
||||||
|
# Optionally an analyse image path may be returned if an analyse image exists
|
||||||
|
# If neither capture nor analyse image exists it will try to extract a frame from .mp4 file if exists
|
||||||
|
# An empty string is returned if no one from methods above works
|
||||||
|
sub generateImage
|
||||||
|
{
|
||||||
|
my $event = shift;
|
||||||
|
my $frame = shift;
|
||||||
|
my $analyse = do { @_ ? shift : 0 }; # don't return analyse image by default
|
||||||
|
|
||||||
|
my $capture_image_path = sprintf("%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", getEventPath($event), $frame->{FrameId});
|
||||||
|
my $analyse_image_path = sprintf("%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-analyse.jpg", getEventPath($event), $frame->{FrameId}) if $analyse;
|
||||||
|
my $video_path = sprintf("%s/%d-video.mp4", getEventPath($event), $event->{Id});
|
||||||
|
my $image_path = "";
|
||||||
|
|
||||||
|
# check if the image file exists. If the file doesn't exist and we use H264 try to extract it from .mp4 video
|
||||||
|
if ( $analyse && -r $analyse_image_path ) {
|
||||||
|
$image_path = $analyse_image_path;
|
||||||
|
} elsif ( -r $capture_image_path ) {
|
||||||
|
$image_path = $capture_image_path;
|
||||||
|
} elsif ( -e $video_path ) {
|
||||||
|
if ( !system("ffmpeg -y -v 0 -i ".$video_path." -vf 'select=gte(n\\,".$frame->{FrameId}."),setpts=PTS-STARTPTS' -vframes 1 -f image2 ".$capture_image_path) ) {
|
||||||
|
$image_path = $capture_image_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $image_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub uploadArchFile
|
sub uploadArchFile
|
||||||
{
|
{
|
||||||
my $filter = shift;
|
my $filter = shift;
|
||||||
|
@ -623,7 +652,7 @@ sub substituteTags
|
||||||
}
|
}
|
||||||
|
|
||||||
# Do we need the image information too?
|
# Do we need the image information too?
|
||||||
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM)%/;
|
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA)%/;
|
||||||
my $first_alarm_frame;
|
my $first_alarm_frame;
|
||||||
my $max_alarm_frame;
|
my $max_alarm_frame;
|
||||||
my $max_alarm_score = 0;
|
my $max_alarm_score = 0;
|
||||||
|
@ -684,16 +713,15 @@ sub substituteTags
|
||||||
$text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
|
$text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g;
|
||||||
if ( $attachments_ref && $text =~ s/%EI1%//g )
|
if ( $attachments_ref && $text =~ s/%EI1%//g )
|
||||||
{
|
{
|
||||||
push( @$attachments_ref,
|
my $path = generateImage( $event, $first_alarm_frame);
|
||||||
{
|
if ( -e $path ) {
|
||||||
type=>"image/jpeg",
|
push( @$attachments_ref,
|
||||||
path=>sprintf(
|
{
|
||||||
"%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg",
|
type=>"image/jpeg",
|
||||||
getEventPath( $event ),
|
path=>$path
|
||||||
$first_alarm_frame->{FrameId}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if ( $attachments_ref && $text =~ s/%EIM%//g )
|
if ( $attachments_ref && $text =~ s/%EIM%//g )
|
||||||
{
|
{
|
||||||
|
@ -702,18 +730,47 @@ sub substituteTags
|
||||||
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
|
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
my $path = generateImage( $event, $max_alarm_frame);
|
||||||
|
if ( -e $path ) {
|
||||||
|
push( @$attachments_ref,
|
||||||
|
{
|
||||||
|
type=>"image/jpeg",
|
||||||
|
path=>$path
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $attachments_ref && $text =~ s/%EI1A%//g )
|
||||||
|
{
|
||||||
|
my $path = generateImage( $event, $first_alarm_frame, "analyse" );
|
||||||
|
if ( -e $path ) {
|
||||||
push( @$attachments_ref,
|
push( @$attachments_ref,
|
||||||
{
|
{
|
||||||
type=>"image/jpeg",
|
type=>"image/jpeg",
|
||||||
path=>sprintf(
|
path=>$path
|
||||||
"%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg",
|
|
||||||
getEventPath( $event ),
|
|
||||||
$max_alarm_frame->{FrameId}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ( $attachments_ref && $text =~ s/%EIMA%//g )
|
||||||
|
{
|
||||||
|
# Don't attach the same image twice
|
||||||
|
if ( !@$attachments_ref
|
||||||
|
|| ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} )
|
||||||
|
)
|
||||||
|
{
|
||||||
|
my $path = generateImage( $event, $max_alarm_frame, "analyse");
|
||||||
|
if ( -e $path ) {
|
||||||
|
push( @$attachments_ref,
|
||||||
|
{
|
||||||
|
type=>"image/jpeg",
|
||||||
|
path=>$path
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} )
|
if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} )
|
||||||
{
|
{
|
||||||
|
@ -1007,4 +1064,3 @@ sub executeCommand
|
||||||
}
|
}
|
||||||
return( 1 );
|
return( 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -311,7 +311,6 @@ int cURLCamera::CaptureAndRecord( Image &image, struct timeval recording, char*
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) {
|
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) {
|
||||||
lock();
|
lock();
|
||||||
|
|
||||||
|
@ -331,8 +330,6 @@ size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *
|
||||||
return size*nmemb;
|
return size*nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata) {
|
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata) {
|
||||||
std::string header;
|
std::string header;
|
||||||
header.assign((const char*)buffer, size*nmemb);
|
header.assign((const char*)buffer, size*nmemb);
|
||||||
|
|
|
@ -2865,7 +2865,6 @@ int Monitor::Capture() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( capture_image->Size() > camera->ImageSize() ) {
|
if ( capture_image->Size() > camera->ImageSize() ) {
|
||||||
Error( "Captured image %d does not match expected size %d check width, height and colour depth",capture_image->Size(),camera->ImageSize() );
|
Error( "Captured image %d does not match expected size %d check width, height and colour depth",capture_image->Size(),camera->ImageSize() );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
|
@ -42,6 +42,12 @@
|
||||||
#cmakedefine HAVE_GNUTLS_GNUTLS_H 1
|
#cmakedefine HAVE_GNUTLS_GNUTLS_H 1
|
||||||
#cmakedefine HAVE_LIBMYSQLCLIENT 1
|
#cmakedefine HAVE_LIBMYSQLCLIENT 1
|
||||||
#cmakedefine HAVE_MYSQL_H 1
|
#cmakedefine HAVE_MYSQL_H 1
|
||||||
|
#cmakedefine HAVE_LIBX264 1
|
||||||
|
#cmakedefine HAVE_X264_H 1
|
||||||
|
#cmakedefine HAVE_LIBMP4V2 1
|
||||||
|
#cmakedefine HAVE_MP4V2_MP4V2_H 1
|
||||||
|
#cmakedefine HAVE_MP4V2_H 1
|
||||||
|
#cmakedefine HAVE_MP4_H 1
|
||||||
#cmakedefine HAVE_LIBAVFORMAT 1
|
#cmakedefine HAVE_LIBAVFORMAT 1
|
||||||
#cmakedefine HAVE_LIBAVFORMAT_AVFORMAT_H 1
|
#cmakedefine HAVE_LIBAVFORMAT_AVFORMAT_H 1
|
||||||
#cmakedefine HAVE_LIBAVCODEC 1
|
#cmakedefine HAVE_LIBAVCODEC 1
|
||||||
|
|
Loading…
Reference in New Issue