oggz_auto.c 12.3 KB
Newer Older
conrad's avatar
conrad committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/*
   Copyright (C) 2003 Commonwealth Scientific and Industrial Research
   Organisation (CSIRO) Australia

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

   - Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

   - Neither the name of CSIRO Australia nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/*
 * oggz_auto.c
 *
 * Conrad Parker <conrad@annodex.net>
 */

#include "config.h"

#if OGGZ_CONFIG_READ
42
#include <stdlib.h>
conrad's avatar
conrad committed
43 44
#include <string.h>

45
#include "oggz_private.h"
conrad's avatar
conrad committed
46 47
#include "oggz_byteorder.h"

conrad's avatar
conrad committed
48 49
/*#define DEBUG*/

50 51 52 53 54
/* Allow use of internal metrics; ie. the user_data for these gets free'd
 * when the metric is overwritten, or on close */
int oggz_set_metric_internal (OGGZ * oggz, long serialno, OggzMetric metric,
			      void * user_data, int internal);

55 56 57 58
int oggz_set_metric_linear (OGGZ * oggz, long serialno,
			    ogg_int64_t granule_rate_numerator,
			    ogg_int64_t granule_rate_denominator);

conrad's avatar
conrad committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
#define INT32_LE_AT(x) _le_32((*(ogg_int32_t *)(x)))
#define INT32_BE_AT(x) _be_32((*(ogg_int32_t *)(x)))
#define INT64_LE_AT(x) _le_64((*(ogg_int64_t *)(x)))

#define OGGZ_AUTO_MULT 1000

static int
auto_speex (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
  ogg_int64_t granule_rate = 0;

  if (op->bytes < 68) return 0;

  granule_rate = (ogg_int64_t) INT32_LE_AT(&header[36]);
#ifdef DEBUG
  printf ("Got speex rate %d\n", (int)granule_rate);
#endif

78
  oggz_set_granulerate (oggz, serialno, granule_rate, OGGZ_AUTO_MULT);
conrad's avatar
conrad committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

  return 1;
}

static int
auto_vorbis (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
  ogg_int64_t granule_rate = 0;

  if (op->bytes < 30) return 0;

  granule_rate = (ogg_int64_t) INT32_LE_AT(&header[12]);
#ifdef DEBUG
  printf ("Got vorbis rate %d\n", (int)granule_rate);
#endif

96
  oggz_set_granulerate (oggz, serialno, granule_rate, OGGZ_AUTO_MULT);
conrad's avatar
conrad committed
97 98 99 100

  return 1;
}

101
#if USE_THEORA_PRE_ALPHA_3_FORMAT
conrad's avatar
conrad committed
102 103 104 105 106 107 108 109
static int intlog(int num) {
  int ret=0;
  while(num>0){
    num=num/2;
    ret=ret+1;
  }
  return(ret);
}
110
#endif
conrad's avatar
conrad committed
111 112 113 114 115

