gstcdparanoia.c 26.7 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 84
    {0, "0", "Disable paranoid checking"},
    {1, "1", "cdda2wav-style overlap checking"},
    {2, "2", "Full paranoia"},
    {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 99 100 101 102 103 104 105 106 107 108
#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[] = {
    {0, "0", "treat drive as little endian"},
    {1, "1", "treat drive as big endian"},
    {0, NULL, NULL},
  };

  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 GstElementStateReturn cdparanoia_change_state (GstElement *element);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
162 163 164 165 166


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

167
static GstFormat track_format;
168
static GstFormat sector_format;
169

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

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
187 188 189 190 191 192 193 194 195 196
    cdparanoia_type = g_type_register_static (GST_TYPE_ELEMENT, "CDParanoia", &cdparanoia_info, 0);
  }
  return cdparanoia_type;
}

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

199
  success = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
200

201 202
  gobject_class = (GObjectClass *) klass;
  gstelement_class = (GstElementClass *) klass;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
203 204 205 206

  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

  cdparanoia_signals[SMILIE_CHANGE] =
207 208 209
    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
210
  cdparanoia_signals[TRANSPORT_ERROR] =
211 212 213
    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
214
  cdparanoia_signals[UNCORRECTED_ERROR] =
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
    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", 
		      0, G_MAXINT, 0, G_PARAM_READWRITE));
  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", 
		       0, G_MAXINT, 0, G_PARAM_READWRITE));
  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
239
  g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TOC_OFFSET,
240 241
    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
242
  g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TOC_BIAS,
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    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
262 263 264 265 266 267 268 269 270 271

  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)
{
272 273
  cdparanoia->srcpad =
    gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (cdparanoia_src_factory), "src");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
274
  gst_pad_set_get_function (cdparanoia->srcpad, cdparanoia_get);
275 276 277 278 279 280
  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);
  gst_pad_set_formats_function (cdparanoia->srcpad, cdparanoia_get_formats);

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

283
  cdparanoia->device = g_strdup ("/dev/cdrom");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
284 285 286 287 288 289 290 291 292
  cdparanoia->generic_device = NULL;
  cdparanoia->start_sector = -1;
  cdparanoia->end_sector = -1;
  cdparanoia->cur_sector = -1;
  cdparanoia->start_track = -1;
  cdparanoia->end_track = -1;
  cdparanoia->last_track = -1;
  cdparanoia->default_sectors = -1;
  cdparanoia->search_overlap = -1;
293
  cdparanoia->endian = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
294 295 296 297 298 299 300 301 302
  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;

  cdparanoia->cur_sector = 0;
  cdparanoia->seq = 0;
303

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


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:
321 322
      if (src->device)
	g_free (src->device);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
323
      /* clear the filename if we get a NULL (is that possible?) */
324 325
      if (!g_ascii_strcasecmp (g_value_get_string (value), ""))
	src->device = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
326 327
      /* otherwise set the new filename */
      else
328
	src->device = g_strdup (g_value_get_string (value));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
329 330 331
      break;
    case ARG_GENERIC_DEVICE:

332 333
      if (src->generic_device)
	g_free (src->generic_device);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
334
      /* reset the device if we get a NULL (is that possible?) */
335 336
      if (!g_ascii_strcasecmp (g_value_get_string (value), ""))
	src->generic_device = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
337 338
      /* otherwise set the new filename */
      else
339
	src->generic_device = g_strdup (g_value_get_string (value));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
340 341 342 343 344 345 346 347
      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:
