1
/*
2
 * Copyright 2011 Various Authors
3
 * Copyright 2011 Johannes Weißl
4
 *
5
 * Based on cdda.c from XMMS2.
6
 *
7
 * This program is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU General Public License as
9
 * published by the Free Software Foundation; either version 2 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful, but
13
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "ip.h"
22
#include "file.h"
23
#include "xmalloc.h"
24
#include "debug.h"
25
#include "utils.h"
26
#include "options.h"
27
#include "comment.h"
28
#include "discid.h"
29
30
#include <cdio/cdda.h>
31
#include <cdio/cdio.h>
32
#include <cdio/logging.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#include <sys/types.h>
37
#include <sys/stat.h>
38
#include <fcntl.h>
39
40
#undef HAVE_CDDB
41
42
#ifdef HAVE_CONFIG
43
#include "config/cdio.h"
44
#endif
45
46
#ifdef HAVE_CDDB
47
#include "http.h"
48
#include "xstrjoin.h"
49
#include <cddb/cddb.h>
50
#endif
51
52
#ifdef HAVE_CDDB
53
static char *cddb_url = NULL;
54
#endif
55
56
static struct {
57
	CdIo_t *cdio;
58
	cdrom_drive_t *drive;
59
	const char *disc_id;
60
	const char *device;
61
} cached;
62
63
struct cdda_private {
64
	CdIo_t *cdio;
65
	cdrom_drive_t *drive;
66
	char *disc_id;
67
	char *device;
68
	track_t track;
69
	lsn_t first_lsn;
70
	lsn_t last_lsn;
71
	lsn_t current_lsn;
72
	int first_read;
73
74
	char read_buf[CDIO_CD_FRAMESIZE_RAW];
75
	unsigned long buf_used;
76
};
77
78
static void libcdio_log(cdio_log_level_t level, const char *message)
79
{
80
	const char *level_names[] = { "DEBUG", "INFO", "WARN", "ERROR", "ASSERT" };
81
	int len = strlen(message);
82
	if (len > 0 && message[len-1] == '\n')
83
		len--;
84
	if (len > 0) {
85
		level = clamp(level, 1, N_ELEMENTS(level_names));
86
		d_print("%s: %.*s\n", level_names[level-1], len, message);
87
	}
88
}
89
90
static int libcdio_open(struct input_plugin_data *ip_data)
91
{
92
	struct cdda_private *priv, priv_init = {
93
		.first_read = 1,
94
		.buf_used = CDIO_CD_FRAMESIZE_RAW
95
	};
96
	CdIo_t *cdio = NULL;
97
	cdrom_drive_t *drive = NULL;
98
	const char *device = cdda_device;
99
	lsn_t first_lsn;
100
	int track = -1;
101
	char *disc_id = NULL;
102
	char *msg = NULL;
103
	int rc = 0, save = 0;
104
105
	if (!parse_cdda_url(ip_data->filename, &disc_id, &track, NULL)) {
106
		rc = -IP_ERROR_INVALID_URI;
107
		goto end;
108
	}
109
110
	if (track == -1) {
111
		d_print("invalid or missing track number, aborting!\n");
112
		rc = -IP_ERROR_INVALID_URI;
113
		goto end;
114
	}
115
116
	/* In case of cue/toc/nrg, take filename (= disc_id) as device.
117
	 * A real disc_id is base64 encoded and never contains a slash */
118
	if (strchr(disc_id, '/'))
119
		device = disc_id;
120
121
	ip_data->fd = open(device, O_RDONLY);
122
	if (ip_data->fd == -1) {
123
		save = errno;
124
		d_print("could not open device %s\n", device);
125
		rc = -IP_ERROR_ERRNO;
126
		goto end;
127
	}
