00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "stdafx.h"
00023 #include <initguid.h>
00024 #include "shoutcastsource.h"
00025 #include "..\..\..\DSUtil\DSUtil.h"
00026 #include "..\..\..\..\include\moreuuids.h"
00027
00028 #define MAXFRAMESIZE ((144 * 320000 / 8000) + 1)
00029 #define BUFFERS 2
00030 #define MINBUFFERLENGTH 1000000i64
00031 #define AVGBUFFERLENGTH 30000000i64
00032 #define MAXBUFFERLENGTH 100000000i64
00033
00034 static const DWORD s_bitrate[2][16] =
00035 {
00036 {1,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0},
00037 {1,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}
00038 };
00039 static const DWORD s_freq[4][4] =
00040 {
00041 {11025,12000,8000,0},
00042 {0,0,0,0},
00043 {22050,24000,16000,0},
00044 {44100,48000,32000,0}
00045 };
00046 static const BYTE s_channels[4] =
00047 {
00048 2,2,2,1
00049 };
00050
00051 typedef struct
00052 {
00053 WORD sync;
00054 BYTE version;
00055 BYTE layer;
00056 DWORD bitrate;
00057 DWORD freq;
00058 BYTE channels;
00059 DWORD framesize;
00060
00061 bool ExtractHeader(CSocket& socket)
00062 {
00063 BYTE buff[4];
00064 if(4 != socket.Receive(buff, 4, MSG_PEEK))
00065 return(false);
00066
00067 sync = (buff[0]<<4)|(buff[1]>>4)|1;
00068 version = (buff[1]>>3)&3;
00069 layer = 4 - ((buff[1]>>1)&3);
00070 bitrate = s_bitrate[version&1][buff[2]>>4]*1000;
00071 freq = s_freq[version][(buff[2]>>2)&3];
00072 channels = s_channels[(buff[3]>>6)&3];
00073 framesize = freq ? ((((version&1)?144:72) * bitrate / freq) + ((buff[2]>>1)&1)) : 0;
00074
00075 return(sync == 0xfff && layer == 3 && bitrate != 0 && freq != 0);
00076 }
00077
00078 } mp3hdr;
00079
00080 #ifdef REGISTER_FILTER
00081
00082 const AMOVIESETUP_MEDIATYPE sudPinTypesOut[] =
00083 {
00084 {&MEDIATYPE_Audio, &MEDIASUBTYPE_MP3},
00085 };
00086
00087 const AMOVIESETUP_PIN sudOpPin[] =
00088 {
00089 {L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesOut), sudPinTypesOut}
00090 };
00091
00092 const AMOVIESETUP_FILTER sudFilter[] =
00093 {
00094 {&__uuidof(CShoutcastSource), L"ShoutcastSource", MERIT_UNLIKELY, countof(sudOpPin), sudOpPin}
00095 };
00096
00097 CFactoryTemplate g_Templates[] =
00098 {
00099 {sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<CShoutcastSource>, NULL, &sudFilter[0]}
00100 };
00101
00102 int g_cTemplates = countof(g_Templates);
00103
00104 STDAPI DllRegisterServer()
00105 {
00106 return AMovieDllRegisterServer2(TRUE);
00107 }
00108
00109 STDAPI DllUnregisterServer()
00110 {
00111 return AMovieDllRegisterServer2(FALSE);
00112 }
00113
00114 extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
00115
00116 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
00117 {
00118 if(dwReason == DLL_PROCESS_ATTACH)
00119 {
00120 if(!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
00121 {
00122 AfxMessageBox(_T("AfxWinInit failed!"));
00123 return FALSE;
00124 }
00125
00126 if(!AfxSocketInit(NULL))
00127 {
00128 AfxMessageBox(_T("AfxSocketInit failed!"));
00129 return FALSE;
00130 }
00131 }
00132 else if(dwReason == DLL_PROCESS_DETACH)
00133 {
00134 AfxWinTerm();
00135 }
00136
00137 return DllEntryPoint((HINSTANCE)hModule, dwReason, 0);
00138 }
00139
00140 #endif
00141
00142
00143
00144
00145
00146 CShoutcastSource::CShoutcastSource(LPUNKNOWN lpunk, HRESULT* phr)
00147 : CSource(NAME("CShoutcastSource"), lpunk, __uuidof(this))
00148 {
00149 #ifndef REGISTER_FILTER
00150 AfxSocketInit();
00151 #endif
00152 }
00153
00154 CShoutcastSource::~CShoutcastSource()
00155 {
00156 }
00157
00158 STDMETHODIMP CShoutcastSource::NonDelegatingQueryInterface(REFIID riid, void** ppv)
00159 {
00160 CheckPointer(ppv, E_POINTER);
00161
00162 return
00163 QI(IFileSourceFilter)
00164 QI(IAMFilterMiscFlags)
00165 QI(IAMOpenProgress)
00166 QI2(IAMMediaContent)
00167 __super::NonDelegatingQueryInterface(riid, ppv);
00168 }
00169
00170
00171
00172 STDMETHODIMP CShoutcastSource::Load(LPCOLESTR pszFileName, const AM_MEDIA_TYPE* pmt)
00173 {
00174 if(GetPinCount() > 0)
00175 return VFW_E_ALREADY_CONNECTED;
00176
00177 HRESULT hr = E_OUTOFMEMORY;
00178
00179 if(!(new CShoutcastStream(pszFileName, this, &hr)) || FAILED(hr))
00180 return hr;
00181
00182 m_fn = pszFileName;
00183
00184 return S_OK;
00185 }
00186
00187 STDMETHODIMP CShoutcastSource::GetCurFile(LPOLESTR* ppszFileName, AM_MEDIA_TYPE* pmt)
00188 {
00189 if(!ppszFileName) return E_POINTER;
00190
00191 if(!(*ppszFileName = (LPOLESTR)CoTaskMemAlloc((m_fn.GetLength()+1)*sizeof(WCHAR))))
00192 return E_OUTOFMEMORY;
00193
00194 wcscpy(*ppszFileName, m_fn);
00195
00196 return S_OK;
00197 }
00198
00199
00200
00201 ULONG CShoutcastSource::GetMiscFlags()
00202 {
00203 return AM_FILTER_MISC_FLAGS_IS_SOURCE;
00204 }
00205
00206
00207
00208 STDMETHODIMP CShoutcastSource::QueryProgress(LONGLONG* pllTotal, LONGLONG* pllCurrent)
00209 {
00210 if(m_iPins == 1)
00211 {
00212 if(pllTotal) *pllTotal = 100;
00213 if(pllCurrent) *pllCurrent = ((CShoutcastStream*)m_paStreams[0])->GetBufferFullness();
00214 return S_OK;
00215 }
00216
00217 return E_UNEXPECTED;
00218 }
00219
00220 STDMETHODIMP CShoutcastSource::AbortOperation()
00221 {
00222 return E_NOTIMPL;
00223 }
00224
00225
00226
00227 STDMETHODIMP CShoutcastSource::get_Title(BSTR* pbstrTitle)
00228 {
00229 CheckPointer(pbstrTitle, E_POINTER);
00230
00231 if(m_iPins == 1)
00232 {
00233 *pbstrTitle = ((CShoutcastStream*)m_paStreams[0])->GetTitle().AllocSysString();
00234 return S_OK;
00235 }
00236
00237 return E_UNEXPECTED;
00238 }
00239
00240
00241
00242 CShoutcastStream::CShoutcastStream(const WCHAR* wfn, CShoutcastSource* pParent, HRESULT* phr)
00243 : CSourceStream(NAME("ShoutcastStream"), phr, pParent, L"Output")
00244 , m_fBuffering(false)
00245 {
00246 ASSERT(phr);
00247
00248 *phr = S_OK;
00249
00250 CString fn(wfn);
00251 if(fn.Find(_T("://")) < 0) fn = _T("http://") + fn;
00252
00253 #if defined(REGISTER_FILTER) && defined(DEBUG)
00254
00255
00256
00257
00258
00259 fn = _T("http://64.236.34.72:80/stream/1011");
00260
00261
00262
00263 #endif
00264
00265 if(!m_url.CrackUrl(fn))
00266 {
00267 *phr = E_FAIL;
00268 return;
00269 }
00270
00271 if(m_url.GetUrlPathLength() == 0)
00272 m_url.SetUrlPath(_T("/"));
00273
00274 if(m_url.GetPortNumber() == ATL_URL_INVALID_PORT_NUMBER)
00275 m_url.SetPortNumber(ATL_URL_DEFAULT_HTTP_PORT);
00276
00277 if(m_url.GetScheme() != ATL_URL_SCHEME_HTTP)
00278 {
00279 *phr = E_FAIL;
00280 return;
00281 }
00282
00283 if(!m_socket.Create() || !m_socket.Connect(m_url))
00284 {
00285 *phr = E_FAIL;
00286 return;
00287 }
00288
00289 m_socket.Close();
00290 }
00291
00292 CShoutcastStream::~CShoutcastStream()
00293 {
00294 }
00295
00296 void CShoutcastStream::EmptyBuffer()
00297 {
00298 CAutoLock cAutoLock(&m_queue);
00299 m_queue.RemoveAll();
00300 }
00301
00302 LONGLONG CShoutcastStream::GetBufferFullness()
00303 {
00304 CAutoLock cAutoLock(&m_queue);
00305 if(!m_fBuffering) return 100;
00306 if(m_queue.IsEmpty()) return 0;
00307 LONGLONG ret = 100i64*(m_queue.GetTail().rtStart - m_queue.GetHead().rtStart) / AVGBUFFERLENGTH;
00308 return(min(ret, 100));
00309 }
00310
00311 CString CShoutcastStream::GetTitle()
00312 {
00313 CAutoLock cAutoLock(&m_queue);
00314 return(m_title);
00315 }
00316
00317 HRESULT CShoutcastStream::DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERTIES* pProperties)
00318 {
00319 ASSERT(pAlloc);
00320 ASSERT(pProperties);
00321
00322 HRESULT hr = NOERROR;
00323
00324 pProperties->cBuffers = BUFFERS;
00325 pProperties->cbBuffer = MAXFRAMESIZE;
00326
00327 ALLOCATOR_PROPERTIES Actual;
00328 if(FAILED(hr = pAlloc->SetProperties(pProperties, &Actual))) return hr;
00329
00330 if(Actual.cbBuffer < pProperties->cbBuffer) return E_FAIL;
00331 ASSERT(Actual.cBuffers == pProperties->cBuffers);
00332
00333 return NOERROR;
00334 }
00335
00336 HRESULT CShoutcastStream::FillBuffer(IMediaSample* pSample)
00337 {
00338 HRESULT hr;
00339
00340 BYTE* pData = NULL;
00341 if(FAILED(hr = pSample->GetPointer(&pData)) || !pData)
00342 return S_FALSE;
00343
00344 do
00345 {
00346
00347 {
00348 CAutoLock cAutoLock(&m_queue);
00349 if(!m_queue.IsEmpty() && m_queue.GetHead().rtStart < m_queue.GetTail().rtStart - MINBUFFERLENGTH)
00350 break;
00351 }
00352
00353 TRACE(_T("START BUFFERING\n"));
00354 m_fBuffering = true;
00355
00356 while(1)
00357 {
00358 if(fExitThread)
00359 return S_FALSE;
00360
00361 Sleep(50);
00362
00363 CAutoLock cAutoLock(&m_queue);
00364 if(!m_queue.IsEmpty() && m_queue.GetHead().rtStart < m_queue.GetTail().rtStart - AVGBUFFERLENGTH)
00365 break;
00366 }
00367
00368 pSample->SetDiscontinuity(TRUE);
00369
00370 DeliverBeginFlush();
00371 DeliverEndFlush();
00372
00373 DeliverNewSegment(0, ~0, 1.0);
00374
00375 TRACE(_T("END BUFFERING\n"));
00376 m_fBuffering = false;
00377 }
00378 while(false);
00379
00380 {
00381 CAutoLock cAutoLock(&m_queue);
00382 ASSERT(!m_queue.IsEmpty());
00383 if(!m_queue.IsEmpty())
00384 {
00385 mp3frame f = m_queue.RemoveHead();
00386 DWORD len = min(pSample->GetSize(), f.len);
00387 memcpy(pData, f.pData, len);
00388 pSample->SetActualDataLength(len);
00389 pSample->SetTime(&f.rtStart, &f.rtStop);
00390 m_title = f.title;
00391 }
00392 }
00393
00394 pSample->SetSyncPoint(TRUE);
00395
00396 return S_OK;
00397 }
00398
00399 HRESULT CShoutcastStream::GetMediaType(int iPosition, CMediaType* pmt)
00400 {
00401 CAutoLock cAutoLock(m_pFilter->pStateLock());
00402
00403 if(iPosition < 0) return E_INVALIDARG;
00404 if(iPosition > 0) return VFW_S_NO_MORE_ITEMS;
00405
00406 pmt->SetType(&MEDIATYPE_Audio);
00407 pmt->SetSubtype(&MEDIASUBTYPE_MP3);
00408 pmt->SetFormatType(&FORMAT_WaveFormatEx);
00409
00410 WAVEFORMATEX* wfe = (WAVEFORMATEX*)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX));
00411 memset(wfe, 0, sizeof(WAVEFORMATEX));
00412 wfe->wFormatTag = (WORD)MEDIASUBTYPE_MP3.Data1;
00413 wfe->nChannels = (WORD)m_socket.m_channels;
00414 wfe->nSamplesPerSec = m_socket.m_freq;
00415 wfe->nAvgBytesPerSec = m_socket.m_bitrate/8;
00416 wfe->nBlockAlign = 1;
00417 wfe->wBitsPerSample = 0;
00418
00419 return NOERROR;
00420 }
00421
00422 HRESULT CShoutcastStream::CheckMediaType(const CMediaType* pmt)
00423 {
00424 if(pmt->majortype == MEDIATYPE_Audio
00425 && pmt->subtype == MEDIASUBTYPE_MP3
00426 && pmt->formattype == FORMAT_WaveFormatEx) return S_OK;
00427
00428 return E_INVALIDARG;
00429 }
00430
00431 static UINT SocketThreadProc(LPVOID pParam)
00432 {
00433 return ((CShoutcastStream*)pParam)->SocketThreadProc();
00434 }
00435
00436 UINT CShoutcastStream::SocketThreadProc()
00437 {
00438 fExitThread = false;
00439
00440 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
00441
00442 AfxSocketInit();
00443
00444 CAutoVectorPtr<BYTE> pData;
00445 if(!m_socket.Create() || !m_socket.Connect(m_url)
00446 || !pData.Allocate(max(m_socket.m_metaint, MAXFRAMESIZE)))
00447 {
00448 m_socket.Close();
00449 return 1;
00450 }
00451
00452 REFERENCE_TIME m_rtSampleTime = 0;
00453
00454 while(!fExitThread)
00455 {
00456 int len = MAXFRAMESIZE;
00457 len = m_socket.Receive(pData, len);
00458 if(len <= 0) break;
00459
00460 mp3frame f(len);
00461 memcpy(f.pData, pData, len);
00462 f.rtStop = (f.rtStart = m_rtSampleTime) + (10000000i64 * len * 8/m_socket.m_bitrate);
00463 m_rtSampleTime = f.rtStop;
00464 f.title = m_socket.m_title;
00465
00466 CAutoLock cAutoLock(&m_queue);
00467 m_queue.AddTail(f);
00468 }
00469
00470 m_socket.Close();
00471
00472 return 0;
00473 }
00474
00475 HRESULT CShoutcastStream::OnThreadCreate()
00476 {
00477 EmptyBuffer();
00478
00479 fExitThread = true;
00480 m_hSocketThread = AfxBeginThread(::SocketThreadProc, this)->m_hThread;
00481 while(fExitThread) Sleep(10);
00482
00483 return NOERROR;
00484 }
00485
00486 HRESULT CShoutcastStream::OnThreadDestroy()
00487 {
00488 EmptyBuffer();
00489
00490 fExitThread = true;
00491 m_socket.CancelBlockingCall();
00492 WaitForSingleObject(m_hSocketThread, -1);
00493
00494 return NOERROR;
00495 }
00496
00497 HRESULT CShoutcastStream::Inactive()
00498 {
00499 fExitThread = true;
00500 return __super::Inactive();
00501 }
00502
00503
00504
00505 int CShoutcastStream::CShoutcastSocket::Receive(void* lpBuf, int nBufLen, int nFlags)
00506 {
00507 if(nFlags&MSG_PEEK)
00508 return __super::Receive(lpBuf, nBufLen, nFlags);
00509
00510 if(m_metaint > 0 && m_nBytesRead + nBufLen > m_metaint)
00511 nBufLen = m_metaint - m_nBytesRead;
00512
00513 int len = __super::Receive(lpBuf, nBufLen, nFlags);
00514 if(len <= 0) return len;
00515
00516 if((m_nBytesRead += len) == m_metaint)
00517 {
00518 m_nBytesRead = 0;
00519
00520 static BYTE buff[255*16], b = 0;
00521 memset(buff, 0, sizeof(buff));
00522 if(1 == __super::Receive(&b, 1) && b && b*16 == __super::Receive(buff, b*16))
00523 {
00524 CStringA str = (LPCSTR)buff, title("StreamTitle='");
00525 TRACE(_T("Metainfo: %s\n"), CString(str));
00526 int i = str.Find(title);
00527 if(i >= 0)
00528 {
00529 i += title.GetLength();
00530 int j = str.Find('\'', i);
00531 if(j > i) m_title = str.Mid(i, j - i);
00532 }
00533 else
00534 {
00535 TRACE(_T("!!!!!!!!!Missing StreamTitle!!!!!!!!!\n"));
00536 }
00537 }
00538 }
00539 else if(m_metaint > 0)
00540 {
00541 char* p = (char*)lpBuf;
00542 char* p0 = p;
00543 char* pend = p + len - 13;
00544 for(; p < pend; p++)
00545 {
00546 if(strncmp(p, "StreamTitle='", 13))
00547 continue;
00548
00549 TRACE(_T("!!!!!!!!!StreamTitle found inside mp3 data!!!!!!!!! offset=%d\n"), p - p0);
00550 TRACE(_T("resyncing...\n"));
00551 while(p-- > p0)
00552 {
00553 if((BYTE)*p >= 0x20)
00554 continue;
00555
00556 TRACE(_T("found possible length byte: %d, skipping %d bytes\n"), *p, 1 + *p*16);
00557 p += 1 + *p*16;
00558 len = (p0 + len) - p;
00559 TRACE(_T("returning the remaining bytes in the packet: %d\n"), len);
00560 if(len <= 0)
00561 {
00562 TRACE(_T("nothing to return, reading a bit more in\n"));
00563 if(len < 0) __super::Receive(lpBuf, -len, nFlags);
00564
00565 int len = __super::Receive(lpBuf, nBufLen, nFlags);
00566 if(len <= 0) return len;
00567 }
00568
00569 m_nBytesRead = len;
00570 memcpy(lpBuf, p, len);
00571
00572 break;
00573 }
00574
00575 break;
00576 }
00577 }
00578
00579 return len;
00580 }
00581
00582 bool CShoutcastStream::CShoutcastSocket::Connect(CUrl& url)
00583 {
00584 if(!__super::Connect(url.GetHostName(), url.GetPortNumber()))
00585 return(false);
00586
00587 CStringA str;
00588 str.Format(
00589 "GET %s HTTP/1.0\r\n"
00590 "Icy-MetaData:1\r\n"
00591 "User-Agent: shoutcastsource\r\n"
00592 "Host: %s\r\n"
00593 "Accept: */*\r\n"
00594 "Connection: Keep-Alive\r\n"
00595 "\r\n", CStringA(url.GetUrlPath()), CStringA(url.GetHostName()));
00596
00597 bool fOK = false;
00598 bool fTryAgain = false;
00599 int metaint = 0;
00600
00601 do
00602 {
00603 int len = Send((BYTE*)(LPCSTR)str, str.GetLength());
00604
00605 m_nBytesRead = 0;
00606 m_metaint = metaint = 0;
00607 m_bitrate = 0;
00608
00609 str.Empty();
00610 BYTE cur = 0, prev = 0;
00611 while(Receive(&cur, 1) == 1 && cur && !(cur == '\n' && prev == '\n'))
00612 {
00613 if(cur == '\r')
00614 continue;
00615
00616 if(cur == '\n')
00617 {
00618 str.MakeLower();
00619 if(str.Find("icy 200 ok") >= 0) fOK = true;
00620 else if(1 == sscanf(str, "icy-br:%d", &m_bitrate)) m_bitrate *= 1000;
00621 else if(1 == sscanf(str, "icy-metaint:%d", &metaint)) metaint = metaint;
00622 str.Empty();
00623 }
00624 else
00625 {
00626 str += cur;
00627 }
00628
00629 prev = cur;
00630 cur = 0;
00631 }
00632
00633 if(!fOK && GetLastError() == WSAECONNRESET && !fTryAgain)
00634 {
00635 str.Format(
00636 "GET %s HTTP/1.0\r\n"
00637 "Icy-MetaData:1\r\n"
00638 "Host: %s\r\n"
00639 "Accept: */*\r\n"
00640 "Connection: Keep-Alive\r\n"
00641 "\r\n", CStringA(url.GetUrlPath()), CStringA(url.GetHostName()));
00642
00643 fTryAgain = true;
00644 }
00645 else
00646 {
00647 fTryAgain = false;
00648 }
00649 }
00650 while(fTryAgain);
00651
00652 if(!fOK || m_bitrate == 0) {Close(); return(false);}
00653
00654 m_metaint = metaint;
00655 m_nBytesRead = 0;
00656
00657 return(FindSync());
00658 }
00659
00660 bool CShoutcastStream::CShoutcastSocket::FindSync()
00661 {
00662 m_freq = -1;
00663 m_channels = -1;
00664
00665 BYTE b;
00666 for(int i = MAXFRAMESIZE; i > 0; i--, Receive(&b, 1))
00667 {
00668 mp3hdr h;
00669 if(h.ExtractHeader(*this) && m_bitrate == h.bitrate)
00670 {
00671 if(h.bitrate > 1) m_bitrate = h.bitrate;
00672 m_freq = h.freq;
00673 m_channels = h.channels;
00674 return(true);
00675 }
00676 }
00677
00678 return(false);
00679 }