[vlc-devel] [PATCH] Add libvlc_media_take_snapshot

Rafaël Carré funman at videolan.org
Wed Apr 24 08:21:20 CEST 2013


This function returns a snapshot for a given libvlc_media_t
This ought to be much faster and convenient than using vmem output to
generate thumbnails

TODO:
 - fix leaking of a single picture_t:
==5115== 3,145,288 (328 direct, 3,144,960 indirect) bytes in 1 blocks are definitely lost in loss record 19 of 19
==5115==    at 0x4C29E46: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==5115==    by 0x548E199: picture_NewFromResource (picture.c:209)
==5115==    by 0x544A8B6: decoder_NewPicture (decoder.c:194)
==5115==    by 0x131E8D0B: ffmpeg_GetFrameBuf (video.c:178)
==5115==    by 0x137F7702: ff_get_buffer (utils.c:620)
==5115==    by 0x13778B69: ff_thread_decode_frame (pthread.c:575)
==5115==    by 0x137F872A: avcodec_decode_video2 (utils.c:1378)
==5115==    by 0x131E9B28: DecodeVideo (video.c:589)
==5115==    by 0x544944E: DecoderDecodeVideo (decoder.c:1533)
==5115==    by 0x544A42F: DecoderProcess (decoder.c:1906)
==5115==    by 0x544A533: DecoderThread (decoder.c:974)
==5115==    by 0x5709E99: start_thread (pthread_create.c:308)

 - find a better API (this patch lets you figure out the video size for example)
 - let user provide desired chroma instead of forcing rgb32?

Sample code:
./a.out /path/to/media.avi > out.raw && \
avplay -f rawvideo -pixel_format rgb32 -video_size 1920x1080 out.raw

-----------------------
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>

 #include <vlc/libvlc.h>
 #include <vlc/libvlc_media.h>

int main(int argc, const char *argv[])
{
    libvlc_instance_t *inst;
    if (argc != 2)
        return 1;

    const char *a[] = { "-vv" };

    inst = libvlc_new(1, a);
    if (!inst)
        return 2;
    libvlc_media_t *m = libvlc_media_new_path(inst, argv[1]);
    if (!m) {
        fprintf(stderr, "no media\n");
        goto end;
    }

    uint8_t *buf;
    size_t len;

    libvlc_media_take_snapshot(m, &buf, &len);

    //printf("len %zu buf %p\n", len, buf);
    fwrite(buf, len, 1, stdout);
    free(buf);

    libvlc_media_release(m);

end:
    libvlc_release(inst);

    return 0;
}
-----------------------
---
 include/vlc/libvlc_media.h   |    3 ++
 include/vlc_es_out.h         |    2 ++
 include/vlc_input.h          |    4 +++
 lib/libvlc.sym               |    1 +
 lib/media.c                  |   47 +++++++++++++++++++++++++
 src/input/decoder.c          |   79 +++++++++++++++++++++++++++++++++++++-----
 src/input/decoder.h          |    2 +-
 src/input/es_out.c           |   13 +++++--
 src/input/es_out_timeshift.c |    1 +
 src/input/input.c            |   43 ++++++++++++++++++++---
 src/input/input_internal.h   |    1 +
 src/libvlccore.sym           |    1 +
 12 files changed, 180 insertions(+), 17 deletions(-)

diff --git a/include/vlc/libvlc_media.h b/include/vlc/libvlc_media.h
index eaa41f0..cbd2cb6 100644
--- a/include/vlc/libvlc_media.h
+++ b/include/vlc/libvlc_media.h
@@ -489,6 +489,9 @@ LIBVLC_API libvlc_time_t
 LIBVLC_API void
 libvlc_media_parse( libvlc_media_t *p_md );
 
+LIBVLC_API void
+libvlc_media_take_snapshot(libvlc_media_t *, uint8_t **, size_t *);
+
 /**
  * Parse a media.
  *
diff --git a/include/vlc_es_out.h b/include/vlc_es_out.h
index cf1abce..5e035b2 100644
--- a/include/vlc_es_out.h
+++ b/include/vlc_es_out.h
@@ -89,6 +89,8 @@ enum es_out_query_e
     ES_OUT_GET_PCR_SYSTEM, /* arg1=mtime_t *, arg2=mtime_t * res=can fail */
     ES_OUT_MODIFY_PCR_SYSTEM, /* arg1=int is_absolute, arg2=mtime_t, res=can fail */
 
