📄 rtp.c
字号:
/* * GPAC - Multimedia Framework C SDK * * Copyright (c) Jean Le Feuvre 2000-2005 * 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>#include <gpac/bitstream.h>#define MAX_RTP_SN 0x10000GF_EXPORTGF_RTPChannel *gf_rtp_new(){ GF_RTPChannel *tmp; GF_SAFEALLOC(tmp, GF_RTPChannel); tmp->first_SR = 1; tmp->SSRC = gf_rand(); return tmp;}GF_EXPORTvoid gf_rtp_del(GF_RTPChannel *ch){ if (!ch) return; if (ch->rtp) gf_sk_del(ch->rtp); if (ch->rtcp) gf_sk_del(ch->rtcp); if (ch->net_info.source) free(ch->net_info.source); if (ch->net_info.destination) free(ch->net_info.destination); if (ch->net_info.Profile) free(ch->net_info.Profile); if (ch->po) gf_rtp_reorderer_del(ch->po); if (ch->send_buffer) free(ch->send_buffer); if (ch->CName) free(ch->CName); if (ch->s_name) free(ch->s_name); if (ch->s_email) free(ch->s_email); if (ch->s_location) free(ch->s_location); if (ch->s_phone) free(ch->s_phone); if (ch->s_tool) free(ch->s_tool); if (ch->s_note) free(ch->s_note); if (ch->s_priv) free(ch->s_priv); free(ch);}GF_EXPORTGF_Err gf_rtp_setup_transport(GF_RTPChannel *ch, GF_RTSPTransport *trans_info, char *remote_address){ if (!ch || !trans_info) return GF_BAD_PARAM; //assert we have at least ONE source ID if (!trans_info->source && !remote_address) return GF_BAD_PARAM; if (ch->net_info.destination) free(ch->net_info.destination); if (ch->net_info.Profile) free(ch->net_info.Profile); if (ch->net_info.source) free(ch->net_info.source); memcpy(&ch->net_info, trans_info, sizeof(GF_RTSPTransport)); if (trans_info->destination) ch->net_info.destination = strdup(trans_info->destination); if (trans_info->Profile) ch->net_info.Profile = strdup(trans_info->Profile); if (trans_info->source) { ch->net_info.source = strdup(trans_info->source); } else if (!ch->net_info.IsUnicast && trans_info->destination) { ch->net_info.source = strdup(trans_info->destination); } else { ch->net_info.source = strdup(remote_address); } if (trans_info->SSRC) ch->SenderSSRC = trans_info->SSRC; //check we REALLY have unicast or multicast if (gf_sk_is_multicast_address(ch->net_info.source) && ch->net_info.IsUnicast) return GF_SERVICE_ERROR; return GF_OK;}GF_EXPORTvoid gf_rtp_reset_buffers(GF_RTPChannel *ch){ if (ch->rtp) gf_sk_reset(ch->rtp); if (ch->rtcp) gf_sk_reset(ch->rtcp); if (ch->po) gf_rtp_reorderer_reset(ch->po); /*also reset ssrc*/ //ch->SenderSSRC = 0; ch->first_SR = 1;}GF_EXPORTvoid gf_rtp_enable_nat_keepalive(GF_RTPChannel *ch, u32 nat_timeout){ if (ch) { ch->nat_keepalive_time_period = nat_timeout; ch->last_nat_keepalive_time = 0; }}GF_EXPORTGF_Err gf_rtp_set_info_rtp(GF_RTPChannel *ch, u32 seq_num, u32 rtp_time, u32 ssrc){ if (!ch) return GF_BAD_PARAM; ch->rtp_time = rtp_time; ch->last_pck_sn = 0; ch->rtp_first_SN = seq_num; ch->num_sn_loops = 0; //reset RTCP ch->ntp_init = 0; ch->first_SR = 1; if (ssrc) ch->SenderSSRC = ssrc; ch->total_pck = ch->total_bytes = ch->last_num_pck_rcv = ch->last_num_pck_expected = ch->last_num_pck_loss = ch->tot_num_pck_rcv = ch->tot_num_pck_expected = ch->rtcp_bytes_sent = 0; return GF_OK;}GF_EXPORTGF_Err gf_rtp_initialize(GF_RTPChannel *ch, u32 UDPBufferSize, Bool IsSource, u32 PathMTU, u32 ReorederingSize, u32 MaxReorderDelay, char *local_interface_ip){ GF_Err e; if (IsSource && !PathMTU) return GF_BAD_PARAM; if (ch->rtp) gf_sk_del(ch->rtp); if (ch->rtcp) gf_sk_del(ch->rtcp); if (ch->po) gf_rtp_reorderer_del(ch->po); ch->CurrentTime = 0; ch->rtp_time = 0; //create sockets for RTP/AVP profile only if (ch->net_info.Profile && ( !stricmp(ch->net_info.Profile, GF_RTSP_PROFILE_RTP_AVP) || !stricmp(ch->net_info.Profile, "RTP/AVP/UDP") || !stricmp(ch->net_info.Profile, "RTP/SAVP") ) ) { //destination MUST be specified for unicast if (IsSource && ch->net_info.IsUnicast && !ch->net_info.destination) return GF_BAD_PARAM; // // RTP // ch->rtp = gf_sk_new(GF_SOCK_TYPE_UDP); if (!ch->rtp) return GF_IP_NETWORK_FAILURE; if (ch->net_info.IsUnicast) { //if client, bind and connect the socket if (!IsSource) { e = gf_sk_bind(ch->rtp, ch->net_info.client_port_first, ch->net_info.source, ch->net_info.port_first, GF_SOCK_REUSE_PORT); if (e) return e; } //else bind and set remote destination else { if (!ch->net_info.port_first) ch->net_info.port_first = ch->net_info.client_port_first; e = gf_sk_bind(ch->rtp, ch->net_info.port_first, ch->net_info.destination, ch->net_info.client_port_first, GF_SOCK_REUSE_PORT); if (e) return e; } } else { //Bind to multicast (auto-join the group). //we do not bind the socket if this is a source-only channel because some servers //don't like that on local loop ... e = gf_sk_setup_multicast(ch->rtp, ch->net_info.source, ch->net_info.port_first, ch->net_info.TTL, (IsSource==2), local_interface_ip); if (e) return e; //destination is used for multicast interface addressing - TO DO } if (UDPBufferSize) gf_sk_set_buffer_size(ch->rtp, IsSource, UDPBufferSize); if (IsSource) { if (ch->send_buffer) free(ch->send_buffer); ch->send_buffer = (char *) malloc(sizeof(char) * PathMTU); ch->send_buffer_size = PathMTU; } //create re-ordering queue for UDP only, and recieve if (ReorederingSize && !IsSource) { if (!MaxReorderDelay) MaxReorderDelay = 200; ch->po = gf_rtp_reorderer_new(ReorederingSize, MaxReorderDelay); } // // RTCP // ch->rtcp = gf_sk_new(GF_SOCK_TYPE_UDP); if (!ch->rtcp) return GF_IP_NETWORK_FAILURE; if (ch->net_info.IsUnicast) { if (!IsSource) { e = gf_sk_bind(ch->rtcp, ch->net_info.client_port_last, ch->net_info.source, ch->net_info.port_last, GF_SOCK_REUSE_PORT); if (e) return e; } else { e = gf_sk_bind(ch->rtcp, ch->net_info.port_last, ch->net_info.destination, ch->net_info.client_port_last, GF_SOCK_REUSE_PORT); if (e) return e; } } else { if (!ch->net_info.port_last) ch->net_info.port_last = ch->net_info.client_port_last; //Bind to multicast (auto-join the group) e = gf_sk_setup_multicast(ch->rtcp, ch->net_info.source, ch->net_info.port_last, ch->net_info.TTL, (IsSource==2), local_interface_ip); if (e) return e; //destination is used for multicast interface addressing - TO DO } } //format CNAME if not done yet if (!ch->CName) { //this is the real CName setup if (!ch->rtp) { ch->CName = strdup("mpeg4rtp"); } else { char name[GF_MAX_IP_NAME_LEN]; s32 start; gf_get_user_name(name, 1024); if (strlen(name)) strcat(name, "@"); start = strlen(name); //get host IP or loopback if error if (gf_sk_get_local_ip(ch->rtp, name+start) != GF_OK) strcpy(name+start, "127.0.0.1"); ch->CName = strdup(name); } } #ifndef GPAC_DISABLE_LOG if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Packet Log Format: SSRC SequenceNumber TimeStamp NTP@recvTime deviance, Jiter, PckLost PckTotal BytesTotal\n")); }#endif return GF_OK;}/*get the UTC time expressed in RTP timescale*/u32 gf_rtp_channel_time(GF_RTPChannel *ch){ u32 sec, frac, res; gf_net_get_ntp(&sec, &frac); res = ( (u32) ( (frac>>26)*ch->TimeScale) ) >> 6; res += ch->TimeScale*(sec - ch->ntp_init); return (u32) res;}u32 gf_rtp_get_report_time(){ u32 sec, frac; gf_net_get_ntp(&sec, &frac); /*in units of 1/65536 seconds*/ return (u32) ( (frac>>16) + 0x10000L*sec );}void gf_rtp_get_next_report_time(GF_RTPChannel *ch){ Double d; /*offset between .5 and 1.5 sec*/ d = 0.5 + ((Double) gf_rand()) / ((Double) RAND_MAX); /*of a minimal 5sec interval expressed in 1/65536 of a sec*/ d = 5.0 * d * 65536; /*we should estimate bandwidth sharing too, but as we only support one sender*/ ch->next_report_time = gf_rtp_get_report_time() + (u32) d;}GF_EXPORTu32 gf_rtp_read_rtp(GF_RTPChannel *ch, char *buffer, u32 buffer_size){ GF_Err e; u32 seq_num, res; char *pck; //only if the socket exist (otherwise RTSP interleaved channel) if (!ch || !ch->rtp) return 0; e = gf_sk_receive(ch->rtp, buffer, buffer_size, 0, &res); if (!res || e || (res < 12)) res = 0; //add the packet to our Queue if any if (ch->po) { if (res) { seq_num = ((buffer[2] << 8) & 0xFF00) | (buffer[3] & 0xFF); gf_rtp_reorderer_add(ch->po, (void *) buffer, res, seq_num); } //pck queue may need to be flushed pck = (char *) gf_rtp_reorderer_get(ch->po, &res); if (pck) { memcpy(buffer, pck, res); free(pck); } } /*monitor keep-alive period*/ if (ch->nat_keepalive_time_period) { u32 now = gf_sys_clock(); if (res) { ch->last_nat_keepalive_time = now; } else { if (now - ch->last_nat_keepalive_time >= ch->nat_keepalive_time_period) { char rtp_nat[12]; rtp_nat[0] = (u8) 0xC0; rtp_nat[1] = ch->PayloadType; rtp_nat[2] = (ch->last_pck_sn>>8)&0xFF; rtp_nat[3] = (ch->last_pck_sn)&0xFF; rtp_nat[4] = (ch->last_pck_ts>>24)&0xFF; rtp_nat[5] = (ch->last_pck_ts>>16)&0xFF; rtp_nat[6] = (ch->last_pck_ts>>8)&0xFF; rtp_nat[7] = (ch->last_pck_ts)&0xFF; rtp_nat[8] = (ch->SenderSSRC>>24)&0xFF; rtp_nat[9] = (ch->SenderSSRC>>16)&0xFF; rtp_nat[10] = (ch->SenderSSRC>>8)&0xFF; rtp_nat[11] = (ch->SenderSSRC)&0xFF; e = gf_sk_send(ch->rtp, buffer, 12); if (e) { GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP] Error sending NAT keep-alive packet: %s - disabling NAT\n", gf_error_to_string(e) )); ch->nat_keepalive_time_period = 0; } else { GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP] Sending NAT keep-alive packet - response %s\n", gf_error_to_string(e) )); } ch->last_nat_keepalive_time = now; } } } return res;}GF_EXPORTGF_Err gf_rtp_decode_rtp(GF_RTPChannel *ch, char *pck, u32 pck_size, GF_RTPHeader *rtp_hdr, u32 *PayloadStart){ GF_Err e; s32 deviance, delta; u32 CurrSeq, LastSeq; u32 ntp, lost, low16; if (!rtp_hdr) return GF_BAD_PARAM; e = GF_OK; //we need to uncompress the RTP header rtp_hdr->Version = (pck[0] & 0xC0 ) >> 6; if (rtp_hdr->Version != 2) return GF_NOT_SUPPORTED; rtp_hdr->Padding = ( pck[0] & 0x20 ) >> 5; rtp_hdr->Extension = ( pck[0] & 0x10 ) >> 4; rtp_hdr->CSRCCount = pck[0] & 0x0F; rtp_hdr->Marker = ( pck[1] & 0x80 ) >> 7; rtp_hdr->PayloadType = pck[1] & 0x7F; /*we don't support multiple CSRC now. Only one source (the server) is allowed*/ if (rtp_hdr->CSRCCount) return GF_NOT_SUPPORTED; /*SeqNum*/ rtp_hdr->SequenceNumber = ((pck[2] << 8) & 0xFF00) | (pck[3] & 0xFF); /*TS*/ rtp_hdr->TimeStamp = (u32) ((pck[4]<<24) &0xFF000000) | ((pck[5]<<16) & 0xFF0000) | ((pck[6]<<8) & 0xFF00) | ((pck[7]) & 0xFF); /*SSRC*/ rtp_hdr->SSRC = ((pck[8]<<24) &0xFF000000) | ((pck[9]<<16) & 0xFF0000) | ((pck[10]<<8) & 0xFF00) | ((pck[11]) & 0xFF); /*first we only work with one payload type...*/ if (rtp_hdr->PayloadType != ch->PayloadType) return GF_NOT_SUPPORTED; /*update RTP time if we didn't get the info*/ if (!ch->rtp_time) { ch->rtp_time = rtp_hdr->TimeStamp; ch->rtp_first_SN = rtp_hdr->SequenceNumber; ch->num_sn_loops = 0; } if (!ch->ntp_init && ch->SenderSSRC && (ch->SenderSSRC != rtp_hdr->SSRC) ) { GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTP] SSRC mismatch: %d vs %d\n", rtp_hdr->SSRC, ch->SenderSSRC)); return GF_IP_NETWORK_EMPTY; } /*RTP specs annexe A.8*/ if (!ch->ntp_init) { gf_net_get_ntp(&ch->ntp_init, &lost); ch->last_pck_sn = (u32) rtp_hdr->SequenceNumber-1; } /*this is a loop in SN - add it*/ if ( (ch->last_pck_sn + 1 > rtp_hdr->SequenceNumber) && (rtp_hdr->SequenceNumber >= ch->last_pck_sn + MAX_RTP_SN/2)) { ch->num_sn_loops += 1; } ntp = gf_rtp_channel_time(ch); deviance = ntp - rtp_hdr->TimeStamp; delta = deviance - ch->last_deviance; ch->last_deviance = deviance; if (delta < 0) delta = -delta; ch->Jitter += delta - ( (ch->Jitter + 8) >> 4); lost = 0; LastSeq = ch->last_pck_sn; CurrSeq = (u32) rtp_hdr->SequenceNumber; /*next sequential pck*/ if ( ( (LastSeq + 1) & 0xffff ) == CurrSeq ) { ch->last_num_pck_rcv += 1; ch->last_num_pck_expected += 1; } /*repeated pck*/ else if ( (LastSeq & 0xffff ) == CurrSeq ) { ch->last_num_pck_rcv += 1; } /*drop pck*/ else { low16 = LastSeq & 0xffff; if ( CurrSeq > low16 ) lost = CurrSeq - low16; else lost = 0xffff - low16 + CurrSeq + 1; ch->last_num_pck_expected += lost; ch->last_num_pck_rcv += 1; ch->last_num_pck_loss += lost; } ch->last_pck_sn = CurrSeq;#ifndef GPAC_DISABLE_LOG if ((gf_log_get_level() >= (GF_LOG_DEBUG)) && (gf_log_get_tools() & (GF_LOG_RTP))) { ch->total_pck++; ch->total_bytes += pck_size-12; GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTP]\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", ch->SenderSSRC, rtp_hdr->SequenceNumber, rtp_hdr->TimeStamp, ntp,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -