📄 mmsh.c
字号:
/* * Copyright (C) 2002-2003 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * $Id: mmsh.c,v 1.5 2006/07/13 12:32:20 shawarma Exp $ * * MMS over HTTP protocol * written by Thibaut Mattern * based on mms.c and specs from avifile * (http://avifile.sourceforge.net/asf-1.0.htm) * * TODO: * error messages * http support cleanup, find a way to share code with input_http.c (http.h|c) * http proxy support */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/socket.h>#include <netinet/in.h>#include <netdb.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#include <stdlib.h>#include <time.h>#include <assert.h>#define LOG_MODULE "mmsh"#define LOG_VERBOSE#ifdef DEBUG# define lprintf printf#else# define lprintf(x...)#endif#include "bswap.h"#include "mmsh.h"#include "asfheader.h"#include "uri.h"/* #define USERAGENT "User-Agent: NSPlayer/7.1.0.3055\r\n" */#define USERAGENT "User-Agent: NSPlayer/4.1.0.3856\r\n"#define CLIENTGUID "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"#define MMSH_PORT 80#define MMSH_UNKNOWN 0#define MMSH_SEEKABLE 1#define MMSH_LIVE 2#define CHUNK_HEADER_LENGTH 4#define EXT_HEADER_LENGTH 8#define CHUNK_TYPE_RESET 0x4324#define CHUNK_TYPE_DATA 0x4424#define CHUNK_TYPE_END 0x4524#define CHUNK_TYPE_ASF_HEADER 0x4824#define CHUNK_SIZE 65536 /* max chunk size */#define ASF_HEADER_SIZE 8192 /* max header size */#define SCRATCH_SIZE 1024static const char* mmsh_FirstRequest = "GET %s HTTP/1.0\r\n" "Accept: */*\r\n" USERAGENT "Host: %s:%d\r\n" "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%u,max-duration=0\r\n" CLIENTGUID "Connection: Close\r\n\r\n";static const char* mmsh_SeekableRequest = "GET %s HTTP/1.0\r\n" "Accept: */*\r\n" USERAGENT "Host: %s:%d\r\n" "Pragma: no-cache,rate=1.000000,stream-time=%u,stream-offset=%u:%u,request-context=%u,max-duration=%u\r\n" CLIENTGUID "Pragma: xPlayStrm=1\r\n" "Pragma: stream-switch-count=%d\r\n" "Pragma: stream-switch-entry=%s\r\n" /* ffff:1:0 ffff:2:0 */ "Connection: Close\r\n\r\n";static const char* mmsh_LiveRequest = "GET %s HTTP/1.0\r\n" "Accept: */*\r\n" USERAGENT "Host: %s:%d\r\n" "Pragma: no-cache,rate=1.000000,request-context=%u\r\n" "Pragma: xPlayStrm=1\r\n" CLIENTGUID "Pragma: stream-switch-count=%d\r\n" "Pragma: stream-switch-entry=%s\r\n" "Connection: Close\r\n\r\n";#if 0/* Unused requests */static const char* mmsh_PostRequest = "POST %s HTTP/1.0\r\n" "Accept: */*\r\n" USERAGENT "Host: %s\r\n" "Pragma: client-id=%u\r\n"/* "Pragma: log-line=no-cache,rate=1.000000,stream-time=%u,stream-offset=%u:%u,request-context=2,max-duration=%u\r\n" */ "Pragma: Content-Length: 0\r\n" CLIENTGUID "\r\n";static const char* mmsh_RangeRequest = "GET %s HTTP/1.0\r\n" "Accept: */*\r\n" USERAGENT "Host: %s:%d\r\n" "Range: bytes=%Lu-\r\n" CLIENTGUID "Connection: Close\r\n\r\n";#endif/* * mmsh specific types */struct mmsh_s { /* FIXME: de-xine-ification */ void *custom_data; int s; /* url parsing */ char *url; char *proxy_url; char *proto; char *connect_host; int connect_port; char *http_host; int http_port; char *proxy_user; char *proxy_password; char *host_user; char *host_password; char *uri; char str[SCRATCH_SIZE]; /* scratch buffer to built strings */ int stream_type; /* seekable or broadcast */ /* receive buffer */ /* chunk */ uint16_t chunk_type; uint16_t chunk_length; uint16_t chunk_seq_number; uint8_t buf[CHUNK_SIZE]; int buf_size; int buf_read; uint8_t asf_header[ASF_HEADER_SIZE]; uint32_t asf_header_len; uint32_t asf_header_read; int seq_num; int num_stream_ids; int stream_ids[ASF_MAX_NUM_STREAMS]; int stream_types[ASF_MAX_NUM_STREAMS]; uint32_t packet_length; int64_t file_length; char guid[37]; uint32_t bitrates[ASF_MAX_NUM_STREAMS]; uint32_t bitrates_pos[ASF_MAX_NUM_STREAMS]; int has_audio; int has_video; off_t current_pos; int user_bandwidth;};static int fallback_io_select(void *data, int socket, int state, int timeout_msec){ fd_set set; struct timeval tv = { timeout_msec / 1000, (timeout_msec % 1000) * 1000}; FD_ZERO(&set); FD_SET(socket, &set); return select(1, (state == MMS_IO_READ_READY) ? &set : NULL, (state == MMS_IO_WRITE_READY) ? &set : NULL, NULL, &tv);}static off_t fallback_io_read(void *data, int socket, char *buf, off_t num){ off_t len = 0, ret;/* lprintf("%d\n", fallback_io_select(data, socket, MMS_IO_READ_READY, 1000)); */ errno = 0; while (len < num) { ret = (off_t)read(socket, buf + len, num - len); if(ret == 0) break; /* EOF */ if(ret < 0) switch(errno) { case EAGAIN: lprintf("len == %lld\n", (long long int) len); break; default: lprintf("len == %lld\n", (long long int) len); perror(NULL); /* if already read something, return it, we will fail next time */ return len ? len : ret; } len += ret; } lprintf("ret len == %lld\nnum == %lld\n", (long long int) len, (long long int) num); lprintf("read\n"); return len;}static off_t fallback_io_write(void *data, int socket, char *buf, off_t num){ return (off_t)write(socket, buf, num);}static int fallback_io_tcp_connect(void *data, const char *host, int port){ struct hostent *h; int i, s; h = gethostbyname(host); if (h == NULL) {/* fprintf(stderr, "unable to resolve host: %s\n", host); */ return -1; } s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) {/* fprintf(stderr, "failed to create socket: %s", strerror(errno)); */ return -1; } if (fcntl (s, F_SETFL, fcntl (s, F_GETFL) & ~O_NONBLOCK) == -1) {/* _x_message(stream, XINE_MSG_CONNECTION_REFUSED, "can't put socket in non-blocking mode", strerror(errno), NULL); */ return -1; } for (i = 0; h->h_addr_list[i]; i++) { struct in_addr ia; struct sockaddr_in sin; memcpy (&ia, h->h_addr_list[i], 4); sin.sin_family = AF_INET; sin.sin_addr = ia; sin.sin_port = htons(port); if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) ==-1 && errno != EINPROGRESS) { /* FIXME: de-xine-ification *//* _x_message(stream, XINE_MSG_CONNECTION_REFUSED, strerror(errno), NULL); */ close(s); continue; } return s; } return -1;}static mms_io_t fallback_io = { &fallback_io_select, NULL, &fallback_io_read, NULL, &fallback_io_write, NULL, &fallback_io_tcp_connect, NULL, };static mms_io_t default_io = { &fallback_io_select, NULL, &fallback_io_read, NULL, &fallback_io_write, NULL, &fallback_io_tcp_connect, NULL, };#define io_read(io, args...) ((io) ? (io)->read(io->read_data , ## args) : default_io.read(NULL , ## args))#define io_write(io, args...) ((io) ? (io)->write(io->write_data , ## args) : default_io.write(NULL , ## args))#define io_select(io, args...) ((io) ? (io)->select(io->select_data , ## args) : default_io.select(NULL , ## args))#define io_connect(io, args...) ((io) ? (io)->connect(io->connect_data , ## args) : default_io.connect(NULL , ## args)) static int get_guid (unsigned char *buffer, int offset) { int i; GUID g; g.Data1 = LE_32(buffer + offset); g.Data2 = LE_16(buffer + offset + 4); g.Data3 = LE_16(buffer + offset + 6); for(i = 0; i < 8; i++) { g.Data4[i] = buffer[offset + 8 + i]; } for (i = 1; i < GUID_END; i++) { if (!memcmp(&g, &guids[i].guid, sizeof(GUID))) { lprintf ("GUID: %s\n", guids[i].name); return i; } } lprintf ("libmmsh: unknown GUID: 0x%x, 0x%x, 0x%x, " "{ 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx, 0x%hx }\n", g.Data1, g.Data2, g.Data3, g.Data4[0], g.Data4[1], g.Data4[2], g.Data4[3], g.Data4[4], g.Data4[5], g.Data4[6], g.Data4[7]); return GUID_ERROR;}static int send_command (mms_io_t *io, mmsh_t *this, char *cmd) { int length; lprintf ("send_command:\n%s\n", cmd); length = strlen(cmd); if (io_write(io, this->s, cmd, length) != length) { lprintf ("mmsh: send error.\n"); return 0; } return 1;}static int get_answer (mms_io_t *io, mmsh_t *this) { int done, len, linenum; char *features; lprintf ("get_answer\n"); done = 0; len = 0; linenum = 0; this->stream_type = MMSH_UNKNOWN; while (!done) { if (io_read(io, this->s, &(this->buf[len]), 1) != 1) { lprintf ("mmsh: alart: end of stream\n"); return 0; } if (this->buf[len] == '\012') { this->buf[len] = '\0'; len--; if ((len >= 0) && (this->buf[len] == '\015')) { this->buf[len] = '\0'; len--; } linenum++; lprintf ("answer: >%s<\n", this->buf); if (linenum == 1) { int httpver, httpsub, httpcode; char httpstatus[51]; if (sscanf(this->buf, "HTTP/%d.%d %d %50[^\015\012]", &httpver, &httpsub, &httpcode, httpstatus) != 4) { lprintf ("mmsh: bad response format\n"); return 0; } if (httpcode >= 300 && httpcode < 400) { lprintf ("mmsh: 3xx redirection not implemented: >%d %s<\n", httpcode, httpstatus); return 0; } if (httpcode < 200 || httpcode >= 300) { lprintf ("mmsh: http status not 2xx: >%d %s<\n", httpcode, httpstatus); return 0; } } else { if (!strncasecmp(this->buf, "Location: ", 10)) { lprintf ("mmsh: Location redirection not implemented.\n"); return 0; } if (!strncasecmp(this->buf, "Pragma:", 7)) { features = strstr(this->buf + 7, "features="); if (features) { if (strstr(features, "seekable")) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -