Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

Diff of /xbmc/FileSystem/FileShoutcast.cpp [1bb066] .. [ed57a4] Maximize Restore

Repo status: analyzing...

  Switch to unified view

a/xbmc/FileSystem/FileShoutcast.cpp b/xbmc/FileSystem/FileShoutcast.cpp
...
...
31
#include "GUISettings.h"
31
#include "GUISettings.h"
32
#include "GUIDialogProgress.h"
32
#include "GUIDialogProgress.h"
33
#include "GUIWindowManager.h"
33
#include "GUIWindowManager.h"
34
#include "URL.h"
34
#include "URL.h"
35
#include "utils/TimeUtils.h"
35
#include "utils/TimeUtils.h"
36
#include "LocalizeStrings.h"
37
36
38
//////////////////////////////////////////////////////////////////////
37
//////////////////////////////////////////////////////////////////////
39
// Construction/Destruction
38
// Construction/Destruction
40
//////////////////////////////////////////////////////////////////////
39
//////////////////////////////////////////////////////////////////////
41
40
42
// prevent inclusion of config.h from libshout
43
#define __SRCONFIG_H__
44
#include "lib/libshout/rip_manager.h"
45
#include "lib/libshout/filelib.h"
46
#undef __SRCONFIG_H__
47
48
#include "RingBuffer.h"
49
#include "ShoutcastRipFile.h"
50
#include "utils/GUIInfoManager.h"
41
#include "utils/GUIInfoManager.h"
51
#include "utils/log.h"
42
#include "utils/log.h"
52
43
53
//using namespace std; On VS2010 error_code conflicts with std::error_code
54
using namespace XFILE;
44
using namespace XFILE;
55
using namespace MUSIC_INFO;
45
using namespace MUSIC_INFO;
56
46
57
#ifndef HAS_SHOUTCAST
58
extern "C"
59
{
60
  error_code rip_manager_start(void (*status_callback)(int message, void *data), RIP_MANAGER_OPTIONS *options) { return 0; }
61
  void       rip_manager_stop() { }
62
  void       set_rip_manager_options_defaults(RIP_MANAGER_OPTIONS*) {}
63
  int        rip_manager_get_content_type() { return 0; }
64
}
65
#endif
66
67
const int SHOUTCASTTIMEOUT = 60;
68
static CRingBuffer m_ringbuf;
69
70
static FileState m_fileState;
71
static CShoutcastRipFile m_ripFile;
72
73
74
static RIP_MANAGER_INFO m_ripInfo;
75
static ERROR_INFO m_errorInfo;
76
77
78
79
void rip_callback(int message, void *data)
80
{
81
  switch (message)
82
  {
83
    RIP_MANAGER_INFO *info;
84
  case RM_UPDATE:
85
    info = (RIP_MANAGER_INFO*)data;
86
    memcpy(&m_ripInfo, info, sizeof(m_ripInfo));
87
    if (info->status == RM_STATUS_BUFFERING)
88
    {
89
      m_fileState.bBuffering = true;
90
    }
91
    else if ( info->status == RM_STATUS_RIPPING)
92
    {
93
      m_ripFile.SetRipManagerInfo( &m_ripInfo );
94
      m_fileState.bBuffering = false;
95
    }
96
    else if (info->status == RM_STATUS_RECONNECTING)
97
  { }
98
    break;
99
  case RM_ERROR:
100
    ERROR_INFO *errInfo;
101
    errInfo = (ERROR_INFO*)data;
102
    memcpy(&m_errorInfo, errInfo, sizeof(m_errorInfo));
103
    m_fileState.bRipError = true;
104
    OutputDebugString("error\n");
105
    break;
106
  case RM_DONE:
107
    OutputDebugString("done\n");
108
    m_fileState.bRipDone = true;
109
    break;
110
  case RM_NEW_TRACK:
111
    char *trackName;
112
    trackName = (char*) data;
113
    m_ripFile.SetTrackname( trackName );
114
    break;
115
  case RM_STARTED:
116
    m_fileState.bRipStarted = true;
117
    OutputDebugString("Started\n");
118
    break;
119
  }
120
121
}
122
123
extern "C" {
124
error_code filelib_write_show(char *buf, u_long size)
125
{
126
  if ((unsigned int)size > m_ringbuf.getSize())
127
  {
128
    CLog::Log(LOGERROR, "Shoutcast chunk too big: %lu", size);
129
    return SR_ERROR_BUFFER_FULL;
130
  }
131
  while (m_ringbuf.getMaxWriteSize() < (unsigned int)size) Sleep(10);
132
  m_ringbuf.WriteData(buf, size);
133
  m_ripFile.Write( buf, size ); //will only write, if it has to
134
  if (m_fileState.bBuffering)
135
  {
136
    if (rip_manager_get_content_type() == CONTENT_TYPE_OGG)
137
    {
138
      if (m_ringbuf.getMaxReadSize() > (m_ringbuf.getSize() / 8) )
139
      {
140
        // hack because ogg streams are very broke, force it to go.
141
        m_fileState.bBuffering = false;
142
      }
143
    }
144
  }
145
146
  return SR_SUCCESS;
147
}
148
}
149
150
CFileShoutcast* m_pShoutCastRipper = NULL;
151
152
CFileShoutcast::CFileShoutcast()
47
CFileShoutcast::CFileShoutcast()
153
{
48
{
154
  // FIXME: without this check
49
  m_lastTime = CTimeUtils::GetTimeMS();
155
  // the playback stops when CFile::Stat()
50
  m_discarded = 0;
156
  // or CFile::Exists() is called
51
  m_currint = 0;
157
52
  m_buffer = NULL;
158
  // Do we already have another file
159
  // using the ripper?
160
  if (!m_pShoutCastRipper)
161
  {
162
    m_fileState.bBuffering = true;
163
    m_fileState.bRipDone = false;
164
    m_fileState.bRipStarted = false;
165
    m_fileState.bRipError = false;
166
    m_ringbuf.Create(1024*1024); // must be big enough. some stations use 192kbps.
167
    m_pShoutCastRipper = this;
168
  }
169
}
53
}
170
54
171
CFileShoutcast::~CFileShoutcast()
55
CFileShoutcast::~CFileShoutcast()
172
{
56
{
173
  // FIXME: without this check
57
  Close();
174
  // the playback stops when CFile::Stat()
175
  // or CFile::Exists() is called
176
177
  // Has this object initialized the ripper?
178
  if (m_pShoutCastRipper==this)
179
  {
180
    rip_manager_stop();
181
    m_pShoutCastRipper = NULL;
182
    m_ripFile.Reset();
183
    m_ringbuf.Destroy();
184
  }
185
}
58
}
186
187
bool CFileShoutcast::CanRecord()
188
{
189
  if ( !m_fileState.bRipStarted )
190
    return false;
191
  return m_ripFile.CanRecord();
192
}
193
194
bool CFileShoutcast::Record()
195
{
196
  return m_ripFile.Record();
197
}
198
199
void CFileShoutcast::StopRecording()
200
{
201
  m_ripFile.StopRecording();
202
}
203
204
59
205
int64_t CFileShoutcast::GetPosition()
60
int64_t CFileShoutcast::GetPosition()
206
{
61
{
207
  return 0;
62
  return m_file.GetPosition()-m_discarded;
208
}
63
}
209
64
210
int64_t CFileShoutcast::GetLength()
65
int64_t CFileShoutcast::GetLength()
211
{
66
{
212
  return 0;
67
  return 0;
213
}
68
}
214
69
215
216
bool CFileShoutcast::Open(const CURL& url)
70
bool CFileShoutcast::Open(const CURL& url)
217
{
71
{
218
  m_lastTime = CTimeUtils::GetTimeMS();
72
  CURL url2(url);
73
  url2.SetProtocolOptions("noshout=true&Icy-MetaData=1");
219
74
220
  CGUIDialogProgress* dlgProgress = NULL;
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().IsEmpty())
83
      m_tag.SetGenre(m_file.GetHttpHeader().GetValue("ice-genre")); // icecast
84
    m_tag.SetLoaded(true);
85
    g_infoManager.SetCurrentSongTag(m_tag);
86
  }
