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