mmsh.c
Go to the documentation of this file.
1 /*
2  * MMS protocol over HTTP
3  * Copyright (c) 2010 Zhentan Feng <spyfeng at gmail dot com>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 /*
23  * Reference
24  * Windows Media HTTP Streaming Protocol.
25  * http://msdn.microsoft.com/en-us/library/cc251059(PROT.10).aspx
26  */
27 
28 #include <string.h>
29 #include "libavutil/intreadwrite.h"
30 #include "libavutil/avstring.h"
31 #include "libavutil/opt.h"
32 #include "internal.h"
33 #include "mms.h"
34 #include "asf.h"
35 #include "http.h"
36 #include "url.h"
37 
38 #define CHUNK_HEADER_LENGTH 4 // 2bytes chunk type and 2bytes chunk length.
39 #define EXT_HEADER_LENGTH 8 // 4bytes sequence, 2bytes useless and 2bytes chunk length.
40 
41 // see Ref 2.2.1.8
42 #define USERAGENT "User-Agent: NSPlayer/4.1.0.3856\r\n"
43 // see Ref 2.2.1.4.33
44 // the guid value can be changed to any valid value.
45 #define CLIENTGUID "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"
46 
47 // see Ref 2.2.3 for packet type define:
48 // chunk type contains 2 fields: Frame and PacketID.
49 // Frame is 0x24 or 0xA4(rarely), different PacketID indicates different packet type.
50 typedef enum {
51  CHUNK_TYPE_DATA = 0x4424,
53  CHUNK_TYPE_END = 0x4524,
55 } ChunkType;
56 
57 typedef struct {
60  int request_seq; ///< request packet sequence
61  int chunk_seq; ///< data packet sequence
62 } MMSHContext;
63 
64 static int mmsh_close(URLContext *h)
65 {
66  MMSHContext *mmsh = (MMSHContext *)h->priv_data;
67  MMSContext *mms = &mmsh->mms;
68  if (mms->mms_hd)
69  ffurl_close(mms->mms_hd);
70  av_free(mms->streams);
71  av_free(mms->asf_header);
72  return 0;
73 }
74 
76 {
77  MMSContext *mms = &mmsh->mms;
78  uint8_t chunk_header[CHUNK_HEADER_LENGTH];
79  uint8_t ext_header[EXT_HEADER_LENGTH];
80  ChunkType chunk_type;
81  int chunk_len, res, ext_header_len;
82 
83  res = ffurl_read_complete(mms->mms_hd, chunk_header, CHUNK_HEADER_LENGTH);
84  if (res != CHUNK_HEADER_LENGTH) {
85  av_log(NULL, AV_LOG_ERROR, "Read data packet header failed!\n");
86  return AVERROR(EIO);
87  }
88  chunk_type = AV_RL16(chunk_header);
89  chunk_len = AV_RL16(chunk_header + 2);
90 
91  switch (chunk_type) {
92  case CHUNK_TYPE_END:
94  ext_header_len = 4;
95  break;
97  case CHUNK_TYPE_DATA:
98  ext_header_len = 8;
99  break;
100  default:
101  av_log(NULL, AV_LOG_ERROR, "Strange chunk type %d\n", chunk_type);
102  return AVERROR_INVALIDDATA;
103  }
104 
105  res = ffurl_read_complete(mms->mms_hd, ext_header, ext_header_len);
106  if (res != ext_header_len) {
107  av_log(NULL, AV_LOG_ERROR, "Read ext header failed!\n");
108  return AVERROR(EIO);
109  }
110  *len = chunk_len - ext_header_len;
111  if (chunk_type == CHUNK_TYPE_END || chunk_type == CHUNK_TYPE_DATA)
112  mmsh->chunk_seq = AV_RL32(ext_header);
113  return chunk_type;
114 }
115 
116 static int read_data_packet(MMSHContext *mmsh, const int len)
117 {
118  MMSContext *mms = &mmsh->mms;
119  int res;
120  if (len > sizeof(mms->in_buffer)) {
122  "Data packet length %d exceeds the in_buffer size %zu\n",
123  len, sizeof(mms->in_buffer));
124  return AVERROR(EIO);
125  }
126  res = ffurl_read_complete(mms->mms_hd, mms->in_buffer, len);
127  av_dlog(NULL, "Data packet len = %d\n", len);
128  if (res != len) {
129  av_log(NULL, AV_LOG_ERROR, "Read data packet failed!\n");
130  return AVERROR(EIO);
131  }
132  if (len > mms->asf_packet_len) {
134  "Chunk length %d exceed packet length %d\n",len, mms->asf_packet_len);
135  return AVERROR_INVALIDDATA;
136  } else {
137  memset(mms->in_buffer + len, 0, mms->asf_packet_len - len); // padding
138  }
139  mms->read_in_ptr = mms->in_buffer;
140  mms->remaining_in_len = mms->asf_packet_len;
141  return 0;
142 }
143 
145 {
146  MMSContext *mms = &mmsh->mms;
147  int res, len;
148  ChunkType chunk_type;
149 
150  for (;;) {
151  len = 0;
152  res = chunk_type = get_chunk_header(mmsh, &len);
153  if (res < 0) {
154  return res;
155  } else if (chunk_type == CHUNK_TYPE_ASF_HEADER){
156  // get asf header and stored it
157  if (!mms->header_parsed) {
158  if (mms->asf_header) {
159  if (len != mms->asf_header_size) {
160  mms->asf_header_size = len;
161  av_dlog(NULL, "Header len changed from %d to %d\n",
162  mms->asf_header_size, len);
163  av_freep(&mms->asf_header);
164  }
165  }
166  mms->asf_header = av_mallocz(len);
167  if (!mms->asf_header) {
168  return AVERROR(ENOMEM);
169  }
170  mms->asf_header_size = len;
171  }
172  if (len > mms->asf_header_size) {
174  "Asf header packet len = %d exceed the asf header buf size %d\n",
175  len, mms->asf_header_size);
176  return AVERROR(EIO);
177  }
178  res = ffurl_read_complete(mms->mms_hd, mms->asf_header, len);
179  if (res != len) {
181  "Recv asf header data len %d != expected len %d\n", res, len);
182  return AVERROR(EIO);
183  }
184  mms->asf_header_size = len;
185  if (!mms->header_parsed) {
186  res = ff_mms_asf_header_parser(mms);
187  mms->header_parsed = 1;
188  return res;
189  }
190  } else if (chunk_type == CHUNK_TYPE_DATA) {
191  // read data packet and do padding
192  return read_data_packet(mmsh, len);
193  } else {
194  if (len) {
195  if (len > sizeof(mms->in_buffer)) {
197  "Other packet len = %d exceed the in_buffer size %zu\n",
198  len, sizeof(mms->in_buffer));
199  return AVERROR(EIO);
200  }
201  res = ffurl_read_complete(mms->mms_hd, mms->in_buffer, len);
202  if (res != len) {
203  av_log(NULL, AV_LOG_ERROR, "Read other chunk type data failed!\n");
204  return AVERROR(EIO);
205  } else {
206  av_dlog(NULL, "Skip chunk type %d \n", chunk_type);
207  continue;
208  }
209  }
210  }
211  }
212 }
213 
214 static int mmsh_open_internal(URLContext *h, const char *uri, int flags, int timestamp, int64_t pos)
215 {
216  int i, port, err;
217  char httpname[256], path[256], host[128];
218  char *stream_selection = NULL;
219  char headers[1024];
220  MMSHContext *mmsh = h->priv_data;
221  MMSContext *mms;
222 
223  mmsh->request_seq = h->is_streamed = 1;
224  mms = &mmsh->mms;
225  av_strlcpy(mmsh->location, uri, sizeof(mmsh->location));
226 
227  av_url_split(NULL, 0, NULL, 0,
228  host, sizeof(host), &port, path, sizeof(path), mmsh->location);
229  if (port<0)
230  port = 80; // default mmsh protocol port
231  ff_url_join(httpname, sizeof(httpname), "http", NULL, host, port, "%s", path);
232 
233  if (ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ,
234  &h->interrupt_callback) < 0) {
235  return AVERROR(EIO);
236  }
237 
238  snprintf(headers, sizeof(headers),
239  "Accept: */*\r\n"
240  USERAGENT
241  "Host: %s:%d\r\n"
242  "Pragma: no-cache,rate=1.000000,stream-time=0,"
243  "stream-offset=0:0,request-context=%u,max-duration=0\r\n"
244  CLIENTGUID
245  "Connection: Close\r\n",
246  host, port, mmsh->request_seq++);
247  av_opt_set(mms->mms_hd->priv_data, "headers", headers, 0);
248 
249  err = ffurl_connect(mms->mms_hd, NULL);
250  if (err) {
251  goto fail;
252  }
253  err = get_http_header_data(mmsh);
254  if (err) {
255  av_log(NULL, AV_LOG_ERROR, "Get http header data failed!\n");
256  goto fail;
257  }
258 
259  // close the socket and then reopen it for sending the second play request.
260  ffurl_close(mms->mms_hd);
261  memset(headers, 0, sizeof(headers));
262  if ((err = ffurl_alloc(&mms->mms_hd, httpname, AVIO_FLAG_READ,
263  &h->interrupt_callback)) < 0) {
264  goto fail;
265  }
266  stream_selection = av_mallocz(mms->stream_num * 19 + 1);
267  if (!stream_selection)
268  return AVERROR(ENOMEM);
269  for (i = 0; i < mms->stream_num; i++) {
270  char tmp[20];
271  err = snprintf(tmp, sizeof(tmp), "ffff:%d:0 ", mms->streams[i].id);
272  if (err < 0)
273  goto fail;
274  av_strlcat(stream_selection, tmp, mms->stream_num * 19 + 1);
275  }
276  // send play request
277  err = snprintf(headers, sizeof(headers),
278  "Accept: */*\r\n"
279  USERAGENT
280  "Host: %s:%d\r\n"
281  "Pragma: no-cache,rate=1.000000,request-context=%u\r\n"
282  "Pragma: xPlayStrm=1\r\n"
283  CLIENTGUID
284  "Pragma: stream-switch-count=%d\r\n"
285  "Pragma: stream-switch-entry=%s\r\n"
286  "Pragma: no-cache,rate=1.000000,stream-time=%u"
287  "Connection: Close\r\n",
288  host, port, mmsh->request_seq++, mms->stream_num, stream_selection, timestamp);
289  av_freep(&stream_selection);
290  if (err < 0) {
291  av_log(NULL, AV_LOG_ERROR, "Build play request failed!\n");
292  goto fail;
293  }
294  av_dlog(NULL, "out_buffer is %s", headers);
295  av_opt_set(mms->mms_hd->priv_data, "headers", headers, 0);
296 
297  err = ffurl_connect(mms->mms_hd, NULL);
298  if (err) {
299  goto fail;
300  }
301 
302  err = get_http_header_data(mmsh);
303  if (err) {
304  av_log(NULL, AV_LOG_ERROR, "Get http header data failed!\n");
305  goto fail;
306  }
307 
308  av_dlog(NULL, "Connection successfully open\n");
309  return 0;
310 fail:
311  av_freep(&stream_selection);
312  mmsh_close(h);
313  av_dlog(NULL, "Connection failed with error %d\n", err);
314  return err;
315 }
316 
317 static int mmsh_open(URLContext *h, const char *uri, int flags)
318 {
319  return mmsh_open_internal(h, uri, flags, 0, 0);
320 }
321 
323 {
324  MMSContext *mms = &mmsh->mms;
325  int res, len = 0;
326  ChunkType chunk_type;
327  chunk_type = get_chunk_header(mmsh, &len);
328 
329  switch (chunk_type) {
330  case CHUNK_TYPE_END:
331  mmsh->chunk_seq = 0;
332  av_log(NULL, AV_LOG_ERROR, "Stream ended!\n");
333  return AVERROR(EIO);
335  mms->header_parsed = 0;
336  if (res = get_http_header_data(mmsh)) {
337  av_log(NULL, AV_LOG_ERROR,"Stream changed! Failed to get new header!\n");
338  return res;
339  }
340  break;
341  case CHUNK_TYPE_DATA:
342  return read_data_packet(mmsh, len);
343  default:
344  av_log(NULL, AV_LOG_ERROR, "Recv other type packet %d\n", chunk_type);
345  return AVERROR_INVALIDDATA;
346  }
347  return 0;
348 }
349 
350 static int mmsh_read(URLContext *h, uint8_t *buf, int size)
351 {
352  int res = 0;
353  MMSHContext *mmsh = h->priv_data;
354  MMSContext *mms = &mmsh->mms;
355  do {
356  if (mms->asf_header_read_size < mms->asf_header_size) {
357  // copy asf header into buffer
358  res = ff_mms_read_header(mms, buf, size);
359  } else {
360  if (!mms->remaining_in_len && (res = handle_chunk_type(mmsh)))
361  return res;
362  res = ff_mms_read_data(mms, buf, size);
363  }
364  } while (!res);
365  return res;
366 }
367 
368 static int64_t mmsh_read_seek(URLContext *h, int stream_index,
369  int64_t timestamp, int flags)
370 {
371  MMSHContext *mmsh = h->priv_data;
372  MMSContext *mms = &mmsh->mms;
373  int ret;
374 
375  ret= mmsh_open_internal(h, mmsh->location, 0, FFMAX(timestamp, 0), 0);
376 
377  if(ret>=0){
378  if (mms->mms_hd)
379  ffurl_close(mms->mms_hd);
380  av_freep(&mms->streams);
381  av_freep(&mms->asf_header);
382  av_free(mmsh);
383  mmsh = h->priv_data;
384  mms = &mmsh->mms;
386  }else
387  h->priv_data= mmsh;
388  return ret;
389 }
390 
391 static int64_t mmsh_seek(URLContext *h, int64_t pos, int whence)
392 {
393  MMSHContext *mmsh = h->priv_data;
394  MMSContext *mms = &mmsh->mms;
395 
396  if(pos == 0 && whence == SEEK_CUR)
397  return mms->asf_header_read_size + mms->remaining_in_len + mmsh->chunk_seq * (int64_t)mms->asf_packet_len;
398  return AVERROR(ENOSYS);
399 }
400 
402  .name = "mmsh",
403  .url_open = mmsh_open,
404  .url_read = mmsh_read,
405  .url_seek = mmsh_seek,
406  .url_close = mmsh_close,
407  .url_read_seek = mmsh_read_seek,
408  .priv_data_size = sizeof(MMSHContext),
410 };
static int mmsh_open(URLContext *h, const char *uri, int flags)
Definition: mmsh.c:317
void av_url_split(char *proto, int proto_size, char *authorization, int authorization_size, char *hostname, int hostname_size, int *port_ptr, char *path, int path_size, const char *url)
Split a URL string into components.
void * av_mallocz(size_t size)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
Definition: mem.c:205
int request_seq
request packet sequence
Definition: mmsh.c:60
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
URLContext * mms_hd
TCP connection handle.
Definition: mms.h:31
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:35
URLProtocol ff_mmsh_protocol
Definition: mmsh.c:401
uint8_t * read_in_ptr
Pointer for reading from incoming buffer.
Definition: mms.h:43
static int get_http_header_data(MMSHContext *mmsh)
Definition: mmsh.c:144
int is_streamed
true if streamed (no seek possible), default = false
Definition: url.h:48
int ffurl_connect(URLContext *uc, AVDictionary **options)
Connect an URLContext that has been allocated by ffurl_alloc.
Definition: avio.c:190
AVIOInterruptCB interrupt_callback
Definition: url.h:50
Definition: mms.h:30
static int read_data_packet(MMSHContext *mmsh, const int len)
Definition: mmsh.c:116
#define EXT_HEADER_LENGTH
Definition: mmsh.c:39
#define AVIO_FLAG_READ
read-only
Definition: avio.h:332
int ff_mms_read_header(MMSContext *mms, uint8_t *buf, const int size)
Definition: mms.c:29
int stream_num
stream numbers.
Definition: mms.h:56
#define AV_RL16
av_dlog(ac->avr,"%d samples - audio_convert: %s to %s (%s)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt), use_generic?ac->func_descr_generic:ac->func_descr)
int header_parsed
The header has been received and parsed.
Definition: mms.h:51
static int handle_chunk_type(MMSHContext *mmsh)
Definition: mmsh.c:322
FFmpeg currently uses a custom build this text attempts to document some of its obscure features and options Makefile the full command issued by make and its output will be shown on the screen DESTDIR Destination directory for the install useful to prepare packages or install FFmpeg in cross environments Makefile builds all the libraries and the executables fate Run the fate test note you must have installed it fate list Will list all fate regression test targets install Install headers
Definition: build_system.txt:1
int ff_url_join(char *str, int size, const char *proto, const char *authorization, const char *hostname, int port, const char *fmt,...) av_printf_format(7
Assemble a URL string from components.
void av_freep(void *arg)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc() and set the pointer ...
Definition: mem.c:198
uint8_t * asf_header
Internal handling of the ASF header.
Definition: mms.h:49
initialize output if(nPeaks >3)%at least 3 peaks in spectrum for trying to find f0 nf0peaks
uint8_t
AVOptions.
static int64_t mmsh_seek(URLContext *h, int64_t pos, int whence)
Definition: mmsh.c:391
int ff_mms_read_data(MMSContext *mms, uint8_t *buf, const int size)
Definition: mms.c:44
int asf_packet_len
Definition: mms.h:52
#define CHUNK_HEADER_LENGTH
Definition: mmsh.c:38
uint8_t in_buffer[65536]
Buffer for incoming packets.
Definition: mms.h:42
MMSContext mms
Definition: mmsh.c:58
int ffurl_alloc(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb)
Create a URLContext for accessing to the resource indicated by url, but do not initiate the connectio...
Definition: avio.c:211
void av_free(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc(). ...
Definition: mem.c:183
static ChunkType get_chunk_header(MMSHContext *mmsh, int *len)
Definition: mmsh.c:75
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:246
#define FFMAX(a, b)
Definition: common.h:56
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition: avstring.c:82
int size
uint8_t location[1024]
Definition: mmsh.c:59
int asf_header_read_size
Definition: mms.h:53
ret
Definition: avfilter.c:821
ChunkType
Definition: mmsh.c:50
#define AV_RL32
NULL
Definition: eval.c:55
int remaining_in_len
Reading length from incoming buffer.
Definition: mms.h:44
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:148
void * buf
Definition: avisynth_c.h:594
Definition: url.h:41
synthesis window for stochastic i
void * priv_data
Definition: url.h:44
static int64_t mmsh_read_seek(URLContext *h, int stream_index, int64_t timestamp, int flags)
Definition: mmsh.c:368
static int mmsh_open_internal(URLContext *h, const char *uri, int flags, int timestamp, int64_t pos)
Definition: mmsh.c:214
#define snprintf
Definition: snprintf.h:34
int chunk_seq
data packet sequence
Definition: mmsh.c:61
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFilterBuffer structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Buffer references ownership and permissions
size_t av_strlcat(char *dst, const char *src, size_t size)
Append the string src to the string dst, but to a total length of no more than size - 1 bytes...
Definition: avstring.c:92
const char * name
Definition: url.h:55
int asf_header_size
Size of stored ASF header.
Definition: mms.h:50
static int flags
Definition: cpu.c:23
int ffurl_close(URLContext *h)
Definition: avio.c:359
#define USERAGENT
Definition: mmsh.c:42
int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
Read as many bytes as possible (up to size), calling the read function multiple times if necessary...
Definition: avio.c:310
static int mmsh_close(URLContext *h)
Definition: mmsh.c:64
int id
Definition: mms.h:27
int len
#define CLIENTGUID
Definition: mmsh.c:45
MMSStream * streams
Definition: mms.h:32
static int mmsh_read(URLContext *h, uint8_t *buf, int size)
Definition: mmsh.c:350
unbuffered private I/O API
int av_opt_set(void *obj, const char *name, const char *val, int search_flags)
Definition: opt.c:252
int ff_mms_asf_header_parser(MMSContext *mms)
Definition: mms.c:54