87
  m_metaint = atoi(m_file.GetHttpHeader().GetValue("icy-metaint").c_str());
88
  m_buffer = new char[16*255];
221
89
222
  // dvdplayer can deadlock with progress dialog so check first
223
  if (g_application.GetCurrentPlayer() == EPC_PAPLAYER)
224
  {
225
    dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
226
  }
227
228
  set_rip_manager_options_defaults(&m_opt);
229
230
  strcpy(m_opt.output_directory, "./");
231
  m_opt.proxyurl[0] = '\0';
232
233
  // Use a proxy, if the GUI was configured as such
234
  bool bProxyEnabled = g_guiSettings.GetBool("network.usehttpproxy");
235
  if (bProxyEnabled)
236
  {
237
    const CStdString &strProxyServer = g_guiSettings.GetString("network.httpproxyserver");
238
    const CStdString &strProxyPort = g_guiSettings.GetString("network.httpproxyport");
239
    // Should we check for valid strings here
240
#ifndef _LINUX
241
    _snprintf( m_opt.proxyurl, MAX_URL_LEN, "http://%s:%s", strProxyServer.c_str(), strProxyPort.c_str() );
242
#else
243
    snprintf( m_opt.proxyurl, MAX_URL_LEN, "http://%s:%s", strProxyServer.c_str(), strProxyPort.c_str() );
244
#endif
245
  }
