rtpfmt.c 57.4 KB
Newer Older
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1
/*****************************************************************************
2
 * rtpfmt.c: RTP payload formats
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
3
 *****************************************************************************
4
 * Copyright (C) 2003-2004 VLC authors and VideoLAN
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
5
6
7
8
 * Copyright © 2007 Rémi Denis-Courmont
 * $Id$
 *
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9
10
 * RFC 4175 support based on gstrtpvrawpay.c (LGPL 2) by:
 * Wim Taymans <wim.taymans@gmail.com>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
11
 *
12
13
14
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
15
16
17
18
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
21
 *
22
23
24
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
25
26
 *****************************************************************************/

27
28
29
30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <vlc_common.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
32
33
#include <vlc_sout.h>
#include <vlc_block.h>
34
#include <vlc_strings.h>
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
35
36

#include "rtp.h"
Pierre Ynard's avatar
Pierre Ynard committed
37
#include "../demux/xiph.h"
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
38

39
40
#include <assert.h>

41
42
43
static int rtp_packetize_mpa  (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_mpv  (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_ac3  (sout_stream_id_sys_t *, block_t *);
44
static int rtp_packetize_simple(sout_stream_id_sys_t *, block_t *);
45
static int rtp_packetize_split(sout_stream_id_sys_t *, block_t *);
46
static int rtp_packetize_pcm(sout_stream_id_sys_t *, block_t *);
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
static int rtp_packetize_swab (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_mp4a (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_mp4a_latm (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_h263 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_h264 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_amr  (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_spx  (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_t140 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_g726_16 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_g726_24 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_g726_32 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_g726_40 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_xiph (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_vp8 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_jpeg (sout_stream_id_sys_t *, block_t *);
62
63
static int rtp_packetize_r420 (sout_stream_id_sys_t *, block_t *);
static int rtp_packetize_rgb24 (sout_stream_id_sys_t *, block_t *);
64

Pierre Ynard's avatar
Pierre Ynard committed
65
#define XIPH_IDENT (0)
Pierre Ynard's avatar
Pierre Ynard committed
66

Pierre Ynard's avatar
Pierre Ynard committed
67
68
69
70
71
/* Helpers common to xiph codecs (vorbis and theora) */

static int rtp_xiph_pack_headers(size_t room, void *p_extra, size_t i_extra,
                                 uint8_t **p_buffer, size_t *i_buffer,
                                 uint8_t *theora_pixel_fmt)
Pierre Ynard's avatar
Pierre Ynard committed
72
73
74
75
{
    unsigned packet_size[XIPH_MAX_HEADER_COUNT];
    void *packet[XIPH_MAX_HEADER_COUNT];
    unsigned packet_count;
76
77
78
    if (xiph_SplitHeaders(packet_size, packet, &packet_count,
                                i_extra, p_extra))
        return VLC_EGENERIC;;
Pierre Ynard's avatar
Pierre Ynard committed
79
    if (packet_count < 3)
80
        return VLC_EGENERIC;;
Pierre Ynard's avatar
Pierre Ynard committed
81

Pierre Ynard's avatar
Pierre Ynard committed
82
83
84
    if (theora_pixel_fmt != NULL)
    {
        if (packet_size[0] < 42)
85
            return VLC_EGENERIC;
Pierre Ynard's avatar
Pierre Ynard committed
86
87
88
        *theora_pixel_fmt = (((uint8_t *)packet[0])[41] >> 3) & 0x03;
    }

Pierre Ynard's avatar
Pierre Ynard committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    unsigned length_size[2] = { 0, 0 };
    for (int i = 0; i < 2; i++)
    {
        unsigned size = packet_size[i];
        while (size > 0)
        {
            length_size[i]++;
            size >>= 7;
        }
    }

    *i_buffer = room + 1 + length_size[0] + length_size[1]
                + packet_size[0] + packet_size[1] + packet_size[2];
    *p_buffer = malloc(*i_buffer);
    if (*p_buffer == NULL)
104
        return VLC_ENOMEM;
Pierre Ynard's avatar
Pierre Ynard committed
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

    uint8_t *p = *p_buffer + room;
    /* Number of headers */
    *p++ = 2;

    for (int i = 0; i < 2; i++)
    {
        unsigned size = length_size[i];
        while (size > 0)
        {
            *p = (packet_size[i] >> (7 * (size - 1))) & 0x7f;
            if (--size > 0)
                *p |= 0x80;
            p++;
        }
    }
    for (int i = 0; i < 3; i++)
    {
        memcpy(p, packet[i], packet_size[i]);
        p += packet_size[i];
    }

127
    return VLC_SUCCESS;
Pierre Ynard's avatar
Pierre Ynard committed
128
129
}

Pierre Ynard's avatar
Pierre Ynard committed
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
static char *rtp_xiph_b64_oob_config(void *p_extra, size_t i_extra,
                                     uint8_t *theora_pixel_fmt)
{
    uint8_t *p_buffer;
    size_t i_buffer;
    if (rtp_xiph_pack_headers(9, p_extra, i_extra, &p_buffer, &i_buffer,
                              theora_pixel_fmt) != VLC_SUCCESS)
        return NULL;

    /* Number of packed headers */
    SetDWBE(p_buffer, 1);
    /* Ident */
    uint32_t ident = XIPH_IDENT;
    SetWBE(p_buffer + 4, ident >> 8);
    p_buffer[6] = ident & 0xff;
    /* Length field */
    SetWBE(p_buffer + 7, i_buffer);

    char *config = vlc_b64_encode_binary(p_buffer, i_buffer);
    free(p_buffer);
    return config;
}

153
static void sprintf_hexa( char *s, const uint8_t *p_data, int i_data )
154
155
156
157
158
159
160
161
162
163
164
165
{
    static const char hex[16] = "0123456789abcdef";

    for( int i = 0; i < i_data; i++ )
    {
        s[2*i+0] = hex[(p_data[i]>>4)&0xf];
        s[2*i+1] = hex[(p_data[i]   )&0xf];
    }
    s[2*i_data] = '\0';
}

/* TODO: make this into something more clever than a big switch? */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
166
167
int rtp_get_fmt( vlc_object_t *obj, const es_format_t *p_fmt, const char *mux,
                 rtp_format_t *rtp_fmt )
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
{
    assert( p_fmt != NULL || mux != NULL );

    /* Dynamic payload type. Payload types are scoped to the RTP
     * session, and we put each ES in its own session, so no risk of
     * conflict. */
    rtp_fmt->payload_type = 96;
    rtp_fmt->cat = mux != NULL ? VIDEO_ES : p_fmt->i_cat;
    if( rtp_fmt->cat == AUDIO_ES )
    {
        rtp_fmt->clock_rate = p_fmt->audio.i_rate;
        rtp_fmt->channels = p_fmt->audio.i_channels;
    }
    else
        rtp_fmt->clock_rate = 90000; /* most common case for video */
    /* Stream bitrate in kbps */
    rtp_fmt->bitrate = p_fmt != NULL ? p_fmt->i_bitrate/1000 : 0;
    rtp_fmt->fmtp = NULL;

    if( mux != NULL )
    {
        if( strncmp( mux, "ts", 2 ) == 0 )
        {
            rtp_fmt->payload_type = 33;
            rtp_fmt->ptname = "MP2T";
        }
        else
            rtp_fmt->ptname = "MP2P";
        return VLC_SUCCESS;
    }

    switch( p_fmt->i_codec )
    {
        case VLC_CODEC_MULAW:
            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 8000 )
                rtp_fmt->payload_type = 0;
            rtp_fmt->ptname = "PCMU";
205
            rtp_fmt->pf_packetize = rtp_packetize_pcm;
206
207
208
209
210
            break;
        case VLC_CODEC_ALAW:
            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 8000 )
                rtp_fmt->payload_type = 8;
            rtp_fmt->ptname = "PCMA";
211
            rtp_fmt->pf_packetize = rtp_packetize_pcm;
212
213
214
215
216
217
218
219
220
221
222
223
224
225
            break;
        case VLC_CODEC_S16B:
        case VLC_CODEC_S16L:
            if( p_fmt->audio.i_channels == 1 && p_fmt->audio.i_rate == 44100 )
            {
                rtp_fmt->payload_type = 11;
            }
            else if( p_fmt->audio.i_channels == 2 &&
                     p_fmt->audio.i_rate == 44100 )
            {
                rtp_fmt->payload_type = 10;
            }
            rtp_fmt->ptname = "L16";
            if( p_fmt->i_codec == VLC_CODEC_S16B )
226
                rtp_fmt->pf_packetize = rtp_packetize_pcm;
227
228
229
230
231
            else
                rtp_fmt->pf_packetize = rtp_packetize_swab;
            break;
        case VLC_CODEC_U8:
            rtp_fmt->ptname = "L8";
232
            rtp_fmt->pf_packetize = rtp_packetize_pcm;
233
            break;
234
235
        case VLC_CODEC_S24B:
            rtp_fmt->ptname = "L24";
236
            rtp_fmt->pf_packetize = rtp_packetize_pcm;
237
            break;
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
        case VLC_CODEC_MPGA:
            rtp_fmt->payload_type = 14;
            rtp_fmt->ptname = "MPA";
            rtp_fmt->clock_rate = 90000; /* not 44100 */
            rtp_fmt->pf_packetize = rtp_packetize_mpa;
            break;
        case VLC_CODEC_MPGV:
            rtp_fmt->payload_type = 32;
            rtp_fmt->ptname = "MPV";
            rtp_fmt->pf_packetize = rtp_packetize_mpv;
            break;
        case VLC_CODEC_ADPCM_G726:
            switch( p_fmt->i_bitrate / 1000 )
            {
            case 16:
                rtp_fmt->ptname = "G726-16";
                rtp_fmt->pf_packetize = rtp_packetize_g726_16;
                break;
            case 24:
                rtp_fmt->ptname = "G726-24";
                rtp_fmt->pf_packetize = rtp_packetize_g726_24;
                break;
            case 32:
                rtp_fmt->ptname = "G726-32";
                rtp_fmt->pf_packetize = rtp_packetize_g726_32;
                break;
            case 40:
                rtp_fmt->ptname = "G726-40";
                rtp_fmt->pf_packetize = rtp_packetize_g726_40;
                break;
            default:
                msg_Err( obj, "cannot add this stream (unsupported "
                         "G.726 bit rate: %u)", p_fmt->i_bitrate );
                return VLC_EGENERIC;
            }
            break;
        case VLC_CODEC_A52:
            rtp_fmt->ptname = "ac3";
            rtp_fmt->pf_packetize = rtp_packetize_ac3;
            break;
        case VLC_CODEC_H263:
            rtp_fmt->ptname = "H263-1998";
            rtp_fmt->pf_packetize = rtp_packetize_h263;
            break;
        case VLC_CODEC_H264:
            rtp_fmt->ptname = "H264";
            rtp_fmt->pf_packetize = rtp_packetize_h264;
            rtp_fmt->fmtp = NULL;

            if( p_fmt->i_extra > 0 )
            {
                uint8_t *p_buffer = p_fmt->p_extra;
                int     i_buffer = p_fmt->i_extra;
                char    *p_64_sps = NULL;
                char    *p_64_pps = NULL;
                char    hexa[6+1];

                while( i_buffer > 4 )
                {
                    int i_offset    = 0;
                    int i_size      = 0;

                    while( p_buffer[0] != 0 || p_buffer[1] != 0 ||
                           p_buffer[2] != 1 )
                    {
                        p_buffer++;
                        i_buffer--;
                        if( i_buffer == 0 ) break;
                    }

                    if( i_buffer < 4 || memcmp(p_buffer, "\x00\x00\x01", 3 ) )
                    {
                        msg_Dbg( obj, "No startcode found..");
                        break;
                    }
                    p_buffer += 3;
                    i_buffer -= 3;

                    const int i_nal_type = p_buffer[0]&0x1f;

                    msg_Dbg( obj, "we found a startcode for NAL with TYPE:%d", i_nal_type );

                    i_size = i_buffer;
                    for( i_offset = 0; i_offset+2 < i_buffer ; i_offset++)
                    {
                        if( !memcmp(p_buffer + i_offset, "\x00\x00\x01", 3 ) )
                        {
                            /* we found another startcode */
                            while( i_offset > 0 && 0 == p_buffer[ i_offset - 1 ] )
                                i_offset--;
                            i_size = i_offset;
                            break;
                        }
                    }

                    if( i_size == 0 )
                    {
                        msg_Dbg( obj, "No-info found in nal ");
                        continue;
                    }

                    if( i_nal_type == 7 )
                    {
                        free( p_64_sps );
                        p_64_sps = vlc_b64_encode_binary( p_buffer, i_size );
                        /* XXX: nothing ensures that i_size >= 4 ?? */
                        sprintf_hexa( hexa, &p_buffer[1], 3 );
                    }
                    else if( i_nal_type == 8 )
                    {
                        free( p_64_pps );
                        p_64_pps = vlc_b64_encode_binary( p_buffer, i_size );
                    }
                    i_buffer -= i_size;
                    p_buffer += i_size;
                }
                /* */
                if( p_64_sps && p_64_pps &&
                    ( asprintf( &rtp_fmt->fmtp,
                                "packetization-mode=1;profile-level-id=%s;"
                                "sprop-parameter-sets=%s,%s;", hexa, p_64_sps,
                                p_64_pps ) == -1 ) )
                    rtp_fmt->fmtp = NULL;
                free( p_64_sps );
                free( p_64_pps );
            }
            if( rtp_fmt->fmtp == NULL )
                rtp_fmt->fmtp = strdup( "packetization-mode=1" );
            break;

        case VLC_CODEC_MP4V:
        {
            rtp_fmt->ptname = "MP4V-ES";
            rtp_fmt->pf_packetize = rtp_packetize_split;
            if( p_fmt->i_extra > 0 )
            {
                char hexa[2*p_fmt->i_extra +1];
                sprintf_hexa( hexa, p_fmt->p_extra, p_fmt->i_extra );
                if( asprintf( &rtp_fmt->fmtp,
                              "profile-level-id=3; config=%s;", hexa ) == -1 )
                    rtp_fmt->fmtp = NULL;
            }
            break;
        }
        case VLC_CODEC_MP4A:
        {
            if( ! var_InheritBool( obj, "sout-rtp-mp4a-latm" ) )
            {
                char hexa[2*p_fmt->i_extra +1];

                rtp_fmt->ptname = "mpeg4-generic";
                rtp_fmt->pf_packetize = rtp_packetize_mp4a;
                sprintf_hexa( hexa, p_fmt->p_extra, p_fmt->i_extra );
                if( asprintf( &rtp_fmt->fmtp,
                              "streamtype=5; profile-level-id=15; "
                              "mode=AAC-hbr; config=%s; SizeLength=13; "
                              "IndexLength=3; IndexDeltaLength=3; Profile=1;",
                              hexa ) == -1 )
                    rtp_fmt->fmtp = NULL;
            }
            else
            {
                char hexa[13];
                int i;
                unsigned char config[6];
                unsigned int aacsrates[15] = {
                    96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
                    16000, 12000, 11025, 8000, 7350, 0, 0 };

                for( i = 0; i < 15; i++ )
                    if( p_fmt->audio.i_rate == aacsrates[i] )
                        break;

                config[0]=0x40;
                config[1]=0;
                config[2]=0x20|i;
                config[3]=p_fmt->audio.i_channels<<4;
                config[4]=0x3f;
                config[5]=0xc0;

                rtp_fmt->ptname = "MP4A-LATM";
                rtp_fmt->pf_packetize = rtp_packetize_mp4a_latm;
                sprintf_hexa( hexa, config, 6 );
                if( asprintf( &rtp_fmt->fmtp, "profile-level-id=15; "
                              "object=2; cpresent=0; config=%s", hexa ) == -1 )
                    rtp_fmt->fmtp = NULL;
            }
            break;
        }
        case VLC_CODEC_AMR_NB:
            rtp_fmt->ptname = "AMR";
            rtp_fmt->fmtp = strdup( "octet-align=1" );
            rtp_fmt->pf_packetize = rtp_packetize_amr;
            break;
        case VLC_CODEC_AMR_WB:
            rtp_fmt->ptname = "AMR-WB";
            rtp_fmt->fmtp = strdup( "octet-align=1" );
            rtp_fmt->pf_packetize = rtp_packetize_amr;
            break;
        case VLC_CODEC_SPEEX:
            rtp_fmt->ptname = "SPEEX";
            rtp_fmt->pf_packetize = rtp_packetize_spx;
            break;
Pierre Ynard's avatar
Pierre Ynard committed
441
442
        case VLC_CODEC_VORBIS:
            rtp_fmt->ptname = "vorbis";
Pierre Ynard's avatar
Pierre Ynard committed
443
            rtp_fmt->pf_packetize = rtp_packetize_xiph;
Pierre Ynard's avatar
Pierre Ynard committed
444
445
446
            if( p_fmt->i_extra > 0 )
            {
                rtp_fmt->fmtp = NULL;
Pierre Ynard's avatar
Pierre Ynard committed
447
448
                char *config = rtp_xiph_b64_oob_config(p_fmt->p_extra,
                                                       p_fmt->i_extra, NULL);
Pierre Ynard's avatar
Pierre Ynard committed
449
450
451
452
453
454
455
456
                if (config == NULL)
                    break;
                if( asprintf( &rtp_fmt->fmtp,
                              "configuration=%s;", config ) == -1 )
                    rtp_fmt->fmtp = NULL;
                free(config);
            }
            break;
Pierre Ynard's avatar
Pierre Ynard committed
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
        case VLC_CODEC_THEORA:
            rtp_fmt->ptname = "theora";
            rtp_fmt->pf_packetize = rtp_packetize_xiph;
            if( p_fmt->i_extra > 0 )
            {
                rtp_fmt->fmtp = NULL;
                uint8_t pixel_fmt, c1, c2;
                char *config = rtp_xiph_b64_oob_config(p_fmt->p_extra,
                                                       p_fmt->i_extra,
                                                       &pixel_fmt);
                if (config == NULL)
                    break;

                if (pixel_fmt == 1)
                {
                    /* reserved */
                    free(config);
                    break;
                }
                switch (pixel_fmt)
                {
                    case 0:
                        c1 = 2;
                        c2 = 0;
                        break;
                    case 2:
                        c1 = c2 = 2;
                        break;
                    case 3:
                        c1 = c2 = 4;
                        break;
                    default:
489
                        vlc_assert_unreachable();
Pierre Ynard's avatar
Pierre Ynard committed
490
491
492
493
                }

                if( asprintf( &rtp_fmt->fmtp,
                              "sampling=YCbCr-4:%d:%d; width=%d; height=%d; "
494
495
496
497
                              "delivery-method=inline; configuration=%s; "
                              "delivery-method=in_band;", c1, c2,
                              p_fmt->video.i_width, p_fmt->video.i_height,
                              config ) == -1 )
Pierre Ynard's avatar
Pierre Ynard committed
498
499
500
501
                    rtp_fmt->fmtp = NULL;
                free(config);
            }
            break;
502
503
504
505
506
        case VLC_CODEC_ITU_T140:
            rtp_fmt->ptname = "t140" ;
            rtp_fmt->clock_rate = 1000;
            rtp_fmt->pf_packetize = rtp_packetize_t140;
            break;
andrey_utkin's avatar
andrey_utkin committed
507
508
509
510
511
        case VLC_CODEC_GSM:
            rtp_fmt->payload_type = 3;
            rtp_fmt->ptname = "GSM";
            rtp_fmt->pf_packetize = rtp_packetize_split;
            break;
andrey_utkin's avatar
andrey_utkin committed
512
513
514
515
516
517
518
519
520
        case VLC_CODEC_OPUS:
            if (p_fmt->audio.i_channels > 2)
            {
                msg_Err( obj, "Multistream opus not supported in RTP"
                         " (having %d channels input)",
                         p_fmt->audio.i_channels );
                return VLC_EGENERIC;
            }
            rtp_fmt->ptname = "opus";
521
            rtp_fmt->pf_packetize = rtp_packetize_simple;
andrey_utkin's avatar
andrey_utkin committed
522
523
524
525
526
            rtp_fmt->clock_rate = 48000;
            rtp_fmt->channels = 2;
            if (p_fmt->audio.i_channels == 2)
                rtp_fmt->fmtp = strdup( "sprop-stereo=1" );
            break;
andrey_utkin's avatar
andrey_utkin committed
527
528
529
530
        case VLC_CODEC_VP8:
            rtp_fmt->ptname = "VP8";
            rtp_fmt->pf_packetize = rtp_packetize_vp8;
            break;
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
        case VLC_CODEC_R420:
            rtp_fmt->ptname = "RAW";
            rtp_fmt->pf_packetize = rtp_packetize_r420;
            if( asprintf( &rtp_fmt->fmtp,
                    "sampling=YCbCr-4:2:0; width=%d; height=%d; "
                    "depth=8; colorimetry=BT%s",
                    p_fmt->video.i_visible_width, p_fmt->video.i_visible_height,
                    p_fmt->video.i_visible_height > 576 ? "709-2" : "601-5") == -1 )
            {
                rtp_fmt->fmtp = NULL;
                return VLC_ENOMEM;
            }
            break;
        case VLC_CODEC_RGB24:
            rtp_fmt->ptname = "RAW";
            rtp_fmt->pf_packetize = rtp_packetize_rgb24;
            if( asprintf( &rtp_fmt->fmtp,
                    "sampling=RGB; width=%d; height=%d; "
                    "depth=8; colorimetry=SMPTE240M",
                    p_fmt->video.i_visible_width,
                    p_fmt->video.i_visible_height ) == -1 )
            {
                rtp_fmt->fmtp = NULL;
                return VLC_ENOMEM;
            }
            break;
andrey_utkin's avatar
andrey_utkin committed
557
558
559
560
561
562
        case VLC_CODEC_MJPG:
        case VLC_CODEC_JPEG:
            rtp_fmt->ptname = "JPEG";
            rtp_fmt->payload_type = 26;
            rtp_fmt->pf_packetize = rtp_packetize_jpeg;
            break;
563
564
565
566
567
568
569
570
571
572
573

        default:
            msg_Err( obj, "cannot add this stream (unsupported "
                     "codec: %4.4s)", (char*)&p_fmt->i_codec );
            return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}


574
static int
575
rtp_packetize_h264_nal( sout_stream_id_sys_t *id,
Rafaël Carré's avatar
Rafaël Carré committed
576
                        const uint8_t *p_data, int i_data, int64_t i_pts,
577
                        int64_t i_dts, bool b_last, int64_t i_length );
Rafaël Carré's avatar
Rafaël Carré committed
578

579
int rtp_packetize_xiph_config( sout_stream_id_sys_t *id, const char *fmtp,
580
581
582
583
584
585
586
587
588
589
590
591
                               int64_t i_pts )
{
    if (fmtp == NULL)
        return VLC_EGENERIC;

    /* extract base64 configuration from fmtp */
    char *start = strstr(fmtp, "configuration=");
    assert(start != NULL);
    start += sizeof("configuration=") - 1;
    char *end = strchr(start, ';');
    assert(end != NULL);
    size_t len = end - start;
592
593
594
595
596

    char *b64 = malloc(len + 1);
    if(!b64)
        return VLC_EGENERIC;

597
598
599
600
601
602
603
604
605
    memcpy(b64, start, len);
    b64[len] = '\0';

    int     i_max   = rtp_mtu (id) - 6; /* payload max in one packet */

    uint8_t *p_orig, *p_data;
    int i_data;

    i_data = vlc_b64_decode_binary(&p_orig, b64);
606
    free(b64);
607
608
609
    if (i_data <= 9)
    {
        free(p_orig);
610
        return VLC_EGENERIC;
611
    }
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
    p_data = p_orig + 9;
    i_data -= 9;

    int i_count = ( i_data + i_max - 1 ) / i_max;

    for( int i = 0; i < i_count; i++ )
    {
        int           i_payload = __MIN( i_max, i_data );
        block_t *out = block_Alloc( 18 + i_payload );

        unsigned fragtype, numpkts;
        if (i_count == 1)
        {
            fragtype = 0;
            numpkts = 1;
        }
        else
        {
            numpkts = 0;
            if (i == 0)
                fragtype = 1;
            else if (i == i_count - 1)
                fragtype = 3;
            else
                fragtype = 2;
        }
        /* Ident:24, Fragment type:2, Vorbis/Theora Data Type:2, # of pkts:4 */
        uint32_t header = ((XIPH_IDENT & 0xffffff) << 8) |
                          (fragtype << 6) | (1 << 4) | numpkts;

        /* rtp common header */
        rtp_packetize_common( id, out, 0, i_pts );

        SetDWBE( out->p_buffer + 12, header);
        SetWBE( out->p_buffer + 16, i_payload);
        memcpy( &out->p_buffer[18], p_data, i_payload );

        out->i_dts    = i_pts;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

    free(p_orig);

    return VLC_SUCCESS;
}

Pierre Ynard's avatar
Pierre Ynard committed
662
/* rfc5215 */
663
static int rtp_packetize_xiph( sout_stream_id_sys_t *id, block_t *in )
Pierre Ynard's avatar
Pierre Ynard committed
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
{
    int     i_max   = rtp_mtu (id) - 6; /* payload max in one packet */
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;

    for( int i = 0; i < i_count; i++ )
    {
        int           i_payload = __MIN( i_max, i_data );
        block_t *out = block_Alloc( 18 + i_payload );

        unsigned fragtype, numpkts;
        if (i_count == 1)
        {
            /* No fragmentation */
            fragtype = 0;
            numpkts = 1;
        }
        else
        {
            /* Fragmentation */
            numpkts = 0;
            if (i == 0)
                fragtype = 1;
            else if (i == i_count - 1)
                fragtype = 3;
            else
                fragtype = 2;
        }
Pierre Ynard's avatar
Pierre Ynard committed
694
695
        /* Ident:24, Fragment type:2, Vorbis/Theora Data Type:2, # of pkts:4 */
        uint32_t header = ((XIPH_IDENT & 0xffffff) << 8) |
Pierre Ynard's avatar
Pierre Ynard committed
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
                          (fragtype << 6) | (0 << 4) | numpkts;

        /* rtp common header */
        rtp_packetize_common( id, out, 0, in->i_pts);

        SetDWBE( out->p_buffer + 12, header);
        SetWBE( out->p_buffer + 16, i_payload);
        memcpy( &out->p_buffer[18], p_data, i_payload );

        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

714
    block_Release(in);
Pierre Ynard's avatar
Pierre Ynard committed
715
716
717
    return VLC_SUCCESS;
}

718
static int rtp_packetize_mpa( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
719
{
720
    int     i_max   = rtp_mtu (id) - 4; /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
721
722
723
724
725
726
727
728
729
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;
    int     i;

    for( i = 0; i < i_count; i++ )
    {
        int           i_payload = __MIN( i_max, i_data );
730
        block_t *out = block_Alloc( 16 + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
731
732
733
734

        /* rtp common header */
        rtp_packetize_common( id, out, (i == i_count - 1)?1:0, in->i_pts );
        /* mbz set to 0 */
735
        SetWBE( out->p_buffer + 12, 0 );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
736
        /* fragment offset in the current frame */
737
        SetWBE( out->p_buffer + 14, i * i_max );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
738
739
740
741
742
743
744
745
746
747
748
        memcpy( &out->p_buffer[16], p_data, i_payload );

        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

749
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
750
751
752
753
    return VLC_SUCCESS;
}

/* rfc2250 */
754
static int rtp_packetize_mpv( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
755
{
756
    int     i_max   = rtp_mtu (id) - 4; /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;
    int     i;
    int     b_sequence_start = 0;
    int     i_temporal_ref = 0;
    int     i_picture_coding_type = 0;
    int     i_fbv = 0, i_bfc = 0, i_ffv = 0, i_ffc = 0;
    int     b_start_slice = 0;

    /* preparse this packet to get some info */
    if( in->i_buffer > 4 )
    {
        uint8_t *p = p_data;
        int      i_rest = in->i_buffer;

        for( ;; )
        {
            while( i_rest > 4 &&
                   ( p[0] != 0x00 || p[1] != 0x00 || p[2] != 0x01 ) )
            {
                p++;
                i_rest--;
            }
            if( i_rest <= 4 )
            {
                break;
            }
            p += 3;
            i_rest -= 4;

            if( *p == 0xb3 )
            {
                /* sequence start code */
                b_sequence_start = 1;
            }
            else if( *p == 0x00 && i_rest >= 4 )
            {
                /* picture */
                i_temporal_ref = ( p[1] << 2) |((p[2]>>6)&0x03);
                i_picture_coding_type = (p[2] >> 3)&0x07;

                if( i_rest >= 4 && ( i_picture_coding_type == 2 ||
                                    i_picture_coding_type == 3 ) )
                {
                    i_ffv = (p[3] >> 2)&0x01;
                    i_ffc = ((p[3]&0x03) << 1)|((p[4]>>7)&0x01);
                    if( i_rest > 4 && i_picture_coding_type == 3 )
                    {
                        i_fbv = (p[4]>>6)&0x01;
                        i_bfc = (p[4]>>3)&0x07;
                    }
                }
            }
            else if( *p <= 0xaf )
            {
                b_start_slice = 1;
            }
        }
    }

    for( i = 0; i < i_count; i++ )
    {
        int           i_payload = __MIN( i_max, i_data );
822
        block_t *out = block_Alloc( 16 + i_payload );
823
        /* MBZ:5 T:1 TR:10 AN:1 N:1 S:1 B:1 E:1 P:3 FBV:1 BFC:3 FFV:1 FFC:3 */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
824
825
826
827
828
829
830
831
832
        uint32_t      h = ( i_temporal_ref << 16 )|
                          ( b_sequence_start << 13 )|
                          ( b_start_slice << 12 )|
                          ( i == i_count - 1 ? 1 << 11 : 0 )|
                          ( i_picture_coding_type << 8 )|
                          ( i_fbv << 7 )|( i_bfc << 4 )|( i_ffv << 3 )|i_ffc;

        /* rtp common header */
        rtp_packetize_common( id, out, (i == i_count - 1)?1:0,
833
                          in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
834

835
        SetDWBE( out->p_buffer + 12, h );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
836
837
838
839
840
841
842
843
844
845
846
847

        memcpy( &out->p_buffer[16], p_data, i_payload );

        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

848
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
849
850
851
    return VLC_SUCCESS;
}

852
static int rtp_packetize_ac3( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
853
{
854
    int     i_max   = rtp_mtu (id) - 2; /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
855
856
857
858
859
860
861
862
863
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;
    int     i;

    for( i = 0; i < i_count; i++ )
    {
        int           i_payload = __MIN( i_max, i_data );
864
        block_t *out = block_Alloc( 14 + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883

        /* rtp common header */
        rtp_packetize_common( id, out, (i == i_count - 1)?1:0, in->i_pts );
        /* unit count */
        out->p_buffer[12] = 1;
        /* unit header */
        out->p_buffer[13] = 0x00;
        /* data */
        memcpy( &out->p_buffer[14], p_data, i_payload );

        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

884
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
885
886
887
    return VLC_SUCCESS;
}

888
889
static int rtp_packetize_simple(sout_stream_id_sys_t *id, block_t *block)
{
890
891
    bool marker = (block->i_flags & BLOCK_FLAG_DISCONTINUITY) != 0;

892
893
894
895
    block = block_Realloc(block, 12, block->i_buffer);
    if (unlikely(block == NULL))
        return VLC_ENOMEM;

896
    rtp_packetize_common(id, block, marker, block->i_pts);
897
898
899
900
    rtp_packetize_send(id, block);
    return VLC_SUCCESS;
}

901
static int rtp_packetize_split( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
902
{
903
    int     i_max   = rtp_mtu (id); /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
904
905
906
907
908
909
910
911
912
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;
    int     i;

    for( i = 0; i < i_count; i++ )
    {
        int           i_payload = __MIN( i_max, i_data );
913
        block_t *out = block_Alloc( 12 + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
914
915
916

        /* rtp common header */
        rtp_packetize_common( id, out, (i == i_count - 1),
917
                      (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
918
919
920
921
922
923
924
925
926
927
928
        memcpy( &out->p_buffer[12], p_data, i_payload );

        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

929
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
930
931
932
    return VLC_SUCCESS;
}

933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
static int rtp_packetize_pcm(sout_stream_id_sys_t *id, block_t *in)
{
    unsigned max = rtp_mtu(id);

    while (in->i_buffer > max)
    {
        unsigned duration = (in->i_length * max) / in->i_buffer;
        bool marker = (in->i_flags & BLOCK_FLAG_DISCONTINUITY) != 0;

        block_t *out = block_Alloc(12 + max);
        if (unlikely(out == NULL))
        {
            block_Release(in);
            return VLC_ENOMEM;
        }

        rtp_packetize_common(id, out, marker, in->i_pts);
        memcpy(out->p_buffer + 12, in->p_buffer, max);
        rtp_packetize_send(id, out);

        in->p_buffer += max;
        in->i_buffer -= max;
        in->i_pts += duration;
        in->i_length -= duration;
        in->i_flags &= ~BLOCK_FLAG_DISCONTINUITY;
    }

    return rtp_packetize_simple(id, in); /* zero copy for the last frame */
}

963
/* split and convert from little endian to network byte order */
964
static int rtp_packetize_swab(sout_stream_id_sys_t *id, block_t *in)
965
{
966
    unsigned max = rtp_mtu(id);
967

968
    while (in->i_buffer > 0)
969
    {
970
971
972
        unsigned payload = (max < in->i_buffer) ? max : in->i_buffer;
        unsigned duration = (in->i_length * payload) / in->i_buffer;
        bool marker = (in->i_flags & BLOCK_FLAG_DISCONTINUITY) != 0;
973

974
975
976
977
978
979
        block_t *out = block_Alloc(12 + payload);
        if (unlikely(out == NULL))
        {
            block_Release(in);
            return VLC_ENOMEM;
        }
980

981
982
983
        rtp_packetize_common(id, out, marker, in->i_pts);
        swab(in->p_buffer, out->p_buffer + 12, payload);
        rtp_packetize_send(id, out);
984

985
986
987
988
989
        in->p_buffer += payload;
        in->i_buffer -= payload;
        in->i_pts += duration;
        in->i_length -= duration;
        in->i_flags &= ~BLOCK_FLAG_DISCONTINUITY;
990
991
    }

992
    block_Release(in);
993
994
995
    return VLC_SUCCESS;
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
996
/* rfc3016 */
997
static int rtp_packetize_mp4a_latm( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
998
{
999
    int     i_max   = rtp_mtu (id) - 2;              /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
    int     latmhdrsize = in->i_buffer / 0xff + 1;
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer, *p_header = NULL;
    int     i_data  = in->i_buffer;
    int     i;

    for( i = 0; i < i_count; i++ )
    {
        int     i_payload = __MIN( i_max, i_data );
        block_t *out;

        if( i != 0 )
            latmhdrsize = 0;
1014
        out = block_Alloc( 12 + latmhdrsize + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1015
1016
1017

        /* rtp common header */
        rtp_packetize_common( id, out, ((i == i_count - 1) ? 1 : 0),
1018
                      (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044

        if( i == 0 )
        {
            int tmp = in->i_buffer;

            p_header=out->p_buffer+12;
            while( tmp > 0xfe )
            {
                *p_header = 0xff;
                p_header++;
                tmp -= 0xff;
            }
            *p_header = tmp;
        }

        memcpy( &out->p_buffer[12+latmhdrsize], p_data, i_payload );

        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

1045
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1046
1047
1048
    return VLC_SUCCESS;
}

1049
static int rtp_packetize_mp4a( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1050
{
1051
    int     i_max   = rtp_mtu (id) - 4; /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1052
1053
1054
1055
1056
1057
1058
1059
1060
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;
    int     i;

    for( i = 0; i < i_count; i++ )
    {
        int           i_payload = __MIN( i_max, i_data );
1061
        block_t *out = block_Alloc( 16 + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1062
1063
1064

        /* rtp common header */
        rtp_packetize_common( id, out, ((i == i_count - 1)?1:0),
1065
                      (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1066
1067
1068
1069
1070
        /* AU headers */
        /* AU headers length (bits) */
        out->p_buffer[12] = 0;
        out->p_buffer[13] = 2*8;
        /* for each AU length 13 bits + idx 3bits, */
1071
        SetWBE( out->p_buffer + 14, (in->i_buffer << 3) | 0 );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083

        memcpy( &out->p_buffer[16], p_data, i_payload );

        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

1084
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1085
1086
1087
1088
1089
1090
1091
    return VLC_SUCCESS;
}


/* rfc2429 */
#define RTP_H263_HEADER_SIZE (2)  // plen = 0
#define RTP_H263_PAYLOAD_START (14)  // plen = 0
1092
static int rtp_packetize_h263( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1093
1094
1095
1096
{
    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;
    int     i;
1097
    int     i_max   = rtp_mtu (id) - RTP_H263_HEADER_SIZE; /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1098
1099
1100
1101
1102
1103
1104
1105
1106
    int     i_count;
    int     b_p_bit;
    int     b_v_bit = 0; // no pesky error resilience
    int     i_plen = 0; // normally plen=0 for PSC packet
    int     i_pebit = 0; // because plen=0
    uint16_t h;

    if( i_data < 2 )
    {
1107
        block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1108
1109
1110
1111
        return VLC_EGENERIC;
    }
    if( p_data[0] || p_data[1] )
    {
1112
        block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
        return VLC_EGENERIC;
    }
    /* remove 2 leading 0 bytes */
    p_data += 2;
    i_data -= 2;
    i_count = ( i_data + i_max - 1 ) / i_max;

    for( i = 0; i < i_count; i++ )
    {
        int      i_payload = __MIN( i_max, i_data );
1123
        block_t *out = block_Alloc( RTP_H263_PAYLOAD_START + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1124
1125
1126
1127
1128
1129
1130
1131
1132
        b_p_bit = (i == 0) ? 1 : 0;
        h = ( b_p_bit << 10 )|
            ( b_v_bit << 9  )|
            ( i_plen  << 3  )|
              i_pebit;

        /* rtp common header */
        //b_m_bit = 1; // always contains end of frame
        rtp_packetize_common( id, out, (i == i_count - 1)?1:0,
1133
                          in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1134
1135

        /* h263 header */
1136
        SetWBE( out->p_buffer + 12, h );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
        memcpy( &out->p_buffer[RTP_H263_PAYLOAD_START], p_data, i_payload );

        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

1148
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1149
1150
1151
1152
    return VLC_SUCCESS;
}

/* rfc3984 */
1153
static int
1154
rtp_packetize_h264_nal( sout_stream_id_sys_t *id,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1155
                        const uint8_t *p_data, int i_data, int64_t i_pts,
1156
                        int64_t i_dts, bool b_last, int64_t i_length )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1157
{
1158
    const int i_max = rtp_mtu (id); /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
    int i_nal_hdr;
    int i_nal_type;

    if( i_data < 5 )
        return VLC_SUCCESS;

    i_nal_hdr = p_data[3];
    i_nal_type = i_nal_hdr&0x1f;

    /* Skip start code */
    p_data += 3;
    i_data -= 3;

    /* */
    if( i_data <= i_max )
    {
        /* Single NAL unit packet */
1176
        block_t *out = block_Alloc( 12 + i_data );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
        out->i_dts    = i_dts;
        out->i_length = i_length;

        /* */
        rtp_packetize_common( id, out, b_last, i_pts );

        memcpy( &out->p_buffer[12], p_data, i_data );

        rtp_packetize_send( id, out );
    }
    else
    {
        /* FU-A Fragmentation Unit without interleaving */
        const int i_count = ( i_data-1 + i_max-2 - 1 ) / (i_max-2);
        int i;

        p_data++;
        i_data--;

        for( i = 0; i < i_count; i++ )
        {
            const int i_payload = __MIN( i_data, i_max-2 );
1199
            block_t *out = block_Alloc( 12 + 2 + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1200
1201
1202
1203
            out->i_dts    = i_dts + i * i_length / i_count;
            out->i_length = i_length / i_count;

            /* */
1204
1205
            rtp_packetize_common( id, out, (b_last && i_payload == i_data),
                                    i_pts );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
            /* FU indicator */
            out->p_buffer[12] = 0x00 | (i_nal_hdr & 0x60) | 28;
            /* FU header */
            out->p_buffer[13] = ( i == 0 ? 0x80 : 0x00 ) | ( (i == i_count-1) ? 0x40 : 0x00 )  | i_nal_type;
            memcpy( &out->p_buffer[14], p_data, i_payload );

            rtp_packetize_send( id, out );

            i_data -= i_payload;
            p_data += i_payload;
        }
    }
    return VLC_SUCCESS;
}

1221
static int rtp_packetize_h264( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
{
    const uint8_t *p_buffer = in->p_buffer;
    int i_buffer = in->i_buffer;

    while( i_buffer > 4 && ( p_buffer[0] != 0 || p_buffer[1] != 0 || p_buffer[2] != 1 ) )
    {
        i_buffer--;
        p_buffer++;
    }

    /* Split nal units */
    while( i_buffer > 4 )
    {
        int i_offset;
        int i_size = i_buffer;
        int i_skip = i_buffer;

        /* search nal end */
        for( i_offset = 4; i_offset+2 < i_buffer ; i_offset++)
        {
            if( p_buffer[i_offset] == 0 && p_buffer[i_offset+1] == 0 && p_buffer[i_offset+2] == 1 )
            {
                /* we found another startcode */
                i_size = i_offset - ( p_buffer[i_offset-1] == 0 ? 1 : 0);
                i_skip = i_offset;
                break;
            }
        }
        /* TODO add STAP-A to remove a lot of overhead with small slice/sei/... */
1251
        rtp_packetize_h264_nal( id, p_buffer, i_size,
1252
1253
                (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts), in->i_dts,
                (i_size >= i_buffer), in->i_length * i_size / in->i_buffer );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1254
1255
1256
1257

        i_buffer -= i_skip;
        p_buffer += i_skip;
    }
1258
1259

    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1260
1261
1262
    return VLC_SUCCESS;
}

1263
static int rtp_packetize_amr( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1264
{
1265
    int     i_max   = rtp_mtu (id) - 2; /* payload max in one packet */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;
    int     i;

    /* Only supports octet-aligned mode */
    for( i = 0; i < i_count; i++ )
    {
        int           i_payload = __MIN( i_max, i_data );
1276
        block_t *out = block_Alloc( 14 + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1277
1278
1279

        /* rtp common header */
        rtp_packetize_common( id, out, ((i == i_count - 1)?1:0),
1280
                      (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1281
1282
1283
1284
1285
1286
1287
        /* Payload header */
        out->p_buffer[12] = 0xF0; /* CMR */
        out->p_buffer[13] = p_data[0]&0x7C; /* ToC */ /* FIXME: frame type */

        /* FIXME: are we fed multiple frames ? */
        memcpy( &out->p_buffer[14], p_data+1, i_payload-1 );

1288
        out->i_buffer--; /* FIXME? */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1289
1290
1291
1292
1293
1294
1295
1296
1297
        out->i_dts    = in->i_dts + i * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

1298
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1299
1300
1301
    return VLC_SUCCESS;
}

1302
static int rtp_packetize_t140( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1303
{
1304
    const size_t   i_max  = rtp_mtu (id);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
    const uint8_t *p_data = in->p_buffer;
    size_t         i_data = in->i_buffer;

    for( unsigned i_packet = 0; i_data > 0; i_packet++ )
    {
        size_t i_payload = i_data;

        /* Make sure we stop on an UTF-8 character boundary
         * (assuming the input is valid UTF-8) */
        if( i_data > i_max )
        {
            i_payload = i_max;

            while( ( p_data[i_payload] & 0xC0 ) == 0x80 )
            {
                if( i_payload == 0 )
1321
1322
                 {
                    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1323
                    return VLC_SUCCESS; /* fishy input! */
1324
                }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1325
1326
1327
1328
                i_payload--;
            }
        }

1329
        block_t *out = block_Alloc( 12 + i_payload );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1330
        if( out == NULL )
1331
1332
        {
            block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1333
            return VLC_SUCCESS;
1334
        }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1335
1336
1337
1338

        rtp_packetize_common( id, out, 0, in->i_pts + i_packet );
        memcpy( out->p_buffer + 12, p_data, i_payload );

Pierre Ynard's avatar
Pierre Ynard committed
1339
        out->i_dts    = in->i_pts;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1340
1341
1342
1343
1344
1345
1346
1347
        out->i_length = 0;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }

1348
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1349
1350
1351
1352
    return VLC_SUCCESS;
}


1353
static int rtp_packetize_spx( sout_stream_id_sys_t *id, block_t *in )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1354
1355
1356
1357
1358
1359
1360
{
    uint8_t *p_buffer = in->p_buffer;
    int i_data_size, i_payload_size, i_payload_padding;
    i_data_size = i_payload_size = in->i_buffer;
    i_payload_padding = 0;
    block_t *p_out;

1361
    if ( in->i_buffer > rtp_mtu (id) )
1362
1363
    {
        block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1364
        return VLC_SUCCESS;
1365
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1366
1367

    /*
1368
      RFC for Speex in RTP says that each packet must end on an octet
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1369
      boundary. So, we check to see if the number of bytes % 4 is zero.
1370
      If not, we have to add some padding.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1371

1372
      This MAY be overkill since packetization is handled elsewhere and
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
      appears to ensure the octet boundary. However, better safe than
      sorry.
    */
    if ( i_payload_size % 4 )
    {
        i_payload_padding = 4 - ( i_payload_size % 4 );
        i_payload_size += i_payload_padding;
    }

    /*
1383
1384
      Allocate a new RTP p_output block of the appropriate size.
      Allow for 12 extra bytes of RTP header.
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1385
    */
1386
    p_out = block_Alloc( 12 + i_payload_size );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401

    if ( i_payload_padding )
    {
    /*
      The padding is required to be a zero followed by all 1s.
    */
        char c_first_pad, c_remaining_pad;
        c_first_pad = 0x7F;
        c_remaining_pad = 0xFF;

        /*
          Allow for 12 bytes before the i_data_size because
          of the expected RTP header added during
          rtp_packetize_common.
        */
1402
        p_out->p_buffer[12 + i_data_size] = c_first_pad;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1403
1404
1405
        switch (i_payload_padding)
        {
          case 2:
1406
            p_out->p_buffer[12 + i_data_size + 1] = c_remaining_pad;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1407
1408
            break;
          case 3:
1409
1410
            p_out->p_buffer[12 + i_data_size + 1] = c_remaining_pad;
            p_out->p_buffer[12 + i_data_size + 2] = c_remaining_pad;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1411
1412
1413
1414
1415
            break;
        }
    }

    /* Add the RTP header to our p_output buffer. */
1416
1417
    rtp_packetize_common( id, p_out, 0,
                        (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1418
1419
1420
1421
1422
    /* Copy the Speex payload to the p_output buffer */
    memcpy( &p_out->p_buffer[12], p_buffer, i_data_size );

    p_out->i_dts = in->i_dts;
    p_out->i_length = in->i_length;
1423
    block_Release(in);
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
1424
1425
1426
1427
1428

    /* Queue the buffer for actual transmission. */
    rtp_packetize_send( id, p_out );
    return VLC_SUCCESS;
}
Rafaël Carré's avatar
Rafaël Carré committed
1429

1430
static int rtp_packetize_g726( sout_stream_id_sys_t *id, block_t *in, int i_pad )
Rafaël Carré's avatar
Rafaël Carré committed
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
{
    int     i_max   = (rtp_mtu( id )- 12 + i_pad - 1) & ~i_pad;
    int     i_count = ( in->i_buffer + i_max - 1 ) / i_max;

    uint8_t *p_data = in->p_buffer;
    int     i_data  = in->i_buffer;
    int     i_packet = 0;

    while( i_data > 0 )
    {
        int           i_payload = __MIN( i_max, i_data );
1442
        block_t *out = block_Alloc( 12 + i_payload );
Rafaël Carré's avatar
Rafaël Carré committed
1443
1444
1445

        /* rtp common header */
        rtp_packetize_common( id, out, 0,
1446
                      (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) );
Rafaël Carré's avatar
Rafaël Carré committed
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457

        memcpy( &out->p_buffer[12], p_data, i_payload );

        out->i_dts    = in->i_dts + i_packet++ * in->i_length / i_count;
        out->i_length = in->i_length / i_count;

        rtp_packetize_send( id, out );

        p_data += i_payload;
        i_data -= i_payload;
    }
1458
1459

    block_Release(in);
Rafaël Carré's avatar
Rafaël Carré committed
1460
1461
1462
    return VLC_SUCCESS;
}

1463
static int rtp_packetize_g726_16( sout_stream_id_sys_t *id, block_t *in )
Rafaël Carré's avatar
Rafaël Carré committed
1464
{
1465
    return rtp_packetize_g726( id, in, 4 );
Rafaël Carré's avatar
Rafaël Carré committed
1466
1467
}

1468
static int rtp_packetize_g726_24( sout_stream_id_sys_t *id, block_t *in )
Rafaël Carré's avatar
Rafaël Carré committed
1469
{
1470
    return rtp_packetize_g726( id, in, 8 );
Rafaël Carré's avatar
Rafaël Carré committed
1471
1472
}

1473
static int rtp_packetize_g726_32( sout_stream_id_sys_t *id, block_t *in )
Rafaël Carré's avatar
Rafaël Carré committed
1474
{
1475
    return rtp_packetize_g726( id, in, 2 );
Rafaël Carré's avatar
Rafaël Carré committed
1476
1477
}

1478
static int rtp_packetize_g726_40( sout_stream_id_sys_t *id, block_t *in )
Rafaël Carré's avatar
Rafaël Carré committed
1479
{
1480
    return rtp_packetize_g726( id, in, 8 );