shithub: opus-tools

ref: dc500a7ac53098c5b360c33936a2e74c6a99cbfc
dir: /src/opus_header.c/

View raw version
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "opus_header.h"
#include <string.h>
#include <stdio.h>

/* Header contents:
  - "OpusHead" (64 bits)
  - version number (8 bits)
  - Channels C (8 bits)
  - Pre-skip (16 bits)
  - Sampling rate (32 bits)
  - Gain in dB (16 bits, S7.8)
  - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
             2..254: reserved, 255: multistream with no mapping)

  - if (mapping != 0)
     - N = totel number of streams (8 bits)
     - M = number of paired streams (8 bits)
     - C times channel origin
          - if (C<2*M)
             - stream = byte/2
             - if (byte&0x1 == 0)
                 - left
               else
                 - right
          - else
             - stream = byte-M
*/

typedef struct {
   unsigned char *data;
   int maxlen;
   int pos;
} Packet;

typedef struct {
   const unsigned char *data;
   int maxlen;
   int pos;
} ROPacket;

static int write_uint32(Packet *p, ogg_uint32_t val)
{
   if (p->pos>p->maxlen-4)
      return 0;
   p->data[p->pos  ] = (val    ) & 0xFF;
   p->data[p->pos+1] = (val>> 8) & 0xFF;
   p->data[p->pos+2] = (val>>16) & 0xFF;
   p->data[p->pos+3] = (val>>24) & 0xFF;
   p->pos += 4;
   return 1;
}

static int write_uint16(Packet *p, ogg_uint16_t val)
{
   if (p->pos>p->maxlen-2)
      return 0;
   p->data[p->pos  ] = (val    ) & 0xFF;
   p->data[p->pos+1] = (val>> 8) & 0xFF;
   p->pos += 2;
   return 1;
}

static int write_chars(Packet *p, const unsigned char *str, int nb_chars)
{
   int i;
   if (p->pos>p->maxlen-nb_chars)
      return 0;
   for (i=0;i<nb_chars;i++)
      p->data[p->pos++] = str[i];
   return 1;
}

static int read_uint32(ROPacket *p, ogg_uint32_t *val)
{
   if (p->pos>p->maxlen-4)
      return 0;
   *val =  (ogg_uint32_t)p->data[p->pos  ];
   *val |= (ogg_uint32_t)p->data[p->pos+1]<< 8;
   *val |= (ogg_uint32_t)p->data[p->pos+2]<<16;
   *val |= (ogg_uint32_t)p->data[p->pos+3]<<24;
   p->pos += 4;
   return 1;
}

static int read_uint16(ROPacket *p, ogg_uint16_t *val)
{
   if (p->pos>p->maxlen-2)
      return 0;
   *val =  (ogg_uint16_t)p->data[p->pos  ];
   *val |= (ogg_uint16_t)p->data[p->pos+1]<<8;
   p->pos += 2;
   return 1;
}

static int read_chars(ROPacket *p, unsigned char *str, int nb_chars)
{
   int i;
   if (p->pos>p->maxlen-nb_chars)
      return 0;
   for (i=0;i<nb_chars;i++)
      str[i] = p->data[p->pos++];
   return 1;
}

int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h)
{
   int i;
   char str[9];
   ROPacket p;
   unsigned char ch;
   ogg_uint16_t shortval;

   p.data = packet;
   p.maxlen = len;
   p.pos = 0;
   str[8] = 0;
   if (len<19)return 0;
   read_chars(&p, (unsigned char*)str, 8);
   if (memcmp(str, "OpusHead", 8)!=0)
      return 0;

   if (!read_chars(&p, &ch, 1))
      return 0;
   h->version = ch;
   if((h->version&240) != 0) /* Only major version 0 supported. */
      return 0;

   if (!read_chars(&p, &ch, 1))
      return 0;
   h->channels = ch;
   if (h->channels == 0)
      return 0;

   if (!read_uint16(&p, &shortval))
      return 0;
   h->preskip = shortval;

   if (!read_uint32(&p, &h->input_sample_rate))
      return 0;

   if (!read_uint16(&p, &shortval))
      return 0;
   h->gain = (short)shortval;

   if (!read_chars(&p, &ch, 1))
      return 0;
   h->channel_mapping = ch;

   if (h->channel_mapping != 0)
   {
      if (!read_chars(&p, &ch, 1))
         return 0;

      if (ch<1)
         return 0;
      h->nb_streams = ch;

      if (!read_chars(&p, &ch, 1))
         return 0;

      if (ch>h->nb_streams || (ch+h->nb_streams)>255)
         return 0;
      h->nb_coupled = ch;

      /* Multi-stream support */
      for (i=0;i<h->channels;i++)
      {
         if (!read_chars(&p, &h->stream_map[i], 1))
            return 0;
         if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255)
            return 0;
      }
   } else {
      if(h->channels>2)
         return 0;
      h->nb_streams = 1;
      h->nb_coupled = h->channels>1;
      h->stream_map[0]=0;
      h->stream_map[1]=1;
   }
   /*For version 0/1 we know there won't be any more data
     so reject any that have data past the end.*/
   if ((h->version==0 || h->version==1) && p.pos != len)
      return 0;
   return 1;
}

int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len)
{
   int i;
   Packet p;
   unsigned char ch;

   p.data = packet;
   p.maxlen = len;
   p.pos = 0;
   if (len<19)return 0;
   if (!write_chars(&p, (const unsigned char*)"OpusHead", 8))
      return 0;
   /* Version is 1 */
   ch = 1;
   if (!write_chars(&p, &ch, 1))
      return 0;

   ch = h->channels;
   if (!write_chars(&p, &ch, 1))
      return 0;

   if (!write_uint16(&p, h->preskip))
      return 0;

   if (!write_uint32(&p, h->input_sample_rate))
      return 0;

   if (!write_uint16(&p, h->gain))
      return 0;

   ch = h->channel_mapping;
   if (!write_chars(&p, &ch, 1))
      return 0;

   if (h->channel_mapping != 0)
   {
      ch = h->nb_streams;
      if (!write_chars(&p, &ch, 1))
         return 0;

      ch = h->nb_coupled;
      if (!write_chars(&p, &ch, 1))
         return 0;

      /* Multi-stream support */
      for (i=0;i<h->channels;i++)
      {
         if (!write_chars(&p, &h->stream_map[i], 1))
            return 0;
      }
   }

   return p.pos;
}