From 49867bd432352d19172ab26cf873bd5651e69e25 Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 31 Jul 2006 17:39:17 +0000 Subject: introduce new 'stream' directory for all stream layer related components and split them from libmpdemux git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@19277 b3059339-0415-0410-9bf9-f77b7e298cf2 --- stream/pnm.c | 921 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 921 insertions(+) create mode 100644 stream/pnm.c (limited to 'stream/pnm.c') diff --git a/stream/pnm.c b/stream/pnm.c new file mode 100644 index 0000000000..033772d8d9 --- /dev/null +++ b/stream/pnm.c @@ -0,0 +1,921 @@ +/* + * 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 protocol implementation + * based upon code from joschka + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef HAVE_WINSOCK2 +#define closesocket close +#include +//#include +//#include +#else +#include +#endif + +#include "stream.h" +#include "demuxer.h" +#include "help_mp.h" +#include "osdep/timer.h" + +#include "pnm.h" +//#include "libreal/rmff.h" + +extern int network_bandwidth; + +#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 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 ) + +/* +#define LOG +*/ + +#define BUF_SIZE 4096 +#define HEADER_SIZE 4096 + +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 */ + uint32_t ts_current; /* timestamp of current chunk */ + uint32_t ts_last[2]; /* timestamps of last chunks */ + unsigned int packet; /* number of last recieved packet */ +}; + +/* + * utility macros + */ + +#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) + +/* sizes */ +#define PREAMBLE_SIZE 8 +#define CHECKSUM_SIZE 3 + + +/* header of rm files */ +#define RM_HEADER_SIZE 0x12 +static 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 +static 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 + +static const unsigned char pnm_challenge[] = "0990f6b4508b51e801bd6da011ad7b56"; +static const unsigned char pnm_timestamp[] = "[15/06/1999:22:22:49 00:00]"; +static const unsigned char pnm_guid[] = "3eac2411-83d5-11d2-f3ea-d7c3a51aa8b0"; +static const unsigned char pnm_response[] = "97715a899cbe41cee00dd434851535bf"; +static const unsigned char client_string[] = "WinNT_9.0_6.0.6.45_plus32_MP60_en-US_686l"; + +#define PNM_HEADER_SIZE 11 +static const unsigned char pnm_header[] = { + 'P','N','A', + 0x00, 0x0a, + 0x00, 0x14, + 0x00, 0x02, + 0x00, 0x01 }; + +#define PNM_CLIENT_CAPS_SIZE 126 +static 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' }; + +static const uint32_t pnm_default_bandwidth=10485800; +static const uint32_t pnm_available_bandwidths[]={14400,19200,28800,33600,34430,57600, + 115200,262200,393216,524300,1544000,10485800}; + +#define PNM_TWENTYFOUR_SIZE 16 +static 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 */ +static int after_chunks_length=6; +static unsigned char after_chunks[]={ + 0x00, 0x00, /* mark */ + + 0x50, 0x84, /* seems to be fixated */ + 0x1f, 0x3a /* varies on each request (checksum ?)*/ + }; + +static void hexdump (char *buf, int length); + +static int rm_write(int s, const char *buf, int len) { + int total, timeout; + + total = 0; timeout = 30; + while (total < len){ + int n; + + n = send (s, &buf[total], len - total, 0); + + if (n > 0) + total += n; + else if (n < 0) { +#ifndef HAVE_WINSOCK2 + if ((timeout>0) && ((errno == EAGAIN) || (errno == EINPROGRESS))) { +#else + if ((timeout>0) && ((errno == EAGAIN) || (WSAGetLastError() == WSAEINPROGRESS))) { +#endif + usec_sleep (1000000); 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 = 3; + timeout.tv_usec = 0; + + if (select (fd+1, &rset, NULL, NULL, &timeout) <= 0) { + return -1; + } + + ret=recv (fd, ((uint8_t*)buf)+total, count-total, 0); + + if (ret<=0) { + mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: read error.\n"); + return ret; + } else + total += ret; + } + + return total; +} + +/* + * debugging utilities + */ + +static void hexdump (char *buf, int length) { + + int i; + + mp_msg(MSGT_OPEN, MSGL_INFO, "input_pnm: ascii>"); + for (i = 0; i < length; i++) { + unsigned char c = buf[i]; + + if ((c >= 32) && (c <= 128)) + mp_msg(MSGT_OPEN, MSGL_INFO, "%c", c); + else + mp_msg(MSGT_OPEN, MSGL_INFO, "."); + } + mp_msg(MSGT_OPEN, MSGL_INFO, "\n"); + + mp_msg(MSGT_OPEN, MSGL_INFO, "input_pnm: hexdump> "); + for (i = 0; i < length; i++) { + unsigned char c = buf[i]; + + mp_msg(MSGT_OPEN, MSGL_INFO, "%02x", c); + + if ((i % 16) == 15) + mp_msg(MSGT_OPEN, MSGL_INFO, "\npnm: "); + + if ((i % 2) == 1) + mp_msg(MSGT_OPEN, MSGL_INFO, " "); + + } + mp_msg(MSGT_OPEN, MSGL_INFO, "\n"); +} + +/* + * pnm_get_chunk gets a chunk from stream + * and returns number of bytes read + */ + +static int pnm_get_chunk(pnm_t *p, + unsigned int max, + unsigned int *chunk_type, + char *data, int *need_response) { + + unsigned int chunk_size; + unsigned int n; + char *ptr; + + if (max < PREAMBLE_SIZE) + return -1; + + /* 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); + + max -= PREAMBLE_SIZE; + + *chunk_type = BE_32(data); + chunk_size = BE_32(data+4); + + switch (*chunk_type) { + case PNA_TAG: + *need_response=0; + ptr=data+PREAMBLE_SIZE; + if (max < 1) + return -1; + rm_read (p->s, ptr++, 1); + max -= 1; + + while(1) { + /* expecting following chunk format: 0x4f */ + + if (max < 2) + return -1; + rm_read (p->s, ptr, 2); + max -= 2; + if (*ptr == 'X') /* checking for server message */ + { + mp_msg(MSGT_OPEN, MSGL_WARN, "input_pnm: got a message from server:\n"); + if (max < 1) + return -1; + rm_read (p->s, ptr+2, 1); + max = -1; + n=BE_16(ptr+1); + if (max < n) + return -1; + rm_read (p->s, ptr+3, n); + max -= n; + ptr[3+n]=0; + mp_msg(MSGT_OPEN, MSGL_WARN, "%s\n",ptr+3); + return -1; + } + + if (*ptr == 'F') /* checking for server error */ + { + mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: server error.\n"); + return -1; + } + if (*ptr == 'i') + { + ptr+=2; + *need_response=1; + continue; + } + if (*ptr != 0x4f) break; + n=ptr[1]; + if (max < n) + return -1; + rm_read (p->s, ptr+2, n); + max -= n; + ptr+=(n+2); + } + /* the checksum of the next chunk is ignored here */ + if (max < 1) + return -1; + rm_read (p->s, ptr+2, 1); + ptr+=3; + chunk_size=ptr-data; + break; + case RMF_TAG: + case DATA_TAG: + case PROP_TAG: + case MDPR_TAG: + case CONT_TAG: + if (chunk_size > max || chunk_size < PREAMBLE_SIZE) { + mp_msg(MSGT_OPEN, MSGL_ERR, "error: max chunk size exceded (max was 0x%04x)\n", max); +#ifdef LOG + n=rm_read (p->s, &data[PREAMBLE_SIZE], 0x100 - PREAMBLE_SIZE); + hexdump(data,n+PREAMBLE_SIZE); +#endif + return -1; + } + 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; + 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)); /* don't 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 + * + * return 0 on error. != 0 on success + */ + +static int pnm_get_headers(pnm_t *p, int *need_response) { + + uint32_t chunk_type; + uint8_t *ptr=p->header; + uint8_t *prop_hdr=NULL; + int chunk_size,size=0; + int nr; +/* rmff_header_t *h; */ + + *need_response=0; + + while(1) { + if (HEADER_SIZE-size<=0) + { + mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: header buffer overflow. exiting\n"); + return 0; + } + chunk_size=pnm_get_chunk(p,HEADER_SIZE-size,&chunk_type,ptr,&nr); + if (chunk_size < 0) return 0; + if (chunk_type == 0) break; + if (chunk_type == PNA_TAG) + { + memcpy(ptr, rm_header, RM_HEADER_SIZE); + chunk_size=RM_HEADER_SIZE; + *need_response=nr; + } + if (chunk_type == DATA_TAG) + chunk_size=0; + if (chunk_type == RMF_TAG) + chunk_size=0; + if (chunk_type == PROP_TAG) + prop_hdr=ptr; + size+=chunk_size; + ptr+=chunk_size; + } + + if (!prop_hdr) { + mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: error while parsing headers.\n"); + return 0; + } + + /* set data offset */ + size--; + prop_hdr[42]=(size>>24)%0xff; + prop_hdr[43]=(size>>16)%0xff; + prop_hdr[44]=(size>>8)%0xff; + prop_hdr[45]=(size)%0xff; + size++; + + /* 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; +/* + h=rmff_scan_header(p->header); + rmff_fix_header(h); + p->header_len=rmff_get_header_size(h); + rmff_dump_header(h, p->header, HEADER_SIZE); +*/ + p->header_len=size; + + return 1; +} + +/* + * 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. */ + /* now we try to decide by timestamps */ + if (p->ts_current < p->ts_last[1]) + return 0; + if (p->ts_current < p->ts_last[0]) + return 1; + /* does not help, we guess type 0 */ +#ifdef LOG + mp_msg(MSGT_OPEN, MSGL_INFO, "guessing stream# 0\n"); +#endif + p->seq_num[0]=p->seq_current[0]+1; + p->seq_num[1]=p->seq_current[1]+1; + return 0; + break; + } + mp_msg(MSGT_OPEN, MSGL_ERR, "input_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) == 42) + { + rm_write(p->s,&keepalive,1); + } + + /* data chunks begin with: 'Z' 'Z' + * where is the offset to next stream chunk, + * is a 16 bit index + * is a 8 bit index which counts from 0x10 to somewhere + */ + + n = rm_read (p->s, p->buffer, 8); + if (n<0) return -1; + 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; +#ifdef LOG + mp_msg(MSGT_OPEN, MSGL_WARN, "input_pnm: had to seek 8 bytes on 0x62\n"); +#endif + } + + /* a server message */ + if (p->buffer[0] == 'X') + { + int size=BE_16(&p->buffer[1]); + + rm_read (p->s, &p->buffer[8], size-5); + p->buffer[size+3]=0; + mp_msg(MSGT_OPEN, MSGL_WARN, "input_pnm: got message from server while reading stream:\n%s\n", &p->buffer[3]); + return -1; + } + if (p->buffer[0] == 'F') + { + mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: server error.\n"); + return -1; + } + + /* skip bytewise to next chunk. + * seems, that we don't 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++; + } + +#ifdef LOG + if (n) mp_msg(MSGT_OPEN, MSGL_WARN, "input_pnm: had to seek %i bytes to next chunk\n", n); +#endif + + /* check for 'Z's */ + if ((p->buffer[0] != 0x5a)||(p->buffer[7] != 0x5a)) + { + mp_msg(MSGT_OPEN, MSGL_ERR, "input_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) + { + mp_msg(MSGT_OPEN, MSGL_ERR, "input_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 timestamp */ + p->ts_current=BE_32(&p->recv[6]); + + /* get stream number */ + stream=pnm_calc_stream(p); + + /* saving timestamp */ + p->ts_last[stream]=p->ts_current; + + /* 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) { +static pnm_t *pnm_connect(int fd, char *path) { + + pnm_t *p=malloc(sizeof(pnm_t)); + int need_response=0; + + p->path=strdup(path); + p->s=fd; + + pnm_send_request(p,pnm_available_bandwidths[10]); + if (!pnm_get_headers(p, &need_response)) { + mp_msg(MSGT_OPEN, MSGL_ERR, "input_pnm: failed to set up stream\n"); + free(p->path); + free(p); + return NULL; + } + if (need_response) + pnm_send_response(p, pnm_response); + p->ts_last[0]=0; + p->ts_last[1]=0; + + /* copy header to recv */ + + memcpy(p->recv, p->header, p->header_len); + p->recv_size = p->header_len; + p->recv_read = 0; + + return p; +} + +static int pnm_read (pnm_t *this, char *data, int len) { + + int to_copy=len; + char *dest=data; + char *source=this->recv + this->recv_read; + int fill=this->recv_size - this->recv_read; + int retval; + + if (len < 0) return 0; + while (to_copy > fill) { + + memcpy(dest, source, fill); + to_copy -= fill; + dest += fill; + this->recv_read=0; + + if ((retval = pnm_get_stream_chunk (this)) <= 0) { +#ifdef LOG + mp_msg(MSGT_OPEN, MSGL_INFO, "input_pnm: %d of %d bytes provided\n", len-to_copy, len); +#endif + if (retval < 0) + return retval; + else + return len-to_copy; + } + source = this->recv; + fill = this->recv_size - this->recv_read; + } + + memcpy(dest, source, to_copy); + this->recv_read += to_copy; + +#ifdef LOG + mp_msg(MSGT_OPEN, MSGL_INFO, "input_pnm: %d bytes provided\n", len); +#endif + + return len; +} + +static int pnm_peek_header (pnm_t *this, char *data) { + + memcpy (data, this->header, this->header_len); + return this->header_len; +} + +static void pnm_close(pnm_t *p) { + + if (p->s >= 0) closesocket(p->s); + free(p->path); + free(p); +} + +static int pnm_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *stream_ctrl ) { + return pnm_read(stream_ctrl->data, buffer, size); +} + +static int open_s(stream_t *stream,int mode, void* opts, int* file_format) { + int fd; + pnm_t *pnm; + URL_t *url; + + mp_msg(MSGT_OPEN, MSGL_INFO, "STREAM_PNM, URL: %s\n", stream->url); + stream->streaming_ctrl = streaming_ctrl_new(); + if(stream->streaming_ctrl==NULL) { + return STREAM_ERROR; + } + stream->streaming_ctrl->bandwidth = network_bandwidth; + url = url_new(stream->url); + stream->streaming_ctrl->url = check4proxies(url); + //url_free(url); + + fd = connect2Server( stream->streaming_ctrl->url->hostname, + stream->streaming_ctrl->url->port ? stream->streaming_ctrl->url->port : 7070,1 ); + + if(fd<0) + goto fail; + + pnm = pnm_connect(fd,stream->streaming_ctrl->url->file); + if(!pnm) + goto fail; + stream->type = STREAMTYPE_STREAM; + stream->fd=fd; + stream->streaming_ctrl->data=pnm; + stream->streaming_ctrl->streaming_read = pnm_streaming_read; + //stream->streaming_ctrl->streaming_seek = nop_streaming_seek; + stream->streaming_ctrl->prebuffer_size = 8*1024; // 8 KBytes + stream->streaming_ctrl->buffering = 1; + stream->streaming_ctrl->status = streaming_playing_e; + *file_format = DEMUXER_TYPE_REAL; + fixup_network_stream_cache(stream); + return STREAM_OK; + +fail: + streaming_ctrl_free(stream->streaming_ctrl); + stream->streaming_ctrl = NULL; + return STREAM_UNSUPORTED; +} + + +stream_info_t stream_info_pnm = { + "RealNetworks pnm", + "pnm", + "Arpi, xine team", + "ported from xine", + open_s, + {"pnm", NULL}, //pnm as fallback + NULL, + 0 // Urls are an option string +}; -- cgit v1.2.3