+    ES_OUT_SET_SNAPSHOT, /* arg1=picture_t ** res=cannot fail */
+
     /* First value usable for private control */
     ES_OUT_PRIVATE_START = 0x10000,
 };
diff --git a/include/vlc_input.h b/include/vlc_input.h
index b48a256..04a5f0d 100644
--- a/include/vlc_input.h
+++ b/include/vlc_input.h
@@ -36,6 +36,7 @@
 #include <vlc_epg.h>
 #include <vlc_events.h>
 #include <vlc_input_item.h>
+#include <vlc_picture.h>
 
 #include <string.h>
 
@@ -504,6 +505,9 @@ VLC_API int input_Start( input_thread_t * );
 
 VLC_API void input_Stop( input_thread_t *, bool b_abort );
 
+VLC_API picture_t *input_Snapshot( vlc_object_t *, input_item_t * );
+#define input_Snapshot(a,b) input_Snapshot(VLC_OBJECT(a),b)
+
 VLC_API int input_Read( vlc_object_t *, input_item_t * );
 #define input_Read(a,b) input_Read(VLC_OBJECT(a),b)
 
diff --git a/lib/libvlc.sym b/lib/libvlc.sym
index 8c431c7..6ea0e78 100644
--- a/lib/libvlc.sym
+++ b/lib/libvlc.sym
@@ -119,6 +119,7 @@ libvlc_media_new_path
 libvlc_media_new_as_node
 libvlc_media_new_from_input_item
 libvlc_media_parse
+libvlc_media_take_snapshot
 libvlc_media_parse_async
 libvlc_media_player_can_pause
 libvlc_media_player_next_frame
diff --git a/lib/media.c b/lib/media.c
index 120f0f4..e23db7c 100644
--- a/lib/media.c
+++ b/lib/media.c
@@ -37,6 +37,7 @@
 #include <vlc_meta.h>
 #include <vlc_playlist.h> /* For the preparser */
 #include <vlc_url.h>
+#include <vlc_image.h>
 
 #include "../src/libvlc.h"
 
@@ -622,6 +623,52 @@ libvlc_media_parse(libvlc_media_t *media)
     vlc_mutex_unlock(&media->parsed_lock);
 }
 
+void
+libvlc_media_take_snapshot(libvlc_media_t *media, uint8_t **buf, size_t *len)
+{
+    vlc_object_t *p_obj = VLC_OBJECT(media->p_libvlc_instance->p_libvlc_int);
+    input_item_t *p_input_item = media->p_input_item;
+    picture_t *pic = input_Snapshot(p_obj, p_input_item);
+
+    if (!pic) {
+        msg_Err(p_obj, "no pic");
+        goto error;
+    }
+
+    image_handler_t *h = image_HandlerCreate(p_obj);
+    if (!h) {
+        msg_Err(p_obj, "no img converter");
+        picture_Release(pic);
+        return;
+    }
+
+    video_format_t in = pic->format;
+    video_format_t out = in;
+    out.i_chroma = VLC_CODEC_RGB32;
+    picture_t *rgb = image_Convert(h, pic, &in, &out);
+    picture_Release(pic);
+    image_HandlerDelete(h);
+
+    if (!rgb)
+        goto error;
+
+    size_t s = rgb->p[0].i_lines * rgb->p[0].i_pitch;
+    *len = s;
+    *buf = malloc(s);
+    if (!*buf) {
+        picture_Release(rgb);
+        goto error;
+    }
+
+    memcpy(*buf, rgb->p[0].p_pixels, s);
+    picture_Release(rgb);
+    return;
+
+error:
+    *len = 0;
+    *buf = NULL;
+}
+
 /**************************************************************************
  * Parse the media but do not wait.
  **************************************************************************/
