History

MMS-over-TCP protocol support. Patch by Zhentan Feng <spyfeng gmail com>.

Originally committed as revision 23301 to svn://svn.ffmpeg.org/ffmpeg/trunk

Zhentan Feng committed 16 years ago

Commit a2403981 parent e34d5db6 files · +696 −2Browse files

Unified · Split

6 files
  1. Changelog +1 −0
  2. doc/general.texi +1 −0
  3. libavformat/Makefile +1 −0
  4. libavformat/allformats.c +1 −0
  5. libavformat/avformat.h +2 −2
  6. libavformat/mmst.c +690 −0
Changelog+1−0
@@ -6,6 +6,7 @@ version <next>: · 5 unmodified lines
66
77 - WebM support in Matroska demuxer
88 - low overhead Ogg muxing
9+- MMS-TCP support
910
1011
1112
doc/general.texi+1−0
@@ -687,6 +687,7 @@ performance on systems without hardware floating point support). · 686 unmodified lines
687687 @item file @tab X
688688 @item Gopher @tab X
689689 @item HTTP @tab X
690+@item MMS @tab X
690691 @item pipe @tab X
691692 @item RTP @tab X
692693 @item TCP @tab X
libavformat/Makefile+1−0
@@ -275,6 +275,7 @@ OBJS+= avio.o aviobuf.o · 274 unmodified lines
275275 OBJS-$(CONFIG_FILE_PROTOCOL) += file.o
276276 OBJS-$(CONFIG_GOPHER_PROTOCOL) += gopher.o
277277 OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o httpauth.o
278+OBJS-$(CONFIG_MMST_PROTOCOL) += mmst.o asf.o
278279 OBJS-$(CONFIG_PIPE_PROTOCOL) += file.o
279280
280281 # external or internal rtmp
libavformat/allformats.c+1−0
@@ -219,6 +219,7 @@ void av_register_all(void) · 218 unmodified lines
219219 REGISTER_PROTOCOL (FILE, file);
220220 REGISTER_PROTOCOL (GOPHER, gopher);
221221 REGISTER_PROTOCOL (HTTP, http);
222+ REGISTER_PROTOCOL (MMST, mmst);
222223 REGISTER_PROTOCOL (PIPE, pipe);
223224 REGISTER_PROTOCOL (RTMP, rtmp);
224225 #if CONFIG_LIBRTMP
libavformat/avformat.h+2−2
@@ -22,8 +22,8 @@ · 21 unmodified lines
2222 #define AVFORMAT_AVFORMAT_H
2323
2424 #define LIBAVFORMAT_VERSION_MAJOR 52
25−#define LIBAVFORMAT_VERSION_MINOR 64
26−#define LIBAVFORMAT_VERSION_MICRO 1
25+#define LIBAVFORMAT_VERSION_MINOR 65
26+#define LIBAVFORMAT_VERSION_MICRO 0
2727
2828 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
2929 LIBAVFORMAT_VERSION_MINOR, \
libavformat/mmst.c+690−0
@@ -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+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t