Difference between revisions of "LibVLC SampleCode Thumbnailer"

From VideoLAN Wiki
Jump to navigation Jump to search
Line 1: Line 1:
 
This sample code will generate a thumbnail from any media.
 
This sample code will generate a thumbnail from any media.
 +
 +
Using VLC 1.2 API (libvlc 5.1.0), last updated 2010, October 25th.
 +
 
It can be used by nautilus instead of totem-video-thumbnailer
 
It can be used by nautilus instead of totem-video-thumbnailer
 
   /* Copyright [[User:Funman|Rafaël Carré]] (licence [http://en.wikipedia.org/wiki/WTFPL WTFPL]) */
 
   /* Copyright [[User:Funman|Rafaël Carré]] (licence [http://en.wikipedia.org/wiki/WTFPL WTFPL]) */
 
<pre>
 
<pre>
 
/* A video thumbnailer compatible with nautilus */
 
/* A video thumbnailer compatible with nautilus */
/* Copyright © 2007-2009 Rafaël Carré <funman@videolanorg> */
+
/* Copyright © 2007-2010 Rafaël Carré <funman@videolanorg> */
  
 +
#include <assert.h>
 
#include <stdio.h>
 
#include <stdio.h>
#include <vlc/libvlc.h>
 
#include <vlc/libvlc_media.h>
 
#include <vlc/libvlc_media_player.h>
 
#include <vlc/libvlc_events.h>
 
 
#include <stdlib.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <string.h>
 
#include <unistd.h>
 
#include <unistd.h>
 
#include <locale.h>
 
#include <locale.h>
#include <sys/types.h>
 
#include <sys/stat.h>
 
  
/* gcc -ansi -Wall -Werror -lvlc-control vlc-video-thumbnailer.c */
+
#include <vlc/vlc.h>
 +
 
 +
/* gcc -pedantic -Wall -Werror -Wextra `pkg-config --cflags --libs libvlc` */
 +
 
 +
/* position at which the snapshot is taken */
 +
#define VLC_THUMBNAIL_POSITION (30./100.)
  
#if 1
+
static void usage(const char *name, int ret)
#  define DEBUG( s ) fprintf( stderr, s"\n" );
+
{
#else
+
    fprintf(stderr, "Usage: %s [-w width] <video> <output.png>\n", name);
#  define DEBUG( s )
+
    exit(ret);
#endif
+
}
  
/* position to seek to (out of 1.) */
+
/* extracts options from command line */
#define POS 0.3
+
static void cmdline(int argc, const char **argv, const char **in,
 +
                    char **out, char **out_with_ext, int *w)
 +
{
 +
    int idx = 1;
 +
    size_t len;
  
/* delay before aborting ( real delay will be * 2 ) */
+
    if (argc != 3 && argc != 5)
#define MAX_DELAY 5.0
+
        usage(argv[0], argc != 2 || strcmp(argv[1], "-h"));
  
/* for the callback to notify the main thread that the position changed */
+
    *w = 0;
static int fini = 0;
 
  
/* to catch problems */
+
    if (argc == 5) {
static libvlc_exception_t ex;
+
        if (strcmp(argv[1], "-w"))
 +
            usage(argv[0], 1);
  
/* "position changed" callback */
+
        idx += 2; /* skip "-w width" */
static void callback( const libvlc_event_t *ev, void *param )
+
        *w = atoi(argv[2]);
{
 
    if( ev->type == libvlc_MediaPlayerPositionChanged )
 
    {
 
        float new_pos;
 
        new_pos = ev->u.media_player_position_changed.new_position;
 
        if( new_pos >= POS - ( POS / 10 ) )
 
        {
 
            DEBUG( "Position set" );
 
            fini = 1; /* now we can take the snapshot */
 
        }
 
 
     }
 
     }
     else
+
 
     {
+
    *in  = argv[idx++];
        fprintf( stderr, "Error: catched event %s\n",
+
    *out = strdup(argv[idx++]);
                libvlc_event_type_name( ev->type ) );
+
     assert(*out);
         exit(1);
+
 
 +
     len = strlen(*out);
 +
    if (len >= 4 && !strcmp(*out + len - 4, ".png")) {
 +
        *out_with_ext = *out;
 +
         return;
 
     }
 
     }
 +
 +
    /* We need to add .png extension to filename,
 +
    * VLC relies on it to detect output format,
 +
    * and nautilus doesn't give filenames ending in .png */
 +
 +
    *out_with_ext = malloc(len + sizeof ".png");
 +
    assert(*out_with_ext);
 +
    strcpy(*out_with_ext, *out);
 +
    strcat(*out_with_ext, ".png");
 
}
 
}
  