static int
auto_theora (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
116
  ogg_int32_t fps_numerator, fps_denominator;
conrad's avatar
conrad committed
117
  char keyframe_granule_shift = 0;
118
  int keyframe_shift;
conrad's avatar
conrad committed
119 120 121

  if (op->bytes < 41) return 0;

122 123
  fps_numerator = INT32_BE_AT(&header[22]);
  fps_denominator = INT32_BE_AT(&header[26]);
conrad's avatar
conrad committed
124

125 126 127 128
  /* Very old theora versions used a value of 0 to mean 1.
   * Unfortunately theora hasn't incremented its version field,
   * hence we hardcode this workaround for old or broken streams.
   */
129
  if (fps_numerator == 0) fps_numerator = 1;
130

conrad's avatar
conrad committed
131 132
#if USE_THEORA_PRE_ALPHA_3_FORMAT
  /* old header format, used by Theora alpha2 and earlier */
conrad's avatar
conrad committed
133
  keyframe_granule_shift = (header[36] & 0xf8) >> 3;
134
  keyframe_shift = intlog (keyframe_granule_shift - 1);
conrad's avatar
conrad committed
135
#else
colinw's avatar
colinw committed
136
  keyframe_granule_shift = (char) ((header[40] & 0x03) << 3);
conrad's avatar
conrad committed
137
  keyframe_granule_shift |= (header[41] & 0xe0) >> 5;
138
  keyframe_shift = keyframe_granule_shift;
conrad's avatar
conrad committed
139
#endif
conrad's avatar
conrad committed
140 141

#ifdef DEBUG
142
  printf ("Got theora fps %d/%d, keyframe_shift %d\n",
143
	  fps_numerator, fps_denominator, keyframe_shift);
conrad's avatar
conrad committed
144 145
#endif

146 147 148
  oggz_set_granulerate (oggz, serialno, (ogg_int64_t)fps_numerator,
			OGGZ_AUTO_MULT * (ogg_int64_t)fps_denominator);
  oggz_set_granuleshift (oggz, serialno, keyframe_shift);
conrad's avatar
conrad committed
149 150 151 152

  return 1;
}

153 154 155
static int
auto_annodex (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
156
  /* Apply a zero metric */
157
  oggz_set_granulerate (oggz, serialno, 0, 1);
158 159 160 161

  return 1;
}

conrad's avatar
conrad committed
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
static int
auto_anxdata (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
  ogg_int64_t granule_rate_numerator = 0, granule_rate_denominator = 0;

  if (op->bytes < 28) return 0;

  granule_rate_numerator = INT64_LE_AT(&header[8]);
  granule_rate_denominator = INT64_LE_AT(&header[16]);
#ifdef DEBUG
  printf ("Got AnxData rate %lld/%lld\n", granule_rate_numerator,
	  granule_rate_denominator);
#endif

177 178 179
  oggz_set_granulerate (oggz, serialno,
			granule_rate_numerator,
			OGGZ_AUTO_MULT * granule_rate_denominator);
conrad's avatar
conrad committed
180 181 182 183

  return 1;
}

184 185 186 187 188 189
static int
auto_flac0 (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
  ogg_int64_t granule_rate = 0;

190 191
  granule_rate = (ogg_int64_t) (header[14] << 12) | (header[15] << 4) | 
            ((header[16] >> 4)&0xf);
192 193 194 195
#ifdef DEBUG
    printf ("Got flac rate %d\n", (int)granule_rate);
#endif
    
196
  oggz_set_granulerate (oggz, serialno, granule_rate, OGGZ_AUTO_MULT);
197 198 199 200

  return 1;
}

201 202 203 204 205 206 207 208
static int
auto_flac (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
  ogg_int64_t granule_rate = 0;

  if (op->bytes < 51) return 0;

209 210
  granule_rate = (ogg_int64_t) (header[27] << 12) | (header[28] << 4) | 
            ((header[29] >> 4)&0xf);
211 212 213 214
#ifdef DEBUG
  printf ("Got flac rate %d\n", (int)granule_rate);
#endif

215
  oggz_set_granulerate (oggz, serialno, granule_rate, OGGZ_AUTO_MULT);
216 217 218 219

  return 1;
}

220 221 222 223 224 225 226 227 228 229
/**
 * Recognizer for OggPCM2:
 * http://wiki.xiph.org/index.php/OggPCM2
 */
static int
auto_oggpcm2 (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
  ogg_int64_t granule_rate;

230
  if (op->bytes < 28) return 0;
231 232 233 234 235 236 237 238 239 240 241

  granule_rate = (ogg_int64_t) INT32_BE_AT(&header[16]);
#ifdef DEBUG
  printf ("Got OggPCM2 rate %d\n", (int)granule_rate);
#endif

  oggz_set_granulerate (oggz, serialno, granule_rate, OGGZ_AUTO_MULT);

  return 1;
}