348
      src->endian = g_value_get_enum (value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
349 350 351
      break;
    case ARG_READ_SPEED:
      src->read_speed = g_value_get_int (value);
352
      cdda_speed_set (src->d, src->read_speed);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
353 354 355 356 357 358 359 360
      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:
361
      src->never_skip = g_value_get_int (value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
362 363 364 365 366
      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
367
      src->paranoia_mode = g_value_get_enum (value);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
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 396 397
      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:
398
      g_value_set_enum (value, src->endian);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
399 400 401 402 403 404 405 406 407 408 409
      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:
410
      g_value_set_int (value, src->never_skip);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
411 412 413 414 415
      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
416
      g_value_set_enum (value, src->paranoia_mode);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
417
      break;
418
    case ARG_DISCID:
419
      g_value_set_string (value, src->discid);
420
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
421 422 423 424 425 426
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

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

432
static GstBuffer *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
433 434 435 436 437 438 439 440 441 442
cdparanoia_get (GstPad *pad)
{
  CDParanoia *src;
  GstBuffer *buf;
  static gint16 *cdda_buf;

  g_return_val_if_fail (pad != NULL, NULL);
  src = CDPARANOIA (gst_pad_get_parent (pad));
  g_return_val_if_fail (GST_FLAG_IS_SET (src, CDPARANOIA_OPEN), NULL);

443
  /* read a sector */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
444 445
  cdda_buf = paranoia_read (src->p, cdparanoia_callback);

446
  /* update current sector and stop things if appropriate */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
447 448 449
  src->cur_sector++;

  if (src->cur_sector == src->end_sector) {
450 451
    GST_DEBUG (0, "setting EOS");
    gst_element_set_eos (GST_ELEMENT (src));
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
452

Wim Taymans's avatar
Wim Taymans committed
453
    buf = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
454
  } else {
455 456
    /* 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
457
    buf = gst_buffer_new_and_alloc (CD_FRAMESIZE_RAW);
Wim Taymans's avatar
Wim Taymans committed
458
    memcpy (GST_BUFFER_DATA (buf), cdda_buf, CD_FRAMESIZE_RAW);
Wim Taymans's avatar
Wim Taymans committed
459

460
    GST_BUFFER_TIMESTAMP (buf) = ((CD_FRAMESIZE_RAW >> 2) * src->seq * GST_SECOND) / 44100;
Wim Taymans's avatar
Wim Taymans committed
461
    src->seq++;
Wim Taymans's avatar
Wim Taymans committed
462
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
463 464 465 466 467

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

468 469 470 471 472 473
/* 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
{
474 475 476 477 478
  byte m;
  byte s;
  byte f;
}
toc_msf;
479 480 481

/* cdparanoia provides the toc in lba format dang we need it in msf so
 * we have to convert it */
482 483
static inline void
lba_to_msf (const gint lba, byte *m, byte *s, byte *f)
484
{
485 486 487 488 489 490 491 492 493 494
  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;
495 496
}

497
static void
Wim Taymans's avatar
Wim Taymans committed
498
lba_toc_to_msf_toc (TOC *lba_toc, toc_msf *msf_toc, gint tracks)
499
{
500 501 502 503
  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);
504 505 506
}

/* the cddb hash function */
Wim Taymans's avatar
Wim Taymans committed
507
static guint
508
cddb_sum (gint n)
509
{
510 511 512 513 514 515 516 517
  guint ret;

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

520 521
static void
cddb_discid (gchar *discid, toc_msf *toc, gint tracks)
522
{
523
  guint i = 0, t = 0, n = 0;
524

525 526 527 528 529 530 531
  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));
532 533 534
}

/* get all the cddb info at once */
535 536
static void
get_cddb_info (TOC *toc, gint tracks, gchar *discid, gint64 *offsets, gint64 *total_seconds)
537
{
538 539 540
  toc_msf msf_toc[MAXTRK];
  gint i;
  gint64 *p = offsets;
541

542 543
  lba_toc_to_msf_toc (toc, &msf_toc[0], tracks);
  cddb_discid (discid, &msf_toc[0], tracks);
544

545 546 547 548 549
  for (i = 0; i < tracks; i++) {
    *p++ = msf_toc[i].f;
  }

  *total_seconds = msf_toc[tracks].f / 75;
550 551 552

}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
553 554 555 556 557 558 559 560 561
/* 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);

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

564
  /* find the device */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
565 566 567 568 569 570 571 572 573 574
  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);
    }
  }

575
  /* fail if the device couldn't be found */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
576
  if (src->d == NULL) {
577
    GST_DEBUG (0, "couldn't open device");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
578 579 580
    return FALSE;
  }

581
  /* set verbosity mode */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
582 583
  cdda_verbose_set (src->d, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);

584
  /* set various other parameters */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
585 586
  if (src->default_sectors != -1) {
    if ((src->default_sectors < 0) || (src->default_sectors > 100)) {
587
      GST_DEBUG (0, "default sector read size must be 1 <= n <= 100");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
588 589 590 591 592 593 594 595 596 597
      cdda_close (src->d);
      src->d = NULL;
      return FALSE;
    } else {
      src->d->nsectors = src->default_sectors;
      src->d->bigbuff = src->default_sectors * CD_FRAMESIZE_RAW;
    }
  }
  if (src->search_overlap != -1) {
    if ((src->search_overlap < 0) || (src->search_overlap > 75)) {
598
      GST_DEBUG (0, "search overlap must be 0 <= n <= 75");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
599 600 601 602 603 604
      cdda_close (src->d);
      src->d = NULL;
      return FALSE;
    }
  }

