aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/gstreamer_extractor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/gstreamer_extractor.c')
-rw-r--r--src/plugins/gstreamer_extractor.c1526
1 files changed, 1526 insertions, 0 deletions
diff --git a/src/plugins/gstreamer_extractor.c b/src/plugins/gstreamer_extractor.c
new file mode 100644
index 0000000..81f2d5b
--- /dev/null
+++ b/src/plugins/gstreamer_extractor.c
@@ -0,0 +1,1526 @@
1/*
2 This file is part of libextractor.
3 (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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, 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
28#include <glib.h>
29#include <glib-object.h>
30#include <gst/pbutils/pbutils.h>
31#include <gst/tag/tag.h>
32
33struct KnownTag
34{
35 const char *gst_tag_id;
36 enum EXTRACTOR_MetaType le_type;
37};
38
39struct KnownTag __known_tags[] =
40{
41/**
42 * GST_TAG_TITLE:
43 *
44 * commonly used title (string)
45 *
46 * The title as it should be displayed, e.g. 'The Doll House'
47 */
48 {GST_TAG_TITLE, EXTRACTOR_METATYPE_TITLE},
49/**
50 * GST_TAG_TITLE_SORTNAME:
51 *
52 * commonly used title, as used for sorting (string)
53 *
54 * The title as it should be sorted, e.g. 'Doll House, The'
55 */
56 {GST_TAG_TITLE_SORTNAME, EXTRACTOR_METATYPE_TITLE},
57/**
58 * GST_TAG_ARTIST:
59 *
60 * person(s) responsible for the recording (string)
61 *
62 * The artist name as it should be displayed, e.g. 'Jimi Hendrix' or
63 * 'The Guitar Heroes'
64 */
65 {GST_TAG_ARTIST, EXTRACTOR_METATYPE_ARTIST},
66/**
67 * GST_TAG_ARTIST_SORTNAME:
68 *
69 * person(s) responsible for the recording, as used for sorting (string)
70 *
71 * The artist name as it should be sorted, e.g. 'Hendrix, Jimi' or
72 * 'Guitar Heroes, The'
73 */
74 {GST_TAG_ARTIST_SORTNAME, EXTRACTOR_METATYPE_ARTIST},
75/**
76 * GST_TAG_ALBUM:
77 *
78 * album containing this data (string)
79 *
80 * The album name as it should be displayed, e.g. 'The Jazz Guitar'
81 */
82 {GST_TAG_ALBUM, EXTRACTOR_METATYPE_ALBUM},
83/**
84 * GST_TAG_ALBUM_SORTNAME:
85 *
86 * album containing this data, as used for sorting (string)
87 *
88 * The album name as it should be sorted, e.g. 'Jazz Guitar, The'
89 */
90 {GST_TAG_ALBUM_SORTNAME, EXTRACTOR_METATYPE_ALBUM},
91/**
92 * GST_TAG_ALBUM_ARTIST:
93 *
94 * The artist of the entire album, as it should be displayed.
95 */
96 {GST_TAG_ALBUM_ARTIST, EXTRACTOR_METATYPE_ARTIST},
97/**
98 * GST_TAG_ALBUM_ARTIST_SORTNAME:
99 *
100 * The artist of the entire album, as it should be sorted.
101 */
102 {GST_TAG_ALBUM_ARTIST_SORTNAME, EXTRACTOR_METATYPE_ARTIST},
103/**
104 * GST_TAG_COMPOSER:
105 *
106 * person(s) who composed the recording (string)
107 */
108 {GST_TAG_COMPOSER, EXTRACTOR_METATYPE_COMPOSER},
109/**
110 * GST_TAG_DATE:
111 *
112 * date the data was created (#GDate structure)
113 */
114 {GST_TAG_DATE, EXTRACTOR_METATYPE_CREATION_TIME},
115/**
116 * GST_TAG_DATE_TIME:
117 *
118 * date and time the data was created (#GstDateTime structure)
119 */
120 {GST_TAG_DATE_TIME, EXTRACTOR_METATYPE_CREATION_TIME},
121/**
122 * GST_TAG_GENRE:
123 *
124 * genre this data belongs to (string)
125 */
126 {GST_TAG_GENRE, EXTRACTOR_METATYPE_GENRE},
127/**
128 * GST_TAG_COMMENT:
129 *
130 * free text commenting the data (string)
131 */
132 {GST_TAG_COMMENT, EXTRACTOR_METATYPE_COMMENT},
133/**
134 * GST_TAG_EXTENDED_COMMENT:
135 *
136 * key/value text commenting the data (string)
137 *
138 * Must be in the form of 'key=comment' or
139 * 'key[lc]=comment' where 'lc' is an ISO-639
140 * language code.
141 *
142 * This tag is used for unknown Vorbis comment tags,
143 * unknown APE tags and certain ID3v2 comment fields.
144 */
145 {GST_TAG_EXTENDED_COMMENT, EXTRACTOR_METATYPE_UNKNOWN},
146/**
147 * GST_TAG_TRACK_NUMBER:
148 *
149 * track number inside a collection (unsigned integer)
150 */
151 {GST_TAG_TRACK_NUMBER, EXTRACTOR_METATYPE_TRACK_NUMBER},
152/**
153 * GST_TAG_TRACK_COUNT:
154 *
155 * count of tracks inside collection this track belongs to (unsigned integer)
156 */
157 {GST_TAG_TRACK_COUNT, EXTRACTOR_METATYPE_SONG_COUNT},
158/**
159 * GST_TAG_ALBUM_VOLUME_NUMBER:
160 *
161 * disc number inside a collection (unsigned integer)
162 */
163 {GST_TAG_ALBUM_VOLUME_NUMBER, EXTRACTOR_METATYPE_DISC_NUMBER},
164/**
165 * GST_TAG_ALBUM_VOLUME_COUNT:
166 *
167 * count of discs inside collection this disc belongs to (unsigned integer)
168 */
169 {GST_TAG_ALBUM_VOLUME_NUMBER, EXTRACTOR_METATYPE_DISC_COUNT}, /* New! */
170/**
171 * GST_TAG_LOCATION:
172 *
173 * Origin of media as a URI (location, where the original of the file or stream
174 * is hosted) (string)
175 */
176 {GST_TAG_LOCATION, EXTRACTOR_METATYPE_URL},
177/**
178 * GST_TAG_HOMEPAGE:
179 *
180 * Homepage for this media (i.e. artist or movie homepage) (string)
181 */
182 {GST_TAG_HOMEPAGE, EXTRACTOR_METATYPE_URL},
183/**
184 * GST_TAG_DESCRIPTION:
185 *
186 * short text describing the content of the data (string)
187 */
188 {GST_TAG_DESCRIPTION, EXTRACTOR_METATYPE_DESCRIPTION},
189/**
190 * GST_TAG_VERSION:
191 *
192 * version of this data (string)
193 */
194 {GST_TAG_VERSION, EXTRACTOR_METATYPE_PRODUCT_VERSION},
195/**
196 * GST_TAG_ISRC:
197 *
198 * International Standard Recording Code - see http://www.ifpi.org/isrc/ (string)
199 */
200 {GST_TAG_ISRC, EXTRACTOR_METATYPE_ISRC},
201/**
202 * GST_TAG_ORGANIZATION:
203 *
204 * organization (string)
205 */
206 {GST_TAG_ORGANIZATION, EXTRACTOR_METATYPE_COMPANY},
207/**
208 * GST_TAG_COPYRIGHT:
209 *
210 * copyright notice of the data (string)
211 */
212 {GST_TAG_COPYRIGHT, EXTRACTOR_METATYPE_COPYRIGHT},
213/**
214 * GST_TAG_COPYRIGHT_URI:
215 *
216 * URI to location where copyright details can be found (string)
217 */
218 {GST_TAG_COPYRIGHT_URI, EXTRACTOR_METATYPE_COPYRIGHT},
219/**
220 * GST_TAG_ENCODED_BY:
221 *
222 * name of the person or organisation that encoded the file. May contain a
223 * copyright message if the person or organisation also holds the copyright
224 * (string)
225 *
226 * Note: do not use this field to describe the encoding application. Use
227 * #GST_TAG_APPLICATION_NAME or #GST_TAG_COMMENT for that.
228 */
229 {GST_TAG_ENCODED_BY, EXTRACTOR_METATYPE_ENCODED_BY},
230/**
231 * GST_TAG_CONTACT:
232 *
233 * contact information (string)
234 */
235 {GST_TAG_CONTACT, EXTRACTOR_METATYPE_CONTACT_INFORMATION},
236/**
237 * GST_TAG_LICENSE:
238 *
239 * license of data (string)
240 */
241 {GST_TAG_LICENSE, EXTRACTOR_METATYPE_LICENSE},
242/**
243 * GST_TAG_LICENSE_URI:
244 *
245 * URI to location where license details can be found (string)
246 */
247 {GST_TAG_LICENSE_URI, EXTRACTOR_METATYPE_LICENSE},
248/**
249 * GST_TAG_PERFORMER:
250 *
251 * person(s) performing (string)
252 */
253 {GST_TAG_PERFORMER, EXTRACTOR_METATYPE_PERFORMER},
254/**
255 * GST_TAG_DURATION:
256 *
257 * length in GStreamer time units (nanoseconds) (unsigned 64-bit integer)
258 */
259 {GST_TAG_DURATION, EXTRACTOR_METATYPE_DURATION},
260/**
261 * GST_TAG_CODEC:
262 *
263 * codec the data is stored in (string)
264 */
265 {GST_TAG_CODEC, EXTRACTOR_METATYPE_CODEC}, /* New! */
266/**
267 * GST_TAG_VIDEO_CODEC:
268 *
269 * codec the video data is stored in (string)
270 */
271 {GST_TAG_VIDEO_CODEC, EXTRACTOR_METATYPE_VIDEO_CODEC}, /* New! */
272/**
273 * GST_TAG_AUDIO_CODEC:
274 *
275 * codec the audio data is stored in (string)
276 */
277 {GST_TAG_AUDIO_CODEC, EXTRACTOR_METATYPE_AUDIO_CODEC}, /* New! */
278/**
279 * GST_TAG_SUBTITLE_CODEC:
280 *
281 * codec/format the subtitle data is stored in (string)
282 */
283 {GST_TAG_SUBTITLE_CODEC, EXTRACTOR_METATYPE_SUBTITLE_CODEC}, /* New! */
284/**
285 * GST_TAG_CONTAINER_FORMAT:
286 *
287 * container format the data is stored in (string)
288 */
289 {GST_TAG_CONTAINER_FORMAT, EXTRACTOR_METATYPE_CONTAINER_FORMAT}, /* New! */
290/**
291 * GST_TAG_BITRATE:
292 *
293 * exact or average bitrate in bits/s (unsigned integer)
294 */
295 {GST_TAG_BITRATE, EXTRACTOR_METATYPE_BITRATE}, /* New! */
296/**
297 * GST_TAG_NOMINAL_BITRATE:
298 *
299 * nominal bitrate in bits/s (unsigned integer). The actual bitrate might be
300 * different from this target bitrate.
301 */
302 {GST_TAG_NOMINAL_BITRATE, EXTRACTOR_METATYPE_NOMINAL_BITRATE}, /* New! */
303/**
304 * GST_TAG_MINIMUM_BITRATE:
305 *
306 * minimum bitrate in bits/s (unsigned integer)
307 */
308 {GST_TAG_MINIMUM_BITRATE, EXTRACTOR_METATYPE_MINIMUM_BITRATE}, /* New! */
309/**
310 * GST_TAG_MAXIMUM_BITRATE:
311 *
312 * maximum bitrate in bits/s (unsigned integer)
313 */
314 {GST_TAG_MAXIMUM_BITRATE, EXTRACTOR_METATYPE_MAXIMUM_BITRATE}, /* New! */
315/**
316 * GST_TAG_SERIAL:
317 *
318 * serial number of track (unsigned integer)
319 */
320 {GST_TAG_SERIAL, EXTRACTOR_METATYPE_SERIAL}, /* New! */
321/**
322 * GST_TAG_ENCODER:
323 *
324 * encoder used to encode this stream (string)
325 */
326 {GST_TAG_ENCODER, EXTRACTOR_METATYPE_ENCODER}, /* New */
327/**
328 * GST_TAG_ENCODER_VERSION:
329 *
330 * version of the encoder used to encode this stream (unsigned integer)
331 */
332 {GST_TAG_ENCODER_VERSION, EXTRACTOR_METATYPE_ENCODER_VERSION}, /* New! */
333/**
334 * GST_TAG_TRACK_GAIN:
335 *
336 * track gain in db (double)
337 */
338 {GST_TAG_TRACK_GAIN, EXTRACTOR_METATYPE_TRACK_GAIN}, /* New! */
339/**
340 * GST_TAG_TRACK_PEAK:
341 *
342 * peak of the track (double)
343 */
344 {GST_TAG_TRACK_PEAK, EXTRACTOR_METATYPE_TRACK_PEAK}, /* New! */
345/**
346 * GST_TAG_ALBUM_GAIN:
347 *
348 * album gain in db (double)
349 */
350 {GST_TAG_ALBUM_GAIN, EXTRACTOR_METATYPE_ALBUM_GAIN}, /* New! */
351/**
352 * GST_TAG_ALBUM_PEAK:
353 *
354 * peak of the album (double)
355 */
356 {GST_TAG_ALBUM_PEAK, EXTRACTOR_METATYPE_ALBUM_PEAK}, /* New! */
357/**
358 * GST_TAG_REFERENCE_LEVEL:
359 *
360 * reference level of track and album gain values (double)
361 */
362 {GST_TAG_REFERENCE_LEVEL, EXTRACTOR_METATYPE_REFERENCE_LEVEL}, /* New! */
363/**
364 * GST_TAG_LANGUAGE_CODE:
365 *
366 * ISO-639-2 or ISO-639-1 code for the language the content is in (string)
367 *
368 * There is utility API in libgsttag in gst-plugins-base to obtain a translated
369 * language name from the language code: gst_tag_get_language_name()
370 */
371 {GST_TAG_LANGUAGE_CODE, EXTRACTOR_METATYPE_LANGUAGE},
372/**
373 * GST_TAG_LANGUAGE_NAME:
374 *
375 * Name of the language the content is in (string)
376 *
377 * Free-form name of the language the content is in, if a language code
378 * is not available. This tag should not be set in addition to a language
379 * code. It is undefined what language or locale the language name is in.
380 */
381 {GST_TAG_LANGUAGE_NAME, EXTRACTOR_METATYPE_LANGUAGE},
382/**
383 * GST_TAG_IMAGE:
384 *
385 * image (sample) (sample taglist should specify the content type and preferably
386 * also set "image-type" field as #GstTagImageType)
387 */
388 {GST_TAG_IMAGE, EXTRACTOR_METATYPE_PICTURE},
389/**
390 * GST_TAG_PREVIEW_IMAGE:
391 *
392 * image that is meant for preview purposes, e.g. small icon-sized version
393 * (sample) (sample taglist should specify the content type)
394 */
395 {GST_TAG_IMAGE, EXTRACTOR_METATYPE_THUMBNAIL},
396/**
397 * GST_TAG_ATTACHMENT:
398 *
399 * generic file attachment (sample) (sample taglist should specify the content
400 * type and if possible set "filename" to the file name of the
401 * attachment)
402 */
403 /* No equivalent, and none needed? */
404/**
405 * GST_TAG_BEATS_PER_MINUTE:
406 *
407 * number of beats per minute in audio (double)
408 */
409 {GST_TAG_BEATS_PER_MINUTE, EXTRACTOR_METATYPE_BEATS_PER_MINUTE},
410/**
411 * GST_TAG_KEYWORDS:
412 *
413 * comma separated keywords describing the content (string).
414 */
415 {GST_TAG_KEYWORDS, EXTRACTOR_METATYPE_KEYWORDS},
416/**
417 * GST_TAG_GEO_LOCATION_NAME:
418 *
419 * human readable descriptive location of where the media has been recorded or
420 * produced. (string).
421 */
422 {GST_TAG_GEO_LOCATION_NAME, EXTRACTOR_METATYPE_LOCATION_NAME}, /* New! */
423/**
424 * GST_TAG_GEO_LOCATION_LATITUDE:
425 *
426 * geo latitude location of where the media has been recorded or produced in
427 * degrees according to WGS84 (zero at the equator, negative values for southern
428 * latitudes) (double).
429 */
430 {GST_TAG_GEO_LOCATION_LATITUDE, EXTRACTOR_METATYPE_GPS_LATITUDE},
431/**
432 * GST_TAG_GEO_LOCATION_LONGITUDE:
433 *
434 * geo longitude location of where the media has been recorded or produced in
435 * degrees according to WGS84 (zero at the prime meridian in Greenwich/UK,
436 * negative values for western longitudes). (double).
437 */
438 {GST_TAG_GEO_LOCATION_LONGITUDE, EXTRACTOR_METATYPE_GPS_LONGITUDE},
439/**
440 * GST_TAG_GEO_LOCATION_ELEVATION:
441 *
442 * geo elevation of where the media has been recorded or produced in meters
443 * according to WGS84 (zero is average sea level) (double).
444 */
445 {GST_TAG_GEO_LOCATION_ELEVATION, EXTRACTOR_METATYPE_LOCATION_ELEVATION}, /* New! */
446/**
447 * GST_TAG_GEO_LOCATION_COUNTRY:
448 *
449 * The country (english name) where the media has been produced (string).
450 */
451 {GST_TAG_GEO_LOCATION_COUNTRY, EXTRACTOR_METATYPE_LOCATION_COUNTRY},
452/**
453 * GST_TAG_GEO_LOCATION_CITY:
454 *
455 * The city (english name) where the media has been produced (string).
456 */
457 {GST_TAG_GEO_LOCATION_CITY, EXTRACTOR_METATYPE_LOCATION_CITY},
458/**
459 * GST_TAG_GEO_LOCATION_SUBLOCATION:
460 *
461 * A location 'smaller' than GST_TAG_GEO_LOCATION_CITY that specifies better
462 * where the media has been produced. (e.g. the neighborhood) (string).
463 *
464 * This tag has been added as this is how it is handled/named in XMP's
465 * Iptc4xmpcore schema.
466 */
467 {GST_TAG_GEO_LOCATION_SUBLOCATION, EXTRACTOR_METATYPE_LOCATION_SUBLOCATION},
468/**
469 * GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR:
470 *
471 * Represents the expected error on the horizontal positioning in
472 * meters (double).
473 */
474 {GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR, EXTRACTOR_METATYPE_LOCATION_HORIZONTAL_ERROR}, /* New! */
475/**
476 * GST_TAG_GEO_LOCATION_MOVEMENT_SPEED:
477 *
478 * Speed of the capturing device when performing the capture.
479 * Represented in m/s. (double)
480 *
481 * See also #GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION
482 */
483 {GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, EXTRACTOR_METATYPE_LOCATION_MOVEMENT_SPEED}, /* New! */
484/**
485 * GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION:
486 *
487 * Indicates the movement direction of the device performing the capture
488 * of a media. It is represented as degrees in floating point representation,
489 * 0 means the geographic north, and increases clockwise (double from 0 to 360)
490 *
491 * See also #GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION
492 */
493 {GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, EXTRACTOR_METATYPE_LOCATION_MOVEMENT_DIRECTION}, /* New! */
494/**
495 * GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION:
496 *
497 * Indicates the direction the device is pointing to when capturing
498 * a media. It is represented as degrees in floating point representation,
499 * 0 means the geographic north, and increases clockwise (double from 0 to 360)
500 *
501 * See also #GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION
502 */
503 {GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, EXTRACTOR_METATYPE_LOCATION_CAPTURE_DIRECTION}, /* New! */
504/**
505 * GST_TAG_SHOW_NAME:
506 *
507 * Name of the show, used for displaying (string)
508 */
509 {GST_TAG_SHOW_NAME, EXTRACTOR_METATYPE_SHOW_NAME},
510/**
511 * GST_TAG_SHOW_SORTNAME:
512 *
513 * Name of the show, used for sorting (string)
514 */
515 {GST_TAG_SHOW_SORTNAME, EXTRACTOR_METATYPE_SHOW_NAME},
516/**
517 * GST_TAG_SHOW_EPISODE_NUMBER:
518 *
519 * Number of the episode within a season/show (unsigned integer)
520 */
521 {GST_TAG_SHOW_EPISODE_NUMBER, EXTRACTOR_METATYPE_SHOW_EPISODE_NUMBER}, /* New! */
522/**
523 * GST_TAG_SHOW_SEASON_NUMBER:
524 *
525 * Number of the season of a show/series (unsigned integer)
526 */
527 {GST_TAG_SHOW_SEASON_NUMBER, EXTRACTOR_METATYPE_SHOW_SEASON_NUMBER}, /* New! */
528/**
529 * GST_TAG_LYRICS:
530 *
531 * The lyrics of the media (string)
532 */
533 {GST_TAG_LYRICS, EXTRACTOR_METATYPE_LYRICS},
534/**
535 * GST_TAG_COMPOSER_SORTNAME:
536 *
537 * The composer's name, used for sorting (string)
538 */
539 {GST_TAG_COMPOSER_SORTNAME, EXTRACTOR_METATYPE_COMPOSER},
540/**
541 * GST_TAG_GROUPING:
542 *
543 * Groups together media that are related and spans multiple tracks. An
544 * example are multiple pieces of a concerto. (string)
545 */
546 {GST_TAG_GROUPING, EXTRACTOR_METATYPE_GROUPING}, /* New! */
547/**
548 * GST_TAG_USER_RATING:
549 *
550 * Rating attributed by a person (likely the application user).
551 * The higher the value, the more the user likes this media
552 * (unsigned int from 0 to 100)
553 */
554 {GST_TAG_USER_RATING, EXTRACTOR_METATYPE_POPULARITY_METER},
555/**
556 * GST_TAG_DEVICE_MANUFACTURER:
557 *
558 * Manufacturer of the device used to create the media (string)
559 */
560 {GST_TAG_DEVICE_MANUFACTURER, EXTRACTOR_METATYPE_DEVICE_MANUFACTURER}, /* New! */
561/**
562 * GST_TAG_DEVICE_MODEL:
563 *
564 * Model of the device used to create the media (string)
565 */
566 {GST_TAG_DEVICE_MODEL, EXTRACTOR_METATYPE_DEVICE_MODEL}, /* New! */
567/**
568 * GST_TAG_APPLICATION_NAME:
569 *
570 * Name of the application used to create the media (string)
571 */
572 {GST_TAG_APPLICATION_NAME, EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE},
573/**
574 * GST_TAG_APPLICATION_DATA:
575 *
576 * Arbitrary application data (sample)
577 *
578 * Some formats allow applications to add their own arbitrary data
579 * into files. This data is application dependent.
580 */
581 /* No equivalent, and none needed (not really metadata)? */
582/**
583 * GST_TAG_IMAGE_ORIENTATION:
584 *
585 * Represents the 'Orientation' tag from EXIF. Defines how the image
586 * should be rotated and mirrored for display. (string)
587 *
588 * This tag has a predefined set of allowed values:
589 * "rotate-0"
590 * "rotate-90"
591 * "rotate-180"
592 * "rotate-270"
593 * "flip-rotate-0"
594 * "flip-rotate-90"
595 * "flip-rotate-180"
596 * "flip-rotate-270"
597 *
598 * The naming is adopted according to a possible transformation to perform
599 * on the image to fix its orientation, obviously equivalent operations will
600 * yield the same result.
601 *
602 * Rotations indicated by the values are in clockwise direction and
603 * 'flip' means an horizontal mirroring.
604 */
605 {GST_TAG_IMAGE_ORIENTATION, EXTRACTOR_METATYPE_ORIENTATION}
606};
607
608enum CurrentStreamType
609{
610 STREAM_TYPE_NONE = 0,
611 STREAM_TYPE_AUDIO = 1,
612 STREAM_TYPE_VIDEO = 2,
613 STREAM_TYPE_SUBTITLE = 3,
614 STREAM_TYPE_CONTAINER = 4
615};
616
617typedef struct
618{
619 GMainLoop *loop;
620 GstDiscoverer *dc;
621 GstElement *source;
622 struct EXTRACTOR_ExtractContext *ec;
623 long length;
624 long offset;
625 long toc_depth;
626 size_t toc_length;
627 size_t toc_pos;
628 gchar *toc;
629 gboolean toc_print_phase;
630 unsigned char time_to_leave;
631 enum CurrentStreamType st;
632} PrivStruct;
633
634static void send_streams (GstDiscovererStreamInfo *info, PrivStruct *ps);
635
636static void send_tag_foreach (const GstTagList * tags, const gchar * tag,
637 gpointer user_data);
638
639static void send_discovered_info (GstDiscovererInfo * info, PrivStruct * ps);
640
641static void _source_setup (GstDiscoverer * dc, GstElement * source, PrivStruct * ps);
642
643static void feed_data (GstElement * appsrc, guint size, PrivStruct * ps);
644static gboolean seek_data (GstElement * appsrc, guint64 position, PrivStruct * ps);
645
646static int initialized = FALSE;
647
648static GstDiscoverer *dc;
649static PrivStruct *ps;
650
651static void
652_new_discovered_uri (GstDiscoverer * dc, GstDiscovererInfo * info, GError * err, PrivStruct * ps)
653{
654 send_discovered_info (info, ps);
655}
656
657static void
658_discoverer_finished (GstDiscoverer * dc, PrivStruct * ps)
659{
660 g_main_loop_quit (ps->loop);
661}
662
663static int
664initialize ()
665{
666 gint timeout = 10;
667 GError *err = NULL;
668
669 gst_init (NULL, NULL);
670
671 dc = gst_discoverer_new (timeout * GST_SECOND, &err);
672 if (G_UNLIKELY (dc == NULL)) {
673 g_print ("Error initializing: %s\n", err->message);
674 return FALSE;
675 }
676
677 ps = g_new0 (PrivStruct, 1);
678
679 ps->dc = dc;
680 ps->loop = g_main_loop_new (NULL, TRUE);
681
682 /* connect signals */
683 g_signal_connect (dc, "discovered", G_CALLBACK (_new_discovered_uri), ps);
684 g_signal_connect (dc, "finished", G_CALLBACK (_discoverer_finished), ps);
685 g_signal_connect (dc, "source-setup", G_CALLBACK (_source_setup), ps);
686
687 return TRUE;
688}
689
690/* this callback is called when discoverer has constructed a source object to
691 * read from. Since we provided the appsrc:// uri to discoverer, this will be
692 * the appsrc that we must handle. We set up some signals - one to push data
693 * into appsrc and one to perform a seek. */
694static void
695_source_setup (GstDiscoverer * dc, GstElement * source, PrivStruct * ps)
696{
697 if (ps->source)
698 gst_object_unref (GST_OBJECT (ps->source));
699 ps->source = source;
700 gst_object_ref (source);
701
702 /* we can set the length in appsrc. This allows some elements to estimate the
703 * total duration of the stream. It's a good idea to set the property when you
704 * can but it's not required. */
705 if (ps->length > 0)
706 {
707 g_object_set (ps->source, "size", (gint64) ps->length, NULL);
708 gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type", "random-access");
709 }
710 else
711 gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type", "seekable");
712
713 /* configure the appsrc, we will push a buffer to appsrc when it needs more
714 * data */
715 g_signal_connect (ps->source, "need-data", G_CALLBACK (feed_data), ps);
716 g_signal_connect (ps->source, "seek-data", G_CALLBACK (seek_data), ps);
717}
718
719static void
720feed_data (GstElement * appsrc, guint size, PrivStruct * ps)
721{
722 GstBuffer *buffer;
723 GstFlowReturn ret;
724 GstMemory *data;
725 GstMapInfo mi;
726 long data_len;
727 int eos = FALSE;
728
729 if (ps->length > 0 && ps->offset >= ps->length) {
730 /* we are at the EOS, send end-of-stream */
731 g_signal_emit_by_name (ps->source, "end-of-stream", &ret);
732 return;
733 }
734
735 buffer = gst_buffer_new ();
736
737 if (ps->length > 0 && ps->offset + size > ps->length)
738 size = ps->length - ps->offset;
739
740 data = gst_allocator_alloc (NULL, size, NULL);
741 eos = TRUE;
742 if (gst_memory_map (data, &mi, GST_MAP_WRITE | GST_MAP_READ))
743 {
744 uint8_t *le_data;
745 data_len = ps->ec->read (ps->ec->cls, (void **) &le_data, size);
746 if (data_len > 0)
747 memcpy (mi.data, le_data, data_len);
748 gst_memory_unmap (data, &mi);
749 if (data_len > 0)
750 {
751 gst_memory_resize (data, 0, data_len);
752 gst_buffer_append_memory (buffer, data);
753
754 /* we need to set an offset for random access */
755 GST_BUFFER_OFFSET (buffer) = ps->offset;
756 GST_BUFFER_OFFSET_END (buffer) = ps->offset + data_len;
757
758 GST_DEBUG ("feed buffer %p, offset %" G_GUINT64_FORMAT "-%u", buffer,
759 ps->offset, data_len);
760 g_signal_emit_by_name (ps->source, "push-buffer", buffer, &ret);
761 eos = FALSE;
762 }
763 }
764
765 if (eos)
766 {
767 g_signal_emit_by_name (ps->source, "end-of-stream", &ret);
768 gst_memory_unref (data);
769 }
770
771 gst_buffer_unref (buffer);
772
773 ps->offset += data_len;
774
775 return;
776}
777
778static gboolean
779seek_data (GstElement * appsrc, guint64 position, PrivStruct * ps)
780{
781 GST_DEBUG ("seek to offset %" G_GUINT64_FORMAT, position);
782 ps->offset = ps->ec->seek (ps->ec->cls, position, SEEK_SET);
783
784 return ps->offset >= 0;
785}
786
787static gboolean
788_run_async (PrivStruct * ps)
789{
790 gst_discoverer_discover_uri_async (ps->dc, "appsrc://");
791 return FALSE;
792}
793
794/**
795 * This will be the main method of your plugin.
796 * Describe a bit what it does here.
797 *
798 * @param ec extraction context, here you get the API
799 * for accessing the file data and for returning
800 * meta data
801 */
802void
803EXTRACTOR_gstreamer_extract_method (struct EXTRACTOR_ExtractContext *ec)
804{
805 int64_t offset;
806 void *data;
807
808 if (!initialized)
809 if (! (initialized = initialize ()))
810 return;
811
812 ps->ec = ec;
813 ps->length = ps->ec->get_size (ps->ec->cls);
814 if (ps->length == UINT_MAX)
815 ps->length = 0;
816
817 gst_discoverer_start (dc);
818
819 g_idle_add ((GSourceFunc) _run_async, ps);
820
821 g_main_loop_run (ps->loop);
822
823 gst_discoverer_stop (dc);
824
825 /* initialize state here */
826
827 /* Call seek (plugin, POSITION, WHENCE) to seek (if you know where
828 * data starts):
829 */
830 // ec->seek (ec->cls, POSITION, SEEK_SET);
831
832 /* Call read (plugin, &data, COUNT) to read COUNT bytes
833 */
834
835
836 /* Once you find something, call proc(). If it returns non-0 - you're done.
837 */
838 // if (0 != ec->proc (ec->cls, ...)) return;
839
840 /* Don't forget to free anything you've allocated before returning! */
841 return;
842}
843
844static gboolean
845send_structure_foreach (GQuark field_id, const GValue *value,
846 gpointer user_data)
847{
848 PrivStruct *ps = (PrivStruct *) user_data;
849 gchar *str;
850 const char *field_name = g_quark_to_string (field_id);
851
852 /* TODO: check a list of known quarks, use specific EXTRACTOR_MetaType */
853 switch (G_VALUE_TYPE (value))
854 {
855 case G_TYPE_STRING:
856 str = g_value_dup_string (value);
857 break;
858 case G_TYPE_UINT:
859 case G_TYPE_DOUBLE:
860 default:
861 str = gst_value_serialize (value);
862 }
863 if (str != NULL)
864 {
865 gchar *senddata = g_strdup_printf ("%s=%s", field_name, str);
866 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
867 EXTRACTOR_METATYPE_UNKNOWN, EXTRACTOR_METAFORMAT_UTF8, "text/plain",
868 (const char *) senddata, strlen (senddata) + 1);
869 g_free (senddata);
870 }
871
872 g_free (str);
873
874 return !ps->time_to_leave;
875}
876
877static int
878send_audio_info (GstDiscovererAudioInfo *info, PrivStruct *ps)
879{
880 gchar *tmp;
881 const gchar *ctmp;
882 guint u;
883
884 ctmp = gst_discoverer_audio_info_get_language (info);
885 if (ctmp)
886 if (ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
887 EXTRACTOR_METATYPE_AUDIO_LANGUAGE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
888 (const char *) ctmp, strlen (ctmp) + 1))
889 return TRUE;
890
891 u = gst_discoverer_audio_info_get_channels (info);
892 if (u > 0)
893 {
894 tmp = g_strdup_printf ("%u", u);
895 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
896 EXTRACTOR_METATYPE_CHANNELS, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
897 (const char *) tmp, strlen (tmp) + 1);
898 g_free (tmp);
899 if (ps->time_to_leave)
900 return TRUE;
901 }
902
903 u = gst_discoverer_audio_info_get_sample_rate (info);
904 if (u > 0)
905 {
906 tmp = g_strdup_printf ("%u", u);
907 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
908 EXTRACTOR_METATYPE_SAMPLE_RATE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
909 (const char *) tmp, strlen (tmp) + 1);
910 g_free (tmp);
911 if (ps->time_to_leave)
912 return TRUE;
913 }
914
915 u = gst_discoverer_audio_info_get_depth (info);
916 if (u > 0)
917 {
918 tmp = g_strdup_printf ("%u", u);
919 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
920 EXTRACTOR_METATYPE_AUDIO_DEPTH, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
921 (const char *) tmp, strlen (tmp) + 1);
922 g_free (tmp);
923 if (ps->time_to_leave)
924 return TRUE;
925 }
926
927 u = gst_discoverer_audio_info_get_bitrate (info);
928 if (u > 0)
929 {
930 tmp = g_strdup_printf ("%u", u);
931 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
932 EXTRACTOR_METATYPE_AUDIO_BITRATE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
933 (const char *) tmp, strlen (tmp) + 1);
934 g_free (tmp);
935 if (ps->time_to_leave)
936 return TRUE;
937 }
938
939 u = gst_discoverer_audio_info_get_max_bitrate (info);
940 if (u > 0)
941 {
942 tmp = g_strdup_printf ("%u", u);
943 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
944 EXTRACTOR_METATYPE_MAXIMUM_AUDIO_BITRATE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
945 (const char *) tmp, strlen (tmp) + 1);
946 g_free (tmp);
947 if (ps->time_to_leave)
948 return TRUE;
949 }
950
951 return FALSE;
952}
953
954static int
955send_video_info (GstDiscovererVideoInfo *info, PrivStruct *ps)
956{
957 gchar *tmp;
958 guint u, u2;
959
960 u = gst_discoverer_video_info_get_width (info);
961 u2 = gst_discoverer_video_info_get_height (info);
962 if (u > 0 && u2 > 0)
963 {
964 tmp = g_strdup_printf ("%ux%u", u, u2);
965 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
966 EXTRACTOR_METATYPE_VIDEO_DIMENSIONS, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
967 (const char *) tmp, strlen (tmp) + 1);
968 g_free (tmp);
969 if (ps->time_to_leave)
970 return TRUE;
971 }
972
973 u = gst_discoverer_video_info_get_depth (info);
974 if (u > 0)
975 {
976 tmp = g_strdup_printf ("%u", u);
977 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
978 EXTRACTOR_METATYPE_VIDEO_DEPTH, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
979 (const char *) tmp, strlen (tmp) + 1);
980 g_free (tmp);
981 if (ps->time_to_leave)
982 return TRUE;
983 }
984
985 u = gst_discoverer_video_info_get_framerate_num (info);
986 u2 = gst_discoverer_video_info_get_framerate_denom (info);
987 if (u > 0 && u2 > 0)
988 {
989 tmp = g_strdup_printf ("%u/%u", u, u2);
990 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
991 EXTRACTOR_METATYPE_FRAME_RATE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
992 (const char *) tmp, strlen (tmp) + 1);
993 g_free (tmp);
994 if (ps->time_to_leave)
995 return TRUE;
996 }
997
998 u = gst_discoverer_video_info_get_par_num (info);
999 u2 = gst_discoverer_video_info_get_par_denom (info);
1000 if (u > 0 && u2 > 0)
1001 {
1002 tmp = g_strdup_printf ("%u/%u", u, u2);
1003 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
1004 EXTRACTOR_METATYPE_PIXEL_ASPECT_RATIO, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
1005 (const char *) tmp, strlen (tmp) + 1);
1006 g_free (tmp);
1007 if (ps->time_to_leave)
1008 return TRUE;
1009 }
1010
1011 /* gst_discoverer_video_info_is_interlaced (info) I don't trust it... */
1012
1013 u = gst_discoverer_video_info_get_bitrate (info);
1014 if (u > 0 && u2 > 0)
1015 {
1016 tmp = g_strdup_printf ("%u", u);
1017 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
1018 EXTRACTOR_METATYPE_VIDEO_BITRATE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
1019 (const char *) tmp, strlen (tmp) + 1);
1020 g_free (tmp);
1021 if (ps->time_to_leave)
1022 return TRUE;
1023 }
1024
1025 u = gst_discoverer_video_info_get_max_bitrate (info);
1026 if (u > 0 && u2 > 0)
1027 {
1028 tmp = g_strdup_printf ("%u", u);
1029 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
1030 EXTRACTOR_METATYPE_MAXIMUM_VIDEO_BITRATE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
1031 (const char *) tmp, strlen (tmp) + 1);
1032 g_free (tmp);
1033 if (ps->time_to_leave)
1034 return TRUE;
1035 }
1036
1037 return FALSE;
1038}
1039
1040static int
1041send_subtitle_info (GstDiscovererSubtitleInfo *info, PrivStruct *ps)
1042{
1043 gchar *tmp;
1044 const gchar *ctmp;
1045
1046 ctmp = gst_discoverer_subtitle_info_get_language (info);
1047 if (ctmp)
1048 if (ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
1049 EXTRACTOR_METATYPE_SUBTITLE_LANGUAGE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", /* New! */
1050 (const char *) ctmp, strlen (ctmp) + 1))
1051 return TRUE;
1052
1053 return FALSE;
1054}
1055
1056static void
1057send_stream_info (GstDiscovererStreamInfo * info, PrivStruct *ps)
1058{
1059 gchar *desc = NULL;
1060 const GstStructure *misc;
1061 GstCaps *caps;
1062 const GstTagList *tags;
1063
1064 caps = gst_discoverer_stream_info_get_caps (info);
1065
1066 if (caps)
1067 {
1068 GstStructure *structure = gst_caps_get_structure (caps, 0);
1069 const gchar *structname = gst_structure_get_name (structure);
1070 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
1071 EXTRACTOR_METATYPE_MIMETYPE, EXTRACTOR_METAFORMAT_UTF8, "text/plain",
1072 (const char *) structname, strlen (structname) + 1);
1073 if (!ps->time_to_leave)
1074 {
1075 gst_structure_foreach (structure, send_structure_foreach, ps);
1076 }
1077 gst_caps_unref (caps);
1078 }
1079
1080 if (ps->time_to_leave)
1081 return;
1082
1083 misc = gst_discoverer_stream_info_get_misc (info);
1084 if (misc)
1085 {
1086 gst_structure_foreach (misc, send_structure_foreach, ps);
1087 }
1088
1089 if (ps->time_to_leave)
1090 return;
1091
1092 tags = gst_discoverer_stream_info_get_tags (info);
1093 if (tags)
1094 {
1095 if (GST_IS_DISCOVERER_AUDIO_INFO (info))
1096 ps->st = STREAM_TYPE_AUDIO;
1097 else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
1098 ps->st = STREAM_TYPE_VIDEO;
1099 else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info))
1100 ps->st = STREAM_TYPE_SUBTITLE;
1101 else if (GST_IS_DISCOVERER_CONTAINER_INFO (info))
1102 ps->st = STREAM_TYPE_CONTAINER;
1103 gst_tag_list_foreach (tags, send_tag_foreach, ps);
1104 ps->st = STREAM_TYPE_NONE;
1105 }
1106
1107 if (ps->time_to_leave)
1108 return;
1109
1110 if (GST_IS_DISCOVERER_AUDIO_INFO (info))
1111 send_audio_info (GST_DISCOVERER_AUDIO_INFO (info), ps);
1112 else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
1113 send_video_info (GST_DISCOVERER_VIDEO_INFO (info), ps);
1114 else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info))
1115 send_subtitle_info (GST_DISCOVERER_SUBTITLE_INFO (info), ps);
1116 else if (GST_IS_DISCOVERER_CONTAINER_INFO (info))
1117 {
1118 GstDiscovererContainerInfo *c = GST_DISCOVERER_CONTAINER_INFO (info);
1119 GList *children = gst_discoverer_container_info_get_streams (c);
1120 if (children)
1121 {
1122 GstDiscovererStreamInfo *sinfo = children->data;
1123 /* send_streams () will unref it */
1124 gst_discoverer_stream_info_ref (sinfo);
1125 send_streams (sinfo, ps);
1126 gst_discoverer_stream_info_list_free (children);
1127 }
1128 }
1129}
1130
1131
1132static void
1133send_tag_foreach (const GstTagList * tags, const gchar * tag,
1134 gpointer user_data)
1135{
1136 PrivStruct *ps = (PrivStruct *) user_data;
1137 size_t i;
1138 size_t tagl = sizeof (__known_tags) / sizeof (struct KnownTag);
1139 struct KnownTag *kt = NULL;
1140
1141 GValue val = { 0, };
1142 gchar *str;
1143
1144 GstSample *sample;
1145
1146 if (ps->time_to_leave)
1147 return;
1148
1149 for (i = 0; i < tagl; i++)
1150 {
1151 if (strcmp (__known_tags[i].gst_tag_id, tag) != 0)
1152 continue;
1153 kt = &__known_tags[i];
1154 break;
1155 }
1156 if (kt == NULL)
1157 return;
1158
1159 gst_tag_list_copy_value (&val, tags, tag);
1160
1161 switch (G_VALUE_TYPE (&val))
1162 {
1163 case G_TYPE_STRING:
1164 str = g_value_dup_string (&val);
1165 break;
1166 case G_TYPE_UINT:
1167 case G_TYPE_DOUBLE:
1168 str = gst_value_serialize (&val);
1169 break;
1170 default:
1171 if (G_VALUE_TYPE (&val) == GST_TYPE_SAMPLE && (sample = gst_value_get_sample (&val)))
1172 {
1173 GstMapInfo mi;
1174 const gchar *structname;
1175 GstCaps *caps;
1176
1177 caps = gst_sample_get_caps (sample);
1178 if (caps)
1179 {
1180 GstTagImageType imagetype;
1181 const GstStructure *info;
1182 GstBuffer *buf;
1183 const gchar *mime_type;
1184 enum EXTRACTOR_MetaType le_type;
1185
1186 mime_type = gst_structure_get_name (gst_caps_get_structure (caps, 0));
1187 info = gst_sample_get_info (sample);
1188
1189 if (!gst_structure_get (info, "image-type", GST_TYPE_TAG_IMAGE_TYPE, &imagetype, NULL))
1190 le_type = EXTRACTOR_METATYPE_PICTURE;
1191 else
1192 {
1193 switch (imagetype)
1194 {
1195 case GST_TAG_IMAGE_TYPE_NONE:
1196 case GST_TAG_IMAGE_TYPE_UNDEFINED:
1197 case GST_TAG_IMAGE_TYPE_FISH:
1198 case GST_TAG_IMAGE_TYPE_ILLUSTRATION:
1199 default:
1200 le_type = EXTRACTOR_METATYPE_PICTURE;
1201 break;
1202 case GST_TAG_IMAGE_TYPE_FRONT_COVER:
1203 case GST_TAG_IMAGE_TYPE_BACK_COVER:
1204 case GST_TAG_IMAGE_TYPE_LEAFLET_PAGE:
1205 case GST_TAG_IMAGE_TYPE_MEDIUM:
1206 le_type = EXTRACTOR_METATYPE_COVER_PICTURE;
1207 break;
1208 case GST_TAG_IMAGE_TYPE_LEAD_ARTIST:
1209 case GST_TAG_IMAGE_TYPE_ARTIST:
1210 case GST_TAG_IMAGE_TYPE_CONDUCTOR:
1211 case GST_TAG_IMAGE_TYPE_BAND_ORCHESTRA:
1212 case GST_TAG_IMAGE_TYPE_COMPOSER:
1213 case GST_TAG_IMAGE_TYPE_LYRICIST:
1214 le_type = EXTRACTOR_METATYPE_CONTRIBUTOR_PICTURE;
1215 break;
1216 case GST_TAG_IMAGE_TYPE_RECORDING_LOCATION:
1217 case GST_TAG_IMAGE_TYPE_DURING_RECORDING:
1218 case GST_TAG_IMAGE_TYPE_DURING_PERFORMANCE:
1219 case GST_TAG_IMAGE_TYPE_VIDEO_CAPTURE:
1220 le_type = EXTRACTOR_METATYPE_EVENT_PICTURE;
1221 break;
1222 case GST_TAG_IMAGE_TYPE_BAND_ARTIST_LOGO:
1223 case GST_TAG_IMAGE_TYPE_PUBLISHER_STUDIO_LOGO:
1224 le_type = EXTRACTOR_METATYPE_LOGO;
1225 break;
1226 }
1227 }
1228
1229 buf = gst_sample_get_buffer (sample);
1230 gst_buffer_map (buf, &mi, GST_MAP_READ);
1231 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer", le_type,
1232 EXTRACTOR_METAFORMAT_BINARY, mime_type,
1233 (const char *) mi.data, mi.size);
1234 gst_buffer_unmap (buf, &mi);
1235 }
1236 }
1237 else
1238 str = gst_value_serialize (&val);
1239 break;
1240 }
1241 if (str != NULL)
1242 {
1243 /* We have one tag-processing routine and use it for different
1244 * stream types. However, tags themselves don't know the type of the
1245 * stream they are attached to. We remember that before listing the
1246 * tags, and adjust LE type accordingly.
1247 */
1248 enum EXTRACTOR_MetaType le_type = kt->le_type;
1249 switch (kt->le_type)
1250 {
1251 case EXTRACTOR_METATYPE_LANGUAGE:
1252 switch (ps->st)
1253 {
1254 case STREAM_TYPE_AUDIO:
1255 le_type = EXTRACTOR_METATYPE_AUDIO_LANGUAGE;
1256 break;
1257 case STREAM_TYPE_SUBTITLE:
1258 le_type = EXTRACTOR_METATYPE_SUBTITLE_LANGUAGE;
1259 break;
1260 case STREAM_TYPE_VIDEO:
1261 le_type = EXTRACTOR_METATYPE_VIDEO_LANGUAGE; /* New! */
1262 break;
1263 default:
1264 break;
1265 }
1266 break;
1267 case EXTRACTOR_METATYPE_BITRATE:
1268 switch (ps->st)
1269 {
1270 case STREAM_TYPE_AUDIO:
1271 le_type = EXTRACTOR_METATYPE_AUDIO_BITRATE;
1272 break;
1273 case STREAM_TYPE_VIDEO:
1274 le_type = EXTRACTOR_METATYPE_VIDEO_BITRATE;
1275 break;
1276 default:
1277 break;
1278 }
1279 break;
1280 case EXTRACTOR_METATYPE_MAXIMUM_BITRATE:
1281 switch (ps->st)
1282 {
1283 case STREAM_TYPE_AUDIO:
1284 le_type = EXTRACTOR_METATYPE_MAXIMUM_AUDIO_BITRATE;
1285 break;
1286 case STREAM_TYPE_VIDEO:
1287 le_type = EXTRACTOR_METATYPE_MAXIMUM_VIDEO_BITRATE;
1288 break;
1289 default:
1290 break;
1291 }
1292 break;
1293 case EXTRACTOR_METATYPE_IMAGE_DIMENSIONS:
1294 switch (ps->st)
1295 {
1296 case STREAM_TYPE_VIDEO:
1297 le_type = EXTRACTOR_METATYPE_VIDEO_DIMENSIONS;
1298 break;
1299 default:
1300 break;
1301 }
1302 break;
1303 default:
1304 break;
1305 }
1306 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer", le_type,
1307 EXTRACTOR_METAFORMAT_UTF8, "text/plain",
1308 (const char *) str, strlen (str) + 1);
1309 }
1310
1311 g_free (str);
1312 g_value_unset (&val);
1313}
1314
1315static void
1316send_toc_tags_foreach (const GstTagList * tags, const gchar * tag,
1317 gpointer user_data)
1318{
1319 PrivStruct *ps = (PrivStruct *) user_data;
1320 GValue val = { 0, };
1321 gchar *topen, *str, *tclose;
1322
1323 gst_tag_list_copy_value (&val, tags, tag);
1324
1325 switch (G_VALUE_TYPE (&val))
1326 {
1327 case G_TYPE_STRING:
1328 str = g_value_dup_string (&val);
1329 break;
1330 case G_TYPE_UINT:
1331 case G_TYPE_INT:
1332 case G_TYPE_DOUBLE:
1333 str = gst_value_serialize (&val);
1334 break;
1335 default:
1336 /* This is a potential source of invalid characters */
1337 /* And it also might attempt to serialize binary data - such as images. */
1338 str = NULL;
1339 break;
1340 }
1341 if (str != NULL)
1342 {
1343 topen = g_strdup_printf ("%*.*s<%s>", ps->toc_depth * 2,
1344 ps->toc_depth * 2, " ", tag);
1345 tclose = g_strdup_printf ("%*.*s</%s>\n", ps->toc_depth * 2,
1346 ps->toc_depth * 2, " ", tag);
1347
1348 if (ps->toc_print_phase)
1349 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos],
1350 ps->toc_length - ps->toc_pos, "%s%s%s", topen, str, tclose);
1351 else
1352 ps->toc_length += strlen (topen) + strlen (str) + strlen (tclose);
1353 g_free (topen);
1354 g_free (tclose);
1355 g_free (str);
1356 }
1357 g_value_unset (&val);
1358}
1359
1360static void
1361send_toc_foreach (gpointer data, gpointer user_data)
1362{
1363 PrivStruct *ps = (PrivStruct *) user_data;
1364 GstTocEntry *entry = (GstTocEntry *) data;
1365 GstTagList *tags;
1366 GList *subentries;
1367 gint64 start, stop;
1368 GstTocEntryType entype;
1369
1370 entype = gst_toc_entry_get_entry_type (entry);
1371 if (GST_TOC_ENTRY_TYPE_INVALID != entype)
1372 {
1373 char *s;
1374 gst_toc_entry_get_start_stop_times (entry, &start, &stop);
1375 s = g_strdup_printf ("%*.*s<%s start=\"%" GST_TIME_FORMAT "\" stop=\"%"
1376 GST_TIME_FORMAT"\">\n", ps->toc_depth * 2, ps->toc_depth * 2, " ",
1377 gst_toc_entry_type_get_nick (entype), GST_TIME_ARGS (start),
1378 GST_TIME_ARGS (stop));
1379 if (ps->toc_print_phase)
1380 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length - ps->toc_pos, "%s", s);
1381 else
1382 ps->toc_length += strlen (s);
1383 g_free (s);
1384 ps->toc_depth += 1;
1385 tags = gst_toc_entry_get_tags (entry);
1386 if (tags)
1387 {
1388 if (ps->toc_print_phase)
1389 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length - ps->toc_pos,
1390 "%*.*s<tags>\n", ps->toc_depth * 2, ps->toc_depth * 2, " ");
1391 else
1392 ps->toc_length += strlen ("<tags>\n") + ps->toc_depth * 2;
1393 ps->toc_depth += 1;
1394 gst_tag_list_foreach (tags, send_toc_tags_foreach, ps);
1395 ps->toc_depth -= 1;
1396 if (ps->toc_print_phase)
1397 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length - ps->toc_pos,
1398 "%*.*s</tags>\n", ps->toc_depth * 2, ps->toc_depth * 2, " ");
1399 else
1400 ps->toc_length += strlen ("</tags>\n") + ps->toc_depth * 2;
1401 }
1402
1403 subentries = gst_toc_entry_get_sub_entries (entry);
1404 g_list_foreach (subentries, send_toc_foreach, ps);
1405 ps->toc_depth -= 1;
1406
1407 s = g_strdup_printf ("%*.*s</%s>\n", ps->toc_depth * 2, ps->toc_depth * 2, " ",
1408 gst_toc_entry_type_get_nick (entype));
1409 if (ps->toc_print_phase)
1410 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length - ps->toc_pos, "%s", s);
1411 else
1412 ps->toc_length += strlen (s);
1413 g_free (s);
1414 }
1415}
1416
1417static void
1418send_streams (GstDiscovererStreamInfo *info, PrivStruct *ps)
1419{
1420 while (NULL != info && !ps->time_to_leave)
1421 {
1422 GstDiscovererStreamInfo *next;
1423 send_stream_info (info, ps);
1424 next = gst_discoverer_stream_info_get_next (info);
1425 gst_discoverer_stream_info_unref (info);
1426 info = next;
1427 }
1428}
1429
1430#define TOC_XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
1431
1432static void
1433send_info (GstDiscovererInfo * info, PrivStruct *ps)
1434{
1435 const GstTagList *tags;
1436 const GstToc *toc;
1437 gchar *s;
1438 GstDiscovererStreamInfo *sinfo;
1439 GstClockTime duration;
1440
1441 duration = gst_discoverer_info_get_duration (info);
1442 if (duration > 0)
1443 {
1444 s = g_strdup_printf ("%" GST_TIME_FORMAT,
1445 GST_TIME_ARGS (gst_discoverer_info_get_duration (info)));
1446 if (s)
1447 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
1448 EXTRACTOR_METATYPE_DURATION, EXTRACTOR_METAFORMAT_UTF8, "text/plain",
1449 (const char *) s, strlen (s) + 1);
1450 g_free (s);
1451 }
1452
1453 if (ps->time_to_leave)
1454 return;
1455
1456 if ((tags = gst_discoverer_info_get_tags (info)))
1457 {
1458 gst_tag_list_foreach (tags, send_tag_foreach, ps);
1459 }
1460
1461 if (ps->time_to_leave)
1462 return;
1463
1464 if (toc = gst_discoverer_info_get_toc (info))
1465 {
1466 GList *entries;
1467
1468 entries = gst_toc_get_entries (toc);
1469 ps->toc_print_phase = FALSE;
1470 ps->toc_length = 0;
1471 g_list_foreach (entries, send_toc_foreach, ps);
1472
1473 /* FIXME: correct limit */
1474 if (ps->toc_length > 0 && ps->toc_length < 32*1024 - 1 - strlen (TOC_XML_HEADER))
1475 {
1476 ps->toc_print_phase = TRUE;
1477 ps->toc_length += 1 + strlen (TOC_XML_HEADER);
1478 ps->toc = g_malloc (ps->toc_length);
1479 ps->toc_pos = 0;
1480 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length - ps->toc_pos, "%s", TOC_XML_HEADER);
1481 g_list_foreach (entries, send_toc_foreach, ps);
1482 ps->toc[ps->toc_length - 1] = '\0';
1483 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer",
1484 EXTRACTOR_METATYPE_TOC, EXTRACTOR_METAFORMAT_XML, "application/xml",
1485 (const char *) ps->toc, ps->toc_length);
1486
1487 }
1488 }
1489
1490 if (ps->time_to_leave)
1491 return;
1492
1493 sinfo = gst_discoverer_info_get_stream_info (info);
1494 send_streams (sinfo, ps);
1495}
1496
1497static void
1498send_discovered_info (GstDiscovererInfo * info, PrivStruct * ps)
1499{
1500 GstDiscovererResult result;
1501
1502 /* Docs don't say that info is guaranteed to be non-NULL */
1503 if (NULL == info)
1504 return;
1505 result = gst_discoverer_info_get_result (info);
1506
1507 switch (result)
1508 {
1509 case GST_DISCOVERER_OK:
1510 break;
1511 case GST_DISCOVERER_URI_INVALID:
1512 break;
1513 case GST_DISCOVERER_ERROR:
1514 break;
1515 case GST_DISCOVERER_TIMEOUT:
1516 break;
1517 case GST_DISCOVERER_BUSY:
1518 break;
1519 case GST_DISCOVERER_MISSING_PLUGINS:
1520 break;
1521 }
1522
1523 send_info (info, ps);
1524}
1525
1526/* end of gstreamer_extractor.c */