[Alsaplayer-devel]http support...
Karl-Anders Wallin
kaw@linux.se
30 Dec 2001 03:33:09 +0100
--=-ucJdSCt7Ye+bsrb8QO1p
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Hi!
As you might now the bbc are testing streaming of ogg. Well=20
anyway I like to listen to radio when I'm using my computer and=20
realplayer is no favorite of mine. I use alsaplayer to play my
ogg audio files so why not use this excellent application...!
To accomplish this, I implemented som basic http functionality=20
that enables alsaplayer to play a ogg stream via http.
The modified version of vorbis_engine.cpp connects to a http=20
server if the requested path starts with http://=20
Some minor changes to Playlist.cpp enables a http:// path
to be forwarded to the input plugin.=20
Ohh I'm to tired to continue writing in english...=20
I will continue this message when the short-circuit in brain has=20
been resolved. :)
To test try the following..
alsaplayer http://ogg.bbc.co.uk:8001/radio4.ogg
alsaplayer http://ogg.bbc.co.uk:8001/radio1-broad.ogg
if the server is down, check http://support.bbc.co.uk/ogg/
Attached files:
New files:
http.c =3D> apps/http.c=20
http.h =3D> include/http.h
Modified files in apps/=20
Makefile.am.patch - adds http.c to alsaplayer sources
Playlist.cpp.patch - enables a http URI (http://)
Modified files in input/vorbis/=20
vorbis_engine.cpp.patch=20
God Jul & Gott Nytt =C5r
/Kalle (Karl-Anders Wallin)=20
--=-ucJdSCt7Ye+bsrb8QO1p
Content-Disposition: attachment; filename=http.c
Content-Transfer-Encoding: quoted-printable
Content-Type: text/x-c; charset=ISO-8859-1
/*
* http.c=20
*
* Written by Karl-Anders Wallin <kaw@linux.se>
* Copyright (C) 2001
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U=
SA.
*
*
* $Id: http.c,v 1.0 2001/11/27 02:32:33 adnans Exp $
*
* HISTORY:
*
* $Log: http.c,v $
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>=20
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "config.h"
#include "http.h"
#define DEFAULT_HTTP_PORT 80
#define HTTP_HEADER_SIZE 1024
#define HTTP_HDR_MAX_LINE_LENGTH 512
#define HTTP_USER_AGENT PACKAGE " " VERSION
#define HTTP_TIMEOUT_SEC (60*3)
#ifdef DEBUG
#define D(x) x
#else
#define D(x)
#endif
typedef struct
{
int code;
char *str;
} http_status_t;
static http_status_t status_message[] =3D
{
{ 100, "Continue" },
{ 101, "Switching Protocols" },
{ 200, "OK" },
{ 201, "Created" },
{ 202, "Accepted" },
{ 203, "Non-Authoritative Information" },
{ 204, "No Content" },
{ 205, "Reset Content" },
{ 206, "Partial Content" },
{ 300, "Multiple Choices" },
{ 301, "Moved Permanently" },
{ 302, "Moved Temporarily" },
{ 303, "See Other" },
{ 304, "Not Modified" },
{ 305, "Use Proxy" },
{ 400, "Bad Request" },
{ 401, "Unauthorized" },
{ 402, "Payment Required" },
{ 403, "Forbidden" },
{ 404, "Not Found" },
{ 405, "Method Not Allowed" },
{ 406, "Not Acceptable" },
{ 407, "Proxy Authentication Required" },
{ 408, "Request Time-out" },
{ 409, "Conflict" },
{ 410, "Gone" },
{ 411, "Length Required" },
{ 412, "Precondition Failed" },
{ 413, "Request Entity Too Large" },
{ 414, "Request-URI Too Large" },
{ 415, "Unsupported Media Type" },
{ 500, "Internal Server Error" },
{ 501, "Not Implemented" },
{ 502, "Bad Gateway" },
{ 503, "Service Unavailable" },
{ 504, "Gateway Time-out" },
{ 505, "HTTP Version not supported" },
{ 0, NULL }
};
/*
* parse 'path' and store the result in url'.=20
*
* syntax : "http:" "//" host [ ":" port ]
*/
static int
http_parse_url( char *path, http_url_t *url )
{
char c;
char *p;
char *start;
char *path_end;
memset( url, 0, sizeof(http_url_t) );
url->port =3D DEFAULT_HTTP_PORT;
=20
if( path=3D=3DNULL || !IS_HTTP_URL(path) )
return -1;
p =3D path + HTTP_URL_PREFIX_LENGTH;
start =3D p;
path_end =3D path + strlen(path);
=20
/* retreive host name */
while( p<path_end && *p!=3D':' && *p!=3D'/' )
p++;
if( p>=3Dpath_end )
{
D( printf( "Bad url - file part missing!\n" ) );
return -1;
}
c =3D *p;
*p =3D '\0';
url->host =3D strdup( start );
=20
p++;
start=3Dp;
if( c=3D=3D':' )
{
/* retreive port */
while( p<path_end && *p!=3D'/' )
p++;
if( p>=3Dpath_end )
{
D( printf( "Bad url - unable to parse port number!\n" ) );
return -1;
}
*p =3D'\0';
url->port =3D atoi( start );
p++;
start =3D p;
}
/* retreive filename */
if( p>=3Dpath_end )
{
D( printf( "Bad url - unable to parse filename!\n" ) );
return -1;
}
=20
url->file =3D malloc( strlen(start) + 1 );
strcpy( url->file, "/" );
strcat( url->file, start );
D( printf( "url->host =3D %s\n", url->host ) );
D( printf( "url->port =3D %d\n", url->port ) );
D( printf( "url->file =3D %s\n", url->file ) );
return 0;
}
/* connect to the server and return a file descriptor */
static int
http_connect( http_info_t *http )
{
int fd;
int port;
char *host;
struct sockaddr_in sa;
struct hostent *he;
if( http->proxy_host !=3D NULL )
{
/* proxy mode */
host =3D http->proxy_host;
port =3D http->proxy_port;
}
else
{
/* no proxy */
host =3D http->url.host;
port =3D http->url.port;
}
printf( "Connecting to %s on port %d\n", host, port );
if( (he=3Dgethostbyname(host)) =3D=3D NULL )=20
{ =20
herror("gethostbyname");
return -1;
}
if( (fd =3D socket(AF_INET, SOCK_STREAM, 0)) <0 )
{
perror("socket");
return -1;
}
=20
sa.sin_family =3D AF_INET; =20
sa.sin_port =3D htons(port);=20
sa.sin_addr =3D *((struct in_addr *) he->h_addr);
memset( &(sa.sin_zero), 0, 8 );
=20
if (connect( fd,=20
(struct sockaddr *) &sa,=20
sizeof(struct sockaddr)) =3D=3D -1)=20
{
perror("connect");
return -1;
}=20
=20
http->fd =3D fd;
=20
return http->fd;
}
/* write buf to socket (fd) in a safe way */
static int
http_write( int fd, char *buf, int len )
{
int nbytes;
nbytes =3D 0;
while( nbytes<len )
{
fd_set wset;
struct timeval timeout;
FD_ZERO( &wset );
FD_SET( fd, &wset );
timeout.tv_sec =3D HTTP_TIMEOUT_SEC;
timeout.tv_usec =3D 0;
if( select(FD_SETSIZE, NULL, &wset, NULL, &timeout) < 0 )
{
perror ("select");
return -1;
}
if( FD_ISSET( fd, &wset ) )
{
int n;
=20
if( (n=3Dwrite( fd, buf+nbytes, len-nbytes )) <0 )
{
perror( "write" );
return -1;
}
nbytes +=3D n;
}
else
{
fprintf( stderr, "timeout =3D> aborting!\n" );
return -1;
}
}
return 0;
}
/* create and send a http request */
static int
http_request( int fd, http_info_t *http )
{
char buf[HTTP_HEADER_SIZE];
/* Request-Line */
memset( buf, 0, HTTP_HEADER_SIZE );
if( http->proxy_host !=3D NULL )
{
/* proxy */
sprintf( buf,=20
"GET http://%s:%s/%s HTTP/1.0\r\n",=20
http->url.host,
http->url.port,
http->url.file );
}
else
{
/* no proxy */
sprintf( buf, "GET %s HTTP/1.0\r\n", http->url.file );
}
/* Request Header Fields */
strcat( buf, "Accept: */*\r\n" );
strcat( buf, "User-Agent: " HTTP_USER_AGENT "\r\n");
strcat( buf, "Host: " );
strcat( buf, http->url.host );
strcat( buf, "\r\n\r\n" );
/* CRLF */
strcat( buf, "\r\n" );
/* Send headers to server */
D( printf( "Sending http request...\n\n%s", buf ) ); =20
=20
return http_write( fd, buf, strlen(buf) );
}
/* parse the status-line received */
static int
http_parse_status_line( char *line )
{
int value;
if (strncmp (line, "HTTP/", 5) !=3D 0)
return -1;
/* ignore http version */
line +=3D 9;
if (!(isdigit(line[0]) && isdigit(line[1]) && isdigit(line[2])))
return -1;
=20
value =3D 100 * (line[0] - '0') + 10 * (line[1] - '0') + (line[2] - '0');=
=20
return value;
}
/* parse a line containing a http response header */
static void
http_parse_header_field( char *line, http_header_t *hdr )
{
char *colon =3D memchr( line, ':', strlen(line) );
char *eol =3D line + strlen(line);
char *p, c;
if( colon=3D=3DNULL || line=3D=3Deol || colon>eol )
{
return;
}
=20
hdr->field_size++;
hdr->field_name =3D realloc( hdr->field_name,
sizeof(char *)*hdr->field_size);=20
hdr->field_value =3D realloc( hdr->field_value,
sizeof(char *)*hdr->field_size);=20
=20
// name
while( isspace(*line) )
line++;
p=3Dcolon;
while( isblank(*(p-1)) )
p--;
*p =3D 0;
hdr->field_name[hdr->field_size-1] =3D strdup( line );
// value
colon++;
while( isblank(*colon) )
colon++;
p=3Deol;
while( isblank(*(p-1)) || *(p-1)=3D=3D'\r' || *(p-1)=3D=3D'\n' )
p--;
*p =3D 0;
hdr->field_value[hdr->field_size-1] =3D strdup( colon );
}
/* parse http response headers */
static int
http_parse_response( int fd, http_info_t *http )
{
int i, c;
int CRLF_count;
char line[HTTP_HDR_MAX_LINE_LENGTH];
/* zero header struct */
memset( &http->header, 0, sizeof(http_header_t) );
/* zero line buffer */ =20
memset( line, 0, sizeof(line) );
i =3D 0;
CRLF_count =3D 0;
while( CRLF_count < 4 )
{
fd_set rset;
struct timeval timeout;
FD_ZERO( &rset );
FD_SET( fd, &rset );
timeout.tv_sec =3D HTTP_TIMEOUT_SEC;
timeout.tv_usec =3D 0;
/* wait for data */
if( select(FD_SETSIZE, &rset, NULL, NULL, &timeout) < 0 )
{
perror ("select");
return -1;
}
if( FD_ISSET( fd, &rset ) )
{
if( read( fd, &c, 1 ) !=3D 1 )
{
perror( "read" );
return -1;
}
line[i++] =3D c;
D( printf( "%c", c ) );
=20
if( c=3D=3D'\r' || c=3D=3D'\n' )
{
CRLF_count++;
if( CRLF_count =3D=3D 2 )
{
/* a http header line received */
if( !http->header.status_code )
{
http->header.status_code =3D http_parse_status_line(line);
}
else
{
http_parse_header_field( line, &http->header );
}
/* zero line buffer */ =20
memset( line, 0, sizeof(line) );
i=3D0; =20
}
}
else
CRLF_count=3D0; =20
}
else
{
fprintf( stderr, "timeout =3D> aborting!\n" );
return -1;
} =20
}
return 0;
}
http_info_t *
http_init( char *proxy_host, int proxy_port )
{
http_info_t *http;
if( (http=3Dmalloc( sizeof(http_info_t) )) =3D=3D NULL )
{
perror( "malloc" );
return NULL;
}
memset( http, 0, sizeof(http_info_t) );
=20
http->proxy_host =3D proxy_host;
http->proxy_port =3D proxy_port;
return http;
}
FILE *
http_open( char *path, http_info_t *http )
{
if( http=3D=3DNULL )
return;
memset( http, 0, sizeof(http_info_t) );
=20
/* parse out host,port and file from our path */
if( http_parse_url( path, &http->url ) <0 )
{
return NULL;
}
/* connect to server */
if( (http->fd=3Dhttp_connect(http)) <0 )
{
return NULL;
}
/* send http request */
if( http_request( http->fd, http ) )
{
close( http->fd );
return NULL;
}
/* read http response */
if( http_parse_response( http->fd, http ) )
{
close( http->fd );
return NULL;
}
http_dump_headers( http );
return fdopen( http->fd, "r+b");
}
void
http_close( http_info_t *http )
{
int i;
if( http=3D=3DNULL )
return;
/* close fd */
if( http->fd >0 )
close( http->fd );
/* free url part */
if( http->url.host )
{
free( http->url.host );
free( http->url.file );
}
/* free http headers */
for( i=3D0; i<http->header.field_size; i++ )
{
free( http->header.field_name[i] );
free( http->header.field_value[i] );
}
free( http->header.field_name );
free( http->header.field_value );
=20
free( http );
}
void=20
http_dump_headers( http_info_t *http )
{
int i;
http_header_t hdr =3D http->header;
=20
fprintf( stderr, "%20s: %d %s\n", "Status-code",=20
hdr.status_code,
http_status_code2string(hdr.status_code) );
for( i=3D0; i<hdr.field_size; i++ )
fprintf( stderr,=20
"%20s: %s\n",=20
hdr.field_name[i],=20
hdr.field_value[i] ); =20
}
char *
http_get_header_field( char *field_name, http_info_t *http )
{
int i;
http_header_t hdr =3D http->header;
for( i=3D0; i<hdr.field_size; i++ )
{
if( strcasecmp( hdr.field_name[i], field_name ) =3D=3D 0 )
{
return hdr.field_value[i];
}
}
return NULL;
}
char *
http_status_code2string( int code )
{
int i =3D 0;
char *msg =3D NULL;
while( status_message[i].str !=3D NULL )
{
if( status_message[i].code =3D=3D code )
return status_message[i].str;
i++;
}
return "Unknown Status-code";
}
--=-ucJdSCt7Ye+bsrb8QO1p
Content-Disposition: attachment; filename=http.h
Content-Transfer-Encoding: quoted-printable
Content-Type: text/x-c; charset=ISO-8859-1
#ifndef __vorbis_http_h__
#define __vorbis_http_h__
#define HTTP_URL_PREFIX "http://"
#define HTTP_URL_PREFIX_LENGTH 7
#define IS_HTTP_URL(path) ( strncmp( path, HTTP_URL_PREFIX, HTTP_URL_PREFIX=
_LENGTH ) =3D=3D 0 )
#ifdef __cplusplus
extern "C" {
#endif
typedef struct=20
{
int status_code;
char **field_name;
char **field_value;
int field_size;
} http_header_t;
typedef struct
{
char *host;
int port;
char *file;
} http_url_t;
typedef struct
{
http_url_t url;
http_header_t header;
char *proxy_host;
int proxy_port;
int fd;
} http_info_t;
=20
http_info_t *http_init( char *proxy_host, int proxy_port );
FILE *http_open( char *path, http_info_t *http );
void http_close( http_info_t *http );
char *http_status_code2string( int status_code );
char *http_get_header_field( char *field_name, http_info_t *http );
void http_dump_headers( http_info_t *http );
#ifdef __cplusplus
}
#endif
#endif=20
--=-ucJdSCt7Ye+bsrb8QO1p
Content-Disposition: attachment; filename=Makefile.am.patch
Content-Transfer-Encoding: quoted-printable
Content-Type: text/x-makefile; charset=ISO-8859-1
Index: Makefile.am
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/alsaplayer/alsaplayer/app/Makefile.am,v
retrieving revision 1.4
diff -r1.4 Makefile.am
45c45,46
< fft.c convolve.c utilities.cpp=20
---
> fft.c convolve.c http.c \
> utilities.cpp=20
--=-ucJdSCt7Ye+bsrb8QO1p
Content-Disposition: attachment; filename=Playlist.cpp.patch
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-1
Index: Playlist.cpp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/alsaplayer/alsaplayer/app/Playlist.cpp,v
retrieving revision 1.13
diff -r1.13 Playlist.cpp
36a37
> #include "http.h"
621a623,629
> // check if path is a http url=20
> if( IS_HTTP_URL(path.c_str()) ) {
> input_plugin *plugin =3D coreplayer->GetPlayer(path.c_str());
> if (plugin)=20
> return true;=20
> }
>=20
650a659,665
>=20
> // check if path is a http url
> if( IS_HTTP_URL(path.c_str()) ) {
> printf( "additems() - a http url found!\n" );
> items->push_back(path);
> return;
> }
--=-ucJdSCt7Ye+bsrb8QO1p
Content-Disposition: attachment; filename=vorbis_engine.cpp.patch
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain; charset=ISO-8859-1
Index: vorbis_engine.cpp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
RCS file: /cvsroot/alsaplayer/alsaplayer/input/vorbis/vorbis_engine.cpp,v
retrieving revision 1.2
diff -r1.2 vorbis_engine.cpp
30a31
> #include "http.h"
37a39
> http_info_t *http;
137c139,144
< case -1:
---
> case OV_HOLE:
> /* hole in the stream, probably harmless */
> ret =3D 0;
> break;
> case OV_EBADLINK:
> /* invalid stream section */
242a250,261
>=20
> /* work to TODO here - view this as an example */ =20
> if( data && data->http )
> {
> char *htinfo;
> =20
> if( (htinfo=3Dhttp_get_header_field("ice-name",data->http)) )
> {
> strcpy( info->title, htinfo );
> }
> }
>=20
326a346
> http_info_t *http =3D NULL;
333,335c353,377
< if ((stream =3D fopen(path, "r")) =3D=3D NULL) {
< return 0;
< }
---
> if( IS_HTTP_URL(path) )
> {
> http =3D http_init( NULL, 0 );
> /* connect to server */
> if ((stream =3D http_open(path,http)) =3D=3D NULL)
> return 0;
> /* check status-code */
> if( http->header.status_code !=3D 200 )
> {
> fprintf( stderr,=20
> "Error: %d %s\n",=20
> http->header.status_code,
> http_status_code2string( http->header.status_code ) );
> fclose( stream );
> http_close( http );
> return 0;
> }
> /* TODO: check content-type (application/x-ogg) */
> }
> else=09
> {
> if ((stream =3D fopen(path, "r")) =3D=3D NULL)
> return 0;
> }
>=20
339a382,383
> if( http )
> http_close( http );
344a389,390
> if( http )
> http_close( http );
349a396,397
> if( http )
> http_close( http );
356a405,406
> if( http )
> http_close( http );
360a411
> data->http =3D http;
362a414
>=20
374,375c426,430
< if (data)
< ov_clear(&data->vf);
---
> if (data) {
> ov_clear(&data->vf);
> if( data->http )
> http_close( data->http );
> }
377,378c432,433
< free(obj->local_data);
< obj->local_data =3D NULL;
---
> free(obj->local_data);
> obj->local_data =3D NULL;
--=-ucJdSCt7Ye+bsrb8QO1p--