00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include <string.h>
00029 #include "libavutil/intreadwrite.h"
00030 #include "libavutil/avstring.h"
00031 #include "libavformat/internal.h"
00032 #include "mms.h"
00033 #include "asf.h"
00034 #include "http.h"
00035
00036 #define CHUNK_HEADER_LENGTH 4 // 2bytes chunk type and 2bytes chunk length.
00037 #define EXT_HEADER_LENGTH 8 // 4bytes sequence, 2bytes useless and 2bytes chunk length.
00038
00039
00040 #define USERAGENT "User-Agent: NSPlayer/4.1.0.3856\r\n"
00041
00042
00043 #define CLIENTGUID "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"
00044
00045
00046
00047
00048 typedef enum {
00049 CHUNK_TYPE_DATA = 0x4424,
00050 CHUNK_TYPE_ASF_HEADER = 0x4824,
00051 CHUNK_TYPE_END = 0x4524,
00052 CHUNK_TYPE_STREAM_CHANGE = 0x4324,
00053 } ChunkType;
00054
00055 typedef struct {
00056 MMSContext mms;
00057 int request_seq;
00058 int chunk_seq;
00059 } MMSHContext;
00060
00061 static int mmsh_close(URLContext *h)
00062 {
00063 MMSHContext *mmsh = (MMSHContext *)h->priv_data;
00064 MMSContext *mms = &mmsh->mms;
00065 if (mms->mms_hd)
00066 url_close(mms->mms_hd);
00067 av_free(mms->streams);
00068 av_free(mms->asf_header);
00069 av_freep(&h->priv_data);
00070 return 0;
00071 }
00072
00073 static ChunkType get_chunk_header(MMSHContext *mmsh, int *len)
00074 {
00075 MMSContext *mms = &mmsh->mms;
00076 uint8_t chunk_header[CHUNK_HEADER_LENGTH];
00077 uint8_t ext_header[EXT_HEADER_LENGTH];
00078 ChunkType chunk_type;
00079 int chunk_len, res, ext_header_len;
00080
00081 res = url_read_complete(mms->mms_hd, chunk_header, CHUNK_HEADER_LENGTH);
00082 if (res != CHUNK_HEADER_LENGTH) {
00083 av_log(NULL, AV_LOG_ERROR, "Read data packet header failed!\n");
00084 return AVERROR(EIO);
00085 }
00086 chunk_type = AV_RL16(chunk_header);
00087 chunk_len = AV_RL16(chunk_header + 2);
00088
00089 switch (chunk_type) {
00090 case CHUNK_TYPE_END:
00091 case CHUNK_TYPE_STREAM_CHANGE:
00092 ext_header_len = 4;
00093 break;
00094 case CHUNK_TYPE_ASF_HEADER:
00095 case CHUNK_TYPE_DATA:
00096 ext_header_len = 8;
00097 break;
00098 default:
00099 av_log(NULL, AV_LOG_ERROR, "Strange chunk type %d\n", chunk_type);
00100 return AVERROR_INVALIDDATA;
00101 }
00102
00103 res = url_read_complete(mms->mms_hd, ext_header, ext_header_len);
00104 if (res != ext_header_len) {
00105 av_log(NULL, AV_LOG_ERROR, "Read ext header failed!\n");
00106 return AVERROR(EIO);
00107 }
00108 *len = chunk_len - ext_header_len;
00109 if (chunk_type == CHUNK_TYPE_END || chunk_type == CHUNK_TYPE_DATA)
00110 mmsh->chunk_seq = AV_RL32(ext_header);
00111 return chunk_type;
00112 }
00113
00114 static int read_data_packet(MMSHContext *mmsh, const int len)
00115 {
00116 MMSContext *mms = &mmsh->mms;
00117 int res;
00118 if (len > sizeof(mms->in_buffer)) {
00119 av_log(NULL, AV_LOG_ERROR,
00120 "Data packet length %d exceeds the in_buffer size %zu\n",
00121 len, sizeof(mms->in_buffer));
00122 return AVERROR(EIO);
00123 }
00124 res = url_read_complete(mms->mms_hd, mms->in_buffer, len);
00125 av_dlog(NULL, "Data packet len = %d\n", len);
00126 if (res != len) {
00127 av_log(NULL, AV_LOG_ERROR, "Read data packet failed!\n");
00128 return AVERROR(EIO);
00129 }
00130 if (len > mms->asf_packet_len) {
00131 av_log(NULL, AV_LOG_ERROR,
00132 "Chunk length %d exceed packet length %d\n",len, mms->asf_packet_len);
00133 return AVERROR_INVALIDDATA;
00134 } else {
00135 memset(mms->in_buffer + len, 0, mms->asf_packet_len - len);
00136 }
00137 mms->read_in_ptr = mms->in_buffer;
00138 mms->remaining_in_len = mms->asf_packet_len;
00139 return 0;
00140 }
00141
00142 static int get_http_header_data(MMSHContext *mmsh)
00143 {
00144 MMSContext *mms = &mmsh->mms;
00145 int res, len;
00146 ChunkType chunk_type;
00147
00148 for (;;) {
00149 len = 0;
00150 res = chunk_type = get_chunk_header(mmsh, &len);
00151 if (res < 0) {
00152 return res;
00153 } else if (chunk_type == CHUNK_TYPE_ASF_HEADER){
00154
00155 if (!mms->header_parsed) {
00156 if (mms->asf_header) {
00157 if (len != mms->asf_header_size) {
00158 mms->asf_header_size = len;
00159 av_dlog(NULL, "Header len changed from %d to %d\n",
00160 mms->asf_header_size, len);
00161 av_freep(&mms->asf_header);
00162 }
00163 }
00164 mms->asf_header = av_mallocz(len);
00165 if (!mms->asf_header) {
00166 return AVERROR(ENOMEM);
00167 }
00168 mms->asf_header_size = len;
00169 }
00170 if (len > mms->asf_header_size) {
00171 av_log(NULL, AV_LOG_ERROR,
00172 "Asf header packet len = %d exceed the asf header buf size %d\n",
00173 len, mms->asf_header_size);
00174 return AVERROR(EIO);
00175 }
00176 res = url_read_complete(mms->mms_hd, mms->asf_header, len);
00177 if (res != len) {
00178 av_log(NULL, AV_LOG_ERROR,
00179 "Recv asf header data len %d != expected len %d\n", res, len);
00180 return AVERROR(EIO);
00181 }
00182 mms->asf_header_size = len;
00183 if (!mms->header_parsed) {
00184 res = ff_mms_asf_header_parser(mms);
00185 mms->header_parsed = 1;
00186 return res;
00187 }
00188 } else if (chunk_type == CHUNK_TYPE_DATA) {
00189
00190 return read_data_packet(mmsh, len);
00191 } else {
00192 if (len) {
00193 if (len > sizeof(mms->in_buffer)) {
00194 av_log(NULL, AV_LOG_ERROR,
00195 "Other packet len = %d exceed the in_buffer size %zu\n",
00196 len, sizeof(mms->in_buffer));
00197 return AVERROR(EIO);
00198 }
00199 res = url_read_complete(mms->mms_hd, mms->in_buffer, len);
00200 if (res != len) {
00201 av_log(NULL, AV_LOG_ERROR, "Read other chunk type data failed!\n");
00202 return AVERROR(EIO);
00203 } else {
00204 av_dlog(NULL, "Skip chunk type %d \n", chunk_type);
00205 continue;
00206 }
00207 }
00208 }
00209 }
00210 return 0;
00211 }
00212
00213 static int mmsh_open(URLContext *h, const char *uri, int flags)
00214 {
00215 int i, port, err;
00216 char httpname[256], path[256], host[128], location[1024];
00217 char *stream_selection = NULL;
00218 char headers[1024];
00219 MMSHContext *mmsh;
00220 MMSContext *mms;
00221
00222 mmsh = h->priv_data = av_mallocz(sizeof(MMSHContext));
00223 if (!h->priv_data)
00224 return AVERROR(ENOMEM);
00225 mmsh->request_seq = h->is_streamed = 1;
00226 mms = &mmsh->mms;
00227 av_strlcpy(location, uri, sizeof(location));
00228
00229 av_url_split(NULL, 0, NULL, 0,
00230 host, sizeof(host), &port, path, sizeof(path), location);
00231 if (port<0)
00232 port = 80;
00233 ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, path);
00234
00235 if (url_alloc(&mms->mms_hd, httpname, URL_RDONLY) < 0) {
00236 return AVERROR(EIO);
00237 }
00238
00239 snprintf(headers, sizeof(headers),
00240 "Accept: */*\r\n"
00241 USERAGENT
00242 "Host: %s:%d\r\n"
00243 "Pragma: no-cache,rate=1.000000,stream-time=0,"
00244 "stream-offset=0:0,request-context=%u,max-duration=0\r\n"
00245 CLIENTGUID
00246 "Connection: Close\r\n\r\n",
00247 host, port, mmsh->request_seq++);
00248 ff_http_set_headers(mms->mms_hd, headers);
00249
00250 err = url_connect(mms->mms_hd);
00251 if (err) {
00252 goto fail;
00253 }
00254 err = get_http_header_data(mmsh);
00255 if (err) {
00256 av_log(NULL, AV_LOG_ERROR, "Get http header data failed!\n");
00257 goto fail;
00258 }
00259
00260
00261 url_close(mms->mms_hd);
00262 memset(headers, 0, sizeof(headers));
00263 if (url_alloc(&mms->mms_hd, httpname, URL_RDONLY) < 0) {
00264 return AVERROR(EIO);
00265 }
00266 stream_selection = av_mallocz(mms->stream_num * 19 + 1);
00267 if (!stream_selection)
00268 return AVERROR(ENOMEM);
00269 for (i = 0; i < mms->stream_num; i++) {
00270 char tmp[20];
00271 err = snprintf(tmp, sizeof(tmp), "ffff:%d:0 ", mms->streams[i].id);
00272 if (err < 0)
00273 goto fail;
00274 av_strlcat(stream_selection, tmp, mms->stream_num * 19 + 1);
00275 }
00276
00277 err = snprintf(headers, sizeof(headers),
00278 "Accept: */*\r\n"
00279 USERAGENT
00280 "Host: %s:%d\r\n"
00281 "Pragma: no-cache,rate=1.000000,request-context=%u\r\n"
00282 "Pragma: xPlayStrm=1\r\n"
00283 CLIENTGUID
00284 "Pragma: stream-switch-count=%d\r\n"
00285 "Pragma: stream-switch-entry=%s\r\n"
00286 "Connection: Close\r\n\r\n",
00287 host, port, mmsh->request_seq++, mms->stream_num, stream_selection);
00288 av_freep(&stream_selection);
00289 if (err < 0) {
00290 av_log(NULL, AV_LOG_ERROR, "Build play request failed!\n");
00291 goto fail;
00292 }
00293 av_dlog(NULL, "out_buffer is %s", headers);
00294 ff_http_set_headers(mms->mms_hd, headers);
00295
00296 err = url_connect(mms->mms_hd);
00297 if (err) {
00298 goto fail;
00299 }
00300
00301 err = get_http_header_data(mmsh);
00302 if (err) {
00303 av_log(NULL, AV_LOG_ERROR, "Get http header data failed!\n");
00304 goto fail;
00305 }
00306
00307 av_dlog(NULL, "Connection successfully open\n");
00308 return 0;
00309 fail:
00310 av_freep(&stream_selection);
00311 mmsh_close(h);
00312 av_dlog(NULL, "Connection failed with error %d\n", err);
00313 return err;
00314 }
00315
00316 static int handle_chunk_type(MMSHContext *mmsh)
00317 {
00318 MMSContext *mms = &mmsh->mms;
00319 int res, len = 0;
00320 ChunkType chunk_type;
00321 chunk_type = get_chunk_header(mmsh, &len);
00322
00323 switch (chunk_type) {
00324 case CHUNK_TYPE_END:
00325 mmsh->chunk_seq = 0;
00326 av_log(NULL, AV_LOG_ERROR, "Stream ended!\n");
00327 return AVERROR(EIO);
00328 case CHUNK_TYPE_STREAM_CHANGE:
00329 mms->header_parsed = 0;
00330 if (res = get_http_header_data(mmsh)) {
00331 av_log(NULL, AV_LOG_ERROR,"Stream changed! Failed to get new header!\n");
00332 return res;
00333 }
00334 break;
00335 case CHUNK_TYPE_DATA:
00336 return read_data_packet(mmsh, len);
00337 default:
00338 av_log(NULL, AV_LOG_ERROR, "Recv other type packet %d\n", chunk_type);
00339 return AVERROR_INVALIDDATA;
00340 }
00341 return 0;
00342 }
00343
00344 static int mmsh_read(URLContext *h, uint8_t *buf, int size)
00345 {
00346 int res = 0;
00347 MMSHContext *mmsh = h->priv_data;
00348 MMSContext *mms = &mmsh->mms;
00349 do {
00350 if (mms->asf_header_read_size < mms->asf_header_size) {
00351
00352 res = ff_mms_read_header(mms, buf, size);
00353 } else {
00354 if (!mms->remaining_in_len && (res = handle_chunk_type(mmsh)))
00355 return res;
00356 res = ff_mms_read_data(mms, buf, size);
00357 }
00358 } while (!res);
00359 return res;
00360 }
00361
00362 URLProtocol ff_mmsh_protocol = {
00363 .name = "mmsh",
00364 .url_open = mmsh_open,
00365 .url_read = mmsh_read,
00366 .url_write = NULL,
00367 .url_seek = NULL,
00368 .url_close = mmsh_close,
00369 };