605
  /* open the disc */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
606
  if (cdda_open (src->d)) {
607
    GST_DEBUG (0, "couldn't open disc");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
608 609 610 611 612
    cdda_close (src->d);
    src->d = NULL;
    return FALSE;
  }

613
  /* set up some more stuff */
614 615 616 617
  src->no_tracks = src->d->tracks;

  /* 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
618
   */
619

620 621
  get_cddb_info (&src->d->disc_toc[0], src->no_tracks, src->discid,
		 src->offsets, &src->total_seconds);
Wim Taymans's avatar
Wim Taymans committed
622 623 624 625

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

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
627 628 629
  if (src->toc_bias) {
    src->toc_offset -= cdda_track_firstsector (src->d, 1);
  }
630
  for (i = 0; i < src->d->tracks + 1; i++) {
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
631 632 633 634 635 636 637
    src->d->disc_toc[i].dwStartSector += src->toc_offset;
  }

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

638
  /* if the start_track is set, override the start_sector */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
639 640
  if (src->start_track != -1) {
    src->start_sector = cdda_track_firstsector (src->d, src->start_track);
641
    /* if neither start_track nor start_sector is set, */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
642 643 644
  } else if (src->start_sector == -1) {
    src->start_sector = cdda_disc_firstsector (src->d);
  }
645
  /* if the end_track is set, override the end_sector */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
646 647
  if (src->end_track != -1) {
    src->end_sector = cdda_track_lastsector (src->d, src->end_track);
648
    /* if neither end_track nor end_sector is set, */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
649 650 651 652
  } else if (src->end_sector == -1) {
    src->end_sector = cdda_disc_lastsector (src->d);
  }

653
  /* set last_track */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
654 655
  src->last_track = cdda_tracks (src->d);

656
  /* create the paranoia struct and set it up */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
657 658
  src->p = paranoia_init (src->d);
  if (src->p == NULL) {
659
    GST_DEBUG (0, "couldn't create paranoia struct");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
660 661 662
    return FALSE;
  }

663 664 665 666 667 668 669 670
  if (src->paranoia_mode == 0)
    paranoia_mode = PARANOIA_MODE_DISABLE;
  else if (src->paranoia_mode == 1)
    paranoia_mode = PARANOIA_MODE_OVERLAP;
  else
    paranoia_mode = PARANOIA_MODE_FULL;
  if (src->never_skip)
    paranoia_mode |= PARANOIA_MODE_NEVERSKIP;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
671 672 673 674 675 676 677 678
  paranoia_modeset (src->p, paranoia_mode);

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

  src->cur_sector = src->start_sector;
  paranoia_seek (src->p, src->cur_sector, SEEK_SET);
679
  GST_DEBUG (0, "successfully seek'd to beginning of disk");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
680 681 682

  GST_FLAG_SET (src, CDPARANOIA_OPEN);

683
  GST_DEBUG_LEAVE ("");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
684 685 686 687 688 689 690 691 692 693

  return TRUE;
}

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

694
  /* kill the paranoia state */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
695 696
  paranoia_free (src->p);
  src->p = NULL;
697 698

  src->total_seconds = 0LL;
699
  /* close the disc */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
700 701 702 703 704 705 706 707 708
  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
709 710
  CDParanoia *cdparanoia;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
711 712
  g_return_val_if_fail (GST_IS_CDPARANOIA (element), GST_STATE_FAILURE);

Wim Taymans's avatar
Wim Taymans committed
713 714 715 716 717 718
  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
719
      if (!cdparanoia_open (CDPARANOIA (element))) {
720 721
	g_warning ("cdparanoia: failed opening cd");
	return GST_STATE_FAILURE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
722
      }
Wim Taymans's avatar
Wim Taymans committed
723 724 725 726 727 728 729 730 731 732 733 734 735 736
      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));
      cdparanoia->seq = 0;
      break;
    case GST_STATE_READY_TO_NULL:
      break;
    default:
      break;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
