libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

gstreamer_extractor.c (62551B)


      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 */