conrad's avatar
conrad committed
242 243 244 245 246
static int
auto_cmml (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
  ogg_int64_t granule_rate_numerator = 0, granule_rate_denominator = 0;
247
  int granuleshift;
conrad's avatar
conrad committed
248 249 250 251 252

  if (op->bytes < 28) return 0;

  granule_rate_numerator = INT64_LE_AT(&header[12]);
  granule_rate_denominator = INT64_LE_AT(&header[20]);
253 254 255 256 257
  if (op->bytes > 28)
    granuleshift = (int)header[28];
  else
    granuleshift = 0;

conrad's avatar
conrad committed
258 259 260 261 262
#ifdef DEBUG
  printf ("Got CMML rate %lld/%lld\n", granule_rate_numerator,
	  granule_rate_denominator);
#endif

263 264 265
  oggz_set_granulerate (oggz, serialno,
			granule_rate_numerator,
			OGGZ_AUTO_MULT * granule_rate_denominator);
266
  oggz_set_granuleshift (oggz, serialno, granuleshift);
conrad's avatar
conrad committed
267 268 269 270

  return 1;
}

271 272 273 274
static int
auto_fisbone (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  unsigned char * header = op->packet;
275
  long fisbone_serialno; /* The serialno referred to in this fisbone */
276
  ogg_int64_t granule_rate_numerator = 0, granule_rate_denominator = 0;
conrad's avatar
conrad committed
277
  int granuleshift;
278 279 280 281

  if (op->bytes < 48) return 0;

  fisbone_serialno = (long) INT32_LE_AT(&header[12]);
282 283

  /* Don't override an already assigned metric */
284
  if (oggz_stream_has_metric (oggz, fisbone_serialno)) return 1;
285 286 287

  granule_rate_numerator = INT64_LE_AT(&header[20]);
  granule_rate_denominator = INT64_LE_AT(&header[28]);
conrad's avatar
conrad committed
288 289
  granuleshift = (int)header[48];

290
#ifdef DEBUG
conrad's avatar
conrad committed
291 292
  printf ("Got fisbone granulerate %lld/%lld, granuleshift %d for serialno %010ld\n",
	  granule_rate_numerator, granule_rate_denominator, granuleshift,
293 294 295
	  fisbone_serialno);
#endif

296 297 298 299
  oggz_set_granulerate (oggz, fisbone_serialno,
			granule_rate_numerator,
			OGGZ_AUTO_MULT * granule_rate_denominator);
  oggz_set_granuleshift (oggz, fisbone_serialno, granuleshift);
conrad's avatar
conrad committed
300
				
301 302 303
  return 1;
}

304 305 306 307 308 309 310 311 312 313 314 315 316
static int
auto_fishead (OGGZ * oggz, ogg_packet * op, long serialno, void * user_data)
{
  if (!op->b_o_s)
  {
    return auto_fisbone(oggz, op, serialno, user_data);
  }
  
  oggz_set_granulerate (oggz, serialno, 0, 1);
  
  return 1;
}

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
static ogg_int64_t 
auto_calc_speex(ogg_int64_t now, oggz_stream_t *stream, ogg_packet *op) {
  
  /*
   * on the first (b_o_s) packet, set calculate_data to be the number
   * of speex frames per packet
   */
  if (stream->calculate_data == NULL) {
    stream->calculate_data = malloc(sizeof(int));
    *(int *)stream->calculate_data = 
        (*(int *)(op->packet + 64)) * (*(int *)(op->packet + 56));
  }
  
  if (now > -1)
    return now;

  /*
   * the first data packet has smaller-than-usual granulepos to account
   * for the fact that several of the output samples from the beginning
   * of this packet need to be thrown away.  We calculate the granulepos
   * by taking the mod of the page's granulepos with respect to the increment
   * between packets.
   */
  if (stream->last_granulepos == 0) {
    return stream->page_granulepos % *(int *)(stream->calculate_data);
  }
  
  return stream->last_granulepos + *(int *)(stream->calculate_data);
}
  
