Statistics
| Branch: | Revision:

ffmpeg / libavformat / rdt.c @ d7fb5a18

History | View | Annotate | Download (14.6 KB)

1 e9dea59f Ronald S. Bultje
/*
2
 * Realmedia RTSP protocol (RDT) support.
3
 * Copyright (c) 2007 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 rdt.c
24
 * @brief Realmedia RTSP protocol (RDT) support
25
 * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net>
26
 */
27
28
#include "avformat.h"
29
#include "libavutil/avstring.h"
30 ff13ba92 Ronald S. Bultje
#include "rtp_internal.h"
31 e9dea59f Ronald S. Bultje
#include "rdt.h"
32
#include "libavutil/base64.h"
33
#include "libavutil/md5.h"
34
#include "rm.h"
35
#include "internal.h"
36 6bafd6f5 Ronald S. Bultje
#include <libavcodec/bitstream.h>
37 e9dea59f Ronald S. Bultje
38 accc248f Ronald S. Bultje
struct RDTDemuxContext {
39 63f412f9 Ronald S. Bultje
    AVFormatContext *ic; /**< the containing (RTSP) demux context */
40 4f602856 Ronald S. Bultje
    /** Each RDT stream-set (represented by one RTSPStream) can contain
41
     * multiple streams (of the same content, but with possibly different
42
     * codecs/bitrates). Each such stream is represented by one AVStream
43
     * in the AVFormatContext, and this variable points to the offset in
44
     * that array such that the first is the first stream of this set. */
45
    AVStream **streams;
46
    int n_streams; /**< streams with identifical content in this set */
47 accc248f Ronald S. Bultje
    void *dynamic_protocol_context;
48
    DynamicPayloadPacketHandlerProc parse_packet;
49 9168f7e6 Ronald S. Bultje
    uint32_t prev_timestamp;
50 7960e18f Ronald S. Bultje
    int prev_set_id, prev_stream_id;
51 accc248f Ronald S. Bultje
};
52
53
RDTDemuxContext *
54 e0d1eabf Ronald S. Bultje
ff_rdt_parse_open(AVFormatContext *ic, int first_stream_of_set_idx,
55 accc248f Ronald S. Bultje
                  void *priv_data, RTPDynamicProtocolHandler *handler)
56
{
57
    RDTDemuxContext *s = av_mallocz(sizeof(RDTDemuxContext));
58
    if (!s)
59
        return NULL;
60
61
    s->ic = ic;
62 4f602856 Ronald S. Bultje
    s->streams = &ic->streams[first_stream_of_set_idx];
63
    do {
64
        s->n_streams++;
65
    } while (first_stream_of_set_idx + s->n_streams < ic->nb_streams &&
66
             s->streams[s->n_streams]->priv_data == s->streams[0]->priv_data);
67 239dec21 Ronald S. Bultje
    s->prev_set_id    = -1;
68 7960e18f Ronald S. Bultje
    s->prev_stream_id = -1;
69 239dec21 Ronald S. Bultje
    s->prev_timestamp = -1;
70 accc248f Ronald S. Bultje
    s->parse_packet = handler->parse_packet;
71
    s->dynamic_protocol_context = priv_data;
72
73
    return s;
74
}
75
76
void
77
ff_rdt_parse_close(RDTDemuxContext *s)
78
{
79
    av_free(s);
80
}
81
82 ed0aacc7 Ronald S. Bultje
struct PayloadContext {
83 ff13ba92 Ronald S. Bultje
    AVFormatContext *rmctx;
84
    uint8_t *mlti_data;
85
    unsigned int mlti_data_size;
86 4fce284c Ronald S. Bultje
    char buffer[RTP_MAX_PACKET_LENGTH + FF_INPUT_BUFFER_PADDING_SIZE];
87 a15ebf34 Ronald S. Bultje
    int audio_pkt_cnt[MAX_STREAMS]; /**< remaining audio packets in rmdec */
88 ed0aacc7 Ronald S. Bultje
};
89 ff13ba92 Ronald S. Bultje
90 e9dea59f Ronald S. Bultje
void
91
ff_rdt_calc_response_and_checksum(char response[41], char chksum[9],
92
                                  const char *challenge)