diff --git a/src/input/decoder.c b/src/input/decoder.c
index b1b038c..0ec64e9 100644
--- a/src/input/decoder.c
+++ b/src/input/decoder.c
@@ -55,7 +55,7 @@
 
 static decoder_t *CreateDecoder( vlc_object_t *, input_thread_t *,
                                  es_format_t *, bool, input_resource_t *,
-                                 sout_instance_t *p_sout );
+                                 sout_instance_t *p_sout, picture_t **);
 static void       DeleteDecoder( decoder_t * );
 
 static void      *DecoderThread( void * );
@@ -169,6 +169,9 @@ struct decoder_owner_sys_t
 
     /* Delay */
     mtime_t i_ts_delay;
+
+    /* Snapshot */
+    picture_t **pp_snapshot;
 };
 
 #define DECODER_MAX_BUFFERING_COUNT (4)
@@ -261,7 +264,7 @@ int decoder_GetDisplayRate( decoder_t *p_dec )
 static decoder_t *decoder_New( vlc_object_t *p_parent, input_thread_t *p_input,
                                es_format_t *fmt, input_clock_t *p_clock,
                                input_resource_t *p_resource,
-                               sout_instance_t *p_sout  )
+                               sout_instance_t *p_sout, picture_t **pp_snapshot )
 {
     decoder_t *p_dec = NULL;
     const char *psz_type = p_sout ? N_("packetizer") : N_("decoder");
@@ -269,7 +272,7 @@ static decoder_t *decoder_New( vlc_object_t *p_parent, input_thread_t *p_input,
 
     /* Create the decoder configuration structure */
     p_dec = CreateDecoder( p_parent, p_input, fmt,
-                           p_sout != NULL, p_resource, p_sout );
+                           p_sout != NULL, p_resource, p_sout, pp_snapshot );
     if( p_dec == NULL )
     {
         msg_Err( p_parent, "could not create %s", psz_type );
@@ -317,10 +320,10 @@ static decoder_t *decoder_New( vlc_object_t *p_parent, input_thread_t *p_input,
  */
 decoder_t *input_DecoderNew( input_thread_t *p_input,
                              es_format_t *fmt, input_clock_t *p_clock,
-                             sout_instance_t *p_sout  )
+                             sout_instance_t *p_sout, picture_t **pp_snapshot )
 {
     return decoder_New( VLC_OBJECT(p_input), p_input, fmt, p_clock,
-                        p_input->p->p_resource, p_sout );
+                        p_input->p->p_resource, p_sout, pp_snapshot );
 }
 
 /**
@@ -329,7 +332,7 @@ decoder_t *input_DecoderNew( input_thread_t *p_input,
 decoder_t *input_DecoderCreate( vlc_object_t *p_parent, es_format_t *fmt,
                                 input_resource_t *p_resource )
 {
-    return decoder_New( p_parent, NULL, fmt, NULL, p_resource, NULL );
+    return decoder_New( p_parent, NULL, fmt, NULL, p_resource, NULL, NULL );
 }
 
 
@@ -460,7 +463,7 @@ int input_DecoderSetCcState( decoder_t *p_dec, bool b_decode, int i_channel )
 
         es_format_Init( &fmt, SPU_ES, fcc[i_channel] );
         p_cc = CreateDecoder( VLC_OBJECT(p_dec), p_owner->p_input, &fmt,
-                              false, p_owner->p_resource, p_owner->p_sout );
+                              false, p_owner->p_resource, p_owner->p_sout, NULL );
         if( !p_cc )
         {
             msg_Err( p_dec, "could not create decoder" );
@@ -726,6 +729,29 @@ static void DecoderUnsupportedCodec( decoder_t *p_dec, vlc_fourcc_t codec )
                   (char*)&codec );
 }
 
+static void vout_link_snapshot_picture( decoder_t *p_dec, picture_t *p_pic )
+{
+    (void)p_dec;
+    picture_Hold(p_pic);
+}
+
+static void vout_unlink_snapshot_picture( decoder_t *p_dec, picture_t *p_pic )
+{
+    (void)p_dec;
+    picture_Release(p_pic);
+}
+
+static picture_t *vout_new_snapshot_buffer( decoder_t *p_dec )
+{
+    return picture_NewFromFormat(&p_dec->fmt_out.video);
+}
+
+static void vout_del_snapshot_buffer( decoder_t *p_dec, picture_t *p_pic )
+{
+    VLC_UNUSED(p_dec);
+    picture_Release(p_pic);
+}
+
 
 /**
  * Create a decoder object
@@ -739,7 +765,8 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
                                   input_thread_t *p_input,
                                   es_format_t *fmt, bool b_packetizer,
                                   input_resource_t *p_resource,
-                                  sout_instance_t *p_sout )
+                                  sout_instance_t *p_sout,
+                                  picture_t **pp_snapshot)
 {
     decoder_t *p_dec;
     decoder_owner_sys_t *p_owner;
@@ -784,6 +811,7 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     p_owner->p_sout_input = NULL;
     p_owner->p_packetizer = NULL;
     p_owner->b_packetizer = b_packetizer;
+    p_owner->pp_snapshot = pp_snapshot;
 
     /* decoder fifo */
     p_owner->p_fifo = block_FifoNew();
@@ -807,6 +835,13 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     p_dec->pf_get_display_date = DecoderGetDisplayDate;
     p_dec->pf_get_display_rate = DecoderGetDisplayRate;
 
+    if (pp_snapshot) {
+        p_dec->pf_vout_buffer_new = vout_new_snapshot_buffer;
+        p_dec->pf_vout_buffer_del = vout_del_snapshot_buffer;
+        p_dec->pf_picture_link = vout_link_snapshot_picture;
+        p_dec->pf_picture_unlink = vout_unlink_snapshot_picture;
+    }
+
     /* Find a suitable decoder/packetizer module */
     if( !b_packetizer )
         p_dec->p_module = module_need( p_dec, "decoder", "$codec", false );
@@ -1475,18 +1510,34 @@ static void DecoderPlayVideo( decoder_t *p_dec, picture_t *p_picture,
 static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
+    bool b_snapshot = !!p_owner->pp_snapshot;
     picture_t      *p_pic;
     int i_lost = 0;
     int i_decoded = 0;
     int i_displayed = 0;
 
+    if (b_snapshot) {
+        const float f = 0.3f;
+        float pos = var_GetFloat(p_owner->p_input, "position");
+        if (pos < f) {
+            var_SetFloat(p_owner->p_input, "position", f);
+            block_Release(p_block);
+            return;
+        }
+        else if (pos > f)
+            input_Stop(p_owner->p_input, true);
+
+        /* XXX: stop after 10 decoded pictures? */
+    }
+
     while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) )
     {
         vout_thread_t  *p_vout = p_owner->p_vout;
         if( DecoderIsExitRequested( p_dec ) )
         {
             /* It prevent freezing VLC in case of broken decoder */
-            vout_ReleasePicture( p_vout, p_pic );
+            if (!b_snapshot)
+                vout_ReleasePicture( p_vout, p_pic );
             if( p_block )
                 block_Release( p_block );
             break;
@@ -1494,6 +1545,16 @@ static void DecoderDecodeVideo( decoder_t *p_dec, block_t *p_block )
 
         i_decoded++;
 
+        if (b_snapshot) {
+            if (*p_owner->pp_snapshot)
+                picture_Release(*p_owner->pp_snapshot);
+            *p_owner->pp_snapshot = p_pic;
+
+            if( p_block )
+                block_Release( p_block );
+            return;
+        }
+
         if( p_owner->i_preroll_end > VLC_TS_INVALID && p_pic->date < p_owner->i_preroll_end )
         {
             vout_ReleasePicture( p_vout, p_pic );
diff --git a/src/input/decoder.h b/src/input/decoder.h
index 1c9e1b6..40b6bdd 100644
--- a/src/input/decoder.h
+++ b/src/input/decoder.h
@@ -32,7 +32,7 @@
 #define BLOCK_FLAG_CORE_EOS   (1 <<(BLOCK_FLAG_CORE_PRIVATE_SHIFT + 1))
 
 decoder_t *input_DecoderNew( input_thread_t *, es_format_t *, input_clock_t *,
-                             sout_instance_t * ) VLC_USED;
+                             sout_instance_t *, picture_t ** ) VLC_USED;
 
 /**
  * This function changes the pause state.
diff --git a/src/input/es_out.c b/src/input/es_out.c
index c566287..af46012 100644
--- a/src/input/es_out.c
+++ b/src/input/es_out.c
@@ -168,6 +168,9 @@ struct es_out_sys_t
 
     /* Record */
     sout_instance_t *p_sout_record;
+
+    /* Snapshot */
+    picture_t **pp_pic;
 };
 
 static es_out_id_t *EsOutAdd    ( es_out_t *, const es_format_t * );
@@ -489,7 +492,7 @@ static int EsOutSetRecord(  es_out_t *out, bool b_record )
             if( !p_es->p_dec || p_es->p_master )
                 continue;
 
-            p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
+            p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record, NULL );
             if( p_es->p_dec_record && p_sys->b_buffering )
                 input_DecoderStartBuffering( p_es->p_dec_record );
         }