737 738 739 740 741 742 743 744 745
  }

  /* 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;
}

746 747 748
static const GstEventMask *
cdparanoia_get_event_mask (GstPad *pad)
{
749 750 751 752
  static const GstEventMask masks[] = {
    {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_METHOD_CUR},
    {0,}
  };
753

754
  return masks;
755 756 757
}

static gboolean
758
cdparanoia_event (GstPad *pad, GstEvent *event)
759
{
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
  CDParanoia *src;
  gint64 offset, endoffset;
  int format, start_sector, end_sector;

  src = CDPARANOIA (gst_pad_get_parent (pad));

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
      format = GST_EVENT_SEEK_FORMAT (event);
      offset = GST_EVENT_SEEK_OFFSET (event);

      if (format == sector_format) {
	start_sector = (int) offset;
      } 
      else if (format == track_format) {
	start_sector = cdda_track_firstsector (src->d, (int) offset);
      } 
      else {
	goto error;
      }

      switch (GST_EVENT_SEEK_METHOD (event)) {
	case GST_SEEK_METHOD_SET:
	  src->start_sector = start_sector;;
	  src->cur_sector = src->start_sector;

	  paranoia_seek (src->p, src->start_sector, SEEK_SET);
	  GST_DEBUG (0, "seeked to %d", src->start_sector);
	  break;
	case GST_SEEK_METHOD_CUR:
	  src->start_sector += start_sector;
	  src->cur_sector = src->start_sector;

	  paranoia_seek (src->p, src->start_sector, SEEK_SET);
	  GST_DEBUG (0, "seeked to %d", src->start_sector);
	  break;
796
	default:
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
	  break;
      }
      break;
    case GST_EVENT_SEEK_SEGMENT:
      format = GST_EVENT_SEEK_FORMAT (event);
      offset = GST_EVENT_SEEK_OFFSET (event);
      endoffset = GST_EVENT_SEEK_ENDOFFSET (event);

      if (format == sector_format) {
	start_sector = (int) offset;
	end_sector = (int) endoffset;
      }
      else if (format == track_format) {
	start_sector = cdda_track_firstsector (src->d, (int) offset);
	end_sector = cdda_track_lastsector (src->d, (int) endoffset);
      }
      else {
	goto error;
      }

      /* Pretend these are tracks for testing */
      src->start_sector = start_sector;
      src->end_sector = end_sector;
      src->cur_sector = src->start_sector;

      paranoia_seek (src->p, src->start_sector, SEEK_SET);
      GST_DEBUG (0, "seeked from %d to %d", src->start_sector, src->end_sector);
      break;
    default:
      goto error;
      break;
  }
829

830 831 832 833 834 835 836
  gst_event_unref (event);
  return TRUE;

error:
  g_print ("Event error\n");
  gst_event_unref (event);
  return FALSE;
837 838 839 840 841
}

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

  formats[2] = track_format;
  formats[3] = 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 862
  gboolean res = TRUE;
  CDParanoia *src;
863

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

866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
  switch (src_format) {
    case GST_FORMAT_TIME:
      break;
    case GST_FORMAT_UNITS:
      break;
    default:
      if (src_format == track_format) {
	switch (*dest_format) {
          case GST_FORMAT_TIME:
            *dest_value = src->offsets[src_value] / 75;
	    break;
          case GST_FORMAT_UNITS:
	    break;
	  default:
	    break;
881
	}
882 883 884 885 886 887 888
      }
      else if (src_format == sector_format) {
      }
      else 
	res = FALSE;
      break;
  }
889

890
  return res;
891 892
}

893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
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:
      switch (*format) {
  	case GST_FORMAT_TIME:
          *value = src->total_seconds;	
          break;
  	case GST_FORMAT_UNITS:
	  res = FALSE;
          break;
        default:
          if (*format == track_format) {
            *value = src->no_tracks;	
          }
          else if (*format == sector_format) {
            *value = cdda_disc_lastsector (src->d);
          }
	  else
            res = FALSE;
	  break;
      }
      break;
    case GST_PAD_QUERY_POSITION:
      switch (*format) {
        default:
	  break;
      }
      break;
    default:
      res = FALSE;
      break;
  }
  return res;
}


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
940 941 942 943 944 945
static gboolean
plugin_init (GModule *module, GstPlugin *plugin)
{
  GstElementFactory *factory;

  /* create an elementfactory for the cdparanoia element */
946 947 948
  factory = gst_element_factory_new ("cdparanoia", 
		                     GST_TYPE_CDPARANOIA, 
				     &cdparanoia_details);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
949 950 951
  g_return_val_if_fail (factory != NULL, FALSE);

  /* register the source's caps */
952 953 954 955 956 957
  gst_element_factory_add_pad_template (factory, 
		  GST_PAD_TEMPLATE_GET (cdparanoia_src_factory));

  /* 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
958 959 960 961 962 963 964 965 966 967 968 969 970

  /* and add the cdparanoia element factory to the plugin */
  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));

  return TRUE;
}

GstPluginDesc plugin_desc = {
  GST_VERSION_MAJOR,
  GST_VERSION_MINOR,
  "cdparanoia",
  plugin_init
};