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 const 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, bool preinit)
218 file=0; info=0; pcm=0;
220 file=new cOggFile(Filename);
221 info=new cOggInfo(file);
225 cOggDecoder::~cOggDecoder()
232 bool cOggDecoder::Valid(void)
236 if(file->Open(false)) res=true;
242 cFileInfo *cOggDecoder::FileInfo(void)
245 if(file->HasInfo()) fi=file;
247 if(file->Open()) { fi=file; file->Close(); }
253 cSongInfo *cOggDecoder::SongInfo(bool get)
256 if(info->HasInfo()) si=info;
257 else if(get && TryLock()) {
258 if(info->DoScan(false)) si=info;
264 cPlayInfo *cOggDecoder::PlayInfo(void)
268 pi.Total=info->Total;
274 void cOggDecoder::Init(void)
277 pcm=new struct mad_pcm;
281 bool cOggDecoder::Clean(void)
289 #define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
291 bool cOggDecoder::Start(void)
294 Init(); playing=true;
295 if(file->Open() && info->DoScan(true)) {
296 d(printf("ogg: open rate=%d channels=%d seek=%d\n",
297 info->SampleFreq,info->Channels,file->CanSeek()))
298 if(info->Channels<=2) {
302 else esyslog("ERROR: cannot play ogg file %s: more than 2 channels",filename);
309 bool cOggDecoder::Stop(void)
317 struct Decode *cOggDecoder::Done(eDecodeStatus status)
322 Unlock(); // release the lock from Decode()
326 struct Decode *cOggDecoder::Decode(void)
328 Lock(); // this is released in Done()
330 short framebuff[2*SF_SAMPLES];
331 int n=file->Stream(framebuff,SF_SAMPLES);
332 if(n<0) return Done(dsError);
333 if(n==0) return Done(dsEof);
335 pcm->samplerate=info->SampleFreq;
336 pcm->channels=info->Channels;
339 index=file->IndexMs();
341 short *data=framebuff;
342 mad_fixed_t *sam0=pcm->samples[0], *sam1=pcm->samples[1];
343 const int s=MAD_F_FRACBITS+1-(sizeof(short)*8); // shift value for mad_fixed conversion
344 if(pcm->channels>1) {
346 *sam0++=(*data++) << s;
347 *sam1++=(*data++) << s;
352 *sam0++=(*data++) << s;
357 return Done(dsError);
360 bool cOggDecoder::Skip(int Seconds, float bsecs)
364 if(playing && file->CanSeek()) {
365 float fsecs=(float)Seconds - bsecs;
366 long long newpos=file->IndexMs()+(long long)(fsecs*1000.0);
367 if(newpos<0) newpos=0;
368 d(printf("ogg: skip: secs=%d fsecs=%f current=%lld new=%lld\n",Seconds,fsecs,file->IndexMs(),newpos))
370 newpos=file->Seek(newpos,false);
372 index=file->IndexMs();
375 printf("ogg: skipping to %02d:%02d\n",i/60,i%60);
384 #endif //HAVE_VORBISFILE