annotate ffmpeg/libavformat/mmsh.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents f445c3017523
children
rev   line source
yading@11 1 /*
yading@11 2 * MMS protocol over HTTP
yading@11 3 * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com>
yading@11 4 *
yading@11 5 * This file is part of FFmpeg.
yading@11 6 *
yading@11 7 * FFmpeg is free software; you can redistribute it and/or
yading@11 8 * modify it under the terms of the GNU Lesser General Public
yading@11 9 * License as published by the Free Software Foundation; either
yading@11 10 * version 2.1 of the License, or (at your option) any later version.
yading@11 11 *
yading@11 12 * FFmpeg is distributed in the hope that it will be useful,
yading@11 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
yading@11 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
yading@11 15 * Lesser General Public License for more details.
yading@11 16 *
yading@11 17 * You should have received a copy of the GNU Lesser General Public
yading@11 18 * License along with FFmpeg; if not, write to the Free Software
yading@11 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
yading@11 20 */
yading@11 21
yading@11 22 /*
yading@11 23 * Reference
yading@11 24 * Windows Media HTTP Streaming Protocol.
yading@11 25 * http://msdn.microsoft.com/en-us/library/cc251059(PROT.10).aspx
yading@11 26 */
yading@11 27
yading@11 28 #include <string.h>
yading@11 29 #include "libavutil/intreadwrite.h"
yading@11 30 #include "libavutil/avstring.h"
yading@11 31 #include "libavutil/opt.h"
yading@11 32 #include "internal.h"
yading@11 33 #include "mms.h"
yading@11 34 #include "asf.h"
yading@11 35 #include "http.h"
yading@11 36 #include "url.h"
yading@11 37
yading@11 38 #define CHUNK_HEADER_LENGTH 4 // 2bytes chunk type and 2bytes chunk length.
yading@11 39 #define EXT_HEADER_LENGTH 8 // 4bytes sequence, 2bytes useless and 2bytes chunk length.
yading@11 40
yading@11 41 // see Ref 2.2.1.8
yading@11 42 #define USERAGENT "User-Agent: NSPlayer/4.1.0.3856\r\n"
yading@11 43 // see Ref 2.2.1.4.33
yading@11 44 // the guid value can be changed to any valid value.
yading@11 45 #define CLIENTGUID "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"
yading@11 46
yading@11 47 // see Ref 2.2.3 for packet type define:
yading@11 48 // chunk type contains 2 fields: Frame and PacketID.
yading@11 49 // Frame is 0x24 or 0xA4(rarely), different PacketID indicates different packet type.
yading@11 50 typedef enum {
yading@11 51 CHUNK_TYPE_DATA = 0x4424,
yading@11 52 CHUNK_TYPE_ASF_HEADER = 0x4824,
yading@11 53 CHUNK_TYPE_END = 0x4524,
yading@11 54 CHUNK_TYPE_STREAM_CHANGE = 0x4324,
yading@11 55 } ChunkType;
yading@11 56
yading@11 57 typedef struct {
yading@11 58 MMSContext mms;
yading@11 59 uint8_t location[1024];
yading@11 60 int request_seq; ///< request packet sequence
yading@11 61 int chunk_seq; ///< data packet sequence
yading@11 62 } MMSHContext;
yading@11 63
yading@11 64 static int mmsh_close(URLContext *h)
yading@11 65 {
yading@11 66 MMSHContext *mmsh = (MMSHContext *)h->priv_data;
yading@11 67 MMSContext *mms = &mmsh->mms;
yading@11 68 if (mms->mms_hd)
yading@11 69 ffurl_close(mms->mms_hd);
yading@11 70 av_free(mms->streams);
yading@11 71 av_free(mms->asf_header);
yading@11 72 return 0;
yading@11 73 }
yading@11 74
yading@11 75 static ChunkType get_chunk_header(MMSHContext *mmsh, int *len)
yading@11 76 {
yading@11 77 MMSContext *mms = &mmsh->mms;
yading@11 78 uint8_t chunk_header[CHUNK_HEADER_LENGTH];
yading@11 79 uint8_t ext_header[EXT_HEADER_LENGTH];
yading@11 80 ChunkType chunk_type;
yading@11 81 int chunk_len, res, ext_header_len;
yading@11 82
yading@11 83 res = ffurl_read_complete(mms->mms_hd, chunk_header, CHUNK_HEADER_LENGTH);
yading@11 84 if (res != CHUNK_HEADER_LENGTH) {
yading@11 85 av_log(NULL, AV_LOG_ERROR, "Read data packet header failed!\n");
yading@11 86 return AVERROR(EIO);
yading@11 87 }
yading@11 88 chunk_type = AV_RL16(chunk_header);
yading@11 89 chunk_len = AV_RL16(chunk_header + 2);
yading@11 90
yading@11 91 switch (chunk_type) {
yading@11 92 case CHUNK_TYPE_END:
yading@11 93 case CHUNK_TYPE_STREAM_CHANGE:
yading@11 94 ext_header_len = 4;
yading@11 95 break;
yading@11 96 case CHUNK_TYPE_ASF_HEADER:
yading@11 97 case CHUNK_TYPE_DATA:
yading@11 98 ext_header_len = 8;
yading@11 99 break;
yading@11 100 default:
yading@11 101 av_log(NULL, AV_LOG_ERROR, "Strange chunk type %d\n", chunk_type);
yading@11 102 return AVERROR_INVALIDDATA;
yading@11 103 }
yading@11 104
yading@11 105 res = ffurl_read_complete(mms->mms_hd, ext_header, ext_header_len);
yading@11 106 if (res != ext_header_len) {
yading@11 107 av_log(NULL, AV_LOG_ERROR, "Read ext header failed!\n");
yading@11 108 return AVERROR(EIO);
yading@11 109 }
yading@11 110 *len = chunk_len - ext_header_len;
yading@11 111 if (chunk_type == CHUNK_TYPE_END || chunk_type == CHUNK_TYPE_DATA)
yading@11 112 mmsh->chunk_seq = AV_RL32(ext_header);
yading@11 113 return chunk_type;
yading@11 114 }
yading@11 115
yading@11 116 static int read_data_packet(MMSHContext *mmsh, const int len)
yading@11 117 {
yading@11 118 MMSContext *mms = &mmsh->mms;
yading@11 119 int res;
yading@11 120 if (len > sizeof(mms->in_buffer)) {
yading@11 121 av_log(NULL, AV_LOG_ERROR,
yading@11 122 "Data packet length %d exceeds the in_buffer size %zu\n",
yading@11 123 len, sizeof(mms->in_buffer));
yading@11 124 return AVERROR(EIO);
yading@11 125 }
yading@11 126 res = ffurl_read_complete(mms->mms_hd, mms->in_buffer, len);
yading@11 127 av_dlog(NULL, "Data packet len = %d\n", len);
yading@11 128 if (res != len) {
yading@11 129 av_log(NULL, AV_LOG_ERROR, "Read data packet failed!\n");
yading@11 130 return AVERROR(EIO);
yading@11 131 }
yading@11 132 if (len > mms->asf_packet_len) {
yading@11 133 av_log(NULL, AV_LOG_ERROR,
yading@11 134 "Chunk length %d exceed packet length %d\n",len, mms->asf_packet_len);
yading@11 135 return AVERROR_INVALIDDATA;
yading@11 136 } else {
yading@11 137 memset(mms->in_buffer + len, 0, mms->asf_packet_len - len); // padding
yading@11 138 }
yading@11 139 mms->read_in_ptr = mms->in_buffer;
yading@11 140 mms->remaining_in_len = mms->asf_packet_len;
yading@11 141 return 0;
yading@11 142 }
yading@11 143
yading@11 144 static int get_http_header_data(MMSHContext *mmsh)
yading@11 145 {
yading@11 146 MMSContext *mms = &mmsh->mms;
yading@11 147 int res, len;
yading@11 148 ChunkType chunk_type;
yading@11 149
yading@11 150 for (;;) {
yading@11 151 len = 0;
yading@11 152 res = chunk_type = get_chunk_header(mmsh, &len);
yading@11 153 if (res < 0) {
yading@11 154 return res;
yading@11 155 } else if (chunk_type == CHUNK_TYPE_ASF_HEADER){
yading@11 156 // get asf header and stored it
yading@11 157 if (!mms->header_parsed) {
yading@11 158 if (mms->asf_header) {
yading@11 159 if (len != mms->asf_header_size) {
yading@11 160 mms->asf_header_size = len;
yading@11 161 av_dlog(NULL, "Header len changed from %d to %d\n",
yading@11 162 mms->asf_header_size, len);
yading@11 163 av_freep(&mms->asf_header);
yading@11 164 }
yading@11 165 }
yading@11 166 mms->asf_header = av_mallocz(len);
yading@11 167 if (!mms->asf_header) {
yading@11 168 return AVERROR(ENOMEM);
yading@11 169 }
yading@11 170 mms->asf_header_size = len;
yading@11 171 }
yading@11 172 if (len > mms->asf_header_size) {
yading@11 173 av_log(NULL, AV_LOG_ERROR,
yading@11 174 "Asf header packet len = %d exceed the asf header buf size %d\n",
yading@11 175 len, mms->asf_header_size);
yading@11 176 return AVERROR(EIO);
yading@11 177 }
yading@11 178 res = ffurl_read_complete(mms->mms_hd, mms->asf_header, len);
yading@11 179 if (res != len) {
yading@11 180 av_log(NULL, AV_LOG_ERROR,
yading@11 181 "Recv asf header data len %d != expected len %d\n", res, len);
yading@11 182 return AVERROR(EIO);
yading@11 183 }
yading@11 184 mms->asf_header_size = len;
yading@11 185 if (!mms->header_parsed) {
yading@11 186 res = ff_mms_asf_header_parser(mms);
yading@11 187 mms->header_parsed = 1;
yading@11 188 return res;
yading@11 189 }
yading@11 190 } else if (chunk_type == CHUNK_TYPE_DATA) {
yading@11 191 // read data packet and do padding
yading@11 192 return read_data_packet(mmsh, len);
yading@11 193 } else {
yading@11 194 if (len) {
yading@11 195 if (len > sizeof(mms->in_buffer)) {
yading@11 196 av_log(NULL, AV_LOG_ERROR,
yading@11 197 "Other packet len = %d exceed the in_buffer size %zu\n",
yading@11 198 len, sizeof(mms->in_buffer));
yading@11 199 return AVERROR(EIO);
yading@11 200 }
yading@11 201 res = ffurl_read_complete(mms->mms_hd, mms->in_buffer, len);
yading@11 202 if (res != len) {
yading@11 203 av_log(NULL, AV_LOG_ERROR, "Read other chunk type data failed!\n");
yading@11 204 return AVERROR(EIO);
yading@11 205 } else {
yading@11 206 av_dlog(NULL, "Skip chunk type %d \n", chunk_type);
yading@11 207 continue;
yading@11 208 }
yading@11 209 }
yading@11 210 }
yading@11 211 }
yading@11 212 }
yading@11 213
yading@11 214 static int mmsh_open_internal(URLContext *h, const char *uri, int flags, int timestamp, int64_t pos)
yading@11 215 {
yading@11 216 int i, port, err;
yading@11 217 char httpname[256], path[256], host[128];
yading@11 218 char *stream_selection = NULL;
yading@11 219 char headers[1024];
yading@11 220 MMSHContext *mmsh = h->priv_data;
yading@11 221 MMSContext *mms;
yading@11 222
yading@11 223 mmsh->request_seq = h->is_streamed = 1;
yading@11 224 mms = &mmsh->mms;
yading@11 225 av_strlcpy(mmsh->location, uri, sizeof(mmsh->location));
yading@11 226
yading@11 227 av_url_split(NULL, 0, NULL, 0,
yading@11 228 host, sizeof(host), &port, path, sizeof(path), mmsh->location);
yading@11 229 if (port<0)
yading@11 230 port = 80; // default mmsh protocol port
yading@11 231 ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path);
yading@11 232
yading@11 233 if (ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ,
yading@11 234 &h->interrupt_callback) < 0) {
yading@11 235 return AVERROR(EIO);
yading@11 236 }
yading@11 237
yading@11 238 snprintf(headers, sizeof(headers),
yading@11 239 "Accept: */*\r\n"
yading@11 240 USERAGENT
yading@11 241 "Host: %s:%d\r\n"
yading@11 242 "Pragma: no-cache,rate=1.000000,stream-time=0,"
yading@11 243 "stream-offset=0:0,request-context=%u,max-duration=0\r\n"
yading@11 244 CLIENTGUID
yading@11 245 "Connection: Close\r\n",
yading@11 246 host, port, mmsh->request_seq++);
yading@11 247 av_opt_set(mms->mms_hd->priv_data, "headers", headers, 0);
yading@11 248
yading@11 249 err = ffurl_connect(mms->mms_hd, NULL);
yading@11 250 if (err) {
yading@11 251 goto fail;
yading@11 252 }
yading@11 253 err = get_http_header_data(mmsh);
yading@11 254 if (err) {
yading@11 255 av_log(NULL, AV_LOG_ERROR, "Get http header data failed!\n");
yading@11 256 goto fail;
yading@11 257 }
yading@11 258
yading@11 259 // close the socket and then reopen it for sending the second play request.
yading@11 260 ffurl_close(mms->mms_hd);
yading@11 261 memset(headers, 0, sizeof(headers));
yading@11 262 if ((err = ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ,
yading@11 263 &h->interrupt_callback)) < 0) {
yading@11 264 goto fail;
yading@11 265 }
yading@11 266 stream_selection = av_mallocz(mms->stream_num * 19 + 1);
yading@11 267 if (!stream_selection)
yading@11 268 return AVERROR(ENOMEM);
yading@11 269 for (i = 0; i < mms->stream_num; i++) {
yading@11 270 char tmp[20];
yading@11 271 err = snprintf(tmp, sizeof(tmp), "ffff:%d:0 ", mms->streams[i].id);
yading@11 272 if (err < 0)
yading@11 273 goto fail;
yading@11 274 av_strlcat(stream_selection, tmp, mms->stream_num * 19 + 1);
yading@11 275 }
yading@11 276 // send play request
yading@11 277 err = snprintf(headers, sizeof(headers),
yading@11 278 "Accept: */*\r\n"
yading@11 279 USERAGENT
yading@11 280 "Host: %s:%d\r\n"
yading@11 281 "Pragma: no-cache,rate=1.000000,request-context=%u\r\n"
yading@11 282 "Pragma: xPlayStrm=1\r\n"
yading@11 283 CLIENTGUID
yading@11 284 "Pragma: stream-switch-count=%d\r\n"
yading@11 285 "Pragma: stream-switch-entry=%s\r\n"
yading@11 286 "Pragma: no-cache,rate=1.000000,stream-time=%u"
yading@11 287 "Connection: Close\r\n",
yading@11 288 host, port, mmsh->request_seq++, mms->stream_num, stream_selection, timestamp);
yading@11 289 av_freep(&stream_selection);
yading@11 290 if (err < 0) {
yading@11 291 av_log(NULL, AV_LOG_ERROR, "Build play request failed!\n");
yading@11 292 goto fail;
yading@11 293 }
yading@11 294 av_dlog(NULL, "out_buffer is %s", headers);
yading@11 295 av_opt_set(mms->mms_hd->priv_data, "headers", headers, 0);
yading@11 296
yading@11 297 err = ffurl_connect(mms->mms_hd, NULL);
yading@11 298 if (err) {
yading@11 299 goto fail;
yading@11 300 }
yading@11 301
yading@11 302 err = get_http_header_data(mmsh);
yading@11 303 if (err) {
yading@11 304 av_log(NULL, AV_LOG_ERROR, "Get http header data failed!\n");
yading@11 305 goto fail;
yading@11 306 }
yading@11 307
yading@11 308 av_dlog(NULL, "Connection successfully open\n");
yading@11 309 return 0;
yading@11 310 fail:
yading@11 311 av_freep(&stream_selection);
yading@11 312 mmsh_close(h);
yading@11 313 av_dlog(NULL, "Connection failed with error %d\n", err);
yading@11 314 return err;
yading@11 315 }
yading@11 316
yading@11 317 static int mmsh_open(URLContext *h, const char *uri, int flags)
yading@11 318 {
yading@11 319 return mmsh_open_internal(h, uri, flags, 0, 0);
yading@11 320 }
yading@11 321
yading@11 322 static int handle_chunk_type(MMSHContext *mmsh)
yading@11 323 {
yading@11 324 MMSContext *mms = &mmsh->mms;
yading@11 325 int res, len = 0;
yading@11 326 ChunkType chunk_type;
yading@11 327 chunk_type = get_chunk_header(mmsh, &len);
yading@11 328
yading@11 329 switch (chunk_type) {
yading@11 330 case CHUNK_TYPE_END:
yading@11 331 mmsh->chunk_seq = 0;
yading@11 332 av_log(NULL, AV_LOG_ERROR, "Stream ended!\n");
yading@11 333 return AVERROR(EIO);
yading@11 334 case CHUNK_TYPE_STREAM_CHANGE:
yading@11 335 mms->header_parsed = 0;
yading@11 336 if (res = get_http_header_data(mmsh)) {
yading@11 337 av_log(NULL, AV_LOG_ERROR,"Stream changed! Failed to get new header!\n");
yading@11 338 return res;
yading@11 339 }
yading@11 340 break;
yading@11 341 case CHUNK_TYPE_DATA:
yading@11 342 return read_data_packet(mmsh, len);
yading@11 343 default:
yading@11 344 av_log(NULL, AV_LOG_ERROR, "Recv other type packet %d\n", chunk_type);
yading@11 345 return AVERROR_INVALIDDATA;
yading@11 346 }
yading@11 347 return 0;
yading@11 348 }
yading@11 349
yading@11 350 static int mmsh_read(URLContext *h, uint8_t *buf, int size)
yading@11 351 {
yading@11 352 int res = 0;
yading@11 353 MMSHContext *mmsh = h->priv_data;
yading@11 354 MMSContext *mms = &mmsh->mms;
yading@11 355 do {
yading@11 356 if (mms->asf_header_read_size < mms->asf_header_size) {
yading@11 357 // copy asf header into buffer
yading@11 358 res = ff_mms_read_header(mms, buf, size);
yading@11 359 } else {
yading@11 360 if (!mms->remaining_in_len && (res = handle_chunk_type(mmsh)))
yading@11 361 return res;
yading@11 362 res = ff_mms_read_data(mms, buf, size);
yading@11 363 }
yading@11 364 } while (!res);
yading@11 365 return res;
yading@11 366 }
yading@11 367
yading@11 368 static int64_t mmsh_read_seek(URLContext *h, int stream_index,
yading@11 369 int64_t timestamp, int flags)
yading@11 370 {
yading@11 371 MMSHContext *mmsh = h->priv_data;
yading@11 372 MMSContext *mms = &mmsh->mms;
yading@11 373 int ret;
yading@11 374
yading@11 375 ret= mmsh_open_internal(h, mmsh->location, 0, FFMAX(timestamp, 0), 0);
yading@11 376
yading@11 377 if(ret>=0){
yading@11 378 if (mms->mms_hd)
yading@11 379 ffurl_close(mms->mms_hd);
yading@11 380 av_freep(&mms->streams);
yading@11 381 av_freep(&mms->asf_header);
yading@11 382 av_free(mmsh);
yading@11 383 mmsh = h->priv_data;
yading@11 384 mms = &mmsh->mms;
yading@11 385 mms->asf_header_read_size= mms->asf_header_size;
yading@11 386 }else
yading@11 387 h->priv_data= mmsh;
yading@11 388 return ret;
yading@11 389 }
yading@11 390
yading@11 391 static int64_t mmsh_seek(URLContext *h, int64_t pos, int whence)
yading@11 392 {
yading@11 393 MMSHContext *mmsh = h->priv_data;
yading@11 394 MMSContext *mms = &mmsh->mms;
yading@11 395
yading@11 396 if(pos == 0 && whence == SEEK_CUR)
yading@11 397 return mms->asf_header_read_size + mms->remaining_in_len + mmsh->chunk_seq * (int64_t)mms->asf_packet_len;
yading@11 398 return AVERROR(ENOSYS);
yading@11 399 }
yading@11 400
yading@11 401 URLProtocol ff_mmsh_protocol = {
yading@11 402 .name = "mmsh",
yading@11 403 .url_open = mmsh_open,
yading@11 404 .url_read = mmsh_read,
yading@11 405 .url_seek = mmsh_seek,
yading@11 406 .url_close = mmsh_close,
yading@11 407 .url_read_seek = mmsh_read_seek,
yading@11 408 .priv_data_size = sizeof(MMSHContext),
yading@11 409 .flags = URL_PROTOCOL_FLAG_NETWORK,
yading@11 410 };