BVB Source Codes

cmus Show cdio.c Source code

Return Download cmus: download cdio.c Source code - Download cmus Source code - Type:.c
  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.  
  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.  
  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.  
  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.  
  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.  
  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
  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.  
  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.  
  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.         int track_comments_found = 0;
  359.         const cdtext_t *cdt;
  360. #ifdef HAVE_CDDB
  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.                 if (title)
  375.                         track_comments_found = 1;
  376.  
  377.                 album = cdtext_get(cdt, CDTEXT_FIELD_TITLE, 0);
  378.                 albumartist = cdtext_get(cdt, CDTEXT_FIELD_PERFORMER, 0);
  379.                 if (!artist)
  380.                         artist = albumartist;
  381.                 if (!genre)
  382.                         genre = cdtext_get(cdt, CDTEXT_FIELD_GENRE, 0);
  383.                 if (!comment)
  384.                         comment = cdtext_get(cdt, CDTEXT_FIELD_MESSAGE, 0);
  385.         }
  386. #else
  387.         cdt = cdio_get_cdtext(priv->cdio, priv->track);
  388.         if (cdt) {
  389.                 char * const *field = cdt->field;
  390.                 track_comments_found = 1;
  391.                 artist = field[CDTEXT_PERFORMER];
  392.                 title = field[CDTEXT_TITLE];
  393.                 genre = field[CDTEXT_GENRE];
  394.                 comment = field[CDTEXT_MESSAGE];
  395.         }
  396.         cdt = cdio_get_cdtext(priv->cdio, 0);
  397.         if (cdt) {
  398.                 char * const *field = cdt->field;
  399.                 album = field[CDTEXT_TITLE];
  400.                 albumartist = field[CDTEXT_PERFORMER];
  401.                 if (!artist)
  402.                         artist = field[CDTEXT_PERFORMER];
  403.                 if (!genre)
  404.                         genre = field[CDTEXT_GENRE];
  405.                 if (!comment)
  406.                         comment = field[CDTEXT_MESSAGE];
  407.         }
  408. #endif
  409.  
  410. #ifdef HAVE_CDDB
  411.         if (!track_comments_found && cddb_url && cddb_url[0]) {
  412.                 cddb_track_t *cddb_track;
  413.                 track_t i_tracks = cdio_get_num_tracks(priv->cdio);
  414.                 track_t i_first_track = cdio_get_first_track_num(priv->cdio);
  415.                 unsigned int year;
  416.                 int i;
  417.  
  418.                 cddb_conn = cddb_new();
  419.                 if (!cddb_conn)
  420.                         malloc_fail();
  421.  
  422.                 setup_cddb_conn(cddb_conn);
  423.  
  424.                 cddb_disc = cddb_disc_new();
  425.                 if (!cddb_disc)
  426.                         malloc_fail();
  427.                 for (i = 0; i < i_tracks; i++) {
  428.                         cddb_track = cddb_track_new();
  429.                         if (!cddb_track)
  430.                                 malloc_fail();
  431.                         cddb_track_set_frame_offset(cddb_track,
  432.                                         cdio_get_track_lba(priv->cdio, i+i_first_track));
  433.                         cddb_disc_add_track(cddb_disc, cddb_track);
  434.                 }
  435.  
  436.                 cddb_disc_set_length(cddb_disc, cdio_get_track_lba(priv->cdio,
  437.                                         CDIO_CDROM_LEADOUT_TRACK) / CDIO_CD_FRAMES_PER_SEC);
  438.                 if (cddb_query(cddb_conn, cddb_disc) == 1 && cddb_read(cddb_conn, cddb_disc)) {
  439.                         albumartist = cddb_disc_get_artist(cddb_disc);
  440.                         album = cddb_disc_get_title(cddb_disc);
  441.                         genre = cddb_disc_get_genre(cddb_disc);
  442.                         year = cddb_disc_get_year(cddb_disc);
  443.                         if (year) {
  444.                                 sprintf(buf, "%u", year);
  445.                                 comments_add_const(&c, "date", buf);
  446.                         }
  447.                         cddb_track = cddb_disc_get_track(cddb_disc, priv->track - 1);
  448.                         artist = cddb_track_get_artist(cddb_track);
  449.                         if (!artist)
  450.                                 artist = albumartist;
  451.                         title = cddb_track_get_title(cddb_track);
  452.                 }
  453.         }
  454. #endif
  455.  
  456.         add_comment(&c, artist);
  457.         add_comment(&c, albumartist);
  458.         add_comment(&c, album);
  459.         add_comment(&c, title);
  460.         add_comment(&c, genre);
  461.         add_comment(&c, comment);
  462.  
  463.         sprintf(buf, "%02d", priv->track);
  464.         comments_add_const(&c, "tracknumber", buf);
  465.  
  466. #ifdef HAVE_CDDB
  467.         if (cddb_disc)
  468.                 cddb_disc_destroy(cddb_disc);
  469.         if (cddb_conn)
  470.                 cddb_destroy(cddb_conn);
  471. #endif
  472.  
  473.         keyvals_terminate(&c);
  474.         *comments = c.keyvals;
  475.         return 0;
  476. }
  477.  
  478. static int libcdio_duration(struct input_plugin_data *ip_data)
  479. {
  480.         struct cdda_private *priv = ip_data->private;
  481.  
  482.         return (priv->last_lsn - priv->first_lsn) / CDIO_CD_FRAMES_PER_SEC;
  483. }
  484.  
  485. static long libcdio_bitrate(struct input_plugin_data *ip_data)
  486. {
  487.         return 44100 * 16 * 2;
  488. }
  489.  
  490. static char *libcdio_codec(struct input_plugin_data *ip_data)
  491. {
  492.         return xstrdup("cdda");
  493. }
  494.  
  495. static char *libcdio_codec_profile(struct input_plugin_data *ip_data)
  496. {
  497.         struct cdda_private *priv = ip_data->private;
  498.         discmode_t cd_discmode = cdio_get_discmode(priv->cdio);
  499.  
  500.         return xstrdup(discmode2str[cd_discmode]);
  501. }
  502.  
  503. #ifdef HAVE_CDDB
  504. static int libcdio_set_cddb_url(const char *val)
  505. {
  506.         struct http_uri http_uri;
  507.         int use_http;
  508.         if (!parse_cddb_url(val, &http_uri, &use_http))
  509.                 return -IP_ERROR_INVALID_URI;
  510.         http_free_uri(&http_uri);
  511.         free(cddb_url);
  512.         cddb_url = xstrdup(val);
  513.         return 0;
  514. }
  515.  
  516. static int libcdio_get_cddb_url(char **val)
  517. {
  518.         if (!cddb_url)
  519.                 cddb_url = xstrdup("freedb.freedb.org:8880");
  520.         *val = xstrdup(cddb_url);
  521.         return 0;
  522. }
  523. #endif
  524.  
  525. const struct input_plugin_ops ip_ops = {
  526.         .open = libcdio_open,
  527.         .close = libcdio_close,
  528.         .read = libcdio_read,
  529.         .seek = libcdio_seek,
  530.         .read_comments = libcdio_read_comments,
  531.         .duration = libcdio_duration,
  532.         .bitrate = libcdio_bitrate,
  533.         .codec = libcdio_codec,
  534.         .codec_profile = libcdio_codec_profile,
  535. };
  536.  
  537. const struct input_plugin_opt ip_options[] = {
  538. #ifdef HAVE_CDDB
  539.         { "cddb_url", libcdio_set_cddb_url, libcdio_get_cddb_url },
  540. #endif
  541.         { NULL },
  542. };
  543.  
  544. const int ip_priority = 50;
  545. const char * const ip_extensions[] = { NULL };
  546. const char * const ip_mime_types[] = { "x-content/audio-cdda", NULL };
  547. const unsigned ip_abi_version = IP_ABI_VERSION;
  548.  