@@ -1554,7 +1557,7 @@ static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
     es_out_sys_t   *p_sys = out->p_sys;
     input_thread_t *p_input = p_sys->p_input;
 
-    p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_input->p->p_sout );
+    p_es->p_dec = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_input->p->p_sout, p_sys->pp_pic );
     if( p_es->p_dec )
     {
         if( p_sys->b_buffering )
@@ -1562,7 +1565,7 @@ static void EsCreateDecoder( es_out_t *out, es_out_id_t *p_es )
 
         if( !p_es->p_master && p_sys->p_sout_record )
         {
-            p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record );
+            p_es->p_dec_record = input_DecoderNew( p_input, &p_es->fmt, p_es->p_pgrm->p_clock, p_sys->p_sout_record, NULL );
             if( p_es->p_dec_record && p_sys->b_buffering )
                 input_DecoderStartBuffering( p_es->p_dec_record );
         }
@@ -2668,6 +2671,10 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
         return VLC_SUCCESS;
     }
 
+    case ES_OUT_SET_SNAPSHOT:
+        p_sys->pp_pic = va_arg( args, picture_t ** );
+        return VLC_SUCCESS;
+
     default:
         msg_Err( p_sys->p_input, "unknown query in es_out_Control" );
         return VLC_EGENERIC;
