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