hythmbox-2.98/plugins/mtpdevice/rb-mtp-gst-sink.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found rb-mtp-gst-sink.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
  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  *  Copyright (C) 2009  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 <string.h>
 32 
 33 #include <glib/gi18n.h>
 34 #include <libmtp.h>
 35 #include <gst/gst.h>
 36 
 37 #include "rb-mtp-thread.h"
 38 #include "rb-debug.h"
 39 #include "rb-file-helpers.h"
 40 
 41 #define RB_TYPE_MTP_SINK (rb_mtp_sink_get_type())
 42 #define RB_MTP_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RB_TYPE_MTP_SINK,RBMTPSink))
 43 #define RB_MTP_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RB_TYPE_MTP_SINK,RBMTPSinkClass))
 44 #define RB_IS_MTP_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RB_TYPE_MTP_SINK))
 45 #define RB_IS_MTP_SINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),RB_TYPE_MTP_SINK))
 46 
 47 typedef struct _RBMTPSink RBMTPSink;
 48 typedef struct _RBMTPSinkClass RBMTPSinkClass;
 49 
 50 struct _RBMTPSink
 51 {
 52 	GstBin parent;
 53 
 54 	RBMtpThread *device_thread;
 55 
 56 	LIBMTP_track_t *track;
 57 	char **folder_path;
 58 	char *tempfile;
 59 
 60 	GstElement *fdsink;
 61 	GstPad *ghostpad;
 62 
 63 	GError *upload_error;
 64 	GMutex upload_mutex;
 65 	GCond upload_cond;
 66 	gboolean got_folder;
 67 	gboolean upload_done;
 68 };
 69 
 70 struct _RBMTPSinkClass
 71 {
 72 	GstBinClass parent_class;
 73 };
 74 
 75 enum
 76 {
 77 	PROP_0,
 78 	PROP_URI,
 79 	PROP_MTP_TRACK,
 80 	PROP_FOLDER_PATH,
 81 	PROP_DEVICE_THREAD
 82 };
 83 
 84 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
 85 	GST_PAD_SINK,
 86 	GST_PAD_ALWAYS,
 87 	GST_STATIC_CAPS_ANY);
 88 
 89 static GstElementDetails rb_mtp_sink_details =
 90 GST_ELEMENT_DETAILS ("RB MTP Sink",
 91 	"Sink/File",
 92 	"Uploads tracks to MTP devices",
 93 	"Jonathan Matthew <jonathan@d14n.org>");
 94 
 95 GType rb_mtp_sink_get_type (void);
 96 static void rb_mtp_sink_uri_handler_init (gpointer g_iface, gpointer iface_data);
 97 
 98 static void
 99 _do_init (GType mtp_sink_type)
