1 /* $Id$ */
2 /*
3 * Copyright (C) 2018 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include <pjmedia/rtcp_fb.h>
21 #include <pjmedia/codec.h>
22 #include <pjmedia/endpoint.h>
23 #include <pjmedia/errno.h>
24 #include <pjmedia/vid_codec.h>
25 #include <pj/assert.h>
26 #include <pj/log.h>
27 #include <pj/os.h>
28 #include <pj/sock.h>
29 #include <pj/string.h>
30
31 #define THIS_FILE "rtcp_fb.c"
32
33 #define RTCP_RTPFB 205
34 #define RTCP_PSFB 206
35
36 /*
37 * Build an RTCP-FB Generic NACK packet.
38 */
pjmedia_rtcp_fb_build_nack(pjmedia_rtcp_session * session,void * buf,pj_size_t * length,unsigned nack_cnt,const pjmedia_rtcp_fb_nack nack[])39 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_nack(
40 pjmedia_rtcp_session *session,
41 void *buf,
42 pj_size_t *length,
43 unsigned nack_cnt,
44 const pjmedia_rtcp_fb_nack nack[])
45 {
46 pjmedia_rtcp_common *hdr;
47 pj_uint8_t *p;
48 unsigned len, i;
49
50 PJ_ASSERT_RETURN(session && buf && length && nack_cnt && nack, PJ_EINVAL);
51
52 len = (3 + nack_cnt) * 4;
53 if (len > *length)
54 return PJ_ETOOSMALL;
55
56 /* Build RTCP-FB NACK header */
57 hdr = (pjmedia_rtcp_common*)buf;
58 pj_memcpy(hdr, &session->rtcp_rr_pkt.common, sizeof(*hdr));
59 hdr->pt = RTCP_RTPFB;
60 hdr->count = 1; /* FMT = 1 */
61 hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
62
63 /* Build RTCP-FB NACK FCI */
64 p = (pj_uint8_t*)hdr + sizeof(*hdr);
65 for (i = 0; i < nack_cnt; ++i) {
66 pj_uint16_t val;
67 val = pj_htons((pj_uint16_t)nack[i].pid);
68 pj_memcpy(p, &val, 2);
69 val = pj_htons(nack[i].blp);
70 pj_memcpy(p+2, &val, 2);
71 p += 4;
72 }
73
74 /* Finally */
75 *length = len;
76
77 return PJ_SUCCESS;
78 }
79
80
81 /*
82 * Build an RTCP-FB Picture Loss Indication (PLI) packet.
83 */
pjmedia_rtcp_fb_build_pli(pjmedia_rtcp_session * session,void * buf,pj_size_t * length)84 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_pli(
85 pjmedia_rtcp_session *session,
86 void *buf,
87 pj_size_t *length)
88 {
89 pjmedia_rtcp_common *hdr;
90 unsigned len;
91
92 PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL);
93
94 len = 12;
95 if (len > *length)
96 return PJ_ETOOSMALL;
97
98 /* Build RTCP-FB PLI header */
99 hdr = (pjmedia_rtcp_common*)buf;
100 pj_memcpy(hdr, &session->rtcp_rr_pkt.common, sizeof(*hdr));
101 hdr->pt = RTCP_PSFB;
102 hdr->count = 1; /* FMT = 1 */
103 hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
104
105 /* Finally */
106 *length = len;
107
108 return PJ_SUCCESS;
109 }
110
111
112 /*
113 * Build an RTCP-FB Slice Loss Indication (SLI) packet.
114 */
pjmedia_rtcp_fb_build_sli(pjmedia_rtcp_session * session,void * buf,pj_size_t * length,unsigned sli_cnt,const pjmedia_rtcp_fb_sli sli[])115 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_sli(
116 pjmedia_rtcp_session *session,
117 void *buf,
118 pj_size_t *length,
119 unsigned sli_cnt,
120 const pjmedia_rtcp_fb_sli sli[])
121 {
122 pjmedia_rtcp_common *hdr;
123 pj_uint8_t *p;
124 unsigned len, i;
125
126 PJ_ASSERT_RETURN(session && buf && length && sli_cnt && sli, PJ_EINVAL);
127
128 len = (3 + sli_cnt) * 4;
129 if (len > *length)
130 return PJ_ETOOSMALL;
131
132 /* Build RTCP-FB SLI header */
133 hdr = (pjmedia_rtcp_common*)buf;
134 pj_memcpy(hdr, &session->rtcp_rr_pkt.common, sizeof(*hdr));
135 hdr->pt = RTCP_PSFB;
136 hdr->count = 2; /* FMT = 2 */
137 hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
138
139 /* Build RTCP-FB SLI FCI */
140 p = (pj_uint8_t*)hdr + sizeof(*hdr);
141 for (i = 0; i < sli_cnt; ++i) {
142 /* 'first' takes 13 bit */
143 *p++ = (pj_uint8_t)((sli[i].first >> 5) & 0xFF); /* 8 MSB bits */
144 *p = (pj_uint8_t)((sli[i].first & 31) << 3); /* 5 LSB bits */
145 /* 'number' takes 13 bit */
146 *p++ |= (pj_uint8_t)((sli[i].number >> 10) & 7); /* 3 MSB bits */
147 *p++ = (pj_uint8_t)((sli[i].number >> 2) & 0xFF); /* 8 mid bits */
148 *p = (pj_uint8_t)((sli[i].number & 3) << 6); /* 2 LSB bits */
149 /* 'pict_id' takes 6 bit */
150 *p++ |= (sli[i].pict_id & 63);
151 }
152
153 /* Finally */
154 *length = len;
155
156 return PJ_SUCCESS;
157 }
158
159
160 /*
161 * Build an RTCP-FB Slice Loss Indication (SLI) packet.
162 */
pjmedia_rtcp_fb_build_rpsi(pjmedia_rtcp_session * session,void * buf,pj_size_t * length,const pjmedia_rtcp_fb_rpsi * rpsi)163 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_rpsi(
164 pjmedia_rtcp_session *session,
165 void *buf,
166 pj_size_t *length,
167 const pjmedia_rtcp_fb_rpsi *rpsi)
168 {
169 pjmedia_rtcp_common *hdr;
170 pj_uint8_t *p;
171 unsigned bitlen, padlen, len;
172
173 PJ_ASSERT_RETURN(session && buf && length && rpsi, PJ_EINVAL);
174
175 bitlen = rpsi->rpsi_bit_len + 16;
176 padlen = (32 - (bitlen % 32)) % 32;
177 len = (3 + (bitlen+padlen)/32) * 4;
178 if (len > *length)
179 return PJ_ETOOSMALL;
180
181 /* Build RTCP-FB RPSI header */
182 hdr = (pjmedia_rtcp_common*)buf;
183 pj_memcpy(hdr, &session->rtcp_rr_pkt.common, sizeof(*hdr));
184 hdr->pt = RTCP_PSFB;
185 hdr->count = 3; /* FMT = 3 */
186 hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
187
188 /* Build RTCP-FB RPSI FCI */
189 p = (pj_uint8_t*)hdr + sizeof(*hdr);
190 /* PB (number of padding bits) */
191 *p++ = (pj_uint8_t)padlen;
192 /* Payload type */
193 *p++ = rpsi->pt & 0x7F;
194 /* RPSI bit string */
195 pj_memcpy(p, rpsi->rpsi.ptr, rpsi->rpsi_bit_len/8);
196 p += rpsi->rpsi_bit_len/8;
197 if (rpsi->rpsi_bit_len % 8) {
198 *p++ = *(rpsi->rpsi.ptr + rpsi->rpsi_bit_len/8);
199 }
200 /* Zero padding */
201 if (padlen >= 8)
202 pj_bzero(p, padlen/8);
203
204 /* Finally */
205 *length = len;
206
207 return PJ_SUCCESS;
208 }
209
210
211 /*
212 * Initialize RTCP Feedback setting with default values.
213 */
pjmedia_rtcp_fb_setting_default(pjmedia_rtcp_fb_setting * opt)214 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_setting_default(
215 pjmedia_rtcp_fb_setting *opt)
216 {
217 pj_bzero(opt, sizeof(*opt));
218 opt->dont_use_avpf = PJ_TRUE;
219
220 return PJ_SUCCESS;
221 }
222
223
pjmedia_rtcp_fb_cap_dup(pj_pool_t * pool,pjmedia_rtcp_fb_cap * dst,const pjmedia_rtcp_fb_cap * src)224 static void pjmedia_rtcp_fb_cap_dup(pj_pool_t *pool,
225 pjmedia_rtcp_fb_cap *dst,
226 const pjmedia_rtcp_fb_cap *src)
227 {
228 pj_strdup(pool, &dst->codec_id, &src->codec_id);
229 dst->type = src->type;
230 pj_strdup(pool, &dst->type_name, &src->type_name);
231 pj_strdup(pool, &dst->param, &src->param);
232 }
233
234
235 /*
236 * Duplicate RTCP Feedback setting.
237 */
pjmedia_rtcp_fb_setting_dup(pj_pool_t * pool,pjmedia_rtcp_fb_setting * dst,const pjmedia_rtcp_fb_setting * src)238 PJ_DEF(void) pjmedia_rtcp_fb_setting_dup( pj_pool_t *pool,
239 pjmedia_rtcp_fb_setting *dst,
240 const pjmedia_rtcp_fb_setting *src)
241 {
242 unsigned i;
243
244 pj_assert(pool && dst && src);
245
246 pj_memcpy(dst, src, sizeof(pjmedia_rtcp_fb_setting));
247 for (i = 0; i < src->cap_count; ++i) {
248 pjmedia_rtcp_fb_cap_dup(pool, &dst->caps[i], &src->caps[i]);
249 }
250 }
251
252
253 /*
254 * Duplicate RTCP Feedback info.
255 */
pjmedia_rtcp_fb_info_dup(pj_pool_t * pool,pjmedia_rtcp_fb_info * dst,const pjmedia_rtcp_fb_info * src)256 PJ_DEF(void) pjmedia_rtcp_fb_info_dup( pj_pool_t *pool,
257 pjmedia_rtcp_fb_info *dst,
258 const pjmedia_rtcp_fb_info *src)
259 {
260 unsigned i;
261
262 pj_assert(pool && dst && src);
263
264 pj_memcpy(dst, src, sizeof(pjmedia_rtcp_fb_info));
265 for (i = 0; i < src->cap_count; ++i) {
266 pjmedia_rtcp_fb_cap_dup(pool, &dst->caps[i], &src->caps[i]);
267 }
268 }
269
270
271
272 struct rtcp_fb_type_name_t
273 {
274 pjmedia_rtcp_fb_type type;
275 const char *name;
276 } rtcp_fb_type_name[] =
277 {
278 {PJMEDIA_RTCP_FB_ACK, "ack"},
279 {PJMEDIA_RTCP_FB_NACK, "nack"},
280 {PJMEDIA_RTCP_FB_TRR_INT, "trr-int"}
281 };
282
283 /* Generate a=rtcp-fb based on the specified PT & RTCP-FB capability */
add_sdp_attr_rtcp_fb(pj_pool_t * pool,const char * pt,const pjmedia_rtcp_fb_cap * cap,pjmedia_sdp_media * m)284 static pj_status_t add_sdp_attr_rtcp_fb( pj_pool_t *pool,
285 const char *pt,
286 const pjmedia_rtcp_fb_cap *cap,
287 pjmedia_sdp_media *m)
288 {
289 pjmedia_sdp_attr *a;
290 char tmp[128];
291 pj_str_t val;
292 pj_str_t type_name = {0};
293
294 if (cap->type < PJMEDIA_RTCP_FB_OTHER)
295 pj_cstr(&type_name, rtcp_fb_type_name[cap->type].name);
296 else if (cap->type == PJMEDIA_RTCP_FB_OTHER)
297 type_name = cap->type_name;
298
299 if (type_name.slen == 0)
300 return PJ_EINVAL;
301
302 /* Generate RTCP FB param */
303 if (cap->param.slen) {
304 pj_ansi_snprintf(tmp, sizeof(tmp), "%s %.*s %.*s", pt,
305 (int)type_name.slen, type_name.ptr,
306 (int)cap->param.slen, cap->param.ptr);
307 } else {
308 pj_ansi_snprintf(tmp, sizeof(tmp), "%s %.*s", pt,
309 (int)type_name.slen, type_name.ptr);
310 }
311 pj_strset2(&val, tmp);
312
313 /* Generate and add SDP attribute a=rtcp-fb */
314 a = pjmedia_sdp_attr_create(pool, "rtcp-fb", &val);
315 m->attr[m->attr_count++] = a;
316
317 return PJ_SUCCESS;
318 }
319
320 /* SDP codec info (ID and PT) */
321 typedef struct sdp_codec_info_t
322 {
323 char id[32];
324 unsigned pt;
325 } sdp_codec_info_t;
326
327
328 /* Populate codec ID/name and PT in SDP */
get_codec_info_from_sdp(pjmedia_endpt * endpt,const pjmedia_sdp_media * m,unsigned * sci_cnt,sdp_codec_info_t sci[])329 static pj_status_t get_codec_info_from_sdp(pjmedia_endpt *endpt,
330 const pjmedia_sdp_media *m,
331 unsigned *sci_cnt,
332 sdp_codec_info_t sci[])
333 {
334 pjmedia_codec_mgr *codec_mgr;
335 unsigned j, cnt = 0;
336 pjmedia_type type = PJMEDIA_TYPE_UNKNOWN;
337 pj_status_t status;
338
339 type = pjmedia_get_type(&m->desc.media);
340 if (type != PJMEDIA_TYPE_AUDIO && type != PJMEDIA_TYPE_VIDEO)
341 return PJMEDIA_EUNSUPMEDIATYPE;
342
343 codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
344 for (j = 0; j < m->desc.fmt_count && cnt < *sci_cnt; ++j) {
345 unsigned pt = 0;
346 pt = pj_strtoul(&m->desc.fmt[j]);
347 if (pt < 96) {
348 if (type == PJMEDIA_TYPE_AUDIO) {
349 const pjmedia_codec_info *ci;
350 status = pjmedia_codec_mgr_get_codec_info(codec_mgr, pt, &ci);
351 if (status != PJ_SUCCESS)
352 continue;
353
354 pjmedia_codec_info_to_id(ci, sci[cnt].id, sizeof(sci[0].id));
355 } else {
356 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
357 const pjmedia_vid_codec_info *ci;
358 status = pjmedia_vid_codec_mgr_get_codec_info(NULL, pt, &ci);
359 if (status != PJ_SUCCESS)
360 continue;
361
362 pjmedia_vid_codec_info_to_id(ci, sci[cnt].id,
363 sizeof(sci[0].id));
364 #else
365 continue;
366 #endif
367 }
368 } else {
369 pjmedia_sdp_attr *a;
370 pjmedia_sdp_rtpmap r;
371 a = pjmedia_sdp_media_find_attr2(m, "rtpmap",
372 &m->desc.fmt[j]);
373 if (a == NULL)
374 continue;
375 status = pjmedia_sdp_attr_get_rtpmap(a, &r);
376 if (status != PJ_SUCCESS)
377 continue;
378
379 if (type == PJMEDIA_TYPE_AUDIO) {
380 /* Audio codec id format: "name/clock-rate/channel-count" */
381 if (r.param.slen) {
382 pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
383 "%.*s/%d/%.*s",
384 (int)r.enc_name.slen, r.enc_name.ptr,
385 r.clock_rate,
386 (int)r.param.slen, r.param.ptr);
387 } else {
388 pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
389 "%.*s/%d/1",
390 (int)r.enc_name.slen, r.enc_name.ptr,
391 r.clock_rate);
392 }
393 } else {
394 /* Video codec id format: "name/payload-type" */
395 pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
396 "%.*s/%d",
397 (int)r.enc_name.slen, r.enc_name.ptr, pt);
398 }
399 }
400 sci[cnt++].pt = pt;
401 }
402 *sci_cnt = cnt;
403
404 return PJ_SUCCESS;
405 }
406
407 /*
408 * Encode RTCP Feedback specific information into the SDP according to
409 * the provided RTCP Feedback setting.
410 */
pjmedia_rtcp_fb_encode_sdp(pj_pool_t * pool,pjmedia_endpt * endpt,const pjmedia_rtcp_fb_setting * opt,pjmedia_sdp_session * sdp_local,unsigned med_idx,const pjmedia_sdp_session * sdp_remote)411 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_encode_sdp(
412 pj_pool_t *pool,
413 pjmedia_endpt *endpt,
414 const pjmedia_rtcp_fb_setting *opt,
415 pjmedia_sdp_session *sdp_local,
416 unsigned med_idx,
417 const pjmedia_sdp_session *sdp_remote)
418 {
419 pjmedia_sdp_media *m = sdp_local->media[med_idx];
420 unsigned i;
421 unsigned sci_cnt = 0;
422 sdp_codec_info_t sci[PJMEDIA_MAX_SDP_FMT];
423 pj_status_t status;
424
425 PJ_UNUSED_ARG(sdp_remote);
426
427 PJ_ASSERT_RETURN(pool && endpt && opt && sdp_local, PJ_EINVAL);
428 PJ_ASSERT_RETURN(med_idx < sdp_local->media_count, PJ_EINVAL);
429
430 /* Add RTCP Feedback profile (AVPF), if configured to */
431 if (!opt->dont_use_avpf) {
432 unsigned proto = pjmedia_sdp_transport_get_proto(&m->desc.transport);
433 if (!PJMEDIA_TP_PROTO_HAS_FLAG(proto, PJMEDIA_TP_PROFILE_RTCP_FB)) {
434 pj_str_t new_tp;
435 pj_strdup_with_null(pool, &new_tp, &m->desc.transport);
436 new_tp.ptr[new_tp.slen++] = 'F';
437 m->desc.transport = new_tp;
438 }
439 }
440
441 /* Add RTCP Feedback capability to SDP */
442 for (i = 0; i < opt->cap_count; ++i) {
443 unsigned j;
444
445 /* All codecs */
446 if (pj_strcmp2(&opt->caps[i].codec_id, "*") == 0) {
447 status = add_sdp_attr_rtcp_fb(pool, "*", &opt->caps[i], m);
448 if (status != PJ_SUCCESS) {
449 PJ_PERROR(3, (THIS_FILE, status,
450 "Failed generating SDP a=rtcp-fb:*"));
451 }
452 continue;
453 }
454
455 /* Specific codec */
456 if (sci_cnt == 0) {
457 sci_cnt = PJ_ARRAY_SIZE(sci);
458 status = get_codec_info_from_sdp(endpt, m, &sci_cnt, sci);
459 if (status != PJ_SUCCESS) {
460 PJ_PERROR(3, (THIS_FILE, status,
461 "Failed populating codec info from SDP"));
462 return status;
463 }
464 }
465
466 for (j = 0; j < sci_cnt; ++j) {
467 if (pj_strnicmp2(&opt->caps[i].codec_id, sci[j].id,
468 opt->caps[i].codec_id.slen) == 0)
469 {
470 char tmp[4];
471 snprintf(tmp, sizeof(tmp), "%d", sci[j].pt);
472 status = add_sdp_attr_rtcp_fb(pool, tmp, &opt->caps[i], m);
473 if (status != PJ_SUCCESS) {
474 PJ_PERROR(3, (THIS_FILE, status,
475 "Failed generating SDP a=rtcp-fb:%d (%s)",
476 sci[j].pt, opt->caps[i].codec_id.ptr));
477 }
478 break;
479 }
480 }
481 if (j == sci_cnt) {
482 /* Codec ID not found in SDP (perhaps better ignore this error
483 * as app may configure audio and video in single setting).
484 */
485 PJ_PERROR(6, (THIS_FILE, PJ_ENOTFOUND,
486 "Failed generating SDP a=rtcp-fb for %s",
487 opt->caps[i].codec_id.ptr));
488 }
489 }
490
491 return PJ_SUCCESS;
492 }
493
494
495 /*
496 * Decode RTCP Feedback specific information from SDP media.
497 */
pjmedia_rtcp_fb_decode_sdp(pj_pool_t * pool,pjmedia_endpt * endpt,const void * opt,const pjmedia_sdp_session * sdp,unsigned med_idx,pjmedia_rtcp_fb_info * info)498 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_decode_sdp(
499 pj_pool_t *pool,
500 pjmedia_endpt *endpt,
501 const void *opt,
502 const pjmedia_sdp_session *sdp,
503 unsigned med_idx,
504 pjmedia_rtcp_fb_info *info)
505 {
506 return pjmedia_rtcp_fb_decode_sdp2(pool, endpt, opt, sdp, med_idx, -1,
507 info);
508 }
509
510 /*
511 * Decode RTCP Feedback specific information from SDP media.
512 */
pjmedia_rtcp_fb_decode_sdp2(pj_pool_t * pool,pjmedia_endpt * endpt,const void * opt,const pjmedia_sdp_session * sdp,unsigned med_idx,int pt,pjmedia_rtcp_fb_info * info)513 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_decode_sdp2(
514 pj_pool_t *pool,
515 pjmedia_endpt *endpt,
516 const void *opt,
517 const pjmedia_sdp_session *sdp,
518 unsigned med_idx,
519 int pt,
520 pjmedia_rtcp_fb_info *info)
521 {
522 unsigned sci_cnt = PJMEDIA_MAX_SDP_FMT;
523 sdp_codec_info_t sci[PJMEDIA_MAX_SDP_FMT];
524 const pjmedia_sdp_media *m;
525 pj_status_t status;
526 unsigned i;
527
528 PJ_UNUSED_ARG(opt);
529
530 PJ_ASSERT_RETURN(pool && endpt && opt==NULL && sdp, PJ_EINVAL);
531 PJ_ASSERT_RETURN(med_idx < sdp->media_count, PJ_EINVAL);
532 PJ_ASSERT_RETURN(pt <= 127, PJ_EINVAL);
533
534 m = sdp->media[med_idx];
535 status = get_codec_info_from_sdp(endpt, m, &sci_cnt, sci);
536 if (status != PJ_SUCCESS)
537 return status;
538
539 pj_bzero(info, sizeof(*info));
540
541 /* Iterate all SDP attribute a=rtcp-fb in the SDP media */
542 for (i = 0; i < m->attr_count; ++i) {
543 const pjmedia_sdp_attr *a = m->attr[i];
544 pj_str_t token;
545 pj_ssize_t tok_idx;
546 unsigned j;
547 const char *codec_id = NULL;
548 pj_str_t type_name = {0};
549 pjmedia_rtcp_fb_type type = PJMEDIA_RTCP_FB_OTHER;
550
551 /* Skip non a=rtcp-fb */
552 if (pj_strcmp2(&a->name, "rtcp-fb") != 0)
553 continue;
554
555 /* Get PT */
556 tok_idx = pj_strtok2(&a->value, " \t", &token, 0);
557 if (tok_idx == a->value.slen)
558 continue;
559
560 if (pj_strcmp2(&token, "*") == 0) {
561 /* All codecs */
562 codec_id = "*";
563 } else {
564 /* Specific PT/codec */
565 unsigned pt_ = (unsigned) pj_strtoul2(&token, NULL, 10);
566 for (j = 0; j < sci_cnt; ++j) {
567 /* Check if payload type is valid and requested */
568 if (pt_ == sci[j].pt && (pt < 0 || pt == (int)pt_)) {
569 codec_id = sci[j].id;
570 break;
571 }
572 }
573 }
574
575 /* Skip this a=rtcp-fb if PT is not recognized or not requested */
576 if (!codec_id)
577 continue;
578
579 /* Get RTCP-FB type */
580 tok_idx = pj_strtok2(&a->value, " \t", &token, tok_idx + token.slen);
581 if (tok_idx == a->value.slen)
582 continue;
583
584 for (j = 0; j < PJ_ARRAY_SIZE(rtcp_fb_type_name); ++j) {
585 if (pj_strcmp2(&token, rtcp_fb_type_name[j].name) == 0) {
586 type = rtcp_fb_type_name[j].type;
587 break;
588 }
589 }
590 if (type == PJMEDIA_RTCP_FB_OTHER)
591 type_name = token;
592
593 /* Got all the mandatory fields, let's initialize RTCP-FB cap */
594 pj_strdup2(pool, &info->caps[info->cap_count].codec_id, codec_id);
595 info->caps[info->cap_count].type = type;
596 if (type == PJMEDIA_RTCP_FB_OTHER)
597 pj_strdup(pool, &info->caps[info->cap_count].type_name, &type_name);
598
599 /* Get RTCP-FB param */
600 tok_idx = pj_strtok2(&a->value, " \t", &token, tok_idx + token.slen);
601 if (tok_idx != a->value.slen)
602 pj_strdup(pool, &info->caps[info->cap_count].param, &token);
603
604 /* Next */
605 if (++info->cap_count == PJMEDIA_RTCP_FB_MAX_CAP)
606 break;
607 }
608
609 return PJ_SUCCESS;
610 }
611
612
613 /*
614 * Check whether the specified payload contains RTCP feedback generic NACK
615 * message, and parse the payload if it does.
616 */
pjmedia_rtcp_fb_parse_nack(const void * buf,pj_size_t length,unsigned * nack_cnt,pjmedia_rtcp_fb_nack nack[])617 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_nack(
618 const void *buf,
619 pj_size_t length,
620 unsigned *nack_cnt,
621 pjmedia_rtcp_fb_nack nack[])
622 {
623 pjmedia_rtcp_common *hdr = (pjmedia_rtcp_common*) buf;
624 pj_uint8_t *p;
625 unsigned cnt, i;
626
627 PJ_ASSERT_RETURN(buf && nack_cnt && nack, PJ_EINVAL);
628 PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_common), PJ_ETOOSMALL);
629
630 /* Generic NACK uses pt==RTCP_RTPFB and FMT==1 */
631 if (hdr->pt != RTCP_RTPFB || hdr->count != 1)
632 return PJ_ENOTFOUND;
633
634 cnt = pj_ntohs((pj_uint16_t)hdr->length) - 2;
635 if (length < (cnt+3)*4)
636 return PJ_ETOOSMALL;
637
638 *nack_cnt = PJ_MIN(*nack_cnt, cnt);
639
640 p = (pj_uint8_t*)hdr + sizeof(*hdr);
641 for (i = 0; i < *nack_cnt; ++i) {
642 pj_uint16_t val;
643
644 pj_memcpy(&val, p, 2);
645 nack[i].pid = pj_ntohs(val);
646 pj_memcpy(&val, p+2, 2);
647 nack[i].blp = pj_ntohs(val);
648 p += 4;
649 }
650
651 return PJ_SUCCESS;
652 }
653
654
655 /*
656 * Check whether the specified payload contains RTCP feedback Picture Loss
657 * Indication (PLI) message.
658 */
pjmedia_rtcp_fb_parse_pli(const void * buf,pj_size_t length)659 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_pli(
660 const void *buf,
661 pj_size_t length)
662 {
663 pjmedia_rtcp_common *hdr = (pjmedia_rtcp_common*) buf;
664
665 PJ_ASSERT_RETURN(buf, PJ_EINVAL);
666 PJ_ASSERT_RETURN(length >= 12, PJ_ETOOSMALL);
667
668 /* PLI uses pt==RTCP_PSFB and FMT==1 */
669 if (hdr->pt != RTCP_PSFB || hdr->count != 1)
670 return PJ_ENOTFOUND;
671
672 return PJ_SUCCESS;
673 }
674
675
676 /*
677 * Check whether the specified payload contains RTCP feedback Slice Loss
678 * Indication (SLI) message, and parse the payload if it does.
679 */
pjmedia_rtcp_fb_parse_sli(const void * buf,pj_size_t length,unsigned * sli_cnt,pjmedia_rtcp_fb_sli sli[])680 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_sli(
681 const void *buf,
682 pj_size_t length,
683 unsigned *sli_cnt,
684 pjmedia_rtcp_fb_sli sli[])
685 {
686 pjmedia_rtcp_common *hdr = (pjmedia_rtcp_common*) buf;
687 pj_uint8_t *p;
688 unsigned cnt, i;
689
690 PJ_ASSERT_RETURN(buf && sli_cnt && sli, PJ_EINVAL);
691 PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_common), PJ_ETOOSMALL);
692
693 /* PLI uses pt==RTCP_PSFB and FMT==2 */
694 if (hdr->pt != RTCP_PSFB || hdr->count != 2)
695 return PJ_ENOTFOUND;
696
697 cnt = pj_ntohs((pj_uint16_t)hdr->length) - 2;
698 if (length < (cnt+3)*4)
699 return PJ_ETOOSMALL;
700
701 *sli_cnt = PJ_MIN(*sli_cnt, cnt);
702
703 p = (pj_uint8_t*)hdr + sizeof(*hdr);
704 for (i = 0; i < *sli_cnt; ++i) {
705 /* 'first' takes 13 bit */
706 sli[i].first = (p[0] << 5) + ((p[1] & 0xF8) >> 3);
707 /* 'number' takes 13 bit */
708 sli[i].number = ((p[1] & 0x07) << 10) +
709 (p[2] << 2) +
710 ((p[3] & 0xC0) >> 6);
711 /* 'pict_id' takes 6 bit */
712 sli[i].pict_id = (p[3] & 0x3F);
713 p += 4;
714 }
715
716 return PJ_SUCCESS;
717 }
718
719
720 /*
721 * Check whether the specified payload contains RTCP feedback Reference
722 * Picture Selection Indication (RPSI) message, and parse the payload
723 * if it does.
724 */
pjmedia_rtcp_fb_parse_rpsi(const void * buf,pj_size_t length,pjmedia_rtcp_fb_rpsi * rpsi)725 PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_rpsi(
726 const void *buf,
727 pj_size_t length,
728 pjmedia_rtcp_fb_rpsi *rpsi)
729 {
730 pjmedia_rtcp_common *hdr = (pjmedia_rtcp_common*) buf;
731 pj_uint8_t *p;
732 pj_uint8_t padlen;
733 pj_size_t rpsi_len;
734
735 PJ_ASSERT_RETURN(buf && rpsi, PJ_EINVAL);
736 PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_common), PJ_ETOOSMALL);
737
738 /* RPSI uses pt==RTCP_PSFB and FMT==3 */
739 if (hdr->pt != RTCP_PSFB || hdr->count != 3)
740 return PJ_ENOTFOUND;
741
742 rpsi_len = (pj_ntohs((pj_uint16_t)hdr->length)-2) * 4;
743 if (length < rpsi_len + 12)
744 return PJ_ETOOSMALL;
745
746 p = (pj_uint8_t*)hdr + sizeof(*hdr);
747 padlen = *p++;
748 rpsi->pt = (*p++ & 0x7F);
749 rpsi->rpsi_bit_len = rpsi_len*8 - 16 - padlen;
750 pj_strset(&rpsi->rpsi, (char*)p, (rpsi->rpsi_bit_len + 7)/8);
751
752 return PJ_SUCCESS;
753 }
754