Re: [Rhythmbox-devel] AudioCD Support
- From: Matt Jones <mattharrison sbcglobal net>
- To: Christophe Fergeau <teuf gnome org>
- Cc: rhythmbox-devel gnome org
- Subject: Re: [Rhythmbox-devel] AudioCD Support
- Date: Fri, 14 Jan 2005 16:55:26 -0800
> But the
> main problem is that it doesn't detect the audio cds I insert ;) It's
> compiled without hal, and actually I don't think monitoring
> mounted/unmounted volumes can work to detect audio cds...
I've attached a small test program I wrote that seems to show this
working for me. I know it's VERY non-intuitive (you don't mount an
audiocd, after all), and I have no idea if this works on a non-hal-
enabled-gnome-vfs system, so if someone could try this out and send me
the results, that would be great.
Compile the attached code with the command: "gcc `pkg-config gnome-
vfs-2.0 gtk+-2.0 --cflags --libs` audio-cd-monitor.c totem-disc.c -o
audio-cd-monitor"
When you run it it should print out a message when you insert an audiocd
in a drive.
--matt jones
#include <libgnomevfs/gnome-vfs-volume.h>
#include <libgnomevfs/gnome-vfs-volume-monitor.h>
#include <glib.h>
#include <gtk/gtk.h>
#include "totem-disc.h"
/*#include <libhal.h>
static gboolean
hal_udi_is_audiocd (const char *udi)
{
LibHalContext *ctx;
gboolean result;
result = FALSE;
ctx = hal_initialize (NULL, FALSE);
if (ctx == NULL)
return FALSE;
g_print ("Type: %d\n", hal_device_get_property_type (ctx, udi, "volume.disc.has_audio"));
if (hal_device_get_property_bool (ctx, udi, "volume.disc.has_audio") == TRUE)
result = TRUE;
*/
/* FIXME: What should we do in the case that volume.disc.has_data is true? Anything?
Just let the user figure it out (CDDB should note it as a data track)? */
/*
hal_shutdown (ctx);
return result;
} */
static gboolean
rb_cd_is_volume_cd (GnomeVFSVolume *volume)
{
/*gboolean result = FALSE;
gchar *udi;*/
GError *error;
/* FIXME: gnome_vfs_volume_get_volume_type seems to think that it has an AUDIO_CD type,
but my own CD drive never returns this. It's possible this is a bug in hal or gnome-vfs -
hal signifies the volume type as the same as the drive, so this could be confusing gnome-vfs */
if (gnome_vfs_volume_get_device_type (volume) != GNOME_VFS_DEVICE_TYPE_CDROM) {
return FALSE;
}
/*
udi = gnome_vfs_volume_get_hal_udi (volume);
if (udi != NULL) {
gboolean result;
result = hal_udi_is_audiocd (udi);
g_free (udi);
return result;
}*/
if (totem_cd_detect_type (gnome_vfs_volume_get_device_path (volume), &error) == MEDIA_TYPE_CDDA)
return TRUE;
return FALSE;
}
static void volume_changed_cb (GnomeVFSVolumeMonitor *monitor,
GnomeVFSVolume *volume,
gpointer data)
{
g_print ("Device: %s\n", gnome_vfs_volume_get_device_path (volume));
GnomeVFSDeviceType type = gnome_vfs_volume_get_device_type (volume);
switch (type)
{
case GNOME_VFS_DEVICE_TYPE_UNKNOWN:
g_print ("UNKNOWN\n");
break;
case GNOME_VFS_DEVICE_TYPE_AUDIO_CD:
g_print ("AUDIO_CD\n");
break;
case GNOME_VFS_DEVICE_TYPE_VIDEO_DVD:
g_print ("VIDEO_DVD\n");
break;
case GNOME_VFS_DEVICE_TYPE_HARDDRIVE:
g_print ("HARD_DRIVE\n");
break;
case GNOME_VFS_DEVICE_TYPE_CDROM:
g_print ("CDROM\n");
break;
}
if (rb_cd_is_volume_cd (volume) == TRUE)
g_print ("Volume is an audioCD!\n");
else
g_print ("Volume is not an audio CD.\n");
}
int main (void)
{
GnomeVFSVolumeMonitor *monitor;
gboolean temp;
temp = gnome_vfs_init ();
monitor = gnome_vfs_get_volume_monitor ();
g_signal_connect (G_OBJECT (monitor), "volume-mounted",
G_CALLBACK (volume_changed_cb),
NULL);
gtk_main ();
gnome_vfs_shutdown ();
}
/* Totem Disc Content Detection
* (c) 2004 Ronald Bultje <rbultje ronald bitfreak net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <mntent.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/cdrom.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <libgnomevfs/gnome-vfs.h>
#include "totem-disc.h"
typedef struct _CdCache {
/* device node and mountpoint */
char *device, *mountpoint;
GnomeVFSDrive *drive;
/* file descriptor to the device */
int fd;
/* capabilities of the device */
int cap;
/* if we're checking a media, or a dir */
gboolean is_media;
/* indicates if we mounted this mountpoint ourselves or if it
* was already mounted. */
gboolean self_mounted;
gboolean mounted;
} CdCache;
/*
* Resolve relative paths
*/
static char *
totem_disc_resolve_link (const char *dev, const char *buf)
{
char *parent, *new;
/* is it an absolute path? */
if (g_path_is_absolute (buf) != FALSE)
return g_strdup (buf);
parent = g_path_get_dirname (dev);
new = g_build_filename (parent, buf, NULL);
g_free (parent);
return new;
}
/*
* So, devices can be symlinks and that screws up.
*/
static char *
get_device (const char *device,
GError **error)
{
char *buf;
char *dev = g_strdup (device);
struct stat st;
while (1) {
char *new;
if (lstat (dev, &st) != 0) {
g_set_error (error, 0, 0,
_("Failed to find real device node for %s: %s"),
dev, g_strerror (errno));
g_free (dev);
return NULL;
}
if (!S_ISLNK (st.st_mode))
break;
if (!(buf = g_file_read_link (dev, NULL))) {
g_set_error (error, 0, 0,
_("Failed to read symbolic link %s: %s"),
dev, g_strerror (errno));
g_free (dev);
return NULL;
}
new = totem_disc_resolve_link (dev, buf);
g_free (dev);
g_free (buf);
dev = new;
}
return dev;
}
static CdCache *
cd_cache_new (const char *dev,
GError **error)
{
CdCache *cache;
char *mountpoint = NULL, *device;
GnomeVFSVolumeMonitor *mon;
GnomeVFSDrive *drive = NULL;
GList *list, *or;
gboolean is_dir;
if (g_str_has_prefix (dev, "file://") != FALSE)
device = g_filename_from_uri (dev, NULL, NULL);
else
device = g_strdup (dev);
if (device == NULL)
return NULL;
is_dir = g_file_test (device, G_FILE_TEST_IS_DIR);
if (is_dir) {
cache = g_new0 (CdCache, 1);
cache->mountpoint = device;
cache->fd = -1;
cache->is_media = FALSE;
return cache;
}
g_free (device);
/* retrieve mountpoint (/etc/fstab). We could also use HAL for this,
* I think (gnome-volume-manager does that). */
if (!(device = get_device (dev, error)))
return NULL;
mon = gnome_vfs_get_volume_monitor ();
for (or = list = gnome_vfs_volume_monitor_get_connected_drives (mon);
list != NULL; list = list->next) {
char *pdev, *pdev2;
drive = list->data;
if (!(pdev = gnome_vfs_drive_get_device_path (drive)))
continue;
if (!(pdev2 = get_device (pdev, NULL))) {
g_free (pdev);
continue;
}
g_free (pdev);
if (strcmp (pdev2, device) == 0) {
char *mnt;
mnt = gnome_vfs_drive_get_activation_uri (drive);
g_free (pdev2);
if (!strncmp (mnt, "file://", 7)) {
mountpoint = g_strdup (mnt + 7);
g_free (mnt);
gnome_vfs_drive_ref (drive);
break;
}
g_free (mnt);
}
g_free (pdev2);
}
g_list_foreach (or, (GFunc) gnome_vfs_drive_unref, NULL);
g_list_free (or);
if (!mountpoint) {
g_set_error (error, 0, 0,
_("Failed to find mountpoint for device %s in /etc/fstab"),
device);
return NULL;
}
/* create struture */
cache = g_new0 (CdCache, 1);
cache->device = device;
cache->mountpoint = mountpoint;
cache->fd = -1;
cache->self_mounted = FALSE;
cache->drive = drive;
cache->is_media = TRUE;
return cache;
}
static gboolean
cd_cache_open_device (CdCache *cache,
GError **error)
{
int drive, err;
/* not a medium? */
if (cache->is_media == FALSE) {
cache->cap = CDC_DVD;
return TRUE;
}
/* already open? */
if (cache->fd > 0)
return TRUE;
/* try to open the CD before creating anything */
if ((cache->fd = open (cache->device, O_RDONLY)) < 0) {
err = errno;
if (err == ENOMEDIUM) {
g_set_error (error, 0, 0,
_("Please check that a disc is present in the drive."));
} else {
g_set_error (error, 0, 0,
_("Failed to open device %s for reading: %s"),
cache->device, g_strerror (err));
}
return FALSE;
}
/* get capabilities */
if ((cache->cap = ioctl (cache->fd, CDROM_GET_CAPABILITY, NULL)) < 0) {
close (cache->fd);
cache->fd = -1;
g_set_error (error, 0, 0,
_("Failed to retrieve capabilities of device %s: %s"),
cache->device, g_strerror (errno));
return FALSE;
}
/* is there a disc in the tray? */
if ((drive = ioctl (cache->fd, CDROM_DRIVE_STATUS, NULL)) != CDS_DISC_OK) {
const char *drive_s;
close (cache->fd);
cache->fd = -1;
switch (drive) {
case CDS_NO_INFO:
drive_s = "Not implemented";
break;
case CDS_NO_DISC:
drive_s = "No disc in tray";
break;
case CDS_TRAY_OPEN:
drive_s = "Tray open";
break;
case CDS_DRIVE_NOT_READY:
drive_s = "Drive not ready";
break;
case CDS_DISC_OK:
drive_s = "OK";
break;
default:
drive_s = "Unknown";
break;
}
g_set_error (error, 0, 0,
_("Drive status 0x%x (%s) - check disc"),
drive, drive_s);
return FALSE;
}
return TRUE;
}
static gboolean
cd_cache_open_mountpoint (CdCache *cache,
GError **error)
{
/* already opened? */
if (cache->mounted || cache->is_media == FALSE)
return TRUE;
/* check for mounting - assume we'll mount ourselves */
cache->self_mounted = !gnome_vfs_drive_is_mounted (cache->drive);
/* mount if we have to */
if (cache->self_mounted) {
char *command;
int status;
command = g_strdup_printf ("mount %s", cache->mountpoint);
if (!g_spawn_command_line_sync (command,
NULL, NULL, &status, error)) {
g_free (command);
return FALSE;
}
g_free (command);
if (status != 0) {
g_set_error (error, 0, 0,
_("Unexpected error status %d while mounting %s"),
status, cache->mountpoint);
return FALSE;
}
}
cache->mounted = TRUE;
return TRUE;
}
static void
cd_cache_free (CdCache *cache)
{
/* umount if we mounted */
if (cache->self_mounted && cache->mounted) {
char *command;
command = g_strdup_printf ("umount %s", cache->mountpoint);
g_spawn_command_line_sync (command, NULL, NULL, NULL, NULL);
g_free (command);
}
/* close file descriptor to device */
if (cache->fd > 0) {
close (cache->fd);
}
/* free mem */
gnome_vfs_drive_unref (cache->drive);
g_free (cache->mountpoint);
g_free (cache->device);
g_free (cache);
}
static MediaType
cd_cache_disc_is_cdda (CdCache *cache,
GError **error)
{
MediaType type = MEDIA_TYPE_DATA;
int disc;
const char *disc_s;
/* We can't have audio CDs on disc, yet */
if (cache->is_media == FALSE)
return type;
/* open disc and open mount */
if (!cd_cache_open_device (cache, error))
return MEDIA_TYPE_ERROR;
if ((disc = ioctl (cache->fd, CDROM_DISC_STATUS, NULL)) < 0) {
g_set_error (error, 0, 0,
_("Error getting %s disc status: %s"),
cache->device, g_strerror (errno));
return MEDIA_TYPE_ERROR;
}
switch (disc) {
case CDS_NO_INFO:
/* The drive doesn't implement CDROM_DISC_STATUS */
break;
case CDS_NO_DISC:
disc_s = "No disc in tray";
type = MEDIA_TYPE_ERROR;
break;
case CDS_AUDIO:
case CDS_MIXED:
type = MEDIA_TYPE_CDDA;
break;
case CDS_DATA_1:
case CDS_DATA_2:
case CDS_XA_2_1:
case CDS_XA_2_2:
break;
default:
disc_s = "Unknown";
type = MEDIA_TYPE_ERROR;
break;
}
if (type == MEDIA_TYPE_ERROR) {
g_set_error (error, 0, 0,
_("Unexpected/unknown cd type 0x%x (%s)"),
disc, disc_s);
return MEDIA_TYPE_ERROR;
}
return type;
}
static gboolean
cd_cache_file_exists (CdCache *cache, const char *subdir, const char *filename)
{
char *path, *dir;
gboolean ret;
dir = NULL;
/* Check whether the directory exists, for a start */
path = g_build_filename (cache->mountpoint, subdir, NULL);
ret = g_file_test (path, G_FILE_TEST_IS_DIR);
if (ret == FALSE) {
char *subdir_low;
g_free (path);
subdir_low = g_ascii_strdown (subdir, -1);
path = g_build_filename (cache->mountpoint, subdir_low, NULL);
ret = g_file_test (path, G_FILE_TEST_IS_DIR);
g_free (path);
if (ret) {
dir = subdir_low;
} else {
g_free (subdir_low);
return FALSE;
}
} else {
g_free (path);
dir = g_strdup (subdir);
}
/* And now the file */
path = g_build_filename (cache->mountpoint, dir, filename, NULL);
ret = g_file_test (path, G_FILE_TEST_IS_REGULAR);
if (ret == FALSE) {
char *fname_low;
g_free (path);
fname_low = g_ascii_strdown (filename, -1);
path = g_build_filename (cache->mountpoint, dir, fname_low, NULL);
ret = g_file_test (path, G_FILE_TEST_IS_REGULAR);
g_free (fname_low);
}
g_free (dir);
g_free (path);
return ret;
}
static MediaType
cd_cache_disc_is_vcd (CdCache *cache,
GError **error)
{
/* open disc and open mount */
if (!cd_cache_open_device (cache, error))
return MEDIA_TYPE_ERROR;
if (!cd_cache_open_mountpoint (cache, error))
return MEDIA_TYPE_ERROR;
/* first is VCD, second is SVCD */
if (cd_cache_file_exists (cache, "MPEGAV", "AVSEQ01.DAT") ||
cd_cache_file_exists (cache, "MPEG2", "AVSEQ01.MPG"))
return MEDIA_TYPE_VCD;
return MEDIA_TYPE_DATA;
}
static MediaType
cd_cache_disc_is_dvd (CdCache *cache,
GError **error)
{
/* open disc, check capabilities and open mount */
if (!cd_cache_open_device (cache, error))
return MEDIA_TYPE_ERROR;
if (!(cache->cap & CDC_DVD))
return MEDIA_TYPE_DATA;
if (!cd_cache_open_mountpoint (cache, error))
return MEDIA_TYPE_ERROR;
if (cd_cache_file_exists (cache, "VIDEO_TS", "VIDEO_TS.IFO"))
return MEDIA_TYPE_DVD;
return MEDIA_TYPE_DATA;
}
MediaType
totem_cd_detect_type_from_dir (const char *dir, char **url, GError **error)
{
CdCache *cache;
MediaType type;
g_return_val_if_fail (dir != NULL, MEDIA_TYPE_ERROR);
if (dir[0] != '/' && g_str_has_prefix (dir, "file://") == FALSE)
return MEDIA_TYPE_ERROR;
if (!(cache = cd_cache_new (dir, error)))
return MEDIA_TYPE_ERROR;
if ((type = cd_cache_disc_is_vcd (cache, error)) == MEDIA_TYPE_DATA &&
(type = cd_cache_disc_is_dvd (cache, error)) == MEDIA_TYPE_DATA) {
/* crap, nothing found */
cd_cache_free (cache);
return type;
}
cd_cache_free (cache);
if (url == NULL) {
return type;
}
if (type == MEDIA_TYPE_DVD) {
if (g_str_has_prefix (dir, "file://") != FALSE) {
char *local;
local = g_filename_from_uri (dir, NULL, NULL);
*url = g_strdup_printf ("dvd://%s", local);
g_free (local);
} else {
*url = g_strdup_printf ("dvd://%s", dir);
}
} else if (type == MEDIA_TYPE_VCD) {
if (g_str_has_prefix (dir, "file://") != FALSE) {
char *local;
local = g_filename_from_uri (dir, NULL, NULL);
*url = g_strdup_printf ("vcd://%s", local);
g_free (local);
} else {
*url = g_strdup_printf ("vcd://%s", dir);
}
}
return type;
}
MediaType
totem_cd_detect_type (const char *device,
GError **error)
{
CdCache *cache;
MediaType type;
if (!(cache = cd_cache_new (device, error)))
return MEDIA_TYPE_ERROR;
if ((type = cd_cache_disc_is_cdda (cache, error)) == MEDIA_TYPE_DATA &&
(type = cd_cache_disc_is_vcd (cache, error)) == MEDIA_TYPE_DATA &&
(type = cd_cache_disc_is_dvd (cache, error)) == MEDIA_TYPE_DATA) {
/* crap, nothing found */
}
cd_cache_free (cache);
return type;
}
const char *
totem_cd_get_human_readable_name (MediaType type)
{
switch (type)
{
case MEDIA_TYPE_CDDA:
return N_("Audio CD");
case MEDIA_TYPE_VCD:
return N_("Video CD");
case MEDIA_TYPE_DVD:
return N_("DVD");
default:
g_assert_not_reached ();
}
return NULL;
}
/*
* vim: sw=2 ts=8 cindent noai bs=2
*/
/* Totem Disc Content Detection
* (c) 2004 Ronald Bultje <rbultje ronald bitfreak net>
*
* totem-disc.h: media content detection
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef TOTEM_DISC_H
#define TOTEM_DISC_H
#include <glib.h>
G_BEGIN_DECLS
typedef enum {
MEDIA_TYPE_ERROR = -1, /* error */
MEDIA_TYPE_DATA = 1,
MEDIA_TYPE_CDDA,
MEDIA_TYPE_VCD,
MEDIA_TYPE_DVD
} MediaType;
MediaType totem_cd_detect_type (const char *device,
GError **error);
MediaType totem_cd_detect_type_from_dir (const char *dir,
char **url,
GError **error);
const char * totem_cd_get_human_readable_name (MediaType type);
G_END_DECLS
#endif /* TOTEM_DISC_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]