| @@ -0,0 +1,690 @@ |
| | 1 | /* |
| | 2 | * MMS protocol over TCP |
| | 3 | * Copyright (c) 2006,2007 Ryan Martell |
| | 4 | * Copyright (c) 2007 Bj�rn Axelsson |
| | 5 | * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com> |
| | 6 | * |
| | 7 | * This file is part of FFmpeg. |
| | 8 | * |
| | 9 | * FFmpeg is free software; you can redistribute it and/or |
| | 10 | * modify it under the terms of the GNU Lesser General Public |
| | 11 | * License as published by the Free Software Foundation; either |
| | 12 | * version 2.1 of the License, or (at your option) any later version. |
| | 13 | * |
| | 14 | * FFmpeg is distributed in the hope that it will be useful, |
| | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| | 17 | * Lesser General Public License for more details. |
| | 18 | * |
| | 19 | * You should have received a copy of the GNU Lesser General Public |
| | 20 | * License along with FFmpeg; if not, write to the Free Software |
| | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| | 22 | */ |
| | 23 | #include "avformat.h" |
| | 24 | #include "internal.h" |
| | 25 | #include "libavutil/intreadwrite.h" |
| | 26 | #include "libavcodec/bytestream.h" |
| | 27 | #include "network.h" |
| | 28 | #include "asf.h" |
| | 29 | |
| | 30 | #define LOCAL_ADDRESS 0xc0a80081 // FIXME get and use correct local ip address. |
| | 31 | #define LOCAL_PORT 1037 // as above. |
| | 32 | /** Client to server packet types. */ |
| | 33 | typedef enum { |
| | 34 | CS_PKT_INITIAL = 0x01, |
| | 35 | CS_PKT_PROTOCOL_SELECT = 0x02, |
| | 36 | CS_PKT_MEDIA_FILE_REQUEST = 0x05, |
| | 37 | CS_PKT_START_FROM_PKT_ID = 0x07, |
| | 38 | CS_PKT_STREAM_PAUSE = 0x09, |
| | 39 | CS_PKT_STREAM_CLOSE = 0x0d, |
| | 40 | CS_PKT_MEDIA_HEADER_REQUEST = 0x15, |
| | 41 | CS_PKT_TIMING_DATA_REQUEST = 0x18, |
| | 42 | CS_PKT_USER_PASSWORD = 0x1a, |
| | 43 | CS_PKT_KEEPALIVE = 0x1b, |
| | 44 | CS_PKT_STREAM_ID_REQUEST = 0x33, |
| | 45 | } MMSCSPacketType; |
| | 46 | |
| | 47 | /** Server to client packet types. */ |
| | 48 | typedef enum { |
| | 49 | /** Control packets. */ |
| | 50 | /*@{*/ |
| | 51 | SC_PKT_CLIENT_ACCEPTED = 0x01, |
| | 52 | SC_PKT_PROTOCOL_ACCEPTED = 0x02, |
| | 53 | SC_PKT_PROTOCOL_FAILED = 0x03, |
| | 54 | SC_PKT_MEDIA_PKT_FOLLOWS = 0x05, |
| | 55 | SC_PKT_MEDIA_FILE_DETAILS = 0x06, |
| | 56 | SC_PKT_HEADER_REQUEST_ACCEPTED = 0x11, |
| | 57 | SC_PKT_TIMING_TEST_REPLY = 0x15, |
| | 58 | SC_PKT_PASSWORD_REQUIRED = 0x1a, |
| | 59 | SC_PKT_KEEPALIVE = 0x1b, |
| | 60 | SC_PKT_STREAM_STOPPED = 0x1e, |
| | 61 | SC_PKT_STREAM_CHANGING = 0x20, |
| | 62 | SC_PKT_STREAM_ID_ACCEPTED = 0x21, |
| | 63 | /*@}*/ |
| | 64 | |
| | 65 | /** Pseudo packets. */ |
| | 66 | /*@{*/ |
| | 67 | SC_PKT_CANCEL = -1, |
| | 68 | SC_PKT_NO_DATA = -2, |
| | 69 | /*@}*/ |
| | 70 | |
| | 71 | /** Data packets. */ |
| | 72 | /*@{*/ |
| | 73 | SC_PKT_ASF_HEADER = 0x010000,// make it bigger than 0xFF in case of |
| | 74 | SC_PKT_ASF_MEDIA = 0x010001,// receiving false data packets. |
| | 75 | /*@}*/ |
| | 76 | } MMSSCPacketType; |
| | 77 | |
| | 78 | typedef struct { |
| | 79 | int id; |
| | 80 | }MMSStream; |
| | 81 | |
| | 82 | typedef struct { |
| | 83 | int outgoing_packet_seq; ///< Outgoing packet sequence number. |
| | 84 | char path[256]; ///< Path of the resource being asked for. |
| | 85 | char host[128]; ///< Host of the resources. |
| | 86 | |
| | 87 | URLContext *mms_hd; ///< TCP connection handle |
| | 88 | MMSStream streams[MAX_STREAMS]; |
| | 89 | |
| | 90 | /** Buffer for outgoing packets. */ |
| | 91 | /*@{*/ |
| | 92 | uint8_t *write_out_ptr; ///< Pointer for writting the buffer. |
| | 93 | uint8_t out_buffer[512]; ///< Buffer for outgoing packet. |
| | 94 | /*@}*/ |
| | 95 | |
| | 96 | /** Buffer for incoming packets. */ |
| | 97 | /*@{*/ |
| | 98 | uint8_t in_buffer[8192]; ///< Buffer for incoming packets. |
| | 99 | uint8_t *read_in_ptr; ///< Pointer for reading from incoming buffer. |
| | 100 | int remaining_in_len; ///< Reading length from incoming buffer. |
| | 101 | /*@}*/ |
| | 102 | |
| | 103 | int incoming_packet_seq; ///< Incoming packet sequence number. |
| | 104 | int incoming_flags; ///< Incoming packet flags. |
| | 105 | |
| | 106 | int packet_id; ///< Identifier for packets in the current stream. |
| | 107 | unsigned int header_packet_id; ///< default is 2. |
| | 108 | |
| | 109 | /** Internal handling of the ASF header */ |
| | 110 | /*@{*/ |
| | 111 | uint8_t *asf_header; ///< Stored ASF header. |
| | 112 | int asf_header_size; ///< Size of stored ASF header. |
| | 113 | int header_parsed; ///< The header has been received and parsed. |
| | 114 | int asf_packet_len; |
| | 115 | /*@}*/ |
| | 116 | |
| | 117 | int stream_num; ///< stream numbers. |
| | 118 | int is_playing; |
| | 119 | } MMSContext; |
| | 120 | |
| | 121 | /** Create MMST command packet header */ |
| | 122 | static void start_command_packet(MMSContext *mms, MMSCSPacketType packet_type) |
| | 123 | { |
| | 124 | mms->write_out_ptr = mms->out_buffer; |
| | 125 | |
| | 126 | bytestream_put_le32(&mms->write_out_ptr, 1); // start sequence |
| | 127 | bytestream_put_le32(&mms->write_out_ptr, 0xb00bface); |
| | 128 | bytestream_put_le32(&mms->write_out_ptr, 0); // Length starts from after the protocol type bytes |
| | 129 | bytestream_put_le32(&mms->write_out_ptr, MKTAG('M','M','S',' ')); |
| | 130 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 131 | bytestream_put_le32(&mms->write_out_ptr, mms->outgoing_packet_seq++); |
| | 132 | bytestream_put_le64(&mms->write_out_ptr, 0); // timestamp |
| | 133 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 134 | bytestream_put_le16(&mms->write_out_ptr, packet_type); |
| | 135 | bytestream_put_le16(&mms->write_out_ptr, 3); // direction to server |
| | 136 | } |
| | 137 | |
| | 138 | /** Add prefixes to MMST command packet. */ |
| | 139 | static void insert_command_prefixes(MMSContext *mms, |
| | 140 | uint32_t prefix1, uint32_t prefix2) |
| | 141 | { |
| | 142 | bytestream_put_le32(&mms->write_out_ptr, prefix1); // first prefix |
| | 143 | bytestream_put_le32(&mms->write_out_ptr, prefix2); // second prefix |
| | 144 | } |
| | 145 | |
| | 146 | /** Send a prepared MMST command packet. */ |
| | 147 | static int send_command_packet(MMSContext *mms) |
| | 148 | { |
| | 149 | int exact_length= mms->write_out_ptr - mms->out_buffer; |
| | 150 | int first_length= exact_length - 16; |
| | 151 | int len8= first_length/8; |
| | 152 | int write_result; |
| | 153 | |
| | 154 | // update packet length fields. |
| | 155 | AV_WL32(mms->out_buffer + 8, first_length); |
| | 156 | AV_WL32(mms->out_buffer + 16, len8); |
| | 157 | AV_WL32(mms->out_buffer + 32, len8-2); |
| | 158 | |
| | 159 | // write it out. |
| | 160 | write_result= url_write(mms->mms_hd, mms->out_buffer, exact_length); |
| | 161 | if(write_result != exact_length) { |
| | 162 | dprintf(NULL, "url_write returned: %d != %d\n", |
| | 163 | write_result, exact_length); |
| | 164 | return AVERROR_IO; |
| | 165 | } |
| | 166 | |
| | 167 | return 0; |
| | 168 | } |
| | 169 | |
| | 170 | static void mms_put_utf16(MMSContext *mms, uint8_t *src) |
| | 171 | { |
| | 172 | ByteIOContext bic; |
| | 173 | int size = mms->write_out_ptr - mms->out_buffer; |
| | 174 | int len; |
| | 175 | init_put_byte(&bic, mms->write_out_ptr, |
| | 176 | sizeof(mms->out_buffer) - size, 1, NULL, NULL, NULL, NULL); |
| | 177 | |
| | 178 | len = ff_put_str16_nolen(&bic, src); |
| | 179 | mms->write_out_ptr += len; |
| | 180 | } |
| | 181 | |
| | 182 | static int send_protocol_select(MMSContext *mms) |
| | 183 | { |
| | 184 | char data_string[256]; |
| | 185 | |
| | 186 | start_command_packet(mms, CS_PKT_PROTOCOL_SELECT); |
| | 187 | insert_command_prefixes(mms, 0, 0xffffffff); |
| | 188 | bytestream_put_le32(&mms->write_out_ptr, 0); // maxFunnelBytes |
| | 189 | bytestream_put_le32(&mms->write_out_ptr, 0x00989680); // maxbitRate |
| | 190 | bytestream_put_le32(&mms->write_out_ptr, 2); // funnelMode |
| | 191 | snprintf(data_string, sizeof(data_string), "\\\\%d.%d.%d.%d\\%s\\%d", |
| | 192 | (LOCAL_ADDRESS>>24)&0xff, |
| | 193 | (LOCAL_ADDRESS>>16)&0xff, |
| | 194 | (LOCAL_ADDRESS>>8)&0xff, |
| | 195 | LOCAL_ADDRESS&0xff, |
| | 196 | "TCP", // or UDP |
| | 197 | LOCAL_PORT); |
| | 198 | |
| | 199 | mms_put_utf16(mms, data_string); |
| | 200 | return send_command_packet(mms); |
| | 201 | } |
| | 202 | |
| | 203 | static int send_media_file_request(MMSContext *mms) |
| | 204 | { |
| | 205 | start_command_packet(mms, CS_PKT_MEDIA_FILE_REQUEST); |
| | 206 | insert_command_prefixes(mms, 1, 0xffffffff); |
| | 207 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 208 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 209 | mms_put_utf16(mms, mms->path + 1); // +1 for skip "/" |
| | 210 | |
| | 211 | return send_command_packet(mms); |
| | 212 | } |
| | 213 | |
| | 214 | static void handle_packet_stream_changing_type(MMSContext *mms) |
| | 215 | { |
| | 216 | dprintf(NULL, "Stream changing!\n"); |
| | 217 | |
| | 218 | // 40 is the packet header size, 7 is the prefix size. |
| | 219 | mms->header_packet_id= AV_RL32(mms->in_buffer + 40 + 7); |
| | 220 | dprintf(NULL, "Changed header prefix to 0x%x", mms->header_packet_id); |
| | 221 | } |
| | 222 | |
| | 223 | static int send_keepalive_packet(MMSContext *mms) |
| | 224 | { |
| | 225 | // respond to a keepalive with a keepalive... |
| | 226 | start_command_packet(mms, CS_PKT_KEEPALIVE); |
| | 227 | insert_command_prefixes(mms, 1, 0x100FFFF); |
| | 228 | return send_command_packet(mms); |
| | 229 | } |
| | 230 | |
| | 231 | /** Pad media packets smaller than max_packet_size and/or adjust read position |
| | 232 | * after a seek. */ |
| | 233 | static void pad_media_packet(MMSContext *mms) |
| | 234 | { |
| | 235 | if(mms->remaining_in_len<mms->asf_packet_len) { |
| | 236 | int padding_size = mms->asf_packet_len - mms->remaining_in_len; |
| | 237 | memset(mms->in_buffer + mms->remaining_in_len, 0, padding_size); |
| | 238 | mms->remaining_in_len += padding_size; |
| | 239 | } |
| | 240 | } |
| | 241 | |
| | 242 | /** Read incoming MMST media, header or command packet. */ |
| | 243 | static MMSSCPacketType get_tcp_server_response(MMSContext *mms) |
| | 244 | { |
| | 245 | int read_result; |
| | 246 | MMSSCPacketType packet_type= -1; |
| | 247 | |
| | 248 | for(;;) { |
| | 249 | if((read_result= url_read_complete(mms->mms_hd, mms->in_buffer, 8))==8) { |
| | 250 | // handle command packet. |
| | 251 | if(AV_RL32(mms->in_buffer + 4)==0xb00bface) { |
| | 252 | mms->incoming_flags= mms->in_buffer[3]; |
| | 253 | read_result= url_read_complete(mms->mms_hd, mms->in_buffer+8, 4); |
| | 254 | if(read_result == 4) { |
| | 255 | int length_remaining= AV_RL32(mms->in_buffer+8) + 4; |
| | 256 | |
| | 257 | dprintf(NULL, "Length remaining is %d\n", length_remaining); |
| | 258 | // read the rest of the packet. |
| | 259 | if (length_remaining < 0 |
| | 260 | || length_remaining > sizeof(mms->in_buffer) - 12) { |
| | 261 | dprintf("Incoming message len %d exceeds buffer len %d\n", |
| | 262 | length_remaining, sizeof(mms->in_buffer) - 12); |
| | 263 | return -1; |
| | 264 | } |
| | 265 | read_result = url_read_complete(mms->mms_hd, mms->in_buffer + 12, |
| | 266 | length_remaining) ; |
| | 267 | if (read_result == length_remaining) { |
| | 268 | packet_type= AV_RL16(mms->in_buffer+36); |
| | 269 | } else { |
| | 270 | dprintf(NULL, "read for packet type failed%d!\n", read_result); |
| | 271 | return -1; |
| | 272 | } |
| | 273 | } else { |
| | 274 | dprintf(NULL, "read for length remaining failed%d!\n", read_result); |
| | 275 | return -1; |
| | 276 | } |
| | 277 | } else { |
| | 278 | int length_remaining; |
| | 279 | int packet_id_type; |
| | 280 | int tmp; |
| | 281 | |
| | 282 | assert(mms->remaining_in_len==0); |
| | 283 | |
| | 284 | // note we cache the first 8 bytes, |
| | 285 | // then fill up the buffer with the others |
| | 286 | tmp = AV_RL16(mms->in_buffer + 6); |
| | 287 | length_remaining = (tmp - 8) & 0xffff; |
| | 288 | mms->incoming_packet_seq = AV_RL32(mms->in_buffer); |
| | 289 | packet_id_type = mms->in_buffer[4]; |
| | 290 | mms->incoming_flags = mms->in_buffer[5]; |
| | 291 | |
| | 292 | if (length_remaining < 0 |
| | 293 | || length_remaining > sizeof(mms->in_buffer) - 8) { |
| | 294 | dprintf("Incoming data len %d exceeds buffer len %d\n", |
| | 295 | length_remaining, sizeof(mms->in_buffer)); |
| | 296 | return -1; |
| | 297 | } |
| | 298 | mms->remaining_in_len = length_remaining; |
| | 299 | mms->read_in_ptr = mms->in_buffer; |
| | 300 | read_result= url_read_complete(mms->mms_hd, mms->in_buffer, length_remaining); |
| | 301 | if(read_result != length_remaining) { |
| | 302 | dprintf(NULL, "read_bytes result: %d asking for %d\n", |
| | 303 | read_result, length_remaining); |
| | 304 | return -1; |
| | 305 | } else { |
| | 306 | // if we successfully read everything. |
| | 307 | if(packet_id_type == mms->header_packet_id) { |
| | 308 | packet_type = SC_PKT_ASF_HEADER; |
| | 309 | // Store the asf header |
| | 310 | if(!mms->header_parsed) { |
| | 311 | void *p = av_realloc(mms->asf_header, |
| | 312 | mms->asf_header_size |
| | 313 | + mms->remaining_in_len); |
| | 314 | if (!p) { |
| | 315 | av_freep(&mms->asf_header); |
| | 316 | return AVERROR(ENOMEM); |
| | 317 | } |
| | 318 | mms->asf_header = p; |
| | 319 | memcpy(mms->asf_header + mms->asf_header_size, |
| | 320 | mms->read_in_ptr, |
| | 321 | mms->remaining_in_len); |
| | 322 | mms->asf_header_size += mms->remaining_in_len; |
| | 323 | } |
| | 324 | } else if(packet_id_type == mms->packet_id) { |
| | 325 | packet_type = SC_PKT_ASF_MEDIA; |
| | 326 | } else { |
| | 327 | dprintf(NULL, "packet id type %d is old.", packet_id_type); |
| | 328 | continue; |
| | 329 | } |
| | 330 | } |
| | 331 | } |
| | 332 | |
| | 333 | // preprocess some packet type |
| | 334 | if(packet_type == SC_PKT_KEEPALIVE) { |
| | 335 | send_keepalive_packet(mms); |
| | 336 | continue; |
| | 337 | } else if(packet_type == SC_PKT_STREAM_CHANGING) { |
| | 338 | handle_packet_stream_changing_type(mms); |
| | 339 | } else if(packet_type == SC_PKT_ASF_MEDIA) { |
| | 340 | pad_media_packet(mms); |
| | 341 | } |
| | 342 | return packet_type; |
| | 343 | } else { |
| | 344 | if(read_result<0) { |
| | 345 | dprintf(NULL, "Read error (or cancelled) returned %d!\n", read_result); |
| | 346 | packet_type = SC_PKT_CANCEL; |
| | 347 | } else { |
| | 348 | dprintf(NULL, "Read result of zero?!\n"); |
| | 349 | packet_type = SC_PKT_NO_DATA; |
| | 350 | } |
| | 351 | return packet_type; |
| | 352 | } |
| | 353 | } |
| | 354 | } |
| | 355 | |
| | 356 | static int mms_safe_send_recv(MMSContext *mms, |
| | 357 | int (*send_fun)(MMSContext *mms), |
| | 358 | const MMSSCPacketType expect_type) |
| | 359 | { |
| | 360 | MMSSCPacketType type; |
| | 361 | if(send_fun) { |
| | 362 | int ret = send_fun(mms); |
| | 363 | if (ret < 0) { |
| | 364 | dprintf(NULL, "Send Packet error before expecting recv packet %d\n", expect_type); |
| | 365 | return ret; |
| | 366 | } |
| | 367 | } |
| | 368 | |
| | 369 | if ((type = get_tcp_server_response(mms)) != expect_type) { |
| | 370 | dprintf(NULL,"Unexpected packet type %d with type %d\n", type, expect_type); |
| | 371 | return -1; |
| | 372 | } else { |
| | 373 | return 0; |
| | 374 | } |
| | 375 | } |
| | 376 | |
| | 377 | static int send_media_header_request(MMSContext *mms) |
| | 378 | { |
| | 379 | start_command_packet(mms, CS_PKT_MEDIA_HEADER_REQUEST); |
| | 380 | insert_command_prefixes(mms, 1, 0); |
| | 381 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 382 | bytestream_put_le32(&mms->write_out_ptr, 0x00800000); |
| | 383 | bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); |
| | 384 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 385 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 386 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 387 | |
| | 388 | // the media preroll value in milliseconds? |
| | 389 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 390 | bytestream_put_le32(&mms->write_out_ptr, 0x40AC2000); |
| | 391 | bytestream_put_le32(&mms->write_out_ptr, 2); |
| | 392 | bytestream_put_le32(&mms->write_out_ptr, 0); |
| | 393 | |
| | 394 | return send_command_packet(mms); |
| | 395 | } |
| | 396 | |
| | 397 | /** Send the initial handshake. */ |
| | 398 | static int send_startup_packet(MMSContext *mms) |
| | 399 | { |
| | 400 | char data_string[256]; |
| | 401 | // SubscriberName is defined in MS specification linked below. |
| | 402 | // The guid value can be any valid value. |
| | 403 | // http://download.microsoft.com/ |
| | 404 | // download/9/5/E/95EF66AF-9026-4BB0-A41D-A4F81802D92C/%5BMS-WMSP%5D.pdf |
| | 405 | snprintf(data_string, sizeof(data_string), |
| | 406 | "NSPlayer/7.0.0.1956; {%s}; Host: %s", |
| | 407 | "7E667F5D-A661-495E-A512-F55686DDA178", mms->host); |
| | 408 | |
| | 409 | start_command_packet(mms, CS_PKT_INITIAL); |
| | 410 | insert_command_prefixes(mms, 0, 0x0004000b); |
| | 411 | bytestream_put_le32(&mms->write_out_ptr, 0x0003001c); |
| | 412 | mms_put_utf16(mms, data_string); |
| | 413 | return send_command_packet(mms); |
| | 414 | } |
| | 415 | |
| | 416 | static int asf_header_parser(MMSContext *mms) |
| | 417 | { |
| | 418 | uint8_t *p = mms->asf_header; |
| | 419 | uint8_t *end; |
| | 420 | int flags, stream_id, real_header_size; |
| | 421 | mms->stream_num = 0; |
| | 422 | |
| | 423 | if (mms->asf_header_size < sizeof(ff_asf_guid) * 2 + 22 || |
| | 424 | memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) |
| | 425 | return -1; |
| | 426 | |
| | 427 | real_header_size = AV_RL64(p + sizeof(ff_asf_guid)); |
| | 428 | end = mms->asf_header + real_header_size; |
| | 429 | |
| | 430 | p += sizeof(ff_asf_guid) + 14; |
| | 431 | while(end - p >= sizeof(ff_asf_guid) + 8) { |
| | 432 | uint64_t chunksize = AV_RL64(p + sizeof(ff_asf_guid)); |
| | 433 | if (!chunksize || chunksize > end - p) { |
| | 434 | dprintf("chunksize is exceptional value:%d!\n", chunksize); |
| | 435 | return -1; |
| | 436 | } |
| | 437 | if (!memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) { |
| | 438 | /* read packet size */ |
| | 439 | if (end - p > sizeof(ff_asf_guid) * 2 + 68) { |
| | 440 | mms->asf_packet_len = AV_RL32(p + sizeof(ff_asf_guid) * 2 + 64); |
| | 441 | if (mms->asf_packet_len <= 0 || mms->asf_packet_len > sizeof(mms->in_buffer)) { |
| | 442 | dprintf(NULL,"Too large packet len:%d" |
| | 443 | " may overwrite in_buffer when padding", mms->asf_packet_len); |
| | 444 | return -1; |
| | 445 | } |
| | 446 | } |
| | 447 | } else if (!memcmp(p, ff_asf_stream_header, sizeof(ff_asf_guid))) { |
| | 448 | flags = AV_RL16(p + sizeof(ff_asf_guid)*3 + 24); |
| | 449 | stream_id = flags & 0x7F; |
| | 450 | //The second condition is for checking CS_PKT_STREAM_ID_REQUEST packet size, |
| | 451 | //we can calcuate the packet size by stream_num. |
| | 452 | //Please see function send_stream_selection_request(). |
| | 453 | if (mms->stream_num < MAX_STREAMS && |
| | 454 | 46 + mms->stream_num * 6 < sizeof(mms->out_buffer)) { |
| | 455 | mms->streams[mms->stream_num].id = stream_id; |
| | 456 | mms->stream_num++; |
| | 457 | } else { |
| | 458 | dprintf("Too many streams.\n"); |
| | 459 | return -1; |
| | 460 | } |
| | 461 | } |
| | 462 | p += chunksize; |
| | 463 | } |
| | 464 | |
| | 465 | return 0; |
| | 466 | } |
| | 467 | |
| | 468 | /** Send MMST stream selection command based on the AVStream->discard values. */ |
| | 469 | static int send_stream_selection_request(MMSContext *mms) |
| | 470 | { |
| | 471 | int i; |
| | 472 | |
| | 473 | // send the streams we want back... |
| | 474 | start_command_packet(mms, CS_PKT_STREAM_ID_REQUEST); |
| | 475 | bytestream_put_le32(&mms->write_out_ptr, mms->stream_num); // stream nums |
| | 476 | for(i= 0; i<mms->stream_num; i++) { |
| | 477 | bytestream_put_le16(&mms->write_out_ptr, 0xffff); // flags |
| | 478 | bytestream_put_le16(&mms->write_out_ptr, mms->streams[i].id); // stream id |
| | 479 | bytestream_put_le16(&mms->write_out_ptr, 0); // selection |
| | 480 | } |
| | 481 | |
| | 482 | bytestream_put_le16(&mms->write_out_ptr, 0); |
| | 483 | |
| | 484 | return send_command_packet(mms); |
| | 485 | } |
| | 486 | |
| | 487 | static int read_data(MMSContext *mms, uint8_t *buf, const int buf_size) |
| | 488 | { |
| | 489 | int read_size; |
| | 490 | read_size = FFMIN(buf_size, mms->remaining_in_len); |
| | 491 | memcpy(buf, mms->read_in_ptr, read_size); |
| | 492 | mms->remaining_in_len -= read_size; |
| | 493 | mms->read_in_ptr += read_size; |
| | 494 | return read_size; |
| | 495 | } |
| | 496 | |
| | 497 | /** Read at most one media packet (or a whole header). */ |
| | 498 | static int read_mms_packet(MMSContext *mms, uint8_t *buf, int buf_size) |
| | 499 | { |
| | 500 | int result = 0, read_header_size = 0; |
| | 501 | int size_to_copy; |
| | 502 | |
| | 503 | do { |
| | 504 | if(read_header_size < mms->asf_header_size && !mms->is_playing) { |
| | 505 | /* Read from ASF header buffer */ |
| | 506 | size_to_copy= FFMIN(buf_size, |
| | 507 | mms->asf_header_size - read_header_size); |
| | 508 | memcpy(buf, mms->asf_header + read_header_size, size_to_copy); |
| | 509 | read_header_size += size_to_copy; |
| | 510 | result += size_to_copy; |
| | 511 | dprintf(NULL, "Copied %d bytes from stored header. left: %d\n", |
| | 512 | size_to_copy, mms->asf_header_size - read_header_size); |
| | 513 | if (mms->asf_header_size == read_header_size) { |
| | 514 | av_freep(&mms->asf_header); |
| | 515 | mms->is_playing = 1; |
| | 516 | } |
| | 517 | } else if(mms->remaining_in_len) { |
| | 518 | /* Read remaining packet data to buffer. |
| | 519 | * the result can not be zero because remaining_in_len is positive.*/ |
| | 520 | result = read_data(mms, buf, buf_size); |
| | 521 | } else { |
| | 522 | /* Read from network */ |
| | 523 | int err = mms_safe_send_recv(mms, NULL, SC_PKT_ASF_MEDIA); |
| | 524 | if (err == 0) { |
| | 525 | if(mms->remaining_in_len>mms->asf_packet_len) { |
| | 526 | dprintf(NULL, "Incoming packet" |
| | 527 | "larger than the asf packet size stated (%d>%d)\n", |
| | 528 | mms->remaining_in_len, mms->asf_packet_len); |
| | 529 | result= AVERROR_IO; |
| | 530 | } else { |
| | 531 | // copy the data to the packet buffer. |
| | 532 | result = read_data(mms, buf, buf_size); |
| | 533 | if (result == 0) { |
| | 534 | dprintf(NULL, "read asf media paket size is zero!\n"); |
| | 535 | break; |
| | 536 | } |
| | 537 | } |
| | 538 | } else { |
| | 539 | dprintf(NULL, "read packet error!\n"); |
| | 540 | break; |
| | 541 | } |
| | 542 | } |
| | 543 | } while(!result); // only return one packet. |
| | 544 | return result; |
| | 545 | } |
| | 546 | |
| | 547 | static int send_close_packet(MMSContext *mms) |
| | 548 | { |
| | 549 | start_command_packet(mms, CS_PKT_STREAM_CLOSE); |
| | 550 | insert_command_prefixes(mms, 1, 1); |
| | 551 | |
| | 552 | return send_command_packet(mms); |
| | 553 | } |
| | 554 | |
| | 555 | /** Close the MMSH/MMST connection */ |
| | 556 | static int mms_close(URLContext *h) |
| | 557 | { |
| | 558 | MMSContext *mms = (MMSContext *)h->priv_data; |
| | 559 | |
| | 560 | if(mms->mms_hd) { |
| | 561 | send_close_packet(mms); |
| | 562 | url_close(mms->mms_hd); |
| | 563 | } |
| | 564 | |
| | 565 | /* free all separately allocated pointers in mms */ |
| | 566 | av_free(mms->asf_header); |
| | 567 | av_freep(&h->priv_data); |
| | 568 | |
| | 569 | return 0; |
| | 570 | } |
| | 571 | |
| | 572 | static int mms_open(URLContext *h, const char *uri, int flags) |
| | 573 | { |
| | 574 | MMSContext *mms; |
| | 575 | int port, err; |
| | 576 | char tcpname[256]; |
| | 577 | |
| | 578 | h->is_streamed = 1; |
| | 579 | mms = h->priv_data = av_mallocz(sizeof(MMSContext)); |
| | 580 | if (!h->priv_data) |
| | 581 | return AVERROR(ENOMEM); |
| | 582 | |
| | 583 | // only for MMS over TCP, so set proto = NULL |
| | 584 | ff_url_split(NULL, 0, NULL, 0, |
| | 585 | mms->host, sizeof(mms->host), &port, mms->path, |
| | 586 | sizeof(mms->path), uri); |
| | 587 | |
| | 588 | if(port<0) |
| | 589 | port = 1755; // defaut mms protocol port |
| | 590 | |
| | 591 | // establish tcp connection. |
| | 592 | ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, mms->host, port, NULL); |
| | 593 | err = url_open(&mms->mms_hd, tcpname, URL_RDWR); |
| | 594 | if (err) |
| | 595 | goto fail; |
| | 596 | |
| | 597 | mms->packet_id = 3; // default, initial value. |
| | 598 | mms->header_packet_id = 2; // default, initial value. |
| | 599 | err = mms_safe_send_recv(mms, send_startup_packet, SC_PKT_CLIENT_ACCEPTED); |
| | 600 | if (err) |
| | 601 | goto fail; |
| | 602 | err = mms_safe_send_recv(mms, send_protocol_select, SC_PKT_PROTOCOL_ACCEPTED); |
| | 603 | if (err) |
| | 604 | goto fail; |
| | 605 | err = mms_safe_send_recv(mms, send_media_file_request, SC_PKT_MEDIA_FILE_DETAILS); |
| | 606 | if (err) |
| | 607 | goto fail; |
| | 608 | err = mms_safe_send_recv(mms, send_media_header_request, SC_PKT_HEADER_REQUEST_ACCEPTED); |
| | 609 | if (err) |
| | 610 | goto fail; |
| | 611 | err = mms_safe_send_recv(mms, NULL, SC_PKT_ASF_HEADER); |
| | 612 | if (err) |
| | 613 | goto fail; |
| | 614 | if((mms->incoming_flags != 0X08) && (mms->incoming_flags != 0X0C)) |
| | 615 | goto fail; |
| | 616 | err = asf_header_parser(mms); |
| | 617 | if (err) { |
| | 618 | dprintf(NULL, "asf header parsed failed!\n"); |
| | 619 | goto fail; |
| | 620 | } |
| | 621 | mms->header_parsed = 1; |
| | 622 | |
| | 623 | if (!mms->asf_packet_len || !mms->stream_num) |
| | 624 | goto fail; |
| | 625 | |
| | 626 | dprintf(NULL, "Leaving open (success)\n"); |
| | 627 | return 0; |
| | 628 | fail: |
| | 629 | mms_close(h); |
| | 630 | dprintf(NULL, "Leaving open (failure: %d)\n", err); |
| | 631 | return err; |
| | 632 | } |
| | 633 | |
| | 634 | static int send_media_packet_request(MMSContext *mms) |
| | 635 | { |
| | 636 | start_command_packet(mms, CS_PKT_START_FROM_PKT_ID); |
| | 637 | insert_command_prefixes(mms, 1, 0x0001FFFF); |
| | 638 | bytestream_put_le64(&mms->write_out_ptr, 0); // seek timestamp |
| | 639 | bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); // unknown |
| | 640 | bytestream_put_le32(&mms->write_out_ptr, 0xffffffff); // packet offset |
| | 641 | bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit |
| | 642 | bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit |
| | 643 | bytestream_put_byte(&mms->write_out_ptr, 0xff); // max stream time limit |
| | 644 | bytestream_put_byte(&mms->write_out_ptr, 0x00); // stream time limit flag |
| | 645 | |
| | 646 | mms->packet_id++; // new packet_id |
| | 647 | bytestream_put_le32(&mms->write_out_ptr, mms->packet_id); |
| | 648 | return send_command_packet(mms); |
| | 649 | } |
| | 650 | |
| | 651 | |
| | 652 | static void clear_stream_buffers(MMSContext *mms) |
| | 653 | { |
| | 654 | mms->remaining_in_len = 0; |
| | 655 | mms->read_in_ptr = mms->in_buffer; |
| | 656 | } |
| | 657 | |
| | 658 | /** Read ASF data through the protocol. */ |
| | 659 | static int mms_read(URLContext *h, uint8_t *buf, int size) |
| | 660 | { |
| | 661 | /* TODO: see tcp.c:tcp_read() about a possible timeout scheme */ |
| | 662 | MMSContext *mms = h->priv_data; |
| | 663 | int result = 0; |
| | 664 | |
| | 665 | /* Since we read the header at open(), this shouldn't be possible */ |
| | 666 | assert(mms->header_parsed); |
| | 667 | |
| | 668 | if (!mms->is_playing) { |
| | 669 | dprintf(NULL, "mms_read() before play().\n"); |
| | 670 | clear_stream_buffers(mms); |
| | 671 | result = mms_safe_send_recv(mms, send_stream_selection_request, SC_PKT_STREAM_ID_ACCEPTED); |
| | 672 | if (result) |
| | 673 | return result; |
| | 674 | // send media packet request |
| | 675 | result = mms_safe_send_recv(mms, send_media_packet_request, SC_PKT_MEDIA_PKT_FOLLOWS); |
| | 676 | if (result) { |
| | 677 | return result; |
| | 678 | } |
| | 679 | } |
| | 680 | return read_mms_packet(mms, buf, size); |
| | 681 | } |
| | 682 | |
| | 683 | URLProtocol mmst_protocol = { |
| | 684 | "mmst", |
| | 685 | mms_open, |
| | 686 | mms_read, |
| | 687 | NULL, // write |
| | 688 | NULL, // seek |
| | 689 | mms_close, |
| | 690 | }; |