decoder-ogg.c
branchtrunk
changeset 0 474a1293c3c0
child 6 111ef8181229
equal deleted inserted replaced
-1:000000000000 0:474a1293c3c0
       
     1 /*
       
     2  * MP3/MPlayer plugin to VDR (C++)
       
     3  *
       
     4  * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
       
     5  *
       
     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.
       
    10  *
       
    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.
       
    15  *
       
    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
       
    20  */
       
    21 
       
    22 #ifdef HAVE_VORBISFILE
       
    23 
       
    24 #include <stdlib.h>
       
    25 #include <stdio.h>
       
    26 #include <errno.h>
       
    27 
       
    28 #include "common.h"
       
    29 #include "decoder-ogg.h"
       
    30 
       
    31 // --- cOggFile ----------------------------------------------------------------
       
    32 
       
    33 cOggFile::cOggFile(const char *Filename)
       
    34 :cFileInfo(Filename)
       
    35 {
       
    36   canSeek=opened=false;
       
    37 }
       
    38 
       
    39 cOggFile::~cOggFile()
       
    40 {
       
    41   Close();
       
    42 }
       
    43 
       
    44 bool cOggFile::Open(bool log)
       
    45 {
       
    46   if(opened) {
       
    47     if(canSeek) return (Seek()>=0);
       
    48     return true;
       
    49     }
       
    50 
       
    51   if(FileInfo(log)) {
       
    52     FILE *f=fopen(Filename,"r");
       
    53     if(f) {
       
    54       int r=ov_open(f,&vf,0,0);
       
    55       if(!r) {
       
    56         canSeek=(ov_seekable(&vf)!=0);
       
    57         opened=true;
       
    58         }
       
    59       else {
       
    60         fclose(f);
       
    61         if(log) Error("open",r);
       
    62         }
       
    63       }
       
    64     else if(log) { esyslog("ERROR: failed to open file %s: %s",Filename,strerror(errno)); }
       
    65     }
       
    66   return opened;
       
    67 }
       
    68 
       
    69 void cOggFile::Close(void)
       
    70 {
       
    71   if(opened) { ov_clear(&vf); opened=false; }
       
    72 }
       
    73 
       
    74 void cOggFile::Error(const char *action, const int err)
       
    75 {
       
    76   char *errstr;
       
    77   switch(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;
       
    93     }
       
    94   esyslog("ERROR: vorbisfile %s failed on %s: %s",action,Filename,errstr);
       
    95 }
       
    96 
       
    97 long long cOggFile::IndexMs(void)
       
    98 {
       
    99   double p=ov_time_tell(&vf);
       
   100   if(p<0.0) p=0.0;
       
   101   return (long long)(p*1000.0);
       
   102 }
       
   103 
       
   104 long long cOggFile::Seek(long long posMs, bool relativ)
       
   105 {
       
   106   if(relativ) posMs+=IndexMs();
       
   107   int r=ov_time_seek(&vf,(double)posMs/1000.0);
       
   108   if(r) {
       
   109     Error("seek",r);
       
   110     return -1;
       
   111     }
       
   112   posMs=IndexMs();
       
   113   return posMs;
       
   114 }
       
   115 
       
   116 int cOggFile::Stream(short *buffer, int samples)
       
   117 {
       
   118   int n;
       
   119   do {
       
   120     int stream;
       
   121     n=ov_read(&vf,(char *)buffer,samples*2,0,2,1,&stream);
       
   122     } while(n==OV_HOLE);
       
   123   if(n<0) Error("read",n);
       
   124   return (n/2);
       
   125 }
       
   126 
       
   127 // --- cOggInfo ----------------------------------------------------------------
       
   128 
       
   129 cOggInfo::cOggInfo(cOggFile *File)
       
   130 {
       
   131   file=File;
       
   132 }
       
   133 
       
   134 bool cOggInfo::Abort(bool result)
       
   135 {
       
   136   if(!keepOpen) file->Close();
       
   137   return result;
       
   138 }
       
   139 
       
   140 bool cOggInfo::DoScan(bool KeepOpen)
       
   141 {
       
   142   keepOpen=KeepOpen;
       
   143   if(!file->Open()) return Abort(false);
       
   144   if(HasInfo()) return Abort(true);
       
   145 
       
   146   // check the infocache
       
   147   cCacheData *dat=InfoCache.Search(file);
       
   148   if(dat) {
       
   149     Set(dat); dat->Unlock();
       
   150     if(!DecoderID) {
       
   151       DecoderID=DEC_OGG;
       
   152       InfoCache.Cache(this,file);
       
   153       }
       
   154     return Abort(true);
       
   155     }
       
   156 
       
   157   Clear();
       
   158 
       
   159   vorbis_comment *vc=ov_comment(&file->vf,-1);
       
   160   if(vc) {
       
   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,'=');
       
   165       if(p) {
       
   166         const int len=p-cc;
       
   167         p++;
       
   168         if(!strncasecmp(cc,"TITLE",len)) {
       
   169           if(!Title) Title=strdup(p);
       
   170           }
       
   171         else if(!strncasecmp(cc,"ARTIST",len)) {
       
   172           if(!Artist) Artist=strdup(p);
       
   173           }
       
   174         else if(!strncasecmp(cc,"ALBUM",len)) {
       
   175           if(!Album) Album=strdup(p);
       
   176           }
       
   177         else if(!strncasecmp(cc,"YEAR",len)) {
       
   178           if(Year<0) {
       
   179             Year=atoi(p);
       
   180             if(Year<1800 || Year>2100) Year=-1;
       
   181             }
       
   182           }
       
   183         }
       
   184       }
       
   185     }
       
   186   if(!Title) FakeTitle(file->Filename);
       
   187 
       
   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;
       
   194   SampleFreq=vi->rate;
       
   195   if(vi->bitrate_upper>0 && vi->bitrate_lower>0) {
       
   196     Bitrate=vi->bitrate_lower;
       
   197     MaxBitrate=vi->bitrate_upper;
       
   198     }
       
   199   else
       
   200     Bitrate=vi->bitrate_nominal;
       
   201 
       
   202   Total=(int)ov_time_total(&file->vf,-1);
       
   203   Frames=-1;
       
   204   DecoderID=DEC_OGG;
       
   205 
       
   206   InfoDone();
       
   207   InfoCache.Cache(this,file);
       
   208   return Abort(true);  
       
   209 }
       
   210 
       
   211 // --- cOggDecoder -------------------------------------------------------------
       
   212 
       
   213 cOggDecoder::cOggDecoder(const char *Filename)
       
   214 :cDecoder(Filename)
       
   215 ,file(Filename)
       
   216 ,info(&file)
       
   217 {
       
   218   pcm=0;
       
   219 }
       
   220 
       
   221 cOggDecoder::~cOggDecoder()
       
   222 {
       
   223   Clean();
       
   224 }
       
   225 
       
   226 bool cOggDecoder::Valid(void)
       
   227 {
       
   228   bool res=false;
       
   229   if(TryLock()) {
       
   230     if(file.Open(false)) res=true;
       
   231     Unlock();
       
   232     }
       
   233   return res;
       
   234 }
       
   235 
       
   236 cFileInfo *cOggDecoder::FileInfo(void)
       
   237 {
       
   238   cFileInfo *fi=0;
       
   239   if(file.HasInfo()) fi=&file;
       
   240   else if(TryLock()){
       
   241     if(file.Open()) { fi=&file; file.Close(); }
       
   242     Unlock();
       
   243     }
       
   244   return fi;
       
   245 }
       
   246 
       
   247 cSongInfo *cOggDecoder::SongInfo(bool get)
       
   248 {
       
   249   cSongInfo *si=0;
       
   250   if(info.HasInfo()) si=&info;
       
   251   else if(get && TryLock()) {
       
   252     if(info.DoScan(false)) si=&info;
       
   253     Unlock();
       
   254     }
       
   255   return si;
       
   256 }
       
   257 
       
   258 cPlayInfo *cOggDecoder::PlayInfo(void)
       
   259 {
       
   260   if(playing) {
       
   261     pi.Index=index/1000;
       
   262     pi.Total=info.Total;
       
   263     return &pi;
       
   264     }
       
   265   return 0;
       
   266 }
       
   267 
       
   268 void cOggDecoder::Init(void)
       
   269 {
       
   270   Clean();
       
   271   pcm=new struct mad_pcm;
       
   272   index=0;
       
   273 }
       
   274 
       
   275 bool cOggDecoder::Clean(void)
       
   276 {
       
   277   playing=false;
       
   278   delete pcm; pcm=0;
       
   279   file.Close();
       
   280   return false;
       
   281 }
       
   282 
       
   283 #define SF_SAMPLES (sizeof(pcm->samples[0])/sizeof(mad_fixed_t))
       
   284 
       
   285 bool cOggDecoder::Start(void)
       
   286 {
       
   287   Lock(true);
       
   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) {
       
   293       Unlock();
       
   294       return true;
       
   295       }
       
   296     else esyslog("ERROR: cannot play ogg file %s: more than 2 channels",filename);
       
   297     }
       
   298   Clean();
       
   299   Unlock();
       
   300   return false;
       
   301 }
       
   302 
       
   303 bool cOggDecoder::Stop(void)
       
   304 {
       
   305   Lock();
       
   306   if(playing) Clean();
       
   307   Unlock();
       
   308   return true;
       
   309 }
       
   310 
       
   311 struct Decode *cOggDecoder::Done(eDecodeStatus status)
       
   312 {
       
   313   ds.status=status;
       
   314   ds.index=index;
       
   315   ds.pcm=pcm;
       
   316   Unlock(); // release the lock from Decode()
       
   317   return &ds;
       
   318 }
       
   319 
       
   320 struct Decode *cOggDecoder::Decode(void)
       
   321 {
       
   322   Lock(); // this is released in Done()
       
   323   if(playing) {
       
   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);
       
   328 
       
   329     pcm->samplerate=info.SampleFreq;
       
   330     pcm->channels=info.Channels;
       
   331     n/=pcm->channels;
       
   332     pcm->length=n;
       
   333     index=file.IndexMs();
       
   334 
       
   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) {
       
   339       for(; n>0 ; n--) {
       
   340         *sam0++=(*data++) << s;
       
   341         *sam1++=(*data++) << s;
       
   342         }
       
   343       }
       
   344     else {
       
   345       for(; n>0 ; n--)
       
   346         *sam0++=(*data++) << s;
       
   347       }
       
   348     return Done(dsPlay);
       
   349     }
       
   350   return Done(dsError);
       
   351 }
       
   352 
       
   353 bool cOggDecoder::Skip(int Seconds, float bsecs)
       
   354 {
       
   355   Lock();
       
   356   bool res=false;
       
   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))
       
   362 
       
   363     newpos=file.Seek(newpos,false);
       
   364     if(newpos>=0) {
       
   365       index=file.IndexMs();
       
   366 #ifdef DEBUG
       
   367       int i=index/1000;
       
   368       printf("ogg: skipping to %02d:%02d\n",i/60,i%60);
       
   369 #endif
       
   370       res=true;
       
   371       }
       
   372     }
       
   373   Unlock();
       
   374   return res;
       
   375 }
       
   376 
       
   377 #endif //HAVE_VORBISFILE