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>
43 /*****************************************************************************
45 *****************************************************************************/
46 static int Create ( vlc_object_t
* );
47 static void Destroy ( vlc_object_t
* );
49 static picture_t
*Filter( filter_t
*, picture_t
* );
51 static void SnapshotRatio( filter_t
*p_filter
, picture_t
*p_pic
);
52 static void SavePicture( filter_t
*, picture_t
* );
54 /*****************************************************************************
56 *****************************************************************************/
57 #define FORMAT_TEXT N_( "Image format" )
58 #define FORMAT_LONGTEXT N_( "Format of the output images (png, jpeg, ...)." )
60 #define WIDTH_TEXT N_( "Image width" )
61 #define WIDTH_LONGTEXT N_( "You can enforce the image width. By default " \
62 "(-1) VLC will adapt to the video " \
65 #define HEIGHT_TEXT N_( "Image height" )
66 #define HEIGHT_LONGTEXT N_( "You can enforce the image height. By default " \
67 "(-1) VLC will adapt to the video " \
70 #define RATIO_TEXT N_( "Recording ratio" )
71 #define RATIO_LONGTEXT N_( "Ratio of images to record. "\
72 "3 means that one image out of three is recorded." )
74 #define PREFIX_TEXT N_( "Filename prefix" )
75 #define PREFIX_LONGTEXT N_( "Prefix of the output images filenames. Output " \
76 "filenames will have the \"prefixNUMBER.format\" "\
77 "form if replace is not true." )
79 #define PATH_TEXT N_( "Directory path prefix" )
80 #define PATH_LONGTEXT N_( "Directory path where images files should be saved." \
81 "If not set, then images will be automatically saved in " \
84 #define REPLACE_TEXT N_( "Always write to the same file" )
85 #define REPLACE_LONGTEXT N_( "Always write to the same file instead of " \
86 "creating one file per image. In this case, " \
87 "the number is not appended to the filename." )
89 #define SCENE_HELP N_("Send your video to picture files")
90 #define CFG_PREFIX "scene-"
93 set_shortname( N_( "Scene filter" ) )
94 set_description( N_( "Scene video filter" ) )
96 set_category( CAT_VIDEO
)
97 set_subcategory( SUBCAT_VIDEO_VOUT
)
98 set_capability( "video filter2", 0 )
100 /* General options */
101 add_string( CFG_PREFIX
"format", "png",
102 FORMAT_TEXT
, FORMAT_LONGTEXT
, false )
103 add_integer( CFG_PREFIX
"width", -1,
104 WIDTH_TEXT
, WIDTH_LONGTEXT
, true )
105 add_integer( CFG_PREFIX
"height", -1,
106 HEIGHT_TEXT
, HEIGHT_LONGTEXT
, true )
107 add_string( CFG_PREFIX
"prefix", "scene",
108 PREFIX_TEXT
, PREFIX_LONGTEXT
, false )
109 add_string( CFG_PREFIX
"path", NULL
,
110 PATH_TEXT
, PATH_LONGTEXT
, false )
111 add_bool( CFG_PREFIX
"replace", false,
112 REPLACE_TEXT
, REPLACE_LONGTEXT
, false )
114 /* Snapshot method */
115 add_integer( CFG_PREFIX
"ratio", 50,
116 RATIO_TEXT
, RATIO_LONGTEXT
, false )
118 set_callbacks( Create
, Destroy
)
121 static const char *const ppsz_vfilter_options
[] = {
122 "format", "width", "height", "ratio", "prefix", "path", "replace", NULL
125 typedef struct scene_t
{
127 video_format_t format
;
130 /*****************************************************************************
131 * filter_sys_t: private data
132 *****************************************************************************/
135 image_handler_t
*p_image
;
141 vlc_fourcc_t i_format
;
144 int32_t i_ratio
; /* save every n-th frame */
145 int32_t i_frames
; /* frames count */
149 /*****************************************************************************
150 * Create: initialize and set pf_video_filter()
151 *****************************************************************************/
152 static int Create( vlc_object_t
*p_this
)
154 filter_t
*p_filter
= (filter_t
*)p_this
;
157 config_ChainParse( p_filter
, CFG_PREFIX
, ppsz_vfilter_options
,
160 p_filter
->p_sys
= p_sys
= calloc( 1, sizeof( filter_sys_t
) );
161 if( p_filter
->p_sys
== NULL
)
164 p_sys
->p_image
= image_HandlerCreate( p_this
);
165 if( !p_sys
->p_image
)
167 msg_Err( p_this
, "Couldn't get handle to image conversion routines." );
172 p_sys
->psz_format
= var_CreateGetString( p_this
, CFG_PREFIX
"format" );
173 p_sys
->i_format
= image_Type2Fourcc( p_sys
->psz_format
);
174 if( !p_sys
->i_format
)
176 msg_Err( p_filter
, "Could not find FOURCC for image type '%s'",
178 image_HandlerDelete( p_sys
->p_image
);
179 free( p_sys
->psz_format
);
183 p_sys
->i_width
= var_CreateGetInteger( p_this
, CFG_PREFIX
"width" );
184 p_sys
->i_height
= var_CreateGetInteger( p_this
, CFG_PREFIX
"height" );
185 p_sys
->i_ratio
= var_CreateGetInteger( p_this
, CFG_PREFIX
"ratio" );
186 p_sys
->b_replace
= var_CreateGetBool( p_this
, CFG_PREFIX
"replace" );
187 p_sys
->psz_prefix
= var_CreateGetString( p_this
, CFG_PREFIX
"prefix" );
188 p_sys
->psz_path
= var_GetNonEmptyString( p_this
, CFG_PREFIX
"path" );
189 if( p_sys
->psz_path
== NULL
)
190 p_sys
->psz_path
= config_GetUserDir( VLC_PICTURES_DIR
);
192 p_filter
->pf_video_filter
= Filter
;
197 /*****************************************************************************
198 * Destroy: destroy video filter method
199 *****************************************************************************/
200 static void Destroy( vlc_object_t
*p_this
)
202 filter_t
*p_filter
= (filter_t
*)p_this
;
203 filter_sys_t
*p_sys
= (filter_sys_t
*) p_filter
->p_sys
;
205 image_HandlerDelete( p_sys
->p_image
);
207 if( p_sys
->scene
.p_pic
)
208 picture_Release( p_sys
->scene
.p_pic
);
209 free( p_sys
->psz_format
);
210 free( p_sys
->psz_prefix
);
211 free( p_sys
->psz_path
);
215 /*****************************************************************************
216 * Filter: Apply filtering logic to picture.
217 *****************************************************************************/
218 static picture_t
*Filter( filter_t
*p_filter
, picture_t
*p_pic
)
220 /* TODO: think of some funky algorithm to detect scene changes. */
221 SnapshotRatio( p_filter
, p_pic
);
225 static void SnapshotRatio( filter_t
*p_filter
, picture_t
*p_pic
)
227 filter_sys_t
*p_sys
= (filter_sys_t
*)p_filter
->p_sys
;
231 if( p_sys
->i_frames
% p_sys
->i_ratio
!= 0 )
238 if( p_sys
->scene
.p_pic
)
239 picture_Release( p_sys
->scene
.p_pic
);
241 if( (p_sys
->i_width
<= 0) && (p_sys
->i_height
> 0) )
243 p_sys
->i_width
= (p_pic
->format
.i_width
* p_sys
->i_height
) / p_pic
->format
.i_height
;
245 else if( (p_sys
->i_height
<= 0) && (p_sys
->i_width
> 0) )
247 p_sys
->i_height
= (p_pic
->format
.i_height
* p_sys
->i_width
) / p_pic
->format
.i_width
;
249 else if( (p_sys
->i_width
<= 0) && (p_sys
->i_height
<= 0) )
251 p_sys
->i_width
= p_pic
->format
.i_width
;
252 p_sys
->i_height
= p_pic
->format
.i_height
;
255 p_sys
->scene
.p_pic
= picture_NewFromFormat( &p_pic
->format
);
256 if( p_sys
->scene
.p_pic
)
258 picture_Copy( p_sys
->scene
.p_pic
, p_pic
);
259 SavePicture( p_filter
, p_sys
->scene
.p_pic
);
263 /*****************************************************************************
264 * Save Picture to disk
265 *****************************************************************************/
266 static void SavePicture( filter_t
*p_filter
, picture_t
*p_pic
)
268 filter_sys_t
*p_sys
= (filter_sys_t
*)p_filter
->p_sys
;
269 video_format_t fmt_in
, fmt_out
;
270 char *psz_filename
= NULL
;
271 char *psz_temp
= NULL
;
274 memset( &fmt_in
, 0, sizeof(video_format_t
) );
275 memset( &fmt_out
, 0, sizeof(video_format_t
) );
277 /* Save snapshot psz_format to a memory zone */
278 fmt_in
= p_pic
->format
;
279 fmt_out
.i_sar_num
= fmt_out
.i_sar_den
= 1;
280 fmt_out
.i_width
= p_sys
->i_width
;
281 fmt_out
.i_height
= p_sys
->i_height
;
282 fmt_out
.i_chroma
= p_sys
->i_format
;
285 * Save the snapshot to a temporary file and
286 * switch it to the real name afterwards.
288 if( p_sys
->b_replace
)
289 i_ret
= asprintf( &psz_filename
, "%s" DIR_SEP
"%s.%s",
290 p_sys
->psz_path
, p_sys
->psz_prefix
,
293 i_ret
= asprintf( &psz_filename
, "%s" DIR_SEP
"%s%05d.%s",
294 p_sys
->psz_path
, p_sys
->psz_prefix
,
295 p_sys
->i_frames
, p_sys
->psz_format
);
299 msg_Err( p_filter
, "could not create snapshot %s", psz_filename
);
302 path_sanitize( psz_filename
);
304 i_ret
= asprintf( &psz_temp
, "%s.swp", psz_filename
);
307 msg_Err( p_filter
, "could not create snapshot temporarily file %s", psz_temp
);
310 path_sanitize( psz_temp
);
313 i_ret
= image_WriteUrl( p_sys
->p_image
, p_pic
, &fmt_in
, &fmt_out
,
315 if( i_ret
!= VLC_SUCCESS
)
317 msg_Err( p_filter
, "could not create snapshot %s", psz_temp
);
321 /* switch to the final destination */
322 #if defined (WIN32) || defined(__OS2__)
323 vlc_unlink( psz_filename
);
325 i_ret
= vlc_rename( psz_temp
, psz_filename
);
328 msg_Err( p_filter
, "could not rename snapshot %s %m", psz_filename
);
335 free( psz_filename
);