Merge branch 'features/upgrade-opusfile-0.5' of https://github.com/xhairball/ioq3 into opus_update

This commit is contained in:
Zack Middleton 2014-02-07 22:11:31 -06:00
commit 166f5ab90c
12 changed files with 2841 additions and 1013 deletions

View file

@ -242,7 +242,7 @@ JPDIR=$(MOUNT_DIR)/jpeg-8c
SPEEXDIR=$(MOUNT_DIR)/libspeex
OGGDIR=$(MOUNT_DIR)/libogg-1.3.1
OPUSDIR=$(MOUNT_DIR)/opus-1.0.2
OPUSFILEDIR=$(MOUNT_DIR)/opusfile-0.2
OPUSFILEDIR=$(MOUNT_DIR)/opusfile-0.5
ZDIR=$(MOUNT_DIR)/zlib
Q3ASMDIR=$(MOUNT_DIR)/tools/asm
LBURGDIR=$(MOUNT_DIR)/tools/lcc/lburg
@ -1955,7 +1955,8 @@ Q3OBJ += \
$(B)/client/info.o \
$(B)/client/internal.o \
$(B)/client/opusfile.o \
$(B)/client/stream.o
$(B)/client/stream.o \
$(B)/client/wincerts.o
endif
endif

View file

@ -1,286 +0,0 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012 *
* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
* *
********************************************************************/
#include "internal.h"
#include <limits.h>
#include <string.h>
static unsigned op_parse_uint16le(const unsigned char *_data){
return _data[0]|_data[1]<<8;
}
static int op_parse_int16le(const unsigned char *_data){
int ret;
ret=_data[0]|_data[1]<<8;
return (ret^0x8000)-0x8000;
}
static opus_uint32 op_parse_uint32le(const unsigned char *_data){
return _data[0]|_data[1]<<8|_data[2]<<16|_data[3]<<24;
}
int opus_head_parse(OpusHead *_head,const unsigned char *_data,size_t _len){
OpusHead head;
if(_len<8)return OP_ENOTFORMAT;
if(memcmp(_data,"OpusHead",8)!=0)return OP_ENOTFORMAT;
if(_len<9)return OP_EBADHEADER;
head.version=_data[8];
if(head.version>15)return OP_EVERSION;
if(_len<19)return OP_EBADHEADER;
head.channel_count=_data[9];
head.pre_skip=op_parse_uint16le(_data+10);
head.input_sample_rate=op_parse_uint32le(_data+12);
head.output_gain=op_parse_int16le(_data+16);
head.mapping_family=_data[18];
if(head.mapping_family==0){
if(head.channel_count<1||head.channel_count>2)return OP_EBADHEADER;
if(head.version<=1&&_len>19)return OP_EBADHEADER;
head.stream_count=1;
head.coupled_count=head.channel_count-1;
if(_head!=NULL){
_head->mapping[0]=0;
_head->mapping[1]=1;
}
}
else if(head.mapping_family==1){
size_t size;
int ci;
if(head.channel_count<1||head.channel_count>8)return OP_EBADHEADER;
size=21+head.channel_count;
if(_len<size||head.version<=1&&_len>size)return OP_EBADHEADER;
head.stream_count=_data[19];
if(head.stream_count<1)return OP_EBADHEADER;
head.coupled_count=_data[20];
if(head.coupled_count>head.stream_count)return OP_EBADHEADER;
for(ci=0;ci<head.channel_count;ci++){
if(_data[21+ci]>=head.stream_count+head.coupled_count
&&_data[21+ci]!=255){
return OP_EBADHEADER;
}
}
if(_head!=NULL)memcpy(_head->mapping,_data+21,head.channel_count);
}
/*General purpose players should not attempt to play back content with
channel mapping family 255.*/
else if(head.mapping_family==255)return OP_EIMPL;
/*No other channel mapping families are currently defined.*/
else return OP_EBADHEADER;
if(_head!=NULL)memcpy(_head,&head,head.mapping-(unsigned char *)&head);
return 0;
}
void opus_tags_init(OpusTags *_tags){
memset(_tags,0,sizeof(*_tags));
}
void opus_tags_clear(OpusTags *_tags){
int i;
for(i=_tags->comments;i-->0;)_ogg_free(_tags->user_comments[i]);
_ogg_free(_tags->user_comments);
_ogg_free(_tags->comment_lengths);
_ogg_free(_tags->vendor);
}
/*The actual implementation of opus_tags_parse().
Unlike the public API, this function requires _tags to already be
initialized, modifies its contents before success is guaranteed, and assumes
the caller will clear it on error.*/
int opus_tags_parse_impl(OpusTags *_tags,
const unsigned char *_data,size_t _len){
opus_uint32 count;
size_t size;
size_t len;
int ncomments;
int i;
len=_len;
if(len<8)return OP_ENOTFORMAT;
if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT;
if(len<16)return OP_EBADHEADER;
_data+=8;
len-=8;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
if(count>len)return OP_EBADHEADER;
if(_tags!=NULL){
char *vendor;
size=count+1;
if(size<count)return OP_EFAULT;
vendor=(char *)_ogg_malloc(size);
if(vendor==NULL)return OP_EFAULT;
memcpy(vendor,_data,count);
vendor[count]='\0';
_tags->vendor=vendor;
}
_data+=count;
len-=count;
if(len<4)return OP_EBADHEADER;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
/*Check to make sure there's minimally sufficient data left in the packet.*/
if(count>len>>2)return OP_EBADHEADER;
/*Check for overflow (the API limits this to an int).*/
if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT;
if(_tags!=NULL){
size=sizeof(*_tags->comment_lengths)*(count+1);
if(size/sizeof(*_tags->comment_lengths)!=count+1)return OP_EFAULT;
_tags->comment_lengths=(int *)_ogg_malloc(size);
size=sizeof(*_tags->user_comments)*(count+1);
if(size/sizeof(*_tags->user_comments)!=count+1)return OP_EFAULT;
_tags->user_comments=(char **)_ogg_malloc(size);
if(_tags->comment_lengths==NULL||_tags->user_comments==NULL){
return OP_EFAULT;
}
}
ncomments=(int)count;
for(i=0;i<ncomments;i++){
/*Check to make sure there's minimally sufficient data left in the packet.*/
if((size_t)(ncomments-i)>len>>2)return OP_EBADHEADER;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
if(count>len)return OP_EBADHEADER;
/*Check for overflow (the API limits this to an int).*/
if(count>(opus_uint32)INT_MAX)return OP_EFAULT;
if(_tags!=NULL){
_tags->comment_lengths[i]=(int)count;
size=count+1;
if(size<count)return OP_EFAULT;
_tags->user_comments[i]=(char *)_ogg_malloc(size);
if(_tags->user_comments[i]==NULL)return OP_EFAULT;
_tags->comments=i+1;
memcpy(_tags->user_comments[i],_data,count);
_tags->user_comments[i][count]='\0';
}
_data+=count;
len-=count;
}
if(_tags!=NULL){
_tags->user_comments[ncomments]=NULL;
_tags->comment_lengths[ncomments]=0;
}
return 0;
}
int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){
if(_tags!=NULL){
OpusTags tags;
int ret;
opus_tags_init(&tags);
ret=opus_tags_parse_impl(&tags,_data,_len);
if(ret<0)opus_tags_clear(&tags);
else *_tags=*&tags;
return ret;
}
else return opus_tags_parse_impl(NULL,_data,_len);
}
/*Add room for a new comment.*/
static int op_tags_add_prepare(OpusTags *_tags){
char **user_comments;
int *comment_lengths;
int ncomments;
ncomments=_tags->comments;
user_comments=_ogg_realloc(_tags->user_comments,
sizeof(*_tags->user_comments)*(ncomments+2));
if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
_tags->user_comments=user_comments;
comment_lengths=_ogg_realloc(_tags->comment_lengths,
sizeof(*_tags->comment_lengths)*(ncomments+2));
if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
_tags->comment_lengths=comment_lengths;
comment_lengths[ncomments]=comment_lengths[ncomments+1]=0;
/*Our caller will always set user_comments[ncomments].*/
user_comments[ncomments+1]=NULL;
return 0;
}
int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
char *comment;
int tag_len;
int value_len;
int ncomments;
int ret;
ret=op_tags_add_prepare(_tags);
if(OP_UNLIKELY(ret<0))return ret;
tag_len=strlen(_tag);
value_len=strlen(_value);
ncomments=_tags->comments;
/*+2 for '=' and '\0'.*/
_tags->user_comments[ncomments]=comment=
(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
_tags->comment_lengths[ncomments]=tag_len+value_len+1;
memcpy(comment,_tag,sizeof(*comment)*tag_len);
comment[tag_len]='=';
memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1));
return 0;
}
int opus_tags_add_comment(OpusTags *_tags,const char *_comment){
char *comment;
int ncomments;
int comment_len;
int ret;
ret=op_tags_add_prepare(_tags);
if(OP_UNLIKELY(ret<0))return ret;
comment_len=strlen(_comment);
ncomments=_tags->comments;
_tags->user_comments[ncomments]=comment=(char *)
_ogg_malloc(sizeof(*_tags->user_comments[ncomments])*(comment_len+1));
if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
_tags->comment_lengths[ncomments]=comment_len;
memcpy(comment,_comment,sizeof(*comment)*(comment_len+1));
return 0;
}
/*Is _a a "tag=value" comment whose tag matches _b?
0 if it is, a non-zero value otherwise.*/
static int op_tagcompare(const char *_a,const char *_b,int _n){
return op_strncasecmp(_a,_b,_n)||_a[_n]!='=';
}
const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
char **user_comments;
int tag_len;
int found;
int ncomments;
int ci;
tag_len=strlen(_tag);
ncomments=_tags->comments;
user_comments=_tags->user_comments;
found=0;
for(ci=0;ci<ncomments;ci++){
if(!op_tagcompare(user_comments[ci],_tag,tag_len)){
/*We return a pointer to the data, not a copy.*/
if(_count==found++)return user_comments[ci]+tag_len+1;
}
}
/*Didn't find anything.*/
return NULL;
}
int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
char **user_comments;
int tag_len;
int found;
int ncomments;
int ci;
tag_len=strlen(_tag);
ncomments=_tags->comments;
user_comments=_tags->user_comments;
found=0;
for(ci=0;ci<ncomments;ci++){
if(!op_tagcompare(user_comments[ci],_tag,tag_len))found++;
}
return found;
}

View file

@ -1,184 +0,0 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012 *
* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
* *
********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $
********************************************************************/
#include "internal.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
typedef struct OpusMemStream OpusMemStream;
#define OP_MEM_SIZE_MAX (~(size_t)0>>1)
#define OP_MEM_DIFF_MAX ((ptrdiff_t)OP_MEM_SIZE_MAX)
/*The context information needed to read from a block of memory as if it were a
file.*/
struct OpusMemStream{
/*The block of memory to read from.*/
const unsigned char *data;
/*The total size of the block.
This must be at most OP_MEM_SIZE_MAX to prevent signed overflow while
seeking.*/
ptrdiff_t size;
/*The current file position.
This is allowed to be set arbitrarily greater than size (i.e., past the end
of the block, though we will not read data past the end of the block), but
is not allowed to be negative (i.e., before the beginning of the block).*/
ptrdiff_t pos;
};
static int op_fread(void *_stream,unsigned char *_ptr,int _buf_size){
FILE *stream;
size_t ret;
/*Check for empty read.*/
if(_buf_size<=0)return 0;
stream=(FILE *)_stream;
ret=fread(_ptr,1,_buf_size,stream);
OP_ASSERT(ret<=(size_t)_buf_size);
/*If ret==0 and !feof(stream), there was a read error.*/
return ret>0||feof(stream)?(int)ret:OP_EREAD;
}
static int op_fseek(void *_stream,opus_int64 _offset,int _whence){
#if defined(__MINGW32__)
return fseeko64((FILE *)_stream,_offset,_whence);
#elif defined(_MSC_VER)
return _fseeki64((FILE *)_stream,_offset,_whence);
#else
return fseeko((FILE *)_stream,(off_t)_offset,_whence);
#endif
}
static opus_int64 op_ftell(void *_stream){
#if defined(__MINGW32__)
return ftello64((FILE *)_stream);
#elif defined(_MSC_VER)
return _ftelli64((FILE *)_stream);
#else
return ftello((FILE *)_stream);
#endif
}
static const OpusFileCallbacks OP_FILE_CALLBACKS={
op_fread,
op_fseek,
op_ftell,
(op_close_func)fclose
};
void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){
FILE *fp;
fp=fopen(_path,_mode);
if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
return fp;
}
void *op_fdopen(OpusFileCallbacks *_cb,int _fd,const char *_mode){
FILE *fp;
fp=fdopen(_fd,_mode);
if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
return fp;
}
void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode,
void *_stream){
FILE *fp;
fp=freopen(_path,_mode,(FILE *)_stream);
if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
return fp;
}
static int op_mem_read(void *_stream,unsigned char *_ptr,int _buf_size){
OpusMemStream *stream;
ptrdiff_t size;
ptrdiff_t pos;
stream=(OpusMemStream *)_stream;
/*Check for empty read.*/
if(_buf_size<=0)return 0;
size=stream->size;
pos=stream->pos;
/*Check for EOF.*/
if(pos>=size)return 0;
/*Check for a short read.*/
_buf_size=(int)OP_MAX(size-pos,_buf_size);
memcpy(_ptr,stream->data+pos,_buf_size);
pos+=_buf_size;
stream->pos=pos;
return _buf_size;
}
static int op_mem_seek(void *_stream,opus_int64 _offset,int _whence){
OpusMemStream *stream;
ptrdiff_t pos;
stream=(OpusMemStream *)_stream;
pos=stream->pos;
switch(_whence){
case SEEK_SET:{
/*Check for overflow:*/
if(_offset<0||_offset>OP_MEM_DIFF_MAX)return -1;
pos=(ptrdiff_t)_offset;
}break;
case SEEK_CUR:{
/*Check for overflow:*/
if(_offset<-pos||_offset>OP_MEM_DIFF_MAX-pos)return -1;
pos=(ptrdiff_t)(pos+_offset);
}break;
case SEEK_END:{
ptrdiff_t size;
size=stream->size;
OP_ASSERT(size>=0);
/*Check for overflow:*/
if(_offset>size||_offset<size-OP_MEM_DIFF_MAX)return -1;
pos=(ptrdiff_t)(size-_offset);
}break;
default:return -1;
}
stream->pos=pos;
return 0;
}
static opus_int64 op_mem_tell(void *_stream){
OpusMemStream *stream;
stream=(OpusMemStream *)_stream;
return (ogg_int64_t)stream->pos;
}
static int op_mem_close(void *_stream){
_ogg_free(_stream);
return 0;
}
static const OpusFileCallbacks OP_MEM_CALLBACKS={
op_mem_read,
op_mem_seek,
op_mem_tell,
op_mem_close
};
void *op_mem_stream_create(OpusFileCallbacks *_cb,
const unsigned char *_data,size_t _size){
OpusMemStream *stream;
if(_size>OP_MEM_SIZE_MAX)return NULL;
stream=(OpusMemStream *)_ogg_malloc(sizeof(*stream));
if(stream!=NULL){
*_cb=*&OP_MEM_CALLBACKS;
stream->data=_data;
stream->size=_size;
stream->pos=0;
}
return stream;
}

