/******************************************************************** * * * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY * * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE. * * PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2003 * * by Stan Seibert AND OTHER CONTRIBUTORS * * http://www.xiph.org/ * * * ******************************************************************** last mod: $Id$ ********************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include "transport.h" #include "format.h" #include "vorbis_comments.h" #include "utf8.h" #include "i18n.h" #ifdef HAVE_OV_READ_FILTER #include "vgfilter.h" #endif typedef struct ovf_private_t { OggVorbis_File vf; vorbis_comment *vc; vorbis_info *vi; int current_section; int bos; /* At beginning of logical bitstream */ decoder_stats_t stats; #ifdef HAVE_OV_READ_FILTER vgain_state vg; #endif } ovf_private_t; /* Forward declarations */ format_t oggvorbis_format; ov_callbacks vorbisfile_callbacks; void print_vorbis_stream_info (decoder_t *decoder); void print_vorbis_comments (vorbis_comment *vc, decoder_callbacks_t *cb, void *callback_arg); /* ----------------------------------------------------------- */ int ovf_can_decode (data_source_t *source) { return 1; /* The file transport is tested last, so always try it */ } decoder_t* ovf_init (data_source_t *source, ogg123_options_t *ogg123_opts, audio_format_t *audio_fmt, decoder_callbacks_t *callbacks, void *callback_arg) { decoder_t *decoder; ovf_private_t *private; int ret; /* Allocate data source structures */ decoder = malloc(sizeof(decoder_t)); private = malloc(sizeof(ovf_private_t)); if (decoder != NULL && private != NULL) { decoder->source = source; decoder->actual_fmt = decoder->request_fmt = *audio_fmt; decoder->format = &oggvorbis_format; decoder->callbacks = callbacks; decoder->callback_arg = callback_arg; decoder->private = private; private->bos = 1; private->current_section = -1; private->stats.total_time = 0.0; private->stats.current_time = 0.0; private->stats.instant_bitrate = 0; private->stats.avg_bitrate = 0; #ifdef HAVE_OV_READ_FILTER private->vg.scale_factor = 1.0; private->vg.max_scale = 1.0; #endif } else { fprintf(stderr, _("ERROR: Out of memory.\n")); exit(1); } /* Initialize vorbisfile decoder */ ret = ov_open_callbacks (decoder, &private->vf, NULL, 0, vorbisfile_callbacks); if (ret < 0) { free(private); /* free(source); nope. caller frees. */ return NULL; } return decoder; } int ovf_read (decoder_t *decoder, void *ptr, int nbytes, int *eos, audio_format_t *audio_fmt) { ovf_private_t *priv = decoder->private; decoder_callbacks_t *cb = decoder->callbacks; int bytes_read = 0; int ret; int old_section; /* Read comments and audio info at the start of a logical bitstream */ if (priv->bos) { priv->vc = ov_comment(&priv->vf, -1); priv->vi = ov_info(&priv->vf, -1); decoder->actual_fmt.rate = priv->vi->rate; decoder->actual_fmt.channels = priv->vi->channels; switch(decoder->actual_fmt.channels){ case 1: decoder->actual_fmt.matrix="M"; break; case 2: decoder->actual_fmt.matrix="L,R"; break; case 3: decoder->actual_fmt.matrix="L,C,R"; break; case 4: decoder->actual_fmt.matrix="L,R,BL,BR"; break; case 5: decoder->actual_fmt.matrix="L,C,R,BL,BR"; break; case 6: decoder->actual_fmt.matrix="L,C,R,BL,BR,LFE"; break; case 7: decoder->actual_fmt.matrix="L,C,R,SL,SR,BC,LFE"; break; case 8: decoder->actual_fmt.matrix="L,C,R,SL,SR,BL,BR,LFE"; break; default: decoder->actual_fmt.matrix=NULL; break; } #ifdef HAVE_OV_READ_FILTER vg_init(&priv->vg, priv->vc); #endif print_vorbis_stream_info(decoder); print_vorbis_comments(priv->vc, cb, decoder->callback_arg); priv->bos = 0; } *audio_fmt = decoder->actual_fmt; /* Attempt to read as much audio as is requested */ while (nbytes >= audio_fmt->word_size * audio_fmt->channels) { old_section = priv->current_section; #ifdef HAVE_OV_READ_FILTER ret = ov_read_filter(&priv->vf, ptr, nbytes, audio_fmt->big_endian, audio_fmt->word_size, audio_fmt->signed_sample, &priv->current_section, vg_filter, &priv->vg); #else ret = ov_read(&priv->vf, ptr, nbytes, audio_fmt->big_endian, audio_fmt->word_size, audio_fmt->signed_sample, &priv->current_section); #endif if (ret == 0) { /* EOF */ *eos = 1; break; } else if (ret == OV_HOLE) { if (cb->printf_error != NULL) cb->printf_error(decoder->callback_arg, INFO, _("--- Hole in the stream; probably harmless\n")); } else if (ret < 0) { if (cb->printf_error != NULL) cb->printf_error(decoder->callback_arg, ERROR, _("=== Vorbis library reported a stream error.\n")); /* EOF */ *eos = 1; break; } else { bytes_read += ret; ptr = (void *)((unsigned char *)ptr + ret); nbytes -= ret; /* did we enter a new logical bitstream? */ if (old_section != priv->current_section && old_section != -1) { *eos = 1; priv->bos = 1; /* Read new headers next time through */ break; } } } return bytes_read; } int ovf_seek (decoder_t *decoder, double offset, int whence) { ovf_private_t *priv = decoder->private; int ret; double cur; if (whence == DECODER_SEEK_CUR) { cur = ov_time_tell(&priv->vf); if (cur >= 0.0) offset += cur; else return 0; } ret = ov_time_seek(&priv->vf, offset); if (ret == 0) return 1; else return 0; } decoder_stats_t *ovf_statistics (decoder_t *decoder) { ovf_private_t *priv = decoder->private; long instant_bitrate; long avg_bitrate; /* ov_time_tell() doesn't work on non-seekable streams, so we use ov_pcm_tell() */ priv->stats.total_time = (double) ov_pcm_total(&priv->vf, -1) / (double) decoder->actual_fmt.rate; priv->stats.current_time = (double) ov_pcm_tell(&priv->vf) / (double) decoder->actual_fmt.rate; /* vorbisfile returns 0 when no bitrate change has occurred */ instant_bitrate = ov_bitrate_instant(&priv->vf); if (instant_bitrate > 0) priv->stats.instant_bitrate = instant_bitrate; avg_bitrate = ov_bitrate(&priv->vf, priv->current_section); /* Catch error case caused by non-seekable stream */ priv->stats.avg_bitrate = avg_bitrate > 0 ? avg_bitrate : 0; return malloc_decoder_stats(&priv->stats); } void ovf_cleanup (decoder_t *decoder) { ovf_private_t *priv = decoder->private; ov_clear(&priv->vf); free(decoder->private); free(decoder); } format_t oggvorbis_format = { "oggvorbis", &ovf_can_decode, &ovf_init, &ovf_read, &ovf_seek, &ovf_statistics, &ovf_cleanup, }; /* ------------------- Vorbisfile Callbacks ----------------- */ size_t vorbisfile_cb_read (void *ptr, size_t size, size_t nmemb, void *arg) { decoder_t *decoder = arg; return decoder->source->transport->read(decoder->source, ptr, size, nmemb); } int vorbisfile_cb_seek (void *arg, ogg_int64_t offset, int whence) { decoder_t *decoder = arg; return decoder->source->transport->seek(decoder->source, offset, whence); } int vorbisfile_cb_close (void *arg) { return 1; /* Ignore close request so transport can be closed later */ } long vorbisfile_cb_tell (void *arg) { decoder_t *decoder = arg; return decoder->source->transport->tell(decoder->source); } ov_callbacks vorbisfile_callbacks = { &vorbisfile_cb_read, &vorbisfile_cb_seek, &vorbisfile_cb_close, &vorbisfile_cb_tell }; /* ------------------- Private functions -------------------- */ void print_vorbis_stream_info (decoder_t *decoder) { ovf_private_t *priv = decoder->private; decoder_callbacks_t *cb = decoder->callbacks; if (cb == NULL || cb->printf_metadata == NULL) return; cb->printf_metadata(decoder->callback_arg, 2, _("Ogg Vorbis stream: %d channel, %ld Hz"), priv->vi->channels, priv->vi->rate); cb->printf_metadata(decoder->callback_arg, 3, _("Vorbis format: Version %d"), priv->vi->version); cb->printf_metadata(decoder->callback_arg, 3, _("Bitrate hints: upper=%ld nominal=%ld lower=%ld " "window=%ld"), priv->vi->bitrate_upper, priv->vi->bitrate_nominal, priv->vi->bitrate_lower, priv->vi->bitrate_window); cb->printf_metadata(decoder->callback_arg, 3, _("Encoded by: %s"), priv->vc->vendor); } void print_vorbis_comments (vorbis_comment *vc, decoder_callbacks_t *cb, void *callback_arg) { int i; char *temp = NULL; int temp_len = 0; for (i = 0; i < vc->comments; i++) { /* Gotta null terminate these things */ if (temp_len < vc->comment_lengths[i] + 1) { temp_len = vc->comment_lengths[i] + 1; temp = realloc(temp, sizeof(char) * temp_len); } strncpy(temp, vc->user_comments[i], vc->comment_lengths[i]); temp[vc->comment_lengths[i]] = '\0'; print_vorbis_comment(temp, cb, callback_arg); } free(temp); }