hythmbox-2.98/metadata/rb-metadata-gst.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found rb-metadata-gst.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
clang-analyzer no-output-found rb-metadata-gst.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
   2  *
   3  *  Copyright (C) 2010 Jonathan Matthew  <jonathan@d14n.org>
   4  *
   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.
   9  *
  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.
  17  *
  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.
  22  *
  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.
  26  *
  27  */
  28 
  29 #include <config.h>
  30 
  31 #include <glib/gi18n.h>
  32 #include <gio/gio.h>
  33 #include <gst/gst.h>
  34 #include <gst/pbutils/gstdiscoverer.h>
  35 #include <gst/pbutils/pbutils.h>
  36 
  37 #include "rb-metadata.h"
  38 #include "rb-metadata-gst-common.h"
  39 #include "rb-gst-media-types.h"
  40 #include "rb-debug.h"
  41 #include "rb-file-helpers.h"
  42 
  43 /* copied from gstplay-enum.h */
  44 typedef enum {
  45   GST_AUTOPLUG_SELECT_TRY,
  46   GST_AUTOPLUG_SELECT_EXPOSE,
  47   GST_AUTOPLUG_SELECT_SKIP
  48 } GstAutoplugSelectResult;
  49 
  50 typedef GstElement *(*RBAddTaggerElem) (GstElement *pipeline, GstPad *srcpad, GstTagList *tags);
  51 
  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)
  54 
  55 struct RBMetaDataPrivate
  56 {
  57 	GstDiscovererInfo *info;
  58 
  59 	char *mediatype;
  60 	gboolean has_audio;
  61 	gboolean has_non_audio;
  62 	gboolean has_video;
  63 	guint audio_bitrate;
  64 	GstCaps *jpeg_image_caps;
  65 
  66 	/* writing */
  67 	GstElement *pipeline;
  68 	GstElement *sink;
  69 	GHashTable *taggers;
  70 	GstTagList *tags;
  71 	gboolean sink_linked;
  72 };
  73 
  74 void
  75 rb_metadata_reset (RBMetaData *md)
  76 {
  77 	if (md->priv->tags != NULL) {
  78 		gst_tag_list_free (md->priv->tags);
  79 		md->priv->tags = NULL;
  80 	}
  81 
  82 	if (md->priv->info != NULL) {
  83 		gst_discoverer_info_unref (md->priv->info);
  84 		md->priv->info = NULL;
  85 	}
  86 	g_free (md->priv->mediatype);
  87 	md->priv->mediatype = NULL;
  88 
  89 	md->priv->has_audio = FALSE;
  90 	md->priv->has_non_audio = FALSE;
  91 	md->priv->has_video = FALSE;
  92 }
  93 
  94 static void
  95 have_type_cb (GstElement *element, guint probability, GstCaps *caps, RBMetaData *md)
  96 {
  97 	md->priv->mediatype = rb_gst_caps_to_media_type (caps);
  98 	rb_debug ("got type %s", md->priv->mediatype);
  99 }
 100 
 101 static void
 102 run_typefind (RBMetaData *md, const char *uri)
 103 {
 104 	GstElement *src;
 105 
 106 	src = gst_element_make_from_uri (GST_URI_SRC, uri, NULL);
 107 	if (src != NULL) {
 108 		GstElement *pipeline = gst_pipeline_new (NULL);
 109 		GstElement *sink = gst_element_factory_make ("fakesink", NULL);
 110 		GstElement *typefind = gst_element_factory_make ("typefind", NULL);
 111 
 112 		gst_bin_add_many (GST_BIN (pipeline), src, typefind, sink, NULL);
 113 		if (gst_element_link_many (src, typefind, sink, NULL)) {
 114 			GstBus *bus;
 115 			GstMessage *message;
 116 			gboolean done;
 117 
 118 			g_signal_connect (typefind, "have-type", G_CALLBACK (have_type_cb), md);
 119 
 120 			bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
 121 			gst_element_set_state (pipeline, GST_STATE_PAUSED);
 122 			done = FALSE;
 123 
 124 			while (done == FALSE && md->priv->mediatype == NULL) {
 125 				message = gst_bus_timed_pop (bus, 5 * GST_SECOND);
 126 				if (message == NULL) {
 127 					rb_debug ("typefind pass timed out");
 128 					break;
 129 				}
 130 
 131 				switch (GST_MESSAGE_TYPE (message)) {
 132 				case GST_MESSAGE_ERROR:
 133 					rb_debug ("typefind pass got an error");
 134 					done = TRUE;
 135 					break;
 136 
 137 				case GST_MESSAGE_STATE_CHANGED:
 138 					if (GST_MESSAGE_SRC (message) == GST_OBJECT (pipeline)) {
 139 						GstState old, new, pending;
 140 						gst_message_parse_state_changed (message, &old, &new, &pending);
 141 						if (new == GST_STATE_PAUSED && pending == GST_STATE_VOID_PENDING) {
 142 							rb_debug ("typefind pipeline reached PAUSED");
 143 							done = TRUE;
 144 						}
 145 					}
 146 					break;
 147 
 148 				default:
 149 					break;
 150 				}
 151 			}
 152 
 153 			gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
 154 		}
 155 
 156 		g_object_unref (pipeline);
 157 	}
 158 }
 159 
 160 void
 161 rb_metadata_load (RBMetaData *md, const char *uri, GError **error)
 162 {
 163 	GList *streams;
 164 	GList *l;
 165 	GstDiscoverer *discoverer;
 166 	GstCaps *caps;
 167 
 168 	rb_metadata_reset (md);
 169 
 170 	discoverer = gst_discoverer_new (30 * GST_SECOND, NULL);
 171 	md->priv->info = gst_discoverer_discover_uri (discoverer, g_strdup (uri), error);
 172 	g_object_unref (discoverer);
 173 
 174 	/* figure out if we've got audio, non-audio, or video streams */
 175 	streams = gst_discoverer_info_get_streams (md->priv->info, GST_TYPE_DISCOVERER_STREAM_INFO);
 176 	for (l = streams; l != NULL; l = l->next) {
 177 		GstDiscovererStreamInfo *s = (GstDiscovererStreamInfo *)l->data;
 178 		const char *mediatype;
 179 		caps = gst_discoverer_stream_info_get_caps (s);
 180 		mediatype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
 181 
 182 		if (GST_IS_DISCOVERER_AUDIO_INFO (s)) {
 183 			md->priv->has_audio = TRUE;
 184 
 185 			md->priv->audio_bitrate = gst_discoverer_audio_info_get_bitrate (GST_DISCOVERER_AUDIO_INFO (s));
 186 
 187 			g_free (md->priv->mediatype);
 188 			md->priv->mediatype = rb_gst_caps_to_media_type (caps);
 189 			rb_debug ("found audio stream, media type %s", md->priv->mediatype);
 190 		} else if (GST_IS_DISCOVERER_CONTAINER_INFO (s)) {
 191 			if (md->priv->mediatype == NULL) {
 192 				md->priv->mediatype = g_strdup (mediatype);
 193 				rb_debug ("found container, media type %s", md->priv->mediatype);
 194 			} else {
 195 				rb_debug ("found container, ignoring media type");
 196 			}
 197 		} else if (g_strcmp0 (mediatype, "application/x-id3") == 0 ||
 198 			   g_strcmp0 (mediatype, "application/x-apetag") == 0) {
 199 			rb_debug ("found tag type, ignoring");
 200 		} else {
 201 			if (GST_IS_DISCOVERER_VIDEO_INFO (s)) {
 202 				/* pretend low-framerate jpeg isn't video */
 203 				if (gst_caps_can_intersect (caps, md->priv->jpeg_image_caps)) {
 204 					rb_debug ("found a jpeg image stream, not actual video");
 205 				} else {
 206 					md->priv->has_video = TRUE;
 207 				}
 208 			}
 209 			md->priv->has_non_audio = TRUE;
 210 			if (md->priv->mediatype == NULL) {
 211 				md->priv->mediatype = g_strdup (mediatype);
 212 				rb_debug ("found video/image/other stream, media type %s", md->priv->mediatype);
 213 			} else {
 214 				rb_debug ("found video/image/other stream, ignoring media type (%s)", mediatype);
 215 			}
 216 			rb_debug ("video caps: %s", gst_caps_to_string (caps));
 217 		}
 218 
 219 		gst_caps_unref (caps);
 220 	}
 221 	gst_discoverer_stream_info_list_free (streams);
 222 
 223 	/* if we don't have a media type, use typefind to get one */
 224 	if (md->priv->mediatype == NULL) {
 225 		run_typefind (md, uri);
 226 	}
 227 
 228 	/* look at missing plugin information too */
 229 	switch (rb_gst_get_missing_plugin_type (gst_discoverer_info_get_misc (md->priv->info))) {
 230 	case MEDIA_TYPE_NONE:
 231 		break;
 232 	case MEDIA_TYPE_CONTAINER:
 233 		/* hm, maybe we need a way to say 'we don't even know what's in here'.
 234 		 * but for now, the things we actually identify as containers are mostly
 235 		 * used for audio, so pretending they actually are is good enough.
 236 		 */
 237 	case MEDIA_TYPE_AUDIO:
 238 		md->priv->has_audio = TRUE;
 239 		break;
 240 	case MEDIA_TYPE_VIDEO:
 241 		md->priv->has_video = TRUE;
 242 		break;
 243 	case MEDIA_TYPE_OTHER:
 244 		md->priv->has_non_audio = TRUE;
 245 		break;
 246 	default:
 247 		g_assert_not_reached ();
 248 	}
 249 }
 250 
 251 gboolean
 252 rb_metadata_has_missing_plugins (RBMetaData *md)
 253 {
 254 	const GstStructure *s;
 255 	if (md->priv->info == NULL) {
 256 		return FALSE;
 257 	}
 258 
 259 	s = gst_discoverer_info_get_misc (md->priv->info);
 260 	return (rb_gst_get_missing_plugin_type (s) != MEDIA_TYPE_NONE);
 261 }
 262 
 263 gboolean
 264 rb_metadata_get_missing_plugins (RBMetaData *md, char ***missing_plugins, char ***plugin_descriptions)
 265 {
 266 	char **mp;
 267 	char **pd;
 268 	GstMessage *msg;
 269 	const GstStructure *misc;
 270 
 271 	if (rb_metadata_has_missing_plugins (md) == FALSE) {
 272 		return FALSE;
 273 	}
 274 
 275 	mp = g_new0 (char *, 2);
 276 	pd = g_new0 (char *, 2);
 277 
 278 	/* wrap the structure in a new message so we can use the
 279 	 * pbutils functions to look at it.
 280 	 */
 281 	misc = gst_discoverer_info_get_misc (md->priv->info);
 282 	msg = gst_message_new_element (NULL, gst_structure_copy (misc));
 283 	mp[0] = gst_missing_plugin_message_get_installer_detail (msg);
 284 	pd[0] = gst_missing_plugin_message_get_description (msg);
 285 	gst_message_unref (msg);
 286 
 287 	*missing_plugins = mp;
 288 	*plugin_descriptions = pd;
 289 	return TRUE;
 290 }
 291 
 292 gboolean
 293 rb_metadata_has_audio (RBMetaData *md)
 294 {
 295 	return md->priv->has_audio;
 296 }
 297 
 298 gboolean
 299 rb_metadata_has_video (RBMetaData *md)
 300 {
 301 	return md->priv->has_video;
 302 }
 303 
 304 gboolean
 305 rb_metadata_has_other_data (RBMetaData *md)
 306 {
 307 	return md->priv->has_non_audio;
 308 }
 309 
 310 gboolean
 311 rb_metadata_can_save (RBMetaData *md, const char *mediatype)
 312 {
 313 	return FALSE;
 314 }
 315 
 316 char **
 317 rb_metadata_get_saveable_types (RBMetaData *md)
 318 {
 319 	char **types;
 320 	GList *taggers;
 321 	GList *t;
 322 	int i;
 323 
 324 	taggers = g_hash_table_get_keys (md->priv->taggers);
 325 	types = g_new0 (char *, g_list_length (taggers) + 1);
 326 	i = 0;
 327 	for (t = taggers; t != NULL; t = t->next) {
 328 		types[i++] = g_strdup (t->data);
 329 	}
 330 
 331 	g_list_free (taggers);
 332 	return types;
 333 }
 334 
 335 static gboolean
 336 check_gst_plugin_version (const char *plugin, const char *element, int major, int minor, int micro)
 337 {
 338 	const char *version;
 339 	GstPlugin *p;
 340 	guint i;
 341 	guint count;
 342 
 343 	if (gst_default_registry_check_feature_version (element, major, minor, micro + 1))
 344 		return TRUE;
 345 
 346 	if (!gst_default_registry_check_feature_version (element, major, minor, micro))
 347 		return FALSE;
 348 
 349 	p = gst_default_registry_find_plugin (plugin);
 350 	if (p == NULL)
 351 		return FALSE;
 352 
 353 	version = gst_plugin_get_version (p);
 354 
 355 	/* check if it's not a release */	/* what */
 356 	count = sscanf (version, "%u.%u.%u.%u", &i, &i, &i, &i);
 357 	return (count > 3);
 358 }
 359 
 360 static gboolean
 361 link_named_pad (GstPad *srcpad, GstElement *element, const char *sinkpadname)
 362 {
 363 	GstPad *sinkpad;
 364 	GstPadLinkReturn result;
 365 
 366 	sinkpad = gst_element_get_static_pad (element, sinkpadname);
 367 	if (sinkpad == NULL) {
 368 		sinkpad = gst_element_get_request_pad (element, sinkpadname);
 369 	}
 370 	result = gst_pad_link (srcpad, sinkpad);
 371 	gst_object_unref (sinkpad);
 372 
 373 	if (GST_PAD_LINK_SUCCESSFUL (result)) {
 374 		return TRUE;
 375 	} else {
 376 		char *srcname = gst_pad_get_name (srcpad);
 377 		char *sinkname = gst_pad_get_name (sinkpad);
 378 		rb_debug ("couldn't link %s to %s: %d",
 379 			  srcname, sinkname, result);
 380 		return FALSE;
 381 	}
 382 }
 383 
 384 static GstElement *
 385 flac_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
 386 {
 387 	GstElement *tagger = NULL;
 388 
 389 	tagger = gst_element_factory_make ("flactag", NULL);
 390 	if (tagger == NULL)
 391 		return NULL;
 392 
 393 	gst_bin_add (GST_BIN (pipeline), tagger);
 394 	if (!link_named_pad (srcpad, tagger, "sink"))
 395 		goto error;
 396 
 397 	gst_element_set_state (tagger, GST_STATE_PAUSED);
 398 	if (tags != NULL) {
 399 		gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
 400 	}
 401 	return tagger;
 402 
 403 error:
 404 	gst_object_unref (tagger);
 405 	return NULL;
 406 }
 407 
 408 static GstElement *
 409 mp3_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
 410 {
 411 	GstElement *mux = NULL;
 412 
 413 	/* try id3mux first, since it's more supported and writes id3v2.3 tags rather than v2.4.  */
 414 	if (check_gst_plugin_version ("id3mux", "id3mux", 0, 10, 12)) {
 415 		mux = gst_element_factory_make ("id3mux", NULL);
 416 	}
 417 
 418 	if (mux == NULL)
 419 		mux = gst_element_factory_make ("id3v2mux", NULL);
 420 
 421 	if (mux == NULL)
 422 		goto error;
 423 
 424 	gst_bin_add (GST_BIN (pipeline), mux);
 425 	if (!link_named_pad (srcpad, mux, "sink")) {
 426 		rb_debug ("couldn't link decoded pad to id3 muxer");
 427 		goto error;
 428 	}
 429 
 430 	gst_element_set_state (mux, GST_STATE_PAUSED);
 431 	if (tags != NULL) {
 432 		gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL);
 433 	}
 434 	rb_debug ("id3 tagger created");
 435 	return mux;
 436 
 437 error:
 438 	if (mux != NULL) {
 439 		g_object_unref (mux);
 440 	}
 441 	return NULL;
 442 }
 443 
 444 static GstElement *
 445 vorbis_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
 446 {
 447 	GstElement *mux;
 448 	GstElement *tagger;
 449 	GstElement *parser;
 450 
 451 	mux = gst_element_factory_make ("oggmux", NULL);
 452 	parser = gst_element_factory_make ("vorbisparse", NULL);
 453 	tagger = gst_element_factory_make ("vorbistag", NULL);
 454 	if (mux == NULL || parser == NULL || tagger == NULL)
 455 		goto error;
 456 
 457 	gst_bin_add_many (GST_BIN (pipeline), parser, tagger, mux, NULL);
 458 	if (!link_named_pad (srcpad, parser, "sink"))
 459 		goto error;
 460 	if (!gst_element_link_many (parser, tagger, mux, NULL))
 461 		goto error;
 462 
 463 	gst_element_set_state (parser, GST_STATE_PAUSED);
 464 	gst_element_set_state (tagger, GST_STATE_PAUSED);
 465 	gst_element_set_state (mux, GST_STATE_PAUSED);
 466 	if (tags != NULL) {
 467 		gst_tag_setter_merge_tags (GST_TAG_SETTER (tagger), tags, GST_TAG_MERGE_REPLACE_ALL);
 468 	}
 469 	return mux;
 470 
 471 error:
 472 	if (parser != NULL)
 473 		g_object_unref (parser);
 474 	if (tagger != NULL)
 475 		g_object_unref (tagger);
 476 	if (mux != NULL)
 477 		g_object_unref (mux);
 478 	return NULL;
 479 }
 480 
 481 static GstElement *
 482 mp4_tagger (GstElement *pipeline, GstPad *srcpad, GstTagList *tags)
 483 {
 484 	GstElement *mux;
 485 
 486 	mux = gst_element_factory_make ("mp4mux", NULL);
 487 	if (mux == NULL)
 488 		return NULL;
 489 
 490 	gst_bin_add (GST_BIN (pipeline), mux);
 491 	if (!link_named_pad (srcpad, mux, "audio_%d"))
 492 		goto error;
 493 
 494 	gst_element_set_state (mux, GST_STATE_PAUSED);
 495 	if (tags != NULL) {
 496 		gst_tag_setter_merge_tags (GST_TAG_SETTER (mux), tags, GST_TAG_MERGE_REPLACE_ALL);
 497 	}
 498 	return mux;
 499 
 500 error:
 501 	g_object_unref (mux);
 502 	return NULL;
 503 }
 504 
 505 static void
 506 metadata_save_new_decoded_pad_cb (GstElement *decodebin, GstPad *pad, gboolean last, RBMetaData *md)
 507 {
 508 	RBAddTaggerElem add_tagger_func = NULL;
 509 	GstElement *retag_end;
 510 	GstCaps *caps;
 511 	char *caps_str;
 512 	GHashTableIter iter;
 513 	gpointer key;
 514 	gpointer value;
 515 
 516 	if (md->priv->sink_linked) {
 517 		GError *error;
 518 		error = g_error_new (GST_STREAM_ERROR,
 519 				     GST_STREAM_ERROR_FORMAT,
 520 				     _("Unable to write tags to this file as it contains multiple streams"));
 521 		gst_element_post_message (decodebin,
 522 					  gst_message_new_error (GST_OBJECT (decodebin),
 523 								 error,
 524 								 NULL));
 525 		g_error_free (error);
 526 		return;
 527 	}
 528 
 529 	/* find a tagger function that accepts the caps */
 530 	caps = gst_pad_get_caps (pad);
 531 	caps_str = gst_caps_to_string (caps);
 532 	rb_debug ("finding tagger for src caps %s", caps_str);
 533 	g_free (caps_str);
 534 
 535 	g_hash_table_iter_init (&iter, md->priv->taggers);
 536 	while (g_hash_table_iter_next (&iter, &key, &value)) {
 537 		GstCaps *tagger_caps;
 538 		const char *media_type = (const char *)key;
 539 
 540 		tagger_caps = rb_gst_media_type_to_caps (media_type);
 541 		/* not sure this is right, really */
 542 		if (gst_caps_is_always_compatible (caps, tagger_caps)) {
 543 			caps_str = gst_caps_to_string (tagger_caps);
 544 			rb_debug ("matched sink caps %s", caps_str);
 545 			g_free (caps_str);
 546 
 547 			gst_caps_unref (tagger_caps);
 548 			add_tagger_func = (RBAddTaggerElem)value;
 549 			break;
 550 		}
 551 		gst_caps_unref (tagger_caps);
 552 	}
 553 	gst_caps_unref (caps);
 554 
 555 	/* add retagging element(s) */
 556 	if (add_tagger_func == NULL) {
 557 		GError *error;
 558 		error = g_error_new (GST_STREAM_ERROR,
 559 				     GST_STREAM_ERROR_FORMAT,
 560 				     _("Unable to write tags to this file as it is not encoded in a supported format"));
 561 		gst_element_post_message (decodebin,
 562 					  gst_message_new_error (GST_OBJECT (decodebin),
 563 								 error,
 564 								 NULL));
 565 		g_error_free (error);
 566 		return;
 567 	}
 568 	retag_end = add_tagger_func (md->priv->pipeline, pad, md->priv->tags);
 569 
 570 	/* link to the sink */
 571 	gst_element_link (retag_end, md->priv->sink);
 572 	md->priv->sink_linked = TRUE;
 573 }
 574 
 575 static gboolean
 576 factory_src_caps_intersect (GstElementFactory *factory, GstCaps *caps)
 577 {
 578 	const GList *templates;
 579 	const GList *l;
 580 
 581 	templates = gst_element_factory_get_static_pad_templates (factory);
 582 	for (l = templates; l != NULL; l = l->next) {
 583 		GstStaticPadTemplate *t = l->data;
 584 		GstCaps *tcaps;
 585 
 586 		if (t->direction != GST_PAD_SRC) {
 587 			continue;
 588 		}
 589 
 590 		tcaps = gst_static_pad_template_get_caps (t);
 591 		if (gst_caps_can_intersect (tcaps, caps)) {
 592 			gst_caps_unref (tcaps);
 593 			return TRUE;
 594 		}
 595 		gst_caps_unref (tcaps);
 596 	}
 597 	return FALSE;
 598 }
 599 
 600 static GstAutoplugSelectResult
 601 metadata_save_autoplug_select_cb (GstElement *decodebin, GstPad *pad, GstCaps *caps, GstElementFactory *factory, RBMetaData *md)
 602 {
 603 	GstCaps *src_caps;
 604 	gboolean is_any;
 605 	gboolean is_raw;
 606 	gboolean is_demuxer;
 607 
 608 	is_demuxer = (strstr (gst_element_factory_get_klass (factory), "Demuxer") != NULL);
 609 	if (is_demuxer) {
 610 		/* allow demuxers, since we're going to remux later */
 611 		return GST_AUTOPLUG_SELECT_TRY;
 612 	}
 613 
 614 	src_caps = gst_caps_new_any ();
 615 	is_any = gst_element_factory_can_src_caps (factory, src_caps);
 616 	gst_caps_unref (src_caps);
 617 	if (is_any) {
 618 		/* this is something like id3demux (though that will match the
 619 		 * above check), allow it so we can get to the actual decoder later
 620 		 */
 621 		return GST_AUTOPLUG_SELECT_TRY;
 622 	}
 623 
 624 	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");
 625 	is_raw = factory_src_caps_intersect (factory, src_caps);
 626 	gst_caps_unref (src_caps);
 627 
 628 	if (is_raw == FALSE) {
 629 		/* this is probably a parser or something, allow it */
 630 		return GST_AUTOPLUG_SELECT_TRY;
 631 	}
 632 
 633 	/* don't allow decoders */
 634 	return GST_AUTOPLUG_SELECT_EXPOSE;
 635 }
 636 
 637 static gboolean
 638 check_file_valid (const char *original, const char *newfile)
 639 {
 640 	RBMetaData *md = rb_metadata_new ();
 641 	GError *error = NULL;
 642 	gboolean ret;
 643 
 644 	rb_metadata_load (md, newfile, &error);
 645 	ret = (error == NULL);
 646 
 647 	/* TODO: check that the tags are correct? */
 648 
 649 	if (error != NULL)
 650 		g_error_free (error);
 651 	g_object_unref (md);
 652 	return ret;
 653 }
 654 
 655 
 656 void
 657 rb_metadata_save (RBMetaData *md, const char *uri, GError **error)
 658 {
 659 	GstElement *pipeline = NULL;
 660 	GstElement *urisrc = NULL;
 661 	GstElement *decodebin = NULL;
 662 	char *tmpname_prefix = NULL;
 663 	char *tmpname = NULL;
 664 	GOutputStream *stream = NULL;
 665 	GError *io_error = NULL;
 666 	GstBus *bus;
 667 	gboolean done;
 668 	GFile *src;
 669 	GFile *dest;
 670 
 671 	rb_debug ("saving metadata for uri: %s", uri);
 672 
 673 	tmpname_prefix = rb_uri_make_hidden (uri);
 674 	rb_debug ("temporary file name prefix: %s", tmpname_prefix);
 675 
 676 	rb_uri_mkstemp (tmpname_prefix, &tmpname, &stream, &io_error);
 677 	g_free (tmpname_prefix);
 678 	if (io_error != NULL) {
 679 		goto gio_error;
 680 	}
 681 
 682 	/* set up pipeline */
 683 	pipeline = gst_pipeline_new ("pipeline");
 684 	md->priv->pipeline = pipeline;
 685 	md->priv->sink_linked = FALSE;
 686 
 687 	urisrc = gst_element_make_from_uri (GST_URI_SRC, uri, "urisrc");
 688 	if (urisrc == NULL) {
 689 		g_set_error (error,
 690 			     RB_METADATA_ERROR,
 691 			     RB_METADATA_ERROR_MISSING_PLUGIN,
 692 			     _("Failed to create a source element; check your installation"));
 693 		rb_debug ("missing an element to load the uri, sadly");
 694 		goto out;
 695 	}
 696 
 697 	decodebin = gst_element_factory_make ("decodebin2", "decoder");
 698 	if (decodebin == NULL) {
 699 		g_set_error (error,
 700 			     RB_METADATA_ERROR,
 701 			     RB_METADATA_ERROR_MISSING_PLUGIN,
 702 			     _("Failed to create the 'decodebin2' element; check your GStreamer installation"));
 703 		goto out;
 704 	}
 705 
 706 	md->priv->sink = gst_element_factory_make ("giostreamsink", "sink");
 707 	if (md->priv->sink == NULL) {
 708 		g_set_error (error,
 709 			     RB_METADATA_ERROR,
 710 			     RB_METADATA_ERROR_MISSING_PLUGIN,
 711 			     _("Failed to create the 'giostreamsink' element; check your GStreamer installation"));
 712 		goto out;
 713 	}
 714 	g_object_set (md->priv->sink, "stream", stream, NULL);
 715 
 716 	gst_bin_add_many (GST_BIN (pipeline), urisrc, decodebin, md->priv->sink, NULL);
 717 	gst_element_link (urisrc, decodebin);
 718 
 719 	g_signal_connect_object (decodebin,
 720 				 "new-decoded-pad",
 721 				 G_CALLBACK (metadata_save_new_decoded_pad_cb),
 722 				 md, 0);
 723 	g_signal_connect_object (decodebin,
 724 				 "autoplug-select",
 725 				 G_CALLBACK (metadata_save_autoplug_select_cb),
 726 				 md, 0);
 727 
 728 	/* run pipeline .. */
 729 	gst_element_set_state (pipeline, GST_STATE_PLAYING);
 730 	bus = gst_element_get_bus (pipeline);
 731 	done = FALSE;
 732 	while (done == FALSE) {
 733 		GstMessage *message;
 734 
 735 		message = gst_bus_timed_pop (bus, GST_CLOCK_TIME_NONE);
 736 		if (message == NULL) {
 737 			/* can this even happen? */
 738 			rb_debug ("breaking out of bus polling loop");
 739 			break;
 740 		}
 741 
 742 		switch (GST_MESSAGE_TYPE (message)) {
 743 		case GST_MESSAGE_ERROR:
 744 			{
 745 				GError *gerror;
 746 				char *debug;
 747 
 748 				gst_message_parse_error (message, &gerror, &debug);
 749 				if (*error != NULL) {
 750 					rb_debug ("caught error: %s (%s), but we've already got one", gerror->message, debug);
 751 				} else {
 752 					rb_debug ("caught error: %s (%s)", gerror->message, debug);
 753 
 754 					g_clear_error (error);
 755 					*error = g_error_new_literal (RB_METADATA_ERROR,
 756 								      RB_METADATA_ERROR_GENERAL,
 757 								      gerror->message);
 758 				}
 759 				done = TRUE;
 760 
 761 				g_error_free (gerror);
 762 				g_free (debug);
 763 			}
 764 			break;
 765 
 766 		case GST_MESSAGE_EOS:
 767 			rb_debug ("got eos message");
 768 			done = TRUE;
 769 			break;
 770 
 771 		default:
 772 			break;
 773 		}
 774 
 775 		gst_message_unref (message);
 776 	}
 777 	gst_object_unref (bus);
 778 	gst_element_set_state (pipeline, GST_STATE_NULL);
 779 
 780 	if (g_output_stream_close (stream, NULL, &io_error) == FALSE) {
 781 		goto gio_error;
 782 	}
 783 	g_object_unref (stream);
 784 	stream = NULL;
 785 
 786 	if (*error == NULL) {
 787 		GFileInfo *originfo;
 788 
 789 		/* check to ensure the file isn't corrupt */
 790 		if (!check_file_valid (uri, tmpname)) {
 791 			g_set_error (error,
 792 				     RB_METADATA_ERROR,
 793 				     RB_METADATA_ERROR_INTERNAL,
 794 				     _("File corrupted during write"));
 795 			goto out_error;
 796 		}
 797 
 798 		src = g_file_new_for_uri (tmpname);
 799 		dest = g_file_new_for_uri (uri);
 800 
 801 		/* try to copy access and ownership attributes over, not likely to help much though */
 802 		originfo = g_file_query_info (dest, "access::*,owner::*", G_FILE_QUERY_INFO_NONE, NULL, NULL);
 803 		if (originfo) {
 804 			g_file_set_attributes_from_info (src, originfo, G_FILE_QUERY_INFO_NONE, NULL, NULL);
 805 			g_object_unref (originfo);
 806 		}
 807 
 808 		g_file_move (src, dest, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &io_error);
 809 		if (io_error != NULL) {
 810 			goto gio_error;
 811 		}
 812 		goto out;
 813 	}
 814 
 815 gio_error:
 816 	if (*error == NULL) {
 817 		g_set_error (error,
 818 			     RB_METADATA_ERROR,
 819 			     RB_METADATA_ERROR_IO,
 820 			     "%s",
 821 			     io_error->message);
 822 	}
 823 out_error:
 824 	if (tmpname != NULL) {
 825 		/* clean up temporary file */
 826 		rb_debug ("removing temporary file %s", tmpname);
 827 		dest = g_file_new_for_uri (tmpname);
 828 		if (g_file_delete (dest, NULL, NULL) == FALSE) {
 829 			rb_debug ("unable to remove temporary file?");
 830 		}
 831 		g_object_unref (dest);
 832 	}
 833 out:
 834 	g_free (tmpname);
 835 	if (pipeline != NULL)
 836 		gst_object_unref (GST_OBJECT (pipeline));
 837 }
 838 
 839 gboolean
 840 rb_metadata_get (RBMetaData *md, RBMetaDataField field, GValue *ret)
 841 {
 842 	const GstTagList *tags;
 843 	const char *tag;
 844 	GValue gstvalue = {0, };
 845 	GstClockTime duration;
 846 
 847 	/* special cases: mostly duration */
 848 	switch (field) {
 849 	case RB_METADATA_FIELD_DURATION:
 850 		duration = gst_discoverer_info_get_duration (md->priv->info);
 851 		if (duration != 0) {
 852 			g_value_init (ret, G_TYPE_ULONG);
 853 			g_value_set_ulong (ret, duration / (1000 * 1000 * 1000));
 854 			return TRUE;
 855 		} else {
 856 			rb_debug ("no duration available");
 857 			return FALSE;
 858 		}
 859 		break;
 860 	case RB_METADATA_FIELD_BITRATE:
 861 		if (md->priv->audio_bitrate != 0) {
 862 			g_value_init (ret, G_TYPE_ULONG);
 863 			g_value_set_ulong (ret, md->priv->audio_bitrate / 1000);
 864 			return TRUE;
 865 		} else {
 866 			return FALSE;
 867 		}
 868 	default:
 869 		break;
 870 	}
 871 
 872 	tags = gst_discoverer_info_get_tags (md->priv->info);
 873 	if (tags == NULL) {
 874 		return FALSE;
 875 	}
 876 
 877 	tag = rb_metadata_gst_field_to_gst_tag (field);
 878 	if (tag == NULL) {
 879 		return FALSE;
 880 	}
 881 
 882 
 883 	if (rb_metadata_get_field_type (field) == G_TYPE_STRING) {
 884 		char *str = NULL;
 885 		const char *v;
 886 		int i;
 887 
 888 		/* pick the first valid utf8 string, or if we find a later
 889 		 * string of which the first is a prefix, pick that.
 890 		 */
 891 		i = 0;
 892 		while (gst_tag_list_peek_string_index (tags, tag, i, &v)) {
 893 			if (g_utf8_validate (v, -1, NULL) && (str == NULL || g_str_has_prefix (v, str))) {
 894 				g_free (str);
 895 				str = g_strdup (v);
 896 			} else {
 897 				rb_debug ("ignoring %s", v);
 898 			}
 899 			i++;
 900 		}
 901 
 902 		if (str != NULL) {
 903 			str = g_strstrip (str);
 904 			g_value_init (ret, G_TYPE_STRING);
 905 			g_value_take_string (ret, str);
 906 			return TRUE;
 907 		} else {
 908 			return FALSE;
 909 		}
 910 	} else {
 911 		if (gst_tag_list_copy_value (&gstvalue, tags, tag) == FALSE) {
 912 			return FALSE;
 913 		}
 914 		g_value_init (ret, rb_metadata_get_field_type (field));
 915 		g_value_transform (&gstvalue, ret);
 916 		g_value_unset (&gstvalue);
 917 		return TRUE;
 918 	}
 919 }
 920 
 921 const char *
 922 rb_metadata_get_media_type (RBMetaData *md)
 923 {
 924 	return md->priv->mediatype;
 925 }
 926 
 927 gboolean
 928 rb_metadata_set (RBMetaData *md, RBMetaDataField field, const GValue *val)
 929 {
 930 	/* don't write this out */
 931 	if (field == RB_METADATA_FIELD_DURATION)
 932 		return TRUE;
 933 
 934 	if (field == RB_METADATA_FIELD_DATE && g_value_get_ulong (val) == 0) {
 935 		/* we should ask gstreamer to remove the tag,
 936 		 * but there is no easy way of doing so
 937 		 */
 938 	} else {
 939 		const char *tag;
 940 		GValue newval = {0,};
 941 
 942 		tag = rb_metadata_gst_field_to_gst_tag (field);
 943 		g_value_init (&newval, gst_tag_get_type (tag));
 944 		if (g_value_transform (val, &newval)) {
 945 			rb_debug ("Setting %s",tag);
 946 
 947 			if (md->priv->tags == NULL) {
 948 				md->priv->tags = gst_tag_list_new ();
 949 			}
 950 
 951 			gst_tag_list_add_values (md->priv->tags,
 952 						 GST_TAG_MERGE_APPEND,
 953 						 tag, &newval,
 954 						 NULL);
 955 		}
 956 		g_value_unset (&newval);
 957 	}
 958 
 959 	return TRUE;
 960 }
 961 
 962 static void
 963 rb_metadata_finalize (GObject *object)
 964 {
 965 	RBMetaData *md;
 966 	md = RB_METADATA (object);
 967 	rb_metadata_reset (md);
 968 
 969 	G_OBJECT_CLASS (rb_metadata_parent_class)->finalize (object);
 970 }
 971 
 972 static void
 973 rb_metadata_init (RBMetaData *md)
 974 {
 975 	md->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((md), RB_TYPE_METADATA, RBMetaDataPrivate));
 976 
 977 	md->priv->taggers = g_hash_table_new (g_str_hash, g_str_equal);
 978 
 979 	if (gst_element_factory_find ("giostreamsink") == FALSE) {
 980 		rb_debug ("giostreamsink not found, can't tag anything");
 981 	} else {
 982 		if (gst_element_factory_find ("vorbistag") &&
 983 		    gst_element_factory_find ("vorbisparse") &&
 984 		    gst_element_factory_find ("oggmux")) {
 985 			rb_debug ("ogg vorbis tagging available");
 986 			g_hash_table_insert (md->priv->taggers, "audio/x-vorbis", (gpointer)vorbis_tagger);
 987 		}
 988 
 989 		if (gst_element_factory_find ("flactag")) {
 990 			rb_debug ("flac tagging available");
 991 			g_hash_table_insert (md->priv->taggers, "audio/x-flac", flac_tagger);
 992 		}
 993 
 994 		/* id3mux > 0.10.12 is the new one, not the broken old one. */
 995 		if (gst_element_factory_find ("id3v2mux") || check_gst_plugin_version ("id3mux", "id3mux", 0, 10, 12)) {
 996 			rb_debug ("id3 tagging available");
 997 			g_hash_table_insert (md->priv->taggers, "audio/mpeg", mp3_tagger);
 998 		}
 999 
1000 		if (gst_element_factory_find ("mp4mux")) {
1001 			rb_debug ("mp4 tagging available");
1002 			g_hash_table_insert (md->priv->taggers, "audio/x-aac", mp4_tagger);
1003 		}
1004 	}
1005 
1006 	md->priv->jpeg_image_caps = gst_caps_from_string ("image/jpeg, framerate = (fraction) [ 0, 1/1 ]");
1007 }
1008 
1009 static void
1010 rb_metadata_class_init (RBMetaDataClass *klass)
1011 {
1012 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
1013 
1014 	object_class->finalize = rb_metadata_finalize;
1015 
1016 	g_type_class_add_private (klass, sizeof (RBMetaDataPrivate));
1017 	rb_metadata_gst_register_transforms ();
1018 }
1019 
1020 RBMetaData *
1021 rb_metadata_new (void)
1022 {
1023 	return RB_METADATA (g_object_new (RB_TYPE_METADATA, NULL, NULL));
1024 }