oggvorbis_format.c 8.5 KB
Newer Older
1 2 3 4 5 6 7
/********************************************************************
 *                                                                  *
 * 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.                     *
 *                                                                  *
Ralph Giles's avatar
Ralph Giles committed
8
 * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2003                *
9 10 11 12 13
 * by Stan Seibert <volsung@xiph.org> AND OTHER CONTRIBUTORS        *
 * http://www.xiph.org/                                             *
 *                                                                  *
 ********************************************************************

Ralph Giles's avatar
Ralph Giles committed
14
 last mod: $Id: oggvorbis_format.c,v 1.13 2003/06/24 12:34:40 giles Exp $
15 16 17

 ********************************************************************/

Ralph Giles's avatar
Ralph Giles committed
18
#include <stdio.h>
19 20
#include <stdlib.h>
#include <string.h>
21
#include <ctype.h>
22 23 24 25 26
#include <ogg/ogg.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#include "transport.h"
#include "format.h"
Stan Seibert's avatar
Stan Seibert committed
27
#include "vorbis_comments.h"
28
#include "utf8.h"
29
#include "i18n.h"
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47


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;
} ovf_private_t;

/* Forward declarations */
format_t oggvorbis_format;
ov_callbacks vorbisfile_callbacks;


Stan Seibert's avatar
Stan Seibert committed
48 49 50
void print_vorbis_stream_info (decoder_t *decoder);
void print_vorbis_comments (vorbis_comment *vc, decoder_callbacks_t *cb, 
			    void *callback_arg);
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90


/* ----------------------------------------------------------- */


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;
  } else {
91
    fprintf(stderr, _("Error: Out of memory.\n"));
92 93 94 95 96 97 98 99 100 101
    exit(1);
  }

  /* Initialize vorbisfile decoder */
  
  ret = ov_open_callbacks (decoder, &private->vf, NULL, 0, 
			   vorbisfile_callbacks);

  if (ret < 0) {
    free(private);
Segher Boessenkool's avatar
Segher Boessenkool committed
102
/*    free(source);     nope.  caller frees. */ 
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
    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;

Stan Seibert's avatar
Stan Seibert committed
127 128
   
    print_vorbis_stream_info(decoder);
129
    print_vorbis_comments(priv->vc, cb, decoder->callback_arg);
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
    priv->bos = 0;
  }

  *audio_fmt = decoder->actual_fmt;

  /* Attempt to read as much audio as is requested */
  while (nbytes > 0) {

    old_section = priv->current_section;
    ret = ov_read(&priv->vf, ptr, nbytes, audio_fmt->big_endian,
		  audio_fmt->word_size, audio_fmt->signed_sample,
		  &priv->current_section);

    if (ret == 0) {

      /* EOF */
      *eos = 1;
      break;

    } else if (ret == OV_HOLE) {
      
      if (cb->printf_error != NULL)
	cb->printf_error(decoder->callback_arg, INFO,
153
			   _("--- Hole in the stream; probably harmless\n"));
154 155 156 157 158
    
    } else if (ret < 0) {
      
      if (cb->printf_error != NULL)
	cb->printf_error(decoder->callback_arg, ERROR,
159
			 _("=== Vorbis library reported a stream error.\n"));
160 161 162 163
      
    } else {
      
      bytes_read += ret;
164
      ptr = (void *)((unsigned char *)ptr + ret);
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
      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 -------------------- */


Stan Seibert's avatar
Stan Seibert committed
293
void print_vorbis_stream_info (decoder_t *decoder)
294 295 296 297 298 299 300 301
{
  ovf_private_t *priv = decoder->private;
  decoder_callbacks_t *cb = decoder->callbacks;


  if (cb == NULL || cb->printf_metadata == NULL)
    return;
    
302 303 304 305
  cb->printf_metadata(decoder->callback_arg, 2,
		      _("Ogg Vorbis stream: %d channel, %ld Hz"),
		      priv->vi->channels,
		      priv->vi->rate);
306 307

  cb->printf_metadata(decoder->callback_arg, 3,
308
		      _("Vorbis format: Version %d"), 
309 310 311
		      priv->vi->version);
  
  cb->printf_metadata(decoder->callback_arg, 3,
312 313
		      _("Bitrate hints: upper=%ld nominal=%ld lower=%ld "
		      "window=%ld"), 
314 315 316 317
		      priv->vi->bitrate_upper,
		      priv->vi->bitrate_nominal,
		      priv->vi->bitrate_lower,
		      priv->vi->bitrate_window);
318
    
319
  cb->printf_metadata(decoder->callback_arg, 3,
320
		      _("Encoded by: %s"), priv->vc->vendor);
321 322
}

Stan Seibert's avatar
Stan Seibert committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
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);
}