]> git.sesse.net Git - vlc/blob - doc/libvlc/vlc-thumb.c
vlc-thumb: use pthread conditions
[vlc] / doc / libvlc / vlc-thumb.c
1 /* Copyright Rafaël Carré (licence WTFPL) */
2 /* A video thumbnailer compatible with nautilus */
3 /* Copyright © 2007-2011 Rafaël Carré <funman@videolanorg> */
4
5 /* Works with : libvlc 1.2.0
6    gcc -pedantic -Wall -Werror -Wextra `pkg-config --cflags --libs libvlc` -lpthread
7
8   # to register the thumbnailer:
9   list=`grep ^Mime vlc.desktop|cut -d= -f2-|sed -e s/";"/\\\n/g -e s,/,@,g`
10   vid=`echo $mimes|grep ^vid`
11   for i in $vid
12   do 
13     key=/desktop/gnome/thumbnailers/$i/enable
14     gconftool-2 -t boolean -s $key true
15     gconftool-2 -t string  -s $key "vlc-thumb -s %s %u %o"
16   done
17  */
18
19 #include <assert.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <locale.h>
25 #include <pthread.h>
26 #include <errno.h>
27 #include <time.h>
28
29 #if !defined (_POSIX_CLOCK_SELECTION)
30 #  define _POSIX_CLOCK_SELECTION (-1)
31 #endif
32
33 #if (_POSIX_CLOCK_SELECTION < 0)
34 #   error Clock selection is not available!
35 #endif
36
37 #include <vlc/vlc.h>
38
39 /* position at which the snapshot is taken */
40 #define VLC_THUMBNAIL_POSITION (30./100.)
41
42 static void usage(const char *name, int ret)
43 {
44     fprintf(stderr, "Usage: %s [-s width] <video> <output.png>\n", name);
45     exit(ret);
46 }
47
48 /* extracts options from command line */
49 static void cmdline(int argc, const char **argv, const char **in,
50                     char **out, char **out_with_ext, int *w)
51 {
52     int idx = 1;
53     size_t len;
54
55     if (argc != 3 && argc != 5)
56         usage(argv[0], argc != 2 || strcmp(argv[1], "-h"));
57
58     *w = 0;
59
60     if (argc == 5) {
61         if (strcmp(argv[1], "-s"))
62             usage(argv[0], 1);
63
64         idx += 2; /* skip "-s width" */
65         *w = atoi(argv[2]);
66     }
67
68     *in  = argv[idx++];
69     *out = strdup(argv[idx++]);
70     if (!*out)
71         abort();
72
73     len = strlen(*out);
74     if (len >= 4 && !strcmp(*out + len - 4, ".png")) {
75         *out_with_ext = *out;
76         return;
77     }
78
79     /* We need to add .png extension to filename,
80      * VLC relies on it to detect output format,
81      * and nautilus doesn't give filenames ending in .png */
82
83     *out_with_ext = malloc(len + sizeof ".png");
84     if (!*out_with_ext)
85         abort();
86     strcpy(*out_with_ext, *out);
87     strcat(*out_with_ext, ".png");
88 }
89
90 static libvlc_instance_t *create_libvlc(void)
91 {
92     static const char* const args[] = {
93         "--intf", "dummy",                  /* no interface                   */
94         "--vout", "dummy",                  /* we don't want video (output)   */
95         "--no-audio",                       /* we don't want audio (decoding) */
96         "--no-video-title-show",            /* nor the filename displayed     */
97         "--no-stats",                       /* no stats                       */
98         "--no-sub-autodetect-file",         /* we don't want subtitles        */
99         "--no-inhibit",                     /* we don't want interfaces       */
100         "--no-disable-screensaver",         /* we don't want interfaces       */
101         "--no-snapshot-preview",            /* no blending in dummy vout      */
102 #ifndef NDEBUG
103         "--verbose=2",                      /* full log                       */
104 #endif
105     };
106
107     return libvlc_new(sizeof args / sizeof *args, args);
108 }
109
110 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
111 static pthread_cond_t  wait;
112 static bool done;
113
114 static void callback(const libvlc_event_t *ev, void *param)
115 {
116     float new_position;
117     (void)param;
118
119     pthread_mutex_lock(&lock);
120     switch (ev->type) {
121     case libvlc_MediaPlayerPositionChanged:
122         new_position = ev->u.media_player_position_changed.new_position;
123         if (new_position < VLC_THUMBNAIL_POSITION * .9 /* 90% margin */)
124             break;
125     case libvlc_MediaPlayerSnapshotTaken:
126         done = true;
127         pthread_cond_signal(&wait);
128         break;
129
130     default:
131         assert(0);
132     }
133     pthread_mutex_unlock(&lock);
134 }
135
136 static void event_wait(const char *error)
137 {
138     int ret;
139     struct timespec ts;
140 #define VLC_THUMBNAIL_TIMEOUT   5.0 /* 5 secs */
141
142     clock_gettime(CLOCK_MONOTONIC, &ts);
143     ts.tv_sec += VLC_THUMBNAIL_TIMEOUT;
144     pthread_mutex_lock(&lock);
145     ret = done ? 0 : pthread_cond_timedwait(&wait, &lock, &ts);
146     pthread_mutex_unlock(&lock);
147
148     assert(!ret || ret == ETIMEDOUT);
149
150     if (ret) {
151         fprintf(stderr,
152                 "%s (timeout after %.2f secs!\n", error, VLC_THUMBNAIL_TIMEOUT);
153         exit(1);
154     }
155 }
156
157 static void set_position(libvlc_media_player_t *mp)
158 {
159     libvlc_event_manager_t *em = libvlc_media_player_event_manager(mp);
160     assert(em);
161
162     libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged, callback, NULL);
163     done = false;
164     libvlc_media_player_set_position(mp, VLC_THUMBNAIL_POSITION);
165     event_wait("Couldn't set position");
166     libvlc_event_detach(em, libvlc_MediaPlayerPositionChanged, callback, NULL);
167 }
168
169 static void snapshot(libvlc_media_player_t *mp, int width, char *out_with_ext)
170 {
171     libvlc_event_manager_t *em = libvlc_media_player_event_manager(mp);
172     assert(em);
173
174     libvlc_event_attach(em, libvlc_MediaPlayerSnapshotTaken, callback, NULL);
175     done = false;
176     libvlc_video_take_snapshot(mp, 0, out_with_ext, width, 0);
177     event_wait("Snapshot has not been written");
178     libvlc_event_detach(em, libvlc_MediaPlayerSnapshotTaken, callback, NULL);
179 }
180
181 int main(int argc, const char **argv)
182 {
183     const char *in;
184     char *out, *out_with_ext;
185     int width;
186     pthread_condattr_t attr;
187     libvlc_instance_t *libvlc;
188     libvlc_media_player_t *mp;
189     libvlc_media_t *m;
190
191     /* mandatory to support UTF-8 filenames (provided the locale is well set)*/
192     setlocale(LC_ALL, "");
193
194     cmdline(argc, argv, &in, &out, &out_with_ext, &width);
195
196     pthread_condattr_init(&attr);
197     pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
198     pthread_cond_init(&wait, &attr);
199     pthread_condattr_destroy(&attr);
200
201     /* starts vlc */
202     libvlc = create_libvlc();
203     assert(libvlc);
204
205     m = libvlc_media_new_path(libvlc, in);
206     assert(m);
207
208     mp = libvlc_media_player_new_from_media(m);
209     assert(mp);
210
211     libvlc_media_player_play(mp);
212
213     /* takes snapshot */
214     set_position(mp);
215     snapshot(mp, width, out_with_ext);
216
217     libvlc_media_player_stop(mp);
218
219     /* clean up */
220     if (out != out_with_ext) {
221         rename(out_with_ext, out);
222         free(out_with_ext);
223     }
224     free(out);
225
226     libvlc_media_player_release(mp);
227     libvlc_media_release(m);
228     libvlc_release(libvlc);
229
230     pthread_cond_destroy(&wait);
231
232     return 0;
233 }