View file

@ -16,7 +16,6 @@
********************************************************************/
#if !defined(_opusfile_h)
# define _opusfile_h (1)
# include <stdarg.h>
/**\mainpage
\section Introduction
@ -50,17 +49,68 @@
Several additional sections are not tied to the main API.
- \ref stream_callbacks
- \ref header_info
- \ref error_codes*/
- \ref error_codes
\section Overview
The <tt>libopusfile</tt> API always decodes files to 48&nbsp;kHz.
The original sample rate is not preserved by the lossy compression, though
it is stored in the header to allow you to resample to it after decoding
(the <tt>libopusfile</tt> API does not currently provide a resampler,
but the
<a href="http://www.speex.org/docs/manual/speex-manual/node7.html#SECTION00760000000000000000">the
Speex resampler</a> is a good choice if you need one).
In general, if you are playing back the audio, you should leave it at
48&nbsp;kHz, provided your audio hardware supports it.
When decoding to a file, it may be worth resampling back to the original
sample rate, so as not to surprise users who might not expect the sample
rate to change after encoding to Opus and decoding.
Opus files can contain anywhere from 1 to 255 channels of audio.
The channel mappings for up to 8 channels are the same as the
<a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
mappings</a>.
A special stereo API can convert everything to 2 channels, making it simple
to support multichannel files in an application which only has stereo
output.
Although the <tt>libopusfile</tt> ABI provides support for the theoretical
maximum number of channels, the current implementation does not support
files with more than 8 channels, as they do not have well-defined channel
mappings.
Like all Ogg files, Opus files may be "chained".
That is, multiple Opus files may be combined into a single, longer file just
by concatenating the original files.
This is commonly done in internet radio streaming, as it allows the title
and artist to be updated each time the song changes, since each link in the
chain includes its own set of metadata.
<tt>libopusfile</tt> fully supports chained files.
It will decode the first Opus stream found in each link of a chained file
(ignoring any other streams that might be concurrently multiplexed with it,
such as a video stream).
The channel count can also change between links.
If your application is not prepared to deal with this, it can use the stereo
API to ensure the audio from all links will always get decoded into a
common format.
Since <tt>libopusfile</tt> always decodes to 48&nbsp;kHz, you do not have to
worry about the sample rate changing between links (as was possible with
Vorbis).
This makes application support for chained files with <tt>libopusfile</tt>
very easy.*/
# if defined(__cplusplus)
extern "C" {
# endif
# include <stdarg.h>
# include <stdio.h>
# include <ogg/ogg.h>
# include <opus_multistream.h>
/**@cond PRIVATE*/
/*Enable special features for gcc and gcc-compatible compilers.*/
# if !defined(OP_GNUC_PREREQ)
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
@ -75,9 +125,12 @@ extern "C" {
# pragma GCC visibility push(default)
# endif
typedef struct OpusHead OpusHead;
typedef struct OpusTags OpusTags;
typedef struct OggOpusFile OggOpusFile;
typedef struct OpusHead OpusHead;
typedef struct OpusTags OpusTags;
typedef struct OpusPictureTag OpusPictureTag;
typedef struct OpusServerInfo OpusServerInfo;
typedef struct OpusFileCallbacks OpusFileCallbacks;
typedef struct OggOpusFile OggOpusFile;
/*Warning attributes for libopusfile functions.*/
# if OP_GNUC_PREREQ(3,4)
@ -91,6 +144,8 @@ typedef struct OggOpusFile OggOpusFile;
# define OP_ARG_NONNULL(_x)
# endif
/**@endcond*/
/**\defgroup error_codes Error Codes*/
/*@{*/
/**\name List of possible error codes
@ -182,8 +237,9 @@ struct OpusHead{
opus_uint32 input_sample_rate;
/**The gain to apply to the decoded output, in dB, as a Q8 value in the range
-32768...32767.
The decoder will automatically scale the output by
pow(10,output_gain/(20.0*256)).*/
The <tt>libopusfile</tt> API will automatically apply this gain to the
decoded output before returning it, scaling it by
<code>pow(10,output_gain/(20.0*256))</code>.*/
int output_gain;
/**The channel mapping family, in the range 0...255.
Channel mapping family 0 covers mono or stereo in a single stream.
@ -253,6 +309,87 @@ struct OpusTags{
char *vendor;
};
/**\name Picture tag image formats*/
/*@{*/
/**The MIME type was not recognized, or the image data did not match the
declared MIME type.*/
#define OP_PIC_FORMAT_UNKNOWN (-1)
/**The MIME type indicates the image data is really a URL.*/
#define OP_PIC_FORMAT_URL (0)
/**The image is a JPEG.*/
#define OP_PIC_FORMAT_JPEG (1)
/**The image is a PNG.*/
#define OP_PIC_FORMAT_PNG (2)
/**The image is a GIF.*/
#define OP_PIC_FORMAT_GIF (3)
/*@}*/
/**The contents of a METADATA_BLOCK_PICTURE tag.*/
struct OpusPictureTag{
/**The picture type according to the ID3v2 APIC frame:
<ol start="0">
<li>Other</li>
<li>32x32 pixels 'file icon' (PNG only)</li>
<li>Other file icon</li>
<li>Cover (front)</li>
<li>Cover (back)</li>
<li>Leaflet page</li>
<li>Media (e.g. label side of CD)</li>
<li>Lead artist/lead performer/soloist</li>
<li>Artist/performer</li>
<li>Conductor</li>
<li>Band/Orchestra</li>
<li>Composer</li>
<li>Lyricist/text writer</li>
<li>Recording Location</li>
<li>During recording</li>
<li>During performance</li>
<li>Movie/video screen capture</li>
<li>A bright colored fish</li>
<li>Illustration</li>
<li>Band/artist logotype</li>
<li>Publisher/Studio logotype</li>
</ol>
Others are reserved and should not be used.
There may only be one each of picture type 1 and 2 in a file.*/
opus_int32 type;
/**The MIME type of the picture, in printable ASCII characters 0x20-0x7E.
The MIME type may also be <code>"-->"</code> to signify that the data part
is a URL pointing to the picture instead of the picture data itself.
In this case, a terminating NUL is appended to the URL string in #data,
but #data_length is set to the length of the string excluding that
terminating NUL.*/
char *mime_type;
/**The description of the picture, in UTF-8.*/
char *description;
/**The width of the picture in pixels.*/
opus_uint32 width;
/**The height of the picture in pixels.*/
opus_uint32 height;
/**The color depth of the picture in bits-per-pixel (<em>not</em>
bits-per-channel).*/
opus_uint32 depth;
/**For indexed-color pictures (e.g., GIF), the number of colors used, or 0
for non-indexed pictures.*/
opus_uint32 colors;
/**The length of the picture data in bytes.*/
opus_uint32 data_length;
/**The binary picture data.*/
unsigned char *data;
/**The format of the picture data, if known.
One of
<ul>
<li>#OP_PIC_FORMAT_UNKNOWN,</li>
<li>#OP_PIC_FORMAT_URL,</li>
<li>#OP_PIC_FORMAT_JPEG,</li>
<li>#OP_PIC_FORMAT_PNG, or</li>
<li>#OP_PIC_FORMAT_GIF.</li>
</ul>*/
int format;
};
/**\name Functions for manipulating header data
These functions manipulate the #OpusHead and #OpusTags structures,
@ -315,7 +452,7 @@ ogg_int64_t opus_granule_sample(const OpusHead *_head,ogg_int64_t _gp)
for validity.
\param[in] _data The contents of the 'comment' header packet.
\param _len The number of bytes of data in the 'info' header packet.
\retval 0 Success.
\retval 0 Success.
\retval #OP_ENOTFORMAT If the data does not start with the "OpusTags"
string.
\retval #OP_EBADHEADER If the contents of the packet otherwise violate the
@ -324,6 +461,15 @@ ogg_int64_t opus_granule_sample(const OpusHead *_head,ogg_int64_t _gp)
OP_WARN_UNUSED_RESULT int opus_tags_parse(OpusTags *_tags,
const unsigned char *_data,size_t _len) OP_ARG_NONNULL(2);
/**Performs a deep copy of an #OpusTags structure.
\param _dst The #OpusTags structure to copy into.
If this function fails, the contents of this structure remain
untouched.
\param _src The #OpusTags structure to copy from.
\retval 0 Success.
\retval #OP_EFAULT If there wasn't enough memory to copy the tags.*/
int opus_tags_copy(OpusTags *_dst,const OpusTags *_src) OP_ARG_NONNULL(1);
/**Initializes an #OpusTags structure.
This should be called on a freshly allocated #OpusTags structure before
attempting to use it.
@ -385,6 +531,24 @@ const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count)
int opus_tags_query_count(const OpusTags *_tags,const char *_tag)
OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
/**Get the track gain from an R128_TRACK_GAIN tag, if one was specified.
This searches for the first R128_TRACK_GAIN tag with a valid signed,
16-bit decimal integer value and returns the value.
This routine is exposed merely for convenience for applications which wish
to do something special with the track gain (i.e., display it).
If you simply wish to apply the track gain instead of the header gain, you
can use op_set_gain_offset() with an #OP_TRACK_GAIN type and no offset.
\param _tags An initialized #OpusTags structure.
\param[out] _gain_q8 The track gain, in 1/256ths of a dB.
This will lie in the range [-32768,32767], and should
be applied in <em>addition</em> to the header gain.
On error, no value is returned, and the previous
contents remain unchanged.
\return 0 on success, or a negative value on error.
\retval #OP_FALSE There was no track gain available in the given tags.*/
int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8)
OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
/**Clears the #OpusTags structure.
This should be called on an #OpusTags structure after it is no longer
needed.
@ -392,6 +556,78 @@ int opus_tags_query_count(const OpusTags *_tags,const char *_tag)
\param _tags The #OpusTags structure to clear.*/
void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
/**Check if \a _comment is an instance of a \a _tag_name tag.
\see opus_tagncompare
\param _tag_name A NUL-terminated, case-insensitive, ASCII string containing
the name of the tag to check for (without the terminating
'=' character).
\param _comment The comment string to check.
\return An integer less than, equal to, or greater than zero if \a _comment
is found respectively, to be less than, to match, or be greater
than a "tag=value" string whose tag matches \a _tag_name.*/
int opus_tagcompare(const char *_tag_name,const char *_comment);
/**Check if \a _comment is an instance of a \a _tag_name tag.
This version is slightly more efficient than opus_tagcompare() if the length
of the tag name is already known (e.g., because it is a constant).
\see opus_tagcompare
\param _tag_name A case-insensitive ASCII string containing the name of the
tag to check for (without the terminating '=' character).
\param _tag_len The number of characters in the tag name.
This must be non-negative.
\param _comment The comment string to check.
\return An integer less than, equal to, or greater than zero if \a _comment
is found respectively, to be less than, to match, or be greater
than a "tag=value" string whose tag matches the first \a _tag_len
characters of \a _tag_name.*/
int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment);
/**Parse a single METADATA_BLOCK_PICTURE tag.
This decodes the BASE64-encoded content of the tag and returns a structure
with the MIME type, description, image parameters (if known), and the
compressed image data.
If the MIME type indicates the presence of an image format we recognize
(JPEG, PNG, or GIF) and the actual image data contains the magic signature
associated with that format, then the OpusPictureTag::format field will be
set to the corresponding format.
This is provided as a convenience to avoid requiring applications to parse
the MIME type and/or do their own format detection for the commonly used
formats.
In this case, we also attempt to extract the image parameters directly from
the image data (overriding any that were present in the tag, which the
specification says applications are not meant to rely on).
The application must still provide its own support for actually decoding the
image data and, if applicable, retrieving that data from URLs.
\param[out] _pic Returns the parsed picture data.
No sanitation is done on the type, MIME type, or
description fields, so these might return invalid values.
The contents of this structure are left unmodified on
failure.
\param _tag The METADATA_BLOCK_PICTURE tag contents.
The leading "METADATA_BLOCK_PICTURE=" portion is optional,
to allow the function to be used on either directly on the
values in OpusTags::user_comments or on the return value
of opus_tags_query().
\return 0 on success or a negative value on error.
\retval #OP_ENOTFORMAT The METADATA_BLOCK_PICTURE contents were not valid.
\retval #OP_EFAULT There was not enough memory to store the picture tag
contents.*/
OP_WARN_UNUSED_RESULT int opus_picture_tag_parse(OpusPictureTag *_pic,
const char *_tag) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
/**Initializes an #OpusPictureTag structure.
This should be called on a freshly allocated #OpusPictureTag structure
before attempting to use it.
\param _pic The #OpusPictureTag structure to initialize.*/
void opus_picture_tag_init(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
/**Clears the #OpusPictureTag structure.
This should be called on an #OpusPictureTag structure after it is no longer
needed.
It will free all memory used by the structure members.
\param _pic The #OpusPictureTag structure to clear.*/
void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
/*@}*/
/*@}*/
@ -408,6 +644,8 @@ void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
They may be expanded in the future.*/
/*@{*/
/**@cond PRIVATE*/
/*These are the raw numbers used to define the request codes.
They should not be used directly.*/
#define OP_SSL_SKIP_CERTIFICATE_CHECK_REQUEST (6464)
@ -415,6 +653,7 @@ void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
#define OP_HTTP_PROXY_PORT_REQUEST (6592)
#define OP_HTTP_PROXY_USER_REQUEST (6656)
#define OP_HTTP_PROXY_PASS_REQUEST (6720)
#define OP_GET_SERVER_INFO_REQUEST (6784)
#define OP_URL_OPT(_request) ((_request)+(char *)0)
@ -422,6 +661,64 @@ void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
provided to one of the URL options.*/
#define OP_CHECK_INT(_x) ((void)((_x)==(opus_int32)0),(opus_int32)(_x))
#define OP_CHECK_CONST_CHAR_PTR(_x) ((_x)+((_x)-(const char *)(_x)))
#define OP_CHECK_SERVER_INFO_PTR(_x) ((_x)+((_x)-(OpusServerInfo *)(_x)))
/**@endcond*/
/**HTTP/Shoutcast/Icecast server information associated with a URL.*/
struct OpusServerInfo{
/**The name of the server (icy-name/ice-name).
This is <code>NULL</code> if there was no <code>icy-name</code> or
<code>ice-name</code> header.*/
char *name;
/**A short description of the server (icy-description/ice-description).
This is <code>NULL</code> if there was no <code>icy-description</code> or
<code>ice-description</code> header.*/
char *description;
/**The genre the server falls under (icy-genre/ice-genre).
This is <code>NULL</code> if there was no <code>icy-genre</code> or
<code>ice-genre</code> header.*/
char *genre;
/**The homepage for the server (icy-url/ice-url).
This is <code>NULL</code> if there was no <code>icy-url</code> or
<code>ice-url</code> header.*/
char *url;
/**The software used by the origin server (Server).
This is <code>NULL</code> if there was no <code>Server</code> header.*/
char *server;
/**The media type of the entity sent to the recepient (Content-Type).
This is <code>NULL</code> if there was no <code>Content-Type</code>
header.*/
char *content_type;
/**The nominal stream bitrate in kbps (icy-br/ice-bitrate).
This is <code>-1</code> if there was no <code>icy-br</code> or
<code>ice-bitrate</code> header.*/
opus_int32 bitrate_kbps;
/**Flag indicating whether the server is public (<code>1</code>) or not
(<code>0</code>) (icy-pub/ice-public).
This is <code>-1</code> if there was no <code>icy-pub</code> or
<code>ice-public</code> header.*/
int is_public;
/**Flag indicating whether the server is using HTTPS instead of HTTP.
This is <code>0</code> unless HTTPS is being used.
This may not match the protocol used in the original URL if there were
redirections.*/
int is_ssl;
};
/**Initializes an #OpusServerInfo structure.
All fields are set as if the corresponding header was not available.
\param _info The #OpusServerInfo structure to initialize.
\note If you use this function, you must link against <tt>libopusurl</tt>.*/
void opus_server_info_init(OpusServerInfo *_info) OP_ARG_NONNULL(1);
/**Clears the #OpusServerInfo structure.
This should be called on an #OpusServerInfo structure after it is no longer
needed.
It will free all memory used by the structure members.
\param _info The #OpusServerInfo structure to clear.
\note If you use this function, you must link against <tt>libopusurl</tt>.*/
void opus_server_info_clear(OpusServerInfo *_info) OP_ARG_NONNULL(1);
/**Skip the certificate check when connecting via TLS/SSL (https).
\param _b <code>opus_int32</code>: Whether or not to skip the certificate
@ -467,7 +764,7 @@ void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
arguments.
\hideinitializer*/
#define OP_HTTP_PROXY_USER(_user) \
OP_URL_OPT(OP_HTTP_PROXY_USER_REQUEST),OP_CHECK_CONST_CHAR_PTR(_host)
OP_URL_OPT(OP_HTTP_PROXY_USER_REQUEST),OP_CHECK_CONST_CHAR_PTR(_user)
/**Use the given password for authentication when proxying connections.
All proxy parameters are ignored for non-http and non-https URLs.
@ -480,7 +777,28 @@ void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
arguments.
\hideinitializer*/
#define OP_HTTP_PROXY_PASS(_pass) \
OP_URL_OPT(OP_HTTP_PROXY_PASS_REQUEST),OP_CHECK_CONST_CHAR_PTR(_host)
OP_URL_OPT(OP_HTTP_PROXY_PASS_REQUEST),OP_CHECK_CONST_CHAR_PTR(_pass)
/**Parse information about the streaming server (if any) and return it.
Very little validation is done.
In particular, OpusServerInfo::url may not be a valid URL,
OpusServerInfo::bitrate_kbps may not really be in kbps, and
OpusServerInfo::content_type may not be a valid MIME type.
The character set of the string fields is not specified anywhere, and should
not be assumed to be valid UTF-8.
\param _info OpusServerInfo *: Returns information about the server.
If there is any error opening the stream, the
contents of this structure remain
unmodified.
On success, fills in the structure with the
server information that was available, if
any.
After a successful return, the contents of
this structure should be freed by calling
opus_server_info_clear().
\hideinitializer*/
#define OP_GET_SERVER_INFO(_info) \
OP_URL_OPT(OP_GET_SERVER_INFO_REQUEST),OP_CHECK_SERVER_INFO_PTR(_info)
/*@}*/
/*@}*/
@ -497,8 +815,6 @@ void opus_tags_clear(OpusTags *_tags) OP_ARG_NONNULL(1);
block of memory, or URLs.*/
/*@{*/
typedef struct OpusFileCallbacks OpusFileCallbacks;
/**Reads up to \a _nbytes bytes of data from \a _stream.
\param _stream The stream to read from.
\param[out] _ptr The buffer to store the data in.
@ -564,6 +880,10 @@ struct OpusFileCallbacks{
If there is an error opening the file, nothing will be
filled in here.
\param _path The path to the file to open.
On Windows, this string must be UTF-8 (to allow access to
files whose names cannot be represented in the current
MBCS code page).
All other systems use the native character encoding.
\param _mode The mode to open the file in.
\return A stream handle to use with the callbacks, or <code>NULL</code> on
error.*/
@ -597,6 +917,10 @@ OP_WARN_UNUSED_RESULT void *op_fdopen(OpusFileCallbacks *_cb,
If there is an error opening the file, nothing will be
filled in here.
\param _path The path to the file to open.
On Windows, this string must be UTF-8 (to allow access
to files whose names cannot be represented in the
current MBCS code page).
All other systems use the native character encoding.
\param _mode The mode to open the file in.
\param _stream A stream previously returned by op_fopen(), op_fdopen(),
or op_freopen().
@ -624,6 +948,7 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
takes a va_list instead of a variable number of arguments.
It does not call the <code>va_end</code> macro, and because it invokes the
<code>va_arg</code> macro, the value of \a _ap is undefined after the call.
\note If you use this function, you must link against <tt>libopusurl</tt>.
\param[out] _cb The callbacks to use for this stream.
If there is an error creating the stream, nothing will
be filled in here.
@ -632,6 +957,10 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
schemes are supported.
Both <http:> and <https:> may be disabled at compile
time, in which case opening such URLs will always fail.
Currently this only supports URIs.
IRIs should be converted to UTF-8 and URL-escaped, with
internationalized domain names encoded in punycode,
before passing them to this function.
\param[in,out] _ap A list of the \ref url_options "optional flags" to use.
This is a variable-length list of options terminated
with <code>NULL</code>.
@ -640,7 +969,8 @@ OP_WARN_UNUSED_RESULT void *op_mem_stream_create(OpusFileCallbacks *_cb,
OP_WARN_UNUSED_RESULT void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
const char *_url,va_list _ap) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
/**Creates a stream that reads from the given URL using the specified proxy.
/**Creates a stream that reads from the given URL.
\note If you use this function, you must link against <tt>libopusurl</tt>.
\param[out] _cb The callbacks to use for this stream.
If there is an error creating the stream, nothing will be
filled in here.
@ -649,6 +979,10 @@ OP_WARN_UNUSED_RESULT void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
are supported.
Both <http:> and <https:> may be disabled at compile time,
in which case opening such URLs will always fail.
Currently this only supports URIs.
IRIs should be converted to UTF-8 and URL-escaped, with
internationalized domain names encoded in punycode, before
passing them to this function.
\param ... The \ref url_options "optional flags" to use.
This is a variable-length list of options terminated with
<code>NULL</code>.
@ -731,12 +1065,17 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_memory(const unsigned char *_data,
takes a va_list instead of a variable number of arguments.
It does not call the <code>va_end</code> macro, and because it invokes the
<code>va_arg</code> macro, the value of \a _ap is undefined after the call.
\note If you use this function, you must link against <tt>libopusurl</tt>.
\param _url The URL to open.
Currently only the <file:>, <http:>, and <https:>
schemes are supported.
Both <http:> and <https:> may be disabled at compile
time, in which case opening such URLs will always
fail.
Currently this only supports URIs.
IRIs should be converted to UTF-8 and URL-escaped,
with internationalized domain names encoded in
punycode, before passing them to this function.
\param[out] _error Returns 0 on success, or a failure code on error.
You may pass in <code>NULL</code> if you don't want
the failure code.
@ -751,13 +1090,16 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_vopen_url(const char *_url,
int *_error,va_list _ap) OP_ARG_NONNULL(1);
/**Open a stream from a URL.
However, this approach will not work for live streams or HTTP/1.0 servers
(which do not support Range requets).
\note If you use this function, you must link against <tt>libopusurl</tt>.
\param _url The URL to open.
Currently only the <file:>, <http:>, and <https:> schemes
are supported.
Both <http:> and <https:> may be disabled at compile
time, in which case opening such URLs will always fail.
Currently this only supports URIs.
IRIs should be converted to UTF-8 and URL-escaped, with
internationalized domain names encoded in punycode,
before passing them to this function.
\param[out] _error Returns 0 on success, or a failure code on error.
You may pass in <code>NULL</code> if you don't want the
failure code.
@ -841,7 +1183,11 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_url(const char *_url,
<dd>The first or last timestamp in a link failed
basic validity checks.</dd>
</dl>
\return A freshly opened \c OggOpusFile, or <code>NULL</code> on error.*/
\return A freshly opened \c OggOpusFile, or <code>NULL</code> on error.
<tt>libopusfile</tt> does <em>not</em> take ownership of the source
if the call fails.
The calling application is responsible for closing the source if
this call returns an error.*/
OP_WARN_UNUSED_RESULT OggOpusFile *op_open_callbacks(void *_source,
const OpusFileCallbacks *_cb,const unsigned char *_initial_data,
size_t _initial_bytes,int *_error) OP_ARG_NONNULL(2);
@ -876,6 +1222,7 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_memory(const unsigned char *_data,
takes a va_list instead of a variable number of arguments.
It does not call the <code>va_end</code> macro, and because it invokes the
<code>va_arg</code> macro, the value of \a _ap is undefined after the call.
\note If you use this function, you must link against <tt>libopusurl</tt>.
\see op_test_url
\see op_test_callbacks
\param _url The URL to open.
@ -884,6 +1231,10 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_memory(const unsigned char *_data,
Both <http:> and <https:> may be disabled at compile
time, in which case opening such URLs will always
fail.
Currently this only supports URIs.
IRIs should be converted to UTF-8 and URL-escaped,
with internationalized domain names encoded in
punycode, before passing them to this function.
\param[out] _error Returns 0 on success, or a failure code on error.
You may pass in <code>NULL</code> if you don't want
the failure code.
@ -898,12 +1249,17 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_vtest_url(const char *_url,
int *_error,va_list _ap) OP_ARG_NONNULL(1);
/**Partially open a stream from a URL.
\note If you use this function, you must link against <tt>libopusurl</tt>.
\see op_test_callbacks
\param _url The URL to open.
Currently only the <file:>, <http:>, and <https:>
schemes are supported.
Both <http:> and <https:> may be disabled at compile
time, in which case opening such URLs will always fail.
Currently this only supports URIs.
IRIs should be converted to UTF-8 and URL-escaped, with
internationalized domain names encoded in punycode,
before passing them to this function.
\param[out] _error Returns 0 on success, or a failure code on error.
You may pass in <code>NULL</code> if you don't want the
failure code.
@ -974,7 +1330,11 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_url(const char *_url,
the failure code.
See op_open_callbacks() for a full list of failure
codes.
\return A partially opened \c OggOpusFile, or <code>NULL</code> on error.*/
\return A partially opened \c OggOpusFile, or <code>NULL</code> on error.
<tt>libopusfile</tt> does <em>not</em> take ownership of the source
if the call fails.
The calling application is responsible for closing the source if
this call returns an error.*/
OP_WARN_UNUSED_RESULT OggOpusFile *op_test_callbacks(void *_source,
const OpusFileCallbacks *_cb,const unsigned char *_initial_data,
size_t _initial_bytes,int *_error) OP_ARG_NONNULL(2);
@ -1046,7 +1406,7 @@ void op_free(OggOpusFile *_of);
This function may be called on partially-opened streams.
\param _of The \c OggOpusFile whose seekable status is to be returned.
\return A non-zero value if seekable, and 0 if unseekable.*/
int op_seekable(OggOpusFile *_of) OP_ARG_NONNULL(1);
int op_seekable(const OggOpusFile *_of) OP_ARG_NONNULL(1);
/**Returns the number of links in this chained stream.
This function may be called on partially-opened streams, but it will always
@ -1054,9 +1414,9 @@ int op_seekable(OggOpusFile *_of) OP_ARG_NONNULL(1);
The actual number of links is not known until the stream is fully opened.
\param _of The \c OggOpusFile from which to retrieve the link count.
\return For fully-open seekable sources, this returns the total number of
links in the whole stream.
links in the whole stream, which will be at least 1.
For partially-open or unseekable sources, this always returns 1.*/
int op_link_count(OggOpusFile *_of) OP_ARG_NONNULL(1);
int op_link_count(const OggOpusFile *_of) OP_ARG_NONNULL(1);
/**Get the serial number of the given link in a (possibly-chained) Ogg Opus
stream.
@ -1071,7 +1431,7 @@ int op_link_count(OggOpusFile *_of) OP_ARG_NONNULL(1);
the serial number of the last link.
If the source is not seekable, this always returns the serial number
of the current link.*/
opus_uint32 op_serialno(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
opus_uint32 op_serialno(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
/**Get the channel count of the given link in a (possibly-chained) Ogg Opus
stream.
@ -1088,7 +1448,7 @@ opus_uint32 op_serialno(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
the channel count of the last link.
If the source is not seekable, this always returns the channel count
of the current link.*/
int op_channel_count(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
int op_channel_count(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
/**Get the total (compressed) size of the stream, or of an individual link in
a (possibly-chained) Ogg Opus stream, including all headers and Ogg muxing
@ -1106,7 +1466,7 @@ int op_channel_count(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
\retval #OP_EINVAL The source is not seekable (so we can't know the length),
\a _li wasn't less than the total number of links in
the stream, or the stream was only partially open.*/
opus_int64 op_raw_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
opus_int64 op_raw_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
/**Get the total PCM length (number of samples at 48 kHz) of the stream, or of
an individual link in a (possibly-chained) Ogg Opus stream.
@ -1124,7 +1484,7 @@ opus_int64 op_raw_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
\retval #OP_EINVAL The source is not seekable (so we can't know the length),
\a _li wasn't less than the total number of links in
the stream, or the stream was only partially open.*/
ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
ogg_int64_t op_pcm_total(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
/**Get the ID header information for the given link in a (possibly chained) Ogg
Opus stream.
@ -1140,7 +1500,7 @@ ogg_int64_t op_pcm_total(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
information for the current link is always returned, if
available.
\return The contents of the ID header for the given link.*/
const OpusHead *op_head(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
const OpusHead *op_head(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
/**Get the comment header information for the given link in a (possibly
chained) Ogg Opus stream.
@ -1158,7 +1518,7 @@ const OpusHead *op_head(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
\return The contents of the comment header for the given link, or
<code>NULL</code> if this is an unseekable stream that encountered
an invalid link.*/
const OpusTags *op_tags(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
const OpusTags *op_tags(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
/**Retrieve the index of the current link.
This is the link that produced the data most recently read by
@ -1175,7 +1535,7 @@ const OpusTags *op_tags(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
each time a new link is encountered (even though op_link_count()
always returns 1).
\retval #OP_EINVAL The stream was only partially open.*/
int op_current_link(OggOpusFile *_of) OP_ARG_NONNULL(1);
int op_current_link(const OggOpusFile *_of) OP_ARG_NONNULL(1);
/**Computes the bitrate for a given link in a (possibly chained) Ogg Opus
stream.
@ -1188,7 +1548,7 @@ int op_current_link(OggOpusFile *_of) OP_ARG_NONNULL(1);
\retval #OP_EINVAL The stream was only partially open, the stream was not
seekable, or \a _li was larger than the number of
links.*/
opus_int32 op_bitrate(OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
opus_int32 op_bitrate(const OggOpusFile *_of,int _li) OP_ARG_NONNULL(1);
/**Compute the instantaneous bitrate, measured as the ratio of bits to playable
samples decoded since a) the last call to op_bitrate_instant(), b) the last
@ -1207,7 +1567,7 @@ opus_int32 op_bitrate_instant(OggOpusFile *_of) OP_ARG_NONNULL(1);
\param _of The \c OggOpusFile from which to retrieve the position indicator.
\return The byte position that is currently being read from.
\retval #OP_EINVAL The stream was only partially open.*/
opus_int64 op_raw_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
opus_int64 op_raw_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
/**Obtain the PCM offset of the next sample to be read.
If the stream is not properly timestamped, this might not increment by the
@ -1216,7 +1576,7 @@ opus_int64 op_raw_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
\param _of The \c OggOpusFile from which to retrieve the PCM offset.
\return The PCM offset of the next sample to be read.
\retval #OP_EINVAL The stream was only partially open.*/
ogg_int64_t op_pcm_tell(OggOpusFile *_of) OP_ARG_NONNULL(1);
ogg_int64_t op_pcm_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
/*@}*/
/*@}*/
@ -1303,11 +1663,12 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
clipping and other issues which might be avoided entirely if, e.g., you
scale down the volume at some other stage.
However, if you intend to direct consume 16-bit samples, the conversion in
<tt>libopusfile</tt> provides noise-shaping dithering API.
<tt>libopusfile</tt> provides noise-shaping dithering and, if compiled
against <tt>libopus</tt>&nbsp;1.1 or later, soft-clipping prevention.
<tt>libopusfile</tt> can also be configured at compile time to use the
fixed-point <tt>libopus</tt> API.
If so, the floating-point API may also be disabled.
If so, <tt>libopusfile</tt>'s floating-point API may also be disabled.
In that configuration, nothing in <tt>libopusfile</tt> will use any
floating-point operations, to simplify support on devices without an
adequate FPU.
@ -1316,20 +1677,125 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
not check the error return code from op_read_float() or its associated
functions.
If the remote peer does not close the connection gracefully (with a TLS
"close notify" message), these functions will return OP_EREAD instead of 0
"close notify" message), these functions will return #OP_EREAD instead of 0
when they reach the end of the file.
If you are reading from an <https:> URL (particularly if seeking is not
supported), you should make sure to check for this error and warn the user
appropriately.*/
/*@{*/
/**Indicates that the decoding callback should produce signed 16-bit
native-endian output samples.*/
#define OP_DEC_FORMAT_SHORT (7008)
/**Indicates that the decoding callback should produce 32-bit native-endian
float samples.*/
#define OP_DEC_FORMAT_FLOAT (7040)
/**Indicates that the decoding callback did not decode anything, and that
<tt>libopusfile</tt> should decode normally instead.*/
#define OP_DEC_USE_DEFAULT (6720)
/**Called to decode an Opus packet.
This should invoke the functional equivalent of opus_multistream_decode() or
opus_multistream_decode_float(), except that it returns 0 on success
instead of the number of decoded samples (which is known a priori).
\param _ctx The application-provided callback context.
\param _decoder The decoder to use to decode the packet.
\param[out] _pcm The buffer to decode into.
This will always have enough room for \a _nchannels of
\a _nsamples samples, which should be placed into this
buffer interleaved.
\param _op The packet to decode.
This will always have its granule position set to a valid
value.
\param _nsamples The number of samples expected from the packet.
\param _nchannels The number of channels expected from the packet.
\param _format The desired sample output format.
This is either #OP_DEC_FORMAT_SHORT or
#OP_DEC_FORMAT_FLOAT.
\param _li The index of the link from which this packet was decoded.
\return A non-negative value on success, or a negative value on error.
The error codes should be the same as those returned by
opus_multistream_decode() or opus_multistream_decode_float().
\retval 0 Decoding was successful.
The application has filled the buffer with
exactly <code>\a _nsamples*\a
_nchannels</code> samples in the requested
format.
\retval #OP_DEC_USE_DEFAULT No decoding was done.
<tt>libopusfile</tt> should decode normally
instead.*/
typedef int (*op_decode_cb_func)(void *_ctx,OpusMSDecoder *_decoder,void *_pcm,
const ogg_packet *_op,int _nsamples,int _nchannels,int _format,int _li);
/**Sets the packet decode callback function.
This is called once for each packet that needs to be decoded.
A call to this function is no guarantee that the audio will eventually be
delivered to the application.
Some or all of the data from the packet may be discarded (i.e., at the
beginning or end of a link, or after a seek), however the callback is
required to provide all of it.
\param _of The \c OggOpusFile on which to set the decode callback.
\param _decode_cb The callback function to call.
This may be <code>NULL</code> to disable calling the
callback.
\param _ctx The application-provided context pointer to pass to the
callback on each call.*/
void op_set_decode_callback(OggOpusFile *_of,
op_decode_cb_func _decode_cb,void *_ctx) OP_ARG_NONNULL(1);
/**Gain offset type that indicates that the provided offset is relative to the
header gain.
This is the default.*/
#define OP_HEADER_GAIN (0)
/**Gain offset type that indicates that the provided offset is relative to the
R128_TRACK_GAIN value (if any), in addition to the header gain.*/
#define OP_TRACK_GAIN (3008)
/**Gain offset type that indicates that the provided offset should be used as
the gain directly, without applying any the header or track gains.*/
#define OP_ABSOLUTE_GAIN (3009)
/**Sets the gain to be used for decoded output.
By default, the gain in the header is applied with no additional offset.
The total gain (including header gain and/or track gain, if applicable, and
this offset), will be clamped to [-32768,32767]/256 dB.
This is more than enough to saturate or underflow 16-bit PCM.
\note The new gain will not be applied to any already buffered, decoded
output.
This means you cannot change it sample-by-sample, as at best it will be
updated packet-by-packet.
It is meant for setting a target volume level, rather than applying smooth
fades, etc.
\param _of The \c OggOpusFile on which to set the gain offset.
\param _gain_type One of #OP_HEADER_GAIN, #OP_TRACK_GAIN, or
#OP_ABSOLUTE_GAIN.
\param _gain_offset_q8 The gain offset to apply, in 1/256ths of a dB.
\return 0 on success or a negative value on error.
\retval #OP_EINVAL The \a _gain_type was unrecognized.*/
int op_set_gain_offset(OggOpusFile *_of,
int _gain_type,opus_int32 _gain_offset_q8) OP_ARG_NONNULL(1);
/**Sets whether or not dithering is enabled for 16-bit decoding.
By default, when <tt>libopusfile</tt> is compiled to use floating-point
internally, calling op_read() or op_read_stereo() will first decode to
float, and then convert to fixed-point using noise-shaping dithering.
This flag can be used to disable that dithering.
When the application uses op_read_float() or op_read_float_stereo(), or when
the library has been compiled to decode directly to fixed point, this flag
has no effect.
\param _of The \c OggOpusFile on which to enable or disable dithering.
\param _enabled A non-zero value to enable dithering, or 0 to disable it.*/
void op_set_dither_enabled(OggOpusFile *_of,int _enabled) OP_ARG_NONNULL(1);
/**Reads more samples from the stream.
\note Although \a _buf_size must indicate the total number of values that
can be stored in \a _pcm, the return value is the number of samples
<em>per channel</em>.
This is done because
<ol>
<li>The channel count cannot be known a prior (reading more samples might
<li>The channel count cannot be known a priori (reading more samples might
advance us into the next link, with a different channel count), so
\a _buf_size cannot also be in units of samples per channel,</li>
<li>Returning the samples per channel matches the <code>libopus</code> API
@ -1346,14 +1812,14 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
</ol>
\param _of The \c OggOpusFile from which to read.
\param[out] _pcm A buffer in which to store the output PCM samples, as
signed native-endian 16-bit values with a nominal
range of <code>[-32768,32767)</code>.
signed native-endian 16-bit values at 48&nbsp;kHz
with a nominal range of <code>[-32768,32767)</code>.
Multiple channels are interleaved using the
<a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
channel ordering</a>.
This must have room for at least \a _buf_size values.
\param _buf_size The number of values that can be stored in \a _pcm.
It is reccommended that this be large enough for at
It is recommended that this be large enough for at
least 120 ms of data at 48 kHz per channel (5760
values per channel).
Smaller buffers will simply return less data, possibly
@ -1411,7 +1877,7 @@ OP_WARN_UNUSED_RESULT int op_read(OggOpusFile *_of,
can be stored in \a _pcm, the return value is the number of samples
<em>per channel</em>.
<ol>
<li>The channel count cannot be known a prior (reading more samples might
<li>The channel count cannot be known a priori (reading more samples might
advance us into the next link, with a different channel count), so
\a _buf_size cannot also be in units of samples per channel,</li>
<li>Returning the samples per channel matches the <code>libopus</code> API
@ -1428,14 +1894,14 @@ OP_WARN_UNUSED_RESULT int op_read(OggOpusFile *_of,
</ol>
\param _of The \c OggOpusFile from which to read.
\param[out] _pcm A buffer in which to store the output PCM samples as
signed floats with a nominal range of
signed floats at 48&nbsp;kHz with a nominal range of
<code>[-1.0,1.0]</code>.
Multiple channels are interleaved using the
<a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
channel ordering</a>.
This must have room for at least \a _buf_size floats.
\param _buf_size The number of floats that can be stored in \a _pcm.
It is reccommended that this be large enough for at
It is recommended that this be large enough for at
least 120 ms of data at 48 kHz per channel (5760
samples per channel).
Smaller buffers will simply return less data, possibly
@ -1497,13 +1963,13 @@ OP_WARN_UNUSED_RESULT int op_read_float(OggOpusFile *_of,
op_read().
\param _of The \c OggOpusFile from which to read.
\param[out] _pcm A buffer in which to store the output PCM samples, as
signed native-endian 16-bit values with a nominal
range of <code>[-32768,32767)</code>.
signed native-endian 16-bit values at 48&nbsp;kHz
with a nominal range of <code>[-32768,32767)</code>.
The left and right channels are interleaved in the
buffer.
This must have room for at least \a _buf_size values.
\param _buf_size The number of values that can be stored in \a _pcm.
It is reccommended that this be large enough for at
It is recommended that this be large enough for at
least 120 ms of data at 48 kHz per channel (11520
values total).
Smaller buffers will simply return less data, possibly
@ -1558,13 +2024,13 @@ OP_WARN_UNUSED_RESULT int op_read_stereo(OggOpusFile *_of,
op_read_float().
\param _of The \c OggOpusFile from which to read.
\param[out] _pcm A buffer in which to store the output PCM samples, as
signed floats with a nominal range of
signed floats at 48&nbsp;kHz with a nominal range of
<code>[-1.0,1.0]</code>.
The left and right channels are interleaved in the
buffer.
This must have room for at least \a _buf_size values.
\param _buf_size The number of values that can be stored in \a _pcm.
It is reccommended that this be large enough for at
It is recommended that this be large enough for at
least 120 ms of data at 48 kHz per channel (11520
values total).
Smaller buffers will simply return less data, possibly

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,683 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012 *
* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
* *
********************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#include <limits.h>
#include <string.h>
static unsigned op_parse_uint16le(const unsigned char *_data){
return _data[0]|_data[1]<<8;
}
static int op_parse_int16le(const unsigned char *_data){
int ret;
ret=_data[0]|_data[1]<<8;
return (ret^0x8000)-0x8000;
}
static opus_uint32 op_parse_uint32le(const unsigned char *_data){
return _data[0]|_data[1]<<8|_data[2]<<16|_data[3]<<24;
}
static opus_uint32 op_parse_uint32be(const unsigned char *_data){
return _data[3]|_data[2]<<8|_data[1]<<16|_data[0]<<24;
}
int opus_head_parse(OpusHead *_head,const unsigned char *_data,size_t _len){
OpusHead head;
if(_len<8)return OP_ENOTFORMAT;
if(memcmp(_data,"OpusHead",8)!=0)return OP_ENOTFORMAT;
if(_len<9)return OP_EBADHEADER;
head.version=_data[8];
if(head.version>15)return OP_EVERSION;
if(_len<19)return OP_EBADHEADER;
head.channel_count=_data[9];
head.pre_skip=op_parse_uint16le(_data+10);
head.input_sample_rate=op_parse_uint32le(_data+12);
head.output_gain=op_parse_int16le(_data+16);
head.mapping_family=_data[18];
if(head.mapping_family==0){
if(head.channel_count<1||head.channel_count>2)return OP_EBADHEADER;
if(head.version<=1&&_len>19)return OP_EBADHEADER;
head.stream_count=1;
head.coupled_count=head.channel_count-1;
if(_head!=NULL){
_head->mapping[0]=0;
_head->mapping[1]=1;
}
}
else if(head.mapping_family==1){
size_t size;
int ci;
if(head.channel_count<1||head.channel_count>8)return OP_EBADHEADER;
size=21+head.channel_count;
if(_len<size||head.version<=1&&_len>size)return OP_EBADHEADER;
head.stream_count=_data[19];
if(head.stream_count<1)return OP_EBADHEADER;
head.coupled_count=_data[20];
if(head.coupled_count>head.stream_count)return OP_EBADHEADER;
for(ci=0;ci<head.channel_count;ci++){
if(_data[21+ci]>=head.stream_count+head.coupled_count
&&_data[21+ci]!=255){
return OP_EBADHEADER;
}
}
if(_head!=NULL)memcpy(_head->mapping,_data+21,head.channel_count);
}
/*General purpose players should not attempt to play back content with
channel mapping family 255.*/
else if(head.mapping_family==255)return OP_EIMPL;
/*No other channel mapping families are currently defined.*/
else return OP_EBADHEADER;
if(_head!=NULL)memcpy(_head,&head,head.mapping-(unsigned char *)&head);
return 0;
}
void opus_tags_init(OpusTags *_tags){
memset(_tags,0,sizeof(*_tags));
}
void opus_tags_clear(OpusTags *_tags){
int ci;
for(ci=_tags->comments;ci-->0;)_ogg_free(_tags->user_comments[ci]);
_ogg_free(_tags->user_comments);
_ogg_free(_tags->comment_lengths);
_ogg_free(_tags->vendor);
}
/*Ensure there's room for up to _ncomments comments.*/
static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
char **user_comments;
int *comment_lengths;
size_t size;
if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size);
if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
comment_lengths[_ncomments]=0;
_tags->comment_lengths=comment_lengths;
size=sizeof(*_tags->user_comments)*(_ncomments+1);
if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT;
user_comments=(char **)_ogg_realloc(_tags->user_comments,size);
if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
user_comments[_ncomments]=NULL;
_tags->user_comments=user_comments;
return 0;
}
/*Duplicate a (possibly non-NUL terminated) string with a known length.*/
static char *op_strdup_with_len(const char *_s,size_t _len){
size_t size;
char *ret;
size=sizeof(*ret)*(_len+1);
if(OP_UNLIKELY(size<_len))return NULL;
ret=(char *)_ogg_malloc(size);
if(OP_LIKELY(ret!=NULL)){
ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len);
ret[_len]='\0';
}
return ret;
}
/*The actual implementation of opus_tags_parse().
Unlike the public API, this function requires _tags to already be
initialized, modifies its contents before success is guaranteed, and assumes
the caller will clear it on error.*/
static int opus_tags_parse_impl(OpusTags *_tags,
const unsigned char *_data,size_t _len){
opus_uint32 count;
size_t len;
int ncomments;
int ci;
len=_len;
if(len<8)return OP_ENOTFORMAT;
if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT;
if(len<16)return OP_EBADHEADER;
_data+=8;
len-=8;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
if(count>len)return OP_EBADHEADER;
if(_tags!=NULL){
_tags->vendor=op_strdup_with_len((char *)_data,count);
if(_tags->vendor==NULL)return OP_EFAULT;
}
_data+=count;
len-=count;
if(len<4)return OP_EBADHEADER;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
/*Check to make sure there's minimally sufficient data left in the packet.*/
if(count>len>>2)return OP_EBADHEADER;
/*Check for overflow (the API limits this to an int).*/
if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT;
if(_tags!=NULL){
int ret;
ret=op_tags_ensure_capacity(_tags,count);
if(ret<0)return ret;
}
ncomments=(int)count;
for(ci=0;ci<ncomments;ci++){
/*Check to make sure there's minimally sufficient data left in the packet.*/
if((size_t)(ncomments-ci)>len>>2)return OP_EBADHEADER;
count=op_parse_uint32le(_data);
_data+=4;
len-=4;
if(count>len)return OP_EBADHEADER;
/*Check for overflow (the API limits this to an int).*/
if(count>(opus_uint32)INT_MAX)return OP_EFAULT;
if(_tags!=NULL){
_tags->user_comments[ci]=op_strdup_with_len((char *)_data,count);
if(_tags->user_comments[ci]==NULL)return OP_EFAULT;
_tags->comment_lengths[ci]=(int)count;
_tags->comments=ci+1;
}
_data+=count;
len-=count;
}
return 0;
}
int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){
if(_tags!=NULL){
OpusTags tags;
int ret;
opus_tags_init(&tags);
ret=opus_tags_parse_impl(&tags,_data,_len);
if(ret<0)opus_tags_clear(&tags);
else *_tags=*&tags;
return ret;
}
else return opus_tags_parse_impl(NULL,_data,_len);
}
/*The actual implementation of opus_tags_copy().
Unlike the public API, this function requires _dst to already be
initialized, modifies its contents before success is guaranteed, and assumes
the caller will clear it on error.*/
static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){
char *vendor;
int ncomments;
int ret;
int ci;
vendor=_src->vendor;
_dst->vendor=op_strdup_with_len(vendor,strlen(vendor));
if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT;
ncomments=_src->comments;
ret=op_tags_ensure_capacity(_dst,ncomments);
if(OP_UNLIKELY(ret<0))return ret;
for(ci=0;ci<ncomments;ci++){
int len;
len=_src->comment_lengths[ci];
OP_ASSERT(len>=0);
_dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len);
if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT;
_dst->comment_lengths[ci]=len;
_dst->comments=ci+1;
}
return 0;
}
int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){
OpusTags dst;
int ret;
opus_tags_init(&dst);
ret=opus_tags_copy_impl(&dst,_src);
if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst);
else *_dst=*&dst;
return 0;
}
int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
char *comment;
int tag_len;
int value_len;
int ncomments;
int ret;
ncomments=_tags->comments;
ret=op_tags_ensure_capacity(_tags,ncomments+1);
if(OP_UNLIKELY(ret<0))return ret;
tag_len=strlen(_tag);
value_len=strlen(_value);
/*+2 for '=' and '\0'.*/
_tags->comment_lengths[ncomments]=0;
_tags->user_comments[ncomments]=comment=
(char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
_tags->comment_lengths[ncomments]=tag_len+value_len+1;
memcpy(comment,_tag,sizeof(*comment)*tag_len);
comment[tag_len]='=';
memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1));
return 0;
}
int opus_tags_add_comment(OpusTags *_tags,const char *_comment){
int comment_len;
int ncomments;
int ret;
ncomments=_tags->comments;
ret=op_tags_ensure_capacity(_tags,ncomments+1);
if(OP_UNLIKELY(ret<0))return ret;
comment_len=(int)strlen(_comment);
_tags->comment_lengths[ncomments]=0;
_tags->user_comments[ncomments]=op_strdup_with_len(_comment,comment_len);
if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT;
_tags->comment_lengths[ncomments]=comment_len;
return 0;
}
int opus_tagcompare(const char *_tag_name,const char *_comment){
return opus_tagncompare(_tag_name,strlen(_tag_name),_comment);
}
int opus_tagncompare(const char *_tag_name,int _tag_len,const char *_comment){
int ret;
OP_ASSERT(_tag_len>=0);
ret=op_strncasecmp(_tag_name,_comment,_tag_len);
return ret?ret:'='-_comment[_tag_len];
}
const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
char **user_comments;
int tag_len;
int found;
int ncomments;
int ci;
tag_len=strlen(_tag);
ncomments=_tags->comments;
user_comments=_tags->user_comments;
found=0;
for(ci=0;ci<ncomments;ci++){
if(!opus_tagncompare(_tag,tag_len,user_comments[ci])){
/*We return a pointer to the data, not a copy.*/
if(_count==found++)return user_comments[ci]+tag_len+1;
}
}
/*Didn't find anything.*/
return NULL;
}
int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
char **user_comments;
int tag_len;
int found;
int ncomments;
int ci;
tag_len=strlen(_tag);
ncomments=_tags->comments;
user_comments=_tags->user_comments;
found=0;
for(ci=0;ci<ncomments;ci++){
if(!opus_tagncompare(_tag,tag_len,user_comments[ci]))found++;
}
return found;
}
int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){
char **comments;
int ncomments;
int ci;
comments=_tags->user_comments;
ncomments=_tags->comments;
/*Look for the first valid R128_TRACK_GAIN tag and use that.*/
for(ci=0;ci<ncomments;ci++){
if(opus_tagncompare("R128_TRACK_GAIN",15,comments[ci])==0){
char *p;
opus_int32 gain_q8;
int negative;
p=comments[ci]+16;
negative=0;
if(*p=='-'){
negative=-1;
p++;
}
else if(*p=='+')p++;
gain_q8=0;
while(*p>='0'&&*p<='9'){
gain_q8=10*gain_q8+*p-'0';
if(gain_q8>32767-negative)break;
p++;
}
/*This didn't look like a signed 16-bit decimal integer.
Not a valid R128_TRACK_GAIN tag.*/
if(*p!='\0')continue;
*_gain_q8=(int)(gain_q8+negative^negative);
return 0;
}
}
return OP_FALSE;
}
static int op_is_jpeg(const unsigned char *_buf,size_t _buf_sz){
return _buf_sz>=11&&memcmp(_buf,"\xFF\xD8\xFF\xE0",4)==0
&&(_buf[4]<<8|_buf[5])>=16&&memcmp(_buf+6,"JFIF",5)==0;
}
/*Tries to extract the width, height, bits per pixel, and palette size of a
JPEG.
On failure, simply leaves its outputs unmodified.*/
static void op_extract_jpeg_params(const unsigned char *_buf,size_t _buf_sz,
opus_uint32 *_width,opus_uint32 *_height,
opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
if(op_is_jpeg(_buf,_buf_sz)){
size_t offs;
offs=2;
for(;;){
size_t segment_len;
int marker;
while(offs<_buf_sz&&_buf[offs]!=0xFF)offs++;
while(offs<_buf_sz&&_buf[offs]==0xFF)offs++;
marker=_buf[offs];
offs++;
/*If we hit EOI* (end of image), or another SOI* (start of image),
or SOS (start of scan), then stop now.*/
if(offs>=_buf_sz||(marker>=0xD8&&marker<=0xDA))break;
/*RST* (restart markers): skip (no segment length).*/
else if(marker>=0xD0&&marker<=0xD7)continue;
/*Read the length of the marker segment.*/
if(_buf_sz-offs<2)break;
segment_len=_buf[offs]<<8|_buf[offs+1];
if(segment_len<2||_buf_sz-offs<segment_len)break;
if(marker==0xC0||(marker>0xC0&&marker<0xD0&&(marker&3)!=0)){
/*Found a SOFn (start of frame) marker segment:*/
if(segment_len>=8){
*_height=_buf[offs+3]<<8|_buf[offs+4];
*_width=_buf[offs+5]<<8|_buf[offs+6];
*_depth=_buf[offs+2]*_buf[offs+7];
*_colors=0;
*_has_palette=0;
}
break;
}
/*Other markers: skip the whole marker segment.*/
offs+=segment_len;
}
}
}
static int op_is_png(const unsigned char *_buf,size_t _buf_sz){
return _buf_sz>=8&&memcmp(_buf,"\x89PNG\x0D\x0A\x1A\x0A",8)==0;
}
/*Tries to extract the width, height, bits per pixel, and palette size of a
PNG.
On failure, simply leaves its outputs unmodified.*/
static void op_extract_png_params(const unsigned char *_buf,size_t _buf_sz,
opus_uint32 *_width,opus_uint32 *_height,
opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
if(op_is_png(_buf,_buf_sz)){
size_t offs;
offs=8;
while(_buf_sz-offs>=12){
ogg_uint32_t chunk_len;
chunk_len=op_parse_uint32be(_buf+offs);
if(chunk_len>_buf_sz-(offs+12))break;
else if(chunk_len==13&&memcmp(_buf+offs+4,"IHDR",4)==0){
int color_type;
*_width=op_parse_uint32be(_buf+offs+8);
*_height=op_parse_uint32be(_buf+offs+12);
color_type=_buf[offs+17];
if(color_type==3){
*_depth=24;
*_has_palette=1;
}
else{
int sample_depth;
sample_depth=_buf[offs+16];
if(color_type==0)*_depth=sample_depth;
else if(color_type==2)*_depth=sample_depth*3;
else if(color_type==4)*_depth=sample_depth*2;
else if(color_type==6)*_depth=sample_depth*4;
*_colors=0;
*_has_palette=0;
break;
}
}
else if(*_has_palette>0&&memcmp(_buf+offs+4,"PLTE",4)==0){
*_colors=chunk_len/3;
break;
}
offs+=12+chunk_len;
}
}
}
static int op_is_gif(const unsigned char *_buf,size_t _buf_sz){
return _buf_sz>=6&&(memcmp(_buf,"GIF87a",6)==0||memcmp(_buf,"GIF89a",6)==0);
}
/*Tries to extract the width, height, bits per pixel, and palette size of a
GIF.
On failure, simply leaves its outputs unmodified.*/
static void op_extract_gif_params(const unsigned char *_buf,size_t _buf_sz,
opus_uint32 *_width,opus_uint32 *_height,
opus_uint32 *_depth,opus_uint32 *_colors,int *_has_palette){
if(op_is_gif(_buf,_buf_sz)&&_buf_sz>=14){
*_width=_buf[6]|_buf[7]<<8;
*_height=_buf[8]|_buf[9]<<8;
/*libFLAC hard-codes the depth to 24.*/
*_depth=24;
*_colors=1<<((_buf[10]&7)+1);
*_has_palette=1;
}
}
/*The actual implementation of opus_picture_tag_parse().
Unlike the public API, this function requires _pic to already be
initialized, modifies its contents before success is guaranteed, and assumes
the caller will clear it on error.*/
static int opus_picture_tag_parse_impl(OpusPictureTag *_pic,const char *_tag,
unsigned char *_buf,size_t _buf_sz,size_t _base64_sz){
opus_int32 picture_type;
opus_uint32 mime_type_length;
char *mime_type;
opus_uint32 description_length;
char *description;
opus_uint32 width;
opus_uint32 height;
opus_uint32 depth;
opus_uint32 colors;
opus_uint32 data_length;
opus_uint32 file_width;
opus_uint32 file_height;
opus_uint32 file_depth;
opus_uint32 file_colors;
int format;
int has_palette;
int colors_set;
size_t i;
/*Decode the BASE64 data.*/
for(i=0;i<_base64_sz;i++){
opus_uint32 value;
int j;
value=0;
for(j=0;j<4;j++){
unsigned c;
unsigned d;
c=(unsigned char)_tag[4*i+j];
if(c=='+')d=62;
else if(c=='/')d=63;
else if(c>='0'&&c<='9')d=52+c-'0';
else if(c>='a'&&c<='z')d=26+c-'a';
else if(c>='A'&&c<='Z')d=c-'A';
else if(c=='='&&3*i+j>_buf_sz)d=0;
else return OP_ENOTFORMAT;
value=value<<6|d;
}
_buf[3*i]=(unsigned char)(value>>16);
if(3*i+1<_buf_sz){
_buf[3*i+1]=(unsigned char)(value>>8);
if(3*i+2<_buf_sz)_buf[3*i+2]=(unsigned char)value;
}
}
i=0;
picture_type=op_parse_uint32be(_buf+i);
i+=4;
/*Extract the MIME type.*/
mime_type_length=op_parse_uint32be(_buf+i);
i+=4;
if(mime_type_length>_buf_sz-32)return OP_ENOTFORMAT;
mime_type=(char *)_ogg_malloc(sizeof(*_pic->mime_type)*(mime_type_length+1));
if(mime_type==NULL)return OP_EFAULT;
memcpy(mime_type,_buf+i,sizeof(*mime_type)*mime_type_length);
mime_type[mime_type_length]='\0';
_pic->mime_type=mime_type;
i+=mime_type_length;
/*Extract the description string.*/
description_length=op_parse_uint32be(_buf+i);
i+=4;
if(description_length>_buf_sz-mime_type_length-32)return OP_ENOTFORMAT;
description=
(char *)_ogg_malloc(sizeof(*_pic->mime_type)*(description_length+1));
if(description==NULL)return OP_EFAULT;
memcpy(description,_buf+i,sizeof(*description)*description_length);
description[description_length]='\0';
_pic->description=description;
i+=description_length;
/*Extract the remaining fields.*/
width=op_parse_uint32be(_buf+i);
i+=4;
height=op_parse_uint32be(_buf+i);
i+=4;
depth=op_parse_uint32be(_buf+i);
i+=4;
colors=op_parse_uint32be(_buf+i);
i+=4;
/*If one of these is set, they all must be, but colors==0 is a valid value.*/
colors_set=width!=0||height!=0||depth!=0||colors!=0;
if(width==0||height==0||depth==0&&colors_set)return OP_ENOTFORMAT;
data_length=op_parse_uint32be(_buf+i);
i+=4;
if(data_length>_buf_sz-i)return OP_ENOTFORMAT;
/*Trim extraneous data so we don't copy it below.*/
_buf_sz=i+data_length;
/*Attempt to determine the image format.*/
format=OP_PIC_FORMAT_UNKNOWN;
if(mime_type_length==3&&strcmp(mime_type,"-->")==0){
format=OP_PIC_FORMAT_URL;
/*Picture type 1 must be a 32x32 PNG.*/
if(picture_type==1&&(width!=0||height!=0)&&(width!=32||height!=32)){
return OP_ENOTFORMAT;
}
/*Append a terminating NUL for the convenience of our callers.*/
_buf[_buf_sz++]='\0';
}
else{
if(mime_type_length==10
&&op_strncasecmp(mime_type,"image/jpeg",mime_type_length)==0){
if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG;
}
else if(mime_type_length==9
&&op_strncasecmp(mime_type,"image/png",mime_type_length)==0){
if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG;
}
else if(mime_type_length==9
&&op_strncasecmp(mime_type,"image/gif",mime_type_length)==0){
if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF;
}
else if(mime_type_length==0||(mime_type_length==6
&&op_strncasecmp(mime_type,"image/",mime_type_length)==0)){
if(op_is_jpeg(_buf+i,data_length))format=OP_PIC_FORMAT_JPEG;
else if(op_is_png(_buf+i,data_length))format=OP_PIC_FORMAT_PNG;
else if(op_is_gif(_buf+i,data_length))format=OP_PIC_FORMAT_GIF;
}
file_width=file_height=file_depth=file_colors=0;
has_palette=-1;
switch(format){
case OP_PIC_FORMAT_JPEG:{
op_extract_jpeg_params(_buf+i,data_length,
&file_width,&file_height,&file_depth,&file_colors,&has_palette);
}break;
case OP_PIC_FORMAT_PNG:{
op_extract_png_params(_buf+i,data_length,
&file_width,&file_height,&file_depth,&file_colors,&has_palette);
}break;
case OP_PIC_FORMAT_GIF:{
op_extract_gif_params(_buf+i,data_length,
&file_width,&file_height,&file_depth,&file_colors,&has_palette);
}break;
}
if(has_palette>=0){
/*If we successfully extracted these parameters from the image, override
any declared values.*/
width=file_width;
height=file_height;
depth=file_depth;
colors=file_colors;
}
/*Picture type 1 must be a 32x32 PNG.*/
if(picture_type==1&&(format!=OP_PIC_FORMAT_PNG||width!=32||height!=32)){
return OP_ENOTFORMAT;
}
}
/*Adjust _buf_sz instead of using data_length to capture the terminating NUL
for URLs.*/
_buf_sz-=i;
memmove(_buf,_buf+i,sizeof(*_buf)*_buf_sz);
_buf=(unsigned char *)_ogg_realloc(_buf,_buf_sz);
if(_buf_sz>0&&_buf==NULL)return OP_EFAULT;
_pic->type=picture_type;
_pic->width=width;
_pic->height=height;
_pic->depth=depth;
_pic->colors=colors;
_pic->data_length=data_length;
_pic->data=_buf;
_pic->format=format;
return 0;
}
int opus_picture_tag_parse(OpusPictureTag *_pic,const char *_tag){
OpusPictureTag pic;
unsigned char *buf;
size_t base64_sz;
size_t buf_sz;
size_t tag_length;
int ret;
if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23;
/*Figure out how much BASE64-encoded data we have.*/
tag_length=strlen(_tag);
if(tag_length&3)return OP_ENOTFORMAT;
base64_sz=tag_length>>2;
buf_sz=3*base64_sz;
if(buf_sz<32)return OP_ENOTFORMAT;
if(_tag[tag_length-1]=='=')buf_sz--;
if(_tag[tag_length-2]=='=')buf_sz--;
if(buf_sz<32)return OP_ENOTFORMAT;
/*Allocate an extra byte to allow appending a terminating NUL to URL data.*/
buf=(unsigned char *)_ogg_malloc(sizeof(*buf)*(buf_sz+1));
if(buf==NULL)return OP_EFAULT;
opus_picture_tag_init(&pic);
ret=opus_picture_tag_parse_impl(&pic,_tag,buf,buf_sz,base64_sz);
if(ret<0){
opus_picture_tag_clear(&pic);
_ogg_free(buf);
}
else *_pic=*&pic;
return ret;
}
void opus_picture_tag_init(OpusPictureTag *_pic){
memset(_pic,0,sizeof(*_pic));
}
void opus_picture_tag_clear(OpusPictureTag *_pic){
_ogg_free(_pic->description);
_ogg_free(_pic->mime_type);
_ogg_free(_pic->data);
}

