Operation options (filtering, sorting and more)



Hi Grileros!

I've been thinking about how we should handle options for operations, in
an extensible and unified way, and tried to narrow down the issues where
choices need to be made.

Executive summary:
I think these are around the way we handle source capabilities, and the
negotiations related to them, and revolve around the following choices:
 - static or dynamic capabilities?
 - one or two instances (of options/capabilities) for negotiation?
 - should we express capabilities with the same type as options?
 - should we express capabilities in the same object instance as options?

Juan has already done some experimentations [1] on filtering, and it
would be good to see how well the choices he tried there turned out.

Below follows a document explaining my thoughts and interrogations, for
ease of commenting, and you can also find it in pdf on line for ease of
reading [2].

Please feel invited to say what alternatives you feel are the best (all
aren't exclusive, some can be combined), and tell why.
Of course, you can propose argumented alternative ideas as well.

Regards,

Guij

[1]
http://www.gitorious.org/~jasuarez/grilo/jasuarez-grilo/commits/wip/filtering
[2] http://emont.org/grilo/operation_options.pdf

----8<--------------

#################################
Extensible Options for Operations
#################################


=================
Problem Statement
=================

We want to have an extensible way to provide options to change the
behaviour of
various operations in Grilo. Such options include the filtering and
sorting of
results. These options would not add state to a media source: they should be
provided when launching an operation, and should affect only this operation.

We also want to have a way for the caller to know in advance to what
extent the
options it wants can be handled by the underlying source.

We are ready to break the API to put in place the new options system,
but the
system should be so that adding new options in the future should be possible
without breaking neither API nor ABI.


=================================
How to handle source capabilities
=================================

I think that's the part where the API decisions seem the least obvious
and need
decision making. These parameters would strongly influence the APIs we
have on
GrlOperationOptions, and whether we have an additional GrlOperationCaps
(type
names are up for discussion as well).

Static
------

The options supported by the source are properties (not necessarily GObjcet
properties), similar to  grl_metadata_source_supported_keys(). There
could be
one per operation.

:Pros: Simple to implement for sources
:Cons: Less flexible

Dynamic
-------

We give a set of options, the operation to achieve (and maybe the other
options
to be passed to the operation function), and the source says whether it can
honour the options, optionally telling what it can honour in the passed
options.

:Pros: More flexible
:Cons: Slightly more to implement for sources


One instance for options negotiation
------------------------------------

:Pros: less instances to create
:Cons: double usage for the instance, semantics lose a bit of clarity


Two instances for options negotiation
-------------------------------------

:Pros: simpler structure(s), lower memory footprint of instance
:Cons: caller needs to handle 2 instances


Examples
--------

Some examples of what various choices would result in code for someone using
the API.

one instance, dynamic::

  options = grl_operation_options_new ();
  /* frobuz and gharble1 matter so much to us that we don't want to do the
   * operation if they aren't available. gharble2 would be cool, but we
can do
   * without it. */
  grl_operation_options_set_frobuz (options, frobuz);
  grl_operation_options_add_gharbles (options, gharble1, gharble2, NULL);
  grl_media_source_fixate_options (source, GRL_OP_BROWSE, options);
  if (grl_operation_options_get_frobuz (options) == frobuz
      && grl_operation_options_has_gharble (options, gharble1))
    grl_media_source_browse (source, container, keys, skip, count, flags,
                             options, my_callback, some_data);

one instance, static::

  options = grl_media_source_get_capabilities (source, GRL_OP_BROWSE);
  /* frobuz and gharble1 matter so much to us that we don't want to do the
   * operation if they aren't available. gharble2 would be cool, but we
can do
   * without it. */
  if (grl_operation_options_can_set_frobuz (options)
      && grl_operation_options_can_have_gharble (options, gharble1)) {
    grl_operation_options_set_frobuz (options, frobuz);
    grl_operation_options_add_gharble (options, gharble1);
    if (grl_operation_options_can_have_gharble (options, gharble2))
      grl_operation_options_add_gharble (options, gharble2);
    grl_media_source_browse (source, container, keys, skip, count, flags,
                             options, my_callback, some_data);
  }


two instances, static::

  /* Here, caps and options could be of different types */
  caps = grl_media_source_get_capabilities (source, GRL_OP_BROWSE);
  /* frobuz and gharble1 matter so much to us that we don't want to do the
   * operation if they aren't available. gharble2 would be cool, but we
can do
   * without it. */
  if (grl_operation_caps_can_set_frobuz (caps)
      && grl_operation_caps_have_gharble (caps, gharble1)) {
    options = grl_operation_options_new ();
    grl_operation_options_set_frobuz (options, frobuz);
    grl_operation_options_add_gharble (options, gharble1);
    if (grl_operation_caps_have_gharble (caps, gharble2))
      grl_operation_options_add_gharble (options, gharble2);
    grl_media_source_browse (source, container, keys, skip, count, flags,
                             options, my_callback, some_data);
  }


two instances, dynamic::

  options = grl_operation_options_new ();
  /* frobuz and gharble1 matter so much to us that we don't want to do the
   * operation if they aren't available. gharble2 would be cool, but we
can do
   * without it. */
  grl_operation_options_set_frobuz (options, frobuz);
  grl_operation_options_add_gharbles (options, gharble1, gharble2, NULL);
  grl_media_source_supported_options (source, GRL_OP_BROWSE, options,
                                      &effective_options);
  if (grl_operation_options_get_frobuz (effective_options) == frobuz
      && grl_operation_options_has_gharble (effective_options, gharble1))
    grl_media_source_browse (source, container, keys, skip, count, flags,
                             effective_options, my_callback, some_data);


two instances, static (variant)::

  caps = grl_media_source_get_capabilities (source, GRL_OP_BROWSE);
  /* frobuz and gharble1 matter so much to us that we don't want to do the
   * operation if they aren't available. gharble2 would be cool, but we
can do
   * without it. */
  options = grl_operation_options_new ();
  grl_operation_options_set_frobuz (options, frobuz);
  grl_operation_options_add_gharbles (options, gharble1, gharble2, NULL);

  effective_options = grl_operation_options_intersect (caps, options);

  if (grl_operation_options_get_frobuz (effective_options) == frobuz
      && grl_operation_options_has_gharble (effective_options, gharble1))
    grl_media_source_browse (source, container, keys, skip, count, flags,
                             effective_options, my_callback, some_data);

=================
Proposed Solution
=================

 - Opaque structure, with setters/getters, so that we can easily add new
   options while preserving the API
 - Padding in the structure, to be able to add new options while
preserving the
   ABI. Alternative: GType private stuff
 - still need a choice: static? dynamic? one instance? two instances?
one type?
   two types (options + caps)?


======================
Object and API changes
======================

New types
---------

Types I think we will need in any case::

  typedef struct _GrlOperationOptions GrlOperationOptions;

  typedef enum {
    GRL_DIRECTION_ASCENDING,
    GRL_DIRECTION_DESCENDING
  } GrlDirection;

  typedef enum {
    GRL_FILTER_TYPE_NONE = 0,
    GRL_FILTER_TYPE_AUDIO = (1 << 0),
    GRL_FILTER_TYPE_VIDEO = (1 << 1),
    GRL_FILTER_TYPE_IMAGE = (1 << 2),
  } GrlTypeFilter;

API additions for media source
------------------------------

At least::

  guint grl_media_source_browse (GrlMediaSource *source,
                                 GrlMedia *container,
                                 const GList *keys,
                                 guint skip,
                                 guint count,
                                 GrlMetadataResolutionFlags flags,
                                 GrlOperationOptions *options,
                                 GrlMediaSourceResultCb callback,
                                 gpointer user_data);

And the same thing for the other operations. Should we do this for Metadata
Sources operations as well?

Then we need some methods to determine the capabilities of a source,

First idea to help the user adjust his options to what the source supports::

  gboolean
  grl_media_source_supported_options (GrlMediaSource *source,
                                      GrlSupportedOps operation,
                                      GrlOperationOptions *options,
                                      GrlOperationOptions
**effective_options);

Alternative idea, similar to GstCaps in gstreamer, simpler to implement for
plugins, slightly less flexible::

  GrlOperationOptions *
  grl_media_source_supported_options (GrlMediaSource *source,
                                      GrlSupportedOps operation);

  GrlOperationOptions *
  grl_operation_options_intersect (GrlOperationOptions *options1,
                                   GrlOperationOptions *options2);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]