[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--