X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fstream_out%2Frtpfmt.c;h=baee82aa50ff50eb0702436aae52e1951e5d786b;hb=72afb8836154356a8cea09d467dfa76b8168df83;hp=0b8b4881bbe4e6a96b2d783cefa09e0395112dec;hpb=c32cb1d118ba028be1df6b98a11811a27b04d782;p=vlc diff --git a/modules/stream_out/rtpfmt.c b/modules/stream_out/rtpfmt.c index 0b8b4881bb..baee82aa50 100644 --- a/modules/stream_out/rtpfmt.c +++ b/modules/stream_out/rtpfmt.c @@ -1,25 +1,25 @@ /***************************************************************************** * rtpfmt.c: RTP payload formats ***************************************************************************** - * Copyright (C) 2003-2004 the VideoLAN team + * Copyright (C) 2003-2004 VLC authors and VideoLAN * Copyright © 2007 Rémi Denis-Courmont * $Id$ * * Authors: Laurent Aimar * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * 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 General Public License for more details. + * 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 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. + * 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 @@ -29,16 +29,657 @@ #include #include #include +#include #include "rtp.h" +#include "../demux/xiph.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_split(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_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 *); + +#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]; + 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 = (((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, 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, 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_split; + 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_split; + 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_split; + else + rtp_fmt->pf_packetize = rtp_packetize_swab; + break; + case VLC_CODEC_U8: + rtp_fmt->ptname = "L8"; + rtp_fmt->pf_packetize = rtp_packetize_split; + break; + case VLC_CODEC_S24B: + rtp_fmt->ptname = "L24"; + rtp_fmt->pf_packetize = rtp_packetize_split; + 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_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; + } + } -int -rtp_packetize_h264_nal( sout_stream_id_t *id, + 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; + 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: + assert(0); + } + + 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_split; + 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_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_mpa( sout_stream_id_t *id, - block_t *in ) +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[len + 1]; + 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); + 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_buffer = 18 + 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_buffer = 18 + 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; + } + + 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; @@ -55,11 +696,9 @@ int rtp_packetize_mpa( sout_stream_id_t *id, /* rtp common header */ rtp_packetize_common( id, out, (i == i_count - 1)?1:0, in->i_pts ); /* mbz set to 0 */ - out->p_buffer[12] = 0; - out->p_buffer[13] = 0; + SetWBE( out->p_buffer + 12, 0 ); /* fragment offset in the current frame */ - out->p_buffer[14] = ( (i*i_max) >> 8 )&0xff; - out->p_buffer[15] = ( (i*i_max) )&0xff; + SetWBE( out->p_buffer + 14, i * i_max ); memcpy( &out->p_buffer[16], p_data, i_payload ); out->i_buffer = 16 + i_payload; @@ -76,7 +715,7 @@ int rtp_packetize_mpa( sout_stream_id_t *id, } /* rfc2250 */ -int rtp_packetize_mpv( sout_stream_id_t *id, block_t *in ) +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; @@ -145,6 +784,7 @@ int rtp_packetize_mpv( sout_stream_id_t *id, block_t *in ) { 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 )| @@ -154,13 +794,9 @@ int rtp_packetize_mpv( sout_stream_id_t *id, block_t *in ) /* rtp common header */ rtp_packetize_common( id, out, (i == i_count - 1)?1:0, - in->i_pts > 0 ? in->i_pts : in->i_dts ); + in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts ); - /* 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 */ - out->p_buffer[12] = ( h >> 24 )&0xff; - out->p_buffer[13] = ( h >> 16 )&0xff; - out->p_buffer[14] = ( h >> 8 )&0xff; - out->p_buffer[15] = ( h )&0xff; + SetDWBE( out->p_buffer + 12, h ); memcpy( &out->p_buffer[16], p_data, i_payload ); @@ -177,7 +813,7 @@ int rtp_packetize_mpv( sout_stream_id_t *id, block_t *in ) return VLC_SUCCESS; } -int rtp_packetize_ac3( sout_stream_id_t *id, block_t *in ) +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; @@ -213,7 +849,7 @@ int rtp_packetize_ac3( sout_stream_id_t *id, block_t *in ) return VLC_SUCCESS; } -int rtp_packetize_split( sout_stream_id_t *id, block_t *in ) +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; @@ -229,7 +865,7 @@ int rtp_packetize_split( sout_stream_id_t *id, block_t *in ) /* rtp common header */ rtp_packetize_common( id, out, (i == i_count - 1), - (in->i_pts > 0 ? in->i_pts : in->i_dts) ); + (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) ); memcpy( &out->p_buffer[12], p_data, i_payload ); out->i_buffer = 12 + i_payload; @@ -245,8 +881,41 @@ int rtp_packetize_split( sout_stream_id_t *id, block_t *in ) return VLC_SUCCESS; } +/* split and convert from little endian to network byte order */ +static int rtp_packetize_swab( 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_TS_INVALID ? in->i_pts : in->i_dts) ); + swab( p_data, out->p_buffer + 12, i_payload ); + + out->i_buffer = 12 + 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; + } + + return VLC_SUCCESS; +} + /* rfc3016 */ -int rtp_packetize_mp4a_latm( sout_stream_id_t *id, block_t *in ) +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; @@ -267,7 +936,7 @@ int rtp_packetize_mp4a_latm( sout_stream_id_t *id, block_t *in ) /* rtp common header */ rtp_packetize_common( id, out, ((i == i_count - 1) ? 1 : 0), - (in->i_pts > 0 ? in->i_pts : in->i_dts) ); + (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) ); if( i == 0 ) { @@ -298,7 +967,7 @@ int rtp_packetize_mp4a_latm( sout_stream_id_t *id, block_t *in ) return VLC_SUCCESS; } -int rtp_packetize_mp4a( sout_stream_id_t *id, block_t *in ) +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; @@ -314,14 +983,13 @@ int rtp_packetize_mp4a( sout_stream_id_t *id, block_t *in ) /* rtp common header */ rtp_packetize_common( id, out, ((i == i_count - 1)?1:0), - (in->i_pts > 0 ? in->i_pts : in->i_dts) ); + (in->i_pts > VLC_TS_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, */ - out->p_buffer[14] = ( in->i_buffer >> 5 )&0xff; - out->p_buffer[15] = ( (in->i_buffer&0xff)<<3 )|0; + SetWBE( out->p_buffer + 14, (in->i_buffer << 3) | 0 ); memcpy( &out->p_buffer[16], p_data, i_payload ); @@ -342,7 +1010,7 @@ int rtp_packetize_mp4a( sout_stream_id_t *id, block_t *in ) /* rfc2429 */ #define RTP_H263_HEADER_SIZE (2) // plen = 0 #define RTP_H263_PAYLOAD_START (14) // plen = 0 -int rtp_packetize_h263( sout_stream_id_t *id, block_t *in ) +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; @@ -381,11 +1049,10 @@ int rtp_packetize_h263( sout_stream_id_t *id, block_t *in ) /* 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 > 0 ? in->i_pts : in->i_dts ); + in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts ); /* h263 header */ - out->p_buffer[12] = ( h >> 8 )&0xff; - out->p_buffer[13] = ( h )&0xff; + SetWBE( out->p_buffer + 12, h ); memcpy( &out->p_buffer[RTP_H263_PAYLOAD_START], p_data, i_payload ); out->i_buffer = RTP_H263_PAYLOAD_START + i_payload; @@ -402,8 +1069,8 @@ int rtp_packetize_h263( sout_stream_id_t *id, block_t *in ) } /* rfc3984 */ -int -rtp_packetize_h264_nal( sout_stream_id_t *id, +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 ) { @@ -454,7 +1121,8 @@ rtp_packetize_h264_nal( sout_stream_id_t *id, out->i_length = i_length / i_count; /* */ - rtp_packetize_common( id, out, (b_last && i_payload == i_data), i_pts ); + rtp_packetize_common( id, out, (b_last && i_payload == i_data), + i_pts ); out->i_buffer = 14 + i_payload; /* FU indicator */ @@ -472,7 +1140,7 @@ rtp_packetize_h264_nal( sout_stream_id_t *id, return VLC_SUCCESS; } -int rtp_packetize_h264( sout_stream_id_t *id, block_t *in ) +static int rtp_packetize_h264( sout_stream_id_sys_t *id, block_t *in ) { const uint8_t *p_buffer = in->p_buffer; int i_buffer = in->i_buffer; @@ -503,8 +1171,8 @@ int rtp_packetize_h264( sout_stream_id_t *id, block_t *in ) } /* TODO add STAP-A to remove a lot of overhead with small slice/sei/... */ rtp_packetize_h264_nal( id, p_buffer, i_size, - (in->i_pts > 0 ? in->i_pts : in->i_dts), in->i_dts, - (i_size >= i_buffer), in->i_length * i_size / in->i_buffer ); + (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 ); i_buffer -= i_skip; p_buffer += i_skip; @@ -512,7 +1180,7 @@ int rtp_packetize_h264( sout_stream_id_t *id, block_t *in ) return VLC_SUCCESS; } -int rtp_packetize_amr( sout_stream_id_t *id, block_t *in ) +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; @@ -529,7 +1197,7 @@ int rtp_packetize_amr( sout_stream_id_t *id, block_t *in ) /* rtp common header */ rtp_packetize_common( id, out, ((i == i_count - 1)?1:0), - (in->i_pts > 0 ? in->i_pts : in->i_dts) ); + (in->i_pts > VLC_TS_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 */ @@ -550,7 +1218,7 @@ int rtp_packetize_amr( sout_stream_id_t *id, block_t *in ) return VLC_SUCCESS; } -int rtp_packetize_t140( sout_stream_id_t *id, block_t *in ) +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; @@ -583,7 +1251,7 @@ int rtp_packetize_t140( sout_stream_id_t *id, block_t *in ) memcpy( out->p_buffer + 12, p_data, i_payload ); out->i_buffer = 12 + i_payload; - out->i_dts = out->i_pts; + out->i_dts = in->i_pts; out->i_length = 0; rtp_packetize_send( id, out ); @@ -596,7 +1264,7 @@ int rtp_packetize_t140( sout_stream_id_t *id, block_t *in ) } -int rtp_packetize_spx( sout_stream_id_t *id, block_t *in ) +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; @@ -656,7 +1324,8 @@ int rtp_packetize_spx( sout_stream_id_t *id, block_t *in ) } /* Add the RTP header to our p_output buffer. */ - rtp_packetize_common( id, p_out, 0, (in->i_pts > 0 ? in->i_pts : in->i_dts) ); + rtp_packetize_common( id, p_out, 0, + (in->i_pts > VLC_TS_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 ); @@ -669,7 +1338,7 @@ int rtp_packetize_spx( sout_stream_id_t *id, block_t *in ) return VLC_SUCCESS; } -static int rtp_packetize_g726( sout_stream_id_t *id, block_t *in, int i_pad ) +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; @@ -681,11 +1350,11 @@ static int rtp_packetize_g726( sout_stream_id_t *id, block_t *in, int i_pad ) while( i_data > 0 ) { int i_payload = __MIN( i_max, i_data ); - block_t *out = block_New( p_stream, 12 + i_payload ); + block_t *out = block_Alloc( 12 + i_payload ); /* rtp common header */ rtp_packetize_common( id, out, 0, - (in->i_pts > 0 ? in->i_pts : in->i_dts) ); + (in->i_pts > VLC_TS_INVALID ? in->i_pts : in->i_dts) ); memcpy( &out->p_buffer[12], p_data, i_payload ); @@ -701,22 +1370,255 @@ static int rtp_packetize_g726( sout_stream_id_t *id, block_t *in, int i_pad ) return VLC_SUCCESS; } -int rtp_packetize_g726_16( sout_stream_id_t *id, block_t *in ) +static int rtp_packetize_g726_16( sout_stream_id_sys_t *id, block_t *in ) { - return rtp_packetize_g726( id, in, 16 ); + return rtp_packetize_g726( id, in, 4 ); } -int rtp_packetize_g726_24( sout_stream_id_t *id, block_t *in ) +static int rtp_packetize_g726_24( sout_stream_id_sys_t *id, block_t *in ) { - return rtp_packetize_g726( id, in, 24 ); + return rtp_packetize_g726( id, in, 8 ); } -int rtp_packetize_g726_32( sout_stream_id_t *id, block_t *in ) +static int rtp_packetize_g726_32( sout_stream_id_sys_t *id, block_t *in ) { - return rtp_packetize_g726( id, in, 32 ); + 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 ) + 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 ) + 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_TS_INVALID ? in->i_pts : in->i_dts) ); + memcpy( &out->p_buffer[RTP_VP8_PAYLOAD_START], p_data, i_payload ); + + out->i_buffer = RTP_VP8_PAYLOAD_START + 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; + } + + return VLC_SUCCESS; } -int rtp_packetize_g726_40( sout_stream_id_t *id, block_t *in ) +static int rtp_packetize_jpeg( sout_stream_id_sys_t *id, block_t *in ) { - return rtp_packetize_g726( id, in, 40 ); + 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) + return VLC_EGENERIC; + 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) + return VLC_EGENERIC; + + assert((marker & 0xff00) == 0xff00); + switch (marker) + { + case 0xffdb /*DQT*/: + if (section_body[0]) + { + // Only 8-bit precision is supported + return VLC_EGENERIC; + } + + /* 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 + return VLC_EGENERIC; + } + // 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 + return VLC_EGENERIC; + 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 + return VLC_EGENERIC; + } + } + 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) + return VLC_EGENERIC; + if (!w || !h) + return VLC_EGENERIC; + + switch (y_sampling_factor) + { + case 0x22: // yuvj420p + type = 1; + break; + case 0x21: // yuvj422p + type = 0; + break; + default: + // Sampling format unsupported by RFC 2435 + return VLC_EGENERIC; + } + + 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 ) + return VLC_EGENERIC; + + block_t *out = block_Alloc( 12 + hdr_size + i_payload ); + if( out == NULL ) + 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_TS_INVALID ? in->i_pts : in->i_dts) ); + memcpy( p, p_data, i_payload ); + + out->i_buffer = 12 + hdr_size + 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; + } + + return VLC_SUCCESS; }