gstcdparanoia.c 28.4 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, "1", "cdda2wav-style overlap checking"},
    { PARANOIA_MODE_FULL,    "2", "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 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
    cdparanoia_type = g_type_register_static (GST_TYPE_ELEMENT, "CDParanoia", &cdparanoia_info, 0);
188 189 190 191
    
    /* 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
192 193 194 195 196 197 198 199 200
  }
  return cdparanoia_type;
}

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

203
  success = NULL;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
204

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

  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);

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

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

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

287
  cdparanoia->device = g_strdup ("/dev/cdrom");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
288 289 290
  cdparanoia->generic_device = NULL;
  cdparanoia->default_sectors = -1;
  cdparanoia->search_overlap = -1;
291
  cdparanoia->endian = 0;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
292 293 294 295 296 297 298
  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;

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


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

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

422 423 424
static void
cdparanoia_callback (long inpos, int function)
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
425 426
}

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

  src = CDPARANOIA (gst_pad_get_parent (pad));

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

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

Wim Taymans's avatar
Wim Taymans committed
441
    buf = GST_BUFFER (gst_event_new (GST_EVENT_EOS));
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
    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);

458 459
    /* 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
460
    buf = gst_buffer_new_and_alloc (CD_FRAMESIZE_RAW);
Wim Taymans's avatar
Wim Taymans committed
461
    memcpy (GST_BUFFER_DATA (buf), cdda_buf, CD_FRAMESIZE_RAW);
462 463 464 465
    GST_BUFFER_TIMESTAMP (buf) = timestamp;
  
    /* update current sector */
    src->cur_sector++;
Wim Taymans's avatar
Wim Taymans committed
466
    src->seq++;
Wim Taymans's avatar
Wim Taymans committed
467
  }
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
468 469 470 471 472

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

473 474 475 476 477 478
/* 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
{
479 480 481 482 483
  byte m;
  byte s;
  byte f;
}
toc_msf;
484 485 486

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

502
static void
Wim Taymans's avatar
Wim Taymans committed
503
lba_toc_to_msf_toc (TOC *lba_toc, toc_msf *msf_toc, gint tracks)
504
{
505 506 507 508
  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);
509 510 511
}

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

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

525 526
static void
cddb_discid (gchar *discid, toc_msf *toc, gint tracks)
527
{
528
  guint i = 0, t = 0, n = 0;
529

530 531 532 533 534 535 536
  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));
537 538 539
}

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

547 548
  lba_toc_to_msf_toc (toc, &msf_toc[0], tracks);
  cddb_discid (discid, &msf_toc[0], tracks);
549

550 551 552 553 554
  for (i = 0; i < tracks; i++) {
    *p++ = msf_toc[i].f;
  }

  *total_seconds = msf_toc[tracks].f / 75;
555 556 557

}

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
558 559 560 561 562 563 564 565 566
/* 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);

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

569
  /* find the device */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
570 571 572 573 574 575 576 577 578 579
  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);
    }
  }

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

586
  /* set verbosity mode */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
587 588
  cdda_verbose_set (src->d, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);

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

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

603 604
  /* 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
605
   */
606

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

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

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

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

625 626 627
  /* 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
628

629 630 631
  /* 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
632

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

640
  paranoia_mode = src->paranoia_mode;
641 642
  if (src->never_skip)
    paranoia_mode |= PARANOIA_MODE_NEVERSKIP;
643

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
644 645 646 647 648 649
  paranoia_modeset (src->p, paranoia_mode);

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

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

  GST_FLAG_SET (src, CDPARANOIA_OPEN);

656
  GST_DEBUG_LEAVE ("");
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
657 658 659 660 661 662 663 664 665 666

  return TRUE;
}

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

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

  src->total_seconds = 0LL;
672
  /* close the disc */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
673 674 675 676 677 678 679 680 681
  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
682 683
  CDParanoia *cdparanoia;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
684 685
  g_return_val_if_fail (GST_IS_CDPARANOIA (element), GST_STATE_FAILURE);

Wim Taymans's avatar
Wim Taymans committed
686 687 688 689 690 691
  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
692
      if (!cdparanoia_open (CDPARANOIA (element))) {
693 694
	g_warning ("cdparanoia: failed opening cd");
	return GST_STATE_FAILURE;
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
695
      }
Wim Taymans's avatar
Wim Taymans committed
696 697 698 699 700 701 702 703 704 705 706 707 708
      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