static void catch (void)
+
static libvlc_instance_t *create_libvlc(void)
 
{
 
{
     if (libvlc_exception_raised (&ex))
+
     static const char* const args[] = {
     {
+
        "--intf", "dummy",                  /* no interface                  */
         fprintf (stderr, "Exception\n");
+
        "--vout", "dummy",                  /* we don't want video (output)  */
         exit(1);
+
        "--no-audio",                      /* we don't want audio (decoding) */
     }
+
        "--no-video-title-show",            /* nor the filename displayed     */
 +
        "--no-stats",                      /* no stats                      */
 +
        "--no-sub-autodetect-file",        /* we don't want subtitles        */
 +
         "--no-inhibit",                     /* we don't want interfaces      */
 +
        "--no-disable-screensaver",        /* we don't want interfaces      */
 +
#if 0
 +
         "--verbose=2",                      /* full log                      */
 +
#endif
 +
     };
  
     libvlc_exception_clear (&ex);
+
     return libvlc_new(sizeof args / sizeof *args, args);
 
}
 
}
  
/* nautilus doesn't use .png extension, and vlc detects the filetype with the
+
static void callback(const libvlc_event_t *ev, void *param)
* extension, so we use a temporary file to be sure we'll use png */
+
{
#define TEMP "/tmp/vlc-video-thumbnailer.png"
+
    float new_position = ev->u.media_player_position_changed.new_position;
  
#define usage() \
+
    switch (ev->type) {
        fprintf( stderr, \
 
            "Usage: %s input output\n" \
 
            "input being any video VLC can read, and output a png file\n" \
 
            , argv[0] )
 
  
int main( int argc, const char **argv )
+
    case libvlc_MediaPlayerPositionChanged:
{
+
        if (new_position >= VLC_THUMBNAIL_POSITION * .9) { /* 90% margin */
    if( argc != 3 && argc != 5 )
+
            *(int*)param = 1;
    {
+
        }
         usage();
+
         break;
         if( argc == 2 && ( !strcmp( argv[1], "-h" ) || strcmp( argv[1], "--help") ) )
+
    case libvlc_MediaPlayerSnapshotTaken:
            return 0;
+
         *(int*)param = 1;
         else
+
        break;
            return 1;
+
 
 +
    default:
 +
         assert(0);
 
     }
 
     }
 +
}
  
    /* mandatory to support UTF-8 filenames (provided the locale is well set)*/
+
/* FIXME: should use pthread notification */
    setlocale( LC_ALL, getenv( "LANG" ) );
+
static void event_wait(const char *error, int *f)
 +
{
 +
#define VLC_THUMBNAIL_TIMEOUT  5.0 /* 5 secs */
 +
#define VLC_THUMBNAIL_LOOP_STEP 0.2 /* 200 ms */
  
     const char const *input, *output;
+
     float max = VLC_THUMBNAIL_TIMEOUT;
     int i_width, i_height = 0;
+
     while ((max -= VLC_THUMBNAIL_LOOP_STEP) > 0.) {
 +
        if (*f)
 +
            return;
  
    if( argc == 5 )
+
         usleep(VLC_THUMBNAIL_LOOP_STEP * 1000000);
    {
 
        if( strcmp( argv[1], "-s" ) )
 
        {
 
            usage();
 
            return 1;
 
        }
 
         i_width = atoi( argv[2] );
 
        input = argv[3];
 
        output = argv[4];
 
    }
 
    else
 
    {
 
        input = argv[1];
 
        output = argv[2];
 
        i_width = 0;
 
 
     }
 
     }
  
     libvlc_exception_init( &ex );
+
     fprintf(stderr,
 +
            "%s (timeout after %.2f secs!\n", error, VLC_THUMBNAIL_TIMEOUT);
 +
    exit(1);
 +
}
 +
 
 +
static void set_position(libvlc_media_player_t *mp)
 +
{
 +
    int f = 0;
 +
    libvlc_event_manager_t *em = libvlc_media_player_event_manager(mp);
 +
    assert(em);
 +
 
 +
    libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged, callback, &f);
 +
    libvlc_media_player_set_position(mp, VLC_THUMBNAIL_POSITION);
 +
    event_wait("Couldn't set position", &f);
 +
    libvlc_event_detach(em, libvlc_MediaPlayerPositionChanged, callback, &f);
 +
}
  
    /* libvlc settings */