static ogg_int64_t 
auto_calc_theora(ogg_int64_t now, oggz_stream_t *stream, ogg_packet *op) {

  long keyframe_no;
  int keyframe_shift;
  unsigned char first_byte;
  
  if (now > (ogg_int64_t)(-1))
    return now;

  first_byte = op->packet[0];

  if (first_byte & 0x80)
  {
    /* header packet */
    return (ogg_int64_t)0;
  }
  
  if (first_byte & 0x40)
  {
    /* inter-coded packet */
    return stream->last_granulepos + 1;
  }

  /* intra-coded packet */
  if (stream->last_granulepos == 0)
  {
    /* first intra-coded packet */
    return (ogg_int64_t)0;
  }

  keyframe_shift = stream->granuleshift; 
  /*
   * retrieve last keyframe number
   */
  keyframe_no = (int)(stream->last_granulepos >> keyframe_shift);
  /*
   * add frames since last keyframe number
   */
  keyframe_no += (stream->last_granulepos & ((1 << keyframe_shift) - 1)) + 1;
  return ((ogg_int64_t)keyframe_no) << keyframe_shift;
  

}

392
const oggz_auto_contenttype_t oggz_auto_codec_ident[] = {
393 394 395 396 397 398 399 400 401 402 403
  {"\200theora", 7, "Theora", auto_theora, auto_calc_theora},
  {"\001vorbis", 7, "Vorbis", auto_vorbis, NULL},
  {"Speex", 5, "Speex", auto_speex, auto_calc_speex},
  {"PCM     ", 8, "PCM", auto_oggpcm2, NULL},
  {"CMML\0\0\0\0", 8, "CMML", auto_cmml, NULL},
  {"Annodex", 8, "Annodex", auto_annodex, NULL},
  {"fishead", 7, "Skeleton", auto_fishead, NULL},
  {"fLaC", 4, "Flac0", auto_flac0, NULL},
  {"\177FLAC", 4, "Flac", auto_flac, NULL},
  {"AnxData", 7, "AnxData", auto_anxdata, NULL},
  {"", 0, "Unknown", NULL}
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
}; 

int oggz_auto_identify (OGGZ *oggz, ogg_page *og, long serialno) {

  int i;
  
  for (i = 0; i < OGGZ_CONTENT_UNKNOWN; i++)
  {
    const oggz_auto_contenttype_t *codec = oggz_auto_codec_ident + i;
    
    if (og->body_len >= codec->bos_str_len &&
              memcmp (og->body, codec->bos_str, codec->bos_str_len) == 0) {
      
      oggz_stream_set_content (oggz, serialno, i);
      
      return 1;
    }
  }
                      
  oggz_stream_set_content (oggz, serialno, OGGZ_CONTENT_UNKNOWN);
  return 0;
}
conrad's avatar
conrad committed
426 427

int
428
oggz_auto_get_granulerate (OGGZ * oggz, ogg_packet * op, long serialno, 
429
                void * user_data) {
conrad's avatar
conrad committed
430
  OggzReadPacket read_packet;
431 432
  int content = 0;
  int will_run_function;
conrad's avatar
conrad committed
433

434 435 436
  content = oggz_stream_get_content(oggz, serialno);
  if (content < 0 || content >= OGGZ_CONTENT_UNKNOWN) {
    return 0;
conrad's avatar
conrad committed
437 438
  }

439
  oggz_auto_codec_ident[content].reader(oggz, op, serialno, user_data);
conrad's avatar
conrad committed
440 441 442
  return 0;
}

443 444 445 446 447 448 449 450 451 452 453
ogg_int64_t 
oggz_auto_calculate_granulepos(int content, ogg_int64_t now, 
                oggz_stream_t *stream, ogg_packet *op) {
  if (oggz_auto_codec_ident[content].calculator != NULL) {
    return oggz_auto_codec_ident[content].calculator(now, stream, op);
  } else {
    return now;
  }
  
}

conrad's avatar
conrad committed
454
#endif /* OGGZ_CONFIG_READ */