[vlc-devel] [PATCH 1/8] stream_extractor: split joint capability into two

Rémi Denis-Courmont remi at remlab.net
Sat Feb 18 09:06:29 CET 2017


Le torstaina 16. helmikuuta 2017, 22.44.11 EET Filip Roséen a écrit :
> These changes splits the functionality for a stream-extractor into two
> different objects, one being a stream_extractor_t (used to extract
> data for an entity within a stream based on an identifier), and the
> other, stream_directory_t, is to list entities within a stream.
> 
> Summary of changes:
> 
>   - update and refine documentation belonging to stream-extractors
>   - stream_extractor_t is now stream_extractor_t and stream_directory_t
>   - expose new function vlc_stream_directory_Attach to probe for a
>     stream_directory to attach to a stream.
> ---
>  include/vlc_stream_extractor.h | 118 +++++++++--------
>  src/input/input.c              |   2 +-
>  src/input/stream_extractor.c   | 286
> +++++++++++++++++++++++++++-------------- src/libvlccore.sym             | 
>  1 +
>  4 files changed, 257 insertions(+), 150 deletions(-)
> 
> diff --git a/include/vlc_stream_extractor.h b/include/vlc_stream_extractor.h
> index 36bbd13131..42cf6cd58c 100644
> --- a/include/vlc_stream_extractor.h
> +++ b/include/vlc_stream_extractor.h
> @@ -35,101 +35,109 @@ extern "C" {
>   *
>   * A \em stream-extractor can do one of two things;
>   *
> - *  - either it lists the logical entries within a stream, or;
> - *  - it extracts the data associated with one of those entries based
> - *    on a unique identifier.
> + *  - lists the logical entries within a stream:
> + *    - type = stream_directory_t
> + *    - capability = "stream_directory"
> + *
> + *  - extract data associated with one specific entry within a stream:
> + *    - type = stream_extractor_t
> + *    - capability = "stream_extractor"
>   *
>   * @{
>   *
>   **/
> +
>  struct stream_extractor_t {
>      VLC_COMMON_MEMBERS
> 
> -    union {
> -        /**
> -         * Callbacks for entity extraction
> -         *
> -         * The following callbacks shall be populated if the
> stream_extractor is -         * used to extract a specific entity from the
> source-stream. Each -         * callback shall behave as those, with the
> same name, specified in \ref -         * stream_t.
> -         *
> -         **/
> -        struct {
> -            ssize_t  (*pf_read)(struct stream_extractor_t *, void *buf,
> size_t len); -            block_t* (*pf_block)(struct stream_extractor_t *,
> bool *eof); -            int      (*pf_seek)(struct stream_extractor_t *,
> uint64_t); -            int      (*pf_control)(struct stream_extractor_t *,
> int i_query, va_list); -
> -        } stream;
> -
> -        /**
> -         * Callbacks for stream directory listing
> -         *
> -         * These callbacks are used when a stream is to be treated as a
> -         * directory, it shall behave as those, with the same name,
> specified -         * in \ref stream_t.
> -         *
> -         **/
> -        struct {
> -            int (*pf_readdir)(struct stream_extractor_t *,
> input_item_node_t *); -
> -        } directory;
> -
> -    };
> -
> -    void* p_sys; /**< Private data pointer */
> -    stream_t* source; /**< The source stream */
> -    char* identifier; /**< name of requested entity to extract, or NULL
> -                       **  when requested to list directories */
> +    /**
> +     * \name Callbacks for entity extraction
> +     *
> +     * The following members shall be populated as specified by the
> +     * documentation associated with \stream_t for the equivalent name.

Does this actually work? It looks weird; I´d expect \ref stream_t or such...

> +     *
> +     * @{
> +     **/
> +    ssize_t  (*pf_read)(struct stream_extractor_t*, void* buf, size_t len);
> +    block_t* (*pf_block)(struct stream_extractor_t*, bool* eof);
> +    int      (*pf_seek)(struct stream_extractor_t*, uint64_t);
> +    int      (*pf_control)(struct stream_extractor_t*, int request, va_list
> args); +    /** @} */
> +
> +    char const* identifier; /**< the name of the entity to be extracted */

This is valid but I don´t know why a very few developpers do this. The defacto 
standard is qualifier before type.

> +    stream_t* source; /**< the source stream to be consumed */
> +    void* p_sys;      /**< private opaque handle to be used by the module
> */ +};
> +
> +struct stream_directory_t {
> +    VLC_COMMON_MEMBERS;
> +
> +    /**
> +     * \name Callbacks for stream directories
> +     *
> +     * The following members shall be populated as specified by the
> +     * documentation associated with \stream_t for the equivalent name.
> +     *
> +     * @{
> +     **/
> +    int (*pf_readdir)(struct stream_directory_t*, input_item_node_t* );
> +    /** @} */
> +
> +    stream_t* source; /**< the source stream to be consumed */
> +    void* p_sys; /**< private opaque handle to be used by the module */
>  };
> 
>  typedef struct stream_extractor_t stream_extractor_t;
> +typedef struct stream_directory_t stream_directory_t;

Should be merged with the struct typedef (when possible).

> 
>  /**
>   * Create a relative MRL for the associated entity
>   *
> - * This function shall be used by stream_extractor_t's in order to
> - * generate a MRL that refers to an entity within the stream. Normally
> + * This function shall be used by stream_directory_t's in order to
> + * generate an MRL that refers to an entity within the stream. Normally
>   * this function will only be invoked within `pf_readdir` in order to
>   * get the virtual path of the listed items.
>   *
>   * \warning the returned value is to be freed by the caller

The (...).

>   *
> - * \param extractor the stream_extractor_t in which the entity belongs
> + * \param extractor the stream_directory_t for which the entity belongs
>   * \param subentry the name of the entity in question
>   *
>   * \return a pointer to the resulting MRL on success, NULL on failure
>   **/
> -VLC_API char* vlc_stream_extractor_CreateMRL( stream_extractor_t*,
> +VLC_API char* vlc_stream_extractor_CreateMRL( stream_directory_t*,
>                                                char const* subentry );
> 
>  /**
> - * Construct a new stream_extractor-based stream
> - *
> - * This function is used to attach a stream extractor to an already
> - * existing stream.
> - *
> - * If \p identifier is `NULL`, `*stream` is guaranteed to refer to a
> - * directory, otherwise \p identifier denotes the specific subentry
> - * that one would like to access within the stream.
> + * \name Attach a stream-extractor to the passed stream
>   *
> - * If \p identifier is not NULL, `*stream` will refer to data for the
> - * entity in question.
> + * These functions are used to attach a stream extractor to an already
> existing + * stream. As hinted by their names, \vlc_stream_extractor_Attach
> will attach + * an entity-extractor, whereas \vlc_stream_directory_Attach
> will attach a + * stream-directory.
>   *
>   * \param[out] stream a pointer-to-pointer to stream, `*stream` will
>   *             refer to the attached stream on success, and left
>   *             untouched on failure.
> - * \param identifier NULL or a c-style string referring to the desired
> entity
> + * \param identifier (if present) NULL or a c-style string
> referring to the desired entity
> * \param module_name NULL or an explicit
> stream-extractor module name *
>   * \return VLC_SUCCESS if a stream-extractor was successfully
>   *         attached, an error-code on failure.
> + *
> + * @{
>   **/
> 
>  VLC_API int vlc_stream_extractor_Attach( stream_t** source,
>                                           char const* identifier,
>                                           char const* module_name );
> +
> +VLC_API int vlc_stream_directory_Attach( stream_t** source,
> +                                         char const* module_name );
> +/**
> + * @}
> + */
> +
>  /**
>   * @}
>   */
> diff --git a/src/input/input.c b/src/input/input.c
> index 9eead35fb5..faab520bf9 100644
> --- a/src/input/input.c
> +++ b/src/input/input.c
> @@ -2299,7 +2299,7 @@ InputStreamHandleAnchor( input_source_t *source,
> stream_t **stream,
> 
>      if( remaining == 0 )
>      {
> -        if( vlc_stream_extractor_Attach( stream, NULL, NULL ) )
> +        if( vlc_stream_directory_Attach( stream, NULL ) )
>              msg_Dbg( source, "attach of directory extractor failed" );
> 
>          return VLC_SUCCESS;
> diff --git a/src/input/stream_extractor.c b/src/input/stream_extractor.c
> index 573ba9d199..57a9a87a68 100644
> --- a/src/input/stream_extractor.c
> +++ b/src/input/stream_extractor.c
> @@ -38,7 +38,7 @@
>  #include "mrl_helpers.h"
> 
>  /**
> - * \defgroup stream_extractor_Private Stream Extractor Private
> + * \defgroup stream_extractor_Internals Stream Extractor Internals
>   * \ingroup stream_extractor
>   * \internal
>   * @{
> @@ -46,40 +46,95 @@
>   **/
> 
>  struct stream_extractor_private {
> -    stream_extractor_t public;
> -    stream_t* stream;
> -    module_t* module;
> -
> -    vlc_object_t* owner;
> +    union {
> +        stream_extractor_t extractor;
> +        stream_directory_t directory;
> +    };
> +
> +    /**
> +     * Callback to handle initialization
> +     *
> +     * \pf_init will be called after successful module probing to
> initialize +     * the relevant members of the underlying stream-extractor
> object, as well +     * as the wrapping stream.
> +     **/
> +    int (*pf_init)( struct stream_extractor_private*, stream_t* );

I don´t really get why this is needed inside a private structure.

> +
> +    /**
> +     * Callback to handle clean-up
> +     *
> +     * \pf_clean, unless NULL, will be called when the stream-extractor is
> to +     * be destroyed, and shall be used to clean-up resources (acquired
> during +     * initialization, see \pf_init).
> +     */
> +    void (*pf_clean)( struct stream_extractor_private* );

Ditto.

> +
> +    stream_t* wrapper; /**< the wrapping stream_t used to access the
> underlying +                            stream-extractor */
> +
> +    stream_t* source; /**< the source stream consumed by the
> stream-extractor */ +    module_t* module; /**< the stream-extractor module
> */
> +
> +    vlc_object_t* object; /**< the underlying stream-extractor object */
>  };
> 
>  /**
> - * Release the private data associated with a stream-extractor
> + * Create an MRL for a specific sub-entry
>   *
> + * This internal function is used to create an MRL that refers to \subentry
> + * within \base, see \mrl_helpers for further information.
> + **/
> +
> +static char*
> +StreamExtractorCreateMRL( char const* base, char const* subentry )
> +{
> +    struct vlc_memstream buffer;
> +    char* escaped;
> +
> +    if( mrl_EscapeFragmentIdentifier( &escaped, subentry ) )
> +        return NULL;
> +
> +    if( vlc_memstream_open( &buffer ) )
> +    {
> +        free( escaped );
> +        return NULL;
> +    }
> +
> +    vlc_memstream_puts( &buffer, base );
> +
> +    if( !strstr( base, "#" ) )
> +        vlc_memstream_putc( &buffer, '#' );
> +
> +    vlc_memstream_printf( &buffer, "!/%s", escaped );
> +
> +    free( escaped );
> +    return vlc_memstream_close( &buffer ) ? NULL : buffer.ptr;
> +}
> +
> +/**
> + * Release the private data associated with a stream-extractor
>   * \param priv pointer to the private section
>   */
>  static void se_Release( struct stream_extractor_private* priv )
>  {
> -    free( priv->public.identifier );
> +    if( priv->pf_clean )
> +        priv->pf_clean( priv );
> 
>      if( priv->module )
>      {
> -        module_unneed( &priv->public, priv->module );
> -        vlc_stream_Delete( priv->public.source );
> +        module_unneed( priv->object, priv->module );
> +
> +        if( priv->source )
> +            vlc_stream_Delete( priv->source );
>      }
> 
> -    vlc_object_release( &priv->public );
> +    vlc_object_release( priv->object );
>  }
> 
>  /**
> - * \defgroup stream_extractor_Callbacks Stream Extractor Callbacks
> - * \ingroup stream_extractor
> + * \name Callbacks to forward work to the underlying stream-extractor
> + *
>   * @{
> - *   \file
> - *   These functions simply forwards the relevant stream-request to
> - *   the underlying stream-extractor. They are a basic form of
> - *   type-erasure in that the outside world sees a stream_t, but the
> - *   work is actually done by a stream_extractor_t.
>   */
> 
>  static void
> @@ -92,42 +147,41 @@ static ssize_t
>  se_StreamRead( stream_t* stream, void* buf, size_t len )
>  {
>      struct stream_extractor_private* priv = stream->p_sys;
> -    stream_extractor_t* extractor = &priv->public;
> -    return extractor->stream.pf_read( extractor, buf, len );
> +    return priv->extractor.pf_read( &priv->extractor, buf, len );
>  }
> 
>  static block_t*
>  se_StreamBlock( stream_t* stream, bool* eof )
>  {
>      struct stream_extractor_private* priv = stream->p_sys;
> -    stream_extractor_t* extractor = &priv->public;
> -    return extractor->stream.pf_block( extractor, eof );
> +    return priv->extractor.pf_block( &priv->extractor, eof );
>  }
> 
>  static int
>  se_StreamSeek( stream_t* stream, uint64_t offset )
>  {
>      struct stream_extractor_private* priv = stream->p_sys;
> -    stream_extractor_t* extractor = &priv->public;
> -    return extractor->stream.pf_seek( extractor, offset );
> +    return priv->extractor.pf_seek( &priv->extractor, offset );
>  }
> 
>  static int
> -se_StreamReadDir( stream_t* stream, input_item_node_t* node )
> +se_ReadDir( stream_t* stream, input_item_node_t* node )
>  {
>      struct stream_extractor_private* priv = stream->p_sys;
> -    stream_extractor_t* extractor = &priv->public;
> -    return extractor->directory.pf_readdir( extractor, node );
> +    return priv->directory.pf_readdir( &priv->directory, node );
>  }
> 
>  static int
>  se_StreamControl( stream_t* stream, int req, va_list args )
>  {
>      struct stream_extractor_private* priv = stream->p_sys;
> -    stream_extractor_t* extractor = &priv->public;
> +    return priv->extractor.pf_control( &priv->extractor, req, args );
> +}
> 
> -    if( extractor->identifier )
> -        return extractor->stream.pf_control( extractor, req, args );
> +static int
> +se_DirControl( stream_t* stream, int req, va_list args )
> +{
> +    (void)stream;
> 
>      if( req == STREAM_IS_DIRECTORY )
>      {
> @@ -137,83 +191,134 @@ se_StreamControl( stream_t* stream, int req, va_list
> args )
> 
>      return VLC_EGENERIC;
>  }
> +
> +/**
> + * @}
> + **/
> +
> +/**
> + * \name stream-extractor resource handlers
> + * \ingroup stream_extractor
> + * @{
> + */
> +
> +static int
> +se_InitStream( struct stream_extractor_private* priv, stream_t* s )
> +{
> +    if( priv->extractor.pf_read ) s->pf_read = se_StreamRead;
> +    else                          s->pf_block = se_StreamBlock;
> +
> +    s->pf_seek = se_StreamSeek;
> +    s->pf_control = se_StreamControl;
> +    s->psz_url = StreamExtractorCreateMRL( priv->extractor.source->psz_url,
> +                                           priv->extractor.identifier ); +
>    if( unlikely( !s->psz_url ) )
> +        return VLC_ENOMEM;
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static void
> +se_CleanStream( struct stream_extractor_private* priv )
> +{
> +    free( (char*)priv->extractor.identifier );
> +}
> +
> +static int
> +se_InitDirectory( struct stream_extractor_private* priv, stream_t* s )
> +{
> +    stream_directory_t* directory = &priv->directory;
> +
> +    s->pf_readdir = se_ReadDir;
> +    s->pf_control = se_DirControl;
> +    s->psz_url = strdup( directory->source->psz_url );
> +
> +    if( unlikely( !s->psz_url ) )
> +        return VLC_EGENERIC;
> +
> +    return VLC_SUCCESS;
> +}
> +
>  /**
>   * @}
>   **/
> 
>  /**
> - * Initialize the public stream_t for a stream_extractor_t
> + * Create the public stream_t that wraps a stream-extractor
>   *
> - * This function simply initializes the relevant data-members of the
> - * public stream_t which is a handle to the internal
> - * stream_extractor_t.
> + * This initializes the relevant data-members of the public stream_t which
> is + * used to read from the underlying stream-extractor.
>   *
> - * \param obj the private section of the stream_extractor_t
> + * \param priv the private section of the stream_extractor_t
>   * \param source the source stream which the stream_extractor_t should
>   *        will read from
>   * \return VLC_SUCCESS on success, an error-code on failure.
>   **/
>  static int
> -se_InitStream( struct stream_extractor_private* priv, stream_t* source )
> +se_AttachWrapper( struct stream_extractor_private* priv, stream_t* source )
> {
> -    stream_t* s = vlc_stream_CommonNew( priv->public.obj.parent,
> -                                        se_StreamDelete );
> -    if( unlikely( !s ) )
> -        return VLC_EGENERIC;
> -
> -    if( priv->public.identifier )
> -    {
> -        if( priv->public.stream.pf_read ) s->pf_read = se_StreamRead;
> -        else                              s->pf_block = se_StreamBlock;
> -
> -        s->pf_seek = se_StreamSeek;
> -        s->psz_url = vlc_stream_extractor_CreateMRL( &priv->public,
> -                                                     
> priv->public.identifier ); -    }
> -    else
> -    {
> -        s->pf_readdir = se_StreamReadDir;
> -        s->psz_url = source->psz_url ? strdup( source->psz_url ) : NULL;
> -    }
> +    stream_t* s = vlc_stream_CommonNew( source->obj.parent, se_StreamDelete
> );
> 
> +    if( unlikely( !s ) )
> +        return VLC_ENOMEM;
> 
> -    if( source->psz_url && unlikely( !s->psz_url ) )
> +    if( priv->pf_init( priv, s ) )
>      {
>          stream_CommonDelete( s );
>          return VLC_EGENERIC;
>      }
> 
> -    priv->stream = s;
> -    priv->stream->pf_control = se_StreamControl;
> -    priv->stream->p_input = source->p_input;
> -    priv->stream->p_sys = priv;
> +    priv->wrapper = s;
> +    priv->wrapper->p_input = source->p_input;
> +    priv->wrapper->p_sys = priv;
> +
> +    priv->source = source;
> 
>      return VLC_SUCCESS;
>  }
> 
> -int
> -vlc_stream_extractor_Attach( stream_t** source, char const* identifier,
> -                             char const* module_name )
> +static int
> +StreamExtractorAttach( stream_t** source, char const* identifier,
> +    char const* module_name )
>  {
> +    char const* capability = identifier ? "stream_extractor"
> +                                        : "stream_directory";
> +
>      struct stream_extractor_private* priv = vlc_custom_create(
> -        (*source)->obj.parent, sizeof( *priv ), "stream_extractor" );
> +        (*source)->obj.parent, sizeof( *priv ), capability );
> 
>      if( unlikely( !priv ) )
>          return VLC_ENOMEM;
> 
> -    priv->public.identifier = identifier ? strdup( identifier ) : NULL;
> +    if( strcmp( capability, "stream_extractor" ) == 0 )
> +    {
> +        priv->object = VLC_OBJECT( &priv->extractor );
> 
> -    if( unlikely( identifier && !priv->public.identifier ) )
> -        goto error;
> +        priv->pf_init = se_InitStream;
> +        priv->pf_clean = se_CleanStream;
> +
> +        priv->extractor.source = *source;
> +        priv->extractor.identifier = strdup( identifier );
> +
> +        if( unlikely( !priv->extractor.identifier ) )
> +            goto error;
> +    }
> +    else
> +    {
> +        priv->object = VLC_OBJECT( &priv->directory );
> +
> +        priv->pf_init = se_InitDirectory;
> +        priv->pf_clean = NULL;
> +
> +        priv->directory.source = *source;
> +    }
> 
> -    priv->public.source = *source;
> -    priv->module = module_need( &priv->public, "stream_extractor",
> -                                module_name, true );
> +    priv->module = module_need( priv->object, capability, module_name, true
> );
> 
> -    if( !priv->module || se_InitStream( priv, *source ) )
> +    if( !priv->module || se_AttachWrapper( priv, *source ) )
>          goto error;
> 
> -    *source = priv->stream;
> +    *source = priv->wrapper;
>      return VLC_SUCCESS;
> 
>  error:
> @@ -221,31 +326,24 @@ error:
>      return VLC_EGENERIC;
>  }
> 
> -char*
> -vlc_stream_extractor_CreateMRL( stream_extractor_t* extractor,
> -                                char const* subentry )
> +int
> +vlc_stream_directory_Attach( stream_t** source, char const* module_name )
>  {
> -    struct vlc_memstream buffer;
> -    char* escaped;
> -
> -    if( mrl_EscapeFragmentIdentifier( &escaped, subentry ) )
> -        return NULL;
> -
> -    if( vlc_memstream_open( &buffer ) )
> -    {
> -        free( escaped );
> -        return NULL;
> -    }
> -
> -    vlc_memstream_puts( &buffer, extractor->source->psz_url );
> -
> -    if( !strstr( extractor->source->psz_url, "#" ) )
> -        vlc_memstream_putc( &buffer, '#' );
> +    return StreamExtractorAttach( source, NULL, module_name );
> +}
> 
> -    vlc_memstream_printf( &buffer, "!/%s", escaped );
> +int
> +vlc_stream_extractor_Attach( stream_t** source, char const* identifier,
> +                             char const* module_name )
> +{
> +    return StreamExtractorAttach( source, identifier, module_name );
> +}
> 
> -    free( escaped );
> -    return vlc_memstream_close( &buffer ) ? NULL : buffer.ptr;
> +char*
> +vlc_stream_extractor_CreateMRL( stream_directory_t* directory,
> +                                char const* subentry )
> +{
> +    return StreamExtractorCreateMRL( directory->source->psz_url, subentry
> ); }
> 
>  /**
> diff --git a/src/libvlccore.sym b/src/libvlccore.sym
> index 150409e073..455a03b2fe 100644
> --- a/src/libvlccore.sym
> +++ b/src/libvlccore.sym
> @@ -394,6 +394,7 @@ spu_ChangeFilters
>  spu_Render
>  spu_RegisterChannel
>  spu_ClearChannel
> +vlc_stream_directory_Attach
>  vlc_stream_extractor_Attach
>  vlc_stream_extractor_CreateMRL
>  vlc_stream_Block


-- 
雷米‧德尼-库尔蒙
https://www.remlab.net/



More information about the vlc-devel mailing list