/***************************************************************************** * rtpfmt.c: RTP payload formats ***************************************************************************** * Copyright (C) 2003-2004 VLC authors and VideoLAN * Copyright © 2007 Rémi Denis-Courmont * $Id$ * * Authors: Laurent Aimar * RFC 4175 support based on gstrtpvrawpay.c (LGPL 2) by: * Wim Taymans * * 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 * (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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * 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. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "rtp.h" #include "../demux/xiph.h" #include "../packetizer/hxxx_nal.h" #include 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 *); static int rtp_packetize_eac3(sout_stream_id_sys_t *, block_t *); static int rtp_packetize_simple(sout_stream_id_sys_t *, block_t *); static int rtp_packetize_split(sout_stream_id_sys_t *, block_t *); static int rtp_packetize_pcm(sout_stream_id_sys_t *, block_t *); 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_h265 (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 *); static int rtp_packetize_r420 (sout_stream_id_sys_t *, block_t *); static int rtp_packetize_rgb24 (sout_stream_id_sys_t *, block_t *); #define XIPH_IDENT (0) /* 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) { unsigned packet_size[XIPH_MAX_HEADER_COUNT]; const void *packet[XIPH_MAX_HEADER_COUNT]; unsigned packet_count; if (xiph_SplitHeaders(packet_size, packet, &packet_count, i_extra, p_extra)) return VLC_EGENERIC;; if (packet_count < 3) return VLC_EGENERIC;; if (theora_pixel_fmt != NULL) { if (packet_size[0] < 42) return VLC_EGENERIC; *theora_pixel_fmt = (((const uint8_t *)packet[0])[41] >> 3) & 0x03; } 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) return VLC_ENOMEM; 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]; } return VLC_SUCCESS; } 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; } static void sprintf_hexa( char *s, const uint8_t *p_data, int i_data ) { 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? */ int rtp_get_fmt( vlc_object_t *obj, const es_format_t *p_fmt, const char *mux, rtp_format_t *rtp_fmt ) { 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"; rtp_fmt->pf_packetize = rtp_packetize_pcm; 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"; rtp_fmt->pf_packetize = rtp_packetize_pcm; 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 ) rtp_fmt->pf_packetize = rtp_packetize_pcm; else rtp_fmt->pf_packetize = rtp_packetize_swab; break; case VLC_CODEC_U8: rtp_fmt->ptname = "L8"; rtp_fmt->pf_packetize = rtp_packetize_pcm; break; case VLC_CODEC_S24B: rtp_fmt->ptname = "L24"; rtp_fmt->pf_packetize = rtp_packetize_pcm; break; 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_EAC3: rtp_fmt->ptname = "eac3"; rtp_fmt->pf_packetize = rtp_packetize_eac3; 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 ) { char *p_64_sps = NULL; char *p_64_pps = NULL; char hexa[6+1]; hxxx_iterator_ctx_t it; hxxx_iterator_init( &it, p_fmt->p_extra, p_fmt->i_extra, 0 ); const uint8_t *p_nal; size_t i_nal; while( hxxx_annexb_iterate_next( &it, &p_nal, &i_nal ) ) { if( i_nal < 2 ) { msg_Dbg( obj, "No-info found in nal "); continue; } const int i_nal_type = p_nal[0]&0x1f; msg_Dbg( obj, "we found a startcode for NAL with TYPE:%d", i_nal_type ); if( i_nal_type == 7 && i_nal >= 4 ) { free( p_64_sps ); p_64_sps = vlc_b64_encode_binary( p_nal, i_nal ); sprintf_hexa( hexa, &p_nal[1], 3 ); } else if( i_nal_type == 8 ) { free( p_64_pps ); p_64_pps = vlc_b64_encode_binary( p_nal, i_nal ); } } /* */ 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_HEVC: { rtp_fmt->ptname = "H265"; rtp_fmt->pf_packetize = rtp_packetize_h265; rtp_fmt->fmtp = NULL; int i_profile = p_fmt->i_profile; int i_level = p_fmt->i_level; int i_tiers = -1; int i_space = -1; struct nalset_e { const uint8_t i_nal; const uint8_t i_extend; const char *psz_name; char *psz_64; } nalsets[4] = { { 32, 0, "vps", NULL }, { 33, 0, "sps", NULL }, { 34, 0, "pps", NULL }, { 39, 1, "sei", NULL }, }; if( p_fmt->i_extra > 0 ) { hxxx_iterator_ctx_t it; for(int i=0; i<4; i++) { struct nalset_e *set = &nalsets[i]; hxxx_iterator_init( &it, p_fmt->p_extra, p_fmt->i_extra, 0 ); const uint8_t *p_nal; size_t i_nal; while( hxxx_annexb_iterate_next( &it, &p_nal, &i_nal ) ) { const uint8_t i_nal_type = (p_nal[0] & 0x7E) >> 1; if( i_nal_type < set->i_nal || i_nal_type > set->i_nal + set->i_extend ) continue; msg_Dbg( obj, "we found a startcode for NAL with TYPE:%" PRIu8, i_nal_type ); char *psz_temp = vlc_b64_encode_binary( p_nal, i_nal ); if( psz_temp ) { if( set->psz_64 == NULL ) { set->psz_64 = psz_temp; } else { char *psz_merged; if( asprintf( &psz_merged, "%s,%s", set->psz_64, psz_temp ) != -1 ) { free( set->psz_64 ); set->psz_64 = psz_merged; } free( psz_temp ); } } if( i_nal_type == 33 && i_nal > 12 ) { if( i_profile < 0 ) i_profile = p_nal[1] & 0x1F; if( i_space < 0 ) i_space = p_nal[1] >> 6; if( i_tiers < 0 ) i_tiers = !!(p_nal[1] & 0x20); if( i_level < 0 ) i_level = p_nal[12]; } } } } rtp_fmt->fmtp = strdup( "tx-mode=SRST;" ); if( rtp_fmt->fmtp ) { char *psz_fmtp; if( i_profile >= 0 && asprintf( &psz_fmtp, "%sprofile-id=%d;", rtp_fmt->fmtp, i_profile ) != -1 ) { free( rtp_fmt->fmtp ); rtp_fmt->fmtp = psz_fmtp; } if( i_level >= 0 && asprintf( &psz_fmtp, "%slevel-id=%d;", rtp_fmt->fmtp, i_level ) != -1 ) { free( rtp_fmt->fmtp ); rtp_fmt->fmtp = psz_fmtp; } if( i_tiers >= 0 && asprintf( &psz_fmtp, "%stier-flag=%d;", rtp_fmt->fmtp, i_tiers ) != -1 ) { free( rtp_fmt->fmtp ); rtp_fmt->fmtp = psz_fmtp; } if( i_space >= 0 && asprintf( &psz_fmtp, "%sprofile-space=%d;", rtp_fmt->fmtp, i_space ) != -1 ) { free( rtp_fmt->fmtp ); rtp_fmt->fmtp = psz_fmtp; } for(int i=0; i<4; i++) { struct nalset_e *set = &nalsets[i]; if( set->psz_64 && asprintf( &psz_fmtp, "%ssprop-%s=%s;", rtp_fmt->fmtp, set->psz_name, set->psz_64 ) != -1 ) { free( rtp_fmt->fmtp ); rtp_fmt->fmtp = psz_fmtp; } } } for(int i=0; i<4; i++) free( nalsets[i].psz_64 ); 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; case VLC_CODEC_VORBIS: rtp_fmt->ptname = "vorbis"; rtp_fmt->pf_packetize = rtp_packetize_xiph; if( p_fmt->i_extra > 0 ) { rtp_fmt->fmtp = NULL; char *config = rtp_xiph_b64_oob_config(p_fmt->p_extra, p_fmt->i_extra, NULL); if (config == NULL) break; if( asprintf( &rtp_fmt->fmtp, "configuration=%s;", config ) == -1 ) rtp_fmt->fmtp = NULL; free(config); } break; 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: vlc_assert_unreachable(); } if( asprintf( &rtp_fmt->fmtp, "sampling=YCbCr-4:%d:%d; width=%d; height=%d; " "delivery-method=inline; configuration=%s; " "delivery-method=in_band;", c1, c2, p_fmt->video.i_width, p_fmt->video.i_height, config ) == -1 ) rtp_fmt->fmtp = NULL; free(config); } break; case VLC_CODEC_ITU_T140: rtp_fmt->ptname = "t140" ; rtp_fmt->clock_rate = 1000; rtp_fmt->pf_packetize = rtp_packetize_t140; break; case VLC_CODEC_GSM: rtp_fmt->payload_type = 3; rtp_fmt->ptname = "GSM"; rtp_fmt->pf_packetize = rtp_packetize_split; break; 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"; rtp_fmt->pf_packetize = rtp_packetize_simple; rtp_fmt->clock_rate = 48000; rtp_fmt->channels = 2; if (p_fmt->audio.i_channels == 2) rtp_fmt->fmtp = strdup( "sprop-stereo=1" ); break; case VLC_CODEC_VP8: rtp_fmt->ptname = "VP8"; rtp_fmt->pf_packetize = rtp_packetize_vp8; break; 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; 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; default: msg_Err( obj, "cannot add this stream (unsupported " "codec: %4.4s)", (char*)&p_fmt->i_codec ); return VLC_EGENERIC; } return VLC_SUCCESS; } static int rtp_packetize_h264_nal( sout_stream_id_sys_t *id, const uint8_t *p_data, int i_data, int64_t i_pts, int64_t i_dts, bool b_last, int64_t i_length ); int rtp_packetize_xiph_config( sout_stream_id_sys_t *id, const char *fmtp, 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; char *b64 = malloc(len + 1); if(!b64) return VLC_EGENERIC; 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); free(b64); if (i_data <= 9) { free(p_orig); return VLC_EGENERIC; } 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; } /* rfc5215 */ static int rtp_packetize_xiph( sout_stream_id_sys_t *id, block_t *in ) { 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; } /* Ident:24, Fragment type:2, Vorbis/Theora Data Type:2, # of pkts:4 */ uint32_t header = ((XIPH_IDENT & 0xffffff) << 8) | (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; } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_mpa( sout_stream_id_sys_t *id, block_t *in ) { int i_max = rtp_mtu (id) - 4; /* 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; int i; for( i = 0; i < i_count; i++ ) { int i_payload = __MIN( i_max, i_data ); block_t *out = block_Alloc( 16 + i_payload ); /* rtp common header */ rtp_packetize_common( id, out, (i == i_count - 1)?1:0, in->i_pts ); /* mbz set to 0 */ SetWBE( out->p_buffer + 12, 0 ); /* fragment offset in the current frame */ SetWBE( out->p_buffer + 14, i * i_max ); 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; } block_Release(in); return VLC_SUCCESS; } /* rfc2250 */ static int rtp_packetize_mpv( sout_stream_id_sys_t *id, block_t *in ) { int i_max = rtp_mtu (id) - 4; /* 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; 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 */ hxxx_iterator_ctx_t it; hxxx_iterator_init( &it, in->p_buffer, in->i_buffer, 0 ); const uint8_t *p_seq; size_t i_seq; while( hxxx_annexb_iterate_next( &it, &p_seq, &i_seq ) ) { const uint8_t *p = p_seq; if( *p == 0xb3 ) { /* sequence start code */ b_sequence_start = 1; } else if( *p == 0x00 && i_seq >= 5 ) { /* picture */ i_temporal_ref = ( p[1] << 2) |((p[2]>>6)&0x03); i_picture_coding_type = (p[2] >> 3)&0x07; if( 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_seq > 5 && 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 ); block_t *out = block_Alloc( 16 + i_payload ); /* 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 */ 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, in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts ); SetDWBE( out->p_buffer + 12, h ); 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; } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_ac3( sout_stream_id_sys_t *id, block_t *in ) { int i_max = rtp_mtu (id) - 2; /* 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; int i; uint8_t hdr[2]; if (i_count <= 1) hdr[0] = 0; /* One complete frame */ else if (i_data * 5 <= i_max * 8) hdr[0] = 1; /* Initial fragment with at least 5/8th of frame */ else hdr[0] = 2; /* Initial fragment with less than 5/8th of frame */ hdr[1] = i_count; for( i = 0; i < i_count; i++ ) { int i_payload = __MIN( i_max, i_data ); block_t *out = block_Alloc( 14 + i_payload ); /* rtp common header */ rtp_packetize_common( id, out, (i == i_count - 1)?1:0, in->i_pts ); /* data */ memcpy( &out->p_buffer[12], hdr, 2 ); 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; hdr[0] = 3; /* Fragment other than initial fragment */ } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_eac3(sout_stream_id_sys_t *id, block_t *in) { size_t mtu = rtp_mtu(id) - 2; uint_fast8_t frag_count = (in->i_buffer + mtu - 1) / mtu - 1; uint8_t frame_type = frag_count > 0; for (unsigned int i = 0; i < frag_count; i++) { bool last = i == (frag_count - 1); size_t len = last ? in->i_buffer : mtu; block_t *out = block_Alloc(14 + len); rtp_packetize_common(id, out, false, in->i_pts); out->p_buffer[12] = frame_type; out->p_buffer[13] = frag_count; memcpy(&out->p_buffer[14], in->p_buffer, len); out->i_dts = in->i_dts + i * in->i_length / frag_count; rtp_packetize_send(id, out); in->p_buffer += len; in->i_buffer -= len; } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_simple(sout_stream_id_sys_t *id, block_t *block) { bool marker = (block->i_flags & BLOCK_FLAG_DISCONTINUITY) != 0; block = block_Realloc(block, 12, block->i_buffer); if (unlikely(block == NULL)) return VLC_ENOMEM; rtp_packetize_common(id, block, marker, block->i_pts); rtp_packetize_send(id, block); return VLC_SUCCESS; } static int rtp_packetize_split( sout_stream_id_sys_t *id, block_t *in ) { int i_max = rtp_mtu (id); /* 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; int i; for( i = 0; i < i_count; i++ ) { int i_payload = __MIN( i_max, i_data ); block_t *out = block_Alloc( 12 + i_payload ); /* rtp common header */ rtp_packetize_common( id, out, (i == i_count - 1), (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); 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; } block_Release(in); return VLC_SUCCESS; } 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 */ } /* split and convert from little endian to network byte order */ static int rtp_packetize_swab(sout_stream_id_sys_t *id, block_t *in) { unsigned max = rtp_mtu(id); while (in->i_buffer > 0) { 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; block_t *out = block_Alloc(12 + payload); if (unlikely(out == NULL)) { block_Release(in); return VLC_ENOMEM; } rtp_packetize_common(id, out, marker, in->i_pts); swab(in->p_buffer, out->p_buffer + 12, payload); rtp_packetize_send(id, out); in->p_buffer += payload; in->i_buffer -= payload; in->i_pts += duration; in->i_length -= duration; in->i_flags &= ~BLOCK_FLAG_DISCONTINUITY; } block_Release(in); return VLC_SUCCESS; } /* rfc3016 */ static int rtp_packetize_mp4a_latm( sout_stream_id_sys_t *id, block_t *in ) { int i_max = rtp_mtu (id) - 2; /* payload max in one packet */ 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; out = block_Alloc( 12 + latmhdrsize + i_payload ); /* rtp common header */ rtp_packetize_common( id, out, ((i == i_count - 1) ? 1 : 0), (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); 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; } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_mp4a( sout_stream_id_sys_t *id, block_t *in ) { int i_max = rtp_mtu (id) - 4; /* 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; int i; for( i = 0; i < i_count; i++ ) { int i_payload = __MIN( i_max, i_data ); block_t *out = block_Alloc( 16 + i_payload ); /* rtp common header */ rtp_packetize_common( id, out, ((i == i_count - 1)?1:0), (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); /* 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, */ SetWBE( out->p_buffer + 14, (in->i_buffer << 3) | 0 ); 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; } block_Release(in); return VLC_SUCCESS; } /* rfc2429 */ #define RTP_H263_HEADER_SIZE (2) // plen = 0 #define RTP_H263_PAYLOAD_START (14) // plen = 0 static int rtp_packetize_h263( sout_stream_id_sys_t *id, block_t *in ) { uint8_t *p_data = in->p_buffer; int i_data = in->i_buffer; int i; int i_max = rtp_mtu (id) - RTP_H263_HEADER_SIZE; /* payload max in one packet */ 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 ) { block_Release(in); return VLC_EGENERIC; } if( p_data[0] || p_data[1] ) { block_Release(in); 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 ); block_t *out = block_Alloc( RTP_H263_PAYLOAD_START + i_payload ); 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, in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts ); /* h263 header */ SetWBE( out->p_buffer + 12, h ); 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; } block_Release(in); return VLC_SUCCESS; } /* rfc3984 */ static int rtp_packetize_h264_nal( sout_stream_id_sys_t *id, const uint8_t *p_data, int i_data, int64_t i_pts, int64_t i_dts, bool b_last, int64_t i_length ) { const int i_max = rtp_mtu (id); /* payload max in one packet */ int i_nal_hdr; int i_nal_type; if( i_data < 2 ) return VLC_SUCCESS; i_nal_hdr = p_data[0]; i_nal_type = i_nal_hdr&0x1f; /* */ if( i_data <= i_max ) { /* Single NAL unit packet */ block_t *out = block_Alloc( 12 + i_data ); 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 ); block_t *out = block_Alloc( 12 + 2 + i_payload ); out->i_dts = i_dts + i * i_length / i_count; out->i_length = i_length / i_count; /* */ rtp_packetize_common( id, out, (b_last && i_payload == i_data), i_pts ); /* 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; } static int rtp_packetize_h264( sout_stream_id_sys_t *id, block_t *in ) { hxxx_iterator_ctx_t it; hxxx_iterator_init( &it, in->p_buffer, in->i_buffer, 0 ); const uint8_t *p_nal; size_t i_nal; while( hxxx_annexb_iterate_next( &it, &p_nal, &i_nal ) ) { /* TODO add STAP-A to remove a lot of overhead with small slice/sei/... */ rtp_packetize_h264_nal( id, p_nal, i_nal, (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts), in->i_dts, it.p_head + 3 >= it.p_tail, in->i_length * i_nal / in->i_buffer ); } block_Release(in); return VLC_SUCCESS; } /* rfc7798 */ static int rtp_packetize_h265_nal( sout_stream_id_sys_t *id, const uint8_t *p_data, size_t i_data, int64_t i_pts, int64_t i_dts, bool b_last, int64_t i_length ) { const size_t i_max = rtp_mtu (id); /* payload max in one packet */ if( i_data < 3 ) return VLC_SUCCESS; /* */ if( i_data <= i_max ) { /* Single NAL unit packet */ block_t *out = block_Alloc( 12 + i_data ); 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 { const uint16_t i_nal_hdr = (GetWBE(p_data) & 0x81FF) | 0x6200 /* 49 */; const uint8_t i_nal_type = (p_data[0] & 0x7E) >> 1; /* FU-A Fragmentation Unit without interleaving */ const size_t i_count = ( i_data-2 + i_max-3 - 2 ) / (i_max-3); p_data += 2; i_data -= 2; for( size_t i = 0; i < i_count; i++ ) { const size_t i_payload = __MIN( i_data, i_max-3 ); block_t *out = block_Alloc( 12 + 3 + i_payload ); out->i_dts = i_dts + i * i_length / i_count; out->i_length = i_length / i_count; /* */ rtp_packetize_common( id, out, (b_last && i_payload == i_data), i_pts ); /* FU indicator */ out->p_buffer[12] = i_nal_hdr >> 8; out->p_buffer[13] = i_nal_hdr & 0x00FF; /* FU header */ out->p_buffer[14] = ( i == 0 ? 0x80 : 0x00 ) | ( (i == i_count-1) ? 0x40 : 0x00 ) | i_nal_type; memcpy( &out->p_buffer[15], p_data, i_payload ); rtp_packetize_send( id, out ); i_data -= i_payload; p_data += i_payload; } } return VLC_SUCCESS; } static int rtp_packetize_h265( sout_stream_id_sys_t *id, block_t *in ) { hxxx_iterator_ctx_t it; hxxx_iterator_init( &it, in->p_buffer, in->i_buffer, 0 ); const uint8_t *p_nal; size_t i_nal; while( hxxx_annexb_iterate_next( &it, &p_nal, &i_nal ) ) { rtp_packetize_h265_nal( id, p_nal, i_nal, (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts), in->i_dts, it.p_head + 3 >= it.p_tail, in->i_length * i_nal / in->i_buffer ); } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_amr( sout_stream_id_sys_t *id, block_t *in ) { int i_max = rtp_mtu (id) - 2; /* 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; int i; /* Only supports octet-aligned mode */ for( i = 0; i < i_count; i++ ) { int i_payload = __MIN( i_max, i_data ); block_t *out = block_Alloc( 14 + i_payload ); /* rtp common header */ rtp_packetize_common( id, out, ((i == i_count - 1)?1:0), (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); /* 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 ); out->i_buffer--; /* FIXME? */ 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; } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_t140( sout_stream_id_sys_t *id, block_t *in ) { const size_t i_max = rtp_mtu (id); 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 ) { block_Release(in); return VLC_SUCCESS; /* fishy input! */ } i_payload--; } } block_t *out = block_Alloc( 12 + i_payload ); if( out == NULL ) { block_Release(in); return VLC_SUCCESS; } rtp_packetize_common( id, out, 0, in->i_pts + i_packet ); memcpy( out->p_buffer + 12, p_data, i_payload ); out->i_dts = in->i_pts; out->i_length = 0; rtp_packetize_send( id, out ); p_data += i_payload; i_data -= i_payload; } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_spx( sout_stream_id_sys_t *id, block_t *in ) { 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; if ( in->i_buffer > rtp_mtu (id) ) { block_Release(in); return VLC_SUCCESS; } /* RFC for Speex in RTP says that each packet must end on an octet boundary. So, we check to see if the number of bytes % 4 is zero. If not, we have to add some padding. This MAY be overkill since packetization is handled elsewhere and 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; } /* Allocate a new RTP p_output block of the appropriate size. Allow for 12 extra bytes of RTP header. */ p_out = block_Alloc( 12 + i_payload_size ); 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. */ p_out->p_buffer[12 + i_data_size] = c_first_pad; switch (i_payload_padding) { case 2: p_out->p_buffer[12 + i_data_size + 1] = c_remaining_pad; break; case 3: p_out->p_buffer[12 + i_data_size + 1] = c_remaining_pad; p_out->p_buffer[12 + i_data_size + 2] = c_remaining_pad; break; } } /* Add the RTP header to our p_output buffer. */ rtp_packetize_common( id, p_out, 0, (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); /* 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; block_Release(in); /* Queue the buffer for actual transmission. */ rtp_packetize_send( id, p_out ); return VLC_SUCCESS; } static int rtp_packetize_g726( sout_stream_id_sys_t *id, block_t *in, int i_pad ) { 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 ); block_t *out = block_Alloc( 12 + i_payload ); /* rtp common header */ rtp_packetize_common( id, out, 0, (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); 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; } block_Release(in); return VLC_SUCCESS; } static int rtp_packetize_g726_16( sout_stream_id_sys_t *id, block_t *in ) { return rtp_packetize_g726( id, in, 4 ); } static int rtp_packetize_g726_24( sout_stream_id_sys_t *id, block_t *in ) { return rtp_packetize_g726( id, in, 8 ); } static int rtp_packetize_g726_32( sout_stream_id_sys_t *id, block_t *in ) { return rtp_packetize_g726( id, in, 2 ); } static int rtp_packetize_g726_40( sout_stream_id_sys_t *id, block_t *in ) { return rtp_packetize_g726( id, in, 8 ); } #define RTP_VP8_HEADER_SIZE 1 #define RTP_VP8_PAYLOAD_START (12 + RTP_VP8_HEADER_SIZE) static int rtp_packetize_vp8( sout_stream_id_sys_t *id, block_t *in ) { int i_max = rtp_mtu (id) - RTP_VP8_HEADER_SIZE; int i_count = ( in->i_buffer + i_max - 1 ) / i_max; uint8_t *p_data = in->p_buffer; int i_data = in->i_buffer; if ( i_max <= 0 ) { block_Release(in); return VLC_EGENERIC; } for( int i = 0; i < i_count; i++ ) { int i_payload = __MIN( i_max, i_data ); block_t *out = block_Alloc( RTP_VP8_PAYLOAD_START + i_payload ); if ( out == NULL ) { block_Release(in); return VLC_ENOMEM; } /* VP8 payload header */ /* All frames are marked as reference ones */ if (i == 0) out->p_buffer[12] = 0x10; // partition start else out->p_buffer[12] = 0; /* rtp common header */ rtp_packetize_common( id, out, (i == i_count - 1), (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); memcpy( &out->p_buffer[RTP_VP8_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; } block_Release(in); return VLC_SUCCESS; } /* See RFC 4175 */ static int rtp_packetize_rawvideo( sout_stream_id_sys_t *id, block_t *in, vlc_fourcc_t i_format ) { int i_width, i_height; rtp_get_video_geometry( id, &i_width, &i_height ); int i_pgroup; /* Size of a group of pixels */ int i_xdec, i_ydec; /* sub-sampling factor in x and y */ switch( i_format ) { case VLC_CODEC_RGB24: i_pgroup = 3; i_xdec = i_ydec = 1; break; case VLC_CODEC_R420: i_pgroup = 6; i_xdec = i_ydec = 2; break; default: vlc_assert_unreachable(); } static const int RTP_HEADER_LEN = 12; /* each partial or complete line needs a 6 byte header */ const int i_line_header_size = 6; const int i_min_line_size = i_line_header_size + i_pgroup; uint8_t *p_data = in->p_buffer; for( uint16_t i_line_number = 0, i_column = 0; i_line_number < i_height; ) { /* Allocate a packet */ int i_payload = (int)(rtp_mtu (id) - RTP_HEADER_LEN); if( i_payload <= 0 ) { block_Release( in ); return VLC_EGENERIC; } block_t *out = block_Alloc( RTP_HEADER_LEN + i_payload ); if( unlikely( out == NULL ) ) { block_Release( in ); return VLC_ENOMEM; } /* Do headers first... */ /* Write extended seqnum */ uint8_t *p_outdata = out->p_buffer + RTP_HEADER_LEN; SetWBE( p_outdata, rtp_get_extended_sequence( id ) ); p_outdata += 2; i_payload -= 2; uint8_t *p_headers = p_outdata; for( uint8_t i_cont = 0x80; i_cont && i_payload > i_min_line_size; ) { i_payload -= i_line_header_size; int i_pixels = i_width - i_column; int i_length = (i_pixels * i_pgroup) / i_xdec; const bool b_next_line = i_payload >= i_length; if( !b_next_line ) { i_pixels = (i_payload / i_pgroup) * i_xdec; i_length = (i_pixels * i_pgroup) / i_xdec; } i_payload -= i_length; /* write length */ SetWBE( p_outdata, i_length ); p_outdata += 2; /* write line number */ /* TODO: support interlaced */ const uint8_t i_field = 0; SetWBE( p_outdata, i_line_number ); *p_outdata |= i_field << 7; p_outdata += 2; /* continue if there's still room in the packet and we have more lines */ i_cont = (i_payload > i_min_line_size && i_line_number < (i_height - i_ydec)) ? 0x80 : 0x00; /* write offset and continuation marker */ SetWBE( p_outdata, i_column ); *p_outdata |= i_cont; p_outdata += 2; if( b_next_line ) { i_column = 0; i_line_number += i_ydec; } else { i_column += i_pixels; } } /* write the actual video data here */ for( uint8_t i_cont = 0x80; i_cont; p_headers += i_line_header_size ) { const uint16_t i_length = GetWBE( p_headers ); const uint16_t i_lin = GetWBE( p_headers + 2 ) & 0x7fff; uint16_t i_offs = GetWBE( p_headers + 4 ) & 0x7fff; i_cont = p_headers[4] & 0x80; if( i_format == VLC_CODEC_RGB24 ) { const int i_ystride = i_width * i_pgroup; i_offs /= i_xdec; memcpy( p_outdata, p_data + (i_lin * i_ystride) + (i_offs * i_pgroup), i_length ); p_outdata += i_length; } else if( i_format == VLC_CODEC_R420 ) { memcpy( p_outdata, p_data, i_length ); p_outdata += i_length; p_data += i_length; } else vlc_assert_unreachable(); } /* rtp common header */ rtp_packetize_common( id, out, i_line_number >= i_height, (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); out->i_dts = in->i_dts; out->i_length = in->i_length; rtp_packetize_send( id, out ); } block_Release( in ); return VLC_SUCCESS; } static int rtp_packetize_r420( sout_stream_id_sys_t *id, block_t *in ) { return rtp_packetize_rawvideo( id, in, VLC_CODEC_R420 ); } static int rtp_packetize_rgb24( sout_stream_id_sys_t *id, block_t *in ) { return rtp_packetize_rawvideo( id, in, VLC_CODEC_RGB24 ); } static int rtp_packetize_jpeg( sout_stream_id_sys_t *id, block_t *in ) { uint8_t *p_data = in->p_buffer; int i_data = in->i_buffer; uint8_t *bufend = p_data + i_data; const uint8_t *qtables = NULL; int nb_qtables = 0; int off = 0; // fragment offset in frame int y_sampling_factor; // type is set by pixel format (determined by y_sampling_factor): // 0 for yuvj422p // 1 for yuvj420p // += 64 if DRI present int type; int w = 0; // Width in multiples of 8 int h = 0; // Height in multiples of 8 int restart_interval; int dri_found = 0; // Skip SOI if (GetWBE(p_data) != 0xffd8) goto error; p_data += 2; i_data -= 2; /* parse the header to get necessary info */ int header_finished = 0; while (!header_finished && p_data + 4 <= bufend) { uint16_t marker = GetWBE(p_data); uint8_t *section = p_data + 2; int section_size = GetWBE(section); uint8_t *section_body = p_data + 4; if (section + section_size > bufend) goto error; assert((marker & 0xff00) == 0xff00); switch (marker) { case 0xffdb /*DQT*/: if (section_body[0]) goto error; // Only 8-bit precision is supported /* a quantization table is 64 bytes long */ nb_qtables = section_size / 65; qtables = section_body; break; case 0xffc0 /*SOF0*/: { int height = GetWBE(§ion_body[1]); int width = GetWBE(§ion_body[3]); if (width > 2040 || height > 2040) { // larger than limit supported by RFC 2435 goto error; } // Round up by 8, divide by 8 w = ((width+7)&~7) >> 3; h = ((height+7)&~7) >> 3; // Get components sampling to determine type // Y has component ID 1 // Possible configurations of sampling factors: // Y - 0x22, Cb - 0x11, Cr - 0x11 => yuvj420p // Y - 0x21, Cb - 0x11, Cr - 0x11 => yuvj422p // Only 3 components are supported by RFC 2435 if (section_body[5] != 3) // Number of components goto error; for (int j = 0; j < 3; j++) { if (section_body[6 + j * 3] == 1 /* Y */) { y_sampling_factor = section_body[6 + j * 3 + 1]; } else if (section_body[6 + j * 3 + 1] != 0x11) { // Sampling factor is unsupported by RFC 2435 goto error; } } break; } case 0xffdd /*DRI*/: restart_interval = GetWBE(section_body); dri_found = 1; break; case 0xffda /*SOS*/: /* SOS is last marker in the header */ header_finished = 1; break; } // Step over marker, 16bit length and section body p_data += 2 + section_size; i_data -= 2 + section_size; } if (!header_finished) goto error; if (!w || !h) goto error; switch (y_sampling_factor) { case 0x22: // yuvj420p type = 1; break; case 0x21: // yuvj422p type = 0; break; default: goto error; // Sampling format unsupported by RFC 2435 } if (dri_found) type += 64; while ( i_data ) { int hdr_size = 8 + dri_found * 4; if (off == 0 && nb_qtables) hdr_size += 4 + 64 * nb_qtables; int i_payload = __MIN( i_data, (int)(rtp_mtu (id) - hdr_size) ); if ( i_payload <= 0 ) goto error; block_t *out = block_Alloc( 12 + hdr_size + i_payload ); if( out == NULL ) { block_Release( in ); return VLC_ENOMEM; } uint8_t *p = out->p_buffer + 12; /* set main header */ /* set type-specific=0, set offset in following 24 bits: */ SetDWBE(p, off & 0x00ffffff); p += 4; *p++ = type; *p++ = 255; // Quant value *p++ = w; *p++ = h; // Write restart_marker_hdr if needed if (dri_found) { SetWBE(p, restart_interval); p += 2; // restart_count. Hardcoded same value as in gstreamer implementation SetWBE(p, 0xffff); p += 2; } if (off == 0 && nb_qtables) { /* set quantization tables header */ *p++ = 0; *p++ = 0; SetWBE (p, 64 * nb_qtables); p += 2; for (int i = 0; i < nb_qtables; i++) { memcpy (p, &qtables[65 * i + 1], 64); p += 64; } } /* rtp common header */ rtp_packetize_common( id, out, (i_payload == i_data), (in->i_pts > VLC_TICK_INVALID ? in->i_pts : in->i_dts) ); memcpy( p, p_data, i_payload ); out->i_dts = in->i_dts; out->i_length = in->i_length; rtp_packetize_send( id, out ); p_data += i_payload; i_data -= i_payload; off += i_payload; } block_Release(in); return VLC_SUCCESS; error: block_Release(in); return VLC_EGENERIC; }