gstcdparanoia.c 29.1 KB
Newer Older
1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
Andy Wingo's avatar
Andy Wingo committed
2
/* GStreamer
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.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.
 */

21
/* #define GST_DEBUG_ENABLED */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
22 23 24 25 26 27 28 29 30 31 32 33 34

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
Wim Taymans's avatar
Wim Taymans committed
35

36
/* taken from linux/cdrom.h */
37 38 39
#define CD_MSF_OFFSET       150	/* MSF numbering offset of first frame */
#define CD_SECS              60	/* seconds per minute */
#define CD_FRAMES            75	/* frames per second */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
40 41 42 43 44 45

#include "gstcdparanoia.h"


static GstElementDetails cdparanoia_details = {
  "CD Audio (cdda) Source, Paranoia IV",
46
  "Source/File",
47
  "LGPL",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48 49 50 51 52 53
  "Read audio from CD in paranoid mode",
  VERSION,
  "Erik Walthinsen <omega@cse.ogi.edu>",
  "(C) 2000",
};

54
GST_PAD_TEMPLATE_FACTORY (cdparanoia_src_factory,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
55 56 57 58 59 60
  "src",
  GST_PAD_SRC,
  GST_PAD_ALWAYS,
  GST_CAPS_NEW (
    "cdparanoia_src",
    "audio/raw",
61 62 63 64 65 66 67 68 69
	"format", 	GST_PROPS_STRING ("int"),
	"law", 		GST_PROPS_INT (0),
	"endianness", 	GST_PROPS_INT (G_BYTE_ORDER),
	"signed", 	GST_PROPS_BOOLEAN (TRUE),
	"width", 	GST_PROPS_INT (16),
	"depth", 	GST_PROPS_INT (16),
	"rate", 	GST_PROPS_INT (44100),
	"channels", 	GST_PROPS_INT (2),
	"chunksize", 	GST_PROPS_INT (CD_FRAMESIZE_RAW)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
70 71 72 73 74
  )
);


/********** Define useful types for non-programmatic interfaces **********/
Wim Taymans's avatar
Wim Taymans committed
75
#define GST_TYPE_PARANOIA_MODE (gst_paranoia_mode_get_type())
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
76
static GType
Wim Taymans's avatar
Wim Taymans committed
77
gst_paranoia_mode_get_type (void)
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
78 79 80
{
  static GType paranoia_mode_type = 0;
  static GEnumValue paranoia_modes[] = {
81 82 83
    { PARANOIA_MODE_DISABLE, "0",   "Disable paranoid checking"},
    { PARANOIA_MODE_OVERLAP, "4",   "cdda2wav-style overlap checking"},
    { PARANOIA_MODE_FULL,    "255", "Full paranoia"},
84
    {0, NULL, NULL},
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
85
  };
86

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
87 88 89 90 91 92
  if (!paranoia_mode_type) {
    paranoia_mode_type = g_enum_register_static ("GstParanoiaMode", paranoia_modes);
  }
  return paranoia_mode_type;
}

93 94 95 96 97 98
#define GST_TYPE_PARANOIA_ENDIAN (gst_paranoia_endian_get_type())
static GType
gst_paranoia_endian_get_type (void)
{
  static GType paranoia_endian_type = 0;
  static GEnumValue paranoia_endians[] = {
99 100 101
    { 0, "0", "treat drive as little endian"},
    { 1, "1", "treat drive as big endian"},
    { 0, NULL, NULL},
102 103 104 105 106 107 108
  };

  if (!paranoia_endian_type) {
    paranoia_endian_type = g_enum_register_static ("GstParanoiaEndian", paranoia_endians);
  }
  return paranoia_endian_type;
}
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109 110 111

/********** Standard stuff for signals and arguments **********/
/* CDParanoia signals and args */
112 113
enum
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
114 115 116 117 118 119
  SMILIE_CHANGE,
  TRANSPORT_ERROR,
  UNCORRECTED_ERROR,
  LAST_SIGNAL
};

120 121
enum
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
122 123 124 125 126 127 128 129 130 131 132 133 134
  ARG_0,
  ARG_LOCATION,
  ARG_GENERIC_DEVICE,
  ARG_DEFAULT_SECTORS,
  ARG_SEARCH_OVERLAP,
  ARG_ENDIAN,
  ARG_READ_SPEED,
  ARG_TOC_OFFSET,
  ARG_TOC_BIAS,
  ARG_NEVER_SKIP,
  ARG_ABORT_ON_SKIP,
  ARG_PARANOIA_MODE,
  ARG_SMILIE,
135
  ARG_DISCID
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
136 137 138
};


139 140
static void 		cdparanoia_class_init 		(CDParanoiaClass *klass);
static void 		cdparanoia_init 		(CDParanoia *cdparanoia);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
141