128
129
	if (cached.cdio && strcmp(disc_id, cached.disc_id) == 0 && strcmp(device, cached.device) == 0) {
130
		cdio = cached.cdio;
131
		drive = cached.drive;
132
	} else {
133
		cdio_log_set_handler(libcdio_log);
134
		cdio = cdio_open(device, DRIVER_UNKNOWN);
135
		if (!cdio) {
136
			d_print("failed to open device %s\n", device);
137
			rc = -IP_ERROR_NO_DISC;
138
			goto end;
139
		}
140
		cdio_set_speed(cdio, 1);
141
142
		drive = cdio_cddap_identify_cdio(cdio, CDDA_MESSAGE_LOGIT, &msg);
143
		if (!drive) {
144
			d_print("failed to identify drive, aborting!\n");
145
			rc = -IP_ERROR_NO_DISC;
146
			goto end;
147
		}
148
		d_print("%s", msg);
149
		cdio_cddap_verbose_set(drive, CDDA_MESSAGE_LOGIT, CDDA_MESSAGE_LOGIT);
150
		drive->b_swap_bytes = 1;
151
152
		if (cdio_cddap_open(drive)) {
153
			d_print("unable to open disc, aborting!\n");
154
			rc = -IP_ERROR_NO_DISC;
155
			goto end;
156
		}
157
	}
158
159
	first_lsn = cdio_cddap_track_firstsector(drive, track);
160
	if (first_lsn == -1) {
161
		d_print("no such track: %d, aborting!\n", track);
162
		rc = -IP_ERROR_INVALID_URI;
163
		goto end;
164
	}
165
166
	priv = xnew(struct cdda_private, 1);
167
	*priv = priv_init;
168
	priv->cdio = cdio;
169
	priv->drive = drive;
170
	priv->disc_id = xstrdup(disc_id);
171
	priv->device = xstrdup(device);
172
	priv->track = track;
173
	priv->first_lsn = first_lsn;
174
	priv->last_lsn = cdio_cddap_track_lastsector(drive, priv->track);
175
	priv->current_lsn = first_lsn;
176
177
	cached.cdio = priv->cdio;
178
	cached.drive = priv->drive;
179
	cached.disc_id = priv->disc_id;
180
	cached.device = priv->device;
181
182
	ip_data->private = priv;
183
	ip_data->sf = sf_bits(16) | sf_rate(44100) | sf_channels(2) | sf_signed(1);
184
#ifdef WORDS_BIGENDIAN
185
	ip_data->sf |= sf_bigendian(1);
186
#endif
187
188
end:
189
	free(disc_id);
190
191
	if (rc < 0) {
192
		if (ip_data->fd != -1)
193
			close(ip_data->fd);
194
		ip_data->fd = -1;
195
	}
196
197
	if (rc == -IP_ERROR_ERRNO)
198
		errno = save;
199
	return rc;
