Feature h264 videostorage (#1882)
* Moved writing of configure options from Controller to Model. Fixes #191.
* Initial commit for saving events as videos :)
* Add zm_video.cpp to autotools
* Add zm_video.h to autotools
* Search for MP4V2 header file 3 times: mp4v2/mp4v2.h, mp4v2.h, mp4.h
* Fix serve memory leak
* Few minor code improvements
* Added the ability to override preset, tune, profile and few other improvements
* Correctly write SPS & PPS from x264 encoder headers
* Remove unnessecary SPS & PPS writing code
* Imported missing files from master to feature-h264-videostorage
* Audio support including fixes for dts/pts, split on keyframe and update to mkv extension to prevent ffmpeg problems writing rtsp audio to mp4 containter (header problem)
* Updates to make gcc happy
* Add html5 video control to timeline and event to support mkv playback
* Add zm_videostore.cpp to CMakeLists.txt
* Remove Modern Branch for now
* Fix minor bug
* Option handled added in master, removing duplicate declaration
* Add CaptureandRecord from zm_camera.h
* Putting placeholder in for CaptureAndRecord function
* Removed duplicate code and brackets
* add digest auth file for cmake
Conflicts:
src/CMakeLists.txt
* Add web dir back into Makefile.am
Revert "Removed web from SUBDIRS in Makefile.am"
This reverts commit d9bbcdf3a94cba4d8955fcd03bd965ed2772f34d.
* Add CaptureAndRecord to vlc, still need to make it record
* Resolve SegFault on videostore
* Swap to mp4 container
* mp4 changes
* spaces to tabs, hide video stuff if video writer is turned off
* Make timeline open event.mp4 instead of mkv
* Missed mkv in timeline.js
* Fix some issues from the merge conflict
* Resolve post merge build issues with braces
* Fix whitespace
* Update Jpeg and Video options for passthrough options
* Whitespace fix zm_camera.h
* Fix array mkssing comma
* Add support for Jpeg save options for h264 branch snapshot. Might remove altogether if snapshots not needed
* Update VideoStoreData memory size comment
* Change from config.use_mkv_storage to per monitor option VideoWriter from video branch
* Fix bracket issues post merge
* Clean up comments and add av_free_packet
* Convert from event_directory to event file as per Video branch
* Testing videojs for video playback
* Fixed a missing bracket post merge and also SQL_values now used for EventID and Monitors
* bring recent improvements in ffmpeg capture function into captureandrecord
* Remove pict from writeAudioFramePacket as not used
* Add translate options for h264 Storage options in Monitor and update en_gb file
* Cherry-Pick from iconnor - make it compile on ubuntu 15.04. Which is libav 56.1.0
Conflicts:
src/zm_ffmpeg.cpp
src/zm_remote_camera_rtsp.cpp
Conflicts:
distros/ubuntu1204/changelog
* Clean up videostore code and remove lots of unused code
* proof of concept for dynamic/automatic video rotation using video-js plugin zoomrotate
Conflicts:
web/skins/classic/views/event.php
* removed redundant field in sql query
Conflicts:
web/skins/classic/views/event.php
* local storage of video js plugin
* Beautify!
Make the code somewhat readable.
* added missing videojs.zoomrotate.js file
added missing videojs.zoomrotate.js file
* Typo
added missing "
* Added missing brackets
* fix to display thumbnails when only storing snapshot.jpg
* added control for video playback rate
Conflicts:
web/skins/classic/views/event.php
* dynamically create jpegs from video file for viewing in browser
* fix timeline view for SaveJPEGs monitors (without enabled VideoWriter)
* only expose monitor info which are being used in client
* fix segmentation fault in zma with ubuntu 14.04 and ffmpeg 2.5.8 (gcc 4.8)
when libx264 is not installed
* better way of detecting showing image or video in timeline and event view
instead of Monitor.VideoWriter, Event.DefaultVideo is used, so even if
VideoWriter/SaveJPEG option is changed, a valid image or video will always be
displayed for historical events in both timeline and event view
this also fixes loading videos in timeline view
* Fixes problem of crashing zmc when bad packet arrives causing av_interleaved_write_frame() to return non-zero (-22). Prefilters common packet issues. Add metadata title to generated video file
* Remove syslog.h
* fixed SaveJPEGs are not working
which is caused in errors introduced when merging with master
* Update README.md
* Fix build warnings specific to h264 branch, unused FrameImg, unused ret and int64_t snprintf issues
* Fix PRId64 issue in travis, builds locally fine, but I can see a gcc version issue here
* Fix PRId64 issue in travis, another try
* Try "STDC_FORMAT_MACROS" to see if that helps Travis on gcc 4.6.3
* Revert space removal around PRId64
* video branch ffmpeg 2.9 fixes
ffmpeg 2.9 patched removed SSE2 CPU
* Add FFMPEGInit back
* use webvvt to overlay timestamp (honoring Monitor.LabelFormat) to videos in timeline and event
also fixed bug which prevented seeking in timeline video preview
* ffmpeg 3.0 API build failure fixes
* Update README.md
* merge all the commits from the messed up iconnor_video branch
* fix whitespace
* revert
* whitespace fixes
* spelling fix
* put back some text
* add these back
* fix spelling mistake
* Steal some packet dumping routines from ffmpeg. Convert them to use our logging routines
* add a test and error message if the codec is not h264
* these have been removed in master
* add a view to check auth and just send the video
* add some comments, and dump filename and AVFormatContext on failure to write header
* add the toggle for RecordAudio so that the checkbox works to turn off Audio
* Must init videoStore in constuctor
* more debug and comments, return checking
* Fix dropped part of sql query.
* fix extra else and some whitespace
* Fix missing } from merge that was preventing building.
* fix tabs
* get rid of use of separator, just use \n
* Restore lost fixes for deprecation
* Why are these failing
* Respect record_audio flag when setting up video file so dont try and initiliase mp4 with unsupported audio
* Forgot that I was trying to solve case of stream is true and record_audio
is false.
* Pass swscale_ctx back in to getCachedContext or it will create new
context every frame and leak memory like a mofo.
* Add libx264-dev and libmp4v2-dev to build requires to save hassle of
ensuring they are installed before build.
* Merge my Rotation/Orientation work and fixes for bad h264 streams
* need arpa/inet for reverse lookups
* pull in the new byte range code for viewing videos
* Move our recording flag deeper into closeevent
* add braces and only call closeEvent if there is an event
* deprecate the z_frame_rate stuff which is deprecated in ffmpeg
* remark out some debugging
* fix for video on stream 1
* fix audio_stream to audio_st
* Ignore bad decodes
* fix problems with content-length causing viewing to not work in chrome/android
* change logic of sending file contents to handle an off by one and be more readable
* Some fixes pointed out by Maxim Romanov. Also simply the loading of events to not join the Monitors table
* fix to sql for timeline
* added RecordAudio to sql in README
* Use sub queries instead of joins to fix errors when using new mysql defaults.
* fix sql queries
* Dockerfile to build feature-h264-videostorage
* Must cast codec
* add php-acpu as a dependency
* require php5-acpu
* fix typo
* remove extra /
* Add a line for out-of-tree builds to do api/lib/Cake/bootstrap.php
* delete merge conflict files
* delete merge conflict files
2017-05-16 10:02:48 +08:00
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License
|
|
|
|
// as published by the Free Software Foundation; either version 2
|
|
|
|
// of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
//
|
|
|
|
#include "zm.h"
|
|
|
|
#include "zm_video.h"
|
|
|
|
#include "zm_image.h"
|
|
|
|
#include "zm_utils.h"
|
|
|
|
#include "zm_rgb.h"
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
VideoWriter::VideoWriter(const char* p_container, const char* p_codec, const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) :
|
|
|
|
container(p_container), codec(p_codec), path(p_path), width(p_width), height(p_height), colours(p_colours), subpixelorder(p_subpixelorder), frame_count(0) {
|
|
|
|
Debug(7,"Video object created");
|
|
|
|
|
|
|
|
/* Parameter checking */
|
|
|
|
if(path.empty()) {
|
|
|
|
Error("Invalid file path");
|
|
|
|
}
|
|
|
|
if(!width || !height) {
|
|
|
|
Error("Invalid width or height");
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoWriter::~VideoWriter() {
|
|
|
|
Debug(7,"Video object destroyed");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int VideoWriter::Reset(const char* new_path) {
|
|
|
|
/* Common variables reset */
|
|
|
|
|
|
|
|
/* If there is a new path, use it */
|
|
|
|
if(new_path != NULL) {
|
|
|
|
path = new_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset frame counter */
|
|
|
|
frame_count = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if ZM_HAVE_VIDEOWRITER_X264MP4
|
|
|
|
X264MP4Writer::X264MP4Writer(const char* p_path, const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const std::vector<EncoderParameter_t>* p_user_params) : VideoWriter("mp4", "h264", p_path, p_width, p_height, p_colours, p_subpixelorder), bOpen(false), bGotH264AVCInfo(false), bFirstFrame(true) {
|
|
|
|
|
|
|
|
/* Initialize ffmpeg if it hasn't been initialized yet */
|
|
|
|
FFMPEGInit();
|
|
|
|
|
|
|
|
/* Initialize swscale */
|
|
|
|
zm_pf = GetFFMPEGPixelFormat(colours,subpixelorder);
|
|
|
|
if(zm_pf == 0) {
|
|
|
|
Error("Unable to match ffmpeg pixelformat");
|
|
|
|
}
|
|
|
|
codec_pf = AV_PIX_FMT_YUV420P;
|
|
|
|
|
|
|
|
swscaleobj.SetDefaults(zm_pf, codec_pf, width, height);
|
|
|
|
|
|
|
|
/* Calculate the image sizes. We will need this for parameter checking */
|
|
|
|
zm_imgsize = colours * width * height;
|
2017-05-20 21:03:04 +08:00
|
|
|
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
|
|
|
|
codec_imgsize = av_image_get_buffer_size( codec_pf, width, height, 1 );
|
|
|
|
#else
|
Feature h264 videostorage (#1882)
* Moved writing of configure options from Controller to Model. Fixes #191.
* Initial commit for saving events as videos :)
* Add zm_video.cpp to autotools
* Add zm_video.h to autotools
* Search for MP4V2 header file 3 times: mp4v2/mp4v2.h, mp4v2.h, mp4.h
* Fix serve memory leak
* Few minor code improvements
* Added the ability to override preset, tune, profile and few other improvements
* Correctly write SPS & PPS from x264 encoder headers
* Remove unnessecary SPS & PPS writing code
* Imported missing files from master to feature-h264-videostorage
* Audio support including fixes for dts/pts, split on keyframe and update to mkv extension to prevent ffmpeg problems writing rtsp audio to mp4 containter (header problem)
* Updates to make gcc happy
* Add html5 video control to timeline and event to support mkv playback
* Add zm_videostore.cpp to CMakeLists.txt
* Remove Modern Branch for now
* Fix minor bug
* Option handled added in master, removing duplicate declaration
* Add CaptureandRecord from zm_camera.h
* Putting placeholder in for CaptureAndRecord function
* Removed duplicate code and brackets
* add digest auth file for cmake
Conflicts:
src/CMakeLists.txt
* Add web dir back into Makefile.am
Revert "Removed web from SUBDIRS in Makefile.am"
This reverts commit d9bbcdf3a94cba4d8955fcd03bd965ed2772f34d.
* Add CaptureAndRecord to vlc, still need to make it record
* Resolve SegFault on videostore
* Swap to mp4 container
* mp4 changes
* spaces to tabs, hide video stuff if video writer is turned off
* Make timeline open event.mp4 instead of mkv
* Missed mkv in timeline.js
* Fix some issues from the merge conflict
* Resolve post merge build issues with braces
* Fix whitespace
* Update Jpeg and Video options for passthrough options
* Whitespace fix zm_camera.h
* Fix array mkssing comma
* Add support for Jpeg save options for h264 branch snapshot. Might remove altogether if snapshots not needed
* Update VideoStoreData memory size comment
* Change from config.use_mkv_storage to per monitor option VideoWriter from video branch
* Fix bracket issues post merge
* Clean up comments and add av_free_packet
* Convert from event_directory to event file as per Video branch
* Testing videojs for video playback
* Fixed a missing bracket post merge and also SQL_values now used for EventID and Monitors
* bring recent improvements in ffmpeg capture function into captureandrecord
* Remove pict from writeAudioFramePacket as not used
* Add translate options for h264 Storage options in Monitor and update en_gb file
* Cherry-Pick from iconnor - make it compile on ubuntu 15.04. Which is libav 56.1.0
Conflicts:
src/zm_ffmpeg.cpp
src/zm_remote_camera_rtsp.cpp
Conflicts:
distros/ubuntu1204/changelog
* Clean up videostore code and remove lots of unused code
* proof of concept for dynamic/automatic video rotation using video-js plugin zoomrotate
Conflicts:
web/skins/classic/views/event.php
* removed redundant field in sql query
Conflicts:
web/skins/classic/views/event.php
* local storage of video js plugin
* Beautify!
Make the code somewhat readable.
* added missing videojs.zoomrotate.js file
added missing videojs.zoomrotate.js file
* Typo
added missing "
* Added missing brackets
* fix to display thumbnails when only storing snapshot.jpg
* added control for video playback rate
Conflicts:
web/skins/classic/views/event.php
* dynamically create jpegs from video file for viewing in browser
* fix timeline view for SaveJPEGs monitors (without enabled VideoWriter)
* only expose monitor info which are being used in client
* fix segmentation fault in zma with ubuntu 14.04 and ffmpeg 2.5.8 (gcc 4.8)
when libx264 is not installed
* better way of detecting showing image or video in timeline and event view
instead of Monitor.VideoWriter, Event.DefaultVideo is used, so even if
VideoWriter/SaveJPEG option is changed, a valid image or video will always be
displayed for historical events in both timeline and event view
this also fixes loading videos in timeline view
* Fixes problem of crashing zmc when bad packet arrives causing av_interleaved_write_frame() to return non-zero (-22). Prefilters common packet issues. Add metadata title to generated video file
* Remove syslog.h
* fixed SaveJPEGs are not working
which is caused in errors introduced when merging with master
* Update README.md
* Fix build warnings specific to h264 branch, unused FrameImg, unused ret and int64_t snprintf issues
* Fix PRId64 issue in travis, builds locally fine, but I can see a gcc version issue here
* Fix PRId64 issue in travis, another try
* Try "STDC_FORMAT_MACROS" to see if that helps Travis on gcc 4.6.3
* Revert space removal around PRId64
* video branch ffmpeg 2.9 fixes
ffmpeg 2.9 patched removed SSE2 CPU
* Add FFMPEGInit back
* use webvvt to overlay timestamp (honoring Monitor.LabelFormat) to videos in timeline and event
also fixed bug which prevented seeking in timeline video preview
* ffmpeg 3.0 API build failure fixes
* Update README.md
* merge all the commits from the messed up iconnor_video branch
* fix whitespace
* revert
* whitespace fixes
* spelling fix
* put back some text
* add these back
* fix spelling mistake
* Steal some packet dumping routines from ffmpeg. Convert them to use our logging routines
* add a test and error message if the codec is not h264
* these have been removed in master
* add a view to check auth and just send the video
* add some comments, and dump filename and AVFormatContext on failure to write header
* add the toggle for RecordAudio so that the checkbox works to turn off Audio
* Must init videoStore in constuctor
* more debug and comments, return checking
* Fix dropped part of sql query.
* fix extra else and some whitespace
* Fix missing } from merge that was preventing building.
* fix tabs
* get rid of use of separator, just use \n
* Restore lost fixes for deprecation
* Why are these failing
* Respect record_audio flag when setting up video file so dont try and initiliase mp4 with unsupported audio
* Forgot that I was trying to solve case of stream is true and record_audio
is false.
* Pass swscale_ctx back in to getCachedContext or it will create new
context every frame and leak memory like a mofo.
* Add libx264-dev and libmp4v2-dev to build requires to save hassle of
ensuring they are installed before build.
* Merge my Rotation/Orientation work and fixes for bad h264 streams
* need arpa/inet for reverse lookups
* pull in the new byte range code for viewing videos
* Move our recording flag deeper into closeevent
* add braces and only call closeEvent if there is an event
* deprecate the z_frame_rate stuff which is deprecated in ffmpeg
* remark out some debugging
* fix for video on stream 1
* fix audio_stream to audio_st
* Ignore bad decodes
* fix problems with content-length causing viewing to not work in chrome/android
* change logic of sending file contents to handle an off by one and be more readable
* Some fixes pointed out by Maxim Romanov. Also simply the loading of events to not join the Monitors table
* fix to sql for timeline
* added RecordAudio to sql in README
* Use sub queries instead of joins to fix errors when using new mysql defaults.
* fix sql queries
* Dockerfile to build feature-h264-videostorage
* Must cast codec
* add php-acpu as a dependency
* require php5-acpu
* fix typo
* remove extra /
* Add a line for out-of-tree builds to do api/lib/Cake/bootstrap.php
* delete merge conflict files
* delete merge conflict files
2017-05-16 10:02:48 +08:00
|
|
|
codec_imgsize = avpicture_get_size( codec_pf, width, height);
|
2017-05-20 21:03:04 +08:00
|
|
|
#endif
|
Feature h264 videostorage (#1882)
* Moved writing of configure options from Controller to Model. Fixes #191.
* Initial commit for saving events as videos :)
* Add zm_video.cpp to autotools
* Add zm_video.h to autotools
* Search for MP4V2 header file 3 times: mp4v2/mp4v2.h, mp4v2.h, mp4.h
* Fix serve memory leak
* Few minor code improvements
* Added the ability to override preset, tune, profile and few other improvements
* Correctly write SPS & PPS from x264 encoder headers
* Remove unnessecary SPS & PPS writing code
* Imported missing files from master to feature-h264-videostorage
* Audio support including fixes for dts/pts, split on keyframe and update to mkv extension to prevent ffmpeg problems writing rtsp audio to mp4 containter (header problem)
* Updates to make gcc happy
* Add html5 video control to timeline and event to support mkv playback
* Add zm_videostore.cpp to CMakeLists.txt
* Remove Modern Branch for now
* Fix minor bug
* Option handled added in master, removing duplicate declaration
* Add CaptureandRecord from zm_camera.h
* Putting placeholder in for CaptureAndRecord function
* Removed duplicate code and brackets
* add digest auth file for cmake
Conflicts:
src/CMakeLists.txt
* Add web dir back into Makefile.am
Revert "Removed web from SUBDIRS in Makefile.am"
This reverts commit d9bbcdf3a94cba4d8955fcd03bd965ed2772f34d.
* Add CaptureAndRecord to vlc, still need to make it record
* Resolve SegFault on videostore
* Swap to mp4 container
* mp4 changes
* spaces to tabs, hide video stuff if video writer is turned off
* Make timeline open event.mp4 instead of mkv
* Missed mkv in timeline.js
* Fix some issues from the merge conflict
* Resolve post merge build issues with braces
* Fix whitespace
* Update Jpeg and Video options for passthrough options
* Whitespace fix zm_camera.h
* Fix array mkssing comma
* Add support for Jpeg save options for h264 branch snapshot. Might remove altogether if snapshots not needed
* Update VideoStoreData memory size comment
* Change from config.use_mkv_storage to per monitor option VideoWriter from video branch
* Fix bracket issues post merge
* Clean up comments and add av_free_packet
* Convert from event_directory to event file as per Video branch
* Testing videojs for video playback
* Fixed a missing bracket post merge and also SQL_values now used for EventID and Monitors
* bring recent improvements in ffmpeg capture function into captureandrecord
* Remove pict from writeAudioFramePacket as not used
* Add translate options for h264 Storage options in Monitor and update en_gb file
* Cherry-Pick from iconnor - make it compile on ubuntu 15.04. Which is libav 56.1.0
Conflicts:
src/zm_ffmpeg.cpp
src/zm_remote_camera_rtsp.cpp
Conflicts:
distros/ubuntu1204/changelog
* Clean up videostore code and remove lots of unused code
* proof of concept for dynamic/automatic video rotation using video-js plugin zoomrotate
Conflicts:
web/skins/classic/views/event.php
* removed redundant field in sql query
Conflicts:
web/skins/classic/views/event.php
* local storage of video js plugin
* Beautify!
Make the code somewhat readable.
* added missing videojs.zoomrotate.js file
added missing videojs.zoomrotate.js file
* Typo
added missing "
* Added missing brackets
* fix to display thumbnails when only storing snapshot.jpg
* added control for video playback rate
Conflicts:
web/skins/classic/views/event.php
* dynamically create jpegs from video file for viewing in browser
* fix timeline view for SaveJPEGs monitors (without enabled VideoWriter)
* only expose monitor info which are being used in client
* fix segmentation fault in zma with ubuntu 14.04 and ffmpeg 2.5.8 (gcc 4.8)
when libx264 is not installed
* better way of detecting showing image or video in timeline and event view
instead of Monitor.VideoWriter, Event.DefaultVideo is used, so even if
VideoWriter/SaveJPEG option is changed, a valid image or video will always be
displayed for historical events in both timeline and event view
this also fixes loading videos in timeline view
* Fixes problem of crashing zmc when bad packet arrives causing av_interleaved_write_frame() to return non-zero (-22). Prefilters common packet issues. Add metadata title to generated video file
* Remove syslog.h
* fixed SaveJPEGs are not working
which is caused in errors introduced when merging with master
* Update README.md
* Fix build warnings specific to h264 branch, unused FrameImg, unused ret and int64_t snprintf issues
* Fix PRId64 issue in travis, builds locally fine, but I can see a gcc version issue here
* Fix PRId64 issue in travis, another try
* Try "STDC_FORMAT_MACROS" to see if that helps Travis on gcc 4.6.3
* Revert space removal around PRId64
* video branch ffmpeg 2.9 fixes
ffmpeg 2.9 patched removed SSE2 CPU
* Add FFMPEGInit back
* use webvvt to overlay timestamp (honoring Monitor.LabelFormat) to videos in timeline and event
also fixed bug which prevented seeking in timeline video preview
* ffmpeg 3.0 API build failure fixes
* Update README.md
* merge all the commits from the messed up iconnor_video branch
* fix whitespace
* revert
* whitespace fixes
* spelling fix
* put back some text
* add these back
* fix spelling mistake
* Steal some packet dumping routines from ffmpeg. Convert them to use our logging routines
* add a test and error message if the codec is not h264
* these have been removed in master
* add a view to check auth and just send the video
* add some comments, and dump filename and AVFormatContext on failure to write header
* add the toggle for RecordAudio so that the checkbox works to turn off Audio
* Must init videoStore in constuctor
* more debug and comments, return checking
* Fix dropped part of sql query.
* fix extra else and some whitespace
* Fix missing } from merge that was preventing building.
* fix tabs
* get rid of use of separator, just use \n
* Restore lost fixes for deprecation
* Why are these failing
* Respect record_audio flag when setting up video file so dont try and initiliase mp4 with unsupported audio
* Forgot that I was trying to solve case of stream is true and record_audio
is false.
* Pass swscale_ctx back in to getCachedContext or it will create new
context every frame and leak memory like a mofo.
* Add libx264-dev and libmp4v2-dev to build requires to save hassle of
ensuring they are installed before build.
* Merge my Rotation/Orientation work and fixes for bad h264 streams
* need arpa/inet for reverse lookups
* pull in the new byte range code for viewing videos
* Move our recording flag deeper into closeevent
* add braces and only call closeEvent if there is an event
* deprecate the z_frame_rate stuff which is deprecated in ffmpeg
* remark out some debugging
* fix for video on stream 1
* fix audio_stream to audio_st
* Ignore bad decodes
* fix problems with content-length causing viewing to not work in chrome/android
* change logic of sending file contents to handle an off by one and be more readable
* Some fixes pointed out by Maxim Romanov. Also simply the loading of events to not join the Monitors table
* fix to sql for timeline
* added RecordAudio to sql in README
* Use sub queries instead of joins to fix errors when using new mysql defaults.
* fix sql queries
* Dockerfile to build feature-h264-videostorage
* Must cast codec
* add php-acpu as a dependency
* require php5-acpu
* fix typo
* remove extra /
* Add a line for out-of-tree builds to do api/lib/Cake/bootstrap.php
* delete merge conflict files
* delete merge conflict files
2017-05-16 10:02:48 +08:00
|
|
|
if(!codec_imgsize) {
|
|
|
|
Error("Failed calculating codec pixel format image size");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If supplied with user parameters to the encoder, copy them */
|
|
|
|
if(p_user_params != NULL) {
|
|
|
|
user_params = *p_user_params;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup x264 parameters */
|
|
|
|
if(x264config() < 0) {
|
|
|
|
Error("Failed setting x264 parameters");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate x264 input picture */
|
|
|
|
x264_picture_alloc(&x264picin, X264_CSP_I420, x264params.i_width, x264params.i_height);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
X264MP4Writer::~X264MP4Writer() {
|
|
|
|
|
|
|
|
/* Free x264 input picture */
|
|
|
|
x264_picture_clean(&x264picin);
|
|
|
|
|
|
|
|
if(bOpen)
|
|
|
|
Close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int X264MP4Writer::Open() {
|
|
|
|
|
|
|
|
/* Open the encoder */
|
|
|
|
x264enc = x264_encoder_open(&x264params);
|
|
|
|
if(x264enc == NULL) {
|
|
|
|
Error("Failed opening x264 encoder");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Debug(4,"x264 maximum delayed frames: %d",x264_encoder_maximum_delayed_frames(x264enc));
|
|
|
|
|
|
|
|
x264_nal_t* nals;
|
|
|
|
int i_nals;
|
|
|
|
if(!x264_encoder_headers(x264enc,&nals,&i_nals)) {
|
|
|
|
Error("Failed getting encoder headers");
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search SPS NAL for AVC information */
|
|
|
|
for(int i=0;i<i_nals;i++) {
|
|
|
|
if(nals[i].i_type == NAL_SPS) {
|
|
|
|
x264_profleindication = nals[i].p_payload[5];
|
|
|
|
x264_profilecompat = nals[i].p_payload[6];
|
|
|
|
x264_levelindication = nals[i].p_payload[7];
|
|
|
|
bGotH264AVCInfo = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!bGotH264AVCInfo) {
|
|
|
|
Warning("Missing AVC information");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the file */
|
|
|
|
mp4h = MP4Create((path + ".incomplete").c_str());
|
|
|
|
if(mp4h == MP4_INVALID_FILE_HANDLE) {
|
|
|
|
Error("Failed creating mp4 file: %s",path.c_str());
|
|
|
|
return -10;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the global timescale */
|
|
|
|
if(!MP4SetTimeScale(mp4h, 1000)) {
|
|
|
|
Error("Failed setting timescale");
|
|
|
|
return -11;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the global video profile */
|
|
|
|
/* I am a bit confused about this one.
|
|
|
|
I couldn't find what the value should be
|
|
|
|
Some use 0x15 while others use 0x7f. */
|
|
|
|
MP4SetVideoProfileLevel(mp4h, 0x7f);
|
|
|
|
|
|
|
|
/* Add H264 video track */
|
|
|
|
mp4vtid = MP4AddH264VideoTrack(mp4h,1000,MP4_INVALID_DURATION,width,height,x264_profleindication,x264_profilecompat,x264_levelindication,3);
|
|
|
|
if(mp4vtid == MP4_INVALID_TRACK_ID) {
|
|
|
|
Error("Failed adding H264 video track");
|
|
|
|
return -12;
|
|
|
|
}
|
|
|
|
|
|
|
|
bOpen = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int X264MP4Writer::Close() {
|
|
|
|
|
|
|
|
/* Flush all pending frames */
|
|
|
|
for(int i = (x264_encoder_delayed_frames(x264enc) + 1); i > 0; i-- ) {
|
|
|
|
x264encodeloop(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Close the encoder */
|
|
|
|
x264_encoder_close(x264enc);
|
|
|
|
|
|
|
|
/* Close MP4 handle */
|
|
|
|
MP4Close(mp4h);
|
|
|
|
|
|
|
|
/* Required for proper HTTP streaming */
|
|
|
|
MP4Optimize((path + ".incomplete").c_str(), path.c_str());
|
|
|
|
|
|
|
|
/* Delete the temporary file */
|
|
|
|
unlink((path + ".incomplete").c_str());
|
|
|
|
|
|
|
|
bOpen = false;
|
|
|
|
|
|
|
|
Debug(7, "Video closed. Total frames: %d", frame_count);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int X264MP4Writer::Reset(const char* new_path) {
|
|
|
|
|
|
|
|
/* Close the encoder and file */
|
|
|
|
if(bOpen)
|
|
|
|
Close();
|
|
|
|
|
|
|
|
/* Reset common variables */
|
|
|
|
VideoWriter::Reset(new_path);
|
|
|
|
|
|
|
|
/* Reset local variables */
|
|
|
|
bFirstFrame = true;
|
|
|
|
bGotH264AVCInfo = false;
|
|
|
|
prevnals.clear();
|
|
|
|
prevpayload.clear();
|
|
|
|
|
|
|
|
/* Reset x264 parameters */
|
|
|
|
x264config();
|
|
|
|
|
|
|
|
/* Open the encoder */
|
|
|
|
Open();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int X264MP4Writer::Encode(const uint8_t* data, const size_t data_size, const unsigned int frame_time) {
|
|
|
|
|
|
|
|
/* Parameter checking */
|
|
|
|
if(data == NULL) {
|
|
|
|
Error("NULL buffer");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data_size != zm_imgsize) {
|
|
|
|
Error("The data buffer size does not match the expected size. Expected: %d Current: %d", zm_imgsize, data_size);
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!bOpen) {
|
|
|
|
Warning("The encoder was not initialized, initializing now");
|
|
|
|
Open();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert the image into the x264 input picture */
|
|
|
|
if(swscaleobj.ConvertDefaults(data, data_size, x264picin.img.plane[0], codec_imgsize) < 0) {
|
|
|
|
Error("Image conversion failed");
|
|
|
|
return -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set PTS */
|
|
|
|
x264picin.i_pts = frame_time;
|
|
|
|
|
|
|
|
/* Do the encoding */
|
|
|
|
x264encodeloop();
|
|
|
|
|
|
|
|
/* Increment frame counter */
|
|
|
|
frame_count++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int X264MP4Writer::Encode(const Image* img, const unsigned int frame_time) {
|
|
|
|
|
|
|
|
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 Encode(img->Buffer(),img->Size(),frame_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
int X264MP4Writer::x264config() {
|
|
|
|
/* Sets up the encoder configuration */
|
|
|
|
|
|
|
|
int x264ret;
|
|
|
|
|
|
|
|
/* Defaults */
|
|
|
|
const char* preset = "veryfast";
|
|
|
|
const char* tune = "stillimage";
|
|
|
|
const char* profile = "main";
|
|
|
|
|
|
|
|
/* Search the user parameters for preset, tune and profile */
|
|
|
|
for(unsigned int i=0; i < user_params.size(); i++) {
|
|
|
|
if(strcmp(user_params[i].pname, "preset") == 0) {
|
|
|
|
/* Got preset */
|
|
|
|
preset = user_params[i].pvalue;
|
|
|
|
} else if(strcmp(user_params[i].pname, "tune") == 0) {
|
|
|
|
/* Got tune */
|
|
|
|
tune = user_params[i].pvalue;
|
|
|
|
} else if(strcmp(user_params[i].pname, "profile") == 0) {
|
|
|
|
/* Got profile */
|
|
|
|
profile = user_params[i].pvalue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the defaults and preset and tune */
|
|
|
|
x264ret = x264_param_default_preset(&x264params, preset, tune);
|
|
|
|
if(x264ret != 0) {
|
|
|
|
Error("Failed setting x264 preset %s and tune %s : %d",preset,tune,x264ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the profile */
|
|
|
|
x264ret = x264_param_apply_profile(&x264params, profile);
|
|
|
|
if(x264ret != 0) {
|
|
|
|
Error("Failed setting x264 profile %s : %d",profile,x264ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Input format */
|
|
|
|
x264params.i_width = width;
|
|
|
|
x264params.i_height = height;
|
|
|
|
x264params.i_csp = X264_CSP_I420;
|
|
|
|
|
|
|
|
/* Quality control */
|
|
|
|
x264params.rc.i_rc_method = X264_RC_CRF;
|
|
|
|
x264params.rc.f_rf_constant = 23.0;
|
|
|
|
|
|
|
|
/* Enable b-frames */
|
|
|
|
x264params.i_bframe = 16;
|
|
|
|
x264params.i_bframe_adaptive = 1;
|
|
|
|
|
|
|
|
/* Timebase */
|
|
|
|
x264params.i_timebase_num = 1;
|
|
|
|
x264params.i_timebase_den = 1000;
|
|
|
|
|
|
|
|
/* Enable variable frame rate */
|
|
|
|
x264params.b_vfr_input = 1;
|
|
|
|
|
|
|
|
/* Disable annex-b (start codes) */
|
|
|
|
x264params.b_annexb = 0;
|
|
|
|
|
|
|
|
/* TODO: Setup error handler */
|
|
|
|
//x264params.i_log_level = X264_LOG_DEBUG;
|
|
|
|
|
|
|
|
/* Process user parameters (excluding preset, tune and profile) */
|
|
|
|
for(unsigned int i=0; i < user_params.size(); i++) {
|
|
|
|
/* Skip preset, tune and profile */
|
|
|
|
if( (strcmp(user_params[i].pname, "preset") == 0) || (strcmp(user_params[i].pname, "tune") == 0) || (strcmp(user_params[i].pname, "profile") == 0) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Pass the name and value to x264 */
|
|
|
|
x264ret = x264_param_parse(&x264params, user_params[i].pname, user_params[i].pvalue);
|
|
|
|
|
|
|
|
/* Error checking */
|
|
|
|
if(x264ret != 0) {
|
|
|
|
if(x264ret == X264_PARAM_BAD_NAME) {
|
|
|
|
Error("Failed processing x264 user parameter %s=%s : Bad name", user_params[i].pname, user_params[i].pvalue);
|
|
|
|
} else if(x264ret == X264_PARAM_BAD_VALUE) {
|
|
|
|
Error("Failed processing x264 user parameter %s=%s : Bad value", user_params[i].pname, user_params[i].pvalue);
|
|
|
|
} else {
|
|
|
|
Error("Failed processing x264 user parameter %s=%s : Unknown error (%d)", user_params[i].pname, user_params[i].pvalue, x264ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void X264MP4Writer::x264encodeloop(bool bFlush) {
|
|
|
|
|
|
|
|
x264_nal_t* nals;
|
|
|
|
int i_nals;
|
|
|
|
int frame_size;
|
|
|
|
|
|
|
|
if(bFlush) {
|
|
|
|
frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, NULL, &x264picout);
|
|
|
|
} else {
|
|
|
|
frame_size = x264_encoder_encode(x264enc, &nals, &i_nals, &x264picin, &x264picout);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame_size > 0 || bFlush) {
|
|
|
|
Debug(8, "x264 Frame: %d PTS: %d DTS: %d Size: %d\n",frame_count, x264picout.i_pts, x264picout.i_dts, frame_size);
|
|
|
|
|
|
|
|
/* Handle the previous frame */
|
|
|
|
if(!bFirstFrame) {
|
|
|
|
|
|
|
|
buffer.clear();
|
|
|
|
|
|
|
|
/* Process the NALs for the previous frame */
|
|
|
|
for(unsigned int i=0; i < prevnals.size(); i++) {
|
|
|
|
Debug(9,"Processing NAL: Type %d Size %d",prevnals[i].i_type,prevnals[i].i_payload);
|
|
|
|
|
|
|
|
switch(prevnals[i].i_type) {
|
|
|
|
case NAL_PPS:
|
|
|
|
/* PPS NAL */
|
|
|
|
MP4AddH264PictureParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4);
|
|
|
|
break;
|
|
|
|
case NAL_SPS:
|
|
|
|
/* SPS NAL */
|
|
|
|
MP4AddH264SequenceParameterSet(mp4h, mp4vtid, prevnals[i].p_payload+4, prevnals[i].i_payload-4);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Anything else, hopefully frames, so copy it into the sample */
|
|
|
|
buffer.append(prevnals[i].p_payload, prevnals[i].i_payload);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate frame duration and offset */
|
|
|
|
int duration = x264picout.i_dts - prevDTS;
|
|
|
|
int offset = prevPTS - prevDTS;
|
|
|
|
|
|
|
|
/* Write the sample */
|
|
|
|
if(!buffer.empty()) {
|
|
|
|
if(!MP4WriteSample(mp4h, mp4vtid, buffer.extract(buffer.size()), buffer.size(), duration, offset, prevKeyframe)) {
|
|
|
|
Error("Failed writing sample");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Cleanup */
|
|
|
|
prevnals.clear();
|
|
|
|
prevpayload.clear();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Got a frame. Copy this new frame into the previous frame */
|
|
|
|
if(frame_size > 0) {
|
|
|
|
/* Copy the NALs and the payloads */
|
|
|
|
for(int i=0;i<i_nals;i++) {
|
|
|
|
|
|
|
|
prevnals.push_back(nals[i]);
|
|
|
|
prevpayload.append(nals[i].p_payload, nals[i].i_payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update the payload pointers */
|
|
|
|
/* This is done in a separate loop because the previous loop might reallocate memory when appending,
|
|
|
|
making the pointers invalid */
|
|
|
|
unsigned int payload_offset = 0;
|
|
|
|
for(unsigned int i=0;i<prevnals.size();i++) {
|
|
|
|
prevnals[i].p_payload = prevpayload.head() + payload_offset;
|
|
|
|
payload_offset += nals[i].i_payload;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We need this for the next frame */
|
|
|
|
prevPTS = x264picout.i_pts;
|
|
|
|
prevDTS = x264picout.i_dts;
|
|
|
|
prevKeyframe = x264picout.b_keyframe;
|
|
|
|
|
|
|
|
bFirstFrame = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if(frame_size == 0) {
|
|
|
|
Debug(7,"x264 encode returned zero. Delayed frames: %d",x264_encoder_delayed_frames(x264enc));
|
|
|
|
} else {
|
|
|
|
Error("x264 encode failed: %d",frame_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // ZM_VIDEOWRITER_X264MP4
|
|
|
|
|
|
|
|
int ParseEncoderParameters(const char* str, std::vector<EncoderParameter_t>* vec) {
|
|
|
|
if(vec == NULL) {
|
|
|
|
Error("NULL Encoder parameters vector pointer");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(str == NULL) {
|
|
|
|
Error("NULL Encoder parameters string");
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
vec->clear();
|
|
|
|
|
|
|
|
if(str[0] == 0) {
|
|
|
|
/* Empty */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
std::stringstream ss(str);
|
|
|
|
size_t valueoffset;
|
|
|
|
size_t valuelen;
|
|
|
|
unsigned int lineno = 0;
|
|
|
|
EncoderParameter_t param;
|
|
|
|
|
|
|
|
while(std::getline(ss, line) ) {
|
|
|
|
lineno++;
|
|
|
|
|
|
|
|
/* Remove CR if exists */
|
|
|
|
if(line.length() >= 1 && line[line.length()-1] == '\r') {
|
|
|
|
line.erase(line.length()-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Skip comments and empty lines */
|
|
|
|
if(line.empty() || line[0] == '#') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
valueoffset = line.find('=');
|
|
|
|
if(valueoffset == std::string::npos || valueoffset+1 >= line.length() || valueoffset == 0) {
|
|
|
|
Warning("Failed parsing encoder parameters line %d: Invalid pair", lineno);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(valueoffset > (sizeof(param.pname)-1) ) {
|
|
|
|
Warning("Failed parsing encoder parameters line %d: Name too long", lineno);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
valuelen = line.length() - (valueoffset+1);
|
|
|
|
|
|
|
|
if( valuelen > (sizeof(param.pvalue)-1) ) {
|
|
|
|
Warning("Failed parsing encoder parameters line %d: Value too long", lineno);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy and NULL terminate */
|
|
|
|
line.copy(param.pname, valueoffset, 0);
|
|
|
|
line.copy(param.pvalue, valuelen, valueoffset+1);
|
|
|
|
param.pname[valueoffset] = 0;
|
|
|
|
param.pvalue[valuelen] = 0;
|
|
|
|
|
|
|
|
/* Push to the vector */
|
|
|
|
vec->push_back(param);
|
|
|
|
|
|
|
|
Debug(7, "Parsed encoder parameter: %s = %s", param.pname, param.pvalue);
|
|
|
|
}
|
|
|
|
|
|
|
|
Debug(7, "Parsed %d lines", lineno);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|