2 * MP3/MPlayer plugin to VDR (C++)
4 * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
6 * This code is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This code is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
22 #ifdef HAVE_VORBISFILE
29 #include "decoder-ogg.h"
31 // --- cOggFile ----------------------------------------------------------------
33 cOggFile::cOggFile(const char *Filename)
44 bool cOggFile::Open(bool log)
47 if(canSeek) return (Seek()>=0);
52 FILE *f=fopen(Filename,"r");
54 int r=ov_open(f,&vf,0,0);
56 canSeek=(ov_seekable(&vf)!=0);
61 if(log) Error("open",r);
64 else if(log) { esyslog("ERROR: failed to open file %s: %s",Filename,strerror(errno)); }
69 void cOggFile::Close(void)
71 if(opened) { ov_clear(&vf); opened=false; }
74 void cOggFile::Error(const char *action, const int err)
78 case OV_FALSE: errstr="false/no data available"; break;
79 case OV_EOF: errstr="EOF"; break;
80 case OV_HOLE: errstr="missing or corrupted data"; break;
81 case OV_EREAD: errstr="read error"; break;
82 case OV_EFAULT: errstr="internal error"; break;
83 case OV_EIMPL: errstr="unimplemented feature"; break;
84 case OV_EINVAL: errstr="invalid argument"; break;
85 case OV_ENOTVORBIS: errstr="no Ogg Vorbis stream"; break;
86 case OV_EBADHEADER: errstr="corrupted Ogg Vorbis stream"; break;
87 case OV_EVERSION: errstr="unsupported bitstream version"; break;
88 case OV_ENOTAUDIO: errstr="ENOTAUDIO"; break;
89 case OV_EBADPACKET: errstr="EBADPACKET"; break;
90 case OV_EBADLINK: errstr="corrupted link"; break;
91 case OV_ENOSEEK: errstr="stream not seekable"; break;
92 default: errstr="unspecified error"; break;
94 esyslog("ERROR: vorbisfile %s failed on %s: %s",action,Filename,errstr);
97 long long cOggFile::IndexMs(void)
99 double p=ov_time_tell(&vf);
101 return (long long)(p*1000.0);
104 long long cOggFile::Seek(long long posMs, bool relativ)
106 if(relativ) posMs+=IndexMs();
107 int r=ov_time_seek(&vf,(double)posMs/1000.0);
116 int cOggFile::Stream(short *buffer, int samples)
121 n=ov_read(&vf,(char *)buffer,samples*2,0,2,1,&stream);
123 if(n<0) Error("read",n);
127 // --- cOggInfo ----------------------------------------------------------------
129 cOggInfo::cOggInfo(cOggFile *File)
134 bool cOggInfo::Abort(bool result)
136 if(!keepOpen) file->Close();
140 bool cOggInfo::DoScan(bool KeepOpen)
143 if(!file->Open()) return Abort(false);
144 if(HasInfo()) return Abort(true);
146 // check the infocache
147 cCacheData *dat=InfoCache.Search(file);
149 Set(dat); dat->Unlock();
152 InfoCache.Cache(this,file);
159 vorbis_comment *vc=ov_comment(&file->vf,-1);
161 for(int i=0 ; i<vc->comments ; i++) {
162 const char *cc=vc->user_comments[i];
163 d(printf("ogg: comment%d='%s'\n",i,cc))
164 char *p=strchr(cc,'=');
168 if(!strncasecmp(cc,"TITLE",len)) {
169 if(!Title) Title=strdup(p);
171 else if(!strncasecmp(cc,"ARTIST",len)) {
172 if(!Artist) Artist=strdup(p);
174 else if(!strncasecmp(cc,"ALBUM",len)) {
175 if(!Album) Album=strdup(p);
177 else if(!strncasecmp(cc,"YEAR",len)) {
180 if(Year<1800 || Year>2100) Year=-1;
186 if(!Title) FakeTitle(file->Filename);
188 vorbis_info *vi=ov_info(&file->vf,-1);
189 if(!vi) Abort(false);
190 d(printf("ogg: info ch=%d srate=%ld brate_low=%ld brate_high=%ld brate_avg=%ld\n",
191 vi->channels,vi->rate,vi->bitrate_lower,vi->bitrate_upper,vi->bitrate_nominal))
192 Channels=vi->channels;
193 ChMode=Channels>1 ? 3:0;
195 if(vi->bitrate_upper>0 && vi->bitrate_lower>0) {
196 Bitrate=vi->bitrate_lower;
197 MaxBitrate=vi->bitrate_upper;
200 Bitrate=vi->bitrate_nominal;
202 Total=(int)ov_time_total(&file->vf,-1);
207 InfoCache.Cache(this,file);
211 // --- cOggDecoder -------------------------------------------------------------
213 cOggDecoder::cOggDecoder(const char *Filename)
221 cOggDecoder::~cOggDecoder()
226 bool cOggDecoder::Valid(void)
230 if(file.Open(false)) res=true;
236 cFileInfo *cOggDecoder::FileInfo(void)
239 if(file.HasInfo()) fi=&file;
241 if(file.Open()) { fi=&file; file.Close(); }
247 cSongInfo *cOggDecoder::SongInfo(bool get)
250 if(info.HasInfo()) si=&info;
251 else if(get && TryLock()) {
252 if(info.DoScan(false)) si=&info;
258 cPlayInfo *cOggDecoder::PlayInfo(void)
268 void cOggDecoder::Init(void)
271 pcm=new struct mad_pcm;
275 bool cOggDecoder::Clean(void)
283 #define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
285 bool cOggDecoder::Start(void)
288 Init(); playing=true;
289 if(file.Open() && info.DoScan(true)) {
290 d(printf("ogg: open rate=%d channels=%d seek=%d\n",
291 info.SampleFreq,info.Channels,file.CanSeek()))
292 if(info.Channels<=2) {
296 else esyslog("ERROR: cannot play ogg file %s: more than 2 channels",filename);
303 bool cOggDecoder::Stop(void)
311 struct Decode *cOggDecoder::Done(eDecodeStatus status)
316 Unlock(); // release the lock from Decode()
320 struct Decode *cOggDecoder::Decode(void)
322 Lock(); // this is released in Done()
324 short framebuff[2*SF_SAMPLES];
325 int n=file.Stream(framebuff,SF_SAMPLES);
326 if(n<0) return Done(dsError);
327 if(n==0) return Done(dsEof);
329 pcm->samplerate=info.SampleFreq;
330 pcm->channels=info.Channels;
333 index=file.IndexMs();
335 short *data=framebuff;
336 mad_fixed_t *sam0=pcm->samples[0], *sam1=pcm->samples[1];
337 const int s=MAD_F_FRACBITS+1-(sizeof(short)*8); // shift value for mad_fixed conversion
338 if(pcm->channels>1) {
340 *sam0++=(*data++) << s;
341 *sam1++=(*data++) << s;
346 *sam0++=(*data++) << s;
350 return Done(dsError);
353 bool cOggDecoder::Skip(int Seconds, float bsecs)
357 if(playing && file.CanSeek()) {
358 float fsecs=(float)Seconds - bsecs;
359 long long newpos=file.IndexMs()+(long long)(fsecs*1000.0);
360 if(newpos<0) newpos=0;
361 d(printf("ogg: skip: secs=%d fsecs=%f current=%lld new=%lld\n",Seconds,fsecs,file.IndexMs(),newpos))
363 newpos=file.Seek(newpos,false);
365 index=file.IndexMs();
368 printf("ogg: skipping to %02d:%02d\n",i/60,i%60);
377 #endif //HAVE_VORBISFILE