comparison stream/asf_streaming.c @ 19271:64d82a45a05d

introduce new 'stream' directory for all stream layer related components and split them from libmpdemux
author ben
date Mon, 31 Jul 2006 17:39:17 +0000
parents libmpdemux/asf_streaming.c@83c3afeab35d
children ab8d6b6deb63
comparison
equal deleted inserted replaced
19270:7d39b911f0bd 19271:64d82a45a05d
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <limits.h>
7
8 #include "config.h"
9 #include "mp_msg.h"
10 #include "help_mp.h"
11
12 #ifndef HAVE_WINSOCK2
13 #define closesocket close
14 #else
15 #include <winsock2.h>
16 #endif
17
18 #include "url.h"
19 #include "http.h"
20 #include "asf.h"
21
22 #include "stream.h"
23 #include "demuxer.h"
24
25 #include "network.h"
26
27 #ifdef ARCH_X86
28 #define ASF_LOAD_GUID_PREFIX(guid) (*(uint32_t *)(guid))
29 #else
30 #define ASF_LOAD_GUID_PREFIX(guid) \
31 ((guid)[3] << 24 | (guid)[2] << 16 | (guid)[1] << 8 | (guid)[0])
32 #endif
33
34 extern int network_bandwidth;
35
36 int asf_mmst_streaming_start( stream_t *stream );
37 static int asf_http_streaming_start(stream_t *stream, int *demuxer_type);
38
39 // We can try several protocol for asf streaming
40 // * first the UDP protcol, if there is a firewall, UDP
41 // packets will not come back, so the mmsu will fail.
42 // * Then we can try TCP, but if there is a proxy for
43 // internet connection, the TCP connection will not get
44 // through
45 // * Then we can try HTTP.
46 //
47 // Note: Using WMP sequence MMSU then MMST and then HTTP.
48
49 static int asf_streaming_start( stream_t *stream, int *demuxer_type) {
50 char *proto = stream->streaming_ctrl->url->protocol;
51 int fd = -1;
52 int port = stream->streaming_ctrl->url->port;
53
54 // Is protocol mms or mmsu?
55 if (!strcasecmp(proto, "mmsu") || !strcasecmp(proto, "mms"))
56 {
57 mp_msg(MSGT_NETWORK,MSGL_V,"Trying ASF/UDP...\n");
58 //fd = asf_mmsu_streaming_start( stream );
59 if( fd>-1 ) return fd; //mmsu support is not implemented yet - using this code
60 mp_msg(MSGT_NETWORK,MSGL_V," ===> ASF/UDP failed\n");
61 if( fd==-2 ) return -1;
62 }
63
64 //Is protocol mms or mmst?
65 if (!strcasecmp(proto, "mmst") || !strcasecmp(proto, "mms"))
66 {
67 mp_msg(MSGT_NETWORK,MSGL_V,"Trying ASF/TCP...\n");
68 fd = asf_mmst_streaming_start( stream );
69 stream->streaming_ctrl->url->port = port;
70 if( fd>-1 ) return fd;
71 mp_msg(MSGT_NETWORK,MSGL_V," ===> ASF/TCP failed\n");
72 if( fd==-2 ) return -1;
73 }
74
75 //Is protocol http, http_proxy, or mms?
76 if (!strcasecmp(proto, "http_proxy") || !strcasecmp(proto, "http") ||
77 !strcasecmp(proto, "mms") || !strcasecmp(proto, "mmshttp"))
78 {
79 mp_msg(MSGT_NETWORK,MSGL_V,"Trying ASF/HTTP...\n");
80 fd = asf_http_streaming_start( stream, demuxer_type );
81 stream->streaming_ctrl->url->port = port;
82 if( fd>-1 ) return fd;
83 mp_msg(MSGT_NETWORK,MSGL_V," ===> ASF/HTTP failed\n");
84 if( fd==-2 ) return -1;
85 }
86
87 //everything failed
88 return -1;
89 }
90
91 static int asf_streaming(ASF_stream_chunck_t *stream_chunck, int *drop_packet ) {
92 /*
93 printf("ASF stream chunck size=%d\n", stream_chunck->size);
94 printf("length: %d\n", length );
95 printf("0x%02X\n", stream_chunck->type );
96 */
97 if( drop_packet!=NULL ) *drop_packet = 0;
98
99 if( stream_chunck->size<8 ) {
100 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_StreamChunkSize2Small, stream_chunck->size);
101 return -1;
102 }
103 if( stream_chunck->size!=stream_chunck->size_confirm ) {
104 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_SizeConfirmMismatch, stream_chunck->size, stream_chunck->size_confirm);
105 return -1;
106 }
107 /*
108 printf(" type: 0x%02X\n", stream_chunck->type );
109 printf(" size: %d (0x%02X)\n", stream_chunck->size, stream_chunck->size );
110 printf(" sequence_number: 0x%04X\n", stream_chunck->sequence_number );
111 printf(" unknown: 0x%02X\n", stream_chunck->unknown );
112 printf(" size_confirm: 0x%02X\n", stream_chunck->size_confirm );
113 */
114 switch(stream_chunck->type) {
115 case ASF_STREAMING_CLEAR: // $C Clear ASF configuration
116 mp_msg(MSGT_NETWORK,MSGL_V,"=====> Clearing ASF stream configuration!\n");
117 if( drop_packet!=NULL ) *drop_packet = 1;
118 return stream_chunck->size;
119 break;
120 case ASF_STREAMING_DATA: // $D Data follows
121 // printf("=====> Data follows\n");
122 break;
123 case ASF_STREAMING_END_TRANS: // $E Transfer complete
124 mp_msg(MSGT_NETWORK,MSGL_V,"=====> Transfer complete\n");
125 if( drop_packet!=NULL ) *drop_packet = 1;
126 return stream_chunck->size;
127 break;
128 case ASF_STREAMING_HEADER: // $H ASF header chunk follows
129 mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF header chunk follows\n");
130 break;
131 default:
132 mp_msg(MSGT_NETWORK,MSGL_V,"=====> Unknown stream type 0x%x\n", stream_chunck->type );
133 }
134 return stream_chunck->size+4;
135 }
136
137 extern int find_asf_guid(char *buf, const char *guid, int cur_pos, int buf_len);
138 extern const char asf_file_header_guid[];
139 extern const char asf_stream_header_guid[];
140 extern const char asf_stream_group_guid[];
141 extern int audio_id;
142 extern int video_id;
143
144 static void close_s(stream_t *stream) {
145 close(stream->fd);
146 stream->fd=-1;
147 }
148
149 static int max_idx(int s_count, int *s_rates, int bound) {
150 int i, best = -1, rate = -1;
151 for (i = 0; i < s_count; i++) {
152 if (s_rates[i] > rate && s_rates[i] <= bound) {
153 rate = s_rates[i];
154 best = i;
155 }
156 }
157 return best;
158 }
159
160 static int asf_streaming_parse_header(int fd, streaming_ctrl_t* streaming_ctrl) {
161 ASF_header_t asfh;
162 ASF_stream_chunck_t chunk;
163 asf_http_streaming_ctrl_t* asf_ctrl = (asf_http_streaming_ctrl_t*) streaming_ctrl->data;
164 char* buffer=NULL, *chunk_buffer=NULL;
165 int i,r,size,pos = 0;
166 int start;
167 int buffer_size = 0;
168 int chunk_size2read = 0;
169 int bw = streaming_ctrl->bandwidth;
170 int *v_rates = NULL, *a_rates = NULL;
171 int v_rate = 0, a_rate = 0, a_idx = -1, v_idx = -1;
172
173 if(asf_ctrl == NULL) return -1;
174
175 // The ASF header can be in several network chunks. For example if the content description
176 // is big, the ASF header will be split in 2 network chunk.
177 // So we need to retrieve all the chunk before starting to parse the header.
178 do {
179 for( r=0; r < (int)sizeof(ASF_stream_chunck_t) ; ) {
180 i = nop_streaming_read(fd,((char*)&chunk)+r,sizeof(ASF_stream_chunck_t) - r,streaming_ctrl);
181 if(i <= 0) return -1;
182 r += i;
183 }
184 // Endian handling of the stream chunk
185 le2me_ASF_stream_chunck_t(&chunk);
186 size = asf_streaming( &chunk, &r) - sizeof(ASF_stream_chunck_t);
187 if(r) mp_msg(MSGT_NETWORK,MSGL_WARN,MSGTR_MPDEMUX_ASF_WarnDropHeader);
188 if(size < 0){
189 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ErrorParsingChunkHeader);
190 return -1;
191 }
192 if (chunk.type != ASF_STREAMING_HEADER) {
193 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_NoHeaderAtFirstChunk);
194 return -1;
195 }
196
197 // audit: do not overflow buffer_size
198 if (size > SIZE_MAX - buffer_size) return -1;
199 buffer = (char*) malloc(size+buffer_size);
200 if(buffer == NULL) {
201 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MPDEMUX_ASF_BufferMallocFailed,size+buffer_size);
202 return -1;
203 }
204 if( chunk_buffer!=NULL ) {
205 memcpy( buffer, chunk_buffer, buffer_size );
206 free( chunk_buffer );
207 }
208 chunk_buffer = buffer;
209 buffer += buffer_size;
210 buffer_size += size;
211
212 for(r = 0; r < size;) {
213 i = nop_streaming_read(fd,buffer+r,size-r,streaming_ctrl);
214 if(i < 0) {
215 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ErrReadingNetworkStream);
216 return -1;
217 }
218 r += i;
219 }
220
221 if( chunk_size2read==0 ) {
222 if(size < (int)sizeof(asfh)) {
223 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ErrChunk2Small);
224 return -1;
225 } else mp_msg(MSGT_NETWORK,MSGL_DBG2,"Got chunk\n");
226 memcpy(&asfh,buffer,sizeof(asfh));
227 le2me_ASF_header_t(&asfh);
228 chunk_size2read = asfh.objh.size;
229 mp_msg(MSGT_NETWORK,MSGL_DBG2,"Size 2 read=%d\n", chunk_size2read);
230 }
231 } while( buffer_size<chunk_size2read);
232 buffer = chunk_buffer;
233 size = buffer_size;
234
235 if(asfh.cno > 256) {
236 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ErrSubChunkNumberInvalid);
237 return -1;
238 }
239
240 start = sizeof(asfh);
241
242 pos = find_asf_guid(buffer, asf_file_header_guid, start, size);
243 if (pos >= 0) {
244 ASF_file_header_t *fileh = (ASF_file_header_t *) &buffer[pos];
245 pos += sizeof(ASF_file_header_t);
246 if (pos > size) goto len_err_out;
247 le2me_ASF_file_header_t(fileh);
248 /*
249 if(fileh.packetsize != fileh.packetsize2) {
250 printf("Error packetsize check don't match\n");
251 return -1;
252 }
253 */
254 asf_ctrl->packet_size = fileh->max_packet_size;
255 // before playing.
256 // preroll: time in ms to bufferize before playing
257 streaming_ctrl->prebuffer_size = (unsigned int)(((double)fileh->preroll/1000.0)*((double)fileh->max_bitrate/8.0));
258 }
259
260 pos = start;
261 while ((pos = find_asf_guid(buffer, asf_stream_header_guid, pos, size)) >= 0)
262 {
263 ASF_stream_header_t *streamh = (ASF_stream_header_t *)&buffer[pos];
264 pos += sizeof(ASF_stream_header_t);
265 if (pos > size) goto len_err_out;
266 le2me_ASF_stream_header_t(streamh);
267 switch(ASF_LOAD_GUID_PREFIX(streamh->type)) {
268 case 0xF8699E40 : // audio stream
269 if(asf_ctrl->audio_streams == NULL){
270 asf_ctrl->audio_streams = malloc(sizeof(int));
271 asf_ctrl->n_audio = 1;
272 } else {
273 asf_ctrl->n_audio++;
274 asf_ctrl->audio_streams = (int*)realloc(asf_ctrl->audio_streams,
275 asf_ctrl->n_audio*sizeof(int));
276 }
277 asf_ctrl->audio_streams[asf_ctrl->n_audio-1] = streamh->stream_no;
278 break;
279 case 0xBC19EFC0 : // video stream
280 if(asf_ctrl->video_streams == NULL){
281 asf_ctrl->video_streams = malloc(sizeof(int));
282 asf_ctrl->n_video = 1;
283 } else {
284 asf_ctrl->n_video++;
285 asf_ctrl->video_streams = (int*)realloc(asf_ctrl->video_streams,
286 asf_ctrl->n_video*sizeof(int));
287 }
288 asf_ctrl->video_streams[asf_ctrl->n_video-1] = streamh->stream_no;
289 break;
290 }
291 }
292
293 // always allocate to avoid lots of ifs later
294 v_rates = calloc(asf_ctrl->n_video, sizeof(int));
295 a_rates = calloc(asf_ctrl->n_audio, sizeof(int));
296
297 pos = find_asf_guid(buffer, asf_stream_group_guid, start, size);
298 if (pos >= 0) {
299 // stream bitrate properties object
300 int stream_count;
301 char *ptr = &buffer[pos];
302
303 mp_msg(MSGT_NETWORK, MSGL_V, "Stream bitrate properties object\n");
304 stream_count = le2me_16(*(uint16_t*)ptr);
305 ptr += sizeof(uint16_t);
306 if (ptr > &buffer[size]) goto len_err_out;
307 mp_msg(MSGT_NETWORK, MSGL_V, " stream count=[0x%x][%u]\n",
308 stream_count, stream_count );
309 for( i=0 ; i<stream_count ; i++ ) {
310 uint32_t rate;
311 int id;
312 int j;
313 id = le2me_16(*(uint16_t*)ptr);
314 ptr += sizeof(uint16_t);
315 if (ptr > &buffer[size]) goto len_err_out;
316 memcpy(&rate, ptr, sizeof(uint32_t));// workaround unaligment bug on sparc
317 ptr += sizeof(uint32_t);
318 if (ptr > &buffer[size]) goto len_err_out;
319 rate = le2me_32(rate);
320 mp_msg(MSGT_NETWORK, MSGL_V,
321 " stream id=[0x%x][%u]\n", id, id);
322 mp_msg(MSGT_NETWORK, MSGL_V,
323 " max bitrate=[0x%x][%u]\n", rate, rate);
324 for (j = 0; j < asf_ctrl->n_video; j++) {
325 if (id == asf_ctrl->video_streams[j]) {
326 mp_msg(MSGT_NETWORK, MSGL_V, " is video stream\n");
327 v_rates[j] = rate;
328 break;
329 }
330 }
331 for (j = 0; j < asf_ctrl->n_audio; j++) {
332 if (id == asf_ctrl->audio_streams[j]) {
333 mp_msg(MSGT_NETWORK, MSGL_V, " is audio stream\n");
334 a_rates[j] = rate;
335 break;
336 }
337 }
338 }
339 }
340 free(buffer);
341
342 // automatic stream selection based on bandwidth
343 if (bw == 0) bw = INT_MAX;
344 mp_msg(MSGT_NETWORK, MSGL_V, "Max bandwidth set to %d\n", bw);
345
346 if (asf_ctrl->n_audio) {
347 // find lowest-bitrate audio stream
348 a_rate = a_rates[0];
349 a_idx = 0;
350 for (i = 0; i < asf_ctrl->n_audio; i++) {
351 if (a_rates[i] < a_rate) {
352 a_rate = a_rates[i];
353 a_idx = i;
354 }
355 }
356 if (max_idx(asf_ctrl->n_video, v_rates, bw - a_rate) < 0) {
357 // both audio and video are not possible, try video only next
358 a_idx = -1;
359 a_rate = 0;
360 }
361 }
362 // find best video stream
363 v_idx = max_idx(asf_ctrl->n_video, v_rates, bw - a_rate);
364 if (v_idx >= 0)
365 v_rate = v_rates[v_idx];
366
367 // find best audio stream
368 a_idx = max_idx(asf_ctrl->n_audio, a_rates, bw - v_rate);
369
370 free(v_rates);
371 free(a_rates);
372
373 if (a_idx < 0 && v_idx < 0) {
374 mp_msg(MSGT_NETWORK, MSGL_FATAL, MSGTR_MPDEMUX_ASF_Bandwidth2SmallCannotPlay);
375 return -1;
376 }
377
378 if (audio_id > 0)
379 // a audio stream was forced
380 asf_ctrl->audio_id = audio_id;
381 else if (a_idx >= 0)
382 asf_ctrl->audio_id = asf_ctrl->audio_streams[a_idx];
383 else if (asf_ctrl->n_audio) {
384 mp_msg(MSGT_NETWORK, MSGL_WARN, MSGTR_MPDEMUX_ASF_Bandwidth2SmallDeselectedAudio);
385 audio_id = -2;
386 }
387
388 if (video_id > 0)
389 // a video stream was forced
390 asf_ctrl->video_id = video_id;
391 else if (v_idx >= 0)
392 asf_ctrl->video_id = asf_ctrl->video_streams[v_idx];
393 else if (asf_ctrl->n_video) {
394 mp_msg(MSGT_NETWORK, MSGL_WARN, MSGTR_MPDEMUX_ASF_Bandwidth2SmallDeselectedVideo);
395 video_id = -2;
396 }
397
398 return 1;
399
400 len_err_out:
401 mp_msg(MSGT_NETWORK, MSGL_FATAL, MSGTR_MPDEMUX_ASF_InvalidLenInHeader);
402 if (buffer) free(buffer);
403 if (v_rates) free(v_rates);
404 if (a_rates) free(a_rates);
405 return -1;
406 }
407
408 static int asf_http_streaming_read( int fd, char *buffer, int size, streaming_ctrl_t *streaming_ctrl ) {
409 static ASF_stream_chunck_t chunk;
410 int read,chunk_size = 0;
411 static int rest = 0, drop_chunk = 0, waiting = 0;
412 asf_http_streaming_ctrl_t *asf_http_ctrl = (asf_http_streaming_ctrl_t*)streaming_ctrl->data;
413
414 while(1) {
415 if (rest == 0 && waiting == 0) {
416 read = 0;
417 while(read < (int)sizeof(ASF_stream_chunck_t)){
418 int r = nop_streaming_read( fd, ((char*)&chunk) + read,
419 sizeof(ASF_stream_chunck_t)-read,
420 streaming_ctrl );
421 if(r <= 0){
422 if( r < 0)
423 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ErrReadingChunkHeader);
424 return -1;
425 }
426 read += r;
427 }
428
429 // Endian handling of the stream chunk
430 le2me_ASF_stream_chunck_t(&chunk);
431 chunk_size = asf_streaming( &chunk, &drop_chunk );
432 if(chunk_size < 0) {
433 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ErrorParsingChunkHeader);
434 return -1;
435 }
436 chunk_size -= sizeof(ASF_stream_chunck_t);
437
438 if(chunk.type != ASF_STREAMING_HEADER && (!drop_chunk)) {
439 if (asf_http_ctrl->packet_size < chunk_size) {
440 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ErrChunkBiggerThanPacket);
441 return -1;
442 }
443 waiting = asf_http_ctrl->packet_size;
444 } else {
445 waiting = chunk_size;
446 }
447
448 } else if (rest){
449 chunk_size = rest;
450 rest = 0;
451 }
452
453 read = 0;
454 if ( waiting >= chunk_size) {
455 if (chunk_size > size){
456 rest = chunk_size - size;
457 chunk_size = size;
458 }
459 while(read < chunk_size) {
460 int got = nop_streaming_read( fd,buffer+read,chunk_size-read,streaming_ctrl );
461 if(got <= 0) {
462 if(got < 0)
463 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ErrReadingChunk);
464 return -1;
465 }
466 read += got;
467 }
468 waiting -= read;
469 if (drop_chunk) continue;
470 }
471 if (rest == 0 && waiting > 0 && size-read > 0) {
472 int s = MIN(waiting,size-read);
473 memset(buffer+read,0,s);
474 waiting -= s;
475 read += s;
476 }
477 break;
478 }
479
480 return read;
481 }
482
483 static int asf_http_streaming_seek( int fd, off_t pos, streaming_ctrl_t *streaming_ctrl ) {
484 return -1;
485 // to shut up gcc warning
486 fd++;
487 pos++;
488 streaming_ctrl=NULL;
489 }
490
491 static int asf_header_check( HTTP_header_t *http_hdr ) {
492 ASF_obj_header_t *objh;
493 if( http_hdr==NULL ) return -1;
494 if( http_hdr->body==NULL || http_hdr->body_size<sizeof(ASF_obj_header_t) ) return -1;
495
496 objh = (ASF_obj_header_t*)http_hdr->body;
497 if( ASF_LOAD_GUID_PREFIX(objh->guid)==0x75B22630 ) return 0;
498 return -1;
499 }
500
501 static int asf_http_streaming_type(char *content_type, char *features, HTTP_header_t *http_hdr ) {
502 if( content_type==NULL ) return ASF_Unknown_e;
503 if( !strcasecmp(content_type, "application/octet-stream") ||
504 !strcasecmp(content_type, "application/vnd.ms.wms-hdr.asfv1") || // New in Corona, first request
505 !strcasecmp(content_type, "application/x-mms-framed") || // New in Corana, second request
506 !strcasecmp(content_type, "video/x-ms-asf")) {
507
508 if( strstr(features, "broadcast") ) {
509 mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Live stream\n");
510 return ASF_Live_e;
511 } else {
512 mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Prerecorded\n");
513 return ASF_Prerecorded_e;
514 }
515 } else {
516 // Ok in a perfect world, web servers should be well configured
517 // so we could used mime type to know the stream type,
518 // but guess what? All of them are not well configured.
519 // So we have to check for an asf header :(, but it works :p
520 if( http_hdr->body_size>sizeof(ASF_obj_header_t) ) {
521 if( asf_header_check( http_hdr )==0 ) {
522 mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Plain text\n");
523 return ASF_PlainText_e;
524 } else if( (!strcasecmp(content_type, "text/html")) ) {
525 mp_msg(MSGT_NETWORK,MSGL_V,"=====> HTML, MPlayer is not a browser...yet!\n");
526 return ASF_Unknown_e;
527 } else {
528 mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Redirector\n");
529 return ASF_Redirector_e;
530 }
531 } else {
532 if( (!strcasecmp(content_type, "audio/x-ms-wax")) ||
533 (!strcasecmp(content_type, "audio/x-ms-wma")) ||
534 (!strcasecmp(content_type, "video/x-ms-asf")) ||
535 (!strcasecmp(content_type, "video/x-ms-afs")) ||
536 (!strcasecmp(content_type, "video/x-ms-wvx")) ||
537 (!strcasecmp(content_type, "video/x-ms-wmv")) ||
538 (!strcasecmp(content_type, "video/x-ms-wma")) ) {
539 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ASFRedirector);
540 return ASF_Redirector_e;
541 } else if( !strcasecmp(content_type, "text/plain") ) {
542 mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF Plain text\n");
543 return ASF_PlainText_e;
544 } else {
545 mp_msg(MSGT_NETWORK,MSGL_V,"=====> ASF unknown content-type: %s\n", content_type );
546 return ASF_Unknown_e;
547 }
548 }
549 }
550 return ASF_Unknown_e;
551 }
552
553 static HTTP_header_t *asf_http_request(streaming_ctrl_t *streaming_ctrl) {
554 HTTP_header_t *http_hdr;
555 URL_t *url = NULL;
556 URL_t *server_url = NULL;
557 asf_http_streaming_ctrl_t *asf_http_ctrl;
558 char str[250];
559 char *ptr;
560 int i, enable;
561
562 int offset_hi=0, offset_lo=0, length=0;
563 int asf_nb_stream=0, stream_id;
564
565 // Sanity check
566 if( streaming_ctrl==NULL ) return NULL;
567 url = streaming_ctrl->url;
568 asf_http_ctrl = (asf_http_streaming_ctrl_t*)streaming_ctrl->data;
569 if( url==NULL || asf_http_ctrl==NULL ) return NULL;
570
571 // Common header for all requests.
572 http_hdr = http_new_header();
573 http_set_field( http_hdr, "Accept: */*" );
574 http_set_field( http_hdr, "User-Agent: NSPlayer/4.1.0.3856" );
575 http_add_basic_authentication( http_hdr, url->username, url->password );
576
577 // Check if we are using a proxy
578 if( !strcasecmp( url->protocol, "http_proxy" ) ) {
579 server_url = url_new( (url->file)+1 );
580 if( server_url==NULL ) {
581 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_InvalidProxyURL);
582 http_free( http_hdr );
583 return NULL;
584 }
585 http_set_uri( http_hdr, server_url->url );
586 sprintf( str, "Host: %.220s:%d", server_url->hostname, server_url->port );
587 url_free( server_url );
588 } else {
589 http_set_uri( http_hdr, url->file );
590 sprintf( str, "Host: %.220s:%d", url->hostname, url->port );
591 }
592
593 http_set_field( http_hdr, str );
594 http_set_field( http_hdr, "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}" );
595 sprintf(str,
596 "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=%u",
597 offset_hi, offset_lo, asf_http_ctrl->request, length );
598 http_set_field( http_hdr, str );
599
600 switch( asf_http_ctrl->streaming_type ) {
601 case ASF_Live_e:
602 case ASF_Prerecorded_e:
603 http_set_field( http_hdr, "Pragma: xPlayStrm=1" );
604 ptr = str;
605 ptr += sprintf( ptr, "Pragma: stream-switch-entry=");
606 if(asf_http_ctrl->n_audio > 0) {
607 for( i=0; i<asf_http_ctrl->n_audio ; i++ ) {
608 stream_id = asf_http_ctrl->audio_streams[i];
609 if(stream_id == asf_http_ctrl->audio_id) {
610 enable = 0;
611 } else {
612 enable = 2;
613 continue;
614 }
615 asf_nb_stream++;
616 ptr += sprintf(ptr, "ffff:%d:%d ", stream_id, enable);
617 }
618 }
619 if(asf_http_ctrl->n_video > 0) {
620 for( i=0; i<asf_http_ctrl->n_video ; i++ ) {
621 stream_id = asf_http_ctrl->video_streams[i];
622 if(stream_id == asf_http_ctrl->video_id) {
623 enable = 0;
624 } else {
625 enable = 2;
626 continue;
627 }
628 asf_nb_stream++;
629 ptr += sprintf(ptr, "ffff:%d:%d ", stream_id, enable);
630 }
631 }
632 http_set_field( http_hdr, str );
633 sprintf( str, "Pragma: stream-switch-count=%d", asf_nb_stream );
634 http_set_field( http_hdr, str );
635 break;
636 case ASF_Redirector_e:
637 break;
638 case ASF_Unknown_e:
639 // First request goes here.
640 break;
641 default:
642 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_UnknownASFStreamType);
643 }
644
645 http_set_field( http_hdr, "Connection: Close" );
646 http_build_request( http_hdr );
647
648 return http_hdr;
649 }
650
651 static int asf_http_parse_response(asf_http_streaming_ctrl_t *asf_http_ctrl, HTTP_header_t *http_hdr ) {
652 char *content_type, *pragma;
653 char features[64] = "\0";
654 size_t len;
655 if( http_response_parse(http_hdr)<0 ) {
656 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_Failed2ParseHTTPResponse);
657 return -1;
658 }
659 switch( http_hdr->status_code ) {
660 case 200:
661 break;
662 case 401: // Authentication required
663 return ASF_Authenticate_e;
664 default:
665 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_ServerReturn, http_hdr->status_code, http_hdr->reason_phrase);
666 return -1;
667 }
668
669 content_type = http_get_field( http_hdr, "Content-Type");
670 //printf("Content-Type: [%s]\n", content_type);
671
672 pragma = http_get_field( http_hdr, "Pragma");
673 while( pragma!=NULL ) {
674 char *comma_ptr=NULL;
675 char *end;
676 //printf("Pragma: [%s]\n", pragma );
677 // The pragma line can get severals attributes
678 // separeted with a comma ','.
679 do {
680 if( !strncasecmp( pragma, "features=", 9) ) {
681 pragma += 9;
682 end = strstr( pragma, "," );
683 if( end==NULL ) {
684 len = strlen(pragma);
685 } else {
686 len = (unsigned int)(end-pragma);
687 }
688 if(len > sizeof(features) - 1) {
689 mp_msg(MSGT_NETWORK,MSGL_WARN,MSGTR_MPDEMUX_ASF_ASFHTTPParseWarnCuttedPragma,pragma,len,sizeof(features) - 1);
690 len = sizeof(features) - 1;
691 }
692 strncpy( features, pragma, len );
693 features[len]='\0';
694 break;
695 }
696 comma_ptr = strstr( pragma, "," );
697 if( comma_ptr!=NULL ) {
698 pragma = comma_ptr+1;
699 if( pragma[0]==' ' ) pragma++;
700 }
701 } while( comma_ptr!=NULL );
702 pragma = http_get_next_field( http_hdr );
703 }
704 asf_http_ctrl->streaming_type = asf_http_streaming_type( content_type, features, http_hdr );
705 return 0;
706 }
707
708 static int asf_http_streaming_start( stream_t *stream, int *demuxer_type ) {
709 HTTP_header_t *http_hdr=NULL;
710 URL_t *url = stream->streaming_ctrl->url;
711 asf_http_streaming_ctrl_t *asf_http_ctrl;
712 char buffer[BUFFER_SIZE];
713 int i, ret;
714 int fd = stream->fd;
715 int done;
716 int auth_retry = 0;
717
718 asf_http_ctrl = malloc(sizeof(asf_http_streaming_ctrl_t));
719 if( asf_http_ctrl==NULL ) {
720 mp_msg(MSGT_NETWORK,MSGL_FATAL,MSGTR_MemAllocFailed);
721 return -1;
722 }
723 asf_http_ctrl->streaming_type = ASF_Unknown_e;
724 asf_http_ctrl->request = 1;
725 asf_http_ctrl->audio_streams = asf_http_ctrl->video_streams = NULL;
726 asf_http_ctrl->n_audio = asf_http_ctrl->n_video = 0;
727 stream->streaming_ctrl->data = (void*)asf_http_ctrl;
728
729 do {
730 done = 1;
731 if( fd>0 ) closesocket( fd );
732
733 if( !strcasecmp( url->protocol, "http_proxy" ) ) {
734 if( url->port==0 ) url->port = 8080;
735 } else {
736 if( url->port==0 ) url->port = 80;
737 }
738 fd = connect2Server( url->hostname, url->port, 1);
739 if( fd<0 ) return fd;
740
741 http_hdr = asf_http_request( stream->streaming_ctrl );
742 mp_msg(MSGT_NETWORK,MSGL_DBG2,"Request [%s]\n", http_hdr->buffer );
743 for(i=0; i < (int)http_hdr->buffer_size ; ) {
744 int r = send( fd, http_hdr->buffer+i, http_hdr->buffer_size-i, 0 );
745 if(r <0) {
746 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_SocketWriteError,strerror(errno));
747 return -1;
748 }
749 i += r;
750 }
751 http_free( http_hdr );
752 http_hdr = http_new_header();
753 do {
754 i = recv( fd, buffer, BUFFER_SIZE, 0 );
755 //printf("read: %d\n", i );
756 if( i<=0 ) {
757 perror("read");
758 http_free( http_hdr );
759 return -1;
760 }
761 http_response_append( http_hdr, buffer, i );
762 } while( !http_is_header_entire( http_hdr ) );
763 if( mp_msg_test(MSGT_NETWORK,MSGL_V) ) {
764 http_hdr->buffer[http_hdr->buffer_size]='\0';
765 mp_msg(MSGT_NETWORK,MSGL_DBG2,"Response [%s]\n", http_hdr->buffer );
766 }
767 ret = asf_http_parse_response(asf_http_ctrl, http_hdr);
768 if( ret<0 ) {
769 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_HeaderParseFailed);
770 http_free( http_hdr );
771 return -1;
772 }
773 switch( asf_http_ctrl->streaming_type ) {
774 case ASF_Live_e:
775 case ASF_Prerecorded_e:
776 case ASF_PlainText_e:
777 if( http_hdr->body_size>0 ) {
778 if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
779 http_free( http_hdr );
780 return -1;
781 }
782 }
783 if( asf_http_ctrl->request==1 ) {
784 if( asf_http_ctrl->streaming_type!=ASF_PlainText_e ) {
785 // First request, we only got the ASF header.
786 ret = asf_streaming_parse_header(fd,stream->streaming_ctrl);
787 if(ret < 0) return -1;
788 if(asf_http_ctrl->n_audio == 0 && asf_http_ctrl->n_video == 0) {
789 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_NoStreamFound);
790 return -1;
791 }
792 asf_http_ctrl->request++;
793 done = 0;
794 } else {
795 done = 1;
796 }
797 }
798 break;
799 case ASF_Redirector_e:
800 if( http_hdr->body_size>0 ) {
801 if( streaming_bufferize( stream->streaming_ctrl, http_hdr->body, http_hdr->body_size )<0 ) {
802 http_free( http_hdr );
803 return -1;
804 }
805 }
806 *demuxer_type = DEMUXER_TYPE_PLAYLIST;
807 done = 1;
808 break;
809 case ASF_Authenticate_e:
810 if( http_authenticate( http_hdr, url, &auth_retry)<0 ) return -1;
811 asf_http_ctrl->streaming_type = ASF_Unknown_e;
812 done = 0;
813 break;
814 case ASF_Unknown_e:
815 default:
816 mp_msg(MSGT_NETWORK,MSGL_ERR,MSGTR_MPDEMUX_ASF_UnknownASFStreamingType);
817 closesocket(fd);
818 http_free( http_hdr );
819 return -1;
820 }
821 // Check if we got a redirect.
822 } while(!done);
823
824 stream->fd = fd;
825 if( asf_http_ctrl->streaming_type==ASF_PlainText_e || asf_http_ctrl->streaming_type==ASF_Redirector_e ) {
826 stream->streaming_ctrl->streaming_read = nop_streaming_read;
827 stream->streaming_ctrl->streaming_seek = nop_streaming_seek;
828 } else {
829 stream->streaming_ctrl->streaming_read = asf_http_streaming_read;
830 stream->streaming_ctrl->streaming_seek = asf_http_streaming_seek;
831 stream->streaming_ctrl->buffering = 1;
832 }
833 stream->streaming_ctrl->status = streaming_playing_e;
834 stream->close = close_s;
835
836 http_free( http_hdr );
837 return 0;
838 }
839
840 static int open_s(stream_t *stream,int mode, void* opts, int* file_format) {
841 URL_t *url;
842
843 stream->streaming_ctrl = streaming_ctrl_new();
844 if( stream->streaming_ctrl==NULL ) {
845 return STREAM_ERROR;
846 }
847 stream->streaming_ctrl->bandwidth = network_bandwidth;
848 url = url_new(stream->url);
849 stream->streaming_ctrl->url = check4proxies(url);
850 url_free(url);
851
852 mp_msg(MSGT_OPEN, MSGL_INFO, MSGTR_MPDEMUX_ASF_InfoStreamASFURL, stream->url);
853 if((!strncmp(stream->url, "http", 4)) && (*file_format!=DEMUXER_TYPE_ASF && *file_format!=DEMUXER_TYPE_UNKNOWN)) {
854 streaming_ctrl_free(stream->streaming_ctrl);
855 stream->streaming_ctrl = NULL;
856 return STREAM_UNSUPORTED;
857 }
858
859 if(asf_streaming_start(stream, file_format) < 0) {
860 mp_msg(MSGT_OPEN, MSGL_ERR, MSGTR_MPDEMUX_ASF_StreamingFailed);
861 streaming_ctrl_free(stream->streaming_ctrl);
862 stream->streaming_ctrl = NULL;
863 return STREAM_UNSUPORTED;
864 }
865
866 *file_format = DEMUXER_TYPE_ASF;
867 stream->type = STREAMTYPE_STREAM;
868 fixup_network_stream_cache(stream);
869 return STREAM_OK;
870 }
871
872 stream_info_t stream_info_asf = {
873 "mms and mms over http streaming",
874 "null",
875 "Bertrand, Reimar Doeffinger, Albeu",
876 "originally based on work by Majormms (is that code still there?)",
877 open_s,
878 {"mms", "mmsu", "mmst", "http", "http_proxy", "mmshttp", NULL},
879 NULL,
880 0 // Urls are an option string
881 };
882