709 710 711 712 713 714 715 716 717
  }

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

718 719 720
static const GstEventMask *
cdparanoia_get_event_mask (GstPad *pad)
{
721
  static const GstEventMask masks[] = {
722 723 724 725 726 727 728 729
    {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 },
730 731
    {0,}
  };
732

733
  return masks;
734 735 736
}

static gboolean
737
cdparanoia_event (GstPad *pad, GstEvent *event)
738
{
739
  CDParanoia *src;
740
  gboolean res = TRUE;
741 742 743

  src = CDPARANOIA (gst_pad_get_parent (pad));

744 745 746
  if (!GST_FLAG_IS_SET (src, CDPARANOIA_OPEN))
    goto error;

747 748
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_SEEK:
749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
    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");
772 773
	goto error;
      }
774
      
775 776
      switch (GST_EVENT_SEEK_METHOD (event)) {
	case GST_SEEK_METHOD_SET:
777
          /* values are set for regular seek set */
778 779
	  break;
	case GST_SEEK_METHOD_CUR:
780 781 782 783 784 785
	  if (seg_start_sector != -1) {
	    seg_start_sector += src->cur_sector;
	  }
	  if (seg_end_sector != -1) {
	    seg_end_sector += src->cur_sector;
	  }
786
	  break;
787 788 789 790 791 792 793
	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;
	  }
794
	  break;
795 796
	default:
	  goto error;
797
      }
798 799 800 801 802 803 804
      /* 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);
805

806 807 808 809 810
	  src->segment_start_sector = seg_start_sector;;
	  src->cur_sector = src->segment_start_sector;
	}
	else
	  goto error;
811
      }
812 813 814 815
      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;;
816
      }
817 818 819
      GST_DEBUG (GST_CAT_PLUGIN_INFO, "configured for %d -> %d sectors\n", 
		      src->segment_start_sector, 
		      src->segment_end_sector);
820
      break;
821
    }
822 823 824
    default:
      goto error;
  }
825

826
  if (FALSE) {
827
error:
828 829
    res = FALSE;
  }
830
  gst_event_unref (event);
831 832

  return res;
833 834 835 836 837
}

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

847 848
  formats[3] = track_format;
  formats[4] = sector_format;
849

850
  return formats;
851 852 853 854
}

static gboolean
cdparanoia_convert (GstPad *pad,
855 856
		    GstFormat src_format, gint64 src_value, 
		    GstFormat *dest_format, gint64 *dest_value)
857
{
858
  CDParanoia *src;
859

860
  src = CDPARANOIA (gst_pad_get_parent (pad));
861

862 863 864
  if (!GST_FLAG_IS_SET (src, CDPARANOIA_OPEN))
    return FALSE;

865 866
  switch (src_format) {
    case GST_FORMAT_TIME:
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
      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;
      }
888
      break;
889 890
    case GST_FORMAT_BYTES:
      src_value >>= 2;
891
    case GST_FORMAT_UNITS:
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
      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;
      }
914 915
      break;
    default:
916 917 918
    {
      gint sector;

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

	sector = cdda_track_firstsector (src->d, src_value + 1);
925 926
      }
      else if (src_format == sector_format) {
927 928 929 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
	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;
955 956
      }
      break;
957
    }
958
  }
959

960
  return TRUE;
961 962
}

963 964 965 966 967 968 969 970 971 972 973 974 975 976
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:
977 978 979 980 981
      /* 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);
982 983
      break;
    case GST_PAD_QUERY_POSITION:
984 985 986 987
      /* bring our current sector to the requested format */
      res = gst_pad_convert (src->srcpad, 
		             sector_format, src->cur_sector,
		             format, value);
988 989 990 991 992 993 994 995 996
      break;
    default:
      res = FALSE;
      break;
  }
  return res;
}


Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
997 998 999 1000 1001 1002
static gboolean
plugin_init (GModule *module, GstPlugin *plugin)
{
  GstElementFactory *factory;

  /* create an elementfactory for the cdparanoia element */
1003 1004 1005
  factory = gst_element_factory_new ("cdparanoia", 
		                     GST_TYPE_CDPARANOIA, 
				     &cdparanoia_details);
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1006 1007 1008
  g_return_val_if_fail (factory != NULL, FALSE);

  /* register the source's caps */
1009 1010 1011
  gst_element_factory_add_pad_template (factory, 
		  GST_PAD_TEMPLATE_GET (cdparanoia_src_factory));

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
  /* 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
};