93
{
94
    int ch_len = strlen (challenge), i;
95
    unsigned char zres[16],
96
        buf[64] = { 0xa1, 0xe9, 0x14, 0x9d, 0x0e, 0x6b, 0x3b, 0x59 };
97
#define XOR_TABLE_SIZE 37
98
    const unsigned char xor_table[XOR_TABLE_SIZE] = {
99
        0x05, 0x18, 0x74, 0xd0, 0x0d, 0x09, 0x02, 0x53,
100
        0xc0, 0x01, 0x05, 0x05, 0x67, 0x03, 0x19, 0x70,
101
        0x08, 0x27, 0x66, 0x10, 0x10, 0x72, 0x08, 0x09,
102
        0x63, 0x11, 0x03, 0x71, 0x08, 0x08, 0x70, 0x02,
103
        0x10, 0x57, 0x05, 0x18, 0x54 };
104
105
    /* some (length) checks */
106
    if (ch_len == 40) /* what a hack... */
107
        ch_len = 32;
108
    else if (ch_len > 56)
109
        ch_len = 56;
110
    memcpy(buf + 8, challenge, ch_len);
111
112
    /* xor challenge bytewise with xor_table */
113
    for (i = 0; i < XOR_TABLE_SIZE; i++)
114
        buf[8 + i] ^= xor_table[i];
115
116
    av_md5_sum(zres, buf, 64);
117
    ff_data_to_hex(response, zres, 16);
118
    for (i=0;i<32;i++) response[i] = tolower(response[i]);
119
120
    /* add tail */
121
    strcpy (response + 32, "01d0a8e3");
122
123
    /* calculate checksum */
124
    for (i = 0; i < 8; i++)
125
        chksum[i] = response[i * 4];
126
    chksum[8] = 0;
127
}
128 ff13ba92 Ronald S. Bultje
129
static int
130 ed0aacc7 Ronald S. Bultje
rdt_load_mdpr (PayloadContext *rdt, AVStream *st, int rule_nr)
131 ff13ba92 Ronald S. Bultje
{
132 a4b8cb3c Ronald S. Bultje
    ByteIOContext pb;
133 ff13ba92 Ronald S. Bultje
    int size;
134
    uint32_t tag;
135
136
    /**
137
     * Layout of the MLTI chunk:
138
     * 4:MLTI
139
     * 2:<number of streams>
140
     * Then for each stream ([number_of_streams] times):
141
     *     2:<mdpr index>
142
     * 2:<number of mdpr chunks>
143
     * Then for each mdpr chunk ([number_of_mdpr_chunks] times):
144
     *     4:<size>
145
     *     [size]:<data>
146
     * we skip MDPR chunks until we reach the one of the stream
147
     * we're interested in, and forward that ([size]+[data]) to
148
     * the RM demuxer to parse the stream-specific header data.
149
     */
150
    if (!rdt->mlti_data)
151
        return -1;
152 a4b8cb3c Ronald S. Bultje
    init_put_byte(&pb, rdt->mlti_data, rdt->mlti_data_size, 0,
153
                  NULL, NULL, NULL, NULL);
154
    tag = get_le32(&pb);
155 ff13ba92 Ronald S. Bultje
    if (tag == MKTAG('M', 'L', 'T', 'I')) {
156
        int num, chunk_nr;
157
158
        /* read index of MDPR chunk numbers */
159 a4b8cb3c Ronald S. Bultje
        num = get_be16(&pb);
160 ff13ba92 Ronald S. Bultje
        if (rule_nr < 0 || rule_nr >= num)
161
            return -1;
162 a4b8cb3c Ronald S. Bultje
        url_fskip(&pb, rule_nr * 2);
163
        chunk_nr = get_be16(&pb);
164
        url_fskip(&pb, (num - 1 - rule_nr) * 2);
165 ff13ba92 Ronald S. Bultje
166
        /* read MDPR chunks */
167 a4b8cb3c Ronald S. Bultje
        num = get_be16(&pb);
168 ff13ba92 Ronald S. Bultje
        if (chunk_nr >= num)
169
            return -1;
170
        while (chunk_nr--)
171 a4b8cb3c Ronald S. Bultje
            url_fskip(&pb, get_be32(&pb));
172
        size = get_be32(&pb);
173 ff13ba92 Ronald S. Bultje
    } else {
174
        size = rdt->mlti_data_size;
175 a4b8cb3c Ronald S. Bultje
        url_fseek(&pb, 0, SEEK_SET);
176 ff13ba92 Ronald S. Bultje
    }
177 a4b8cb3c Ronald S. Bultje
    if (ff_rm_read_mdpr_codecdata(rdt->rmctx, &pb, st, size) < 0)
178 ff13ba92 Ronald S. Bultje
        return -1;
179
180
    return 0;
181
}
182
183 4fce284c Ronald S. Bultje
/**
184
 * Actual data handling.
185
 */
