Merge pull request #5095 from koying/fixdroidappcrash
[vuplus_xbmc] / xbmc / filesystem / ShoutcastFile.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21
22 // FileShoutcast.cpp: implementation of the CShoutcastFile class.
23 //
24 //////////////////////////////////////////////////////////////////////
25
26 #include "threads/SystemClock.h"
27 #include "system.h"
28 #include "ShoutcastFile.h"
29 #include "guilib/GUIWindowManager.h"
30 #include "URL.h"
31 #include "utils/RegExp.h"
32 #include "utils/HTMLUtil.h"
33 #include "utils/CharsetConverter.h"
34 #include "utils/TimeUtils.h"
35 #include "ApplicationMessenger.h"
36 #include "utils/log.h"
37 #include "FileCache.h"
38 #include <climits>
39
40 using namespace XFILE;
41 using namespace MUSIC_INFO;
42
43 CShoutcastFile::CShoutcastFile() :
44   IFile(), CThread("ShoutcastFile")
45 {
46   m_discarded = 0;
47   m_currint = 0;
48   m_buffer = NULL;
49   m_cacheReader = NULL;
50   m_tagPos = 0;
51 }
52
53 CShoutcastFile::~CShoutcastFile()
54 {
55   StopThread();
56   Close();
57 }
58
59 int64_t CShoutcastFile::GetPosition()
60 {
61   return m_file.GetPosition()-m_discarded;
62 }
63
64 int64_t CShoutcastFile::GetLength()
65 {
66   return 0;
67 }
68
69 bool CShoutcastFile::Open(const CURL& url)
70 {
71   CURL url2(url);
72   url2.SetProtocolOptions(url2.GetProtocolOptions()+"&noshout=true&Icy-MetaData=1");
73   url2.SetProtocol("http");
74
75   bool result = m_file.Open(url2.Get());
76   if (result)
77   {
78     m_tag.SetTitle(m_file.GetHttpHeader().GetValue("icy-name"));
79     if (m_tag.GetTitle().empty())
80       m_tag.SetTitle(m_file.GetHttpHeader().GetValue("ice-name")); // icecast
81     m_tag.SetGenre(m_file.GetHttpHeader().GetValue("icy-genre"));
82     if (m_tag.GetGenre().empty())
83       m_tag.SetGenre(m_file.GetHttpHeader().GetValue("ice-genre")); // icecast
84     m_tag.SetLoaded(true);
85   }
86   m_fileCharset = m_file.GetServerReportedCharset();
87   m_metaint = atoi(m_file.GetHttpHeader().GetValue("icy-metaint").c_str());
88   if (!m_metaint)
89     m_metaint = -1;
90   m_buffer = new char[16*255];
91   m_tagPos = 1;
92   m_tagChange.Set();
93   Create();
94
95   return result;
96 }
97
98 unsigned int CShoutcastFile::Read(void* lpBuf, int64_t uiBufSize)
99 {
100   if (m_currint >= m_metaint && m_metaint > 0)
101   {
102     unsigned char header;
103     m_file.Read(&header,1);
104     ReadTruncated(m_buffer, header*16);
105     if (ExtractTagInfo(m_buffer)
106         // this is here to workaround issues caused by application posting callbacks to itself (3cf882d9)
107         // the callback will set an empty tag in the info manager item, while we think we have ours set
108         || (m_file.GetPosition() < 10*m_metaint && !m_tagPos))
109     {
110       m_tagPos = m_file.GetPosition();
111       m_tagChange.Set();
112     }
113     m_discarded += header*16+1;
114     m_currint = 0;
115   }
116
117   unsigned int toRead;
118   if (m_metaint > 0)
119     toRead = std::min((unsigned int)uiBufSize,(unsigned int)m_metaint-m_currint);
120   else
121     toRead = std::min((unsigned int)uiBufSize,(unsigned int)16*255);
122   toRead = m_file.Read(lpBuf,toRead);
123   m_currint += toRead;
124   return toRead;
125 }
126
127 int64_t CShoutcastFile::Seek(int64_t iFilePosition, int iWhence)
128 {
129   return -1;
130 }
131
132 void CShoutcastFile::Close()
133 {
134   StopThread();
135   delete[] m_buffer;
136   m_buffer = NULL;
137   m_file.Close();
138 }
139
140 bool CShoutcastFile::ExtractTagInfo(const char* buf)
141 {
142   CStdString strBuffer = buf;
143
144   if (!m_fileCharset.empty())
145   {
146     std::string converted;
147     g_charsetConverter.ToUtf8(m_fileCharset, strBuffer, converted);
148     strBuffer = converted;
149   }
150   else
151     g_charsetConverter.unknownToUTF8(strBuffer);
152   
153   bool result=false;
154
155   CStdStringW wBuffer, wConverted;
156   g_charsetConverter.utf8ToW(strBuffer, wBuffer, false);
157   HTML::CHTMLUtil::ConvertHTMLToW(wBuffer, wConverted);
158   g_charsetConverter.wToUTF8(wConverted, strBuffer);
159
160   CRegExp reTitle(true);
161   reTitle.RegComp("StreamTitle=\'(.*?)\';");
162
163   if (reTitle.RegFind(strBuffer.c_str()) != -1)
164   {
165     std::string newtitle(reTitle.GetMatch(1));
166     result = (m_tag.GetTitle() != newtitle);
167     m_tag.SetTitle(newtitle);
168   }
169
170   return result;
171 }
172
173 void CShoutcastFile::ReadTruncated(char* buf2, int size)
174 {
175   char* buf = buf2;
176   while (size > 0)
177   {
178     int read = m_file.Read(buf,size);
179     size -= read;
180     buf += read;
181   }
182 }
183
184 int CShoutcastFile::IoControl(EIoControl control, void* payload)
185 {
186   if (control == IOCTRL_SET_CACHE)
187     m_cacheReader = (CFileCache*)payload;
188
189   return IFile::IoControl(control, payload);
190 }
191
192 void CShoutcastFile::Process()
193 {
194   if (!m_cacheReader)
195     return;
196
197   while (!m_bStop)
198   {
199     if (m_tagChange.WaitMSec(500))
200     {
201       while (!m_bStop && m_cacheReader->GetPosition() < m_tagPos)
202         Sleep(20);
203       CApplicationMessenger::Get().SetCurrentSongTag(m_tag);
204       m_tagPos = 0;
205     }
206   }
207 }