No issues found
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 |
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 "");