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
_data_timeout(struct PrivStruct * ps)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
feed_data(GstElement * appsrc,guint size,struct PrivStruct * ps)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
seek_data(GstElement * appsrc,guint64 position,struct PrivStruct * ps)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
send_structure_foreach(GQuark field_id,const GValue * value,gpointer user_data)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
send_audio_info(GstDiscovererAudioInfo * info,struct PrivStruct * ps)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
send_video_info(GstDiscovererVideoInfo * info,struct PrivStruct * ps)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
send_subtitle_info(GstDiscovererSubtitleInfo * info,struct PrivStruct * ps)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
send_tag_foreach(const GstTagList * tags,const gchar * tag,gpointer user_data)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
send_stream_info(GstDiscovererStreamInfo * info,struct PrivStruct * ps)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
send_streams(GstDiscovererStreamInfo * info,struct PrivStruct * ps)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
send_toc_tags_foreach(const GstTagList * tags,const gchar * tag,gpointer user_data)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
send_toc_foreach(gpointer data,gpointer user_data)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
send_info(GstDiscovererInfo * info,struct PrivStruct * ps)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
send_discovered_info(GstDiscovererInfo * info,struct PrivStruct * ps)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
_new_discovered_uri(GstDiscoverer * dc,GstDiscovererInfo * info,GError * err,struct PrivStruct * ps)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
_discoverer_finished(GstDiscoverer * dc,struct PrivStruct * ps)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
_source_setup(GstDiscoverer * dc,GstElement * source,struct PrivStruct * ps)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
log_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer unused_data)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
_run_async(struct PrivStruct * ps)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
EXTRACTOR_gstreamer_extract_method(struct EXTRACTOR_ExtractContext * ec)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))
gstreamer_init()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 */
2279