xine internals
 
  Engine architecture and data flow
  
   
    
   
   
    
   
   
    xine engine architecture
   
  
  
   Media streams usually consist of audio and video data multiplexed
   into one bitstream in the so-called system-layer (e.g. AVI, Quicktime or MPEG).
   A demuxer plugin is used to parse the system layer and extract audio and video
   packages. The demuxer uses an input plugin to read the data and stores it
   in pre-allocated buffers from the global buffer pool.
   The buffers are then added to the audio or video stream fifo.
  
  
   From the other end of these fifos the audio and video decoder threads
   consume the buffers and hand them over to the current audio or video
   decoder plugin for decompression. These plugins then send the decoded
   data to the output layer. The buffer holding the encoded
   data is no longer needed and thus released to the global buffer pool.
  
  
   In the output layer, the video frames and audio samples pass through a
   post plugin tree, which can apply effects or other operations to the data.
   When reaching the output loops, frames and samples are enqueued to be
   displayed, when the presentation time has arrived.
  
  
   A set of extra information travels with the data. Starting at the input and
   demuxer level, where this information is generated, the data is attached to
   the buffers as they wait in the fifo. The decoder loops copy the data to
   a storage of their own. From there, every frame and audio buffer leaving
   the stream layer is tagged with the data the decoder loop storage currently
   holds.
  
 
 
  Plugin system
  
   The plugin system enables some of xine's most valuable features:
   
    
     
      drop-in extensiability
     
    
    
     
      support parallel installation of multiple (incompatible) libxine versions
     
    
    
     
      support for multiple plugin directories
      ($prefix/lib/xine/plugins,
      $HOME/.xine/plugins, …)
     
    
    
     
      support for recursive plugin directories
      (plugins are found even in subdirectories of the plugin directories)
     
    
    
     
      version management
      (On start, xine finds all plugins in its plugin (sub)directories and
      chooses an appropriate version (usually the newest) for each plugin.)
     
    
    
     
      simplification
      (Plugins don't have to follow any special naming convention,
      and any plugin may contain an arbitrary subset of input, demuxer,
      decoder or output plugins.)
     
    
   
  
  
   Essentally, plugins are just shared objects, ie dynamic libraries. In
   contrast to normal dynamic libraries, they are stored outside of the
   system's library PATHs and libxine does its own bookkeeping, which
   enables most advanced features mentioned above.
  
  
   Plugin location and filesystem layout
   
    The primary goal for this new plugin mechanism was the need to support
    simultaneous installation of several (most likely incompatible)
    libxine versions without them overwriting each other's
    plugins. Therefore, we have this simple layout:
   
   
    Plugins are installed below XINE_PLUGINDIR
    (/usr/local/lib/xine/plugins by default).
    Note that plugins are never directly installed into XINE_PLUGINDIR.
    Instead, a separate subdirectory is created for each "plugin
    provider". A plugin provider is equivalent with the exact version of
    one source package. Typical examples include "xine-lib-0.9.11" or
    "xine-vcdnav-1.0". Every source package is free to install an
    arbitrary number of plugins in its own, private directory. If a
    package installs several plugins, they may optionally be organized
    further into subdirectories.
   
   
    So you will finally end up with something like this:
    
   /usr/local/lib/xine/plugins
     xine-lib-0.9.11
         demux_mpeg_block.so
         decode_mpeg.so
         video_out_xv.so
         …
     xine-vcdnav-0.9.11
         input_vcdnav.so
     xine-lib-1.2
         input
             file.so
             stdin_fifo.so
             vcd.so
         demuxers
             fli.so
             avi.so
             …
         decoders
             ffmpeg.so
             mpeg.so (may contain mpeg 1/2 audio and video decoders)
             pcm.so
             …
         output
             video_xv.so
             audio_oss.so
             …
     xine-lib-3.0
             avi.so (avi demuxer)
             mpeg.so (contains mpeg demuxers and audio/video decoders)
             video_out_xv.so (Xv video out)
             …
   
   
    As you can see, every package is free to organize plugins at will
    below its own plugin provider directory.
    Additionally, administrators may choose to put plugins directly into
    XINE_PLUGINDIR, or in a "local" subdirectory.
    Users may wish to put additional plugins in ~/.xine/plugins/.
    Again, there may be subdirectories to help organize the plugins.
   
   
    The default value for XINE_PLUGINDIR can be obtained using the
    pkg-config --variable=plugindir libxine command.
   
  
  
   Plugin Content: What's inside the .so?
   
    Each plugin library (.so file) contains an arbitrary number of (virtual)
    plugins. Typically, it will contain exactly one plugin. However, it
    may be useful to put a set of related plugins in one library, so they
    can share common code.
   
   
    First of all, what is a virtual plugin?
    A virtual plugin is essentially a structure that is defined by the
    xine engine. This structure typically contains lots of function
    pointers to the actual API functions.
    For each plugin API, there are several API versions, and each API
    version may specify a new, incompatible structure. Therefore, it is
    essential that only those plugins are loaded that support current
    libxine's API, so the .so file needs a plugin list that
    provides libxine with the version information, even before it tries to
    load any of the plugins.
   
   
    This plugin list is held in an array named xine_plugin_info":
    
   plugin_info_t xine_plugin_info[] = {
     /* type, API, "name", version, special_info, init_function */
     { PLUGIN_DEMUX, 20, "flac", XINE_VERSION_CODE, NULL, demux_flac_init_class },
     { PLUGIN_AUDIO_DECODER, 13, "flacdec", XINE_VERSION_CODE, &dec_info_audio, init_plugin },
     { PLUGIN_NONE, 0, "", 0, NULL, NULL }
   };
   
   
    The structure of xine_plugin_info may never be changed.
    If it ever needs to be changed, it must be renamed to avoid
    erraneous loading of incompatible plugins.
   
   
    xine_plugin_info can contain any number of plugins
    and must be terminated with a PLUGIN_NONE entry. Available plugin
    types are:
    
   #define PLUGIN_NONE           0
   #define PLUGIN_INPUT          1
   #define PLUGIN_DEMUX          2
   #define PLUGIN_AUDIO_DECODER  3
   #define PLUGIN_VIDEO_DECODER  4
   #define PLUGIN_SPU_DECODER    5
   #define PLUGIN_AUDIO_OUT      6
   #define PLUGIN_VIDEO_OUT      7
   #define PLUGIN_POST           8
   
   
    The plugin version number is generated from xine-lib's version number
    like this: MAJOR * 10000 + MINOR * 100 + SUBMINOR.
    This is not required, but it's an easy way to ensure that the version
    increases for every release.
   
   
    Every entry in xine_plugin_info has an initialization
    function for the plugin class context.
    This function returns a pointer to freshly allocated (typically
    via malloc()) structure containing mainly function
    pointers; these are the "methods" of the plugin class.
   
   
    The "plugin class" is not what we call to do the job yet (like decoding
    a video or something), it must be instantiated. One reason for having the
    class is to hold any global settings that must be accessed by every
    instance. Remember that xine library is multistream capable: multible
    videos can be decoded at the same time, thus several instances of the
    same plugin are possible.
   
   
    If you think this is pretty much an object-oriented aproach,
    then you're right.
   
   
    A fictitious file input plugin that supports input plugin API 12 and
    13, found in xine-lib 2.13.7 would then define this plugin list:
    
   #include <xine/plugin.h>
   …
   plugin_t *init_api12(void) {
     input_plugin_t *this;
   
     this = malloc(sizeof(input_plugin_t));
     …
     return (plugin_t *)this;
   }
   /* same thing, with different initialization for API 13 */
   
   const plugin_info_t xine_plugin_info[] = {
     { PLUGIN_INPUT, 12, "file", 21307, init_api12 },
     { PLUGIN_INPUT, 13, "file", 21307, init_api13 },
     { PLUGIN_NONE, 0, "", 0, NULL }
   }
    This input plugin supports two APIs, other plugins might provide a
    mixture of demuxer and decoder plugins that belong together somehow
    (ie. share common code).
   
   
    You'll find exact definitions of public functions and plugin structs
    in the appropriate header files for each plugin type:
    input/input_plugin.h for input plugins,
    demuxers/demux.h for demuxer plugins,
    xine-engine/video_decoder.h for video decoder plugins,
    xine-engine/audio_decoder.h for audio decoder plugins,
    xine-engine/post.h for post plugins,
    xine-engine/video_out.h for video out plugins,
    xine-engine/audio_out.h for audio out plugins.
    Additional information will also be given in the dedicated sections below.
   
   
    Many plugins will need some additional "private" data fields.
    These should be simply added at the end of the plugin structure.
    For example a demuxer plugin called "foo" with two private
    fields "xine" and "count" may have a plugin structure declared in
    the following way:
    
   typedef struct {
     /* public fields "inherited" from demux.h */
     demux_plugin_t    demux_plugin;
   
     xine_t           *xine;
     int               count;
   } demux_foo_t;
   
   
    The plugin would then access public members via the
    demux_plugin field and private fields directly.
   
   
    Summary: Plugins consist of two C-style classes, each representing a different context.
    
     
      
       The first is the so called "plugin class" context. This is a singleton context,
       which means it will exist either not at all or at most once per xine context.
       This plugin class context is a C-style class which is subclassing the related
       class from the xine plugin headers. This contains functions, which are
       independent of the actual instance of the plugin. Most prominently, it contains
       a factory method to instantiate the next context.
      
     
     
      
       The second context is the instance context. This is another C-style class, which
       is constructed and disposed withing the plugin class context. This one does
       the actual work and subclasses the related plugin struct from the xine plugin
       headers. It is instantiated for every separate running instance of the plugin
      
     
    
   
  
 
 
  What is this metronom thingy?
  
   Metronom serves two purposes:
   
    
     
      Generate vpts (virtual presentation time stamps) from pts (presentation time stamps)
      for a/v output and synchronization.
     
    
    
     
      Provide a master clock (system clock reference, scr), possibly provided
      by external scr plugins (this can be used if some hardware decoder or network
      server dictates the time).
     
    
   
  
  
   pts/vpts values are given in 1/90000 sec units. pts values in mpeg streams
   may wrap (that is, return to zero or any other value without further notice),
   can be missing on some frames or (for broken streams) may "dance" around
   the correct values. Metronom therefore has some heuristics built-in to generate
   clean vpts values which can then be used in the output layers to schedule audio/video
   output.
  
  
   The heuristics used in metronom have always been a field of research. Current metronom's
   implementation tries to stick to pts values as reported from demuxers,
   that is, vpts may be obtained by a simple operation of vpts = pts + vpts_offset,
   where vpts_offset takes into account any wraps. Whenever pts is zero,
   metronom will estimate vpts based on previous values. If a difference is found between the
   estimated and calculated vpts values by above formula, it will be smoothed by using a
   "drift correction".
  
 
 
  How does xine synchronize audio and video?
  
   Every image frame or audio buffer leaving decoder is tagged by metronom with
   a vpts information. This will tell video_out and audio_out threads when that
   data should be presented. Usually there isn't a significative delay associated
   with video driver, so we expect it to get on screen at the time it's
   delivered for drawing. Unfortunately the same isn't true for audio: all sound
   systems implement some amount of buffering (or fifo), any data being send to it
   now will only get played some time in future. audio_out thread
   must take this into account for making perfect A-V sync by asking the sound latency
   to audio driver.
  
  
   Some audio drivers can't tell the current delay introduced in playback. This is
   especially true for most sound servers like ESD or aRts and explain why in such
   cases the sync is far from perfect.
  
  
   Another problem xine must handle is the sound card clock drift. vpts are
   compared to the system clock (or even to a different clock provided by a scr plugin)
   for presentation but sound card is sampling audio by its own clocking
   mechanism, so a small drift may occur. As the playback goes on this
   error will accumulate possibly resulting in audio gaps or audio drops. To avoid that
   annoying effect, two countermeasures are available (switchable with xine config
   option audio.synchronization.av_sync_method):
   
    
     
      The small sound card errors are feedbacked to metronom. The details
      are given by audio_out.c comments:
      
   /* By adding gap errors (difference between reported and expected
    * sound card clock) into metronom's vpts_offset we can use its
    * smoothing algorithms to correct sound card clock drifts.
    * obs: previously this error was added to xine scr.
    *
    * audio buf ---> metronom --> audio fifo --> (buf->vpts - hw_vpts)
    *           (vpts_offset + error)                     gap
    *                    <---------- control --------------|
    *
    * Unfortunately audio fifo adds a large delay to our closed loop.
    *
    * These are designed to avoid updating the metronom too fast.
    * - it will only be updated 1 time per second (so it has a chance of
    *   distributing the error for several frames).
    * - it will only be updated 2 times for the whole audio fifo size
    *   length (so the control will wait to see the feedback effect)
    * - each update will be of gap/SYNC_GAP_RATE.
    *
    * Sound card clock correction can only provide smooth playback for
    * errors < 1% nominal rate. For bigger errors (bad streams) audio
    * buffers may be dropped or gaps filled with silence.
    */
     
    
    
     
      The audio is stretched or squeezed a slight bit by resampling, thus compensating
      the drift: The next comment in audio_out.c explains:
      
   /* Alternative for metronom feedback: fix sound card clock drift
    * by resampling all audio data, so that the sound card keeps in
    * sync with the system clock. This may help, if one uses a DXR3/H+
    * decoder board. Those have their own clock (which serves as xine's
    * master clock) and can only operate at fixed frame rates (if you
    * want smooth playback). Resampling then avoids A/V sync problems,
    * gaps filled with 0-frames and jerky video playback due to different
    * clock speeds of the sound card and DXR3/H+.
    */
     
    
   
  
 
 
  Overlays and OSD
  
   The roots of xine overlay capabilities are DVD subpictures and subtitles support
   (also known as 'spu'). The DVD subtitles are encoded in a RLE (Run Length Encoding - the
   most simple compressing technique) format, with a palette of colors and transparency
   levels. You probably thought that subtitles were just simple text saved into DVDs, right?
   Wrong, they are bitmaps.
  
  
   In order to optimize to the most common case, xine's internal format for screen overlays
   is a similar representation to the 'spu' data. This brings not only performance
   benefit (since blending functions may skip large image areas due to RLE) but also
   compatibility: it's possible to re-encode any xine overlay to the original spu format
   for displaying with mpeg hardware decoders like DXR3.
  
  
   Displaying subtitles requires the ability to sync them to the video stream. This
   is done using the same kind of pts/vpts stuff of a-v sync code. DVD subtitles,
   for example, may request: show this spu at pts1 and hide it at pts2. This brings the
   concept of the 'video overlay manager', that is a event-driven module for managing
   overlay's showing and hiding.
  
  
   The drawback of using internal RLE format is the difficulty in manipulating it
   as graphic. To overcome that we created the 'OSD renderer', where OSD stands
   for On Screen Display just like in TV sets. The osd renderer is a module
   providing simple graphic primitives (lines, rectagles, draw text etc) over
   a "virtual" bitmap area. Everytime we want to show that bitmap it will
   be RLE encoded and sent to the overlay manager for displaying.
  
  
   
    
   
   
    
   
   
    overlays architecture
   
  
  
   Overlay Manager
   
    The overlay manager interface is available to any xine plugin. It's a bit unlikely
    to be used directly, anyway here's a code snippet for enqueueing an overlay for
    displaying:
    
   video_overlay_event_t       event;
   
   event.object.handle = this->video_overlay->get_handle(this->video_overlay,0);
   
   memset(this->event.object.overlay, 0, sizeof(*this->event.object.overlay));
   
   /* set position and size for this overlay */
   event.object.overlay->x = 0;
   event.object.overlay->y = 0;
   event.object.overlay->width = 100;
   event.object.overlay->height = 100;
   
   /* clipping region is mostly used by dvd menus for highlighting buttons */
   event.object.overlay->clip_top    = 0;
   event.object.overlay->clip_bottom = image_height;
   event.object.overlay->clip_left   = 0;
   event.object.overlay->clip_right  = image_width;
   
   /* the hard part: provide a RLE image */
   event.object.overlay->rle = your_rle;
   event.object.overlay->data_size = your_size;
   event.object.overlay->num_rle = your_rle_count;
   
   /* palette must contain YUV values for each color index */
   memcpy(event.object.overlay->clip_color, color, sizeof(color));
   
   /* this table contains transparency levels for each color index.
      0 = completely transparent, 15 - completely opaque */
   memcpy(event.object.overlay->clip_trans, trans, sizeof(trans));
   
   /* set the event type and time for displaying */
   event.event_type = EVENT_SHOW_SPU;
   event.vpts = 0; /* zero is a special vpts value, it means 'now' */
   video_overlay->add_event(video_overlay, &event);
   
  
  
   OSD Renderer
   
    OSD is a general API for rendering stuff over playing video. It's available both
    to xine plugins and to frontends.
   
   
    The first thing you need is to allocate a OSD object for drawing from the
    renderer. The code below allocates a 300x200 area. This size can't be changed
    during the lifetime of a OSD object, but it's possible to place it anywhere
    over the image.
   
   
   osd_object_t osd;
   
   osd = this->osd_renderer->new_object(osd_renderer, 300, 200);
   
    Now we may want to set font and color for text rendering. Although we will
    refer to fonts over this document, in fact the OSD can be any kind of bitmap. Font
    files are searched and loaded during initialization from
    $prefix/share/xine/fonts/ and ~/.xine/fonts.
    There's a sample utility to convert truetype fonts at
    xine-lib/misc/xine-fontconv.c. Palette may be manipulated directly,
    however most of the time it's convenient to use pre-defined text palettes.
   
   
   /* set sans serif 24 font */
   osd_renderer->set_font(osd, "sans", 24);
   
   /* copy pre-defined colors for white, black border, transparent background to
      starting at the index used by the first text palette */
   osd_renderer->set_text_palette(osd, TEXTPALETTE_WHITE_BLACK_TRANSPARENT, OSD_TEXT1);
   
   /* copy pre-defined colors for white, no border, translucid background to
      starting at the index used by the second text palette */
   osd_renderer->set_text_palette(osd, TEXTPALETTE_WHITE_NONE_TRANSLUCID, OSD_TEXT2);
   
    Now render the text and show it:
    
   osd_renderer->render_text(osd, 0, 0, "white text, black border", OSD_TEXT1);
   osd_renderer->render_text(osd, 0, 30, "white text, no border", OSD_TEXT2);
   
   osd_renderer->show(osd, 0); /* 0 stands for 'now' */
   
   
    There's a 1:1 mapping between OSD objects and overlays, therefore the
    second time you send an OSD object for displaying it will actually substitute
    the first image. By using set_position() function we can move overlay
    over the video.
   
   
   for( i=0; i < 100; i+=10 ) {
     osd_renderer->set_position(osd, i, i );
     osd_renderer->show(osd, 0);
     sleep(1);
   }
   osd_renderer->hide(osd, 0);
   
    For additional functions please check osd.h or the public header.
   
   
    OSD palette notes
    
     The palette functions demand some additional explanation, skip this if you
     just want to write text fast without worring with details! :)
    
    
     We have a 256-entry palette, each one defining yuv and transparency levels.
     Although xine fonts are bitmaps and may use any index they want, we have
     defined a small convention:
    
    
   /*
    Palette entries as used by osd fonts:
   
    0: not used by font, always transparent
    1: font background, usually transparent, may be used to implement
       translucid boxes where the font will be printed.
    2-5: transition between background and border (usually only alpha
         value changes).
    6: font border. if the font is to be displayed without border this
       will probably be adjusted to font background or near.
    7-9: transition between border and foreground
    10: font color (foreground)
   */
    
     The so called 'transitions' are used to implement font anti-aliasing. That
     convention requires that any font file must use only the colors from 1 to 10.
     When we use the set_text_palette() function we are just copying 11 palette
     entries to the specified base index.
    
    
     That base index is the same we pass to render_text() function to use the
     text palette. With this scheme is possible to have several diferent text
     colors at the same time and also draw fonts over custom background.
    
    
   /* obtains size the text will occupy */
   renderer->get_text_size(osd, text, &width, &height);
   
   /* draws a box using font background color (translucid) */
   renderer->filled_rect(osd, x1, y1, x1+width, y1+height, OSD_TEXT2 + 1);
   
   /* render text */
   renderer->render_text(osd, x1, y1, text, OSD_TEXT2);
   
   
    OSD text and palette FAQ
    
     Q: What is the format of the color palette entries?
    
    
     A: It's the same as used by overlay blending code (YUV).
    
    
     Q: What is the relation between a text palette and a palette
        I set with xine_osd_set_palette?
    
    
     A: xine_osd_set_palette will set the entire 256 color palette
        to be used when we blend the osd image.
        "text palette" is a sequence of 11 colors from palette to be
        used to render text. that is, by calling osd_render_text()
        with color_base=100 will render text using colors 100-110.
    
    
     Q: Can I render text with colors in my own palette?
    
    
     A: Sure. Just pass the color_base to osd_render_text()
    
    
     Q: Has a text palette change effects on already drawed text?
    
    
     A: osd_set_text_palette() will overwrite some colors on palette
        with pre-defined ones. So yes, it will change the color
        on already drawed text (if you do it before calling osd_show,
        of course).
        If you don't want to change the colors of drawed text just
        use different color_base values.
    
    
     Q: What about the shadows of osd-objects? Can I turn them off
        or are they hardcoded?
    
    
     A: osd objects have no shadows by itself, but fonts use 11
        colors to produce an anti-aliased effect.
        if you set a "text palette" with entries 0-9 being transparent
        and 10 being foreground you will get rid of any borders or
        anti-aliasing.
    
   
  
 
 
  MRLs
  
   This section defines a draft for a syntactic specification of MRLs as
   used by xine-lib. The language of MRLs is designed to be a true subset
   of the language of URIs as given in RFC2396. A type 2 grammar for the
   language of MRLs is given in EBNF below.
  
  
   Semantically, MRLs consist of two distinct parts that are evaluated by
   different components of the xine architecture. The first part,
   derivable from the symbol <input_source> in the given grammar, is
   completely handed to the input plugins, with input plugins signaling
   if they can handle the MRL.
  
  
   The second part, derivable from <stream_setup> and delimited from the
   first by a crosshatch ('#') contains parameters that modify the
   initialization and playback behaviour of the stream to which the MRL
   is passed. The possible parameters are mentioned in the manpage to
   xine-ui.
  
  
   The following definition should be regarded as a guideline only.
   Of course any given input plugin only understands a subset of all
   possible MRLs. On the other hand, invalid MRLs according to this
   definition might be understood for convenience reasons.
   Some user awareness is required at this point.
  
  
   EBNF grammar for MRLs:
   
   <mrl>           ::= <input_source>[#<stream_setup>]
   <input_source>  ::= (<absolute_mrl>|<relative_mrl>)
   <absolute_mrl>  ::= <input>:(<hierarch_part>|<opaque_part>)
   <hierarch_part> ::= (<net_path>|<abs_path>)[?<query>]
   <opaque_part>   ::= (<unreserved>|<escaped>|;|?|:|@|&|=|+|$|,){<mrl_char>}
   <relative_mrl>  ::= (<abs_path>|<rel_path>)
   <net_path>      ::= //<authority>[<abs_path>]
   <abs_path>      ::= /<path_segments>
   <rel_path>      ::= <rel_segment>[<abs_path>]
   <rel_segment>   ::= <rel_char>{<rel_char>}
   <rel_char>      ::= (<unreserved>|<escaped>|;|@|&|=|+|$|,)
   <input>         ::= <alpha>{(<alpha>|<digit>|+|-|.)}
   <authority>     ::= (<server>|<reg_name>)
   <server>        ::= [[<userinfo>@]<host>[:<port>]]
   <userinfo>      ::= {(<unreserved>|<escaped>|;|:|&|=|+|$|,)}
   <host>          ::= (<hostname>|<ipv4_address>|<ipv6_reference>)
   <hostname>      ::= {<domainlabel>.}<toplabel>[.]
   <domainlabel>   ::= (<alphanum>|<alphanum>{(<alphanum>|-)}<alphanum>)
   <toplabel>      ::= (<alpha>|<alpha>{(<alphanum>|-)}<alphanum>)
   <ipv4_address>  ::= <digit>{<digit>}.<digit>{<digit>}.<digit>{<digit>}.<digit>{<digit>}
   <port>          ::= {<digit>}
   <reg_name>      ::= <reg_char>{<reg_char>}
   <reg_char>      ::= (<unreserved>|<escaped>|;|:|@|&|=|+|$|,)
   <path_segments> ::= <segment>{/<segment>}
   <segment>       ::= {<path_char>}{;<param>}
   <param>         ::= {<path_char>}
   <path_char>     ::= (<unreserved>|<escaped>|:|@|&|=|+|$|,)
   <query>         ::= {<mrl_char>}
   <stream_setup>  ::= <stream_option>;{<stream_option>}
   <stream_option> ::= (<configoption>|<engine_option>|novideo|noaudio|nospu)
   <configoption>  ::= <configentry>:<configvalue>
   <configentry>   ::= <unreserved>{<unreserved>}
   <configvalue>   ::= <stream_char>{<stream_char>}
   <engine_option> ::= <unreserved>{<unreserved>}:<stream_char>{<stream_char>}
   <stream_char>   ::= (<unreserved>|<escaped>|:|@|&|=|+|$|,)
   <mrl_char>      ::= (<reserved>|<unreserved>|<escaped>)
   <reserved>      ::= (;|/|?|:|@|&|=|+|$|,|[|])
   <unreserved>    ::= (<alphanum>|<mark>)
   <mark>          ::= (-|_|.|!|~|*|'|(|))
   <escaped>       ::= %<hex><hex>
   <hex>           ::= (<digit>|A|B|C|D|E|F|a|b|c|d|e|f)
   <alphanum>      ::= (<alpha>|<digit>)
   <alpha>         ::= (<lowalpha>|<upalpha>)
   <lowalpha>      ::= (a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z)
   <upalpha>       ::= (A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z)
   <digit>         ::= (0|1|2|3|4|5|6|7|8|9)
   With <ipv6_reference> being an IPv6 address enclosed in [ and ] as defined in RFC2732.