Built SDL2_image and _mixer static

This commit is contained in:
2022-09-30 15:49:16 -04:00
parent e2605bf6c1
commit 1dec4347e0
4473 changed files with 1964551 additions and 9 deletions

View File

@ -0,0 +1,389 @@
/********************************************************************
* *
* 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/ *
* *
********************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/*For fileno()*/
#if !defined(_POSIX_SOURCE)
# define _POSIX_SOURCE 1
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <opusfile.h>
#if defined(_WIN32)
# include "win32utf8.h"
# undef fileno
# define fileno _fileno
#endif
static void print_duration(FILE *_fp,ogg_int64_t _nsamples,int _frac){
ogg_int64_t seconds;
ogg_int64_t minutes;
ogg_int64_t hours;
ogg_int64_t days;
ogg_int64_t weeks;
_nsamples+=_frac?24:24000;
seconds=_nsamples/48000;
_nsamples-=seconds*48000;
minutes=seconds/60;
seconds-=minutes*60;
hours=minutes/60;
minutes-=hours*60;
days=hours/24;
hours-=days*24;
weeks=days/7;
days-=weeks*7;
if(weeks)fprintf(_fp,"%liw",(long)weeks);
if(weeks||days)fprintf(_fp,"%id",(int)days);
if(weeks||days||hours){
if(weeks||days)fprintf(_fp,"%02ih",(int)hours);
else fprintf(_fp,"%ih",(int)hours);
}
if(weeks||days||hours||minutes){
if(weeks||days||hours)fprintf(_fp,"%02im",(int)minutes);
else fprintf(_fp,"%im",(int)minutes);
fprintf(_fp,"%02i",(int)seconds);
}
else fprintf(_fp,"%i",(int)seconds);
if(_frac)fprintf(_fp,".%03i",(int)(_nsamples/48));
fprintf(_fp,"s");
}
static void print_size(FILE *_fp,opus_int64 _nbytes,int _metric,
const char *_spacer){
static const char SUFFIXES[7]={' ','k','M','G','T','P','E'};
opus_int64 val;
opus_int64 den;
opus_int64 round;
int base;
int shift;
base=_metric?1000:1024;
round=0;
den=1;
for(shift=0;shift<6;shift++){
if(_nbytes<den*base-round)break;
den*=base;
round=den>>1;
}
val=(_nbytes+round)/den;
if(den>1&&val<10){
if(den>=1000000000)val=(_nbytes+(round/100))/(den/100);
else val=(_nbytes*100+round)/den;
fprintf(_fp,"%li.%02i%s%c",(long)(val/100),(int)(val%100),
_spacer,SUFFIXES[shift]);
}
else if(den>1&&val<100){
if(den>=1000000000)val=(_nbytes+(round/10))/(den/10);
else val=(_nbytes*10+round)/den;
fprintf(_fp,"%li.%i%s%c",(long)(val/10),(int)(val%10),
_spacer,SUFFIXES[shift]);
}
else fprintf(_fp,"%li%s%c",(long)val,_spacer,SUFFIXES[shift]);
}
static void put_le32(unsigned char *_dst,opus_uint32 _x){
_dst[0]=(unsigned char)(_x&0xFF);
_dst[1]=(unsigned char)(_x>>8&0xFF);
_dst[2]=(unsigned char)(_x>>16&0xFF);
_dst[3]=(unsigned char)(_x>>24&0xFF);
}
/*Make a header for a 48 kHz, stereo, signed, 16-bit little-endian PCM WAV.*/
static void make_wav_header(unsigned char _dst[44],ogg_int64_t _duration){
/*The chunk sizes are set to 0x7FFFFFFF by default.
Many, though not all, programs will interpret this to mean the duration is
"undefined", and continue to read from the file so long as there is actual
data.*/
static const unsigned char WAV_HEADER_TEMPLATE[44]={
'R','I','F','F',0xFF,0xFF,0xFF,0x7F,
'W','A','V','E','f','m','t',' ',
0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00,
0x80,0xBB,0x00,0x00,0x00,0xEE,0x02,0x00,
0x04,0x00,0x10,0x00,'d','a','t','a',
0xFF,0xFF,0xFF,0x7F
};
memcpy(_dst,WAV_HEADER_TEMPLATE,sizeof(WAV_HEADER_TEMPLATE));
if(_duration>0){
if(_duration>0x1FFFFFF6){
fprintf(stderr,"WARNING: WAV output would be larger than 2 GB.\n");
fprintf(stderr,
"Writing non-standard WAV header with invalid chunk sizes.\n");
}
else{
opus_uint32 audio_size;
audio_size=(opus_uint32)(_duration*4);
put_le32(_dst+4,audio_size+36);
put_le32(_dst+40,audio_size);
}
}
}
int main(int _argc,const char **_argv){
OggOpusFile *of;
ogg_int64_t duration;
unsigned char wav_header[44];
int ret;
int is_ssl;
int output_seekable;
#if defined(_WIN32)
win32_utf8_setup(&_argc,&_argv);
#endif
if(_argc!=2){
fprintf(stderr,"Usage: %s <file.opus>\n",_argv[0]);
return EXIT_FAILURE;
}
is_ssl=0;
if(strcmp(_argv[1],"-")==0){
OpusFileCallbacks cb={NULL,NULL,NULL,NULL};
of=op_open_callbacks(op_fdopen(&cb,fileno(stdin),"rb"),&cb,NULL,0,&ret);
}
else{
OpusServerInfo info;
/*Try to treat the argument as a URL.*/
of=op_open_url(_argv[1],&ret,OP_GET_SERVER_INFO(&info),NULL);
#if 0
if(of==NULL){
OpusFileCallbacks cb={NULL,NULL,NULL,NULL};
void *fp;
/*For debugging: force a file to not be seekable.*/
fp=op_fopen(&cb,_argv[1],"rb");
cb.seek=NULL;
cb.tell=NULL;
of=op_open_callbacks(fp,&cb,NULL,0,NULL);
}
#else
if(of==NULL)of=op_open_file(_argv[1],&ret);
#endif
else{
if(info.name!=NULL){
fprintf(stderr,"Station name: %s\n",info.name);
}
if(info.description!=NULL){
fprintf(stderr,"Station description: %s\n",info.description);
}
if(info.genre!=NULL){
fprintf(stderr,"Station genre: %s\n",info.genre);
}
if(info.url!=NULL){
fprintf(stderr,"Station homepage: %s\n",info.url);
}
if(info.bitrate_kbps>=0){
fprintf(stderr,"Station bitrate: %u kbps\n",
(unsigned)info.bitrate_kbps);
}
if(info.is_public>=0){
fprintf(stderr,"%s\n",
info.is_public?"Station is public.":"Station is private.");
}
if(info.server!=NULL){
fprintf(stderr,"Server software: %s\n",info.server);
}
if(info.content_type!=NULL){
fprintf(stderr,"Content-Type: %s\n",info.content_type);
}
is_ssl=info.is_ssl;
opus_server_info_clear(&info);
}
}
if(of==NULL){
fprintf(stderr,"Failed to open file '%s': %i\n",_argv[1],ret);
return EXIT_FAILURE;
}
duration=0;
output_seekable=fseek(stdout,0,SEEK_CUR)!=-1;
if(op_seekable(of)){
opus_int64 size;
fprintf(stderr,"Total number of links: %i\n",op_link_count(of));
duration=op_pcm_total(of,-1);
fprintf(stderr,"Total duration: ");
print_duration(stderr,duration,3);
fprintf(stderr," (%li samples @ 48 kHz)\n",(long)duration);
size=op_raw_total(of,-1);
fprintf(stderr,"Total size: ");
print_size(stderr,size,0,"");
fprintf(stderr,"\n");
}
else if(!output_seekable){
fprintf(stderr,"WARNING: Neither input nor output are seekable.\n");
fprintf(stderr,
"Writing non-standard WAV header with invalid chunk sizes.\n");
}
make_wav_header(wav_header,duration);
if(!fwrite(wav_header,sizeof(wav_header),1,stdout)){
fprintf(stderr,"Error writing WAV header: %s\n",strerror(errno));
ret=EXIT_FAILURE;
}
else{
ogg_int64_t pcm_offset;
ogg_int64_t pcm_print_offset;
ogg_int64_t nsamples;
opus_int32 bitrate;
int prev_li;
prev_li=-1;
nsamples=0;
pcm_offset=op_pcm_tell(of);
if(pcm_offset!=0){
fprintf(stderr,"Non-zero starting PCM offset: %li\n",(long)pcm_offset);
}
pcm_print_offset=pcm_offset-48000;
bitrate=0;
for(;;){
ogg_int64_t next_pcm_offset;
opus_int16 pcm[120*48*2];
unsigned char out[120*48*2*2];
int li;
int si;
/*Although we would generally prefer to use the float interface, WAV
files with signed, 16-bit little-endian samples are far more
universally supported, so that's what we output.*/
ret=op_read_stereo(of,pcm,sizeof(pcm)/sizeof(*pcm));
if(ret==OP_HOLE){
fprintf(stderr,"\nHole detected! Corrupt file segment?\n");
continue;
}
else if(ret<0){
fprintf(stderr,"\nError decoding '%s': %i\n",_argv[1],ret);
if(is_ssl)fprintf(stderr,"Possible truncation attack?\n");
ret=EXIT_FAILURE;
break;
}
li=op_current_link(of);
if(li!=prev_li){
const OpusHead *head;
const OpusTags *tags;
int binary_suffix_len;
int ci;
/*We found a new link.
Print out some information.*/
fprintf(stderr,"Decoding link %i: \n",li);
head=op_head(of,li);
fprintf(stderr," Channels: %i\n",head->channel_count);
if(op_seekable(of)){
ogg_int64_t duration;
opus_int64 size;
duration=op_pcm_total(of,li);
fprintf(stderr," Duration: ");
print_duration(stderr,duration,3);
fprintf(stderr," (%li samples @ 48 kHz)\n",(long)duration);
size=op_raw_total(of,li);
fprintf(stderr," Size: ");
print_size(stderr,size,0,"");
fprintf(stderr,"\n");
}
if(head->input_sample_rate){
fprintf(stderr," Original sampling rate: %lu Hz\n",
(unsigned long)head->input_sample_rate);
}
tags=op_tags(of,li);
fprintf(stderr," Encoded by: %s\n",tags->vendor);
for(ci=0;ci<tags->comments;ci++){
const char *comment;
comment=tags->user_comments[ci];
if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,comment)==0){
OpusPictureTag pic;
int err;
err=opus_picture_tag_parse(&pic,comment);
fprintf(stderr," %.23s",comment);
if(err>=0){
fprintf(stderr,"%u|%s|%s|%ux%ux%u",pic.type,pic.mime_type,
pic.description,pic.width,pic.height,pic.depth);
if(pic.colors!=0)fprintf(stderr,"/%u",pic.colors);
if(pic.format==OP_PIC_FORMAT_URL){
fprintf(stderr,"|%s\n",pic.data);
}
else{
fprintf(stderr,"|<%u bytes of image data>\n",pic.data_length);
}
opus_picture_tag_clear(&pic);
}
else fprintf(stderr,"<error parsing picture tag>\n");
}
else fprintf(stderr," %s\n",tags->user_comments[ci]);
}
if(opus_tags_get_binary_suffix(tags,&binary_suffix_len)!=NULL){
fprintf(stderr,"<%u bytes of unknown binary metadata>\n",
binary_suffix_len);
}
fprintf(stderr,"\n");
if(!op_seekable(of)){
pcm_offset=op_pcm_tell(of)-ret;
if(pcm_offset!=0){
fprintf(stderr,"Non-zero starting PCM offset in link %i: %li\n",
li,(long)pcm_offset);
}
}
}
if(li!=prev_li||pcm_offset>=pcm_print_offset+48000){
opus_int32 next_bitrate;
opus_int64 raw_offset;
next_bitrate=op_bitrate_instant(of);
if(next_bitrate>=0)bitrate=next_bitrate;
raw_offset=op_raw_tell(of);
fprintf(stderr,"\r ");
print_size(stderr,raw_offset,0,"");
fprintf(stderr," ");
print_duration(stderr,pcm_offset,0);
fprintf(stderr," (");
print_size(stderr,bitrate,1," ");
fprintf(stderr,"bps) \r");
pcm_print_offset=pcm_offset;
fflush(stderr);
}
next_pcm_offset=op_pcm_tell(of);
if(pcm_offset+ret!=next_pcm_offset){
fprintf(stderr,"\nPCM offset gap! %li+%i!=%li\n",
(long)pcm_offset,ret,(long)next_pcm_offset);
}
pcm_offset=next_pcm_offset;
if(ret<=0){
ret=EXIT_SUCCESS;
break;
}
/*Ensure the data is little-endian before writing it out.*/
for(si=0;si<2*ret;si++){
out[2*si+0]=(unsigned char)(pcm[si]&0xFF);
out[2*si+1]=(unsigned char)(pcm[si]>>8&0xFF);
}
if(!fwrite(out,sizeof(*out)*4*ret,1,stdout)){
fprintf(stderr,"\nError writing decoded audio data: %s\n",
strerror(errno));
ret=EXIT_FAILURE;
break;
}
nsamples+=ret;
prev_li=li;
}
if(ret==EXIT_SUCCESS){
fprintf(stderr,"\nDone: played ");
print_duration(stderr,nsamples,3);
fprintf(stderr," (%li samples @ 48 kHz).\n",(long)nsamples);
}
if(op_seekable(of)&&nsamples!=duration){
fprintf(stderr,"\nWARNING: "
"Number of output samples does not match declared file duration.\n");
if(!output_seekable)fprintf(stderr,"Output WAV file will be corrupt.\n");
}
if(output_seekable&&nsamples!=duration){
make_wav_header(wav_header,nsamples);
if(fseek(stdout,0,SEEK_SET)||
!fwrite(wav_header,sizeof(wav_header),1,stdout)){
fprintf(stderr,"Error rewriting WAV header: %s\n",strerror(errno));
ret=EXIT_FAILURE;
}
}
}
op_free(of);
return ret;
}