View file

@ -9,6 +9,10 @@
* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
* *
********************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#if defined(OP_ENABLE_ASSERTIONS)

View file

@ -32,10 +32,22 @@
# include <opusfile.h>
typedef struct OggOpusLink OggOpusLink;
# if defined(OP_FIXED_POINT)
typedef opus_int16 op_sample;
# else
typedef float op_sample;
/*We're using this define to test for libopus 1.1 or later until libopus
provides a better mechanism.*/
# if defined(OPUS_GET_EXPERT_FRAME_DURATION_REQUEST)
/*Enable soft clipping prevention in 16-bit decodes.*/
# define OP_SOFT_CLIP (1)
# endif
# endif
# if OP_GNUC_PREREQ(4,2)
@ -80,8 +92,10 @@ void op_fatal_impl(const char *_str,const char *_file,int _line);
# define OP_ALWAYS_TRUE(_cond) ((void)(_cond))
# endif
# define OP_INT64_MAX ((ogg_int64_t)0x7FFFFFFFFFFFFFFFLL)
# define OP_INT64_MAX (2*(((ogg_int64_t)1<<62)-1)|1)
# define OP_INT64_MIN (-OP_INT64_MAX-1)
# define OP_INT32_MAX (2*(((ogg_int32_t)1<<30)-1)|1)
# define OP_INT32_MIN (-OP_INT32_MAX-1)
# define OP_MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
# define OP_MAX(_a,_b) ((_a)>(_b)?(_a):(_b))
@ -90,7 +104,7 @@ void op_fatal_impl(const char *_str,const char *_file,int _line);
/*Advance a file offset by the given amount, clamping against OP_INT64_MAX.
This is used to advance a known offset by things like OP_CHUNK_SIZE or
OP_PAGE_SIZE_MAX, while making sure to avoid signed overflow.
It assumes that both _offset and _amount are positive.*/
It assumes that both _offset and _amount are non-negative.*/
#define OP_ADV_OFFSET(_offset,_amount) \
(OP_MIN(_offset,OP_INT64_MAX-(_amount))+(_amount))
@ -189,6 +203,10 @@ struct OggOpusFile{
int op_count;
/*Central working state for the packet-to-PCM decoder.*/
OpusMSDecoder *od;
/*The application-provided packet decode callback.*/
op_decode_cb_func decode_cb;
/*The application-provided packet decode callback context.*/
void *decode_cb_ctx;
/*The stream count used to initialize the decoder.*/
int od_stream_count;
/*The coupled stream count used to initialize the decoder.*/
@ -203,12 +221,26 @@ struct OggOpusFile{
int od_buffer_pos;
/*The number of valid samples in the decoded buffer.*/
int od_buffer_size;
/*Internal state for dithering float->short output.*/
/*The type of gain offset to apply.
One of OP_HEADER_GAIN, OP_TRACK_GAIN, or OP_ABSOLUTE_GAIN.*/
int gain_type;
/*The offset to apply to the gain.*/
opus_int32 gain_offset_q8;
/*Internal state for soft clipping and dithering float->short output.*/
#if !defined(OP_FIXED_POINT)
# if defined(OP_SOFT_CLIP)
float clip_state[OP_NCHANNELS_MAX];
# endif
float dither_a[OP_NCHANNELS_MAX*4];
float dither_b[OP_NCHANNELS_MAX*4];
int dither_mute;
opus_uint32 dither_seed;
int dither_mute;
int dither_disabled;
/*The number of channels represented by the internal state.
This gets set to 0 whenever anything that would prevent state propagation
occurs (switching between the float/short APIs, or between the
stereo/multistream APIs).*/
int state_channel_count;
#endif
};

View file

@ -0,0 +1,366 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012 *
* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
* *
********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $
********************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#if defined(_WIN32)
# include <io.h>
#endif
typedef struct OpusMemStream OpusMemStream;
#define OP_MEM_SIZE_MAX (~(size_t)0>>1)
#define OP_MEM_DIFF_MAX ((ptrdiff_t)OP_MEM_SIZE_MAX)
/*The context information needed to read from a block of memory as if it were a
file.*/
struct OpusMemStream{
/*The block of memory to read from.*/
const unsigned char *data;
/*The total size of the block.
This must be at most OP_MEM_SIZE_MAX to prevent signed overflow while
seeking.*/
ptrdiff_t size;
/*The current file position.
This is allowed to be set arbitrarily greater than size (i.e., past the end
of the block, though we will not read data past the end of the block), but
is not allowed to be negative (i.e., before the beginning of the block).*/
ptrdiff_t pos;
};
static int op_fread(void *_stream,unsigned char *_ptr,int _buf_size){
FILE *stream;
size_t ret;
/*Check for empty read.*/
if(_buf_size<=0)return 0;
stream=(FILE *)_stream;
ret=fread(_ptr,1,_buf_size,stream);
OP_ASSERT(ret<=(size_t)_buf_size);
/*If ret==0 and !feof(stream), there was a read error.*/
return ret>0||feof(stream)?(int)ret:OP_EREAD;
}
static int op_fseek(void *_stream,opus_int64 _offset,int _whence){
#if defined(_WIN32)
/*_fseeki64() is not exposed until MSCVCRT80.
This is the default starting with MSVC 2005 (_MSC_VER>=1400), but we want
to allow linking against older MSVCRT versions for compatibility back to
XP without installing extra runtime libraries.
i686-pc-mingw32 does not have fseeko() and requires
__MSVCRT_VERSION__>=0x800 for _fseeki64(), which screws up linking with
other libraries (that don't use MSVCRT80 from MSVC 2005 by default).
i686-w64-mingw32 does have fseeko() and respects _FILE_OFFSET_BITS, but I
don't know how to detect that at compile time.
We could just use fseeko64() (which is available in both), but its
implemented using fgetpos()/fsetpos() just like this code, except without
the overflow checking, so we prefer our version.*/
opus_int64 pos;
/*We don't use fpos_t directly because it might be a struct if __STDC__ is
non-zero or _INTEGRAL_MAX_BITS < 64.
I'm not certain when the latter is true, but someone could in theory set
the former.
Either way, it should be binary compatible with a normal 64-bit int (this
assumption is not portable, but I believe it is true for MSVCRT).*/
OP_ASSERT(sizeof(pos)==sizeof(fpos_t));
/*Translate the seek to an absolute one.*/
if(_whence==SEEK_CUR){
int ret;
ret=fgetpos((FILE *)_stream,(fpos_t *)&pos);
if(ret)return ret;
}
else if(_whence==SEEK_END)pos=_filelengthi64(_fileno((FILE *)_stream));
else if(_whence==SEEK_SET)pos=0;
else return -1;
/*Check for errors or overflow.*/
if(pos<0||_offset<-pos||_offset>OP_INT64_MAX-pos)return -1;
pos+=_offset;
return fsetpos((FILE *)_stream,(fpos_t *)&pos);
#else
/*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer
it except on Windows.*/
return fseeko((FILE *)_stream,(off_t)_offset,_whence);
#endif
}
static opus_int64 op_ftell(void *_stream){
#if defined(_WIN32)
/*_ftelli64() is not exposed until MSCVCRT80, and ftello()/ftello64() have
the same problems as fseeko()/fseeko64() in MingW.
See above for a more detailed explanation.*/
opus_int64 pos;
OP_ASSERT(sizeof(pos)==sizeof(fpos_t));
return fgetpos((FILE *)_stream,(fpos_t *)&pos)?-1:pos;
#else
/*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer
it except on Windows.*/
return ftello((FILE *)_stream);
#endif
}
static const OpusFileCallbacks OP_FILE_CALLBACKS={
op_fread,
op_fseek,
op_ftell,
(op_close_func)fclose
};
#if defined(_WIN32)
# include <stddef.h>
# include <errno.h>
/*Windows doesn't accept UTF-8 by default, and we don't have a wchar_t API,
so if we just pass the path to fopen(), then there'd be no way for a user
of our API to open a Unicode filename.
Instead, we translate from UTF-8 to UTF-16 and use Windows' wchar_t API.
This makes this API more consistent with platforms where the character set
used by fopen is the same as used on disk, which is generally UTF-8, and
with our metadata API, which always uses UTF-8.*/
static wchar_t *op_utf8_to_utf16(const char *_src){
wchar_t *dst;
size_t len;
len=strlen(_src);
/*Worst-case output is 1 wide character per 1 input character.*/
dst=(wchar_t *)_ogg_malloc(sizeof(*dst)*(len+1));
if(dst!=NULL){
size_t si;
size_t di;
for(di=si=0;si<len;si++){
int c0;
c0=(unsigned char)_src[si];
if(!(c0&0x80)){
/*Start byte says this is a 1-byte sequence.*/
dst[di++]=(wchar_t)c0;
continue;
}
else{
int c1;
/*This is safe, because c0 was not 0 and _src is NUL-terminated.*/
c1=(unsigned char)_src[si+1];
if((c1&0xC0)==0x80){
/*Found at least one continuation byte.*/
if((c0&0xE0)==0xC0){
wchar_t w;
/*Start byte says this is a 2-byte sequence.*/
w=(c0&0x1F)<<6|c1&0x3F;
if(w>=0x80U){
/*This is a 2-byte sequence that is not overlong.*/
dst[di++]=w;
si++;
continue;
}
}
else{
int c2;
/*This is safe, because c1 was not 0 and _src is NUL-terminated.*/
c2=(unsigned char)_src[si+2];
if((c2&0xC0)==0x80){
/*Found at least two continuation bytes.*/
if((c0&0xF0)==0xE0){
wchar_t w;
/*Start byte says this is a 3-byte sequence.*/
w=(c0&0xF)<<12|(c1&0x3F)<<6|c2&0x3F;
if(w>=0x800U&&(w<0xD800||w>=0xE000)&&w<0xFFFE){
/*This is a 3-byte sequence that is not overlong, not a
UTF-16 surrogate pair value, and not a 'not a character'
value.*/
dst[di++]=w;
si+=2;
continue;
}
}
else{
int c3;
/*This is safe, because c2 was not 0 and _src is
NUL-terminated.*/
c3=(unsigned char)_src[si+3];
if((c3&0xC0)==0x80){
/*Found at least three continuation bytes.*/
if((c0&0xF8)==0xF0){
opus_uint32 w;
/*Start byte says this is a 4-byte sequence.*/
w=(c0&7)<<18|(c1&0x3F)<<12|(c2&0x3F)<<6&(c3&0x3F);
if(w>=0x10000U&&w<0x110000U){
/*This is a 4-byte sequence that is not overlong and not
greater than the largest valid Unicode code point.
Convert it to a surrogate pair.*/
w-=0x10000;
dst[di++]=(wchar_t)(0xD800+(w>>10));
dst[di++]=(wchar_t)(0xDC00+(w&0x3FF));
si+=3;
continue;
}
}
}
}
}
}
}
}
/*If we got here, we encountered an illegal UTF-8 sequence.*/
_ogg_free(dst);
return NULL;
}
OP_ASSERT(di<=len);
dst[di]='\0';
}
return dst;
}
#endif
void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){
FILE *fp;
#if !defined(_WIN32)
fp=fopen(_path,_mode);
#else
fp=NULL;
if(_path==NULL||_mode==NULL)errno=EINVAL;
else{
wchar_t *wpath;
wchar_t *wmode;
wpath=op_utf8_to_utf16(_path);
wmode=op_utf8_to_utf16(_mode);
if(wmode==NULL)errno=EINVAL;
else if(wpath==NULL)errno=ENOENT;
else fp=_wfopen(wpath,wmode);
_ogg_free(wmode);
_ogg_free(wpath);
}
#endif
if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
return fp;
}
void *op_fdopen(OpusFileCallbacks *_cb,int _fd,const char *_mode){
FILE *fp;
fp=fdopen(_fd,_mode);
if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
return fp;
}
void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode,
void *_stream){
FILE *fp;
#if !defined(_WIN32)
fp=freopen(_path,_mode,(FILE *)_stream);
#else
fp=NULL;
if(_path==NULL||_mode==NULL)errno=EINVAL;
else{
wchar_t *wpath;
wchar_t *wmode;
wpath=op_utf8_to_utf16(_path);
wmode=op_utf8_to_utf16(_mode);
if(wmode==NULL)errno=EINVAL;
else if(wpath==NULL)errno=ENOENT;
else fp=_wfreopen(wpath,wmode,(FILE *)_stream);
_ogg_free(wmode);
_ogg_free(wpath);
}
#endif
if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
return fp;
}
static int op_mem_read(void *_stream,unsigned char *_ptr,int _buf_size){
OpusMemStream *stream;
ptrdiff_t size;
ptrdiff_t pos;
stream=(OpusMemStream *)_stream;
/*Check for empty read.*/
if(_buf_size<=0)return 0;
size=stream->size;
pos=stream->pos;
/*Check for EOF.*/
if(pos>=size)return 0;
/*Check for a short read.*/
_buf_size=(int)OP_MIN(size-pos,_buf_size);
memcpy(_ptr,stream->data+pos,_buf_size);
pos+=_buf_size;
stream->pos=pos;
return _buf_size;
}
static int op_mem_seek(void *_stream,opus_int64 _offset,int _whence){
OpusMemStream *stream;
ptrdiff_t pos;
stream=(OpusMemStream *)_stream;
pos=stream->pos;
OP_ASSERT(pos>=0);
switch(_whence){
case SEEK_SET:{
/*Check for overflow:*/
if(_offset<0||_offset>OP_MEM_DIFF_MAX)return -1;
pos=(ptrdiff_t)_offset;
}break;
case SEEK_CUR:{
/*Check for overflow:*/
if(_offset<-pos||_offset>OP_MEM_DIFF_MAX-pos)return -1;
pos=(ptrdiff_t)(pos+_offset);
}break;
case SEEK_END:{
ptrdiff_t size;
size=stream->size;
OP_ASSERT(size>=0);
/*Check for overflow:*/
if(_offset>size||_offset<size-OP_MEM_DIFF_MAX)return -1;
pos=(ptrdiff_t)(size-_offset);
}break;
default:return -1;
}
stream->pos=pos;
return 0;
}
static opus_int64 op_mem_tell(void *_stream){
OpusMemStream *stream;
stream=(OpusMemStream *)_stream;
return (ogg_int64_t)stream->pos;
}
static int op_mem_close(void *_stream){
_ogg_free(_stream);
return 0;
}
static const OpusFileCallbacks OP_MEM_CALLBACKS={
op_mem_read,
op_mem_seek,
op_mem_tell,
op_mem_close
};
void *op_mem_stream_create(OpusFileCallbacks *_cb,
const unsigned char *_data,size_t _size){
OpusMemStream *stream;
if(_size>OP_MEM_SIZE_MAX)return NULL;
stream=(OpusMemStream *)_ogg_malloc(sizeof(*stream));
if(stream!=NULL){
*_cb=*&OP_MEM_CALLBACKS;
stream->data=_data;
stream->size=_size;
stream->pos=0;
}
return stream;
}

