2 * MP3/MPlayer plugin to VDR (C++)
4 * (C) 2001-2009 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();
153 InfoCache.Cache(this,file);
160 vorbis_comment *vc=ov_comment(&file->vf,-1);
162 for(int i=0 ; i<vc->comments ; i++) {
163 const char *cc=vc->user_comments[i];
164 d(printf("ogg: comment%d='%s'\n",i,cc))
165 char *p=strchr(cc,'=');
169 if(!strncasecmp(cc,"TITLE",len)) {
170 if(!Title) Title=strdup(p);
172 else if(!strncasecmp(cc,"ARTIST",len)) {
173 if(!Artist) Artist=strdup(p);
175 else if(!strncasecmp(cc,"ALBUM",len)) {
176 if(!Album) Album=strdup(p);
178 else if(!strncasecmp(cc,"YEAR",len)) {
181 if(Year<1800 || Year>2100) Year=-1;
187 if(!Title) FakeTitle(file->Filename);
189 vorbis_info *vi=ov_info(&file->vf,-1);
190 if(!vi) Abort(false);
191 d(printf("ogg: info ch=%d srate=%ld brate_low=%ld brate_high=%ld brate_avg=%ld\n",
192 vi->channels,vi->rate,vi->bitrate_lower,vi->bitrate_upper,vi->bitrate_nominal))
193 Channels=vi->channels;
194 ChMode=Channels>1 ? 3:0;
196 if(vi->bitrate_upper>0 && vi->bitrate_lower>0) {
197 Bitrate=vi->bitrate_lower;
198 MaxBitrate=vi->bitrate_upper;
201 Bitrate=vi->bitrate_nominal;
203 Total=(int)ov_time_total(&file->vf,-1);
208 InfoCache.Cache(this,file);
213 // --- cOggDecoder -------------------------------------------------------------
215 cOggDecoder::cOggDecoder(const char *Filename)
223 cOggDecoder::~cOggDecoder()
228 bool cOggDecoder::Valid(void)
232 if(file.Open(false)) res=true;
238 cFileInfo *cOggDecoder::FileInfo(void)
241 if(file.HasInfo()) fi=&file;
243 if(file.Open()) { fi=&file; file.Close(); }
249 cSongInfo *cOggDecoder::SongInfo(bool get)
252 if(info.HasInfo()) si=&info;
253 else if(get && TryLock()) {
254 if(info.DoScan(false)) si=&info;
260 cPlayInfo *cOggDecoder::PlayInfo(void)
270 void cOggDecoder::Init(void)
273 pcm=new struct mad_pcm;
277 bool cOggDecoder::Clean(void)
285 #define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
287 bool cOggDecoder::Start(void)
290 Init(); playing=true;
291 if(file.Open() && info.DoScan(true)) {
292 d(printf("ogg: open rate=%d channels=%d seek=%d\n",
293 info.SampleFreq,info.Channels,file.CanSeek()))
294 if(info.Channels<=2) {
298 else esyslog("ERROR: cannot play ogg file %s: more than 2 channels",filename);
305 bool cOggDecoder::Stop(void)
313 struct Decode *cOggDecoder::Done(eDecodeStatus status)
318 Unlock(); // release the lock from Decode()
322 struct Decode *cOggDecoder::Decode(void)
324 Lock(); // this is released in Done()
326 short framebuff[2*SF_SAMPLES];
327 int n=file.Stream(framebuff,SF_SAMPLES);
328 if(n<0) return Done(dsError);
329 if(n==0) return Done(dsEof);
331 pcm->samplerate=info.SampleFreq;
332 pcm->channels=info.Channels;
335 index=file.IndexMs();
337 short *data=framebuff;
338 mad_fixed_t *sam0=pcm->samples[0], *sam1=pcm->samples[1];
339 const int s=MAD_F_FRACBITS+1-(sizeof(short)*8); // shift value for mad_fixed conversion
340 if(pcm->channels>1) {
342 *sam0++=(*data++) << s;
343 *sam1++=(*data++) << s;
348 *sam0++=(*data++) << s;
352 return Done(dsError);
355 bool cOggDecoder::Skip(int Seconds, float bsecs)
359 if(playing && file.CanSeek()) {
360 float fsecs=(float)Seconds - bsecs;
361 long long newpos=file.IndexMs()+(long long)(fsecs*1000.0);
362 if(newpos<0) newpos=0;
363 d(printf("ogg: skip: secs=%d fsecs=%f current=%lld new=%lld\n",Seconds,fsecs,file.IndexMs(),newpos))
365 newpos=file.Seek(newpos,false);
367 index=file.IndexMs();
370 printf("ogg: skipping to %02d:%02d\n",i/60,i%60);
379 #endif //HAVE_VORBISFILE