View File

@ -0,0 +1,464 @@
/********************************************************************
* *
* 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/ *
* *
********************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/*For fileno()*/
#if !defined(_POSIX_SOURCE)
# define _POSIX_SOURCE 1
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <string.h>
#include <opusfile.h>
#if defined(_WIN32)
# include "win32utf8.h"
# undef fileno
# define fileno _fileno
#endif
/*Use shorts, they're smaller.*/
#if !defined(OP_FIXED_POINT)
# define OP_FIXED_POINT (1)
#endif
#if defined(OP_FIXED_POINT)
typedef opus_int16 op_sample;
# define op_read_native op_read
/*TODO: The convergence after 80 ms of preroll is far from exact.
Our comparison is very rough.
Need to find some way to do this better.*/
# define MATCH_TOL (16384)
# define ABS(_x) ((_x)<0?-(_x):(_x))
# define MATCH(_a,_b) (ABS((_a)-(_b))<MATCH_TOL)
/*Don't have fixed-point downmixing code.*/
# undef OP_WRITE_SEEK_SAMPLES
#else
typedef float op_sample;
# define op_read_native op_read_float
/*TODO: The convergence after 80 ms of preroll is far from exact.
Our comparison is very rough.
Need to find some way to do this better.*/
# define MATCH_TOL (16384.0/32768)
# define FABS(_x) ((_x)<0?-(_x):(_x))
# define MATCH(_a,_b) (FABS((_a)-(_b))<MATCH_TOL)
# if defined(OP_WRITE_SEEK_SAMPLES)
/*Matrices for downmixing from the supported channel counts to stereo.*/
static const float DOWNMIX_MATRIX[8][8][2]={
/*mono*/
{
{1.F,1.F}
},
/*stereo*/
{
{1.F,0.F},{0.F,1.F}
},
/*3.0*/
{
{0.5858F,0.F},{0.4142F,0.4142F},{0,0.5858F}
},
/*quadrophonic*/
{
{0.4226F,0.F},{0,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
},
/*5.0*/
{
{0.651F,0.F},{0.46F,0.46F},{0,0.651F},{0.5636F,0.3254F},{0.3254F,0.5636F}
},
/*5.1*/
{
{0.529F,0.F},{0.3741F,0.3741F},{0.F,0.529F},{0.4582F,0.2645F},
{0.2645F,0.4582F},{0.3741F,0.3741F}
},
/*6.1*/
{
{0.4553F,0.F},{0.322F,0.322F},{0.F,0.4553F},{0.3943F,0.2277F},
{0.2277F,0.3943F},{0.2788F,0.2788F},{0.322F,0.322F}
},
/*7.1*/
{
{0.3886F,0.F},{0.2748F,0.2748F},{0.F,0.3886F},{0.3366F,0.1943F},
{0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F},{0.2748F,0.2748F}
}
};
static void write_samples(float *_samples,int _nsamples,int _nchannels){
float stereo_pcm[120*48*2];
int i;
for(i=0;i<_nsamples;i++){
float l;
float r;
int ci;
l=r=0.F;
for(ci=0;ci<_nchannels;ci++){
l+=DOWNMIX_MATRIX[_nchannels-1][ci][0]*_samples[i*_nchannels+ci];
r+=DOWNMIX_MATRIX[_nchannels-1][ci][1]*_samples[i*_nchannels+ci];
}
stereo_pcm[2*i+0]=l;
stereo_pcm[2*i+1]=r;
}
fwrite(stereo_pcm,sizeof(*stereo_pcm)*2,_nsamples,stdout);
}
# endif
#endif
static long nfailures;
static void verify_seek(OggOpusFile *_of,opus_int64 _byte_offset,
ogg_int64_t _pcm_offset,ogg_int64_t _pcm_length,op_sample *_bigassbuffer){
opus_int64 byte_offset;
ogg_int64_t pcm_offset;
ogg_int64_t duration;
op_sample buffer[120*48*8];
int nchannels;
int nsamples;
int li;
int lj;
int i;
byte_offset=op_raw_tell(_of);
if(_byte_offset!=-1&&byte_offset<_byte_offset){
fprintf(stderr,"\nRaw position out of tolerance: requested %li, "
"got %li.\n",(long)_byte_offset,(long)byte_offset);
nfailures++;
}
pcm_offset=op_pcm_tell(_of);
if(_pcm_offset!=-1&&pcm_offset>_pcm_offset){
fprintf(stderr,"\nPCM position out of tolerance: requested %li, "
"got %li.\n",(long)_pcm_offset,(long)pcm_offset);
nfailures++;
}
if(pcm_offset<0||pcm_offset>_pcm_length){
fprintf(stderr,"\nPCM position out of bounds: got %li.\n",
(long)pcm_offset);
nfailures++;
}
nsamples=op_read_native(_of,buffer,sizeof(buffer)/sizeof(*buffer),&li);
if(nsamples<0){
fprintf(stderr,"\nFailed to read PCM data after seek: %i\n",nsamples);
nfailures++;
li=op_current_link(_of);
}
for(lj=0;lj<li;lj++){
duration=op_pcm_total(_of,lj);
if(0<=pcm_offset&&pcm_offset<duration){
fprintf(stderr,"\nPCM data after seek came from the wrong link: "
"expected %i, got %i.\n",lj,li);
nfailures++;
}
pcm_offset-=duration;
if(_bigassbuffer!=NULL)_bigassbuffer+=op_channel_count(_of,lj)*duration;
}
duration=op_pcm_total(_of,li);
if(pcm_offset+nsamples>duration){
fprintf(stderr,"\nPCM data after seek exceeded link duration: "
"limit %li, got %li.\n",(long)duration,(long)(pcm_offset+nsamples));
nfailures++;
}
nchannels=op_channel_count(_of,li);
if(_bigassbuffer!=NULL){
for(i=0;i<nsamples*nchannels;i++){
if(!MATCH(buffer[i],_bigassbuffer[pcm_offset*nchannels+i])){
ogg_int64_t j;
fprintf(stderr,"\nData after seek doesn't match declared PCM "
"position: mismatch %G\n",
(double)buffer[i]-_bigassbuffer[pcm_offset*nchannels+i]);
for(j=0;j<duration-nsamples;j++){
for(i=0;i<nsamples*nchannels;i++){
if(!MATCH(buffer[i],_bigassbuffer[j*nchannels+i]))break;
}
if(i==nsamples*nchannels){
fprintf(stderr,"\nData after seek appears to match position %li.\n",
(long)i);
}
}
nfailures++;
break;
}
}
}
#if defined(OP_WRITE_SEEK_SAMPLES)
write_samples(buffer,nsamples,nchannels);
#endif
}
#define OP_MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
/*A simple wrapper that lets us count the number of underlying seek calls.*/
static op_seek_func real_seek;
static long nreal_seeks;
static int seek_stat_counter(void *_stream,opus_int64 _offset,int _whence){
if(_whence==SEEK_SET)nreal_seeks++;
/*SEEK_CUR with an offset of 0 is free, as is SEEK_END with an offset of 0
(assuming we know the file size), so don't count them.*/
else if(_offset!=0)nreal_seeks++;
return (*real_seek)(_stream,_offset,_whence);
}
#define NSEEK_TESTS (1000)
static void print_duration(FILE *_fp,ogg_int64_t _nsamples){
ogg_int64_t seconds;
ogg_int64_t minutes;
ogg_int64_t hours;
ogg_int64_t days;
ogg_int64_t weeks;
seconds=_nsamples/48000;
_nsamples-=seconds*48000;
minutes=seconds/60;
seconds-=minutes*60;
hours=minutes/60;
minutes-=hours*60;
days=hours/24;
hours-=days*24;
weeks=days/7;
days-=weeks*7;
if(weeks)fprintf(_fp,"%liw",(long)weeks);
if(weeks||days)fprintf(_fp,"%id",(int)days);
if(weeks||days||hours){
if(weeks||days)fprintf(_fp,"%02ih",(int)hours);
else fprintf(_fp,"%ih",(int)hours);
}
if(weeks||days||hours||minutes){
if(weeks||days||hours)fprintf(_fp,"%02im",(int)minutes);
else fprintf(_fp,"%im",(int)minutes);
fprintf(_fp,"%02i",(int)seconds);
}
else fprintf(_fp,"%i",(int)seconds);
fprintf(_fp,".%03is",(int)(_nsamples+24)/48);
}
int main(int _argc,const char **_argv){
OpusFileCallbacks cb;
OggOpusFile *of;
void *fp;
#if defined(_WIN32)
win32_utf8_setup(&_argc,&_argv);
#endif
if(_argc!=2){
fprintf(stderr,"Usage: %s <file.opus>\n",_argv[0]);
return EXIT_FAILURE;
}
memset(&cb,0,sizeof(cb));
if(strcmp(_argv[1],"-")==0)fp=op_fdopen(&cb,fileno(stdin),"rb");
else{
/*Try to treat the argument as a URL.*/
fp=op_url_stream_create(&cb,_argv[1],
OP_SSL_SKIP_CERTIFICATE_CHECK(1),NULL);
/*Fall back assuming it's a regular file name.*/
if(fp==NULL)fp=op_fopen(&cb,_argv[1],"rb");
}
if(cb.seek!=NULL){
real_seek=cb.seek;
cb.seek=seek_stat_counter;
}
of=op_open_callbacks(fp,&cb,NULL,0,NULL);
if(of==NULL){
fprintf(stderr,"Failed to open file '%s'.\n",_argv[1]);
return EXIT_FAILURE;
}
if(op_seekable(of)){
op_sample *bigassbuffer;
ogg_int64_t size;
ogg_int64_t pcm_offset;
ogg_int64_t pcm_length;
ogg_int64_t nsamples;
long max_seeks;
int nlinks;
int ret;
int li;
int i;
/*Because we want to do sample-level verification that the seek does what
it claimed, decode the entire file into memory.*/
nlinks=op_link_count(of);
fprintf(stderr,"Opened file containing %i links with %li seeks "
"(%0.3f per link).\n",nlinks,nreal_seeks,nreal_seeks/(double)nlinks);
/*Reset the seek counter.*/
nreal_seeks=0;
nsamples=0;
for(li=0;li<nlinks;li++){
nsamples+=op_pcm_total(of,li)*op_channel_count(of,li);
}
/*Until we find another way to do the comparisons that solves the MATCH_TOL
problem, disable this.*/
#if 0
bigassbuffer=_ogg_malloc(sizeof(*bigassbuffer)*nsamples);
if(bigassbuffer==NULL){
fprintf(stderr,
"Buffer allocation failed. Seek offset detection disabled.\n");
}
#else
bigassbuffer=NULL;
#endif
pcm_offset=op_pcm_tell(of);
if(pcm_offset!=0){
fprintf(stderr,"Initial PCM offset was not 0, got %li instead.!\n",
(long)pcm_offset);
nfailures++;
}
/*Disabling the linear scan for now.
Only test on non-borken files!*/
#if 0
{
op_sample smallerbuffer[120*48*8];
ogg_int64_t pcm_print_offset;
ogg_int64_t si;
opus_int32 bitrate;
int saw_hole;
pcm_print_offset=pcm_offset-48000;
bitrate=0;
saw_hole=0;
for(si=0;si<nsamples;){
ogg_int64_t next_pcm_offset;
opus_int32 next_bitrate;
op_sample *buf;
int buf_size;
buf=bigassbuffer==NULL?smallerbuffer:bigassbuffer+si;
buf_size=(int)OP_MIN(nsamples-si,
(int)(sizeof(smallerbuffer)/sizeof(*smallerbuffer))),
ret=op_read_native(of,buf,buf_size,&li);
if(ret==OP_HOLE){
/*Only warn once in a row.*/
if(saw_hole)continue;
saw_hole=1;
/*This is just a warning.
As long as the timestamps are still contiguous we're okay.*/
fprintf(stderr,"\nHole in PCM data at sample %li\n",
(long)pcm_offset);
continue;
}
else if(ret<=0){
fprintf(stderr,"\nFailed to read PCM data: %i\n",ret);
exit(EXIT_FAILURE);
}
saw_hole=0;
/*If we have gaps in the PCM positions, seeking is not likely to work
near them.*/
next_pcm_offset=op_pcm_tell(of);
if(pcm_offset+ret!=next_pcm_offset){
fprintf(stderr,"\nGap in PCM offset: expecting %li, got %li\n",
(long)(pcm_offset+ret),(long)next_pcm_offset);
nfailures++;
}
pcm_offset=next_pcm_offset;
si+=ret*op_channel_count(of,li);
if(pcm_offset>=pcm_print_offset+48000){
next_bitrate=op_bitrate_instant(of);
if(next_bitrate>=0)bitrate=next_bitrate;
fprintf(stderr,"\r%s... [%li left] (%0.3f kbps) ",
bigassbuffer==NULL?"Scanning":"Loading",nsamples-si,bitrate/1000.0);
pcm_print_offset=pcm_offset;
}
}
ret=op_read_native(of,smallerbuffer,8,&li);
if(ret<0){
fprintf(stderr,"Failed to read PCM data: %i\n",ret);
nfailures++;
}
if(ret>0){
fprintf(stderr,"Read too much PCM data!\n");
nfailures++;
}
}
#endif
pcm_length=op_pcm_total(of,-1);
size=op_raw_total(of,-1);
fprintf(stderr,"\rLoaded (%0.3f kbps average). \n",
op_bitrate(of,-1)/1000.0);
fprintf(stderr,"Testing raw seeking to random places in %li bytes...\n",
(long)size);
max_seeks=0;
for(i=0;i<NSEEK_TESTS;i++){
long nseeks_tmp;
opus_int64 byte_offset;
nseeks_tmp=nreal_seeks;
byte_offset=(opus_int64)(rand()/(double)RAND_MAX*size);
fprintf(stderr,"\r\t%3i [raw position %li]... ",
i,(long)byte_offset);
ret=op_raw_seek(of,byte_offset);
if(ret<0){
fprintf(stderr,"\nSeek failed: %i.\n",ret);
nfailures++;
}
if(i==28){
i=28;
}
verify_seek(of,byte_offset,-1,pcm_length,bigassbuffer);
nseeks_tmp=nreal_seeks-nseeks_tmp;
max_seeks=nseeks_tmp>max_seeks?nseeks_tmp:max_seeks;
}
fprintf(stderr,"\rTotal seek operations: %li (%.3f per raw seek, %li maximum).\n",
nreal_seeks,nreal_seeks/(double)NSEEK_TESTS,max_seeks);
nreal_seeks=0;
fprintf(stderr,"Testing exact PCM seeking to random places in %li "
"samples (",(long)pcm_length);
print_duration(stderr,pcm_length);
fprintf(stderr,")...\n");
max_seeks=0;
for(i=0;i<NSEEK_TESTS;i++){
ogg_int64_t pcm_offset2;
long nseeks_tmp;
nseeks_tmp=nreal_seeks;
pcm_offset=(ogg_int64_t)(rand()/(double)RAND_MAX*pcm_length);
fprintf(stderr,"\r\t%3i [PCM position %li]... ",
i,(long)pcm_offset);
ret=op_pcm_seek(of,pcm_offset);
if(ret<0){
fprintf(stderr,"\nSeek failed: %i.\n",ret);
nfailures++;
}
pcm_offset2=op_pcm_tell(of);
if(pcm_offset!=pcm_offset2){
fprintf(stderr,"\nDeclared PCM position did not perfectly match "
"request: requested %li, got %li.\n",
(long)pcm_offset,(long)pcm_offset2);
nfailures++;
}
verify_seek(of,-1,pcm_offset,pcm_length,bigassbuffer);
nseeks_tmp=nreal_seeks-nseeks_tmp;
max_seeks=nseeks_tmp>max_seeks?nseeks_tmp:max_seeks;
}
fprintf(stderr,"\rTotal seek operations: %li (%.3f per exact seek, %li maximum).\n",
nreal_seeks,nreal_seeks/(double)NSEEK_TESTS,max_seeks);
nreal_seeks=0;
fprintf(stderr,"OK.\n");
_ogg_free(bigassbuffer);
}
else{
fprintf(stderr,"Input was not seekable.\n");
exit(EXIT_FAILURE);
}
op_free(of);
if(nfailures>0){
fprintf(stderr,"FAILED: %li failure conditions encountered.\n",nfailures);
}
return nfailures!=0?EXIT_FAILURE:EXIT_SUCCESS;
}

View File

@ -0,0 +1,111 @@
#if defined(_WIN32)
# include <stdio.h>
# include <stdlib.h>
# include <wchar.h>
/*We need the following two to set stdin/stdout to binary.*/
# include <io.h>
# include <fcntl.h>
# define WIN32_LEAN_AND_MEAN
# define WIN32_EXTRA_LEAN
# include <windows.h>
# include "win32utf8.h"
static char *utf16_to_utf8(const wchar_t *_src){
char *dst;
size_t len;
size_t si;
size_t di;
len=wcslen(_src);
dst=(char *)malloc(sizeof(*dst)*(3*len+1));
if(dst==NULL)return dst;
for(di=si=0;si<len;si++){
unsigned c0;
c0=_src[si];
if(c0<0x80){
/*Can be represented by a 1-byte sequence.*/
dst[di++]=(char)c0;
continue;
}
else if(c0<0x800){
/*Can be represented by a 2-byte sequence.*/
dst[di++]=(char)(0xC0|c0>>6);
dst[di++]=(char)(0x80|c0&0x3F);
continue;
}
else if(c0>=0xD800&&c0<0xDC00){
unsigned c1;
/*This is safe, because c0 was not 0 and _src is NUL-terminated.*/
c1=_src[si+1];
if(c1>=0xDC00&&c1<0xE000){
unsigned w;
/*Surrogate pair.*/
w=((c0&0x3FF)<<10|c1&0x3FF)+0x10000;
/*Can be represented by a 4-byte sequence.*/
dst[di++]=(char)(0xF0|w>>18);
dst[di++]=(char)(0x80|w>>12&0x3F);
dst[di++]=(char)(0x80|w>>6&0x3F);
dst[di++]=(char)(0x80|w&0x3F);
si++;
continue;
}
}
/*Anything else is either a valid 3-byte sequence, an invalid surrogate
pair, or 'not a character'.
In the latter two cases, we just encode the value as a 3-byte
sequence anyway (producing technically invalid UTF-8).
Later error handling will detect the problem, with a better
chance of giving a useful error message.*/
dst[di++]=(char)(0xE0|c0>>12);
dst[di++]=(char)(0x80|c0>>6&0x3F);
dst[di++]=(char)(0x80|c0&0x3F);
}
dst[di++]='\0';
return dst;
}
typedef LPWSTR *(APIENTRY *command_line_to_argv_w_func)(LPCWSTR cmd_line,
int *num_args);
/*Make a best-effort attempt to support UTF-8 on Windows.*/
void win32_utf8_setup(int *_argc,const char ***_argv){
HMODULE hlib;
/*We need to set stdin/stdout to binary mode.
This is unrelated to UTF-8 support, but it's platform specific and we need
to do it in the same places.*/
_setmode(_fileno(stdin),_O_BINARY);
_setmode(_fileno(stdout),_O_BINARY);
hlib=LoadLibraryA("shell32.dll");
if(hlib!=NULL){
command_line_to_argv_w_func command_line_to_argv_w;
/*This function is only available on Windows 2000 or later.*/
command_line_to_argv_w=(command_line_to_argv_w_func)GetProcAddress(hlib,
"CommandLineToArgvW");
if(command_line_to_argv_w!=NULL){
wchar_t **argvw;
int argc;
argvw=(*command_line_to_argv_w)(GetCommandLineW(),&argc);
if(argvw!=NULL){
int ai;
/*Really, I don't see why argc would ever differ from *_argc, but let's
be paranoid.*/
if(argc>*_argc)argc=*_argc;
for(ai=0;ai<argc;ai++){
char *argv;
argv=utf16_to_utf8(argvw[ai]);
if(argv!=NULL)(*_argv)[ai]=argv;
}
*_argc=argc;
LocalFree(argvw);
}
}
FreeLibrary(hlib);
}
# if defined(CP_UTF8)
/*This does not work correctly in all environments (it breaks output in
mingw32 for me), and requires a Unicode font (e.g., when using the default
Raster font, even characters that are available in the font's codepage
won't display properly).*/
/*SetConsoleOutputCP(CP_UTF8);*/
# endif
}
#endif

View File

@ -0,0 +1,9 @@
#if !defined(_win32utf8_H)
# define _win32utf8_H (1)
# if defined(_WIN32)
/*Make a best-effort attempt to support UTF-8 on Windows.*/
void win32_utf8_setup(int *_argc,const char ***_argv);
# endif
#endif