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