1 /*
2  * Copyright (C) 2002-2020 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
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.
10  *
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.
15  *
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, USA
19  *
20  * MMS over HTTP protocol
21  *   written by Thibaut Mattern
22  *   based on mms.c and specs from avifile
23  *   (http://avifile.sourceforge.net/asf-1.0.htm)
24  *
25  * TODO:
26  *   error messages
27  *   http support cleanup, find a way to share code with input_http.c (http.h|c)
28  *   http proxy support
29  */
30 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <stdlib.h>
39 
40 #define LOG_MODULE "mmsh"
41 #define LOG_VERBOSE
42 /*
43 #define LOG
44 */
45 #include <xine/xine_internal.h>
46 #include <xine/xineutils.h>
47 
48 #include "bswap.h"
49 #include "http_helper.h"
50 #include "mmsh.h"
51 #include "../demuxers/asfheader.h"
52 
53 /* #define USERAGENT "User-Agent: NSPlayer/7.1.0.3055\r\n" */
54 #define USERAGENT "User-Agent: NSPlayer/4.1.0.3856\r\n"
55 #define CLIENTGUID "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"
56 
57 
58 #define MMSH_PORT                  80
59 #define MMSH_UNKNOWN                0
60 #define MMSH_SEEKABLE               1
61 #define MMSH_LIVE                   2
62 
63 #define CHUNK_HEADER_LENGTH         4
64 #define EXT_HEADER_LENGTH           8
65 #define CHUNK_TYPE_RESET       0x4324
66 #define CHUNK_TYPE_DATA        0x4424
67 #define CHUNK_TYPE_END         0x4524
68 #define CHUNK_TYPE_ASF_HEADER  0x4824
69 #define CHUNK_SIZE              65536  /* max chunk size */
70 #define ASF_HEADER_SIZE          8192  /* max header size */
71 
72 #define SCRATCH_SIZE             1024
73 
74 #define mmsh_FirstRequest \
75     "GET %s HTTP/1.0\r\n" \
76     "Accept: */*\r\n" \
77     USERAGENT \
78     "Host: %s:%d\r\n" \
79     "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%u,max-duration=0\r\n" \
80     CLIENTGUID \
81     "Connection: Close\r\n\r\n"
82 
83 #define mmsh_SeekableRequest \
84     "GET %s HTTP/1.0\r\n" \
85     "Accept: */*\r\n" \
86     USERAGENT \
87     "Host: %s:%d\r\n" \
88     "Pragma: no-cache,rate=1.000000,stream-time=%u,stream-offset=%u:%u,request-context=%u,max-duration=%u\r\n" \
89     CLIENTGUID \
90     "Pragma: xPlayStrm=1\r\n" \
91     "Pragma: stream-switch-count=%d\r\n" \
92     "Pragma: stream-switch-entry=%s\r\n" /*  ffff:1:0 ffff:2:0 */ \
93     "Connection: Close\r\n\r\n"
94 
95 #define mmsh_LiveRequest \
96     "GET %s HTTP/1.0\r\n" \
97     "Accept: */*\r\n" \
98     USERAGENT \
99     "Host: %s:%d\r\n" \
100     "Pragma: no-cache,rate=1.000000,request-context=%u\r\n" \
101     "Pragma: xPlayStrm=1\r\n" \
102     CLIENTGUID \
103     "Pragma: stream-switch-count=%d\r\n" \
104     "Pragma: stream-switch-entry=%s\r\n" \
105     "Connection: Close\r\n\r\n"
106 
107 /* Unused requests */
108 #if 0
109 #define mmsh_PostRequest \
110     "POST %s HTTP/1.0\r\n" \
111     "Accept: */*\r\n" \
112     USERAGENT \
113     "Host: %s\r\n" \
114     "Pragma: client-id=%u\r\n" \
115 /*    "Pragma: log-line=no-cache,rate=1.000000,stream-time=%u,stream-offset=%u:%u,request-context=2,max-duration=%u\r\n"
116  */ \
117     "Pragma: Content-Length: 0\r\n" \
118     CLIENTGUID \
119     "\r\n"
120 
121 #define mmsh_RangeRequest \
122     "GET %s HTTP/1.0\r\n" \
123     "Accept: */*\r\n" \
124     USERAGENT \
125     "Host: %s:%d\r\n" \
126     "Range: bytes=%Lu-\r\n" \
127     CLIENTGUID \
128     "Connection: Close\r\n\r\n"
129 #endif
130 
131 
132 /*
133  * mmsh specific types
134  */
135 
136 
137 struct mmsh_s {
138 
139   xine_stream_t *stream;
140 
141   int           s;
142 
143   /* url parsing */
144   xine_url_t    url;
145 
146   char          str[SCRATCH_SIZE]; /* scratch buffer to built strings */
147 
148   asf_header_t *asf_header;
149   int           stream_type;
150 
151   /* receive buffer */
152 
153   /* chunk */
154   uint16_t      chunk_type;
155   uint16_t      chunk_length;
156   uint16_t      chunk_seq_number;
157   uint8_t       buf[CHUNK_SIZE];
158 
159   int           buf_size;
160   int           buf_read;
161 
162   uint8_t       asf_header_buffer[ASF_HEADER_SIZE];
163   uint32_t      asf_header_len;
164   uint32_t      asf_header_read;
165   int           seq_num;
166 
167   int           video_stream;
168   int           audio_stream;
169 
170   off_t         current_pos;
171   int           user_bandwidth;
172 
173   int           playing;
174   unsigned int  start_time;
175 };
176 
send_command(mmsh_t * this,char * cmd)177 static int send_command (mmsh_t *this, char *cmd)  {
178   lprintf ("send_command:\n%s\n", cmd);
179 
180   const size_t length = strlen(cmd);
181   if ((size_t)_x_io_tcp_write (this->stream, this->s, cmd, length) != length) {
182     xprintf (this->stream->xine, XINE_LOG_MSG, _("libmmsh: send error\n"));
183     return 0;
184   }
185   return 1;
186 }
187 
get_answer(mmsh_t * this)188 static int get_answer (mmsh_t *this) {
189 
190   int done, len, linenum;
191   char *features;
192 
193   lprintf ("get_answer\n");
194 
195   done = 0; len = 0; linenum = 0;
196   this->stream_type = MMSH_UNKNOWN;
197 
198   while (!done) {
199 
200     if (_x_io_tcp_read(this->stream, this->s, (char*)&(this->buf[len]), 1) != 1) {
201       xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
202                "libmmsh: alert: end of stream\n");
203       return 0;
204     }
205 
206     if (this->buf[len] == '\012') {
207 
208       this->buf[len] = '\0';
209       len--;
210 
211       if ((len >= 0) && (this->buf[len] == '\015')) {
212         this->buf[len] = '\0';
213         len--;
214       }
215 
216       linenum++;
217 
218       lprintf ("answer: >%s<\n", this->buf);
219 
220       if (linenum == 1) {
221         int httpver, httpsub, httpcode;
222         char httpstatus[51];
223 
224         if (sscanf((char*)this->buf, "HTTP/%d.%d %d %50[^\015\012]", &httpver, &httpsub,
225             &httpcode, httpstatus) != 4) {
226           xine_log (this->stream->xine, XINE_LOG_MSG,
227                     _("libmmsh: bad response format\n"));
228           return 0;
229         }
230 
231         if (httpcode >= 300 && httpcode < 400) {
232           xine_log (this->stream->xine, XINE_LOG_MSG,
233                     _("libmmsh: 3xx redirection not implemented: >%d %s<\n"),
234                     httpcode, httpstatus);
235           return 0;
236         }
237 
238         if (httpcode < 200 || httpcode >= 300) {
239           xine_log (this->stream->xine, XINE_LOG_MSG,
240                     _("libmmsh: http status not 2xx: >%d %s<\n"),
241                     httpcode, httpstatus);
242           return 0;
243         }
244       } else {
245 
246         if (!strncasecmp((char*)this->buf, "Location: ", 10)) {
247           xine_log (this->stream->xine, XINE_LOG_MSG,
248                     _("libmmsh: Location redirection not implemented\n"));
249           return 0;
250         }
251 
252         if (!strncasecmp((char*)this->buf, "Pragma:", 7)) {
253           features = strstr((char*)(this->buf + 7), "features=");
254           if (features) {
255             if (strstr(features, "seekable")) {
256               lprintf("seekable stream\n");
257               this->stream_type = MMSH_SEEKABLE;
258             } else {
259               if (strstr(features, "broadcast")) {
260                 lprintf("live stream\n");
261                 this->stream_type = MMSH_LIVE;
262               }
263             }
264           }
265         }
266       }
267 
268       if (len == -1) {
269         done = 1;
270       } else {
271         len = 0;
272       }
273     } else {
274       len ++;
275     }
276   }
277   if (this->stream_type == MMSH_UNKNOWN) {
278     xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
279              "libmmsh: unknown stream type\n");
280     this->stream_type = MMSH_SEEKABLE; /* FIXME ? */
281   }
282   return 1;
283 }
284 
get_chunk_header(mmsh_t * this)285 static int get_chunk_header (mmsh_t *this) {
286   uint8_t chunk_header[CHUNK_HEADER_LENGTH];
287   uint8_t ext_header[EXT_HEADER_LENGTH];
288   int read_len;
289   int ext_header_len;
290 
291   lprintf ("get_chunk_header\n");
292 
293   /* read chunk header */
294   read_len = _x_io_tcp_read(this->stream, this->s, (char*)chunk_header, CHUNK_HEADER_LENGTH);
295   if (read_len != CHUNK_HEADER_LENGTH) {
296     xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
297              "libmmsh: chunk header read failed, %d != %d\n", read_len, CHUNK_HEADER_LENGTH);
298     return 0;
299   }
300   this->chunk_type       = _X_LE_16 (&chunk_header[0]);
301   this->chunk_length     = _X_LE_16 (&chunk_header[2]);
302 
303   switch (this->chunk_type) {
304     case CHUNK_TYPE_DATA:
305       ext_header_len = 8;
306       break;
307     case CHUNK_TYPE_END:
308       ext_header_len = 4;
309       break;
310     case CHUNK_TYPE_ASF_HEADER:
311       ext_header_len = 8;
312       break;
313     case CHUNK_TYPE_RESET:
314       ext_header_len = 4;
315       break;
316     default:
317       ext_header_len = 0;
318   }
319   /* read extended header */
320   if (ext_header_len > 0) {
321     read_len = _x_io_tcp_read(this->stream, this->s, (char*)ext_header, ext_header_len);
322     if (read_len != ext_header_len) {
323       xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
324                "extended header read failed, %d != %d\n", read_len, ext_header_len);
325       return 0;
326     }
327   }
328 
329   switch (this->chunk_type) {
330     case CHUNK_TYPE_DATA:
331       this->chunk_seq_number = _X_LE_32 (&ext_header[0]);
332       lprintf ("chunk type:       CHUNK_TYPE_DATA\n");
333       lprintf ("chunk length:     %d\n", this->chunk_length);
334       lprintf ("chunk seq:        %d\n", this->chunk_seq_number);
335       lprintf ("unknown:          %d\n", ext_header[4]);
336       lprintf ("mmsh seq:         %d\n", ext_header[5]);
337       lprintf ("len2:             %d\n", _X_LE_16(&ext_header[6]));
338       break;
339     case CHUNK_TYPE_END:
340       this->chunk_seq_number = _X_LE_32 (&ext_header[0]);
341       lprintf ("chunk type:       CHUNK_TYPE_END\n");
342       lprintf ("continue: %d\n", this->chunk_seq_number);
343       break;
344     case CHUNK_TYPE_ASF_HEADER:
345       lprintf ("chunk type:       CHUNK_TYPE_ASF_HEADER\n");
346       lprintf ("chunk length:     %d\n", this->chunk_length);
347       lprintf ("unknown:          %2X %2X %2X %2X %2X %2X\n",
348                ext_header[0], ext_header[1], ext_header[2], ext_header[3],
349                ext_header[4], ext_header[5]);
350       lprintf ("len2:             %d\n", _X_LE_16(&ext_header[6]));
351       break;
352     case CHUNK_TYPE_RESET:
353       lprintf ("chunk type:       CHUNK_TYPE_RESET\n");
354       lprintf ("chunk seq:        %d\n", this->chunk_seq_number);
355       lprintf ("unknown:          %2X %2X %2X %2X\n",
356                ext_header[0], ext_header[1], ext_header[2], ext_header[3]);
357       break;
358     default:
359       lprintf ("unknown chunk:          %4X\n", this->chunk_type);
360   }
361 
362   this->chunk_length -= ext_header_len;
363   return 1;
364 }
365 
get_header(mmsh_t * this)366 static int get_header (mmsh_t *this) {
367   int len = 0;
368 
369   lprintf("get_header\n");
370 
371   this->asf_header_len = 0;
372 
373   /* read chunk */
374   while (1) {
375     if (get_chunk_header(this)) {
376       if (this->chunk_type == CHUNK_TYPE_ASF_HEADER) {
377         if ((this->asf_header_len + this->chunk_length) > ASF_HEADER_SIZE) {
378           xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
379                    "libmmsh: the asf header exceed %d bytes\n", ASF_HEADER_SIZE);
380           return 0;
381         } else {
382           len = _x_io_tcp_read(this->stream, this->s, (char*)(this->asf_header_buffer + this->asf_header_len),
383                              this->chunk_length);
384           this->asf_header_len += len;
385           if (len != this->chunk_length) {
386             return 0;
387           }
388         }
389       } else {
390         break;
391       }
392     } else {
393       lprintf("get_chunk_header failed\n");
394       return 0;
395     }
396   }
397 
398   if (this->chunk_type == CHUNK_TYPE_DATA) {
399     /* read the first data chunk */
400     len = _x_io_tcp_read(this->stream, this->s, (char*)this->buf, this->chunk_length);
401     if (len != this->chunk_length) {
402       return 0;
403     } else {
404       return 1;
405     }
406   } else {
407     /* unexpected packet type */
408     return 0;
409   }
410 }
411 
interp_header(mmsh_t * this)412 static int interp_header (mmsh_t *this) {
413 
414   lprintf ("interp_header, header_len=%d\n", this->asf_header_len);
415 
416   /* delete previous header */
417   if (this->asf_header) {
418     asf_header_delete(this->asf_header);
419   }
420 
421   /* the header starts with :
422    *   byte  0-15: header guid
423    *   byte 16-23: header length
424    */
425   this->asf_header = asf_header_new(this->asf_header_buffer + 24, this->asf_header_len - 24);
426   if (!this->asf_header)
427     return 0;
428 
429   this->buf_size = this->asf_header->file->packet_size;
430   return 1;
431 }
432 
433 static const char mmsh_proto_s[][8] = { "mms", "mmsh", "" };
434 
mmsh_valid_proto(const char * proto)435 static int mmsh_valid_proto (const char *proto) {
436   int i = 0;
437 
438   lprintf("mmsh_valid_proto\n");
439 
440   if (!proto)
441     return 0;
442 
443   while(*(mmsh_proto_s[i])) {
444     if (!strcasecmp(proto, mmsh_proto_s[i])) {
445       return 1;
446     }
447     i++;
448   }
449   return 0;
450 }
451 
report_progress(xine_stream_t * stream,int p)452 static void report_progress (xine_stream_t *stream, int p) {
453 
454   xine_event_t             event;
455   xine_progress_data_t     prg;
456 
457   prg.description = _("Connecting MMS server (over http)...");
458   prg.percent = p;
459 
460   event.type = XINE_EVENT_PROGRESS;
461   event.data = &prg;
462   event.data_length = sizeof (xine_progress_data_t);
463 
464   xine_event_send (stream, &event);
465 }
466 
467 /*
468  * returns 1 on error
469  */
mmsh_tcp_connect(mmsh_t * this)470 static int mmsh_tcp_connect(mmsh_t *this) {
471   int progress, res;
472 
473   if (!this->url.port)
474     this->url.port = MMSH_PORT;
475 
476   /*
477    * try to connect
478    */
479   lprintf("try to connect to %s on port %d \n", this->url.host, this->url.port);
480 
481   this->s = _x_io_tcp_connect (this->stream, this->url.host, this->url.port);
482 
483   if (this->s < 0) {
484     xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
485              "libmmsh: failed to connect '%s'\n", this->url.host);
486     return 1;
487   }
488 
489   /* connection timeout 15s */
490   progress = 0;
491   do {
492     report_progress(this->stream, progress);
493     res = _x_io_select (this->stream, this->s, XIO_WRITE_READY, 500);
494     progress += 1;
495   } while ((res == XIO_TIMEOUT) && (progress < 30));
496   if (res != XIO_READY) {
497     return 1;
498   }
499   lprintf ("connected\n");
500 
501   return 0;
502 }
503 
504 /*
505  * firts http request
506  */
mmsh_connect_int(mmsh_t * this,int bandwidth)507 static int mmsh_connect_int(mmsh_t *this, int bandwidth) {
508   /*
509    * let the negotiations begin...
510    */
511 
512   /* first request */
513   lprintf("first http request\n");
514 
515   snprintf (this->str, SCRATCH_SIZE, mmsh_FirstRequest, this->url.uri,
516             this->url.host, this->url.port, 1);
517 
518   if (!send_command (this, this->str))
519     return 0;
520 
521   if (!get_answer (this))
522     return 0;
523 
524   get_header (this); /* FIXME: it returns 0 */
525 
526   if (!interp_header (this))
527     return 0;
528 
529   _x_io_tcp_close (this->stream, this->s);
530   this->s = -1;
531   report_progress (this->stream, 20);
532 
533   asf_header_choose_streams (this->asf_header, bandwidth,
534                              &this->video_stream, &this->audio_stream);
535 
536   lprintf("audio stream %d, video stream %d\n",
537           this->audio_stream, this->video_stream);
538 
539   asf_header_disable_streams (this->asf_header,
540                               this->video_stream, this->audio_stream);
541 
542   if (mmsh_tcp_connect(this))
543     return 0;
544 
545   return 1;
546 }
547 
548 /*
549  * second http request
550  */
mmsh_connect_int2(mmsh_t * this,int bandwidth)551 static int mmsh_connect_int2(mmsh_t *this, int bandwidth) {
552   int    i;
553   char   stream_selection[10 * ASF_MAX_NUM_STREAMS]; /* 10 chars per stream */
554   int    offset;
555 
556   /* second request */
557   lprintf("second http request\n");
558 
559   (void)bandwidth;
560   /* stream selection string */
561   /* The same selection is done with mmst */
562   /* 0 means selected */
563   /* 2 means disabled */
564   offset = 0;
565   for (i = 0; i < this->asf_header->stream_count; i++) {
566     int size;
567     if ((i == this->audio_stream) ||
568         (i == this->video_stream)) {
569       size = snprintf(stream_selection + offset, sizeof(stream_selection) - offset,
570                       "ffff:%d:0 ", this->asf_header->streams[i]->stream_number);
571     } else {
572       xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
573                "disabling stream %d\n", this->asf_header->streams[i]->stream_number);
574       size = snprintf(stream_selection + offset, sizeof(stream_selection) - offset,
575                       "ffff:%d:2 ", this->asf_header->streams[i]->stream_number);
576     }
577     if (size < 0)
578       return 0;
579     offset += size;
580   }
581 
582   switch (this->stream_type) {
583     case MMSH_SEEKABLE:
584       snprintf (this->str, SCRATCH_SIZE, mmsh_SeekableRequest, this->url.uri,
585                 this->url.host, this->url.port, this->start_time, 0, 0, 2, 0,
586                 this->asf_header->stream_count, stream_selection);
587       break;
588     case MMSH_LIVE:
589       snprintf (this->str, SCRATCH_SIZE, mmsh_LiveRequest, this->url.uri,
590                 this->url.host, this->url.port, 2,
591                 this->asf_header->stream_count, stream_selection);
592       break;
593   }
594 
595   if (!send_command (this, this->str))
596     return 0;
597 
598   lprintf("before read \n");
599 
600   if (!get_answer (this))
601     return 0;
602 
603   if (!get_header (this))
604     return 0;
605 
606 #if 0
607   if (!interp_header (this))
608     return 0;
609 
610   asf_header_disable_streams (this->asf_header,
611                               this->video_stream, this->audio_stream);
612 #endif
613 
614   return 1;
615 }
616 
mmsh_connect(xine_stream_t * stream,const char * url,int bandwidth)617 mmsh_t *mmsh_connect (xine_stream_t *stream, const char *url, int bandwidth) {
618   mmsh_t *this;
619 
620   if (!url)
621     return NULL;
622 
623   report_progress (stream, 0);
624 
625   this = calloc(1, sizeof (mmsh_t));
626   if (!this)
627     return NULL;
628 
629   this->stream          = stream;
630   //this->url             = strdup(url);
631   this->s               = -1;
632   this->asf_header_len  = 0;
633   this->asf_header_read = 0;
634   this->buf_size        = 0;
635   this->buf_read        = 0;
636   this->current_pos     = 0;
637   this->user_bandwidth  = bandwidth;
638 
639   report_progress (stream, 0);
640 
641   if (!_x_url_parse2 (url, &this->url)) {
642     xine_log (this->stream->xine, XINE_LOG_MSG, _("invalid url\n"));
643     goto fail;
644   }
645 
646   if (!mmsh_valid_proto(this->url.proto)) {
647     xine_log (this->stream->xine, XINE_LOG_MSG, _("unsupported protocol\n"));
648     goto fail;
649   }
650 
651   if (mmsh_tcp_connect(this))
652     goto fail;
653 
654   report_progress (stream, 30);
655 
656   if (!mmsh_connect_int(this, this->user_bandwidth))
657     goto fail;
658 
659   report_progress (stream, 100);
660 
661   lprintf("mmsh_connect: passed\n" );
662 
663   return this;
664 
665 fail:
666   lprintf("mmsh_connect: failed\n" );
667   if (this->s != -1)
668     _x_io_tcp_close(this->stream, this->s);
669 
670   _x_url_cleanup(&this->url);
671 
672   free(this);
673 
674   lprintf("mmsh_connect: failed return\n" );
675   return NULL;
676 }
677 
678 
679 /*
680  * returned value:
681  *  0: error
682  *  1: data packet read
683  *  2: new header read
684  */
get_media_packet(mmsh_t * this)685 static int get_media_packet (mmsh_t *this) {
686   int len = 0;
687 
688   lprintf("get_media_packet: this->packet_length: %d\n", this->asf_header->file->packet_size);
689 
690   if (get_chunk_header(this)) {
691     switch (this->chunk_type) {
692       case CHUNK_TYPE_END:
693         /* this->chunk_seq_number:
694          *     0: stop
695          *     1: a new stream follows
696          */
697         if (this->chunk_seq_number == 0)
698           return 0;
699 
700         _x_io_tcp_close(this->stream, this->s);
701         this->s = -1;
702 
703         if (mmsh_tcp_connect(this))
704           return 0;
705 
706         if (!mmsh_connect_int(this, this->user_bandwidth))
707           return 0;
708 
709         this->playing = 0;
710 
711         /* mmsh_connect_int reads the first data packet */
712         /* this->buf_size is set by mmsh_connect_int */
713         return 2;
714 
715       case CHUNK_TYPE_DATA:
716         /* nothing to do */
717         break;
718 
719       case CHUNK_TYPE_RESET:
720         /* next chunk is an ASF header */
721 
722         if (this->chunk_length != 0) {
723           /* that's strange, don't know what to do */
724           return 0;
725         }
726         if (!get_header(this))
727           return 0;
728         interp_header(this);
729         return 2;
730 
731       default:
732         xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
733                  "libmmsh: unexpected chunk type\n");
734         return 0;
735     }
736 
737     len = _x_io_tcp_read (this->stream, this->s, (char*)this->buf, this->chunk_length);
738 
739     if (len == this->chunk_length) {
740       /* explicit padding with 0 */
741       if (this->chunk_length > this->asf_header->file->packet_size) {
742         xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
743                  "libmmsh: chunk_length(%d) > packet_length(%d)\n",
744                  this->chunk_length, this->asf_header->file->packet_size);
745         return 0;
746       }
747       memset(this->buf + this->chunk_length, 0,
748              this->asf_header->file->packet_size - this->chunk_length);
749       return 1;
750     } else {
751       xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
752                "libmmsh: read error, %d != %d\n", len, this->chunk_length);
753       return 0;
754     }
755   } else {
756     return 0;
757   }
758 }
759 
mmsh_peek_header(mmsh_t * this,char * data,size_t maxsize)760 size_t mmsh_peek_header (mmsh_t *this, char *data, size_t maxsize) {
761   size_t len;
762 
763   lprintf("mmsh_peek_header\n");
764 
765   len = (this->asf_header_len < maxsize) ? this->asf_header_len : maxsize;
766 
767   memcpy(data, this->asf_header_buffer, len);
768   return len;
769 }
770 
mmsh_read(mmsh_t * this,char * data,int len)771 int mmsh_read (mmsh_t *this, char *data, int len) {
772   int total;
773 
774   total = 0;
775 
776   lprintf ("mmsh_read: len: %d\n", len);
777 
778   while (total < len) {
779 
780     if (this->asf_header_read < this->asf_header_len) {
781       int n, bytes_left ;
782 
783       bytes_left = this->asf_header_len - this->asf_header_read;
784 
785       if ((len-total) < bytes_left)
786         n = len-total;
787       else
788         n = bytes_left;
789 
790       xine_fast_memcpy (&data[total], &this->asf_header_buffer[this->asf_header_read], n);
791 
792       this->asf_header_read += n;
793       total += n;
794       this->current_pos += n;
795 
796       if (this->asf_header_read == this->asf_header_len)
797 	break;
798     } else {
799 
800       int n, bytes_left ;
801 
802       if (!this->playing) {
803         if (!mmsh_connect_int2 (this, this->user_bandwidth))
804           break;
805         this->playing = 1;
806       }
807 
808       bytes_left = this->buf_size - this->buf_read;
809 
810       if (bytes_left == 0) {
811         int packet_type;
812 
813         this->buf_read = 0;
814         packet_type = get_media_packet (this);
815 
816         if (packet_type == 0) {
817           xprintf (this->stream->xine, XINE_VERBOSITY_LOG,
818                    "libmmsh: get_media_packet failed\n");
819           return total;
820         } else if (packet_type == 2) {
821           continue;
822         }
823 
824         bytes_left = this->buf_size;
825       }
826 
827       if ((len - total) < bytes_left)
828         n = len - total;
829       else
830         n = bytes_left;
831 
832       xine_fast_memcpy (&data[total], &this->buf[this->buf_read], n);
833 
834       this->buf_read += n;
835       total += n;
836       this->current_pos += n;
837     }
838   }
839   return total;
840 }
841 
842 
mmsh_close(mmsh_t * this)843 void mmsh_close (mmsh_t *this) {
844 
845   lprintf("mmsh_close\n");
846 
847   if (this->s != -1)
848     _x_io_tcp_close(this->stream, this->s);
849 
850   if (this->asf_header)
851     asf_header_delete(this->asf_header);
852 
853   _x_url_cleanup(&this->url);
854 
855   free(this);
856 }
857 
858 
mmsh_get_length(mmsh_t * this)859 uint32_t mmsh_get_length (mmsh_t *this) {
860   return this->asf_header->file->file_size;
861 }
862 
mmsh_get_current_pos(mmsh_t * this)863 off_t mmsh_get_current_pos (mmsh_t *this) {
864   return this->current_pos;
865 }
866 
mmsh_set_start_time(mmsh_t * this,int time_offset)867 void mmsh_set_start_time (mmsh_t *this, int time_offset) {
868   if (time_offset >= 0)
869     this->start_time = time_offset;
870 }
871