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