From e63f94885c127d2d0c671b71da755aca754aaea7 Mon Sep 17 00:00:00 2001 From: lowflyerUK Date: Fri, 13 Nov 2015 20:04:48 +0000 Subject: [PATCH 1/2] dirty hack to get ffmpeg to compile with jessie - libav10? --- ffmpeg.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ffmpeg.c b/ffmpeg.c index 57eeda4..78248c1 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -72,7 +72,15 @@ #define URL_RDWR AVIO_FLAG_READ_WRITE /**< read-write pseudo flag */ #endif - +#define CODEC_ID_MSMPEG4V2 AV_CODEC_ID_MSMPEG4V2 +#define CODEC_ID_FLV1 AV_CODEC_ID_FLV1 +#define CODEC_ID_FFV1 AV_CODEC_ID_FFV1 +#define CODEC_ID_NONE AV_CODEC_ID_NONE +#define CODEC_ID_MPEG2VIDEO AV_CODEC_ID_MPEG2VIDEO +#define CODEC_ID_H264 AV_CODEC_ID_H264 +#define CODEC_ID_HEVC AV_CODEC_ID_HEVC + +#define CODEC_ID_MPEG1VIDEO AV_CODEC_ID_MPEG2VIDEO /* * Name of custom file protocol for appending to existing files instead * of truncating. From 003a99e58fa7954bdf65a8f949056032fa58d793 Mon Sep 17 00:00:00 2001 From: lowflyerUK Date: Sat, 14 Nov 2015 13:45:55 +0000 Subject: [PATCH 2/2] Slightly better hack to get ffmpeg working with jessie. Added option to output both secondary and normal pictures. --- README | 18 +- conf.c | 14 +- conf.h | 3 +- configs/motion-mmalcam-both.conf | 790 +++++++++++++++++++++++ event.c | 9 +- ffmpeg.c | 1032 ++++++++++-------------------- ffmpeg.h | 74 ++- motion.c | 4 +- picture.c | 6 + 9 files changed, 1218 insertions(+), 732 deletions(-) create mode 100644 configs/motion-mmalcam-both.conf diff --git a/README b/README index 13f7499..a3e3e44 100644 --- a/README +++ b/README @@ -1,5 +1,19 @@ -MMAL README ------------ +Even more experimental MMAL README +---------------------------------- +Forked from dozencrows superb work. + +Added option to output both secondary and normal pictures. +The smaller picture is a good thumbnail if you need to reduce network load. + +Manually merged updates from https://github.com/Mr-Dave/motion so that it works with libavformat56 on Rasbian Jessie. +This needs testing - in particular timelapse movies and on older versions of libavformat. + +Added configs/motion-mmalcam-both.conf. +Works on Pi 2 at 640x480 at 4 fps with x4 upscale so stills are 2560x1920. +Uses up to about 50% of one cpu (so about 13% of available cpu). + +Original MMAL README +-------------------- This is an experimental version of motion that adds support for the Raspberry Pi's CSI camera module, via the MMAL API. diff --git a/conf.c b/conf.c index 7fa8927..00b1ee8 100644 --- a/conf.c +++ b/conf.c @@ -157,6 +157,7 @@ struct config conf_template = { mmalcam_raw_capture_file: NULL, mmalcam_buffer2_upscale: 0, mmalcam_buffer2_jpeg: 0, + output_both_pictures: 0, #endif text_changes: 0, text_left: NULL, @@ -486,6 +487,15 @@ config_param config_params[] = { copy_string, print_string }, + { + "output_both_pictures", + "# Output both primary and secondary pictures\n" + " Default: off", + 0, + CONF_OFFSET(output_both_pictures), + copy_bool, + print_bool + }, #endif { "auto_brightness", @@ -850,7 +860,7 @@ config_param config_params[] = { copy_string, print_string }, - { +/* { "ffmpeg_deinterlace", "# Use ffmpeg to deinterlace video. Necessary if you use an analog camera\n" "# and see horizontal combing on moving objects in video or pictures.\n" @@ -859,7 +869,7 @@ config_param config_params[] = { CONF_OFFSET(ffmpeg_deinterlace), copy_bool, print_bool - }, + }, */ #endif /* HAVE_FFMPEG */ #ifdef HAVE_SDL { diff --git a/conf.h b/conf.h index 365ff27..0c455f1 100644 --- a/conf.h +++ b/conf.h @@ -65,7 +65,7 @@ struct config { int ffmpeg_output_secondary; int ffmpeg_bps; int ffmpeg_vbr; - int ffmpeg_deinterlace; +/* int ffmpeg_deinterlace; */ const char *ffmpeg_video_codec; #ifdef HAVE_SDL int sdl_threadnr; @@ -135,6 +135,7 @@ struct config { int mmalcam_buffer2_upscale; const char *mmalcam_raw_capture_file; int mmalcam_buffer2_jpeg; + int output_both_pictures; #endif int text_changes; const char *text_left; diff --git a/configs/motion-mmalcam-both.conf b/configs/motion-mmalcam-both.conf new file mode 100644 index 0000000..78f1578 --- /dev/null +++ b/configs/motion-mmalcam-both.conf @@ -0,0 +1,790 @@ +############################################################ +# Daemon +############################################################ + +# Start in daemon (background) mode and release terminal (default: off) +daemon off + +# File to store the process ID, also called pid file. (default: not defined) +process_id_file /var/run/motion/motion.pid + +############################################################ +# Basic Setup Mode +############################################################ + +# Start in Setup-Mode, daemon disabled. (default: off) +setup_mode off + + +# Use a file to save logs messages, if not defined stderr and syslog is used. (default: not defined) +#logfile /run/shm/motion.log + +# Level of log messages [1..9] (EMR, ALR, CRT, ERR, WRN, NTC, INF, DBG, ALL). (default: 6 / NTC) +log_level 2 + +# Filter to log messages by type (COR, STR, ENC, NET, DBL, EVT, TRK, VID, ALL). (default: ALL) +log_type all + +########################################################### +# Capture device options +############################################################ + +# Videodevice to be used for capturing (default /dev/video0) +# for FreeBSD default is /dev/bktr0 +#videodevice /dev/video0 + +# v4l2_palette allows to choose preferable palette to be use by motion +# to capture from those supported by your videodevice. (default: 17) +# E.g. if your videodevice supports both V4L2_PIX_FMT_SBGGR8 and +# V4L2_PIX_FMT_MJPEG then motion will by default use V4L2_PIX_FMT_MJPEG. +# Setting v4l2_palette to 2 forces motion to use V4L2_PIX_FMT_SBGGR8 +# instead. +# +# Values : +# V4L2_PIX_FMT_SN9C10X : 0 'S910' +# V4L2_PIX_FMT_SBGGR16 : 1 'BYR2' +# V4L2_PIX_FMT_SBGGR8 : 2 'BA81' +# V4L2_PIX_FMT_SPCA561 : 3 'S561' +# V4L2_PIX_FMT_SGBRG8 : 4 'GBRG' +# V4L2_PIX_FMT_SGRBG8 : 5 'GRBG' +# V4L2_PIX_FMT_PAC207 : 6 'P207' +# V4L2_PIX_FMT_PJPG : 7 'PJPG' +# V4L2_PIX_FMT_MJPEG : 8 'MJPEG' +# V4L2_PIX_FMT_JPEG : 9 'JPEG' +# V4L2_PIX_FMT_RGB24 : 10 'RGB3' +# V4L2_PIX_FMT_SPCA501 : 11 'S501' +# V4L2_PIX_FMT_SPCA505 : 12 'S505' +# V4L2_PIX_FMT_SPCA508 : 13 'S508' +# V4L2_PIX_FMT_UYVY : 14 'UYVY' +# V4L2_PIX_FMT_YUYV : 15 'YUYV' +# V4L2_PIX_FMT_YUV422P : 16 '422P' +# V4L2_PIX_FMT_YUV420 : 17 'YU12' +# +v4l2_palette 17 + +# Tuner device to be used for capturing using tuner as source (default /dev/tuner0) +# This is ONLY used for FreeBSD. Leave it commented out for Linux +; tunerdevice /dev/tuner0 + +# The video input to be used (default: -1) +# Should normally be set to 0 or 1 for video/TV cards, and -1 for USB cameras +input -1 + +# The video norm to use (only for video capture and TV tuner cards) +# Values: 0 (PAL), 1 (NTSC), 2 (SECAM), 3 (PAL NC no colour). Default: 0 (PAL) +norm 0 + +# The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0) +frequency 0 + +# Rotate image this number of degrees. The rotation affects all saved images as +# well as movies. Valid values: 0 (default = no rotation), 90, 180 and 270. +rotate 0 + +# Image width (pixels). Valid range: Camera dependent, default: 352 +#width 320 +width 640 +#width 1024 + +# Image height (pixels). Valid range: Camera dependent, default: 288 +#height 188 +height 480 +#height 576 + +# Maximum number of frames to be captured per second. +# Valid range: 2-100. Default: 100 (almost no limit). +framerate 4 + +# Minimum time in seconds between capturing picture frames from the camera. +# Default: 0 = disabled - the capture rate is given by the camera framerate. +# This option is used when you want to capture images at a rate lower than 2 per second. +minimum_frame_time 0 + +# URL to use if you are using a network camera, size will be autodetected (incl http:// ftp:// mjpg:// or file:///) +# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined +;netcam_url http://127.0.0.1/cgi-bin/raspicam.sh + +# Username and password for network camera (only if required). Default: not defined +# Syntax is user:password +; netcam_userpass value + +# The setting for keep-alive of network socket, should improve performance on compatible net cameras. +# off: The historical implementation using HTTP/1.0, closing the socket after each http request. +# force: Use HTTP/1.0 requests with keep alive header to reuse the same connection. +# on: Use HTTP/1.1 requests that support keep alive as default. +# Default: off +netcam_keepalive off + +# URL to use for a netcam proxy server, if required, e.g. "http://myproxy". +# If a port number other than 80 is needed, use "http://myproxy:1234". +# Default: not defined +; netcam_proxy value + +# Set less strict jpeg checks for network cameras with a poor/buggy firmware. +# Default: off +netcam_tolerant_check off + +# Let motion regulate the brightness of a video device (default: off). +# The auto_brightness feature uses the brightness option as its target value. +# If brightness is zero auto_brightness will adjust to average brightness value 128. +# Only recommended for cameras without auto brightness +auto_brightness off + +# Set the initial brightness of a video device. +# If auto_brightness is enabled, this value defines the average brightness level +# which Motion will try and adjust to. +# Valid range 0-255, default 0 = disabled +brightness 0 + +# Set the contrast of a video device. +# Valid range 0-255, default 0 = disabled +contrast 0 + +# Set the saturation of a video device. +# Valid range 0-255, default 0 = disabled +saturation 0 + +# Set the hue of a video device (NTSC feature). +# Valid range 0-255, default 0 = disabled +hue 0 + +############################################################ +# File "camera" support - read raw YUV data from a file +############################################################ +#filecam_path /home/pi/test-cap/motion-mmal.capture + +############################################################ +# OpenMax/MMAL camera support for Raspberry Pi +############################################################ +mmalcam_name vc.ril.camera +mmalcam_control_params --exposure night +#mmalcam_control_params -ss 900000 -ISO 1600 --exposure night + +#mmalcam_raw_capture_file /run/shm/motion-mmal.capture + +# Switch this setting to "on" to use the still image mode of the Pi's camera +# instead of video. This gives a wider field of view, but requires +# a much slower frame-rate to achieve exposure stability +# (e.g. 0.25 fps or slower). You can use the minimum_frame_time +# parameter above to achieve this + +mmalcam_use_still off + +# Set this to non-zero value to enable a secondary buffer used for outputting results +# This value is used to multiply the original width & height settings to decide the +# secondary buffer size, so you can use it to capture a high-res image as well as a +# normal (low) resolution one for the primary. +# +# Use the options output_secondary_pictures, ffmpeg_output_secondary_movies, stream_secondary +# to use this high-res secondary image as the source for picture, movie and web stream output, +# while the actual motion detection is performed on the lower-resolution primary image (and +# thus use less CPU power). + +mmalcam_secondary_buffer_upscale 4 + +# Pre-encode the secondary buffer to this jpeg quality +# Note that overlaid text or motion location markers will NOT be drawn into a pre-encoded buffer +# as encoding is done on the MMAL side before the image is passed to motion. +# Default: 0 (off), range 1-100 +mmalcam_secondary_buffer_jpeg 50 + +############################################################ +# Round Robin (multiple inputs on same video device name) +############################################################ + +# Number of frames to capture in each roundrobin step (default: 1) +roundrobin_frames 1 + +# Number of frames to skip before each roundrobin step (default: 1) +roundrobin_skip 1 + +# Try to filter out noise generated by roundrobin (default: off) +switchfilter off + + +############################################################ +# Motion Detection Settings: +############################################################ + +# Threshold for number of changed pixels in an image that +# triggers motion detection (default: 1500) +threshold 1500 + +# Automatically tune the threshold down if possible (default: off) +threshold_tune off + +# Noise threshold for the motion detection (default: 32) +noise_level 32 + +# Automatically tune the noise threshold (default: on) +noise_tune on + +# Despeckle motion image using (e)rode or (d)ilate or (l)abel (Default: not defined) +# Recommended value is EedDl. Any combination (and number of) of E, e, d, and D is valid. +# (l)abeling must only be used once and the 'l' must be the last letter. +# Comment out to disable +despeckle_filter EedDl + +# Detect motion in predefined areas (1 - 9). Areas are numbered like that: 1 2 3 +# A script (on_area_detected) is started immediately when motion is 4 5 6 +# detected in one of the given areas, but only once during an event. 7 8 9 +# One or more areas can be specified with this option. Take care: This option +# does NOT restrict detection to these areas! (Default: not defined) +; area_detect value + +# PGM file to use as a sensitivity mask. +# Full path name to. (Default: not defined) +; mask_file value + +# Dynamically create a mask file during operation (default: 0) +# Adjust speed of mask changes from 0 (off) to 10 (fast) +smart_mask_speed 0 + +# Ignore sudden massive light intensity changes given as a percentage of the picture +# area that changed intensity. Valid range: 0 - 100 , default: 0 = disabled +lightswitch 25 + +# Picture frames must contain motion at least the specified number of frames +# in a row before they are detected as true motion. At the default of 1, all +# motion is detected. Valid range: 1 to thousands, recommended 1-5 +minimum_motion_frames 5 + +# Specifies the number of pre-captured (buffered) pictures from before motion +# was detected that will be output at motion detection. +# Recommended range: 0 to 5 (default: 0) +# Do not use large values! Large values will cause Motion to skip video frames and +# cause unsmooth movies. To smooth movies use larger values of post_capture instead. +pre_capture 4 + +# Number of frames to capture after motion is no longer detected (default: 0) +post_capture 4 + +# Event Gap is the seconds of no motion detection that triggers the end of an event. +# An event is defined as a series of motion images taken within a short timeframe. +# Recommended value is 60 seconds (Default). The value -1 is allowed and disables +# events causing all Motion to be written to one single movie file and no pre_capture. +# If set to 0, motion is running in gapless mode. Movies don't have gaps anymore. An +# event ends right after no more motion is detected and post_capture is over. +event_gap 10 + +# Maximum length in seconds of a movie +# When value is exceeded a new movie file is created. (Default: 0 = infinite) +max_movie_time 0 + +# Always save images even if there was no motion (default: off) +emulate_motion off + + +############################################################ +# Image File Output +############################################################ + +# Output 'normal' pictures when motion is detected (default: on) +# Valid values: on, off, first, best, center +# When set to 'first', only the first picture of an event is saved. +# Picture with most motion of an event is saved when set to 'best'. +# Picture with motion nearest center of picture is saved when set to 'center'. +# Can be used as preview shot for the corresponding movie. +output_pictures best + +# Output pictures with only the pixels moving object (ghost images) (default: off) +output_debug_pictures off + +# Output pictures from secondary capture (see mmalcam_secondary_buffer_upscale above) +# Use this to perform motion detection on a low-res image and record a high-res output +output_secondary_pictures on + +# Output both primary and secondary pictures - only if output_secondary_pictures is on. (default: off) +output_both_pictures on + +# The quality (in percent) to be used by the jpeg compression (default: 75) +quality 80 + +# Type of output images +# Valid values: jpeg, ppm (default: jpeg) +picture_type jpeg + +############################################################ +# FFMPEG related options +# Film (movies) file output, and deinterlacing of the video input +# The options movie_filename and timelapse_filename are also used +# by the ffmpeg feature +############################################################ + +# Use ffmpeg to encode movies in realtime (default: off) +ffmpeg_output_movies on + +# Use ffmpeg to make movies with only the pixels moving +# object (ghost images) (default: off) +ffmpeg_output_debug_movies off + +# Use ffmpeg to make movies using any enabled secondary buffer (default: off) +ffmpeg_output_secondary_movies off + +# Use ffmpeg to encode a timelapse movie +# Default value 0 = off - else save frame every Nth second +ffmpeg_timelapse 0 + +# The file rollover mode of the timelapse video +# Valid values: hourly, daily (default), weekly-sunday, weekly-monday, monthly, manual +ffmpeg_timelapse_mode daily + +# Bitrate to be used by the ffmpeg encoder (default: 400000) +# This option is ignored if ffmpeg_variable_bitrate is not 0 (disabled) +ffmpeg_bps 500000 + +# Enables and defines variable bitrate for the ffmpeg encoder. +# ffmpeg_bps is ignored if variable bitrate is enabled. +# Valid values: 0 (default) = fixed bitrate defined by ffmpeg_bps, +# or the range 2 - 31 where 2 means best quality and 31 is worst. +ffmpeg_variable_bitrate 10 + +# Codec to used by ffmpeg for the video compression. +# Timelapse mpegs are always made in mpeg1 format independent from this option. +# Supported formats are: mpeg1 (ffmpeg-0.4.8 only), mpeg4 (default), and msmpeg4. +# mpeg1 - gives you files with extension .mpg +# mpeg4 or msmpeg4 - gives you files with extension .avi +# msmpeg4 is recommended for use with Windows Media Player because +# it requires no installation of codec on the Windows client. +# swf - gives you a flash film with extension .swf +# flv - gives you a flash video with extension .flv +# ffv1 - FF video codec 1 for Lossless Encoding ( experimental ) +# mov - QuickTime ( testing ) +# ogg - Ogg/Theora ( testing ) +#ffmpeg_video_codec msmpeg4 +ffmpeg_video_codec mpeg4 + +# Use ffmpeg to deinterlace video. Necessary if you use an analog camera +# and see horizontal combing on moving objects in video or pictures. +# (default: off) +ffmpeg_deinterlace off + +############################################################ +# SDL Window +############################################################ + +# Number of motion thread to show in SDL Window (default: 0 = disabled) +#sdl_threadnr 0 + +############################################################ +# External pipe to video encoder +# Replacement for FFMPEG builtin encoder for ffmpeg_output_movies only. +# The options movie_filename and timelapse_filename are also used +# by the ffmpeg feature +############################################################# + +# Bool to enable or disable extpipe (default: off) +use_extpipe off +extpipe_secondary on + +# External program (full path and opts) to pipe raw video to +# Generally, use '-' for STDIN... +extpipe mencoder -demuxer rawvideo -rawvideo w=1280:h=960:i420 -ovc x264 -x264encopts bframes=4:frameref=1:subq=1:scenecut=-1:nob_adapt:threads=1:keyint=1000:8x8dct:vbv_bufsize=4000:crf=24:partitions=i8x8,i4x4:vbv_maxrate=800:no-chroma-me -of avi -o %f.avi - -fps %fps +;extpipe mencoder -demuxer rawvideo -rawvideo w=1024:h=576 -ovc raw -of avi -o %f.avi - -fps 15 -noskip + + + +############################################################ +# Snapshots (Traditional Periodic Webcam File Output) +############################################################ + +# Make automated snapshot every N seconds (default: 0 = disabled) +snapshot_interval 10 + + +############################################################ +# Text Display +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, %T = HH:MM:SS, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, \n = new line, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event - do not use with text_event! +# You can put quotation marks around the text to allow +# leading spaces +############################################################ + +# Locate and draw a box around the moving object. +# Valid values: on, off, preview (default: off) +# Set to 'preview' will only draw a box in preview_shot pictures. +locate_motion_mode off + +# Set the look and style of the locate box if enabled. +# Valid values: box, redbox, cross, redcross (default: box) +# Set to 'box' will draw the traditional box. +# Set to 'redbox' will draw a red box. +# Set to 'cross' will draw a little cross to mark center. +# Set to 'redcross' will draw a little red cross to mark center. +locate_motion_style redbox + +# Draws the timestamp using same options as C function strftime(3) +# Default: %Y-%m-%d\n%T = date in ISO format and time in 24 hour clock +# Text is placed in lower right corner +text_right %Y-%m-%d\n%T-%q + +# Draw a user defined text on the images using same options as C function strftime(3) +# Default: Not defined = no text +# Text is placed in lower left corner +;text_left CAMERA %t + +# Draw the number of changed pixed on the images (default: off) +# Will normally be set to off except when you setup and adjust the motion settings +# Text is placed in upper right corner +text_changes on + +# This option defines the value of the special event conversion specifier %C +# You can use any conversion specifier in this option except %C. Date and time +# values are from the timestamp of the first image in the current event. +# Default: %Y%m%d%H%M%S +# The idea is that %C can be used filenames and text_left/right for creating +# a unique identifier for each event. +text_event %Y%m%d%H%M%S + +# Draw characters at twice normal size on images. (default: off) +text_double off + +# Text to include in a JPEG EXIF comment +# May be any text, including conversion specifiers. +# The EXIF timestamp is included independent of this text. +;exif_text %i%J/%K%L +exif_text "Test exif" + +############################################################ +# Target Directories and filenames For Images And Films +# For the options snapshot_, picture_, movie_ and timelapse_filename +# you can use conversion specifiers +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# Quotation marks round string are allowed. +############################################################ + +# Target base directory for pictures and films +# Recommended to use absolute path. (Default: current working directory) +target_dir /run/shm + +# File path for snapshots (jpeg or ppm) relative to target_dir +# Default: %v-%Y%m%d%H%M%S-snapshot +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-snapshot +# File extension .jpg or .ppm is automatically added so do not include this. +# Note: A symbolic link called lastsnap.jpg created in the target_dir will always +# point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' +#snapshot_filename %v-%Y%m%d%H%M%S-snapshot +snapshot_filename lastsnap + +# File path for motion triggered images (jpeg or ppm) relative to target_dir +# Default: %v-%Y%m%d%H%M%S-%q +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H/%M/%S-%q +# File extension .jpg or .ppm is automatically added so do not include this +# Set to 'preview' together with best-preview feature enables special naming +# convention for preview shots. See motion guide for details +#picture_filename %v-%Y%m%d%H%M%S-%q +#picture_filename %v-%Y%m%d%H%M%S-%q-%D +picture_filename preview + +# File path for motion triggered ffmpeg films (movies) relative to target_dir +# Default: %v-%Y%m%d%H%M%S +# Default value is equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d/%H%M%S +# File extension .mpg or .avi is automatically added so do not include this +# This option was previously called ffmpeg_filename +#movie_filename %v-%Y%m%d%H%M%S +movie_filename %Y%m%d%H%M%S + +# File path for timelapse movies relative to target_dir +# Default: %Y%m%d-timelapse +# Default value is near equivalent to legacy oldlayout option +# For Motion 3.0 compatible mode choose: %Y/%m/%d-timelapse +# File extension .mpg is automatically added so do not include this +timelapse_filename %Y%m%d-timelapse + +############################################################ +# Global Network Options +############################################################ +# Enable or disable IPV6 for http control and stream (default: off ) +ipv6_enabled off + +############################################################ +# Live Stream Server +############################################################ + +# The mini-http server listens to this port for requests (default: 0 = disabled) +stream_port 8081 + +# Quality of the jpeg (in percent) images produced (default: 50) +stream_quality 50 + +# Output frames at 1 fps when no motion is detected and increase to the +# rate given by stream_maxrate when motion is detected (default: off) +stream_motion off + +# Use secondary buffer as stream image source (default: off) +stream_secondary off + +# Maximum framerate for stream streams (default: 1) +stream_maxrate 4 + +# Restrict stream connections to localhost only (default: on) +stream_localhost off + +# Limits the number of images per connection (default: 0 = unlimited) +# Number can be defined by multiplying actual stream rate by desired number of seconds +# Actual stream rate is the smallest of the numbers framerate and stream_maxrate +stream_limit 0 + +# Set the authentication method (default: 0) +# 0 = disabled +# 1 = Basic authentication +# 2 = MD5 digest (the safer authentication) +stream_auth_method 0 + +# Authentication for the stream. Syntax username:password +# Default: not defined (Disabled) +; stream_authentication username:password + + +############################################################ +# HTTP Based Control +############################################################ + +# TCP/IP port for the http server to listen on (default: 0 = disabled) +webcontrol_port 8080 + +# Restrict control connections to localhost only (default: on) +webcontrol_localhost off + +# Output for http server, select off to choose raw text plain (default: on) +webcontrol_html_output on + +# Authentication for the http based control. Syntax username:password +# Default: not defined (Disabled) +; webcontrol_authentication username:password + + +############################################################ +# Tracking (Pan/Tilt) +############################################################# + +# Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic, 5=uvcvideo, 6=servo) +# The generic type enables the definition of motion center and motion size to +# be used with the conversion specifiers for options like on_motion_detected +track_type 0 + +# Enable auto tracking (default: off) +track_auto off + +# Serial port of motor (default: none) +;track_port /dev/ttyS0 + +# Motor number for x-axis (default: 0) +;track_motorx 0 + +# Set motorx reverse (default: 0) +;track_motorx_reverse 0 + +# Motor number for y-axis (default: 0) +;track_motory 1 + +# Set motory reverse (default: 0) +;track_motory_reverse 0 + +# Maximum value on x-axis (default: 0) +;track_maxx 200 + +# Minimum value on x-axis (default: 0) +;track_minx 50 + +# Maximum value on y-axis (default: 0) +;track_maxy 200 + +# Minimum value on y-axis (default: 0) +;track_miny 50 + +# Center value on x-axis (default: 0) +;track_homex 128 + +# Center value on y-axis (default: 0) +;track_homey 128 + +# ID of an iomojo camera if used (default: 0) +track_iomojo_id 0 + +# Angle in degrees the camera moves per step on the X-axis +# with auto-track (default: 10) +# Currently only used with pwc type cameras +track_step_angle_x 10 + +# Angle in degrees the camera moves per step on the Y-axis +# with auto-track (default: 10) +# Currently only used with pwc type cameras +track_step_angle_y 10 + +# Delay to wait for after tracking movement as number +# of picture frames (default: 10) +track_move_wait 10 + +# Speed to set the motor to (stepper motor option) (default: 255) +track_speed 255 + +# Number of steps to make (stepper motor option) (default: 40) +track_stepsize 40 + + +############################################################ +# External Commands, Warnings and Logging: +# You can use conversion specifiers for the on_xxxx commands +# %Y = year, %m = month, %d = date, +# %H = hour, %M = minute, %S = second, +# %v = event, %q = frame number, %t = thread (camera) number, +# %D = changed pixels, %N = noise level, +# %i and %J = width and height of motion area, +# %K and %L = X and Y coordinates of motion center +# %C = value defined by text_event +# %f = filename with full path +# %n = number indicating filetype +# Both %f and %n are only defined for on_picture_save, +# on_movie_start and on_movie_end +# Quotation marks round string are allowed. +############################################################ + +# Do not sound beeps when detecting motion (default: on) +# Note: Motion never beeps when running in daemon mode. +quiet on + +# Command to be executed when an event starts. (default: none) +# An event starts at first motion detected after a period of no motion defined by event_gap +; on_event_start value + +# Command to be executed when an event ends after a period of no motion +# (default: none). The period of no motion is defined by option event_gap. +#on_event_end /home/pi/bin/motion_event_end.sh %v + +# Command to be executed when a picture (.ppm|.jpg) is saved (default: none) +# To give the filename as an argument to a command append it with %f +on_picture_save /home/pi/bin/motion_resize.py %f %n + +# Command to be executed when a motion frame is detected (default: none) +; on_motion_detected value + +# Command to be executed when motion in a predefined area is detected +# Check option 'area_detect'. (default: none) +; on_area_detected value + +# Command to be executed when a movie file (.mpg|.avi) is created. (default: none) +# To give the filename as an argument to a command append it with %f +; on_movie_start value + +# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none) +# To give the filename as an argument to a command append it with %f +#on_movie_end /home/pi/bin/motion_resize.py %f + +# Command to be executed when a camera can't be opened or if it is lost +# NOTE: There is situations when motion don't detect a lost camera! +# It depends on the driver, some drivers dosn't detect a lost camera at all +# Some hangs the motion thread. Some even hangs the PC! (default: none) +; on_camera_lost value + +##################################################################### +# Common Options for database features. +# Options require database options to be active also. +##################################################################### + +# Log to the database when creating motion triggered picture file (default: on) +; sql_log_picture on + +# Log to the database when creating a snapshot image file (default: on) +; sql_log_snapshot on + +# Log to the database when creating motion triggered movie file (default: off) +; sql_log_movie off + +# Log to the database when creating timelapse movies file (default: off) +; sql_log_timelapse off + +# SQL query string that is sent to the database +# Use same conversion specifiers has for text features +# Additional special conversion specifiers are +# %n = the number representing the file_type +# %f = filename with full path +# Default value: +# Create tables : +## +# Mysql +# CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp(14), event_time_stamp timestamp(14)); +# +# Postgresql +# CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp without time zone, event_time_stamp timestamp without time zone); +# +# insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') +; sql_query insert into security(camera, filename, frame, file_type, time_stamp, event_time_stamp) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') + + +############################################################ +# Database Options +############################################################ + +# database type : mysql, postgresql, sqlite3 (default : not defined) +; database_type value + +# database to log to (default: not defined) +; database_dbname value + +# The host on which the database is located (default: localhost) +; database_host value + +# User account name for database (default: not defined) +; database_user value + +# User password for database (default: not defined) +; database_password value + +# Port on which the database is located +# mysql 3306 , postgresql 5432 (default: not defined) +; database_port value + +############################################################ +# Database Options For SQLite3 +############################################################ + +# SQLite3 database (file path) (default: not defined) +; sqlite3_db value + + + +############################################################ +# Video Loopback Device (vloopback project) +############################################################ + +# Output images to a video4linux loopback device +# The value '-' means next available (default: not defined) +; video_pipe value + +# Output motion images to a video4linux loopback device +# The value '-' means next available (default: not defined) +; motion_video_pipe value + + +############################################################## +# Thread config files - One for each camera. +# Except if only one camera - You only need this config file. +# If you have more than one camera you MUST define one thread +# config file for each camera in addition to this config file. +############################################################## + +# Remember: If you have more than one camera you must have one +# thread file for each camera. E.g. 2 cameras requires 3 files: +# This motion.conf file AND thread1.conf and thread2.conf. +# Only put the options that are unique to each camera in the +# thread config files. +; thread /usr/local/etc/thread1.conf +; thread /usr/local/etc/thread2.conf +; thread /usr/local/etc/thread3.conf +; thread /usr/local/etc/thread4.conf + diff --git a/event.c b/event.c index 87f679a..dd55972 100644 --- a/event.c +++ b/event.c @@ -633,7 +633,7 @@ static void event_ffmpeg_newfile(struct context *cnt, int type ATTRIBUTE_UNUSED, if ((cnt->ffmpeg_output = ffmpeg_open((char *)cnt->conf.ffmpeg_video_codec, cnt->newfilename, y, u, v, width, height, cnt->movie_fps, cnt->conf.ffmpeg_bps, - cnt->conf.ffmpeg_vbr)) == NULL) { + cnt->conf.ffmpeg_vbr,TIMELAPSE_NONE)) == NULL) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: ffopen_open error creating (new) file [%s]", cnt->newfilename); cnt->finish = 1; @@ -661,7 +661,7 @@ static void event_ffmpeg_newfile(struct context *cnt, int type ATTRIBUTE_UNUSED, if ((cnt->ffmpeg_output_debug = ffmpeg_open((char *)cnt->conf.ffmpeg_video_codec, cnt->motionfilename, y, u, v, cnt->imgs.width, cnt->imgs.height, cnt->movie_fps, cnt->conf.ffmpeg_bps, - cnt->conf.ffmpeg_vbr)) == NULL) { + cnt->conf.ffmpeg_vbr,TIMELAPSE_NONE)) == NULL) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: ffopen_open error creating (motion) file [%s]", cnt->motionfilename); cnt->finish = 1; @@ -685,6 +685,7 @@ static void event_ffmpeg_timelapse(struct context *cnt, if (!cnt->ffmpeg_timelapse) { char tmp[PATH_MAX]; const char *timepath; + char codec_mpeg[5] = "mpeg4"; /* * conf.timepath would normally be defined but if someone deleted it by control interface @@ -714,9 +715,9 @@ static void event_ffmpeg_timelapse(struct context *cnt, } if ((cnt->ffmpeg_timelapse = - ffmpeg_open((char *)TIMELAPSE_CODEC, cnt->timelapsefilename, y, u, v, + ffmpeg_open(codec_mpeg, cnt->timelapsefilename, y, u, v, width, height, 24, cnt->conf.ffmpeg_bps, - cnt->conf.ffmpeg_vbr)) == NULL) { + cnt->conf.ffmpeg_vbr,TIMELAPSE_NEW)) == NULL) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: ffopen_open error creating " "(timelapse) file [%s]", cnt->timelapsefilename); cnt->finish = 1; diff --git a/ffmpeg.c b/ffmpeg.c index 78248c1..df2b47e 100644 --- a/ffmpeg.c +++ b/ffmpeg.c @@ -8,277 +8,110 @@ * The contents of this file has been derived from output_example.c * and apiexample.c from the FFmpeg distribution. * + * This file has been modified so that only major versions greater than + * 53 are supported. */ +#include "config.h" + #ifdef HAVE_FFMPEG #include "ffmpeg.h" #include "motion.h" -#if LIBAVCODEC_BUILD > 4680 -/* - * FFmpeg after build 4680 doesn't have support for mpeg1 videos with - * non-standard framerates. Previous builds contained a broken hack - * that padded with B frames to obtain the correct framerate. - */ -# define FFMPEG_NO_NONSTD_MPEG1 -# ifdef __GNUC__ -/* #warning is a non-standard gcc extension */ -# warning ************************************************** -# warning Your version of FFmpeg is newer than version 0.4.8 -# warning Newer versions of ffmpeg do not support MPEG1 with -# warning non-standard framerate. MPEG1 will be disabled for -# warning normal video output. You can still use mpeg4 and -# warning and mpeg4ms which are both better in terms of size -# warning and quality. MPEG1 is always used for timelapse. -# warning Please read the Motion Guide for more information. -# warning Note that this is not an error message! -# warning ************************************************** -# endif /* __GNUC__ */ -#endif /* LIBAVCODEC_BUILD > 4680 */ - -#if defined LIBAVFORMAT_VERSION_MAJOR && defined LIBAVFORMAT_VERSION_MINOR -# if LIBAVFORMAT_VERSION_MAJOR < 53 && LIBAVFORMAT_VERSION_MINOR < 45 -# define GUESS_NO_DEPRECATED -# endif -#endif - -#if LIBAVFORMAT_BUILD >= 4616 -/* - * The API for av_write_frame changed with FFmpeg version 0.4.9pre1. - * It now uses an AVPacket struct instead of direct parameters to the - * function. - */ -# define FFMPEG_AVWRITEFRAME_NEWAPI -#endif /* LIBAVFORMAT_BUILD >= 4616 */ - -#if LIBAVFORMAT_BUILD >= 4629 -/* - * In this build/header version, the codec member of struct AVStream - * was changed to a pointer so changes to AVCodecContext shouldn't - * break binary compatibility with AVStream. - */ -# define AVSTREAM_CODEC_PTR(avs_ptr) (avs_ptr->codec) -#else -# define AVSTREAM_CODEC_PTR(avs_ptr) (&avs_ptr->codec) -#endif /* LIBAVFORMAT_BUILD >= 4629 */ - -// AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c) -// (54*2^16 | 6*2^8 | 100) -#if LIBAVFORMAT_BUILD >= 3540580 -#define FF_API_NEW_AVIO -#define URL_RDONLY AVIO_FLAG_READ /**< read-only */ -#define URL_WRONLY AVIO_FLAG_WRITE /**< write-only */ -#define URL_RDWR AVIO_FLAG_READ_WRITE /**< read-write pseudo flag */ -#endif +#define AVSTREAM_CODEC_PTR(avs_ptr) (avs_ptr->codec) -#define CODEC_ID_MSMPEG4V2 AV_CODEC_ID_MSMPEG4V2 -#define CODEC_ID_FLV1 AV_CODEC_ID_FLV1 -#define CODEC_ID_FFV1 AV_CODEC_ID_FFV1 -#define CODEC_ID_NONE AV_CODEC_ID_NONE -#define CODEC_ID_MPEG2VIDEO AV_CODEC_ID_MPEG2VIDEO -#define CODEC_ID_H264 AV_CODEC_ID_H264 -#define CODEC_ID_HEVC AV_CODEC_ID_HEVC -#define CODEC_ID_MPEG1VIDEO AV_CODEC_ID_MPEG2VIDEO -/* - * Name of custom file protocol for appending to existing files instead - * of truncating. - */ -#define APPEND_PROTO "appfile" +/**************************************************************************** + * The section below is the "my" section of functions. + * These are designed to be extremely simple version specific + * variants of the libav functions. + ****************************************************************************/ +#if (LIBAVFORMAT_VERSION_MAJOR >= 55) || ((LIBAVFORMAT_VERSION_MAJOR == 54) && (LIBAVFORMAT_VERSION_MINOR > 6)) -/* Some forward-declarations. */ -int ffmpeg_put_frame(struct ffmpeg *, AVFrame *); -void ffmpeg_cleanups(struct ffmpeg *); -AVFrame *ffmpeg_prepare_frame(struct ffmpeg *, unsigned char *, - unsigned char *, unsigned char *); +#define MY_FLAG_READ AVIO_FLAG_READ +#define MY_FLAG_WRITE AVIO_FLAG_WRITE +#define MY_FLAG_READ_WRITE AVIO_FLAG_READ_WRITE -/* This is the trailer used to end mpeg1 videos. */ -static unsigned char mpeg1_trailer[] = {0x00, 0x00, 0x01, 0xb7}; +#else //Older versions +#define MY_FLAG_READ URL_RDONLY +#define MY_FLAG_WRITE URL_WRONLY +#define MY_FLAG_READ_WRITE URL_RDWR -// FFMPEG API changed in 0.8 -#if defined FF_API_NEW_AVIO +#endif +/*********************************************/ +#if (LIBAVFORMAT_VERSION_MAJOR >= 56) -// TODO +#define MY_CODEC_ID_MSMPEG4V2 AV_CODEC_ID_MSMPEG4V2 +#define MY_CODEC_ID_FLV1 AV_CODEC_ID_FLV1 +#define MY_CODEC_ID_FFV1 AV_CODEC_ID_FFV1 +#define MY_CODEC_ID_NONE AV_CODEC_ID_NONE +#define MY_CODEC_ID_MPEG2VIDEO AV_CODEC_ID_MPEG2VIDEO +#define MY_CODEC_ID_H264 AV_CODEC_ID_H264 +#define MY_CODEC_ID_HEVC AV_CODEC_ID_HEVC - #else -/** - * file_open_append - * Append version of the file open function used in libavformat when opening - * an ordinary file. The original file open function truncates an existing - * file, but this version appends to it instead. - * - * Returns 0 on success and AVERROR(ENOENT) on error. - * - */ -static int file_open_append(URLContext *h, const char *filename, int flags) -{ - const char *colon; - const char *mode; - FILE *fh; - size_t bufsize = 0; - - /* Skip past the protocol part of filename. */ - colon = strchr(filename, ':'); - - if (colon) - filename = colon + 1; - - - if (flags & URL_RDWR) { - mode = "ab+"; - bufsize = BUFSIZE_1MEG; - } else if (flags & URL_WRONLY) { - mode = "ab"; - bufsize = BUFSIZE_1MEG; - } else { - mode = "rb"; - } - - fh = myfopen(filename, mode, bufsize); - if (fh == NULL) - return AVERROR(ENOENT); - - h->priv_data = (void *)fh; - return 0; +#define MY_CODEC_ID_MSMPEG4V2 CODEC_ID_MSMPEG4V2 +#define MY_CODEC_ID_FLV1 CODEC_ID_FLV1 +#define MY_CODEC_ID_FFV1 CODEC_ID_FFV1 +#define MY_CODEC_ID_NONE CODEC_ID_NONE +#define MY_CODEC_ID_MPEG2VIDEO CODEC_ID_MPEG2VIDEO +#define MY_CODEC_ID_H264 CODEC_ID_H264 +#define MY_CODEC_ID_HEVC CODEC_ID_H264 +#endif +/*********************************************/ +AVFrame *my_frame_alloc(void){ + AVFrame *pic; +#if (LIBAVFORMAT_VERSION_MAJOR >= 55) + pic = av_frame_alloc(); +#else + pic = avcodec_alloc_frame(); +#endif + return pic; } - -/* - * URLProtocol entry for the append file protocol, which we use for mpeg1 videos - * in order to get append behavior with url_fopen. - * - * Libavformat uses protocols for achieving flexibility when handling files - * and other resources. A call to url_fopen will eventually be redirected to - * a protocol-specific open function. - * - * The remaining functions (for writing, seeking etc.) are set in ffmpeg_init. - */ -URLProtocol mpeg1_file_protocol = { - .name = APPEND_PROTO, - .url_open = file_open_append -}; - - -#ifdef HAVE_FFMPEG_NEW - -/* file_procotol has been removed from avio.h */ -#ifdef FFMPEG_NEW_INCLUDES -#include +/*********************************************/ +void my_frame_free(AVFrame *frame){ +#if (LIBAVFORMAT_VERSION_MAJOR >= 55) + av_frame_free(&frame); #else -#include "avstring.h" + av_freep(&frame); #endif - +} +/**************************************************************************** + **************************************************************************** + ****************************************************************************/ /** - * file_open + * timelapse_exists + * Determines whether the timelapse file exists * + * Returns + * 0: File doesn't exist + * 1: File exists */ -static int file_open(URLContext *h, const char *filename, int flags) -{ - const char *mode; - FILE *fh; - size_t bufsize = 0; - - av_strstart(filename, "file:", &filename); - - if (flags & URL_RDWR) { - mode = "wb+"; - bufsize = BUFSIZE_1MEG; - } else if (flags & URL_WRONLY) { - mode = "wb"; - bufsize = BUFSIZE_1MEG; - } else { - mode = "rb"; +int timelapse_exists(const char *fname){ + FILE *file; + file = fopen(fname, "r"); + if (file) + { + fclose(file); + return 1; } - fh = myfopen(filename, mode, bufsize); - if (fh == NULL) - return AVERROR(ENOENT); - h->priv_data = (void *)fh; return 0; } +int timelapse_append(struct ffmpeg *ffmpeg, AVPacket pkt){ + FILE *file; -/** - * file_read - */ -static int file_read(URLContext *h, unsigned char *buf, int size) -{ - FILE *fh = (FILE *)h->priv_data; - return fread(buf, 1, size, fh); -} - -/** - * file_write - */ -static int file_write(URLContext *h, unsigned char *buf, int size) -{ - FILE *fh = (FILE *)h->priv_data; - return fwrite(buf, 1, size, fh); -} - -/** - * file_seek - */ -static int64_t file_seek(URLContext *h, int64_t pos, int whence) -{ - FILE *fh = (FILE *)h->priv_data; - if (fseek(fh, pos, whence)) - return -1; - return ftell(fh); -} - -/** - * file_close - */ -static int file_close(URLContext *h) -{ - FILE *fh = (FILE *)h->priv_data; - return myfclose(fh); -} - -URLProtocol file_protocol = { - "file", - file_open, - file_read, - file_write, - file_seek, - file_close, -#if LIBAVFORMAT_BUILD >= (52<<16 | 31<<8) - NULL, - NULL, - NULL, -#endif -}; - -#endif // HAVE_FFMPEG_NEW + file = fopen(ffmpeg->oc->filename, "a"); + if (!file) return -1; -#endif // FF_API_NEW_AVIO + fwrite(pkt.data,1,pkt.size,file); -/** - * mpeg1_write_trailer - * We set AVOutputFormat->write_trailer to this function for mpeg1. That way, - * the mpeg1 video gets a proper trailer when it is closed. - * - * Returns 0 - * - */ -static int mpeg1_write_trailer(AVFormatContext *s) -{ -#if defined FF_API_NEW_AVIO - avio_write(s->pb, mpeg1_trailer, 4); - avio_flush(s->pb); -#elif LIBAVFORMAT_BUILD >= (52<<16) - put_buffer(s->pb, mpeg1_trailer, 4); - put_flush_packet(s->pb); -#else - put_buffer(&s->pb, mpeg1_trailer, 4); - put_flush_packet(&s->pb); -#endif /* FF_API_NEW_AVIO -- LIBAVFORMAT_BUILD >= (52<<16) */ + fclose(file); - return 0; /* success */ + return 0; } - /** * ffmpeg_init * Initializes for libavformat. @@ -286,42 +119,15 @@ static int mpeg1_write_trailer(AVFormatContext *s) * Returns * Function returns nothing. */ -void ffmpeg_init() -{ +void ffmpeg_init(){ MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s: ffmpeg LIBAVCODEC_BUILD %d" - " LIBAVFORMAT_BUILD %d", LIBAVCODEC_BUILD, + " LIBAVFORMAT_BUILD %d", LIBAVCODEC_BUILD, LIBAVFORMAT_BUILD); av_register_all(); - -#if LIBAVCODEC_BUILD > 4680 + avcodec_register_all(); av_log_set_callback((void *)ffmpeg_avcodec_log); av_log_set_level(AV_LOG_ERROR); -#endif - -#if defined FF_API_NEW_AVIO -#else - /* - * Copy the functions to use for the append file protocol from the standard - * file protocol. - */ - mpeg1_file_protocol.url_read = file_protocol.url_read; - mpeg1_file_protocol.url_write = file_protocol.url_write; - mpeg1_file_protocol.url_seek = file_protocol.url_seek; - mpeg1_file_protocol.url_close = file_protocol.url_close; - -/* Register the append file protocol. */ -#ifdef have_av_register_protocol2 - av_register_protocol2(&mpeg1_file_protocol, sizeof(mpeg1_file_protocol)); -#elif defined have_av_register_protocol - av_register_protocol(&mpeg1_file_protocol); -#else -# warning av_register_protocolXXX missing -#endif - -#endif // FF_API_NEW_AVIO - } - /** * get_oformat * Obtains the output format used for the specified codec. For mpeg4 codecs, @@ -331,104 +137,55 @@ void ffmpeg_init() * Returns * AVOutputFormat pointer or NULL if any error happens. */ -static AVOutputFormat *get_oformat(const char *codec, char *filename) -{ +static AVOutputFormat *get_oformat(const char *codec, char *filename){ const char *ext; AVOutputFormat *of = NULL; /* * Here, we use guess_format to automatically setup the codec information. * If we are using msmpeg4, manually set that codec here. - * We also dynamically add the file extension to the filename here. This was - * done to support both mpeg1 and mpeg4 codecs since they have different extensions. + * We also dynamically add the file extension to the filename here. */ - if ((strcmp(codec, TIMELAPSE_CODEC) == 0) -#ifndef FFMPEG_NO_NONSTD_MPEG1 - || (strcmp(codec, "mpeg1") == 0) -#endif - ) { - ext = ".mpg"; - /* - * We use "mpeg1video" for raw mpeg1 format. Using "mpeg" would - * result in a muxed output file, which isn't appropriate here. - */ -#ifdef GUESS_NO_DEPRECATED - of = guess_format("mpeg1video", NULL, NULL); -#else - of = av_guess_format("mpeg1video", NULL, NULL); -#endif - /* But we want the trailer to be correctly written. */ - if (of) - of->write_trailer = mpeg1_write_trailer; - -#ifdef FFMPEG_NO_NONSTD_MPEG1 - } else if (strcmp(codec, "mpeg1") == 0) { - MOTION_LOG(WRN, TYPE_ENCODER, NO_ERRNO, "%s: *** mpeg1 support for normal" - " videos has been disabled ***"); - return NULL; -#endif + if (strcmp(codec, "tlapse") == 0) { + ext = ".swf"; + of = av_guess_format("swf", NULL, NULL); + if (of) of->video_codec = MY_CODEC_ID_MPEG2VIDEO; } else if (strcmp(codec, "mpeg4") == 0) { ext = ".avi"; -#ifdef GUESS_NO_DEPRECATED - of = guess_format("mpeg1video", NULL, NULL); -#else of = av_guess_format("avi", NULL, NULL); -#endif } else if (strcmp(codec, "msmpeg4") == 0) { ext = ".avi"; -#ifdef GUESS_NO_DEPRECATED - of = guess_format("mpeg1video", NULL, NULL); -#else of = av_guess_format("avi", NULL, NULL); -#endif /* Manually override the codec id. */ - if (of) - of->video_codec = CODEC_ID_MSMPEG4V2; - + if (of) of->video_codec = MY_CODEC_ID_MSMPEG4V2; } else if (strcmp(codec, "swf") == 0) { ext = ".swf"; -#ifdef GUESS_NO_DEPRECATED - of = guess_format("mpeg1video", NULL, NULL); -#else of = av_guess_format("swf", NULL, NULL); -#endif } else if (strcmp(codec, "flv") == 0) { ext = ".flv"; -#ifdef GUESS_NO_DEPRECATED - of = guess_format("mpeg1video", NULL, NULL); -#else of = av_guess_format("flv", NULL, NULL); -#endif - of->video_codec = CODEC_ID_FLV1; + of->video_codec = MY_CODEC_ID_FLV1; } else if (strcmp(codec, "ffv1") == 0) { ext = ".avi"; -#ifdef GUESS_NO_DEPRECATED - of = guess_format("mpeg1video", NULL, NULL); -#else of = av_guess_format("avi", NULL, NULL); -#endif - /* - * Use the FFMPEG Lossless Video codec (experimental!). - * Requires strict_std_compliance to be <= -2 - */ - if (of) - of->video_codec = CODEC_ID_FFV1; - + if (of) of->video_codec = MY_CODEC_ID_FFV1; } else if (strcmp(codec, "mov") == 0) { ext = ".mov"; -#ifdef GUESS_NO_DEPRECATED - of = guess_format("mpeg1video", NULL, NULL); -#else of = av_guess_format("mov", NULL, NULL); -#endif - } - else if (strcmp (codec, "ogg") == 0) - { + } else if (strcmp (codec, "ogg") == 0){ ext = ".ogg"; -#ifdef GUESS_NO_DEPRECATED - of = guess_format ("ogg", NULL, NULL); -#else of = av_guess_format ("ogg", NULL, NULL); -#endif + } else if (strcmp (codec, "mp4") == 0){ + ext = ".mp4"; + of = av_guess_format ("mp4", NULL, NULL); + of->video_codec = MY_CODEC_ID_H264; + } else if (strcmp (codec, "mkv") == 0){ + ext = ".mkv"; + of = av_guess_format ("matroska", NULL, NULL); + of->video_codec = MY_CODEC_ID_H264; + } else if (strcmp (codec, "hevc") == 0){ + ext = ".mp4"; + of = av_guess_format ("mp4", NULL, NULL); + of->video_codec = MY_CODEC_ID_HEVC; } else { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: ffmpeg_video_codec option value" " %s is not supported", codec); @@ -436,8 +193,7 @@ static AVOutputFormat *get_oformat(const char *codec, char *filename) } if (!of) { - MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not guess format for %s", - codec); + MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not guess format for %s", codec); return NULL; } @@ -446,7 +202,6 @@ static AVOutputFormat *get_oformat(const char *codec, char *filename) return of; } - /** * ffmpeg_open * Opens an mpeg file using the new libavformat method. Both mpeg1 @@ -459,45 +214,42 @@ static AVOutputFormat *get_oformat(const char *codec, char *filename) */ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, unsigned char *y, unsigned char *u, unsigned char *v, - int width, int height, int rate, int bps, int vbr) + int width, int height, int rate, int bps, int vbr, int tlapse) { AVCodecContext *c; AVCodec *codec; struct ffmpeg *ffmpeg; - int is_mpeg1; int ret; + char errstr[128]; + AVDictionary *opts = 0; + /* * Allocate space for our ffmpeg structure. This structure contains all the * codec and image information we need to generate movies. - * FIXME when motion exits we should close the movie to ensure that - * ffmpeg is freed. */ ffmpeg = mymalloc(sizeof(struct ffmpeg)); - memset(ffmpeg, 0, sizeof(struct ffmpeg)); ffmpeg->vbr = vbr; + ffmpeg->tlapse = tlapse; /* Store codec name in ffmpeg->codec, with buffer overflow check. */ snprintf(ffmpeg->codec, sizeof(ffmpeg->codec), "%s", ffmpeg_video_codec); /* Allocation the output media context. */ -#ifdef have_avformat_alloc_context ffmpeg->oc = avformat_alloc_context(); -#elif defined have_av_avformat_alloc_context - ffmpeg->oc = av_alloc_format_context(); -#else - ffmpeg->oc = av_mallocz(sizeof(AVFormatContext)); -#endif if (!ffmpeg->oc) { - MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Memory error while allocating" - " output media context"); + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Could not allocate output context"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Setup output format */ - ffmpeg->oc->oformat = get_oformat(ffmpeg_video_codec, filename); + if (ffmpeg->tlapse == TIMELAPSE_APPEND){ + ffmpeg->oc->oformat = get_oformat("tlapse", filename); + } else { + ffmpeg->oc->oformat = get_oformat(ffmpeg_video_codec, filename); + } if (!ffmpeg->oc->oformat) { ffmpeg_cleanups(ffmpeg); return NULL; @@ -505,146 +257,96 @@ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, snprintf(ffmpeg->oc->filename, sizeof(ffmpeg->oc->filename), "%s", filename); - /* Create a new video stream and initialize the codecs. */ ffmpeg->video_st = NULL; - if (ffmpeg->oc->oformat->video_codec != CODEC_ID_NONE) { -#if defined FF_API_NEW_AVIO - ffmpeg->video_st = avformat_new_stream(ffmpeg->oc, NULL /* Codec */); -#else - ffmpeg->video_st = av_new_stream(ffmpeg->oc, 0); -#endif + if (ffmpeg->oc->oformat->video_codec != MY_CODEC_ID_NONE) { + + codec = avcodec_find_encoder(ffmpeg->oc->oformat->video_codec); + if (!codec) { + MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Codec %s not found", ffmpeg_video_codec); + ffmpeg_cleanups(ffmpeg); + return NULL; + } + + ffmpeg->video_st = avformat_new_stream(ffmpeg->oc, codec); if (!ffmpeg->video_st) { - MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: av_new_stream - could" - " not alloc stream"); + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Could not alloc stream"); ffmpeg_cleanups(ffmpeg); return NULL; } } else { /* We did not get a proper video codec. */ - MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Failed to obtain a proper" - " video codec"); + MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not get the codec"); ffmpeg_cleanups(ffmpeg); return NULL; } ffmpeg->c = c = AVSTREAM_CODEC_PTR(ffmpeg->video_st); c->codec_id = ffmpeg->oc->oformat->video_codec; -#if LIBAVCODEC_VERSION_MAJOR < 53 - c->codec_type = CODEC_TYPE_VIDEO; -#else c->codec_type = AVMEDIA_TYPE_VIDEO; -#endif - is_mpeg1 = c->codec_id == CODEC_ID_MPEG1VIDEO; - - if (strcmp(ffmpeg_video_codec, "ffv1") == 0) - c->strict_std_compliance = -2; - - /* Uncomment to allow non-standard framerates. */ - //c->strict_std_compliance = -1; - - /* Set default parameters */ - c->bit_rate = bps; - c->width = width; - c->height = height; -#if LIBAVCODEC_BUILD >= 4754 - /* Frame rate = 1/time_base, so we set 1/rate, not rate/1 */ + c->bit_rate = bps; + c->width = width; + c->height = height; c->time_base.num = 1; c->time_base.den = rate; -#else - c->frame_rate = rate; - c->frame_rate_base = 1; -#endif /* LIBAVCODEC_BUILD >= 4754 */ - - MOTION_LOG(INF, TYPE_ENCODER, NO_ERRNO, "%s FPS %d", - rate); - - if (vbr) - c->flags |= CODEC_FLAG_QSCALE; - - /* - * Set codec specific parameters. - * Set intra frame distance in frames depending on codec. - */ - c->gop_size = is_mpeg1 ? 10 : 12; - - /* Some formats want stream headers to be separate. */ + c->gop_size = 12; + c->pix_fmt = MY_PIX_FMT_YUV420P; + c->max_b_frames = 0; + + if (c->codec_id == MY_CODEC_ID_H264 || + c->codec_id == MY_CODEC_ID_HEVC){ + av_dict_set(&opts, "preset", "ultrafast", 0); + av_dict_set(&opts, "crf", "18", 0); + av_dict_set(&opts, "tune", "zerolatency", 0); + } + if (strcmp(ffmpeg_video_codec, "ffv1") == 0) c->strict_std_compliance = -2; + if (vbr) c->flags |= CODEC_FLAG_QSCALE; if (!strcmp(ffmpeg->oc->oformat->name, "mp4") || !strcmp(ffmpeg->oc->oformat->name, "mov") || !strcmp(ffmpeg->oc->oformat->name, "3gp")) { c->flags |= CODEC_FLAG_GLOBAL_HEADER; } -#if defined FF_API_NEW_AVIO -// pass the options to avformat_write_header directly -#else - /* Set the output parameters (must be done even if no parameters). */ - if (av_set_parameters(ffmpeg->oc, NULL) < 0) { - MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: av_set_parameters error:" - " Invalid output format parameters"); - ffmpeg_cleanups(ffmpeg); - return NULL; - } -#endif - - /* Dump the format settings. This shows how the various streams relate to each other. */ - //dump_format(ffmpeg->oc, 0, filename, 1); - - /* - * Now that all the parameters are set, we can open the video - * codec and allocate the necessary encode buffers. - */ - codec = avcodec_find_encoder(c->codec_id); - - if (!codec) { - MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Codec %s not found", - ffmpeg_video_codec); - ffmpeg_cleanups(ffmpeg); - return NULL; - } - - /* Set the picture format - need in ffmpeg starting round April-May 2005 */ - c->pix_fmt = PIX_FMT_YUV420P; - - /* Get a mutex lock. */ pthread_mutex_lock(&global_lock); - - /* Open the codec */ -#if defined FF_API_NEW_AVIO - ret = avcodec_open2(c, codec, NULL /* options */ ); -#else - ret = avcodec_open(c, codec); -#endif - + ret = avcodec_open2(c, codec, &opts); + pthread_mutex_unlock(&global_lock); if (ret < 0) { - /* Release the lock. */ + if (codec->supported_framerates) { + const AVRational *fps = codec->supported_framerates; + while (fps->num) { + MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s Reported FPS Supported %d/%d", fps->num, fps->den); + fps++; + } + } + int chkrate = 1; + pthread_mutex_lock(&global_lock); + while ((chkrate < 36) && (ret != 0)) { + c->time_base.den = chkrate; + ret = avcodec_open2(c, codec, &opts); + chkrate++; + } pthread_mutex_unlock(&global_lock); - MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: avcodec_open - could not open codec %s", - ffmpeg_video_codec); - ffmpeg_cleanups(ffmpeg); - return NULL; - } + if (ret < 0){ + av_strerror(ret, errstr, sizeof(errstr)); + MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not open codec %s",errstr); + av_dict_free(&opts); + ffmpeg_cleanups(ffmpeg); + return NULL; + } - /* Release the lock. */ - pthread_mutex_unlock(&global_lock); + } + av_dict_free(&opts); + MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s Selected Output FPS %d", c->time_base.den); ffmpeg->video_outbuf = NULL; - if (!(ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE)) { - /* - * Allocate output buffer - * XXX: API change will be done - * ffmpeg->video_outbuf_size = 200000 - */ ffmpeg->video_outbuf_size = ffmpeg->c->width * 512; ffmpeg->video_outbuf = mymalloc(ffmpeg->video_outbuf_size); } - /* Allocate the encoded raw picture. */ - ffmpeg->picture = avcodec_alloc_frame(); + ffmpeg->picture = my_frame_alloc(); if (!ffmpeg->picture) { - MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: avcodec_alloc_frame -" - " could not alloc frame"); + MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: could not alloc frame"); ffmpeg_cleanups(ffmpeg); return NULL; } @@ -653,7 +355,6 @@ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, if (ffmpeg->vbr) ffmpeg->picture->quality = ffmpeg->vbr; - /* Set the frame data. */ ffmpeg->picture->data[0] = y; ffmpeg->picture->data[1] = u; @@ -662,74 +363,45 @@ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, ffmpeg->picture->linesize[1] = ffmpeg->c->width / 2; ffmpeg->picture->linesize[2] = ffmpeg->c->width / 2; - /* Open the output file, if needed. */ - if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { - char file_proto[256]; - /* - * Use append file protocol for mpeg1, to get the append behavior from - * url_fopen, but no protocol (=> default) for other codecs. - */ - if (is_mpeg1) -#if defined FF_API_NEW_AVIO - snprintf(file_proto, sizeof(file_proto), "%s", filename); -#else - snprintf(file_proto, sizeof(file_proto), APPEND_PROTO ":%s", filename); -#endif - else - snprintf(file_proto, sizeof(file_proto), "%s", filename); - - -#if defined FF_API_NEW_AVIO - if (avio_open(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { -#else - if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { -#endif - /* Path did not exist? */ - if (errno == ENOENT) { - /* Create path for file (don't use file_proto)... */ - if (create_path(filename) == -1) { + /* Open the output file, if needed. */ + if ((timelapse_exists(filename) == 0) || (ffmpeg->tlapse != TIMELAPSE_APPEND)) { + if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { + if (avio_open(&ffmpeg->oc->pb, filename, MY_FLAG_WRITE) < 0) { + if (errno == ENOENT) { + if (create_path(filename) == -1) { + ffmpeg_cleanups(ffmpeg); + return NULL; + } + if (avio_open(&ffmpeg->oc->pb, filename, MY_FLAG_WRITE) < 0) { + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: error opening file %s", filename); + ffmpeg_cleanups(ffmpeg); + return NULL; + } + /* Permission denied */ + } else if (errno == EACCES) { + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO,"%s: Permission denied. %s",filename); ffmpeg_cleanups(ffmpeg); return NULL; - } - -#if defined FF_API_NEW_AVIO - if (avio_open(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { -#else - /* And retry opening the file (use file_proto). */ - if (url_fopen(&ffmpeg->oc->pb, file_proto, URL_WRONLY) < 0) { -#endif - MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: url_fopen -" - " error opening file %s", filename); + } else { + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error opening file %s", filename); ffmpeg_cleanups(ffmpeg); return NULL; } - /* Permission denied */ - } else if (errno == EACCES) { - MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, - "%s: url_fopen - error opening file %s" - " ... check access rights to target directory", - filename); - ffmpeg_cleanups(ffmpeg); - return NULL; - } else { - MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error opening file %s", - filename); - ffmpeg_cleanups(ffmpeg); - return NULL; } } + /* Write the stream header, For the TIMELAPSE_APPEND + * we write the data via standard file I/O so we close the + * items here + */ + avformat_write_header(ffmpeg->oc, NULL); + if (ffmpeg->tlapse == TIMELAPSE_APPEND) { + av_write_trailer(ffmpeg->oc); + avio_close(ffmpeg->oc->pb); + } } - - /* Write the stream header, if any. */ -#if defined FF_API_NEW_AVIO - avformat_write_header(ffmpeg->oc, NULL); -#else - av_write_header(ffmpeg->oc); -#endif // FF_API_NEW_AVIO return ffmpeg; } - /** * ffmpeg_cleanups * Clean up ffmpeg struct if something was wrong. @@ -737,34 +409,19 @@ struct ffmpeg *ffmpeg_open(char *ffmpeg_video_codec, char *filename, * Returns * Function returns nothing. */ -void ffmpeg_cleanups(struct ffmpeg *ffmpeg) -{ - unsigned int i; +void ffmpeg_cleanups(struct ffmpeg *ffmpeg){ /* Close each codec */ if (ffmpeg->video_st) { pthread_mutex_lock(&global_lock); -#if LIBAVCODEC_BUILD > 4680 - if (ffmpeg->video_st->codec->priv_data != NULL) -#endif - avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); + avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); pthread_mutex_unlock(&global_lock); - av_freep(&ffmpeg->picture); - free(ffmpeg->video_outbuf); } - - /* Free the streams */ - for (i = 0; i < ffmpeg->oc->nb_streams; i++) - av_freep(&ffmpeg->oc->streams[i]); - - /* Free the stream */ - av_free(ffmpeg->oc); -#if LIBAVFORMAT_BUILD >= 4629 - av_free(ffmpeg->c); -#endif + free(ffmpeg->video_outbuf); + av_freep(&ffmpeg->picture); + avformat_free_context(ffmpeg->oc); free(ffmpeg); } - /** * ffmpeg_close * Closes a video file. @@ -772,46 +429,29 @@ void ffmpeg_cleanups(struct ffmpeg *ffmpeg) * Returns * Function returns nothing. */ -void ffmpeg_close(struct ffmpeg *ffmpeg) -{ - unsigned int i; +void ffmpeg_close(struct ffmpeg *ffmpeg){ + if (ffmpeg->tlapse != TIMELAPSE_APPEND) { + av_write_trailer(ffmpeg->oc); + } /* Close each codec */ if (ffmpeg->video_st) { pthread_mutex_lock(&global_lock); avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); pthread_mutex_unlock(&global_lock); - av_freep(&ffmpeg->picture); - free(ffmpeg->video_outbuf); } - - /* Write the trailer, if any. */ - av_write_trailer(ffmpeg->oc); - - /* Free the streams. */ - for (i = 0; i < ffmpeg->oc->nb_streams; i++) - av_freep(&ffmpeg->oc->streams[i]); + av_freep(&ffmpeg->picture); + free(ffmpeg->video_outbuf); if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { - /* Close the output file. */ -#if defined FF_API_NEW_AVIO - avio_close(ffmpeg->oc->pb); -#elif LIBAVFORMAT_BUILD >= (52<<16) - url_fclose(ffmpeg->oc->pb); -#else - url_fclose(&ffmpeg->oc->pb); -#endif /* FF_API_NEW_AVIO -- LIBAVFORMAT_BUILD >= (52<<16) */ + if (ffmpeg->tlapse != TIMELAPSE_APPEND) { + avio_close(ffmpeg->oc->pb); + } } - - - /* Free the stream. */ - av_free(ffmpeg->oc); -#if LIBAVFORMAT_BUILD >= 4629 - av_free(ffmpeg->c); -#endif + avformat_free_context(ffmpeg->oc); free(ffmpeg); -} +} /** * ffmpeg_put_image * Puts the image pointed to by ffmpeg->picture. @@ -819,11 +459,33 @@ void ffmpeg_close(struct ffmpeg *ffmpeg) * Returns * value returned by ffmpeg_put_frame call. */ -int ffmpeg_put_image(struct ffmpeg *ffmpeg) -{ - return ffmpeg_put_frame(ffmpeg, ffmpeg->picture); -} +int ffmpeg_put_image(struct ffmpeg *ffmpeg){ + /* A return code of -2 is thrown by the put_frame + * when a image is buffered. For timelapse, we absolutely + * never want a frame buffered so we keep sending back the + * the same pic until it flushes or fails in a different way + */ + int retcd; + int cnt = 0; + + retcd = ffmpeg_put_frame(ffmpeg, ffmpeg->picture); + while ((retcd == -2) && (ffmpeg->tlapse != TIMELAPSE_NONE)) { + retcd = ffmpeg_put_frame(ffmpeg, ffmpeg->picture); + cnt++; + if (cnt > 50){ + MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Excessive attempts to clear buffered packet"); + retcd = -1; + } + } + //non timelapse buffered is ok + if (retcd == -2){ + retcd = 0; + MOTION_LOG(DBG, TYPE_ENCODER, NO_ERRNO, "%s: Buffered packet"); + } + + return retcd; +} /** * ffmpeg_put_other_image * Puts an arbitrary picture defined by y, u and v. @@ -834,23 +496,39 @@ int ffmpeg_put_image(struct ffmpeg *ffmpeg) * 0 if error allocating picture. */ int ffmpeg_put_other_image(struct ffmpeg *ffmpeg, unsigned char *y, - unsigned char *u, unsigned char *v) -{ + unsigned char *u, unsigned char *v){ AVFrame *picture; - int ret = 0; + int retcd = 0; + int cnt = 0; /* Allocate the encoded raw picture. */ picture = ffmpeg_prepare_frame(ffmpeg, y, u, v); if (picture) { - ret = ffmpeg_put_frame(ffmpeg, picture); - if (!ret) - av_free(picture); + /* A return code of -2 is thrown by the put_frame + * when a image is buffered. For timelapse, we absolutely + * never want a frame buffered so we keep sending back the + * the same pic until it flushes or fails in a different way + */ + retcd = ffmpeg_put_frame(ffmpeg, picture); + while ((retcd == -2) && (ffmpeg->tlapse != TIMELAPSE_NONE)) { + retcd = ffmpeg_put_frame(ffmpeg, picture); + cnt++; + if (cnt > 50){ + MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Excessive attempts to clear buffered packet"); + retcd = -1; + } + } + //non timelapse buffered is ok + if (retcd == -2){ + retcd = 0; + MOTION_LOG(DBG, TYPE_ENCODER, NO_ERRNO, "%s: Buffered packet"); + } + av_free(picture); } - return ret; + return retcd; } - /** * ffmpeg_put_frame * Encodes and writes a video frame using the av_write_frame API. This is @@ -859,87 +537,112 @@ int ffmpeg_put_other_image(struct ffmpeg *ffmpeg, unsigned char *y, * Returns * Number of bytes written or -1 if any error happens. */ -int ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic) -{ - int out_size, ret, got_packet_ptr; - -#ifdef FFMPEG_AVWRITEFRAME_NEWAPI +int ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic){ +/** + * Since the logic,return values and conditions changed so + * dramatically between versions, the encoding of the frame + * is 100% blocked based upon Libav/FFMpeg version + */ +#if (LIBAVFORMAT_VERSION_MAJOR >= 55) || ((LIBAVFORMAT_VERSION_MAJOR == 54) && (LIBAVFORMAT_VERSION_MINOR > 6)) + int retcd; + int got_packet_ptr; AVPacket pkt; + char errstr[128]; av_init_packet(&pkt); /* Init static structure. */ - pkt.stream_index = ffmpeg->video_st->index; -#endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ - if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { - /* Raw video case. The API will change slightly in the near future for that. */ -#ifdef FFMPEG_AVWRITEFRAME_NEWAPI -# if LIBAVCODEC_VERSION_MAJOR < 53 - pkt.flags |= PKT_FLAG_KEY; -# else - pkt.flags |= AV_PKT_FLAG_KEY; -# endif + pkt.stream_index = ffmpeg->video_st->index; + pkt.flags |= AV_PKT_FLAG_KEY; pkt.data = (uint8_t *)pic; pkt.size = sizeof(AVPicture); - ret = av_write_frame(ffmpeg->oc, &pkt); -#else - ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, - (uint8_t *)pic, sizeof(AVPicture)); -#endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ } else { - /* Encodes the image. */ -#if defined FF_API_NEW_AVIO - pkt.data = ffmpeg->video_outbuf; - pkt.size = ffmpeg->video_outbuf_size; - - out_size = avcodec_encode_video2(AVSTREAM_CODEC_PTR(ffmpeg->video_st), + pkt.data = NULL; + pkt.size = 0; + retcd = avcodec_encode_video2(AVSTREAM_CODEC_PTR(ffmpeg->video_st), &pkt, pic, &got_packet_ptr); - if (out_size < 0) - // Error encondig - out_size = 0; - else - out_size = pkt.size; -#else - out_size = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), + if (retcd < 0 ){ + av_strerror(retcd, errstr, sizeof(errstr)); + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error encoding video:%s",errstr); + //Packet is freed upon failure of encoding + return -1; + } + if (got_packet_ptr == 0){ + //Buffered packet. Throw special return code + av_free_packet(&pkt); + return -2; + } + if (pkt.pts != AV_NOPTS_VALUE) + pkt.pts = av_rescale_q(pkt.pts, + ffmpeg->video_st->codec->time_base, + ffmpeg->video_st->time_base); + if (pkt.dts != AV_NOPTS_VALUE) + pkt.dts = av_rescale_q(pkt.dts, + ffmpeg->video_st->codec->time_base, + ffmpeg->video_st->time_base); + } + if (ffmpeg->tlapse == TIMELAPSE_APPEND) { + retcd = timelapse_append(ffmpeg, pkt); + } else { + retcd = av_write_frame(ffmpeg->oc, &pkt); + } + av_free_packet(&pkt); + + if (retcd != 0) { + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error while writing video frame"); + ffmpeg_cleanups(ffmpeg); + return -1; + } + + return retcd; + +#else // Old versions of Libav/FFmpeg + int retcd; + AVPacket pkt; + + av_init_packet(&pkt); /* Init static structure. */ + pkt.stream_index = ffmpeg->video_st->index; + if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { + // Raw video case. + pkt.size = sizeof(AVPicture); + pkt.data = (uint8_t *)pic; + pkt.flags |= AV_PKT_FLAG_KEY; + } else { + retcd = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), ffmpeg->video_outbuf, ffmpeg->video_outbuf_size, pic); -#endif - /* If zero size, it means the image was buffered. */ - if (out_size != 0) { - /* - * Writes the compressed frame in the media file. - * XXX: in case of B frames, the pts is not yet valid. - */ -#ifdef FFMPEG_AVWRITEFRAME_NEWAPI - pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; - - if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) -# if LIBAVCODEC_VERSION_MAJOR < 53 - pkt.flags |= PKT_FLAG_KEY; -# else - pkt.flags |= AV_PKT_FLAG_KEY; -# endif - - pkt.data = ffmpeg->video_outbuf; - pkt.size = out_size; - ret = av_write_frame(ffmpeg->oc, &pkt); -#else - ret = av_write_frame(ffmpeg->oc, ffmpeg->video_st->index, - ffmpeg->video_outbuf, out_size); -#endif /* FFMPEG_AVWRITEFRAME_NEWAPI */ - - } else { - ret = 0; + if (retcd < 0 ){ + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error encoding video"); + av_free_packet(&pkt); + return -1; + } + if (retcd == 0 ){ + // No bytes encoded => buffered=>special handling + av_free_packet(&pkt); + return -2; } + + pkt.size = retcd; + pkt.data = ffmpeg->video_outbuf; + pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; + if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) + pkt.flags |= AV_PKT_FLAG_KEY; } + if (ffmpeg->tlapse == TIMELAPSE_APPEND) { + retcd = timelapse_append(ffmpeg, pkt); + } else { + retcd = av_write_frame(ffmpeg->oc, &pkt); + } + av_free_packet(&pkt); - if (ret != 0) { - MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error while writing" - " video frame"); + if (retcd != 0) { + MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error while writing video frame"); ffmpeg_cleanups(ffmpeg); return -1; } - return ret; + return retcd; + +#endif } /** @@ -957,7 +660,7 @@ AVFrame *ffmpeg_prepare_frame(struct ffmpeg *ffmpeg, unsigned char *y, { AVFrame *picture; - picture = avcodec_alloc_frame(); + picture = my_frame_alloc(); if (!picture) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Could not alloc frame"); @@ -978,43 +681,6 @@ AVFrame *ffmpeg_prepare_frame(struct ffmpeg *ffmpeg, unsigned char *y, return picture; } - - -/** - * ffmpeg_deinterlace - * Make the image suitable for deinterlacing using ffmpeg, then deinterlace the picture. - * - * Parameters - * img image in YUV420P format - * width image width in pixels - * height image height in pixels - * - * Returns - * Function returns nothing. - * img contains deinterlaced image - */ -void ffmpeg_deinterlace(unsigned char *img, int width, int height) -{ - AVPicture picture; - int width2 = width / 2; - - picture.data[0] = img; - picture.data[1] = img + width * height; - picture.data[2] = picture.data[1] + (width * height) / 4; - picture.linesize[0] = width; - picture.linesize[1] = width2; - picture.linesize[2] = width2; - - /* We assume using 'PIX_FMT_YUV420P' always */ - avpicture_deinterlace(&picture, &picture, PIX_FMT_YUV420P, width, height); - -#if !defined(__SSE_MATH__) && (defined(__i386__) || defined(__x86_64__)) - __asm__ __volatile__ ( "emms"); -#endif - - return; -} - /** * ffmpeg_avcodec_log * Handle any logging output from the ffmpeg library avcodec. diff --git a/ffmpeg.h b/ffmpeg.h index a1e2e9d..3cbea4d 100644 --- a/ffmpeg.h +++ b/ffmpeg.h @@ -1,39 +1,35 @@ #ifndef _INCLUDE_FFMPEG_H_ #define _INCLUDE_FFMPEG_H_ +#include +#include + +#include "config.h" + #ifdef HAVE_FFMPEG -#include -#ifdef FFMPEG_NEW_INCLUDES +#include #include -#else -#include -#endif +#include -#ifndef AVERROR /* 0.4.8 & 0.4.9-pre1 */ +#if (LIBAVFORMAT_VERSION_MAJOR >= 56) + +#define MY_PIX_FMT_YUV420P AV_PIX_FMT_YUV420P +#define MY_PIX_FMT_YUVJ420P AV_PIX_FMT_YUVJ420P -#if EINVAL > 0 -#define AVERROR(e) (-(e)) -#define AVUNERROR(e) (-(e)) #else -/* Some platforms have E* and errno already negated. */ -#define AVERROR(e) (e) -#define AVUNERROR(e) (e) -#endif -#endif /* AVERROR */ +#define MY_PIX_FMT_YUV420P PIX_FMT_YUV420P +#define MY_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P -#endif /* HAVE_FFMPEG */ +#endif -#include -#include +#endif /* HAVE_FFMPEG */ -/* - * Define a codec name/identifier for timelapse videos, so that we can - * differentiate between normal mpeg1 videos and timelapse videos. - */ -#define TIMELAPSE_CODEC "mpeg1_tl" +#define TIMELAPSE_NONE 0 /* No timelapse, regular processing */ +#define TIMELAPSE_APPEND 1 /* Use append version of timelapse */ +#define TIMELAPSE_NEW 2 /* Use create new file version of timelapse */ struct ffmpeg { #ifdef HAVE_FFMPEG @@ -48,6 +44,7 @@ struct ffmpeg { void *udata; /* U & V planes for greyscale images */ int vbr; /* variable bitrate setting */ char codec[20]; /* codec name */ + int tlapse; #else int dummy; #endif @@ -56,23 +53,18 @@ struct ffmpeg { /* Initialize FFmpeg stuff. Needs to be called before ffmpeg_open. */ void ffmpeg_init(void); -/* - * Open an mpeg file. This is a generic interface for opening either an mpeg1 or - * an mpeg4 video. If non-standard mpeg1 isn't supported (FFmpeg build > 4680), - * calling this function with "mpeg1" as codec results in an error. To create a - * timelapse video, use TIMELAPSE_CODEC as codec name. - */ struct ffmpeg *ffmpeg_open( - char *ffmpeg_video_codec, - char *filename, + char *ffmpeg_video_codec, + char *filename, unsigned char *y, /* YUV420 Y plane */ unsigned char *u, /* YUV420 U plane */ unsigned char *v, /* YUV420 V plane */ int width, - int height, + int height, int rate, /* framerate, fps */ int bps, /* bitrate; bits per second */ - int vbr /* variable bitrate */ + int vbr, /* variable bitrate */ + int tlapse ); /* Puts the image pointed to by the picture member of struct ffmpeg. */ @@ -80,19 +72,25 @@ int ffmpeg_put_image(struct ffmpeg *); /* Puts the image defined by u, y and v (YUV420 format). */ int ffmpeg_put_other_image( - struct ffmpeg *ffmpeg, - unsigned char *y, - unsigned char *u, + struct ffmpeg *ffmpeg, + unsigned char *y, + unsigned char *u, unsigned char *v ); /* Closes the mpeg file. */ void ffmpeg_close(struct ffmpeg *); -/* Deinterlace the image. */ -void ffmpeg_deinterlace(unsigned char *, int, int); - /* Setup an avcodec log handler. */ void ffmpeg_avcodec_log(void *, int, const char *, va_list); +#ifdef HAVE_FFMPEG +AVFrame *my_frame_alloc(void); +void my_frame_free(AVFrame *frame); +int ffmpeg_put_frame(struct ffmpeg *, AVFrame *); +void ffmpeg_cleanups(struct ffmpeg *); +AVFrame *ffmpeg_prepare_frame(struct ffmpeg *, unsigned char *, + unsigned char *, unsigned char *); +#endif + #endif /* _INCLUDE_FFMPEG_H_ */ diff --git a/motion.c b/motion.c index b379197..d73364a 100644 --- a/motion.c +++ b/motion.c @@ -1416,13 +1416,13 @@ static void *motion_loop(void *arg) cnt->missing_frame_counter = 0; #ifdef HAVE_FFMPEG - /* Deinterlace the image with ffmpeg, before the image is modified. */ + /* Deinterlace the image with ffmpeg, before the image is modified. if (cnt->conf.ffmpeg_deinterlace) { ffmpeg_deinterlace(cnt->current_image->image, cnt->imgs.width, cnt->imgs.height); if (cnt->current_image->secondary_image && cnt->imgs.secondary_type == SECONDARY_TYPE_RAW) { ffmpeg_deinterlace(cnt->current_image->secondary_image, cnt->imgs.secondary_width, cnt->imgs.secondary_height); } - } + } */ #endif /* diff --git a/picture.c b/picture.c index 8c36793..4bacb7a 100644 --- a/picture.c +++ b/picture.c @@ -1133,6 +1133,12 @@ void put_fixed_mask(struct context *cnt, const char *file) void put_image(struct context *cnt, char* fullfilename, struct image_data * imgdat, int ftype) { if (imgdat->secondary_image && cnt->conf.output_secondary_pictures) { + if (cnt->conf.output_both_pictures) { + put_picture(cnt, fullfilename, imgdat->image, ftype); + fullfilename[strlen(fullfilename) - 3] = 'J'; + fullfilename[strlen(fullfilename) - 2] = 'P'; + fullfilename[strlen(fullfilename) - 1] = 'G'; + } if (cnt->imgs.secondary_type == SECONDARY_TYPE_RAW) { put_sized_picture(cnt, fullfilename, imgdat->secondary_image, cnt->imgs.secondary_width, cnt->imgs.secondary_height, ftype); }