1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/decoder-ogg.c Sat Dec 29 14:47:40 2007 +0100
1.3 @@ -0,0 +1,377 @@
1.4 +/*
1.5 + * MP3/MPlayer plugin to VDR (C++)
1.6 + *
1.7 + * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
1.8 + *
1.9 + * This code is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License
1.11 + * as published by the Free Software Foundation; either version 2
1.12 + * of the License, or (at your option) any later version.
1.13 + *
1.14 + * This code is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1.22 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
1.23 + */
1.24 +
1.25 +#ifdef HAVE_VORBISFILE
1.26 +
1.27 +#include <stdlib.h>
1.28 +#include <stdio.h>
1.29 +#include <errno.h>
1.30 +
1.31 +#include "common.h"
1.32 +#include "decoder-ogg.h"
1.33 +
1.34 +// --- cOggFile ----------------------------------------------------------------
1.35 +
1.36 +cOggFile::cOggFile(const char *Filename)
1.37 +:cFileInfo(Filename)
1.38 +{
1.39 + canSeek=opened=false;
1.40 +}
1.41 +
1.42 +cOggFile::~cOggFile()
1.43 +{
1.44 + Close();
1.45 +}
1.46 +
1.47 +bool cOggFile::Open(bool log)
1.48 +{
1.49 + if(opened) {
1.50 + if(canSeek) return (Seek()>=0);
1.51 + return true;
1.52 + }
1.53 +
1.54 + if(FileInfo(log)) {
1.55 + FILE *f=fopen(Filename,"r");
1.56 + if(f) {
1.57 + int r=ov_open(f,&vf,0,0);
1.58 + if(!r) {
1.59 + canSeek=(ov_seekable(&vf)!=0);
1.60 + opened=true;
1.61 + }
1.62 + else {
1.63 + fclose(f);
1.64 + if(log) Error("open",r);
1.65 + }
1.66 + }
1.67 + else if(log) { esyslog("ERROR: failed to open file %s: %s",Filename,strerror(errno)); }
1.68 + }
1.69 + return opened;
1.70 +}
1.71 +
1.72 +void cOggFile::Close(void)
1.73 +{
1.74 + if(opened) { ov_clear(&vf); opened=false; }
1.75 +}
1.76 +
1.77 +void cOggFile::Error(const char *action, const int err)
1.78 +{
1.79 + char *errstr;
1.80 + switch(err) {
1.81 + case OV_FALSE: errstr="false/no data available"; break;
1.82 + case OV_EOF: errstr="EOF"; break;
1.83 + case OV_HOLE: errstr="missing or corrupted data"; break;
1.84 + case OV_EREAD: errstr="read error"; break;
1.85 + case OV_EFAULT: errstr="internal error"; break;
1.86 + case OV_EIMPL: errstr="unimplemented feature"; break;
1.87 + case OV_EINVAL: errstr="invalid argument"; break;
1.88 + case OV_ENOTVORBIS: errstr="no Ogg Vorbis stream"; break;
1.89 + case OV_EBADHEADER: errstr="corrupted Ogg Vorbis stream"; break;
1.90 + case OV_EVERSION: errstr="unsupported bitstream version"; break;
1.91 + case OV_ENOTAUDIO: errstr="ENOTAUDIO"; break;
1.92 + case OV_EBADPACKET: errstr="EBADPACKET"; break;
1.93 + case OV_EBADLINK: errstr="corrupted link"; break;
1.94 + case OV_ENOSEEK: errstr="stream not seekable"; break;
1.95 + default: errstr="unspecified error"; break;
1.96 + }
1.97 + esyslog("ERROR: vorbisfile %s failed on %s: %s",action,Filename,errstr);
1.98 +}
1.99 +
1.100 +long long cOggFile::IndexMs(void)
1.101 +{
1.102 + double p=ov_time_tell(&vf);
1.103 + if(p<0.0) p=0.0;
1.104 + return (long long)(p*1000.0);
1.105 +}
1.106 +
1.107 +long long cOggFile::Seek(long long posMs, bool relativ)
1.108 +{
1.109 + if(relativ) posMs+=IndexMs();
1.110 + int r=ov_time_seek(&vf,(double)posMs/1000.0);
1.111 + if(r) {
1.112 + Error("seek",r);
1.113 + return -1;
1.114 + }
1.115 + posMs=IndexMs();
1.116 + return posMs;
1.117 +}
1.118 +
1.119 +int cOggFile::Stream(short *buffer, int samples)
1.120 +{
1.121 + int n;
1.122 + do {
1.123 + int stream;
1.124 + n=ov_read(&vf,(char *)buffer,samples*2,0,2,1,&stream);
1.125 + } while(n==OV_HOLE);
1.126 + if(n<0) Error("read",n);
1.127 + return (n/2);
1.128 +}
1.129 +
1.130 +// --- cOggInfo ----------------------------------------------------------------
1.131 +
1.132 +cOggInfo::cOggInfo(cOggFile *File)
1.133 +{
1.134 + file=File;
1.135 +}
1.136 +
1.137 +bool cOggInfo::Abort(bool result)
1.138 +{
1.139 + if(!keepOpen) file->Close();
1.140 + return result;
1.141 +}
1.142 +
1.143 +bool cOggInfo::DoScan(bool KeepOpen)
1.144 +{
1.145 + keepOpen=KeepOpen;
1.146 + if(!file->Open()) return Abort(false);
1.147 + if(HasInfo()) return Abort(true);
1.148 +
1.149 + // check the infocache
1.150 + cCacheData *dat=InfoCache.Search(file);
1.151 + if(dat) {
1.152 + Set(dat); dat->Unlock();
1.153 + if(!DecoderID) {
1.154 + DecoderID=DEC_OGG;
1.155 + InfoCache.Cache(this,file);
1.156 + }
1.157 + return Abort(true);
1.158 + }
1.159 +
1.160 + Clear();
1.161 +
1.162 + vorbis_comment *vc=ov_comment(&file->vf,-1);
1.163 + if(vc) {
1.164 + for(int i=0 ; i<vc->comments ; i++) {
1.165 + const char *cc=vc->user_comments[i];
1.166 + d(printf("ogg: comment%d='%s'\n",i,cc))
1.167 + char *p=strchr(cc,'=');
1.168 + if(p) {
1.169 + const int len=p-cc;
1.170 + p++;
1.171 + if(!strncasecmp(cc,"TITLE",len)) {
1.172 + if(!Title) Title=strdup(p);
1.173 + }
1.174 + else if(!strncasecmp(cc,"ARTIST",len)) {
1.175 + if(!Artist) Artist=strdup(p);
1.176 + }
1.177 + else if(!strncasecmp(cc,"ALBUM",len)) {
1.178 + if(!Album) Album=strdup(p);
1.179 + }
1.180 + else if(!strncasecmp(cc,"YEAR",len)) {
1.181 + if(Year<0) {
1.182 + Year=atoi(p);
1.183 + if(Year<1800 || Year>2100) Year=-1;
1.184 + }
1.185 + }
1.186 + }
1.187 + }
1.188 + }
1.189 + if(!Title) FakeTitle(file->Filename);
1.190 +
1.191 + vorbis_info *vi=ov_info(&file->vf,-1);
1.192 + if(!vi) Abort(false);
1.193 + d(printf("ogg: info ch=%d srate=%ld brate_low=%ld brate_high=%ld brate_avg=%ld\n",
1.194 + vi->channels,vi->rate,vi->bitrate_lower,vi->bitrate_upper,vi->bitrate_nominal))
1.195 + Channels=vi->channels;
1.196 + ChMode=Channels>1 ? 3:0;
1.197 + SampleFreq=vi->rate;
1.198 + if(vi->bitrate_upper>0 && vi->bitrate_lower>0) {
1.199 + Bitrate=vi->bitrate_lower;
1.200 + MaxBitrate=vi->bitrate_upper;
1.201 + }
1.202 + else
1.203 + Bitrate=vi->bitrate_nominal;
1.204 +
1.205 + Total=(int)ov_time_total(&file->vf,-1);
1.206 + Frames=-1;
1.207 + DecoderID=DEC_OGG;
1.208 +
1.209 + InfoDone();
1.210 + InfoCache.Cache(this,file);
1.211 + return Abort(true);
1.212 +}
1.213 +
1.214 +// --- cOggDecoder -------------------------------------------------------------
1.215 +
1.216 +cOggDecoder::cOggDecoder(const char *Filename)
1.217 +:cDecoder(Filename)
1.218 +,file(Filename)
1.219 +,info(&file)
1.220 +{
1.221 + pcm=0;
1.222 +}
1.223 +
1.224 +cOggDecoder::~cOggDecoder()
1.225 +{
1.226 + Clean();
1.227 +}
1.228 +
1.229 +bool cOggDecoder::Valid(void)
1.230 +{
1.231 + bool res=false;
1.232 + if(TryLock()) {
1.233 + if(file.Open(false)) res=true;
1.234 + Unlock();
1.235 + }
1.236 + return res;
1.237 +}
1.238 +
1.239 +cFileInfo *cOggDecoder::FileInfo(void)
1.240 +{
1.241 + cFileInfo *fi=0;
1.242 + if(file.HasInfo()) fi=&file;
1.243 + else if(TryLock()){
1.244 + if(file.Open()) { fi=&file; file.Close(); }
1.245 + Unlock();
1.246 + }
1.247 + return fi;
1.248 +}
1.249 +
1.250 +cSongInfo *cOggDecoder::SongInfo(bool get)
1.251 +{
1.252 + cSongInfo *si=0;
1.253 + if(info.HasInfo()) si=&info;
1.254 + else if(get && TryLock()) {
1.255 + if(info.DoScan(false)) si=&info;
1.256 + Unlock();
1.257 + }
1.258 + return si;
1.259 +}
1.260 +
1.261 +cPlayInfo *cOggDecoder::PlayInfo(void)
1.262 +{
1.263 + if(playing) {
1.264 + pi.Index=index/1000;
1.265 + pi.Total=info.Total;
1.266 + return π
1.267 + }
1.268 + return 0;
1.269 +}
1.270 +
1.271 +void cOggDecoder::Init(void)
1.272 +{
1.273 + Clean();
1.274 + pcm=new struct mad_pcm;
1.275 + index=0;
1.276 +}
1.277 +
1.278 +bool cOggDecoder::Clean(void)
1.279 +{
1.280 + playing=false;
1.281 + delete pcm; pcm=0;
1.282 + file.Close();
1.283 + return false;
1.284 +}
1.285 +
1.286 +#define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
1.287 +
1.288 +bool cOggDecoder::Start(void)
1.289 +{
1.290 + Lock(true);
1.291 + Init(); playing=true;
1.292 + if(file.Open() && info.DoScan(true)) {
1.293 + d(printf("ogg: open rate=%d channels=%d seek=%d\n",
1.294 + info.SampleFreq,info.Channels,file.CanSeek()))
1.295 + if(info.Channels<=2) {
1.296 + Unlock();
1.297 + return true;
1.298 + }
1.299 + else esyslog("ERROR: cannot play ogg file %s: more than 2 channels",filename);
1.300 + }
1.301 + Clean();
1.302 + Unlock();
1.303 + return false;
1.304 +}
1.305 +
1.306 +bool cOggDecoder::Stop(void)
1.307 +{
1.308 + Lock();
1.309 + if(playing) Clean();
1.310 + Unlock();
1.311 + return true;
1.312 +}
1.313 +
1.314 +struct Decode *cOggDecoder::Done(eDecodeStatus status)
1.315 +{
1.316 + ds.status=status;
1.317 + ds.index=index;
1.318 + ds.pcm=pcm;
1.319 + Unlock(); // release the lock from Decode()
1.320 + return &ds;
1.321 +}
1.322 +
1.323 +struct Decode *cOggDecoder::Decode(void)
1.324 +{
1.325 + Lock(); // this is released in Done()
1.326 + if(playing) {
1.327 + short framebuff[2*SF_SAMPLES];
1.328 + int n=file.Stream(framebuff,SF_SAMPLES);
1.329 + if(n<0) return Done(dsError);
1.330 + if(n==0) return Done(dsEof);
1.331 +
1.332 + pcm->samplerate=info.SampleFreq;
1.333 + pcm->channels=info.Channels;
1.334 + n/=pcm->channels;
1.335 + pcm->length=n;
1.336 + index=file.IndexMs();
1.337 +
1.338 + short *data=framebuff;
1.339 + mad_fixed_t *sam0=pcm->samples[0], *sam1=pcm->samples[1];
1.340 + const int s=MAD_F_FRACBITS+1-(sizeof(short)*8); // shift value for mad_fixed conversion
1.341 + if(pcm->channels>1) {
1.342 + for(; n>0 ; n--) {
1.343 + *sam0++=(*data++) << s;
1.344 + *sam1++=(*data++) << s;
1.345 + }
1.346 + }
1.347 + else {
1.348 + for(; n>0 ; n--)
1.349 + *sam0++=(*data++) << s;
1.350 + }
1.351 + return Done(dsPlay);
1.352 + }
1.353 + return Done(dsError);
1.354 +}
1.355 +
1.356 +bool cOggDecoder::Skip(int Seconds, float bsecs)
1.357 +{
1.358 + Lock();
1.359 + bool res=false;
1.360 + if(playing && file.CanSeek()) {
1.361 + float fsecs=(float)Seconds - bsecs;
1.362 + long long newpos=file.IndexMs()+(long long)(fsecs*1000.0);
1.363 + if(newpos<0) newpos=0;
1.364 + d(printf("ogg: skip: secs=%d fsecs=%f current=%lld new=%lld\n",Seconds,fsecs,file.IndexMs(),newpos))
1.365 +
1.366 + newpos=file.Seek(newpos,false);
1.367 + if(newpos>=0) {
1.368 + index=file.IndexMs();
1.369 +#ifdef DEBUG
1.370 + int i=index/1000;
1.371 + printf("ogg: skipping to %02d:%02d\n",i/60,i%60);
1.372 +#endif
1.373 + res=true;
1.374 + }
1.375 + }
1.376 + Unlock();
1.377 + return res;
1.378 +}
1.379 +
1.380 +#endif //HAVE_VORBISFILE