200
}
201
202
static int libcdio_close(struct input_plugin_data *ip_data)
203
{
204
	struct cdda_private *priv = ip_data->private;
205
206
	if (ip_data->fd != -1)
207
		close(ip_data->fd);
208
	ip_data->fd = -1;
209
210
	if (strcmp(priv->disc_id, cached.disc_id) != 0 || strcmp(priv->device, cached.device) != 0) {
211
		cdio_cddap_close_no_free_cdio(priv->drive);
212
		cdio_destroy(priv->cdio);
213
		free(priv->disc_id);
214
		free(priv->device);
215
	}
216
217
	free(priv);
218
	ip_data->private = NULL;
219
	return 0;
220
}
221
222
static int libcdio_read(struct input_plugin_data *ip_data, char *buffer, int count)
223
{
224
	struct cdda_private *priv = ip_data->private;
225
	int rc = 0;
226
227
	if (priv->first_read || cdio_get_media_changed(priv->cdio)) {
228
		char *disc_id;
229
		priv->first_read = 0;
230
		if (!get_disc_id(priv->device, &disc_id, NULL))
231
			return -IP_ERROR_NO_DISC;
232
		if (strcmp(disc_id, priv->disc_id) != 0) {
233
			free(disc_id);
234
			return -IP_ERROR_WRONG_DISC;
235
		}
236
		free(disc_id);
237
	}
238
239
	if (priv->current_lsn >= priv->last_lsn)
240
		return 0;
241
242
	if (priv->buf_used == CDIO_CD_FRAMESIZE_RAW) {
243
		cdio_cddap_read(priv->drive, priv->read_buf, priv->current_lsn, 1);
244
		priv->current_lsn++;
245
		priv->buf_used = 0;
246
	}
247
248
	if (count >= CDIO_CD_FRAMESIZE_RAW) {
249
		rc = CDIO_CD_FRAMESIZE_RAW - priv->buf_used;
250
		memcpy(buffer, priv->read_buf + priv->buf_used, rc);
251
	} else {
252
		unsigned long buf_left = CDIO_CD_FRAMESIZE_RAW - priv->buf_used;
253
254
		if (buf_left < count) {
255
			memcpy(buffer, priv->read_buf + priv->buf_used, buf_left);
256
			rc = buf_left;
257
		} else {
258
			memcpy(buffer, priv->read_buf + priv->buf_used, count);
259
			rc = count;
260
		}
261
	}
262
	priv->buf_used += rc;
263
264
	return rc;
265
}
266
267
static int libcdio_seek(struct input_plugin_data *ip_data, double offset)
268
{
269
	struct cdda_private *priv = ip_data->private;
270
	lsn_t new_lsn;
271
	int64_t samples = offset * 44100;
272
273
	/* Magic number 42... really should think of a better way to do this but
274
	 * it seemed that the lsn is off by about 42 everytime...
275
	 */
276
	new_lsn = samples / 441.0 * CDIO_CD_FRAMES_PER_SEC / 100 + 42;
277
278
	if ((priv->first_lsn + new_lsn) > priv->last_lsn) {
279
		d_print("trying to seek past the end of track.\n");
280
		return -1;
281
	}
282
283
	priv->current_lsn = priv->first_lsn + new_lsn;
284
285
	return 0;
286
}
287
288
#ifdef HAVE_CDDB
289
static int parse_cddb_url(const char *url, struct http_uri *http_uri, int *use_http)
290
{
291
	char *full_url;
292
	int rc;
293
294
	if (is_http_url(url)) {
295
		*use_http = 1;
296
		full_url = xstrdup(url);
297
	} else {
298
		*use_http = 0;
299
		full_url = xstrjoin("http://", url);
300
	}
301
302
	rc = http_parse_uri(full_url, http_uri);
303
	free(full_url);
304
	return rc == 0;
305
}
306
307
static void setup_cddb_conn(cddb_conn_t *cddb_conn)
308
{
309
	struct http_uri http_uri, http_proxy_uri;
310
	const char *proxy;
311
	int use_http;
312
313
	parse_cddb_url(cddb_url, &http_uri, &use_http);
314
315
	proxy = getenv("http_proxy");
316
	if (proxy && http_parse_uri(proxy, &http_proxy_uri) == 0) {
317
		cddb_http_proxy_enable(cddb_conn);
318
		cddb_set_http_proxy_server_name(cddb_conn, http_proxy_uri.host);
319
		cddb_set_http_proxy_server_port(cddb_conn, http_proxy_uri.port);
320
		if (http_proxy_uri.user)
321
			cddb_set_http_proxy_username(cddb_conn, http_proxy_uri.user);
322
		if (http_proxy_uri.pass)
323
			cddb_set_http_proxy_password(cddb_conn, http_proxy_uri.pass);
324
		http_free_uri(&http_proxy_uri);
325
	} else
326
		cddb_http_proxy_disable(cddb_conn);
327
328
	if (use_http)
329
		cddb_http_enable(cddb_conn);
330
	else
331
		cddb_http_disable(cddb_conn);
332
333
	cddb_set_server_name(cddb_conn, http_uri.host);
334
	cddb_set_email_address(cddb_conn, "me@home");
335
	cddb_set_server_port(cddb_conn, http_uri.port);
336
	if (strcmp(http_uri.path, "/") != 0)
337
		cddb_set_http_path_query(cddb_conn, http_uri.path);
338
#ifdef DEBUG_CDDB
339
	cddb_cache_disable(cddb_conn);
340
#endif
341
342
	http_free_uri(&http_uri);
343
}
344
#endif
345
346
347
#define add_comment(c, x)	do { if (x) comments_add_const(c, #x, x); } while (0)
348
349
static int libcdio_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
350
{
351
	struct cdda_private *priv = ip_data->private;
352
	GROWING_KEYVALS(c);
353
	const char *artist = NULL, *albumartist = NULL, *album = NULL,
354
		*title = NULL, *genre = NULL, *comment = NULL;
355
	const cdtext_t *cdtext_track, *cdtext_album;
356
#ifdef HAVE_CDDB
357
	cddb_conn_t *cddb_conn = NULL;
358
	cddb_disc_t *cddb_disc = NULL;
359
#endif
360
	char buf[64];
361
362
	cdtext_track = cdio_get_cdtext(priv->cdio, priv->track);
363
	if (cdtext_track) {
364
		char * const *field = cdtext_track->field;
365
		artist = field[CDTEXT_PERFORMER];
366
		title = field[CDTEXT_TITLE];
367
		genre = field[CDTEXT_GENRE];
368
		comment = field[CDTEXT_MESSAGE];
369
	}
370
	cdtext_album = cdio_get_cdtext(priv->cdio, 0);
371
	if (cdtext_album) {
372
		char * const *field = cdtext_album->field;
373
		album = field[CDTEXT_TITLE];
374
		albumartist = field[CDTEXT_PERFORMER];
375
		if (!artist)
376
			artist = field[CDTEXT_PERFORMER];
377
		if (!genre)
378
			genre = field[CDTEXT_GENRE];
379
		if (!comment)
380
			comment = field[CDTEXT_MESSAGE];
381
	}
382
383
#ifdef HAVE_CDDB
384
	if (!cdtext_track && cddb_url && cddb_url[0]) {
385
		cddb_track_t *cddb_track;
386
		track_t i_tracks = cdio_get_num_tracks(priv->cdio);
387
		track_t i_first_track = cdio_get_first_track_num(priv->cdio);
388
		unsigned int year;
389
		int i;
390
391
		cddb_conn = cddb_new();
392
		if (!cddb_conn)
393
			malloc_fail();
394
395
		setup_cddb_conn(cddb_conn);
396
397
		cddb_disc = cddb_disc_new();
398
		if (!cddb_disc)
399
			malloc_fail();
400
		for (i = 0; i < i_tracks; i++) {
401
			cddb_track = cddb_track_new();
402
			if (!cddb_track)
403
				malloc_fail();
404
			cddb_track_set_frame_offset(cddb_track,
405
					cdio_get_track_lba(priv->cdio, i+i_first_track));
406
			cddb_disc_add_track(cddb_disc, cddb_track);
407
		}
408
409
		cddb_disc_set_length(cddb_disc, cdio_get_track_lba(priv->cdio,
410
					CDIO_CDROM_LEADOUT_TRACK) / CDIO_CD_FRAMES_PER_SEC);
411
		if (cddb_query(cddb_conn, cddb_disc) == 1 && cddb_read(cddb_conn, cddb_disc)) {
412
			albumartist = cddb_disc_get_artist(cddb_disc);
413
			album = cddb_disc_get_title(cddb_disc);
414
			genre = cddb_disc_get_genre(cddb_disc);
415
			year = cddb_disc_get_year(cddb_disc);
416
			if (year) {
417
				sprintf(buf, "%u", year);
418
				comments_add_const(&c, "date", buf);
419
			}
420
			cddb_track = cddb_disc_get_track(cddb_disc, priv->track - 1);
421
			artist = cddb_track_get_artist(cddb_track);
422
			if (!artist)
423
				artist = albumartist;
424
			title = cddb_track_get_title(cddb_track);
425
		}
426
	}
427
#endif
428
429
	add_comment(&c, artist);
430
	add_comment(&c, albumartist);
431
	add_comment(&c, album);
432
	add_comment(&c, title);
433
	add_comment(&c, genre);
434
	add_comment(&c, comment);
435
436
	sprintf(buf, "%02d", priv->track);
437
	comments_add_const(&c, "tracknumber", buf);
438
439
#ifdef HAVE_CDDB
440
	if (cddb_disc)
441
		cddb_disc_destroy(cddb_disc);
442
	if (cddb_conn)
443
		cddb_destroy(cddb_conn);
444
#endif
445
446
	keyvals_terminate(&c);
447
	*comments = c.keyvals;
448
	return 0;
449
}
450
451
static int libcdio_duration(struct input_plugin_data *ip_data)
452
{
453
	struct cdda_private *priv = ip_data->private;
454
455
	return (priv->last_lsn - priv->first_lsn) / CDIO_CD_FRAMES_PER_SEC;
456
}
457
458
static long libcdio_bitrate(struct input_plugin_data *ip_data)
459
{
460
	return 44100 * 16 * 2;
461
}
462
463
static char *libcdio_codec(struct input_plugin_data *ip_data)
464
{
465
	return xstrdup("cdda");
466
}
467
468
static char *libcdio_codec_profile(struct input_plugin_data *ip_data)
469
{
470
	struct cdda_private *priv = ip_data->private;
471
	discmode_t cd_discmode = cdio_get_discmode(priv->cdio);
472
473
	return xstrdup(discmode2str[cd_discmode]);
474
}
475
476
static int libcdio_set_option(int key, const char *val)
477
{
478
#ifdef HAVE_CDDB
479
	struct http_uri http_uri;
480
	int use_http;
481
#endif
482
	switch (key) {
483
#ifdef HAVE_CDDB
484
	case 0:
485
		if (parse_cddb_url(val, &http_uri, &use_http)) {
486
			http_free_uri(&http_uri);
487
			free(cddb_url);
488
			cddb_url = xstrdup(val);
489
		} else
490
			return -IP_ERROR_INVALID_URI;
491
		break;
492
#endif
493
	default:
494
		return -IP_ERROR_NOT_OPTION;
495
	}
496
	return 0;
497
}
498
499
static int libcdio_get_option(int key, char **val)
500
{
501
	switch (key) {
502
#ifdef HAVE_CDDB
503
	case 0:
504
		if (!cddb_url)
505
			cddb_url = xstrdup("freedb.freedb.org:8880");
506
		*val = xstrdup(cddb_url);
507
		break;
508
#endif
509
	default:
510
		return -IP_ERROR_NOT_OPTION;
511
	}
512
	return 0;
513
}
514
515
const struct input_plugin_ops ip_ops = {
516
	.open = libcdio_open,
517
	.close = libcdio_close,
518
	.read = libcdio_read,
519
	.seek = libcdio_seek,
520
	.read_comments = libcdio_read_comments,
521
	.duration = libcdio_duration,
522
	.bitrate = libcdio_bitrate,
523
	.codec = libcdio_codec,
524
	.codec_profile = libcdio_codec_profile,
525
	.set_option = libcdio_set_option,
526
	.get_option = libcdio_get_option
527
};
528
529
const char * const ip_options[] = {
530
#ifdef HAVE_CDDB
531
	"cddb_url",
532
#endif
533
	NULL
534
};
535
536
const int ip_priority = 50;
537
const char * const ip_extensions[] = { NULL };
538
const char * const ip_mime_types[] = { "x-content/audio-cdda", NULL };