diff options
Diffstat (limited to '')
-rw-r--r-- | src/decoder/vorbis_decoder_plugin.c (renamed from src/decoder/vorbis_plugin.c) | 260 |
1 files changed, 123 insertions, 137 deletions
diff --git a/src/decoder/vorbis_plugin.c b/src/decoder/vorbis_decoder_plugin.c index 7c782a779..0a3944ad6 100644 --- a/src/decoder/vorbis_plugin.c +++ b/src/decoder/vorbis_decoder_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2009 The Music Player Daemon Project + * Copyright (C) 2003-2010 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,19 +17,19 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -/* TODO 'ogg' should probably be replaced with 'oggvorbis' in all instances */ - -#include "_ogg_common.h" #include "config.h" +#include "_ogg_common.h" +#include "audio_check.h" #include "uri.h" #ifndef HAVE_TREMOR +#define OV_EXCLUDE_STATIC_CALLBACKS #include <vorbis/vorbisfile.h> #else #include <tremor/ivorbisfile.h> /* Macros to make Tremor's API look like libogg. Tremor always returns host-byte-order 16-bit signed data, and uses integer - milliseconds where libogg uses double seconds. + milliseconds where libogg uses double seconds. */ #define ov_read(VF, BUFFER, LENGTH, BIGENDIANP, WORD, SGNED, BITSTREAM) \ ov_read(VF, BUFFER, LENGTH, BITSTREAM) @@ -55,46 +55,46 @@ #define OGG_DECODE_USE_BIGENDIAN 0 #endif -typedef struct _OggCallbackData { +struct vorbis_input_stream { struct decoder *decoder; struct input_stream *input_stream; bool seekable; -} OggCallbackData; +}; -static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *vdata) +static size_t ogg_read_cb(void *ptr, size_t size, size_t nmemb, void *data) { + struct vorbis_input_stream *vis = data; size_t ret; - OggCallbackData *data = (OggCallbackData *) vdata; - ret = decoder_read(data->decoder, data->input_stream, ptr, size * nmemb); + ret = decoder_read(vis->decoder, vis->input_stream, ptr, size * nmemb); errno = 0; return ret / size; } -static int ogg_seek_cb(void *vdata, ogg_int64_t offset, int whence) +static int ogg_seek_cb(void *data, ogg_int64_t offset, int whence) { - const OggCallbackData *data = (const OggCallbackData *) vdata; + struct vorbis_input_stream *vis = data; - return data->seekable && - decoder_get_command(data->decoder) != DECODE_COMMAND_STOP && - input_stream_seek(data->input_stream, offset, whence) + return vis->seekable && + (!vis->decoder || decoder_get_command(vis->decoder) != DECODE_COMMAND_STOP) && + input_stream_seek(vis->input_stream, offset, whence, NULL) ? 0 : -1; } /* TODO: check Ogg libraries API and see if we can just not have this func */ -static int ogg_close_cb(G_GNUC_UNUSED void *vdata) +static int ogg_close_cb(G_GNUC_UNUSED void *data) { return 0; } -static long ogg_tell_cb(void *vdata) +static long ogg_tell_cb(void *data) { - const OggCallbackData *data = (const OggCallbackData *) vdata; + const struct vorbis_input_stream *vis = data; - return (long)data->input_stream->offset; + return (long)vis->input_stream->offset; } static const ov_callbacks vorbis_is_callbacks = { @@ -105,6 +105,52 @@ static const ov_callbacks vorbis_is_callbacks = { }; static const char * +vorbis_strerror(int code) +{ + switch (code) { + case OV_EREAD: + return "read error"; + + case OV_ENOTVORBIS: + return "not vorbis stream"; + + case OV_EVERSION: + return "vorbis version mismatch"; + + case OV_EBADHEADER: + return "invalid vorbis header"; + + case OV_EFAULT: + return "internal logic error"; + + default: + return "unknown error"; + } +} + +static bool +vorbis_is_open(struct vorbis_input_stream *vis, OggVorbis_File *vf, + struct decoder *decoder, struct input_stream *input_stream) +{ + vis->decoder = decoder; + vis->input_stream = input_stream; + vis->seekable = input_stream->seekable && + (input_stream->uri == NULL || + !uri_has_scheme(input_stream->uri)); + + int ret = ov_open_callbacks(vis, vf, NULL, 0, vorbis_is_callbacks); + if (ret < 0) { + if (decoder == NULL || + decoder_get_command(decoder) == DECODE_COMMAND_NONE) + g_warning("Failed to open Ogg Vorbis stream: %s", + vorbis_strerror(ret)); + return false; + } + + return true; +} + +static const char * vorbis_comment_value(const char *comment, const char *needle) { size_t len = strlen(needle); @@ -116,14 +162,13 @@ vorbis_comment_value(const char *comment, const char *needle) return NULL; } -static struct replay_gain_info * -vorbis_comments_to_replay_gain(char **comments) +static bool +vorbis_comments_to_replay_gain(struct replay_gain_info *rgi, char **comments) { - struct replay_gain_info *rgi; const char *temp; bool found = false; - rgi = replay_gain_info_new(); + replay_gain_info_init(rgi); while (*comments) { if ((temp = @@ -147,12 +192,7 @@ vorbis_comments_to_replay_gain(char **comments) comments++; } - if (!found) { - replay_gain_info_free(rgi); - rgi = NULL; - } - - return rgi; + return found; } static const char *VORBIS_COMMENT_TRACK_KEY = "tracknumber"; @@ -183,11 +223,11 @@ vorbis_parse_comment(struct tag *tag, const char *comment) assert(tag != NULL); if (vorbis_copy_comment(tag, comment, VORBIS_COMMENT_TRACK_KEY, - TAG_ITEM_TRACK) || + TAG_TRACK) || vorbis_copy_comment(tag, comment, VORBIS_COMMENT_DISC_KEY, - TAG_ITEM_DISC) || + TAG_DISC) || vorbis_copy_comment(tag, comment, "album artist", - TAG_ITEM_ALBUM_ARTIST)) + TAG_ALBUM_ARTIST)) return; for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) @@ -226,42 +266,23 @@ vorbis_send_comments(struct decoder *decoder, struct input_stream *is, tag_free(tag); } -static bool -oggvorbis_seekable(struct decoder *decoder) -{ - char *uri; - bool seekable; - - uri = decoder_get_uri(decoder); - if (uri == NULL) - return false; - - /* disable seeking on remote streams, because libvorbis seeks - around like crazy, and due to being very expensive, this - delays song playback my 10 or 20 seconds */ - seekable = !uri_has_scheme(uri); - g_free(uri); - - return seekable; -} - /* public */ static void vorbis_stream_decode(struct decoder *decoder, struct input_stream *input_stream) { + GError *error = NULL; OggVorbis_File vf; - OggCallbackData data; + struct vorbis_input_stream vis; struct audio_format audio_format; + float total_time; int current_section; int prev_section = -1; long ret; char chunk[OGG_CHUNK_SIZE]; long bitRate = 0; long test; - struct replay_gain_info *replay_gain_info = NULL; - char **comments; - bool initialized = false; + const vorbis_info *vi; enum decoder_command cmd = DECODE_COMMAND_NONE; if (ogg_stream_type_detect(input_stream) != VORBIS) @@ -269,43 +290,30 @@ vorbis_stream_decode(struct decoder *decoder, /* rewind the stream, because ogg_stream_type_detect() has moved it */ - input_stream_seek(input_stream, 0, SEEK_SET); - - data.decoder = decoder; - data.input_stream = input_stream; - data.seekable = input_stream->seekable && oggvorbis_seekable(decoder); + input_stream_seek(input_stream, 0, SEEK_SET, NULL); - if ((ret = ov_open_callbacks(&data, &vf, NULL, 0, - vorbis_is_callbacks)) < 0) { - const char *error; - if (decoder_get_command(decoder) != DECODE_COMMAND_NONE) - return; + if (!vorbis_is_open(&vis, &vf, decoder, input_stream)) + return; - switch (ret) { - case OV_EREAD: - error = "read error"; - break; - case OV_ENOTVORBIS: - error = "not vorbis stream"; - break; - case OV_EVERSION: - error = "vorbis version mismatch"; - break; - case OV_EBADHEADER: - error = "invalid vorbis header"; - break; - case OV_EFAULT: - error = "internal logic error"; - break; - default: - error = "unknown error"; - break; - } + vi = ov_info(&vf, -1); + if (vi == NULL) { + g_warning("ov_info() has failed"); + return; + } - g_warning("Error decoding Ogg Vorbis stream: %s", error); + if (!audio_format_init_checked(&audio_format, vi->rate, + SAMPLE_FORMAT_S16, + vi->channels, &error)) { + g_warning("%s", error->message); + g_error_free(error); return; } - audio_format.bits = 16; + + total_time = ov_time_total(&vf, -1); + if (total_time < 0) + total_time = 0; + + decoder_initialized(decoder, &audio_format, vis.seekable, total_time); do { if (cmd == DECODE_COMMAND_SEEK) { @@ -325,83 +333,61 @@ vorbis_stream_decode(struct decoder *decoder, break; if (current_section != prev_section) { - /*printf("new song!\n"); */ - vorbis_info *vi = ov_info(&vf, -1); - struct replay_gain_info *new_rgi; - - audio_format.channels = vi->channels; - audio_format.sample_rate = vi->rate; - - if (!audio_format_valid(&audio_format)) { - g_warning("Invalid audio format: %u:%u:%u\n", - audio_format.sample_rate, - audio_format.bits, - audio_format.channels); + char **comments; + + vi = ov_info(&vf, -1); + if (vi == NULL) { + g_warning("ov_info() has failed"); break; } - if (!initialized) { - float total_time = ov_time_total(&vf, -1); - if (total_time < 0) - total_time = 0; - decoder_initialized(decoder, &audio_format, - data.seekable, - total_time); - initialized = true; + if (vi->rate != (long)audio_format.sample_rate || + vi->channels != (int)audio_format.channels) { + /* we don't support audio format + change yet */ + g_warning("audio format change, stopping here"); + break; } + comments = ov_comment(&vf, -1)->user_comments; vorbis_send_comments(decoder, input_stream, comments); - new_rgi = vorbis_comments_to_replay_gain(comments); - if (new_rgi != NULL) { - if (replay_gain_info != NULL) - replay_gain_info_free(replay_gain_info); - replay_gain_info = new_rgi; - } - } - prev_section = current_section; + struct replay_gain_info rgi; + if (vorbis_comments_to_replay_gain(&rgi, comments)) + decoder_replay_gain(decoder, &rgi); + + prev_section = current_section; + } if ((test = ov_bitrate_instant(&vf)) > 0) bitRate = test / 1000; cmd = decoder_data(decoder, input_stream, chunk, ret, - ov_pcm_tell(&vf) / audio_format.sample_rate, - bitRate, replay_gain_info); + bitRate); } while (cmd != DECODE_COMMAND_STOP); - if (replay_gain_info) - replay_gain_info_free(replay_gain_info); - ov_clear(&vf); } static struct tag * -vorbis_tag_dup(const char *file) +vorbis_stream_tag(struct input_stream *is) { - struct tag *ret; - FILE *fp; + struct vorbis_input_stream vis; OggVorbis_File vf; - fp = fopen(file, "r"); - if (!fp) { + if (!vorbis_is_open(&vis, &vf, NULL, is)) return NULL; - } - if (ov_open(fp, &vf, NULL, 0) < 0) { - fclose(fp); - return NULL; - } - - ret = vorbis_comments_to_tag(ov_comment(&vf, -1)->user_comments); + struct tag *tag = vorbis_comments_to_tag(ov_comment(&vf, -1)->user_comments); - if (!ret) - ret = tag_new(); - ret->time = (int)(ov_time_total(&vf, -1) + 0.5); + if (tag == NULL) + tag = tag_new(); + tag->time = (int)(ov_time_total(&vf, -1) + 0.5); ov_clear(&vf); - return ret; + return tag; } static const char *const vorbis_suffixes[] = { @@ -423,7 +409,7 @@ static const char *const vorbis_mime_types[] = { const struct decoder_plugin vorbis_decoder_plugin = { .name = "vorbis", .stream_decode = vorbis_stream_decode, - .tag_dup = vorbis_tag_dup, + .stream_tag = vorbis_stream_tag, .suffixes = vorbis_suffixes, .mime_types = vorbis_mime_types }; |