[xiph-cvs] cvs commit: vorbis-plugins/winamp httpstream.c httpstream.h resource.h resource.rc
Jack Moffitt
jack at xiph.org
Tue Sep 26 23:45:31 PDT 2000
jack 00/09/26 23:45:31
Added: winamp httpstream.c httpstream.h resource.h resource.rc
Log:
moved from vorbis module
Revision Changes Path
1.1 vorbis-plugins/winamp/httpstream.c
Index: httpstream.c
===================================================================
/*********************************************
*
* httpstream.c
*
* HTTP streaming support for OggVorbis by Aaron Porter <aaron at javasource.org>
* Licensed under terms of the LGPL
*
* Playback will start as soon as Ogg has enough data. Ogg doesn't have to wait
* till the buffer's full (like the MP3 plugin) before it starts playback. :-)
*
* As long as the connection is faster than the Vorbis bitrate everything should
* be fine (no interruptions).
*
*********************************************/
#include <windows.h>
#include <string.h>
#include <process.h>
#include <commctrl.h>
#include <vorbis/vorbisfile.h>
#include "httpstream.h"
#include "resource.h"
#define OGG_PROTOCOL "http://"
#define OGG_MIME_TYPE "application/x-ogg"
#define USER_AGENT "OggVorbis Plugin"
#define CIRCULAR_BUFFER_SIZE 65536 // 64K should be plenty
typedef struct
{
SOCKET tcpSocket;
int killBufferThread; // trigger to kill buffering thread
HANDLE bufferThreadHandle; // the handle to the http buffering thread
int circularBufferStart;
int circularBufferEnd;
long bytesRead;
char circularBuffer[CIRCULAR_BUFFER_SIZE];
char httpResponseHeader[16384]; // 16K HTTP header should be enough
} BufferThreadData;
char lastUrlChecked[2048];
int lastUrlOgg = 0;
char proxy[256];
HWND hwndPlayer = 0;
HWND hwndMessage = 0;
WINDOWPOS messageWindowPos;
HWND hwndText = 0;
HWND hwndProgress = 0;
void BufferThread(void *vp); // the buffer thread procedure
BufferThreadData * createBufferThreadData()
{
BufferThreadData * pData = malloc(sizeof(BufferThreadData));
if (!pData)
return 0;
pData->tcpSocket = socket(AF_INET, SOCK_STREAM, 0);
pData->killBufferThread = 0;
pData->bufferThreadHandle = INVALID_HANDLE_VALUE;
pData->circularBufferStart = 0;
pData->circularBufferEnd = 0;
pData->bytesRead = 0;
*(pData->httpResponseHeader) = 0;
return pData;
}
void showMessage(const char * message)
{
RECT playerRect;
RECT messageRect;
int top;
if (!hwndMessage || !hwndText || !hwndProgress)
return;
ShowWindow(hwndProgress, SW_HIDE);
ShowWindow(hwndText, SW_SHOW);
SetWindowText(hwndText, message);
GetWindowRect(hwndPlayer, &playerRect);
GetWindowRect(hwndMessage, &messageRect);
top = playerRect.top - (messageRect.bottom - messageRect.top);
if (top < 0)
top = 0;
MoveWindow(hwndMessage, playerRect.left, top, messageRect.right - messageRect.left, messageRect.bottom - messageRect.top, TRUE);
ShowWindow(hwndMessage, SW_SHOW);
UpdateWindow(hwndMessage);
}
void showError(const char * message)
{
showMessage(message);
SetTimer(hwndMessage, 2, 5000, NULL);
}
void showPercentage(int percentage)
{
PostMessage(hwndProgress, PBM_SETPOS, (WPARAM) percentage, 0);
RedrawWindow(hwndMessage, 0, 0, 0);
}
void hideMessage()
{
if (hwndMessage)
ShowWindow(hwndMessage, SW_HIDE);
}
int readHttpResponseHeader(BufferThreadData * pThreadData)
{
int index;
char * space;
for (index = 0; index < 8191; index++)
{
if (recv(pThreadData->tcpSocket, pThreadData->httpResponseHeader + index, 1, 0) == SOCKET_ERROR)
return -1;
if (pThreadData->httpResponseHeader[index] == '\r')
index--; // discard any CRs
else if ((index > 0) && (pThreadData->httpResponseHeader[index] == '\n') && (pThreadData->httpResponseHeader[index-1] == '\n'))
break; // "\n\n" ends the HTTP header
}
pThreadData->httpResponseHeader[index + 1] = 0;
space = strchr(pThreadData->httpResponseHeader, ' ');
if (space)
{
char status[4];
for (index = 0; index < 3; index++)
status[index] = *(space + 1 + index);
status[3] = 0;
return atoi(status);
}
return -1;
}
int httpConnect(const char * constUrl, BufferThreadData * pThreadData, BOOL getContent, BOOL showMessages)
{
char * url;
char * colon;
char * slash;
char * server;
int port;
unsigned long ulAddr=0;
struct sockaddr_in sockaddr;
struct hostent * host_ent;
char buff[2048];
int status;
char uri[2048];
uri[0] = 0;
url = strdup(constUrl + strlen(OGG_PROTOCOL));
colon = strchr(url, ':');
slash = strchr(url, '/');
if (*proxy)
colon = strchr(proxy, ':');
if (colon)
*colon = 0;
if (slash)
*slash = 0;
if (*proxy)
server = proxy;
else
server = url;
if (getContent && showMessages)
{
_snprintf(buff, 2048, "Looking up %s...", server);
showMessage(buff);
}
host_ent = gethostbyname(server);
if (!host_ent)
{
_snprintf(buff, 2048, "Couldn't find server %s!", server);
showError(buff);
return 0;
}
memcpy(&sockaddr.sin_addr, host_ent->h_addr, host_ent->h_length);
port = 80;
if (colon && (!slash || (colon < slash)))
port = atoi(colon + 1);
if (slash)
{
int index;
int count;
*slash = '/';
count = strlen(slash);
for (index = 0; index < count; index++) // URL encode
{
char c = slash[index];
if ((c == '/') || (c == '.') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
strncat(uri, slash + index, 1);
else
{
int uriLength;
strcat(uri, "%");
uriLength = strlen(uri);
_snprintf(uri + uriLength, 2048 - uriLength, "%X", c);
}
}
*slash = 0;
}
else
strcpy(uri, "/");
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons((unsigned short) port);
if (getContent && showMessages)
{
_snprintf(buff, 2048, "Connecting to %s...", server);
showMessage(buff);
}
if (connect(pThreadData->tcpSocket, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) == SOCKET_ERROR)
{
_snprintf(buff, 2048, "Couldn't connect to server %s!", server);
showError(buff);
return 0;
}
if (getContent && showMessages)
{
_snprintf(buff, 2048, "Requesting %s...", uri);
showMessage(buff);
}
if (*proxy)
{
server = url;
colon = strchr(server, ':');
if (colon)
{
*colon = 0;
port = atoi(colon + 1);
}
else
port = 80;
_snprintf(buff, 2048, "%s http://%s:%d%s HTTP/1.0\nHost: %s\nUser-Agent: %s\nAccept: %s, */*\n\n", (getContent ? "GET" : "HEAD"), server, port, uri, server, USER_AGENT, OGG_MIME_TYPE);
}
else
_snprintf(buff, 2048, "%s %s HTTP/1.0\nHost: %s\nUser-Agent: %s\nAccept: %s, */*\n\n", (getContent ? "GET" : "HEAD"), uri, server, USER_AGENT, OGG_MIME_TYPE);
if (send(pThreadData->tcpSocket, buff, strlen(buff), 0) == SOCKET_ERROR)
{
_snprintf(buff, 2048, "Couldn't send request to server %s!", server);
showError(buff);
return 0;
}
status = readHttpResponseHeader(pThreadData);
if (status != 200)
{
hideMessage();
if (status == -1)
{
_snprintf(buff, 2048, "Error reading response from %s!", server);
showError(buff);
return 0;
}
_snprintf(buff, 2048, "Server Error: %s", strchr(pThreadData->httpResponseHeader, ' ') + 1);
showError(buff);
return 0;
}
free(url);
return 1;
}
char * getHttpResponseHeader(BufferThreadData *pThreadData, const char * headerName)
{
char * lwrHeader;
char * lwrHeaderName;
char * valueStart;
char * lineEnd;
char * value;
lwrHeader = _strlwr(strdup(pThreadData->httpResponseHeader));
lwrHeaderName = _strlwr(strdup(headerName));
valueStart = strstr(lwrHeader, lwrHeaderName);
if (!valueStart)
return NULL;
valueStart = pThreadData->httpResponseHeader + (valueStart - lwrHeader); // Use the real header not the lower case one
valueStart += strlen(headerName);
if (*valueStart != ':')
return NULL;
valueStart++;
while (*valueStart == ' ')
valueStart++;
lineEnd = strchr(valueStart, '\n');
if (!lineEnd)
return NULL;
*lineEnd = 0;
value = strdup(valueStart);
*lineEnd = '\n';
return value;
}
BOOL CALLBACK NullDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SETFOCUS:
SetFocus(hwndPlayer);
return 0;
case WM_INITDIALOG:
SetFocus(hwndPlayer);
return 0;
case WM_TIMER:
switch (wParam)
{
case 1:
{
RECT playerRect;
RECT messageRect;
int top;
if (!hwndMessage || !hwndText || !hwndProgress)
break;
GetWindowRect(hwndPlayer, &playerRect);
GetWindowRect(hwndMessage, &messageRect);
top = playerRect.top - (messageRect.bottom - messageRect.top);
if (top < 0)
top = 0;
if ((messageRect.left != playerRect.left) || (messageRect.top != top))
MoveWindow(hwndMessage, playerRect.left, top, messageRect.right - messageRect.left, messageRect.bottom - messageRect.top, TRUE);
}
return 0;
case 2:
{
hideMessage();
KillTimer(hwndMessage, wParam);
}
return 0;
}
break;
}
return 0;
}
void httpSetHwnd(HWND hWnd)
{
hwndPlayer = hWnd;
}
void httpSetProxy(const char *string)
{
strcpy(proxy, string);
}
void httpInit()
{
WORD wVersionRequested = MAKEWORD(1, 0);
WSADATA wsaData;
HRSRC dialogResource;
if (WSAStartup(wVersionRequested, &wsaData) != 0)
{
MessageBox(hwndPlayer, "Error Initializing Windows Sockets", "Vorbis Init Error", MB_OK);
}
lastUrlChecked[0] = 0;
proxy[0] = 0;
dialogResource = FindResource(GetModuleHandle("in_vorbis.dll"), MAKEINTRESOURCE(IDD_MESSAGE), RT_DIALOG);
if (dialogResource)
{
HGLOBAL hGlobal = LoadResource(GetModuleHandle("in_vorbis.dll"), dialogResource);
if (hGlobal)
{
hwndMessage = CreateDialogIndirect((HINSTANCE) GetWindowLong(hwndPlayer, GWL_HINSTANCE), LockResource(hGlobal), hwndPlayer, NullDialogProc);
if (hwndMessage)
{
hwndText = GetDlgItem(hwndMessage, IDC_TEXT);
hwndProgress = GetDlgItem(hwndMessage, IDC_PROGRESS);
SetTimer(hwndMessage, 1, 20, NULL);
}
}
}
}
void httpShutdown()
{
WSACleanup();
}
int isOggUrl(const char *url)
{
if (strncmp(url, OGG_PROTOCOL, strlen(OGG_PROTOCOL)) == 0)
{
BufferThreadData * pThreadData = 0;
if (strcmp(url, lastUrlChecked) == 0)
return lastUrlOgg;
strcpy(lastUrlChecked, url);
if (stricmp(url + strlen(url) - 4, ".ogg") == 0)
return lastUrlOgg = 1;
pThreadData = createBufferThreadData();
if (!httpConnect(url, pThreadData, 0, FALSE))
{
free(pThreadData);
return lastUrlOgg = 0;
}
closesocket(pThreadData->tcpSocket);
if (strcmp(getHttpResponseHeader(pThreadData, "Content-Type"), OGG_MIME_TYPE) == 0)
return lastUrlOgg = 1;
free(pThreadData);
}
return lastUrlOgg = 0;
}
ize_t httpRead(void *ptr, size_t size, size_t nmemb, void *datasource)
{
int bytesRequested = size * nmemb;
int bytesRead = 0;
int bytesToCopy;
BufferThreadData * pThreadData = (BufferThreadData *) datasource;
while ((bytesRead < bytesRequested) && (!pThreadData->killBufferThread || (pThreadData->circularBufferStart != pThreadData->circularBufferEnd)))
{
if (pThreadData->circularBufferStart != pThreadData->circularBufferEnd)
{
if (pThreadData->circularBufferStart < pThreadData->circularBufferEnd)
bytesToCopy = pThreadData->circularBufferEnd - pThreadData->circularBufferStart;
else
bytesToCopy = CIRCULAR_BUFFER_SIZE - pThreadData->circularBufferStart;
if (bytesRead + bytesToCopy > bytesRequested)
bytesToCopy = bytesRequested - bytesRead;
memcpy((char *) ptr + bytesRead, pThreadData->circularBuffer + pThreadData->circularBufferStart, bytesToCopy);
pThreadData->circularBufferStart = (pThreadData->circularBufferStart + bytesToCopy) % CIRCULAR_BUFFER_SIZE;
bytesRead += bytesToCopy;
}
}
return bytesRead / size;
}
int httpSeek(void *datasource, int64_t offset, int whence)
{
return -1; // No HTTP seeking allowed yet. Maybe we can add support for the HTTP "Range" header later.
}
int httpClose(void *datasource)
{
BufferThreadData * pThreadData = (BufferThreadData *) datasource;
int retVal = closesocket(pThreadData->tcpSocket) ? 0 : EOF; // same as CloseHandle below
free(pThreadData);
return retVal;
}
long httpTell(void *datasource)
{
BufferThreadData * pThreadData = (BufferThreadData *) datasource;
return pThreadData->bytesRead;
}
void * httpStartBuffering(const char *url, OggVorbis_File *oggVorbisFile, BOOL showMessages)
{
vorbis_info *vi = NULL;
ov_callbacks callbacks = {httpRead, httpSeek, httpClose, httpTell};
BufferThreadData * pThreadData = createBufferThreadData();
if (!httpConnect(url, pThreadData, 1, showMessages))
return 0;
pThreadData->bufferThreadHandle = (HANDLE)_beginthread(BufferThread, 0, (void *)(pThreadData));
if (ov_open_callbacks(pThreadData, oggVorbisFile, NULL, 0, callbacks) < 0)
{
showError("ov_open_callbacks failed in httpstream. :-(");
pThreadData->killBufferThread = 1;
return 0;
}
return (void *) pThreadData;
}
void httpStopBuffering(void * vp)
{
BufferThreadData * pThreadData = (BufferThreadData *) vp;
if (pThreadData->bufferThreadHandle != INVALID_HANDLE_VALUE)
{
pThreadData->killBufferThread = 1;
if (WaitForSingleObject(pThreadData->bufferThreadHandle, INFINITE) == WAIT_TIMEOUT)
{
showError("Error asking thread to die!");
TerminateThread(pThreadData->bufferThreadHandle, 0);
}
pThreadData->bufferThreadHandle = INVALID_HANDLE_VALUE;
}
pThreadData->circularBufferStart = 0;
pThreadData->circularBufferEnd = 0;
hideMessage();
}
char *httpGetTitle(const char *url)
{
if (strncmp(url, OGG_PROTOCOL, strlen(OGG_PROTOCOL)) == 0)
{
char * period;
char * title = strrchr(url, '/');
if (title)
{
title = strdup(title + 1);
period = strrchr(title, '.');
if (period && (period - title == (int) strlen(title) - 4) && (stricmp(period, ".ogg") == 0)) // remove the ".ogg" extension if there is one
*period = 0;
return title;
}
}
return 0;
}
void BufferThread(void *vp)
{
BufferThreadData * pThreadData = (BufferThreadData *) vp;
int bytesRead;
int bytesToRead;
DWORD lastUpdate = 0;
showPercentage(0);
ShowWindowAsync(hwndText, SW_HIDE);
ShowWindowAsync(hwndProgress, SW_SHOW);
while (!pThreadData->killBufferThread)
{
// read into the buffer if it isn't full
if (((pThreadData->circularBufferEnd + 1) % CIRCULAR_BUFFER_SIZE) != pThreadData->circularBufferStart)
{
if (pThreadData->circularBufferStart > pThreadData->circularBufferEnd)
bytesToRead = pThreadData->circularBufferStart - pThreadData->circularBufferEnd - 1;
else
bytesToRead = CIRCULAR_BUFFER_SIZE - pThreadData->circularBufferEnd;
bytesRead = recv(pThreadData->tcpSocket, pThreadData->circularBuffer + pThreadData->circularBufferEnd, 1, 0);
if ((bytesRead == SOCKET_ERROR) || (bytesRead == 0))
break;
pThreadData->bytesRead += bytesRead;
pThreadData->circularBufferEnd = (pThreadData->circularBufferEnd + bytesRead) % CIRCULAR_BUFFER_SIZE;
}
else
Sleep(10);
if (GetTickCount() - lastUpdate > 100)
{
int start = pThreadData->circularBufferStart;
int end = pThreadData->circularBufferEnd;
if (end < start)
end += CIRCULAR_BUFFER_SIZE;
showPercentage((end - start) * 100 / CIRCULAR_BUFFER_SIZE);
lastUpdate = GetTickCount();
}
}
pThreadData->killBufferThread = 1;
_endthread();
}
1.1 vorbis-plugins/winamp/httpstream.h
Index: httpstream.h
===================================================================
/*********************************************
*
* httpstream.h
*
* HTTP streaming support for OggVorbis by Aaron Porter <aaron at javasource.org>
* Licensed under terms of the LGPL
*
*********************************************/
#ifndef _OGG_VORBIS_HTTP_STREAMING_
#define _OGG_VORBIS_HTTP_STREAMING_
void httpInit();
void httpSetHwnd(HWND hWnd);
void httpSetProxy(const char *proxy);
void httpShutdown();
int isOggUrl(const char *url);
char * httpGetTitle(const char *url);
void * httpStartBuffering(const char *url, OggVorbis_File * input_file, BOOL showMessages);
void httpStopBuffering();
#endif
1.1 vorbis-plugins/winamp/resource.h
Index: resource.h
===================================================================
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by resource.rc
//
#define IDC_TEXT 0
#define IDD_MESSAGE 101
#define IDC_PROGRESS 1000
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
1.1 vorbis-plugins/winamp/resource.rc
Index: resource.rc
===================================================================
//Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_MESSAGE DIALOG DISCARDABLE 0, 0, 179, 12
STYLE DS_MODALFRAME | WS_POPUP
FONT 8, "MS Sans Serif"
BEGIN
CONTROL "Progress1",IDC_PROGRESS,"msctls_progress32",NOT
WS_VISIBLE,2,2,175,8
LTEXT "Place Message Here!",IDC_TEXT,2,2,175,8
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_MESSAGE, DIALOG
BEGIN
LEFTMARGIN, 2
RIGHTMARGIN, 177
TOPMARGIN, 2
BOTTOMMARGIN, 10
END
END
#endif // APSTUDIO_INVOKED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
--- >8 ----
List archives: http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body. No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.
More information about the commits
mailing list