Add libopus 1.0.2 and libopusfile 0.2

This commit is contained in:
Zack Middleton 2013-02-17 18:33:39 -06:00
parent 3dd5c5d862
commit 6d56d8bb33
219 changed files with 55165 additions and 0 deletions

3075
code/opusfile-0.2/src/http.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,286 @@
/********************************************************************
* *
* 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

@ -0,0 +1,38 @@
/********************************************************************
* *
* 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"
#if defined(OP_ENABLE_ASSERTIONS)
void op_fatal_impl(const char *_str,const char *_file,int _line){
fprintf(stderr,"Fatal (internal) error in %s, line %i: %s\n",
_file,_line,_str);
abort();
}
#endif
/*A version of strncasecmp() that is guaranteed to only ignore the case of
ASCII characters.*/
int op_strncasecmp(const char *_a,const char *_b,int _n){
int i;
for(i=0;i<_n;i++){
int a;
int b;
int d;
a=_a[i];
b=_b[i];
if(a>='a'&&a<='z')a-='a'-'A';
if(b>='a'&&b<='z')b-='a'-'A';
d=a-b;
if(d)return d;
}
return 0;
}

View file

@ -0,0 +1,217 @@
/********************************************************************
* *
* 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_internal_h)
# define _opusfile_internal_h (1)
# if !defined(_REENTRANT)
# define _REENTRANT
# endif
# if !defined(_GNU_SOURCE)
# define _GNU_SOURCE
# endif
# if !defined(_LARGEFILE_SOURCE)
# define _LARGEFILE_SOURCE
# endif
# if !defined(_LARGEFILE64_SOURCE)
# define _LARGEFILE64_SOURCE
# endif
# if !defined(_FILE_OFFSET_BITS)
# define _FILE_OFFSET_BITS 64
# endif
# include <stdlib.h>
# include <opusfile.h>
typedef struct OggOpusLink OggOpusLink;
# if defined(OP_FIXED_POINT)
typedef opus_int16 op_sample;
# else
typedef float op_sample;
# endif
# if OP_GNUC_PREREQ(4,2)
/*Disable excessive warnings about the order of operations.*/
# pragma GCC diagnostic ignored "-Wparentheses"
# elif defined(_MSC_VER)
/*Disable excessive warnings about the order of operations.*/
# pragma warning(disable:4554)
/*Disable warnings about "deprecated" POSIX functions.*/
# pragma warning(disable:4996)
# endif
# if OP_GNUC_PREREQ(3,0)
/*Another alternative is
(__builtin_constant_p(_x)?!!(_x):__builtin_expect(!!(_x),1))
but that evaluates _x multiple times, which may be bad.*/
# define OP_LIKELY(_x) (__builtin_expect(!!(_x),1))
# define OP_UNLIKELY(_x) (__builtin_expect(!!(_x),0))
# else
# define OP_LIKELY(_x) (!!(_x))
# define OP_UNLIKELY(_x) (!!(_x))
# endif
# if defined(OP_ENABLE_ASSERTIONS)
# if OP_GNUC_PREREQ(2,5)||__SUNPRO_C>=0x590
__attribute__((noreturn))
# endif
void op_fatal_impl(const char *_str,const char *_file,int _line);
# define OP_FATAL(_str) (op_fatal_impl(_str,__FILE__,__LINE__))
# define OP_ASSERT(_cond) \
do{ \
if(OP_UNLIKELY(!(_cond)))OP_FATAL("assertion failed: " #_cond); \
} \
while(0)
# define OP_ALWAYS_TRUE(_cond) OP_ASSERT(_cond)
# else
# define OP_FATAL(_str) abort()
# define OP_ASSERT(_cond)
# define OP_ALWAYS_TRUE(_cond) ((void)(_cond))
# endif
# define OP_INT64_MAX ((ogg_int64_t)0x7FFFFFFFFFFFFFFFLL)
# define OP_INT64_MIN (-OP_INT64_MAX-1)
# define OP_MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
# define OP_MAX(_a,_b) ((_a)>(_b)?(_a):(_b))
# define OP_CLAMP(_lo,_x,_hi) (OP_MAX(_lo,OP_MIN(_x,_hi)))
/*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.*/
#define OP_ADV_OFFSET(_offset,_amount) \
(OP_MIN(_offset,OP_INT64_MAX-(_amount))+(_amount))
/*The maximum channel count for any mapping we'll actually decode.*/
# define OP_NCHANNELS_MAX (8)
/*Initial state.*/
# define OP_NOTOPEN (0)
/*We've found the first Opus stream in the first link.*/
# define OP_PARTOPEN (1)
# define OP_OPENED (2)
/*We've found the first Opus stream in the current link.*/
# define OP_STREAMSET (3)
/*We've initialized the decoder for the chosen Opus stream in the current
link.*/
# define OP_INITSET (4)
/*Information cached for a single link in a chained Ogg Opus file.
We choose the first Opus stream encountered in each link to play back (and
require at least one).*/
struct OggOpusLink{
/*The byte offset of the first header page in this link.*/
opus_int64 offset;
/*The byte offset of the first data page from the chosen Opus stream in this
link (after the headers).*/
opus_int64 data_offset;
/*The byte offset of the last page from the chosen Opus stream in this link.
This is used when seeking to ensure we find a page before the last one, so
that end-trimming calculations work properly.
This is only valid for seekable sources.*/
opus_int64 end_offset;
/*The granule position of the last sample.
This is only valid for seekable sources.*/
ogg_int64_t pcm_end;
/*The granule position before the first sample.*/
ogg_int64_t pcm_start;
/*The serial number.*/
ogg_uint32_t serialno;
/*The contents of the info header.*/
OpusHead head;
/*The contents of the comment header.*/
OpusTags tags;
};
struct OggOpusFile{
/*The callbacks used to access the data source.*/
OpusFileCallbacks callbacks;
/*A FILE *, memory bufer, etc.*/
void *source;
/*Whether or not we can seek with this data source.*/
int seekable;
/*The number of links in this chained Ogg Opus file.*/
int nlinks;
/*The cached information from each link in a chained Ogg Opus file.
If source isn't seekable (e.g., it's a pipe), only the current link
appears.*/
OggOpusLink *links;
/*The number of serial numbers from a single link.*/
int nserialnos;
/*The capacity of the list of serial numbers from a single link.*/
int cserialnos;
/*Storage for the list of serial numbers from a single link.*/
ogg_uint32_t *serialnos;
/*This is the current offset of the data processed by the ogg_sync_state.
After a seek, this should be set to the target offset so that we can track
the byte offsets of subsequent pages.
After a call to op_get_next_page(), this will point to the first byte after
that page.*/
opus_int64 offset;
/*The total size of this data source, or -1 if it's unseekable.*/
opus_int64 end;
/*Used to locate pages in the data source.*/
ogg_sync_state oy;
/*One of OP_NOTOPEN, OP_PARTOPEN, OP_OPENED, OP_STREAMSET, OP_INITSET.*/
int ready_state;
/*The current link being played back.*/
int cur_link;
/*The number of decoded samples to discard from the start of decoding.*/
opus_int32 cur_discard_count;
/*The granule position of the previous packet (current packet start time).*/
ogg_int64_t prev_packet_gp;
/*The number of bytes read since the last bitrate query, including framing.*/
opus_int64 bytes_tracked;
/*The number of samples decoded since the last bitrate query.*/
ogg_int64_t samples_tracked;
/*Takes physical pages and welds them into a logical stream of packets.*/
ogg_stream_state os;
/*Re-timestamped packets from a single page.
Buffering these relies on the undocumented libogg behavior that ogg_packet
pointers remain valid until the next page is submitted to the
ogg_stream_state they came from.*/
ogg_packet op[255];
/*The index of the next packet to return.*/
int op_pos;
/*The total number of packets available.*/
int op_count;
/*Central working state for the packet-to-PCM decoder.*/
OpusMSDecoder *od;
/*The stream count used to initialize the decoder.*/
int od_stream_count;
/*The coupled stream count used to initialize the decoder.*/
int od_coupled_count;
/*The channel count used to initialize the decoder.*/
int od_channel_count;
/*The channel mapping used to initialize the decoder.*/
unsigned char od_mapping[OP_NCHANNELS_MAX];
/*The buffered data for one decoded packet.*/
op_sample *od_buffer;
/*The current position in the decoded buffer.*/
int od_buffer_pos;
/*The number of valid samples in the decoded buffer.*/
int od_buffer_size;
/*Internal state for dithering float->short output.*/
#if !defined(OP_FIXED_POINT)
float dither_a[OP_NCHANNELS_MAX*4];
float dither_b[OP_NCHANNELS_MAX*4];
int dither_mute;
opus_uint32 dither_seed;
#endif
};
int op_strncasecmp(const char *_a,const char *_b,int _n);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,180 @@
/********************************************************************
* *
* 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(_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(_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;
}