| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2024 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project | |
| * | |
| * GPAC 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, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC 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 library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/internal/ietf_dev.h> | |
| #ifndef GPAC_DISABLE_STREAMING | |
| #include <gpac/bitstream.h> | |
| GF_EXPORT | |
| u32 gf_rtp_read_rtcp(GF_RTPChannel *ch, u8 *buffer, u32 buffer_size) | |
| { | |
| GF_Err e; | |
| u32 res; | |
| //only if the socket exist (otherwise RTSP interleaved channel) | |
| if (!ch || !ch->rtcp) return 0; | |
| if (ch->no_select) { | |
| e = gf_sk_receive_no_select(ch->rtcp, buffer, buffer_size, &res); | |
| } else { | |
| e = gf_sk_receive(ch->rtcp, buffer, buffer_size, &res); | |
| } | |
| if (e) return 0; | |
| return res; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_rtp_decode_rtcp(GF_RTPChannel *ch, u8 *pck, u32 pck_size, Bool *has_sr) | |
| { | |
| GF_RTCPHeader rtcp_hdr; | |
| char sdes_buffer[300]; | |
| u32 i, sender_ssrc, cur_ssrc, val, sdes_type, sdes_len, res, first; | |
| GF_Err e = GF_OK; | |
| if (has_sr) *has_sr = GF_FALSE; | |
| //bad RTCP packet | |
| if (pck_size < 4 ) return GF_NON_COMPLIANT_BITSTREAM; | |
| gf_bs_reassign_buffer(ch->bs_r, pck, pck_size); | |
| first = 1; | |
| while (pck_size) { | |
| //global header | |
| rtcp_hdr.Version = gf_bs_read_int(ch->bs_r, 2); | |
| if (rtcp_hdr.Version != 2 ) { | |
| return GF_NOT_SUPPORTED; | |
| } | |
| rtcp_hdr.Padding = gf_bs_read_int(ch->bs_r, 1); | |
| rtcp_hdr.Count = gf_bs_read_int(ch->bs_r, 5); | |
| rtcp_hdr.PayloadType = gf_bs_read_u8(ch->bs_r); | |
| rtcp_hdr.Length = 1 + gf_bs_read_u16(ch->bs_r); | |
| //check pck size | |
| if (pck_size < (u32) rtcp_hdr.Length * 4) { | |
| //we return OK | |
| return GF_CORRUPTED_DATA; | |
| } | |
| //subtract this RTCP pck size | |
| pck_size -= rtcp_hdr.Length * 4; | |
| /*we read the RTCP header*/ | |
| rtcp_hdr.Length -= 1; | |
| //in all RTCP Compounds (>1 pck), the first RTCP report SHALL be SR or RR without padding | |
| if (first) { | |
| if ( ( (rtcp_hdr.PayloadType!=200) && (rtcp_hdr.PayloadType!=201) ) | |
| || rtcp_hdr.Padding | |
| ) { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTCP] Corrupted RTCP packet: payload type %d (200 or 201 expected) - Padding %d (0 expected)\n", rtcp_hdr.PayloadType, rtcp_hdr.Padding)); | |
| return GF_CORRUPTED_DATA; | |
| } | |
| first = 0; | |
| } | |
| //specific extensions | |
| switch (rtcp_hdr.PayloadType) { | |
| //Sender report - we assume there's only one sender | |
| case 200: | |
| /*sender ssrc*/ | |
| sender_ssrc = gf_bs_read_u32(ch->bs_r); | |
| rtcp_hdr.Length -= 1; | |
| /*not for us...*/ | |
| if (ch->SenderSSRC && (ch->SenderSSRC != sender_ssrc)) break; | |
| if (ch->first_SR) { | |
| gf_rtp_get_next_report_time(ch); | |
| ch->SenderSSRC = sender_ssrc; | |
| } | |
| ch->last_report_time = gf_rtp_get_report_time(); | |
| ch->last_SR_NTP_sec = gf_bs_read_u32(ch->bs_r); | |
| ch->last_SR_NTP_frac = gf_bs_read_u32(ch->bs_r); | |
| ch->last_SR_rtp_time = gf_bs_read_u32(ch->bs_r); | |
| /*nb_pck = */gf_bs_read_u32(ch->bs_r); | |
| /*nb_bytes =*/gf_bs_read_u32(ch->bs_r); | |
| rtcp_hdr.Length -= 5; | |
| if (has_sr) *has_sr = GF_TRUE; | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_RTP, GF_LOG_INFO)) { | |
| #ifndef _WIN32_WCE | |
| time_t gtime = ch->last_SR_NTP_sec - GF_NTP_SEC_1900_TO_1970; | |
| const char *ascTime = asctime(gf_gmtime(>ime)); | |
| #else | |
| const char *ascTime = "Not Available"; | |
| #endif | |
| u64 clock_us = ch->last_SR_NTP_frac; | |
| clock_us *= 1000000; | |
| clock_us /= 0xFFFFFFFF; | |
| GF_LOG(ch->first_SR ? GF_LOG_INFO : GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] RTCP %sSR: SSRC %d - RTP Time %u - Nb Pck %d - Nb Bytes %d - NTP %s %u us\n", | |
| ch->first_SR ? "Initial " : "", | |
| ch->SenderSSRC, | |
| ch->last_SR_rtp_time, | |
| ch->total_pck, | |
| ch->total_bytes, | |
| ascTime, | |
| clock_us | |
| )); | |
| } | |
| #endif | |
| ch->first_SR = 0; | |
| //common encoding for SR and RR, for now we don't process reports on SR | |
| break; | |
| case 201: | |
| //sender ssrc | |
| /*sender_ssrc = */gf_bs_read_u32(ch->bs_r); | |
| rtcp_hdr.Length -= 1; | |
| ch->nb_rctp_rr=0; | |
| //process all reports | |
| for (i=0; i<rtcp_hdr.Count; i++) { | |
| if (i>=MAX_RTCP_RR) break; | |
| GF_RTCP_Report *rr = &ch->rtcp_rr[i]; | |
| ch->nb_rctp_rr++; | |
| //ssrc | |
| rr->ssrc = gf_bs_read_u32(ch->bs_r); | |
| //frac lost | |
| rr->frac_lost = gf_bs_read_u8(ch->bs_r); | |
| //cumulative lost | |
| rr->total_loss = gf_bs_read_u24(ch->bs_r); | |
| //extended seq num | |
| rr->last_rtp_sn = gf_bs_read_u32(ch->bs_r); | |
| //jitter | |
| rr->jitter = gf_bs_read_u32(ch->bs_r); | |
| //LSR | |
| rr->last_sr = gf_bs_read_u32(ch->bs_r); | |
| //DLSR | |
| rr->delay_last_sr = gf_bs_read_u32(ch->bs_r); | |
| rtcp_hdr.Length -= 6; | |
| } | |
| //remaining bytes? we skip (this includes padding and crypto - not supported) | |
| break; | |
| //SDES | |
| case 202: | |
| for (i=0; i<rtcp_hdr.Count; i++) { | |
| /*cur_ssrc = */gf_bs_read_u32(ch->bs_r); | |
| rtcp_hdr.Length -= 1; | |
| val = 0; | |
| while (1) { | |
| sdes_type = gf_bs_read_u8(ch->bs_r); | |
| val += 1; | |
| if (!sdes_type) break; | |
| sdes_len = gf_bs_read_u8(ch->bs_r); | |
| val += 1; | |
| gf_bs_read_data(ch->bs_r, sdes_buffer, sdes_len); | |
| sdes_buffer[sdes_len] = 0; | |
| val += sdes_len; | |
| } | |
| //re-align on 32bit | |
| res = val%4; | |
| if (res) { | |
| gf_bs_skip_bytes(ch->bs_r, (4-res)); | |
| val = val/4 + 1; | |
| } else { | |
| val = val/4; | |
| } | |
| rtcp_hdr.Length -= val; | |
| } | |
| break; | |
| //BYE packet - close the channel - we work with 1 SSRC only */ | |
| case 203: | |
| for (i=0; i<rtcp_hdr.Count; i++) { | |
| cur_ssrc = gf_bs_read_u32(ch->bs_r); | |
| rtcp_hdr.Length -= 1; | |
| if (ch->SenderSSRC == cur_ssrc) { | |
| e = GF_EOS; | |
| break; | |
| } | |
| } | |
| //extra info - skip it | |
| while (rtcp_hdr.Length) { | |
| gf_bs_read_u32(ch->bs_r); | |
| rtcp_hdr.Length -= 1; | |
| } | |
| break; | |
| /* | |
| //APP packet | |
| case 204: | |
| //sender ssrc | |
| sender_ssrc = gf_bs_read_u32(bs); | |
| //ASCI 4 char type | |
| gf_bs_read_u8(bs); | |
| gf_bs_read_u8(bs); | |
| gf_bs_read_u8(bs); | |
| gf_bs_read_u8(bs); | |
| rtcp_hdr.Length -= 2; | |
| //till endd of pck | |
| gf_bs_read_data(bs, sdes_buffer, rtcp_hdr.Length*4); | |
| rtcp_hdr.Length = 0; | |
| break; | |
| */ | |
| default: | |
| //read all till end | |
| gf_bs_read_data(ch->bs_r, sdes_buffer, MIN(rtcp_hdr.Length*4, sizeof(sdes_buffer))); | |
| rtcp_hdr.Length = 0; | |
| break; | |
| } | |
| //WE SHALL CONSUME EVERYTHING otherwise the packet is bad | |
| if (rtcp_hdr.Length) { | |
| return GF_CORRUPTED_DATA; | |
| } | |
| } | |
| return e; | |
| } | |
| static u32 RTCP_FormatReport(GF_RTPChannel *ch, GF_BitStream *bs, u32 NTP_Time) | |
| { | |
| u32 length, is_sr, sec, frac, expected, val, size; | |
| s32 extended, expect_diff, loss_diff; | |
| Double f; | |
| is_sr = ch->pck_sent_since_last_sr ? 1 : 0; | |
| if (ch->forced_ntp_sec) { | |
| sec = ch->forced_ntp_sec; | |
| frac = ch->forced_ntp_frac; | |
| is_sr = 1; | |
| } else { | |
| gf_net_get_ntp(&sec, &frac); | |
| } | |
| //common header | |
| //version | |
| gf_bs_write_int(bs, 2, 2); | |
| //padding - reports are aligned | |
| gf_bs_write_int(bs, 0, 1); | |
| //count - only one for now in RR, 0 in sender mode | |
| gf_bs_write_int(bs, !is_sr, 5); | |
| //if we have sent stuff send an SR, otherwise an RR. We need to determine whether | |
| //we are active or not | |
| //type | |
| gf_bs_write_u8(bs, is_sr ? 200 : 201); | |
| //length = (num of 32bit words in full pck) - 1 | |
| //we're updating only one ssrc for now in RR and none in SR | |
| length = is_sr ? 6 : (1 + 6 * 1); | |
| gf_bs_write_u16(bs, length); | |
| //sender SSRC | |
| gf_bs_write_u32(bs, ch->SSRC); | |
| size = 8; | |
| //SenderReport part | |
| if (is_sr) { | |
| //sender time | |
| gf_bs_write_u32(bs, sec); | |
| gf_bs_write_u32(bs, frac); | |
| //RTP time at this time | |
| f = 1000 * (sec - ch->last_pck_ntp_sec); | |
| f += ((frac - ch->last_pck_ntp_frac) >> 4) / 0x10000; | |
| f /= 1000; | |
| f *= ch->TimeScale; | |
| val = (u32) f + ch->last_pck_ts; | |
| gf_bs_write_u32(bs, val); | |
| //num pck sent | |
| gf_bs_write_u32(bs, ch->num_pck_sent); | |
| //num payload bytes sent | |
| gf_bs_write_u32(bs, ch->num_payload_bytes); | |
| size += 20; | |
| //nota: as we only support single-way channels we are done for SR... | |
| return size; | |
| } | |
| //loop through all our sources (1) and send information... | |
| gf_bs_write_u32(bs, ch->SenderSSRC); | |
| //Fraction lost and cumulative lost | |
| extended = ( (ch->num_sn_loops << 16) | ch->last_pck_sn); | |
| expected = extended - ch->rtp_first_SN; | |
| expect_diff = expected - ch->tot_num_pck_expected; | |
| loss_diff = expect_diff - ch->last_num_pck_rcv; | |
| if (!expect_diff || (loss_diff <= 0)) loss_diff = 0; | |
| else loss_diff = (loss_diff<<8) / expect_diff; | |
| if (ch->force_loss_rate) { | |
| loss_diff = ch->force_loss_rate-1; | |
| ch->force_loss_rate = 0; | |
| } | |
| gf_bs_write_u8(bs, loss_diff); | |
| //update and write cumulative loss | |
| ch->tot_num_pck_rcv += ch->last_num_pck_rcv; | |
| ch->tot_num_pck_expected = expected; | |
| gf_bs_write_u24(bs, (expected - ch->tot_num_pck_rcv)); | |
| //Extend sequence number | |
| gf_bs_write_u32(bs, extended); | |
| //Jitter | |
| //RTP specs annexe A.8 | |
| gf_bs_write_u32(bs, ( ch->Jitter >> 4)); | |
| //LSR | |
| if (ch->last_SR_NTP_sec) { | |
| val = ( ((ch->last_SR_NTP_sec & 0x0000ffff) << 16) | ((ch->last_SR_NTP_frac & 0xffff0000) >> 16)); | |
| } else { | |
| val = 0; | |
| } | |
| gf_bs_write_u32(bs, val); | |
| // DLSR | |
| gf_bs_write_u32(bs, (NTP_Time - ch->last_report_time)); | |
| #ifndef GPAC_DISABLE_LOG | |
| if (gf_log_tool_level_on(GF_LOG_RTP, GF_LOG_DEBUG)) { | |
| #ifndef _WIN32_WCE | |
| time_t gtime = ch->last_SR_NTP_sec - GF_NTP_SEC_1900_TO_1970; | |
| const char *ascTime = asctime(gf_gmtime(>ime)); | |
| #else | |
| const char *ascTime = "Not Available"; | |
| #endif | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] RTCP-RR: SSRC %d Jitter %d extended %d expect_diff %d loss_diff %d time %s\n", | |
| ch->SSRC, | |
| ch->Jitter >> 4, | |
| extended, | |
| expect_diff, | |
| loss_diff, | |
| ascTime | |
| )); | |
| } | |
| #endif | |
| size += 24; | |
| return size; | |
| } | |
| static u32 RTCP_FormatSDES(GF_RTPChannel *ch, GF_BitStream *bs) | |
| { | |
| u32 length, padd; | |
| //we start with header and SSRC: 2x32 bits word | |
| length = 8; | |
| //ten only send one CName item is type (1byte) , len (1byte), data (len byte) + NULL marker (0 on 1 byte) at the end of the item list | |
| length += 2 + (u32) strlen(ch->CName) + 1; | |
| //we padd the end of the content to 32-bit boundary, and set length to the number of 32 bit words | |
| padd = length % 4; | |
| if (padd*4 != 0) { | |
| //padding octets | |
| padd = 4 - padd; | |
| length = length/4 + 1; | |
| } else { | |
| padd = 0; | |
| length = length/4; | |
| } | |
| //common part as usual | |
| gf_bs_write_int(bs, 2, 2); | |
| //notify padding? according to RFC1889 "In a compound RTCP packet, padding should | |
| //only be required on the last individual packet because the compound packet is | |
| //encrypted as a whole" -> we write it without notifying it (this is a bit messy in | |
| //the spec IMO) | |
| gf_bs_write_int(bs, 0, 1); | |
| //report count is one | |
| gf_bs_write_int(bs, 1, 5); | |
| //SDES pck type | |
| gf_bs_write_u8(bs, 202); | |
| //write length minus one | |
| gf_bs_write_u16(bs, length - 1); | |
| //SSRC | |
| gf_bs_write_u32(bs, ch->SSRC); | |
| //CNAME type | |
| gf_bs_write_u8(bs, 1); | |
| //length and cname | |
| gf_bs_write_u8(bs, (u32) strlen(ch->CName)); | |
| gf_bs_write_data(bs, ch->CName, (u32) strlen(ch->CName)); | |
| gf_bs_write_u8(bs, 0); | |
| //32-align field with 0 | |
| gf_bs_write_int(bs, 0, 8*padd); | |
| return (length + 1)*4; | |
| } | |
| static u32 RTCP_FormatBYE(GF_RTPChannel *ch, GF_BitStream *bs) | |
| { | |
| //version | |
| gf_bs_write_int(bs, 2, 2); | |
| //no padding | |
| gf_bs_write_int(bs, 0, 1); | |
| //count - only one for now | |
| gf_bs_write_int(bs, 1, 5); | |
| //type=BYE | |
| gf_bs_write_u8(bs, 203); | |
| //length = (num of 32bit words in full pck) - 1 | |
| gf_bs_write_u16(bs, 1); | |
| //sender SSRC | |
| gf_bs_write_u32(bs, ch->SSRC); | |
| return 8; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_rtp_send_bye(GF_RTPChannel *ch) | |
| { | |
| GF_BitStream *bs; | |
| u32 report_size; | |
| u8 *report_buf; | |
| GF_Err e = GF_OK; | |
| bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| /*k were received/sent send the RR/SR - note we don't wait for next Repor and force its emission now*/ | |
| if (ch->last_num_pck_rcv || ch->pck_sent_since_last_sr) { | |
| RTCP_FormatReport(ch, bs, gf_rtp_get_report_time()); | |
| } | |
| //always send SDES (CNAME shall be sent at each RTCP) | |
| RTCP_FormatSDES(ch, bs); | |
| //send BYE | |
| RTCP_FormatBYE(ch, bs); | |
| report_buf = NULL; | |
| report_size = 0; | |
| gf_bs_get_content(bs, &report_buf, &report_size); | |
| gf_bs_del(bs); | |
| if (ch->rtcp) { | |
| e = gf_sk_send(ch->rtcp, report_buf, report_size); | |
| } else { | |
| if (ch->send_interleave) | |
| e = ch->send_interleave(ch->interleave_cbk1, ch->interleave_cbk2, GF_TRUE, report_buf, report_size); | |
| else | |
| e = GF_BAD_PARAM; | |
| } | |
| gf_free(report_buf); | |
| return e; | |
| } | |
| GF_EXPORT | |
| void gf_rtp_set_loss_rate(GF_RTPChannel *ch, u32 loss_rate) | |
| { | |
| if (!ch) return; | |
| if (loss_rate>1000) ch->force_loss_rate = 256; | |
| else ch->force_loss_rate = 1 + (loss_rate*255)/1000; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_rtp_send_rtcp_report(GF_RTPChannel *ch) | |
| { | |
| u32 Time, report_size; | |
| GF_BitStream *bs; | |
| u8 *report_buf; | |
| GF_Err e = GF_OK; | |
| /*skip first SR when acting as a receiver*/ | |
| if (!ch->forced_ntp_sec && ch->first_SR) return GF_OK; | |
| Time = gf_rtp_get_report_time(); | |
| if ( Time < ch->next_report_time) return GF_OK; | |
| bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| //pck were received/sent send the RR/SR | |
| if (ch->last_num_pck_rcv || ch->pck_sent_since_last_sr || ch->forced_ntp_sec) { | |
| RTCP_FormatReport(ch, bs, Time); | |
| } | |
| //always send SDES (CNAME shall be sent at each RTCP) | |
| RTCP_FormatSDES(ch, bs); | |
| //get content | |
| report_buf = NULL; | |
| report_size = 0; | |
| gf_bs_get_content(bs, &report_buf, &report_size); | |
| gf_bs_del(bs); | |
| if (ch->rtcp) { | |
| e = gf_sk_send(ch->rtcp, report_buf, report_size); | |
| } else { | |
| if (ch->send_interleave) | |
| e = ch->send_interleave(ch->interleave_cbk1, ch->interleave_cbk2, GF_TRUE, report_buf, report_size); | |
| else | |
| e = GF_BAD_PARAM; | |
| } | |
| ch->rtcp_bytes_sent += report_size; | |
| gf_free(report_buf); | |
| if (!e) { | |
| //Update the channel record if no error - otherwise next RTCP will triger an RR | |
| ch->last_num_pck_rcv = ch->last_num_pck_expected = ch->last_num_pck_loss = 0; | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTCP] SSRC %d: sending RTCP report\n", ch->SSRC)); | |
| } | |
| else { | |
| GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTCP] SSRC %d: error when sending RTCP report\n", ch->SSRC)); | |
| } | |
| gf_rtp_get_next_report_time(ch); | |
| return e; | |
| } | |
| #if 0 //unused | |
| enum | |
| { | |
| GF_RTCP_INFO_NAME = 0, | |
| GF_RTCP_INFO_EMAIL, | |
| GF_RTCP_INFO_PHONE, | |
| GF_RTCP_INFO_LOCATION, | |
| GF_RTCP_INFO_TOOL, | |
| GF_RTCP_INFO_NOTE, | |
| GF_RTCP_INFO_PRIV | |
| }; | |
| #define RTCP_SAFE_FREE(p) if (p) gf_free(p); \ | |
| p = NULL; | |
| GF_Err gf_rtp_set_info_rtcp(GF_RTPChannel *ch, u32 InfoCode, char *info_string) | |
| { | |
| if (!ch) return GF_BAD_PARAM; | |
| switch (InfoCode) { | |
| case GF_RTCP_INFO_NAME: | |
| RTCP_SAFE_FREE(ch->s_name); | |
| if (info_string) ch->s_name = gf_strdup(info_string); | |
| break; | |
| case GF_RTCP_INFO_EMAIL: | |
| RTCP_SAFE_FREE(ch->s_email); | |
| if (info_string) ch->s_email = gf_strdup(info_string); | |
| break; | |
| case GF_RTCP_INFO_PHONE: | |
| RTCP_SAFE_FREE(ch->s_phone); | |
| if (info_string) ch->s_phone = gf_strdup(info_string); | |
| break; | |
| case GF_RTCP_INFO_LOCATION: | |
| RTCP_SAFE_FREE(ch->s_location); | |
| if (info_string) ch->s_location = gf_strdup(info_string); | |
| break; | |
| case GF_RTCP_INFO_TOOL: | |
| RTCP_SAFE_FREE(ch->s_tool); | |
| if (info_string) ch->s_tool = gf_strdup(info_string); | |
| break; | |
| case GF_RTCP_INFO_NOTE: | |
| RTCP_SAFE_FREE(ch->s_note); | |
| if (info_string) ch->s_note = gf_strdup(info_string); | |
| break; | |
| case GF_RTCP_INFO_PRIV: | |
| RTCP_SAFE_FREE(ch->s_priv); | |
| if (info_string) ch->s_name = gf_strdup(info_string); | |
| break; | |
| default: | |
| return GF_BAD_PARAM; | |
| } | |
| return GF_OK; | |
| } | |
| #endif | |
| #endif /*GPAC_DISABLE_STREAMING*/ |