[xiph-commits] r18306 - icecast/trunk/libshout/src
gmaxwell at svn.xiph.org
gmaxwell at svn.xiph.org
Thu May 24 11:12:47 PDT 2012
Author: gmaxwell
Date: 2012-05-24 11:12:46 -0700 (Thu, 24 May 2012)
New Revision: 18306
Added:
icecast/trunk/libshout/src/opus.c
Modified:
icecast/trunk/libshout/src/Makefile.am
icecast/trunk/libshout/src/ogg.c
icecast/trunk/libshout/src/shout_ogg.h
Log:
Support for Opus. This uses packet duration counting and ignores the granpos.
Modified: icecast/trunk/libshout/src/Makefile.am
===================================================================
--- icecast/trunk/libshout/src/Makefile.am 2012-05-24 18:11:49 UTC (rev 18305)
+++ icecast/trunk/libshout/src/Makefile.am 2012-05-24 18:12:46 UTC (rev 18306)
@@ -22,7 +22,7 @@
EXTRA_DIST = theora.c speex.c
noinst_HEADERS = shout_ogg.h shout_private.h util.h
-libshout_la_SOURCES = shout.c util.c ogg.c vorbis.c mp3.c webm.c $(MAYBE_THEORA) $(MAYBE_SPEEX)
+libshout_la_SOURCES = shout.c util.c ogg.c vorbis.c mp3.c webm.c opus.c $(MAYBE_THEORA) $(MAYBE_SPEEX)
AM_CFLAGS = @XIPH_CFLAGS@
libshout_la_LIBADD = net/libicenet.la timing/libicetiming.la avl/libiceavl.la\
Modified: icecast/trunk/libshout/src/ogg.c
===================================================================
--- icecast/trunk/libshout/src/ogg.c 2012-05-24 18:11:49 UTC (rev 18305)
+++ icecast/trunk/libshout/src/ogg.c 2012-05-24 18:12:46 UTC (rev 18306)
@@ -57,6 +57,7 @@
#ifdef HAVE_THEORA
_shout_open_theora,
#endif
+ _shout_open_opus,
#ifdef HAVE_SPEEX
_shout_open_speex,
#endif
Added: icecast/trunk/libshout/src/opus.c
===================================================================
--- icecast/trunk/libshout/src/opus.c (rev 0)
+++ icecast/trunk/libshout/src/opus.c 2012-05-24 18:12:46 UTC (rev 18306)
@@ -0,0 +1,278 @@
+/* -*- c-basic-offset: 8; -*- */
+/* opus.c: Ogg Opus data handlers for libshout
+ *
+ * Copyright (C) 2005 the Icecast team <team at icecast.org>
+ * Copyright (C) 2011,2012 Xiph.Org Foundation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#include <string.h>
+
+#include <ogg/ogg.h>
+
+#include "shout_private.h"
+#include "shout_ogg.h"
+
+/* -- 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];
+} OpusHeader;
+
+typedef struct {
+ OpusHeader oh;
+ int skipped;
+} opus_data_t;
+
+typedef struct {
+ const unsigned char *data;
+ int maxlen;
+ int pos;
+} ROPacket;
+
+/* -- local prototypes -- */
+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);
+
+/* -- 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;
+}
+
+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;
+}
+
+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;
+}
+
+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))
+ return 0;
+ }
+ } else {
+ h->nb_streams = 1;
+ h->nb_coupled = h->channels>1;
+ h->stream_map[0]=0;
+ h->stream_map[1]=1;
+ }
+ if (h->version==0 && 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;
+}
+
+/* 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;
+}
+
+/* -- 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;
+
+ if (!opus_data)
+ return SHOUTERR_MALLOC;
+
+ 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;
+
+ codec->codec_data = opus_data;
+ codec->read_page = read_opus_page;
+ codec->free_data = free_opus_data;
+
+ 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;
+
+ /*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 && 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;
+ }
+ }
+ }
+
+ return SHOUTERR_SUCCESS;
+}
+
+static void free_opus_data(void *codec_data)
+{
+ free(codec_data);
+}
Modified: icecast/trunk/libshout/src/shout_ogg.h
===================================================================
--- icecast/trunk/libshout/src/shout_ogg.h 2012-05-24 18:11:49 UTC (rev 18305)
+++ icecast/trunk/libshout/src/shout_ogg.h 2012-05-24 18:12:46 UTC (rev 18306)
@@ -51,5 +51,6 @@
#ifdef HAVE_SPEEX
int _shout_open_speex(ogg_codec_t *codec, ogg_page *page);
#endif
+int _shout_open_opus(ogg_codec_t *codec, ogg_page *page);
#endif
More information about the commits
mailing list