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