View file

@ -0,0 +1,171 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2013 *
* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
* *
********************************************************************/
/*This should really be part of OpenSSL, but there's been a patch [1] sitting
in their bugtracker for over two years that implements this, without any
action, so I'm giving up and re-implementing it locally.
[1] <http://rt.openssl.org/Ticket/Display.html?id=2158>*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "internal.h"
#if defined(OP_ENABLE_HTTP)&&defined(_WIN32)
/*You must include windows.h before wincrypt.h and x509.h.*/
# define WIN32_LEAN_AND_MEAN
# define WIN32_EXTRA_LEAN
# include <windows.h>
/*You must include wincrypt.h before x509.h, too, or X509_NAME doesn't get
defined properly.*/
# include <wincrypt.h>
# include <openssl/ssl.h>
# include <openssl/err.h>
# include <openssl/x509.h>
static int op_capi_new(X509_LOOKUP *_lu){
HCERTSTORE h_store;
h_store=CertOpenStore(CERT_STORE_PROV_SYSTEM_A,0,0,
CERT_STORE_OPEN_EXISTING_FLAG|CERT_STORE_READONLY_FLAG|
CERT_SYSTEM_STORE_CURRENT_USER|CERT_STORE_SHARE_CONTEXT_FLAG,"ROOT");
if(h_store!=NULL){
_lu->method_data=(char *)h_store;
return 1;
}
return 0;
}
static void op_capi_free(X509_LOOKUP *_lu){
HCERTSTORE h_store;
h_store=(HCERTSTORE)_lu->method_data;
# if defined(OP_ENABLE_ASSERTIONS)
OP_ALWAYS_TRUE(CertCloseStore(h_store,CERT_CLOSE_STORE_CHECK_FLAG));
# else
CertCloseStore(h_store,0);
# endif
}
static int op_capi_retrieve_by_subject(X509_LOOKUP *_lu,int _type,
X509_NAME *_name,X509_OBJECT *_ret){
X509_OBJECT *obj;
CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
obj=X509_OBJECT_retrieve_by_subject(_lu->store_ctx->objs,_type,_name);
CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
if(obj!=NULL){
_ret->type=obj->type;
memcpy(&_ret->data,&obj->data,sizeof(_ret->data));
return 1;
}
return 0;
}
static int op_capi_get_by_subject(X509_LOOKUP *_lu,int _type,X509_NAME *_name,
X509_OBJECT *_ret){
HCERTSTORE h_store;
if(_name==NULL)return 0;
if(_name->bytes==NULL||_name->bytes->length<=0||_name->modified){
if(i2d_X509_NAME(_name,NULL)<0)return 0;
OP_ASSERT(_name->bytes->length>0);
}
h_store=(HCERTSTORE)_lu->method_data;
switch(_type){
case X509_LU_X509:{
CERT_NAME_BLOB find_para;
PCCERT_CONTEXT cert;
X509 *x;
int ret;
/*Although X509_NAME contains a canon_enc field, that "canonical" [1]
encoding was just made up by OpenSSL.
It doesn't correspond to any actual standard, and since it drops the
initial sequence header, won't be recognized by the Crypto API.
The assumption here is that CertFindCertificateInStore() will allow any
appropriate variations in the encoding when it does its comparison.
This is, however, emphatically not true under Wine, which just compares
the encodings with memcmp().
Most of the time things work anyway, though, and there isn't really
anything we can do to make the situation better.
[1] A "canonical form" is defined as the one where, if you locked 10
mathematicians in a room and asked them to come up with a
representation for something, it's the answer that 9 of them would
give you back.
I don't think OpenSSL's encoding qualifies.*/
find_para.cbData=_name->bytes->length;
find_para.pbData=(unsigned char *)_name->bytes->data;
cert=CertFindCertificateInStore(h_store,X509_ASN_ENCODING,0,
CERT_FIND_SUBJECT_NAME,&find_para,NULL);
if(cert==NULL)return 0;
x=d2i_X509(NULL,(const unsigned char **)&cert->pbCertEncoded,
cert->cbCertEncoded);
CertFreeCertificateContext(cert);
if(x==NULL)return 0;
ret=X509_STORE_add_cert(_lu->store_ctx,x);
X509_free(x);
if(ret)return op_capi_retrieve_by_subject(_lu,_type,_name,_ret);
}break;
case X509_LU_CRL:{
CERT_INFO cert_info;
CERT_CONTEXT find_para;
PCCRL_CONTEXT crl;
X509_CRL *x;
int ret;
ret=op_capi_retrieve_by_subject(_lu,_type,_name,_ret);
if(ret>0)return ret;
memset(&cert_info,0,sizeof(cert_info));
cert_info.Issuer.cbData=_name->bytes->length;
cert_info.Issuer.pbData=(unsigned char *)_name->bytes->data;
memset(&find_para,0,sizeof(find_para));
find_para.pCertInfo=&cert_info;
crl=CertFindCRLInStore(h_store,0,0,CRL_FIND_ISSUED_BY,&find_para,NULL);
if(crl==NULL)return 0;
x=d2i_X509_CRL(NULL,(const unsigned char **)&crl->pbCrlEncoded,
crl->cbCrlEncoded);
CertFreeCRLContext(crl);
if(x==NULL)return 0;
ret=X509_STORE_add_crl(_lu->store_ctx,x);
X509_CRL_free(x);
if(ret)return op_capi_retrieve_by_subject(_lu,_type,_name,_ret);
}break;
}
return 0;
}
/*This is not const because OpenSSL doesn't allow it, even though it won't
write to it.*/
static X509_LOOKUP_METHOD X509_LOOKUP_CAPI={
"Load Crypto API store into cache",
op_capi_new,
op_capi_free,
NULL,
NULL,
NULL,
op_capi_get_by_subject,
NULL,
NULL,
NULL
};
int SSL_CTX_set_default_verify_paths_win32(SSL_CTX *_ssl_ctx){
X509_STORE *store;
X509_LOOKUP *lu;
/*We intentionally do not add the normal default paths, as they are usually
wrong, and are just asking to be used as an exploit vector.*/
store=SSL_CTX_get_cert_store(_ssl_ctx);
OP_ASSERT(store!=NULL);
lu=X509_STORE_add_lookup(store,&X509_LOOKUP_CAPI);
if(lu==NULL)return 0;
ERR_clear_error();
return 1;
}
#endif