downloadcdio.c Source code - Download cmus Source code
Related Source Codes/Software:
Ink - An HTML5/CSS3 framework used at SAPO for fast and ... 2017-04-22
memory-stats.js - minimal monitor for JS Heap Size via performance.m... 2017-04-22
LayoutKit - LayoutKit is a fast view layout library for iOS, m... 2017-04-22
Cpp-Primer - C++ Primer 5 answer 2017-04-22
RBBAnimation - Block-based animations made easy, comes with easin... 2017-04-22
phpDocumentor2 - Documentation Generator for PHP ... 2017-04-22
flexboxfroggy - A game for learning CSS flexbox ... 2017-04-22
wicked - Use wicked to turn your controller into a wizard ... 2017-04-22
Begin-Latex-in-minutes - Brief Intro to LaTeX for beginners that helps you ... 2017-04-22
guard-livereload - Guard::LiveReload automatically reload your browse... 2017-04-22
swifter - Tiny http server engine written in Swift programmi... 2017-04-29
Chartbuilder - A front-end charting application that facilitates ... 2017-04-29
offline-first - 2017-04-29
plotly.py - An interactive, browser-based graphing library for... 2017-04-29
statsmodels - Statsmodels: statistical modeling and econometrics... 2017-04-29
android-maps-utils - Handy extensions to the Google Maps Android API. 2017-04-29
enyo - A JavaScript application framework emphasizing mod... 2017-04-29
darkforestGo - DarkForest, the Facebook Go engine. 2017-04-29
erpnext - ERP made Simple http://erpn... 2017-04-29
SwiftInFlux - An attempt to gather all that is in flux in Swift. 2017-04-29

 Back to top