gstreamer_extractor.c (62638B)
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_COUNT, 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_PREVIEW_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 if ( (NULL != buf) && 1556 (gst_buffer_map (buf, &mi, GST_MAP_READ)) ) 1557 { 1558 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1559 "gstreamer", 1560 le_type, 1561 EXTRACTOR_METAFORMAT_BINARY, 1562 mime_type, 1563 (const char *) mi.data, mi.size); 1564 gst_buffer_unmap (buf, &mi); 1565 } 1566 } 1567 } 1568 else if ((G_VALUE_TYPE (&val) == G_TYPE_UINT64) && 1569 (tag_quark == duration_quark)) 1570 { 1571 GstClockTime duration = (GstClockTime) g_value_get_uint64 (&val); 1572 if ((GST_CLOCK_TIME_IS_VALID (duration)) && (duration > 0)) 1573 str = g_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); 1574 } 1575 else 1576 str = gst_value_serialize (&val); 1577 break; 1578 } 1579 if (NULL != str) 1580 { 1581 /* Discoverer internally uses some tags to provide streaminfo; 1582 * ignore these tags to avoid duplicates. 1583 * This MIGHT be fixed in new GStreamer versions, but won't affect 1584 * this code (we simply won't get the tags that we think we should skip). 1585 */gboolean skip = FALSE; 1586 /* We have one tag-processing routine and use it for different 1587 * stream types. However, tags themselves don't know the type of the 1588 * stream they are attached to. We remember that before listing the 1589 * tags, and adjust LE type accordingly. 1590 */enum EXTRACTOR_MetaType le_type = kt->le_type; 1591 switch (kt->le_type) 1592 { 1593 case EXTRACTOR_METATYPE_LANGUAGE: 1594 switch (ps->st) 1595 { 1596 case STREAM_TYPE_AUDIO: 1597 skip = TRUE; 1598 break; 1599 case STREAM_TYPE_SUBTITLE: 1600 skip = TRUE; 1601 break; 1602 case STREAM_TYPE_VIDEO: 1603 le_type = EXTRACTOR_METATYPE_VIDEO_LANGUAGE; 1604 break; 1605 default: 1606 break; 1607 } 1608 break; 1609 case EXTRACTOR_METATYPE_BITRATE: 1610 switch (ps->st) 1611 { 1612 case STREAM_TYPE_AUDIO: 1613 skip = TRUE; 1614 break; 1615 case STREAM_TYPE_VIDEO: 1616 skip = TRUE; 1617 break; 1618 default: 1619 break; 1620 } 1621 break; 1622 case EXTRACTOR_METATYPE_MAXIMUM_BITRATE: 1623 switch (ps->st) 1624 { 1625 case STREAM_TYPE_AUDIO: 1626 skip = TRUE; 1627 break; 1628 case STREAM_TYPE_VIDEO: 1629 skip = TRUE; 1630 break; 1631 default: 1632 break; 1633 } 1634 break; 1635 case EXTRACTOR_METATYPE_NOMINAL_BITRATE: 1636 switch (ps->st) 1637 { 1638 case STREAM_TYPE_AUDIO: 1639 skip = TRUE; 1640 break; 1641 case STREAM_TYPE_VIDEO: 1642 skip = TRUE; 1643 break; 1644 default: 1645 break; 1646 } 1647 break; 1648 case EXTRACTOR_METATYPE_IMAGE_DIMENSIONS: 1649 switch (ps->st) 1650 { 1651 case STREAM_TYPE_VIDEO: 1652 le_type = EXTRACTOR_METATYPE_VIDEO_DIMENSIONS; 1653 break; 1654 default: 1655 break; 1656 } 1657 break; 1658 case EXTRACTOR_METATYPE_DURATION: 1659 switch (ps->st) 1660 { 1661 case STREAM_TYPE_VIDEO: 1662 le_type = EXTRACTOR_METATYPE_VIDEO_DURATION; 1663 break; 1664 case STREAM_TYPE_AUDIO: 1665 le_type = EXTRACTOR_METATYPE_AUDIO_DURATION; 1666 break; 1667 case STREAM_TYPE_SUBTITLE: 1668 le_type = EXTRACTOR_METATYPE_SUBTITLE_DURATION; 1669 break; 1670 default: 1671 break; 1672 } 1673 break; 1674 case EXTRACTOR_METATYPE_UNKNOWN: 1675 /* Convert to "key=value" form */ 1676 { 1677 gchar *new_str; 1678 /* GST_TAG_EXTENDED_COMMENT is already in key=value form */ 1679 if ((0 != strcmp (tag, "extended-comment")) || ! strchr (str, '=')) 1680 { 1681 new_str = g_strdup_printf ("%s=%s", tag, str); 1682 if (NULL != str) 1683 g_free (str); 1684 str = new_str; 1685 } 1686 } 1687 break; 1688 default: 1689 break; 1690 } 1691 if ( (! skip) && 1692 (NULL != str) ) 1693 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 1694 "gstreamer", 1695 le_type, 1696 EXTRACTOR_METAFORMAT_UTF8, 1697 "text/plain", 1698 (const char *) str, 1699 strlen (str) + 1); 1700 } 1701 if (NULL != str) 1702 g_free (str); 1703 g_value_unset (&val); 1704 } 1705 } 1706 1707 1708 static void 1709 send_streams (GstDiscovererStreamInfo *info, 1710 struct PrivStruct *ps); 1711 1712 1713 static void 1714 send_stream_info (GstDiscovererStreamInfo *info, 1715 struct PrivStruct *ps) 1716 { 1717 const GstStructure *misc; 1718 GstCaps *caps; 1719 const GstTagList *tags; 1720 1721 caps = gst_discoverer_stream_info_get_caps (info); 1722 1723 if (GST_IS_DISCOVERER_AUDIO_INFO (info)) 1724 ps->st = STREAM_TYPE_AUDIO; 1725 else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) 1726 ps->st = STREAM_TYPE_VIDEO; 1727 else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) 1728 ps->st = STREAM_TYPE_SUBTITLE; 1729 else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) 1730 ps->st = STREAM_TYPE_CONTAINER; 1731 1732 if (caps) 1733 { 1734 GstStructure *structure = gst_caps_get_structure (caps, 0); 1735 const gchar *structname = gst_structure_get_name (structure); 1736 if (g_str_has_prefix (structname, "image/")) 1737 ps->st = STREAM_TYPE_IMAGE; 1738 ps->time_to_leave = ps->ec->proc (ps->ec->cls, "gstreamer", 1739 EXTRACTOR_METATYPE_MIMETYPE, 1740 EXTRACTOR_METAFORMAT_UTF8, "text/plain", 1741 (const char *) structname, strlen ( 1742 structname) + 1); 1743 if (! ps->time_to_leave) 1744 { 1745 gst_structure_foreach (structure, send_structure_foreach, ps); 1746 } 1747 gst_caps_unref (caps); 1748 } 1749 1750 if (ps->time_to_leave) 1751 { 1752 ps->st = STREAM_TYPE_NONE; 1753 return; 1754 } 1755 1756 misc = gst_discoverer_stream_info_get_misc (info); 1757 if (misc) 1758 { 1759 gst_structure_foreach (misc, send_structure_foreach, ps); 1760 } 1761 1762 if (ps->time_to_leave) 1763 { 1764 ps->st = STREAM_TYPE_NONE; 1765 return; 1766 } 1767 1768 tags = gst_discoverer_stream_info_get_tags (info); 1769 if (tags) 1770 { 1771 gst_tag_list_foreach (tags, send_tag_foreach, ps); 1772 } 1773 ps->st = STREAM_TYPE_NONE; 1774 1775 if (ps->time_to_leave) 1776 return; 1777 1778 if (GST_IS_DISCOVERER_AUDIO_INFO (info)) 1779 send_audio_info (GST_DISCOVERER_AUDIO_INFO (info), ps); 1780 else if (GST_IS_DISCOVERER_VIDEO_INFO (info)) 1781 send_video_info (GST_DISCOVERER_VIDEO_INFO (info), ps); 1782 else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info)) 1783 send_subtitle_info (GST_DISCOVERER_SUBTITLE_INFO (info), ps); 1784 else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) 1785 { 1786 GList *child; 1787 GstDiscovererContainerInfo *c = GST_DISCOVERER_CONTAINER_INFO (info); 1788 GList *children = gst_discoverer_container_info_get_streams (c); 1789 for (child = children; (NULL != child) && (! ps->time_to_leave); 1790 child = child->next) 1791 { 1792 GstDiscovererStreamInfo *sinfo = child->data; 1793 /* send_streams () will unref it */ 1794 sinfo = gst_discoverer_stream_info_ref (sinfo); 1795 send_streams (sinfo, ps); 1796 } 1797 if (children) 1798 gst_discoverer_stream_info_list_free (children); 1799 } 1800 } 1801 1802 1803 static void 1804 send_streams (GstDiscovererStreamInfo *info, 1805 struct PrivStruct *ps) 1806 { 1807 GstDiscovererStreamInfo *next; 1808 1809 while ( (NULL != info) && (! ps->time_to_leave) ) 1810 { 1811 send_stream_info (info, ps); 1812 next = gst_discoverer_stream_info_get_next (info); 1813 gst_discoverer_stream_info_unref (info); 1814 info = next; 1815 } 1816 } 1817 1818 1819 /** 1820 * Callback used to construct the XML 'toc'. 1821 * 1822 * @param tags FIXME 1823 * @param tag FIXME 1824 * @param user_data the 'struct PrivStruct' with the 'toc' string we are assembling 1825 */ 1826 static void 1827 send_toc_tags_foreach (const GstTagList *tags, 1828 const gchar *tag, 1829 gpointer user_data) 1830 { 1831 struct PrivStruct *ps = user_data; 1832 GValue val = { 0 }; 1833 gchar *topen, *str, *tclose; 1834 GType gst_fraction = GST_TYPE_FRACTION; 1835 1836 gst_tag_list_copy_value (&val, tags, tag); 1837 switch (G_VALUE_TYPE (&val)) 1838 { 1839 case G_TYPE_STRING: 1840 str = g_value_dup_string (&val); 1841 break; 1842 case G_TYPE_UINT: 1843 case G_TYPE_INT: 1844 case G_TYPE_DOUBLE: 1845 str = gst_value_serialize (&val); 1846 break; 1847 default: 1848 if (G_VALUE_TYPE (&val) == gst_fraction) 1849 { 1850 str = gst_value_serialize (&val); 1851 break; 1852 } 1853 /* This is a potential source of invalid characters */ 1854 /* And it also might attempt to serialize binary data - such as images. */ 1855 str = gst_value_serialize (&val); 1856 if (NULL != str) 1857 { 1858 g_free (str); 1859 str = NULL; 1860 } 1861 break; 1862 } 1863 if (NULL != str) 1864 { 1865 topen = g_strdup_printf ("%*.*s<%s>", 1866 ps->toc_depth * 2, 1867 ps->toc_depth * 2, " ", tag); 1868 tclose = g_strdup_printf ("%*.*s</%s>\n", 1869 ps->toc_depth * 2, 1870 ps->toc_depth * 2, " ", 1871 tag); 1872 if ( (NULL != topen) && 1873 (NULL != tclose) ) 1874 { 1875 if (ps->toc_print_phase) 1876 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], 1877 ps->toc_length - ps->toc_pos, 1878 "%s%s%s", 1879 topen, 1880 str, 1881 tclose); 1882 else 1883 ps->toc_length += strlen (topen) + strlen (str) + strlen (tclose); 1884 } 1885 if (NULL != topen) 1886 g_free (topen); 1887 if (NULL != tclose) 1888 g_free (tclose); 1889 g_free (str); 1890 } 1891 g_value_unset (&val); 1892 } 1893 1894 1895 /** 1896 * Top-level callback used to construct the XML 'toc'. 1897 * 1898 * @param data the GstTocEntry we're processing 1899 * @param user_data the 'struct PrivStruct' with the 'toc' string we are assembling 1900 */ 1901 static void 1902 send_toc_foreach (gpointer data, gpointer user_data) 1903 { 1904 GstTocEntry *entry = data; 1905 struct PrivStruct *ps = user_data; 1906 GstTagList *tags; 1907 GList *subentries; 1908 gint64 start; 1909 gint64 stop; 1910 GstTocEntryType entype; 1911 gchar *s; 1912 1913 entype = gst_toc_entry_get_entry_type (entry); 1914 if (GST_TOC_ENTRY_TYPE_INVALID == entype) 1915 return; 1916 gst_toc_entry_get_start_stop_times (entry, &start, &stop); 1917 s = g_strdup_printf ("%*.*s<%s start=\"%" GST_TIME_FORMAT "\" stop=\"%" 1918 GST_TIME_FORMAT "\">\n", ps->toc_depth * 2, 1919 ps->toc_depth * 2, " ", 1920 gst_toc_entry_type_get_nick (entype), GST_TIME_ARGS ( 1921 start), 1922 GST_TIME_ARGS (stop)); 1923 if (NULL != s) 1924 { 1925 if (ps->toc_print_phase) 1926 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], 1927 ps->toc_length - ps->toc_pos, 1928 "%s", 1929 s); 1930 else 1931 ps->toc_length += strlen (s); 1932 g_free (s); 1933 } 1934 ps->toc_depth++; 1935 tags = gst_toc_entry_get_tags (entry); 1936 if (tags) 1937 { 1938 if (ps->toc_print_phase) 1939 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length 1940 - ps->toc_pos, 1941 "%*.*s<tags>\n", ps->toc_depth * 2, 1942 ps->toc_depth * 2, " "); 1943 else 1944 ps->toc_length += strlen ("<tags>\n") + ps->toc_depth * 2; 1945 ps->toc_depth++; 1946 gst_tag_list_foreach (tags, &send_toc_tags_foreach, ps); 1947 ps->toc_depth--; 1948 if (ps->toc_print_phase) 1949 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length 1950 - ps->toc_pos, 1951 "%*.*s</tags>\n", ps->toc_depth * 2, 1952 ps->toc_depth * 2, " "); 1953 else 1954 ps->toc_length += strlen ("</tags>\n") + ps->toc_depth * 2; 1955 } 1956 1957 subentries = gst_toc_entry_get_sub_entries (entry); 1958 g_list_foreach (subentries, send_toc_foreach, ps); 1959 ps->toc_depth--; 1960 1961 s = g_strdup_printf ("%*.*s</%s>\n", 1962 ps->toc_depth * 2, 1963 ps->toc_depth * 2, " ", 1964 gst_toc_entry_type_get_nick (entype)); 1965 if (NULL != s) 1966 { 1967 if (ps->toc_print_phase) 1968 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], ps->toc_length 1969 - ps->toc_pos, "%s", s); 1970 else 1971 ps->toc_length += strlen (s); 1972 g_free (s); 1973 } 1974 } 1975 1976 1977 /** 1978 * XML header for the table-of-contents meta data. 1979 */ 1980 #define TOC_XML_HEADER "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" 1981 1982 1983 static void 1984 send_info (GstDiscovererInfo *info, 1985 struct PrivStruct *ps) 1986 { 1987 const GstToc *toc; 1988 gchar *s; 1989 GstDiscovererStreamInfo *sinfo; 1990 GstClockTime duration; 1991 GList *entries; 1992 1993 duration = gst_discoverer_info_get_duration (info); 1994 if ((GST_CLOCK_TIME_IS_VALID (duration)) && (duration > 0)) 1995 { 1996 s = g_strdup_printf ("%" GST_TIME_FORMAT, GST_TIME_ARGS (duration)); 1997 if (NULL != s) 1998 { 1999 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 2000 "gstreamer", 2001 EXTRACTOR_METATYPE_DURATION, 2002 EXTRACTOR_METAFORMAT_UTF8, 2003 "text/plain", 2004 (const char *) s, 2005 strlen (s) + 1); 2006 g_free (s); 2007 } 2008 } 2009 2010 if (ps->time_to_leave) 2011 return; 2012 2013 /* Disable this for now (i.e. only print per-stream tags) 2014 if ((tags = gst_discoverer_info_get_tags (info))) 2015 { 2016 gst_tag_list_foreach (tags, send_tag_foreach, ps); 2017 } 2018 */if (ps->time_to_leave) 2019 return; 2020 2021 if ((toc = gst_discoverer_info_get_toc (info))) 2022 { 2023 entries = gst_toc_get_entries (toc); 2024 ps->toc_print_phase = FALSE; 2025 ps->toc_length = 0; 2026 g_list_foreach (entries, &send_toc_foreach, ps); 2027 2028 if (ps->toc_length > 0) 2029 { 2030 ps->toc_print_phase = TRUE; 2031 ps->toc_length += 1 + strlen (TOC_XML_HEADER); 2032 ps->toc = g_malloc (ps->toc_length); 2033 if (NULL != ps->toc) 2034 { 2035 ps->toc_pos = 0; 2036 ps->toc_pos += g_snprintf (&ps->toc[ps->toc_pos], 2037 ps->toc_length - ps->toc_pos, 2038 "%s", 2039 TOC_XML_HEADER); 2040 g_list_foreach (entries, &send_toc_foreach, ps); 2041 ps->toc[ps->toc_length - 1] = '\0'; 2042 ps->time_to_leave = ps->ec->proc (ps->ec->cls, 2043 "gstreamer", 2044 EXTRACTOR_METATYPE_TOC, 2045 EXTRACTOR_METAFORMAT_C_STRING, 2046 "application/xml", 2047 (const char *) ps->toc, 2048 ps->toc_length); 2049 g_free (ps->toc); 2050 ps->toc = NULL; 2051 } 2052 } 2053 } 2054 2055 if (0 != ps->time_to_leave) 2056 return; 2057 2058 sinfo = gst_discoverer_info_get_stream_info (info); 2059 send_streams (sinfo, ps); 2060 } 2061 2062 2063 static void 2064 send_discovered_info (GstDiscovererInfo *info, 2065 struct PrivStruct *ps) 2066 { 2067 GstDiscovererResult result; 2068 2069 /* Docs don't say that info is guaranteed to be non-NULL */ 2070 if (NULL == info) 2071 return; 2072 result = gst_discoverer_info_get_result (info); 2073 2074 switch (result) 2075 { 2076 case GST_DISCOVERER_OK: 2077 break; 2078 case GST_DISCOVERER_URI_INVALID: 2079 break; 2080 case GST_DISCOVERER_ERROR: 2081 break; 2082 case GST_DISCOVERER_TIMEOUT: 2083 break; 2084 case GST_DISCOVERER_BUSY: 2085 break; 2086 case GST_DISCOVERER_MISSING_PLUGINS: 2087 break; 2088 } 2089 pthread_mutex_lock (&pipe_mutex); 2090 send_info (info, ps); 2091 pthread_mutex_unlock (&pipe_mutex); 2092 } 2093 2094 2095 static void 2096 _new_discovered_uri (GstDiscoverer *dc, 2097 GstDiscovererInfo *info, 2098 GError *err, 2099 struct PrivStruct *ps) 2100 { 2101 send_discovered_info (info, ps); 2102 if (ps->timeout_id > 0) 2103 g_source_remove (ps->timeout_id); 2104 ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout, 2105 ps); 2106 } 2107 2108 2109 static void 2110 _discoverer_finished (GstDiscoverer *dc, struct PrivStruct *ps) 2111 { 2112 if (ps->timeout_id > 0) 2113 g_source_remove (ps->timeout_id); 2114 ps->timeout_id = 0; 2115 g_main_loop_quit (ps->loop); 2116 } 2117 2118 2119 /** 2120 * This callback is called when discoverer has constructed a source object to 2121 * read from. Since we provided the appsrc:// uri to discoverer, this will be 2122 * the appsrc that we must handle. We set up some signals - one to push data 2123 * into appsrc and one to perform a seek. 2124 * 2125 * @param dc 2126 * @param source 2127 * @param ps 2128 */ 2129 static void 2130 _source_setup (GstDiscoverer *dc, 2131 GstElement *source, 2132 struct PrivStruct *ps) 2133 { 2134 if (ps->source) 2135 gst_object_unref (GST_OBJECT (ps->source)); 2136 ps->source = source; 2137 gst_object_ref (source); 2138 2139 /* we can set the length in appsrc. This allows some elements to estimate the 2140 * total duration of the stream. It's a good idea to set the property when you 2141 * can but it's not required. */ 2142 if (ps->length > 0) 2143 { 2144 g_object_set (ps->source, "size", (gint64) ps->length, NULL); 2145 gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type", 2146 "random-access"); 2147 } 2148 else 2149 gst_util_set_object_arg (G_OBJECT (ps->source), "stream-type", "seekable"); 2150 2151 /* configure the appsrc, we will push a buffer to appsrc when it needs more 2152 * data */ 2153 g_signal_connect (ps->source, "need-data", G_CALLBACK (feed_data), ps); 2154 g_signal_connect (ps->source, "seek-data", G_CALLBACK (seek_data), ps); 2155 ps->timeout_id = g_timeout_add (DATA_TIMEOUT, (GSourceFunc) _data_timeout, 2156 ps); 2157 } 2158 2159 2160 static void 2161 log_handler (const gchar *log_domain, 2162 GLogLevelFlags log_level, 2163 const gchar *message, 2164 gpointer unused_data) 2165 { 2166 /* do nothing */ 2167 } 2168 2169 2170 /** 2171 * Task run from the main loop to call 'gst_discoverer_uri_async'. 2172 * 2173 * @param ps our execution context 2174 * @return FALSE (always) 2175 */ 2176 static gboolean 2177 _run_async (struct PrivStruct *ps) 2178 { 2179 g_log_set_default_handler (&log_handler, NULL); 2180 g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL 2181 | G_LOG_FLAG_RECURSION, 2182 &log_handler, NULL); 2183 gst_discoverer_discover_uri_async (ps->dc, "appsrc://"); 2184 return FALSE; 2185 } 2186 2187 2188 /** 2189 * This will be the main method of your plugin. 2190 * Describe a bit what it does here. 2191 * 2192 * @param ec extraction context, here you get the API 2193 * for accessing the file data and for returning 2194 * meta data 2195 */ 2196 void 2197 EXTRACTOR_gstreamer_extract_method (struct EXTRACTOR_ExtractContext *ec) 2198 { 2199 struct PrivStruct ps; 2200 GError *err = NULL; 2201 2202 memset (&ps, 0, sizeof (ps)); 2203 ps.dc = gst_discoverer_new (8 * GST_SECOND, &err); 2204 if (NULL == ps.dc) 2205 { 2206 if (NULL != err) 2207 g_error_free (err); 2208 return; 2209 } 2210 if (NULL != err) 2211 g_error_free (err); 2212 /* connect signals */ 2213 g_signal_connect (ps.dc, "discovered", G_CALLBACK (_new_discovered_uri), &ps); 2214 g_signal_connect (ps.dc, "finished", G_CALLBACK (_discoverer_finished), &ps); 2215 g_signal_connect (ps.dc, "source-setup", G_CALLBACK (_source_setup), &ps); 2216 ps.loop = g_main_loop_new (NULL, TRUE); 2217 ps.ec = ec; 2218 ps.length = ps.ec->get_size (ps.ec->cls); 2219 if (ps.length == UINT64_MAX) 2220 ps.length = 0; 2221 g_log_set_default_handler (&log_handler, NULL); 2222 g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL 2223 | G_LOG_FLAG_RECURSION, 2224 &log_handler, NULL); 2225 gst_discoverer_start (ps.dc); 2226 g_idle_add ((GSourceFunc) & _run_async, &ps); 2227 g_main_loop_run (ps.loop); 2228 if (ps.timeout_id > 0) 2229 g_source_remove (ps.timeout_id); 2230 gst_discoverer_stop (ps.dc); 2231 g_object_unref (ps.dc); 2232 g_main_loop_unref (ps.loop); 2233 } 2234 2235 2236 /** 2237 * Initialize glib and globals. 2238 */ 2239 void __attribute__ ((constructor)) 2240 gstreamer_init () 2241 { 2242 gst_init (NULL, NULL); 2243 g_log_set_default_handler (&log_handler, NULL); 2244 g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL 2245 | G_LOG_FLAG_RECURSION, 2246 &log_handler, NULL); 2247 GST_DEBUG_CATEGORY_INIT (gstreamer_extractor, "GstExtractor", 2248 0, "GStreamer-based libextractor plugin"); 2249 2250 audio_quarks = g_new0 (GQuark, 4); 2251 if (NULL != audio_quarks) 2252 { 2253 audio_quarks[0] = g_quark_from_string ("rate"); 2254 audio_quarks[1] = g_quark_from_string ("channels"); 2255 audio_quarks[2] = g_quark_from_string ("depth"); 2256 audio_quarks[3] = g_quark_from_string (NULL); 2257 } 2258 video_quarks = g_new0 (GQuark, 6); 2259 if (NULL != video_quarks) 2260 { 2261 video_quarks[0] = g_quark_from_string ("width"); 2262 video_quarks[1] = g_quark_from_string ("height"); 2263 video_quarks[2] = g_quark_from_string ("framerate"); 2264 video_quarks[3] = g_quark_from_string ("max-framerate"); 2265 video_quarks[4] = g_quark_from_string ("pixel-aspect-ratio"); 2266 video_quarks[5] = g_quark_from_string (NULL); 2267 } 2268 subtitle_quarks = g_new0 (GQuark, 2); 2269 if (NULL != subtitle_quarks) 2270 { 2271 subtitle_quarks[0] = g_quark_from_string ("language-code"); 2272 subtitle_quarks[1] = g_quark_from_string (NULL); 2273 } 2274 2275 duration_quark = g_quark_from_string ("duration"); 2276 2277 pthread_mutex_init (&pipe_mutex, NULL); 2278 } 2279 2280 2281 /* end of gstreamer_extractor.c */