ffmpeg / libavformat / rtpdec_asf.c @ 06ed024d
History | View | Annotate | Download (9.39 KB)
1 |
/*
|
---|---|
2 |
* Microsoft RTP/ASF support.
|
3 |
* Copyright (c) 2008 Ronald S. Bultje
|
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 |
* @file
|
24 |
* @brief Microsoft RTP/ASF support
|
25 |
* @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
26 |
*/
|
27 |
|
28 |
#include "libavutil/base64.h" |
29 |
#include "libavutil/avstring.h" |
30 |
#include "libavutil/intreadwrite.h" |
31 |
#include "rtp.h" |
32 |
#include "rtpdec_formats.h" |
33 |
#include "rtsp.h" |
34 |
#include "asf.h" |
35 |
|
36 |
/**
|
37 |
* From MSDN 2.2.1.4, we learn that ASF data packets over RTP should not
|
38 |
* contain any padding. Unfortunately, the header min/max_pktsize are not
|
39 |
* updated (thus making min_pktsize invalid). Here, we "fix" these faulty
|
40 |
* min_pktsize values in the ASF file header.
|
41 |
* @return 0 on success, <0 on failure (currently -1).
|
42 |
*/
|
43 |
static int rtp_asf_fix_header(uint8_t *buf, int len) |
44 |
{ |
45 |
uint8_t *p = buf, *end = buf + len; |
46 |
|
47 |
if (len < sizeof(ff_asf_guid) * 2 + 22 || |
48 |
memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) {
|
49 |
return -1; |
50 |
} |
51 |
p += sizeof(ff_asf_guid) + 14; |
52 |
do {
|
53 |
uint64_t chunksize = AV_RL64(p + sizeof(ff_asf_guid));
|
54 |
if (memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) { |
55 |
if (chunksize > end - p)
|
56 |
return -1; |
57 |
p += chunksize; |
58 |
continue;
|
59 |
} |
60 |
|
61 |
/* skip most of the file header, to min_pktsize */
|
62 |
p += 6 * 8 + 3 * 4 + sizeof(ff_asf_guid) * 2; |
63 |
if (p + 8 <= end && AV_RL32(p) == AV_RL32(p + 4)) { |
64 |
/* and set that to zero */
|
65 |
AV_WL32(p, 0);
|
66 |
return 0; |
67 |
} |
68 |
break;
|
69 |
} while (end - p >= sizeof(ff_asf_guid) + 8); |
70 |
|
71 |
return -1; |
72 |
} |
73 |
|
74 |
/**
|
75 |
* The following code is basically a buffered ByteIOContext,
|
76 |
* with the added benefit of returning -EAGAIN (instead of 0)
|
77 |
* on packet boundaries, such that the ASF demuxer can return
|
78 |
* safely and resume business at the next packet.
|
79 |
*/
|
80 |
static int packetizer_read(void *opaque, uint8_t *buf, int buf_size) |
81 |
{ |
82 |
return AVERROR(EAGAIN);
|
83 |
} |
84 |
|
85 |
static void init_packetizer(ByteIOContext *pb, uint8_t *buf, int len) |
86 |
{ |
87 |
init_put_byte(pb, buf, len, 0, NULL, packetizer_read, NULL, NULL); |
88 |
|
89 |
/* this "fills" the buffer with its current content */
|
90 |
pb->pos = len; |
91 |
pb->buf_end = buf + len; |
92 |
} |
93 |
|
94 |
int ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p) |
95 |
{ |
96 |
int ret = 0; |
97 |
if (av_strstart(p, "pgmpu:data:application/vnd.ms.wms-hdr.asfv1;base64,", &p)) { |
98 |
ByteIOContext pb; |
99 |
RTSPState *rt = s->priv_data; |
100 |
int len = strlen(p) * 6 / 8; |
101 |
char *buf = av_mallocz(len);
|
102 |
av_base64_decode(buf, p, len); |
103 |
|
104 |
if (rtp_asf_fix_header(buf, len) < 0) |
105 |
av_log(s, AV_LOG_ERROR, |
106 |
"Failed to fix invalid RTSP-MS/ASF min_pktsize\n");
|
107 |
init_packetizer(&pb, buf, len); |
108 |
if (rt->asf_ctx) {
|
109 |
av_close_input_stream(rt->asf_ctx); |
110 |
rt->asf_ctx = NULL;
|
111 |
} |
112 |
ret = av_open_input_stream(&rt->asf_ctx, &pb, "", &asf_demuxer, NULL); |
113 |
if (ret < 0) |
114 |
return ret;
|
115 |
av_metadata_copy(&s->metadata, rt->asf_ctx->metadata, 0);
|
116 |
rt->asf_pb_pos = url_ftell(&pb); |
117 |
av_free(buf); |
118 |
rt->asf_ctx->pb = NULL;
|
119 |
} |
120 |
return ret;
|
121 |
} |
122 |
|
123 |
static int asfrtp_parse_sdp_line(AVFormatContext *s, int stream_index, |
124 |
PayloadContext *asf, const char *line) |
125 |
{ |
126 |
if (av_strstart(line, "stream:", &line)) { |
127 |
RTSPState *rt = s->priv_data; |
128 |
|
129 |
s->streams[stream_index]->id = strtol(line, NULL, 10); |
130 |
|
131 |
if (rt->asf_ctx) {
|
132 |
int i;
|
133 |
|
134 |
for (i = 0; i < rt->asf_ctx->nb_streams; i++) { |
135 |
if (s->streams[stream_index]->id == rt->asf_ctx->streams[i]->id) {
|
136 |
*s->streams[stream_index]->codec = |
137 |
*rt->asf_ctx->streams[i]->codec; |
138 |
rt->asf_ctx->streams[i]->codec->extradata_size = 0;
|
139 |
rt->asf_ctx->streams[i]->codec->extradata = NULL;
|
140 |
av_set_pts_info(s->streams[stream_index], 32, 1, 1000); |
141 |
} |
142 |
} |
143 |
} |
144 |
} |
145 |
|
146 |
return 0; |
147 |
} |
148 |
|
149 |
struct PayloadContext {
|
150 |
ByteIOContext *pktbuf, pb; |
151 |
uint8_t *buf; |
152 |
}; |
153 |
|
154 |
/**
|
155 |
* @return 0 when a packet was written into /p pkt, and no more data is left;
|
156 |
* 1 when a packet was written into /p pkt, and more packets might be left;
|
157 |
* <0 when not enough data was provided to return a full packet, or on error.
|
158 |
*/
|
159 |
static int asfrtp_parse_packet(AVFormatContext *s, PayloadContext *asf, |
160 |
AVStream *st, AVPacket *pkt, |
161 |
uint32_t *timestamp, |
162 |
const uint8_t *buf, int len, int flags) |
163 |
{ |
164 |
ByteIOContext *pb = &asf->pb; |
165 |
int res, mflags, len_off;
|
166 |
RTSPState *rt = s->priv_data; |
167 |
|
168 |
if (!rt->asf_ctx)
|
169 |
return -1; |
170 |
|
171 |
if (len > 0) { |
172 |
int off, out_len = 0; |
173 |
|
174 |
if (len < 4) |
175 |
return -1; |
176 |
|
177 |
av_freep(&asf->buf); |
178 |
|
179 |
init_put_byte(pb, buf, len, 0, NULL, NULL, NULL, NULL); |
180 |
|
181 |
while (url_ftell(pb) + 4 < len) { |
182 |
int start_off = url_ftell(pb);
|
183 |
|
184 |
mflags = get_byte(pb); |
185 |
if (mflags & 0x80) |
186 |
flags |= RTP_FLAG_KEY; |
187 |
len_off = get_be24(pb); |
188 |
if (mflags & 0x20) /**< relative timestamp */ |
189 |
url_fskip(pb, 4);
|
190 |
if (mflags & 0x10) /**< has duration */ |
191 |
url_fskip(pb, 4);
|
192 |
if (mflags & 0x8) /**< has location ID */ |
193 |
url_fskip(pb, 4);
|
194 |
off = url_ftell(pb); |
195 |
|
196 |
if (!(mflags & 0x40)) { |
197 |
/**
|
198 |
* If 0x40 is not set, the len_off field specifies an offset
|
199 |
* of this packet's payload data in the complete (reassembled)
|
200 |
* ASF packet. This is used to spread one ASF packet over
|
201 |
* multiple RTP packets.
|
202 |
*/
|
203 |
if (asf->pktbuf && len_off != url_ftell(asf->pktbuf)) {
|
204 |
uint8_t *p; |
205 |
url_close_dyn_buf(asf->pktbuf, &p); |
206 |
asf->pktbuf = NULL;
|
207 |
av_free(p); |
208 |
} |
209 |
if (!len_off && !asf->pktbuf &&
|
210 |
(res = url_open_dyn_buf(&asf->pktbuf)) < 0)
|
211 |
return res;
|
212 |
if (!asf->pktbuf)
|
213 |
return AVERROR(EIO);
|
214 |
|
215 |
put_buffer(asf->pktbuf, buf + off, len - off); |
216 |
url_fskip(pb, len - off); |
217 |
if (!(flags & RTP_FLAG_MARKER))
|
218 |
return -1; |
219 |
out_len = url_close_dyn_buf(asf->pktbuf, &asf->buf); |
220 |
asf->pktbuf = NULL;
|
221 |
} else {
|
222 |
/**
|
223 |
* If 0x40 is set, the len_off field specifies the length of
|
224 |
* the next ASF packet that can be read from this payload
|
225 |
* data alone. This is commonly the same as the payload size,
|
226 |
* but could be less in case of packet splitting (i.e.
|
227 |
* multiple ASF packets in one RTP packet).
|
228 |
*/
|
229 |
|
230 |
int cur_len = start_off + len_off - off;
|
231 |
int prev_len = out_len;
|
232 |
out_len += cur_len; |
233 |
asf->buf = av_realloc(asf->buf, out_len); |
234 |
memcpy(asf->buf + prev_len, buf + off, |
235 |
FFMIN(cur_len, len - off)); |
236 |
url_fskip(pb, cur_len); |
237 |
} |
238 |
} |
239 |
|
240 |
init_packetizer(pb, asf->buf, out_len); |
241 |
pb->pos += rt->asf_pb_pos; |
242 |
pb->eof_reached = 0;
|
243 |
rt->asf_ctx->pb = pb; |
244 |
} |
245 |
|
246 |
for (;;) {
|
247 |
int i;
|
248 |
|
249 |
res = av_read_packet(rt->asf_ctx, pkt); |
250 |
rt->asf_pb_pos = url_ftell(pb); |
251 |
if (res != 0) |
252 |
break;
|
253 |
for (i = 0; i < s->nb_streams; i++) { |
254 |
if (s->streams[i]->id == rt->asf_ctx->streams[pkt->stream_index]->id) {
|
255 |
pkt->stream_index = i; |
256 |
return 1; // FIXME: return 0 if last packet |
257 |
} |
258 |
} |
259 |
av_free_packet(pkt); |
260 |
} |
261 |
|
262 |
return res == 1 ? -1 : res; |
263 |
} |
264 |
|
265 |
static PayloadContext *asfrtp_new_context(void) |
266 |
{ |
267 |
return av_mallocz(sizeof(PayloadContext)); |
268 |
} |
269 |
|
270 |
static void asfrtp_free_context(PayloadContext *asf) |
271 |
{ |
272 |
if (asf->pktbuf) {
|
273 |
uint8_t *p = NULL;
|
274 |
url_close_dyn_buf(asf->pktbuf, &p); |
275 |
asf->pktbuf = NULL;
|
276 |
av_free(p); |
277 |
} |
278 |
av_freep(&asf->buf); |
279 |
av_free(asf); |
280 |
} |
281 |
|
282 |
#define RTP_ASF_HANDLER(n, s, t) \
|
283 |
RTPDynamicProtocolHandler ff_ms_rtp_ ## n ## _handler = { \ |
284 |
.enc_name = s, \ |
285 |
.codec_type = t, \ |
286 |
.codec_id = CODEC_ID_NONE, \ |
287 |
.parse_sdp_a_line = asfrtp_parse_sdp_line, \ |
288 |
.open = asfrtp_new_context, \ |
289 |
.close = asfrtp_free_context, \ |
290 |
.parse_packet = asfrtp_parse_packet, \ |
291 |
}; |
292 |
|
293 |
RTP_ASF_HANDLER(asf_pfv, "x-asf-pf", AVMEDIA_TYPE_VIDEO);
|
294 |
RTP_ASF_HANDLER(asf_pfa, "x-asf-pf", AVMEDIA_TYPE_AUDIO);
|