"Fossies" - the Fresh Open Source Software Archive

Member "libextractor-1.11/src/plugins/gstreamer_extractor.c" (30 Jan 2021, 62551 Bytes) of package /linux/privat/libextractor-1.11.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gstreamer_extractor.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.5_vs_1.6.

    1 /*
    2      This file is part of libextractor.
    3      Copyright (C) 2002, 2003, 2004, 2009, 2012 Vidyut Samanta and Christian Grothoff
    4 
    5      libextractor is free software; you can redistribute it and/or modify
    6      it under the terms of the GNU General Public License as published
    7      by the Free Software Foundation; either version 3, or (at your
    8      option) any later version.
    9 
   10      libextractor is distributed in the hope that it will be useful, but
   11      WITHOUT ANY WARRANTY; without even the implied warranty of
   12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   13      General Public License for more details.
   14 
   15      You should have received a copy of the GNU General Public License
   16      along with libextractor; see the file COPYING.  If not, write to the
   17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   18      Boston, MA 02110-1301, USA.
   19  */
   20 /**
   21  * @file plugins/gstreamer_extractor.c
   22  * @brief extracts metadata from files using GStreamer
   23  * @author LRN
   24  */
   25 #include "platform.h"
   26 #include "extractor.h"
   27 #include <glib.h>
   28 #include <glib-object.h>
   29 #include <gst/pbutils/pbutils.h>
   30 #include <gst/tag/tag.h>
   31 #include <gst/app/gstappsrc.h>
   32 #include <pthread.h>
   33 
   34 GST_DEBUG_CATEGORY_STATIC (gstreamer_extractor);
   35 #define GST_CAT_DEFAULT gstreamer_extractor
   36 
   37 /**
   38  * Once discoverer has gone for that long without asking for data or
   39  * asking to seek, or giving us discovered info, assume it hanged up
   40  * and kill it.
   41  * In milliseconds.
   42  */
   43 #define DATA_TIMEOUT 750 /* 750ms */
   44 
   45 pthread_mutex_t pipe_mutex;
   46 
   47 /**
   48  * Struct mapping GSTREAMER tags to LE tags.
   49  */
   50 struct KnownTag
   51 {
   52   /**
   53    * GStreamer tag.
   54    */
   55   const char *gst_tag_id;
   56 
   57   /**
   58    * Corresponding LE tag.
   59    */
   60   enum EXTRACTOR_MetaType le_type;
   61 };
   62 
   63 
   64 /**
   65  * Struct mapping known tags (that do occur in GST API) to LE tags.
   66  */
   67 static struct KnownTag __known_tags[] = {
   68   /**
   69    * GST_TAG_TITLE:
   70    *
   71    * commonly used title (string)
   72    *
   73    * The title as it should be displayed, e.g. 'The Doll House'
   74    */
   75   {GST_TAG_TITLE, EXTRACTOR_METATYPE_TITLE},
   76 
   77   /**
   78    * GST_TAG_TITLE_SORTNAME:
   79    *
   80    * commonly used title, as used for sorting (string)
   81    *
   82    * The title as it should be sorted, e.g. 'Doll House, The'
   83    */
   84   {GST_TAG_TITLE_SORTNAME, EXTRACTOR_METATYPE_TITLE},
   85 
   86   /**
   87    * GST_TAG_ARTIST:
   88    *
   89    * person(s) responsible for the recording (string)
   90    *
   91    * The artist name as it should be displayed, e.g. 'Jimi Hendrix' or
   92    * 'The Guitar Heroes'
   93    */
   94   {GST_TAG_ARTIST, EXTRACTOR_METATYPE_ARTIST},
   95 
   96   /**
   97    * GST_TAG_ARTIST_SORTNAME:
   98    *
   99    * person(s) responsible for the recording, as used for sorting (string)
  100    *
  101    * The artist name as it should be sorted, e.g. 'Hendrix, Jimi' or
  102    * 'Guitar Heroes, The'
  103    */
  104   {GST_TAG_ARTIST_SORTNAME, EXTRACTOR_METATYPE_ARTIST},
  105 
  106   /**
  107    * GST_TAG_ALBUM:
  108    *
  109    * album containing this data (string)
  110    *
  111    * The album name as it should be displayed, e.g. 'The Jazz Guitar'
  112    */
  113   {GST_TAG_ALBUM, EXTRACTOR_METATYPE_ALBUM},
  114 
  115   /**
  116    * GST_TAG_ALBUM_SORTNAME:
  117    *
  118    * album containing this data, as used for sorting (string)
  119    *
  120    * The album name as it should be sorted, e.g. 'Jazz Guitar, The'
  121    */
  122   {GST_TAG_ALBUM_SORTNAME, EXTRACTOR_METATYPE_ALBUM},
  123 
  124   /**
  125    * GST_TAG_ALBUM_ARTIST:
  126    *
  127    * The artist of the entire album, as it should be displayed.
  128    */
  129   {GST_TAG_ALBUM_ARTIST, EXTRACTOR_METATYPE_ARTIST},
  130 
  131   /**
  132    * GST_TAG_ALBUM_ARTIST_SORTNAME:
  133    *
  134    * The artist of the entire album, as it should be sorted.
  135    */
  136   {GST_TAG_ALBUM_ARTIST_SORTNAME, EXTRACTOR_METATYPE_ARTIST},
  137 
  138   /**
  139    * GST_TAG_COMPOSER:
  140    *
  141    * person(s) who composed the recording (string)
  142    */
  143   {GST_TAG_COMPOSER, EXTRACTOR_METATYPE_COMPOSER},
  144 
  145   /**
  146    * GST_TAG_DATE:
  147    *
  148    * date the data was created (#GDate structure)
  149    */
  150   {GST_TAG_DATE, EXTRACTOR_METATYPE_CREATION_TIME},
  151 
  152   /**
  153    * GST_TAG_DATE_TIME:
  154    *
  155    * date and time the data was created (#GstDateTime structure)
  156    */
  157   {GST_TAG_DATE_TIME, EXTRACTOR_METATYPE_CREATION_TIME},
  158 
  159   /**
  160    * GST_TAG_GENRE:
  161    *
  162    * genre this data belongs to (string)
  163    */
  164   {GST_TAG_GENRE, EXTRACTOR_METATYPE_GENRE},
  165 
  166   /**
  167    * GST_TAG_COMMENT:
  168    *
  169    * free text commenting the data (string)
  170    */
  171   {GST_TAG_COMMENT, EXTRACTOR_METATYPE_COMMENT},
  172 
  173   /**
  174    * GST_TAG_EXTENDED_COMMENT:
  175    *
  176    * key/value text commenting the data (string)
  177    *
  178    * Must be in the form of 'key=comment' or
  179    * 'key[lc]=comment' where 'lc' is an ISO-639
  180    * language code.
  181    *
  182    * This tag is used for unknown Vorbis comment tags,
  183    * unknown APE tags and certain ID3v2 comment fields.
  184    */
  185   {GST_TAG_EXTENDED_COMMENT, EXTRACTOR_METATYPE_COMMENT},
  186 
  187   /**
  188    * GST_TAG_TRACK_NUMBER:
  189    *
  190    * track number inside a collection (unsigned integer)
  191    */
  192   {GST_TAG_TRACK_NUMBER, EXTRACTOR_METATYPE_TRACK_NUMBER},
  193 
  194   /**
  195    * GST_TAG_TRACK_COUNT:
  196    *
  197    * count of tracks inside collection this track belongs to (unsigned integer)
  198    */
  199   {GST_TAG_TRACK_COUNT, EXTRACTOR_METATYPE_SONG_COUNT},
  200 
  201   /**
  202    * GST_TAG_ALBUM_VOLUME_NUMBER:
  203    *
  204    * disc number inside a collection (unsigned integer)
  205    */
  206   {GST_TAG_ALBUM_VOLUME_NUMBER, EXTRACTOR_METATYPE_DISC_NUMBER},
  207 
  208   /**
  209    * GST_TAG_ALBUM_VOLUME_COUNT:
  210    *
  211    * count of discs inside collection this disc belongs to (unsigned integer)
  212    */
  213   {GST_TAG_ALBUM_VOLUME_NUMBER, EXTRACTOR_METATYPE_DISC_COUNT},
  214 
  215   /**
  216    * GST_TAG_LOCATION:
  217    *
  218    * Origin of media as a URI (location, where the original of the file or stream
  219    * is hosted) (string)
  220    */
  221   {GST_TAG_LOCATION, EXTRACTOR_METATYPE_URL},
  222 
  223   /**
  224    * GST_TAG_HOMEPAGE:
  225    *
  226    * Homepage for this media (i.e. artist or movie homepage) (string)
  227    */
  228   {GST_TAG_HOMEPAGE, EXTRACTOR_METATYPE_URL},
  229 
  230   /**
  231    * GST_TAG_DESCRIPTION:
  232    *
  233    * short text describing the content of the data (string)
  234    */
  235   {GST_TAG_DESCRIPTION, EXTRACTOR_METATYPE_DESCRIPTION},
  236 
  237   /**
  238    * GST_TAG_VERSION:
  239    *
  240    * version of this data (string)
  241    */
  242   {GST_TAG_VERSION, EXTRACTOR_METATYPE_PRODUCT_VERSION},
  243 
  244   /**
  245    * GST_TAG_ISRC:
  246    *
  247    * International Standard Recording Code - see http://www.ifpi.org/isrc/ (string)
  248    */
  249   {GST_TAG_ISRC, EXTRACTOR_METATYPE_ISRC},
  250 
  251   /**
  252    * GST_TAG_ORGANIZATION:
  253    *
  254    * organization (string)
  255    */
  256   {GST_TAG_ORGANIZATION, EXTRACTOR_METATYPE_COMPANY},
  257 
  258   /**
  259    * GST_TAG_COPYRIGHT:
  260    *
  261    * copyright notice of the data (string)
  262    */
  263   {GST_TAG_COPYRIGHT, EXTRACTOR_METATYPE_COPYRIGHT},
  264 
  265   /**
  266    * GST_TAG_COPYRIGHT_URI:
  267    *
  268    * URI to location where copyright details can be found (string)
  269    */
  270   {GST_TAG_COPYRIGHT_URI, EXTRACTOR_METATYPE_COPYRIGHT},
  271 
  272   /**
  273    * GST_TAG_ENCODED_BY:
  274    *
  275    * name of the person or organisation that encoded the file. May contain a
  276    * copyright message if the person or organisation also holds the copyright
  277    * (string)
  278    *
  279    * Note: do not use this field to describe the encoding application. Use
  280    * #GST_TAG_APPLICATION_NAME or #GST_TAG_COMMENT for that.
  281    */
  282   {GST_TAG_ENCODED_BY, EXTRACTOR_METATYPE_ENCODED_BY},
  283 
  284   /**
  285    * GST_TAG_CONTACT:
  286    *
  287    * contact information (string)
  288    */
  289   {GST_TAG_CONTACT, EXTRACTOR_METATYPE_CONTACT_INFORMATION},
  290 
  291   /**
  292    * GST_TAG_LICENSE:
  293    *
  294    * license of data (string)
  295    */
  296   {GST_TAG_LICENSE, EXTRACTOR_METATYPE_LICENSE},
  297 
  298   /**
  299    * GST_TAG_LICENSE_URI:
  300    *
  301    * URI to location where license details can be found (string)
  302    */
  303   {GST_TAG_LICENSE_URI, EXTRACTOR_METATYPE_LICENSE},
  304 
  305   /**
  306    * GST_TAG_PERFORMER:
  307    *
  308    * person(s) performing (string)
  309    */
  310   {GST_TAG_PERFORMER, EXTRACTOR_METATYPE_PERFORMER},
  311 
  312   /**
  313    * GST_TAG_DURATION:
  314    *
  315    * length in GStreamer time units (nanoseconds) (unsigned 64-bit integer)
  316    */
  317   {GST_TAG_DURATION, EXTRACTOR_METATYPE_DURATION},
  318 
  319   /**
  320    * GST_TAG_CODEC:
  321    *
  322    * codec the data is stored in (string)
  323    */
  324   {GST_TAG_CODEC, EXTRACTOR_METATYPE_CODEC},
  325 
  326   /**
  327    * GST_TAG_VIDEO_CODEC:
  328    *
  329    * codec the video data is stored in (string)
  330    */
  331   {GST_TAG_VIDEO_CODEC, EXTRACTOR_METATYPE_VIDEO_CODEC},
  332 
  333   /**
  334    * GST_TAG_AUDIO_CODEC:
  335    *
  336    * codec the audio data is stored in (string)
  337    */
  338   {GST_TAG_AUDIO_CODEC, EXTRACTOR_METATYPE_AUDIO_CODEC},
  339 
  340   /**
  341    * GST_TAG_SUBTITLE_CODEC:
  342    *
  343    * codec/format the subtitle data is stored in (string)
  344    */
  345   {GST_TAG_SUBTITLE_CODEC, EXTRACTOR_METATYPE_SUBTITLE_CODEC},
  346 
  347   /**
  348    * GST_TAG_CONTAINER_FORMAT:
  349    *
  350    * container format the data is stored in (string)
  351    */
  352   {GST_TAG_CONTAINER_FORMAT, EXTRACTOR_METATYPE_CONTAINER_FORMAT},
  353 
  354   /**
  355    * GST_TAG_BITRATE:
  356    *
  357    * exact or average bitrate in bits/s (unsigned integer)
  358    */
  359   {GST_TAG_BITRATE, EXTRACTOR_METATYPE_BITRATE},
  360 
  361   /**
  362    * GST_TAG_NOMINAL_BITRATE:
  363    *
  364    * nominal bitrate in bits/s (unsigned integer). The actual bitrate might be
  365    * different from this target bitrate.
  366    */
  367   {GST_TAG_NOMINAL_BITRATE, EXTRACTOR_METATYPE_NOMINAL_BITRATE},
  368 
  369   /**
  370    * GST_TAG_MINIMUM_BITRATE:
  371    *
  372    * minimum bitrate in bits/s (unsigned integer)
  373    */
  374   {GST_TAG_MINIMUM_BITRATE, EXTRACTOR_METATYPE_MINIMUM_BITRATE},
  375 
  376   /**
  377    * GST_TAG_MAXIMUM_BITRATE:
  378    *
  379    * maximum bitrate in bits/s (unsigned integer)
  380    */
  381   {GST_TAG_MAXIMUM_BITRATE, EXTRACTOR_METATYPE_MAXIMUM_BITRATE},
  382 
  383   /**
  384    * GST_TAG_SERIAL:
  385    *
  386    * serial number of track (unsigned integer)
  387    */
  388   {GST_TAG_SERIAL, EXTRACTOR_METATYPE_SERIAL},
  389 
  390   /**
  391    * GST_TAG_ENCODER:
  392    *
  393    * encoder used to encode this stream (string)
  394    */
  395   {GST_TAG_ENCODER, EXTRACTOR_METATYPE_ENCODER}, /* New */
  396 
  397   /**
  398    * GST_TAG_ENCODER_VERSION:
  399    *
  400    * version of the encoder used to encode this stream (unsigned integer)
  401    */
  402   {GST_TAG_ENCODER_VERSION, EXTRACTOR_METATYPE_ENCODER_VERSION},
  403 
  404   /**
  405    * GST_TAG_TRACK_GAIN:
  406    *
  407    * track gain in db (double)
  408    */
  409   {GST_TAG_TRACK_GAIN, EXTRACTOR_METATYPE_TRACK_GAIN},
  410 
  411   /**
  412    * GST_TAG_TRACK_PEAK:
  413    *
  414    * peak of the track (double)
  415    */
  416   {GST_TAG_TRACK_PEAK, EXTRACTOR_METATYPE_TRACK_PEAK},
  417 
  418   /**
  419    * GST_TAG_ALBUM_GAIN:
  420    *
  421    * album gain in db (double)
  422    */
  423   {GST_TAG_ALBUM_GAIN, EXTRACTOR_METATYPE_ALBUM_GAIN},
  424 
  425   /**
  426    * GST_TAG_ALBUM_PEAK:
  427    *
  428    * peak of the album (double)
  429    */
  430   {GST_TAG_ALBUM_PEAK, EXTRACTOR_METATYPE_ALBUM_PEAK},
  431 
  432   /**
  433    * GST_TAG_REFERENCE_LEVEL:
  434    *
  435    * reference level of track and album gain values (double)
  436    */
  437   {GST_TAG_REFERENCE_LEVEL, EXTRACTOR_METATYPE_REFERENCE_LEVEL},
  438 
  439   /**
  440    * GST_TAG_LANGUAGE_CODE:
  441    *
  442    * ISO-639-2 or ISO-639-1 code for the language the content is in (string)
  443    *
  444    * There is utility API in libgsttag in gst-plugins-base to obtain a translated
  445    * language name from the language code: gst_tag_get_language_name()
  446    */
  447   {GST_TAG_LANGUAGE_CODE, EXTRACTOR_METATYPE_LANGUAGE},
  448 
  449   /**
  450    * GST_TAG_LANGUAGE_NAME:
  451    *
  452    * Name of the language the content is in (string)
  453    *
  454    * Free-form name of the language the content is in, if a language code
  455    * is not available. This tag should not be set in addition to a language
  456    * code. It is undefined what language or locale the language name is in.
  457    */
  458   {GST_TAG_LANGUAGE_NAME, EXTRACTOR_METATYPE_LANGUAGE},
  459 
  460   /**
  461    * GST_TAG_IMAGE:
  462    *
  463    * image (sample) (sample taglist should specify the content type and preferably
  464    * also set "image-type" field as #GstTagImageType)
  465    */
  466   {GST_TAG_IMAGE, EXTRACTOR_METATYPE_PICTURE},
  467 
  468   /**
  469    * GST_TAG_PREVIEW_IMAGE:
  470    *
  471    * image that is meant for preview purposes, e.g. small icon-sized version
  472    * (sample) (sample taglist should specify the content type)
  473    */
  474   {GST_TAG_IMAGE, EXTRACTOR_METATYPE_THUMBNAIL},
  475 
  476   /**
  477    * GST_TAG_ATTACHMENT:
  478    *
  479    * generic file attachment (sample) (sample taglist should specify the content
  480    * type and if possible set "filename" to the file name of the
  481    * attachment)
  482    */
  483   /* No equivalent, and none needed? */
  484 
  485   /**
  486    * GST_TAG_BEATS_PER_MINUTE:
  487    *
  488    * number of beats per minute in audio (double)
  489    */
  490   {GST_TAG_BEATS_PER_MINUTE, EXTRACTOR_METATYPE_BEATS_PER_MINUTE},
  491 
  492   /**
  493    * GST_TAG_KEYWORDS:
  494    *
  495    * comma separated keywords describing the content (string).
  496    */
  497   {GST_TAG_KEYWORDS, EXTRACTOR_METATYPE_KEYWORDS},
  498 
  499   /**
  500    * GST_TAG_GEO_LOCATION_NAME:
  501    *
  502    * human readable descriptive location of where the media has been recorded or
  503    * produced. (string).
  504    */
  505   {GST_TAG_GEO_LOCATION_NAME, EXTRACTOR_METATYPE_LOCATION_NAME},
  506 
  507   /**
  508    * GST_TAG_GEO_LOCATION_LATITUDE:
  509    *
  510    * geo latitude location of where the media has been recorded or produced in
  511    * degrees according to WGS84 (zero at the equator, negative values for southern
  512    * latitudes) (double).
  513    */
  514   {GST_TAG_GEO_LOCATION_LATITUDE, EXTRACTOR_METATYPE_GPS_LATITUDE},
  515 
  516   /**
  517    * GST_TAG_GEO_LOCATION_LONGITUDE:
  518    *
  519    * geo longitude location of where the media has been recorded or produced in
  520    * degrees according to WGS84 (zero at the prime meridian in Greenwich/UK,
  521    * negative values for western longitudes). (double).
  522    */
  523   {GST_TAG_GEO_LOCATION_LONGITUDE, EXTRACTOR_METATYPE_GPS_LONGITUDE},
  524 
  525   /**
  526    * GST_TAG_GEO_LOCATION_ELEVATION:
  527    *
  528    * geo elevation of where the media has been recorded or produced in meters
  529    * according to WGS84 (zero is average sea level) (double).
  530    */
  531   {GST_TAG_GEO_LOCATION_ELEVATION, EXTRACTOR_METATYPE_LOCATION_ELEVATION},
  532 
  533   /**
  534    * GST_TAG_GEO_LOCATION_COUNTRY:
  535    *
  536    * The country (english name) where the media has been produced (string).
  537    */
  538   {GST_TAG_GEO_LOCATION_COUNTRY, EXTRACTOR_METATYPE_LOCATION_COUNTRY},
  539 
  540   /**
  541    * GST_TAG_GEO_LOCATION_CITY:
  542    *
  543    * The city (english name) where the media has been produced (string).
  544    */
  545   {GST_TAG_GEO_LOCATION_CITY, EXTRACTOR_METATYPE_LOCATION_CITY},
  546 
  547   /**
  548    * GST_TAG_GEO_LOCATION_SUBLOCATION:
  549    *
  550    * A location 'smaller' than GST_TAG_GEO_LOCATION_CITY that specifies better
  551    * where the media has been produced. (e.g. the neighborhood) (string).
  552    *
  553    * This tag has been added as this is how it is handled/named in XMP's
  554    * Iptc4xmpcore schema.
  555    */
  556   {GST_TAG_GEO_LOCATION_SUBLOCATION, EXTRACTOR_METATYPE_LOCATION_SUBLOCATION},
  557 
  558   /**
  559    * GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR:
  560    *
  561    * Represents the expected error on the horizontal positioning in
  562    * meters (double).
  563    */
  564   {GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR,
  565    EXTRACTOR_METATYPE_LOCATION_HORIZONTAL_ERROR},
  566 
  567   /**
  568    * GST_TAG_GEO_LOCATION_MOVEMENT_SPEED:
  569    *
  570    * Speed of the capturing device when performing the capture.
  571    * Represented in m/s. (double)
  572    *
  573    * See also #GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION
  574    */
  575   {GST_TAG_GEO_LOCATION_MOVEMENT_SPEED,
  576    EXTRACTOR_METATYPE_LOCATION_MOVEMENT_SPEED},
  577 
  578   /**
  579    * GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION:
  580    *
  581    * Indicates the movement direction of the device performing the capture
  582    * of a media. It is represented as degrees in floating point representation,
  583    * 0 means the geographic north, and increases clockwise (double from 0 to 360)
  584    *
  585    * See also #GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION
  586    */
  587   {GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION,
  588    EXTRACTOR_METATYPE_LOCATION_MOVEMENT_DIRECTION},
  589 
  590   /**
  591    * GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION:
  592    *
  593    * Indicates the direction the device is pointing to when capturing
  594    * a media. It is represented as degrees in floating point representation,
  595    * 0 means the geographic north, and increases clockwise (double from 0 to 360)
  596    *
  597    * See also #GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION
  598    */
  599   {GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION,
  600    EXTRACTOR_METATYPE_LOCATION_CAPTURE_DIRECTION},
  601 
  602   /**
  603    * GST_TAG_SHOW_NAME:
  604    *
  605    * Name of the show, used for displaying (string)
  606    */
  607   {GST_TAG_SHOW_NAME, EXTRACTOR_METATYPE_SHOW_NAME},
  608 
  609   /**
  610    * GST_TAG_SHOW_SORTNAME:
  611    *
  612    * Name of the show, used for sorting (string)
  613    */
  614   {GST_TAG_SHOW_SORTNAME, EXTRACTOR_METATYPE_SHOW_NAME},
  615 
  616   /**
  617    * GST_TAG_SHOW_EPISODE_NUMBER:
  618    *
  619    * Number of the episode within a season/show (unsigned integer)
  620    */
  621   {GST_TAG_SHOW_EPISODE_NUMBER, EXTRACTOR_METATYPE_SHOW_EPISODE_NUMBER},
  622 
  623   /**
  624    * GST_TAG_SHOW_SEASON_NUMBER:
  625    *
  626    * Number of the season of a show/series (unsigned integer)
  627    */
  628   {GST_TAG_SHOW_SEASON_NUMBER, EXTRACTOR_METATYPE_SHOW_SEASON_NUMBER},
  629 
  630   /**
  631    * GST_TAG_LYRICS:
  632    *
  633    * The lyrics of the media (string)
  634    */
  635   {GST_TAG_LYRICS, EXTRACTOR_METATYPE_LYRICS},
  636 
  637   /**
  638    * GST_TAG_COMPOSER_SORTNAME:
  639    *
  640    * The composer's name, used for sorting (string)
  641    */
  642   {GST_TAG_COMPOSER_SORTNAME, EXTRACTOR_METATYPE_COMPOSER},
  643 
  644   /**
  645    * GST_TAG_GROUPING:
  646    *
  647    * Groups together media that are related and spans multiple tracks. An
  648    * example are multiple pieces of a concerto. (string)
  649    */
  650   {GST_TAG_GROUPING, EXTRACTOR_METATYPE_GROUPING},
  651 
  652   /**
  653    * GST_TAG_USER_RATING:
  654    *
  655    * Rating attributed by a person (likely the application user).
  656    * The higher the value, the more the user likes this media
  657    * (unsigned int from 0 to 100)
  658    */
  659   {GST_TAG_USER_RATING, EXTRACTOR_METATYPE_POPULARITY_METER},
  660 
  661   /**
  662    * GST_TAG_DEVICE_MANUFACTURER:
  663    *
  664    * Manufacturer of the device used to create the media (string)
  665    */
  666   {GST_TAG_DEVICE_MANUFACTURER, EXTRACTOR_METATYPE_DEVICE_MANUFACTURER},
  667 
  668   /**
  669    * GST_TAG_DEVICE_MODEL:
  670    *
  671    * Model of the device used to create the media (string)
  672    */
  673   {GST_TAG_DEVICE_MODEL, EXTRACTOR_METATYPE_DEVICE_MODEL},
  674 
  675   /**
  676    * GST_TAG_APPLICATION_NAME:
  677    *
  678    * Name of the application used to create the media (string)
  679    */
  680   {GST_TAG_APPLICATION_NAME, EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE},
  681 
  682   /**
  683    * GST_TAG_APPLICATION_DATA:
  684    *
  685    * Arbitrary application data (sample)
  686    *
  687    * Some formats allow applications to add their own arbitrary data
  688    * into files. This data is application dependent.
  689    */
  690   /* No equivalent, and none needed (not really metadata)? */
  691 
  692   /**
  693    * GST_TAG_IMAGE_ORIENTATION:
  694    *
  695    * Represents the 'Orientation' tag from EXIF. Defines how the image
  696    * should be rotated and mirrored for display. (string)
  697    *
  698    * This tag has a predefined set of allowed values:
  699    *   "rotate-0"
  700    *   "rotate-90"
  701    *   "rotate-180"
  702    *   "rotate-270"
  703    *   "flip-rotate-0"
  704    *   "flip-rotate-90"
  705    *   "flip-rotate-180"
  706    *   "flip-rotate-270"
  707    *
  708    * The naming is adopted according to a possible transformation to perform
  709    * on the image to fix its orientation, obviously equivalent operations will
  710    * yield the same result.
  711    *
  712    * Rotations indicated by the values are in clockwise direction and
  713    * 'flip' means an horizontal mirroring.
  714    */
  715   {GST_TAG_IMAGE_ORIENTATION, EXTRACTOR_METATYPE_ORIENTATION}
  716 
  717 };
  718 
  719 
  720 /**
  721  * Struct mapping named tags (that don't occur in GST API) to LE tags.
  722  */
  723 struct NamedTag
  724 {
  725   /**
  726    * tag.
  727    */
  728   const char *tag;
  729 
  730   /**
  731    * Corresponding LE tag.
  732    */
  733   enum EXTRACTOR_MetaType le_type;
  734 };
  735 
  736 
  737 /**
  738  * Mapping from GST tag names to LE types for tags that are not in
  739  * the public GST API.
  740  */
  741 struct NamedTag named_tags[] = {
  742   { "FPS", EXTRACTOR_METATYPE_FRAME_RATE },
  743   { "PLAY_COUNTER", EXTRACTOR_METATYPE_PLAY_COUNTER },
  744   { "RATING", EXTRACTOR_METATYPE_RATING },
  745   { "SUMMARY", EXTRACTOR_METATYPE_SUMMARY },
  746   { "SUBJECT", EXTRACTOR_METATYPE_SUBJECT },
  747   { "MOOD", EXTRACTOR_METATYPE_MOOD },
  748   { "LEAD_PERFORMER", EXTRACTOR_METATYPE_PERFORMER },
  749   { "DIRECTOR", EXTRACTOR_METATYPE_MOVIE_DIRECTOR },
  750   { "WRITTEN_BY", EXTRACTOR_METATYPE_WRITER },
  751   { "PRODUCER", EXTRACTOR_METATYPE_PRODUCER },
  752   { "PUBLISHER", EXTRACTOR_METATYPE_PUBLISHER },
  753   { "ORIGINAL/ARTIST", EXTRACTOR_METATYPE_ORIGINAL_ARTIST },
  754   { "ORIGINAL/TITLE", EXTRACTOR_METATYPE_ORIGINAL_TITLE },
  755   { NULL, EXTRACTOR_METATYPE_UNKNOWN }
  756 };
  757 
  758 
  759 /**
  760  *
  761  */
  762 enum CurrentStreamType
  763 {
  764   /**
  765    *
  766    */
  767   STREAM_TYPE_NONE = 0,
  768 
  769   /**
  770    *
  771    */
  772   STREAM_TYPE_AUDIO = 1,
  773 
  774   /**
  775    *
  776    */
  777   STREAM_TYPE_VIDEO = 2,
  778 
  779   /**
  780    *
  781    */
  782   STREAM_TYPE_SUBTITLE = 3,
  783 
  784   /**
  785    *
  786    */
  787   STREAM_TYPE_CONTAINER = 4,
  788 
  789   /**
  790    *
  791    */
  792   STREAM_TYPE_IMAGE = 5
  793 };
  794 
  795 
  796 /**
  797  * Closure we pass when processing a request.
  798  */
  799 struct PrivStruct
  800 {
  801   /**
  802    * Current read-offset in the 'ec' context (based on our read/seek calls).
  803    */
  804   guint64 offset;
  805 
  806   /**
  807    * Overall size of the file we're processing, UINT64_MAX if unknown.
  808    */
  809   uint64_t length;
  810 
  811   /**
  812    *
  813    */
  814   GstElement *source;
  815 
  816   /**
  817    * Extraction context for IO on the underlying data.
  818    */
  819   struct EXTRACTOR_ExtractContext *ec;
  820 
  821   /**
  822    * Glib main loop.
  823    */
  824   GMainLoop *loop;
  825 
  826   /**
  827    * Discoverer object we are using.
  828    */
  829   GstDiscoverer *dc;
  830 
  831   /**
  832    * Location for building the XML 'table of contents' (EXTRACTOR_METATYPE_TOC) for
  833    * the input.  Used only during 'send_info'.
  834    */
  835   gchar *toc;
  836 
  837   /**
  838    * Length of the 'toc' string.
  839    */
  840   size_t toc_length;
  841 
  842   /**
  843    * Current position (used when creating the 'toc' string).
  844    */
  845   size_t toc_pos;
  846 
  847   /**
  848    * Identifier of the timeout event source
  849    */
  850   guint timeout_id;
  851 
  852   /**
  853    * Counter used to determine our current depth in the TOC hierarchy.
  854    */
  855   int toc_depth;
  856 
  857   /**
  858    *
  859    */
  860   enum CurrentStreamType st;
  861 
  862   /**
  863    * Last return value from the meta data processor.  Set to
  864    * 1 to abort, 0 to continue extracting.
  865    */
  866   int time_to_leave;
  867 
  868   /**
  869    * TOC generation is executed in two phases.  First phase determines
  870    * the size of the string and the second phase actually does the
  871    * 'printing' (string construction).  This bit is TRUE if we are
  872    * in the 'printing' phase.
  873    */
  874   gboolean toc_print_phase;
  875 
  876 };
  877 
  878 
  879 /**
  880  *
  881  */
  882 static GQuark *audio_quarks;
  883 
  884 /**
  885  *
  886  */
  887 static GQuark *video_quarks;
  888 
  889 /**
  890  *
  891  */
  892 static GQuark *subtitle_quarks;
  893 
  894 /**
  895  *
  896  */
  897 static GQuark duration_quark;
  898 
  899 
  900 static gboolean
  901 _data_timeout (struct PrivStruct *ps)
  902 {
  903   GST_ERROR ("GstDiscoverer I/O timed out");
  904   ps->timeout_id = 0;
  905   g_main_loop_quit (ps->loop);
  906   return FALSE;
  907 }
  908 
  909 
  910 /**
  911  * Implementation of GstElement's "need-data" callback.  Reads data from
  912  * the extraction context and passes it to GStreamer.
  913  *
  914  * @param appsrc the GstElement for which we are implementing "need-data"
  915  * @param size number of bytes requested
  916  * @param ps our execution context
  917  */
  918 static void
  919 feed_data (GstElement *appsrc,
  920            guint size,
  921            struct PrivStruct *ps)
  922 {
  923   ssize_t data_len;
  924   uint8_t *le_data;
  925   guint accumulated;
  926   GstMemory *mem;
  927   GstMapInfo mi;
  928   GstBuffer *buffer;
  929 
  930   GST_DEBUG ("Request %u bytes", size);
  931 
  932   if (ps->timeout_id > 0)
  933     g_source_remove (ps->timeout_id);
  934   ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout,
  935                                   ps);
  936 
  937   if ( (ps->length > 0) && (ps->offset >= ps->length) )
  938   {
  939     /* we are at the EOS, send end-of-stream */
  940     gst_app_src_end_of_stream (GST_APP_SRC (ps->source));
  941     return;
  942   }
  943 
  944   if ((ps->length > 0) && (ps->offset + size > ps->length))
  945     size = ps->length - ps->offset;
  946 
  947   mem = gst_allocator_alloc (NULL, size, NULL);
  948   if (! gst_memory_map (mem, &mi, GST_MAP_WRITE))
  949   {
  950     gst_memory_unref (mem);
  951     GST_DEBUG ("Failed to map the memory");
  952     gst_app_src_end_of_stream (GST_APP_SRC (ps->source));
  953     return;
  954   }
  955 
  956   accumulated = 0;
  957   data_len = 1;
  958   pthread_mutex_lock (&pipe_mutex);
  959   while ( (accumulated < size) && (data_len > 0) )
  960   {
  961     data_len = ps->ec->read (ps->ec->cls, (void **) &le_data, size
  962                              - accumulated);
  963     if (data_len > 0)
  964     {
  965       memcpy (&mi.data[accumulated], le_data, data_len);
  966       accumulated += data_len;
  967     }
  968   }
  969   pthread_mutex_unlock (&pipe_mutex);
  970   gst_memory_unmap (mem, &mi);
  971   if (size == accumulated)
  972   {
  973     buffer = gst_buffer_new ();
  974     gst_buffer_append_memory (buffer, mem);
  975 
  976     /* we need to set an offset for random access */
  977     GST_BUFFER_OFFSET (buffer) = ps->offset;
  978     GST_BUFFER_OFFSET_END (buffer) = ps->offset + size;
  979 
  980     GST_DEBUG ("feed buffer %p, offset %" G_GUINT64_FORMAT "-%u",
  981                buffer, ps->offset, size);
  982     gst_app_src_push_buffer (GST_APP_SRC (ps->source), buffer);
  983     ps->offset += size;
  984   }
  985   else
  986   {
  987     gst_memory_unref (mem);
  988     gst_app_src_end_of_stream (GST_APP_SRC (ps->source));
  989     ps->offset = UINT64_MAX; /* set to invalid value */
  990   }
  991 
  992   if (ps->timeout_id > 0)
  993     g_source_remove (ps->timeout_id);
  994   ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout,
  995                                   ps);
  996 }
  997 
  998 
  999 /**
 1000  * Implementation of GstElement's "seek-data" callback.  Seeks to a new
 1001  * position in the extraction context.
 1002  *
 1003  * @param appsrc the GstElement for which we are implementing "need-data"
 1004  * @param position new desired absolute position in the file
 1005  * @param ps our execution context
 1006  * @return TRUE if seeking succeeded, FALSE if not
 1007  */
 1008 static gboolean
 1009 seek_data (GstElement *appsrc,
 1010            guint64 position,
 1011            struct PrivStruct *ps)
 1012 {
 1013   GST_DEBUG ("seek to offset %" G_GUINT64_FORMAT, position);
 1014   pthread_mutex_lock (&pipe_mutex);
 1015   ps->offset = ps->ec->seek (ps->ec->cls, position, SEEK_SET);
 1016   pthread_mutex_unlock (&pipe_mutex);
 1017   if (ps->timeout_id > 0)
 1018     g_source_remove (ps->timeout_id);
 1019   ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout,
 1020                                   ps);
 1021   return ps->offset == position;
 1022 }
 1023 
 1024 
 1025 /**
 1026  * FIXME
 1027  *
 1028  * @param field_id FIXME
 1029  * @param value FIXME
 1030  * @param user_data our 'struct PrivStruct'
 1031  * @return TRUE to continue processing, FALSE to abort
 1032  */
 1033 static gboolean
 1034 send_structure_foreach (GQuark field_id,
 1035                         const GValue *value,
 1036                         gpointer user_data)
 1037 {
 1038   struct PrivStruct *ps = user_data;
 1039   gchar *str;
 1040   const gchar *field_name = g_quark_to_string (field_id);
 1041   GType gst_fraction = GST_TYPE_FRACTION;
 1042   GQuark *quark;
 1043 
 1044   switch (ps->st)
 1045   {
 1046   case STREAM_TYPE_AUDIO:
 1047     if (NULL == audio_quarks)
 1048       return FALSE;
 1049     for (quark = audio_quarks; *quark != 0; quark++)
 1050       if (*quark == field_id)
 1051         return TRUE;
 1052     break;
 1053   case STREAM_TYPE_VIDEO:
 1054   case STREAM_TYPE_IMAGE:
 1055     if (NULL == video_quarks)
 1056       return FALSE;
 1057     for (quark = video_quarks; *quark != 0; quark++)
 1058       if (*quark == field_id)
 1059         return TRUE;
 1060     break;
 1061   case STREAM_TYPE_SUBTITLE:
 1062     if (NULL == subtitle_quarks)
 1063       return FALSE;
 1064     for (quark = subtitle_quarks; *quark != 0; quark++)
 1065       if (*quark == field_id)
 1066         return TRUE;
 1067     break;
 1068   case STREAM_TYPE_CONTAINER:
 1069   case STREAM_TYPE_NONE:
 1070     break;
 1071   }
 1072 
 1073   switch (G_VALUE_TYPE (value))
 1074   {
 1075   case G_TYPE_STRING:
 1076     str = g_value_dup_string (value);
 1077     break;
 1078   case G_TYPE_UINT:
 1079   case G_TYPE_INT:
 1080   case G_TYPE_DOUBLE:
 1081   case G_TYPE_BOOLEAN:
 1082     str = gst_value_serialize (value);
 1083     break;
 1084   default:
 1085     if (G_VALUE_TYPE (value) == gst_fraction)
 1086     {
 1087       str = gst_value_serialize (value);
 1088       break;
 1089     }
 1090     /* This is a potential source of invalid characters */
 1091     /* And it also might attempt to serialize binary data - such as images. */
 1092     str = gst_value_serialize (value);
 1093     if (NULL != str)
 1094     {
 1095       g_free (str);
 1096       str = NULL;
 1097     }
 1098     break;
 1099   }
 1100   if (NULL != str)
 1101   {
 1102     unsigned int i;
 1103 
 1104     for (i = 0; NULL != named_tags[i].tag; i++)
 1105       if (0 == strcmp (named_tags[i].tag, field_name))
 1106       {
 1107         ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
 1108                                           named_tags[i].le_type,
 1109                                           EXTRACTOR_METAFORMAT_UTF8,
 1110                                           "text/plain",
 1111                                           (const char *) str, strlen (str) + 1);
 1112         if (NULL != str)
 1113         {
 1114           g_free (str);
 1115           str = NULL;
 1116         }
 1117         break;
 1118       }
 1119   }
 1120   if (NULL != str)
 1121   {
 1122     gchar *senddata = g_strdup_printf ("%s=%s",
 1123                                        field_name,
 1124                                        str);
 1125     if (NULL != senddata)
 1126     {
 1127       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1128                                         "gstreamer",
 1129                                         EXTRACTOR_METATYPE_UNKNOWN,
 1130                                         EXTRACTOR_METAFORMAT_UTF8,
 1131                                         "text/plain",
 1132                                         (const char *) senddata,
 1133                                         strlen (senddata) + 1);
 1134       g_free (senddata);
 1135     }
 1136   }
 1137   if (NULL != str)
 1138     g_free (str);
 1139 
 1140   return ! ps->time_to_leave;
 1141 }
 1142 
 1143 
 1144 /**
 1145  * FIXME
 1146  *
 1147  * @param info FIXME
 1148  * @param ps processing context
 1149  * @return FALSE to continue processing, TRUE to abort
 1150  */
 1151 static gboolean
 1152 send_audio_info (GstDiscovererAudioInfo *info,
 1153                  struct PrivStruct *ps)
 1154 {
 1155   gchar *tmp;
 1156   const gchar *ctmp;
 1157   guint u;
 1158 
 1159   ctmp = gst_discoverer_audio_info_get_language (info);
 1160   if (ctmp)
 1161     if ((ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
 1162                                            EXTRACTOR_METATYPE_AUDIO_LANGUAGE,
 1163                                            EXTRACTOR_METAFORMAT_UTF8,
 1164                                            "text/plain",
 1165                                            (const char *) ctmp, strlen (ctmp)
 1166                                            + 1)))
 1167       return TRUE;
 1168 
 1169   u = gst_discoverer_audio_info_get_channels (info);
 1170   if (u > 0)
 1171   {
 1172     tmp = g_strdup_printf ("%u", u);
 1173     if (NULL != tmp)
 1174     {
 1175       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1176                                         "gstreamer",
 1177                                         EXTRACTOR_METATYPE_CHANNELS,
 1178                                         EXTRACTOR_METAFORMAT_UTF8,
 1179                                         "text/plain",
 1180                                         (const char *) tmp,
 1181                                         strlen (tmp) + 1);
 1182       g_free (tmp);
 1183     }
 1184     if (ps->time_to_leave)
 1185       return TRUE;
 1186   }
 1187 
 1188   u = gst_discoverer_audio_info_get_sample_rate (info);
 1189   if (u > 0)
 1190   {
 1191     tmp = g_strdup_printf ("%u", u);
 1192     if (NULL != tmp)
 1193     {
 1194       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1195                                         "gstreamer",
 1196                                         EXTRACTOR_METATYPE_SAMPLE_RATE,
 1197                                         EXTRACTOR_METAFORMAT_UTF8,
 1198                                         "text/plain",
 1199                                         (const char *) tmp,
 1200                                         strlen (tmp) + 1);
 1201       g_free (tmp);
 1202     }
 1203     if (ps->time_to_leave)
 1204       return TRUE;
 1205   }
 1206 
 1207   u = gst_discoverer_audio_info_get_depth (info);
 1208   if (u > 0)
 1209   {
 1210     tmp = g_strdup_printf ("%u", u);
 1211     if (NULL != tmp)
 1212     {
 1213       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1214                                         "gstreamer",
 1215                                         EXTRACTOR_METATYPE_AUDIO_DEPTH,
 1216                                         EXTRACTOR_METAFORMAT_UTF8,
 1217                                         "text/plain",
 1218                                         (const char *) tmp,
 1219                                         strlen (tmp) + 1);
 1220       g_free (tmp);
 1221     }
 1222     if (ps->time_to_leave)
 1223       return TRUE;
 1224   }
 1225 
 1226   u = gst_discoverer_audio_info_get_bitrate (info);
 1227   if (u > 0)
 1228   {
 1229     tmp = g_strdup_printf ("%u", u);
 1230     if (NULL != tmp)
 1231     {
 1232       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1233                                         "gstreamer",
 1234                                         EXTRACTOR_METATYPE_AUDIO_BITRATE,
 1235                                         EXTRACTOR_METAFORMAT_UTF8,
 1236                                         "text/plain",
 1237                                         (const char *) tmp,
 1238                                         strlen (tmp) + 1);
 1239       g_free (tmp);
 1240     }
 1241     if (ps->time_to_leave)
 1242       return TRUE;
 1243   }
 1244 
 1245   u = gst_discoverer_audio_info_get_max_bitrate (info);
 1246   if (u > 0)
 1247   {
 1248     tmp = g_strdup_printf ("%u", u);
 1249     if (NULL != tmp)
 1250     {
 1251       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1252                                         "gstreamer",
 1253                                         EXTRACTOR_METATYPE_MAXIMUM_AUDIO_BITRATE,
 1254                                         EXTRACTOR_METAFORMAT_UTF8,
 1255                                         "text/plain",
 1256                                         (const char *) tmp,
 1257                                         strlen (tmp) + 1);
 1258       g_free (tmp);
 1259     }
 1260     if (ps->time_to_leave)
 1261       return TRUE;
 1262   }
 1263 
 1264   return FALSE;
 1265 }
 1266 
 1267 
 1268 /**
 1269  * FIXME
 1270  *
 1271  * @param info FIXME
 1272  * @param ps processing context
 1273  * @return FALSE to continue processing, TRUE to abort
 1274  */
 1275 static int
 1276 send_video_info (GstDiscovererVideoInfo *info,
 1277                  struct PrivStruct *ps)
 1278 {
 1279   gchar *tmp;
 1280   guint u;
 1281   guint u2;
 1282 
 1283   u = gst_discoverer_video_info_get_width (info);
 1284   u2 = gst_discoverer_video_info_get_height (info);
 1285   if ( (u > 0) && (u2 > 0) )
 1286   {
 1287     tmp = g_strdup_printf ("%ux%u", u, u2);
 1288     if (NULL != tmp)
 1289     {
 1290       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1291                                         "gstreamer",
 1292                                         EXTRACTOR_METATYPE_VIDEO_DIMENSIONS,
 1293                                         EXTRACTOR_METAFORMAT_UTF8,
 1294                                         "text/plain",
 1295                                         (const char *) tmp,
 1296                                         strlen (tmp) + 1);
 1297       g_free (tmp);
 1298     }
 1299     if (ps->time_to_leave)
 1300       return TRUE;
 1301   }
 1302 
 1303   u = gst_discoverer_video_info_get_depth (info);
 1304   if (u > 0)
 1305   {
 1306     tmp = g_strdup_printf ("%u", u);
 1307     if (NULL != tmp)
 1308     {
 1309       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1310                                         "gstreamer",
 1311                                         EXTRACTOR_METATYPE_VIDEO_DEPTH,
 1312                                         EXTRACTOR_METAFORMAT_UTF8,
 1313                                         "text/plain",
 1314                                         (const char *) tmp,
 1315                                         strlen (tmp) + 1);
 1316       g_free (tmp);
 1317     }
 1318     if (ps->time_to_leave)
 1319       return TRUE;
 1320   }
 1321 
 1322   u = gst_discoverer_video_info_get_framerate_num (info);
 1323   u2 = gst_discoverer_video_info_get_framerate_denom (info);
 1324   if ( (u > 0) && (u2 > 0) )
 1325   {
 1326     tmp = g_strdup_printf ("%u/%u", u, u2);
 1327     if (NULL != tmp)
 1328     {
 1329       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1330                                         "gstreamer",
 1331                                         EXTRACTOR_METATYPE_FRAME_RATE,
 1332                                         EXTRACTOR_METAFORMAT_UTF8,
 1333                                         "text/plain",
 1334                                         (const char *) tmp,
 1335                                         strlen (tmp) + 1);
 1336       g_free (tmp);
 1337     }
 1338     if (ps->time_to_leave)
 1339       return TRUE;
 1340   }
 1341 
 1342   u = gst_discoverer_video_info_get_par_num (info);
 1343   u2 = gst_discoverer_video_info_get_par_denom (info);
 1344   if ( (u > 0) && (u2 > 0) )
 1345   {
 1346     tmp = g_strdup_printf ("%u/%u", u, u2);
 1347     if (NULL != tmp)
 1348     {
 1349       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1350                                         "gstreamer",
 1351                                         EXTRACTOR_METATYPE_PIXEL_ASPECT_RATIO,
 1352                                         EXTRACTOR_METAFORMAT_UTF8,
 1353                                         "text/plain",
 1354                                         (const char *) tmp,
 1355                                         strlen (tmp) + 1);
 1356       g_free (tmp);
 1357     }
 1358     if (ps->time_to_leave)
 1359       return TRUE;
 1360   }
 1361 
 1362   /* gst_discoverer_video_info_is_interlaced (info) I don't trust it... */
 1363 
 1364   u = gst_discoverer_video_info_get_bitrate (info);
 1365   if (u > 0)
 1366   {
 1367     tmp = g_strdup_printf ("%u", u);
 1368     if (NULL != tmp)
 1369     {
 1370       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1371                                         "gstreamer",
 1372                                         EXTRACTOR_METATYPE_VIDEO_BITRATE,
 1373                                         EXTRACTOR_METAFORMAT_UTF8,
 1374                                         "text/plain",
 1375                                         (const char *) tmp,
 1376                                         strlen (tmp) + 1);
 1377       g_free (tmp);
 1378     }
 1379     if (ps->time_to_leave)
 1380       return TRUE;
 1381   }
 1382 
 1383   u = gst_discoverer_video_info_get_max_bitrate (info);
 1384   if (u > 0)
 1385   {
 1386     tmp = g_strdup_printf ("%u", u);
 1387     if (NULL != tmp)
 1388     {
 1389       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1390                                         "gstreamer",
 1391                                         EXTRACTOR_METATYPE_MAXIMUM_VIDEO_BITRATE,
 1392                                         EXTRACTOR_METAFORMAT_UTF8,
 1393                                         "text/plain",
 1394                                         (const char *) tmp,
 1395                                         strlen (tmp) + 1);
 1396       g_free (tmp);
 1397     }
 1398     if (ps->time_to_leave)
 1399       return TRUE;
 1400   }
 1401 
 1402   return FALSE;
 1403 }
 1404 
 1405 
 1406 /**
 1407  * FIXME
 1408  *
 1409  * @param info FIXME
 1410  * @param ps processing context
 1411  * @return FALSE to continue processing, TRUE to abort
 1412  */
 1413 static int
 1414 send_subtitle_info (GstDiscovererSubtitleInfo *info,
 1415                     struct PrivStruct *ps)
 1416 {
 1417   const gchar *ctmp;
 1418 
 1419   ctmp = gst_discoverer_subtitle_info_get_language (info);
 1420   if ( (NULL != ctmp) &&
 1421        (0 != (ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
 1422                                                 EXTRACTOR_METATYPE_SUBTITLE_LANGUAGE,
 1423                                                 EXTRACTOR_METAFORMAT_UTF8,
 1424                                                 "text/plain",
 1425                                                 (const char *) ctmp, strlen (
 1426                                                   ctmp) + 1))) )
 1427     return TRUE;
 1428   return FALSE;
 1429 }
 1430 
 1431 
 1432 static void
 1433 send_tag_foreach (const GstTagList *tags,
 1434                   const gchar *tag,
 1435                   gpointer user_data)
 1436 {
 1437   static struct KnownTag unknown_tag = {NULL, EXTRACTOR_METATYPE_UNKNOWN};
 1438   struct PrivStruct *ps = user_data;
 1439   size_t i;
 1440   size_t tagl = sizeof (__known_tags) / sizeof (struct KnownTag);
 1441   const struct KnownTag *kt = NULL;
 1442   GQuark tag_quark;
 1443   guint vallen;
 1444   GstSample *sample;
 1445 
 1446   if (ps->time_to_leave)
 1447     return;
 1448 
 1449   for (i = 0; i < tagl; i++)
 1450   {
 1451     if (strcmp (__known_tags[i].gst_tag_id, tag) != 0)
 1452       continue;
 1453     kt = &__known_tags[i];
 1454     break;
 1455   }
 1456   if (kt == NULL)
 1457     kt = &unknown_tag;
 1458 
 1459   vallen = gst_tag_list_get_tag_size (tags, tag);
 1460   if (vallen == 0)
 1461     return;
 1462 
 1463   tag_quark = g_quark_from_string (tag);
 1464 
 1465   for (i = 0; i < vallen; i++)
 1466   {
 1467     GValue val = { 0, };
 1468     const GValue *val_ref;
 1469     gchar *str = NULL;
 1470 
 1471     val_ref = gst_tag_list_get_value_index (tags, tag, i);
 1472     if (val_ref == NULL)
 1473     {
 1474       g_value_unset (&val);
 1475       continue;
 1476     }
 1477     g_value_init (&val, G_VALUE_TYPE (val_ref));
 1478     g_value_copy (val_ref, &val);
 1479 
 1480     switch (G_VALUE_TYPE (&val))
 1481     {
 1482     case G_TYPE_STRING:
 1483       str = g_value_dup_string (&val);
 1484       break;
 1485     case G_TYPE_UINT:
 1486     case G_TYPE_INT:
 1487     case G_TYPE_DOUBLE:
 1488     case G_TYPE_BOOLEAN:
 1489       str = gst_value_serialize (&val);
 1490       break;
 1491     default:
 1492       if ((G_VALUE_TYPE (&val) == GST_TYPE_SAMPLE) && (sample =
 1493                                                          gst_value_get_sample (
 1494                                                            &val)))
 1495       {
 1496         GstMapInfo mi;
 1497         GstCaps *caps;
 1498 
 1499         caps = gst_sample_get_caps (sample);
 1500         if (caps)
 1501         {
 1502           GstTagImageType imagetype;
 1503           const GstStructure *info;
 1504           GstBuffer *buf;
 1505           const gchar *mime_type;
 1506           enum EXTRACTOR_MetaType le_type;
 1507 
 1508           mime_type = gst_structure_get_name (gst_caps_get_structure (caps, 0));
 1509           info = gst_sample_get_info (sample);
 1510 
 1511           if ( (NULL == info) ||
 1512                (! gst_structure_get (info, "image-type",
 1513                                      GST_TYPE_TAG_IMAGE_TYPE, &imagetype,
 1514                                      NULL)) )
 1515             le_type = EXTRACTOR_METATYPE_PICTURE;
 1516           else
 1517           {
 1518             switch (imagetype)
 1519             {
 1520             case GST_TAG_IMAGE_TYPE_NONE:
 1521             case GST_TAG_IMAGE_TYPE_UNDEFINED:
 1522             case GST_TAG_IMAGE_TYPE_FISH:
 1523             case GST_TAG_IMAGE_TYPE_ILLUSTRATION:
 1524             default:
 1525               le_type = EXTRACTOR_METATYPE_PICTURE;
 1526               break;
 1527             case GST_TAG_IMAGE_TYPE_FRONT_COVER:
 1528             case GST_TAG_IMAGE_TYPE_BACK_COVER:
 1529             case GST_TAG_IMAGE_TYPE_LEAFLET_PAGE:
 1530             case GST_TAG_IMAGE_TYPE_MEDIUM:
 1531               le_type = EXTRACTOR_METATYPE_COVER_PICTURE;
 1532               break;
 1533             case GST_TAG_IMAGE_TYPE_LEAD_ARTIST:
 1534             case GST_TAG_IMAGE_TYPE_ARTIST:
 1535             case GST_TAG_IMAGE_TYPE_CONDUCTOR:
 1536             case GST_TAG_IMAGE_TYPE_BAND_ORCHESTRA:
 1537             case GST_TAG_IMAGE_TYPE_COMPOSER:
 1538             case GST_TAG_IMAGE_TYPE_LYRICIST:
 1539               le_type = EXTRACTOR_METATYPE_CONTRIBUTOR_PICTURE;
 1540               break;
 1541             case GST_TAG_IMAGE_TYPE_RECORDING_LOCATION:
 1542             case GST_TAG_IMAGE_TYPE_DURING_RECORDING:
 1543             case GST_TAG_IMAGE_TYPE_DURING_PERFORMANCE:
 1544             case GST_TAG_IMAGE_TYPE_VIDEO_CAPTURE:
 1545               le_type = EXTRACTOR_METATYPE_EVENT_PICTURE;
 1546               break;
 1547             case GST_TAG_IMAGE_TYPE_BAND_ARTIST_LOGO:
 1548             case GST_TAG_IMAGE_TYPE_PUBLISHER_STUDIO_LOGO:
 1549               le_type = EXTRACTOR_METATYPE_LOGO;
 1550               break;
 1551             }
 1552           }
 1553 
 1554           buf = gst_sample_get_buffer (sample);
 1555           gst_buffer_map (buf, &mi, GST_MAP_READ);
 1556           ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1557                                             "gstreamer",
 1558                                             le_type,
 1559                                             EXTRACTOR_METAFORMAT_BINARY,
 1560                                             mime_type,
 1561                                             (const char *) mi.data, mi.size);
 1562           gst_buffer_unmap (buf, &mi);
 1563         }
 1564       }
 1565       else if ((G_VALUE_TYPE (&val) == G_TYPE_UINT64) &&
 1566                (tag_quark == duration_quark))
 1567       {
 1568         GstClockTime duration = (GstClockTime) g_value_get_uint64 (&val);
 1569         if ((GST_CLOCK_TIME_IS_VALID (duration)) && (duration > 0))
 1570           str = g_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
 1571       }
 1572       else
 1573         str = gst_value_serialize (&val);
 1574       break;
 1575     }
 1576     if (NULL != str)
 1577     {
 1578       /* Discoverer internally uses some tags to provide streaminfo;
 1579        * ignore these tags to avoid duplicates.
 1580        * This MIGHT be fixed in new GStreamer versions, but won't affect
 1581        * this code (we simply won't get the tags that we think we should skip).
 1582        */gboolean skip = FALSE;
 1583       /* We have one tag-processing routine and use it for different
 1584        * stream types. However, tags themselves don't know the type of the
 1585        * stream they are attached to. We remember that before listing the
 1586        * tags, and adjust LE type accordingly.
 1587        */enum EXTRACTOR_MetaType le_type = kt->le_type;
 1588       switch (kt->le_type)
 1589       {
 1590       case EXTRACTOR_METATYPE_LANGUAGE:
 1591         switch (ps->st)
 1592         {
 1593         case STREAM_TYPE_AUDIO:
 1594           skip = TRUE;
 1595           break;
 1596         case STREAM_TYPE_SUBTITLE:
 1597           skip = TRUE;
 1598           break;
 1599         case STREAM_TYPE_VIDEO:
 1600           le_type = EXTRACTOR_METATYPE_VIDEO_LANGUAGE;
 1601           break;
 1602         default:
 1603           break;
 1604         }
 1605         break;
 1606       case EXTRACTOR_METATYPE_BITRATE:
 1607         switch (ps->st)
 1608         {
 1609         case STREAM_TYPE_AUDIO:
 1610           skip = TRUE;
 1611           break;
 1612         case STREAM_TYPE_VIDEO:
 1613           skip = TRUE;
 1614           break;
 1615         default:
 1616           break;
 1617         }
 1618         break;
 1619       case EXTRACTOR_METATYPE_MAXIMUM_BITRATE:
 1620         switch (ps->st)
 1621         {
 1622         case STREAM_TYPE_AUDIO:
 1623           skip = TRUE;
 1624           break;
 1625         case STREAM_TYPE_VIDEO:
 1626           skip = TRUE;
 1627           break;
 1628         default:
 1629           break;
 1630         }
 1631         break;
 1632       case EXTRACTOR_METATYPE_NOMINAL_BITRATE:
 1633         switch (ps->st)
 1634         {
 1635         case STREAM_TYPE_AUDIO:
 1636           skip = TRUE;
 1637           break;
 1638         case STREAM_TYPE_VIDEO:
 1639           skip = TRUE;
 1640           break;
 1641         default:
 1642           break;
 1643         }
 1644         break;
 1645       case EXTRACTOR_METATYPE_IMAGE_DIMENSIONS:
 1646         switch (ps->st)
 1647         {
 1648         case STREAM_TYPE_VIDEO:
 1649           le_type = EXTRACTOR_METATYPE_VIDEO_DIMENSIONS;
 1650           break;
 1651         default:
 1652           break;
 1653         }
 1654         break;
 1655       case EXTRACTOR_METATYPE_DURATION:
 1656         switch (ps->st)
 1657         {
 1658         case STREAM_TYPE_VIDEO:
 1659           le_type = EXTRACTOR_METATYPE_VIDEO_DURATION;
 1660           break;
 1661         case STREAM_TYPE_AUDIO:
 1662           le_type = EXTRACTOR_METATYPE_AUDIO_DURATION;
 1663           break;
 1664         case STREAM_TYPE_SUBTITLE:
 1665           le_type = EXTRACTOR_METATYPE_SUBTITLE_DURATION;
 1666           break;
 1667         default:
 1668           break;
 1669         }
 1670         break;
 1671       case EXTRACTOR_METATYPE_UNKNOWN:
 1672         /* Convert to "key=value" form */
 1673         {
 1674           gchar *new_str;
 1675           /* GST_TAG_EXTENDED_COMMENT is already in key=value form */
 1676           if ((0 != strcmp (tag, "extended-comment")) || ! strchr (str, '='))
 1677           {
 1678             new_str = g_strdup_printf ("%s=%s", tag, str);
 1679             if (NULL != str)
 1680               g_free (str);
 1681             str = new_str;
 1682           }
 1683         }
 1684         break;
 1685       default:
 1686         break;
 1687       }
 1688       if ( (! skip) &&
 1689            (NULL != str) )
 1690         ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1691                                           "gstreamer",
 1692                                           le_type,
 1693                                           EXTRACTOR_METAFORMAT_UTF8,
 1694                                           "text/plain",
 1695                                           (const char *) str,
 1696                                           strlen (str) + 1);
 1697     }
 1698     if (NULL != str)
 1699       g_free (str);
 1700     g_value_unset (&val);
 1701   }
 1702 }
 1703 
 1704 
 1705 static void
 1706 send_streams (GstDiscovererStreamInfo *info,
 1707               struct PrivStruct *ps);
 1708 
 1709 
 1710 static void
 1711 send_stream_info (GstDiscovererStreamInfo *info,
 1712                   struct PrivStruct *ps)
 1713 {
 1714   const GstStructure *misc;
 1715   GstCaps *caps;
 1716   const GstTagList *tags;
 1717 
 1718   caps = gst_discoverer_stream_info_get_caps (info);
 1719 
 1720   if (GST_IS_DISCOVERER_AUDIO_INFO (info))
 1721     ps->st = STREAM_TYPE_AUDIO;
 1722   else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
 1723     ps->st = STREAM_TYPE_VIDEO;
 1724   else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info))
 1725     ps->st = STREAM_TYPE_SUBTITLE;
 1726   else if (GST_IS_DISCOVERER_CONTAINER_INFO (info))
 1727     ps->st = STREAM_TYPE_CONTAINER;
 1728 
 1729   if (caps)
 1730   {
 1731     GstStructure *structure = gst_caps_get_structure (caps, 0);
 1732     const gchar *structname = gst_structure_get_name (structure);
 1733     if (g_str_has_prefix (structname, "image/"))
 1734       ps->st = STREAM_TYPE_IMAGE;
 1735     ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
 1736                                       EXTRACTOR_METATYPE_MIMETYPE,
 1737                                       EXTRACTOR_METAFORMAT_UTF8, "text/plain",
 1738                                       (const char *) structname, strlen (
 1739                                         structname) + 1);
 1740     if (! ps->time_to_leave)
 1741     {
 1742       gst_structure_foreach (structure, send_structure_foreach, ps);
 1743     }
 1744     gst_caps_unref (caps);
 1745   }
 1746 
 1747   if (ps->time_to_leave)
 1748   {
 1749     ps->st = STREAM_TYPE_NONE;
 1750     return;
 1751   }
 1752 
 1753   misc = gst_discoverer_stream_info_get_misc (info);
 1754   if (misc)
 1755   {
 1756     gst_structure_foreach (misc, send_structure_foreach, ps);
 1757   }
 1758 
 1759   if (ps->time_to_leave)
 1760   {
 1761     ps->st = STREAM_TYPE_NONE;
 1762     return;
 1763   }
 1764 
 1765   tags = gst_discoverer_stream_info_get_tags (info);
 1766   if (tags)
 1767   {
 1768     gst_tag_list_foreach (tags, send_tag_foreach, ps);
 1769   }
 1770   ps->st = STREAM_TYPE_NONE;
 1771 
 1772   if (ps->time_to_leave)
 1773     return;
 1774 
 1775   if (GST_IS_DISCOVERER_AUDIO_INFO (info))
 1776     send_audio_info (GST_DISCOVERER_AUDIO_INFO (info), ps);
 1777   else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
 1778     send_video_info (GST_DISCOVERER_VIDEO_INFO (info), ps);
 1779   else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info))
 1780     send_subtitle_info (GST_DISCOVERER_SUBTITLE_INFO (info), ps);
 1781   else if (GST_IS_DISCOVERER_CONTAINER_INFO (info))
 1782   {
 1783     GList *child;
 1784     GstDiscovererContainerInfo *c = GST_DISCOVERER_CONTAINER_INFO (info);
 1785     GList *children = gst_discoverer_container_info_get_streams (c);
 1786     for (child = children; (NULL != child) && (! ps->time_to_leave);
 1787          child = child->next)
 1788     {
 1789       GstDiscovererStreamInfo *sinfo = child->data;
 1790       /* send_streams () will unref it */
 1791       sinfo = gst_discoverer_stream_info_ref (sinfo);
 1792       send_streams (sinfo, ps);
 1793     }
 1794     if (children)
 1795       gst_discoverer_stream_info_list_free (children);
 1796   }
 1797 }
 1798 
 1799 
 1800 static void
 1801 send_streams (GstDiscovererStreamInfo *info,
 1802               struct PrivStruct *ps)
 1803 {
 1804   GstDiscovererStreamInfo *next;
 1805 
 1806   while ( (NULL != info) && (! ps->time_to_leave) )
 1807   {
 1808     send_stream_info (info, ps);
 1809     next = gst_discoverer_stream_info_get_next (info);
 1810     gst_discoverer_stream_info_unref (info);
 1811     info = next;
 1812   }
 1813 }
 1814 
 1815 
 1816 /**
 1817  * Callback used to construct the XML 'toc'.
 1818  *
 1819  * @param tags  FIXME
 1820  * @param tag FIXME
 1821  * @param user_data the 'struct PrivStruct' with the 'toc' string we are assembling
 1822  */
 1823 static void
 1824 send_toc_tags_foreach (const GstTagList *tags,
 1825                        const gchar *tag,
 1826                        gpointer user_data)
 1827 {
 1828   struct PrivStruct *ps = user_data;
 1829   GValue val = { 0 };
 1830   gchar *topen, *str, *tclose;
 1831   GType gst_fraction = GST_TYPE_FRACTION;
 1832 
 1833   gst_tag_list_copy_value (&val, tags, tag);
 1834   switch (G_VALUE_TYPE (&val))
 1835   {
 1836   case G_TYPE_STRING:
 1837     str = g_value_dup_string (&val);
 1838     break;
 1839   case G_TYPE_UINT:
 1840   case G_TYPE_INT:
 1841   case G_TYPE_DOUBLE:
 1842     str = gst_value_serialize (&val);
 1843     break;
 1844   default:
 1845     if (G_VALUE_TYPE (&val) == gst_fraction)
 1846     {
 1847       str = gst_value_serialize (&val);
 1848       break;
 1849     }
 1850     /* This is a potential source of invalid characters */
 1851     /* And it also might attempt to serialize binary data - such as images. */
 1852     str = gst_value_serialize (&val);
 1853     if (NULL != str)
 1854     {
 1855       g_free (str);
 1856       str = NULL;
 1857     }
 1858     break;
 1859   }
 1860   if (NULL != str)
 1861   {
 1862     topen = g_strdup_printf ("%*.*s<%s>",
 1863                              ps->toc_depth * 2,
 1864                              ps->toc_depth * 2, " ", tag);
 1865     tclose = g_strdup_printf ("%*.*s</%s>\n",
 1866                               ps->toc_depth * 2,
 1867                               ps->toc_depth * 2, " ",
 1868                               tag);
 1869     if ( (NULL != topen) &&
 1870          (NULL != tclose) )
 1871     {
 1872       if (ps->toc_print_phase)
 1873         ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos],
 1874                                    ps->toc_length - ps->toc_pos,
 1875                                    "%s%s%s",
 1876                                    topen,
 1877                                    str,
 1878                                    tclose);
 1879       else
 1880         ps->toc_length += strlen (topen) + strlen (str) + strlen (tclose);
 1881     }
 1882     if (NULL != topen)
 1883       g_free (topen);
 1884     if (NULL != tclose)
 1885       g_free (tclose);
 1886     g_free (str);
 1887   }
 1888   g_value_unset (&val);
 1889 }
 1890 
 1891 
 1892 /**
 1893  * Top-level callback used to construct the XML 'toc'.
 1894  *
 1895  * @param data the GstTocEntry we're processing
 1896  * @param user_data the 'struct PrivStruct' with the 'toc' string we are assembling
 1897  */
 1898 static void
 1899 send_toc_foreach (gpointer data, gpointer user_data)
 1900 {
 1901   GstTocEntry *entry = data;
 1902   struct PrivStruct *ps = user_data;
 1903   GstTagList *tags;
 1904   GList *subentries;
 1905   gint64 start;
 1906   gint64 stop;
 1907   GstTocEntryType entype;
 1908   gchar *s;
 1909 
 1910   entype = gst_toc_entry_get_entry_type (entry);
 1911   if (GST_TOC_ENTRY_TYPE_INVALID == entype)
 1912     return;
 1913   gst_toc_entry_get_start_stop_times (entry, &start, &stop);
 1914   s = g_strdup_printf ("%*.*s<%s start=\"%" GST_TIME_FORMAT "\" stop=\"%"
 1915                        GST_TIME_FORMAT "\">\n", ps->toc_depth * 2,
 1916                        ps->toc_depth * 2, " ",
 1917                        gst_toc_entry_type_get_nick (entype), GST_TIME_ARGS (
 1918                          start),
 1919                        GST_TIME_ARGS (stop));
 1920   if (NULL != s)
 1921   {
 1922     if (ps->toc_print_phase)
 1923       ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos],
 1924                                  ps->toc_length - ps->toc_pos,
 1925                                  "%s",
 1926                                  s);
 1927     else
 1928       ps->toc_length += strlen (s);
 1929     g_free (s);
 1930   }
 1931   ps->toc_depth++;
 1932   tags = gst_toc_entry_get_tags (entry);
 1933   if (tags)
 1934   {
 1935     if (ps->toc_print_phase)
 1936       ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length
 1937                                  - ps->toc_pos,
 1938                                  "%*.*s<tags>\n", ps->toc_depth * 2,
 1939                                  ps->toc_depth * 2, " ");
 1940     else
 1941       ps->toc_length += strlen ("<tags>\n") + ps->toc_depth * 2;
 1942     ps->toc_depth++;
 1943     gst_tag_list_foreach (tags, &send_toc_tags_foreach, ps);
 1944     ps->toc_depth--;
 1945     if (ps->toc_print_phase)
 1946       ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length
 1947                                  - ps->toc_pos,
 1948                                  "%*.*s</tags>\n", ps->toc_depth * 2,
 1949                                  ps->toc_depth * 2, " ");
 1950     else
 1951       ps->toc_length += strlen ("</tags>\n") + ps->toc_depth * 2;
 1952   }
 1953 
 1954   subentries = gst_toc_entry_get_sub_entries (entry);
 1955   g_list_foreach (subentries, send_toc_foreach, ps);
 1956   ps->toc_depth--;
 1957 
 1958   s = g_strdup_printf ("%*.*s</%s>\n",
 1959                        ps->toc_depth * 2,
 1960                        ps->toc_depth * 2, " ",
 1961                        gst_toc_entry_type_get_nick (entype));
 1962   if (NULL != s)
 1963   {
 1964     if (ps->toc_print_phase)
 1965       ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length
 1966                                  - ps->toc_pos, "%s", s);
 1967     else
 1968       ps->toc_length += strlen (s);
 1969     g_free (s);
 1970   }
 1971 }
 1972 
 1973 
 1974 /**
 1975  * XML header for the table-of-contents meta data.
 1976  */
 1977 #define TOC_XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
 1978 
 1979 
 1980 static void
 1981 send_info (GstDiscovererInfo *info,
 1982            struct PrivStruct *ps)
 1983 {
 1984   const GstToc *toc;
 1985   gchar *s;
 1986   GstDiscovererStreamInfo *sinfo;
 1987   GstClockTime duration;
 1988   GList *entries;
 1989 
 1990   duration = gst_discoverer_info_get_duration (info);
 1991   if ((GST_CLOCK_TIME_IS_VALID (duration)) && (duration > 0))
 1992   {
 1993     s = g_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
 1994     if (NULL != s)
 1995     {
 1996       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 1997                                         "gstreamer",
 1998                                         EXTRACTOR_METATYPE_DURATION,
 1999                                         EXTRACTOR_METAFORMAT_UTF8,
 2000                                         "text/plain",
 2001                                         (const char *) s,
 2002                                         strlen (s) + 1);
 2003       g_free (s);
 2004     }
 2005   }
 2006 
 2007   if (ps->time_to_leave)
 2008     return;
 2009 
 2010   /* Disable this for now (i.e. only print per-stream tags)
 2011   if ((tags = gst_discoverer_info_get_tags (info)))
 2012   {
 2013     gst_tag_list_foreach (tags, send_tag_foreach, ps);
 2014   }
 2015   */if (ps->time_to_leave)
 2016     return;
 2017 
 2018   if ((toc = gst_discoverer_info_get_toc (info)))
 2019   {
 2020     entries = gst_toc_get_entries (toc);
 2021     ps->toc_print_phase = FALSE;
 2022     ps->toc_length = 0;
 2023     g_list_foreach (entries, &send_toc_foreach, ps);
 2024 
 2025     if (ps->toc_length > 0)
 2026     {
 2027       ps->toc_print_phase = TRUE;
 2028       ps->toc_length += 1 + strlen (TOC_XML_HEADER);
 2029       ps->toc = g_malloc (ps->toc_length);
 2030       if (NULL != ps->toc)
 2031       {
 2032         ps->toc_pos = 0;
 2033         ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos],
 2034                                    ps->toc_length - ps->toc_pos,
 2035                                    "%s",
 2036                                    TOC_XML_HEADER);
 2037         g_list_foreach (entries, &send_toc_foreach, ps);
 2038         ps->toc[ps->toc_length - 1] = '\0';
 2039         ps->time_to_leave = ps->ec->proc (ps->ec->cls,
 2040                                           "gstreamer",
 2041                                           EXTRACTOR_METATYPE_TOC,
 2042                                           EXTRACTOR_METAFORMAT_C_STRING,
 2043                                           "application/xml",
 2044                                           (const char *) ps->toc,
 2045                                           ps->toc_length);
 2046         g_free (ps->toc);
 2047         ps->toc = NULL;
 2048       }
 2049     }
 2050   }
 2051 
 2052   if (0 != ps->time_to_leave)
 2053     return;
 2054 
 2055   sinfo = gst_discoverer_info_get_stream_info (info);
 2056   send_streams (sinfo, ps);
 2057 }
 2058 
 2059 
 2060 static void
 2061 send_discovered_info (GstDiscovererInfo *info,
 2062                       struct PrivStruct *ps)
 2063 {
 2064   GstDiscovererResult result;
 2065 
 2066   /* Docs don't say that info is guaranteed to be non-NULL */
 2067   if (NULL == info)
 2068     return;
 2069   result = gst_discoverer_info_get_result (info);
 2070 
 2071   switch (result)
 2072   {
 2073   case GST_DISCOVERER_OK:
 2074     break;
 2075   case GST_DISCOVERER_URI_INVALID:
 2076     break;
 2077   case GST_DISCOVERER_ERROR:
 2078     break;
 2079   case GST_DISCOVERER_TIMEOUT:
 2080     break;
 2081   case GST_DISCOVERER_BUSY:
 2082     break;
 2083   case GST_DISCOVERER_MISSING_PLUGINS:
 2084     break;
 2085   }
 2086   pthread_mutex_lock (&pipe_mutex);
 2087   send_info (info, ps);
 2088   pthread_mutex_unlock (&pipe_mutex);
 2089 }
 2090 
 2091 
 2092 static void
 2093 _new_discovered_uri (GstDiscoverer *dc,
 2094                      GstDiscovererInfo *info,
 2095                      GError *err,
 2096                      struct PrivStruct *ps)
 2097 {
 2098   send_discovered_info (info, ps);
 2099   if (ps->timeout_id > 0)
 2100     g_source_remove (ps->timeout_id);
 2101   ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout,
 2102                                   ps);
 2103 }
 2104 
 2105 
 2106 static void
 2107 _discoverer_finished (GstDiscoverer *dc, struct PrivStruct *ps)
 2108 {
 2109   if (ps->timeout_id > 0)
 2110     g_source_remove (ps->timeout_id);
 2111   ps->timeout_id = 0;
 2112   g_main_loop_quit (ps->loop);
 2113 }
 2114 
 2115 
 2116 /**
 2117  * This callback is called when discoverer has constructed a source object to
 2118  * read from. Since we provided the appsrc:// uri to discoverer, this will be
 2119  * the appsrc that we must handle. We set up some signals - one to push data
 2120  * into appsrc and one to perform a seek.
 2121  *
 2122  * @param dc
 2123  * @param source
 2124  * @param ps
 2125  */
 2126 static void
 2127 _source_setup (GstDiscoverer *dc,
 2128                GstElement *source,
 2129                struct PrivStruct *ps)
 2130 {
 2131   if (ps->source)
 2132     gst_object_unref (GST_OBJECT (ps->source));
 2133   ps->source = source;
 2134   gst_object_ref (source);
 2135 
 2136   /* we can set the length in appsrc. This allows some elements to estimate the
 2137    * total duration of the stream. It's a good idea to set the property when you
 2138    * can but it's not required. */
 2139   if (ps->length > 0)
 2140   {
 2141     g_object_set (ps->source, "size", (gint64) ps->length, NULL);
 2142     gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type",
 2143                              "random-access");
 2144   }
 2145   else
 2146     gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type", "seekable");
 2147 
 2148   /* configure the appsrc, we will push a buffer to appsrc when it needs more
 2149    * data */
 2150   g_signal_connect (ps->source, "need-data", G_CALLBACK (feed_data), ps);
 2151   g_signal_connect (ps->source, "seek-data", G_CALLBACK (seek_data), ps);
 2152   ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout,
 2153                                   ps);
 2154 }
 2155 
 2156 
 2157 static void
 2158 log_handler (const gchar *log_domain,
 2159              GLogLevelFlags log_level,
 2160              const gchar *message,
 2161              gpointer unused_data)
 2162 {
 2163   /* do nothing */
 2164 }
 2165 
 2166 
 2167 /**
 2168  * Task run from the main loop to call 'gst_discoverer_uri_async'.
 2169  *
 2170  * @param ps our execution context
 2171  * @return FALSE (always)
 2172  */
 2173 static gboolean
 2174 _run_async (struct PrivStruct *ps)
 2175 {
 2176   g_log_set_default_handler (&log_handler, NULL);
 2177   g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
 2178                      | G_LOG_FLAG_RECURSION,
 2179                      &log_handler, NULL);
 2180   gst_discoverer_discover_uri_async (ps->dc, "appsrc://");
 2181   return FALSE;
 2182 }
 2183 
 2184 
 2185 /**
 2186  * This will be the main method of your plugin.
 2187  * Describe a bit what it does here.
 2188  *
 2189  * @param ec extraction context, here you get the API
 2190  *   for accessing the file data and for returning
 2191  *   meta data
 2192  */
 2193 void
 2194 EXTRACTOR_gstreamer_extract_method (struct EXTRACTOR_ExtractContext *ec)
 2195 {
 2196   struct PrivStruct ps;
 2197   GError *err = NULL;
 2198 
 2199   memset (&ps, 0, sizeof (ps));
 2200   ps.dc = gst_discoverer_new (8 * GST_SECOND, &err);
 2201   if (NULL == ps.dc)
 2202   {
 2203     if (NULL != err)
 2204       g_error_free (err);
 2205     return;
 2206   }
 2207   if (NULL != err)
 2208     g_error_free (err);
 2209   /* connect signals */
 2210   g_signal_connect (ps.dc, "discovered", G_CALLBACK (_new_discovered_uri), &ps);
 2211   g_signal_connect (ps.dc, "finished", G_CALLBACK (_discoverer_finished), &ps);
 2212   g_signal_connect (ps.dc, "source-setup", G_CALLBACK (_source_setup), &ps);
 2213   ps.loop = g_main_loop_new (NULL, TRUE);
 2214   ps.ec = ec;
 2215   ps.length = ps.ec->get_size (ps.ec->cls);
 2216   if (ps.length == UINT_MAX)
 2217     ps.length = 0;
 2218   g_log_set_default_handler (&log_handler, NULL);
 2219   g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
 2220                      | G_LOG_FLAG_RECURSION,
 2221                      &log_handler, NULL);
 2222   gst_discoverer_start (ps.dc);
 2223   g_idle_add ((GSourceFunc) & _run_async, &ps);
 2224   g_main_loop_run (ps.loop);
 2225   if (ps.timeout_id > 0)
 2226     g_source_remove (ps.timeout_id);
 2227   gst_discoverer_stop (ps.dc);
 2228   g_object_unref (ps.dc);
 2229   g_main_loop_unref (ps.loop);
 2230 }
 2231 
 2232 
 2233 /**
 2234  * Initialize glib and globals.
 2235  */
 2236 void __attribute__ ((constructor))
 2237 gstreamer_init ()
 2238 {
 2239   gst_init (NULL, NULL);
 2240   g_log_set_default_handler (&log_handler, NULL);
 2241   g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
 2242                      | G_LOG_FLAG_RECURSION,
 2243                      &log_handler, NULL);
 2244   GST_DEBUG_CATEGORY_INIT (gstreamer_extractor, "GstExtractor",
 2245                            0, "GStreamer-based libextractor plugin");
 2246 
 2247   audio_quarks = g_new0 (GQuark, 4);
 2248   if (NULL != audio_quarks)
 2249   {
 2250     audio_quarks[0] = g_quark_from_string ("rate");
 2251     audio_quarks[1] = g_quark_from_string ("channels");
 2252     audio_quarks[2] = g_quark_from_string ("depth");
 2253     audio_quarks[3] = g_quark_from_string (NULL);
 2254   }
 2255   video_quarks = g_new0 (GQuark, 6);
 2256   if (NULL != video_quarks)
 2257   {
 2258     video_quarks[0] = g_quark_from_string ("width");
 2259     video_quarks[1] = g_quark_from_string ("height");
 2260     video_quarks[2] = g_quark_from_string ("framerate");
 2261     video_quarks[3] = g_quark_from_string ("max-framerate");
 2262     video_quarks[4] = g_quark_from_string ("pixel-aspect-ratio");
 2263     video_quarks[5] = g_quark_from_string (NULL);
 2264   }
 2265   subtitle_quarks = g_new0 (GQuark, 2);
 2266   if (NULL != subtitle_quarks)
 2267   {
 2268     subtitle_quarks[0] = g_quark_from_string ("language-code");
 2269     subtitle_quarks[1] = g_quark_from_string (NULL);
 2270   }
 2271 
 2272   duration_quark = g_quark_from_string ("duration");
 2273 
 2274   pthread_mutex_init (&pipe_mutex, NULL);
 2275 }
 2276 
 2277 
 2278 /* end of gstreamer_extractor.c */