Commit 0327f339 authored by Charles Schmidt's avatar Charles Schmidt

Added *BSD (and Darwin) ioctl cdaudio playing. Couple bugfixes....

Added *BSD (and Darwin) ioctl cdaudio playing. Couple bugfixes. 'end-track','current-track' and 'cddb-discid' propert...

Original commit message from CVS:
Added *BSD (and Darwin) ioctl cdaudio playing. Couple bugfixes. 'end-track','current-track' and 'cddb-discid' properties and 'track-change' signal for the element.
parent 186ba5c3
......@@ -285,7 +285,35 @@ GST_CHECK_FEATURE(VGA, [VGA], vgavideosink, [
dnl *** CDROM Audio ***
translit(dnm, m, l) AM_CONDITIONAL(USE_CDROM, true)
GST_CHECK_FEATURE(CDROM, [CDROM Audio], cdrom, [
AC_CHECK_HEADER(linux/cdrom.h, HAVE_CDROM="yes", HAVE_CDROM="no")
AC_CHECK_HEADERS(linux/cdrom.h) dnl linux
AC_CHECK_HEADERS(sys/cdio.h) dnl almost everything else
dnl AC_CHECK_HEADERS(dmedia/cdaudio.h) dnl irix
if test "${ac_cv_header_linux_cdrom_h}" = "yes" || test "${ac_cv_header_sys_cdio_h}" = "yes" || test "${ac_cv_header_dmedia_cdaudio_h}" = "yes"; then
case "$host" in
*-sun-* | *-*-linux*)
AC_DEFINE(HAVE_CDROM_SOLARIS,, [Define if cdrom access is in Solaris style])
;;
*-*-freebsd*)
AC_DEFINE(HAVE_CDROM_BSD,, [Define if cdrom access is in BSD style])
;;
*-*-netbsd* | *-*-openbsd*)
AC_DEFINE(HAVE_CDROM_BSD,, [Define if cdrom access is in BSD style])
AC_DEFINE(HAVE_CDROM_BSD_NETBSD,, [Define if cdrom access uses NetBSD variant])
;;
*-*darwin*)
AC_DEFINE(HAVE_CDROM_BSD,, [Define if cdrom access is in BSD style])
AC_DEFINE(HAVE_CDROM_BSD_DARWIN,, [Define if cdrom access uses Darwin variant])
;;
dnl *-irix-*)
dnl AC_DEFINE(HAVE_CDROM_IRIX,, [Define if cdrom access is in Irix DMedia style])
dnl ;;
esac
HAVE_CDROM="yes"
else
HAVE_CDROM="no"
fi
])
dnl *** XVideo ***
......
......@@ -7,4 +7,4 @@ libgstcdplayer_la_CFLAGS = $(GST_CFLAGS)
libgstcdplayer_la_LIBADD =
libgstcdplayer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = gstcdplayer.h gstcdplayer_ioctl.h
noinst_HEADERS = gstcdplayer.h gstcdplayer_ioctl.h gstcdplayer_ioctl_solaris.h gstcdplayer_ioctl_bsd.h
......@@ -17,8 +17,9 @@
* Boston, MA 02111-1307, USA.
*/
/* #define GST_DEBUG_ENABLED */
#include "gstcdplayer.h"
#include "gstcdplayer_ioctl.h"
/* props */
enum {
......@@ -26,10 +27,14 @@ enum {
ARG_DEVICE,
ARG_NUM_TRACKS,
ARG_START_TRACK,
ARG_END_TRACK,
ARG_CURRENT_TRACK,
ARG_CDDB_DISCID,
};
/* signals */
enum {
TRACK_CHANGE,
LAST_SIGNAL,
};
......@@ -38,6 +43,7 @@ static void cdplayer_init(CDPlayer *cdp);
static void cdplayer_set_property(GObject *object,guint prop_id,const GValue *value,GParamSpec *spec);
static void cdplayer_get_property(GObject *object,guint prop_id,GValue *value,GParamSpec *spec);
static void cdplayer_dispose(GObject *object);
static gboolean cdplayer_iterate(GstBin *bin);
static GstElementStateReturn cdplayer_change_state(GstElement *element);
static gboolean plugin_init(GModule *module,GstPlugin *plugin);
......@@ -45,7 +51,7 @@ static gboolean plugin_init(GModule *module,GstPlugin *plugin);
static GstElementClass *parent_class;
/* static guint cdplayer_signals[LAST_SIGNAL] = {0}; */
static guint cdplayer_signals[LAST_SIGNAL] = {0};
static GstElementDetails cdplayer_details = {
......@@ -87,19 +93,27 @@ static void cdplayer_class_init(CDPlayerClass *klass)
{
GObjectClass *gobject_klass;
GstElementClass *gstelement_klass;
GstBinClass *gstbin_klass;
gobject_klass = (GObjectClass *)klass;
gstelement_klass = (GstElementClass *)klass;
gstbin_klass = (GstBinClass *)klass;
parent_class = g_type_class_ref(gst_bin_get_type());
gobject_klass->dispose = GST_DEBUG_FUNCPTR(cdplayer_dispose);
gstelement_klass->change_state = GST_DEBUG_FUNCPTR(cdplayer_change_state);
gstbin_klass->iterate = GST_DEBUG_FUNCPTR(cdplayer_iterate);
g_object_class_install_property(gobject_klass,ARG_DEVICE,g_param_spec_string("device","device","CDROM device",NULL,G_PARAM_READWRITE));
g_object_class_install_property(gobject_klass,ARG_NUM_TRACKS,g_param_spec_int("num_tracks","num_tracks","Number of Tracks",G_MININT,G_MAXINT,0,G_PARAM_READABLE));
g_object_class_install_property(gobject_klass,ARG_START_TRACK,g_param_spec_int("start_track","start_track","Track to start playback on",1,CDPLAYER_MAX_TRACKS-1,1,G_PARAM_READABLE));
g_object_class_install_property(gobject_klass,ARG_START_TRACK,g_param_spec_int("start_track","start_track","Track to start playback on",1,CDPLAYER_MAX_TRACKS-1,1,G_PARAM_READWRITE));
g_object_class_install_property(gobject_klass,ARG_END_TRACK,g_param_spec_int("end_track","end_track","Track to end playback on",0,CDPLAYER_MAX_TRACKS-1,0,G_PARAM_READWRITE));
g_object_class_install_property(gobject_klass,ARG_CURRENT_TRACK,g_param_spec_int("current_track","current_track","Current track playing",1,CDPLAYER_MAX_TRACKS-1,1,G_PARAM_READABLE));
g_object_class_install_property(gobject_klass,ARG_CDDB_DISCID,g_param_spec_uint("cddb_discid","cddb_discid","CDDB Disc ID",0,G_MAXUINT,1,G_PARAM_READABLE));
cdplayer_signals[TRACK_CHANGE] = g_signal_new("track_change",G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,G_STRUCT_OFFSET(CDPlayerClass,track_change),NULL,NULL,gst_marshal_VOID__INT,G_TYPE_NONE,1,G_TYPE_INT);
gobject_klass->set_property = cdplayer_set_property;
gobject_klass->get_property = cdplayer_get_property;
......@@ -112,11 +126,12 @@ static void cdplayer_init(CDPlayer *cdp)
cdp->device = g_strdup("/dev/cdrom");
cdp->num_tracks = -1;
cdp->start_track = 1;
cdp->end_track = 0;
cdp->current_track = 1;
cdp->paused = FALSE;
GST_FLAG_SET(cdp,GST_BIN_FLAG_MANAGER);
GST_FLAG_SET(cdp,GST_BIN_SELF_SCHEDULABLE);
// GST_FLAG_SET(cdp,GST_BIN_FLAG_MANAGER);
return;
}
......@@ -140,7 +155,12 @@ static void cdplayer_set_property(GObject *object,guint prop_id,const GValue *va
break;
case ARG_START_TRACK:
// FIXME prolly should uhh.. restart play, i guess... or something whatever
cdp->start_track = g_value_get_int(value);
// FIXME we should only set current_track if its not playing...
cdp->current_track = cdp->start_track = g_value_get_int(value);
break;
case ARG_END_TRACK:
// FIXME prolly should restart play, maybe, or try to set it without interrupt..
cdp->end_track = g_value_get_int(value);
break;
default:
break;
......@@ -168,6 +188,13 @@ static void cdplayer_get_property(GObject *object,guint prop_id,GValue *value,GP
case ARG_START_TRACK:
g_value_set_int(value,cdp->start_track);
break;
case ARG_END_TRACK:
g_value_set_int(value,cdp->end_track);
case ARG_CURRENT_TRACK:
g_value_set_int(value,cdp->current_track);
break;
case ARG_CDDB_DISCID:
g_value_set_uint(value,cdp->cddb_discid);
default:
break;
}
......@@ -182,20 +209,49 @@ static void cdplayer_dispose(GObject *object)
g_return_if_fail(GST_IS_CDPLAYER(object));
cdp = CDPLAYER(object);
G_OBJECT_CLASS(parent_class)->dispose(object);
g_free(cdp->device);
if (GST_ELEMENT_SCHED(cdp)) {
gst_scheduler_reset(GST_ELEMENT_SCHED(cdp));
gst_object_unref(GST_OBJECT(GST_ELEMENT_SCHED(cdp)));
GST_ELEMENT_SCHED(cdp) = NULL;
if (G_OBJECT_CLASS(parent_class)->dispose) {
G_OBJECT_CLASS(parent_class)->dispose(object);
}
return;
}
static gboolean cdplayer_iterate(GstBin *bin)
{
CDPlayer *cdp = CDPLAYER(bin);
gint current_track;
switch (cd_status(CDPLAYER_CD(cdp))) {
case CD_PLAYING:
current_track = cd_current_track(CDPLAYER_CD(cdp));
if (current_track > cdp->end_track && cdp->end_track != 0) {
return FALSE;
}
if (current_track != -1 && current_track != cdp->current_track) {
cdp->current_track = current_track;
g_signal_emit(G_OBJECT(cdp),cdplayer_signals[TRACK_CHANGE],0,cdp->current_track);
}
return TRUE;
break;
case CD_ERROR:
gst_element_set_state(GST_ELEMENT(bin),GST_STATE_PAUSED);
return FALSE;
break;
case CD_COMPLETED:
gst_element_set_state(GST_ELEMENT(bin),GST_STATE_PAUSED);
gst_element_set_eos(GST_ELEMENT(bin));
return FALSE;
break;
}
return FALSE;
}
static GstElementStateReturn cdplayer_change_state(GstElement *element)
{
CDPlayer *cdp;
......@@ -213,6 +269,7 @@ static GstElementStateReturn cdplayer_change_state(GstElement *element)
return GST_STATE_FAILURE;
}
cdp->num_tracks = cdp->cd.num_tracks;
cdp->cddb_discid = cd_cddb_discid(CDPLAYER_CD(cdp));
}
break;
case GST_STATE_PAUSED:
......@@ -234,7 +291,7 @@ static GstElementStateReturn cdplayer_change_state(GstElement *element)
cdp->paused = FALSE;
} else {
if (cd_start(CDPLAYER_CD(cdp),cdp->start_track) == FALSE) {
if (cd_start(CDPLAYER_CD(cdp),cdp->start_track,cdp->end_track) == FALSE) {
return GST_STATE_FAILURE;
}
}
......
......@@ -45,7 +45,10 @@ struct _CDPlayer {
/* properties */
gchar *device;
gint num_tracks;
guint start_track;
gint start_track;
gint end_track;
gint current_track;
guint32 cddb_discid;
/* private */
struct cd cd;
......@@ -56,6 +59,7 @@ struct _CDPlayerClass {
GstBinClass parent_class;
/* signal callbacks */
void (*track_change) (GstElement *element,guint track);
};
GType cdplayer_get_type(void);
......
......@@ -29,137 +29,97 @@
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/cdrom.h>
gboolean cd_init(struct cd *cd,const gchar *device)
{
struct cdrom_tochdr toc_header;
struct cdrom_tocentry toc_entry;
guint i;
cd->fd = open(device,O_RDONLY | O_NONBLOCK);
if (cd->fd == -1) {
return FALSE;
}
/* get the toc header information */
if (ioctl(cd->fd,CDROMREADTOCHDR,&toc_header) != 0) {
return FALSE;
}
/* read each entry in the toc header */
for (i = 1; i < toc_header.cdth_trk1; i++) {
toc_entry.cdte_format = CDROM_MSF;
toc_entry.cdte_track = i;
if (ioctl(cd->fd,CDROMREADTOCENTRY,&toc_entry) != 0) {
return FALSE;
}
/* private functions */
static void cd_fix_track_range(struct cd *cd,gint *start_track,gint *end_track);
static gint cddb_sum(gint n);
cd->tracks[i].minute = toc_entry.cdte_addr.msf.minute;
cd->tracks[i].second = toc_entry.cdte_addr.msf.second;
cd->tracks[i].frame = toc_entry.cdte_addr.msf.frame;
cd->tracks[i].data_track = (toc_entry.cdte_ctrl == CDROM_DATA_TRACK);
}
/* read the leadout */
toc_entry.cdte_track = CDROM_LEADOUT;
toc_entry.cdte_format = CDROM_MSF;
if (ioctl(cd->fd,CDROMREADTOCENTRY,&toc_entry) != 0) {
return FALSE;
}
cd->tracks[LEADOUT].minute = toc_entry.cdte_addr.msf.minute;
cd->tracks[LEADOUT].second = toc_entry.cdte_addr.msf.second;
cd->tracks[LEADOUT].frame = toc_entry.cdte_addr.msf.frame;
cd->num_tracks = toc_header.cdth_trk1;
return TRUE;
}
gboolean cd_start(struct cd *cd,guint start_track)
#ifdef HAVE_LINUX_CDROM_H
#include <linux/cdrom.h>
#elif define HAVE_SYS_CDIO_H
#include <sys/cdio.h>
/*
irix cdaudio works quite a bit differently than ioctl(), so its not ready
#elif define HAVE_DMEDIA_CDAUDIO_H
#include <dmedia/cdaudio.h>
*/
#endif
/* these headers define low level functions:
gboolean cd_init(struct cd *cd,const gchar *device);
gboolean cd_start(struct cd *cd,gint start_track,gint end_track);
gboolean cd_pause(struct cd *cd);
gboolean cd_resume(struct cd *cd);
gboolean cd_stop(struct cd *cd);
CDStatus cd_status(struct cd *cd);
gint cd_current_track(struct cd *cd);
gboolean cd_close(struct cd *cd);
*/
#ifdef HAVE_CDROM_SOLARIS
#include "gstcdplayer_ioctl_solaris.h"
#elif defined HAVE_CDROM_BSD
#include "gstcdplayer_ioctl_bsd.h"
/*
#elif define HAVE_CDROM_IRIX
#include "gstcdplayer_ioctl_irix.h"
*/
#endif
static void cd_fix_track_range(struct cd *cd,gint *start_track,gint *end_track)
{
struct cdrom_msf msf;
if (cd->fd == -1) {
return FALSE;
if (*start_track <= 0) {
*start_track = 1;
}
if (start_track <= 0) {
start_track = 1;
if (*start_track > cd->num_tracks) {
*start_track = cd->num_tracks;
}
if (start_track > cd->num_tracks) {
start_track = cd->num_tracks;
if (*end_track < *start_track && *end_track != LEADOUT) {
*end_track = *start_track;
}
msf.cdmsf_min0 = cd->tracks[start_track].minute;
msf.cdmsf_sec0 = cd->tracks[start_track].second;
msf.cdmsf_frame0 = cd->tracks[start_track].frame;
msf.cdmsf_min1 = cd->tracks[LEADOUT].minute;
msf.cdmsf_sec1 = cd->tracks[LEADOUT].second;
msf.cdmsf_frame1 = cd->tracks[LEADOUT].frame;
if (ioctl(cd->fd,CDROMPLAYMSF,&msf) != 0) {
return FALSE;
if (*end_track > cd->num_tracks || *end_track + 1 > cd->num_tracks) {
*end_track = LEADOUT;
}
return TRUE;
return;
}
gboolean cd_pause(struct cd *cd)
{
if (cd->fd == -1) {
return FALSE;
}
if (ioctl(cd->fd,CDROMPAUSE,NULL) != 0) {
return FALSE;
}
/* this cddb info is from
http://www.freedb.org/modules.php?name=Sections&sop=viewarticle&artid=6
return TRUE;
}
gboolean cd_resume(struct cd *cd)
this will probably be of interest to anyone wishing to actually use the discid
http://www.freedb.org/modules.php?name=Sections&sop=viewarticle&artid=28
*/
static gint cddb_sum(gint n)
{
if (cd->fd == -1) {
return FALSE;
}
gint ret = 0;
if (ioctl(cd->fd,CDROMRESUME,NULL) != 0) {
return FALSE;
while (n > 0) {
ret += n % 10;
n /= 10;
}
return TRUE;
return ret;
}
gboolean cd_stop(struct cd *cd)
guint32 cd_cddb_discid(struct cd *cd)
{
if (cd->fd == -1) {
return FALSE;
}
if (ioctl(cd->fd,CDROMSTOP,NULL) != 0) {
return FALSE;
}
return TRUE;
}
guint i;
guint n = 0;
guint t;
gboolean cd_close(struct cd *cd)
{
if (cd->fd == -1) {
return TRUE;
for (i = 1; i <= cd->num_tracks; i++) {
n += cddb_sum(cd->tracks[i].minute * 60 + cd->tracks[i].second);
}
if (close(cd->fd) != 0) {
return FALSE;
}
t = (cd->tracks[LEADOUT].minute * 60 + cd->tracks[LEADOUT].second) - (cd->tracks[1].minute * 60 + cd->tracks[1].second);
return TRUE;
return ((n % 0xff) << 24 | t << 8 | (cd->num_tracks));
}
......@@ -17,8 +17,8 @@
* Boston, MA 02111-1307, USA.
*/
#ifndef __CDPLAYER_IOCTL_H__
#define __CDPLAYER_IOCTL_H__
#ifndef __CDPLAYER_LL_H__
#define __CDPLAYER_LL_H__
#ifdef HAVE_CONFIG_H
#include <config.h>
......@@ -32,6 +32,12 @@
#define CDPLAYER_MAX_TRACKS 128
typedef enum {
CD_PLAYING,
CD_COMPLETED,
CD_ERROR
} CDStatus;
struct cd_msf {
guint8 minute;
guint8 second;
......@@ -46,13 +52,19 @@ struct cd {
struct cd_msf tracks[CDPLAYER_MAX_TRACKS];
};
/* these are defined by the different cdrom type header files */
gboolean cd_init(struct cd *cd,const gchar *device);
gboolean cd_start(struct cd *cd,guint start_track);
gboolean cd_start(struct cd *cd,gint start_track,gint end_track);
gboolean cd_pause(struct cd *cd);
gboolean cd_resume(struct cd *cd);
gboolean cd_stop(struct cd *cd);
CDStatus cd_status(struct cd *cd);
gint cd_current_track(struct cd *cd);
gboolean cd_close(struct cd *cd);
guint32 cd_cddb_discid(struct cd *cd);
#endif
/* gstcdplay
* Copyright (c) 2002 Charles Schmidt <cbschmid@uiuc.edu>
* 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_CDROM_BSD_NETBSD /* net & open */
gboolean cd_init(struct cd *cd,const gchar *device)
{
struct ioc_toc_header toc_header;
struct ioc_read_toc_entry toc_entry;
struct cd_toc_entry toc_entry_data;
guint i;
cd->fd = open(device,O_RDONLY | O_NONBLOCK);
if (cd->fd == -1) {
return FALSE;
}
/* get the toc header information */
if (ioctl(cd->fd,CDIOREADTOCHDR,&toc_header) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}
/* read each entry in the toc header */
for (i = 1; i <= toc_header.ending_track; i++) {
toc_entry.address_format = CD_MSF_FORMAT;
toc_entry.starting_track = i;
toc_entry.data = &toc_entry_data;
toc_entry.data_len = sizeof(toc_entry_data);
if (ioctl(cd->fd,CDIOREADTOCENTRYS,&toc_entry) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}
cd->tracks[i].minute = toc_entry.data->addr.msf.minute;
cd->tracks[i].second = toc_entry.data->addr.msf.second;
cd->tracks[i].frame = toc_entry.data->addr.msf.frame;
cd->tracks[i].data_track = (toc_entry.data->control & 4) == 4;
}
/* read the leadout */
toc_entry.address_format = CD_MSF_FORMAT;
toc_entry.starting_track = 0xAA; /* leadout */
toc_entry.data = &toc_entry_data;
toc_entry.data_len = sizeof(toc_entry_data);
if (ioctl(cd->fd,CDIOREADTOCENTRYS,&toc_entry) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}
cd->tracks[LEADOUT].minute = toc_entry.data->addr.msf.minute;
cd->tracks[LEADOUT].second = toc_entry.data->addr.msf.second;
cd->tracks[LEADOUT].frame = toc_entry.data->addr.msf.frame;
cd->num_tracks = toc_header.ending_track;
return TRUE;
}
#elif defined HAVE_CDROM_BSD_DARWIN
gboolean cd_init(struct cd *cd,const gchar *device)
{
struct ioc_toc_header toc_header;
struct ioc_read_toc_entry toc_entry;
guint i;
cd->fd = open(device,O_RDONLY | O_NONBLOCK);
if (cd->fd == -1) {
return FALSE;
}
/* get the toc header information */
if (ioctl(cd->fd,CDIOREADTOCHDR,&toc_header) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}
/* read each entry in the toc header */
for (i = 1; i <= toc_header.ending_track; i++) {
toc_entry.address_format = CD_MSF_FORMAT;
toc_entry.starting_track = i;
if (ioctl(cd->fd,CDIOREADTOCENTRYS,&toc_entry) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}
cd->tracks[i].minute = toc_entry.data->addr[1];
cd->tracks[i].second = toc_entry.data->addr[2];
cd->tracks[i].frame = toc_entry.data->addr[3];
cd->tracks[i].data_track = (toc_entry.data->control & 4) == 4;
}
/* read the leadout */
toc_entry.address_format = CD_MSF_FORMAT;
toc_entry.starting_track = 0xAA; /* leadout */
toc_entry.data = &toc_entry_data;
toc_entry.data_len = sizeof(toc_entry_data);
if (ioctl(cd->fd,CDIOREADTOCENTRYS,&toc_entry) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}
cd->tracks[LEADOUT].minute = toc_entry.data->addr[1];
cd->tracks[LEADOUT].second = toc_entry.data->addr[2];
cd->tracks[LEADOUT].frame = toc_entry.data->addr[3];
cd->num_tracks = toc_header.ending_track;
return TRUE;
}
#else /* free */
gboolean cd_init(struct cd *cd,const gchar *device)
{
struct ioc_toc_header toc_header;
struct ioc_read_toc_entry toc_entry;
guint i;
cd->fd = open(device,O_RDONLY | O_NONBLOCK);
if (cd->fd == -1) {
return FALSE;
}
/* get the toc header information */
if (ioctl(cd->fd,CDIOREADTOCHDR,&toc_header) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}
/* read each entry in the toc header */
for (i = 1; i <= toc_header.ending_track; i++) {
toc_entry.address_format = CD_MSF_FORMAT;
toc_entry.starting_track = i;
if (ioctl(cd->fd,CDIOREADTOCENTRYS,&toc_entry) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}
cd->tracks[i].minute = toc_entry.entry.addr.msf.minute;
cd->tracks[i].second = toc_entry.entry.addr.msf.second;
cd->tracks[i].frame = toc_entry.entry.addr.msf.frame;
cd->tracks[i].data_track = (toc_entry.data->control & 4) == 4;
}
/* read the leadout */
toc_entry.address_format = CD_MSF_FORMAT;
toc_entry.starting_track = 0xAA; /* leadout */
toc_entry.data = &toc_entry_data;
toc_entry.data_len = sizeof(toc_entry_data);
if (ioctl(cd->fd,CDIOREADTOCENTRYS,&toc_entry) != 0) {
close(cd->fd);
cd->fd = -1;
return FALSE;
}