📄 mms.c
字号:
/* * Copyright (C) 2002-2004 the xine project * * This file is part of LibMMS, an MMS protocol handling library. * * 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 ree 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: mms.c,v 1.21 2006/07/13 12:32:20 shawarma Exp $ * * MMS over TCP protocol * based on work from major mms * utility functions to handle communication with an mms server * * TODO: * error messages * enable seeking ! */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <unistd.h>#include <stdio.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>#if defined(HAVE_ICONV) && defined(HAVE_LANGINFO_CODESET)#define USE_ICONV#include <iconv.h>#include <locale.h>#include <langinfo.h>#endif/********** logging **********/#define LOG_MODULE "mms"#define LOG_VERBOSE#ifdef DEBUG# define lprintf printf#else# define lprintf(x...)#endif#include "bswap.h"#include "mms.h"#include "asfheader.h"#include "uri.h"/* * mms specific types */#define MMST_PORT 1755#define BUF_SIZE 102400#define CMD_HEADER_LEN 40#define CMD_PREFIX_LEN 8#define CMD_BODY_LEN 1024#define ASF_HEADER_LEN 8192#define MMS_PACKET_ERR 0#define MMS_PACKET_COMMAND 1#define MMS_PACKET_ASF_HEADER 2#define MMS_PACKET_ASF_PACKET 3#define ASF_HEADER_PACKET_ID_TYPE 2#define ASF_MEDIA_PACKET_ID_TYPE 4typedef struct mms_buffer_s mms_buffer_t;struct mms_buffer_s { uint8_t *buffer; int pos;};typedef struct mms_packet_header_s mms_packet_header_t;struct mms_packet_header_s { uint32_t packet_len; uint8_t flags; uint8_t packet_id_type; uint32_t packet_seq;};struct mms_s { /* FIXME: de-xine-ification */ void *custom_data; int s; /* url parsing */ char *url; char *proto; char *host; int port; char *user; char *password; char *uri; /* command to send */ char scmd[CMD_HEADER_LEN + CMD_BODY_LEN]; char *scmd_body; /* pointer to &scmd[CMD_HEADER_LEN] */ int scmd_len; /* num bytes written in header */ char str[1024]; /* scratch buffer to built strings */ /* receive buffer */ uint8_t buf[BUF_SIZE]; int buf_size; int buf_read; uint8_t asf_header[ASF_HEADER_LEN]; 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]; off_t start_packet_seq; /* for live streams != 0, need to keep it around */ int need_discont; /* whether we need to set start_packet_seq */ uint32_t asf_packet_len; uint64_t file_len; char guid[37]; uint32_t bitrates[ASF_MAX_NUM_STREAMS]; uint32_t bitrates_pos[ASF_MAX_NUM_STREAMS]; int bandwidth; int has_audio; int has_video; int live_flag; off_t current_pos; int eos;};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); 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)) const mms_io_t* mms_get_default_io_impl(){ return &default_io;}void mms_set_default_io_impl(const mms_io_t *io){ if(io->select) { default_io.select = io->select; default_io.select_data = io->select_data; } else { default_io.select = fallback_io.select; default_io.select_data = fallback_io.select_data; } if(io->read) { default_io.read = io->read; default_io.read_data = io->read_data; } else { default_io.read = fallback_io.read; default_io.read_data = fallback_io.read_data; } if(io->write) { default_io.write = io->write; default_io.write_data = io->write_data; } else { default_io.write = fallback_io.write; default_io.write_data = fallback_io.write_data; } if(io->connect) { default_io.connect = io->connect; default_io.connect_data = io->connect_data; } else { default_io.connect = fallback_io.connect; default_io.connect_data = fallback_io.connect_data; }}static void mms_buffer_init (mms_buffer_t *mms_buffer, uint8_t *buffer) { mms_buffer->buffer = buffer; mms_buffer->pos = 0;}static void mms_buffer_put_8 (mms_buffer_t *mms_buffer, uint8_t value) { mms_buffer->buffer[mms_buffer->pos] = value & 0xff; mms_buffer->pos += 1;}#if 0static void mms_buffer_put_16 (mms_buffer_t *mms_buffer, uint16_t value) { mms_buffer->buffer[mms_buffer->pos] = value & 0xff; mms_buffer->buffer[mms_buffer->pos + 1] = (value >> 8) & 0xff; mms_buffer->pos += 2;}#endifstatic void mms_buffer_put_32 (mms_buffer_t *mms_buffer, uint32_t value) { mms_buffer->buffer[mms_buffer->pos] = value & 0xff; mms_buffer->buffer[mms_buffer->pos + 1] = (value >> 8) & 0xff; mms_buffer->buffer[mms_buffer->pos + 2] = (value >> 16) & 0xff; mms_buffer->buffer[mms_buffer->pos + 3] = (value >> 24) & 0xff; mms_buffer->pos += 4;}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("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 void print_command (char *data, int len) {#ifdef DEBUG int i; int dir = LE_32 (data + 36) >> 16; int comm = LE_32 (data + 36) & 0xFFFF; lprintf ("----------------------------------------------\n"); if (dir == 3) { lprintf ("send command 0x%02x, %d bytes\n", comm, len); } else { lprintf ("receive command 0x%02x, %d bytes\n", comm, len); } lprintf (" start sequence %08x\n", LE_32 (data + 0)); lprintf (" command id %08x\n", LE_32 (data + 4)); lprintf (" length %8x \n", LE_32 (data + 8)); lprintf (" protocol %08x\n", LE_32 (data + 12)); lprintf (" len8 %8x \n", LE_32 (data + 16)); lprintf (" sequence # %08x\n", LE_32 (data + 20)); lprintf (" len8 (II) %8x \n", LE_32 (data + 32)); lprintf (" dir | comm %08x\n", LE_32 (data + 36)); if (len >= 4) lprintf (" prefix1 %08x\n", LE_32 (data + 40)); if (len >= 8) lprintf (" prefix2 %08x\n", LE_32 (data + 44)); for (i = (CMD_HEADER_LEN + CMD_PREFIX_LEN); i < (CMD_HEADER_LEN + CMD_PREFIX_LEN + len); i += 1) { unsigned char c = data[i]; if ((c >= 32) && (c < 128)) lprintf ("%c", c); else lprintf (" %02x ", c); } if (len > CMD_HEADER_LEN) lprintf ("\n"); lprintf ("----------------------------------------------\n");#endif} static int send_command (mms_io_t *io, mms_t *this, int command, uint32_t prefix1, uint32_t prefix2, int length) { int len8; off_t n; mms_buffer_t command_buffer; len8 = (length + 7) / 8; this->scmd_len = 0; mms_buffer_init(&command_buffer, this->scmd); mms_buffer_put_32 (&command_buffer, 0x00000001); /* start sequence */ mms_buffer_put_32 (&command_buffer, 0xB00BFACE); /* #-)) */ mms_buffer_put_32 (&command_buffer, len8 * 8 + 32); mms_buffer_put_32 (&command_buffer, 0x20534d4d); /* protocol type "MMS " */ mms_buffer_put_32 (&command_buffer, len8 + 4); mms_buffer_put_32 (&command_buffer, this->seq_num); this->seq_num++; mms_buffer_put_32 (&command_buffer, 0x0); /* timestamp */ mms_buffer_put_32 (&command_buffer, 0x0); mms_buffer_put_32 (&command_buffer, len8 + 2); mms_buffer_put_32 (&command_buffer, 0x00030000 | command); /* dir | command */ /* end of the 40 byte command header */ mms_buffer_put_32 (&command_buffer, prefix1); mms_buffer_put_32 (&command_buffer, prefix2); if (length & 7) memset(this->scmd + length + CMD_HEADER_LEN + CMD_PREFIX_LEN, 0, 8 - (length & 7)); n = io_write(io, this->s, this->scmd, len8 * 8 + CMD_HEADER_LEN + CMD_PREFIX_LEN); if (n != (len8 * 8 + CMD_HEADER_LEN + CMD_PREFIX_LEN)) { return 0; } print_command (this->scmd, length); return 1;}#ifdef USE_ICONVstatic iconv_t string_utf16_open() { return iconv_open("UTF-16LE", nl_langinfo(CODESET));}static void string_utf16_close(iconv_t url_conv) { if (url_conv != (iconv_t)-1) { iconv_close(url_conv); }}static void string_utf16(iconv_t url_conv, char *dest, char *src, int len) { memset(dest, 0, 2 * len); if (url_conv == (iconv_t)-1) { int i; for (i = 0; i < len; i++) { dest[i * 2] = src[i]; dest[i * 2 + 1] = 0; } dest[i * 2] = 0; dest[i * 2 + 1] = 0; } else { size_t len1, len2; char *ip, *op; len1 = len; len2 = 1000; ip = src; op = dest; iconv(url_conv, &ip, &len1, &op, &len2); }}#elsestatic void string_utf16(int unused, char *dest, char *src, int len) { int i; memset (dest, 0, 2 * len); for (i = 0; i < len; i++) { dest[i * 2] = src[i]; dest[i * 2 + 1] = 0; } dest[i * 2] = 0; dest[i * 2 + 1] = 0;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -