hythmbox-2.98/plugins/audiocd/rb-audiocd-plugin.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2  *
  3  * rb-audiocd-plugin.c
  4  *
  5  * Copyright (C) 2006  James Livingston
  6  *
  7  * This program is free software; you can redistribute it and/or modify
  8  * it under the terms of the GNU General Public License as published by
  9  * the Free Software Foundation; either version 2, or (at your option)
 10  * any later version.
 11  * 
 12  * The Rhythmbox authors hereby grant permission for non-GPL compatible
 13  * GStreamer plugins to be used and distributed together with GStreamer
 14  * and Rhythmbox. This permission is above and beyond the permissions granted
 15  * by the GPL license by which Rhythmbox is covered. If you modify this code
 16  * you may extend this exception to your version of the code, but you are not
 17  * obligated to do so. If you do not wish to do so, delete this exception
 18  * statement from your version.
 19  *
 20  * This program is distributed in the hope that it will be useful,
 21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 23  * GNU General Public License for more details.
 24  *
 25  * You should have received a copy of the GNU General Public License
 26  * along with this program; if not, write to the Free Software
 27  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 28  */
 29 
 30 #define __EXTENSIONS__
 31 
 32 #include "config.h"
 33 
 34 #include <string.h> /* For strlen */
 35 
 36 #include <glib.h>
 37 #include <glib-object.h>
 38 #include <glib/gi18n-lib.h>
 39 #include <gmodule.h>
 40 #include <gtk/gtk.h>
 41 
 42 #include <gst/gst.h>
 43 
 44 #include "rb-plugin-macros.h"
 45 #include "rb-debug.h"
 46 #include "rb-shell.h"
 47 #include "rb-shell-player.h"
 48 #include "rb-dialog.h"
 49 #include "rb-removable-media-manager.h"
 50 #include "rb-audiocd-source.h"
 51 #include "rb-player.h"
 52 #include "rb-encoder.h"
 53 #include "rb-file-helpers.h"
 54 
 55 
 56 #define RB_TYPE_AUDIOCD_PLUGIN		(rb_audiocd_plugin_get_type ())
 57 #define RB_AUDIOCD_PLUGIN(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_AUDIOCD_PLUGIN, RBAudioCdPlugin))
 58 #define RB_AUDIOCD_PLUGIN_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_AUDIOCD_PLUGIN, RBAudioCdPluginClass))
 59 #define RB_IS_AUDIOCD_PLUGIN(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_AUDIOCD_PLUGIN))
 60 #define RB_IS_AUDIOCD_PLUGIN_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_AUDIOCD_PLUGIN))
 61 #define RB_AUDIOCD_PLUGIN_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_AUDIOCD_PLUGIN, RBAudioCdPluginClass))
 62 
 63 typedef struct
 64 {
 65 	PeasExtensionBase parent;
 66 
 67 	RBShell    *shell;
 68 	guint       ui_merge_id;
 69 
 70 	GHashTable *sources;
 71 	char       *playing_uri;
 72 } RBAudioCdPlugin;
 73 
 74 typedef struct
 75 {
 76 	PeasExtensionBaseClass parent_class;
 77 } RBAudioCdPluginClass;
 78 
 79 
 80 G_MODULE_EXPORT void peas_register_types (PeasObjectModule *module);
 81 GType	rb_audiocd_plugin_get_type		(void) G_GNUC_CONST;
 82 
 83 static void rb_audiocd_plugin_init (RBAudioCdPlugin *plugin);
 84 
 85 RB_DEFINE_PLUGIN(RB_TYPE_AUDIOCD_PLUGIN, RBAudioCdPlugin, rb_audiocd_plugin,)
 86 
 87 static void
 88 rb_audiocd_plugin_init (RBAudioCdPlugin *plugin)
 89 {
 90 	rb_debug ("RBAudioCdPlugin initialising");
 91 }
 92 
 93 static gboolean
 94 parse_cdda_uri (const char *uri, char **device, gulong *track)
 95 {
 96 	const char *fragment;
 97 	const char *device_name;
 98 
 99 	if (g_str_has_prefix (uri, "cdda://") == FALSE)
100 		return FALSE;
101 
102 	fragment = g_utf8_strrchr (uri, -1, '#');
103 	if (fragment == NULL)
104 		return FALSE;
105 
106 	if (track != NULL) {
107 		*track = strtoul (fragment + 1, NULL, 0);
108 	}
109 
110 	if (device != NULL) {
111 		device_name = uri + strlen("cdda://");
112 		*device = g_malloc0 ((fragment - device_name) + 1);
113 		memcpy (*device, device_name, (fragment - device_name));
114 	}
115 	return TRUE;
116 }
117 
118 static void
119 rb_audiocd_plugin_playing_uri_changed_cb (RBShellPlayer   *player,
120 					  const char      *uri,
121 					  RBAudioCdPlugin *plugin)
122 {
123 	g_free (plugin->playing_uri);
124 	plugin->playing_uri = uri ? g_strdup (uri) : NULL;
125 }
126 
127 static void
128 set_source_properties (GstElement *source, const char *uri, gboolean playback_mode)
129 {
130 	char *device = NULL;
131 	gulong track;
132 
133 	if (parse_cdda_uri (uri, &device, &track) == FALSE)
134 		return;
135 
136 	g_object_set (source, "device", device, "track", track, NULL);
137 	g_free (device);
138 
139 	if (playback_mode) {
140 		/* disable paranoia (if using cdparanoiasrc) and set read speed to 1 */
141 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
142 			g_object_set (source, "paranoia-mode", 0, NULL);
143 
144 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "read-speed"))
145 			g_object_set (source, "read-speed", 1, NULL);
146 	} else {
147 		/* enable full paranoia; maybe this should be configurable. */
148 		/* also, sound-juicer defaults to 8 (scratch) not 0xff (full) here.. */
149 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
150 			g_object_set (source, "paranoia-mode", 0xff, NULL);
151 
152 		/* trick cdparanoiasrc into resetting the device speed in case we've
153 		 * previously set it to 1 for playback
154 		 */
155 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "read-speed"))
156 			g_object_set (source, "read-speed", 0xffff, NULL);
157 	}
158 }
159 
160 static void
161 rb_audiocd_plugin_prepare_player_source_cb (RBPlayer *player,
162 					    const char *stream_uri,
163 					    GstElement *source,
164 					    RBAudioCdPlugin *plugin)
165 {
166 	set_source_properties (source, stream_uri, TRUE);
167 }
168 
169 static void
170 rb_audiocd_plugin_prepare_encoder_source_cb (RBEncoderFactory *factory,
171 					     const char *stream_uri,
172 					     GObject *source,
173 					     RBAudioCdPlugin *plugin)
174 {
175 	set_source_properties (GST_ELEMENT (source), stream_uri, FALSE);
176 }
177 
178 static gboolean
179 rb_audiocd_plugin_can_reuse_stream_cb (RBPlayer *player,
180 				       const char *new_uri,
181 				       const char *stream_uri,
182 				       GstElement *stream_bin,
183 				       RBAudioCdPlugin *plugin)
184 {
185 	char *new_device = NULL;
186 	char *old_device = NULL;
187 	gboolean result = FALSE;
188 
189 	if (parse_cdda_uri (new_uri, &new_device, NULL) &&
190 	    parse_cdda_uri (stream_uri, &old_device, NULL)) {
191 		result = (g_strcmp0 (old_device, new_device) == 0);
192 	}
193 
194 	g_free (new_device);
195 	g_free (old_device);
196 	return result;
197 }
198 
199 static void
200 rb_audiocd_plugin_reuse_stream_cb (RBPlayer *player,
201 				   const char *new_uri,
202 				   const char *stream_uri,
203 				   GstElement *element,
204 				   RBAudioCdPlugin *plugin)
205 {
206 	GstFormat track_format = gst_format_get_by_nick ("track");
207 	gulong track;
208 	char *device = NULL;
209 
210 	if (parse_cdda_uri (new_uri, &device, &track) == FALSE) {
211 		g_assert_not_reached ();
212 	}
213 
214 	rb_debug ("seeking to track %lu on CD device %s", track, device);
215 	g_free (device);
216 
217 	gst_element_seek (element,
218 			  1.0, track_format, GST_SEEK_FLAG_FLUSH,
219 			  GST_SEEK_TYPE_SET, track-1,
220 			  GST_SEEK_TYPE_NONE, -1);
221 }
222 
223 static void
224 rb_audiocd_plugin_source_deleted (RBAudioCdSource *source,
225 				  RBAudioCdPlugin *plugin)
226 {
227 	GVolume *volume;
228 
229 	g_object_get (source, "volume", &volume, NULL);
230 	g_hash_table_remove (plugin->sources, volume);
231 	g_object_unref (volume);
232 }
233 
234 static RBSource *
235 create_source_cb (RBRemovableMediaManager *rmm,
236 		  GMount                  *mount,
237 		  MPIDDevice              *device_info,
238 		  RBAudioCdPlugin         *plugin)
239 {
240 	RBSource *source = NULL;
241 	GVolume *volume = NULL;
242 	RBShell *shell;
243 
244 	g_object_get (plugin, "object", &shell, NULL);
245 
246 	if (rb_audiocd_is_mount_audiocd (mount)) {
247 
248 		volume = g_mount_get_volume (mount);
249 		if (volume != NULL) {
250 			source = rb_audiocd_source_new (G_OBJECT (plugin), shell, volume);
251 			g_object_unref (volume);
252 		}
253 	}
254 
255 	if (source != NULL) {
256 		g_hash_table_insert (plugin->sources, g_object_ref (volume), g_object_ref (source));
257 		g_signal_connect_object (G_OBJECT (source),
258 					 "deleted", G_CALLBACK (rb_audiocd_plugin_source_deleted),
259 					 plugin, 0);
260 
261 		if (plugin->ui_merge_id == 0) {
262 			char *filename;
263 			GtkUIManager *uimanager;
264 
265 			g_object_get (shell, "ui-manager", &uimanager, NULL);
266 
267 			filename = rb_find_plugin_data_file (G_OBJECT (plugin), "audiocd-ui.xml");
268 			if (filename != NULL) {
269 				plugin->ui_merge_id = gtk_ui_manager_add_ui_from_file (uimanager, filename, NULL);
270 				gtk_ui_manager_ensure_update (uimanager);
271 			} else {
272 				g_warning ("Unable to find file: audiocd-ui.xml");
273 			}
274 
275 			g_free (filename);
276 			g_object_unref (uimanager);
277 		}
278 	}
279 
280 	g_object_unref (shell);
281 	return source;
282 }
283 
284 static void
285 impl_activate (PeasActivatable *plugin)
286 {
287 	RBAudioCdPlugin         *pi = RB_AUDIOCD_PLUGIN (plugin);
288 	RBRemovableMediaManager *rmm;
289 	gboolean                 scanned;
290 	GObject                 *shell_player;
291 	RBPlayer                *player_backend;
292 	RBShell                 *shell;
293 
294 	pi->sources = g_hash_table_new_full (g_direct_hash,
295 					     g_direct_equal,
296 					     g_object_unref,
297 					     g_object_unref);
298 
299 	g_object_get (plugin, "object", &shell, NULL);
300 	g_object_get (shell, "removable-media-manager", &rmm, NULL);
301 
302 
303 	/* watch for new removable media.  use connect_after so
304 	 * plugins for more specific device types can get in first.
305 	 */
306 	g_signal_connect_after (rmm,
307 				"create-source-mount", G_CALLBACK (create_source_cb),
308 				pi);
309 
310 	/* only scan if we're being loaded after the initial scan has been done */
311 	g_object_get (G_OBJECT (rmm), "scanned", &scanned, NULL);
312 	if (scanned) {
313 		rb_removable_media_manager_scan (rmm);
314 	}
315 
316 	g_object_unref (rmm);
317 
318 	/* player backend hooks: specify the device, limit read speed, and disable paranoia
319 	 * in source elements, and when changing between tracks on the same CD, just seek
320 	 * between them, rather than closing and reopening the device.
321 	 */
322 	g_object_get (shell, "shell-player", &shell_player, NULL);
323 	g_object_get (shell_player, "player", &player_backend, NULL);
324 	if (player_backend) {
325 		GObjectClass *klass = G_OBJECT_GET_CLASS (player_backend);
326 		if (g_signal_lookup ("prepare-source", G_OBJECT_CLASS_TYPE (klass)) != 0) {
327 			g_signal_connect_object (player_backend,
328 						 "prepare-source",
329 						 G_CALLBACK (rb_audiocd_plugin_prepare_player_source_cb),
330 						 plugin, 0);
331 		}
332 		if (g_signal_lookup ("reuse-stream", G_OBJECT_CLASS_TYPE (klass)) != 0) {
333 			g_signal_connect_object (player_backend,
334 						 "can-reuse-stream",
335 						 G_CALLBACK (rb_audiocd_plugin_can_reuse_stream_cb),
336 						 plugin, 0);
337 			g_signal_connect_object (player_backend,
338 						 "reuse-stream",
339 						 G_CALLBACK (rb_audiocd_plugin_reuse_stream_cb),
340 						 plugin, 0);
341 		}
342 	}
343 	g_object_unref (shell_player);
344 
345 	/* encoder hooks: specify the device and set the paranoia level (if available) on
346 	 * source elements.
347 	 */
348 	g_signal_connect_object (rb_encoder_factory_get (),
349 				 "prepare-source",
350 				 G_CALLBACK (rb_audiocd_plugin_prepare_encoder_source_cb),
351 				 plugin, 0);
352 
353 	g_signal_connect_object (shell_player, "playing-uri-changed",
354 				 G_CALLBACK (rb_audiocd_plugin_playing_uri_changed_cb),
355 				 plugin, 0);
356 
357 	g_object_unref (shell);
358 }
359 
360 static void
361 _delete_cb (GVolume         *volume,
362 	    RBSource        *source,
363 	    RBAudioCdPlugin *plugin)
364 {
365 	/* block the source deleted handler so we don't modify the hash table
366 	 * while iterating it.
367 	 */
368 	g_signal_handlers_block_by_func (source, rb_audiocd_plugin_source_deleted, plugin);
369 	rb_display_page_delete_thyself (RB_DISPLAY_PAGE (source));
370 }
371 
372 static void
373 impl_deactivate	(PeasActivatable *bplugin)
374 {
375 	RBAudioCdPlugin         *plugin = RB_AUDIOCD_PLUGIN (bplugin);
376 	RBRemovableMediaManager *rmm = NULL;
377 	GtkUIManager            *uimanager = NULL;
378 	RBShell                 *shell;
379 
380 	g_object_get (plugin, "object", &shell, NULL);
381 	g_object_get (shell,
382 		      "removable-media-manager", &rmm,
383 		      "ui-manager", &uimanager,
384 		      NULL);
385 	g_signal_handlers_disconnect_by_func (rmm, create_source_cb, plugin);
386 
387 	g_hash_table_foreach (plugin->sources, (GHFunc)_delete_cb, plugin);
388 	g_hash_table_destroy (plugin->sources);
389 	plugin->sources = NULL;
390 	if (plugin->ui_merge_id) {
391 		gtk_ui_manager_remove_ui (uimanager, plugin->ui_merge_id);
392 		plugin->ui_merge_id = 0;
393 	}
394 
395 	g_object_unref (uimanager);
396 	g_object_unref (rmm);
397 	g_object_unref (shell);
398 }
399 
400 G_MODULE_EXPORT void
401 peas_register_types (PeasObjectModule *module)
402 {
403 	rb_audiocd_plugin_register_type (G_TYPE_MODULE (module));
404 	_rb_audiocd_source_register_type (G_TYPE_MODULE (module));
405 	peas_object_module_register_extension_type (module,
406 						    PEAS_TYPE_ACTIVATABLE,
407 						    RB_TYPE_AUDIOCD_PLUGIN);
408 }