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