246
247
  CStdString strUrl = url.Get();
248
  strUrl.Replace("shout://", "http://");
249
  strncpy(m_opt.url, strUrl.c_str(), MAX_URL_LEN);
250
  sprintf(m_opt.useragent, "x%s", url.GetFileName().c_str());
251
  if (dlgProgress)
252
  {
253
    dlgProgress->SetHeading(260);
254
    dlgProgress->SetLine(0, 259);
255
    dlgProgress->SetLine(1, strUrl);
256
    dlgProgress->SetLine(2, "");
257
    if (!dlgProgress->IsDialogRunning())
258
      dlgProgress->StartModal();
259
    dlgProgress->Progress();
260
  }
261
262
  if (rip_manager_start(rip_callback, &m_opt) != SR_SUCCESS)
263
  {
264
    if (dlgProgress) dlgProgress->Close();
265
    return false;
266
  }
267
  int iShoutcastTimeout = 10 * SHOUTCASTTIMEOUT; //i.e: 10 * 10 = 100 * 100ms = 10s
268
  int iCount = 0;
269
  while (!m_fileState.bRipDone && !m_fileState.bRipStarted && !m_fileState.bRipError && (!dlgProgress || !dlgProgress->IsCanceled()))
270
  {
271
    if (iCount <= iShoutcastTimeout) //Normally, this isn't the problem,
272
      //because if RIP_MANAGER fails, this would be here
273
      //with m_fileState.bRipError
274
    {
275
      Sleep(100);
276
    }
277
    else
278
    {
279
      if (dlgProgress)
280
      {
281
        dlgProgress->SetLine(1, 257);
282
        dlgProgress->SetLine(2, "Connection timed out...");
283
        Sleep(1500);
284
        dlgProgress->Close();
285
      }
286
      return false;
287
    }
288
    iCount++;
289
  }
290
291
  if (dlgProgress && dlgProgress->IsCanceled())
292
  {
293
     Close();
294
     dlgProgress->Close();
295
     return false;
296
  }
297
298
  /* store content type of stream */
299
  m_mimetype = rip_manager_get_content_type();
300
301
  //CHANGED CODE: Don't reset timer anymore.
302
303
  while (!m_fileState.bRipDone && !m_fileState.bRipError && m_fileState.bBuffering && (!dlgProgress || !dlgProgress->IsCanceled()))
304
  {
305
    if (iCount <= iShoutcastTimeout) //Here is the real problem: Sometimes the buffer fills just to
306
      //slowly, thus the quality of the stream will be bad, and should be
307
      //aborted...
308
    {
309
      Sleep(100);
310
      char szTmp[1024];
311
      //g_dialog.SetCaption(0, "Shoutcast" );
312
      sprintf(szTmp, g_localizeStrings.Get(23052).c_str(), m_ringbuf.getMaxReadSize());
313
      if (dlgProgress)
314
      {
315
        dlgProgress->SetLine(2, szTmp );
316
        dlgProgress->Progress();
317
      }
318
319
      sprintf(szTmp, "%s", m_ripInfo.filename);
320
      for (int i = 0; i < (int)strlen(szTmp); i++)
321
        szTmp[i] = tolower((unsigned char)szTmp[i]);
322
      szTmp[50] = 0;
323
      if (dlgProgress)
324
      {
325
        dlgProgress->SetLine(1, szTmp );
326
        dlgProgress->Progress();
327
      }
328
    }
329
    else //it's not really a connection timeout, but it's here,
330
      //where things get boring, if connection is slow.
331
      //trust me, i did a lot of testing... Doesn't happen often,
332
      //but if it does it sucks to wait here forever.
333
      //CHANGED: Other message here
334
    {
335
      if (dlgProgress)
336
      {
337
        dlgProgress->SetLine(1, 257);
338
        dlgProgress->SetLine(2, "Connection to server too slow...");
339
        dlgProgress->Close();
340
      }
341
      return false;
342
    }
343
    iCount++;
344
  }
345
  if (dlgProgress && dlgProgress->IsCanceled())
346
  {
347
     Close();
348
     dlgProgress->Close();
349
     return false;
350
  }
351
  if ( m_fileState.bRipError )
352
  {
353
    if (dlgProgress)
354
    {
355
      dlgProgress->SetLine(1, 257);
356
      dlgProgress->SetLine(2, 16029);
357
      CLog::Log(LOGERROR, "%s: error - %s", __FUNCTION__, m_errorInfo.error_str);
358
      dlgProgress->Progress();
359
360
      Sleep(1500);
361
      dlgProgress->Close();
362
    }
363
    return false;
364
  }