diff --git a/src/input/es_out_timeshift.c b/src/input/es_out_timeshift.c
index 520f735..1498a00 100644
--- a/src/input/es_out_timeshift.c
+++ b/src/input/es_out_timeshift.c
@@ -608,6 +608,7 @@ static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
     case ES_OUT_SET_TIMES:
     case ES_OUT_SET_JITTER:
     case ES_OUT_SET_EOS:
+    case ES_OUT_SET_SNAPSHOT:
     {
         ts_cmd_t cmd;
         if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
diff --git a/src/input/input.c b/src/input/input.c
index be1959a..9f93da0 100644
--- a/src/input/input.c
+++ b/src/input/input.c
@@ -65,7 +65,7 @@ static void Destructor( input_thread_t * p_input );
 static  void *Run            ( void * );
 
 static input_thread_t * Create  ( vlc_object_t *, input_item_t *,
-                                  const char *, bool, input_resource_t * );
+                                  const char *, bool, bool, input_resource_t * );
 static  int             Init    ( input_thread_t *p_input );
 static void             End     ( input_thread_t *p_input );
 static void             MainLoop( input_thread_t *p_input, bool b_interactive );
@@ -131,7 +131,7 @@ input_thread_t *input_Create( vlc_object_t *p_parent,
                               input_item_t *p_item,
                               const char *psz_log, input_resource_t *p_resource )
 {
-    return Create( p_parent, p_item, psz_log, false, p_resource );
+    return Create( p_parent, p_item, psz_log, false, false, p_resource );
 }
 
 #undef input_CreateAndStart