View file

@ -0,0 +1,90 @@
/********************************************************************
* *
* THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 2012 *
* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
* *
********************************************************************/
#if !defined(_opusfile_winerrno_h)
# define _opusfile_winerrno_h (1)
# include <errno.h>
# include <winerror.h>
/*These conflict with the MSVC errno.h definitions, but we don't need to use
the original ones in any file that deals with sockets.
We could map the WSA errors to the errno.h ones (most of which are only
available on sufficiently new versions of MSVC), but they aren't ordered the
same, and given how rarely we actually look at the values, I don't think
it's worth a lookup table.*/
# undef EWOULDBLOCK
# undef EINPROGRESS
# undef EALREADY
# undef ENOTSOCK
# undef EDESTADDRREQ
# undef EMSGSIZE
# undef EPROTOTYPE
# undef ENOPROTOOPT
# undef EPROTONOSUPPORT
# undef EOPNOTSUPP
# undef EAFNOSUPPORT
# undef EADDRINUSE
# undef EADDRNOTAVAIL
# undef ENETDOWN
# undef ENETUNREACH
# undef ENETRESET
# undef ECONNABORTED
# undef ECONNRESET
# undef ENOBUFS
# undef EISCONN
# undef ENOTCONN
# undef ETIMEDOUT
# undef ECONNREFUSED
# undef ELOOP
# undef ENAMETOOLONG
# undef EHOSTUNREACH
# undef ENOTEMPTY
# define EWOULDBLOCK (WSAEWOULDBLOCK-WSABASEERR)
# define EINPROGRESS (WSAEINPROGRESS-WSABASEERR)
# define EALREADY (WSAEALREADY-WSABASEERR)
# define ENOTSOCK (WSAENOTSOCK-WSABASEERR)
# define EDESTADDRREQ (WSAEDESTADDRREQ-WSABASEERR)
# define EMSGSIZE (WSAEMSGSIZE-WSABASEERR)
# define EPROTOTYPE (WSAEPROTOTYPE-WSABASEERR)
# define ENOPROTOOPT (WSAENOPROTOOPT-WSABASEERR)
# define EPROTONOSUPPORT (WSAEPROTONOSUPPORT-WSABASEERR)
# define ESOCKTNOSUPPORT (WSAESOCKTNOSUPPORT-WSABASEERR)
# define EOPNOTSUPP (WSAEOPNOTSUPP-WSABASEERR)
# define EPFNOSUPPORT (WSAEPFNOSUPPORT-WSABASEERR)
# define EAFNOSUPPORT (WSAEAFNOSUPPORT-WSABASEERR)
# define EADDRINUSE (WSAEADDRINUSE-WSABASEERR)
# define EADDRNOTAVAIL (WSAEADDRNOTAVAIL-WSABASEERR)
# define ENETDOWN (WSAENETDOWN-WSABASEERR)
# define ENETUNREACH (WSAENETUNREACH-WSABASEERR)
# define ENETRESET (WSAENETRESET-WSABASEERR)
# define ECONNABORTED (WSAECONNABORTED-WSABASEERR)
# define ECONNRESET (WSAECONNRESET-WSABASEERR)
# define ENOBUFS (WSAENOBUFS-WSABASEERR)
# define EISCONN (WSAEISCONN-WSABASEERR)
# define ENOTCONN (WSAENOTCONN-WSABASEERR)
# define ESHUTDOWN (WSAESHUTDOWN-WSABASEERR)
# define ETOOMANYREFS (WSAETOOMANYREFS-WSABASEERR)
# define ETIMEDOUT (WSAETIMEDOUT-WSABASEERR)
# define ECONNREFUSED (WSAECONNREFUSED-WSABASEERR)
# define ELOOP (WSAELOOP-WSABASEERR)
# define ENAMETOOLONG (WSAENAMETOOLONG-WSABASEERR)
# define EHOSTDOWN (WSAEHOSTDOWN-WSABASEERR)
# define EHOSTUNREACH (WSAEHOSTUNREACH-WSABASEERR)
# define ENOTEMPTY (WSAENOTEMPTY-WSABASEERR)
# define EPROCLIM (WSAEPROCLIM-WSABASEERR)
# define EUSERS (WSAEUSERS-WSABASEERR)
# define EDQUOT (WSAEDQUOT-WSABASEERR)
# define ESTALE (WSAESTALE-WSABASEERR)
# define EREMOTE (WSAEREMOTE-WSABASEERR)
#endif