Commit c58954cc authored by Steven Walters's avatar Steven Walters Committed by Fiona Glaser

Add video filtering system to x264cli

Similar to mplayer's -vf system.
Supports some basic operations like resizing and cropping.  Will support more in the future.
See the help for more details.
parent da978ebe
......@@ -13,10 +13,12 @@ SRCS = common/mc.c common/predict.c common/pixel.c common/macroblock.c \
encoder/set.c encoder/macroblock.c encoder/cabac.c \
encoder/cavlc.c encoder/encoder.c encoder/lookahead.c
SRCCLI = x264.c input/timecode.c \
input/yuv.c input/y4m.c output/raw.c \
output/matroska.c output/matroska_ebml.c \
output/flv.c output/flv_bytestream.c
SRCCLI = x264.c input/input.c input/timecode.c input/raw.c input/y4m.c \
output/raw.c output/matroska.c output/matroska_ebml.c \
output/flv.c output/flv_bytestream.c filters/filters.c \
filters/video/video.c filters/video/source.c filters/video/internal.c \
filters/video/resize.c filters/video/cache.c filters/video/fix_vfr_pts.c \
filters/video/select_every.c filters/video/crop.c
SRCSO =
......@@ -129,7 +131,7 @@ $(SONAME): .depend $(OBJS) $(OBJASM) $(OBJSO)
$(CC) -shared -o $@ $(OBJS) $(OBJASM) $(OBJSO) $(SOFLAGS) $(LDFLAGS)
x264$(EXE): $(OBJCLI) libx264.a
$(CC) -o $@ $+ $(LDFLAGS) $(LDFLAGSCLI)
$(CC) -o $@ $+ $(LDFLAGSCLI) $(LDFLAGS)
checkasm: tools/checkasm.o libx264.a
$(CC) -o $@ $+ $(LDFLAGS)
......
......@@ -220,7 +220,7 @@ void x264_frame_delete( x264_frame_t *frame )
int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src )
{
int i_csp = src->img.i_csp & X264_CSP_MASK;
if( i_csp != X264_CSP_I420 && i_csp != X264_CSP_YV12 )
if( i_csp <= X264_CSP_NONE || i_csp >= X264_CSP_MAX )
{
x264_log( h, X264_LOG_ERROR, "Invalid input colorspace\n" );
return -1;
......
......@@ -12,6 +12,7 @@ echo " --disable-lavf disables libavformat support"
echo " --disable-ffms disables ffmpegsource support"
echo " --disable-gpac disables gpac support"
echo " --disable-pthread disables multithreaded encoding"
echo " --disable-swscale disables swscale support"
echo " --disable-asm disables platform-specific assembly optimizations"
echo " --enable-debug adds -g, doesn't strip"
echo " --enable-gprof adds -pg, doesn't strip"
......@@ -62,7 +63,7 @@ cc_check() {
rm -f conftest.c
[ -n "$1" ] && echo "#include <$1>" > conftest.c
echo "int main () { $3 return 0; }" >> conftest.c
if $CC conftest.c $CFLAGS $LDFLAGS $LDFLAGSCLI $2 -o conftest >conftest.log 2>&1; then
if $CC conftest.c $CFLAGS $2 $LDFLAGSCLI $LDFLAGS -o conftest >conftest.log 2>&1; then
res=$?
log_ok
else
......@@ -70,7 +71,26 @@ cc_check() {
log_fail
log_msg "Failed commandline was:"
log_msg "--------------------------------------------------"
log_msg "$CC conftest.c $CFLAGS $LDFLAGS $LDFLAGSCLI $2"
log_msg "$CC conftest.c $CFLAGS $2 $LDFLAGSCLI $LDFLAGS"
cat conftest.log >> config.log
log_msg "--------------------------------------------------"
fi
return $res
}
cpp_check() {
log_check "whether $3 is true"
rm -f conftest.c
[ -n "$1" ] && echo "#include <$1>" > conftest.c
echo -e "#if !($3) \n#error $4 \n#endif " >> conftest.c
if $CC conftest.c $CFLAGS $2 -E -o conftest >conftest.log 2>&1; then
res=$?
log_ok
else
res=$?
log_fail
log_msg "--------------------------------------------------"
cat conftest.log >> config.log
log_msg "--------------------------------------------------"
fi
......@@ -119,6 +139,7 @@ lavf="auto"
ffms="auto"
gpac="auto"
pthread="auto"
swscale="auto"
asm="auto"
debug="no"
gprof="no"
......@@ -183,6 +204,9 @@ for opt do
--disable-pthread)
pthread="no"
;;
--disable-swscale)
swscale="no"
;;
--enable-debug)
debug="yes"
;;
......@@ -514,6 +538,25 @@ else
vis="no"
fi
if [ "$swscale" = "auto" ] ; then
swscale="no"
if ${cross_prefix}pkg-config --exists libswscale 2>$DEVNULL; then
SWSCALE_LIBS="$SWSCALE_LIBS $(${cross_prefix}pkg-config --libs libswscale)"
SWSCALE_CFLAGS="$SWSCALE_CFLAGS $(${cross_prefix}pkg-config --cflags libswscale)"
fi
[ -z "$SWSCALE_LIBS" ] && SWSCALE_LIBS="-lswscale -lavutil"
error="swscale must be at least version 0.9.0"
if cc_check "libswscale/swscale.h" "$SWSCALE_CFLAGS $SWSCALE_LIBS" "sws_getContext(0,0,0,0,0,0,0,0,0,0);" ; then
if cpp_check "libswscale/swscale.h" "$SWSCALE_CFLAGS" "LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0,9,0)" "$error"; then
define HAVE_SWSCALE
swscale="yes"
else
echo "Warning: ${error}"
fi
fi
fi
if [ "$lavf" = "auto" ] ; then
lavf="no"
if ${cross_prefix}pkg-config --exists libavformat libavcodec libswscale 2>$DEVNULL; then
......@@ -521,36 +564,34 @@ if [ "$lavf" = "auto" ] ; then
LAVF_CFLAGS="$LAVF_CFLAGS $(${cross_prefix}pkg-config --cflags libavformat libavcodec libswscale)"
fi
if [ -z "$LAVF_LIBS" -a -z "$LAVF_CFLAGS" ]; then
LAVF_LIBS="-lavformat -lswscale"
for lib in -lpostproc -lavcodec -lavutil -lm -lz -lbz2 $libpthread -lavifil32; do
LAVF_LIBS="-lavformat"
for lib in -lpostproc -lavcodec -lswscale -lavutil -lm -lz -lbz2 $libpthread -lavifil32; do
cc_check "" $lib && LAVF_LIBS="$LAVF_LIBS $lib"
done
fi
LAVF_LIBS="-L. $LAVF_LIBS"
if cc_check libavformat/avformat.h "$LAVF_CFLAGS $LAVF_LIBS" && \
cc_check libswscale/swscale.h "$LAVF_CFLAGS $LAVF_LIBS" ; then
# avcodec_decode_video2 is currently the most recently added function that we use; it was added in r18351
if cc_check libavformat/avformat.h "$LAVF_CFLAGS $LAVF_LIBS" "avcodec_decode_video2( NULL, NULL, NULL, NULL );" ; then
lavf="yes"
define HAVE_LAVF
if cc_check libavformat/avformat.h "$LAVF_CFLAGS $LAVF_LIBS" "avcodec_decode_video2(0,0,0,0);" ; then
# libvautil/pixdesc.h included the private header intreadwrite.h until r21854
if cc_check libavutil/pixdesc.h "$LAVF_CFLAGS $LAVF_LIBS" ; then
if [ "$swscale" = "yes" ]; then
lavf="yes"
define HAVE_LAVF
else
echo "Warning: libavformat is not supported without swscale support"
fi
else
echo "Warning: libavformat is too old, update to ffmpeg r18351+"
echo "Warning: libavutil is too old, update to ffmpeg r21854+"
fi
fi
fi
if [ "$ffms" = "auto" ] ; then
ffms_major="2"; ffms_minor="13"; ffms_micro="1"; ffms_bump="0"
ffms="no"
[ $ffms_micro -gt 0 -o $ffms_bump -gt 0 ] && vmicro=".$ffms_micro"
[ $ffms_bump -gt 0 ] && vbump=".$ffms_bump"
if ${cross_prefix}pkg-config --atleast-version="$ffms_major.$ffms_minor$vmicro$vbump" ffms2 2>$DEVNULL; then
if ${cross_prefix}pkg-config --exists ffms2 2>$DEVNULL; then
FFMS2_LIBS="$FFMS2_LIBS $(${cross_prefix}pkg-config --libs ffms2)"
FFMS2_CFLAGS="$FFMS2_CFLAGS $(${cross_prefix}pkg-config --cflags ffms2)"
api_check="no"
else
api_check="yes"
fi
[ -z "$FFMS2_LIBS" ] && FFMS2_LIBS="-lffms2"
......@@ -561,25 +602,27 @@ if [ "$ffms" = "auto" ] ; then
FFMS2_LIBS="$FFMS2_LIBS -lstdc++ $LAVF_LIBS"
fi
if [ $api_check = "yes" -a $ffms = "yes" ]; then
log_check "whether ffms2 version is at least $ffms_major.$ffms_minor$vmicro$vbump"
$CC $CFLAGS $FFMS2_CFLAGS -c -o conftest -x c - >$DEVNULL 2>&1 <<EOF
#include <ffms.h>
#if FFMS_VERSION < (($ffms_major << 24) | ($ffms_minor << 16) | ($ffms_micro << 8) | $ffms_bump)
#error Requires ffms2 version 2.13.1
#endif
EOF
[ $? = 0 ] && log_ok || { ffms="no"; log_fail; }
error="ffms must be at least version $ffms_major.$ffms_minor.$ffms_micro.$ffms_bump"
if [ $ffms = "yes" ] && ! cpp_check "ffms.h" "$FFMS2_CFLAGS" "FFMS_VERSION >= (($ffms_major << 24) | ($ffms_minor << 16) | ($ffms_micro << 8) | $ffms_bump)" "$error"; then
ffms="no"
echo "Warning: $error"
fi
if [ "$ffms" = "yes" -a "$swscale" = "no" ]; then
echo "Warning: ffms is not supported without swscale support"
ffms="no"
fi
fi
if [ "$ffms" = "yes" ]; then
LDFLAGSCLI="$FFMS2_LIBS $LDFLAGSCLI"
[ -n "$FFMS2_CFLAGS" ] && CFLAGS="$CFLAGS $FFMS2_CFLAGS"
CFLAGS="$CFLAGS $FFMS2_CFLAGS"
define HAVE_FFMS
elif [ "$lavf" = "yes" ]; then
LDFLAGSCLI="$LAVF_LIBS $LDFLAGSCLI"
[ -n "$LAVF_CFLAGS" ] && CFLAGS="$CFLAGS $LAVF_CFLAGS"
CFLAGS="$CFLAGS $LAVF_CFLAGS"
elif [ "$swscale" = "yes" ]; then
LDFLAGSCLI="$SWSCALE_LIBS $LDFLAGSCLI"
CFLAGS="$CFLAGS $SWSCALE_CFLAGS"
fi
GPAC_LIBS="-lgpac_static"
......@@ -601,7 +644,7 @@ if [ "$gpac" = "yes" ] ; then
if cc_check gpac/isomedia.h "-Werror $GPAC_LIBS" "gf_malloc(1); gf_free(NULL);" ; then
define HAVE_GF_MALLOC
fi
LDFLAGSCLI="$LDFLAGSCLI $GPAC_LIBS"
LDFLAGSCLI="$GPAC_LIBS $LDFLAGSCLI"
fi
if [ "$avs" = "auto" ] ; then
......@@ -726,6 +769,9 @@ Libs: $pclibs
Cflags: -I$includedir
EOF
filters="crop select_every"
[ $swscale = yes ] && filters="resize $filters"
cat > conftest.log <<EOF
Platform: $ARCH
System: $SYS
......@@ -735,6 +781,7 @@ lavf: $lavf
ffms: $ffms
gpac: $gpac
pthread: $pthread
filters: $filters
debug: $debug
gprof: $gprof
PIC: $pic
......
......@@ -403,9 +403,9 @@ static int x264_validate_parameters( x264_t *h )
return -1;
}
int i_csp = h->param.i_csp & X264_CSP_MASK;
if( i_csp != X264_CSP_I420 && i_csp != X264_CSP_YV12 )
if( i_csp <= X264_CSP_NONE || i_csp >= X264_CSP_MAX )
{
x264_log( h, X264_LOG_ERROR, "invalid CSP (only I420/YV12 supported)\n" );
x264_log( h, X264_LOG_ERROR, "invalid CSP\n" );
return -1;
}
......
/*****************************************************************************
* filters.c: x264 filter common
*****************************************************************************
* Copyright (C) 2010 x264 project
*
* Authors: Diogo Franco <diogomfranco@gmail.com>
* Steven Walters <kemuri9@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*****************************************************************************/
#include "filters.h"
#define RETURN_IF_ERROR( cond, ... ) RETURN_IF_ERR( cond, "options", NULL, __VA_ARGS__ )
char **x264_split_string( char *string, char *sep, uint32_t limit )
{
if( !string )
return NULL;
int sep_count = 0;
char *tmp = string;
while( ( tmp = ( tmp = strstr( tmp, sep ) ) ? tmp + strlen( sep ) : 0 ) )
++sep_count;
if( sep_count == 0 )
{
if( string[0] == '\0' )
return calloc( 1, sizeof( char** ) );
char **ret = calloc( 2, sizeof( char** ) );
ret[0] = strdup( string );
return ret;
}
char **split = calloc( ( limit > 0 ? limit : sep_count ) + 2, sizeof(char**) );
int i = 0;
char *str = strdup( string );
assert( str );
char *esc = NULL;
char *tok = str, *nexttok = str;
do
{
nexttok = strstr( nexttok, sep );
if( nexttok )
*nexttok++ = '\0';
if( ( limit > 0 && i >= limit ) ||
( i > 0 && ( ( esc = strrchr( split[i-1], '\\' ) ) ? esc[1] == '\0' : 0 ) ) ) // Allow escaping
{
int j = i-1;
if( esc )
esc[0] = '\0';
split[j] = realloc( split[j], strlen( split[j] ) + strlen( sep ) + strlen( tok ) + 1 );
assert( split[j] );
strcat( split[j], sep );
strcat( split[j], tok );
esc = NULL;
}
else
assert( ( split[i++] = strdup( tok ) ) );
tok = nexttok;
} while ( tok );
free( str );
assert( !split[i] );
return split;
}
void x264_free_string_array( char **array )
{
if( !array )
return;
for( int i = 0; array[i] != NULL; i++ )
free( array[i] );
free( array );
}
char **x264_split_options( const char *opt_str, const char *options[] )
{
if( !opt_str )
return NULL;
char *opt_str_dup = strdup( opt_str );
char **split = x264_split_string( opt_str_dup, ",", 0 );
free( opt_str_dup );
int split_count = 0;
while( split[split_count] != NULL )
++split_count;
int options_count = 0;
while( options[options_count] != NULL )
++options_count;
char **opts = calloc( split_count * 2 + 2, sizeof( char ** ) );
char **arg = NULL;
int opt = 0, found_named = 0, invalid = 0;
for( int i = 0; split[i] != NULL; i++, invalid = 0 )
{
arg = x264_split_string( split[i], "=", 2 );
if( arg == NULL )
{
if( found_named )
invalid = 1;
else RETURN_IF_ERROR( i > options_count || options[i] == NULL, "Too many options given\n" )
else
{
opts[opt++] = strdup( options[i] );
opts[opt++] = strdup( "" );
}
}
else if( arg[0] == NULL || arg[1] == NULL )
{
if( found_named )
invalid = 1;
else RETURN_IF_ERROR( i > options_count || options[i] == NULL, "Too many options given\n" )
else
{
opts[opt++] = strdup( options[i] );
if( arg[0] )
opts[opt++] = strdup( arg[0] );
else
opts[opt++] = strdup( "" );
}
}
else
{
found_named = 1;
int j = 0;
while( options[j] != NULL && strcmp( arg[0], options[j] ) )
++j;
RETURN_IF_ERROR( options[j] == NULL, "Invalid option '%s'\n", arg[0] )
else
{
opts[opt++] = strdup( arg[0] );
opts[opt++] = strdup( arg[1] );
}
}
RETURN_IF_ERROR( invalid, "Ordered option given after named\n" )
x264_free_string_array( arg );
}
x264_free_string_array( split );
return opts;
}
char *x264_get_option( const char *name, char **split_options )
{
if( !split_options )
return NULL;
int last_i = -1;
for( int i = 0; split_options[i] != NULL; i += 2 )
if( !strcmp( split_options[i], name ) )
last_i = i;
if( last_i >= 0 )
return split_options[last_i+1][0] ? split_options[last_i+1] : NULL;
return NULL;
}
int x264_otob( char *str, int def )
{
int ret = def;
if( str )
ret = !strcasecmp( str, "true" ) ||
!strcmp( str, "1" ) ||
!strcasecmp( str, "yes" );
return ret;
}
double x264_otof( char *str, double def )
{
double ret = def;
if( str )
{
char *end;
ret = strtod( str, &end );
if( end == str || *end != '\0' )
ret = def;
}
return ret;
}
int x264_otoi( char *str, int def )
{
int ret = def;
if( str )
{
char *end;
ret = strtol( str, &end, 0 );
if( end == str || *end != '\0' )
ret = def;
}
return ret;
}
char *x264_otos( char *str, char *def )
{
return str ? str : def;
}
/*****************************************************************************
* filters.h: x264 filter common
*****************************************************************************
* Copyright (C) 2010 x264 project
*
* Authors: Diogo Franco <diogomfranco@gmail.com>
* Steven Walters <kemuri9@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*****************************************************************************/
#ifndef X264_FILTERS_H
#define X264_FILTERS_H
#include "x264cli.h"
#include "filters/video/video.h"
char **x264_split_string( char *string, char *sep, uint32_t limit );
void x264_free_string_array( char **array );
char **x264_split_options( const char *opt_str, const char *options[] );
char *x264_get_option( const char *name, char **split_options );
int x264_otob( char *str, int def ); // option to bool
double x264_otof( char *str, double def ); // option to float/double
int x264_otoi( char *str, int def ); // option to int
char *x264_otos( char *str, char *def ); // option to string
#endif
/*****************************************************************************
* cache.c: x264 video cache filter
*****************************************************************************
* Copyright (C) 2010 Steven Walters <kemuri9@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*****************************************************************************/
#include "video.h"
#include "internal.h"
#define NAME "cache"
#define LAST_FRAME (h->first_frame + h->cur_size - 1)
typedef struct
{
hnd_t prev_hnd;
cli_vid_filter_t prev_filter;
int max_size;
int first_frame; /* first cached frame */
cli_pic_t **cache;
int cur_size;
int eof; /* frame beyond end of the file */
} cache_hnd_t;
cli_vid_filter_t cache_filter;
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
{
intptr_t size = (intptr_t)opt_string;
/* upon a <= 0 cache request, do nothing */
if( size <= 0 )
return 0;
cache_hnd_t *h = calloc( 1, sizeof(cache_hnd_t) );
if( !h )
return -1;
h->max_size = size;
h->cache = malloc( (h->max_size+1) * sizeof(cli_pic_t*) );
if( !h->cache )
return -1;
for( int i = 0; i < h->max_size; i++ )
{
h->cache[i] = malloc( sizeof(cli_pic_t) );
if( !h->cache[i] || x264_cli_pic_alloc( h->cache[i], info->csp, info->width, info->height ) )
return -1;
}
h->cache[h->max_size] = NULL; /* require null terminator for list methods */
h->prev_filter = *filter;
h->prev_hnd = *handle;
*handle = h;
*filter = cache_filter;
return 0;
}
static void fill_cache( cache_hnd_t *h, int frame )
{
/* shift frames out of the cache as the frame request is beyond the filled cache */
int shift = frame - LAST_FRAME;
/* no frames to shift or no frames left to read */
if( shift <= 0 || h->eof )
return;
/* the next frames to read are either
* A) starting at the end of the current cache, or
* B) starting at a new frame that has the end of the cache at the desired frame
* and proceeding to fill the entire cache */
int cur_frame = X264_MAX( h->first_frame + h->cur_size, frame - h->max_size + 1 );
/* the new starting point is either
* A) the current one shifted the number of frames entering/leaving the cache, or
* B) at a new frame that has the end of the cache at the desired frame. */
h->first_frame = X264_MIN( h->first_frame + shift, cur_frame );
h->cur_size = X264_MAX( h->cur_size - shift, 0 );
while( h->cur_size < h->max_size )
{
cli_pic_t temp;
/* the old front frame is going to shift off, overwrite it with the new frame */
cli_pic_t *cache = h->cache[0];
if( h->prev_filter.get_frame( h->prev_hnd, &temp, cur_frame ) ||
x264_cli_pic_copy( cache, &temp ) ||
h->prev_filter.release_frame( h->prev_hnd, &temp, cur_frame ) )
{
h->eof = cur_frame;
return;
}
/* the read was successful, shift the frame off the front to the end */
x264_frame_push( (void*)h->cache, x264_frame_shift( (void*)h->cache ) );
cur_frame++;
h->cur_size++;
}
}
static int get_frame( hnd_t handle, cli_pic_t *output, int frame )
{
cache_hnd_t *h = handle;
FAIL_IF_ERR( frame < h->first_frame, NAME, "frame %d is before first cached frame %d \n", frame, h->first_frame );
fill_cache( h, frame );
if( frame > LAST_FRAME ) /* eof */
return -1;
int idx = frame - (h->eof ? h->eof - h->max_size : h->first_frame);
*output = *h->cache[idx];
return 0;
}
static int release_frame( hnd_t handle, cli_pic_t *pic, int frame )
{
/* the parent filter's frame has already been released so do nothing here */
return 0;
}
static void free_filter( hnd_t handle )
{
cache_hnd_t *h = handle;
h->prev_filter.free( h->prev_hnd );
for( int i = 0; i < h->max_size; i++ )
{
x264_cli_pic_clean( h->cache[i] );
free( h->cache[i] );
}
free( h->cache );
free( h );
}
cli_vid_filter_t cache_filter = { NAME, NULL, init, get_frame, release_frame, free_filter, NULL };
/*****************************************************************************
* crop.c: x264 crop video filter
*****************************************************************************
* Copyright (C) 2010 x264 project
*
* Authors: Steven Walters <kemuri9@gmail.com>
* James Darnley <james.darnley@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*****************************************************************************/
#include "video.h"
#define NAME "crop"
#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, NAME, __VA_ARGS__ )
cli_vid_filter_t crop_filter;
typedef struct
{
hnd_t prev_hnd;
cli_vid_filter_t prev_filter;
int dims[4]; /* left, top, width, height */
const x264_cli_csp_t *csp;
} crop_hnd_t;
static void help( int longhelp )
{
printf( " "NAME":left,top,right,bottom\n" );
if( !longhelp )
return;
printf( " removes pixels from the edges of the frame\n" );
}
static int init( hnd_t *handle, cli_vid_filter_t *filter, video_info_t *info, x264_param_t *param, char *opt_string )
{
FAIL_IF_ERROR( x264_cli_csp_is_invalid( info->csp ), "invalid csp %d\n", info->csp )
crop_hnd_t *h = calloc( 1, sizeof(crop_hnd_t) );
if( !h )
return -1;
h->csp = x264_cli_get_csp( info->csp );
static const char *optlist[] = { "left", "top", "right", "bottom", NULL };
char **opts = x264_split_options( opt_string, optlist );
if( !opts )
return -1;
for( int i = 0; i < 4; i++ )
{
char *opt = x264_get_option( optlist[i], opts );
FAIL_IF_ERROR( !opt, "%s crop value not specified\n", optlist[i] )
h->dims[i] = x264_otoi( opt, -1 );
FAIL_IF_ERROR( h->dims[i] < 0, "%s crop value `%s' is less than 0\n", optlist[i], opt )
int dim_mod = i&1 ? (h->csp->mod_height << info->interlaced) : h->csp->mod_width;
FAIL_IF_ERROR( h->dims[i] % dim_mod, "%s crop value `%s' is not a multiple of %d\n", optlist[i], opt, dim_mod )
}
x264_free_string_array( opts );
h->dims[2] = info->width - h->dims[0] - h->dims[2];
h->dims[3] = info->height - h->dims[1] - h->dims[3];
FAIL_IF_ERROR( h->dims[2] <= 0 || h->dims[3] <= 0, "invalid output resolution %dx%d\n", h->dims[2], h->dims[3] )
if( info->width != h->dims[2] || info->height != h->dims[3] )
x264_cli_log( NAME, X264_LOG_INFO, "cropping to %dx%d\n", h->dims[2], h->dims[3] );
else
{
/* do nothing as the user supplied 0s for all the values */
free( h );
return 0;
}
/* done initializing, overwrite values */
info->width = h->dims[2];
info->height = h->dims[3];
h->prev_filter = *filter;
h->prev_hnd = *handle;
*handle = h;
*filter = crop_filter;
return 0;