142 143 144 145
static void 		cdparanoia_set_property 	(GObject *object, guint prop_id, 
							 const GValue *value, GParamSpec *pspec);
static void 		cdparanoia_get_property 	(GObject *object, guint prop_id,
							 GValue *value, GParamSpec *pspec);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
146

147 148 149 150 151 152 153 154 155 156 157 158 159
static GstBuffer*	cdparanoia_get 			(GstPad *pad);
static gboolean 	cdparanoia_event 		(GstPad *pad, GstEvent *event);
static const GstEventMask*
			cdparanoia_get_event_mask 	(GstPad *pad);
static const GstFormat*
			cdparanoia_get_formats 		(GstPad *pad);
static gboolean 	cdparanoia_convert 		(GstPad *pad,
				    			 GstFormat src_format,
				    			 gint64 src_value, 
							 GstFormat *dest_format, 
							 gint64 *dest_value);
static gboolean 	cdparanoia_query 		(GstPad *pad, GstPadQueryType type,
		     					 GstFormat *format, gint64 *value);
160 161
static const GstPadQueryType*
			cdparanoia_get_query_types 	(GstPad *pad);
162

163
static GstElementStateReturn cdparanoia_change_state (GstElement *element);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
164 165 166 167 168


static GstElementClass *parent_class = NULL;
static guint cdparanoia_signals[LAST_SIGNAL] = { 0 };

169
static GstFormat track_format;
170
static GstFormat sector_format;
171

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
172 173 174 175 176 177 178
GType
cdparanoia_get_type (void)
{
  static GType cdparanoia_type = 0;

  if (!cdparanoia_type) {
    static const GTypeInfo cdparanoia_info = {
179
      sizeof (CDParanoiaClass), NULL,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
180
      NULL,
181
      (GClassInitFunc) cdparanoia_class_init,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
182 183
      NULL,
      NULL,
184
      sizeof (CDParanoia),
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
185
      0,
186
      (GInstanceInitFunc) cdparanoia_init,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
187
    };
188

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
189
    cdparanoia_type = g_type_register_static (GST_TYPE_ELEMENT, "CDParanoia", &cdparanoia_info, 0);
190 191 192 193
    
    /* Register the track format */
    track_format = gst_format_register ("track", "CD track");
    sector_format = gst_format_register ("sector", "CD sector");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
194 195 196 197 198 199 200 201 202
  }
  return cdparanoia_type;
}

