Merge pull request #2948 from ace20022/blu_lang_fix
[vuplus_xbmc] / xbmc / filesystem / ShoutcastFile.cpp
1 /*
2  *      Copyright (C) 2005-2013 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, 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=false;
76   if ((result=m_file.Open(url2.Get())))
77   {
78     m_tag.SetTitle(m_file.GetHttpHeader().GetValue("icy-name"));
79     if (m_tag.GetTitle().IsEmpty())
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_metaint = atoi(m_file.GetHttpHeader().GetValue("icy-metaint").c_str());
87   if (!m_metaint)
88     m_metaint = -1;
89   m_buffer = new char[16*255];
90   m_tagPos = 1;
91   m_tagChange.Set();
92   Create();
93
94   return result;
95 }
96
97 unsigned int CShoutcastFile::Read(void* lpBuf, int64_t uiBufSize)
98 {
99   if (m_currint >= m_metaint && m_metaint > 0)
100   {
101     unsigned char header;
102     m_file.Read(&header,1);
103     ReadTruncated(m_buffer, header*16);
104     if (ExtractTagInfo(m_buffer)
105         // this is here to workaround issues caused by application posting callbacks to itself (3cf882d9)
106         // the callback will set an empty tag in the info manager item, while we think we have ours set
107         || (m_file.GetPosition() < 10*m_metaint && !m_tagPos))
108     {
109       m_tagPos = m_file.GetPosition();
110       m_tagChange.Set();
111     }
112     m_discarded += header*16+1;
113     m_currint = 0;
114   }
115
116   unsigned int toRead;
117   if (m_metaint > 0)
118     toRead = std::min((unsigned int)uiBufSize,(unsigned int)m_metaint-m_currint);
119   else
120     toRead = std::min((unsigned int)uiBufSize,(unsigned int)16*255);
121   toRead = m_file.Read(lpBuf,toRead);
122   m_currint += toRead;
123   return toRead;
124 }
125
126 int64_t CShoutcastFile::Seek(int64_t iFilePosition, int iWhence)
127 {
128   return -1;
129 }
130
131 void CShoutcastFile::Close()
132 {
133   StopThread();
134   delete[] m_buffer;
135   m_buffer = NULL;
136   m_file.Close();
137 }
138
139 bool CShoutcastFile::ExtractTagInfo(const char* buf)
140 {
141   CStdString strBuffer = buf;
142   g_charsetConverter.unknownToUTF8(strBuffer);
143   
144   bool result=false;
145
146   CStdStringW wBuffer, wConverted;
147   g_charsetConverter.utf8ToW(strBuffer, wBuffer, false);
148   HTML::CHTMLUtil::ConvertHTMLToW(wBuffer, wConverted);
149   g_charsetConverter.wToUTF8(wConverted, strBuffer);
150
151   CRegExp reTitle(true);
152   reTitle.RegComp("StreamTitle=\'(.*?)\';");
153
154   if (reTitle.RegFind(strBuffer.c_str()) != -1)
155   {
156     std::string newtitle = reTitle.GetReplaceString("\\1");
157     result = (m_tag.GetTitle() != newtitle);
158     m_tag.SetTitle(newtitle);
159   }
160
161   return result;
162 }
163
164 void CShoutcastFile::ReadTruncated(char* buf2, int size)
165 {
166   char* buf = buf2;
167   while (size > 0)
168   {
169     int read = m_file.Read(buf,size);
170     size -= read;
171     buf += read;
172   }
173 }
174
175 int CShoutcastFile::IoControl(EIoControl control, void* payload)
176 {
177   if (control == IOCTRL_SET_CACHE)
178     m_cacheReader = (CFileCache*)payload;
179
180   return IFile::IoControl(control, payload);
181 }
182
183 void CShoutcastFile::Process()
184 {
185   if (!m_cacheReader)
186     return;
187
188   while (!m_bStop)
189   {
190     if (m_tagChange.WaitMSec(500))
191     {
192       while (!m_bStop && m_cacheReader->GetPosition() < m_tagPos)
193         Sleep(20);
194       CApplicationMessenger::Get().SetCurrentSongTag(m_tag);
195       m_tagPos = 0;
196     }
197   }
198 }