gstreamer_extractor.c (62551B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2002, 2003, 2004, 2009, 2012 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file plugins/gstreamer_extractor.c 22 * @brief extracts metadata from files using GStreamer 23 * @author LRN 24 */ 25 #include "platform.h" 26 #include "extractor.h" 27 #include <glib.h> 28 #include <glib-object.h> 29 #include <gst/pbutils/pbutils.h> 30 #include <gst/tag/tag.h> 31 #include <gst/app/gstappsrc.h> 32 #include <pthread.h> 33 34 GST_DEBUG_CATEGORY_STATIC (gstreamer_extractor); 35 #define GST_CAT_DEFAULT gstreamer_extractor 36 37 /** 38 * Once discoverer has gone for that long without asking for data or 39 * asking to seek, or giving us discovered info, assume it hanged up 40 * and kill it. 41 * In milliseconds. 42 */ 43 #define DATA_TIMEOUT 750 /* 750ms */ 44 45 pthread_mutex_t pipe_mutex; 46 47 /** 48 * Struct mapping GSTREAMER tags to LE tags. 49 */ 50 struct KnownTag 51 { 52 /** 53 * GStreamer tag. 54 */ 55 const char *gst_tag_id; 56 57 /** 58 * Corresponding LE tag. 59 */ 60 enum EXTRACTOR_MetaType le_type; 61 }; 62 63 64 /** 65 * Struct mapping known tags (that do occur in GST API) to LE tags. 66 */ 67 static struct KnownTag __known_tags[] = { 68 /** 69 * GST_TAG_TITLE: 70 * 71 * commonly used title (string) 72 * 73 * The title as it should be displayed, e.g. 'The Doll House' 74 */ 75 {GST_TAG_TITLE, EXTRACTOR_METATYPE_TITLE}, 76 77 /** 78 * GST_TAG_TITLE_SORTNAME: 79 * 80 * commonly used title, as used for sorting (string) 81 * 82 * The title as it should be sorted, e.g. 'Doll House, The' 83 */ 84 {GST_TAG_TITLE_SORTNAME, EXTRACTOR_METATYPE_TITLE}, 85 86 /** 87 * GST_TAG_ARTIST: 88 * 89 * person(s) responsible for the recording (string) 90 * 91 * The artist name as it should be displayed, e.g. 'Jimi Hendrix' or 92 * 'The Guitar Heroes' 93 */ 94 {GST_TAG_ARTIST, EXTRACTOR_METATYPE_ARTIST}, 95 96 /** 97 * GST_TAG_ARTIST_SORTNAME: 98 * 99 * person(s) responsible for the recording, as used for sorting (string) 100 * 101 * The artist name as it should be sorted, e.g. 'Hendrix, Jimi' or 102 * 'Guitar Heroes, The' 103 */ 104 {GST_TAG_ARTIST_SORTNAME, EXTRACTOR_METATYPE_ARTIST}, 105 106 /** 107 * GST_TAG_ALBUM: 108 * 109 * album containing this data (string) 110 * 111 * The album name as it should be displayed, e.g. 'The Jazz Guitar' 112 */ 113 {GST_TAG_ALBUM, EXTRACTOR_METATYPE_ALBUM}, 114 115 /** 116 * GST_TAG_ALBUM_SORTNAME: 117 * 118 * album containing this data, as used for sorting (string) 119 * 120 * The album name as it should be sorted, e.g. 'Jazz Guitar, The' 121 */ 122 {GST_TAG_ALBUM_SORTNAME, EXTRACTOR_METATYPE_ALBUM}, 123 124 /** 125 * GST_TAG_ALBUM_ARTIST: 126 * 127 * The artist of the entire album, as it should be displayed. 128 */ 129 {GST_TAG_ALBUM_ARTIST, EXTRACTOR_METATYPE_ARTIST}, 130 131 /** 132 * GST_TAG_ALBUM_ARTIST_SORTNAME: 133 * 134 * The artist of the entire album, as it should be sorted. 135 */ 136 {GST_TAG_ALBUM_ARTIST_SORTNAME, EXTRACTOR_METATYPE_ARTIST}, 137 138 /** 139 * GST_TAG_COMPOSER: 140 * 141 * person(s) who composed the recording (string) 142 */ 143 {GST_TAG_COMPOSER, EXTRACTOR_METATYPE_COMPOSER}, 144 145 /** 146 * GST_TAG_DATE: 147 * 148 * date the data was created (#GDate structure) 149 */ 150 {GST_TAG_DATE, EXTRACTOR_METATYPE_CREATION_TIME}, 151 152 /** 153 * GST_TAG_DATE_TIME: 154 * 155 * date and time the data was created (#GstDateTime structure) 156 */ 157 {GST_TAG_DATE_TIME, EXTRACTOR_METATYPE_CREATION_TIME}, 158 159 /** 160 * GST_TAG_GENRE: 161 * 162 * genre this data belongs to (string) 163 */ 164 {GST_TAG_GENRE, EXTRACTOR_METATYPE_GENRE}, 165 166 /** 167 * GST_TAG_COMMENT: 168 * 169 * free text commenting the data (string) 170 */ 171 {GST_TAG_COMMENT, EXTRACTOR_METATYPE_COMMENT}, 172 173 /** 174 * GST_TAG_EXTENDED_COMMENT: 175 * 176 * key/value text commenting the data (string) 177 * 178 * Must be in the form of 'key=comment' or 179 * 'key[lc]=comment' where 'lc' is an ISO-639 180 * language code. 181 * 182 * This tag is used for unknown Vorbis comment tags, 183 * unknown APE tags and certain ID3v2 comment fields. 184 */ 185 {GST_TAG_EXTENDED_COMMENT, EXTRACTOR_METATYPE_COMMENT}, 186 187 /** 188 * GST_TAG_TRACK_NUMBER: 189 * 190 * track number inside a collection (unsigned integer) 191 */ 192 {GST_TAG_TRACK_NUMBER, EXTRACTOR_METATYPE_TRACK_NUMBER}, 193 194 /** 195 * GST_TAG_TRACK_COUNT: 196 * 197 * count of tracks inside collection this track belongs to (unsigned integer) 198 */ 199 {GST_TAG_TRACK_COUNT, EXTRACTOR_METATYPE_SONG_COUNT}, 200 201 /** 202 * GST_TAG_ALBUM_VOLUME_NUMBER: 203 * 204 * disc number inside a collection (unsigned integer) 205 */ 206 {GST_TAG_ALBUM_VOLUME_NUMBER, EXTRACTOR_METATYPE_DISC_NUMBER}, 207 208 /** 209 * GST_TAG_ALBUM_VOLUME_COUNT: 210 * 211 * count of discs inside collection this disc belongs to (unsigned integer) 212 */ 213 {GST_TAG_ALBUM_VOLUME_NUMBER, EXTRACTOR_METATYPE_DISC_COUNT}, 214 215 /** 216 * GST_TAG_LOCATION: 217 * 218 * Origin of media as a URI (location, where the original of the file or stream 219 * is hosted) (string) 220 */ 221 {GST_TAG_LOCATION, EXTRACTOR_METATYPE_URL}, 222 223 /** 224 * GST_TAG_HOMEPAGE: 225 * 226 * Homepage for this media (i.e. artist or movie homepage) (string) 227 */ 228 {GST_TAG_HOMEPAGE, EXTRACTOR_METATYPE_URL}, 229 230 /** 231 * GST_TAG_DESCRIPTION: 232 * 233 * short text describing the content of the data (string) 234 */ 235 {GST_TAG_DESCRIPTION, EXTRACTOR_METATYPE_DESCRIPTION}, 236 237 /** 238 * GST_TAG_VERSION: 239 * 240 * version of this data (string) 241 */ 242 {GST_TAG_VERSION, EXTRACTOR_METATYPE_PRODUCT_VERSION}, 243 244 /** 245 * GST_TAG_ISRC: 246 * 247 * International Standard Recording Code - see http://www.ifpi.org/isrc/ (string) 248 */ 249 {GST_TAG_ISRC, EXTRACTOR_METATYPE_ISRC}, 250 251 /** 252 * GST_TAG_ORGANIZATION: 253 * 254 * organization (string) 255 */ 256 {GST_TAG_ORGANIZATION, EXTRACTOR_METATYPE_COMPANY}, 257 258 /** 259 * GST_TAG_COPYRIGHT: 260 * 261 * copyright notice of the data (string) 262 */ 263 {GST_TAG_COPYRIGHT, EXTRACTOR_METATYPE_COPYRIGHT}, 264 265 /** 266 * GST_TAG_COPYRIGHT_URI: 267 * 268 * URI to location where copyright details can be found (string) 269 */ 270 {GST_TAG_COPYRIGHT_URI, EXTRACTOR_METATYPE_COPYRIGHT}, 271 272 /** 273 * GST_TAG_ENCODED_BY: 274 * 275 * name of the person or organisation that encoded the file. May contain a 276 * copyright message if the person or organisation also holds the copyright 277 * (string) 278 * 279 * Note: do not use this field to describe the encoding application. Use 280 * #GST_TAG_APPLICATION_NAME or #GST_TAG_COMMENT for that. 281 */ 282 {GST_TAG_ENCODED_BY, EXTRACTOR_METATYPE_ENCODED_BY}, 283 284 /** 285 * GST_TAG_CONTACT: 286 * 287 * contact information (string) 288 */ 289 {GST_TAG_CONTACT, EXTRACTOR_METATYPE_CONTACT_INFORMATION}, 290 291 /** 292 * GST_TAG_LICENSE: 293 * 294 * license of data (string) 295 */ 296 {GST_TAG_LICENSE, EXTRACTOR_METATYPE_LICENSE}, 297 298 /** 299 * GST_TAG_LICENSE_URI: 300 * 301 * URI to location where license details can be found (string) 302 */ 303 {GST_TAG_LICENSE_URI, EXTRACTOR_METATYPE_LICENSE}, 304 305 /** 306 * GST_TAG_PERFORMER: 307 * 308 * person(s) performing (string) 309 */ 310 {GST_TAG_PERFORMER, EXTRACTOR_METATYPE_PERFORMER}, 311 312 /** 313 * GST_TAG_DURATION: 314 * 315 * length in GStreamer time units (nanoseconds) (unsigned 64-bit integer) 316 */ 317 {GST_TAG_DURATION, EXTRACTOR_METATYPE_DURATION}, 318 319 /** 320 * GST_TAG_CODEC: 321 * 322 * codec the data is stored in (string) 323 */ 324 {GST_TAG_CODEC, EXTRACTOR_METATYPE_CODEC}, 325 326 /** 327 * GST_TAG_VIDEO_CODEC: 328 * 329 * codec the video data is stored in (string) 330 */ 331 {GST_TAG_VIDEO_CODEC, EXTRACTOR_METATYPE_VIDEO_CODEC}, 332 333 /** 334 * GST_TAG_AUDIO_CODEC: 335 * 336 * codec the audio data is stored in (string) 337 */ 338 {GST_TAG_AUDIO_CODEC, EXTRACTOR_METATYPE_AUDIO_CODEC}, 339 340 /** 341 * GST_TAG_SUBTITLE_CODEC: 342 * 343 * codec/format the subtitle data is stored in (string) 344 */ 345 {GST_TAG_SUBTITLE_CODEC, EXTRACTOR_METATYPE_SUBTITLE_CODEC}, 346 347 /** 348 * GST_TAG_CONTAINER_FORMAT: 349 * 350 * container format the data is stored in (string) 351 */ 352 {GST_TAG_CONTAINER_FORMAT, EXTRACTOR_METATYPE_CONTAINER_FORMAT}, 353 354 /** 355 * GST_TAG_BITRATE: 356 * 357 * exact or average bitrate in bits/s (unsigned integer) 358 */ 359 {GST_TAG_BITRATE, EXTRACTOR_METATYPE_BITRATE}, 360 361 /** 362 * GST_TAG_NOMINAL_BITRATE: 363 * 364 * nominal bitrate in bits/s (unsigned integer). The actual bitrate might be 365 * different from this target bitrate. 366 */ 367 {GST_TAG_NOMINAL_BITRATE, EXTRACTOR_METATYPE_NOMINAL_BITRATE}, 368 369 /** 370 * GST_TAG_MINIMUM_BITRATE: 371 * 372 * minimum bitrate in bits/s (unsigned integer) 373 */ 374 {GST_TAG_MINIMUM_BITRATE, EXTRACTOR_METATYPE_MINIMUM_BITRATE}, 375 376 /** 377 * GST_TAG_MAXIMUM_BITRATE: 378 * 379 * maximum bitrate in bits/s (unsigned integer) 380 */ 381 {GST_TAG_MAXIMUM_BITRATE, EXTRACTOR_METATYPE_MAXIMUM_BITRATE}, 382 383 /** 384 * GST_TAG_SERIAL: 385 * 386 * serial number of track (unsigned integer) 387 */ 388 {GST_TAG_SERIAL, EXTRACTOR_METATYPE_SERIAL}, 389 390 /** 391 * GST_TAG_ENCODER: 392 * 393 * encoder used to encode this stream (string) 394 */ 395 {GST_TAG_ENCODER, EXTRACTOR_METATYPE_ENCODER}, /* New */ 396 397 /** 398 * GST_TAG_ENCODER_VERSION: 399 * 400 * version of the encoder used to encode this stream (unsigned integer) 401 */ 402 {GST_TAG_ENCODER_VERSION, EXTRACTOR_METATYPE_ENCODER_VERSION}, 403 404 /** 405 * GST_TAG_TRACK_GAIN: 406 * 407 * track gain in db (double) 408 */ 409 {GST_TAG_TRACK_GAIN, EXTRACTOR_METATYPE_TRACK_GAIN}, 410 411 /** 412 * GST_TAG_TRACK_PEAK: 413 * 414 * peak of the track (double) 415 */ 416 {GST_TAG_TRACK_PEAK, EXTRACTOR_METATYPE_TRACK_PEAK}, 417 418 /** 419 * GST_TAG_ALBUM_GAIN: 420 * 421 * album gain in db (double) 422 */ 423 {GST_TAG_ALBUM_GAIN, EXTRACTOR_METATYPE_ALBUM_GAIN}, 424 425 /** 426 * GST_TAG_ALBUM_PEAK: 427 * 428 * peak of the album (double) 429 */ 430 {GST_TAG_ALBUM_PEAK, EXTRACTOR_METATYPE_ALBUM_PEAK}, 431 432 /** 433 * GST_TAG_REFERENCE_LEVEL: 434 * 435 * reference level of track and album gain values (double) 436 */ 437 {GST_TAG_REFERENCE_LEVEL, EXTRACTOR_METATYPE_REFERENCE_LEVEL}, 438 439 /** 440 * GST_TAG_LANGUAGE_CODE: 441 * 442 * ISO-639-2 or ISO-639-1 code for the language the content is in (string) 443 * 444 * There is utility API in libgsttag in gst-plugins-base to obtain a translated 445 * language name from the language code: gst_tag_get_language_name() 446 */ 447 {GST_TAG_LANGUAGE_CODE, EXTRACTOR_METATYPE_LANGUAGE}, 448 449 /** 450 * GST_TAG_LANGUAGE_NAME: 451 * 452 * Name of the language the content is in (string) 453 * 454 * Free-form name of the language the content is in, if a language code 455 * is not available. This tag should not be set in addition to a language 456 * code. It is undefined what language or locale the language name is in. 457 */ 458 {GST_TAG_LANGUAGE_NAME, EXTRACTOR_METATYPE_LANGUAGE}, 459 460 /** 461 * GST_TAG_IMAGE: 462 * 463 * image (sample) (sample taglist should specify the content type and preferably 464 * also set "image-type" field as #GstTagImageType) 465 */ 466 {GST_TAG_IMAGE, EXTRACTOR_METATYPE_PICTURE}, 467 468 /** 469 * GST_TAG_PREVIEW_IMAGE: 470 * 471 * image that is meant for preview purposes, e.g. small icon-sized version 472 * (sample) (sample taglist should specify the content type) 473 */ 474 {GST_TAG_IMAGE, EXTRACTOR_METATYPE_THUMBNAIL}, 475 476 /** 477 * GST_TAG_ATTACHMENT: 478 * 479 * generic file attachment (sample) (sample taglist should specify the content 480 * type and if possible set "filename" to the file name of the 481 * attachment) 482 */ 483 /* No equivalent, and none needed? */ 484 485 /** 486 * GST_TAG_BEATS_PER_MINUTE: 487 * 488 * number of beats per minute in audio (double) 489 */ 490 {GST_TAG_BEATS_PER_MINUTE, EXTRACTOR_METATYPE_BEATS_PER_MINUTE}, 491 492 /** 493 * GST_TAG_KEYWORDS: 494 * 495 * comma separated keywords describing the content (string). 496 */ 497 {GST_TAG_KEYWORDS, EXTRACTOR_METATYPE_KEYWORDS}, 498 499 /** 500 * GST_TAG_GEO_LOCATION_NAME: 501 * 502 * human readable descriptive location of where the media has been recorded or 503 * produced. (string). 504 */ 505 {GST_TAG_GEO_LOCATION_NAME, EXTRACTOR_METATYPE_LOCATION_NAME}, 506 507 /** 508 * GST_TAG_GEO_LOCATION_LATITUDE: 509 * 510 * geo latitude location of where the media has been recorded or produced in 511 * degrees according to WGS84 (zero at the equator, negative values for southern 512 * latitudes) (double). 513 */ 514 {GST_TAG_GEO_LOCATION_LATITUDE, EXTRACTOR_METATYPE_GPS_LATITUDE}, 515 516 /** 517 * GST_TAG_GEO_LOCATION_LONGITUDE: 518 * 519 * geo longitude location of where the media has been recorded or produced in 520 * degrees according to WGS84 (zero at the prime meridian in Greenwich/UK, 521 * negative values for western longitudes). (double). 522 */ 523 {GST_TAG_GEO_LOCATION_LONGITUDE, EXTRACTOR_METATYPE_GPS_LONGITUDE}, 524 525 /** 526 * GST_TAG_GEO_LOCATION_ELEVATION: 527 * 528 * geo elevation of where the media has been recorded or produced in meters 529 * according to WGS84 (zero is average sea level) (double). 530 */ 531 {GST_TAG_GEO_LOCATION_ELEVATION, EXTRACTOR_METATYPE_LOCATION_ELEVATION}, 532 533 /** 534 * GST_TAG_GEO_LOCATION_COUNTRY: 535 * 536 * The country (english name) where the media has been produced (string). 537 */ 538 {GST_TAG_GEO_LOCATION_COUNTRY, EXTRACTOR_METATYPE_LOCATION_COUNTRY}, 539 540 /** 541 * GST_TAG_GEO_LOCATION_CITY: 542 * 543 * The city (english name) where the media has been produced (string). 544 */ 545 {GST_TAG_GEO_LOCATION_CITY, EXTRACTOR_METATYPE_LOCATION_CITY}, 546 547 /** 548 * GST_TAG_GEO_LOCATION_SUBLOCATION: 549 * 550 * A location 'smaller' than GST_TAG_GEO_LOCATION_CITY that specifies better 551 * where the media has been produced. (e.g. the neighborhood) (string). 552 * 553 * This tag has been added as this is how it is handled/named in XMP's 554 * Iptc4xmpcore schema. 555 */ 556 {GST_TAG_GEO_LOCATION_SUBLOCATION, EXTRACTOR_METATYPE_LOCATION_SUBLOCATION}, 557 558 /** 559 * GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR: 560 * 561 * Represents the expected error on the horizontal positioning in 562 * meters (double). 563 */ 564 {GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR, 565 EXTRACTOR_METATYPE_LOCATION_HORIZONTAL_ERROR}, 566 567 /** 568 * GST_TAG_GEO_LOCATION_MOVEMENT_SPEED: 569 * 570 * Speed of the capturing device when performing the capture. 571 * Represented in m/s. (double) 572 * 573 * See also #GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION 574 */ 575 {GST_TAG_GEO_LOCATION_MOVEMENT_SPEED, 576 EXTRACTOR_METATYPE_LOCATION_MOVEMENT_SPEED}, 577 578 /** 579 * GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION: 580 * 581 * Indicates the movement direction of the device performing the capture 582 * of a media. It is represented as degrees in floating point representation, 583 * 0 means the geographic north, and increases clockwise (double from 0 to 360) 584 * 585 * See also #GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION 586 */ 587 {GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION, 588 EXTRACTOR_METATYPE_LOCATION_MOVEMENT_DIRECTION}, 589 590 /** 591 * GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION: 592 * 593 * Indicates the direction the device is pointing to when capturing 594 * a media. It is represented as degrees in floating point representation, 595 * 0 means the geographic north, and increases clockwise (double from 0 to 360) 596 * 597 * See also #GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION 598 */ 599 {GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION, 600 EXTRACTOR_METATYPE_LOCATION_CAPTURE_DIRECTION}, 601 602 /** 603 * GST_TAG_SHOW_NAME: 604 * 605 * Name of the show, used for displaying (string) 606 */ 607 {GST_TAG_SHOW_NAME, EXTRACTOR_METATYPE_SHOW_NAME}, 608 609 /** 610 * GST_TAG_SHOW_SORTNAME: 611 * 612 * Name of the show, used for sorting (string) 613 */ 614 {GST_TAG_SHOW_SORTNAME, EXTRACTOR_METATYPE_SHOW_NAME}, 615 616 /** 617 * GST_TAG_SHOW_EPISODE_NUMBER: 618 * 619 * Number of the episode within a season/show (unsigned integer) 620 */ 621 {GST_TAG_SHOW_EPISODE_NUMBER, EXTRACTOR_METATYPE_SHOW_EPISODE_NUMBER}, 622 623 /** 624 * GST_TAG_SHOW_SEASON_NUMBER: 625 * 626 * Number of the season of a show/series (unsigned integer) 627 */ 628 {GST_TAG_SHOW_SEASON_NUMBER, EXTRACTOR_METATYPE_SHOW_SEASON_NUMBER}, 629 630 /** 631 * GST_TAG_LYRICS: 632 * 633 * The lyrics of the media (string) 634 */ 635 {GST_TAG_LYRICS, EXTRACTOR_METATYPE_LYRICS}, 636 637 /** 638 * GST_TAG_COMPOSER_SORTNAME: 639 * 640 * The composer's name, used for sorting (string) 641 */ 642 {GST_TAG_COMPOSER_SORTNAME, EXTRACTOR_METATYPE_COMPOSER}, 643 644 /** 645 * GST_TAG_GROUPING: 646 * 647 * Groups together media that are related and spans multiple tracks. An 648 * example are multiple pieces of a concerto. (string) 649 */ 650 {GST_TAG_GROUPING, EXTRACTOR_METATYPE_GROUPING}, 651 652 /** 653 * GST_TAG_USER_RATING: 654 * 655 * Rating attributed by a person (likely the application user). 656 * The higher the value, the more the user likes this media 657 * (unsigned int from 0 to 100) 658 */ 659 {GST_TAG_USER_RATING, EXTRACTOR_METATYPE_POPULARITY_METER}, 660 661 /** 662 * GST_TAG_DEVICE_MANUFACTURER: 663 * 664 * Manufacturer of the device used to create the media (string) 665 */ 666 {GST_TAG_DEVICE_MANUFACTURER, EXTRACTOR_METATYPE_DEVICE_MANUFACTURER}, 667 668 /** 669 * GST_TAG_DEVICE_MODEL: 670 * 671 * Model of the device used to create the media (string) 672 */ 673 {GST_TAG_DEVICE_MODEL, EXTRACTOR_METATYPE_DEVICE_MODEL}, 674 675 /** 676 * GST_TAG_APPLICATION_NAME: 677 * 678 * Name of the application used to create the media (string) 679 */ 680 {GST_TAG_APPLICATION_NAME, EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE}, 681 682 /** 683 * GST_TAG_APPLICATION_DATA: 684 * 685 * Arbitrary application data (sample) 686 * 687 * Some formats allow applications to add their own arbitrary data 688 * into files. This data is application dependent. 689 */ 690 /* No equivalent, and none needed (not really metadata)? */ 691 692 /** 693 * GST_TAG_IMAGE_ORIENTATION: 694 * 695 * Represents the 'Orientation' tag from EXIF. Defines how the image 696 * should be rotated and mirrored for display. (string) 697 * 698 * This tag has a predefined set of allowed values: 699 * "rotate-0" 700 * "rotate-90" 701 * "rotate-180" 702 * "rotate-270" 703 * "flip-rotate-0" 704 * "flip-rotate-90" 705 * "flip-rotate-180" 706 * "flip-rotate-270" 707 * 708 * The naming is adopted according to a possible transformation to perform 709 * on the image to fix its orientation, obviously equivalent operations will 710 * yield the same result. 711 * 712 * Rotations indicated by the values are in clockwise direction and 713 * 'flip' means an horizontal mirroring. 714 */ 715 {GST_TAG_IMAGE_ORIENTATION, EXTRACTOR_METATYPE_ORIENTATION} 716 717 }; 718 719 720 /** 721 * Struct mapping named tags (that don't occur in GST API) to LE tags. 722 */ 723 struct NamedTag 724 { 725 /** 726 * tag. 727 */ 728 const char *tag; 729 730 /** 731 * Corresponding LE tag. 732 */ 733 enum EXTRACTOR_MetaType le_type; 734 }; 735 736 737 /** 738 * Mapping from GST tag names to LE types for tags that are not in 739 * the public GST API. 740 */ 741 struct NamedTag named_tags[] = { 742 { "FPS", EXTRACTOR_METATYPE_FRAME_RATE }, 743 { "PLAY_COUNTER", EXTRACTOR_METATYPE_PLAY_COUNTER }, 744 { "RATING", EXTRACTOR_METATYPE_RATING }, 745 { "SUMMARY", EXTRACTOR_METATYPE_SUMMARY }, 746 { "SUBJECT", EXTRACTOR_METATYPE_SUBJECT }, 747 { "MOOD", EXTRACTOR_METATYPE_MOOD }, 748 { "LEAD_PERFORMER", EXTRACTOR_METATYPE_PERFORMER }, 749 { "DIRECTOR", EXTRACTOR_METATYPE_MOVIE_DIRECTOR }, 750 { "WRITTEN_BY", EXTRACTOR_METATYPE_WRITER }, 751 { "PRODUCER", EXTRACTOR_METATYPE_PRODUCER }, 752 { "PUBLISHER", EXTRACTOR_METATYPE_PUBLISHER }, 753 { "ORIGINAL/ARTIST", EXTRACTOR_METATYPE_ORIGINAL_ARTIST }, 754 { "ORIGINAL/TITLE", EXTRACTOR_METATYPE_ORIGINAL_TITLE }, 755 { NULL, EXTRACTOR_METATYPE_UNKNOWN } 756 }; 757 758 759 /** 760 * 761 */ 762 enum CurrentStreamType 763 { 764 /** 765 * 766 */ 767 STREAM_TYPE_NONE = 0, 768 769 /** 770 * 771 */ 772 STREAM_TYPE_AUDIO = 1, 773 774 /** 775 * 776 */ 777 STREAM_TYPE_VIDEO = 2, 778 779 /** 780 * 781 */ 782 STREAM_TYPE_SUBTITLE = 3, 783 784 /** 785 * 786 */ 787 STREAM_TYPE_CONTAINER = 4, 788 789 /** 790 * 791 */ 792 STREAM_TYPE_IMAGE = 5 793 }; 794 795 796 /** 797 * Closure we pass when processing a request. 798 */ 799 struct PrivStruct 800 { 801 /** 802 * Current read-offset in the 'ec' context (based on our read/seek calls). 803 */ 804 guint64 offset; 805 806 /** 807 * Overall size of the file we're processing, UINT64_MAX if unknown. 808 */ 809 uint64_t length; 810 811 /** 812 * 813 */ 814 GstElement *source; 815 816 /** 817 * Extraction context for IO on the underlying data. 818 */ 819 struct EXTRACTOR_ExtractContext *ec; 820 821 /** 822 * Glib main loop. 823 */ 824 GMainLoop *loop; 825 826 /** 827 * Discoverer object we are using. 828 */ 829 GstDiscoverer *dc; 830 831 /** 832 * Location for building the XML 'table of contents' (EXTRACTOR_METATYPE_TOC) for 833 * the input. Used only during 'send_info'. 834 */ 835 gchar *toc; 836 837 /** 838 * Length of the 'toc' string. 839 */ 840 size_t toc_length; 841 842 /** 843 * Current position (used when creating the 'toc' string). 844 */ 845 size_t toc_pos; 846 847 /** 848 * Identifier of the timeout event source 849 */ 850 guint timeout_id; 851 852 /** 853 * Counter used to determine our current depth in the TOC hierarchy. 854 */ 855 int toc_depth; 856 857 /** 858 * 859 */ 860 enum CurrentStreamType st; 861 862 /** 863 * Last return value from the meta data processor. Set to 864 * 1 to abort, 0 to continue extracting. 865 */ 866 int time_to_leave; 867 868 /** 869 * TOC generation is executed in two phases. First phase determines 870 * the size of the string and the second phase actually does the 871 * 'printing' (string construction). This bit is TRUE if we are 872 * in the 'printing' phase. 873 */ 874 gboolean toc_print_phase; 875 876 }; 877 878 879 /** 880 * 881 */ 882 static GQuark *audio_quarks; 883 884 /** 885 * 886 */ 887 static GQuark *video_quarks; 888 889 /** 890 * 891 */ 892 static GQuark *subtitle_quarks; 893 894 /** 895 * 896 */ 897 static GQuark duration_quark; 898 899 900 static gboolean 901 _data_timeout (struct PrivStruct *ps) 902 { 903 GST_ERROR ("GstDiscoverer I/O timed out"); 904 ps->timeout_id = 0; 905 g_main_loop_quit (ps->loop); 906 return FALSE; 907 } 908 909 910 /** 911 * Implementation of GstElement's "need-data" callback. Reads data from 912 * the extraction context and passes it to GStreamer. 913 * 914 * @param appsrc the GstElement for which we are implementing "need-data" 915 * @param size number of bytes requested 916 * @param ps our execution context 917 */ 918 static void 919 feed_data (GstElement *appsrc, 920 guint size, 921 struct PrivStruct *ps) 922 { 923 ssize_t data_len; 924 uint8_t *le_data; 925 guint accumulated; 926 GstMemory *mem; 927 GstMapInfo mi; 928 GstBuffer *buffer; 929 930 GST_DEBUG ("Request %u bytes", size); 931 932 if (ps->timeout_id > 0) 933 g_source_remove (ps->timeout_id); 934 ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout, 935 ps); 936 937 if ( (ps->length > 0) && (ps->offset >= ps->length) ) 938 { 939 /* we are at the EOS, send end-of-stream */ 940 gst_app_src_end_of_stream (GST_APP_SRC (ps->source)); 941 return; 942 } 943 944 if ((ps->length > 0) && (ps->offset + size > ps->length)) 945 size = ps->length - ps->offset; 946 947 mem = gst_allocator_alloc (NULL, size, NULL); 948 if (! gst_memory_map (mem, &mi, GST_MAP_WRITE)) 949 { 950 gst_memory_unref (mem); 951 GST_DEBUG ("Failed to map the memory"); 952 gst_app_src_end_of_stream (GST_APP_SRC (ps->source)); 953 return; 954 } 955 956 accumulated = 0; 957 data_len = 1; 958 pthread_mutex_lock (&pipe_mutex); 959 while ( (accumulated < size) && (data_len > 0) ) 960 { 961 data_len = ps->ec->read (ps->ec->cls, (void **) &le_data, size 962 - accumulated); 963 if (data_len > 0) 964 { 965 memcpy (&mi.data[accumulated], le_data, data_len); 966 accumulated += data_len; 967 } 968 } 969 pthread_mutex_unlock (&pipe_mutex); 970 gst_memory_unmap (mem, &mi); 971 if (size == accumulated) 972 { 973 buffer = gst_buffer_new (); 974 gst_buffer_append_memory (buffer, mem); 975 976 /* we need to set an offset for random access */ 977 GST_BUFFER_OFFSET (buffer) = ps->offset; 978 GST_BUFFER_OFFSET_END (buffer) = ps->offset + size; 979 980 GST_DEBUG ("feed buffer %p, offset %" G_GUINT64_FORMAT "-%u", 981 buffer, ps->offset, size); 982 gst_app_src_push_buffer (GST_APP_SRC (ps->source), buffer); 983 ps->offset += size; 984 } 985 else 986 { 987 gst_memory_unref (mem); 988 gst_app_src_end_of_stream (GST_APP_SRC (ps->source)); 989 ps->offset = UINT64_MAX; /* set to invalid value */ 990 } 991 992 if (ps->timeout_id > 0) 993 g_source_remove (ps->timeout_id); 994 ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout, 995 ps); 996 } 997 998 999 /** 1000 * Implementation of GstElement's "seek-data" callback. Seeks to a new 1001 * position in the extraction context. 1002 * 1003 * @param appsrc the GstElement for which we are implementing "need-data" 1004 * @param position new desired absolute position in the file 1005 * @param ps our execution context 1006 * @return TRUE if seeking succeeded, FALSE if not 1007 */ 1008 static gboolean 1009 seek_data (GstElement *appsrc, 1010 guint64 position, 1011 struct PrivStruct *ps) 1012 { 1013 GST_DEBUG ("seek to offset %" G_GUINT64_FORMAT, position); 1014 pthread_mutex_lock (&pipe_mutex); 1015 ps->offset = ps->ec->seek (ps->ec->cls, position, SEEK_SET); 1016 pthread_mutex_unlock (&pipe_mutex); 1017 if (ps->timeout_id > 0) 1018 g_source_remove (ps->timeout_id); 1019 ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout, 1020 ps); 1021 return ps->offset == position; 1022 } 1023 1024 1025 /** 1026 * FIXME 1027 * 1028 * @param field_id FIXME 1029 * @param value FIXME 1030 * @param user_data our 'struct PrivStruct' 1031 * @return TRUE to continue processing, FALSE to abort 1032 */ 1033 static gboolean 1034 send_structure_foreach (GQuark field_id, 1035 const GValue *value, 1036 gpointer user_data) 1037 { 1038 struct PrivStruct *ps = user_data; 1039 gchar *str; 1040 const gchar *field_name = g_quark_to_string (field_id); 1041 GType gst_fraction = GST_TYPE_FRACTION; 1042 GQuark *quark; 1043 1044 switch (ps->st) 1045 { 1046 case STREAM_TYPE_AUDIO: 1047 if (NULL == audio_quarks) 1048 return FALSE; 1049 for (quark = audio_quarks; *quark != 0; quark++) 1050 if (*quark == field_id) 1051 return TRUE; 1052 break; 1053 case STREAM_TYPE_VIDEO: 1054 case STREAM_TYPE_IMAGE: 1055 if (NULL == video_quarks) 1056 return FALSE; 1057 for (quark = video_quarks; *quark != 0; quark++) 1058 if (*quark == field_id) 1059 return TRUE; 1060 break; 1061 case STREAM_TYPE_SUBTITLE: 1062 if (NULL == subtitle_quarks) 1063 return FALSE; 1064 for (quark = subtitle_quarks; *quark != 0; quark++) 1065 if (*quark == field_id) 1066 return TRUE; 1067 break; 1068 case STREAM_TYPE_CONTAINER: 1069 case STREAM_TYPE_NONE: 1070 break; 1071 } 1072 1073 switch (G_VALUE_TYPE (value)) 1074 { 1075 case G_TYPE_STRING: 1076 str = g_value_dup_string (value); 1077 break; 1078 case G_TYPE_UINT: 1079 case G_TYPE_INT: 1080 case G_TYPE_DOUBLE: 1081 case G_TYPE_BOOLEAN: 1082 str = gst_value_serialize (value); 1083 break; 1084 default: 1085 if (G_VALUE_TYPE (value) == gst_fraction) 1086 { 1087 str = gst_value_serialize (value); 1088 break; 1089 } 1090 /* This is a potential source of invalid characters */ 1091 /* And it also might attempt to serialize binary data - such as images. */ 1092 str = gst_value_serialize (value); 1093 if (NULL != str) 1094 { 1095 g_free (str); 1096 str = NULL; 1097 } 1098 break; 1099 } 1100 if (NULL != str) 1101 { 1102 unsigned int i; 1103 1104 for (i = 0; NULL != named_tags[i].tag; i++) 1105 if (0 == strcmp (named_tags[i].tag, field_name)) 1106 { 1107 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer", 1108 named_tags[i].le_type, 1109 EXTRACTOR_METAFORMAT_UTF8, 1110 "text/plain", 1111 (const char *) str, strlen (str) + 1); 1112 if (NULL != str) 1113 { 1114 g_free (str); 1115 str = NULL; 1116 } 1117 break; 1118 } 1119 } 1120 if (NULL != str) 1121 { 1122 gchar *senddata = g_strdup_printf ("%s=%s", 1123 field_name, 1124 str); 1125 if (NULL != senddata) 1126 { 1127 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1128 "gstreamer", 1129 EXTRACTOR_METATYPE_UNKNOWN, 1130 EXTRACTOR_METAFORMAT_UTF8, 1131 "text/plain", 1132 (const char *) senddata, 1133 strlen (senddata) + 1); 1134 g_free (senddata); 1135 } 1136 } 1137 if (NULL != str) 1138 g_free (str); 1139 1140 return ! ps->time_to_leave; 1141 } 1142 1143 1144 /** 1145 * FIXME 1146 * 1147 * @param info FIXME 1148 * @param ps processing context 1149 * @return FALSE to continue processing, TRUE to abort 1150 */ 1151 static gboolean 1152 send_audio_info (GstDiscovererAudioInfo *info, 1153 struct PrivStruct *ps) 1154 { 1155 gchar *tmp; 1156 const gchar *ctmp; 1157 guint u; 1158 1159 ctmp = gst_discoverer_audio_info_get_language (info); 1160 if (ctmp) 1161 if ((ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer", 1162 EXTRACTOR_METATYPE_AUDIO_LANGUAGE, 1163 EXTRACTOR_METAFORMAT_UTF8, 1164 "text/plain", 1165 (const char *) ctmp, strlen (ctmp) 1166 + 1))) 1167 return TRUE; 1168 1169 u = gst_discoverer_audio_info_get_channels (info); 1170 if (u > 0) 1171 { 1172 tmp = g_strdup_printf ("%u", u); 1173 if (NULL != tmp) 1174 { 1175 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1176 "gstreamer", 1177 EXTRACTOR_METATYPE_CHANNELS, 1178 EXTRACTOR_METAFORMAT_UTF8, 1179 "text/plain", 1180 (const char *) tmp, 1181 strlen (tmp) + 1); 1182 g_free (tmp); 1183 } 1184 if (ps->time_to_leave) 1185 return TRUE; 1186 } 1187 1188 u = gst_discoverer_audio_info_get_sample_rate (info); 1189 if (u > 0) 1190 { 1191 tmp = g_strdup_printf ("%u", u); 1192 if (NULL != tmp) 1193 { 1194 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1195 "gstreamer", 1196 EXTRACTOR_METATYPE_SAMPLE_RATE, 1197 EXTRACTOR_METAFORMAT_UTF8, 1198 "text/plain", 1199 (const char *) tmp, 1200 strlen (tmp) + 1); 1201 g_free (tmp); 1202 } 1203 if (ps->time_to_leave) 1204 return TRUE; 1205 } 1206 1207 u = gst_discoverer_audio_info_get_depth (info); 1208 if (u > 0) 1209 { 1210 tmp = g_strdup_printf ("%u", u); 1211 if (NULL != tmp) 1212 { 1213 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1214 "gstreamer", 1215 EXTRACTOR_METATYPE_AUDIO_DEPTH, 1216 EXTRACTOR_METAFORMAT_UTF8, 1217 "text/plain", 1218 (const char *) tmp, 1219 strlen (tmp) + 1); 1220 g_free (tmp); 1221 } 1222 if (ps->time_to_leave) 1223 return TRUE; 1224 } 1225 1226 u = gst_discoverer_audio_info_get_bitrate (info); 1227 if (u > 0) 1228 { 1229 tmp = g_strdup_printf ("%u", u); 1230 if (NULL != tmp) 1231 { 1232 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1233 "gstreamer", 1234 EXTRACTOR_METATYPE_AUDIO_BITRATE, 1235 EXTRACTOR_METAFORMAT_UTF8, 1236 "text/plain", 1237 (const char *) tmp, 1238 strlen (tmp) + 1); 1239 g_free (tmp); 1240 } 1241 if (ps->time_to_leave) 1242 return TRUE; 1243 } 1244 1245 u = gst_discoverer_audio_info_get_max_bitrate (info); 1246 if (u > 0) 1247 { 1248 tmp = g_strdup_printf ("%u", u); 1249 if (NULL != tmp) 1250 { 1251 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1252 "gstreamer", 1253 EXTRACTOR_METATYPE_MAXIMUM_AUDIO_BITRATE, 1254 EXTRACTOR_METAFORMAT_UTF8, 1255 "text/plain", 1256 (const char *) tmp, 1257 strlen (tmp) + 1); 1258 g_free (tmp); 1259 } 1260 if (ps->time_to_leave) 1261 return TRUE; 1262 } 1263 1264 return FALSE; 1265 } 1266 1267 1268 /** 1269 * FIXME 1270 * 1271 * @param info FIXME 1272 * @param ps processing context 1273 * @return FALSE to continue processing, TRUE to abort 1274 */ 1275 static int 1276 send_video_info (GstDiscovererVideoInfo *info, 1277 struct PrivStruct *ps) 1278 { 1279 gchar *tmp; 1280 guint u; 1281 guint u2; 1282 1283 u = gst_discoverer_video_info_get_width (info); 1284 u2 = gst_discoverer_video_info_get_height (info); 1285 if ( (u > 0) && (u2 > 0) ) 1286 { 1287 tmp = g_strdup_printf ("%ux%u", u, u2); 1288 if (NULL != tmp) 1289 { 1290 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1291 "gstreamer", 1292 EXTRACTOR_METATYPE_VIDEO_DIMENSIONS, 1293 EXTRACTOR_METAFORMAT_UTF8, 1294 "text/plain", 1295 (const char *) tmp, 1296 strlen (tmp) + 1); 1297 g_free (tmp); 1298 } 1299 if (ps->time_to_leave) 1300 return TRUE; 1301 } 1302 1303 u = gst_discoverer_video_info_get_depth (info); 1304 if (u > 0) 1305 { 1306 tmp = g_strdup_printf ("%u", u); 1307 if (NULL != tmp) 1308 { 1309 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1310 "gstreamer", 1311 EXTRACTOR_METATYPE_VIDEO_DEPTH, 1312 EXTRACTOR_METAFORMAT_UTF8, 1313 "text/plain", 1314 (const char *) tmp, 1315 strlen (tmp) + 1); 1316 g_free (tmp); 1317 } 1318 if (ps->time_to_leave) 1319 return TRUE; 1320 } 1321 1322 u = gst_discoverer_video_info_get_framerate_num (info); 1323 u2 = gst_discoverer_video_info_get_framerate_denom (info); 1324 if ( (u > 0) && (u2 > 0) ) 1325 { 1326 tmp = g_strdup_printf ("%u/%u", u, u2); 1327 if (NULL != tmp) 1328 { 1329 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1330 "gstreamer", 1331 EXTRACTOR_METATYPE_FRAME_RATE, 1332 EXTRACTOR_METAFORMAT_UTF8, 1333 "text/plain", 1334 (const char *) tmp, 1335 strlen (tmp) + 1); 1336 g_free (tmp); 1337 } 1338 if (ps->time_to_leave) 1339 return TRUE; 1340 } 1341 1342 u = gst_discoverer_video_info_get_par_num (info); 1343 u2 = gst_discoverer_video_info_get_par_denom (info); 1344 if ( (u > 0) && (u2 > 0) ) 1345 { 1346 tmp = g_strdup_printf ("%u/%u", u, u2); 1347 if (NULL != tmp) 1348 { 1349 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1350 "gstreamer", 1351 EXTRACTOR_METATYPE_PIXEL_ASPECT_RATIO, 1352 EXTRACTOR_METAFORMAT_UTF8, 1353 "text/plain", 1354 (const char *) tmp, 1355 strlen (tmp) + 1); 1356 g_free (tmp); 1357 } 1358 if (ps->time_to_leave) 1359 return TRUE; 1360 } 1361 1362 /* gst_discoverer_video_info_is_interlaced (info) I don't trust it... */ 1363 1364 u = gst_discoverer_video_info_get_bitrate (info); 1365 if (u > 0) 1366 { 1367 tmp = g_strdup_printf ("%u", u); 1368 if (NULL != tmp) 1369 { 1370 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1371 "gstreamer", 1372 EXTRACTOR_METATYPE_VIDEO_BITRATE, 1373 EXTRACTOR_METAFORMAT_UTF8, 1374 "text/plain", 1375 (const char *) tmp, 1376 strlen (tmp) + 1); 1377 g_free (tmp); 1378 } 1379 if (ps->time_to_leave) 1380 return TRUE; 1381 } 1382 1383 u = gst_discoverer_video_info_get_max_bitrate (info); 1384 if (u > 0) 1385 { 1386 tmp = g_strdup_printf ("%u", u); 1387 if (NULL != tmp) 1388 { 1389 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1390 "gstreamer", 1391 EXTRACTOR_METATYPE_MAXIMUM_VIDEO_BITRATE, 1392 EXTRACTOR_METAFORMAT_UTF8, 1393 "text/plain", 1394 (const char *) tmp, 1395 strlen (tmp) + 1); 1396 g_free (tmp); 1397 } 1398 if (ps->time_to_leave) 1399 return TRUE; 1400 } 1401 1402 return FALSE; 1403 } 1404 1405 1406 /** 1407 * FIXME 1408 * 1409 * @param info FIXME 1410 * @param ps processing context 1411 * @return FALSE to continue processing, TRUE to abort 1412 */ 1413 static int 1414 send_subtitle_info (GstDiscovererSubtitleInfo *info, 1415 struct PrivStruct *ps) 1416 { 1417 const gchar *ctmp; 1418 1419 ctmp = gst_discoverer_subtitle_info_get_language (info); 1420 if ( (NULL != ctmp) && 1421 (0 != (ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer", 1422 EXTRACTOR_METATYPE_SUBTITLE_LANGUAGE, 1423 EXTRACTOR_METAFORMAT_UTF8, 1424 "text/plain", 1425 (const char *) ctmp, strlen ( 1426 ctmp) + 1))) ) 1427 return TRUE; 1428 return FALSE; 1429 } 1430 1431 1432 static void 1433 send_tag_foreach (const GstTagList *tags, 1434 const gchar *tag, 1435 gpointer user_data) 1436 { 1437 static struct KnownTag unknown_tag = {NULL, EXTRACTOR_METATYPE_UNKNOWN}; 1438 struct PrivStruct *ps = user_data; 1439 size_t i; 1440 size_t tagl = sizeof (__known_tags) / sizeof (struct KnownTag); 1441 const struct KnownTag *kt = NULL; 1442 GQuark tag_quark; 1443 guint vallen; 1444 GstSample *sample; 1445 1446 if (ps->time_to_leave) 1447 return; 1448 1449 for (i = 0; i < tagl; i++) 1450 { 1451 if (strcmp (__known_tags[i].gst_tag_id, tag) != 0) 1452 continue; 1453 kt = &__known_tags[i]; 1454 break; 1455 } 1456 if (kt == NULL) 1457 kt = &unknown_tag; 1458 1459 vallen = gst_tag_list_get_tag_size (tags, tag); 1460 if (vallen == 0) 1461 return; 1462 1463 tag_quark = g_quark_from_string (tag); 1464 1465 for (i = 0; i < vallen; i++) 1466 { 1467 GValue val = { 0, }; 1468 const GValue *val_ref; 1469 gchar *str = NULL; 1470 1471 val_ref = gst_tag_list_get_value_index (tags, tag, i); 1472 if (val_ref == NULL) 1473 { 1474 g_value_unset (&val); 1475 continue; 1476 } 1477 g_value_init (&val, G_VALUE_TYPE (val_ref)); 1478 g_value_copy (val_ref, &val); 1479 1480 switch (G_VALUE_TYPE (&val)) 1481 { 1482 case G_TYPE_STRING: 1483 str = g_value_dup_string (&val); 1484 break; 1485 case G_TYPE_UINT: 1486 case G_TYPE_INT: 1487 case G_TYPE_DOUBLE: 1488 case G_TYPE_BOOLEAN: 1489 str = gst_value_serialize (&val); 1490 break; 1491 default: 1492 if ((G_VALUE_TYPE (&val) == GST_TYPE_SAMPLE) && (sample = 1493 gst_value_get_sample ( 1494 &val))) 1495 { 1496 GstMapInfo mi; 1497 GstCaps *caps; 1498 1499 caps = gst_sample_get_caps (sample); 1500 if (caps) 1501 { 1502 GstTagImageType imagetype; 1503 const GstStructure *info; 1504 GstBuffer *buf; 1505 const gchar *mime_type; 1506 enum EXTRACTOR_MetaType le_type; 1507 1508 mime_type = gst_structure_get_name (gst_caps_get_structure (caps, 0)); 1509 info = gst_sample_get_info (sample); 1510 1511 if ( (NULL == info) || 1512 (! gst_structure_get (info, "image-type", 1513 GST_TYPE_TAG_IMAGE_TYPE, &imagetype, 1514 NULL)) ) 1515 le_type = EXTRACTOR_METATYPE_PICTURE; 1516 else 1517 { 1518 switch (imagetype) 1519 { 1520 case GST_TAG_IMAGE_TYPE_NONE: 1521 case GST_TAG_IMAGE_TYPE_UNDEFINED: 1522 case GST_TAG_IMAGE_TYPE_FISH: 1523 case GST_TAG_IMAGE_TYPE_ILLUSTRATION: 1524 default: 1525 le_type = EXTRACTOR_METATYPE_PICTURE; 1526 break; 1527 case GST_TAG_IMAGE_TYPE_FRONT_COVER: 1528 case GST_TAG_IMAGE_TYPE_BACK_COVER: 1529 case GST_TAG_IMAGE_TYPE_LEAFLET_PAGE: 1530 case GST_TAG_IMAGE_TYPE_MEDIUM: 1531 le_type = EXTRACTOR_METATYPE_COVER_PICTURE; 1532 break; 1533 case GST_TAG_IMAGE_TYPE_LEAD_ARTIST: 1534 case GST_TAG_IMAGE_TYPE_ARTIST: 1535 case GST_TAG_IMAGE_TYPE_CONDUCTOR: 1536 case GST_TAG_IMAGE_TYPE_BAND_ORCHESTRA: 1537 case GST_TAG_IMAGE_TYPE_COMPOSER: 1538 case GST_TAG_IMAGE_TYPE_LYRICIST: 1539 le_type = EXTRACTOR_METATYPE_CONTRIBUTOR_PICTURE; 1540 break; 1541 case GST_TAG_IMAGE_TYPE_RECORDING_LOCATION: 1542 case GST_TAG_IMAGE_TYPE_DURING_RECORDING: 1543 case GST_TAG_IMAGE_TYPE_DURING_PERFORMANCE: 1544 case GST_TAG_IMAGE_TYPE_VIDEO_CAPTURE: 1545 le_type = EXTRACTOR_METATYPE_EVENT_PICTURE; 1546 break; 1547 case GST_TAG_IMAGE_TYPE_BAND_ARTIST_LOGO: 1548 case GST_TAG_IMAGE_TYPE_PUBLISHER_STUDIO_LOGO: 1549 le_type = EXTRACTOR_METATYPE_LOGO; 1550 break; 1551 } 1552 } 1553 1554 buf = gst_sample_get_buffer (sample); 1555 gst_buffer_map (buf, &mi, GST_MAP_READ); 1556 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1557 "gstreamer", 1558 le_type, 1559 EXTRACTOR_METAFORMAT_BINARY, 1560 mime_type, 1561 (const char *) mi.data, mi.size); 1562 gst_buffer_unmap (buf, &mi); 1563 } 1564 } 1565 else if ((G_VALUE_TYPE (&val) == G_TYPE_UINT64) && 1566 (tag_quark == duration_quark)) 1567 { 1568 GstClockTime duration = (GstClockTime) g_value_get_uint64 (&val); 1569 if ((GST_CLOCK_TIME_IS_VALID (duration)) && (duration > 0)) 1570 str = g_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); 1571 } 1572 else 1573 str = gst_value_serialize (&val); 1574 break; 1575 } 1576 if (NULL != str) 1577 { 1578 /* Discoverer internally uses some tags to provide streaminfo; 1579 * ignore these tags to avoid duplicates. 1580 * This MIGHT be fixed in new GStreamer versions, but won't affect 1581 * this code (we simply won't get the tags that we think we should skip). 1582 */gboolean skip = FALSE; 1583 /* We have one tag-processing routine and use it for different 1584 * stream types. However, tags themselves don't know the type of the 1585 * stream they are attached to. We remember that before listing the 1586 * tags, and adjust LE type accordingly. 1587 */enum EXTRACTOR_MetaType le_type = kt->le_type; 1588 switch (kt->le_type) 1589 { 1590 case EXTRACTOR_METATYPE_LANGUAGE: 1591 switch (ps->st) 1592 { 1593 case STREAM_TYPE_AUDIO: 1594 skip = TRUE; 1595 break; 1596 case STREAM_TYPE_SUBTITLE: 1597 skip = TRUE; 1598 break; 1599 case STREAM_TYPE_VIDEO: 1600 le_type = EXTRACTOR_METATYPE_VIDEO_LANGUAGE; 1601 break; 1602 default: 1603 break; 1604 } 1605 break; 1606 case EXTRACTOR_METATYPE_BITRATE: 1607 switch (ps->st) 1608 { 1609 case STREAM_TYPE_AUDIO: 1610 skip = TRUE; 1611 break; 1612 case STREAM_TYPE_VIDEO: 1613 skip = TRUE; 1614 break; 1615 default: 1616 break; 1617 } 1618 break; 1619 case EXTRACTOR_METATYPE_MAXIMUM_BITRATE: 1620 switch (ps->st) 1621 { 1622 case STREAM_TYPE_AUDIO: 1623 skip = TRUE; 1624 break; 1625 case STREAM_TYPE_VIDEO: 1626 skip = TRUE; 1627 break; 1628 default: 1629 break; 1630 } 1631 break; 1632 case EXTRACTOR_METATYPE_NOMINAL_BITRATE: 1633 switch (ps->st) 1634 { 1635 case STREAM_TYPE_AUDIO: 1636 skip = TRUE; 1637 break; 1638 case STREAM_TYPE_VIDEO: 1639 skip = TRUE; 1640 break; 1641 default: 1642 break; 1643 } 1644 break; 1645 case EXTRACTOR_METATYPE_IMAGE_DIMENSIONS: 1646 switch (ps->st) 1647 { 1648 case STREAM_TYPE_VIDEO: 1649 le_type = EXTRACTOR_METATYPE_VIDEO_DIMENSIONS; 1650 break; 1651 default: 1652 break; 1653 } 1654 break; 1655 case EXTRACTOR_METATYPE_DURATION: 1656 switch (ps->st) 1657 { 1658 case STREAM_TYPE_VIDEO: 1659 le_type = EXTRACTOR_METATYPE_VIDEO_DURATION; 1660 break; 1661 case STREAM_TYPE_AUDIO: 1662 le_type = EXTRACTOR_METATYPE_AUDIO_DURATION; 1663 break; 1664 case STREAM_TYPE_SUBTITLE: 1665 le_type = EXTRACTOR_METATYPE_SUBTITLE_DURATION; 1666 break; 1667 default: 1668 break; 1669 } 1670 break; 1671 case EXTRACTOR_METATYPE_UNKNOWN: 1672 /* Convert to "key=value" form */ 1673 { 1674 gchar *new_str; 1675 /* GST_TAG_EXTENDED_COMMENT is already in key=value form */ 1676 if ((0 != strcmp (tag, "extended-comment")) || ! strchr (str, '=')) 1677 { 1678 new_str = g_strdup_printf ("%s=%s", tag, str); 1679 if (NULL != str) 1680 g_free (str); 1681 str = new_str; 1682 } 1683 } 1684 break; 1685 default: 1686 break; 1687 } 1688 if ( (! skip) && 1689 (NULL != str) ) 1690 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1691 "gstreamer", 1692 le_type, 1693 EXTRACTOR_METAFORMAT_UTF8, 1694 "text/plain", 1695 (const char *) str, 1696 strlen (str) + 1); 1697 } 1698 if (NULL != str) 1699 g_free (str); 1700 g_value_unset (&val); 1701 } 1702 } 1703 1704 1705 static void 1706 send_streams (GstDiscovererStreamInfo *info, 1707 struct PrivStruct *ps); 1708 1709 1710 static void 1711 send_stream_info (GstDiscovererStreamInfo *info, 1712 struct PrivStruct *ps) 1713 { 1714 const GstStructure *misc; 1715 GstCaps *caps; 1716 const GstTagList *tags; 1717 1718 caps = gst_discoverer_stream_info_get_caps (info); 1719 1720 if (GST_IS_DISCOVERER_AUDIO_INFO (info)) 1721 ps->st = STREAM_TYPE_AUDIO; 1722 else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) 1723 ps->st = STREAM_TYPE_VIDEO; 1724 else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) 1725 ps->st = STREAM_TYPE_SUBTITLE; 1726 else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) 1727 ps->st = STREAM_TYPE_CONTAINER; 1728 1729 if (caps) 1730 { 1731 GstStructure *structure = gst_caps_get_structure (caps, 0); 1732 const gchar *structname = gst_structure_get_name (structure); 1733 if (g_str_has_prefix (structname, "image/")) 1734 ps->st = STREAM_TYPE_IMAGE; 1735 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer", 1736 EXTRACTOR_METATYPE_MIMETYPE, 1737 EXTRACTOR_METAFORMAT_UTF8, "text/plain", 1738 (const char *) structname, strlen ( 1739 structname) + 1); 1740 if (! ps->time_to_leave) 1741 { 1742 gst_structure_foreach (structure, send_structure_foreach, ps); 1743 } 1744 gst_caps_unref (caps); 1745 } 1746 1747 if (ps->time_to_leave) 1748 { 1749 ps->st = STREAM_TYPE_NONE; 1750 return; 1751 } 1752 1753 misc = gst_discoverer_stream_info_get_misc (info); 1754 if (misc) 1755 { 1756 gst_structure_foreach (misc, send_structure_foreach, ps); 1757 } 1758 1759 if (ps->time_to_leave) 1760 { 1761 ps->st = STREAM_TYPE_NONE; 1762 return; 1763 } 1764 1765 tags = gst_discoverer_stream_info_get_tags (info); 1766 if (tags) 1767 { 1768 gst_tag_list_foreach (tags, send_tag_foreach, ps); 1769 } 1770 ps->st = STREAM_TYPE_NONE; 1771 1772 if (ps->time_to_leave) 1773 return; 1774 1775 if (GST_IS_DISCOVERER_AUDIO_INFO (info)) 1776 send_audio_info (GST_DISCOVERER_AUDIO_INFO (info), ps); 1777 else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) 1778 send_video_info (GST_DISCOVERER_VIDEO_INFO (info), ps); 1779 else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) 1780 send_subtitle_info (GST_DISCOVERER_SUBTITLE_INFO (info), ps); 1781 else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) 1782 { 1783 GList *child; 1784 GstDiscovererContainerInfo *c = GST_DISCOVERER_CONTAINER_INFO (info); 1785 GList *children = gst_discoverer_container_info_get_streams (c); 1786 for (child = children; (NULL != child) && (! ps->time_to_leave); 1787 child = child->next) 1788 { 1789 GstDiscovererStreamInfo *sinfo = child->data; 1790 /* send_streams () will unref it */ 1791 sinfo = gst_discoverer_stream_info_ref (sinfo); 1792 send_streams (sinfo, ps); 1793 } 1794 if (children) 1795 gst_discoverer_stream_info_list_free (children); 1796 } 1797 } 1798 1799 1800 static void 1801 send_streams (GstDiscovererStreamInfo *info, 1802 struct PrivStruct *ps) 1803 { 1804 GstDiscovererStreamInfo *next; 1805 1806 while ( (NULL != info) && (! ps->time_to_leave) ) 1807 { 1808 send_stream_info (info, ps); 1809 next = gst_discoverer_stream_info_get_next (info); 1810 gst_discoverer_stream_info_unref (info); 1811 info = next; 1812 } 1813 } 1814 1815 1816 /** 1817 * Callback used to construct the XML 'toc'. 1818 * 1819 * @param tags FIXME 1820 * @param tag FIXME 1821 * @param user_data the 'struct PrivStruct' with the 'toc' string we are assembling 1822 */ 1823 static void 1824 send_toc_tags_foreach (const GstTagList *tags, 1825 const gchar *tag, 1826 gpointer user_data) 1827 { 1828 struct PrivStruct *ps = user_data; 1829 GValue val = { 0 }; 1830 gchar *topen, *str, *tclose; 1831 GType gst_fraction = GST_TYPE_FRACTION; 1832 1833 gst_tag_list_copy_value (&val, tags, tag); 1834 switch (G_VALUE_TYPE (&val)) 1835 { 1836 case G_TYPE_STRING: 1837 str = g_value_dup_string (&val); 1838 break; 1839 case G_TYPE_UINT: 1840 case G_TYPE_INT: 1841 case G_TYPE_DOUBLE: 1842 str = gst_value_serialize (&val); 1843 break; 1844 default: 1845 if (G_VALUE_TYPE (&val) == gst_fraction) 1846 { 1847 str = gst_value_serialize (&val); 1848 break; 1849 } 1850 /* This is a potential source of invalid characters */ 1851 /* And it also might attempt to serialize binary data - such as images. */ 1852 str = gst_value_serialize (&val); 1853 if (NULL != str) 1854 { 1855 g_free (str); 1856 str = NULL; 1857 } 1858 break; 1859 } 1860 if (NULL != str) 1861 { 1862 topen = g_strdup_printf ("%*.*s<%s>", 1863 ps->toc_depth * 2, 1864 ps->toc_depth * 2, " ", tag); 1865 tclose = g_strdup_printf ("%*.*s</%s>\n", 1866 ps->toc_depth * 2, 1867 ps->toc_depth * 2, " ", 1868 tag); 1869 if ( (NULL != topen) && 1870 (NULL != tclose) ) 1871 { 1872 if (ps->toc_print_phase) 1873 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], 1874 ps->toc_length - ps->toc_pos, 1875 "%s%s%s", 1876 topen, 1877 str, 1878 tclose); 1879 else 1880 ps->toc_length += strlen (topen) + strlen (str) + strlen (tclose); 1881 } 1882 if (NULL != topen) 1883 g_free (topen); 1884 if (NULL != tclose) 1885 g_free (tclose); 1886 g_free (str); 1887 } 1888 g_value_unset (&val); 1889 } 1890 1891 1892 /** 1893 * Top-level callback used to construct the XML 'toc'. 1894 * 1895 * @param data the GstTocEntry we're processing 1896 * @param user_data the 'struct PrivStruct' with the 'toc' string we are assembling 1897 */ 1898 static void 1899 send_toc_foreach (gpointer data, gpointer user_data) 1900 { 1901 GstTocEntry *entry = data; 1902 struct PrivStruct *ps = user_data; 1903 GstTagList *tags; 1904 GList *subentries; 1905 gint64 start; 1906 gint64 stop; 1907 GstTocEntryType entype; 1908 gchar *s; 1909 1910 entype = gst_toc_entry_get_entry_type (entry); 1911 if (GST_TOC_ENTRY_TYPE_INVALID == entype) 1912 return; 1913 gst_toc_entry_get_start_stop_times (entry, &start, &stop); 1914 s = g_strdup_printf ("%*.*s<%s start=\"%" GST_TIME_FORMAT "\" stop=\"%" 1915 GST_TIME_FORMAT "\">\n", ps->toc_depth * 2, 1916 ps->toc_depth * 2, " ", 1917 gst_toc_entry_type_get_nick (entype), GST_TIME_ARGS ( 1918 start), 1919 GST_TIME_ARGS (stop)); 1920 if (NULL != s) 1921 { 1922 if (ps->toc_print_phase) 1923 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], 1924 ps->toc_length - ps->toc_pos, 1925 "%s", 1926 s); 1927 else 1928 ps->toc_length += strlen (s); 1929 g_free (s); 1930 } 1931 ps->toc_depth++; 1932 tags = gst_toc_entry_get_tags (entry); 1933 if (tags) 1934 { 1935 if (ps->toc_print_phase) 1936 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length 1937 - ps->toc_pos, 1938 "%*.*s<tags>\n", ps->toc_depth * 2, 1939 ps->toc_depth * 2, " "); 1940 else 1941 ps->toc_length += strlen ("<tags>\n") + ps->toc_depth * 2; 1942 ps->toc_depth++; 1943 gst_tag_list_foreach (tags, &send_toc_tags_foreach, ps); 1944 ps->toc_depth--; 1945 if (ps->toc_print_phase) 1946 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length 1947 - ps->toc_pos, 1948 "%*.*s</tags>\n", ps->toc_depth * 2, 1949 ps->toc_depth * 2, " "); 1950 else 1951 ps->toc_length += strlen ("</tags>\n") + ps->toc_depth * 2; 1952 } 1953 1954 subentries = gst_toc_entry_get_sub_entries (entry); 1955 g_list_foreach (subentries, send_toc_foreach, ps); 1956 ps->toc_depth--; 1957 1958 s = g_strdup_printf ("%*.*s</%s>\n", 1959 ps->toc_depth * 2, 1960 ps->toc_depth * 2, " ", 1961 gst_toc_entry_type_get_nick (entype)); 1962 if (NULL != s) 1963 { 1964 if (ps->toc_print_phase) 1965 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length 1966 - ps->toc_pos, "%s", s); 1967 else 1968 ps->toc_length += strlen (s); 1969 g_free (s); 1970 } 1971 } 1972 1973 1974 /** 1975 * XML header for the table-of-contents meta data. 1976 */ 1977 #define TOC_XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" 1978 1979 1980 static void 1981 send_info (GstDiscovererInfo *info, 1982 struct PrivStruct *ps) 1983 { 1984 const GstToc *toc; 1985 gchar *s; 1986 GstDiscovererStreamInfo *sinfo; 1987 GstClockTime duration; 1988 GList *entries; 1989 1990 duration = gst_discoverer_info_get_duration (info); 1991 if ((GST_CLOCK_TIME_IS_VALID (duration)) && (duration > 0)) 1992 { 1993 s = g_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); 1994 if (NULL != s) 1995 { 1996 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1997 "gstreamer", 1998 EXTRACTOR_METATYPE_DURATION, 1999 EXTRACTOR_METAFORMAT_UTF8, 2000 "text/plain", 2001 (const char *) s, 2002 strlen (s) + 1); 2003 g_free (s); 2004 } 2005 } 2006 2007 if (ps->time_to_leave) 2008 return; 2009 2010 /* Disable this for now (i.e. only print per-stream tags) 2011 if ((tags = gst_discoverer_info_get_tags (info))) 2012 { 2013 gst_tag_list_foreach (tags, send_tag_foreach, ps); 2014 } 2015 */if (ps->time_to_leave) 2016 return; 2017 2018 if ((toc = gst_discoverer_info_get_toc (info))) 2019 { 2020 entries = gst_toc_get_entries (toc); 2021 ps->toc_print_phase = FALSE; 2022 ps->toc_length = 0; 2023 g_list_foreach (entries, &send_toc_foreach, ps); 2024 2025 if (ps->toc_length > 0) 2026 { 2027 ps->toc_print_phase = TRUE; 2028 ps->toc_length += 1 + strlen (TOC_XML_HEADER); 2029 ps->toc = g_malloc (ps->toc_length); 2030 if (NULL != ps->toc) 2031 { 2032 ps->toc_pos = 0; 2033 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], 2034 ps->toc_length - ps->toc_pos, 2035 "%s", 2036 TOC_XML_HEADER); 2037 g_list_foreach (entries, &send_toc_foreach, ps); 2038 ps->toc[ps->toc_length - 1] = '\0'; 2039 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 2040 "gstreamer", 2041 EXTRACTOR_METATYPE_TOC, 2042 EXTRACTOR_METAFORMAT_C_STRING, 2043 "application/xml", 2044 (const char *) ps->toc, 2045 ps->toc_length); 2046 g_free (ps->toc); 2047 ps->toc = NULL; 2048 } 2049 } 2050 } 2051 2052 if (0 != ps->time_to_leave) 2053 return; 2054 2055 sinfo = gst_discoverer_info_get_stream_info (info); 2056 send_streams (sinfo, ps); 2057 } 2058 2059 2060 static void 2061 send_discovered_info (GstDiscovererInfo *info, 2062 struct PrivStruct *ps) 2063 { 2064 GstDiscovererResult result; 2065 2066 /* Docs don't say that info is guaranteed to be non-NULL */ 2067 if (NULL == info) 2068 return; 2069 result = gst_discoverer_info_get_result (info); 2070 2071 switch (result) 2072 { 2073 case GST_DISCOVERER_OK: 2074 break; 2075 case GST_DISCOVERER_URI_INVALID: 2076 break; 2077 case GST_DISCOVERER_ERROR: 2078 break; 2079 case GST_DISCOVERER_TIMEOUT: 2080 break; 2081 case GST_DISCOVERER_BUSY: 2082 break; 2083 case GST_DISCOVERER_MISSING_PLUGINS: 2084 break; 2085 } 2086 pthread_mutex_lock (&pipe_mutex); 2087 send_info (info, ps); 2088 pthread_mutex_unlock (&pipe_mutex); 2089 } 2090 2091 2092 static void 2093 _new_discovered_uri (GstDiscoverer *dc, 2094 GstDiscovererInfo *info, 2095 GError *err, 2096 struct PrivStruct *ps) 2097 { 2098 send_discovered_info (info, ps); 2099 if (ps->timeout_id > 0) 2100 g_source_remove (ps->timeout_id); 2101 ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout, 2102 ps); 2103 } 2104 2105 2106 static void 2107 _discoverer_finished (GstDiscoverer *dc, struct PrivStruct *ps) 2108 { 2109 if (ps->timeout_id > 0) 2110 g_source_remove (ps->timeout_id); 2111 ps->timeout_id = 0; 2112 g_main_loop_quit (ps->loop); 2113 } 2114 2115 2116 /** 2117 * This callback is called when discoverer has constructed a source object to 2118 * read from. Since we provided the appsrc:// uri to discoverer, this will be 2119 * the appsrc that we must handle. We set up some signals - one to push data 2120 * into appsrc and one to perform a seek. 2121 * 2122 * @param dc 2123 * @param source 2124 * @param ps 2125 */ 2126 static void 2127 _source_setup (GstDiscoverer *dc, 2128 GstElement *source, 2129 struct PrivStruct *ps) 2130 { 2131 if (ps->source) 2132 gst_object_unref (GST_OBJECT (ps->source)); 2133 ps->source = source; 2134 gst_object_ref (source); 2135 2136 /* we can set the length in appsrc. This allows some elements to estimate the 2137 * total duration of the stream. It's a good idea to set the property when you 2138 * can but it's not required. */ 2139 if (ps->length > 0) 2140 { 2141 g_object_set (ps->source, "size", (gint64) ps->length, NULL); 2142 gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type", 2143 "random-access"); 2144 } 2145 else 2146 gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type", "seekable"); 2147 2148 /* configure the appsrc, we will push a buffer to appsrc when it needs more 2149 * data */ 2150 g_signal_connect (ps->source, "need-data", G_CALLBACK (feed_data), ps); 2151 g_signal_connect (ps->source, "seek-data", G_CALLBACK (seek_data), ps); 2152 ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout, 2153 ps); 2154 } 2155 2156 2157 static void 2158 log_handler (const gchar *log_domain, 2159 GLogLevelFlags log_level, 2160 const gchar *message, 2161 gpointer unused_data) 2162 { 2163 /* do nothing */ 2164 } 2165 2166 2167 /** 2168 * Task run from the main loop to call 'gst_discoverer_uri_async'. 2169 * 2170 * @param ps our execution context 2171 * @return FALSE (always) 2172 */ 2173 static gboolean 2174 _run_async (struct PrivStruct *ps) 2175 { 2176 g_log_set_default_handler (&log_handler, NULL); 2177 g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL 2178 | G_LOG_FLAG_RECURSION, 2179 &log_handler, NULL); 2180 gst_discoverer_discover_uri_async (ps->dc, "appsrc://"); 2181 return FALSE; 2182 } 2183 2184 2185 /** 2186 * This will be the main method of your plugin. 2187 * Describe a bit what it does here. 2188 * 2189 * @param ec extraction context, here you get the API 2190 * for accessing the file data and for returning 2191 * meta data 2192 */ 2193 void 2194 EXTRACTOR_gstreamer_extract_method (struct EXTRACTOR_ExtractContext *ec) 2195 { 2196 struct PrivStruct ps; 2197 GError *err = NULL; 2198 2199 memset (&ps, 0, sizeof (ps)); 2200 ps.dc = gst_discoverer_new (8 * GST_SECOND, &err); 2201 if (NULL == ps.dc) 2202 { 2203 if (NULL != err) 2204 g_error_free (err); 2205 return; 2206 } 2207 if (NULL != err) 2208 g_error_free (err); 2209 /* connect signals */ 2210 g_signal_connect (ps.dc, "discovered", G_CALLBACK (_new_discovered_uri), &ps); 2211 g_signal_connect (ps.dc, "finished", G_CALLBACK (_discoverer_finished), &ps); 2212 g_signal_connect (ps.dc, "source-setup", G_CALLBACK (_source_setup), &ps); 2213 ps.loop = g_main_loop_new (NULL, TRUE); 2214 ps.ec = ec; 2215 ps.length = ps.ec->get_size (ps.ec->cls); 2216 if (ps.length == UINT_MAX) 2217 ps.length = 0; 2218 g_log_set_default_handler (&log_handler, NULL); 2219 g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL 2220 | G_LOG_FLAG_RECURSION, 2221 &log_handler, NULL); 2222 gst_discoverer_start (ps.dc); 2223 g_idle_add ((GSourceFunc) & _run_async, &ps); 2224 g_main_loop_run (ps.loop); 2225 if (ps.timeout_id > 0) 2226 g_source_remove (ps.timeout_id); 2227 gst_discoverer_stop (ps.dc); 2228 g_object_unref (ps.dc); 2229 g_main_loop_unref (ps.loop); 2230 } 2231 2232 2233 /** 2234 * Initialize glib and globals. 2235 */ 2236 void __attribute__ ((constructor)) 2237 gstreamer_init () 2238 { 2239 gst_init (NULL, NULL); 2240 g_log_set_default_handler (&log_handler, NULL); 2241 g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL 2242 | G_LOG_FLAG_RECURSION, 2243 &log_handler, NULL); 2244 GST_DEBUG_CATEGORY_INIT (gstreamer_extractor, "GstExtractor", 2245 0, "GStreamer-based libextractor plugin"); 2246 2247 audio_quarks = g_new0 (GQuark, 4); 2248 if (NULL != audio_quarks) 2249 { 2250 audio_quarks[0] = g_quark_from_string ("rate"); 2251 audio_quarks[1] = g_quark_from_string ("channels"); 2252 audio_quarks[2] = g_quark_from_string ("depth"); 2253 audio_quarks[3] = g_quark_from_string (NULL); 2254 } 2255 video_quarks = g_new0 (GQuark, 6); 2256 if (NULL != video_quarks) 2257 { 2258 video_quarks[0] = g_quark_from_string ("width"); 2259 video_quarks[1] = g_quark_from_string ("height"); 2260 video_quarks[2] = g_quark_from_string ("framerate"); 2261 video_quarks[3] = g_quark_from_string ("max-framerate"); 2262 video_quarks[4] = g_quark_from_string ("pixel-aspect-ratio"); 2263 video_quarks[5] = g_quark_from_string (NULL); 2264 } 2265 subtitle_quarks = g_new0 (GQuark, 2); 2266 if (NULL != subtitle_quarks) 2267 { 2268 subtitle_quarks[0] = g_quark_from_string ("language-code"); 2269 subtitle_quarks[1] = g_quark_from_string (NULL); 2270 } 2271 2272 duration_quark = g_quark_from_string ("duration"); 2273 2274 pthread_mutex_init (&pipe_mutex, NULL); 2275 } 2276 2277 2278 /* end of gstreamer_extractor.c */