1/* @(#) implementation of RTP-protocol parsing functions for udpxy 2 * 3 * Copyright 2008-2011 Pavel V. Cherenkov (pcherenkov@gmail.com) 4 * 5 * This file is part of udpxy. 6 * 7 * udpxy is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * udpxy is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with udpxy. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21#include <stdio.h> 22#include <assert.h> 23#include <sys/types.h> 24#include <errno.h> 25 26#include "rtp.h" 27#include "mtrace.h" 28#include "util.h" 29 30 31/* check if the buffer is an RTP packet 32 */ 33int 34RTP_check( const char* buf, const size_t len, int* is_rtp, FILE* log ) 35{ 36 int rtp_version = 0; 37 int rtp_payload_type = 0; 38 39 assert( buf && len && is_rtp && log ); 40 41 if( len < RTP_MIN_SIZE ) { 42 (void)tmfprintf( log, "RTP_check: buffer size [%lu] is " 43 "less than minimum [%lu]\n" , (u_long)len, (u_long)RTP_MIN_SIZE ); 44 return -1; 45 } 46 47 /* initial assumption: is NOT RTP */ 48 *is_rtp = 0; 49 50 if( MPEG_TS_SIG == buf[0] ) { 51 TRACE( (void)tmfputs( "MPEG-TS stream detected\n", log ) ); 52 return 0; 53 } 54 55 rtp_version = ( buf[0] & 0xC0 ) >> 6; 56 if( RTP_VER2 != rtp_version ) { 57 TRACE( (void)tmfprintf( log, "RTP_check: wrong RTP version [%d], " 58 "must be [%d]\n", (int)rtp_version, (int)RTP_VER2) ); 59 return 0; 60 } 61 62 if( len < RTP_HDR_SIZE ) { 63 TRACE( (void)tmfprintf( log, "RTP_check: header size " 64 "is too small [%lu]\n", (u_long)len ) ); 65 return 0; 66 } 67 68 *is_rtp = 1; 69 70 rtp_payload_type = buf[1] & 0x7F; 71 switch( rtp_payload_type ) { 72 case P_MPGA: 73 TRACE( (void)tmfprintf( log, "RTP_check: [%d] MPEG audio stream\n", 74 (int)rtp_payload_type ) ); 75 break; 76 case P_MPGV: 77 TRACE( (void)tmfprintf( log, "RTP_check: [%d] MPEG video stream\n", 78 (int)rtp_payload_type ) ); 79 break; 80 case P_MPGTS: 81 TRACE( (void)tmfprintf( log, "RTP_check: [%d] MPEG TS stream\n", 82 (int)rtp_payload_type ) ); 83 break; 84 85 default: 86 (void) tmfprintf( log, "RTP_check: unsupported RTP " 87 "payload type [%d]\n", (int)rtp_payload_type ); 88 return -1; 89 } 90 91 return 0; 92} 93 94 95/* return 1 if buffer contains an RTP packet, 0 otherwise 96 */ 97int 98RTP_verify( const char* buf, const size_t len, FILE* log ) 99{ 100 int rtp_version = 0; 101 int rtp_payload_type = 0; 102 103 if( (len < RTP_MIN_SIZE) || (len < RTP_HDR_SIZE) ) { 104 (void)tmfprintf( log, "RTP_verify: inappropriate size=[%lu] " 105 "of RTP packet\n", (u_long)len ); 106 return 0; 107 } 108 109 rtp_version = ( buf[0] & 0xC0 ) >> 6; 110 rtp_payload_type = buf[1] & 0x7F; 111 112 /* 113 TRACE( (void)tmfprintf( log, "RTP version [%d] at [%p]\n", rtp_version, 114 (const void*)buf ) ); 115 */ 116 117 if( RTP_VER2 != rtp_version ) { 118 /* 119 TRACE( (void)tmfprintf( log, "RTP_verify: wrong RTP version [%d], " 120 "must be [%d]\n", (int)rtp_version, (int)RTP_VER2) ); 121 */ 122 return 0; 123 } 124 125 switch( rtp_payload_type ) { 126 case P_MPGA: 127 case P_MPGV: 128 case P_MPGTS: 129 break; 130 default: 131 (void) tmfprintf( log, "RTP_verify: unsupported RTP " 132 "payload type [%d]\n", (int)rtp_payload_type ); 133 return 0; 134 } 135 136 return 1; 137} 138 139 140/* calculate length of an RTP header 141 */ 142int 143RTP_hdrlen( const char* buf, const size_t len, size_t* hdrlen, FILE* log ) 144{ 145 int rtp_CSRC = -1, rtp_ext = -1; 146 int rtp_payload = -1; 147 ssize_t front_skip = 0, 148 ext_len = 0; 149 150 assert( buf && (len >= RTP_HDR_SIZE) && hdrlen && log ); 151 152 rtp_payload = buf[1] & 0x7F; 153 rtp_CSRC = buf[0] & 0x0F; 154 rtp_ext = buf[0] & 0x10; 155 156 /* profile-based skip: adopted from vlc 0.8.6 code */ 157 if( (P_MPGA == rtp_payload) || (P_MPGV == rtp_payload) ) 158 front_skip = 4; 159 else if( P_MPGTS != rtp_payload ) { 160 (void)tmfprintf( log, "RTP_process: " 161 "Unsupported payload type [%d]\n", rtp_payload ); 162 return -1; 163 } 164 165 if( rtp_ext ) { 166 /* 167 TRACE( (void)tmfprintf( log, "%s: RTP x-header detected, CSRC=[%d]\n", 168 __func__, rtp_CSRC) ); 169 */ 170 171 if( len < RTP_XTHDRLEN ) { 172 /* 173 TRACE( (void)tmfprintf( log, "%s: RTP x-header requires " 174 "[%lu] bytes, only [%lu] provided\n", __func__, 175 (u_long)(XTLEN_OFFSET + 1), (u_long)len ) ); 176 */ 177 return ENOMEM; 178 } 179 } 180 181 if( rtp_ext ) { 182 ext_len = XTSIZE + 183 sizeof(int32_t) * ((buf[ XTLEN_OFFSET ] << 8) + buf[ XTLEN_OFFSET + 1 ]); 184 } 185 186 front_skip += RTP_HDR_SIZE + (CSRC_SIZE * rtp_CSRC) + ext_len; 187 188 *hdrlen = front_skip; 189 return 0; 190} 191 192 193/* process RTP package to retrieve the payload 194 */ 195int 196RTP_process( void** pbuf, size_t* len, int verify, FILE* log ) 197{ 198 int rtp_padding = -1; 199 size_t front_skip = 0, back_skip = 0, pad_len = 0; 200 201 char* buf = NULL; 202 size_t pkt_len = 0; 203 204 assert( pbuf && len && log ); 205 buf = *pbuf; 206 pkt_len = *len; 207 208 if( verify && !RTP_verify( buf, pkt_len, log ) ) 209 return -1; 210 211 if( 0 != RTP_hdrlen( buf, pkt_len, &front_skip, log ) ) 212 return -1; 213 214 rtp_padding = buf[0] & 0x20; 215 if( rtp_padding ) { 216 pad_len = buf[ pkt_len - 1 ]; 217 } 218 219 back_skip += pad_len; 220 221 if( verify && (pkt_len < (front_skip + back_skip)) ) { 222 (void) tmfprintf( log, "RTP_process: invalid header " 223 "(skip [%lu] exceeds packet length [%lu])\n", 224 (u_long)(front_skip + back_skip), (u_long)pkt_len ); 225 return -1; 226 } 227 228 /* adjust buffer/length to skip heading and padding */ 229 /* 230 TRACE( (void)tmfprintf( log, "In: RTP buf=[%p] of [%lu] bytes, " 231 "fskip=[%ld], bskip=[%lu]\n", 232 (void*)buf, (u_long)pkt_len, 233 (u_long)front_skip, (u_long)back_skip ) ); 234 */ 235 236 buf += front_skip; 237 pkt_len -= (front_skip + back_skip); 238 239 /* 240 TRACE( (void)tmfprintf( log, "Out RTP buf=[%p] of [%lu] bytes\n", 241 (void*)buf, (u_long)pkt_len ) ); 242 */ 243 *pbuf = buf; 244 *len = pkt_len; 245 246 return 0; 247} 248 249 250/* __EOF__ */ 251 252