diff options
-rw-r--r-- | src/input/Makefile.am | 9 | ||||
-rw-r--r-- | src/input/input_pnm.c | 282 | ||||
-rw-r--r-- | src/input/libdvdread/ifo_read.c | 13 | ||||
-rw-r--r-- | src/input/pnm.c | 938 | ||||
-rw-r--r-- | src/input/pnm.h | 42 |
5 files changed, 1282 insertions, 2 deletions
diff --git a/src/input/Makefile.am b/src/input/Makefile.am index a8974e46a..a5d301066 100644 --- a/src/input/Makefile.am +++ b/src/input/Makefile.am @@ -47,7 +47,8 @@ lib_LTLIBRARIES = \ $(in_dvd) \ $(in_vcd) \ xineplug_inp_mms.la \ - xineplug_inp_stdin_fifo.la + xineplug_inp_stdin_fifo.la \ + xineplug_inp_pnm.la #lib_LTLIBRARIES = \ # $(in_cda) \ @@ -97,8 +98,12 @@ xineplug_inp_http_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ #xineplug_inp_cda_la_LIBADD = $(XINE_LIB) #xineplug_inp_cda_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ +xineplug_inp_pnm_la_SOURCES = input_pnm.c net_buf_ctrl.c pnm.c +xineplug_inp_pnm_la_LIBADD = $(XINE_LIB) +xineplug_inp_pnm_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ + include_HEADERS = input_plugin.h -noinst_HEADERS = net_buf_ctrl.h mms.h +noinst_HEADERS = net_buf_ctrl.h mms.h pnm.h EXTRA_DIST = input_dvd.c input_vcd.c diff --git a/src/input/input_pnm.c b/src/input/input_pnm.c new file mode 100644 index 000000000..e30d0e721 --- /dev/null +++ b/src/input/input_pnm.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2002 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + * + * pnm input plugin by joschka + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <stdio.h> +#include <assert.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "bswap.h" + +#include "xine_internal.h" +#include "xineutils.h" +#include "input_plugin.h" + +#include "pnm.h" +#include "net_buf_ctrl.h" + + +#define LOG + + +extern int errno; + +#if !defined(NDELAY) && defined(O_NDELAY) +#define FNDELAY O_NDELAY +#endif + +typedef struct { + + input_class_t input_class; + + xine_t *xine; +} pnm_input_class_t; + +typedef struct { + input_plugin_t input_plugin; + + pnm_t *pnm; + + char *mrl; + + off_t curpos; + + nbc_t *nbc; + + char scratch[1025]; + +} pnm_input_plugin_t; + + +static off_t pnm_plugin_read (input_plugin_t *this_gen, + char *buf, off_t len) { + pnm_input_plugin_t *this = (pnm_input_plugin_t *) this_gen; + off_t n; + +#ifdef LOG + printf ("pnm_plugin_read: %lld bytes ...\n", + len); +#endif + + nbc_check_buffers (this->nbc); + + n = pnm_read (this->pnm, buf, len); + this->curpos += n; + + return n; +} + +static buf_element_t *pnm_plugin_read_block (input_plugin_t *this_gen, + fifo_buffer_t *fifo, off_t todo) { + /*pnm_input_plugin_t *this = (pnm_input_plugin_t *) this_gen; */ + buf_element_t *buf = fifo->buffer_pool_alloc (fifo); + int total_bytes; + +#ifdef LOG + printf ("pnm_plugin_read_block: %lld bytes...\n", + todo); +#endif + + buf->content = buf->mem; + buf->type = BUF_DEMUX_BLOCK; + + total_bytes = pnm_plugin_read (this_gen, buf->content, todo); + + if (total_bytes != todo) { + buf->free_buffer (buf); + return NULL; + } + + buf->size = total_bytes; + + return buf; +} + +static off_t pnm_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) { + /* + pnm_input_plugin_t *this = (pnm_input_plugin_t *) this_gen; + */ + + return -1; +} + +static off_t pnm_plugin_get_length (input_plugin_t *this_gen) { + + /* + pnm_input_plugin_t *this = (pnm_input_plugin_t *) this_gen; + off_t length; + */ + + return -1; +} + +static uint32_t pnm_plugin_get_capabilities (input_plugin_t *this_gen) { + return INPUT_CAP_NOCAP; +} + +static uint32_t pnm_plugin_get_blocksize (input_plugin_t *this_gen) { + return 0; +} + +static off_t pnm_plugin_get_current_pos (input_plugin_t *this_gen){ + pnm_input_plugin_t *this = (pnm_input_plugin_t *) this_gen; + + /* + printf ("current pos is %lld\n", this->curpos); + */ + + return this->curpos; +} + +static void pnm_plugin_dispose (input_plugin_t *this_gen) { + pnm_input_plugin_t *this = (pnm_input_plugin_t *) this_gen; + + if (this->pnm) { + pnm_close (this->pnm); + this->pnm = NULL; + } + + if (this->nbc) { + nbc_close (this->nbc); + this->nbc = NULL; + } + + if(this->mrl) + free(this->mrl); + + free (this); +} + +static char* pnm_plugin_get_mrl (input_plugin_t *this_gen) { + pnm_input_plugin_t *this = (pnm_input_plugin_t *) this_gen; + + return this->mrl; +} + +static int pnm_plugin_get_optional_data (input_plugin_t *this_gen, + void *data, int data_type) { + /* pnm_input_plugin_t *this = (pnm_input_plugin_t *) this_gen; */ + + return INPUT_OPTIONAL_UNSUPPORTED; +} + +static input_plugin_t *open_plugin (input_class_t *cls_gen, xine_stream_t *stream, + const char *data) { + + /* pnm_input_class_t *cls = (pnm_input_class_t *) cls_gen; */ + pnm_input_plugin_t *this; + pnm_t *pnm; + char *mrl = strdup(data); + +#ifdef LOG + printf ("input_pnm: trying to open '%s'\n", mrl); +#endif + + if (strncasecmp (mrl, "pnm://", 6)) { + free (mrl); + return NULL; + } + + pnm = pnm_connect (mrl); + + if (!pnm) { + free (mrl); + return NULL; + } + + this = (pnm_input_plugin_t *) malloc (sizeof (pnm_input_plugin_t)); + + this->pnm = pnm; + this->mrl = mrl; + this->nbc = nbc_init (stream); + + this->input_plugin.get_capabilities = pnm_plugin_get_capabilities; + this->input_plugin.read = pnm_plugin_read; + this->input_plugin.read_block = pnm_plugin_read_block; + this->input_plugin.seek = pnm_plugin_seek; + this->input_plugin.get_current_pos = pnm_plugin_get_current_pos; + this->input_plugin.get_length = pnm_plugin_get_length; + this->input_plugin.get_blocksize = pnm_plugin_get_blocksize; + this->input_plugin.get_mrl = pnm_plugin_get_mrl; + this->input_plugin.dispose = pnm_plugin_dispose; + this->input_plugin.get_optional_data = pnm_plugin_get_optional_data; + this->input_plugin.input_class = cls_gen; + + return &this->input_plugin; +} + +/* + * pnm input plugin class stuff + */ + +static char *pnm_class_get_description (input_class_t *this_gen) { + return _("pnm streaming input plugin"); +} + +static char *pnm_class_get_identifier (input_class_t *this_gen) { + return "pnm"; +} + +static void pnm_class_dispose (input_class_t *this_gen) { + pnm_input_class_t *this = (pnm_input_class_t *) this_gen; + + free (this); +} + +static void *init_class (xine_t *xine, void *data) { + + pnm_input_class_t *this; + + this = (pnm_input_class_t *) xine_xmalloc (sizeof (pnm_input_class_t)); + + this->xine = xine; + + this->input_class.open_plugin = open_plugin; + this->input_class.get_identifier = pnm_class_get_identifier; + this->input_class.get_description = pnm_class_get_description; + this->input_class.get_dir = NULL; + this->input_class.get_autoplay_list = NULL; + this->input_class.dispose = pnm_class_dispose; + this->input_class.eject_media = NULL; + + return this; +} + +/* + * exported plugin catalog entry + */ + +plugin_info_t xine_plugin_info[] = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_INPUT, 10, "pnm", XINE_VERSION_CODE, NULL, init_class }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; + diff --git a/src/input/libdvdread/ifo_read.c b/src/input/libdvdread/ifo_read.c index 9654ae7eb..c8379fb39 100644 --- a/src/input/libdvdread/ifo_read.c +++ b/src/input/libdvdread/ifo_read.c @@ -1655,6 +1655,17 @@ static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, return 1; } +void hexdump (uint8_t *buf, int size) { + int i; + + for (i=0; i<size; i++) { + printf ("%02x ", buf[i]); + if ( (i%8)==7) + printf ("\n"); + } + printf ("\n"); + +} int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) { @@ -1687,6 +1698,8 @@ int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) { return 0; } + hexdump (vts_atrt, VTS_ATRT_SIZE); + B2N_16(vts_atrt->nr_of_vtss); B2N_32(vts_atrt->last_byte); diff --git a/src/input/pnm.c b/src/input/pnm.c new file mode 100644 index 000000000..8341e23e6 --- /dev/null +++ b/src/input/pnm.c @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2000-2002 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + * + * $Id: pnm.c,v 1.1 2002/11/23 00:04:32 guenter Exp $ + * + * pnm protocol implementation by joschka + */ + +#include <unistd.h> +#include <stdio.h> +#include <assert.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <time.h> + +#include "pnm.h" + +#define LOG + +#define BUF_SIZE 1024 +#define HEADER_SIZE 1024 + +struct pnm_s { + + int s; + + char *host; + int port; + char *path; + char *url; + + char buffer[BUF_SIZE]; /* scratch buffer */ + + /* receive buffer */ + uint8_t recv[BUF_SIZE]; + int recv_size; + int recv_read; + + uint8_t header[HEADER_SIZE]; + int header_len; + int header_read; + unsigned int seq_num[4]; /* two streams with two indices */ + unsigned int seq_current[2]; /* seqs of last stream chunk read */ + unsigned int packet; /* number of last recieved packet */ +}; + +/* + * utility macros + */ + +#define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \ + ( (long)(unsigned char)(ch3) | \ + ( (long)(unsigned char)(ch2) << 8 ) | \ + ( (long)(unsigned char)(ch1) << 16 ) | \ + ( (long)(unsigned char)(ch0) << 24 ) ) + +#define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1]) +#define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \ + (((uint8_t*)(x))[1] << 16) | \ + (((uint8_t*)(x))[2] << 8) | \ + ((uint8_t*)(x))[3]) + +/* D means direct (no pointer) */ +#define BE_16D(x) ((x & 0xff00) >> 8)|((x & 0x00ff) << 8) + +/* + * constants + */ + +#define RMF_TAG FOURCC_TAG('.', 'R', 'M', 'F') +#define PROP_TAG FOURCC_TAG('P', 'R', 'O', 'P') +#define MDPR_TAG FOURCC_TAG('M', 'D', 'P', 'R') +#define CONT_TAG FOURCC_TAG('C', 'O', 'N', 'T') +#define DATA_TAG FOURCC_TAG('D', 'A', 'T', 'A') +#define INDX_TAG FOURCC_TAG('I', 'N', 'D', 'X') +#define PNA_TAG FOURCC_TAG('P', 'N', 'A', 0 ) + +/* prop flags */ +#define PN_SAVE_ENABLED 0x01 +#define PN_PERFECT_PLAY_ENABLED 0x02 +#define PN_LIVE_BROADCAST 0x04 + + +/* sizes */ +#define PREAMBLE_SIZE 8 +#define CHECKSUM_SIZE 3 + + +/* header of rm files */ +#define RM_HEADER_SIZE 0x12 +const unsigned char rm_header[]={ + 0x2e, 0x52, 0x4d, 0x46, /* object_id ".RMF" */ + 0x00, 0x00, 0x00, 0x12, /* header_size 0x12 */ + 0x00, 0x00, /* object_version 0x00 */ + 0x00, 0x00, 0x00, 0x00, /* file_version 0x00 */ + 0x00, 0x00, 0x00, 0x06 /* num_headers 0x06 */ +}; + +/* data chunk header */ +#define PNM_DATA_HEADER_SIZE 18 +const unsigned char pnm_data_header[]={ + 'D','A','T','A', + 0,0,0,0, /* data chunk size */ + 0,0, /* object version */ + 0,0,0,0, /* num packets */ + 0,0,0,0}; /* next data header */ + +/* pnm request chunk ids */ + +#define PNA_CLIENT_CAPS 0x03 +#define PNA_CLIENT_CHALLANGE 0x04 +#define PNA_BANDWIDTH 0x05 +#define PNA_GUID 0x13 +#define PNA_TIMESTAMP 0x17 +#define PNA_TWENTYFOUR 0x18 + +#define PNA_CLIENT_STRING 0x63 +#define PNA_PATH_REQUEST 0x52 + +const unsigned char pnm_challenge[] = "0990f6b4508b51e801bd6da011ad7b56"; +const unsigned char pnm_timestamp[] = "[15/06/1999:22:22:49 00:00]"; +const unsigned char pnm_guid[] = "3eac2411-83d5-11d2-f3ea-d7c3a51aa8b0"; +const unsigned char pnm_response[] = "97715a899cbe41cee00dd434851535bf"; +const unsigned char client_string[] = "WinNT_4.0_6.0.6.45_plus32_MP60_en-US_686l"; + +#define PNM_HEADER_SIZE 11 +const unsigned char pnm_header[] = { + 'P','N','A', + 0x00, 0x0a, + 0x00, 0x14, + 0x00, 0x02, + 0x00, 0x01 }; + +#define PNM_CLIENT_CAPS_SIZE 126 +const unsigned char pnm_client_caps[] = { + 0x07, 0x8a, 'p','n','r','v', + 0, 0x90, 'p','n','r','v', + 0, 0x64, 'd','n','e','t', + 0, 0x46, 'p','n','r','v', + 0, 0x32, 'd','n','e','t', + 0, 0x2b, 'p','n','r','v', + 0, 0x28, 'd','n','e','t', + 0, 0x24, 'p','n','r','v', + 0, 0x19, 'd','n','e','t', + 0, 0x18, 'p','n','r','v', + 0, 0x14, 's','i','p','r', + 0, 0x14, 'd','n','e','t', + 0, 0x24, '2','8','_','8', + 0, 0x12, 'p','n','r','v', + 0, 0x0f, 'd','n','e','t', + 0, 0x0a, 's','i','p','r', + 0, 0x0a, 'd','n','e','t', + 0, 0x08, 's','i','p','r', + 0, 0x06, 's','i','p','r', + 0, 0x12, 'l','p','c','J', + 0, 0x07, '0','5','_','6' }; + +const uint32_t pnm_default_bandwidth=10485800; +const uint32_t pnm_available_bandwidths[]={14400,19200,28800,33600,34430,57600, + 115200,262200,393216,524300,1544000,10485800}; + +/* some unknown chunks */ +#define PNM_AFTER_CLIENT_CAPS_SIZE 18 +const unsigned char pnm_after_client_caps[]={ + 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x16, 0x00, 0x02, 0x00, 0x01 }; + +#define PNM_AFTER_BANDWIDTH_SIZE 28 +const unsigned char pnm_after_bandwidth[]={ + 0x00, 0x08, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + 0x00, 0x15, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x00 }; + +#define PNM_TWENTYFOUR_SIZE 16 +unsigned char pnm_twentyfour[]={ + 0xd5, 0x42, 0xa3, 0x1b, 0xef, 0x1f, 0x70, 0x24, + 0x85, 0x29, 0xb3, 0x8d, 0xba, 0x11, 0xf3, 0xd6 }; + +/* now other data follows. marked with 0x0000 at the beginning */ +int after_chunks_length=6; +unsigned char after_chunks[]={ + 0x00, 0x00, /* mark */ + + 0x50, 0x84, /* seems to be fixated */ + 0x1f, 0x3a /* varies on each request (checksum ?)*/ + }; + +/* + * network utilities + */ + +static int host_connect_attempt(struct in_addr ia, int port) { + + int s; + struct sockaddr_in sin; + + s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) { + printf ("pnm: socket(): %s\n", strerror(errno)); + return -1; + } + + sin.sin_family = AF_INET; + sin.sin_addr = ia; + sin.sin_port = htons(port); + + if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 + && errno != EINPROGRESS) { + printf ("pnm: connect(): %s\n", strerror(errno)); + close(s); + return -1; + } + + return s; +} + +static int host_connect(const char *host, int port) { + + struct hostent *h; + int i, s; + + h = gethostbyname(host); + if (h == NULL) { + printf ("pnm: unable to resolve '%s'.\n", host); + return -1; + } + + for (i = 0; h->h_addr_list[i]; i++) { + struct in_addr ia; + + memcpy (&ia, h->h_addr_list[i], 4); + s = host_connect_attempt(ia, port); + if(s != -1) + return s; + } + printf ("pnm: unable to connect to '%s'.\n", host); + return -1; +} + +static int rm_write(int s, const char *buf, int len) { + int total, timeout; + + total = 0; timeout = 30; + while (total < len){ + int n; + + n = write (s, &buf[total], len - total); + + if (n > 0) + total += n; + else if (n < 0) { + if ((timeout>0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) { + sleep (1); timeout--; + } else + return -1; + } + } + + return total; +} + +static ssize_t rm_read(int fd, void *buf, size_t count) { + + ssize_t ret, total; + + total = 0; + + while (total < count) { + + fd_set rset; + struct timeval timeout; + + FD_ZERO (&rset); + FD_SET (fd, &rset); + + timeout.tv_sec = 30; + timeout.tv_usec = 0; + + if (select (fd+1, &rset, NULL, NULL, &timeout) <= 0) { + return -1; + } + + ret=read (fd, buf+total, count-total); + + if (ret<=0) { + printf ("pnm: read error.\n"); + return ret; + } else + total += ret; + } + + return total; +} + +/* + * debugging utilities + */ + +static void hexdump (char *buf, int length) { + + int i; + + printf ("pnm: ascii>"); + for (i = 0; i < length; i++) { + unsigned char c = buf[i]; + + if ((c >= 32) && (c <= 128)) + printf ("%c", c); + else + printf ("."); + } + printf ("\n"); + + printf ("pnm: hexdump> "); + for (i = 0; i < length; i++) { + unsigned char c = buf[i]; + + printf ("%02x", c); + + if ((i % 16) == 15) + printf ("\npnm: "); + + if ((i % 2) == 1) + printf (" "); + + } + printf ("\n"); +} + +/* + * basic stream reading + */ + +static char *pnm_get_string(unsigned char *data) { + + char *string; + int length; + + length=BE_16(data); + string=malloc(sizeof(char)*(length+1)); + memcpy(string,&data[2],length); + string[length]=0; + + return string; +} + +/* + * pnm_get_chunk gets a chunk from stream + * and returns number of bytes read + */ + +static unsigned int pnm_get_chunk(pnm_t *p, + unsigned int max, + unsigned int *chunk_type, + char *data) { + + unsigned int chunk_size; + int n; + char *ptr; + + /* get first PREAMBLE_SIZE bytes and ignore checksum */ + rm_read (p->s, data, CHECKSUM_SIZE); + if (data[0] == 0x72) + rm_read (p->s, data, PREAMBLE_SIZE); + else + rm_read (p->s, &data[CHECKSUM_SIZE], PREAMBLE_SIZE-CHECKSUM_SIZE); + + *chunk_type = BE_32(&data[0]); + chunk_size = BE_32(&data[4]); + + switch (*chunk_type) { + case PNA_TAG: + ptr=&data[PREAMBLE_SIZE]; + rm_read (p->s, ptr, 0x0b - PREAMBLE_SIZE); + ptr+=0x0b-PREAMBLE_SIZE; + if (data[PREAMBLE_SIZE+0x01] == 'X') /* checking for server message */ + { + printf("pnm: got a message from server:\n"); /* print message and exit */ + n=BE_16(&data[PREAMBLE_SIZE+0x02]); + rm_read (p->s, &data[PREAMBLE_SIZE+0x04], n); + data[PREAMBLE_SIZE+0x04+n]=0; + printf("%s\n",&data[PREAMBLE_SIZE+0x04]); + exit(0); + } + /* expecting following chunk format: 0x4f <chunk size> <data...> */ + rm_read (p->s, ptr, 2); + while (*ptr == 0x4f) { + ptr++; + n=(*ptr); + ptr++; + rm_read (p->s, ptr, n+2); + ptr+=n; + } + /* the checksum of the next chunk is ignored here */ + rm_read (p->s, ptr+1, 1); + ptr++; + chunk_size=ptr-data; + break; + case PROP_TAG: + case MDPR_TAG: + case CONT_TAG: + if (chunk_size > max) { + printf("error: max chunk size exeeded (max was 0x%04x)\n", max); + n=rm_read (p->s, &data[PREAMBLE_SIZE], 0x100 - PREAMBLE_SIZE); + hexdump(data,n+PREAMBLE_SIZE); + exit(0); + } + rm_read (p->s, &data[PREAMBLE_SIZE], chunk_size-PREAMBLE_SIZE); + break; + default: + *chunk_type = 0; + chunk_size = PREAMBLE_SIZE; + break; + } + return chunk_size; +} + +/* + * writes a chunk to a buffer, returns number of bytes written + */ + +static int pnm_write_chunk(uint16_t chunk_id, uint16_t length, + const char *chunk, char *data) { + + data[0]=(chunk_id>>8)%0xff; + data[1]=chunk_id%0xff; + data[2]=(length>>8)%0xff; + data[3]=length%0xff; + memcpy(&data[4],chunk,length); + + return length+4; +} + +/* + * constructs a request and sends it + */ + +static void pnm_send_request(pnm_t *p, uint32_t bandwidth) { + + uint16_t i16; + uint32_t bw; + int c=PNM_HEADER_SIZE; + char fixme[]={0,1}; + + memcpy(p->buffer,pnm_header,PNM_HEADER_SIZE); + c+=pnm_write_chunk(PNA_CLIENT_CHALLANGE,strlen(pnm_challenge), + pnm_challenge,&p->buffer[c]); + c+=pnm_write_chunk(PNA_CLIENT_CAPS,PNM_CLIENT_CAPS_SIZE, + pnm_client_caps,&p->buffer[c]); + c+=pnm_write_chunk(0x0a,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x0c,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x0d,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x16,2,fixme,&p->buffer[c]); + c+=pnm_write_chunk(PNA_TIMESTAMP,strlen(pnm_timestamp), + pnm_timestamp,&p->buffer[c]); + c+=pnm_write_chunk(PNA_BANDWIDTH,4, + (const char *)&pnm_default_bandwidth,&p->buffer[c]); + c+=pnm_write_chunk(0x08,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x0e,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x0f,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x11,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x10,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x15,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(0x12,0,NULL,&p->buffer[c]); + c+=pnm_write_chunk(PNA_GUID,strlen(pnm_guid), + pnm_guid,&p->buffer[c]); + c+=pnm_write_chunk(PNA_TWENTYFOUR,PNM_TWENTYFOUR_SIZE, + pnm_twentyfour,&p->buffer[c]); + + /* data after chunks */ + memcpy(&p->buffer[c],after_chunks,after_chunks_length); + c+=after_chunks_length; + + /* client id string */ + p->buffer[c]=PNA_CLIENT_STRING; + i16=BE_16D(strlen(client_string)-1); /* dont know why do we have -1 here */ + memcpy(&p->buffer[c+1],&i16,2); + memcpy(&p->buffer[c+3],client_string,strlen(client_string)+1); + c=c+3+strlen(client_string)+1; + + /* file path */ + p->buffer[c]=0; + p->buffer[c+1]=PNA_PATH_REQUEST; + i16=BE_16D(strlen(p->path)); + memcpy(&p->buffer[c+2],&i16,2); + memcpy(&p->buffer[c+4],p->path,strlen(p->path)); + c=c+4+strlen(p->path); + + /* some trailing bytes */ + p->buffer[c]='y'; + p->buffer[c+1]='B'; + + rm_write(p->s,p->buffer,c+2); +} + +/* + * pnm_send_response sends a response of a challenge + */ + +static void pnm_send_response(pnm_t *p, const char *response) { + + int size=strlen(response); + + p->buffer[0]=0x23; + p->buffer[1]=0; + p->buffer[2]=(unsigned char) size; + + memcpy(&p->buffer[3], response, size); + + rm_write (p->s, p->buffer, size+3); +} + +/* + * get headers and challenge and fix headers + * write headers to p->header + * write challenge to p->buffer + */ + +static void pnm_get_headers(pnm_t *p) { + + uint32_t chunk_type; + uint8_t *ptr=p->header; + uint8_t *prop_hdr=NULL; + int chunk_size,size=0; + + while(1) { + if (HEADER_SIZE-size<=0) + { + printf("pnm: header buffer overflow. exiting\n"); + exit(1); + } + chunk_size=pnm_get_chunk(p,HEADER_SIZE-size,&chunk_type,ptr); + if (chunk_type == 0) break; + if (chunk_type == PNA_TAG) + { + memcpy(ptr, rm_header, RM_HEADER_SIZE); + chunk_size=RM_HEADER_SIZE; + } + if (chunk_type == PROP_TAG) + prop_hdr=ptr; + size+=chunk_size; + ptr+=chunk_size; + } + + /* set pre-buffer to a low number */ + prop_hdr[36]=0x01; + prop_hdr[37]=0xd6; + + prop_hdr[42]=(size>>24)%0xff; + prop_hdr[43]=(size>>16)%0xff; + prop_hdr[44]=(size>>8)%0xff; + prop_hdr[45]=(size)%0xff; + + /* read challenge */ + memcpy (p->buffer, ptr, PREAMBLE_SIZE); + rm_read (p->s, &p->buffer[PREAMBLE_SIZE], 64); + + /* now write a data header */ + memcpy(ptr, pnm_data_header, PNM_DATA_HEADER_SIZE); + size+=PNM_DATA_HEADER_SIZE; + + p->header_len=size; +} + +/* + * determine correct stream number by looking at indices + */ + +static int pnm_calc_stream(pnm_t *p) { + + char str0=0,str1=0; + + /* looking at the first index to + * find possible stream types + */ + if (p->seq_current[0]==p->seq_num[0]) str0=1; + if (p->seq_current[0]==p->seq_num[2]) str1=1; + + switch (str0+str1) { + case 1: /* one is possible, good. */ + if (str0) + { + p->seq_num[0]++; + p->seq_num[1]=p->seq_current[1]+1; + return 0; + } else + { + p->seq_num[2]++; + p->seq_num[3]=p->seq_current[1]+1; + return 1; + } + break; + case 0: + case 2: /* both types or none possible, not so good */ + /* try to figure out by second index */ + if ( (p->seq_current[1] == p->seq_num[1]) + &&(p->seq_current[1] != p->seq_num[3])) + { + /* ok, only stream0 matches */ + p->seq_num[0]=p->seq_current[0]+1; + p->seq_num[1]++; + return 0; + } + if ( (p->seq_current[1] == p->seq_num[3]) + &&(p->seq_current[1] != p->seq_num[1])) + { + /* ok, only stream1 matches */ + p->seq_num[2]=p->seq_current[0]+1; + p->seq_num[3]++; + return 1; + } + /* wow, both streams match, or not. */ + /* in this case, we guess type 0 */ + p->seq_num[0]=p->seq_current[0]+1; + p->seq_num[1]=p->seq_current[1]+1; + return 0; + break; + } + printf("pnm: wow, something very nasty happened in pnm_calc_stream\n"); + return 2; +} + +/* + * gets a stream chunk and writes it to a recieve buffer + */ + +static int pnm_get_stream_chunk(pnm_t *p) { + + int n; + char keepalive='!'; + unsigned int fof1, fof2, stream; + + /* send a keepalive */ + /* realplayer seems to do that every 43th package */ + if (!(p->packet%43)) + { + rm_write(p->s,&keepalive,1); + } + + /* data chunks begin with: 'Z' <o> <o> <i1> 'Z' <i2> + * where <o> is the offset to next stream chunk, + * <i1> is a 16 bit index + * <i2> is a 8 bit index which counts from 0x10 to somewhere + */ + + n = rm_read (p->s, p->buffer, 8); + if (n<8) return 0; + + /* skip 8 bytes if 0x62 is read */ + if (p->buffer[0] == 0x62) + { + n = rm_read (p->s, p->buffer, 8); + if (n<8) return 0; + } + + /* a server message */ + if (p->buffer[0] == 'X') + { + int size=BE_16(&p->buffer[1]); + + rm_read (p->s, &p->buffer[8], size-8); + p->buffer[size+8]=0; + printf("pnm: got message from server:\n%s\n", &p->buffer[3]); + exit(0); + } + + /* skip bytewise to next chunk. + * seems, that we dont need that, if we send enough + * keepalives + */ + n=0; + while (p->buffer[0] != 0x5a) { + int i; + for (i=1; i<8; i++) { + p->buffer[i-1]=p->buffer[i]; + } + rm_read (p->s, &p->buffer[7], 1); + n++; + } + if (n) printf("pnm: had to seek %i bytes to next chunk\n", n); + + /* check for 'Z's */ + if ((p->buffer[0] != 0x5a)||(p->buffer[7] != 0x5a)) + { + printf("pnm: bad boundaries\n"); + hexdump(p->buffer, 8); + return 0; + } + + /* check offsets */ + fof1=BE_16(&p->buffer[1]); + fof2=BE_16(&p->buffer[3]); + if (fof1 != fof2) + { + printf("pnm: frame offsets are different: 0x%04x 0x%04x\n",fof1,fof2); + return 0; + } + + /* get first index */ + p->seq_current[0]=BE_16(&p->buffer[5]); + + /* now read the rest of stream chunk */ + n = rm_read (p->s, &p->recv[5], fof1-5); + if (n<(fof1-5)) return 0; + + /* get second index */ + p->seq_current[1]=p->recv[5]; + + /* get stream number */ + stream=pnm_calc_stream(p); + + /* constructing a data packet header */ + + p->recv[0]=0; /* object version */ + p->recv[1]=0; + + fof2=BE_16(&fof2); + memcpy(&p->recv[2], &fof2, 2); + /*p->recv[2]=(fof2>>8)%0xff;*/ /* length */ + /*p->recv[3]=(fof2)%0xff;*/ + + p->recv[4]=0; /* stream number */ + p->recv[5]=stream; + + p->recv[10]=p->recv[10] & 0xfe; /* streambox seems to do that... */ + + p->packet++; + + p->recv_size=fof1; + + return fof1; +} + +pnm_t *pnm_connect(const char *mrl) { + + const char *mrl_ptr=mrl; + char *slash, *colon; + pnm_t *p=malloc(sizeof(pnm_t)); + int fd; + + if (strncmp(mrl,"pnm://",6)) + { + return NULL; + } + + mrl_ptr+=6; + + p->port=7070; + p->url=strdup(mrl); + + slash=strchr(mrl_ptr,'/'); + colon=strchr(mrl_ptr,':'); + + if (colon != NULL) { + if (slash != NULL) { + if (slash > colon) { /* host, p->port, slash found */ + + p->host=malloc(sizeof(char)*(colon-mrl_ptr+1)); + strncpy(p->host,mrl_ptr,colon-mrl_ptr); + p->host[colon-mrl_ptr]=0; + + strncpy(p->buffer,colon+1, slash-colon+1); + p->buffer[slash-colon+1]=0; + p->port=atoi(p->buffer); + + p->path=strdup(slash); + + } else + { /* p->host and slash found */ + p->host=malloc(sizeof(char)*(slash-mrl_ptr+1)); + strncpy(p->host,mrl_ptr,slash-mrl_ptr); + p->host[slash-mrl_ptr]=0; + + p->path=strdup(slash); + + } + } else + { /* p->host and p->port found */ + p->host=malloc(sizeof(char)*(colon-mrl_ptr+1)); + strncpy(p->host,mrl_ptr,colon-mrl_ptr); + p->host[colon-mrl_ptr]=0; + + strcpy(p->buffer,colon+1); + p->port=atoi(p->buffer); + + } + } else + { + if (slash != NULL) { /* host and slash found */ + p->host=malloc(sizeof(char)*(slash-mrl_ptr+1)); + strncpy(p->host,mrl_ptr,slash-mrl_ptr); + p->host[slash-mrl_ptr]=0; + + p->path=strdup(slash); + + } else + { /* only host found */ + p->host=strdup(mrl_ptr); + } + } + + fd = host_connect (p->host, p->port); + + if (fd == -1) { + printf ("pnm: failed to connect '%s'\n", p->host); + free(p->path); + free(p->host); + free(p->url); + free(p); + return NULL; + } + p->s=fd; + + pnm_send_request(p,pnm_available_bandwidths[10]); + pnm_get_headers(p); + pnm_send_response(p, pnm_response); + + return p; +} + +int pnm_read (pnm_t *this, char *data, int len) { + int total; + + total = 0; + + while (total < len) { + +#ifdef LOG + printf ("libpnm: read, got %d / %d bytes\n", total, len); +#endif + + if (this->header_read < this->header_len) { + int n, bytes_left ; + + printf ("libpnm: reading from header (%d<%d)\n", + this->header_read, this->header_len); + + bytes_left = this->header_len - this->header_read ; + + if ((len-total) < bytes_left) + n = len-total; + else + n = bytes_left; + + memcpy (&data[total], &this->header[this->header_read], n); + + printf ("libpnm: copied %d bytes\n", n); + + this->header_read += n; + total += n; + } else { + + int n, bytes_left ; + + printf ("libpnm: reading from chunk (%d>=%d)\n", + this->header_read, this->header_len); + + bytes_left = this->recv_size - this->recv_read; + + while (!bytes_left) { + + this->recv_read = 0; + + if (!pnm_get_stream_chunk (this)) { + printf ("libpnm: pnm_get_stream_chunk failed\n"); + return total; + } + bytes_left = this->recv_size - this->recv_read; + } + + + if ((len-total)<bytes_left) + n = len-total; + else + n = bytes_left; + + memcpy (&data[total], &this->recv[this->recv_read], n); + + printf ("libpnm: copied %d bytes\n", n); + + this->recv_read += n; + total += n; + } + } + + printf ("libpnm: total=%d\n", total); + + hexdump (data, total); + + return total; +} + +int pnm_peek_header (pnm_t *this, char *data) { + + memcpy (data, this->header, this->header_len); + return this->header_len; +} + +void pnm_close(pnm_t *p) { + + if (p->s >= 0) close(p->s); + free(p->path); + free(p->host); + free(p->url); + free(p); +} + + + diff --git a/src/input/pnm.h b/src/input/pnm.h new file mode 100644 index 000000000..8d07986ab --- /dev/null +++ b/src/input/pnm.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2002 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + * + * $Id: pnm.h,v 1.1 2002/11/23 00:04:32 guenter Exp $ + * + * pnm util functions header by joschka + * + */ + +#ifndef HAVE_PNM_H +#define HAVE_PNM_H + +#include <inttypes.h> +/*#include "xine_internal.h" */ + +typedef struct pnm_s pnm_t; + +pnm_t* pnm_connect (const char *url); + +int pnm_read (pnm_t *this, char *data, int len); +void pnm_close (pnm_t *this); + +int pnm_peek_header (pnm_t *this, char *data); + +#endif + |