Commit af480570 authored by .'s avatar . Committed by Rafaël Carré

Opus decoder.

This patch uses the information passed by the demuxer in block_t's
i_nb_samples and i_length to properly handle pre-skip, seeking
pre-roll, and end-trim. Multi-channel decoding should work. It also
adds .opus to the list of supported formats.
Signed-off-by: Rafaël Carré's avatarRafaël Carré <funman@videolan.org>
parent 675072a8
......@@ -9,6 +9,7 @@ Important changes for packagers:
* /extras/contrib has been replaced by a better system in /contrib
Codecs:
* Support for OPUS via libopus.
* Support for CDXL, Ut Video, VBLE, Dxtory codecs via libavcodec.
* Numerous improvements in OpenMAX IL codec
* Support for Ulead DV audio
......
......@@ -617,7 +617,7 @@ AC_CHECK_FUNC(getopt_long,, [
AC_SUBST(GNUGETOPT_LIBS)
AC_CHECK_LIB(m,cos,[
VLC_ADD_LIBS([adjust wave ripple psychedelic gradient a52tofloat32 dtstofloat32 x264 goom visual panoramix rotate noise grain scene kate flac lua chorus_flanger freetype avcodec avformat access_avio swscale postproc i420_rgb faad twolame equalizer spatializer param_eq samplerate freetype mpc dmo mp4 quicktime qt4 compressor headphone_channel_mixer normvol audiobargraph_a speex mono colorthres extract ball access_imem hotkeys mosaic gaussianblur dbus x26410b hqdn3d anaglyph oldrc ncurses],[-lm])
VLC_ADD_LIBS([adjust wave ripple psychedelic gradient a52tofloat32 dtstofloat32 x264 goom visual panoramix rotate noise grain scene kate flac lua chorus_flanger freetype avcodec avformat access_avio swscale postproc i420_rgb faad twolame equalizer spatializer param_eq samplerate freetype mpc dmo mp4 quicktime qt4 compressor headphone_channel_mixer normvol audiobargraph_a speex opus mono colorthres extract ball access_imem hotkeys mosaic gaussianblur dbus x26410b hqdn3d anaglyph oldrc ncurses],[-lm])
LIBM="-lm"
], [
LIBM=""
......@@ -2711,6 +2711,11 @@ AS_IF([test "${enable_speex}" != "no"], [
])
AM_CONDITIONAL([HAVE_SPEEXDSP], [test "$have_speexdsp" = "yes"])
dnl
dnl Opus plugin
dnl
PKG_ENABLE_MODULES_VLC([OPUS], [], [ogg opus], [Opus support], [auto])
dnl
dnl theora decoder plugin
dnl
......
......@@ -42,7 +42,7 @@ complete list of available options.
.B VLC
recognizes several URL-style items:
.TP
.B *.mpg, *.vob, *.avi, *.mp3, *.ogg
.B *.mpg, *.vob, *.avi, *.mp3, *.ogg, *.opus
Various multimedia file formats
.TP
.B dvd://[<device>][@<raw device>][#[<title>][:[<chapter>][:<angle>]]]
......
......@@ -142,8 +142,9 @@ FunctionEnd
!insertmacro ${_action} ".mp3"
!insertmacro ${_action} ".mpc"
!insertmacro ${_action} ".mpga"
!insertmacro ${_action} ".oma"
!insertmacro ${_action} ".oga"
!insertmacro ${_action} ".oma"
!insertmacro ${_action} ".opus"
!insertmacro ${_action} ".qcp"
!insertmacro ${_action} ".ra"
!insertmacro ${_action} ".rmi"
......
......@@ -228,6 +228,7 @@ typedef enum vlc_dialog {
"*.oga;" \
"*.ogg;" \
"*.oma;" \
"*.opus;" \
"*.qcp;" \
"*.ra;" \
"*.rmi;" \
......
......@@ -226,6 +226,7 @@ $Id$
* opencv_example: OpenCV example (face identification)
* opencv_wrapper: OpenCV wrapper video filter
* opensles_android: OpenSL ES audio output for Android
* opus: a opus audio decoder/packetizer using the libopus library
* os2drive: service discovery for OS/2 drives
* osd_parser: OSD import module
* osdmenu: video_filter for displaying and streaming a On Screen Display menu
......
......@@ -9,6 +9,7 @@ SOURCES_theora = theora.c
SOURCES_tremor = vorbis.c
SOURCES_speex = speex.c
SOURCES_adpcm = adpcm.c
SOURCES_opus = opus.c opus_header.c opus_header.h
SOURCES_uleaddvaudio = uleaddvaudio.c
SOURCES_mpeg_audio = mpeg_audio.c
SOURCES_libmpeg2 = libmpeg2.c
......
/*****************************************************************************
* opus.c: opus decoder/encoder module making use of libopus.
*****************************************************************************
* Copyright (C) 2003-2009, 2012 the VideoLAN team
*
* Authors: Gregory Maxwell <greg@xiph.org>
* Based on speex.c by: Gildas Bazin <gbazin@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*
* TODO: preskip, trimming, file duration
*/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_input.h>
#include <vlc_codec.h>
#include <vlc_aout.h>
#include "../demux/xiph.h"
#include <ogg/ogg.h>
#include <opus.h>
#include <opus_multistream.h>
#include "opus_header.h"
#ifndef OPUS_SET_GAIN
#include <math.h>
#endif
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int OpenDecoder ( vlc_object_t * );
static void CloseDecoder ( vlc_object_t * );
vlc_module_begin ()
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_ACODEC )
set_description( N_("Opus audio decoder") )
set_capability( "decoder", 100 )
set_shortname( N_("Opus") )
set_callbacks( OpenDecoder, CloseDecoder )
vlc_module_end ()
/*****************************************************************************
* decoder_sys_t : opus decoder descriptor
*****************************************************************************/
struct decoder_sys_t
{
/*
* Input properties
*/
bool b_has_headers;
/*
* Opus properties
*/
OpusHeader header;
OpusMSDecoder *p_st;
/*
* Common properties
*/
date_t end_date;
};
static const int pi_channels_maps[9] =
{
0,
AOUT_CHAN_CENTER,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
| AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT
| AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
| AOUT_CHAN_LFE,
};
/*
** channel order as defined in http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
*/
/* recommended vorbis channel order for 8 channels */
static const uint32_t pi_8channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,AOUT_CHAN_LFE, 0 };
/* recommended vorbis channel order for 7 channels */
static const uint32_t pi_7channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
AOUT_CHAN_REARCENTER, AOUT_CHAN_LFE, 0 };
/* recommended vorbis channel order for 6 channels */
static const uint32_t pi_6channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_LFE, 0 };
/* recommended vorbis channel order for 5 channels */
static const uint32_t pi_5channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 };
/* recommended vorbis channel order for 4 channels */
static const uint32_t pi_4channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 };
/* recommended vorbis channel order for 3 channels */
static const uint32_t pi_3channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT, 0 };
/****************************************************************************
* Local prototypes
****************************************************************************/
static block_t *DecodeBlock ( decoder_t *, block_t ** );
static int ProcessHeaders( decoder_t * );
static int ProcessInitialHeader ( decoder_t *, ogg_packet * );
static void *ProcessPacket( decoder_t *, ogg_packet *, block_t ** );
static block_t *DecodePacket( decoder_t *, ogg_packet *, int, int );
/*****************************************************************************
* OpenDecoder: probe the decoder and return score
*****************************************************************************/
static int OpenDecoder( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys;
if( p_dec->fmt_in.i_codec != VLC_CODEC_OPUS )
return VLC_EGENERIC;
/* Allocate the memory needed to store the decoder's structure */
if( ( p_dec->p_sys = p_sys = malloc(sizeof(decoder_sys_t)) ) == NULL )
return VLC_ENOMEM;
p_dec->p_sys->b_has_headers = false;
date_Set( &p_sys->end_date, 0 );
/* Set output properties */
p_dec->fmt_out.i_cat = AUDIO_ES;
p_dec->fmt_out.i_codec = VLC_CODEC_FL32;
p_dec->pf_decode_audio = DecodeBlock;
p_dec->pf_packetize = DecodeBlock;
p_sys->p_st = NULL;
return VLC_SUCCESS;
}
/****************************************************************************
* DecodeBlock: the whole thing
****************************************************************************
* This function must be fed with ogg packets.
****************************************************************************/
static block_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
ogg_packet oggpacket;
if( !pp_block || !*pp_block)
return NULL;
/* Block to Ogg packet */
oggpacket.packet = (*pp_block)->p_buffer;
oggpacket.bytes = (*pp_block)->i_buffer;
oggpacket.granulepos = -1;
oggpacket.b_o_s = 0;
oggpacket.e_o_s = 0;
oggpacket.packetno = 0;
/* Check for headers */
if( !p_sys->b_has_headers )
{
if( ProcessHeaders( p_dec ) )
{
block_Release( *pp_block );
return NULL;
}
p_sys->b_has_headers = true;
}
return ProcessPacket( p_dec, &oggpacket, pp_block );
}
/*****************************************************************************
* ProcessHeaders: process Opus headers.
*****************************************************************************/
static int ProcessHeaders( decoder_t *p_dec )
{
ogg_packet oggpacket;
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
int ret = VLC_EGENERIC;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) )
return VLC_EGENERIC;
if( i_count < 2 )
goto end;
oggpacket.granulepos = -1;
oggpacket.e_o_s = 0;
oggpacket.packetno = 0;
/* Take care of the initial Opus header */
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
oggpacket.bytes = pi_size[0];
oggpacket.packet = pp_data[0];
ret = ProcessInitialHeader( p_dec, &oggpacket );
if (ret != VLC_SUCCESS)
msg_Err( p_dec, "initial Opus header is corrupted" );
end:
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return ret;
}
/*****************************************************************************
* ProcessInitialHeader: processes the inital Opus header packet.
*****************************************************************************/
static int ProcessInitialHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
{
int err;
int pi_chan_table[AOUT_CHAN_MAX];
unsigned char new_stream_map[8];
decoder_sys_t *p_sys = p_dec->p_sys;
OpusHeader *p_header = &p_sys->header;
if( !opus_header_parse((unsigned char *)p_oggpacket->packet,p_oggpacket->bytes,p_header) )
{
msg_Err( p_dec, "cannot read Opus header" );
return VLC_EGENERIC;
}
msg_Dbg( p_dec, "Opus audio with %d channels", p_header->channels);
if((p_header->channels>2 && p_header->channel_mapping==0) ||
(p_header->channels>8 && p_header->channel_mapping==1) ||
p_header->channel_mapping>1)
{
msg_Err( p_dec, "Unsupported channel mapping" );
return VLC_EGENERIC;
}
/* Setup the format */
p_dec->fmt_out.audio.i_physical_channels =
p_dec->fmt_out.audio.i_original_channels =
pi_channels_maps[p_header->channels];
p_dec->fmt_out.audio.i_channels = p_header->channels;
p_dec->fmt_out.audio.i_rate = 48000;
if( p_header->channels>2 )
{
static const uint32_t *pi_ch[6] = { pi_3channels_in, pi_4channels_in,
pi_5channels_in, pi_6channels_in,
pi_7channels_in, pi_8channels_in };
aout_CheckChannelReorder( pi_ch[p_header->channels-3], NULL,
p_dec->fmt_out.audio.i_physical_channels,
p_header->channels,
pi_chan_table );
for(int i=0;i<p_header->channels;i++)
new_stream_map[pi_chan_table[i]]=p_header->stream_map[i];
}
/* Opus decoder init */
p_sys->p_st = opus_multistream_decoder_create( 48000, p_header->channels,
p_header->nb_streams, p_header->nb_coupled,
p_header->channels>2?new_stream_map:p_header->stream_map,
&err );
if( !p_sys->p_st || err!=OPUS_OK )
{
msg_Err( p_dec, "decoder initialization failed" );
return VLC_EGENERIC;
}
#ifdef OPUS_SET_GAIN
if( opus_multistream_decoder_ctl( p_sys->p_st,OPUS_SET_GAIN(p_header->gain) ) != OPUS_OK )
{
msg_Err( p_dec, "OPUS_SET_GAIN failed" );
opus_multistream_decoder_destroy( p_sys->p_st );
return VLC_EGENERIC;
}
#endif
date_Init( &p_sys->end_date, 48000, 1 );
return VLC_SUCCESS;
}
/*****************************************************************************
* ProcessPacket: processes a Opus packet.
*****************************************************************************/
static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_block = *pp_block;
/* Date management */
if( p_block && p_block->i_pts > VLC_TS_INVALID &&
p_block->i_pts != date_Get( &p_sys->end_date ) )
{
date_Set( &p_sys->end_date, p_block->i_pts );
}
if( !date_Get( &p_sys->end_date ) )
{
/* We've just started the stream, wait for the first PTS. */
if( p_block ) block_Release( p_block );
return NULL;
}
*pp_block = NULL; /* To avoid being fed the same packet again */
{
block_t *p_aout_buffer = DecodePacket( p_dec, p_oggpacket,
p_block->i_nb_samples,
(int)p_block->i_length );
block_Release( p_block );
return p_aout_buffer;
}
}
/*****************************************************************************
* DecodePacket: decodes a Opus packet.
*****************************************************************************/
static block_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
int i_nb_samples, int i_end_trim )
{
decoder_sys_t *p_sys = p_dec->p_sys;
if( !p_oggpacket->bytes )
return NULL;
int spp;
spp=opus_packet_get_nb_frames(p_oggpacket->packet,p_oggpacket->bytes);
if(spp>0)spp*=opus_packet_get_samples_per_frame(p_oggpacket->packet,48000);
if(spp<120||spp>120*48)return NULL;
block_t *p_aout_buffer=decoder_NewAudioBuffer( p_dec, spp );
if ( !p_aout_buffer )
{
msg_Err(p_dec, "Oops: No new buffer was returned!");
return NULL;
}
spp=opus_multistream_decode_float(p_sys->p_st, p_oggpacket->packet,
p_oggpacket->bytes, (float *)p_aout_buffer->p_buffer, spp, 0);
if( spp < 0 || i_nb_samples <= 0 || i_end_trim >= i_nb_samples)
{
block_Release(p_aout_buffer);
if( spp < 0 )
msg_Err( p_dec, "Error: corrupted stream?" );
return NULL;
}
if( spp > i_nb_samples )
{
memmove(p_aout_buffer->p_buffer,
p_aout_buffer->p_buffer
+ (spp - i_nb_samples)*p_sys->header.channels*sizeof(float),
(i_nb_samples - i_end_trim)*p_sys->header.channels*sizeof(float));
}
i_nb_samples -= i_end_trim;
#ifndef OPUS_SET_GAIN
if(p_sys->header.gain!=0)
{
float gain = pow(10., p_sys->header.gain/5120.);
float *buf =(float *)p_aout_buffer->p_buffer;
int i;
for( i = 0; i < i_nb_samples*p_sys->header.channels; i++)
buf[i] *= gain;
}
#endif
p_aout_buffer->i_nb_samples = i_nb_samples;
p_aout_buffer->i_pts = date_Get( &p_sys->end_date );
p_aout_buffer->i_length = date_Increment( &p_sys->end_date, i_nb_samples )
- p_aout_buffer->i_pts;
return p_aout_buffer;
}
/*****************************************************************************
* CloseDecoder: Opus decoder destruction
*****************************************************************************/
static void CloseDecoder( vlc_object_t *p_this )
{
decoder_t * p_dec = (decoder_t *)p_this;
decoder_sys_t *p_sys = p_dec->p_sys;
if( p_sys->p_st ) opus_multistream_decoder_destroy(p_sys->p_st);
free( p_sys );
}
/* Copyright (C)2012 Xiph.Org Foundation
File: opus_header.c
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.
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 FOUNDATION 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.
*/
#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)
{
if (p->pos>p->maxlen-nb_chars)
return 0;
for (int 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)
{
if (p->pos>p->maxlen-nb_chars)
return 0;
for (int 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)
{
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 (int 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)
{
Packet p;
unsigned char ch;
p.data = packet;
p.maxlen = len;
p.pos = 0;
if (len<19)