add OGG streaming support trunk
authornathan
Fri, 13 Nov 2009 19:27:36 +0800
branchtrunk
changeset 3365ed49cbc08b
parent 32 cea1b4f741be
child 34 afc13760179b
add OGG streaming support
HISTORY
Makefile
decoder-ogg-stream.c
decoder-ogg-stream.h
decoder-ogg.c
decoder-ogg.h
decoder.c
     1.1 --- a/HISTORY	Sat Oct 24 11:31:53 2009 +0800
     1.2 +++ b/HISTORY	Fri Nov 13 19:27:36 2009 +0800
     1.3 @@ -7,6 +7,7 @@
     1.4  - Fixed possible division by zero in libsndfile decoder.
     1.5  - Fixed some gcc 4.x warnings.
     1.6  - Added commandline option for a user defined default background image.
     1.7 +- Added OGG streaming support. Implemented by Manuel Reimer.
     1.8  - Made the handling of song information (e.g. ID3) UTF-8 aware. The infocache
     1.9    file is kept in UTF-8 format now.
    1.10  - Ignore errors while scaning recursive directories. Patch provided by Steffen
     2.1 --- a/Makefile	Sat Oct 24 11:31:53 2009 +0800
     2.2 +++ b/Makefile	Fri Nov 13 19:27:36 2009 +0800
     2.3 @@ -108,7 +108,7 @@
     2.4  OBJS     = $(PLUGIN).o $(COM_OBJS)\
     2.5              data-mp3.o setup-mp3.o player-mp3.o stream.o network.o\
     2.6              decoder.o decoder-mp3.o decoder-mp3-stream.o decoder-snd.o \
     2.7 -            decoder-ogg.o compat.o
     2.8 +            decoder-ogg.o decoder-ogg-stream.o compat.o
     2.9  LIBS     = -lmad -lid3tag
    2.10  
    2.11  ifndef WITHOUT_LIBSNDFILE
    2.12 @@ -245,4 +245,4 @@
    2.13  	@-rm -f version.c i18n.c
    2.14  	@-rm -f $(PODIR)/*.mo
    2.15  
    2.16 -FORCE:
    2.17 \ No newline at end of file
    2.18 +FORCE:
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/decoder-ogg-stream.c	Fri Nov 13 19:27:36 2009 +0800
     3.3 @@ -0,0 +1,161 @@
     3.4 +/*
     3.5 + * MP3/MPlayer plugin to VDR (C++)
     3.6 + *
     3.7 + * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
     3.8 + *
     3.9 + * OGG stream support initialy developed by Manuel Reimer <manuel.reimer@gmx.de>
    3.10 + *
    3.11 + * This code is free software; you can redistribute it and/or
    3.12 + * modify it under the terms of the GNU General Public License
    3.13 + * as published by the Free Software Foundation; either version 2
    3.14 + * of the License, or (at your option) any later version.
    3.15 + *
    3.16 + * This code is distributed in the hope that it will be useful,
    3.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.19 + * GNU General Public License for more details.
    3.20 + *
    3.21 + * You should have received a copy of the GNU General Public License
    3.22 + * along with this program; if not, write to the Free Software
    3.23 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    3.24 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    3.25 + */
    3.26 +
    3.27 +#include <stdlib.h>
    3.28 +#include <stdio.h>
    3.29 +
    3.30 +#include "common.h"
    3.31 +#include "decoder-ogg-stream.h"
    3.32 +#include "stream.h"
    3.33 +
    3.34 +// --- Ogg callbacks -----------------------------------------------------------
    3.35 +
    3.36 +static size_t callback_read(void *ptr, size_t size, size_t nmemb, void *datasource)
    3.37 +{
    3.38 +  cNetStream *nstr=(cNetStream*)datasource;
    3.39 +  unsigned char *sdata;
    3.40 +  unsigned long slen=0;
    3.41 +  // Read in loop until we either get data or function "Stream" fails
    3.42 +  do {
    3.43 +    if(!nstr->Stream(sdata,slen)) {
    3.44 +      d(printf("oggstream-callback-read: EOF?\n"))
    3.45 +      return 0;
    3.46 +      }
    3.47 +    } while(slen==0);
    3.48 +
    3.49 +  size_t read_size=size*nmemb;
    3.50 +  if(slen>read_size) {
    3.51 +    // If someone ever gets this message, buffer handling has to be improved...
    3.52 +    d(printf("oggstream-callback-read: buffer size too small...\n"))
    3.53 +    slen=read_size;
    3.54 +    }
    3.55 +  memcpy(ptr,sdata,slen);
    3.56 +  return slen/size;
    3.57 +}
    3.58 +
    3.59 +static int callback_close(void *datasource)
    3.60 +{
    3.61 +  cNetStream *nstr=(cNetStream*)datasource;
    3.62 +  nstr->Close();
    3.63 +  return 0;
    3.64 +}
    3.65 +
    3.66 +static const ov_callbacks callbacks = {
    3.67 +  callback_read,
    3.68 +  NULL,
    3.69 +  callback_close,
    3.70 +  NULL
    3.71 +  };
    3.72 +
    3.73 +// --- cNetOggFile -------------------------------------------------------------
    3.74 +
    3.75 +cNetOggFile::cNetOggFile(const char *Filename)
    3.76 +:cOggFile(Filename)
    3.77 +{
    3.78 +  nstr=new cNetStream(Filename);
    3.79 +}
    3.80 +
    3.81 +bool cNetOggFile::Open(bool log)
    3.82 +{
    3.83 +  if(opened) return true;
    3.84 +  if(!nstr->Open(log)) return false;
    3.85 +  int r=ov_open_callbacks(nstr,&vf,NULL,0,callbacks);
    3.86 +  if(!r) opened=true;
    3.87 +  else {
    3.88 +    nstr->Close();
    3.89 +    if(log) Error("open",r);
    3.90 +    }
    3.91 +  return opened;
    3.92 +}
    3.93 +
    3.94 +// --- cNetOggInfo -------------------------------------------------------------
    3.95 +
    3.96 +cNetOggInfo::cNetOggInfo(cNetOggFile *File)
    3.97 +:cOggInfo(File)
    3.98 +{
    3.99 +  nfile=File;
   3.100 +  nstr=nfile->nstr;
   3.101 +}
   3.102 +
   3.103 +bool cNetOggInfo::DoScan(bool KeepOpen)
   3.104 +{
   3.105 +  Clear();
   3.106 +  IcyInfo();
   3.107 +  if(!Title) FakeTitle(nstr->Filename);
   3.108 +  Total=0;
   3.109 +  ChMode=3;
   3.110 +  DecoderID=DEC_OGGS;
   3.111 +  InfoDone();
   3.112 +  return true;
   3.113 +}
   3.114 +
   3.115 +void cNetOggInfo::InfoHook()
   3.116 +{
   3.117 +  if(nstr->IcyChanged()) IcyInfo();
   3.118 +  vorbis_info *vi=ov_info(&nfile->vf,-1);
   3.119 +  if(!vi) return;
   3.120 +  Channels=vi->channels;
   3.121 +  ChMode=Channels>1 ? 3:0;
   3.122 +  SampleFreq=vi->rate;
   3.123 +  if(vi->bitrate_upper>0 && vi->bitrate_lower>0) {
   3.124 +    Bitrate=vi->bitrate_lower;
   3.125 +    MaxBitrate=vi->bitrate_upper;
   3.126 +    }
   3.127 +  else
   3.128 +    Bitrate=vi->bitrate_nominal;
   3.129 +
   3.130 +  Total=(int)ov_time_total(&nfile->vf,-1);
   3.131 +  Frames=-1;
   3.132 +}
   3.133 +
   3.134 +void cNetOggInfo::IcyInfo(void)
   3.135 +{
   3.136 +  const char *t=nstr->IcyTitle();
   3.137 +  const char *a;
   3.138 +  if(t) {
   3.139 +    a=nstr->IcyName();
   3.140 +    if(!a) a=nstr->IcyUrl();
   3.141 +    }
   3.142 +  else {
   3.143 +    t=nstr->IcyName();
   3.144 +    a=nstr->IcyUrl();
   3.145 +    }
   3.146 +  if(t && (!Title || strcmp(t,Title))) {
   3.147 +    free(Title);
   3.148 +    Title=strdup(t);
   3.149 +    }
   3.150 +  if(a && (!Album || strcmp(a,Album))) {
   3.151 +    free(Album);
   3.152 +    Album=strdup(a);
   3.153 +    }
   3.154 +}
   3.155 +
   3.156 +// --- cOggStreamDecoder -------------------------------------------------------
   3.157 +
   3.158 +cOggStreamDecoder::cOggStreamDecoder(const char *Filename)
   3.159 +:cOggDecoder(Filename,false)
   3.160 +{
   3.161 +  nfile=new cNetOggFile(Filename);
   3.162 +  file=nfile;
   3.163 +  info=new cNetOggInfo(nfile);
   3.164 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/decoder-ogg-stream.h	Fri Nov 13 19:27:36 2009 +0800
     4.3 @@ -0,0 +1,68 @@
     4.4 +/*
     4.5 + * MP3/MPlayer plugin to VDR (C++)
     4.6 + *
     4.7 + * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
     4.8 + *
     4.9 + * OGG stream support initialy developed by Manuel Reimer <manuel.reimer@gmx.de>
    4.10 + *
    4.11 + * This code is free software; you can redistribute it and/or
    4.12 + * modify it under the terms of the GNU General Public License
    4.13 + * as published by the Free Software Foundation; either version 2
    4.14 + * of the License, or (at your option) any later version.
    4.15 + *
    4.16 + * This code is distributed in the hope that it will be useful,
    4.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.19 + * GNU General Public License for more details.
    4.20 + *
    4.21 + * You should have received a copy of the GNU General Public License
    4.22 + * along with this program; if not, write to the Free Software
    4.23 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    4.24 + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
    4.25 + */
    4.26 +
    4.27 +#ifndef ___DECODER_OGG_STREAM_H
    4.28 +#define ___DECODER_OGG_STREAM_H
    4.29 +
    4.30 +#define DEC_OGGS     DEC_ID('O','G','G','S')
    4.31 +#define DEC_OGGS_STR "OGGS"
    4.32 +
    4.33 +#include "decoder-ogg.h"
    4.34 +
    4.35 +class cNetStream;
    4.36 +
    4.37 +// ----------------------------------------------------------------
    4.38 +
    4.39 +class cNetOggFile : public cOggFile {
    4.40 +friend class cNetOggInfo;
    4.41 +private:
    4.42 +  cNetStream *nstr;
    4.43 +public:
    4.44 +  cNetOggFile(const char *Filename);
    4.45 +  virtual bool Open(bool log=true);
    4.46 +  };
    4.47 +
    4.48 +// ----------------------------------------------------------------
    4.49 +
    4.50 +class cNetOggInfo : public cOggInfo {
    4.51 +private:
    4.52 +  cNetOggFile *nfile;
    4.53 +  cNetStream *nstr;
    4.54 +  void IcyInfo(void);
    4.55 +public:
    4.56 +  cNetOggInfo(cNetOggFile *File);
    4.57 +  virtual bool DoScan(bool KeepOpen=false);
    4.58 +  virtual void InfoHook(void);
    4.59 +  };
    4.60 +
    4.61 +// ----------------------------------------------------------------
    4.62 +
    4.63 +class cOggStreamDecoder : public cOggDecoder {
    4.64 +private:
    4.65 +  cNetOggFile *nfile;
    4.66 +public:
    4.67 +  cOggStreamDecoder(const char *Filename);
    4.68 +  virtual bool IsStream(void) { return true; }
    4.69 +  };
    4.70 +
    4.71 +#endif //___DECODER_OGG_STREAM_H
     5.1 --- a/decoder-ogg.c	Sat Oct 24 11:31:53 2009 +0800
     5.2 +++ b/decoder-ogg.c	Fri Nov 13 19:27:36 2009 +0800
     5.3 @@ -212,24 +212,28 @@
     5.4  
     5.5  // --- cOggDecoder -------------------------------------------------------------
     5.6  
     5.7 -cOggDecoder::cOggDecoder(const char *Filename)
     5.8 +cOggDecoder::cOggDecoder(const char *Filename, bool preinit)
     5.9  :cDecoder(Filename)
    5.10 -,file(Filename)
    5.11 -,info(&file)
    5.12 -{
    5.13 -  pcm=0;
    5.14 +{
    5.15 +  file=0; info=0; pcm=0;
    5.16 +  if(preinit) {
    5.17 +    file=new cOggFile(Filename);
    5.18 +    info=new cOggInfo(file);
    5.19 +  }
    5.20  }
    5.21  
    5.22  cOggDecoder::~cOggDecoder()
    5.23  {
    5.24    Clean();
    5.25 +  delete info;
    5.26 +  delete file;
    5.27  }
    5.28  
    5.29  bool cOggDecoder::Valid(void)
    5.30  {
    5.31    bool res=false;
    5.32    if(TryLock()) {
    5.33 -    if(file.Open(false)) res=true;
    5.34 +    if(file->Open(false)) res=true;
    5.35      Unlock();
    5.36      }
    5.37    return res;
    5.38 @@ -238,9 +242,9 @@
    5.39  cFileInfo *cOggDecoder::FileInfo(void)
    5.40  {
    5.41    cFileInfo *fi=0;
    5.42 -  if(file.HasInfo()) fi=&file;
    5.43 +  if(file->HasInfo()) fi=file;
    5.44    else if(TryLock()){
    5.45 -    if(file.Open()) { fi=&file; file.Close(); }
    5.46 +    if(file->Open()) { fi=file; file->Close(); }
    5.47      Unlock();
    5.48      }
    5.49    return fi;
    5.50 @@ -249,9 +253,9 @@
    5.51  cSongInfo *cOggDecoder::SongInfo(bool get)
    5.52  {
    5.53    cSongInfo *si=0;
    5.54 -  if(info.HasInfo()) si=&info;
    5.55 +  if(info->HasInfo()) si=info;
    5.56    else if(get && TryLock()) {
    5.57 -    if(info.DoScan(false)) si=&info;
    5.58 +    if(info->DoScan(false)) si=info;
    5.59      Unlock();
    5.60      }
    5.61    return si;
    5.62 @@ -261,7 +265,7 @@
    5.63  {
    5.64    if(playing) {
    5.65      pi.Index=index/1000;
    5.66 -    pi.Total=info.Total;
    5.67 +    pi.Total=info->Total;
    5.68      return &pi;
    5.69      }
    5.70    return 0;
    5.71 @@ -278,7 +282,7 @@
    5.72  {
    5.73    playing=false;
    5.74    delete pcm; pcm=0;
    5.75 -  file.Close();
    5.76 +  file->Close();
    5.77    return false;
    5.78  }
    5.79  
    5.80 @@ -288,10 +292,10 @@
    5.81  {
    5.82    Lock(true);
    5.83    Init(); playing=true;
    5.84 -  if(file.Open() && info.DoScan(true)) {
    5.85 +  if(file->Open() && info->DoScan(true)) {
    5.86      d(printf("ogg: open rate=%d channels=%d seek=%d\n",
    5.87 -             info.SampleFreq,info.Channels,file.CanSeek()))
    5.88 -    if(info.Channels<=2) {
    5.89 +             info->SampleFreq,info->Channels,file->CanSeek()))
    5.90 +    if(info->Channels<=2) {
    5.91        Unlock();
    5.92        return true;
    5.93        }
    5.94 @@ -324,15 +328,15 @@
    5.95    Lock(); // this is released in Done()
    5.96    if(playing) {
    5.97      short framebuff[2*SF_SAMPLES];
    5.98 -    int n=file.Stream(framebuff,SF_SAMPLES);
    5.99 +    int n=file->Stream(framebuff,SF_SAMPLES);
   5.100      if(n<0) return Done(dsError);
   5.101      if(n==0) return Done(dsEof);
   5.102  
   5.103 -    pcm->samplerate=info.SampleFreq;
   5.104 -    pcm->channels=info.Channels;
   5.105 +    pcm->samplerate=info->SampleFreq;
   5.106 +    pcm->channels=info->Channels;
   5.107      n/=pcm->channels;
   5.108      pcm->length=n;
   5.109 -    index=file.IndexMs();
   5.110 +    index=file->IndexMs();
   5.111  
   5.112      short *data=framebuff;
   5.113      mad_fixed_t *sam0=pcm->samples[0], *sam1=pcm->samples[1]; 
   5.114 @@ -347,6 +351,7 @@
   5.115        for(; n>0 ; n--)
   5.116          *sam0++=(*data++) << s;
   5.117        }
   5.118 +    info->InfoHook();
   5.119      return Done(dsPlay);
   5.120      }
   5.121    return Done(dsError);
   5.122 @@ -356,15 +361,15 @@
   5.123  {
   5.124    Lock();
   5.125    bool res=false;
   5.126 -  if(playing && file.CanSeek()) {
   5.127 +  if(playing && file->CanSeek()) {
   5.128      float fsecs=(float)Seconds - bsecs;
   5.129 -    long long newpos=file.IndexMs()+(long long)(fsecs*1000.0);
   5.130 +    long long newpos=file->IndexMs()+(long long)(fsecs*1000.0);
   5.131      if(newpos<0) newpos=0;
   5.132 -    d(printf("ogg: skip: secs=%d fsecs=%f current=%lld new=%lld\n",Seconds,fsecs,file.IndexMs(),newpos))
   5.133 -
   5.134 -    newpos=file.Seek(newpos,false);
   5.135 +    d(printf("ogg: skip: secs=%d fsecs=%f current=%lld new=%lld\n",Seconds,fsecs,file->IndexMs(),newpos))
   5.136 +
   5.137 +    newpos=file->Seek(newpos,false);
   5.138      if(newpos>=0) {
   5.139 -      index=file.IndexMs();
   5.140 +      index=file->IndexMs();
   5.141  #ifdef DEBUG
   5.142        int i=index/1000;
   5.143        printf("ogg: skipping to %02d:%02d\n",i/60,i%60);
     6.1 --- a/decoder-ogg.h	Sat Oct 24 11:31:53 2009 +0800
     6.2 +++ b/decoder-ogg.h	Fri Nov 13 19:27:36 2009 +0800
     6.3 @@ -1,7 +1,7 @@
     6.4  /*
     6.5   * MP3/MPlayer plugin to VDR (C++)
     6.6   *
     6.7 - * (C) 2001-2005 Stefan Huelswitt <s.huelswitt@gmx.de>
     6.8 + * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
     6.9   *
    6.10   * This code is free software; you can redistribute it and/or
    6.11   * modify it under the terms of the GNU General Public License
    6.12 @@ -40,14 +40,16 @@
    6.13  class cOggFile : public cFileInfo {
    6.14  friend class cOggInfo;
    6.15  private:
    6.16 -  bool opened, canSeek;
    6.17 +  bool canSeek;
    6.18 +protected:
    6.19 +  bool opened;
    6.20    OggVorbis_File vf;
    6.21    //
    6.22    void Error(const char *action, const int err);
    6.23  public:
    6.24    cOggFile(const char *Filename);
    6.25    ~cOggFile();
    6.26 -  bool Open(bool log=true);
    6.27 +  virtual bool Open(bool log=true);
    6.28    void Close(void);
    6.29    long long Seek(long long posMs=0, bool relativ=false);
    6.30    int Stream(short *buffer, int samples);
    6.31 @@ -65,15 +67,14 @@
    6.32    bool Abort(bool result);
    6.33  public:
    6.34    cOggInfo(cOggFile *File);
    6.35 -  bool DoScan(bool KeepOpen=false);
    6.36 +  virtual bool DoScan(bool KeepOpen=false);
    6.37 +  virtual void InfoHook(void) {};
    6.38    };
    6.39  
    6.40  // ----------------------------------------------------------------
    6.41  
    6.42  class cOggDecoder : public cDecoder {
    6.43  private:
    6.44 -  cOggFile file;
    6.45 -  cOggInfo info;
    6.46    struct Decode ds;
    6.47    struct mad_pcm *pcm;
    6.48    unsigned long long index;
    6.49 @@ -82,8 +83,11 @@
    6.50    bool Clean(void);
    6.51    bool GetInfo(bool keepOpen);
    6.52    struct Decode *Done(eDecodeStatus status);
    6.53 +protected:
    6.54 +  cOggFile *file;
    6.55 +  cOggInfo *info;
    6.56  public:
    6.57 -  cOggDecoder(const char *Filename);
    6.58 +  cOggDecoder(const char *Filename, bool preinit=true);
    6.59    ~cOggDecoder();
    6.60    virtual bool Valid(void);
    6.61    virtual cFileInfo *FileInfo(void);
     7.1 --- a/decoder.c	Sat Oct 24 11:31:53 2009 +0800
     7.2 +++ b/decoder.c	Fri Nov 13 19:27:36 2009 +0800
     7.3 @@ -36,6 +36,7 @@
     7.4  #include "decoder-mp3-stream.h"
     7.5  #include "decoder-snd.h"
     7.6  #include "decoder-ogg.h"
     7.7 +#include "decoder-ogg-stream.h"
     7.8  
     7.9  #define CACHEFILENAME     "id3info.cache"
    7.10  #define CACHESAVETIMEOUT  120 // secs
    7.11 @@ -258,6 +259,7 @@
    7.12  #endif
    7.13  #ifdef HAVE_VORBISFILE
    7.14          case DEC_OGG:  decoder=new cOggDecoder(full); break;
    7.15 +        case DEC_OGGS: decoder=new cOggStreamDecoder(full); break;
    7.16  #endif
    7.17          default:       esyslog("ERROR: bad DecoderID '%s' from info cache: %s",cDecoders::ID2Str(dat->DecoderID),full); break;
    7.18          }
    7.19 @@ -281,6 +283,10 @@
    7.20        decoder=new cOggDecoder(full);
    7.21        if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
    7.22        }
    7.23 +    if(!decoder) {
    7.24 +      decoder=new cOggStreamDecoder(full);
    7.25 +      if(!decoder || !decoder->Valid()) { delete decoder; decoder=0; }
    7.26 +      }
    7.27  #endif
    7.28      if(!decoder) {
    7.29        decoder=new cMP3StreamDecoder(full);
    7.30 @@ -302,6 +308,7 @@
    7.31      case DEC_MP3S: return DEC_MP3S_STR;
    7.32      case DEC_SND:  return DEC_SND_STR;
    7.33      case DEC_OGG:  return DEC_OGG_STR;
    7.34 +    case DEC_OGGS: return DEC_OGGS_STR;
    7.35      }
    7.36    return 0;
    7.37  }
    7.38 @@ -312,6 +319,7 @@
    7.39    else if(!strcmp(str,DEC_MP3S_STR)) return DEC_MP3S;
    7.40    else if(!strcmp(str,DEC_SND_STR )) return DEC_SND;
    7.41    else if(!strcmp(str,DEC_OGG_STR )) return DEC_OGG;
    7.42 +  else if(!strcmp(str,DEC_OGGS_STR)) return DEC_OGGS;
    7.43    return 0;
    7.44  }
    7.45