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

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found rb-mtp-gst-src.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 #include <stdio.h>
 33 #include <sys/types.h>
 34 #include <sys/stat.h>
 35 #include <fcntl.h>
 36 
 37 #include <glib/gi18n.h>
 38 #include <libmtp.h>
 39 #include <gst/gst.h>
 40 #include <gst/base/gstbasesrc.h>
 41 
 42 #include "rb-mtp-thread.h"
 43 #include "rb-debug.h"
 44 #include "rb-file-helpers.h"
 45 
 46 #define RB_TYPE_MTP_SRC (rb_mtp_src_get_type())
 47 #define RB_MTP_SRC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RB_TYPE_MTP_SRC,RBMTPSrc))
 48 #define RB_MTP_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RB_TYPE_MTP_SRC,RBMTPSrcClass))
 49 #define RB_IS_MTP_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RB_TYPE_MTP_SRC))
 50 #define RB_IS_MTP_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),RB_TYPE_MTP_SRC))
 51 
 52 typedef struct _RBMTPSrc RBMTPSrc;
 53 typedef struct _RBMTPSrcClass RBMTPSrcClass;
 54 
 55 struct _RBMTPSrc
 56 {
 57 	GstBaseSrc parent;
 58 
 59 	RBMtpThread *device_thread;
 60 
 61 	char *track_uri;
 62 	uint32_t track_id;
 63 	char *tempfile;
 64 	int fd;
 65 	guint64 read_position;
 66 
 67 	GError *download_error;
 68 	GMutex download_mutex;
 69 	GCond download_cond;
 70 	gboolean download_done;
 71 };
 72 
 73 struct _RBMTPSrcClass
 74 {
 75 	GstBaseSrcClass parent_class;
 76 };
 77 
 78 enum
 79 {
 80 	PROP_0,
 81 	PROP_URI,
 82 	PROP_DEVICE_THREAD
 83 };
 84 
 85 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
 86 	GST_PAD_SRC,
 87 	GST_PAD_ALWAYS,
 88 	GST_STATIC_CAPS_ANY);
 89 
 90 GType rb_mtp_src_get_type (void);
 91 static void rb_mtp_src_uri_handler_init (gpointer g_iface, gpointer iface_data);
 92 
 93 static void
 94 _do_init (GType mtp_src_type)
 95 {
 96 	static const GInterfaceInfo urihandler_info = {
 97 		rb_mtp_src_uri_handler_init,
 98 		NULL,
 99 		NULL
100 	};
101 
102 	g_type_add_interface_static (mtp_src_type, GST_TYPE_URI_HANDLER,
103 			&urihandler_info);
104 }
105 
106 GST_BOILERPLATE_FULL (RBMTPSrc, rb_mtp_src, GstBaseSrc, GST_TYPE_BASE_SRC, _do_init);
107 
108 static void
109 rb_mtp_src_base_init (gpointer g_class)
110 {
111 	GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
112 	gst_element_class_add_pad_template (element_class,
113 		gst_static_pad_template_get (&srctemplate));
114 	gst_element_class_set_details_simple (element_class,
115 					      "RB MTP Source",
116 					      "Source/File",
117 					      "Downloads and plays files from MTP devices",
118 					      "Jonathan Matthew <jonathan@d14n.org>");
119 }
120 
121 static void
122 rb_mtp_src_init (RBMTPSrc *src, RBMTPSrcClass *klass)
123 {
124 }
125 
126 static gboolean
127 rb_mtp_src_set_uri (RBMTPSrc *src, const char *uri)
128 {
129 	const char *trackid;
130 
131 	rb_debug ("stream uri: %s", uri);
132 
133 	src->track_uri = g_strdup (uri);
134 
135 	/* extract the track ID */
136 	if (g_str_has_prefix (uri, "xrbmtp://") == FALSE) {
137 		rb_debug ("unexpected uri scheme");
138 		return FALSE;
139 	}
140 	trackid = uri + strlen ("xrbmtp://");
141 	src->track_id = strtoul (trackid, NULL, 0);
142 
143 	/* delete any existing file */
144 	if (src->tempfile != NULL) {
145 		rb_debug ("deleting tempfile %s", src->tempfile);
146 		remove (src->tempfile);
147 		g_free (src->tempfile);
148 		src->tempfile = NULL;
149 	}
150 
151 	return TRUE;
152 }
153 
154 static GstFlowReturn
155 rb_mtp_src_create (GstBaseSrc *basesrc, guint64 offset, guint length, GstBuffer **buffer)
156 {
157 	RBMTPSrc *src = RB_MTP_SRC (basesrc);
158 	GstBuffer *buf;
159 	int ret;
160 
161 	/* seek if required */
162 	if (offset != src->read_position) {
163 		off_t res;
164 		res = lseek (src->fd, offset, SEEK_SET);
165 		if (res < 0 || res != offset) {
166 			GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
167 			return GST_FLOW_ERROR;
168 		}
169 
170 		src->read_position = offset;
171 	}
172 
173 	buf = gst_buffer_try_new_and_alloc (length);
174 	if (buf == NULL && length > 0) {
175 		GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", length);
176 		return GST_FLOW_ERROR;
177 	}
178 
179 	if (length > 0) {
180 		ret = read (src->fd, GST_BUFFER_DATA (buf), length);
181 		if (ret < length) {
182 			GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
183 			gst_buffer_unref (buf);
184 			return GST_FLOW_ERROR;
185 		}
186 
187 		length = ret;
188 		GST_BUFFER_SIZE (buf) = length;
189 		GST_BUFFER_OFFSET (buf) = offset;
190 		GST_BUFFER_OFFSET_END (buf) = offset + length;
191 
192 		src->read_position += length;
193 	}
194 
195 	*buffer = buf;
196 	return GST_FLOW_OK;
197 }
198 
199 static gboolean
200 rb_mtp_src_is_seekable (GstBaseSrc *basesrc)
201 {
202 	return TRUE;
203 }
204 
205 static gboolean
206 rb_mtp_src_get_size (GstBaseSrc *basesrc, guint64 *size)
207 {
208 	RBMTPSrc *src = RB_MTP_SRC (basesrc);
209 	struct stat stat_results;
210 
211 	if (fstat (src->fd, &stat_results) < 0) {
212 		return FALSE;
213 	}
214 
215 	*size = stat_results.st_size;
216 	return TRUE;
217 }
218 
219 static void
220 download_cb (LIBMTP_track_t *track, const char *filename, GError *error, RBMTPSrc *src)
221 {
222 	rb_debug ("mtp download callback for %s: %s", filename, error ? error->message : "OK");
223 	g_mutex_lock (&src->download_mutex);
224 
225 	if (filename == NULL) {
226 		src->download_error = g_error_copy (error);
227 	} else {
228 		src->tempfile = g_strdup (filename);
229 	}
230 	src->download_done = TRUE;
231 
232 	g_cond_signal (&src->download_cond);
233 	g_mutex_unlock (&src->download_mutex);
234 }
235 
236 static gboolean
237 rb_mtp_src_start (GstBaseSrc *basesrc)
238 {
239 	RBMTPSrc *src = RB_MTP_SRC (basesrc);
240 
241 	/* download the file, if we haven't already */
242 	if (src->tempfile == NULL) {
243 		g_mutex_lock (&src->download_mutex);
244 		src->download_done = FALSE;
245 		rb_mtp_thread_download_track (src->device_thread,
246 					      src->track_id,
247 					      "",
248 					      (RBMtpDownloadCallback)download_cb,
249 					      g_object_ref (src),
250 					      g_object_unref);
251 
252 		while (src->download_done == FALSE) {
253 			g_cond_wait (&src->download_cond, &src->download_mutex);
254 		}
255 		g_mutex_unlock (&src->download_mutex);
256 		rb_debug ("download finished");
257 
258 		if (src->download_error) {
259 			int code;
260 			switch (src->download_error->code) {
261 			case RB_MTP_THREAD_ERROR_NO_SPACE:
262 				code = GST_RESOURCE_ERROR_NO_SPACE_LEFT;
263 				break;
264 
265 			case RB_MTP_THREAD_ERROR_TEMPFILE:
266 				code = GST_RESOURCE_ERROR_OPEN_WRITE;
267 				break;
268 
269 			default:
270 			case RB_MTP_THREAD_ERROR_GET_TRACK:
271 				code = GST_RESOURCE_ERROR_READ;
272 				break;
273 
274 			}
275 
276 			GST_WARNING_OBJECT (src, "error: %s", src->download_error->message);
277 			gst_element_message_full (GST_ELEMENT (src),
278 						  GST_MESSAGE_ERROR,
279 						  GST_RESOURCE_ERROR, code,
280 						  src->download_error->message, NULL,
281 						  __FILE__, GST_FUNCTION, __LINE__);
282 			return FALSE;
283 		}
284 	}
285 
286 	/* open file - maybe do this in create after waiting for it to finish downloading */
287 	src->fd = open (src->tempfile, O_RDONLY, 0);
288 	if (src->fd < 0) {
289 		switch (errno) {
290 		case ENOENT:
291 			GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL),
292 					   ("Could not find temporary file"));
293 			break;
294 		default:
295 			GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
296 					   ("Could not open temporary file for reading"));
297 		}
298 		return FALSE;
299 	}
300 
301 	src->read_position = 0;
302 
303 	return TRUE;
304 }
305 
306 static gboolean
307 rb_mtp_src_stop (GstBaseSrc *basesrc)
308 {
309 	RBMTPSrc *src = RB_MTP_SRC (basesrc);
310 	close (src->fd);
311 	src->fd = 0;
312 	src->read_position = 0;
313 	return TRUE;
314 }
315 
316 static void
317 rb_mtp_src_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
318 {
319 	RBMTPSrc *src = RB_MTP_SRC (object);
320 
321 	switch (prop_id) {
322 	case PROP_URI:
323 		rb_mtp_src_set_uri (src, g_value_get_string (value));
324 		break;
325 	case PROP_DEVICE_THREAD:
326 		src->device_thread = g_value_dup_object (value);
327 		break;
328 	default:
329 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
330 		break;
331 	}
332 }
333 
334 static void
335 rb_mtp_src_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
336 {
337 	RBMTPSrc *src = RB_MTP_SRC (object);
338 
339 	switch (prop_id) {
340 	case PROP_URI:
341 		g_value_set_string (value, src->track_uri);
342 		break;
343 	case PROP_DEVICE_THREAD:
344 		g_value_set_object (value, src->device_thread);
345 		break;
346 	default:
347 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
348 		break;
349 	}
350 }
351 
352 static void
353 rb_mtp_src_dispose (GObject *object)
354 {
355 	RBMTPSrc *src;
356 	src = RB_MTP_SRC (object);
357 
358 	if (src->device_thread) {
359 		g_object_unref (src->device_thread);
360 		src->device_thread = NULL;
361 	}
362 
363 	G_OBJECT_CLASS (parent_class)->dispose (object);
364 }
365 
366 static void
367 rb_mtp_src_finalize (GObject *object)
368 {
369 	RBMTPSrc *src;
370 	src = RB_MTP_SRC (object);
371 
372 	if (src->download_error) {
373 		g_error_free (src->download_error);
374 	}
375 
376 	if (src->tempfile != NULL) {
377 		rb_debug ("deleting tempfile %s", src->tempfile);
378 		remove (src->tempfile);
379 		g_free (src->tempfile);
380 		src->tempfile = NULL;
381 	}
382 
383 	G_OBJECT_CLASS (parent_class)->finalize (object);
384 }
385 
386 static void
387 rb_mtp_src_class_init (RBMTPSrcClass *klass)
388 {
389 	GObjectClass *gobject_class;
390 	GstBaseSrcClass *basesrc_class;
391 
392 	gobject_class = G_OBJECT_CLASS (klass);
393 	gobject_class->dispose = rb_mtp_src_dispose;
394 	gobject_class->finalize = rb_mtp_src_finalize;
395 	gobject_class->set_property = rb_mtp_src_set_property;
396 	gobject_class->get_property = rb_mtp_src_get_property;
397 
398 	basesrc_class = GST_BASE_SRC_CLASS (klass);
399 	basesrc_class->start = GST_DEBUG_FUNCPTR (rb_mtp_src_start);
400 	basesrc_class->stop = GST_DEBUG_FUNCPTR (rb_mtp_src_stop);
401 	basesrc_class->is_seekable = GST_DEBUG_FUNCPTR (rb_mtp_src_is_seekable);
402 	basesrc_class->get_size = GST_DEBUG_FUNCPTR (rb_mtp_src_get_size);
403 	basesrc_class->create = GST_DEBUG_FUNCPTR (rb_mtp_src_create);
404 
405 	g_object_class_install_property (gobject_class,
406 					 PROP_URI,
407 					 g_param_spec_string ("uri",
408 							      "uri",
409 							      "MTP track uri",
410 							      NULL,
411 							      G_PARAM_READWRITE));
412 	g_object_class_install_property (gobject_class,
413 					 PROP_DEVICE_THREAD,
414 					 g_param_spec_object ("device-thread",
415 							      "device-thread",
416 							      "device handling thread",
417 							      G_TYPE_OBJECT,
418 							      G_PARAM_READWRITE));
419 }
420 
421 
422 /* URI handler interface */
423 
424 static guint
425 rb_mtp_src_uri_get_type (void)
426 {
427 	return GST_URI_SRC;
428 }
429 
430 static gchar **
431 rb_mtp_src_uri_get_protocols (void)
432 {
433 	static gchar *protocols[] = {"xrbmtp", NULL};
434 	return protocols;
435 }
436 
437 static const gchar *
438 rb_mtp_src_uri_get_uri (GstURIHandler *handler)
439 {
440 	RBMTPSrc *src = RB_MTP_SRC (handler);
441 
442 	return src->track_uri;
443 }
444 
445 static gboolean
446 rb_mtp_src_uri_set_uri (GstURIHandler *handler, const gchar *uri)
447 {
448 	RBMTPSrc *src = RB_MTP_SRC (handler);
449 
450 	if (GST_STATE (src) == GST_STATE_PLAYING || GST_STATE (src) == GST_STATE_PAUSED) {
451 		return FALSE;
452 	}
453 
454 	if (g_str_has_prefix (uri, "xrbmtp://") == FALSE) {
455 		return FALSE;
456 	}
457 
458 	return rb_mtp_src_set_uri (src, uri);
459 }
460 
461 static void
462 rb_mtp_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
463 {
464 	GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
465 
466 	iface->get_type = rb_mtp_src_uri_get_type;
467 	iface->get_protocols = rb_mtp_src_uri_get_protocols;
468 	iface->get_uri = rb_mtp_src_uri_get_uri;
469 	iface->set_uri = rb_mtp_src_uri_set_uri;
470 }
471 
472 static gboolean
473 plugin_init (GstPlugin *plugin)
474 {
475 	gboolean ret = gst_element_register (plugin, "rbmtpsrc", GST_RANK_PRIMARY, RB_TYPE_MTP_SRC);
476 	return ret;
477 }
478 
479 GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR,
480 			  GST_VERSION_MINOR,
481 			  "rbmtpsrc",
482 			  "element to download and play files from MTP devices",
483 			  plugin_init,
484 			  VERSION,
485 			  "GPL",
486 			  PACKAGE,
487 			  "");