[vlc-devel] [PATCH v4 2/3] Update media tree on browsing/preparsing

Romain Vimont rom1v at videolabs.io
Mon Jul 9 14:38:51 CEST 2018


On Mon, Jul 09, 2018 at 12:13:53PM +0200, Romain Vimont wrote:
> Browsing and preparsing, started by libvlc_MetadataRequest(), trigger
> input item events.
> 
> Listen to these events to update the media tree accordingly.
> ---
>  include/vlc_media_tree.h    |  19 ++++++
>  src/libvlccore.sym          |   1 +
>  src/media_tree/media_tree.c | 124 ++++++++++++++++++++++++++++++++++--
>  3 files changed, 138 insertions(+), 6 deletions(-)
> 
> diff --git a/include/vlc_media_tree.h b/include/vlc_media_tree.h
> index b9d2c6f194b..2c9097480ba 100644
> --- a/include/vlc_media_tree.h
> +++ b/include/vlc_media_tree.h
> @@ -57,6 +57,14 @@ typedef struct vlc_media_tree_callbacks_t
>       */
>      void (*listener_added)(vlc_media_tree_t *, void *userdata);
>  
> +    /**
> +     * Called when an input item notifies that a subtree has been added.
> +     *
> +     * Use vlc_media_tree_subtree_added_default implementation to call
> +     * node_added() for every new node.
> +     */
> +    void (*subtree_added)(vlc_media_tree_t *, const input_item_node_t *, void *userdata);
> +
>      /**
>       * Called after a new node has been added to the media tree, with lock held.
>       */
> @@ -68,6 +76,11 @@ typedef struct vlc_media_tree_callbacks_t
>       */
>      void (*node_removed)(vlc_media_tree_t *, const input_item_node_t *parent,
>                           const input_item_node_t *, void *userdata);
> +
> +    /**
> +     * Called when an input item notifies that it has been updated.
> +     */
> +    void (*input_updated)(vlc_media_tree_t *, const input_item_node_t *, void *userdata);
>  } vlc_media_tree_callbacks_t;
>  
>  /**
> @@ -85,6 +98,12 @@ typedef struct vlc_media_tree_listener_t
>   **/
>  VLC_API void vlc_media_tree_listener_added_default(vlc_media_tree_t *, void *userdata);
>  
> +/**
> + * Default implementation for subtree_added(), which calls node_added()
> + * for every new node.
> + **/
> +VLC_API void vlc_media_tree_subtree_added_default(vlc_media_tree_t *, const input_item_node_t *, void *userdata);
> +
>  /**
>   * Add listener. The lock must NOT be held.
>   */
> diff --git a/src/libvlccore.sym b/src/libvlccore.sym
> index fd2d4bbe69c..802dcaee08f 100644
> --- a/src/libvlccore.sym
> +++ b/src/libvlccore.sym
> @@ -765,3 +765,4 @@ vlc_media_tree_Lock
>  vlc_media_tree_Unlock
>  vlc_media_tree_Find
>  vlc_media_tree_listener_added_default
> +vlc_media_tree_subtree_added_default
> diff --git a/src/media_tree/media_tree.c b/src/media_tree/media_tree.c
> index dec2418efa6..316bc794adb 100644
> --- a/src/media_tree/media_tree.c
> +++ b/src/media_tree/media_tree.c
> @@ -100,6 +100,28 @@ static void NotifyNodeRemoved(vlc_media_tree_t *tree, const input_item_node_t *p
>      FOREACH_END();
>  }
>  
> +static void NotifySubtreeAdded(vlc_media_tree_t *tree, const input_item_node_t *node)
> +{
> +    AssertLocked(tree);
> +    media_tree_private_t *priv = mt_priv(tree);
> +
> +    FOREACH_ARRAY(vlc_media_tree_listener_t *listener, priv->listeners)
> +        if (listener->cbs->subtree_added)
> +            listener->cbs->subtree_added(tree, node, listener->userdata);
> +    FOREACH_END();
> +}
> +
> +static void NotifyInputChanged(vlc_media_tree_t *tree, const input_item_node_t *node)
> +{
> +    AssertLocked(tree);
> +    media_tree_private_t *priv = mt_priv(tree);
> +
> +    FOREACH_ARRAY(vlc_media_tree_listener_t *listener, priv->listeners)
> +        if (listener->cbs->input_updated)
> +            listener->cbs->input_updated(tree, node, listener->userdata);
> +    FOREACH_END();
> +}
> +
>  static bool FindNodeByInput(input_item_node_t *parent, const input_item_t *input,
>                              input_item_node_t **result, input_item_node_t **result_parent)
>  {
> @@ -121,11 +143,95 @@ static bool FindNodeByInput(input_item_node_t *parent, const input_item_t *input
>      return false;
>  }
>  
> +static input_item_node_t *AddChild(vlc_media_tree_t *tree, input_item_node_t *parent, input_item_t *input);
> +
> +static void AddSubtree(vlc_media_tree_t *tree, input_item_node_t *p_to, input_item_node_t *p_from)
> +{
> +    for(int i = 0; i < p_from->i_children; ++i)
> +    {
> +        input_item_node_t *p_child = p_from->pp_children[i];
> +        input_item_node_t *node = AddChild(tree, p_to, p_child->p_item);
> +        if (unlikely(!node))
> +            abort(); /* what could we do? */

Just to highlight: this is wrong, but what should we do instead if the
malloc fails?

This function is called by the callback input_item_subtree_added().
(Also, since vlc_media_tree_t is not a vlc_object_t, we cannot easily
log.)

> +
> +        AddSubtree(tree, node, p_child);
> +    }
> +}
> +
> +static void input_item_subtree_added(const vlc_event_t *p_event, void *userdata)
> +{
> +    vlc_media_tree_t *tree = userdata;
> +    input_item_t *input = p_event->p_obj;
> +
> +    vlc_media_tree_Lock(tree);
> +    input_item_node_t *p_subtree_root;
> +    // TODO retrieve the node without traversing the tree
> +    bool found = FindNodeByInput(&tree->root, input, &p_subtree_root, NULL);
> +    assert(found);
> +    VLC_UNUSED(found);
> +
> +    input_item_node_t *p_from = p_event->u.input_item_subitem_tree_added.p_root;
> +    AddSubtree(tree, p_subtree_root, p_from);
> +    NotifySubtreeAdded(tree, p_subtree_root);
> +    vlc_media_tree_Unlock(tree);
> +}
> +
> +static void input_item_changed(const vlc_event_t *p_event, void *userdata)
> +{
> +    vlc_media_tree_t *tree = userdata;
> +    input_item_t *input = p_event->p_obj;
> +
> +    vlc_media_tree_Lock(tree);
> +    input_item_node_t *node;
> +    // TODO retrieve the node without traversing the tree
> +    bool found = FindNodeByInput(&tree->root, input, &node, NULL);
> +    assert(found);
> +    VLC_UNUSED(found);
> +
> +    NotifyInputChanged(tree, node);
> +    vlc_media_tree_Unlock(tree);
> +}
> +
> +static void RegisterInputEvents(vlc_media_tree_t *tree, input_item_t *input)
> +{
> +    vlc_event_manager_t *p_em = &input->event_manager;
> +    vlc_event_attach(p_em, vlc_InputItemSubItemTreeAdded, input_item_subtree_added, tree);
> +    vlc_event_attach(p_em, vlc_InputItemDurationChanged, input_item_changed, tree);
> +    vlc_event_attach(p_em, vlc_InputItemMetaChanged, input_item_changed, tree);
> +    vlc_event_attach(p_em, vlc_InputItemNameChanged, input_item_changed, tree);
> +    vlc_event_attach(p_em, vlc_InputItemInfoChanged, input_item_changed, tree);
> +    vlc_event_attach(p_em, vlc_InputItemErrorWhenReadingChanged, input_item_changed, tree);
> +}
> +
> +static void DeregisterInputEvents(vlc_media_tree_t *tree, input_item_t *input)
> +{
> +    vlc_event_manager_t *p_em = &input->event_manager;
> +    vlc_event_detach(p_em, vlc_InputItemSubItemTreeAdded, input_item_subtree_added, tree);
> +    vlc_event_detach(p_em, vlc_InputItemDurationChanged, input_item_changed, tree);
> +    vlc_event_detach(p_em, vlc_InputItemMetaChanged, input_item_changed, tree);
> +    vlc_event_detach(p_em, vlc_InputItemNameChanged, input_item_changed, tree);
> +    vlc_event_detach(p_em, vlc_InputItemInfoChanged, input_item_changed, tree);
> +    vlc_event_detach(p_em, vlc_InputItemErrorWhenReadingChanged, input_item_changed, tree);
> +}
> +
> +/* same as input_item_node_Delete(), but also deregister input events for every node */
> +static void DestroyNode(vlc_media_tree_t *tree, input_item_node_t *node)
> +{
> +    DeregisterInputEvents(tree, node->p_item);
> +
> +    for (int i = 0; i < node->i_children; ++i)
> +        DestroyNode(tree, node->pp_children[i]);
> +
> +    input_item_Release(node->p_item);
> +    free(node->pp_children);
> +    free(node);
> +}
> +
>  static void DestroyRootNode(vlc_media_tree_t *tree)
>  {
>      input_item_node_t *root = &tree->root;
>      for (int i = 0; i < root->i_children; ++i)
> -        input_item_node_Delete(root->pp_children[i]);
> +        DestroyNode(tree, root->pp_children[i]);
>  
>      free(root->pp_children);
>  }
> @@ -164,13 +270,14 @@ void vlc_media_tree_Unlock(vlc_media_tree_t *tree)
>      vlc_mutex_unlock(&priv->lock);
>  }
>  
> -static input_item_node_t *AddChild(input_item_node_t *parent, input_item_t *input)
> +static input_item_node_t *AddChild(vlc_media_tree_t *tree, input_item_node_t *parent, input_item_t *input)
>  {
>      input_item_node_t *node = input_item_node_Create(input);
>      if (unlikely(!node))
>          return NULL;
>  
>      input_item_node_AppendNode(parent, node);
> +    RegisterInputEvents(tree, input);
>  
>      return node;
>  }
> @@ -187,7 +294,7 @@ static void NotifyChildren(vlc_media_tree_t *tree, const input_item_node_t *node
>      }
>  }
>  
> -void vlc_media_tree_listener_added_default(vlc_media_tree_t *tree, void *userdata)
> +void vlc_media_tree_subtree_added_default(vlc_media_tree_t *tree, const input_item_node_t *node, void *userdata)
>  {
>      VLC_UNUSED(userdata);
>      AssertLocked(tree);
> @@ -195,10 +302,15 @@ void vlc_media_tree_listener_added_default(vlc_media_tree_t *tree, void *userdat
>      FOREACH_ARRAY(vlc_media_tree_listener_t *listener, priv->listeners)
>          /* notify "node added" for every node */
>          if (listener->cbs->node_added)
> -            NotifyChildren(tree, &tree->root, listener);
> +            NotifyChildren(tree, node, listener);
>      FOREACH_END();
>  }
>  
> +void vlc_media_tree_listener_added_default(vlc_media_tree_t *tree, void *userdata)
> +{
> +    vlc_media_tree_subtree_added_default(tree, &tree->root, userdata);
> +}
> +
>  void vlc_media_tree_AddListener(vlc_media_tree_t *tree, vlc_media_tree_listener_t *listener)
>  {
>      media_tree_private_t *priv = mt_priv(tree);
> @@ -227,7 +339,7 @@ input_item_node_t *vlc_media_tree_Add(vlc_media_tree_t *tree, input_item_node_t
>  {
>      AssertLocked(tree);
>  
> -    input_item_node_t *node = AddChild(parent, input);
> +    input_item_node_t *node = AddChild(tree, parent, input);
>      if (unlikely(!node))
>          return NULL;
>  
> @@ -256,6 +368,6 @@ bool vlc_media_tree_Remove(vlc_media_tree_t *tree, input_item_t *input)
>  
>      input_item_node_RemoveNode(parent, node);
>      NotifyNodeRemoved(tree, parent, node);
> -    input_item_node_Delete(node);
> +    DestroyNode(tree, node);
>      return true;
>  }
> -- 
> 2.18.0
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list