365
  if (dlgProgress)
366
  {
367
    dlgProgress->SetLine(2, 261);
368
    dlgProgress->Progress();
369
    dlgProgress->Close();
370
  }
371
  return true;
90
  return result;
372
}
91
}
373
92
374
unsigned int CFileShoutcast::Read(void* lpBuf, int64_t uiBufSize)
93
unsigned int CFileShoutcast::Read(void* lpBuf, int64_t uiBufSize)
375
{
94
{
376
  if (m_fileState.bRipDone)
95
  if (m_currint >= m_metaint && m_metaint > 0)
377
  {
96
  {
378
    OutputDebugString("Read done\n");
97
    unsigned char header;
379
    return 0;
98
    m_file.Read(&header,1);
99
    ReadTruncated(m_buffer, header*16);
100
    ExtractTagInfo(m_buffer);
101
    m_discarded += header*16+1;
102
    m_currint = 0;
380
  }
103
  }
381
382
  int slept=0;
383
  while (m_ringbuf.getMaxReadSize() <= 0)
384
  {
385
    Sleep(10);
386
    if (slept += 10 > SHOUTCASTTIMEOUT*1000)
387
      return -1;
388
  }
389
390
  int iRead = m_ringbuf.getMaxReadSize();
391
  if (iRead > uiBufSize) iRead = (int)uiBufSize;
392
  m_ringbuf.ReadData((char*)lpBuf, iRead);
393
394
  if (CTimeUtils::GetTimeMS() - m_lastTime > 500)
104
  if (CTimeUtils::GetTimeMS() - m_lastTime > 500)
395
  {
105
  {
396
    m_lastTime = CTimeUtils::GetTimeMS();
106
    m_lastTime = CTimeUtils::GetTimeMS();
397
    CMusicInfoTag tag;
398
    GetMusicInfoTag(tag);
399
    g_infoManager.SetCurrentSongTag(tag);
107
    g_infoManager.SetCurrentSongTag(m_tag);
400
  }
108
  }
401
  return iRead;
402
}
403
109
404
void CFileShoutcast::outputTimeoutMessage(const char* message)
110
  unsigned int toRead = std::min((unsigned int)uiBufSize,(unsigned int)m_metaint-m_currint);
405
{
111
  toRead = m_file.Read(lpBuf,toRead);
406
  //g_dialog.SetCaption(0, "Shoutcast"  );
112
  m_currint += toRead;
407
  //g_dialog.SetMessage(0,  message );
113
  return toRead;
408
  //g_dialog.Render();
409
  Sleep(1500);
410
}
114
}
411
115
412
int64_t CFileShoutcast::Seek(int64_t iFilePosition, int iWhence)
116
int64_t CFileShoutcast::Seek(int64_t iFilePosition, int iWhence)
413
{
117
{
414
  return -1;
118
  return -1;
415
}
119
}
416
120
417
void CFileShoutcast::Close()
121
void CFileShoutcast::Close()
418
{
122
{
419
  OutputDebugString("Shoutcast Stopping\n");
123
  delete[] m_buffer;
420
  if ( m_ripFile.IsRecording() )
124
  m_file.Close();
421
    m_ripFile.StopRecording();
422
  m_ringbuf.Clear();
423
  rip_manager_stop();
424
  m_ripFile.Reset();
425
  OutputDebugString("Shoutcast Stopped\n");
426
}
125
}
427
126
428
bool CFileShoutcast::IsRecording()
127
void CFileShoutcast::ExtractTagInfo(const char* buf)
429
{
128
{
430
  return m_ripFile.IsRecording();
129
  char temp[1024];
130
  if (sscanf(buf,"StreamTitle='%[^']",temp) > 0)
131
    m_tag.SetTitle(temp);
431
}
132
}
432
133
433
bool CFileShoutcast::GetMusicInfoTag(CMusicInfoTag& tag)
134
void CFileShoutcast::ReadTruncated(char* buf2, int size)
434
{
135
{
435
  m_ripFile.GetMusicInfoTag(tag);
136
  char* buf = buf2;
436
  return true;
137
  while (size > 0)
138
  {
139
    int read = m_file.Read(buf,size);
140
    size -= read;
141
    buf += read;
142
  }
437
}
143
}
438
144
439
CStdString CFileShoutcast::GetContent()
440
{
441
  switch (m_mimetype)
442
  {
443
    case CONTENT_TYPE_MP3:
444
      return "audio/mpeg";
445
    case CONTENT_TYPE_OGG:
446
      return "audio/ogg";
447
    case CONTENT_TYPE_AAC:
448
      return "audio/aac";
449
    default:
450
      return "application/octet-stream";
451
  }
452
}