186
187 985b05d3 Ronald S. Bultje
int
188
ff_rdt_parse_header(const uint8_t *buf, int len,
189 e269ab79 Ronald S. Bultje
                    int *pset_id, int *pseq_no, int *pstream_id,
190
                    int *pis_keyframe, uint32_t *ptimestamp)
191 4fce284c Ronald S. Bultje
{
192 6bafd6f5 Ronald S. Bultje
    GetBitContext gb;
193 43af8b2b Ronald S. Bultje
    int consumed = 0, set_id, seq_no, stream_id, is_keyframe,
194
        len_included, need_reliable;
195 6bafd6f5 Ronald S. Bultje
    uint32_t timestamp;
196 4fce284c Ronald S. Bultje
197 e3b7216b Ronald S. Bultje
    /* skip status packets */
198
    while (len >= 5 && buf[1] == 0xFF /* status packet */) {
199
        int pkt_len;
200
201
        if (!(buf[0] & 0x80))
202
            return -1; /* not followed by a data packet */
203
204
        pkt_len = AV_RB16(buf+3);
205
        buf += pkt_len;
206
        len -= pkt_len;
207
        consumed += pkt_len;
208 4fce284c Ronald S. Bultje
    }
209 43af8b2b Ronald S. Bultje
    if (len < 16)
210 985b05d3 Ronald S. Bultje
        return -1;
211 9e164392 Ronald S. Bultje
    /**
212
     * Layout of the header (in bits):
213
     * 1:  len_included
214
     *     Flag indicating whether this header includes a length field;
215
     *     this can be used to concatenate multiple RDT packets in a
216
     *     single UDP/TCP data frame and is used to precede RDT data
217
     *     by stream status packets
218
     * 1:  need_reliable
219
     *     Flag indicating whether this header includes a "reliable
220
     *     sequence number"; these are apparently sequence numbers of
221
     *     data packets alone. For data packets, this flag is always
222
     *     set, according to the Real documentation [1]
223
     * 5:  set_id
224
     *     ID of a set of streams of identical content, possibly with
225
     *     different codecs or bitrates
226
     * 1:  is_reliable
227
     *     Flag set for certain streams deemed less tolerable for packet
228
     *     loss
229
     * 16: seq_no
230
     *     Packet sequence number; if >=0xFF00, this is a non-data packet
231
     *     containing stream status info, the second byte indicates the
232
     *     type of status packet (see wireshark docs / source code [2])
233
     * if (len_included) {
234
     *     16: packet_len
235
     * } else {
236
     *     packet_len = remainder of UDP/TCP frame
237
     * }
238
     * 1:  is_back_to_back
239
     *     Back-to-Back flag; used for timing, set for one in every 10
240
     *     packets, according to the Real documentation [1]
241
     * 1:  is_slow_data
242
     *     Slow-data flag; currently unused, according to Real docs [1]
243
     * 5:  stream_id
244
     *     ID of the stream within this particular set of streams
245
     * 1:  is_no_keyframe
246
     *     Non-keyframe flag (unset if packet belongs to a keyframe)
247
     * 32: timestamp (PTS)
248
     * if (set_id == 0x1F) {
249
     *     16: set_id (extended set-of-streams ID; see set_id)
250
     * }
251
     * if (need_reliable) {
252
     *     16: reliable_seq_no
253
     *         Reliable sequence number (see need_reliable)
254
     * }
255
     * if (stream_id == 0x3F) {
256
     *     16: stream_id (extended stream ID; see stream_id)
257
     * }
258
     * [1] https://protocol.helixcommunity.org/files/2005/devdocs/RDT_Feature_Level_20.txt
259
     * [2] http://www.wireshark.org/docs/dfref/r/rdt.html and
260
     *     http://anonsvn.wireshark.org/viewvc/trunk/epan/dissectors/packet-rdt.c
261
     */
262 6bafd6f5 Ronald S. Bultje
    init_get_bits(&gb, buf, len << 3);
263 43af8b2b Ronald S. Bultje
    len_included  = get_bits1(&gb);
264
    need_reliable = get_bits1(&gb);
265 6bafd6f5 Ronald S. Bultje
    set_id        = get_bits(&gb, 5);
266
    skip_bits(&gb, 1);
267
    seq_no        = get_bits(&gb, 16);
268 43af8b2b Ronald S. Bultje
    if (len_included)
269
        skip_bits(&gb, 16);
270 6bafd6f5 Ronald S. Bultje
    skip_bits(&gb, 2);
271
    stream_id     = get_bits(&gb, 5);
272
    is_keyframe   = !get_bits1(&gb);
273
    timestamp     = get_bits_long(&gb, 32);
274 43af8b2b Ronald S. Bultje
    if (set_id == 0x1f)
275
        set_id    = get_bits(&gb, 16);
276
    if (need_reliable)
277 90e0450f Ronald S. Bultje
        skip_bits(&gb, 16);
278 43af8b2b Ronald S. Bultje
    if (stream_id == 0x1f)
279
        stream_id = get_bits(&gb, 16);
280 4fce284c Ronald S. Bultje
281 6bafd6f5 Ronald S. Bultje
    if (pset_id)      *pset_id      = set_id;
282
    if (pseq_no)      *pseq_no      = seq_no;
283
    if (pstream_id)   *pstream_id   = stream_id;
284
    if (pis_keyframe) *pis_keyframe = is_keyframe;
285
    if (ptimestamp)   *ptimestamp   = timestamp;
286
287
    return consumed + (get_bits_count(&gb) >> 3);
288 4fce284c Ronald S. Bultje
}
289
290
/**< return 0 on packet, no more left, 1 on packet, 1 on partial packet... */
291
static int
292 9b932b8a Ronald S. Bultje
rdt_parse_packet (PayloadContext *rdt, AVStream *st,
293
                  AVPacket *pkt, uint32_t *timestamp,
294 4fce284c Ronald S. Bultje
                  const uint8_t *buf, int len, int flags)