100 {
101 	static const GInterfaceInfo urihandler_info = {
102 		rb_mtp_sink_uri_handler_init,
103 		NULL,
104 		NULL
105 	};
106 
107 	g_type_add_interface_static (mtp_sink_type, GST_TYPE_URI_HANDLER,
108 			&urihandler_info);
109 }
110 
111 GST_BOILERPLATE_FULL (RBMTPSink, rb_mtp_sink, GstBin, GST_TYPE_BIN, _do_init);
112 
113 static void
114 rb_mtp_sink_base_init (gpointer g_class)
115 {
116 	GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
117 	gst_element_class_add_pad_template (element_class,
118 		gst_static_pad_template_get (&sinktemplate));
119 	gst_element_class_set_details (element_class, &rb_mtp_sink_details);
120 }
121 
122 static void
123 rb_mtp_sink_init (RBMTPSink *sink, RBMTPSinkClass *klass)
124 {
125 	GstPad *pad;
126 
127 	/* create actual sink */
128 	sink->fdsink = gst_element_factory_make ("fdsink", NULL);
129 	if (sink->fdsink == NULL) {
130 		g_warning ("couldn't create fdsink element");
131 		return;
132 	}
133 
134 	gst_bin_add (GST_BIN (sink), sink->fdsink);
135 	gst_object_ref (sink->fdsink);
136 
137 	/* create ghost pad */
138 	pad = gst_element_get_pad (sink->fdsink, "sink");
139 	sink->ghostpad = gst_ghost_pad_new ("sink", pad);
140 	gst_element_add_pad (GST_ELEMENT (sink), sink->ghostpad);
141 	gst_object_ref (sink->ghostpad);
142 	gst_object_unref (pad);
143 
144 }
145 
146 static GstStateChangeReturn
147 rb_mtp_sink_open_tempfile (RBMTPSink *sink)
148 {
149 	int fd;
150 	GError *tmperror = NULL;
151 
152 	fd = g_file_open_tmp ("rb-mtp-temp-XXXXXX", &sink->tempfile, &tmperror);
153 	if (fd == -1) {
154 		GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (_("Unable to open temporary file: %s"), tmperror->message), NULL);
155 		return GST_STATE_CHANGE_FAILURE;
156 	}
157 	rb_debug ("opened temporary file %s", sink->tempfile);
158 
159 	g_object_set (sink->fdsink, "fd", fd, "sync", FALSE, NULL);
160 	return GST_STATE_CHANGE_SUCCESS;
161 }
162 
163 static GstStateChangeReturn
164 rb_mtp_sink_close_tempfile (RBMTPSink *sink)
165 {
166 	if (sink->tempfile != NULL) {
167 		rb_debug ("deleting tempfile %s", sink->tempfile);
168 		remove (sink->tempfile);
169 		g_free (sink->tempfile);
170 		sink->tempfile = NULL;
171 	}
172 
173 	return GST_STATE_CHANGE_SUCCESS;
174 }
175 
176 static void
177 folder_callback (uint32_t folder_id, RBMTPSink *sink)
178 {
179 	g_mutex_lock (&sink->upload_mutex);
180 	if (folder_id == 0) {
181 		rb_debug ("mtp folder create failed");
182 	} else {
183 		rb_debug ("mtp folder for upload: %u", folder_id);
184 		sink->track->parent_id = folder_id;
185 	}
186 
187 	sink->got_folder = TRUE;
188 
189 	g_cond_signal (&sink->upload_cond);
190 	g_mutex_unlock (&sink->upload_mutex);
191 }
192 
193 static void
194 upload_callback (LIBMTP_track_t *track, GError *error, RBMTPSink *sink)
195 {
196 	rb_debug ("mtp upload callback for %s: item ID %d", track->filename, track->item_id);
197 	g_mutex_lock (&sink->upload_mutex);
198 
199 	if (error != NULL) {
200 		sink->upload_error = g_error_copy (error);
201 	}
202 	sink->upload_done = TRUE;
203 
204 	g_cond_signal (&sink->upload_cond);
205 	g_mutex_unlock (&sink->upload_mutex);
206 }
207 
208 static void
209 rb_mtp_sink_handle_message (GstBin *bin, GstMessage *message)
210 {
211 	/* when we get an EOS message from the fdsink, close the fd and upload the
212 	 * file to the device.
213 	 */
214 	if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS) {
215 		int fd;
216 		struct stat stat_buf;
217 
218 		RBMTPSink *sink = RB_MTP_SINK (bin);
219 
220 		/* fill in the file size and close the fd */
221 		g_object_get (sink->fdsink, "fd", &fd, NULL);
222 		fstat (fd, &stat_buf);
223 		sink->track->filesize = stat_buf.st_size;
224 		close (fd);
225 
226 		rb_debug ("handling EOS from fdsink; file size is %" G_GUINT64_FORMAT, sink->track->filesize);
227 
228 		/* we can just block waiting for mtp thread operations to finish here
229 		 * as we're on a streaming thread.
230 		 */
231 		g_mutex_lock (&sink->upload_mutex);
232 
233 		if (sink->folder_path != NULL) {
234 			/* find or create the target folder.
235 			 * if this fails, we just upload to the default music folder
236 			 * rather than giving up entirely.
237 			 */
238 			sink->got_folder = FALSE;
239 			rb_mtp_thread_create_folder (sink->device_thread,
240 						     (const char **)sink->folder_path,
241 						     (RBMtpCreateFolderCallback) folder_callback,
242 						     g_object_ref (sink),
243 						     g_object_unref);
244 			while (sink->got_folder == FALSE) {
245 				g_cond_wait (&sink->upload_cond, &sink->upload_mutex);
246 			}
247 		}
248 
249 		/* and upload the file */
250 		sink->upload_done = FALSE;
251 		rb_mtp_thread_upload_track (sink->device_thread,
252 					    sink->track,
253 					    sink->tempfile,
254 					    (RBMtpUploadCallback) upload_callback,
255 					    g_object_ref (sink),
256 					    g_object_unref);
257 
258 		while (sink->upload_done == FALSE) {
259 			g_cond_wait (&sink->upload_cond, &sink->upload_mutex);
260 		}
261 		g_mutex_unlock (&sink->upload_mutex);
262 
263 		/* post error message if the upload failed - this should get there before
264 		 * this EOS message does, so it should work OK.
265 		 */
266 		if (sink->upload_error != NULL) {
267 			int code;
268 
269 			switch (sink->upload_error->code) {
270 			case RB_MTP_THREAD_ERROR_NO_SPACE:
271 				code = GST_RESOURCE_ERROR_NO_SPACE_LEFT;
272 				break;
273 
274 			default:
275 			case RB_MTP_THREAD_ERROR_SEND_TRACK:
276 				code = GST_RESOURCE_ERROR_WRITE;
277 				break;
278 			}
279 
280 			GST_WARNING_OBJECT (sink, "error: %s", sink->upload_error->message);
281 			gst_element_message_full (GST_ELEMENT (sink),
282 						  GST_MESSAGE_ERROR,
283 						  GST_RESOURCE_ERROR, code,
284 						  g_strdup (sink->upload_error->message), NULL,
285 						  __FILE__, GST_FUNCTION, __LINE__);
286 		}
287 	}
288 
289 	GST_BIN_CLASS (parent_class)->handle_message (bin, message);
290 }
291 
292 static GstStateChangeReturn
293 rb_mtp_sink_change_state (GstElement *element, GstStateChange transition)
294 {
295 	GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
296 	RBMTPSink *sink = RB_MTP_SINK (element);
297 
298 	switch (transition) {
299 		case GST_STATE_CHANGE_NULL_TO_READY:
300 			ret = rb_mtp_sink_open_tempfile (sink);
301 			if (ret != GST_STATE_CHANGE_SUCCESS)
302 				return ret;
303 			break;
304 
305 		case GST_STATE_CHANGE_READY_TO_PAUSED:
306 			break;
307 
308 		case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
309 			break;
310 
311 		default:
312 			break;
313 	}
314 
315 	ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
316 
317 	switch (transition) {
318 		case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
319 			break;
320 		case GST_STATE_CHANGE_PAUSED_TO_READY:
321 			break;
322 		case GST_STATE_CHANGE_READY_TO_NULL:
323 			ret = rb_mtp_sink_close_tempfile (sink);
324 			break;
325 		default:
326 			break;
327 	}
328 
329 	return ret;
330 }
331 
332 static void
333 rb_mtp_sink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
334 {
335 	RBMTPSink *sink = RB_MTP_SINK (object);
336 	char **path;
337 
338 	switch (prop_id) {
339 	case PROP_MTP_TRACK:
340 		sink->track = g_value_get_pointer (value);
341 		break;
342 	case PROP_FOLDER_PATH:
343 		path = g_value_get_pointer (value);
344 		sink->folder_path = g_strdupv (path);
345 		break;
346 	case PROP_DEVICE_THREAD:
347 		sink->device_thread = g_value_dup_object (value);
348 		break;
349 	default:
350 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
351 		break;
352 	}
353 }
354 
355 static void
356 rb_mtp_sink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
357 {
358 	RBMTPSink *sink = RB_MTP_SINK (object);
359 
360 	switch (prop_id) {
361 	case PROP_MTP_TRACK:
362 		g_value_set_pointer (value, sink->track);
363 		break;
364 	case PROP_FOLDER_PATH:
365 		g_value_set_pointer (value, sink->folder_path);
366 		break;
367 	case PROP_DEVICE_THREAD:
368 		g_value_set_object (value, sink->device_thread);
369 		break;
370 	default:
371 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
372 		break;
373 	}
374 }
375 
376 static void
377 rb_mtp_sink_dispose (GObject *object)
378 {
379 	RBMTPSink *sink;
380 	sink = RB_MTP_SINK (object);
381 
382 	if (sink->ghostpad) {
383 		gst_object_unref (sink->ghostpad);
384 		sink->ghostpad = NULL;
385 	}
386 
387 	if (sink->fdsink) {
388 		gst_object_unref (sink->fdsink);
389 		sink->fdsink = NULL;
390 	}
391 
392 	if (sink->device_thread) {
393 		g_object_unref (sink->device_thread);
394 		sink->device_thread = NULL;
395 	}
396 
397 	G_OBJECT_CLASS (parent_class)->dispose (object);
398 }
399 
400 static void
401 rb_mtp_sink_finalize (GObject *object)
402 {
403 	RBMTPSink *sink;
404 	sink = RB_MTP_SINK (object);
405 
406 	if (sink->upload_error) {
407 		g_error_free (sink->upload_error);
408 	}
409 
410 	if (sink->folder_path) {
411 		g_strfreev (sink->folder_path);
412 	}
413 
414 	G_OBJECT_CLASS (parent_class)->finalize (object);
415 }
416 
417 static void
418 rb_mtp_sink_class_init (RBMTPSinkClass *klass)
419 {
420 	GObjectClass *gobject_class;
421 	GstElementClass *element_class;
422 	GstBinClass *bin_class;
423 
424 	gobject_class = G_OBJECT_CLASS (klass);
425 	gobject_class->dispose = rb_mtp_sink_dispose;
426 	gobject_class->finalize = rb_mtp_sink_finalize;
427 	gobject_class->set_property = rb_mtp_sink_set_property;
428 	gobject_class->get_property = rb_mtp_sink_get_property;
429 
430 	element_class = GST_ELEMENT_CLASS (klass);
431 	element_class->change_state = rb_mtp_sink_change_state;
432 	
433 	bin_class = GST_BIN_CLASS (klass);
434 	bin_class->handle_message = rb_mtp_sink_handle_message;
435 
436 	g_object_class_install_property (gobject_class,
437 					 PROP_MTP_TRACK,
438 					 g_param_spec_pointer ("mtp-track",
439 						 	       "libmtp track",
440 							       "libmtp track",
441 							       G_PARAM_READWRITE));
442 	g_object_class_install_property (gobject_class,
443 					 PROP_FOLDER_PATH,
444 					 g_param_spec_pointer ("folder-path",
445 							       "folder path",
446 							       "upload folder path",
447 							       G_PARAM_READWRITE));
448 	g_object_class_install_property (gobject_class,
449 					 PROP_DEVICE_THREAD,
450 					 g_param_spec_object ("device-thread",
451 							      "device-thread",
452 							      "device handling thread",
453 							      G_TYPE_OBJECT,
454 							      G_PARAM_READWRITE));
455 }
456 
457 
458 /* URI handler interface */
459 
460 static guint
461 rb_mtp_sink_uri_get_type (void)
462 {
463 	return GST_URI_SINK;
464 }
465 
466 static gchar **
467 rb_mtp_sink_uri_get_protocols (void)
468 {
469 	static gchar *protocols[] = {"xrbmtp", NULL};
470 	return protocols;
471 }
472 
473 static const gchar *
474 rb_mtp_sink_uri_get_uri (GstURIHandler *handler)
475 {
476 	/* more or less */
477 	return "xrbmtp://";
478 }
479 
480 static gboolean
481 rb_mtp_sink_uri_set_uri (GstURIHandler *handler, const gchar *uri)
482 {
483 	RBMTPSink *sink = RB_MTP_SINK (handler);
484 
485 	if (GST_STATE (sink) == GST_STATE_PLAYING || GST_STATE (sink) == GST_STATE_PAUSED) {
486 		return FALSE;
487 	}
488 
489 	if (g_str_has_prefix (uri, "xrbmtp://") == FALSE) {
490 		return FALSE;
491 	}
492 
493 	/* URI doesn't actually contain any information, it all comes from the track */
494 
495 	return TRUE;
496 }
497 
498 static void
499 rb_mtp_sink_uri_handler_init (gpointer g_iface, gpointer iface_data)
500 {
501 	GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
502 
503 	iface->get_type = rb_mtp_sink_uri_get_type;
504 	iface->get_protocols = rb_mtp_sink_uri_get_protocols;
505 	iface->get_uri = rb_mtp_sink_uri_get_uri;
506 	iface->set_uri = rb_mtp_sink_uri_set_uri;
507 }
508 
509 static gboolean
510 plugin_init (GstPlugin *plugin)
511 {
512 	gboolean ret = gst_element_register (plugin, "rbmtpsink", GST_RANK_PRIMARY, RB_TYPE_MTP_SINK);
513 	return ret;
514 }
515 
516 GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
517 			  GST_VERSION_MINOR,
518 			  "rbmtpsink",
519 			  "element to upload files to MTP devices",
520 			  plugin_init,
521 			  VERSION,
522 			  "GPL",
523 			  PACKAGE,
524 			  "");