Browse code

Add RTP depacketization of the X-QT QuickTime format

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

Martin Storsjö authored on 2010/10/06 21:42:18
Showing 7 changed files
... ...
@@ -43,6 +43,7 @@ version <next>:
43 43
 - yadif filter
44 44
 - blackframe filter
45 45
 - Demuxer for Leitch/Harris' VR native stream format (LXF)
46
+- RTP depacketization of the X-QT QuickTime format
46 47
 
47 48
 
48 49
 version 0.6:
... ...
@@ -1363,7 +1363,7 @@ ogg_demuxer_select="golomb"
1363 1363
 psp_muxer_select="mov_muxer"
1364 1364
 rtsp_demuxer_select="http_protocol sdp_demuxer"
1365 1365
 rtsp_muxer_select="rtp_muxer http_protocol sdp_demuxer"
1366
-sdp_demuxer_select="asf_demuxer rm_demuxer rtp_protocol mpegts_demuxer"
1366
+sdp_demuxer_select="asf_demuxer rm_demuxer rtp_protocol mpegts_demuxer mov_demuxer"
1367 1367
 spdif_muxer_select="aac_parser"
1368 1368
 tg2_muxer_select="mov_muxer"
1369 1369
 tgp_muxer_select="mov_muxer"
... ...
@@ -240,6 +240,7 @@ OBJS-$(CONFIG_SDP_DEMUXER)               += rtsp.o        \
240 240
                                             rtpdec_latm.o \
241 241
                                             rtpdec_mpeg4.o \
242 242
                                             rtpdec_qdm2.o \
243
+                                            rtpdec_qt.o   \
243 244
                                             rtpdec_svq3.o \
244 245
                                             rtpdec_vp8.o  \
245 246
                                             rtpdec_xiph.o
... ...
@@ -22,7 +22,7 @@
22 22
 #define AVFORMAT_AVFORMAT_H
23 23
 
24 24
 #define LIBAVFORMAT_VERSION_MAJOR 52
25
-#define LIBAVFORMAT_VERSION_MINOR 79
25
+#define LIBAVFORMAT_VERSION_MINOR 80
26 26
 #define LIBAVFORMAT_VERSION_MICRO  0
27 27
 
28 28
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
... ...
@@ -70,6 +70,11 @@ void av_register_rtp_dynamic_payload_handlers(void)
70 70
 
71 71
     ff_register_dynamic_payload_handler(&ff_ms_rtp_asf_pfv_handler);
72 72
     ff_register_dynamic_payload_handler(&ff_ms_rtp_asf_pfa_handler);
73
+
74
+    ff_register_dynamic_payload_handler(&ff_qt_rtp_aud_handler);
75
+    ff_register_dynamic_payload_handler(&ff_qt_rtp_vid_handler);
76
+    ff_register_dynamic_payload_handler(&ff_quicktime_rtp_aud_handler);
77
+    ff_register_dynamic_payload_handler(&ff_quicktime_rtp_vid_handler);
73 78
 }
74 79
 
75 80
 static int rtcp_parse_packet(RTPDemuxContext *s, const unsigned char *buf, int len)
... ...
@@ -42,6 +42,10 @@ extern RTPDynamicProtocolHandler ff_mpeg4_generic_dynamic_handler;
42 42
 extern RTPDynamicProtocolHandler ff_ms_rtp_asf_pfa_handler;
43 43
 extern RTPDynamicProtocolHandler ff_ms_rtp_asf_pfv_handler;
44 44
 extern RTPDynamicProtocolHandler ff_qdm2_dynamic_handler;
45
+extern RTPDynamicProtocolHandler ff_qt_rtp_aud_handler;
46
+extern RTPDynamicProtocolHandler ff_qt_rtp_vid_handler;
47
+extern RTPDynamicProtocolHandler ff_quicktime_rtp_aud_handler;
48
+extern RTPDynamicProtocolHandler ff_quicktime_rtp_vid_handler;
45 49
 extern RTPDynamicProtocolHandler ff_svq3_dynamic_handler;
46 50
 extern RTPDynamicProtocolHandler ff_theora_dynamic_handler;
47 51
 extern RTPDynamicProtocolHandler ff_vorbis_dynamic_handler;
48 52
new file mode 100644
... ...
@@ -0,0 +1,255 @@
0
+/*
1
+ * RTP/Quicktime support.
2
+ * Copyright (c) 2009 Ronald S. Bultje
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+/**
22
+ * @file
23
+ * @brief Quicktime-style RTP support
24
+ * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
25
+ */
26
+
27
+#include "avformat.h"
28
+#include "rtp.h"
29
+#include "rtpdec.h"
30
+#include "isom.h"
31
+#include "libavcodec/get_bits.h"
32
+
33
+struct PayloadContext {
34
+    AVPacket pkt;
35
+    int bytes_per_frame, remaining;
36
+    uint32_t timestamp;
37
+};
38
+
39
+static int qt_rtp_parse_packet(AVFormatContext *s, PayloadContext *qt,
40
+                               AVStream *st, AVPacket *pkt,
41
+                               uint32_t *timestamp, const uint8_t *buf,
42
+                               int len, int flags)
43
+{
44
+    ByteIOContext pb;
45
+    GetBitContext gb;
46
+    int packing_scheme, has_payload_desc, has_packet_info, alen,
47
+        has_marker_bit = flags & RTP_FLAG_MARKER;
48
+
49
+    if (qt->remaining) {
50
+        int num = qt->pkt.size / qt->bytes_per_frame;
51
+
52
+        if (av_new_packet(pkt, qt->bytes_per_frame))
53
+            return AVERROR(ENOMEM);
54
+        pkt->stream_index = st->index;
55
+        pkt->flags        = qt->pkt.flags;
56
+        memcpy(pkt->data,
57
+               &qt->pkt.data[(num - qt->remaining) * qt->bytes_per_frame],
58
+               qt->bytes_per_frame);
59
+        if (--qt->remaining == 0) {
60
+            av_freep(&qt->pkt.data);
61
+            qt->pkt.size = 0;
62
+        }
63
+        return qt->remaining > 0;
64
+    }
65
+
66
+    /**
67
+     * The RTP payload is described in:
68
+     * http://developer.apple.com/quicktime/icefloe/dispatch026.html
69
+     */
70
+    init_get_bits(&gb, buf, len << 3);
71
+    init_put_byte(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
72
+
73
+    if (len < 4)
74
+        return AVERROR_INVALIDDATA;
75
+
76
+    skip_bits(&gb, 4); // version
77
+    if ((packing_scheme = get_bits(&gb, 2)) == 0)
78
+        return AVERROR_INVALIDDATA;
79
+    if (get_bits1(&gb))
80
+        flags          |= RTP_FLAG_KEY;
81
+    has_payload_desc    = get_bits1(&gb);
82
+    has_packet_info     = get_bits1(&gb);
83
+    skip_bits(&gb, 23); // reserved:7, cache payload info:1, payload ID:15
84
+
85
+    if (has_payload_desc) {
86
+        int data_len, pos, is_start, is_finish;
87
+        uint32_t tag;
88
+
89
+        pos = get_bits_count(&gb) >> 3;
90
+        if (pos + 12 > len)
91
+            return AVERROR_INVALIDDATA;
92
+
93
+        skip_bits(&gb, 2); // has non-I frames:1, is sparse:1
94
+        is_start  = get_bits1(&gb);
95
+        is_finish = get_bits1(&gb);
96
+        if (!is_start || !is_finish) {
97
+            av_log_missing_feature(s, "RTP-X-QT with payload description "
98
+                                      "split over several packets", 1);
99
+            return AVERROR_NOTSUPP;
100
+        }
101
+        skip_bits(&gb, 12); // reserved
102
+        data_len = get_bits(&gb, 16);
103
+
104
+        url_fseek(&pb, pos + 4, SEEK_SET);
105
+        tag = get_le32(&pb);
106
+        if ((st->codec->codec_type == CODEC_TYPE_VIDEO &&
107
+                 tag != MKTAG('v','i','d','e')) ||
108
+            (st->codec->codec_type == CODEC_TYPE_AUDIO &&
109
+                 tag != MKTAG('s','o','u','n')))
110
+            return AVERROR_INVALIDDATA;
111
+        av_set_pts_info(st, 32, 1, get_be32(&pb));
112
+
113
+        if (pos + data_len > len)
114
+            return AVERROR_INVALIDDATA;
115
+        /* TLVs */
116
+        while (url_ftell(&pb) + 4 < pos + data_len) {
117
+            int tlv_len = get_be16(&pb);
118
+            tag = get_le16(&pb);
119
+            if (url_ftell(&pb) + tlv_len > pos + data_len)
120
+                return AVERROR_INVALIDDATA;
121
+
122
+#define MKTAG16(a,b) MKTAG(a,b,0,0)
123
+            switch (tag) {
124
+            case MKTAG16('s','d'): {
125
+                MOVStreamContext *msc;
126
+                void *priv_data = st->priv_data;
127
+                int nb_streams = s->nb_streams;
128
+                MOVContext *mc = av_mallocz(sizeof(*mc));
129
+                if (!mc)
130
+                    return AVERROR(ENOMEM);
131
+                mc->fc = s;
132
+                st->priv_data = msc = av_mallocz(sizeof(MOVStreamContext));
133
+                if (!msc) {
134
+                    av_free(mc);
135
+                    st->priv_data = priv_data;
136
+                    return AVERROR(ENOMEM);
137
+                }
138
+                /* ff_mov_read_stsd_entries updates stream s->nb_streams-1,
139
+                 * so set it temporarily to indicate which stream to update. */
140
+                s->nb_streams = st->index + 1;
141
+                ff_mov_read_stsd_entries(mc, &pb, 1);
142
+                qt->bytes_per_frame = msc->bytes_per_frame;
143
+                av_free(msc);
144
+                av_free(mc);
145
+                st->priv_data = priv_data;
146
+                s->nb_streams = nb_streams;
147
+                break;
148
+            }
149
+            default:
150
+                url_fskip(&pb, tlv_len);
151
+                break;
152
+            }
153
+        }
154
+
155
+        /* 32-bit alignment */
156
+        url_fskip(&pb, ((url_ftell(&pb) + 3) & ~3) - url_ftell(&pb));
157
+    } else
158
+        url_fseek(&pb, 4, SEEK_SET);
159
+
160
+    if (has_packet_info) {
161
+        av_log_missing_feature(s, "RTP-X-QT with packet specific info", 1);
162
+        return AVERROR_NOTSUPP;
163
+    }
164
+
165
+    alen = len - url_ftell(&pb);
166
+    if (alen <= 0)
167
+        return AVERROR_INVALIDDATA;
168
+
169
+    switch (packing_scheme) {
170
+    case 3: /* one data packet spread over 1 or multiple RTP packets */
171
+        if (qt->pkt.size > 0 && qt->timestamp == *timestamp) {
172
+            qt->pkt.data = av_realloc(qt->pkt.data, qt->pkt.size + alen +
173
+                                      FF_INPUT_BUFFER_PADDING_SIZE);
174
+        } else {
175
+            av_freep(&qt->pkt.data);
176
+            av_init_packet(&qt->pkt);
177
+            qt->pkt.data = av_malloc(alen + FF_INPUT_BUFFER_PADDING_SIZE);
178
+            qt->pkt.size = 0;
179
+            qt->timestamp = *timestamp;
180
+        }
181
+        if (!qt->pkt.data)
182
+            return AVERROR(ENOMEM);
183
+        memcpy(qt->pkt.data + qt->pkt.size, buf + url_ftell(&pb), alen);
184
+        qt->pkt.size += alen;
185
+        if (has_marker_bit) {
186
+            *pkt = qt->pkt;
187
+            qt->pkt.size = 0;
188
+            qt->pkt.data = NULL;
189
+            pkt->flags        = flags & RTP_FLAG_KEY ? AV_PKT_FLAG_KEY : 0;
190
+            pkt->stream_index = st->index;
191
+            pkt->destruct     = av_destruct_packet;
192
+            memset(pkt->data + pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
193
+            return 0;
194
+        }
195
+        return AVERROR(EAGAIN);
196
+
197
+    case 1: /* constant packet size, multiple packets per RTP packet */
198
+        if (qt->bytes_per_frame == 0 ||
199
+            alen % qt->bytes_per_frame != 0)
200
+            return AVERROR_INVALIDDATA; /* wrongly padded */
201
+        qt->remaining = (alen / qt->bytes_per_frame) - 1;
202
+        if (av_new_packet(pkt, qt->bytes_per_frame))
203
+            return AVERROR(ENOMEM);
204
+        memcpy(pkt->data, buf + url_ftell(&pb), qt->bytes_per_frame);
205
+        pkt->flags = flags & RTP_FLAG_KEY ? AV_PKT_FLAG_KEY : 0;
206
+        pkt->stream_index = st->index;
207
+        if (qt->remaining > 0) {
208
+            av_freep(&qt->pkt.data);
209
+            qt->pkt.data = av_malloc(qt->remaining * qt->bytes_per_frame);
210
+            if (!qt->pkt.data) {
211
+                av_free_packet(pkt);
212
+                return AVERROR(ENOMEM);
213
+            }
214
+            qt->pkt.size = qt->remaining * qt->bytes_per_frame;
215
+            memcpy(qt->pkt.data,
216
+                   buf + url_ftell(&pb) + qt->bytes_per_frame,
217
+                   qt->remaining * qt->bytes_per_frame);
218
+            qt->pkt.flags = pkt->flags;
219
+            return 1;
220
+        }
221
+        return 0;
222
+
223
+    default:  /* unimplemented */
224
+        av_log_missing_feature(NULL, "RTP-X-QT with packing scheme 2", 1);
225
+        return AVERROR_NOTSUPP;
226
+    }
227
+}
228
+
229
+static PayloadContext *qt_rtp_new(void)
230
+{
231
+    return av_mallocz(sizeof(PayloadContext));
232
+}
233
+
234
+static void qt_rtp_free(PayloadContext *qt)
235
+{
236
+    av_freep(&qt->pkt.data);
237
+    av_free(qt);
238
+}
239
+
240
+#define RTP_QT_HANDLER(m, n, s, t) \
241
+RTPDynamicProtocolHandler ff_ ## m ## _rtp_ ## n ## _handler = { \
242
+    s, \
243
+    t, \
244
+    CODEC_ID_NONE, \
245
+    NULL,          \
246
+    qt_rtp_new,    \
247
+    qt_rtp_free,   \
248
+    qt_rtp_parse_packet, \
249
+};
250
+
251
+RTP_QT_HANDLER(qt,        vid, "X-QT",        CODEC_TYPE_VIDEO);
252
+RTP_QT_HANDLER(qt,        aud, "X-QT",        CODEC_TYPE_AUDIO);
253
+RTP_QT_HANDLER(quicktime, vid, "X-QUICKTIME", CODEC_TYPE_VIDEO);
254
+RTP_QT_HANDLER(quicktime, aud, "X-QUICKTIME", CODEC_TYPE_AUDIO);