295
{
296
    int seq = 1, res;
297 a4b8cb3c Ronald S. Bultje
    ByteIOContext pb;
298 4fce284c Ronald S. Bultje
299 a15ebf34 Ronald S. Bultje
    if (rdt->audio_pkt_cnt == 0) {
300 4fce284c Ronald S. Bultje
        int pos;
301
302 a4b8cb3c Ronald S. Bultje
        init_put_byte(&pb, buf, len, 0, NULL, NULL, NULL, NULL);
303 4fce284c Ronald S. Bultje
        flags = (flags & PKT_FLAG_KEY) ? 2 : 0;
304 a4b8cb3c Ronald S. Bultje
        res = ff_rm_parse_packet (rdt->rmctx, &pb, st, len, pkt,
305 4fce284c Ronald S. Bultje
                                  &seq, &flags, timestamp);
306 a4b8cb3c Ronald S. Bultje
        pos = url_ftell(&pb);
307 4fce284c Ronald S. Bultje
        if (res < 0)
308
            return res;
309 a15ebf34 Ronald S. Bultje
        rdt->audio_pkt_cnt[st->id] = res;
310
        if (rdt->audio_pkt_cnt[st->id] > 0 &&
311 4fce284c Ronald S. Bultje
            st->codec->codec_id == CODEC_ID_AAC) {
312
            memcpy (rdt->buffer, buf + pos, len - pos);
313 a4b8cb3c Ronald S. Bultje
            rdt->rmctx->pb = av_alloc_put_byte (rdt->buffer, len - pos, 0,
314
                                                NULL, NULL, NULL, NULL);
315 4fce284c Ronald S. Bultje
        }
316
    } else {
317 fcc995a5 Ronald S. Bultje
        ff_rm_retrieve_cache (rdt->rmctx, rdt->rmctx->pb, st, pkt);
318 a15ebf34 Ronald S. Bultje
        if (rdt->audio_pkt_cnt[st->id] == 0 &&
319 4fce284c Ronald S. Bultje
            st->codec->codec_id == CODEC_ID_AAC)
320 a4b8cb3c Ronald S. Bultje
            av_freep(&rdt->rmctx->pb);
321 4fce284c Ronald S. Bultje
    }
322
    pkt->stream_index = st->index;
323
    pkt->pts = *timestamp;
324
325 a15ebf34 Ronald S. Bultje
    return rdt->audio_pkt_cnt[st->id] > 0;
326 4fce284c Ronald S. Bultje
}
327
328
int
329 accc248f Ronald S. Bultje
ff_rdt_parse_packet(RDTDemuxContext *s, AVPacket *pkt,
330 4fce284c Ronald S. Bultje
                    const uint8_t *buf, int len)