+
static void snapshot(libvlc_media_player_t *mp, int width, char *out_with_ext)
    const char* const args[] = {
+
{
        "-I", "dummy",                      /* no interface */
+
    int f = 0;
        "--vout", "dummy",                 /* we don't want video (output) */
+
    libvlc_event_manager_t *em = libvlc_media_player_event_manager(mp);
        "--no-audio",                      /* we don't want audio */
+
     assert(em);
        "--verbose=0",                      /* show only errors */
 
        "--no-media-library",              /* don't want that */
 
        "--services-discovery", "",        /* nor that */
 
        "--no-video-title-show",            /* nor the filename displayed */
 
        "--no-stats",                      /* no stats */
 
        "--ignore-config",            /* don't use/overwrite the config */
 
        "--no-sub-autodetect",              /* don't want subtitles */
 
        "--control", "",                    /* don't want interface (again) */
 
        "--no-inhibit",                    /* i say no interface ! */
 
        "--no-disable-screensaver",        /* wanna fight ? */
 
        "--extraintf", ""                  /* ok, it will be a piece of cake */
 
     };
 
  
     int nargs = sizeof(args) / sizeof(args[0]);
+
     libvlc_event_attach(em, libvlc_MediaPlayerSnapshotTaken, callback, &f);
     libvlc_instance_t *libvlc = libvlc_new( nargs, args, &ex );
+
    libvlc_video_take_snapshot(mp, 0, out_with_ext, width, 0);
     catch();
+
     event_wait("Snapshot has not been written", &f);
 +
     libvlc_event_detach(em, libvlc_MediaPlayerSnapshotTaken, callback, &f);
 +
}
  
 +
