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 π
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