diff options
Diffstat (limited to 'lib/libshout-idjc/src/codec_opus.c')
-rw-r--r-- | lib/libshout-idjc/src/codec_opus.c | 387 |
1 files changed, 199 insertions, 188 deletions
diff --git a/lib/libshout-idjc/src/codec_opus.c b/lib/libshout-idjc/src/codec_opus.c index 45b0ee8eb7..6450f484c1 100644 --- a/lib/libshout-idjc/src/codec_opus.c +++ b/lib/libshout-idjc/src/codec_opus.c @@ -20,7 +20,7 @@ */ #ifdef HAVE_CONFIG_H -# include <config.h> +# include <config.h> #endif #ifdef HAVE_INTTYPES_H @@ -35,250 +35,261 @@ /* -- local data structures -- */ typedef struct { - int version; - int channels; /* Number of channels: 1..255 */ - int preskip; - uint32_t input_sample_rate; - int gain; /* in dB S7.8 should be zero whenever possible */ - int channel_mapping; - /* The rest is only used if channel_mapping != 0 */ - int nb_streams; - int nb_coupled; - unsigned char stream_map[255]; + int version; + int channels; /* Number of channels: 1..255 */ + int preskip; + uint32_t input_sample_rate; + int gain; /* in dB S7.8 should be zero whenever possible */ + int channel_mapping; + + /* The rest is only used if channel_mapping != 0 */ + int nb_streams; + int nb_coupled; + unsigned char stream_map[255]; } OpusHeader; typedef struct { - OpusHeader oh; - int skipped; + OpusHeader oh; + int skipped; } opus_data_t; typedef struct { - const unsigned char *data; - int maxlen; - int pos; + const unsigned char *data; + int maxlen; + int pos; } ROPacket; /* -- local prototypes -- */ -static int read_opus_page(ogg_codec_t *codec, ogg_page *page); +static int read_opus_page(ogg_codec_t *codec, ogg_page *page); static void free_opus_data(void *codec_data); -static int opus_header_parse(const unsigned char *header, int len, OpusHeader *h); +static int opus_header_parse(const unsigned char *header, int len, OpusHeader *h); /* -- header functions -- */ static int read_uint32(ROPacket *p, uint32_t *val) { - if (p->pos>p->maxlen-4) - return 0; - *val = (uint32_t)p->data[p->pos ]; - *val |= (uint32_t)p->data[p->pos+1]<< 8; - *val |= (uint32_t)p->data[p->pos+2]<<16; - *val |= (uint32_t)p->data[p->pos+3]<<24; - p->pos += 4; - return 1; + if (p->pos>p->maxlen-4) + return 0; + *val = (uint32_t)p->data[p->pos ]; + *val |= (uint32_t)p->data[p->pos + 1] << 8; + *val |= (uint32_t)p->data[p->pos + 2] << 16; + *val |= (uint32_t)p->data[p->pos + 3] << 24; + p->pos += 4; + return 1; } static int read_uint16(ROPacket *p, uint16_t *val) { - if (p->pos>p->maxlen-2) - return 0; - *val = (uint16_t)p->data[p->pos ]; - *val |= (uint16_t)p->data[p->pos+1]<<8; - p->pos += 2; - return 1; + if (p->pos>p->maxlen-2) + return 0; + *val = (uint16_t)p->data[p->pos ]; + *val |= (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 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; } static int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h) { - int i; - char str[9]; - ROPacket p; - unsigned char ch; - 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 (strcmp(str, "OpusHead")!=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; - h->nb_streams = ch; - - if (!read_chars(&p, &ch, 1)) - 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)) + int i; + char str[9]; + ROPacket p; + unsigned char ch; + 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 (strcmp(str, "OpusHead")!=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; + + h->nb_streams = ch; + if (!read_chars(&p, &ch, 1)) return 0; - } - } else { - 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; + + 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; + } + } else { + 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; } /* From libopus, src/opus_decode.c */ static int packet_get_samples_per_frame(const unsigned char *data, int32_t Fs) { - int audiosize; - if (data[0]&0x80) - { - audiosize = ((data[0]>>3)&0x3); - audiosize = (Fs<<audiosize)/400; - } else if ((data[0]&0x60) == 0x60) - { - audiosize = (data[0]&0x08) ? Fs/50 : Fs/100; - } else { - audiosize = ((data[0]>>3)&0x3); - if (audiosize == 3) - audiosize = Fs*60/1000; - else - audiosize = (Fs<<audiosize)/100; - } - return audiosize; + int audiosize; + + if (data[0] & 0x80) { + audiosize = ((data[0] >> 3) & 0x3); + audiosize = (Fs << audiosize) / 400; + } else if ((data[0]&0x60) == 0x60) { + audiosize = (data[0] & 0x08) ? Fs / 50 : Fs / 100; + } else { + audiosize = ((data[0] >> 3) & 0x3); + if (audiosize == 3) { + audiosize = Fs * 60 / 1000; + } else { + audiosize = (Fs << audiosize) / 100; + } + } + return audiosize; } /* From libopus, src/opus_decode.c */ static int packet_get_nb_frames(const unsigned char packet[], int32_t len) { - int count; - if (len<1) - return -1; - count = packet[0]&0x3; - if (count==0) - return 1; - else if (count!=3) - return 2; - else if (len<2) - return -4; - else - return packet[1]&0x3F; + int count; + + if (len < 1) + return -1; + + count = packet[0] & 0x3; + if (count==0) { + return 1; + } else if (count!=3) { + return 2; + } else if (len<2) { + return -4; + } else { + return packet[1] & 0x3F; + } } /* -- opus functions -- */ int _shout_open_opus(ogg_codec_t *codec, ogg_page *page) { - opus_data_t *opus_data = calloc(1, sizeof(opus_data_t)); - ogg_packet packet; + opus_data_t *opus_data = calloc(1, sizeof(opus_data_t)); + ogg_packet packet; - (void)page; + (void) page; if (!opus_data) - return SHOUTERR_MALLOC; + return SHOUTERR_MALLOC; - ogg_stream_packetout(&codec->os, &packet); + ogg_stream_packetout(&codec->os, &packet); - if (!opus_header_parse(packet.packet,packet.bytes,&opus_data->oh)) { - free_opus_data(opus_data); - return SHOUTERR_UNSUPPORTED; - } - opus_data->skipped = 0; + if (!opus_header_parse(packet.packet, packet.bytes, &opus_data->oh)) { + free_opus_data(opus_data); + return SHOUTERR_UNSUPPORTED; + } + opus_data->skipped = 0; - codec->codec_data = opus_data; - codec->read_page = read_opus_page; - codec->free_data = free_opus_data; + codec->codec_data = opus_data; + codec->read_page = read_opus_page; + codec->free_data = free_opus_data; - return SHOUTERR_SUCCESS; + return SHOUTERR_SUCCESS; } static int read_opus_page(ogg_codec_t *codec, ogg_page *page) { - ogg_packet packet; - opus_data_t *opus_data = codec->codec_data; - - (void)page; - - /* We use the strategy of counting the packet times and ignoring - the granpos. This has the advantage of needing less code to - sanely handle non-zero starttimes and slightly saner behavior - on files with holes. */ - while (ogg_stream_packetout (&codec->os, &packet) > 0){ - if(packet.bytes>0 && (packet.bytes<2 || memcmp(packet.packet, "Op",2)!=0)){ - int32_t spf; - spf = packet_get_samples_per_frame(packet.packet,48000); - if(spf>0){ - int32_t spp; - spp=packet_get_nb_frames(packet.packet,packet.bytes); - if(spp>0){ - int needskip; - needskip=opus_data->oh.preskip-opus_data->skipped; - spp*=spf; - /*Opus files can begin with some frames which are - just there to prime the decoder and are not played - these should just be sent as fast as we get them.*/ - if(needskip>0){ - int skip; - skip = spp<needskip?spp:needskip; - spp-=skip; - opus_data->skipped+=skip; - } - codec->senttime += ((spp * 1000000ULL) / 48000ULL); + ogg_packet packet; + opus_data_t *opus_data = codec->codec_data; + + (void) page; + + /* We use the strategy of counting the packet times and ignoring + * the granpos. This has the advantage of needing less code to + * sanely handle non-zero starttimes and slightly saner behavior + * on files with holes. + */ + while (ogg_stream_packetout(&codec->os, &packet) > 0) { + if (packet.bytes > 0 && (packet.bytes < 2 || memcmp(packet.packet, "Op", 2) != 0)) { + int32_t spf; + spf = packet_get_samples_per_frame(packet.packet, 48000); + if (spf > 0) { + int32_t spp; + spp = packet_get_nb_frames(packet.packet, packet.bytes); + if (spp > 0) { + int needskip; + needskip = opus_data->oh.preskip - opus_data->skipped; + spp *= spf; + /* Opus files can begin with some frames which are + * just there to prime the decoder and are not played + * these should just be sent as fast as we get them. + */ + if (needskip > 0) { + int skip; + skip = spp < needskip ? spp : needskip; + spp -= skip; + opus_data->skipped += skip; + } + codec->senttime += ((spp * 1000000ULL) / 48000ULL); + } + } else if (packet.bytes >= 19 && memcmp(packet.packet, "OpusHead", 8) == 0) { + /* We appear to be chaining, reset skip to burst the pregap. */ + if (opus_header_parse(packet.packet,packet.bytes,&opus_data->oh)) + opus_data->skipped = 0; } - }else if (packet.bytes>=19 && memcmp(packet.packet, "OpusHead",8)==0){ - /* We appear to be chaining, reset skip to burst the pregap. */ - if(opus_header_parse(packet.packet,packet.bytes,&opus_data->oh)) - opus_data->skipped=0; - } - } - } - - return SHOUTERR_SUCCESS; + } + } + return SHOUTERR_SUCCESS; } static void free_opus_data(void *codec_data) { - free(codec_data); + free(codec_data); } |