331
{
332 114732f4 Ronald S. Bultje
    int seq_no, flags = 0, stream_id, set_id, is_keyframe;
333 4fce284c Ronald S. Bultje
    uint32_t timestamp;
334
    int rv= 0;
335
336 3ff2a062 Ronald S. Bultje
    if (!s->parse_packet)
337
        return -1;
338
339 7960e18f Ronald S. Bultje
    if (!buf && s->prev_stream_id != -1) {
340 4fce284c Ronald S. Bultje
        /* return the next packets, if any */
341
        timestamp= 0; ///< Should not be used if buf is NULL, but should be set to the timestamp of the packet returned....
342 9b932b8a Ronald S. Bultje
        rv= s->parse_packet(s->dynamic_protocol_context,
343 7960e18f Ronald S. Bultje
                            s->streams[s->prev_stream_id],
344
                            pkt, &timestamp, NULL, 0, flags);
345 4fce284c Ronald S. Bultje
        return rv;
346
    }
347
348
    if (len < 12)
349
        return -1;
350 114732f4 Ronald S. Bultje
    rv = ff_rdt_parse_header(buf, len, &set_id, &seq_no, &stream_id, &is_keyframe, &timestamp);
351 4fce284c Ronald S. Bultje
    if (rv < 0)
352
        return rv;
353 7960e18f Ronald S. Bultje
    if (is_keyframe &&
354
        (set_id != s->prev_set_id || timestamp != s->prev_timestamp ||
355
         stream_id != s->prev_stream_id)) {
356 985b05d3 Ronald S. Bultje
        flags |= PKT_FLAG_KEY;
357 239dec21 Ronald S. Bultje
        s->prev_set_id    = set_id;
358
        s->prev_timestamp = timestamp;
359 985b05d3 Ronald S. Bultje
    }
360 7960e18f Ronald S. Bultje
    s->prev_stream_id = stream_id;
361 4fce284c Ronald S. Bultje
    buf += rv;
362
    len -= rv;
363
364 7960e18f Ronald S. Bultje
     if (s->prev_stream_id >= s->n_streams) {
365
         s->prev_stream_id = -1;
366
         return -1;
367
     }
368
369 9b932b8a Ronald S. Bultje
    rv = s->parse_packet(s->dynamic_protocol_context,
370 7960e18f Ronald S. Bultje
                         s->streams[s->prev_stream_id],
371
                         pkt, &timestamp, buf, len, flags);
372 4fce284c Ronald S. Bultje
373
    return rv;
374
}
375
376 1256d16b Ronald S. Bultje
void
377 ab63fb03 Ronald S. Bultje
ff_rdt_subscribe_rule (char *cmd, int size,
378 1256d16b Ronald S. Bultje
                       int stream_nr, int rule_nr)
379
{
380
    av_strlcatf(cmd, size, "stream=%d;rule=%d,stream=%d;rule=%d",
381 ab63fb03 Ronald S. Bultje
                stream_nr, rule_nr * 2, stream_nr, rule_nr * 2 + 1);
382
}
383
384
void
385 accc248f Ronald S. Bultje
ff_rdt_subscribe_rule2 (RDTDemuxContext *s, char *cmd, int size,
386 ab63fb03 Ronald S. Bultje
                        int stream_nr, int rule_nr)