int main(int argc, const char **argv)
 +
{
 +
    const char *in;
 +
    char *out, *out_with_ext;
 +
    int width;
 +
    libvlc_instance_t *libvlc;
 
     libvlc_media_player_t *mp;
 
     libvlc_media_player_t *mp;
 
     libvlc_media_t *m;
 
     libvlc_media_t *m;
  
 +
    /* mandatory to support UTF-8 filenames (provided the locale is well set)*/
 +
    setlocale(LC_ALL, getenv("LANG"));
  
     m = libvlc_media_new( libvlc, input, &ex );
+
     cmdline(argc, argv, &in, &out, &out_with_ext, &width);
    catch();
 
 
 
    mp = libvlc_media_player_new_from_media( m, &ex );
 
    catch();
 
 
 
    libvlc_media_release( m );
 
  
     libvlc_media_player_play( mp, &ex );
+
     /* starts vlc */
     catch();
+
    libvlc = create_libvlc();
 +
     assert(libvlc);
  
     /* avoid introduction */
+
     m = libvlc_media_new_path(libvlc, in);
    libvlc_media_player_set_position( mp, POS, &ex );
+
     assert(m);
     catch();
 
  
     libvlc_event_manager_t *em;
+
     mp = libvlc_media_player_new_from_media(m);
    em = libvlc_media_player_event_manager( mp, &ex );
+
     assert(mp);
     catch();
 
  
     libvlc_event_attach( em, libvlc_MediaPlayerPositionChanged,
+
     libvlc_media_player_play(mp);
                        callback, NULL, &ex );
 
    catch();
 
  
     float max = MAX_DELAY;  /* don't wait more MAX_DELAY seconds */
+
     /* takes snapshot */
     while( !fini && ( max -= .2 ) > 0 )  /* wait for the position changed event */
+
     set_position(mp);
        usleep( 200000 );
+
    snapshot(mp, width, out_with_ext);
  
     libvlc_event_detach( em, libvlc_MediaPlayerPositionChanged,
+
     libvlc_media_player_stop(mp);
                        callback, NULL, &ex );
 
    catch();
 
  
     DEBUG( "Taking snapshot" );
+
     /* clean up */
    libvlc_video_take_snapshot( mp, TEMP, i_width, i_height, &ex );
+
     if (out != out_with_ext) {
    catch();
+
         rename(out_with_ext, out);
 
+
         free(out_with_ext);
    sleep( 1 ); /* be sure that the snapshot has been written */
 
 
 
     DEBUG( "Stopping" );
 
    libvlc_media_player_stop( mp, &ex );
 
    catch();
 
 
 
    max = MAX_DELAY;
 
    while( ( max -= .2 ) > 0 )
 
    {
 
         struct stat st;
 
        if( stat( TEMP, &st ) == 0 )
 
            break;
 
         DEBUG( TEMP" doesn't exist" );
 
        if( max <= .2 )
 
        {
 
            DEBUG( "Snapshot has not been written :(" );
 
            goto end;
 
        }
 
        usleep( 200000 );
 
 
     }
 
     }
 +
    free(out);
  
     DEBUG( "Moving snapshot" );
+
     libvlc_media_player_release(mp);
     char *mv;
+
     libvlc_media_release(m);
    if( asprintf( &mv, "mv \"%s\" \"%s\"", TEMP, output ) == -1 )
+
     libvlc_release(libvlc);
        return 1;
 
    system( mv );
 
    free( mv );
 
 
 
end:
 
    libvlc_media_player_release( mp );
 
 
 
     libvlc_release( libvlc );
 
    catch();
 
  
 
     return 0;
 
     return 0;
 +
}
 
}</pre>
 
}</pre>

Revision as of 12:18, 25 October 2010

This sample code will generate a thumbnail from any media.

Using VLC 1.2 API (libvlc 5.1.0), last updated 2010, October 25th.

It can be used by nautilus instead of totem-video-thumbnailer

 /* Copyright Rafaël Carré (licence WTFPL) */
/* A video thumbnailer compatible with nautilus */
/* Copyright © 2007-2010 Rafaël Carré <funman@videolanorg> */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <locale.h>

#include <vlc/vlc.h>

/* gcc -pedantic -Wall -Werror -Wextra `pkg-config --cflags --libs libvlc` */

/* position at which the snapshot is taken */
#define VLC_THUMBNAIL_POSITION (30./100.)

static void usage(const char *name, int ret)
{
    fprintf(stderr, "Usage: %s [-w width] <video> <output.png>\n", name);
    exit(ret);
}

/* extracts options from command line */
static void cmdline(int argc, const char **argv, const char **in,
                    char **out, char **out_with_ext, int *w)
{
    int idx = 1;
    size_t len;

    if (argc != 3 && argc != 5)
        usage(argv[0], argc != 2 || strcmp(argv[1], "-h"));

    *w = 0;

    if (argc == 5) {
        if (strcmp(argv[1], "-w"))
            usage(argv[0], 1);

        idx += 2; /* skip "-w width" */
        *w = atoi(argv[2]);
    }

    *in  = argv[idx++];
    *out = strdup(argv[idx++]);
    assert(*out);

    len = strlen(*out);
    if (len >= 4 && !strcmp(*out + len - 4, ".png")) {
        *out_with_ext = *out;
        return;
    }

    /* We need to add .png extension to filename,
     * VLC relies on it to detect output format,
     * and nautilus doesn't give filenames ending in .png */

    *out_with_ext = malloc(len + sizeof ".png");
    assert(*out_with_ext);
    strcpy(*out_with_ext, *out);
    strcat(*out_with_ext, ".png");
}