static void
cdparanoia_class_init (CDParanoiaClass *klass)
{
  GObjectClass *gobject_class;
  GstElementClass *gstelement_class;
203
  char *success = strerror_tr[0];
204

205
  success = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
206

207 208
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
209 210 211 212

  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

  cdparanoia_signals[SMILIE_CHANGE] =
213 214 215
    g_signal_new ("smilie_change", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (CDParanoiaClass, smilie_change), NULL, NULL,
		  g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
216
  cdparanoia_signals[TRANSPORT_ERROR] =
217 218 219
    g_signal_new ("transport_error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (CDParanoiaClass, transport_error), NULL, NULL,
		  g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
220
  cdparanoia_signals[UNCORRECTED_ERROR] =
221 222 223 224 225 226 227 228 229 230 231 232 233
    g_signal_new ("uncorrected_error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (CDParanoiaClass, uncorrected_error), NULL, NULL,
		  g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);

  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION, 
    g_param_spec_string ("location", "location", "location", 
	                 NULL, G_PARAM_READWRITE));	
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_GENERIC_DEVICE, 
    g_param_spec_string ("generic_device", "Generic device", "Use specified generic scsi device", 
	                 NULL, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEFAULT_SECTORS, 
    g_param_spec_int ("default_sectors", "Default sectors", 
	    	      "Force default number of sectors in read to n sectors", 
234
		      -1, 100, -1, G_PARAM_READWRITE));
235 236 237
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SEARCH_OVERLAP, 
    g_param_spec_int ("search_overlap", "Search overlap", 
	    	      "Force minimum overlap search during verification to n sectors", 
238
		       -1, 75, -1, G_PARAM_READWRITE));
239 240 241 242 243 244
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ENDIAN, 
    g_param_spec_enum ("endian", "Endian", "Force endian on drive", 
		       GST_TYPE_PARANOIA_ENDIAN, 0, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_READ_SPEED, 
    g_param_spec_int ("read_speed", "Read speed", "Read from device at specified speed", 
		      G_MININT, G_MAXINT, 0, G_PARAM_READWRITE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
245
  g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TOC_OFFSET,
246 247
    g_param_spec_int("toc_offset", "TOC offset", "Add <n> sectors to the values reported",
                     G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); 
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
248
  g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TOC_BIAS,
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
    g_param_spec_boolean("toc_bias", "TOC bias",
	    		 "Assume that the beginning offset of track 1 as reported in the TOC "
			 "will be addressed as LBA 0.  Necessary for some Toshiba drives to "
			 "get track boundaries",
                         TRUE,G_PARAM_READWRITE)); 
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NEVER_SKIP, 
    g_param_spec_int ("never_skip", "Never skip", 
	    	      "never accept any less than perfect data reconstruction (don't allow "
		      "'V's) but if [n] is given, skip after [n] retries without progress.", 
		      0, G_MAXINT, 0, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ABORT_ON_SKIP, 
    g_param_spec_boolean ("abort_on_skip", "Abort on skip", "Abort on imperfect reads/skips", 
		          TRUE, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PARANOIA_MODE, 
    g_param_spec_enum ("paranoia_mode", "Paranoia mode", "Type of checking to perform", 
		       GST_TYPE_PARANOIA_MODE, 0, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DISCID,
    g_param_spec_string ("discid", "discid", "The dics id",
			 NULL, G_PARAM_READABLE));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
268 269 270 271 272 273 274 275 276 277

  gobject_class->set_property = cdparanoia_set_property;
  gobject_class->get_property = cdparanoia_get_property;

  gstelement_class->change_state = cdparanoia_change_state;
}

static void
cdparanoia_init (CDParanoia *cdparanoia)
{
278 279
  cdparanoia->srcpad =
    gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (cdparanoia_src_factory), "src");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
280
  gst_pad_set_get_function (cdparanoia->srcpad, cdparanoia_get);
281 282 283 284
  gst_pad_set_event_function (cdparanoia->srcpad, cdparanoia_event);
  gst_pad_set_event_mask_function (cdparanoia->srcpad, cdparanoia_get_event_mask);
  gst_pad_set_convert_function (cdparanoia->srcpad, cdparanoia_convert);
  gst_pad_set_query_function (cdparanoia->srcpad, cdparanoia_query);
285
  gst_pad_set_query_type_function (cdparanoia->srcpad, cdparanoia_get_query_types);
286 287
  gst_pad_set_formats_function (cdparanoia->srcpad, cdparanoia_get_formats);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
288 289
  gst_element_add_pad (GST_ELEMENT (cdparanoia), cdparanoia->srcpad);

290
  cdparanoia->device = g_strdup ("/dev/cdrom");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
291 292 293
  cdparanoia->generic_device = NULL;
  cdparanoia->default_sectors = -1;
  cdparanoia->search_overlap = -1;
294
  cdparanoia->endian = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
295 296 297 298 299 300 301
  cdparanoia->read_speed = -1;
  cdparanoia->toc_offset = 0;
  cdparanoia->toc_bias = FALSE;
  cdparanoia->never_skip = FALSE;
  cdparanoia->paranoia_mode = 2;
  cdparanoia->abort_on_skip = FALSE;

302
  cdparanoia->total_seconds = 0;
303
  cdparanoia->discont_pending = FALSE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
}


static void
cdparanoia_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
  CDParanoia *src;

  /* it's not null if we got it, but it might not be ours */
  g_return_if_fail (GST_IS_CDPARANOIA (object));

  src = CDPARANOIA (object);

  switch (prop_id) {
    case ARG_LOCATION:
319 320
      if (src->device)
	g_free (src->device);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
321
      /* clear the filename if we get a NULL (is that possible?) */
322 323
      if (!g_ascii_strcasecmp (g_value_get_string (value), ""))
	src->device = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
324 325
      /* otherwise set the new filename */
      else
326
	src->device = g_strdup (g_value_get_string (value));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
327 328 329
      break;
    case ARG_GENERIC_DEVICE:

330 331
      if (src->generic_device)
	g_free (src->generic_device);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
332
      /* reset the device if we get a NULL (is that possible?) */
333 334
      if (!g_ascii_strcasecmp (g_value_get_string (value), ""))
	src->generic_device = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
335 336
      /* otherwise set the new filename */
      else
337
	src->generic_device = g_strdup (g_value_get_string (value));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
338 339 340 341 342 343 344 345
      break;
    case ARG_DEFAULT_SECTORS:
      src->default_sectors = g_value_get_int (value);
      break;
    case ARG_SEARCH_OVERLAP:
      src->search_overlap = g_value_get_int (value);
      break;
    case ARG_ENDIAN:
346
      src->endian = g_value_get_enum (value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
347 348 349
      break;
    case ARG_READ_SPEED:
      src->read_speed = g_value_get_int (value);
350
      cdda_speed_set (src->d, src->read_speed);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
351 352 353 354 355 356 357 358
      break;
    case ARG_TOC_OFFSET:
      src->toc_offset = g_value_get_int (value);
      break;
    case ARG_TOC_BIAS:
      src->toc_bias = g_value_get_boolean (value);
      break;
    case ARG_NEVER_SKIP:
359
      src->never_skip = g_value_get_int (value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
360 361 362 363 364
      break;
    case ARG_ABORT_ON_SKIP:
      src->abort_on_skip = g_value_get_boolean (value);
      break;
    case ARG_PARANOIA_MODE:
Wim Taymans's avatar
Wim Taymans committed
365
      src->paranoia_mode = g_value_get_enum (value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
      break;
    default:
      break;
  }

}

static void
cdparanoia_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
  CDParanoia *src;

  g_return_if_fail (GST_IS_CDPARANOIA (object));

  src = CDPARANOIA (object);

  switch (prop_id) {
    case ARG_LOCATION:
      g_value_set_string (value, src->device);
      break;
    case ARG_GENERIC_DEVICE:
      g_value_set_string (value, src->generic_device);
      break;
    case ARG_DEFAULT_SECTORS:
      g_value_set_int (value, src->default_sectors);
      break;
    case ARG_SEARCH_OVERLAP:
      g_value_set_int (value, src->search_overlap);
      break;
    case ARG_ENDIAN:
396
      g_value_set_enum (value, src->endian);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
397 398 399 400 401 402 403 404 405 406 407
      break;
    case ARG_READ_SPEED:
      g_value_set_int (value, src->read_speed);
      break;
    case ARG_TOC_OFFSET:
      g_value_set_int (value, src->toc_offset);
      break;
    case ARG_TOC_BIAS:
      g_value_set_boolean (value, src->toc_bias);
      break;
    case ARG_NEVER_SKIP:
408
      g_value_set_int (value, src->never_skip);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
409 410 411 412 413
      break;
    case ARG_ABORT_ON_SKIP:
      g_value_set_boolean (value, src->abort_on_skip);
      break;
    case ARG_PARANOIA_MODE:
Wim Taymans's avatar
Wim Taymans committed
414
      g_value_set_enum (value, src->paranoia_mode);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
415
      break;
416
    case ARG_DISCID:
417
      g_value_set_string (value, src->discid);
418
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
419 420 421 422 423 424
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

425 426 427
static void
cdparanoia_callback (long inpos, int function)
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
428 429
}

430
static GstBuffer *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
431 432 433 434 435 436 437
cdparanoia_get (GstPad *pad)
{
  CDParanoia *src;
  GstBuffer *buf;

  src = CDPARANOIA (gst_pad_get_parent (pad));

438
  g_return_val_if_fail (GST_FLAG_IS_SET (src, CDPARANOIA_OPEN), NULL);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
439

440 441
  /* stop things apropriatly */
  if (src->cur_sector > src->segment_end_sector) {
442
    GST_DEBUG (0, "setting EOS");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
443

Wim Taymans's avatar
Wim Taymans committed
444
    buf = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
    gst_element_set_eos (GST_ELEMENT (src));
  }
  else {
    gint16 *cdda_buf;
    gint64 timestamp;
    GstFormat format;

    /* read a sector */
    cdda_buf = paranoia_read (src->p, cdparanoia_callback);

    /* convert the sequence sector number to a timestamp */
    format = GST_FORMAT_TIME;
    timestamp = 0LL;
    gst_pad_convert (src->srcpad, sector_format, src->seq, 
		    &format, &timestamp);

461 462
    /* have to copy the buffer for now since we don't own it... */
    /* FIXME must ask monty about allowing ownership transfer */
Wim Taymans's avatar
Wim Taymans committed
463
    buf = gst_buffer_new_and_alloc (CD_FRAMESIZE_RAW);
Wim Taymans's avatar
Wim Taymans committed
464
    memcpy (GST_BUFFER_DATA (buf), cdda_buf, CD_FRAMESIZE_RAW);
465 466 467 468
    GST_BUFFER_TIMESTAMP (buf) = timestamp;
  
    /* update current sector */
    src->cur_sector++;
Wim Taymans's avatar
Wim Taymans committed
469
    src->seq++;
Wim Taymans's avatar
Wim Taymans committed
470
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
471 472 473 474 475

  /* we're done, push the buffer off now */
  return buf;
}

476 477 478 479 480 481
/* need some stuff to get a discid (cdparanoia doesn't do cddb but lets
 * not stop other ppl doing it ;-) */
typedef int byte;

typedef struct
{
482 483 484 485 486
  byte m;
  byte s;
  byte f;
}
toc_msf;
487 488 489

/* cdparanoia provides the toc in lba format dang we need it in msf so
 * we have to convert it */
490 491
static inline void
lba_to_msf (const gint lba, byte *m, byte *s, byte *f)
492
{
493 494 495 496 497 498 499 500 501 502
  gint lba2 = lba;

  lba2 += CD_MSF_OFFSET;
  lba2 &= 0xffffff;
  *m = lba2 / (CD_SECS * CD_FRAMES);
  lba2 %= (CD_SECS * CD_FRAMES);
  *s = lba2 / CD_FRAMES;
  *f = lba2 % CD_FRAMES;
  *f += (*m) * 60 * 75;
  *f += (*s) * 75;
503 504
}

505
static void
Wim Taymans's avatar
Wim Taymans committed
506
lba_toc_to_msf_toc (TOC *lba_toc, toc_msf *msf_toc, gint tracks)
507
{
508 509 510 511
  gint i;

  for (i = 0; i <= tracks; i++)
    lba_to_msf (lba_toc[i].dwStartSector, &msf_toc[i].m, &msf_toc[i].s, &msf_toc[i].f);
512 513 514
}

/* the cddb hash function */
Wim Taymans's avatar
Wim Taymans committed
515
static guint
516
cddb_sum (gint n)
517
{
518 519 520 521 522 523 524 525
  guint ret;

  ret = 0;
  while (n > 0) {
    ret += (n % 10);
    n /= 10;
  }
  return ret;
526 527
}

528 529
static void
cddb_discid (gchar *discid, toc_msf *toc, gint tracks)
530
{
531
  guint i = 0, t = 0, n = 0;
532

533 534 535 536 537 538 539
  while (i < tracks) {
    n = n + cddb_sum ((toc[i].m * 60) + toc[i].s);
    i++;
  }
  t = ((toc[tracks].m * 60) + toc[tracks].s) - ((toc[0].m * 60)
						+ toc[0].s);
  sprintf (discid, "%08x", ((n % 0xff) << 24 | t << 8 | tracks));
540 541 542
}

/* get all the cddb info at once */
543 544
static void
get_cddb_info (TOC *toc, gint tracks, gchar *discid, gint64 *offsets, gint64 *total_seconds)
545
{
546 547 548
  toc_msf msf_toc[MAXTRK];
  gint i;
  gint64 *p = offsets;
549

550 551
  lba_toc_to_msf_toc (toc, &msf_toc[0], tracks);
  cddb_discid (discid, &msf_toc[0], tracks);
552

553 554 555 556 557
  for (i = 0; i < tracks; i++) {
    *p++ = msf_toc[i].f;
  }

  *total_seconds = msf_toc[tracks].f / 75;
558 559 560

}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
561 562 563 564 565 566 567 568 569
/* open the file, necessary to go to RUNNING state */
static gboolean
cdparanoia_open (CDParanoia *src)
{
  gint i;
  gint paranoia_mode;

  g_return_val_if_fail (!GST_FLAG_IS_SET (src, CDPARANOIA_OPEN), FALSE);

570
  GST_DEBUG_ENTER ("(\"%s\",...)", gst_element_get_name (GST_ELEMENT (src)));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
571

572
  /* find the device */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
573 574 575 576 577 578 579 580 581 582
  if (src->generic_device != NULL) {
    src->d = cdda_identify_scsi (src->generic_device, src->device, FALSE, NULL);
  } else {
    if (src->device != NULL) {
      src->d = cdda_identify (src->device, FALSE, NULL);
    } else {
      src->d = cdda_identify ("/dev/cdrom", FALSE, NULL);
    }
  }

583
  /* fail if the device couldn't be found */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
584
  if (src->d == NULL) {
585
    GST_DEBUG (0, "couldn't open device");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
586 587 588
    return FALSE;
  }

589
  /* set verbosity mode */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
590 591
  cdda_verbose_set (src->d, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);

592
  /* set various other parameters */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
593
  if (src->default_sectors != -1) {
594 595
    src->d->nsectors = src->default_sectors;
    src->d->bigbuff = src->default_sectors * CD_FRAMESIZE_RAW;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
596 597
  }

598
  /* open the disc */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
599
  if (cdda_open (src->d)) {
600
    GST_DEBUG (0, "couldn't open disc");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
601 602 603 604 605
    cdda_close (src->d);
    src->d = NULL;
    return FALSE;
  }

606 607
  /* I don't like this here i would prefer it under get_cddb_info but for somereason
   * when leaving the function it clobbers the allocated mem and all is lost bugger
608
   */
609

610
  get_cddb_info (&src->d->disc_toc[0], src->d->tracks, src->discid,
611
		 src->offsets, &src->total_seconds);
Wim Taymans's avatar
Wim Taymans committed
612 613 614 615

  g_object_freeze_notify (G_OBJECT (src));
  g_object_notify (G_OBJECT (src), "discid");
  g_object_thaw_notify (G_OBJECT (src));
616

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
617 618 619
  if (src->toc_bias) {
    src->toc_offset -= cdda_track_firstsector (src->d, 1);
  }
620
  for (i = 0; i < src->d->tracks + 1; i++) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
621 622 623 624 625 626 627
    src->d->disc_toc[i].dwStartSector += src->toc_offset;
  }

  if (src->read_speed != -1) {
    cdda_speed_set (src->d, src->read_speed);
  }

628 629 630
  /* save thse ones */
  src->first_sector = cdda_disc_firstsector (src->d);
  src->last_sector  = cdda_disc_lastsector (src->d);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
631

632 633 634
  /* this is the default segment we will play */
  src->segment_start_sector = src->first_sector;
  src->segment_end_sector   = src->last_sector;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
635

636
  /* create the paranoia struct and set it up */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
637 638
  src->p = paranoia_init (src->d);
  if (src->p == NULL) {
639
    GST_DEBUG (0, "couldn't create paranoia struct");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
640 641 642
    return FALSE;
  }

643
  paranoia_mode = src->paranoia_mode;
644 645
  if (src->never_skip)
    paranoia_mode |= PARANOIA_MODE_NEVERSKIP;
646

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
647 648 649 650 651 652
  paranoia_modeset (src->p, paranoia_mode);

  if (src->search_overlap != -1) {
    paranoia_overlapset (src->p, src->search_overlap);
  }

653
  src->cur_sector = src->first_sector;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
654
  paranoia_seek (src->p, src->cur_sector, SEEK_SET);
655
  GST_DEBUG (0, "successfully seek'd to beginning of disk");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
656 657 658

  GST_FLAG_SET (src, CDPARANOIA_OPEN);

659
  GST_DEBUG_LEAVE ("");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
660 661 662 663 664 665 666 667 668 669

  return TRUE;
}

/* close the file */
static void
cdparanoia_close (CDParanoia *src)
{
  g_return_if_fail (GST_FLAG_IS_SET (src, CDPARANOIA_OPEN));

670
  /* kill the paranoia state */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
671 672
  paranoia_free (src->p);
  src->p = NULL;
673 674

  src->total_seconds = 0LL;
675
  /* close the disc */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
676 677 678 679 680 681 682 683 684
  cdda_close (src->d);
  src->d = NULL;

  GST_FLAG_UNSET (src, CDPARANOIA_OPEN);
}

static GstElementStateReturn
cdparanoia_change_state (GstElement *element)
{
Wim Taymans's avatar
Wim Taymans committed
685 686
  CDParanoia *cdparanoia;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
687 688
  g_return_val_if_fail (GST_IS_CDPARANOIA (element), GST_STATE_FAILURE);

Wim Taymans's avatar
Wim Taymans committed
689 690 691 692 693 694
  cdparanoia = CDPARANOIA (element);

  switch (GST_STATE_TRANSITION (element)) {
    case GST_STATE_NULL_TO_READY:
      break;
    case GST_STATE_READY_TO_PAUSED:
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
695
      if (!cdparanoia_open (CDPARANOIA (element))) {
696 697
	g_warning ("cdparanoia: failed opening cd");
	return GST_STATE_FAILURE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
698
      }
Wim Taymans's avatar
Wim Taymans committed
699 700 701 702 703 704 705 706 707 708 709 710 711
      cdparanoia->seq = 0;
      break;
    case GST_STATE_PAUSED_TO_PLAYING:
      break;
    case GST_STATE_PLAYING_TO_PAUSED:
      break;
    case GST_STATE_PAUSED_TO_READY:
      cdparanoia_close (CDPARANOIA (element));
      break;
    case GST_STATE_READY_TO_NULL:
      break;
    default:
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
712 713 714 715 716 717 718 719 720
  }

  /* if we haven't failed already, give the parent class a chance too ;-) */
  if (GST_ELEMENT_CLASS (parent_class)->change_state)
    return GST_ELEMENT_CLASS (parent_class)->change_state (element);

  return GST_STATE_SUCCESS;
}

721 722 723
static const GstEventMask *
cdparanoia_get_event_mask (GstPad *pad)
{
724
  static const GstEventMask masks[] = {
725 726 727 728 729 730 731 732
    {GST_EVENT_SEEK,         GST_SEEK_METHOD_SET | 
	                     GST_SEEK_METHOD_CUR | 
	                     GST_SEEK_METHOD_END | 
		             GST_SEEK_FLAG_FLUSH },
    {GST_EVENT_SEEK_SEGMENT, GST_SEEK_METHOD_SET | 
	                     GST_SEEK_METHOD_CUR | 
	                     GST_SEEK_METHOD_END | 
		             GST_SEEK_FLAG_FLUSH },
733 734
    {0,}
  };
735

736
  return masks;
737 738 739
}

static gboolean
740
cdparanoia_event (GstPad *pad, GstEvent *event)
741
{
742
  CDParanoia *src;
743
  gboolean res = TRUE;
744 745 746

  src = CDPARANOIA (gst_pad_get_parent (pad));

747 748 749
  if (!GST_FLAG_IS_SET (src, CDPARANOIA_OPEN))
    goto error;

750 751
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
    case GST_EVENT_SEEK_SEGMENT:
    {
      gint64 offset, endoffset;
      gint format;
      gint64 seg_start_sector = -1, seg_end_sector = -1;
	    
      format    = GST_EVENT_SEEK_FORMAT (event);
      offset    = GST_EVENT_SEEK_OFFSET (event);
      endoffset = GST_EVENT_SEEK_ENDOFFSET (event);

      /* we can only seek on sectors, so we convert the requested
       * offsets to sectors first */
      if (offset != -1) {
        res &= gst_pad_convert (src->srcpad, format, offset, 
		                &sector_format, &seg_start_sector);
      }
      if (endoffset != -1) {
        res &= gst_pad_convert (src->srcpad, format, endoffset, 
		                &sector_format, &seg_end_sector);
      }
      
      if (!res) {
	GST_DEBUG (0, "could not convert offsets to sectors");
775 776
	goto error;
      }
777
      
778 779
      switch (GST_EVENT_SEEK_METHOD (event)) {
	case GST_SEEK_METHOD_SET:
780
          /* values are set for regular seek set */
781 782
	  break;
	case GST_SEEK_METHOD_CUR:
783 784 785 786 787 788
	  if (seg_start_sector != -1) {
	    seg_start_sector += src->cur_sector;
	  }
	  if (seg_end_sector != -1) {
	    seg_end_sector += src->cur_sector;
	  }
789
	  break;
790 791 792 793 794 795 796
	case GST_SEEK_METHOD_END:
	  if (seg_start_sector != -1) {
	    seg_start_sector = src->last_sector - seg_start_sector;
	  }
	  if (seg_end_sector != -1) {
	    seg_end_sector = src->last_sector - seg_end_sector;
	  }
797
	  break;
798 799
	default:
	  goto error;
800
      }
801 802 803 804 805 806 807
      /* do we need to update the start sector? */
      if (seg_start_sector != -1) {
        seg_start_sector = CLAMP (seg_start_sector, 
			          src->first_sector, src->last_sector);

        if (paranoia_seek (src->p, seg_start_sector, SEEK_SET) > -1) {
	  GST_DEBUG (0, "seeked to %lld", seg_start_sector);
808

809 810 811 812 813
	  src->segment_start_sector = seg_start_sector;;
	  src->cur_sector = src->segment_start_sector;
	}
	else
	  goto error;
814
      }
815 816 817 818
      if (seg_end_sector != -1) {
        seg_end_sector = CLAMP (seg_end_sector, 
			        src->first_sector, src->last_sector);
        src->segment_end_sector = seg_end_sector;;
819
      }
820 821 822
      GST_DEBUG (GST_CAT_PLUGIN_INFO, "configured for %d -> %d sectors\n", 
		      src->segment_start_sector, 
		      src->segment_end_sector);
823
      break;
824
    }
825 826 827
    default:
      goto error;
  }
828

829
  if (FALSE) {
830
error:
831 832
    res = FALSE;
  }
833
  gst_event_unref (event);
834 835

  return res;
836 837 838 839 840
}

static const GstFormat *
cdparanoia_get_formats (GstPad *pad)
{
841 842
  static GstFormat formats[] = {
    GST_FORMAT_TIME,
843
    GST_FORMAT_BYTES,
844
    GST_FORMAT_UNITS,
845 846
    0,			/* filled later */
    0,			/* filled later */
847 848 849
    0
  };

850 851
  formats[3] = track_format;
  formats[4] = sector_format;
852

853
  return formats;
854 855 856 857
}

static gboolean
cdparanoia_convert (GstPad *pad,
858 859
		    GstFormat src_format, gint64 src_value, 
		    GstFormat *dest_format, gint64 *dest_value)
860
{
861
  CDParanoia *src;
862

863
  src = CDPARANOIA (gst_pad_get_parent (pad));
864

865 866 867
  if (!GST_FLAG_IS_SET (src, CDPARANOIA_OPEN))
    return FALSE;

868 869
  switch (src_format) {
    case GST_FORMAT_TIME:
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
          src_value <<= 2;	/* 4 bytes per sample */
        case GST_FORMAT_UNITS:
	  *dest_value = src_value * 44100 / GST_SECOND;
	  break;
	default:
          if (*dest_format == track_format || *dest_format == sector_format) {
	    gint sector = (src_value * 44100) / ((CD_FRAMESIZE_RAW >> 2) * GST_SECOND);

	    if (*dest_format == sector_format) {
	      *dest_value = sector;
	    }
	    else {
	      *dest_value = cdda_sector_gettrack (src->d, sector) - 1;
	    }
	  }
          else 
	    return FALSE;
	  break;
      }
891
      break;
892 893
    case GST_FORMAT_BYTES:
      src_value >>= 2;
894
    case GST_FORMAT_UNITS:
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
      switch (*dest_format) {
        case GST_FORMAT_BYTES:
          *dest_value = src_value * 4;
	  break;
        case GST_FORMAT_TIME:
          *dest_value = src_value * GST_SECOND / 44100;
	  break;
	default:
          if (*dest_format == track_format || *dest_format == sector_format) {
            gint sector = src_value / (CD_FRAMESIZE_RAW >> 2);

            if (*dest_format == track_format) {
	      *dest_value = cdda_sector_gettrack (src->d, sector) - 1;
	    }
	    else {
	      *dest_value = sector;
	    }
	  }
          else 
	    return FALSE;
	  break;
      }
917 918
      break;
    default:
919 920 921
    {
      gint sector;

922
      if (src_format == track_format) {
923 924 925 926 927
	/* some sanity checks */
	if (src_value < 0 || src_value > src->d->tracks)
          return FALSE;

	sector = cdda_track_firstsector (src->d, src_value + 1);
928 929
      }
      else if (src_format == sector_format) {
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
	sector = src_value;
      }
      else
        return FALSE;

      switch (*dest_format) {
        case GST_FORMAT_TIME:
          *dest_value = ((CD_FRAMESIZE_RAW >> 2) * sector * GST_SECOND) / 44100;
	  break;
        case GST_FORMAT_BYTES:
          sector <<= 2;
        case GST_FORMAT_UNITS:
          *dest_value = (CD_FRAMESIZE_RAW >> 2) * sector;
	  break;
	default:
          if (*dest_format == sector_format) {
	    *dest_value = sector;
	  }
	  else if (*dest_format == track_format) {
	    /* if we go past the last sector, make sure to report the last track */
	    if (sector > src->last_sector)
	      *dest_value = cdda_sector_gettrack (src->d, src->last_sector);
	    else 
	      *dest_value = cdda_sector_gettrack (src->d, sector) - 1;
	  }
          else 
            return FALSE;
	  break;
958 959
      }
      break;
960
    }
961
  }
962

963
  return TRUE;
964 965
}

966 967 968 969 970 971 972 973 974 975 976 977 978
static const GstPadQueryType*
cdparanoia_get_query_types (GstPad *pad)
{
  static const GstPadQueryType src_query_types[] = {
    GST_PAD_QUERY_TOTAL,
    GST_PAD_QUERY_POSITION,
    GST_PAD_QUERY_START,
    GST_PAD_QUERY_SEGMENT_END,
    0
  };
  return src_query_types;
}

979 980 981 982 983 984 985 986 987 988 989 990 991 992
static gboolean
cdparanoia_query (GstPad *pad, GstPadQueryType type,
		  GstFormat *format, gint64 *value)
{
  gboolean res = TRUE;
  CDParanoia *src;

  src = CDPARANOIA (gst_pad_get_parent (pad));

  if (!GST_FLAG_IS_SET (src, CDPARANOIA_OPEN))
    return FALSE;

  switch (type) {
    case GST_PAD_QUERY_TOTAL:
993 994 995 996 997
      /* we take the last sector + 1 so that we also have the full
       * size of that last sector */
      res = gst_pad_convert (src->srcpad, 
		             sector_format, src->last_sector + 1,
		             format, value);
998 999
      break;
    case GST_PAD_QUERY_POSITION:
1000 1001 1002 1003
      /* bring our current sector to the requested format */
      res = gst_pad_convert (src->srcpad, 
		             sector_format, src->cur_sector,
		             format, value);
1004
      break;
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
    case GST_PAD_QUERY_START:
      res = gst_pad_convert (src->srcpad, 
		             sector_format, src->segment_start_sector,
		             format, value);
      break;
    case GST_PAD_QUERY_SEGMENT_END:
      res = gst_pad_convert (src->srcpad, 
		             sector_format, src->segment_end_sector,
		             format, value);
      break;
1015 1016 1017 1018 1019 1020 1021 1022
    default:
      res = FALSE;
      break;
  }
  return res;
}


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1023 1024 1025 1026 1027 1028
static gboolean
plugin_init (GModule *module, GstPlugin *plugin)
{
  GstElementFactory *factory;

  /* create an elementfactory for the cdparanoia element */
1029 1030 1031
  factory = gst_element_factory_new ("cdparanoia", 
		                     GST_TYPE_CDPARANOIA, 
				     &cdparanoia_details);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1032 1033 1034
  g_return_val_if_fail (factory != NULL, FALSE);

  /* register the source's caps */
1035 1036 1037
  gst_element_factory_add_pad_template (factory, 
		  GST_PAD_TEMPLATE_GET (cdparanoia_src_factory));

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
  /* and add the cdparanoia element factory to the plugin */
  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory