/* * Copyright 2008-2013 Various Authors * Copyright 2004-2005 Timo Hirvonen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "../ip.h" #include "../xmalloc.h" #include "../read_wrapper.h" #include "../debug.h" #ifdef HAVE_CONFIG #include "../config/tremor.h" #endif #include "../comment.h" #ifdef CONFIG_TREMOR #include #else #include #endif #include #include #include #include #include struct vorbis_private { OggVorbis_File vf; int current_section; }; /* http://www.xiph.org/vorbis/doc/vorbisfile/callbacks.html */ static size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { struct input_plugin_data *ip_data = datasource; int rc; rc = read_wrapper(ip_data, ptr, size * nmemb); if (rc == -1) { d_print("error: %s\n", strerror(errno)); return 0; } if (rc == 0) { errno = 0; return 0; } return rc / size; } static int seek_func(void *datasource, ogg_int64_t offset, int whence) { struct input_plugin_data *ip_data = datasource; if (lseek(ip_data->fd, offset, whence) == -1) return -1; return 0; } static int close_func(void *datasource) { struct input_plugin_data *ip_data = datasource; int rc; rc = close(ip_data->fd); ip_data->fd = -1; return rc; } static long tell_func(void *datasource) { struct input_plugin_data *ip_data = datasource; off_t off; off = lseek(ip_data->fd, 0, SEEK_CUR); return (off == -1) ? -1 : off; } /* * typedef struct { * size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); * int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); * int (*close_func) (void *datasource); * long (*tell_func) (void *datasource); * } ov_callbacks; */ static ov_callbacks callbacks = { .read_func = read_func, .seek_func = seek_func, .close_func = close_func, .tell_func = tell_func }; /* http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9 */ static void channel_map_init_vorbis(int channels, channel_position_t *map) { switch (channels) { case 8: channel_map_init_vorbis(7, map); map[5] = CHANNEL_POSITION_REAR_LEFT; map[6] = CHANNEL_POSITION_REAR_RIGHT; map[7] = CHANNEL_POSITION_LFE; break; case 7: channel_map_init_vorbis(3, map); map[3] = CHANNEL_POSITION_SIDE_LEFT; map[4] = CHANNEL_POSITION_SIDE_RIGHT; map[5] = CHANNEL_POSITION_REAR_CENTER; map[6] = CHANNEL_POSITION_LFE; break; case 6: map[5] = CHANNEL_POSITION_LFE; /* Fall through */ case 5: map[3] = CHANNEL_POSITION_REAR_LEFT; map[4] = CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ case 3: map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_CENTER; map[2] = CHANNEL_POSITION_FRONT_RIGHT; break; case 4: map[2] = CHANNEL_POSITION_REAR_LEFT; map[3] = CHANNEL_POSITION_REAR_RIGHT; /* Fall through */ case 2: map[0] = CHANNEL_POSITION_FRONT_LEFT; map[1] = CHANNEL_POSITION_FRONT_RIGHT; break; case 1: map[0] = CHANNEL_POSITION_MONO; break; default: map[0] = CHANNEL_POSITION_INVALID; break; } } static int vorbis_open(struct input_plugin_data *ip_data) { struct vorbis_private *priv; vorbis_info *vi; int rc; priv = xnew(struct vorbis_private, 1); priv->current_section = -1; memset(&priv->vf, 0, sizeof(priv->vf)); rc = ov_open_callbacks(ip_data, &priv->vf, NULL, 0, callbacks); if (rc != 0) { d_print("ov_open failed: %d\n", rc); free(priv); /* ogg is a container format, so it is likely to contain * something else if it isn't vorbis */ return -IP_ERROR_UNSUPPORTED_FILE_TYPE; } ip_data->private = priv; vi = ov_info(&priv->vf, -1); ip_data->sf = sf_rate(vi->rate) | sf_channels(vi->channels) | sf_bits(16) | sf_signed(1); ip_data->sf |= sf_host_endian(); channel_map_init_vorbis(vi->channels, ip_data->channel_map); return 0; } static int vorbis_close(struct input_plugin_data *ip_data) { struct vorbis_private *priv; int rc; priv = ip_data->private; /* this closes ip_data->fd! */ rc = ov_clear(&priv->vf); ip_data->fd = -1; if (rc) d_print("ov_clear returned %d\n", rc); free(priv); ip_data->private = NULL; return 0; } static inline int vorbis_endian(void) { #ifdef WORDS_BIGENDIAN return 1; #else return 0; #endif } /* * OV_HOLE * indicates there was an interruption in the data. * (one of: garbage between pages, loss of sync followed by recapture, * or a corrupt page) * OV_EBADLINK * indicates that an invalid stream section was supplied to libvorbisfile, * or the requested link is corrupt. * 0 * indicates EOF * n * indicates actual number of bytes read. ov_read() will decode at most * one vorbis packet per invocation, so the value returned will generally * be less than length. */ static int vorbis_read(struct input_plugin_data *ip_data, char *buffer, int count) { struct vorbis_private *priv; int rc; int current_section; priv = ip_data->private; #ifdef CONFIG_TREMOR /* Tremor can only handle signed 16 bit data */ rc = ov_read(&priv->vf, buffer, count, ¤t_section); #else rc = ov_read(&priv->vf, buffer, count, vorbis_endian(), 2, 1, ¤t_section); #endif if (ip_data->remote && current_section != priv->current_section) { ip_data->metadata_changed = 1; priv->current_section = current_section; } switch (rc) { case OV_HOLE: errno = EAGAIN; return -1; case OV_EBADLINK: errno = EINVAL; return -1; case OV_EINVAL: errno = EINVAL; return -1; case 0: if (errno) { d_print("error: %s\n", strerror(errno)); return -1; /* return -IP_ERROR_INTERNAL; */ } /* EOF */ return 0; default: if (rc < 0) { d_print("error: %d\n", rc); rc = -IP_ERROR_FILE_FORMAT; } return rc; } } static int vorbis_seek(struct input_plugin_data *ip_data, double offset) { struct vorbis_private *priv; int rc; priv = ip_data->private; #ifdef CONFIG_TREMOR rc = ov_time_seek(&priv->vf, offset * 1000); #else rc = ov_time_seek(&priv->vf, offset); #endif switch (rc) { case OV_ENOSEEK: return -IP_ERROR_FUNCTION_NOT_SUPPORTED; case OV_EINVAL: return -IP_ERROR_INTERNAL; case OV_EREAD: return -IP_ERROR_INTERNAL; case OV_EFAULT: return -IP_ERROR_INTERNAL; case OV_EBADLINK: return -IP_ERROR_INTERNAL; } return 0; } static int vorbis_read_comments(struct input_plugin_data *ip_data, struct keyval **comments) { GROWING_KEYVALS(c); struct vorbis_private *priv; vorbis_comment *vc; int i; priv = ip_data->private; vc = ov_comment(&priv->vf, -1); if (vc == NULL) { d_print("vc == NULL\n"); *comments = keyvals_new(0); return 0; } for (i = 0; i < vc->comments; i++) { const char *str = vc->user_comments[i]; const char *eq = strchr(str, '='); char *key; if (!eq) { d_print("invalid comment: '%s' ('=' expected)\n", str); continue; } key = xstrndup(str, eq - str); comments_add_const(&c, key, eq + 1); free(key); } keyvals_terminate(&c); *comments = c.keyvals; return 0; } static int vorbis_duration(struct input_plugin_data *ip_data) { struct vorbis_private *priv; int duration; priv = ip_data->private; duration = ov_time_total(&priv->vf, -1); if (duration == OV_EINVAL) return -IP_ERROR_FUNCTION_NOT_SUPPORTED; #ifdef CONFIG_TREMOR duration = (duration + 500) / 1000; #endif return duration; } static long vorbis_bitrate(struct input_plugin_data *ip_data) { struct vorbis_private *priv = ip_data->private; long bitrate = ov_bitrate(&priv->vf, -1); if (bitrate == OV_EINVAL || bitrate == OV_FALSE) return -IP_ERROR_FUNCTION_NOT_SUPPORTED; return bitrate; } static long vorbis_current_bitrate(struct input_plugin_data *ip_data) { struct vorbis_private *priv = ip_data->private; return ov_bitrate_instant(&priv->vf); } static char *vorbis_codec(struct input_plugin_data *ip_data) { return xstrdup("vorbis"); } static const long rate_mapping_44[2][12] = { { 32000, 48000, 60000, 70000, 80000, 86000, 96000, 110000, 120000, 140000, 160000, 239920 }, { 45000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 499821 } }; static char *vorbis_codec_profile(struct input_plugin_data *ip_data) { struct vorbis_private *priv = ip_data->private; vorbis_info *vi = ov_info(&priv->vf, -1); long b = vi->bitrate_nominal; char buf[64]; if (b <= 0) return NULL; if (vi->channels > 2 || vi->rate < 44100) { sprintf(buf, "%ldkbps", b / 1000); } else { const long *map = rate_mapping_44[vi->channels - 1]; float q; int i; for (i = 0; i < 12-1; i++) { if (b >= map[i] && b < map[i+1]) break; } /* This is used even if upper / lower bitrate are set * because it gives a good approximation. */ q = (i - 1) + (float) (b - map[i]) / (map[i+1] - map[i]); sprintf(buf, "q%g", roundf(q * 100.f) / 100.f); } return xstrdup(buf); } const struct input_plugin_ops ip_ops = { .open = vorbis_open, .close = vorbis_close, .read = vorbis_read, .seek = vorbis_seek, .read_comments = vorbis_read_comments, .duration = vorbis_duration, .bitrate = vorbis_bitrate, .bitrate_current = vorbis_current_bitrate, .codec = vorbis_codec, .codec_profile = vorbis_codec_profile }; const int ip_priority = 50; const char * const ip_extensions[] = { "ogg", "oga", "ogx", NULL }; const char * const ip_mime_types[] = { "application/ogg", "audio/x-ogg", NULL }; const struct input_plugin_opt ip_options[] = { { NULL } }; const unsigned ip_abi_version = IP_ABI_VERSION;