static libvlc_instance_t *create_libvlc(void)
{
    static const char* const args[] = {
        "--intf", "dummy",                  /* no interface                   */
        "--vout", "dummy",                  /* we don't want video (output)   */
        "--no-audio",                       /* we don't want audio (decoding) */
        "--no-video-title-show",            /* nor the filename displayed     */
        "--no-stats",                       /* no stats                       */
        "--no-sub-autodetect-file",         /* we don't want subtitles        */
        "--no-inhibit",                     /* we don't want interfaces       */
        "--no-disable-screensaver",         /* we don't want interfaces       */
#if 0
        "--verbose=2",                      /* full log                       */
#endif
    };

    return libvlc_new(sizeof args / sizeof *args, args);
}

static void callback(const libvlc_event_t *ev, void *param)
{
    float new_position = ev->u.media_player_position_changed.new_position;

    switch (ev->type) {

    case libvlc_MediaPlayerPositionChanged:
        if (new_position >= VLC_THUMBNAIL_POSITION * .9) { /* 90% margin */
            *(int*)param = 1;
        }
        break;
    case libvlc_MediaPlayerSnapshotTaken:
        *(int*)param = 1;
        break;

    default:
        assert(0);
    }
}

/* FIXME: should use pthread notification */
static void event_wait(const char *error, int *f)
{
#define VLC_THUMBNAIL_TIMEOUT   5.0 /* 5 secs */
#define VLC_THUMBNAIL_LOOP_STEP 0.2 /* 200 ms */

    float max = VLC_THUMBNAIL_TIMEOUT;
    while ((max -= VLC_THUMBNAIL_LOOP_STEP) > 0.) {
        if (*f)
            return;

        usleep(VLC_THUMBNAIL_LOOP_STEP * 1000000);
    }

    fprintf(stderr,
            "%s (timeout after %.2f secs!\n", error, VLC_THUMBNAIL_TIMEOUT);
    exit(1);
}

static void set_position(libvlc_media_player_t *mp)
{
    int f = 0;
    libvlc_event_manager_t *em = libvlc_media_player_event_manager(mp);
    assert(em);

    libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged, callback, &f);
    libvlc_media_player_set_position(mp, VLC_THUMBNAIL_POSITION);
    event_wait("Couldn't set position", &f);
    libvlc_event_detach(em, libvlc_MediaPlayerPositionChanged, callback, &f);
}

static void snapshot(libvlc_media_player_t *mp, int width, char *out_with_ext)
{
    int f = 0;
    libvlc_event_manager_t *em = libvlc_media_player_event_manager(mp);
    assert(em);

    libvlc_event_attach(em, libvlc_MediaPlayerSnapshotTaken, callback, &f);
    libvlc_video_take_snapshot(mp, 0, out_with_ext, width, 0);
    event_wait("Snapshot has not been written", &f);
    libvlc_event_detach(em, libvlc_MediaPlayerSnapshotTaken, callback, &f);
}

int main(int argc, const char **argv)
{
    const char *in;
    char *out, *out_with_ext;
    int width;
    libvlc_instance_t *libvlc;
    libvlc_media_player_t *mp;
    libvlc_media_t *m;

    /* mandatory to support UTF-8 filenames (provided the locale is well set)*/
    setlocale(LC_ALL, getenv("LANG"));

    cmdline(argc, argv, &in, &out, &out_with_ext, &width);

    /* starts vlc */
    libvlc = create_libvlc();
    assert(libvlc);

    m = libvlc_media_new_path(libvlc, in);
    assert(m);

    mp = libvlc_media_player_new_from_media(m);
    assert(mp);

    libvlc_media_player_play(mp);

    /* takes snapshot */
    set_position(mp);
    snapshot(mp, width, out_with_ext);

    libvlc_media_player_stop(mp);

    /* clean up */
    if (out != out_with_ext) {
        rename(out_with_ext, out);
        free(out_with_ext);
    }
    free(out);

    libvlc_media_player_release(mp);
    libvlc_media_release(m);
    libvlc_release(libvlc);

    return 0;
}
}