387
{
388 ed0aacc7 Ronald S. Bultje
    PayloadContext *rdt = s->dynamic_protocol_context;
389 1256d16b Ronald S. Bultje
390 4f602856 Ronald S. Bultje
    rdt_load_mdpr(rdt, s->streams[0], rule_nr * 2);
391 1256d16b Ronald S. Bultje
}
392
393 ff13ba92 Ronald S. Bultje
static unsigned char *
394
rdt_parse_b64buf (unsigned int *target_len, const char *p)
395
{
396
    unsigned char *target;
397
    int len = strlen(p);
398
    if (*p == '\"') {
399
        p++;
400
        len -= 2; /* skip embracing " at start/end */
401
    }
402
    *target_len = len * 3 / 4;
403
    target = av_mallocz(*target_len + FF_INPUT_BUFFER_PADDING_SIZE);
404
    av_base64_decode(target, p, *target_len);
405
    return target;
406
}
407
408
static int
409 7b2a0708 Ronald S. Bultje
rdt_parse_sdp_line (AVFormatContext *s, int st_index,
410
                    PayloadContext *rdt, const char *line)
411 ff13ba92 Ronald S. Bultje
{
412 7b2a0708 Ronald S. Bultje
    AVStream *stream = s->streams[st_index];
413 ff13ba92 Ronald S. Bultje
    const char *p = line;
414
415
    if (av_strstart(p, "OpaqueData:buffer;", &p)) {
416
        rdt->mlti_data = rdt_parse_b64buf(&rdt->mlti_data_size, p);
417
    } else if (av_strstart(p, "StartTime:integer;", &p))
418
        stream->first_dts = atoi(p);
419
420
    return 0;
421
}
422
423 ed0aacc7 Ronald S. Bultje
static PayloadContext *
424 ff13ba92 Ronald S. Bultje
rdt_new_extradata (void)
425
{
426 ed0aacc7 Ronald S. Bultje
    PayloadContext *rdt = av_mallocz(sizeof(PayloadContext));
427 ff13ba92 Ronald S. Bultje
428
    av_open_input_stream(&rdt->rmctx, NULL, "", &rdt_demuxer, NULL);
429
430
    return rdt;
431
}
432
433
static void
434 ed0aacc7 Ronald S. Bultje
rdt_free_extradata (PayloadContext *rdt)
435 ff13ba92 Ronald S. Bultje
{
436
    if (rdt->rmctx)
437
        av_close_input_stream(rdt->rmctx);
438
    av_freep(&rdt->mlti_data);
439
    av_free(rdt);
440
}
441
442
#define RDT_HANDLER(n, s, t) \
443
static RTPDynamicProtocolHandler ff_rdt_ ## n ## _handler = { \
444
    s, \
445
    t, \
446
    CODEC_ID_NONE, \
447
    rdt_parse_sdp_line, \
448
    rdt_new_extradata, \
449 3ff2a062 Ronald S. Bultje
    rdt_free_extradata, \
450
    rdt_parse_packet \
451 ff13ba92 Ronald S. Bultje
};
452
453
RDT_HANDLER(live_video, "x-pn-multirate-realvideo-live", CODEC_TYPE_VIDEO);
454
RDT_HANDLER(live_audio, "x-pn-multirate-realaudio-live", CODEC_TYPE_AUDIO);
455
RDT_HANDLER(video,      "x-pn-realvideo",                CODEC_TYPE_VIDEO);
456
RDT_HANDLER(audio,      "x-pn-realaudio",                CODEC_TYPE_AUDIO);
457
458
void av_register_rdt_dynamic_payload_handlers(void)
459
{
460
    ff_register_dynamic_payload_handler(&ff_rdt_video_handler);
461
    ff_register_dynamic_payload_handler(&ff_rdt_audio_handler);
462
    ff_register_dynamic_payload_handler(&ff_rdt_live_video_handler);
463
    ff_register_dynamic_payload_handler(&ff_rdt_live_audio_handler);
464
}