[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