diff options
Diffstat (limited to 'src/plugins/gstreamer_extractor.c')
-rw-r--r-- | src/plugins/gstreamer_extractor.c | 1526 |
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 | |||
33 | struct KnownTag | ||
34 | { | ||
35 | const char *gst_tag_id; | ||
36 | enum EXTRACTOR_MetaType le_type; | ||
37 | }; | ||
38 | |||
39 | struct 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 | |||
608 | enum 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 | |||
617 | typedef 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 | |||
634 | static void send_streams (GstDiscovererStreamInfo *info, PrivStruct *ps); | ||
635 | |||
636 | static void send_tag_foreach (const GstTagList * tags, const gchar * tag, | ||
637 | gpointer user_data); | ||
638 | |||
639 | static void send_discovered_info (GstDiscovererInfo * info, PrivStruct * ps); | ||
640 | |||
641 | static void _source_setup (GstDiscoverer * dc, GstElement * source, PrivStruct * ps); | ||
642 | |||
643 | static void feed_data (GstElement * appsrc, guint size, PrivStruct * ps); | ||
644 | static gboolean seek_data (GstElement * appsrc, guint64 position, PrivStruct * ps); | ||
645 | |||
646 | static int initialized = FALSE; | ||
647 | |||
648 | static GstDiscoverer *dc; | ||
649 | static PrivStruct *ps; | ||
650 | |||
651 | static void | ||
652 | _new_discovered_uri (GstDiscoverer * dc, GstDiscovererInfo * info, GError * err, PrivStruct * ps) | ||
653 | { | ||
654 | send_discovered_info (info, ps); | ||
655 | } | ||
656 | |||
657 | static void | ||
658 | _discoverer_finished (GstDiscoverer * dc, PrivStruct * ps) | ||
659 | { | ||
660 | g_main_loop_quit (ps->loop); | ||
661 | } | ||
662 | |||
663 | static int | ||
664 | initialize () | ||
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. */ | ||
694 | static 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 | |||
719 | static void | ||
720 | feed_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 | |||
778 | static gboolean | ||
779 | seek_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 | |||
787 | static 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 | */ | ||
802 | void | ||
803 | EXTRACTOR_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 | |||
844 | static gboolean | ||
845 | send_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 | |||
877 | static int | ||
878 | send_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 | |||
954 | static int | ||
955 | send_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 | |||
1040 | static int | ||
1041 | send_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 | |||
1056 | static void | ||
1057 | send_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 | |||
1132 | static void | ||
1133 | send_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 | |||
1315 | static void | ||
1316 | send_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 | |||
1360 | static void | ||
1361 | send_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 | |||
1417 | static void | ||
1418 | send_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 | |||
1432 | static void | ||
1433 | send_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 | |||
1497 | static void | ||
1498 | send_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 */ | ||