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 #include <sys/socket.h>
40 //#include <netinet/in.h>
46 #include "ffmpeg_files/intreadwrite.h"
49 #include "libmpdemux/demuxer.h"
50 #include "osdep/timer.h"
54 //#include "libreal/rmff.h"
56 extern int network_bandwidth
;
58 #define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
59 (((long)(unsigned char)(ch3) ) | \
60 ( (long)(unsigned char)(ch2) << 8 ) | \
61 ( (long)(unsigned char)(ch1) << 16 ) | \
62 ( (long)(unsigned char)(ch0) << 24 ) )
65 #define RMF_TAG FOURCC_TAG('.', 'R', 'M', 'F')
66 #define PROP_TAG FOURCC_TAG('P', 'R', 'O', 'P')
67 #define MDPR_TAG FOURCC_TAG('M', 'D', 'P', 'R')
68 #define CONT_TAG FOURCC_TAG('C', 'O', 'N', 'T')
69 #define DATA_TAG FOURCC_TAG('D', 'A', 'T', 'A')
70 #define INDX_TAG FOURCC_TAG('I', 'N', 'D', 'X')
71 #define PNA_TAG FOURCC_TAG('P', 'N', 'A', 0 )
78 #define HEADER_SIZE 4096
89 char buffer
[BUF_SIZE
]; /* scratch buffer */
92 uint8_t recv
[BUF_SIZE
];
96 uint8_t header
[HEADER_SIZE
];
99 unsigned int seq_num
[4]; /* two streams with two indices */
100 unsigned int seq_current
[2]; /* seqs of last stream chunk read */
101 uint32_t ts_current
; /* timestamp of current chunk */
102 uint32_t ts_last
[2]; /* timestamps of last chunks */
103 unsigned int packet
; /* number of last recieved packet */
107 #define PREAMBLE_SIZE 8
108 #define CHECKSUM_SIZE 3
111 /* header of rm files */
112 static const unsigned char rm_header
[]={
113 0x2e, 0x52, 0x4d, 0x46, /* object_id ".RMF" */
114 0x00, 0x00, 0x00, 0x12, /* header_size 0x12 */
115 0x00, 0x00, /* object_version 0x00 */
116 0x00, 0x00, 0x00, 0x00, /* file_version 0x00 */
117 0x00, 0x00, 0x00, 0x06 /* num_headers 0x06 */
120 /* data chunk header */
121 static const unsigned char pnm_data_header
[]={
123 0,0,0,0, /* data chunk size */
124 0,0, /* object version */
125 0,0,0,0, /* num packets */
126 0,0,0,0}; /* next data header */
128 /* pnm request chunk ids */
130 #define PNA_CLIENT_CAPS 0x03
131 #define PNA_CLIENT_CHALLANGE 0x04
132 #define PNA_BANDWIDTH 0x05
133 #define PNA_GUID 0x13
134 #define PNA_TIMESTAMP 0x17
135 #define PNA_TWENTYFOUR 0x18
137 #define PNA_CLIENT_STRING 0x63
138 #define PNA_PATH_REQUEST 0x52
140 static const unsigned char pnm_challenge
[] = "0990f6b4508b51e801bd6da011ad7b56";
141 static const unsigned char pnm_timestamp
[] = "[15/06/1999:22:22:49 00:00]";
142 static const unsigned char pnm_guid
[] = "3eac2411-83d5-11d2-f3ea-d7c3a51aa8b0";
143 static const unsigned char pnm_response
[] = "97715a899cbe41cee00dd434851535bf";
144 static const unsigned char client_string
[] = "WinNT_9.0_6.0.6.45_plus32_MP60_en-US_686l";
146 static const unsigned char pnm_header
[] = {
153 static const unsigned char pnm_client_caps
[] = {
154 0x07, 0x8a, 'p','n','r','v',
155 0, 0x90, 'p','n','r','v',
156 0, 0x64, 'd','n','e','t',
157 0, 0x46, 'p','n','r','v',
158 0, 0x32, 'd','n','e','t',
159 0, 0x2b, 'p','n','r','v',
160 0, 0x28, 'd','n','e','t',
161 0, 0x24, 'p','n','r','v',
162 0, 0x19, 'd','n','e','t',
163 0, 0x18, 'p','n','r','v',
164 0, 0x14, 's','i','p','r',
165 0, 0x14, 'd','n','e','t',
166 0, 0x24, '2','8','_','8',
167 0, 0x12, 'p','n','r','v',
168 0, 0x0f, 'd','n','e','t',
169 0, 0x0a, 's','i','p','r',
170 0, 0x0a, 'd','n','e','t',
171 0, 0x08, 's','i','p','r',
172 0, 0x06, 's','i','p','r',
173 0, 0x12, 'l','p','c','J',
174 0, 0x07, '0','5','_','6' };
176 static const uint32_t pnm_default_bandwidth
=10485800;
177 static const uint32_t pnm_available_bandwidths
[]={14400,19200,28800,33600,34430,57600,
178 115200,262200,393216,524300,1544000,10485800};
180 static const unsigned char pnm_twentyfour
[]={
181 0xd5, 0x42, 0xa3, 0x1b, 0xef, 0x1f, 0x70, 0x24,
182 0x85, 0x29, 0xb3, 0x8d, 0xba, 0x11, 0xf3, 0xd6 };
184 /* now other data follows. marked with 0x0000 at the beginning */
185 static const unsigned char after_chunks
[]={
186 0x00, 0x00, /* mark */
188 0x50, 0x84, /* seems to be fixated */
189 0x1f, 0x3a /* varies on each request (checksum ?)*/
192 static void hexdump (char *buf
, int length
);
194 static int rm_write(int s
, const char *buf
, int len
) {
197 total
= 0; timeout
= 30;
201 n
= send (s
, &buf
[total
], len
- total
, 0);
207 if (timeout
>0 && (errno
== EAGAIN
|| errno
== EINPROGRESS
)) {
209 if (timeout
>0 && (errno
== EAGAIN
|| WSAGetLastError() == WSAEINPROGRESS
)) {
211 usec_sleep (1000000); timeout
--;
220 static ssize_t
rm_read(int fd
, void *buf
, size_t count
) {
226 while (total
< count
) {
229 struct timeval timeout
;
237 if (select (fd
+1, &rset
, NULL
, NULL
, &timeout
) <= 0) {
241 ret
=recv (fd
, ((uint8_t*)buf
)+total
, count
-total
, 0);
244 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: read error.\n");
254 * debugging utilities
257 static void hexdump (char *buf
, int length
) {
261 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: ascii>");
262 for (i
= 0; i
< length
; i
++) {
263 unsigned char c
= buf
[i
];
265 if (c
>= 32 && c
<= 128)
266 mp_msg(MSGT_OPEN
, MSGL_INFO
, "%c", c
);
268 mp_msg(MSGT_OPEN
, MSGL_INFO
, ".");
270 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\n");
272 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: hexdump> ");
273 for (i
= 0; i
< length
; i
++) {
274 unsigned char c
= buf
[i
];
276 mp_msg(MSGT_OPEN
, MSGL_INFO
, "%02x", c
);
279 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\npnm: ");
282 mp_msg(MSGT_OPEN
, MSGL_INFO
, " ");
285 mp_msg(MSGT_OPEN
, MSGL_INFO
, "\n");
289 * pnm_get_chunk gets a chunk from stream
290 * and returns number of bytes read
293 static int pnm_get_chunk(pnm_t
*p
,
295 unsigned int *chunk_type
,
296 char *data
, int *need_response
) {
298 unsigned int chunk_size
;
302 if (max
< PREAMBLE_SIZE
)
305 /* get first PREAMBLE_SIZE bytes and ignore checksum */
306 rm_read (p
->s
, data
, CHECKSUM_SIZE
);
308 rm_read (p
->s
, data
, PREAMBLE_SIZE
);
310 rm_read (p
->s
, data
+CHECKSUM_SIZE
, PREAMBLE_SIZE
-CHECKSUM_SIZE
);
312 max
-= PREAMBLE_SIZE
;
314 *chunk_type
= AV_RB32(data
);
315 chunk_size
= AV_RB32(data
+4);
317 switch (*chunk_type
) {
320 ptr
=data
+PREAMBLE_SIZE
;
323 rm_read (p
->s
, ptr
++, 1);
327 /* expecting following chunk format: 0x4f <chunk size> <data...> */
331 rm_read (p
->s
, ptr
, 2);
333 if (*ptr
== 'X') /* checking for server message */
335 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got a message from server:\n");
338 rm_read (p
->s
, ptr
+2, 1);
343 rm_read (p
->s
, ptr
+3, n
);
346 mp_msg(MSGT_OPEN
, MSGL_WARN
, "%s\n",ptr
+3);
350 if (*ptr
== 'F') /* checking for server error */
352 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
361 if (*ptr
!= 0x4f) break;
365 rm_read (p
->s
, ptr
+2, n
);
369 /* the checksum of the next chunk is ignored here */
372 rm_read (p
->s
, ptr
+2, 1);
381 if (chunk_size
> max
|| chunk_size
< PREAMBLE_SIZE
) {
382 mp_msg(MSGT_OPEN
, MSGL_ERR
, "error: max chunk size exceeded (max was 0x%04x)\n", max
);
384 n
=rm_read (p
->s
, &data
[PREAMBLE_SIZE
], 0x100 - PREAMBLE_SIZE
);
385 hexdump(data
,n
+PREAMBLE_SIZE
);
389 rm_read (p
->s
, &data
[PREAMBLE_SIZE
], chunk_size
-PREAMBLE_SIZE
);
393 chunk_size
= PREAMBLE_SIZE
;
401 * writes a chunk to a buffer, returns number of bytes written
404 static int pnm_write_chunk(uint16_t chunk_id
, uint16_t length
,
405 const char *chunk
, char *data
) {
407 AV_WB16(&data
[0], chunk_id
);
408 AV_WB16(&data
[2], length
);
409 memcpy(&data
[4],chunk
,length
);
415 * constructs a request and sends it
418 static void pnm_send_request(pnm_t
*p
, uint32_t bandwidth
) {
420 int c
=sizeof(pnm_header
);
423 memcpy(p
->buffer
,pnm_header
,sizeof(pnm_header
));
424 c
+=pnm_write_chunk(PNA_CLIENT_CHALLANGE
,strlen(pnm_challenge
),
425 pnm_challenge
,&p
->buffer
[c
]);
426 c
+=pnm_write_chunk(PNA_CLIENT_CAPS
,sizeof(pnm_client_caps
),
427 pnm_client_caps
,&p
->buffer
[c
]);
428 c
+=pnm_write_chunk(0x0a,0,NULL
,&p
->buffer
[c
]);
429 c
+=pnm_write_chunk(0x0c,0,NULL
,&p
->buffer
[c
]);
430 c
+=pnm_write_chunk(0x0d,0,NULL
,&p
->buffer
[c
]);
431 c
+=pnm_write_chunk(0x16,2,fixme
,&p
->buffer
[c
]);
432 c
+=pnm_write_chunk(PNA_TIMESTAMP
,strlen(pnm_timestamp
),
433 pnm_timestamp
,&p
->buffer
[c
]);
434 c
+=pnm_write_chunk(PNA_BANDWIDTH
,4,
435 (const char *)&pnm_default_bandwidth
,&p
->buffer
[c
]);
436 c
+=pnm_write_chunk(0x08,0,NULL
,&p
->buffer
[c
]);
437 c
+=pnm_write_chunk(0x0e,0,NULL
,&p
->buffer
[c
]);
438 c
+=pnm_write_chunk(0x0f,0,NULL
,&p
->buffer
[c
]);
439 c
+=pnm_write_chunk(0x11,0,NULL
,&p
->buffer
[c
]);
440 c
+=pnm_write_chunk(0x10,0,NULL
,&p
->buffer
[c
]);
441 c
+=pnm_write_chunk(0x15,0,NULL
,&p
->buffer
[c
]);
442 c
+=pnm_write_chunk(0x12,0,NULL
,&p
->buffer
[c
]);
443 c
+=pnm_write_chunk(PNA_GUID
,strlen(pnm_guid
),
444 pnm_guid
,&p
->buffer
[c
]);
445 c
+=pnm_write_chunk(PNA_TWENTYFOUR
,sizeof(pnm_twentyfour
),
446 pnm_twentyfour
,&p
->buffer
[c
]);
448 /* data after chunks */
449 memcpy(&p
->buffer
[c
],after_chunks
,sizeof(after_chunks
));
450 c
+=sizeof(after_chunks
);
452 /* client id string */
453 p
->buffer
[c
]=PNA_CLIENT_STRING
;
454 AV_WB16(&p
->buffer
[c
+1], strlen(client_string
)-1); /* don't know why do we have -1 here */
455 memcpy(&p
->buffer
[c
+3],client_string
,strlen(client_string
)+1);
456 c
=c
+3+strlen(client_string
)+1;
460 p
->buffer
[c
+1]=PNA_PATH_REQUEST
;
461 AV_WB16(&p
->buffer
[c
+2], strlen(p
->path
));
462 memcpy(&p
->buffer
[c
+4],p
->path
,strlen(p
->path
));
463 c
=c
+4+strlen(p
->path
);
465 /* some trailing bytes */
469 rm_write(p
->s
,p
->buffer
,c
+2);
473 * pnm_send_response sends a response of a challenge
476 static void pnm_send_response(pnm_t
*p
, const char *response
) {
478 int size
=strlen(response
);
482 p
->buffer
[2]=(unsigned char) size
;
484 memcpy(&p
->buffer
[3], response
, size
);
486 rm_write (p
->s
, p
->buffer
, size
+3);
491 * get headers and challenge and fix headers
492 * write headers to p->header
493 * write challenge to p->buffer
495 * return 0 on error. != 0 on success
498 static int pnm_get_headers(pnm_t
*p
, int *need_response
) {
501 uint8_t *ptr
=p
->header
;
502 uint8_t *prop_hdr
=NULL
;
503 int chunk_size
,size
=0;
505 /* rmff_header_t *h; */
510 if (HEADER_SIZE
-size
<=0)
512 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: header buffer overflow. exiting\n");
515 chunk_size
=pnm_get_chunk(p
,HEADER_SIZE
-size
,&chunk_type
,ptr
,&nr
);
516 if (chunk_size
< 0) return 0;
517 if (chunk_type
== 0) break;
518 if (chunk_type
== PNA_TAG
)
520 memcpy(ptr
, rm_header
, sizeof(rm_header
));
521 chunk_size
=sizeof(rm_header
);
524 if (chunk_type
== DATA_TAG
)
526 if (chunk_type
== RMF_TAG
)
528 if (chunk_type
== PROP_TAG
)
535 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: error while parsing headers.\n");
539 /* set data offset */
540 AV_WB32(&prop_hdr
[42], size
- 1);
543 memcpy (p
->buffer
, ptr
, PREAMBLE_SIZE
);
544 rm_read (p
->s
, &p
->buffer
[PREAMBLE_SIZE
], 64);
546 /* now write a data header */
547 memcpy(ptr
, pnm_data_header
, sizeof(pnm_data_header
));
548 size
+=sizeof(pnm_data_header
);
550 h=rmff_scan_header(p->header);
552 p->header_len=rmff_get_header_size(h);
553 rmff_dump_header(h, p->header, HEADER_SIZE);
561 * determine correct stream number by looking at indices
564 static int pnm_calc_stream(pnm_t
*p
) {
568 /* looking at the first index to
569 * find possible stream types
571 if (p
->seq_current
[0]==p
->seq_num
[0]) str0
=1;
572 if (p
->seq_current
[0]==p
->seq_num
[2]) str1
=1;
575 case 1: /* one is possible, good. */
579 p
->seq_num
[1]=p
->seq_current
[1]+1;
584 p
->seq_num
[3]=p
->seq_current
[1]+1;
589 case 2: /* both types or none possible, not so good */
590 /* try to figure out by second index */
591 if ( p
->seq_current
[1] == p
->seq_num
[1]
592 && p
->seq_current
[1] != p
->seq_num
[3])
594 /* ok, only stream0 matches */
595 p
->seq_num
[0]=p
->seq_current
[0]+1;
599 if ( p
->seq_current
[1] == p
->seq_num
[3]
600 && p
->seq_current
[1] != p
->seq_num
[1])
602 /* ok, only stream1 matches */
603 p
->seq_num
[2]=p
->seq_current
[0]+1;
607 /* wow, both streams match, or not. */
608 /* now we try to decide by timestamps */
609 if (p
->ts_current
< p
->ts_last
[1])
611 if (p
->ts_current
< p
->ts_last
[0])
613 /* does not help, we guess type 0 */
615 mp_msg(MSGT_OPEN
, MSGL_INFO
, "guessing stream# 0\n");
617 p
->seq_num
[0]=p
->seq_current
[0]+1;
618 p
->seq_num
[1]=p
->seq_current
[1]+1;
622 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: wow, something very nasty happened in pnm_calc_stream\n");
627 * gets a stream chunk and writes it to a recieve buffer
630 static int pnm_get_stream_chunk(pnm_t
*p
) {
634 unsigned int fof1
, fof2
, stream
;
636 /* send a keepalive */
637 /* realplayer seems to do that every 43th package */
638 if (p
->packet
%43 == 42)
639 rm_write(p
->s
,&keepalive
,1);
641 /* data chunks begin with: 'Z' <o> <o> <i1> 'Z' <i2>
642 * where <o> is the offset to next stream chunk,
643 * <i1> is a 16 bit index
644 * <i2> is a 8 bit index which counts from 0x10 to somewhere
647 n
= rm_read (p
->s
, p
->buffer
, 8);
651 /* skip 8 bytes if 0x62 is read */
652 if (p
->buffer
[0] == 0x62)
654 n
= rm_read (p
->s
, p
->buffer
, 8);
657 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek 8 bytes on 0x62\n");
661 /* a server message */
662 if (p
->buffer
[0] == 'X')
664 int size
=AV_RB16(&p
->buffer
[1]);
666 rm_read (p
->s
, &p
->buffer
[8], size
-5);
668 mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: got message from server while reading stream:\n%s\n", &p
->buffer
[3]);
671 if (p
->buffer
[0] == 'F')
673 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: server error.\n");
677 /* skip bytewise to next chunk.
678 * seems, that we don't need that, if we send enough
682 while (p
->buffer
[0] != 0x5a) {
685 p
->buffer
[i
-1]=p
->buffer
[i
];
686 rm_read (p
->s
, &p
->buffer
[7], 1);
691 if (n
) mp_msg(MSGT_OPEN
, MSGL_WARN
, "input_pnm: had to seek %i bytes to next chunk\n", n
);
695 if (p
->buffer
[0] != 0x5a || p
->buffer
[7] != 0x5a)
697 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: bad boundaries\n");
698 hexdump(p
->buffer
, 8);
703 fof1
=AV_RB16(&p
->buffer
[1]);
704 fof2
=AV_RB16(&p
->buffer
[3]);
707 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: frame offsets are different: 0x%04x 0x%04x\n",fof1
,fof2
);
711 /* get first index */
712 p
->seq_current
[0]=AV_RB16(&p
->buffer
[5]);
714 /* now read the rest of stream chunk */
715 n
= rm_read (p
->s
, &p
->recv
[5], fof1
-5);
716 if (n
<fof1
-5) return 0;
718 /* get second index */
719 p
->seq_current
[1]=p
->recv
[5];
722 p
->ts_current
=AV_RB32(&p
->recv
[6]);
724 /* get stream number */
725 stream
=pnm_calc_stream(p
);
727 /* saving timestamp */
728 p
->ts_last
[stream
]=p
->ts_current
;
730 /* constructing a data packet header */
732 p
->recv
[0]=0; /* object version */
735 AV_WB16(&p
->recv
[2], fof2
); /* length */
737 p
->recv
[4]=0; /* stream number */
740 p
->recv
[10] &= 0xfe; /* streambox seems to do that... */
749 // pnm_t *pnm_connect(const char *mrl) {
750 static pnm_t
*pnm_connect(int fd
, char *path
) {
752 pnm_t
*p
=malloc(sizeof(pnm_t
));
755 p
->path
=strdup(path
);
758 pnm_send_request(p
,pnm_available_bandwidths
[10]);
759 if (!pnm_get_headers(p
, &need_response
)) {
760 mp_msg(MSGT_OPEN
, MSGL_ERR
, "input_pnm: failed to set up stream\n");
766 pnm_send_response(p
, pnm_response
);
770 /* copy header to recv */
772 memcpy(p
->recv
, p
->header
, p
->header_len
);
773 p
->recv_size
= p
->header_len
;
779 static int pnm_read (pnm_t
*this, char *data
, int len
) {
783 char *source
=this->recv
+ this->recv_read
;
784 int fill
=this->recv_size
- this->recv_read
;
787 if (len
< 0) return 0;
788 while (to_copy
> fill
) {
790 memcpy(dest
, source
, fill
);
795 if ((retval
= pnm_get_stream_chunk (this)) <= 0) {
797 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d of %d bytes provided\n", len
-to_copy
, len
);
805 fill
= this->recv_size
- this->recv_read
;
808 memcpy(dest
, source
, to_copy
);
809 this->recv_read
+= to_copy
;
812 mp_msg(MSGT_OPEN
, MSGL_INFO
, "input_pnm: %d bytes provided\n", len
);
818 static int pnm_streaming_read( int fd
, char *buffer
, int size
, streaming_ctrl_t
*stream_ctrl
) {
819 return pnm_read(stream_ctrl
->data
, buffer
, size
);
822 static int open_s(stream_t
*stream
,int mode
, void* opts
, int* file_format
) {
827 mp_msg(MSGT_OPEN
, MSGL_INFO
, "STREAM_PNM, URL: %s\n", stream
->url
);
828 stream
->streaming_ctrl
= streaming_ctrl_new();
829 if(stream
->streaming_ctrl
==NULL
)
832 stream
->streaming_ctrl
->bandwidth
= network_bandwidth
;
833 url
= url_new(stream
->url
);
834 stream
->streaming_ctrl
->url
= check4proxies(url
);
837 fd
= connect2Server( stream
->streaming_ctrl
->url
->hostname
,
838 stream
->streaming_ctrl
->url
->port
? stream
->streaming_ctrl
->url
->port
: 7070,1 );
843 pnm
= pnm_connect(fd
,stream
->streaming_ctrl
->url
->file
);
846 stream
->type
= STREAMTYPE_STREAM
;
848 stream
->streaming_ctrl
->data
=pnm
;
849 stream
->streaming_ctrl
->streaming_read
= pnm_streaming_read
;
850 //stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
851 stream
->streaming_ctrl
->prebuffer_size
= 8*1024; // 8 KBytes
852 stream
->streaming_ctrl
->buffering
= 1;
853 stream
->streaming_ctrl
->status
= streaming_playing_e
;
854 *file_format
= DEMUXER_TYPE_REAL
;
855 fixup_network_stream_cache(stream
);
859 streaming_ctrl_free(stream
->streaming_ctrl
);
860 stream
->streaming_ctrl
= NULL
;
861 return STREAM_UNSUPPORTED
;
865 const stream_info_t stream_info_pnm
= {
871 {"pnm", NULL
}, //pnm as fallback
873 0 // Urls are an option string