ffmpeg / libavformat / rtpdec_qt.c @ e731b8d8
History | View | Annotate | Download (8.76 KB)
1 | 3ece3e4c | Martin Storsjö | /*
|
---|---|---|---|
2 | * RTP/Quicktime support.
|
||
3 | * Copyright (c) 2009 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 Quicktime-style RTP support
|
||
25 | * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||
26 | */
|
||
27 | |||
28 | #include "avformat.h" |
||
29 | e731b8d8 | Anton Khirnov | #include "avio_internal.h" |
30 | 3ece3e4c | Martin Storsjö | #include "rtp.h" |
31 | #include "rtpdec.h" |
||
32 | #include "isom.h" |
||
33 | #include "libavcodec/get_bits.h" |
||
34 | |||
35 | struct PayloadContext {
|
||
36 | AVPacket pkt; |
||
37 | int bytes_per_frame, remaining;
|
||
38 | uint32_t timestamp; |
||
39 | }; |
||
40 | |||
41 | static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt, |
||
42 | AVStream *st, AVPacket *pkt, |
||
43 | uint32_t *timestamp, const uint8_t *buf,
|
||
44 | int len, int flags) |
||
45 | { |
||
46 | ae628ec1 | Anton Khirnov | AVIOContext pb; |
47 | 3ece3e4c | Martin Storsjö | GetBitContext gb; |
48 | int packing_scheme, has_payload_desc, has_packet_info, alen,
|
||
49 | has_marker_bit = flags & RTP_FLAG_MARKER; |
||
50 | |||
51 | if (qt->remaining) {
|
||
52 | int num = qt->pkt.size / qt->bytes_per_frame;
|
||
53 | |||
54 | if (av_new_packet(pkt, qt->bytes_per_frame))
|
||
55 | return AVERROR(ENOMEM);
|
||
56 | pkt->stream_index = st->index; |
||
57 | pkt->flags = qt->pkt.flags; |
||
58 | memcpy(pkt->data, |
||
59 | &qt->pkt.data[(num - qt->remaining) * qt->bytes_per_frame], |
||
60 | qt->bytes_per_frame); |
||
61 | if (--qt->remaining == 0) { |
||
62 | av_freep(&qt->pkt.data); |
||
63 | qt->pkt.size = 0;
|
||
64 | } |
||
65 | return qt->remaining > 0; |
||
66 | } |
||
67 | |||
68 | /**
|
||
69 | * The RTP payload is described in:
|
||
70 | * http://developer.apple.com/quicktime/icefloe/dispatch026.html
|
||
71 | */
|
||
72 | init_get_bits(&gb, buf, len << 3);
|
||
73 | e731b8d8 | Anton Khirnov | ffio_init_context(&pb, buf, len, 0, NULL, NULL, NULL, NULL); |
74 | 3ece3e4c | Martin Storsjö | |
75 | if (len < 4) |
||
76 | return AVERROR_INVALIDDATA;
|
||
77 | |||
78 | skip_bits(&gb, 4); // version |
||
79 | if ((packing_scheme = get_bits(&gb, 2)) == 0) |
||
80 | return AVERROR_INVALIDDATA;
|
||
81 | if (get_bits1(&gb))
|
||
82 | flags |= RTP_FLAG_KEY; |
||
83 | has_payload_desc = get_bits1(&gb); |
||
84 | has_packet_info = get_bits1(&gb); |
||
85 | skip_bits(&gb, 23); // reserved:7, cache payload info:1, payload ID:15 |
||
86 | |||
87 | if (has_payload_desc) {
|
||
88 | int data_len, pos, is_start, is_finish;
|
||
89 | uint32_t tag; |
||
90 | |||
91 | pos = get_bits_count(&gb) >> 3;
|
||
92 | if (pos + 12 > len) |
||
93 | return AVERROR_INVALIDDATA;
|
||
94 | |||
95 | skip_bits(&gb, 2); // has non-I frames:1, is sparse:1 |
||
96 | is_start = get_bits1(&gb); |
||
97 | is_finish = get_bits1(&gb); |
||
98 | if (!is_start || !is_finish) {
|
||
99 | av_log_missing_feature(s, "RTP-X-QT with payload description "
|
||
100 | "split over several packets", 1); |
||
101 | return AVERROR_NOTSUPP;
|
||
102 | } |
||
103 | skip_bits(&gb, 12); // reserved |
||
104 | data_len = get_bits(&gb, 16);
|
||
105 | |||
106 | url_fseek(&pb, pos + 4, SEEK_SET);
|
||
107 | tag = get_le32(&pb); |
||
108 | b2ed95ec | Anton Khirnov | if ((st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
|
109 | 3ece3e4c | Martin Storsjö | tag != MKTAG('v','i','d','e')) || |
110 | b2ed95ec | Anton Khirnov | (st->codec->codec_type == AVMEDIA_TYPE_AUDIO && |
111 | 3ece3e4c | Martin Storsjö | tag != MKTAG('s','o','u','n'))) |
112 | return AVERROR_INVALIDDATA;
|
||
113 | av_set_pts_info(st, 32, 1, get_be32(&pb)); |
||
114 | |||
115 | if (pos + data_len > len)
|
||
116 | return AVERROR_INVALIDDATA;
|
||
117 | /* TLVs */
|
||
118 | while (url_ftell(&pb) + 4 < pos + data_len) { |
||
119 | int tlv_len = get_be16(&pb);
|
||
120 | tag = get_le16(&pb); |
||
121 | if (url_ftell(&pb) + tlv_len > pos + data_len)
|
||
122 | return AVERROR_INVALIDDATA;
|
||
123 | |||
124 | #define MKTAG16(a,b) MKTAG(a,b,0,0) |
||
125 | switch (tag) {
|
||
126 | case MKTAG16('s','d'): { |
||
127 | MOVStreamContext *msc; |
||
128 | void *priv_data = st->priv_data;
|
||
129 | int nb_streams = s->nb_streams;
|
||
130 | MOVContext *mc = av_mallocz(sizeof(*mc));
|
||
131 | if (!mc)
|
||
132 | return AVERROR(ENOMEM);
|
||
133 | mc->fc = s; |
||
134 | st->priv_data = msc = av_mallocz(sizeof(MOVStreamContext));
|
||
135 | if (!msc) {
|
||
136 | av_free(mc); |
||
137 | st->priv_data = priv_data; |
||
138 | return AVERROR(ENOMEM);
|
||
139 | } |
||
140 | /* ff_mov_read_stsd_entries updates stream s->nb_streams-1,
|
||
141 | * so set it temporarily to indicate which stream to update. */
|
||
142 | s->nb_streams = st->index + 1;
|
||
143 | ff_mov_read_stsd_entries(mc, &pb, 1);
|
||
144 | qt->bytes_per_frame = msc->bytes_per_frame; |
||
145 | av_free(msc); |
||
146 | av_free(mc); |
||
147 | st->priv_data = priv_data; |
||
148 | s->nb_streams = nb_streams; |
||
149 | break;
|
||
150 | } |
||
151 | default:
|
||
152 | url_fskip(&pb, tlv_len); |
||
153 | break;
|
||
154 | } |
||
155 | } |
||
156 | |||
157 | /* 32-bit alignment */
|
||
158 | url_fskip(&pb, ((url_ftell(&pb) + 3) & ~3) - url_ftell(&pb)); |
||
159 | } else
|
||
160 | url_fseek(&pb, 4, SEEK_SET);
|
||
161 | |||
162 | if (has_packet_info) {
|
||
163 | av_log_missing_feature(s, "RTP-X-QT with packet specific info", 1); |
||
164 | return AVERROR_NOTSUPP;
|
||
165 | } |
||
166 | |||
167 | alen = len - url_ftell(&pb); |
||
168 | if (alen <= 0) |
||
169 | return AVERROR_INVALIDDATA;
|
||
170 | |||
171 | switch (packing_scheme) {
|
||
172 | case 3: /* one data packet spread over 1 or multiple RTP packets */ |
||
173 | if (qt->pkt.size > 0 && qt->timestamp == *timestamp) { |
||
174 | qt->pkt.data = av_realloc(qt->pkt.data, qt->pkt.size + alen + |
||
175 | FF_INPUT_BUFFER_PADDING_SIZE); |
||
176 | } else {
|
||
177 | av_freep(&qt->pkt.data); |
||
178 | av_init_packet(&qt->pkt); |
||
179 | qt->pkt.data = av_malloc(alen + FF_INPUT_BUFFER_PADDING_SIZE); |
||
180 | qt->pkt.size = 0;
|
||
181 | qt->timestamp = *timestamp; |
||
182 | } |
||
183 | if (!qt->pkt.data)
|
||
184 | return AVERROR(ENOMEM);
|
||
185 | memcpy(qt->pkt.data + qt->pkt.size, buf + url_ftell(&pb), alen); |
||
186 | qt->pkt.size += alen; |
||
187 | if (has_marker_bit) {
|
||
188 | *pkt = qt->pkt; |
||
189 | qt->pkt.size = 0;
|
||
190 | qt->pkt.data = NULL;
|
||
191 | pkt->flags = flags & RTP_FLAG_KEY ? AV_PKT_FLAG_KEY : 0;
|
||
192 | pkt->stream_index = st->index; |
||
193 | pkt->destruct = av_destruct_packet; |
||
194 | memset(pkt->data + pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
|
||
195 | return 0; |
||
196 | } |
||
197 | return AVERROR(EAGAIN);
|
||
198 | |||
199 | case 1: /* constant packet size, multiple packets per RTP packet */ |
||
200 | if (qt->bytes_per_frame == 0 || |
||
201 | alen % qt->bytes_per_frame != 0)
|
||
202 | return AVERROR_INVALIDDATA; /* wrongly padded */ |
||
203 | qt->remaining = (alen / qt->bytes_per_frame) - 1;
|
||
204 | if (av_new_packet(pkt, qt->bytes_per_frame))
|
||
205 | return AVERROR(ENOMEM);
|
||
206 | memcpy(pkt->data, buf + url_ftell(&pb), qt->bytes_per_frame); |
||
207 | pkt->flags = flags & RTP_FLAG_KEY ? AV_PKT_FLAG_KEY : 0;
|
||
208 | pkt->stream_index = st->index; |
||
209 | if (qt->remaining > 0) { |
||
210 | av_freep(&qt->pkt.data); |
||
211 | qt->pkt.data = av_malloc(qt->remaining * qt->bytes_per_frame); |
||
212 | if (!qt->pkt.data) {
|
||
213 | av_free_packet(pkt); |
||
214 | return AVERROR(ENOMEM);
|
||
215 | } |
||
216 | qt->pkt.size = qt->remaining * qt->bytes_per_frame; |
||
217 | memcpy(qt->pkt.data, |
||
218 | buf + url_ftell(&pb) + qt->bytes_per_frame, |
||
219 | qt->remaining * qt->bytes_per_frame); |
||
220 | qt->pkt.flags = pkt->flags; |
||
221 | return 1; |
||
222 | } |
||
223 | return 0; |
||
224 | |||
225 | default: /* unimplemented */ |
||
226 | av_log_missing_feature(NULL, "RTP-X-QT with packing scheme 2", 1); |
||
227 | return AVERROR_NOTSUPP;
|
||
228 | } |
||
229 | } |
||
230 | |||
231 | static PayloadContext *qt_rtp_new(void) |
||
232 | { |
||
233 | return av_mallocz(sizeof(PayloadContext)); |
||
234 | } |
||
235 | |||
236 | static void qt_rtp_free(PayloadContext *qt) |
||
237 | { |
||
238 | av_freep(&qt->pkt.data); |
||
239 | av_free(qt); |
||
240 | } |
||
241 | |||
242 | #define RTP_QT_HANDLER(m, n, s, t) \
|
||
243 | RTPDynamicProtocolHandler ff_ ## m ## _rtp_ ## n ## _handler = { \ |
||
244 | fe70a95e | Martin Storsjö | .enc_name = s, \ |
245 | .codec_type = t, \ |
||
246 | .codec_id = CODEC_ID_NONE, \ |
||
247 | .open = qt_rtp_new, \ |
||
248 | .close = qt_rtp_free, \ |
||
249 | .parse_packet = qt_rtp_parse_packet, \ |
||
250 | 44adbebe | Mans Rullgard | } |
251 | 3ece3e4c | Martin Storsjö | |
252 | b2ed95ec | Anton Khirnov | RTP_QT_HANDLER(qt, vid, "X-QT", AVMEDIA_TYPE_VIDEO);
|
253 | RTP_QT_HANDLER(qt, aud, "X-QT", AVMEDIA_TYPE_AUDIO);
|
||
254 | RTP_QT_HANDLER(quicktime, vid, "X-QUICKTIME", AVMEDIA_TYPE_VIDEO);
|
||
255 | RTP_QT_HANDLER(quicktime, aud, "X-QUICKTIME", AVMEDIA_TYPE_AUDIO); |