@@ -165,7 +165,7 @@ input_thread_t *input_CreateAndStart( vlc_object_t *p_parent,
  */
 int input_Read( vlc_object_t *p_parent, input_item_t *p_item )
 {
-    input_thread_t *p_input = Create( p_parent, p_item, NULL, false, NULL );
+    input_thread_t *p_input = Create( p_parent, p_item, NULL, false, false, NULL );
     if( !p_input )
         return VLC_EGENERIC;
 
@@ -180,6 +180,37 @@ int input_Read( vlc_object_t *p_parent, input_item_t *p_item )
 }
 
 /**
+ * Initialize an input and initialize it to take a snapshot
+ * This function is blocking. It will only accept snapshotting regular files.
+ *
+ * \param p_parent a vlc_object_t
+ * \param p_item an input item
+ * \return snapshot or NULL
+ */
+#undef input_Snapshot
+picture_t *input_Snapshot( vlc_object_t *p_parent, input_item_t *p_item )
+{
+    input_item_AddOption(p_item, ":no-audio", VLC_INPUT_OPTION_UNIQUE|VLC_INPUT_OPTION_TRUSTED);
+    input_item_AddOption(p_item, ":no-osd", VLC_INPUT_OPTION_UNIQUE|VLC_INPUT_OPTION_TRUSTED);
+
+    /* Allocate descriptor */
+    input_thread_t *p_input = Create( p_parent, p_item, NULL, false, true, NULL );
+    if( !p_input )
+        return NULL;
+
+    if( !Init( p_input ) ) {
+        MainLoop(p_input, false);
+        End( p_input );
+    }
+
+    picture_t *p_pic = p_input->p->p_snapshot;
+
+    vlc_object_release( p_input );
+
+    return p_pic;
+}
+
+/**
  * Initialize an input and initialize it to preparse the item
  * This function is blocking. It will only accept parsing regular files.
  *
@@ -192,7 +223,7 @@ int input_Preparse( vlc_object_t *p_parent, input_item_t *p_item )
     input_thread_t *p_input;
 
     /* Allocate descriptor */
-    p_input = Create( p_parent, p_item, NULL, true, NULL );
+    p_input = Create( p_parent, p_item, NULL, true, false, NULL );
     if( !p_input )
         return VLC_EGENERIC;
 
@@ -291,6 +322,7 @@ input_item_t *input_GetItem( input_thread_t *p_input )
  *****************************************************************************/
 static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
                                const char *psz_header, bool b_quick,
+                               bool b_snapshot,
                                input_resource_t *p_resource )
 {
     input_thread_t *p_input = NULL;                 /* thread descriptor */
@@ -329,6 +361,7 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     /* Init Common fields */
     p_input->b_eof = false;
     p_input->p->b_can_pace_control = true;
+    p_input->p->p_snapshot = NULL;
     p_input->p->i_start = 0;
     p_input->p->i_time  = 0;
     p_input->p->i_stop  = 0;
@@ -465,6 +498,8 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     vlc_mutex_init( &p_input->p->counters.counters_lock );
 
     p_input->p->p_es_out_display = input_EsOutNew( p_input, p_input->p->i_rate );
+    if (b_snapshot)
+        es_out_Control(p_input->p->p_es_out_display, ES_OUT_SET_SNAPSHOT, &p_input->p->p_snapshot);
     p_input->p->p_es_out = NULL;
 
     /* Set the destructor when we are sure we are initialized */
diff --git a/src/input/input_internal.h b/src/input/input_internal.h
index e13022d..755b555 100644
--- a/src/input/input_internal.h
+++ b/src/input/input_internal.h
@@ -90,6 +90,7 @@ struct input_thread_private_t
     /* Current state */
     bool        b_recording;
     int         i_rate;
+    picture_t   *p_snapshot;
 
     /* Playtime configuration and state */
     int64_t     i_start;    /* :start-time,0 by default */
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 4411e6e..67f65e0 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -212,6 +212,7 @@ input_resource_Release
 input_resource_TerminateVout
 input_resource_Terminate
 input_resource_HoldAout
+input_Snapshot
 input_Start
 input_Stop
 input_vaControl
-- 
1.7.10.4



More information about the vlc-devel mailing list