1 /*****************************************************************************
2 * scene.c : scene video filter (based on modules/video_output/image.c)
3 *****************************************************************************
4 * Copyright (C) 2004-2008 the VideoLAN team
7 * Authors: Jean-Paul Saman <jpsaman@videolan.org>
8 * Clément Stenac <zorglub@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_block.h>
37 #include <vlc_filter.h>
38 #include "filter_picture.h"
39 #include <vlc_image.h>
40 #include <vlc_strings.h>
42 /*****************************************************************************
44 *****************************************************************************/
45 static int Create ( vlc_object_t
* );
46 static void Destroy ( vlc_object_t
* );
48 static picture_t
*Filter( filter_t
*, picture_t
* );
50 static void SnapshotRatio( filter_t
*p_filter
, picture_t
*p_pic
);
51 static void SavePicture( filter_t
*, picture_t
* );
53 /*****************************************************************************
55 *****************************************************************************/
56 #define FORMAT_TEXT N_( "Image format" )
57 #define FORMAT_LONGTEXT N_( "Format of the output images (png, jpeg, ...)." )
59 #define WIDTH_TEXT N_( "Image width" )
60 #define WIDTH_LONGTEXT N_( "You can enforce the image width. By default " \
61 "(-1) VLC will adapt to the video " \
64 #define HEIGHT_TEXT N_( "Image height" )
65 #define HEIGHT_LONGTEXT N_( "You can enforce the image height. By default " \
66 "(-1) VLC will adapt to the video " \
69 #define RATIO_TEXT N_( "Recording ratio" )
70 #define RATIO_LONGTEXT N_( "Ratio of images to record. "\
71 "3 means that one image out of three is recorded." )
73 #define PREFIX_TEXT N_( "Filename prefix" )
74 #define PREFIX_LONGTEXT N_( "Prefix of the output images filenames. Output " \
75 "filenames will have the \"prefixNUMBER.format\" "\
76 "form if replace is not true." )
78 #define PATH_TEXT N_( "Directory path prefix" )
79 #define PATH_LONGTEXT N_( "Directory path where images files should be saved." \
80 "If not set, then images will be automatically saved in " \
83 #define REPLACE_TEXT N_( "Always write to the same file" )
84 #define REPLACE_LONGTEXT N_( "Always write to the same file instead of " \
85 "creating one file per image. In this case, " \
86 "the number is not appended to the filename." )
88 #define SCENE_HELP N_("Send your video to picture files")
89 #define CFG_PREFIX "scene-"
92 set_shortname( N_( "Scene filter" ) )
93 set_description( N_( "Scene video filter" ) )
95 set_category( CAT_VIDEO
)
96 set_subcategory( SUBCAT_VIDEO_VOUT
)
97 set_capability( "video filter2", 0 )
100 add_string( CFG_PREFIX
"format", "png", NULL
,
101 FORMAT_TEXT
, FORMAT_LONGTEXT
, false )
102 add_integer( CFG_PREFIX
"width", -1, NULL
,
103 WIDTH_TEXT
, WIDTH_LONGTEXT
, true )
104 add_integer( CFG_PREFIX
"height", -1, NULL
,
105 HEIGHT_TEXT
, HEIGHT_LONGTEXT
, true )
106 add_string( CFG_PREFIX
"prefix", "scene", NULL
,
107 PREFIX_TEXT
, PREFIX_LONGTEXT
, false )
108 add_string( CFG_PREFIX
"path", NULL
, NULL
,
109 PATH_TEXT
, PATH_LONGTEXT
, false )
110 add_bool( CFG_PREFIX
"replace", false, NULL
,
111 REPLACE_TEXT
, REPLACE_LONGTEXT
, false )
113 /* Snapshot method */
114 add_integer( CFG_PREFIX
"ratio", 50, NULL
,
115 RATIO_TEXT
, RATIO_LONGTEXT
, false )
117 set_callbacks( Create
, Destroy
)
120 static const char *const ppsz_vfilter_options
[] = {
121 "format", "width", "height", "ratio", "prefix", "path", "replace", NULL
124 typedef struct scene_t
{
126 video_format_t format
;
129 /*****************************************************************************
130 * filter_sys_t: private data
131 *****************************************************************************/
134 image_handler_t
*p_image
;
140 vlc_fourcc_t i_format
;
143 int32_t i_ratio
; /* save every n-th frame */
144 int32_t i_frames
; /* frames count */
148 /*****************************************************************************
149 * Create: initialize and set pf_video_filter()
150 *****************************************************************************/
151 static int Create( vlc_object_t
*p_this
)
153 filter_t
*p_filter
= (filter_t
*)p_this
;
156 config_ChainParse( p_filter
, CFG_PREFIX
, ppsz_vfilter_options
,
159 p_filter
->p_sys
= p_sys
= calloc( 1, sizeof( filter_sys_t
) );
160 if( p_filter
->p_sys
== NULL
)
163 p_sys
->p_image
= image_HandlerCreate( p_this
);
164 if( !p_sys
->p_image
)
166 msg_Err( p_this
, "Couldn't get handle to image conversion routines." );
171 p_sys
->psz_format
= var_CreateGetString( p_this
, CFG_PREFIX
"format" );
172 p_sys
->i_format
= image_Type2Fourcc( p_sys
->psz_format
);
173 if( !p_sys
->i_format
)
175 msg_Err( p_filter
, "Could not find FOURCC for image type '%s'",
177 image_HandlerDelete( p_sys
->p_image
);
178 free( p_sys
->psz_format
);
182 p_sys
->i_width
= var_CreateGetInteger( p_this
, CFG_PREFIX
"width" );
183 p_sys
->i_height
= var_CreateGetInteger( p_this
, CFG_PREFIX
"height" );
184 p_sys
->i_ratio
= var_CreateGetInteger( p_this
, CFG_PREFIX
"ratio" );
185 p_sys
->b_replace
= var_CreateGetBool( p_this
, CFG_PREFIX
"replace" );
186 p_sys
->psz_prefix
= var_CreateGetString( p_this
, CFG_PREFIX
"prefix" );
187 p_sys
->psz_path
= var_GetNonEmptyString( p_this
, CFG_PREFIX
"path" );
188 if( p_sys
->psz_path
== NULL
)
189 p_sys
->psz_path
= config_GetUserDir( VLC_PICTURES_DIR
);
191 p_filter
->pf_video_filter
= Filter
;
196 /*****************************************************************************
197 * Destroy: destroy video filter method
198 *****************************************************************************/
199 static void Destroy( vlc_object_t
*p_this
)
201 filter_t
*p_filter
= (filter_t
*)p_this
;
202 filter_sys_t
*p_sys
= (filter_sys_t
*) p_filter
->p_sys
;
204 image_HandlerDelete( p_sys
->p_image
);
206 if( p_sys
->scene
.p_pic
)
207 picture_Release( p_sys
->scene
.p_pic
);
208 free( p_sys
->psz_format
);
209 free( p_sys
->psz_prefix
);
210 free( p_sys
->psz_path
);
214 /*****************************************************************************
215 * Filter: Apply filtering logic to picture.
216 *****************************************************************************/
217 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
219 /* TODO: think of some funky algorithm to detect scene changes. */
220 SnapshotRatio( p_filter
, p_pic
);
224 static void SnapshotRatio( filter_t
*p_filter
, picture_t
*p_pic
)
226 filter_sys_t
*p_sys
= (filter_sys_t
*)p_filter
->p_sys
;
230 if( p_sys
->i_frames
% p_sys
->i_ratio
!= 0 )
237 if( p_sys
->scene
.p_pic
)
238 picture_Release( p_sys
->scene
.p_pic
);
240 if( (p_sys
->i_width
<= 0) && (p_sys
->i_height
> 0) )
242 p_sys
->i_width
= (p_pic
->format
.i_width
* p_sys
->i_height
) / p_pic
->format
.i_height
;
244 else if( (p_sys
->i_height
<= 0) && (p_sys
->i_width
> 0) )
246 p_sys
->i_height
= (p_pic
->format
.i_height
* p_sys
->i_width
) / p_pic
->format
.i_width
;
248 else if( (p_sys
->i_width
<= 0) && (p_sys
->i_height
<= 0) )
250 p_sys
->i_width
= p_pic
->format
.i_width
;
251 p_sys
->i_height
= p_pic
->format
.i_height
;
254 p_sys
->scene
.p_pic
= picture_NewFromFormat( &p_pic
->format
);
255 if( p_sys
->scene
.p_pic
)
257 picture_Copy( p_sys
->scene
.p_pic
, p_pic
);
258 SavePicture( p_filter
, p_sys
->scene
.p_pic
);
262 /*****************************************************************************
263 * Save Picture to disk
264 *****************************************************************************/
265 static void SavePicture( filter_t
*p_filter
, picture_t
*p_pic
)
267 filter_sys_t
*p_sys
= (filter_sys_t
*)p_filter
->p_sys
;
268 video_format_t fmt_in
, fmt_out
;
269 char *psz_filename
= NULL
;
270 char *psz_temp
= NULL
;
273 memset( &fmt_in
, 0, sizeof(video_format_t
) );
274 memset( &fmt_out
, 0, sizeof(video_format_t
) );
276 /* Save snapshot psz_format to a memory zone */
277 fmt_in
= p_pic
->format
;
278 fmt_out
.i_sar_num
= fmt_out
.i_sar_den
= 1;
279 fmt_out
.i_width
= p_sys
->i_width
;
280 fmt_out
.i_height
= p_sys
->i_height
;
281 fmt_out
.i_chroma
= p_sys
->i_format
;
284 * Save the snapshot to a temporary file and
285 * switch it to the real name afterwards.
287 if( p_sys
->b_replace
)
288 i_ret
= asprintf( &psz_filename
, "%s" DIR_SEP
"%s.%s",
289 p_sys
->psz_path
, p_sys
->psz_prefix
,
292 i_ret
= asprintf( &psz_filename
, "%s" DIR_SEP
"%s%05d.%s",
293 p_sys
->psz_path
, p_sys
->psz_prefix
,
294 p_sys
->i_frames
, p_sys
->psz_format
);
298 msg_Err( p_filter
, "could not create snapshot %s", psz_filename
);
301 path_sanitize( psz_filename
);
303 i_ret
= asprintf( &psz_temp
, "%s.swp", psz_filename
);
306 msg_Err( p_filter
, "could not create snapshot temporarily file %s", psz_temp
);
309 path_sanitize( psz_temp
);
312 i_ret
= image_WriteUrl( p_sys
->p_image
, p_pic
, &fmt_in
, &fmt_out
,
314 if( i_ret
!= VLC_SUCCESS
)
316 msg_Err( p_filter
, "could not create snapshot %s", psz_temp
);
320 /* switch to the final destination */
321 i_ret
= rename( psz_temp
, psz_filename
);
324 msg_Err( p_filter
, "could not rename snapshot %s %m", psz_filename
);
331 free( psz_filename
);