1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* Copyright (C) 2010 Jonathan Matthew <jonathan@d14n.org>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* The Rhythmbox authors hereby grant permission for non-GPL compatible
11
* GStreamer plugins to be used and distributed together with GStreamer
12
* and Rhythmbox. This permission is above and beyond the permissions granted
13
* by the GPL license by which Rhythmbox is covered. If you modify this code
14
* you may extend this exception to your version of the code, but you are not
15
* obligated to do so. If you do not wish to do so, delete this exception
16
* statement from your version.
18
* This program is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU General Public License for more details.
23
* You should have received a copy of the GNU General Public License
24
* along with this program; if not, write to the Free Software
25
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
31
#include <glib/gi18n.h>
34
#include <gst/pbutils/gstdiscoverer.h>
35
#include <gst/pbutils/pbutils.h>
37
#include "rb-metadata.h"
38
#include "rb-metadata-gst-common.h"
39
#include "rb-gst-media-types.h"
41
#include "rb-file-helpers.h"
43
/* copied from gstplay-enum.h */
45
GST_AUTOPLUG_SELECT_TRY,
46
GST_AUTOPLUG_SELECT_EXPOSE,
47
GST_AUTOPLUG_SELECT_SKIP
48
} GstAutoplugSelectResult;
50
typedef GstElement *(*RBAddTaggerElem) (GstElement *pipeline, GstPad *srcpad, GstTagList *tags);
52
static gboolean check_gst_plugin_version (const char *plugin, const char *element, gint major, gint minor, gint micro);
53
G_DEFINE_TYPE(RBMetaData, rb_metadata, G_TYPE_OBJECT)
55
struct RBMetaDataPrivate
57
GstDiscovererInfo *info;
61
gboolean has_non_audio;
64
GstCaps *jpeg_image_caps;
75
rb_metadata_reset (RBMetaData *md)
77
if (md->priv->tags != NULL) {
78
g_object_unref (md->priv->tags);
81
if (md->priv->info != NULL) {
82
gst_discoverer_info_unref (md->priv->info);
83
md->priv->info = NULL;
85
g_free (md->priv->mediatype);
86
md->priv->mediatype = NULL;
88
md->priv->has_audio = FALSE;
89
md->priv->has_non_audio = FALSE;
90
md->priv->has_video = FALSE;
94
have_type_cb (GstElement *element, guint probability, GstCaps *caps, RBMetaData *md)
96
md->priv->mediatype = rb_gst_caps_to_media_type (caps);
97
rb_debug ("got type %s", md->priv->mediatype);
101
run_typefind (RBMetaData *md, const char *uri)
105
src = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
107
GstElement *pipeline = gst_pipeline_new (NULL);
108
GstElement *sink = gst_element_factory_make ("fakesink", NULL);
109
GstElement *typefind = gst_element_factory_make ("typefind", NULL);
111
gst_bin_add_many (GST_BIN (pipeline), src, typefind, sink, NULL);
112
if (gst_element_link_many (src, typefind, sink, NULL)) {
117
g_signal_connect (typefind, "have-type", G_CALLBACK (have_type_cb), md);
119
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
120
gst_element_set_state (pipeline, GST_STATE_PAUSED);
123
while (done == FALSE && md->priv->mediatype == NULL) {
124
message = gst_bus_timed_pop (bus, 5 * GST_SECOND);
125
if (message == NULL) {
126
rb_debug ("typefind pass timed out");
130
switch (GST_MESSAGE_TYPE (message)) {
131
case GST_MESSAGE_ERROR:
132
rb_debug ("typefind pass got an error");
136
case GST_MESSAGE_STATE_CHANGED:
137
if (GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) {
138
GstState old, new, pending;
139
gst_message_parse_state_changed (message, &old, &new, &pending);
140
if (new == GST_STATE_PAUSED && pending == GST_STATE_VOID_PENDING) {
141
rb_debug ("typefind pipeline reached PAUSED");
152
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
155
g_object_unref (pipeline);
160
rb_metadata_load (RBMetaData *md, const char *uri, GError **error)
164
GstDiscoverer *discoverer;
167
rb_metadata_reset (md);
169
discoverer = gst_discoverer_new (30 * GST_SECOND, NULL);
170
md->priv->info = gst_discoverer_discover_uri (discoverer, g_strdup (uri), error);
171
g_object_unref (discoverer);
173
/* figure out if we've got audio, non-audio, or video streams */
174
streams = gst_discoverer_info_get_streams (md->priv->info, GST_TYPE_DISCOVERER_STREAM_INFO);
175
for (l = streams; l != NULL; l = l->next) {
176
GstDiscovererStreamInfo *s = (GstDiscovererStreamInfo *)l->data;
177
const char *mediatype;
178
caps = gst_discoverer_stream_info_get_caps (s);
179
mediatype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
181
if (GST_IS_DISCOVERER_AUDIO_INFO (s)) {
182
md->priv->has_audio = TRUE;
184
md->priv->audio_bitrate = gst_discoverer_audio_info_get_bitrate (GST_DISCOVERER_AUDIO_INFO (s));
186
g_free (md->priv->mediatype);
187
md->priv->mediatype = rb_gst_caps_to_media_type (caps);
188
rb_debug ("found audio stream, media type %s", md->priv->mediatype);
189
} else if (GST_IS_DISCOVERER_CONTAINER_INFO (s)) {
190
if (md->priv->mediatype == NULL) {
191
md->priv->mediatype = g_strdup (mediatype);
192
rb_debug ("found container, media type %s", md->priv->mediatype);
194
rb_debug ("found container, ignoring media type");
196
} else if (g_strcmp0 (mediatype, "application/x-id3") == 0 ||
197
g_strcmp0 (mediatype, "application/x-apetag") == 0) {
198
rb_debug ("found tag type, ignoring");
200
if (GST_IS_DISCOVERER_VIDEO_INFO (s)) {
201
/* pretend low-framerate jpeg isn't video */
202
if (gst_caps_can_intersect (caps, md->priv->jpeg_image_caps)) {
203
rb_debug ("found a jpeg image stream, not actual video");
205
md->priv->has_video = TRUE;
208
md->priv->has_non_audio = TRUE;
209
if (md->priv->mediatype == NULL) {
210
md->priv->mediatype = g_strdup (mediatype);
211
rb_debug ("found video/image/other stream, media type %s", md->priv->mediatype);
213
rb_debug ("found video/image/other stream, ignoring media type (%s)", mediatype);
215
rb_debug ("video caps: %s", gst_caps_to_string (caps));
218
gst_caps_unref (caps);
220
gst_discoverer_stream_info_list_free (streams);
222
/* if we don't have a media type, use typefind to get one */
223
if (md->priv->mediatype == NULL) {
224
run_typefind (md, uri);
227
/* look at missing plugin information too */
228
switch (rb_gst_get_missing_plugin_type (gst_discoverer_info_get_misc (md->priv->info))) {
229
case MEDIA_TYPE_NONE:
231
case MEDIA_TYPE_CONTAINER:
232
/* hm, maybe we need a way to say 'we don't even know what's in here'.
233
* but for now, the things we actually identify as containers are mostly
234
* used for audio, so pretending they actually are is good enough.
236
case MEDIA_TYPE_AUDIO:
237
md->priv->has_audio = TRUE;
239
case MEDIA_TYPE_VIDEO:
240
md->priv->has_video = TRUE;
242
case MEDIA_TYPE_OTHER:
243
md->priv->has_non_audio = TRUE;
246
g_assert_not_reached ();
251
rb_metadata_has_missing_plugins (RBMetaData *md)
253
const GstStructure *s;
254
if (md->priv->info == NULL) {
258
s = gst_discoverer_info_get_misc (md->priv->info);
259
return (rb_gst_get_missing_plugin_type (s) != MEDIA_TYPE_NONE);
263
rb_metadata_get_missing_plugins (RBMetaData *md, char ***missing_plugins, char ***plugin_descriptions)
268
const GstStructure *misc;
270
if (rb_metadata_has_missing_plugins (md) == FALSE) {
274
mp = g_new0 (char *, 2);
275
pd = g_new0 (char *, 2);
277
/* wrap the structure in a new message so we can use the
278
* pbutils functions to look at it.
280
misc = gst_discoverer_info_get_misc (md->priv->info);
281
msg = gst_message_new_element (NULL, gst_structure_copy (misc));
282
mp[0] = gst_missing_plugin_message_get_installer_detail (msg);
283
pd[0] = gst_missing_plugin_message_get_description (msg);
284
gst_message_unref (msg);
286
*missing_plugins = mp;
287
*plugin_descriptions = pd;
292
rb_metadata_has_audio (RBMetaData *md)
294
return md->priv->has_audio;
298
rb_metadata_has_video (RBMetaData *md)
300
return md->priv->has_video;
304
rb_metadata_has_other_data (RBMetaData *md)
306
return md->priv->has_non_audio;
310
rb_metadata_can_save (RBMetaData *md, const char *mediatype)
316
rb_metadata_get_saveable_types (RBMetaData *md)
323
taggers = g_hash_table_get_keys (md->priv->taggers);
324
types = g_new0 (char *, g_list_length (taggers) + 1);
326
for (t = taggers; t != NULL; t = t->next) {
327
types[i++] = g_strdup (t->data);
330
g_list_free (taggers);
335
check_gst_plugin_version (const char *plugin, const char *element, int major, int minor, int micro)
342
if (gst_default_registry_check_feature_version (element, major, minor, micro + 1))
345
if (!gst_default_registry_check_feature_version (element, major, minor, micro))
348
p = gst_default_registry_find_plugin (plugin);
352
version = gst_plugin_get_version (p);
354
/* check if it's not a release */ /* what */
355
count = sscanf (version, "%u.%u.%u.%u", &i, &i, &i, &i);
360
link_named_pad (GstPad *srcpad, GstElement *element, const char *sinkpadname)
363
GstPadLinkReturn result;
365
sinkpad = gst_element_get_static_pad (element, sinkpadname);
366
if (sinkpad == NULL) {
367
sinkpad = gst_element_get_request_pad (element, sinkpadname);
369
result = gst_pad_link (srcpad, sinkpad);
370
gst_object_unref (sinkpad);
372
if (GST_PAD_LINK_SUCCESSFUL (result)) {
375
char *srcname = gst_pad_get_name (srcpad);
376
char *sinkname = gst_pad_get_name (sinkpad);
377
rb_debug ("couldn't link %s to %s: %d",
378
srcname, sinkname, result);
384
flac_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
386
GstElement *tagger = NULL;
388
tagger = gst_element_factory_make ("flactag", NULL);
392
gst_bin_add (GST_BIN (pipeline), tagger);
393
if (!link_named_pad (srcpad, tagger, "sink"))
396
gst_element_set_state (tagger, GST_STATE_PAUSED);
398
gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
403
gst_object_unref (tagger);
408
mp3_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
410
GstElement *mux = NULL;
412
/* try id3mux first, since it's more supported and writes id3v2.3 tags rather than v2.4. */
413
if (check_gst_plugin_version ("id3mux", "id3mux", 0, 10, 12)) {
414
mux = gst_element_factory_make ("id3mux", NULL);
418
mux = gst_element_factory_make ("id3v2mux", NULL);
423
gst_bin_add (GST_BIN (pipeline), mux);
424
if (!link_named_pad (srcpad, mux, "sink")) {
425
rb_debug ("couldn't link decoded pad to id3 muxer");
429
gst_element_set_state (mux, GST_STATE_PAUSED);
431
gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL);
433
rb_debug ("id3 tagger created");
438
g_object_unref (mux);
444
vorbis_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
450
mux = gst_element_factory_make ("oggmux", NULL);
451
parser = gst_element_factory_make ("vorbisparse", NULL);
452
tagger = gst_element_factory_make ("vorbistag", NULL);
453
if (mux == NULL || parser == NULL || tagger == NULL)
456
gst_bin_add_many (GST_BIN (pipeline), parser, tagger, mux, NULL);
457
if (!link_named_pad (srcpad, parser, "sink"))
459
if (!gst_element_link_many (parser, tagger, mux, NULL))
462
gst_element_set_state (parser, GST_STATE_PAUSED);
463
gst_element_set_state (tagger, GST_STATE_PAUSED);
464
gst_element_set_state (mux, GST_STATE_PAUSED);
466
gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
472
g_object_unref (parser);
474
g_object_unref (tagger);
476
g_object_unref (mux);
481
mp4_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
485
mux = gst_element_factory_make ("mp4mux", NULL);
489
gst_bin_add (GST_BIN (pipeline), mux);
490
if (!link_named_pad (srcpad, mux, "audio_%d"))
493
gst_element_set_state (mux, GST_STATE_PAUSED);
495
gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL);
500
g_object_unref (mux);
505
metadata_save_new_decoded_pad_cb (GstElement *decodebin, GstPad *pad, gboolean last, RBMetaData *md)
507
RBAddTaggerElem add_tagger_func = NULL;
508
GstElement *retag_end;
515
if (md->priv->sink_linked) {
517
error = g_error_new (GST_STREAM_ERROR,
518
GST_STREAM_ERROR_FORMAT,
519
_("Unable to write tags to this file as it contains multiple streams"));
520
gst_element_post_message (decodebin,
521
gst_message_new_error (GST_OBJECT (decodebin),
524
g_error_free (error);
528
/* find a tagger function that accepts the caps */
529
caps = gst_pad_get_caps (pad);
530
caps_str = gst_caps_to_string (caps);
531
rb_debug ("finding tagger for src caps %s", caps_str);
534
g_hash_table_iter_init (&iter, md->priv->taggers);
535
while (g_hash_table_iter_next (&iter, &key, &value)) {
536
GstCaps *tagger_caps;
537
const char *media_type = (const char *)key;
539
tagger_caps = rb_gst_media_type_to_caps (media_type);
540
/* not sure this is right, really */
541
if (gst_caps_is_always_compatible (caps, tagger_caps)) {
542
caps_str = gst_caps_to_string (tagger_caps);
543
rb_debug ("matched sink caps %s", caps_str);
546
gst_caps_unref (tagger_caps);
547
add_tagger_func = (RBAddTaggerElem)value;
550
gst_caps_unref (tagger_caps);
552
gst_caps_unref (caps);
554
/* add retagging element(s) */
555
if (add_tagger_func == NULL) {
557
error = g_error_new (GST_STREAM_ERROR,
558
GST_STREAM_ERROR_FORMAT,
559
_("Unable to write tags to this file as it is not encoded in a supported format"));
560
gst_element_post_message (decodebin,
561
gst_message_new_error (GST_OBJECT (decodebin),
564
g_error_free (error);
567
retag_end = add_tagger_func (md->priv->pipeline, pad, md->priv->tags);
569
/* link to the sink */
570
gst_element_link (retag_end, md->priv->sink);
571
md->priv->sink_linked = TRUE;
575
factory_src_caps_intersect (GstElementFactory *factory, GstCaps *caps)
577
const GList *templates;
580
templates = gst_element_factory_get_static_pad_templates (factory);
581
for (l = templates; l != NULL; l = l->next) {
582
GstStaticPadTemplate *t = l->data;
585
if (t->direction != GST_PAD_SRC) {
589
tcaps = gst_static_pad_template_get_caps (t);
590
if (gst_caps_can_intersect (tcaps, caps)) {
591
gst_caps_unref (tcaps);
594
gst_caps_unref (tcaps);
599
static GstAutoplugSelectResult
600
metadata_save_autoplug_select_cb (GstElement *decodebin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, RBMetaData *md)
607
is_demuxer = (strstr (gst_element_factory_get_klass (factory), "Demuxer") != NULL);
609
/* allow demuxers, since we're going to remux later */
610
return GST_AUTOPLUG_SELECT_TRY;
613
src_caps = gst_caps_new_any ();
614
is_any = gst_element_factory_can_src_caps (factory, src_caps);
615
gst_caps_unref (src_caps);
617
/* this is something like id3demux (though that will match the
618
* above check), allow it so we can get to the actual decoder later
620
return GST_AUTOPLUG_SELECT_TRY;
623
src_caps = gst_caps_from_string ("audio/x-raw-int; audio/x-raw-float; video/x-raw-yuv; video/x-raw-rgb; video/x-raw-gray");
624
is_raw = factory_src_caps_intersect (factory, src_caps);
625
gst_caps_unref (src_caps);
627
if (is_raw == FALSE) {
628
/* this is probably a parser or something, allow it */
629
return GST_AUTOPLUG_SELECT_TRY;
632
/* don't allow decoders */
633
return GST_AUTOPLUG_SELECT_EXPOSE;
637
check_file_valid (const char *original, const char *newfile)
639
RBMetaData *md = rb_metadata_new ();
640
GError *error = NULL;
643
rb_metadata_load (md, newfile, &error);
644
ret = (error == NULL);
646
/* TODO: check that the tags are correct? */
649
g_error_free (error);
656
rb_metadata_save (RBMetaData *md, const char *uri, GError **error)
658
GstElement *pipeline = NULL;
659
GstElement *urisrc = NULL;
660
GstElement *decodebin = NULL;
661
char *tmpname_prefix = NULL;
662
char *tmpname = NULL;
663
GOutputStream *stream = NULL;
664
GError *io_error = NULL;
670
rb_debug ("saving metadata for uri: %s", uri);
672
tmpname_prefix = rb_uri_make_hidden (uri);
673
rb_debug ("temporary file name prefix: %s", tmpname_prefix);
675
rb_uri_mkstemp (tmpname_prefix, &tmpname, &stream, &io_error);
676
g_free (tmpname_prefix);
677
if (io_error != NULL) {
681
/* set up pipeline */
682
pipeline = gst_pipeline_new ("pipeline");
683
md->priv->pipeline = pipeline;
684
md->priv->sink_linked = FALSE;
686
urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, "urisrc");
687
if (urisrc == NULL) {
690
RB_METADATA_ERROR_MISSING_PLUGIN,
691
_("Failed to create a source element; check your installation"));
692
rb_debug ("missing an element to load the uri, sadly");
696
decodebin = gst_element_factory_make ("decodebin2", "decoder");
697
if (decodebin == NULL) {
700
RB_METADATA_ERROR_MISSING_PLUGIN,
701
_("Failed to create the 'decodebin2' element; check your GStreamer installation"));
705
md->priv->sink = gst_element_factory_make ("giostreamsink", "sink");
706
if (md->priv->sink == NULL) {
709
RB_METADATA_ERROR_MISSING_PLUGIN,
710
_("Failed to create the 'giostreamsink' element; check your GStreamer installation"));
713
g_object_set (md->priv->sink, "stream", stream, NULL);
715
gst_bin_add_many (GST_BIN (pipeline), urisrc, decodebin, md->priv->sink, NULL);
716
gst_element_link (urisrc, decodebin);
718
g_signal_connect_object (decodebin,
720
G_CALLBACK (metadata_save_new_decoded_pad_cb),
722
g_signal_connect_object (decodebin,
724
G_CALLBACK (metadata_save_autoplug_select_cb),
727
/* run pipeline .. */
728
gst_element_set_state (pipeline, GST_STATE_PLAYING);
729
bus = gst_element_get_bus (pipeline);
731
while (done == FALSE) {
734
message = gst_bus_timed_pop (bus, GST_CLOCK_TIME_NONE);
735
if (message == NULL) {
736
/* can this even happen? */
737
rb_debug ("breaking out of bus polling loop");
741
switch (GST_MESSAGE_TYPE (message)) {
742
case GST_MESSAGE_ERROR:
747
gst_message_parse_error (message, &gerror, &debug);
748
if (*error != NULL) {
749
rb_debug ("caught error: %s (%s), but we've already got one", gerror->message, debug);
751
rb_debug ("caught error: %s (%s)", gerror->message, debug);
753
g_clear_error (error);
754
*error = g_error_new_literal (RB_METADATA_ERROR,
755
RB_METADATA_ERROR_GENERAL,
760
g_error_free (gerror);
765
case GST_MESSAGE_EOS:
766
rb_debug ("got eos message");
774
gst_message_unref (message);
776
gst_object_unref (bus);
777
gst_element_set_state (pipeline, GST_STATE_NULL);
779
if (g_output_stream_close (stream, NULL, &io_error) == FALSE) {
782
g_object_unref (stream);
785
if (*error == NULL) {
786
/* check to ensure the file isn't corrupt */
787
if (!check_file_valid (uri, tmpname)) {
790
RB_METADATA_ERROR_INTERNAL,
791
_("File corrupted during write"));
795
src = g_file_new_for_uri (tmpname);
796
dest = g_file_new_for_uri (uri);
798
/* try to copy attributes over, not likely to help much though */
799
g_file_copy_attributes (dest, src, G_FILE_COPY_ALL_METADATA, NULL, NULL);
801
g_file_move (src, dest, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &io_error);
802
if (io_error != NULL) {
809
if (*error == NULL) {
812
RB_METADATA_ERROR_IO,
817
if (tmpname != NULL) {
818
/* clean up temporary file */
819
rb_debug ("removing temporary file %s", tmpname);
820
dest = g_file_new_for_uri (tmpname);
821
if (g_file_delete (dest, NULL, NULL) == FALSE) {
822
rb_debug ("unable to remove temporary file?");
824
g_object_unref (dest);
828
if (pipeline != NULL)
829
gst_object_unref (GST_OBJECT (pipeline));
833
rb_metadata_get (RBMetaData *md, RBMetaDataField field, GValue *ret)
835
const GstTagList *tags;
837
GValue gstvalue = {0, };
838
GstClockTime duration;
840
/* special cases: mostly duration */
842
case RB_METADATA_FIELD_DURATION:
843
duration = gst_discoverer_info_get_duration (md->priv->info);
845
g_value_init (ret, G_TYPE_ULONG);
846
g_value_set_ulong (ret, duration / (1000 * 1000 * 1000));
849
rb_debug ("no duration available");
853
case RB_METADATA_FIELD_BITRATE:
854
if (md->priv->audio_bitrate != 0) {
855
g_value_init (ret, G_TYPE_ULONG);
856
g_value_set_ulong (ret, md->priv->audio_bitrate / 1000);
865
tags = gst_discoverer_info_get_tags (md->priv->info);
870
tag = rb_metadata_gst_field_to_gst_tag (field);
876
if (rb_metadata_get_field_type (field) == G_TYPE_STRING) {
881
/* pick the first valid utf8 string, or if we find a later
882
* string of which the first is a prefix, pick that.
885
while (gst_tag_list_peek_string_index (tags, tag, i, &v)) {
886
if (g_utf8_validate (v, -1, NULL) && (str == NULL || g_str_has_prefix (v, str))) {
890
rb_debug ("ignoring %s", v);
896
str = g_strstrip (str);
897
g_value_init (ret, G_TYPE_STRING);
898
g_value_take_string (ret, str);
904
if (gst_tag_list_copy_value (&gstvalue, tags, tag) == FALSE) {
907
g_value_init (ret, rb_metadata_get_field_type (field));
908
g_value_transform (&gstvalue, ret);
909
g_value_unset (&gstvalue);
915
rb_metadata_get_media_type (RBMetaData *md)
917
return md->priv->mediatype;
921
rb_metadata_set (RBMetaData *md, RBMetaDataField field, const GValue *val)
923
/* don't write this out */
924
if (field == RB_METADATA_FIELD_DURATION)
927
if (field == RB_METADATA_FIELD_DATE && g_value_get_ulong (val) == 0) {
928
/* we should ask gstreamer to remove the tag,
929
* but there is no easy way of doing so
933
GValue newval = {0,};
935
tag = rb_metadata_gst_field_to_gst_tag (field);
936
g_value_init (&newval, gst_tag_get_type (tag));
937
if (g_value_transform (val, &newval)) {
938
rb_debug ("Setting %s",tag);
940
if (md->priv->tags == NULL) {
941
md->priv->tags = gst_tag_list_new ();
944
gst_tag_list_add_values (md->priv->tags,
945
GST_TAG_MERGE_APPEND,
949
g_value_unset (&newval);
956
rb_metadata_finalize (GObject *object)
959
md = RB_METADATA (object);
960
rb_metadata_reset (md);
962
G_OBJECT_CLASS (rb_metadata_parent_class)->finalize (object);
966
rb_metadata_init (RBMetaData *md)
968
md->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((md), RB_TYPE_METADATA, RBMetaDataPrivate));
970
md->priv->taggers = g_hash_table_new (g_str_hash, g_str_equal);
972
if (gst_element_factory_find ("giostreamsink") == FALSE) {
973
rb_debug ("giostreamsink not found, can't tag anything");
975
if (gst_element_factory_find ("vorbistag") &&
976
gst_element_factory_find ("vorbisparse") &&
977
gst_element_factory_find ("oggmux")) {
978
rb_debug ("ogg vorbis tagging available");
979
g_hash_table_insert (md->priv->taggers, "audio/x-vorbis", (gpointer)vorbis_tagger);
982
if (gst_element_factory_find ("flactag")) {
983
rb_debug ("flac tagging available");
984
g_hash_table_insert (md->priv->taggers, "audio/x-flac", flac_tagger);
987
/* id3mux > 0.10.12 is the new one, not the broken old one. */
988
if (gst_element_factory_find ("id3v2mux") || check_gst_plugin_version ("id3mux", "id3mux", 0, 10, 12)) {
989
rb_debug ("id3 tagging available");
990
g_hash_table_insert (md->priv->taggers, "audio/mpeg", mp3_tagger);
993
if (gst_element_factory_find ("mp4mux")) {
994
rb_debug ("mp4 tagging available");
995
g_hash_table_insert (md->priv->taggers, "audio/x-aac", mp4_tagger);
999
md->priv->jpeg_image_caps = gst_caps_from_string ("image/jpeg, framerate = (fraction) [ 0, 1/1 ]");
1003
rb_metadata_class_init (RBMetaDataClass *klass)
1005
GObjectClass *object_class = G_OBJECT_CLASS (klass);
1007
object_class->finalize = rb_metadata_finalize;
1009
g_type_class_add_private (klass, sizeof (RBMetaDataPrivate));
1010
rb_metadata_gst_register_transforms ();
1014
rb_metadata_new (void)
1016
return RB_METADATA (g_object_new (RB_TYPE_METADATA, NULL, NULL));