strip added smb:// shares of their user/pass when adding, and instead store that...
[vuplus_xbmc] / xbmc / cores / paplayer / OGGcodec.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "OGGcodec.h"
23 #include "music/tags/OggTag.h"
24 #include "FileItem.h"
25 #include "utils/log.h"
26 #include "utils/URIUtils.h"
27
28 using namespace MUSIC_INFO;
29
30 OGGCodec::OGGCodec() : m_callback(m_file)
31 {
32   m_SampleRate = 0;
33   m_Channels = 0;
34   m_BitsPerSample = 0;
35   m_DataFormat = AE_FMT_INVALID;
36   m_Bitrate = 0;
37   m_CodecName = "OGG";
38   m_TimeOffset = 0.0;
39   m_CurrentStream=0;
40   m_TotalTime = 0;
41   m_VorbisFile.datasource = NULL;
42   m_inited = false;
43 }
44
45 OGGCodec::~OGGCodec()
46 {
47   DeInit();
48 }
49
50 bool OGGCodec::Init(const CStdString &strFile1, unsigned int filecache)
51 {
52   if (m_inited)
53     return true;
54   CStdString strFile=strFile1;
55   if (!m_dll.Load())
56     return false;
57
58   m_CurrentStream=0;
59
60   CStdString strExtension;
61   URIUtils::GetExtension(strFile, strExtension);
62
63   //  A bitstream inside a ogg file?
64   if (strExtension==".oggstream")
65   {
66     //  Extract the bitstream to play
67     CStdString strFileName=URIUtils::GetFileName(strFile);
68     int iStart=strFileName.ReverseFind('-')+1;
69     m_CurrentStream = atoi(strFileName.substr(iStart, strFileName.size()-iStart-10).c_str())-1;
70     //  The directory we are in, is the file
71     //  that contains the bitstream to play,
72     //  so extract it
73     CStdString strPath=strFile;
74     URIUtils::GetDirectory(strPath, strFile);
75     URIUtils::RemoveSlashAtEnd(strFile); // we want the filename
76   }
77
78   CFileItem item(strFile, false);
79
80   //  Open the file to play
81   if (!m_file.Open(strFile, READ_CACHED))
82   {
83     CLog::Log(LOGERROR, "OGGCodec: Can't open %s", strFile1.c_str());
84     return false;
85   }
86
87   //  setup ogg i/o callbacks
88   ov_callbacks oggIOCallbacks = m_callback.Get(strFile);
89
90   //  open ogg file with decoder
91   if (m_dll.ov_open_callbacks(&m_callback, &m_VorbisFile, NULL, 0, oggIOCallbacks)!=0)
92   {
93     CLog::Log(LOGERROR, "OGGCodec: Can't open decoder for %s", strFile1.c_str());
94     return false;
95   }
96
97   long iStreams=m_dll.ov_streams(&m_VorbisFile);
98   if (iStreams>1)
99   {
100     if (m_CurrentStream > iStreams)
101       return false;
102   }
103
104   //  Calculate the offset in secs where the bitstream starts
105   for (int i=0; i<m_CurrentStream; ++i)
106     m_TimeOffset += m_dll.ov_time_total(&m_VorbisFile, i);
107
108   //  get file info
109   vorbis_info* pInfo=m_dll.ov_info(&m_VorbisFile, m_CurrentStream);
110   if (!pInfo)
111   {
112     CLog::Log(LOGERROR, "OGGCodec: Can't get stream info from %s", strFile1.c_str());
113     return false;
114   }
115
116   m_SampleRate = pInfo->rate;
117   m_Channels = pInfo->channels;
118   m_BitsPerSample = 16;
119   m_DataFormat = AE_FMT_S16NE;
120   if (item.IsInternetStream())
121     m_TotalTime = -1;
122   else
123     m_TotalTime = (int64_t)m_dll.ov_time_total(&m_VorbisFile, m_CurrentStream)*1000;
124   m_Bitrate = pInfo->bitrate_nominal;
125   if (m_Bitrate == 0 && m_TotalTime > 0 && !item.IsInternetStream())
126     m_Bitrate = (int)(m_file.GetLength()*8 / (m_TotalTime / 1000));
127
128   if (m_SampleRate==0 || m_Channels==0 || m_BitsPerSample==0 || m_TotalTime==0)
129   {
130     CLog::Log(LOGERROR, "OGGCodec: incomplete stream info from %s, SampleRate=%i, Channels=%i, BitsPerSample=%i, TotalTime=%"PRIu64, strFile1.c_str(), m_SampleRate, m_Channels, m_BitsPerSample, m_TotalTime);
131     return false;
132   }
133
134   //  Get replay gain tags
135   vorbis_comment* pComments=m_dll.ov_comment(&m_VorbisFile, m_CurrentStream);
136   if (pComments)
137   {
138     COggTag oggTag;
139     for (int i=0; i < pComments->comments; ++i)
140     {
141       CStdString strTag=pComments->user_comments[i];
142       oggTag.ParseTagEntry(strTag);
143     }
144     m_replayGain=oggTag.GetReplayGain();
145   }
146
147   //  Seek to the logical bitstream to play
148   if (m_TimeOffset>0.0)
149   {
150     if (m_dll.ov_time_seek(&m_VorbisFile, m_TimeOffset)!=0)
151     {
152       CLog::Log(LOGERROR, "OGGCodec: Can't seek to the bitstream start time (%s)", strFile1.c_str());
153       return false;
154     }
155   }
156
157   return true;
158 }
159
160 void OGGCodec::DeInit()
161 {
162   if (m_VorbisFile.datasource)
163     m_dll.ov_clear(&m_VorbisFile);
164   m_VorbisFile.datasource = NULL;
165   m_inited = false;
166 }
167
168 int64_t OGGCodec::Seek(int64_t iSeekTime)
169 {
170   if (m_dll.ov_time_seek(&m_VorbisFile, m_TimeOffset+(double)(iSeekTime/1000.0f))!=0)
171     return 0;
172
173   return iSeekTime;
174 }
175
176 int OGGCodec::ReadPCM(BYTE *pBuffer, int size, int *actualsize)
177 {
178   *actualsize=0;
179   int iBitStream=-1;
180
181   //  the maximum chunk size the vorbis decoder seem to return with one call is 4096
182   long lRead=m_dll.ov_read(&m_VorbisFile, (char*)pBuffer, size, 0, 2, 1, &iBitStream);
183
184   if (lRead == OV_HOLE)
185     return READ_SUCCESS;
186
187   //  Our logical bitstream changed, we reached the eof
188   if (lRead > 0 && m_CurrentStream!=iBitStream)
189     lRead=0;
190
191   if (lRead<0)
192   {
193     CLog::Log(LOGERROR, "OGGCodec: Read error %lu", lRead);
194     return READ_ERROR;
195   }
196   else if (lRead==0)
197     return READ_EOF;
198   else
199     *actualsize=lRead;
200
201   return READ_SUCCESS;
202 }
203
204 bool OGGCodec::CanInit()
205 {
206   return m_dll.CanLoad();
207 }
208
209 CAEChannelInfo OGGCodec::GetChannelInfo()
210 {
211   static enum AEChannel map[8][9] = {
212     {AE_CH_FC, AE_CH_NULL},
213     {AE_CH_FL, AE_CH_FR, AE_CH_NULL},
214     {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_NULL},
215     {AE_CH_FL, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
216     {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
217     {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_BL, AE_CH_BR, AE_CH_LFE, AE_CH_NULL},
218     {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_SL, AE_CH_SR, AE_CH_BL, AE_CH_BR, AE_CH_NULL},
219     {AE_CH_FL, AE_CH_FC, AE_CH_FR, AE_CH_SL, AE_CH_SR, AE_CH_BL, AE_CH_BR, AE_CH_LFE, AE_CH_NULL}
220   };
221
222   if (m_Channels > 8)
223     return CAEUtil::GuessChLayout(m_Channels);
224
225   return CAEChannelInfo(map[m_Channels - 1]);
226 }