| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2026 | |
| * 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/constants.h> | |
| #include <gpac/maths.h> | |
| void InitSL_RTP(GF_SLConfig *slc); | |
| GF_EXPORT | |
| GP_RTPPacketizer *gf_rtp_builder_new(u32 rtp_payt, GF_SLConfig *slc, u32 flags, | |
| void *cbk_obj, | |
| void (*OnNewPacket)(void *cbk, GF_RTPHeader *header), | |
| void (*OnPacketDone)(void *cbk, GF_RTPHeader *header), | |
| void (*OnDataReference)(void *cbk, u32 payload_size, u32 offset_from_orig), | |
| void (*OnData)(void *cbk, u8 *data, u32 data_size, Bool is_head) | |
| ) | |
| { | |
| GP_RTPPacketizer *tmp; | |
| if (!rtp_payt || !cbk_obj || !OnPacketDone) return NULL; | |
| GF_SAFEALLOC(tmp, GP_RTPPacketizer); | |
| if (!tmp) return NULL; | |
| if (slc) { | |
| memcpy(&tmp->sl_config, slc, sizeof(GF_SLConfig)); | |
| } else { | |
| memset(&tmp->sl_config, 0, sizeof(GF_SLConfig)); | |
| tmp->sl_config.useTimestampsFlag = 1; | |
| tmp->sl_config.timestampLength = 32; | |
| } | |
| tmp->OnNewPacket = OnNewPacket; | |
| tmp->OnDataReference = OnDataReference; | |
| tmp->OnData = OnData; | |
| tmp->cbk_obj = cbk_obj; | |
| tmp->OnPacketDone = OnPacketDone; | |
| tmp->rtp_payt = rtp_payt; | |
| tmp->flags = flags; | |
| //default init | |
| tmp->sl_header.AU_sequenceNumber = 1; | |
| tmp->sl_header.packetSequenceNumber = 1; | |
| //we assume we start on a new AU | |
| tmp->sl_header.accessUnitStartFlag = 1; | |
| return tmp; | |
| } | |
| GF_EXPORT | |
| void gf_rtp_builder_del(GP_RTPPacketizer *builder) | |
| { | |
| if (!builder) return; | |
| if (builder->payload) gf_bs_del(builder->payload); | |
| if (builder->pck_hdr) gf_bs_del(builder->pck_hdr); | |
| gf_free(builder); | |
| } | |
| GF_EXPORT | |
| GF_Err gf_rtp_builder_process(GP_RTPPacketizer *builder, u8 *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration, u8 descIndex) | |
| { | |
| if (!builder) return GF_BAD_PARAM; | |
| switch (builder->rtp_payt) { | |
| case GF_RTP_PAYT_MPEG4: | |
| return gp_rtp_builder_do_mpeg4(builder, data, data_size, IsAUEnd, FullAUSize); | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| case GF_RTP_PAYT_MPEG12_VIDEO: | |
| return gp_rtp_builder_do_mpeg12_video(builder, data, data_size, IsAUEnd, FullAUSize); | |
| #endif | |
| case GF_RTP_PAYT_MPEG12_AUDIO: | |
| return gp_rtp_builder_do_mpeg12_audio(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_H263: | |
| return gp_rtp_builder_do_h263(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_AMR: | |
| case GF_RTP_PAYT_AMR_WB: | |
| return gp_rtp_builder_do_amr(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_3GPP_TEXT: | |
| return gp_rtp_builder_do_tx3g(builder, data, data_size, IsAUEnd, FullAUSize, duration, descIndex); | |
| case GF_RTP_PAYT_H264_AVC: | |
| case GF_RTP_PAYT_H264_SVC: | |
| return gp_rtp_builder_do_avc(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_QCELP: | |
| return gp_rtp_builder_do_qcelp(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_EVRC_SMV: | |
| return gp_rtp_builder_do_smv(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_LATM: | |
| return gp_rtp_builder_do_latm(builder, data, data_size, IsAUEnd, FullAUSize, duration); | |
| #if GPAC_ENABLE_3GPP_DIMS_RTP | |
| case GF_RTP_PAYT_3GPP_DIMS: | |
| return gp_rtp_builder_do_dims(builder, data, data_size, IsAUEnd, FullAUSize, duration); | |
| #endif | |
| case GF_RTP_PAYT_AC3: | |
| case GF_RTP_PAYT_EAC3: | |
| return gp_rtp_builder_do_ac3(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_HEVC: | |
| case GF_RTP_PAYT_LHVC: | |
| return gp_rtp_builder_do_hevc(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_VVC: | |
| return gp_rtp_builder_do_vvc(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_MP2T: | |
| return gp_rtp_builder_do_mp2t(builder, data, data_size, IsAUEnd, FullAUSize); | |
| case GF_RTP_PAYT_OPUS: | |
| return gp_rtp_builder_do_opus(builder, data, data_size, IsAUEnd, FullAUSize); | |
| default: | |
| return GF_NOT_SUPPORTED; | |
| } | |
| } | |
| //Compute the #params of the slMap | |
| GF_EXPORT | |
| void gf_rtp_builder_init(GP_RTPPacketizer *builder, u8 PayloadType, u32 PathMTU, u32 max_ptime, | |
| u32 StreamType, u32 codecid, u32 PL_ID, | |
| u32 avgSize, u32 maxSize, | |
| u32 avgTS, u32 maxDTS, | |
| u32 IV_length, u32 KI_length, | |
| char *pref_mode) | |
| { | |
| u32 k, ismacrypt_flags; | |
| memset(&builder->slMap, 0, sizeof(GP_RTPSLMap)); | |
| builder->Path_MTU = PathMTU; | |
| builder->PayloadType = PayloadType; | |
| builder->slMap.StreamType = StreamType; | |
| builder->slMap.CodecID = codecid; | |
| builder->slMap.PL_ID = PL_ID; | |
| builder->max_ptime = max_ptime; | |
| if (pref_mode) gf_strcpy(builder->slMap.mode, pref_mode); | |
| //some cst vars | |
| builder->rtp_header.Version = 2; | |
| builder->rtp_header.PayloadType = builder->PayloadType; | |
| /*our max config is with 1 packet only (SingleSL)*/ | |
| builder->first_sl_in_rtp = GF_TRUE; | |
| /*no AUX data*/ | |
| builder->slMap.AuxiliaryDataSizeLength = 0; | |
| /*just compute max aggregation size*/ | |
| switch (builder->rtp_payt) { | |
| case GF_RTP_PAYT_QCELP: | |
| case GF_RTP_PAYT_EVRC_SMV: | |
| case GF_RTP_PAYT_AMR: | |
| case GF_RTP_PAYT_AMR_WB: | |
| { | |
| u32 nb_pck = 1; | |
| u32 block_size = 160; | |
| /*compute max frames per packet - if no avg size, use max size per codec*/ | |
| if (builder->flags & GP_RTP_PCK_USE_MULTI) { | |
| if (builder->rtp_payt == GF_RTP_PAYT_QCELP) { | |
| if (!avgSize) avgSize = 35; | |
| nb_pck = (PathMTU-1) / avgSize; /*one-byte header*/ | |
| if (nb_pck>10) nb_pck=10; /*cf RFC2658*/ | |
| } else if (builder->rtp_payt == GF_RTP_PAYT_EVRC_SMV) { | |
| if (!avgSize) avgSize = 23; | |
| nb_pck = (PathMTU) / avgSize; | |
| if (nb_pck>32) nb_pck=32; /*cf RFC3558*/ | |
| } else if (builder->rtp_payt == GF_RTP_PAYT_AMR_WB) { | |
| if (!avgSize) avgSize = 61; | |
| nb_pck = (PathMTU-1) / avgSize; | |
| block_size = 320; | |
| } else { | |
| if (!avgSize) avgSize = 32; | |
| nb_pck = (PathMTU-1) / avgSize; | |
| } | |
| if (max_ptime) { | |
| u32 max_pck = max_ptime / block_size; | |
| if (nb_pck > max_pck) nb_pck = max_pck; | |
| } | |
| } | |
| if (nb_pck<=1) { | |
| builder->flags &= ~(GP_RTP_PCK_USE_MULTI|GP_RTP_PCK_USE_INTERLEAVING); | |
| builder->auh_size = 1; | |
| } else { | |
| builder->auh_size = nb_pck; | |
| } | |
| /*remove all MPEG-4 and ISMA flags */ | |
| builder->flags &= 0x07; | |
| } | |
| return; | |
| case GF_RTP_PAYT_LATM: | |
| case GF_RTP_PAYT_MPEG4: | |
| break; | |
| default: | |
| /*remove all MPEG-4 and ISMA flags */ | |
| builder->flags &= 0x07; | |
| /*disable aggregation for visual streams, except for AVC where STAP/MTAP can be used*/ | |
| if (StreamType==GF_STREAM_VISUAL) { | |
| if ((codecid != GF_CODECID_AVC) && (codecid != GF_CODECID_SVC) && (codecid != GF_CODECID_MVC) && (codecid != GF_CODECID_HEVC) && (codecid != GF_CODECID_LHVC)) { | |
| builder->flags &= ~GP_RTP_PCK_USE_MULTI; | |
| } | |
| } | |
| else if (avgSize && (PathMTU <= avgSize) ) { | |
| builder->flags &= ~GP_RTP_PCK_USE_MULTI; | |
| } | |
| return; | |
| } | |
| builder->slMap.IV_length = IV_length; | |
| builder->slMap.KI_length = KI_length; | |
| ismacrypt_flags = 0; | |
| if (builder->flags & GP_RTP_PCK_SELECTIVE_ENCRYPTION) ismacrypt_flags |= GP_RTP_PCK_SELECTIVE_ENCRYPTION; | |
| if (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU) ismacrypt_flags |= GP_RTP_PCK_KEY_IDX_PER_AU; | |
| /*mode setup*/ | |
| if (!strnicmp(builder->slMap.mode, "AAC", 3)) { | |
| builder->flags = GP_RTP_PCK_USE_MULTI | GP_RTP_PCK_SIGNAL_SIZE | GP_RTP_PCK_SIGNAL_AU_IDX | ismacrypt_flags; | |
| /*if (builder->flags & GP_RTP_PCK_USE_INTERLEAVING) */ | |
| builder->slMap.ConstantDuration = avgTS; | |
| /*AAC LBR*/ | |
| if (maxSize < 63) { | |
| gf_strcpy(builder->slMap.mode, "AAC-lbr"); | |
| builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 2; | |
| builder->slMap.SizeLength = 6; | |
| } | |
| /*AAC HBR*/ | |
| else { | |
| gf_strcpy(builder->slMap.mode, "AAC-hbr"); | |
| builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 3; | |
| builder->slMap.SizeLength = 13; | |
| } | |
| goto check_header; | |
| } | |
| if (!strnicmp(builder->slMap.mode, "CELP", 4)) { | |
| /*CELP-cbr*/ | |
| if (maxSize == avgSize) { | |
| /*reset flags (interleaving forbidden)*/ | |
| builder->flags = GP_RTP_PCK_USE_MULTI | ismacrypt_flags; | |
| gf_strcpy(builder->slMap.mode, "CELP-cbr"); | |
| builder->slMap.ConstantSize = avgSize; | |
| builder->slMap.ConstantDuration = avgTS; | |
| } | |
| /*CELP VBR*/ | |
| else { | |
| gf_strcpy(builder->slMap.mode, "CELP-vbr"); | |
| builder->slMap.IndexLength = builder->slMap.IndexDeltaLength = 2; | |
| builder->slMap.SizeLength = 6; | |
| /*if (builder->flags & GP_RTP_PCK_USE_INTERLEAVING) */builder->slMap.ConstantDuration = avgTS; | |
| builder->flags = GP_RTP_PCK_USE_MULTI | GP_RTP_PCK_SIGNAL_SIZE | GP_RTP_PCK_SIGNAL_AU_IDX | ismacrypt_flags; | |
| } | |
| goto check_header; | |
| } | |
| /*generic setup by flags*/ | |
| /*size*/ | |
| if (builder->flags & GP_RTP_PCK_SIGNAL_SIZE) { | |
| if (avgSize==maxSize) { | |
| builder->slMap.SizeLength = 0; | |
| builder->slMap.ConstantSize = maxSize; | |
| } else { | |
| builder->slMap.SizeLength = gf_get_bit_size(maxSize ? maxSize : PathMTU); | |
| builder->slMap.ConstantSize = 0; | |
| } | |
| } else { | |
| builder->slMap.SizeLength = 0; | |
| if (builder->flags & GP_RTP_PCK_USE_MULTI) | |
| builder->slMap.ConstantSize = (avgSize==maxSize) ? maxSize : 0; | |
| else | |
| builder->slMap.ConstantSize = 0; | |
| } | |
| /*single SL per RTP*/ | |
| if (!(builder->flags & GP_RTP_PCK_USE_MULTI)) { | |
| if ( builder->sl_config.AUSeqNumLength && (builder->flags & GP_RTP_PCK_SIGNAL_AU_IDX)) { | |
| builder->slMap.IndexLength = builder->sl_config.AUSeqNumLength; | |
| } else { | |
| builder->slMap.IndexLength = 0; | |
| } | |
| /*one packet per RTP so no delta*/ | |
| builder->slMap.IndexDeltaLength = 0; | |
| builder->slMap.IV_delta_length = 0; | |
| /*CTS Delta is always 0 since we have one SL packet per RTP*/ | |
| builder->slMap.CTSDeltaLength = 0; | |
| /*DTS Delta depends on the video type*/ | |
| if ((builder->flags & GP_RTP_PCK_SIGNAL_TS) && maxDTS ) | |
| builder->slMap.DTSDeltaLength = gf_get_bit_size(maxDTS); | |
| else | |
| builder->slMap.DTSDeltaLength = 0; | |
| /*RAP*/ | |
| if (builder->sl_config.useRandomAccessPointFlag && (builder->flags & GP_RTP_PCK_SIGNAL_RAP)) { | |
| builder->slMap.RandomAccessIndication = GF_TRUE; | |
| } else { | |
| builder->slMap.RandomAccessIndication = GF_FALSE; | |
| } | |
| /*stream state*/ | |
| if (builder->flags & GP_RTP_PCK_SYSTEMS_CAROUSEL) { | |
| if (!builder->sl_config.AUSeqNumLength) builder->sl_config.AUSeqNumLength = 4; | |
| builder->slMap.StreamStateIndication = builder->sl_config.AUSeqNumLength; | |
| } | |
| goto check_header; | |
| } | |
| /*this is the avg samples we can store per RTP packet*/ | |
| k = PathMTU / avgSize; | |
| if (k<=1) { | |
| builder->flags &= ~GP_RTP_PCK_USE_MULTI; | |
| /*keep TS signaling for B-frames (eg never default to M4V-ES when B-frames are present)*/ | |
| //builder->flags &= ~GP_RTP_PCK_SIGNAL_TS; | |
| builder->flags &= ~GP_RTP_PCK_SIGNAL_SIZE; | |
| builder->flags &= ~GP_RTP_PCK_SIGNAL_AU_IDX; | |
| builder->flags &= ~GP_RTP_PCK_USE_INTERLEAVING; | |
| builder->flags &= ~GP_RTP_PCK_KEY_IDX_PER_AU; | |
| gf_rtp_builder_init(builder, PayloadType, PathMTU, max_ptime, StreamType, codecid, PL_ID, avgSize, maxSize, avgTS, maxDTS, IV_length, KI_length, pref_mode); | |
| return; | |
| } | |
| /*multiple SL per RTP - check if we have to send TS*/ | |
| builder->slMap.ConstantDuration = builder->sl_config.CUDuration; | |
| if (!builder->slMap.ConstantDuration) { | |
| builder->flags |= GP_RTP_PCK_SIGNAL_TS; | |
| } | |
| /*if we have a constant duration and are not writting TSs, make sure we write AU IDX when interleaving*/ | |
| else if (! (builder->flags & GP_RTP_PCK_SIGNAL_TS) && (builder->flags & GP_RTP_PCK_USE_INTERLEAVING)) { | |
| builder->flags |= GP_RTP_PCK_SIGNAL_AU_IDX; | |
| } | |
| if (builder->flags & GP_RTP_PCK_SIGNAL_TS) { | |
| /*compute CTS delta*/ | |
| builder->slMap.CTSDeltaLength = gf_get_bit_size(k*avgTS); | |
| /*compute DTS delta. Delta is ALWAYS from the CTS of the same sample*/ | |
| if (maxDTS) | |
| builder->slMap.DTSDeltaLength = gf_get_bit_size(maxDTS); | |
| else | |
| builder->slMap.DTSDeltaLength = 0; | |
| } | |
| if ((builder->flags & GP_RTP_PCK_SIGNAL_AU_IDX) && builder->sl_config.AUSeqNumLength) { | |
| builder->slMap.IndexLength = builder->sl_config.AUSeqNumLength; | |
| /*and k-1 AUs in Delta*/ | |
| builder->slMap.IndexDeltaLength = (builder->flags & GP_RTP_PCK_USE_INTERLEAVING) ? gf_get_bit_size(k-1) : 0; | |
| } | |
| else if (builder->flags & GP_RTP_PCK_SYSTEMS_CAROUSEL) { | |
| if (!builder->sl_config.AUSeqNumLength) builder->sl_config.AUSeqNumLength = 4; | |
| builder->slMap.StreamStateIndication = builder->sl_config.AUSeqNumLength; | |
| } | |
| /*RAP*/ | |
| if (builder->sl_config.useRandomAccessPointFlag && (builder->flags & GP_RTP_PCK_SIGNAL_RAP)) { | |
| builder->slMap.RandomAccessIndication = GF_TRUE; | |
| } else { | |
| builder->slMap.RandomAccessIndication = GF_FALSE; | |
| } | |
| check_header: | |
| /*IV delta only if interleaving (otherwise reconstruction from IV is trivial)*/ | |
| if (IV_length && (builder->flags & GP_RTP_PCK_USE_INTERLEAVING)) { | |
| builder->slMap.IV_delta_length = gf_get_bit_size(maxSize); | |
| } | |
| /*ISMACryp video mode*/ | |
| if ((builder->slMap.StreamType==GF_STREAM_VISUAL) && (builder->slMap.CodecID==GF_CODECID_MPEG4_PART2) | |
| && (builder->flags & GP_RTP_PCK_SIGNAL_RAP) && builder->slMap.IV_length | |
| && !(builder->flags & GP_RTP_PCK_SIGNAL_AU_IDX) && !(builder->flags & GP_RTP_PCK_SIGNAL_SIZE) | |
| /*shall have SignalTS*/ | |
| && (builder->flags & GP_RTP_PCK_SIGNAL_TS) && !(builder->flags & GP_RTP_PCK_USE_MULTI) | |
| ) { | |
| gf_strcpy(builder->slMap.mode, "mpeg4-video"); | |
| } | |
| /*ISMACryp AVC video mode*/ | |
| else if ((builder->slMap.StreamType==GF_STREAM_VISUAL) && (builder->slMap.CodecID==GF_CODECID_AVC) | |
| && (builder->flags & GP_RTP_PCK_SIGNAL_RAP) && builder->slMap.IV_length | |
| && !(builder->flags & GP_RTP_PCK_SIGNAL_AU_IDX) && !(builder->flags & GP_RTP_PCK_SIGNAL_SIZE) | |
| /*shall have SignalTS*/ | |
| && (builder->flags & GP_RTP_PCK_SIGNAL_TS) && !(builder->flags & GP_RTP_PCK_USE_MULTI) | |
| ) { | |
| gf_strcpy(builder->slMap.mode, "avc-video"); | |
| } | |
| /*check if we use AU header or not*/ | |
| if (!builder->slMap.SizeLength | |
| && !builder->slMap.IndexLength | |
| && !builder->slMap.IndexDeltaLength | |
| && !builder->slMap.DTSDeltaLength | |
| && !builder->slMap.CTSDeltaLength | |
| && !builder->slMap.RandomAccessIndication | |
| && !builder->slMap.IV_length | |
| && !builder->slMap.KI_length | |
| ) { | |
| builder->has_AU_header = GF_FALSE; | |
| } else { | |
| builder->has_AU_header = GF_TRUE; | |
| } | |
| } | |
| void gf_rtp_builder_set_cryp_info(GP_RTPPacketizer *builder, u64 IV, char *key_indicator, Bool is_encrypted) | |
| { | |
| if (!builder) return; | |
| if (!key_indicator) { | |
| if (builder->key_indicator) { | |
| /*force flush if no provision for keyIndicator per AU*/ | |
| builder->force_flush = (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU) ? GF_FALSE : GF_TRUE; | |
| gf_free(builder->key_indicator); | |
| builder->key_indicator = NULL; | |
| } | |
| } else if (!builder->key_indicator | |
| || | |
| memcmp(builder->key_indicator, key_indicator, sizeof(char)*builder->slMap.KI_length) | |
| ) { | |
| /*force flush if no provision for keyIndicator per AU*/ | |
| builder->force_flush = (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU) ? GF_FALSE : GF_TRUE; | |
| if (!builder->key_indicator) builder->key_indicator = (char *) gf_malloc(sizeof(char)*builder->slMap.KI_length); | |
| memcpy(builder->key_indicator, key_indicator, sizeof(char)*builder->slMap.KI_length); | |
| } | |
| if (builder->IV != IV) { | |
| builder->IV = IV; | |
| if (builder->slMap.IV_delta_length && (builder->slMap.IV_delta_length < gf_get_bit_size((u32) (IV - builder->first_AU_IV) ))) { | |
| builder->first_AU_IV = IV; | |
| builder->force_flush = GF_TRUE; | |
| } | |
| } | |
| builder->is_encrypted = is_encrypted; | |
| } | |
| #define MAX_PAYT_LEN 20 | |
| #define MAX_MEDIA_LEN 20 | |
| GF_EXPORT | |
| Bool gf_rtp_builder_get_payload_name(GP_RTPPacketizer *rtpb, char szPayloadName[MAX_PAYT_LEN], char szMediaName[MAX_MEDIA_LEN]) | |
| { | |
| u32 flags = rtpb->flags; | |
| switch (rtpb->rtp_payt) { | |
| case GF_RTP_PAYT_MPEG4: | |
| if ((rtpb->slMap.StreamType==GF_STREAM_VISUAL) && (rtpb->slMap.CodecID==GF_CODECID_MPEG4_PART2)) { | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| /*ISMACryp video*/ | |
| if ( (flags & GP_RTP_PCK_SIGNAL_RAP) && rtpb->slMap.IV_length | |
| && !(flags & GP_RTP_PCK_SIGNAL_AU_IDX) && !(flags & GP_RTP_PCK_SIGNAL_SIZE) | |
| && (flags & GP_RTP_PCK_SIGNAL_TS) && !(flags & GP_RTP_PCK_USE_MULTI) | |
| ) | |
| { | |
| gf_strlcpy(szPayloadName, "enc-mpeg4-generic", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| } | |
| /*mpeg4-generic*/ | |
| if ( (flags & GP_RTP_PCK_SIGNAL_RAP) || (flags & GP_RTP_PCK_SIGNAL_AU_IDX) || (flags & GP_RTP_PCK_SIGNAL_SIZE) | |
| || (flags & GP_RTP_PCK_SIGNAL_TS) || (flags & GP_RTP_PCK_USE_MULTI) ) { | |
| gf_strlcpy(szPayloadName, "mpeg4-generic", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| } else { | |
| gf_strlcpy(szPayloadName, "MP4V-ES", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| } | |
| } | |
| /*for all other types*/ | |
| if (rtpb->slMap.StreamType==GF_STREAM_AUDIO) gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| else if (rtpb->slMap.StreamType==GF_STREAM_MPEGJ) gf_strlcpy(szMediaName, "application", MAX_MEDIA_LEN); | |
| else gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, rtpb->slMap.IV_length ? "enc-mpeg4-generic" : "mpeg4-generic", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_MPEG12_VIDEO: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "MPV", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_MPEG12_AUDIO: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "MPA", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_H263: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "H263-1998", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_AMR: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "AMR", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_AMR_WB: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "AMR-WB", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_3GPP_TEXT: | |
| gf_strlcpy(szMediaName, "text", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "3gpp-tt", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_H264_AVC: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "H264", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_QCELP: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "QCELP", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_EVRC_SMV: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| if ((rtpb->slMap.CodecID==0xA0) || (rtpb->slMap.CodecID==GF_CODECID_EVRC)) | |
| gf_strlcpy(szPayloadName, "EVRC", MAX_PAYT_LEN); | |
| else | |
| gf_strlcpy(szPayloadName, "SMV", MAX_PAYT_LEN); | |
| /*header-free version*/ | |
| if (rtpb->auh_size<=1) gf_strlcat(szPayloadName, "0", 20); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_LATM: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "MP4A-LATM", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| #if GPAC_ENABLE_3GPP_DIMS_RTP | |
| case GF_RTP_PAYT_3GPP_DIMS: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "richmedia+xml", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| #endif | |
| case GF_RTP_PAYT_AC3: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "ac3", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_EAC3: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "eac3", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_OPUS: | |
| gf_strlcpy(szMediaName, "audio", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "opus", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_H264_SVC: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "H264-SVC", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_HEVC: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "H265", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_LHVC: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "H265-SHVC", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_VVC: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "H266", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| case GF_RTP_PAYT_MP2T: | |
| gf_strlcpy(szMediaName, "video", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "MP2T", MAX_PAYT_LEN); | |
| return GF_TRUE; | |
| default: | |
| gf_strlcpy(szMediaName, "", MAX_MEDIA_LEN); | |
| gf_strlcpy(szPayloadName, "", MAX_PAYT_LEN); | |
| return GF_FALSE; | |
| } | |
| return GF_FALSE; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_rtp_builder_format_sdp(GP_RTPPacketizer *builder, char *payload_name, char **out_sdp_line, char *dsi, u32 dsi_size) | |
| { | |
| char buffer[100]; | |
| u32 i; | |
| Bool is_first = GF_TRUE; | |
| GF_Err e = GF_OK; | |
| if (!out_sdp_line) return GF_BAD_PARAM; | |
| if (*out_sdp_line) | |
| (*out_sdp_line)[0] = 0; | |
| if ((builder->rtp_payt!=GF_RTP_PAYT_MPEG4) && (builder->rtp_payt!=GF_RTP_PAYT_LATM) ) return GF_BAD_PARAM; | |
| #define SDP_ADD_INT(_name, _val) {\ | |
| if (!is_first) gf_dynstrcat(out_sdp_line, "; ", NULL); \ | |
| gf_dynstrcat(out_sdp_line, _name, NULL);\ | |
| sprintf(buffer, "%d", _val);\ | |
| gf_dynstrcat(out_sdp_line, buffer, "=");\ | |
| is_first = 0;\ | |
| } | |
| #define SDP_ADD_STR(_name, _val) {\ | |
| if (!is_first) gf_dynstrcat(out_sdp_line, "; ", NULL);\ | |
| gf_dynstrcat(out_sdp_line, _name, NULL);\ | |
| gf_dynstrcat(out_sdp_line, _val, "=");\ | |
| is_first = 0;\ | |
| } | |
| sprintf(buffer, "a=fmtp:%d ", builder->PayloadType); | |
| gf_dynstrcat(out_sdp_line, buffer, NULL); | |
| /*mandatory fields*/ | |
| if (builder->slMap.PL_ID) SDP_ADD_INT("profile-level-id", builder->slMap.PL_ID); | |
| if (builder->rtp_payt == GF_RTP_PAYT_LATM) SDP_ADD_INT("cpresent", 0); | |
| if (dsi && dsi_size) { | |
| if (dsi_size>10000) { | |
| e = GF_OUT_OF_MEM; | |
| goto exit; | |
| } | |
| if (!is_first) gf_dynstrcat(out_sdp_line, "; ", NULL); | |
| gf_dynstrcat(out_sdp_line, "config=", NULL); | |
| buffer[2]=0; | |
| for (i=0; i<dsi_size; i++) { | |
| sprintf(buffer, "%02x", (unsigned char) dsi[i]); | |
| gf_dynstrcat(out_sdp_line, buffer, NULL); | |
| } | |
| is_first = 0; | |
| } | |
| if (!strcmp(payload_name, "MP4V-ES") || (builder->rtp_payt == GF_RTP_PAYT_LATM) ) { | |
| e = GF_OK; | |
| goto exit; | |
| } | |
| SDP_ADD_INT("streamType", builder->slMap.StreamType); | |
| if (strcmp(builder->slMap.mode, "") && strcmp(builder->slMap.mode, "default")) { | |
| SDP_ADD_STR("mode", builder->slMap.mode); | |
| } else { | |
| SDP_ADD_STR("mode", "generic"); | |
| } | |
| /*optional fields*/ | |
| if (builder->slMap.CodecID) SDP_ADD_INT("objectType", builder->slMap.CodecID); | |
| if (builder->slMap.ConstantSize) SDP_ADD_INT("constantSize", builder->slMap.ConstantSize); | |
| if (builder->slMap.ConstantDuration) SDP_ADD_INT("constantDuration", builder->slMap.ConstantDuration); | |
| if (builder->slMap.maxDisplacement) SDP_ADD_INT("maxDisplacement", builder->slMap.maxDisplacement); | |
| if (builder->slMap.deinterleaveBufferSize) SDP_ADD_INT("de-interleaveBufferSize", builder->slMap.deinterleaveBufferSize); | |
| if (builder->slMap.SizeLength) SDP_ADD_INT("sizeLength", builder->slMap.SizeLength); | |
| if (builder->slMap.IndexLength) SDP_ADD_INT("indexLength", builder->slMap.IndexLength); | |
| if (builder->slMap.IndexDeltaLength) SDP_ADD_INT("indexDeltaLength", builder->slMap.IndexDeltaLength); | |
| if (builder->slMap.CTSDeltaLength) SDP_ADD_INT("CTSDeltaLength", builder->slMap.CTSDeltaLength); | |
| if (builder->slMap.DTSDeltaLength) SDP_ADD_INT("DTSDeltaLength", builder->slMap.DTSDeltaLength); | |
| if (builder->slMap.RandomAccessIndication) SDP_ADD_INT("randomAccessIndication", builder->slMap.RandomAccessIndication); | |
| if (builder->slMap.StreamStateIndication) SDP_ADD_INT("streamStateIndication", builder->slMap.StreamStateIndication); | |
| if (builder->slMap.AuxiliaryDataSizeLength) SDP_ADD_INT("auxiliaryDataSizeLength", builder->slMap.AuxiliaryDataSizeLength); | |
| /*ISMACryp config*/ | |
| if (builder->slMap.IV_length) { | |
| /*don't write default*/ | |
| /*SDP_ADD_STR("ISMACrypCryptoSuite", "AES_CTR_128");*/ | |
| if (builder->flags & GP_RTP_PCK_SELECTIVE_ENCRYPTION) SDP_ADD_INT("ISMACrypSelectiveEncryption", 1); | |
| SDP_ADD_INT("ISMACrypIVLength", builder->slMap.IV_length); | |
| if (builder->slMap.IV_delta_length) SDP_ADD_INT("ISMACrypDeltaIVLength", builder->slMap.IV_delta_length); | |
| if (builder->slMap.KI_length) SDP_ADD_INT("ISMACrypKeyIndicatorLength", builder->slMap.KI_length); | |
| if (builder->flags & GP_RTP_PCK_KEY_IDX_PER_AU) SDP_ADD_INT("ISMACrypKeyIndicatorPerAU", 1); | |
| } | |
| exit: | |
| if (!out_sdp_line) return GF_OUT_OF_MEM; | |
| return e; | |
| } | |
| #endif /*GPAC_DISABLE_STREAMING*/ |