]> git.sesse.net Git - vlc/blob - src/video_output/snapshot.c
Moved out snapshot code to a standalone file.
[vlc] / src / video_output / snapshot.c
1 /*****************************************************************************
2  * snapshot.c : vout internal snapshot
3  *****************************************************************************
4  * Copyright (C) 2009 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin _AT_ videolan _DOT_ org>
8  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_charset.h>
31 #include <vlc_strings.h>
32 #include <vlc_block.h>
33
34 #include "snapshot.h"
35
36 #include <sys/stat.h>
37 #include <sys/types.h>
38 #include <dirent.h>
39 #include <time.h>
40
41 /* */
42 void vout_snapshot_Init(vout_snapshot_t *snap)
43 {
44     vlc_mutex_init(&snap->lock);
45     vlc_cond_init(&snap->wait);
46
47     snap->is_available = true;
48     snap->request_count = 0;
49     snap->picture = NULL;
50 }
51 void vout_snapshot_Clean(vout_snapshot_t *snap)
52 {
53     picture_t *picture = snap->picture;
54     while (picture) {
55         picture_t *next = picture->p_next;
56         picture_Release(picture);
57         picture = next;
58     }
59
60     vlc_cond_destroy(&snap->wait);
61     vlc_mutex_destroy(&snap->lock);
62 }
63
64 void vout_snapshot_End(vout_snapshot_t *snap)
65 {
66     vlc_mutex_lock(&snap->lock);
67
68     snap->is_available = false;
69
70     vlc_cond_broadcast(&snap->wait);
71     vlc_mutex_unlock(&snap->lock);
72 }
73
74 /* */
75 picture_t *vout_snapshot_Get(vout_snapshot_t *snap, mtime_t timeout)
76 {
77     vlc_mutex_lock(&snap->lock);
78
79     /* */
80     snap->request_count++;
81
82     /* */
83     const mtime_t deadline = mdate() + timeout;
84     while (snap->is_available && !snap->picture && mdate() < deadline)
85         vlc_cond_timedwait(&snap->wait, &snap->lock, deadline);
86
87     /* */
88     picture_t *picture = snap->picture;
89     if (picture)
90         snap->picture = picture->p_next;
91     else if (snap->request_count > 0)
92         snap->request_count--;
93
94     vlc_mutex_unlock(&snap->lock);
95
96     return picture;
97 }
98
99 /* */
100 bool vout_snapshot_IsRequested(vout_snapshot_t *snap)
101 {
102     bool has_request = false;
103     if (!vlc_mutex_trylock(&snap->lock)) {
104         has_request = snap->request_count > 0;
105         vlc_mutex_unlock(&snap->lock);
106     }
107     return has_request;
108 }
109 void vout_snapshot_Set(vout_snapshot_t *snap,
110                        const video_format_t *fmt,
111                        const picture_t *picture)
112 {
113     if (!fmt)
114         fmt = &picture->format;
115
116     vlc_mutex_lock(&snap->lock);
117     while (snap->request_count > 0) {
118         picture_t *dup = picture_NewFromFormat(fmt);
119         if (!dup)
120             break;
121
122         picture_Copy(dup, picture);
123
124         dup->p_next = snap->picture;
125         snap->picture = dup;
126         snap->request_count--;
127     }
128     vlc_cond_broadcast(&snap->wait);
129     vlc_mutex_unlock(&snap->lock);
130 }
131 /* */
132 char *vout_snapshot_GetDirectory(void)
133 {
134     char *psz_path = NULL;
135 #if defined(__APPLE__) || defined(SYS_BEOS)
136
137     if (asprintf(&psz_path, "%s/Desktop",
138                   config_GetHomeDir()) == -1)
139         psz_path = NULL;
140
141 #elif defined(WIN32) && !defined(UNDER_CE)
142
143     /* Get the My Pictures folder path */
144     char *p_mypicturesdir = NULL;
145     typedef HRESULT (WINAPI *SHGETFOLDERPATH)(HWND, int, HANDLE, DWORD,
146                                                LPWSTR);
147     #ifndef CSIDL_FLAG_CREATE
148     #   define CSIDL_FLAG_CREATE 0x8000
149     #endif
150     #ifndef CSIDL_MYPICTURES
151     #   define CSIDL_MYPICTURES 0x27
152     #endif
153     #ifndef SHGFP_TYPE_CURRENT
154     #   define SHGFP_TYPE_CURRENT 0
155     #endif
156
157     HINSTANCE shfolder_dll;
158     SHGETFOLDERPATH SHGetFolderPath ;
159
160     /* load the shfolder dll to retrieve SHGetFolderPath */
161     if ((shfolder_dll = LoadLibrary(_T("SHFolder.dll"))) != NULL)
162     {
163        wchar_t wdir[PATH_MAX];
164        SHGetFolderPath = (void *)GetProcAddress(shfolder_dll,
165                                                   _T("SHGetFolderPathW"));
166         if ((SHGetFolderPath != NULL)
167          && SUCCEEDED (SHGetFolderPath (NULL,
168                                        CSIDL_MYPICTURES | CSIDL_FLAG_CREATE,
169                                        NULL, SHGFP_TYPE_CURRENT,
170                                        wdir)))
171             p_mypicturesdir = FromWide (wdir);
172
173         FreeLibrary(shfolder_dll);
174     }
175
176     if (p_mypicturesdir == NULL)
177         psz_path = strdup(config_GetHomeDir());
178     else
179         psz_path = p_mypicturesdir;
180
181 #else
182
183     /* XXX: This saves in the data directory. Shouldn't we try saving
184      *      to psz_homedir/Desktop or something nicer ? */
185     char *psz_datadir = config_GetUserDataDir();
186     if (psz_datadir)
187     {
188         if (asprintf(&psz_path, "%s", psz_datadir) == -1)
189             psz_path = NULL;
190         free(psz_datadir);
191     }
192
193 #endif
194     return psz_path;
195 }
196 /* */
197 int vout_snapshot_SaveImage(char **name, int *sequential,
198                              const block_t *image,
199                              vlc_object_t *object,
200                              const vout_snapshot_save_cfg_t *cfg)
201 {
202     /* */
203     char *filename;
204     DIR *pathdir = utf8_opendir(cfg->path);
205     if (pathdir != NULL) {
206         /* The use specified a directory path */
207         closedir(pathdir);
208
209         /* */
210         char *prefix = NULL;
211         if (cfg->prefix_fmt)
212             prefix = str_format(object, cfg->prefix_fmt);
213         if (!prefix) {
214             prefix = strdup("vlcsnap-");
215             if (!prefix)
216                 goto error;
217         }
218
219         if (cfg->is_sequential) {
220             for (int num = cfg->sequence; ; num++) {
221                 struct stat st;
222
223                 if (asprintf(&filename, "%s" DIR_SEP "%s%05d.%s",
224                              cfg->path, prefix, num, cfg->format) < 0) {
225                     free(prefix);
226                     goto error;
227                 }
228                 if (utf8_stat(filename, &st)) {
229                     *sequential = num;
230                     break;
231                 }
232                 free(filename);
233             }
234         } else {
235             struct tm    curtime;
236             time_t       lcurtime = time(NULL) ;
237
238             if (!localtime_r(&lcurtime, &curtime)) {
239                 const unsigned int id = (image->i_pts / 100000) & 0xFFFFFF;
240
241                 msg_Warn(object, "failed to get current time. Falling back to legacy snapshot naming");
242
243                 if (asprintf(&filename, "%s" DIR_SEP "%s%u.%s",
244                              cfg->path, prefix, id, cfg->format) < 0)
245                     filename = NULL;
246             } else {
247                 /* suffix with the last decimal digit in 10s of seconds resolution
248                  * FIXME gni ? */
249                 const int id = (image->i_pts / (100*1000)) & 0xFF;
250                 char buffer[128];
251
252                 if (!strftime(buffer, sizeof(buffer), "%Y-%m-%d-%Hh%Mm%Ss", &curtime))
253                     strcpy(buffer, "error");
254
255                 if (asprintf(&filename, "%s" DIR_SEP "%s%s%1u.%s",
256                              cfg->path, prefix, buffer, id, cfg->format) < 0)
257                     filename = NULL;
258             }
259         }
260         free(prefix);
261     } else {
262         /* The user specified a full path name (including file name) */
263         filename = str_format(object, cfg->path);
264         path_sanitize(filename);
265     }
266
267     if (!filename)
268         goto error;
269
270     /* Save the snapshot */
271     FILE *file = utf8_fopen(filename, "wb");
272     if (!file) {
273         msg_Err(object, "Failed to open '%s'", filename);
274         free(filename);
275         goto error;
276     }
277     if (fwrite(image->p_buffer, image->i_buffer, 1, file) != 1) {
278         msg_Err(object, "Failed to write to '%s'", filename);
279         fclose(file);
280         free(filename);
281         goto error;
282     }
283     fclose(file);
284
285     /* */
286     if (name)
287         *name = filename;
288     else
289         free(filename);
290
291     return VLC_SUCCESS;
292
293 error:
294     msg_Err(object, "could not save snapshot");
295     return VLC_EGENERIC;
296 }
297