[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