2 * Copyright (C) 2000-2002 the xine project
4 * This file is part of xine, a free video player.
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 * pnm protocol implementation
23 * based upon code from joschka
39 #define closesocket close
40 #include <sys/socket.h>
41 //#include <netinet/in.h>
47 #include "libavutil/intreadwrite.h"
50 #include "libmpdemux/demuxer.h"
52 #include "osdep/timer.h"
56 //#include "libreal/rmff.h"
58 extern int network_bandwidth
;
60 #define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
61 (((long)(unsigned char)(ch3) ) | \
62 ( (long)(unsigned char)(ch2) << 8 ) | \
63 ( (long)(unsigned char)(ch1) << 16 ) | \
64 ( (long)(unsigned char)(ch0) << 24 ) )
67 #define RMF_TAG FOURCC_TAG('.', 'R', 'M', 'F')
68 #define PROP_TAG FOURCC_TAG('P', 'R', 'O', 'P')
69 #define MDPR_TAG FOURCC_TAG('M', 'D', 'P', 'R')
70 #define CONT_TAG FOURCC_TAG('C', 'O', 'N', 'T')
71 #define DATA_TAG FOURCC_TAG('D', 'A', 'T', 'A')
72 #define INDX_TAG FOURCC_TAG('I', 'N', 'D', 'X')
73 #define PNA_TAG FOURCC_TAG('P', 'N', 'A', 0 )
80 #define HEADER_SIZE 4096
91 char buffer
[BUF_SIZE
]; /* scratch buffer */
94 uint8_t recv
[BUF_SIZE
];
98 uint8_t header
[HEADER_SIZE
];
101 unsigned int seq_num
[4]; /* two streams with two indices */
102 unsigned int seq_current
[2]; /* seqs of last stream chunk read */
103 uint32_t ts_current
; /* timestamp of current chunk */
104 uint32_t ts_last
[2]; /* timestamps of last chunks */
105 unsigned int packet
; /* number of last recieved packet */
109 #define PREAMBLE_SIZE 8
110 #define CHECKSUM_SIZE 3
113 /* header of rm files */
114 static const unsigned char rm_header
[]={
115 0x2e, 0x52, 0x4d, 0x46, /* object_id ".RMF" */
116 0x00, 0x00, 0x00, 0x12, /* header_size 0x12 */
117 0x00, 0x00, /* object_version 0x00 */
118 0x00, 0x00, 0x00, 0x00, /* file_version 0x00 */
119 0x00, 0x00, 0x00, 0x06 /* num_headers 0x06 */
122 /* data chunk header */
123 static const unsigned char pnm_data_header
[]={
125 0,0,0,0, /* data chunk size */
126 0,0, /* object version */
127 0,0,0,0, /* num packets */
128 0,0,0,0}; /* next data header */
130 /* pnm request chunk ids */
132 #define PNA_CLIENT_CAPS 0x03
133 #define PNA_CLIENT_CHALLANGE 0x04
134 #define PNA_BANDWIDTH 0x05
135 #define PNA_GUID 0x13
136 #define PNA_TIMESTAMP 0x17
137 #define PNA_TWENTYFOUR 0x18
139 #define PNA_CLIENT_STRING 0x63
140 #define PNA_PATH_REQUEST 0x52
142 static const unsigned char pnm_challenge
[] = "0990f6b4508b51e801bd6da011ad7b56";
143 static const unsigned char pnm_timestamp
[] = "[15/06/1999:22:22:49 00:00]";
144 static const unsigned char pnm_guid
[] = "3eac2411-83d5-11d2-f3ea-d7c3a51aa8b0";
145 static const unsigned char pnm_response
[] = "97715a899cbe41cee00dd434851535bf";
146 static const unsigned char client_string
[] = "WinNT_9.0_6.0.6.45_plus32_MP60_en-US_686l";
148 static const unsigned char pnm_header
[] = {
155 static const unsigned char pnm_client_caps
[] = {
156 0x07, 0x8a, 'p','n','r','v',
157 0, 0x90, 'p','n','r','v',
158 0, 0x64, 'd','n','e','t',
159 0, 0x46, 'p','n','r','v',
160 0, 0x32, 'd','n','e','t',
161 0, 0x2b, 'p','n','r','v',
162 0, 0x28, 'd','n','e','t',
163 0, 0x24, 'p','n','r','v',
164 0, 0x19, 'd','n','e','t',
165 0, 0x18, 'p','n','r','v',
166 0, 0x14, 's','i','p','r',
167 0, 0x14, 'd','n','e','t',
168 0, 0x24, '2','8','_','8',
169 0, 0x12, 'p','n','r','v',
170 0, 0x0f, 'd','n','e','t',
171 0, 0x0a, 's','i','p','r',
172 0, 0x0a, 'd','n','e','t',
173 0, 0x08, 's','i','p','r',
174 0, 0x06, 's','i','p','r',
175 0, 0x12, 'l','p','c','J',
176 0, 0x07, '0','5','_','6' };
178 static const uint32_t pnm_default_bandwidth
=10485800;
179 static const uint32_t pnm_available_bandwidths
[]={14400,19200,28800,33600,34430,57600,
180 115200,262200,393216,524300,1544000,10485800};
182 static const unsigned char pnm_twentyfour
[]={
183 0xd5, 0x42, 0xa3, 0x1b, 0xef, 0x1f, 0x70, 0x24,
184 0x85, 0x29, 0xb3, 0x8d, 0xba, 0x11, 0xf3, 0xd6 };
186 /* now other data follows. marked with 0x0000 at the beginning */
187 static const unsigned char after_chunks
[]={
188 0x00, 0x00, /* mark */
190 0x50, 0x84, /* seems to be fixated */
191 0x1f, 0x3a /* varies on each request (checksum ?)*/
194 static void hexdump (char *buf
, int length
);
196 static int rm_write(int s
, const char *buf
, int len
) {
199 total
= 0; timeout
= 30;
203 n
= send (s
, &buf
[total
], len
- total
, 0);
208 #ifndef HAVE_WINSOCK2
209 if (timeout
>0 && (errno
== EAGAIN
|| errno
== EINPROGRESS
)) {
211 if (timeout
>0 && (errno
== EAGAIN
|| WSAGetLastError() == WSAEINPROGRESS
)) {
213 usec_sleep (1000000); timeout
--;
222 static ssize_t
rm_read(int fd
, void *buf
, size_t count
) {
228 while (total
< count
) {
231 struct timeval timeout
;
239 if (select (fd
+1, &rset
, NULL
, NULL
, &timeout
) <= 0) {
243 ret
=recv (fd
, ((uint8_t*)buf
)+total
, count
-total
, 0);
246 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: read error.\n");
256 * debugging utilities
259 static void hexdump (char *buf
, int length
) {
263 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: ascii>");
264 for (i
= 0; i
< length
; i
++) {
265 unsigned char c
= buf
[i
];
267 if (c
>= 32 && c
<= 128)
268 mp_msg(MSGT_OPEN
, MSGL_INFO
, "%c", c
);
270 mp_msg(MSGT_OPEN
, MSGL_INFO
, ".");
272 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\n");
274 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: hexdump> ");
275 for (i
= 0; i
< length
; i
++) {
276 unsigned char c
= buf
[i
];
278 mp_msg(MSGT_OPEN
, MSGL_INFO
, "%02x", c
);
281 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\npnm: ");
284 mp_msg(MSGT_OPEN
, MSGL_INFO
, " ");
287 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\n");
291 * pnm_get_chunk gets a chunk from stream
292 * and returns number of bytes read
295 static int pnm_get_chunk(pnm_t
*p
,
297 unsigned int *chunk_type
,
298 char *data
, int *need_response
) {
300 unsigned int chunk_size
;
304 if (max
< PREAMBLE_SIZE
)
307 /* get first PREAMBLE_SIZE bytes and ignore checksum */
308 rm_read (p
->s
, data
, CHECKSUM_SIZE
);
310 rm_read (p
->s
, data
, PREAMBLE_SIZE
);
312 rm_read (p
->s
, data
+CHECKSUM_SIZE
, PREAMBLE_SIZE
-CHECKSUM_SIZE
);
314 max
-= PREAMBLE_SIZE
;
316 *chunk_type
= AV_RB32(data
);
317 chunk_size
= AV_RB32(data
+4);
319 switch (*chunk_type
) {
322 ptr
=data
+PREAMBLE_SIZE
;
325 rm_read (p
->s
, ptr
++, 1);
329 /* expecting following chunk format: 0x4f <chunk size> <data...> */
333 rm_read (p
->s
, ptr
, 2);
335 if (*ptr
== 'X') /* checking for server message */
337 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got a message from server:\n");
340 rm_read (p
->s
, ptr
+2, 1);
345 rm_read (p
->s
, ptr
+3, n
);
348 mp_msg(MSGT_OPEN
, MSGL_WARN
, "%s\n",ptr
+3);
352 if (*ptr
== 'F') /* checking for server error */
354 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
363 if (*ptr
!= 0x4f) break;
367 rm_read (p
->s
, ptr
+2, n
);
371 /* the checksum of the next chunk is ignored here */
374 rm_read (p
->s
, ptr
+2, 1);
383 if (chunk_size
> max
|| chunk_size
< PREAMBLE_SIZE
) {
384 mp_msg(MSGT_OPEN
, MSGL_ERR
, "error: max chunk size exceded (max was 0x%04x)\n", max
);
386 n
=rm_read (p
->s
, &data
[PREAMBLE_SIZE
], 0x100 - PREAMBLE_SIZE
);
387 hexdump(data
,n
+PREAMBLE_SIZE
);
391 rm_read (p
->s
, &data
[PREAMBLE_SIZE
], chunk_size
-PREAMBLE_SIZE
);
395 chunk_size
= PREAMBLE_SIZE
;
403 * writes a chunk to a buffer, returns number of bytes written
406 static int pnm_write_chunk(uint16_t chunk_id
, uint16_t length
,
407 const char *chunk
, char *data
) {
409 AV_WB16(&data
[0], chunk_id
);
410 AV_WB16(&data
[2], length
);
411 memcpy(&data
[4],chunk
,length
);
417 * constructs a request and sends it
420 static void pnm_send_request(pnm_t
*p
, uint32_t bandwidth
) {
423 int c
=sizeof(pnm_header
);
426 memcpy(p
->buffer
,pnm_header
,sizeof(pnm_header
));
427 c
+=pnm_write_chunk(PNA_CLIENT_CHALLANGE
,strlen(pnm_challenge
),
428 pnm_challenge
,&p
->buffer
[c
]);
429 c
+=pnm_write_chunk(PNA_CLIENT_CAPS
,sizeof(pnm_client_caps
),
430 pnm_client_caps
,&p
->buffer
[c
]);
431 c
+=pnm_write_chunk(0x0a,0,NULL
,&p
->buffer
[c
]);
432 c
+=pnm_write_chunk(0x0c,0,NULL
,&p
->buffer
[c
]);
433 c
+=pnm_write_chunk(0x0d,0,NULL
,&p
->buffer
[c
]);
434 c
+=pnm_write_chunk(0x16,2,fixme
,&p
->buffer
[c
]);
435 c
+=pnm_write_chunk(PNA_TIMESTAMP
,strlen(pnm_timestamp
),
436 pnm_timestamp
,&p
->buffer
[c
]);
437 c
+=pnm_write_chunk(PNA_BANDWIDTH
,4,
438 (const char *)&pnm_default_bandwidth
,&p
->buffer
[c
]);
439 c
+=pnm_write_chunk(0x08,0,NULL
,&p
->buffer
[c
]);
440 c
+=pnm_write_chunk(0x0e,0,NULL
,&p
->buffer
[c
]);
441 c
+=pnm_write_chunk(0x0f,0,NULL
,&p
->buffer
[c
]);
442 c
+=pnm_write_chunk(0x11,0,NULL
,&p
->buffer
[c
]);
443 c
+=pnm_write_chunk(0x10,0,NULL
,&p
->buffer
[c
]);
444 c
+=pnm_write_chunk(0x15,0,NULL
,&p
->buffer
[c
]);
445 c
+=pnm_write_chunk(0x12,0,NULL
,&p
->buffer
[c
]);
446 c
+=pnm_write_chunk(PNA_GUID
,strlen(pnm_guid
),
447 pnm_guid
,&p
->buffer
[c
]);
448 c
+=pnm_write_chunk(PNA_TWENTYFOUR
,sizeof(pnm_twentyfour
),
449 pnm_twentyfour
,&p
->buffer
[c
]);
451 /* data after chunks */
452 memcpy(&p
->buffer
[c
],after_chunks
,sizeof(after_chunks
));
453 c
+=sizeof(after_chunks
);
455 /* client id string */
456 p
->buffer
[c
]=PNA_CLIENT_STRING
;
457 AV_WB16(&p
->buffer
[c
+1], strlen(client_string
)-1); /* don't know why do we have -1 here */
458 memcpy(&p
->buffer
[c
+1],&i16
,2);
459 memcpy(&p
->buffer
[c
+3],client_string
,strlen(client_string
)+1);
460 c
=c
+3+strlen(client_string
)+1;
464 p
->buffer
[c
+1]=PNA_PATH_REQUEST
;
465 AV_WB16(&p
->buffer
[c
+2], strlen(p
->path
));
466 memcpy(&p
->buffer
[c
+4],p
->path
,strlen(p
->path
));
467 c
=c
+4+strlen(p
->path
);
469 /* some trailing bytes */
473 rm_write(p
->s
,p
->buffer
,c
+2);
477 * pnm_send_response sends a response of a challenge
480 static void pnm_send_response(pnm_t
*p
, const char *response
) {
482 int size
=strlen(response
);
486 p
->buffer
[2]=(unsigned char) size
;
488 memcpy(&p
->buffer
[3], response
, size
);
490 rm_write (p
->s
, p
->buffer
, size
+3);
495 * get headers and challenge and fix headers
496 * write headers to p->header
497 * write challenge to p->buffer
499 * return 0 on error. != 0 on success
502 static int pnm_get_headers(pnm_t
*p
, int *need_response
) {
505 uint8_t *ptr
=p
->header
;
506 uint8_t *prop_hdr
=NULL
;
507 int chunk_size
,size
=0;
509 /* rmff_header_t *h; */
514 if (HEADER_SIZE
-size
<=0)
516 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: header buffer overflow. exiting\n");
519 chunk_size
=pnm_get_chunk(p
,HEADER_SIZE
-size
,&chunk_type
,ptr
,&nr
);
520 if (chunk_size
< 0) return 0;
521 if (chunk_type
== 0) break;
522 if (chunk_type
== PNA_TAG
)
524 memcpy(ptr
, rm_header
, sizeof(rm_header
));
525 chunk_size
=sizeof(rm_header
);
528 if (chunk_type
== DATA_TAG
)
530 if (chunk_type
== RMF_TAG
)
532 if (chunk_type
== PROP_TAG
)
539 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: error while parsing headers.\n");
543 /* set data offset */
544 AV_WB32(&prop_hdr
[42], size
- 1);
547 memcpy (p
->buffer
, ptr
, PREAMBLE_SIZE
);
548 rm_read (p
->s
, &p
->buffer
[PREAMBLE_SIZE
], 64);
550 /* now write a data header */
551 memcpy(ptr
, pnm_data_header
, sizeof(pnm_data_header
));
552 size
+=sizeof(pnm_data_header
);
554 h=rmff_scan_header(p->header);
556 p->header_len=rmff_get_header_size(h);
557 rmff_dump_header(h, p->header, HEADER_SIZE);
565 * determine correct stream number by looking at indices
568 static int pnm_calc_stream(pnm_t
*p
) {
572 /* looking at the first index to
573 * find possible stream types
575 if (p
->seq_current
[0]==p
->seq_num
[0]) str0
=1;
576 if (p
->seq_current
[0]==p
->seq_num
[2]) str1
=1;
579 case 1: /* one is possible, good. */
583 p
->seq_num
[1]=p
->seq_current
[1]+1;
588 p
->seq_num
[3]=p
->seq_current
[1]+1;
593 case 2: /* both types or none possible, not so good */
594 /* try to figure out by second index */
595 if ( p
->seq_current
[1] == p
->seq_num
[1]
596 && p
->seq_current
[1] != p
->seq_num
[3])
598 /* ok, only stream0 matches */
599 p
->seq_num
[0]=p
->seq_current
[0]+1;
603 if ( p
->seq_current
[1] == p
->seq_num
[3]
604 && p
->seq_current
[1] != p
->seq_num
[1])
606 /* ok, only stream1 matches */
607 p
->seq_num
[2]=p
->seq_current
[0]+1;
611 /* wow, both streams match, or not. */
612 /* now we try to decide by timestamps */
613 if (p
->ts_current
< p
->ts_last
[1])
615 if (p
->ts_current
< p
->ts_last
[0])
617 /* does not help, we guess type 0 */
619 mp_msg(MSGT_OPEN
, MSGL_INFO
, "guessing stream# 0\n");
621 p
->seq_num
[0]=p
->seq_current
[0]+1;
622 p
->seq_num
[1]=p
->seq_current
[1]+1;
626 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: wow, something very nasty happened in pnm_calc_stream\n");
631 * gets a stream chunk and writes it to a recieve buffer
634 static int pnm_get_stream_chunk(pnm_t
*p
) {
638 unsigned int fof1
, fof2
, stream
;
640 /* send a keepalive */
641 /* realplayer seems to do that every 43th package */
642 if (p
->packet
%43 == 42)
643 rm_write(p
->s
,&keepalive
,1);
645 /* data chunks begin with: 'Z' <o> <o> <i1> 'Z' <i2>
646 * where <o> is the offset to next stream chunk,
647 * <i1> is a 16 bit index
648 * <i2> is a 8 bit index which counts from 0x10 to somewhere
651 n
= rm_read (p
->s
, p
->buffer
, 8);
655 /* skip 8 bytes if 0x62 is read */
656 if (p
->buffer
[0] == 0x62)
658 n
= rm_read (p
->s
, p
->buffer
, 8);
661 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek 8 bytes on 0x62\n");
665 /* a server message */
666 if (p
->buffer
[0] == 'X')
668 int size
=AV_RB16(&p
->buffer
[1]);
670 rm_read (p
->s
, &p
->buffer
[8], size
-5);
672 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got message from server while reading stream:\n%s\n", &p
->buffer
[3]);
675 if (p
->buffer
[0] == 'F')
677 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
681 /* skip bytewise to next chunk.
682 * seems, that we don't need that, if we send enough
686 while (p
->buffer
[0] != 0x5a) {
689 p
->buffer
[i
-1]=p
->buffer
[i
];
690 rm_read (p
->s
, &p
->buffer
[7], 1);
695 if (n
) mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek %i bytes to next chunk\n", n
);
699 if (p
->buffer
[0] != 0x5a || p
->buffer
[7] != 0x5a)
701 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: bad boundaries\n");
702 hexdump(p
->buffer
, 8);
707 fof1
=AV_RB16(&p
->buffer
[1]);
708 fof2
=AV_RB16(&p
->buffer
[3]);
711 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: frame offsets are different: 0x%04x 0x%04x\n",fof1
,fof2
);
715 /* get first index */
716 p
->seq_current
[0]=AV_RB16(&p
->buffer
[5]);
718 /* now read the rest of stream chunk */
719 n
= rm_read (p
->s
, &p
->recv
[5], fof1
-5);
720 if (n
<fof1
-5) return 0;
722 /* get second index */
723 p
->seq_current
[1]=p
->recv
[5];
726 p
->ts_current
=AV_RB32(&p
->recv
[6]);
728 /* get stream number */
729 stream
=pnm_calc_stream(p
);
731 /* saving timestamp */
732 p
->ts_last
[stream
]=p
->ts_current
;
734 /* constructing a data packet header */
736 p
->recv
[0]=0; /* object version */
739 AV_WB16(&p
->recv
[2], fof2
); /* length */
741 p
->recv
[4]=0; /* stream number */
744 p
->recv
[10] &= 0xfe; /* streambox seems to do that... */
753 // pnm_t *pnm_connect(const char *mrl) {
754 static pnm_t
*pnm_connect(int fd
, char *path
) {
756 pnm_t
*p
=malloc(sizeof(pnm_t
));
759 p
->path
=strdup(path
);
762 pnm_send_request(p
,pnm_available_bandwidths
[10]);
763 if (!pnm_get_headers(p
, &need_response
)) {
764 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: failed to set up stream\n");
770 pnm_send_response(p
, pnm_response
);
774 /* copy header to recv */
776 memcpy(p
->recv
, p
->header
, p
->header_len
);
777 p
->recv_size
= p
->header_len
;
783 static int pnm_read (pnm_t
*this, char *data
, int len
) {
787 char *source
=this->recv
+ this->recv_read
;
788 int fill
=this->recv_size
- this->recv_read
;
791 if (len
< 0) return 0;
792 while (to_copy
> fill
) {
794 memcpy(dest
, source
, fill
);
799 if ((retval
= pnm_get_stream_chunk (this)) <= 0) {
801 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d of %d bytes provided\n", len
-to_copy
, len
);
809 fill
= this->recv_size
- this->recv_read
;
812 memcpy(dest
, source
, to_copy
);
813 this->recv_read
+= to_copy
;
816 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d bytes provided\n", len
);
822 static int pnm_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
) {
823 return pnm_read(stream_ctrl
->data
, buffer
, size
);
826 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
831 mp_msg(MSGT_OPEN
, MSGL_INFO
, "STREAM_PNM, URL: %s\n", stream
->url
);
832 stream
->streaming_ctrl
= streaming_ctrl_new();
833 if(stream
->streaming_ctrl
==NULL
)
836 stream
->streaming_ctrl
->bandwidth
= network_bandwidth
;
837 url
= url_new(stream
->url
);
838 stream
->streaming_ctrl
->url
= check4proxies(url
);
841 fd
= connect2Server( stream
->streaming_ctrl
->url
->hostname
,
842 stream
->streaming_ctrl
->url
->port
? stream
->streaming_ctrl
->url
->port
: 7070,1 );
847 pnm
= pnm_connect(fd
,stream
->streaming_ctrl
->url
->file
);
850 stream
->type
= STREAMTYPE_STREAM
;
852 stream
->streaming_ctrl
->data
=pnm
;
853 stream
->streaming_ctrl
->streaming_read
= pnm_streaming_read
;
854 //stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
855 stream
->streaming_ctrl
->prebuffer_size
= 8*1024; // 8 KBytes
856 stream
->streaming_ctrl
->buffering
= 1;
857 stream
->streaming_ctrl
->status
= streaming_playing_e
;
858 *file_format
= DEMUXER_TYPE_REAL
;
859 fixup_network_stream_cache(stream
);
863 streaming_ctrl_free(stream
->streaming_ctrl
);
864 stream
->streaming_ctrl
= NULL
;
865 return STREAM_UNSUPPORTED
;
869 const stream_info_t stream_info_pnm
= {
875 {"pnm", NULL
}, //pnm as fallback
877 0 // Urls are an option string