libextractor

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

gstreamer_extractor.c (62638B)


      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_COUNT, 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_PREVIEW_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           if ( (NULL != buf) &&
   1556                (gst_buffer_map (buf, &mi, GST_MAP_READ)) )
   1557           {
   1558             ps->time_to_leave = ps->ec->proc (ps->ec->cls,
   1559                                               "gstreamer",
   1560                                               le_type,
   1561                                               EXTRACTOR_METAFORMAT_BINARY,
   1562                                               mime_type,
   1563                                               (const char *) mi.data, mi.size);
   1564             gst_buffer_unmap (buf, &mi);
   1565           }
   1566         }
   1567       }
   1568       else if ((G_VALUE_TYPE (&val) == G_TYPE_UINT64) &&
   1569                (tag_quark == duration_quark))
   1570       {
   1571         GstClockTime duration = (GstClockTime) g_value_get_uint64 (&val);
   1572         if ((GST_CLOCK_TIME_IS_VALID (duration)) && (duration > 0))
   1573           str = g_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
   1574       }
   1575       else
   1576         str = gst_value_serialize (&val);
   1577       break;
   1578     }
   1579     if (NULL != str)
   1580     {
   1581       /* Discoverer internally uses some tags to provide streaminfo;
   1582        * ignore these tags to avoid duplicates.
   1583        * This MIGHT be fixed in new GStreamer versions, but won't affect
   1584        * this code (we simply won't get the tags that we think we should skip).
   1585        */gboolean skip = FALSE;
   1586       /* We have one tag-processing routine and use it for different
   1587        * stream types. However, tags themselves don't know the type of the
   1588        * stream they are attached to. We remember that before listing the
   1589        * tags, and adjust LE type accordingly.
   1590        */enum EXTRACTOR_MetaType le_type = kt->le_type;
   1591       switch (kt->le_type)
   1592       {
   1593       case EXTRACTOR_METATYPE_LANGUAGE:
   1594         switch (ps->st)
   1595         {
   1596         case STREAM_TYPE_AUDIO:
   1597           skip = TRUE;
   1598           break;
   1599         case STREAM_TYPE_SUBTITLE:
   1600           skip = TRUE;
   1601           break;
   1602         case STREAM_TYPE_VIDEO:
   1603           le_type = EXTRACTOR_METATYPE_VIDEO_LANGUAGE;
   1604           break;
   1605         default:
   1606           break;
   1607         }
   1608         break;
   1609       case EXTRACTOR_METATYPE_BITRATE:
   1610         switch (ps->st)
   1611         {
   1612         case STREAM_TYPE_AUDIO:
   1613           skip = TRUE;
   1614           break;
   1615         case STREAM_TYPE_VIDEO:
   1616           skip = TRUE;
   1617           break;
   1618         default:
   1619           break;
   1620         }
   1621         break;
   1622       case EXTRACTOR_METATYPE_MAXIMUM_BITRATE:
   1623         switch (ps->st)
   1624         {
   1625         case STREAM_TYPE_AUDIO:
   1626           skip = TRUE;
   1627           break;
   1628         case STREAM_TYPE_VIDEO:
   1629           skip = TRUE;
   1630           break;
   1631         default:
   1632           break;
   1633         }
   1634         break;
   1635       case EXTRACTOR_METATYPE_NOMINAL_BITRATE:
   1636         switch (ps->st)
   1637         {
   1638         case STREAM_TYPE_AUDIO:
   1639           skip = TRUE;
   1640           break;
   1641         case STREAM_TYPE_VIDEO:
   1642           skip = TRUE;
   1643           break;
   1644         default:
   1645           break;
   1646         }
   1647         break;
   1648       case EXTRACTOR_METATYPE_IMAGE_DIMENSIONS:
   1649         switch (ps->st)
   1650         {
   1651         case STREAM_TYPE_VIDEO:
   1652           le_type = EXTRACTOR_METATYPE_VIDEO_DIMENSIONS;
   1653           break;
   1654         default:
   1655           break;
   1656         }
   1657         break;
   1658       case EXTRACTOR_METATYPE_DURATION:
   1659         switch (ps->st)
   1660         {
   1661         case STREAM_TYPE_VIDEO:
   1662           le_type = EXTRACTOR_METATYPE_VIDEO_DURATION;
   1663           break;
   1664         case STREAM_TYPE_AUDIO:
   1665           le_type = EXTRACTOR_METATYPE_AUDIO_DURATION;
   1666           break;
   1667         case STREAM_TYPE_SUBTITLE:
   1668           le_type = EXTRACTOR_METATYPE_SUBTITLE_DURATION;
   1669           break;
   1670         default:
   1671           break;
   1672         }
   1673         break;
   1674       case EXTRACTOR_METATYPE_UNKNOWN:
   1675         /* Convert to "key=value" form */
   1676         {
   1677           gchar *new_str;
   1678           /* GST_TAG_EXTENDED_COMMENT is already in key=value form */
   1679           if ((0 != strcmp (tag, "extended-comment")) || ! strchr (str, '='))
   1680           {
   1681             new_str = g_strdup_printf ("%s=%s", tag, str);
   1682             if (NULL != str)
   1683               g_free (str);
   1684             str = new_str;
   1685           }
   1686         }
   1687         break;
   1688       default:
   1689         break;
   1690       }
   1691       if ( (! skip) &&
   1692            (NULL != str) )
   1693         ps->time_to_leave = ps->ec->proc (ps->ec->cls,
   1694                                           "gstreamer",
   1695                                           le_type,
   1696                                           EXTRACTOR_METAFORMAT_UTF8,
   1697                                           "text/plain",
   1698                                           (const char *) str,
   1699                                           strlen (str) + 1);
   1700     }
   1701     if (NULL != str)
   1702       g_free (str);
   1703     g_value_unset (&val);
   1704   }
   1705 }
   1706 
   1707 
   1708 static void
   1709 send_streams (GstDiscovererStreamInfo *info,
   1710               struct PrivStruct *ps);
   1711 
   1712 
   1713 static void
   1714 send_stream_info (GstDiscovererStreamInfo *info,
   1715                   struct PrivStruct *ps)
   1716 {
   1717   const GstStructure *misc;
   1718   GstCaps *caps;
   1719   const GstTagList *tags;
   1720 
   1721   caps = gst_discoverer_stream_info_get_caps (info);
   1722 
   1723   if (GST_IS_DISCOVERER_AUDIO_INFO (info))
   1724     ps->st = STREAM_TYPE_AUDIO;
   1725   else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
   1726     ps->st = STREAM_TYPE_VIDEO;
   1727   else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info))
   1728     ps->st = STREAM_TYPE_SUBTITLE;
   1729   else if (GST_IS_DISCOVERER_CONTAINER_INFO (info))
   1730     ps->st = STREAM_TYPE_CONTAINER;
   1731 
   1732   if (caps)
   1733   {
   1734     GstStructure *structure = gst_caps_get_structure (caps, 0);
   1735     const gchar *structname = gst_structure_get_name (structure);
   1736     if (g_str_has_prefix (structname, "image/"))
   1737       ps->st = STREAM_TYPE_IMAGE;
   1738     ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
   1739                                       EXTRACTOR_METATYPE_MIMETYPE,
   1740                                       EXTRACTOR_METAFORMAT_UTF8, "text/plain",
   1741                                       (const char *) structname, strlen (
   1742                                         structname) + 1);
   1743     if (! ps->time_to_leave)
   1744     {
   1745       gst_structure_foreach (structure, send_structure_foreach, ps);
   1746     }
   1747     gst_caps_unref (caps);
   1748   }
   1749 
   1750   if (ps->time_to_leave)
   1751   {
   1752     ps->st = STREAM_TYPE_NONE;
   1753     return;
   1754   }
   1755 
   1756   misc = gst_discoverer_stream_info_get_misc (info);
   1757   if (misc)
   1758   {
   1759     gst_structure_foreach (misc, send_structure_foreach, ps);
   1760   }
   1761 
   1762   if (ps->time_to_leave)
   1763   {
   1764     ps->st = STREAM_TYPE_NONE;
   1765     return;
   1766   }
   1767 
   1768   tags = gst_discoverer_stream_info_get_tags (info);
   1769   if (tags)
   1770   {
   1771     gst_tag_list_foreach (tags, send_tag_foreach, ps);
   1772   }
   1773   ps->st = STREAM_TYPE_NONE;
   1774 
   1775   if (ps->time_to_leave)
   1776     return;
   1777 
   1778   if (GST_IS_DISCOVERER_AUDIO_INFO (info))
   1779     send_audio_info (GST_DISCOVERER_AUDIO_INFO (info), ps);
   1780   else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
   1781     send_video_info (GST_DISCOVERER_VIDEO_INFO (info), ps);
   1782   else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info))
   1783     send_subtitle_info (GST_DISCOVERER_SUBTITLE_INFO (info), ps);
   1784   else if (GST_IS_DISCOVERER_CONTAINER_INFO (info))
   1785   {
   1786     GList *child;
   1787     GstDiscovererContainerInfo *c = GST_DISCOVERER_CONTAINER_INFO (info);
   1788     GList *children = gst_discoverer_container_info_get_streams (c);
   1789     for (child = children; (NULL != child) && (! ps->time_to_leave);
   1790          child = child->next)
   1791     {
   1792       GstDiscovererStreamInfo *sinfo = child->data;
   1793       /* send_streams () will unref it */
   1794       sinfo = gst_discoverer_stream_info_ref (sinfo);
   1795       send_streams (sinfo, ps);
   1796     }
   1797     if (children)
   1798       gst_discoverer_stream_info_list_free (children);
   1799   }
   1800 }
   1801 
   1802 
   1803 static void
   1804 send_streams (GstDiscovererStreamInfo *info,
   1805               struct PrivStruct *ps)
   1806 {
   1807   GstDiscovererStreamInfo *next;
   1808 
   1809   while ( (NULL != info) && (! ps->time_to_leave) )
   1810   {
   1811     send_stream_info (info, ps);
   1812     next = gst_discoverer_stream_info_get_next (info);
   1813     gst_discoverer_stream_info_unref (info);
   1814     info = next;
   1815   }
   1816 }
   1817 
   1818 
   1819 /**
   1820  * Callback used to construct the XML 'toc'.
   1821  *
   1822  * @param tags  FIXME
   1823  * @param tag FIXME
   1824  * @param user_data the 'struct PrivStruct' with the 'toc' string we are assembling
   1825  */
   1826 static void
   1827 send_toc_tags_foreach (const GstTagList *tags,
   1828                        const gchar *tag,
   1829                        gpointer user_data)
   1830 {
   1831   struct PrivStruct *ps = user_data;
   1832   GValue val = { 0 };
   1833   gchar *topen, *str, *tclose;
   1834   GType gst_fraction = GST_TYPE_FRACTION;
   1835 
   1836   gst_tag_list_copy_value (&val, tags, tag);
   1837   switch (G_VALUE_TYPE (&val))
   1838   {
   1839   case G_TYPE_STRING:
   1840     str = g_value_dup_string (&val);
   1841     break;
   1842   case G_TYPE_UINT:
   1843   case G_TYPE_INT:
   1844   case G_TYPE_DOUBLE:
   1845     str = gst_value_serialize (&val);
   1846     break;
   1847   default:
   1848     if (G_VALUE_TYPE (&val) == gst_fraction)
   1849     {
   1850       str = gst_value_serialize (&val);
   1851       break;
   1852     }
   1853     /* This is a potential source of invalid characters */
   1854     /* And it also might attempt to serialize binary data - such as images. */
   1855     str = gst_value_serialize (&val);
   1856     if (NULL != str)
   1857     {
   1858       g_free (str);
   1859       str = NULL;
   1860     }
   1861     break;
   1862   }
   1863   if (NULL != str)
   1864   {
   1865     topen = g_strdup_printf ("%*.*s<%s>",
   1866                              ps->toc_depth * 2,
   1867                              ps->toc_depth * 2, " ", tag);
   1868     tclose = g_strdup_printf ("%*.*s</%s>\n",
   1869                               ps->toc_depth * 2,
   1870                               ps->toc_depth * 2, " ",
   1871                               tag);
   1872     if ( (NULL != topen) &&
   1873          (NULL != tclose) )
   1874     {
   1875       if (ps->toc_print_phase)
   1876         ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos],
   1877                                    ps->toc_length - ps->toc_pos,
   1878                                    "%s%s%s",
   1879                                    topen,
   1880                                    str,
   1881                                    tclose);
   1882       else
   1883         ps->toc_length += strlen (topen) + strlen (str) + strlen (tclose);
   1884     }
   1885     if (NULL != topen)
   1886       g_free (topen);
   1887     if (NULL != tclose)
   1888       g_free (tclose);
   1889     g_free (str);
   1890   }
   1891   g_value_unset (&val);
   1892 }
   1893 
   1894 
   1895 /**
   1896  * Top-level callback used to construct the XML 'toc'.
   1897  *
   1898  * @param data the GstTocEntry we're processing
   1899  * @param user_data the 'struct PrivStruct' with the 'toc' string we are assembling
   1900  */
   1901 static void
   1902 send_toc_foreach (gpointer data, gpointer user_data)
   1903 {
   1904   GstTocEntry *entry = data;
   1905   struct PrivStruct *ps = user_data;
   1906   GstTagList *tags;
   1907   GList *subentries;
   1908   gint64 start;
   1909   gint64 stop;
   1910   GstTocEntryType entype;
   1911   gchar *s;
   1912 
   1913   entype = gst_toc_entry_get_entry_type (entry);
   1914   if (GST_TOC_ENTRY_TYPE_INVALID == entype)
   1915     return;
   1916   gst_toc_entry_get_start_stop_times (entry, &start, &stop);
   1917   s = g_strdup_printf ("%*.*s<%s start=\"%" GST_TIME_FORMAT "\" stop=\"%"
   1918                        GST_TIME_FORMAT "\">\n", ps->toc_depth * 2,
   1919                        ps->toc_depth * 2, " ",
   1920                        gst_toc_entry_type_get_nick (entype), GST_TIME_ARGS (
   1921                          start),
   1922                        GST_TIME_ARGS (stop));
   1923   if (NULL != s)
   1924   {
   1925     if (ps->toc_print_phase)
   1926       ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos],
   1927                                  ps->toc_length - ps->toc_pos,
   1928                                  "%s",
   1929                                  s);
   1930     else
   1931       ps->toc_length += strlen (s);
   1932     g_free (s);
   1933   }
   1934   ps->toc_depth++;
   1935   tags = gst_toc_entry_get_tags (entry);
   1936   if (tags)
   1937   {
   1938     if (ps->toc_print_phase)
   1939       ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length
   1940                                  - ps->toc_pos,
   1941                                  "%*.*s<tags>\n", ps->toc_depth * 2,
   1942                                  ps->toc_depth * 2, " ");
   1943     else
   1944       ps->toc_length += strlen ("<tags>\n") + ps->toc_depth * 2;
   1945     ps->toc_depth++;
   1946     gst_tag_list_foreach (tags, &send_toc_tags_foreach, ps);
   1947     ps->toc_depth--;
   1948     if (ps->toc_print_phase)
   1949       ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length
   1950                                  - ps->toc_pos,
   1951                                  "%*.*s</tags>\n", ps->toc_depth * 2,
   1952                                  ps->toc_depth * 2, " ");
   1953     else
   1954       ps->toc_length += strlen ("</tags>\n") + ps->toc_depth * 2;
   1955   }
   1956 
   1957   subentries = gst_toc_entry_get_sub_entries (entry);
   1958   g_list_foreach (subentries, send_toc_foreach, ps);
   1959   ps->toc_depth--;
   1960 
   1961   s = g_strdup_printf ("%*.*s</%s>\n",
   1962                        ps->toc_depth * 2,
   1963                        ps->toc_depth * 2, " ",
   1964                        gst_toc_entry_type_get_nick (entype));
   1965   if (NULL != s)
   1966   {
   1967     if (ps->toc_print_phase)
   1968       ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length
   1969                                  - ps->toc_pos, "%s", s);
   1970     else
   1971       ps->toc_length += strlen (s);
   1972     g_free (s);
   1973   }
   1974 }
   1975 
   1976 
   1977 /**
   1978  * XML header for the table-of-contents meta data.
   1979  */
   1980 #define TOC_XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
   1981 
   1982 
   1983 static void
   1984 send_info (GstDiscovererInfo *info,
   1985            struct PrivStruct *ps)
   1986 {
   1987   const GstToc *toc;
   1988   gchar *s;
   1989   GstDiscovererStreamInfo *sinfo;
   1990   GstClockTime duration;
   1991   GList *entries;
   1992 
   1993   duration = gst_discoverer_info_get_duration (info);
   1994   if ((GST_CLOCK_TIME_IS_VALID (duration)) && (duration > 0))
   1995   {
   1996     s = g_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
   1997     if (NULL != s)
   1998     {
   1999       ps->time_to_leave = ps->ec->proc (ps->ec->cls,
   2000                                         "gstreamer",
   2001                                         EXTRACTOR_METATYPE_DURATION,
   2002                                         EXTRACTOR_METAFORMAT_UTF8,
   2003                                         "text/plain",
   2004                                         (const char *) s,
   2005                                         strlen (s) + 1);
   2006       g_free (s);
   2007     }
   2008   }
   2009 
   2010   if (ps->time_to_leave)
   2011     return;
   2012 
   2013   /* Disable this for now (i.e. only print per-stream tags)
   2014   if ((tags = gst_discoverer_info_get_tags (info)))
   2015   {
   2016     gst_tag_list_foreach (tags, send_tag_foreach, ps);
   2017   }
   2018   */if (ps->time_to_leave)
   2019     return;
   2020 
   2021   if ((toc = gst_discoverer_info_get_toc (info)))
   2022   {
   2023     entries = gst_toc_get_entries (toc);
   2024     ps->toc_print_phase = FALSE;
   2025     ps->toc_length = 0;
   2026     g_list_foreach (entries, &send_toc_foreach, ps);
   2027 
   2028     if (ps->toc_length > 0)
   2029     {
   2030       ps->toc_print_phase = TRUE;
   2031       ps->toc_length += 1 + strlen (TOC_XML_HEADER);
   2032       ps->toc = g_malloc (ps->toc_length);
   2033       if (NULL != ps->toc)
   2034       {
   2035         ps->toc_pos = 0;
   2036         ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos],
   2037                                    ps->toc_length - ps->toc_pos,
   2038                                    "%s",
   2039                                    TOC_XML_HEADER);
   2040         g_list_foreach (entries, &send_toc_foreach, ps);
   2041         ps->toc[ps->toc_length - 1] = '\0';
   2042         ps->time_to_leave = ps->ec->proc (ps->ec->cls,
   2043                                           "gstreamer",
   2044                                           EXTRACTOR_METATYPE_TOC,
   2045                                           EXTRACTOR_METAFORMAT_C_STRING,
   2046                                           "application/xml",
   2047                                           (const char *) ps->toc,
   2048                                           ps->toc_length);
   2049         g_free (ps->toc);
   2050         ps->toc = NULL;
   2051       }
   2052     }
   2053   }
   2054 
   2055   if (0 != ps->time_to_leave)
   2056     return;
   2057 
   2058   sinfo = gst_discoverer_info_get_stream_info (info);
   2059   send_streams (sinfo, ps);
   2060 }
   2061 
   2062 
   2063 static void
   2064 send_discovered_info (GstDiscovererInfo *info,
   2065                       struct PrivStruct *ps)
   2066 {
   2067   GstDiscovererResult result;
   2068 
   2069   /* Docs don't say that info is guaranteed to be non-NULL */
   2070   if (NULL == info)
   2071     return;
   2072   result = gst_discoverer_info_get_result (info);
   2073 
   2074   switch (result)
   2075   {
   2076   case GST_DISCOVERER_OK:
   2077     break;
   2078   case GST_DISCOVERER_URI_INVALID:
   2079     break;
   2080   case GST_DISCOVERER_ERROR:
   2081     break;
   2082   case GST_DISCOVERER_TIMEOUT:
   2083     break;
   2084   case GST_DISCOVERER_BUSY:
   2085     break;
   2086   case GST_DISCOVERER_MISSING_PLUGINS:
   2087     break;
   2088   }
   2089   pthread_mutex_lock (&pipe_mutex);
   2090   send_info (info, ps);
   2091   pthread_mutex_unlock (&pipe_mutex);
   2092 }
   2093 
   2094 
   2095 static void
   2096 _new_discovered_uri (GstDiscoverer *dc,
   2097                      GstDiscovererInfo *info,
   2098                      GError *err,
   2099                      struct PrivStruct *ps)
   2100 {
   2101   send_discovered_info (info, ps);
   2102   if (ps->timeout_id > 0)
   2103     g_source_remove (ps->timeout_id);
   2104   ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout,
   2105                                   ps);
   2106 }
   2107 
   2108 
   2109 static void
   2110 _discoverer_finished (GstDiscoverer *dc, struct PrivStruct *ps)
   2111 {
   2112   if (ps->timeout_id > 0)
   2113     g_source_remove (ps->timeout_id);
   2114   ps->timeout_id = 0;
   2115   g_main_loop_quit (ps->loop);
   2116 }
   2117 
   2118 
   2119 /**
   2120  * This callback is called when discoverer has constructed a source object to
   2121  * read from. Since we provided the appsrc:// uri to discoverer, this will be
   2122  * the appsrc that we must handle. We set up some signals - one to push data
   2123  * into appsrc and one to perform a seek.
   2124  *
   2125  * @param dc
   2126  * @param source
   2127  * @param ps
   2128  */
   2129 static void
   2130 _source_setup (GstDiscoverer *dc,
   2131                GstElement *source,
   2132                struct PrivStruct *ps)
   2133 {
   2134   if (ps->source)
   2135     gst_object_unref (GST_OBJECT (ps->source));
   2136   ps->source = source;
   2137   gst_object_ref (source);
   2138 
   2139   /* we can set the length in appsrc. This allows some elements to estimate the
   2140    * total duration of the stream. It's a good idea to set the property when you
   2141    * can but it's not required. */
   2142   if (ps->length > 0)
   2143   {
   2144     g_object_set (ps->source, "size", (gint64) ps->length, NULL);
   2145     gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type",
   2146                              "random-access");
   2147   }
   2148   else
   2149     gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type", "seekable");
   2150 
   2151   /* configure the appsrc, we will push a buffer to appsrc when it needs more
   2152    * data */
   2153   g_signal_connect (ps->source, "need-data", G_CALLBACK (feed_data), ps);
   2154   g_signal_connect (ps->source, "seek-data", G_CALLBACK (seek_data), ps);
   2155   ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout,
   2156                                   ps);
   2157 }
   2158 
   2159 
   2160 static void
   2161 log_handler (const gchar *log_domain,
   2162              GLogLevelFlags log_level,
   2163              const gchar *message,
   2164              gpointer unused_data)
   2165 {
   2166   /* do nothing */
   2167 }
   2168 
   2169 
   2170 /**
   2171  * Task run from the main loop to call 'gst_discoverer_uri_async'.
   2172  *
   2173  * @param ps our execution context
   2174  * @return FALSE (always)
   2175  */
   2176 static gboolean
   2177 _run_async (struct PrivStruct *ps)
   2178 {
   2179   g_log_set_default_handler (&log_handler, NULL);
   2180   g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
   2181                      | G_LOG_FLAG_RECURSION,
   2182                      &log_handler, NULL);
   2183   gst_discoverer_discover_uri_async (ps->dc, "appsrc://");
   2184   return FALSE;
   2185 }
   2186 
   2187 
   2188 /**
   2189  * This will be the main method of your plugin.
   2190  * Describe a bit what it does here.
   2191  *
   2192  * @param ec extraction context, here you get the API
   2193  *   for accessing the file data and for returning
   2194  *   meta data
   2195  */
   2196 void
   2197 EXTRACTOR_gstreamer_extract_method (struct EXTRACTOR_ExtractContext *ec)
   2198 {
   2199   struct PrivStruct ps;
   2200   GError *err = NULL;
   2201 
   2202   memset (&ps, 0, sizeof (ps));
   2203   ps.dc = gst_discoverer_new (8 * GST_SECOND, &err);
   2204   if (NULL == ps.dc)
   2205   {
   2206     if (NULL != err)
   2207       g_error_free (err);
   2208     return;
   2209   }
   2210   if (NULL != err)
   2211     g_error_free (err);
   2212   /* connect signals */
   2213   g_signal_connect (ps.dc, "discovered", G_CALLBACK (_new_discovered_uri), &ps);
   2214   g_signal_connect (ps.dc, "finished", G_CALLBACK (_discoverer_finished), &ps);
   2215   g_signal_connect (ps.dc, "source-setup", G_CALLBACK (_source_setup), &ps);
   2216   ps.loop = g_main_loop_new (NULL, TRUE);
   2217   ps.ec = ec;
   2218   ps.length = ps.ec->get_size (ps.ec->cls);
   2219   if (ps.length == UINT64_MAX)
   2220     ps.length = 0;
   2221   g_log_set_default_handler (&log_handler, NULL);
   2222   g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
   2223                      | G_LOG_FLAG_RECURSION,
   2224                      &log_handler, NULL);
   2225   gst_discoverer_start (ps.dc);
   2226   g_idle_add ((GSourceFunc) & _run_async, &ps);
   2227   g_main_loop_run (ps.loop);
   2228   if (ps.timeout_id > 0)
   2229     g_source_remove (ps.timeout_id);
   2230   gst_discoverer_stop (ps.dc);
   2231   g_object_unref (ps.dc);
   2232   g_main_loop_unref (ps.loop);
   2233 }
   2234 
   2235 
   2236 /**
   2237  * Initialize glib and globals.
   2238  */
   2239 void __attribute__ ((constructor))
   2240 gstreamer_init ()
   2241 {
   2242   gst_init (NULL, NULL);
   2243   g_log_set_default_handler (&log_handler, NULL);
   2244   g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
   2245                      | G_LOG_FLAG_RECURSION,
   2246                      &log_handler, NULL);
   2247   GST_DEBUG_CATEGORY_INIT (gstreamer_extractor, "GstExtractor",
   2248                            0, "GStreamer-based libextractor plugin");
   2249 
   2250   audio_quarks = g_new0 (GQuark, 4);
   2251   if (NULL != audio_quarks)
   2252   {
   2253     audio_quarks[0] = g_quark_from_string ("rate");
   2254     audio_quarks[1] = g_quark_from_string ("channels");
   2255     audio_quarks[2] = g_quark_from_string ("depth");
   2256     audio_quarks[3] = g_quark_from_string (NULL);
   2257   }
   2258   video_quarks = g_new0 (GQuark, 6);
   2259   if (NULL != video_quarks)
   2260   {
   2261     video_quarks[0] = g_quark_from_string ("width");
   2262     video_quarks[1] = g_quark_from_string ("height");
   2263     video_quarks[2] = g_quark_from_string ("framerate");
   2264     video_quarks[3] = g_quark_from_string ("max-framerate");
   2265     video_quarks[4] = g_quark_from_string ("pixel-aspect-ratio");
   2266     video_quarks[5] = g_quark_from_string (NULL);
   2267   }
   2268   subtitle_quarks = g_new0 (GQuark, 2);
   2269   if (NULL != subtitle_quarks)
   2270   {
   2271     subtitle_quarks[0] = g_quark_from_string ("language-code");
   2272     subtitle_quarks[1] = g_quark_from_string (NULL);
   2273   }
   2274 
   2275   duration_quark = g_quark_from_string ("duration");
   2276 
   2277   pthread_mutex_init (&pipe_mutex, NULL);
   2278 }
   2279 
   2280 
   2281 /* end of gstreamer_extractor.c */