756 lines
19 KiB
C++
756 lines
19 KiB
C++
// RFC-4566 SDP
|
|
// 6. SDP Attributes (p30)
|
|
// a=fmtp:<format> <format specific parameters>
|
|
|
|
#include "sdp-a-fmtp.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#if defined(OS_WINDOWS)
|
|
#define strncasecmp _strnicmp
|
|
#endif
|
|
|
|
// RFC6184 RTP Payload Format for H.264 Video
|
|
// m=video 49170 RTP/AVP 98
|
|
// a=rtpmap:98 H264/90000
|
|
// a=fmtp:98 profile-level-id=42A01E;
|
|
// packetization-mode=1;
|
|
// sprop-parameter-sets=<parameter sets data>
|
|
int sdp_a_fmtp_h264(const char* fmtp, int *format, struct sdp_a_fmtp_h264_t *h264)
|
|
{
|
|
size_t nc, vc;
|
|
const char *p1, *p2;
|
|
const char *p = fmtp;
|
|
|
|
// payload type
|
|
*format = atoi(p);
|
|
p1 = strchr(p, ' ');
|
|
if(!p1 || ' ' != *p1)
|
|
return -1;
|
|
|
|
h264->flags = 0;
|
|
assert(' ' == *p1);
|
|
p = p1 + 1;
|
|
while(*p)
|
|
{
|
|
p1 = strchr(p, '=');
|
|
if(!p1 || '=' != *p1)
|
|
return -1;
|
|
|
|
p2 = strchr(p1+1, ';');
|
|
if(!p2)
|
|
p2 = p1 + strlen(p1);
|
|
|
|
while(' ' == *p) p++; // skip space
|
|
|
|
nc = (size_t)(p1 - p); // ptrdiff_t to size_t
|
|
vc = (size_t)(p2 - p1 - 1); // ptrdiff_t to size_t
|
|
switch(*p)
|
|
{
|
|
case 'p':
|
|
// profile-level-id
|
|
// packetization-mode
|
|
if(0 == strncasecmp("profile-level-id", p, nc))
|
|
{
|
|
if(6 != vc) return -1;
|
|
h264->flags |= SDP_A_FMTP_H264_PROFILE_LEVEL_ID;
|
|
memcpy(h264->profile_level_id, p1+1, 6);
|
|
h264->profile_level_id[6] = '\0';
|
|
}
|
|
else if(0 == strncasecmp("packetization-mode", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_PACKETIZATION_MODE;
|
|
h264->packetization_mode = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
// max-recv-level
|
|
// max-mbps
|
|
// max-smbps
|
|
// max-fs
|
|
// max-cbp
|
|
// max-dbp
|
|
// max-br
|
|
// max-rcmd-nalu-size
|
|
if(0 == strncasecmp("max-recv-level", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_MAX_RECV_LEVEL;
|
|
h264->max_recv_level = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("max-mbps", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_MAX_MBPS;
|
|
h264->max_mbps = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("max-smbps", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_MAX_SMBPS;
|
|
h264->max_smbps = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("max-fs", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_MAX_FS;
|
|
h264->max_fs = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("max-cbp", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_MAX_CPB;
|
|
h264->max_cpb = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("max-dbp", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_MAX_DPB;
|
|
h264->max_dpb = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("max-br", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_MAX_BR;
|
|
h264->max_br = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("max-rcmd-nalu-size", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_MAX_RCMD_NALU_SIZE;
|
|
h264->max_rcmd_nalu_size = (unsigned int)atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 's':
|
|
// sprop-parameter-sets
|
|
// sprop-level-parameter-sets
|
|
// sprop-deint-buf-req
|
|
// sprop-interleaving-depth
|
|
// sprop-max-don-diff
|
|
// sprop-init-buf-time
|
|
// sar-understood
|
|
// sar-supported
|
|
if(0 == strncasecmp("sprop-parameter-sets", p, nc))
|
|
{
|
|
if(vc >= sizeof(h264->sprop_parameter_sets)) return -1;
|
|
h264->flags |= SDP_A_FMTP_H264_SPROP_PARAMETER_SETS;
|
|
memcpy(h264->sprop_parameter_sets, p1+1, vc);
|
|
h264->sprop_parameter_sets[vc] = '\0';
|
|
}
|
|
else if(0 == strncasecmp("sprop-level-parameter-sets", p, nc))
|
|
{
|
|
if(vc >= sizeof(h264->sprop_level_parameter_sets)) return -1;
|
|
h264->flags |= SDP_A_FMTP_H264_SPROP_LEVEL_PARAMETER_SETS;
|
|
memcpy(h264->sprop_level_parameter_sets, p1+1, vc);
|
|
h264->sprop_level_parameter_sets[vc] = '\0';
|
|
}
|
|
else if(0 == strncasecmp("sprop-deint-buf-req", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_SPROP_DEINT_BUF_REQ;
|
|
h264->sprop_deint_buf_req = (unsigned int)atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("sprop-interleaving-depth", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_SPROP_INTERLEAVING_DEPTH;
|
|
h264->sprop_interleaving_depth = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("sprop-max-don-diff", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_SPROP_MAX_DON_DIFF;
|
|
h264->sprop_max_don_diff = (unsigned int)atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("sprop-init-buf-time", p, nc))
|
|
{
|
|
if(vc >= sizeof(h264->sprop_init_buf_time)) return -1;
|
|
h264->flags |= SDP_A_FMTP_H264_SPROP_INIT_BUF_TIME;
|
|
memcpy(h264->sprop_init_buf_time, p1+1, vc);
|
|
h264->sprop_init_buf_time[vc] = '\0';
|
|
}
|
|
else if(0 == strncasecmp("sar-understood", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_SAR_UNDERSTOOD;
|
|
h264->sar_understood = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("sar-supported", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_SAR_SUPPORTED;
|
|
h264->sar_supported = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
// redundant-pic-cap
|
|
if(0 == strncasecmp("redundant-pic-cap", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_REDUNDANT_PIC_CAP;
|
|
h264->redundant_pic_cap = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'd':
|
|
// deint-buf-cap
|
|
if(0 == strncasecmp("deint-buf-cap", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_DEINT_BUF_CAP;
|
|
h264->deint_buf_cap = (unsigned int)atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'i':
|
|
// in-band-parameter-sets
|
|
if(0 == strncasecmp("in-band-parameter-sets", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_IN_BAND_PARAMETER_SETS;
|
|
h264->in_band_parameter_sets = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'u':
|
|
// use-level-src-parameter-sets
|
|
if(0 == strncasecmp("use-level-src-parameter-sets", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_USE_LEVEL_SRC_PARAMETER_SETS;
|
|
h264->use_level_src_parameter_sets = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
// level-asymmetry-allowed
|
|
if(0 == strncasecmp("level-asymmetry-allowed", p, nc))
|
|
{
|
|
h264->flags |= SDP_A_FMTP_H264_LEVEL_ASYMMETRY_ALLOWED;
|
|
h264->level_asymmetry_allowed = atoi(p1+1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
p = *p2 ? p2 + 1 : p2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// RFC7798 RTP Payload Format for High Efficiency Video Coding (HEVC)
|
|
// m=video 49170 RTP/AVP 98
|
|
// a=rtpmap:98 H265/90000
|
|
// a=fmtp:98 profile-id=1; sprop-vps=<video parameter sets data>
|
|
int sdp_a_fmtp_h265(const char* fmtp, int *format, struct sdp_a_fmtp_h265_t *h265)
|
|
{
|
|
size_t nc, vc;
|
|
const char *p1, *p2;
|
|
const char *p = fmtp;
|
|
|
|
// payload type
|
|
*format = atoi(p);
|
|
p1 = strchr(p, ' ');
|
|
if (!p1 || ' ' != *p1)
|
|
return -1;
|
|
|
|
h265->flags = 0;
|
|
assert(' ' == *p1);
|
|
p = p1 + 1;
|
|
while (*p)
|
|
{
|
|
p1 = strchr(p, '=');
|
|
if (!p1 || '=' != *p1)
|
|
return -1;
|
|
|
|
p2 = strchr(p1 + 1, ';');
|
|
if (!p2)
|
|
p2 = p1 + strlen(p1);
|
|
|
|
while (' ' == *p) p++; // skip space
|
|
|
|
nc = (size_t)(p1 - p); // ptrdiff_t to size_t
|
|
vc = (size_t)(p2 - p1 - 1); // ptrdiff_t to size_t
|
|
switch (*p)
|
|
{
|
|
case 'i':
|
|
// interop-constraints
|
|
break;
|
|
case 'l':
|
|
// level-id
|
|
break;
|
|
case 'p':
|
|
// profile-space
|
|
// profile-id
|
|
// profile-compatibility-indicator
|
|
break;
|
|
|
|
case 's':
|
|
// sprop-vps
|
|
// sprop-sps
|
|
// sprop-pps
|
|
// sprop-sei
|
|
if (0 == strncasecmp("sprop-vps", p, nc))
|
|
{
|
|
if (vc >= sizeof(h265->sprop_vps)) return -1;
|
|
h265->flags |= SDP_A_FMTP_H265_SPROP_VPS;
|
|
memcpy(h265->sprop_vps, p1 + 1, vc);
|
|
h265->sprop_vps[vc] = '\0';
|
|
}
|
|
else if (0 == strncasecmp("sprop-sps", p, nc))
|
|
{
|
|
if (vc >= sizeof(h265->sprop_sps)) return -1;
|
|
h265->flags |= SDP_A_FMTP_H265_SPROP_SPS;
|
|
memcpy(h265->sprop_sps, p1 + 1, vc);
|
|
h265->sprop_sps[vc] = '\0';
|
|
}
|
|
else if (0 == strncasecmp("sprop-pps", p, nc))
|
|
{
|
|
if (vc >= sizeof(h265->sprop_pps)) return -1;
|
|
h265->flags |= SDP_A_FMTP_H265_SPROP_PPS;
|
|
memcpy(h265->sprop_pps, p1 + 1, vc);
|
|
h265->sprop_pps[vc] = '\0';
|
|
}
|
|
else if (0 == strncasecmp("sprop-sei", p, nc))
|
|
{
|
|
if (vc >= sizeof(h265->sprop_sei)) return -1;
|
|
h265->flags |= SDP_A_FMTP_H265_SPROP_SEI;
|
|
memcpy(h265->sprop_sei, p1 + 1, vc);
|
|
h265->sprop_sei[vc] = '\0';
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
// tier-flag
|
|
break;
|
|
}
|
|
|
|
p = *p2 ? p2 + 1 : p2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// RFC9328 RTP Payload Format for Versatile Video Coding(VVC)
|
|
// m=video 49170 RTP/AVP 98
|
|
// a=rtpmap:98 H266/90000
|
|
// a=fmtp:98 profile-id=1; sprop-vps=<video parameter sets data>
|
|
int sdp_a_fmtp_h266(const char* fmtp, int* format, struct sdp_a_fmtp_h266_t* h266)
|
|
{
|
|
size_t nc, vc;
|
|
const char* p1, * p2;
|
|
const char* p = fmtp;
|
|
|
|
// payload type
|
|
*format = atoi(p);
|
|
p1 = strchr(p, ' ');
|
|
if (!p1 || ' ' != *p1)
|
|
return -1;
|
|
|
|
h266->flags = 0;
|
|
assert(' ' == *p1);
|
|
p = p1 + 1;
|
|
while (*p)
|
|
{
|
|
p1 = strchr(p, '=');
|
|
if (!p1 || '=' != *p1)
|
|
return -1;
|
|
|
|
p2 = strchr(p1 + 1, ';');
|
|
if (!p2)
|
|
p2 = p1 + strlen(p1);
|
|
|
|
while (' ' == *p) p++; // skip space
|
|
|
|
nc = (size_t)(p1 - p); // ptrdiff_t to size_t
|
|
vc = (size_t)(p2 - p1 - 1); // ptrdiff_t to size_t
|
|
switch (*p)
|
|
{
|
|
case 'i':
|
|
// interop-constraints
|
|
break;
|
|
case 'l':
|
|
// level-id
|
|
break;
|
|
case 'p':
|
|
// profile-space
|
|
// profile-id
|
|
// profile-compatibility-indicator
|
|
break;
|
|
|
|
case 's':
|
|
// sprop-dci
|
|
// sprop-vps
|
|
// sprop-sps
|
|
// sprop-pps
|
|
// sprop-sei
|
|
if (0 == strncasecmp("sprop-vps", p, nc))
|
|
{
|
|
if (vc >= sizeof(h266->sprop_vps)) return -1;
|
|
h266->flags |= SDP_A_FMTP_H266_SPROP_VPS;
|
|
memcpy(h266->sprop_vps, p1 + 1, vc);
|
|
h266->sprop_vps[vc] = '\0';
|
|
}
|
|
else if (0 == strncasecmp("sprop-sps", p, nc))
|
|
{
|
|
if (vc >= sizeof(h266->sprop_sps)) return -1;
|
|
h266->flags |= SDP_A_FMTP_H266_SPROP_SPS;
|
|
memcpy(h266->sprop_sps, p1 + 1, vc);
|
|
h266->sprop_sps[vc] = '\0';
|
|
}
|
|
else if (0 == strncasecmp("sprop-pps", p, nc))
|
|
{
|
|
if (vc >= sizeof(h266->sprop_pps)) return -1;
|
|
h266->flags |= SDP_A_FMTP_H266_SPROP_PPS;
|
|
memcpy(h266->sprop_pps, p1 + 1, vc);
|
|
h266->sprop_pps[vc] = '\0';
|
|
}
|
|
else if (0 == strncasecmp("sprop-sei", p, nc))
|
|
{
|
|
if (vc >= sizeof(h266->sprop_sei)) return -1;
|
|
h266->flags |= SDP_A_FMTP_H266_SPROP_SEI;
|
|
memcpy(h266->sprop_sei, p1 + 1, vc);
|
|
h266->sprop_sei[vc] = '\0';
|
|
}
|
|
else if (0 == strncasecmp("sprop-dci", p, nc))
|
|
{
|
|
if (vc >= sizeof(h266->sprop_dci)) return -1;
|
|
h266->flags |= SDP_A_FMTP_H266_SPROP_DCI;
|
|
memcpy(h266->sprop_dci, p1 + 1, vc);
|
|
h266->sprop_dci[vc] = '\0';
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
// tier-flag
|
|
break;
|
|
}
|
|
|
|
p = *p2 ? p2 + 1 : p2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// RFC3640 RTP Payload Format for Transport of MPEG-4 Elementary Streams
|
|
// m=audio 49230 RTP/AVP 96
|
|
// a=rtpmap:96 mpeg4-generic/16000/1
|
|
// a=fmtp:96 streamtype=5; profile-level-id=14; mode=CELP-cbr; config=440E00; constantSize=27; constantDuration=240
|
|
//
|
|
// m=video 49230 RTP/AVP 96
|
|
// a=rtpmap:96 mpeg4-generic/1000
|
|
// a=fmtp:96 streamType=3; profile-level-id=1807; mode=generic;
|
|
// objectType=2; config=0842237F24001FB400094002C0; sizeLength=10;
|
|
// CTSDeltaLength=16; randomAccessIndication=1; streamStateIndication=4
|
|
int sdp_a_fmtp_mpeg4(const char* fmtp, int *format, struct sdp_a_fmtp_mpeg4_t *mpeg4)
|
|
{
|
|
size_t nc, vc;
|
|
const char *p1, *p2;
|
|
const char *p = fmtp;
|
|
|
|
// payload type
|
|
*format = atoi(p);
|
|
p1 = strchr(p, ' ');
|
|
if(!p1 || ' ' != *p1)
|
|
return -1;
|
|
|
|
mpeg4->flags = 0;
|
|
assert(' ' == *p1);
|
|
p = p1 + 1;
|
|
while(*p)
|
|
{
|
|
p1 = strchr(p, '=');
|
|
if(!p1 || '=' != *p1)
|
|
return -1;
|
|
|
|
p2 = strchr(p1+1, ';');
|
|
if(!p2)
|
|
p2 = p1 + strlen(p1);
|
|
|
|
while(' ' == *p) p++; // skip space
|
|
|
|
nc = (size_t)(p1 - p); // ptrdiff_t to size_t
|
|
vc = (size_t)(p2 - p1 - 1); // ptrdiff_t to size_t
|
|
switch(*p)
|
|
{
|
|
case 's':
|
|
// streamType
|
|
// sizeLength
|
|
// streamStateIndication
|
|
if(0 == strncasecmp("streamType", p, nc))
|
|
{
|
|
mpeg4->streamType = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("sizeLength", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_SIZELENGTH;
|
|
mpeg4->sizeLength = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("streamStateIndication", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_STREAMSTATEINDICATION;
|
|
mpeg4->streamStateIndication = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'p':
|
|
// profile-level-id
|
|
if(0 == strncasecmp("profile-level-id", p, nc))
|
|
{
|
|
if(vc >= sizeof(mpeg4->profile_level_id)) return -1;
|
|
memcpy(mpeg4->profile_level_id, p1+1, vc);
|
|
mpeg4->profile_level_id[vc] = '\0';
|
|
}
|
|
break;
|
|
|
|
case 'c':
|
|
// config
|
|
// constantSize
|
|
// constantDuration
|
|
if(0 == strncasecmp("config", p, nc))
|
|
{
|
|
if(vc >= sizeof(mpeg4->config)) return -1;
|
|
memcpy(mpeg4->config, p1+1, vc);
|
|
mpeg4->config[vc] = '\0';
|
|
}
|
|
else if(0 == strncasecmp("constantSize", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_CONSTANTSIZE;
|
|
mpeg4->constantSize = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("constantDuration", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_CONSTANTDURATION;
|
|
mpeg4->constantDuration = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'm':
|
|
// mode
|
|
// maxDisplacement
|
|
if(0 == strncasecmp("mode", p, nc))
|
|
{
|
|
if(0 == strncasecmp("generic", p1+1, vc))
|
|
mpeg4->mode = 1;
|
|
else if(0 == strncasecmp("CELP-cbr", p1+1, vc))
|
|
mpeg4->mode = 2;
|
|
else if(0 == strncasecmp("CELP-vbr", p1+1, vc))
|
|
mpeg4->mode = 3;
|
|
else if(0 == strncasecmp("AAC-lbr", p1+1, vc))
|
|
mpeg4->mode = 4;
|
|
else if(0 == strncasecmp("AAC-hbr", p1+1, vc))
|
|
mpeg4->mode = 5;
|
|
else
|
|
mpeg4->mode = 0; // unknown
|
|
}
|
|
else if(0 == strncasecmp("maxDisplacement", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_MAXDISPLACEMENT;
|
|
mpeg4->maxDisplacement = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'o':
|
|
// objectType
|
|
if(0 == strncasecmp("objectType", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_OBJECTTYPE;
|
|
mpeg4->objectType = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'd':
|
|
// deinterleaveBufferSize
|
|
if(0 == strncasecmp("deinterleaveBufferSize", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_DEINTERLEAVEBUFFERSIZE;
|
|
mpeg4->deinterleaveBufferSize = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'i':
|
|
// indexLength
|
|
// indexDeltaLength
|
|
if(0 == strncasecmp("indexLength", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_INDEXLENGTH;
|
|
mpeg4->indexLength = atoi(p1+1);
|
|
}
|
|
else if(0 == strncasecmp("indexDeltaLength", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_INDEXDELTALENGTH;
|
|
mpeg4->indexDeltaLength = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'C':
|
|
// CTSDeltaLength
|
|
if(0 == strncasecmp("CTSDeltaLength", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_CTSDELTALENGTH;
|
|
mpeg4->CTSDeltaLength = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'D':
|
|
// DTSDeltaLength
|
|
if(0 == strncasecmp("DTSDeltaLength", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_DTSDELTALENGTH;
|
|
mpeg4->DTSDeltaLength = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
// randomAccessIndication
|
|
if(0 == strncasecmp("randomAccessIndication", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_RANDOMACCESSINDICATION;
|
|
mpeg4->randomAccessIndication = atoi(p1+1);
|
|
}
|
|
break;
|
|
|
|
case 'a':
|
|
// auxiliaryDataSizeLength
|
|
if(0 == strncasecmp("auxiliaryDataSizeLength", p, nc))
|
|
{
|
|
mpeg4->flags |= SDP_A_FMTP_MPEG4_AUXILIARYDATASIZELENGTH;
|
|
mpeg4->auxiliaryDataSizeLength = atoi(p1+1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
p = *p2 ? p2 + 1 : p2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// RFC4588 RTP Retransmission Payload Format
|
|
// a=fmtp:<number> apt=<apt-value>;rtx-time=<rtx-time-val>
|
|
int sdp_a_fmtp_rtx(const char* fmtp, int* format, struct sdp_a_fmtp_rtx_t* rtx)
|
|
{
|
|
size_t nc, vc;
|
|
const char* p1, * p2;
|
|
const char* p = fmtp;
|
|
|
|
// payload type
|
|
*format = atoi(p);
|
|
p1 = strchr(p, ' ');
|
|
if (!p1 || ' ' != *p1)
|
|
return -1;
|
|
|
|
assert(' ' == *p1);
|
|
p = p1 + 1;
|
|
while (*p)
|
|
{
|
|
p1 = strchr(p, '=');
|
|
if (!p1 || '=' != *p1)
|
|
return -1;
|
|
|
|
p2 = strchr(p1 + 1, ';');
|
|
if (!p2)
|
|
p2 = p1 + strlen(p1);
|
|
|
|
while (' ' == *p) p++; // skip space
|
|
|
|
nc = (size_t)(p1 - p); // ptrdiff_t to size_t
|
|
vc = (size_t)(p2 - p1 - 1); // ptrdiff_t to size_t
|
|
switch (*p)
|
|
{
|
|
case 'a':
|
|
if (0 == strncasecmp("apt", p, nc))
|
|
{
|
|
rtx->apt = atoi(p1 + 1);
|
|
}
|
|
break;
|
|
|
|
case 'r':
|
|
if (0 == strncasecmp("rtx-time", p, nc))
|
|
{
|
|
rtx->rtx_time = atoi(p1 + 1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
p = *p2 ? p2 + 1 : p2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
static void sdp_a_fmtp_h264_test(void)
|
|
{
|
|
int format = 0;
|
|
struct sdp_a_fmtp_h264_t h264;
|
|
const char* fmtp1 = "98 profile-level-id=42A01E;packetization-mode=1; sprop-parameter-sets=abcd";
|
|
|
|
assert(0 == sdp_a_fmtp_h264(fmtp1, &format, &h264));
|
|
assert(98 == format);
|
|
assert(h264.flags == (SDP_A_FMTP_H264_PROFILE_LEVEL_ID|SDP_A_FMTP_H264_PACKETIZATION_MODE|SDP_A_FMTP_H264_SPROP_PARAMETER_SETS));
|
|
assert(0 == strcmp("42A01E", h264.profile_level_id));
|
|
assert(1 == h264.packetization_mode);
|
|
assert(0 == strcmp("abcd", h264.sprop_parameter_sets));
|
|
}
|
|
|
|
static void sdp_a_fmtp_h265_test(void)
|
|
{
|
|
int format = 0;
|
|
struct sdp_a_fmtp_h265_t h265;
|
|
memset(&h265, 0, sizeof(h265));
|
|
const char* fmtp1 = "96 sprop-vps=QAEMAf//AWAAAAMAsAAAAwAAAwBdFcCQ; sprop-sps=QgEBAWAAAAMAsAAAAwAAAwBdoAWiAFAWIFe5FlRA; sprop-pps=RAHALLwUyQ==";
|
|
|
|
assert(0 == sdp_a_fmtp_h265(fmtp1, &format, &h265));
|
|
assert(96 == format);
|
|
assert(h265.flags == (SDP_A_FMTP_H265_SPROP_VPS| SDP_A_FMTP_H265_SPROP_SPS| SDP_A_FMTP_H265_SPROP_PPS));
|
|
assert(0 == strcmp("QAEMAf//AWAAAAMAsAAAAwAAAwBdFcCQ", h265.sprop_vps));
|
|
assert(0 == strcmp("QgEBAWAAAAMAsAAAAwAAAwBdoAWiAFAWIFe5FlRA", h265.sprop_sps));
|
|
assert(0 == strcmp("RAHALLwUyQ==", h265.sprop_pps));
|
|
assert(0 == strcmp("", h265.sprop_sei));
|
|
}
|
|
|
|
static void sdp_a_fmtp_mpeg4_test(void)
|
|
{
|
|
int format = 0;
|
|
struct sdp_a_fmtp_mpeg4_t mpeg4;
|
|
const char* fmtp = "96 streamType=3;profile-level-id=1807; mode=generic;objectType=2; config=0842237F24001FB400094002C0;sizeLength=10;CTSDeltaLength=16;randomAccessIndication=1;streamStateIndication=4";
|
|
|
|
assert(0 == sdp_a_fmtp_mpeg4(fmtp, &format, &mpeg4));
|
|
assert(96 == format);
|
|
assert(mpeg4.flags == (SDP_A_FMTP_MPEG4_OBJECTTYPE|SDP_A_FMTP_MPEG4_SIZELENGTH|SDP_A_FMTP_MPEG4_CTSDELTALENGTH|SDP_A_FMTP_MPEG4_RANDOMACCESSINDICATION|SDP_A_FMTP_MPEG4_STREAMSTATEINDICATION));
|
|
assert(3 == mpeg4.streamType);
|
|
assert(0 == strcmp("1807", mpeg4.profile_level_id));
|
|
assert(1 == mpeg4.mode);
|
|
assert(2 == mpeg4.objectType);
|
|
assert(0 == strcmp(mpeg4.config, "0842237F24001FB400094002C0"));
|
|
assert(10 == mpeg4.sizeLength);
|
|
assert(16 == mpeg4.CTSDeltaLength);
|
|
assert(1 == mpeg4.randomAccessIndication);
|
|
assert(4 == mpeg4.streamStateIndication);
|
|
}
|
|
|
|
static void sdp_a_fmtp_mpeg4_aac_test(void)
|
|
{
|
|
int format = 0;
|
|
struct sdp_a_fmtp_mpeg4_t mpeg4;
|
|
const char* fmtp = "97 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=131056E59D4800";
|
|
|
|
assert(0 == sdp_a_fmtp_mpeg4(fmtp, &format, &mpeg4));
|
|
assert(97 == format);
|
|
assert(mpeg4.flags == (SDP_A_FMTP_MPEG4_SIZELENGTH | SDP_A_FMTP_MPEG4_INDEXLENGTH | SDP_A_FMTP_MPEG4_INDEXDELTALENGTH));
|
|
assert(5 == mpeg4.streamType);
|
|
assert(0 == strcmp("1", mpeg4.profile_level_id));
|
|
assert(5 == mpeg4.mode);
|
|
assert(0 == strcmp(mpeg4.config, "131056E59D4800"));
|
|
assert(13 == mpeg4.sizeLength);
|
|
assert(3 == mpeg4.indexLength);
|
|
assert(3 == mpeg4.indexDeltaLength);
|
|
}
|
|
|
|
static void sdp_a_fmtp_rtx_test(void)
|
|
{
|
|
int format = 0;
|
|
struct sdp_a_fmtp_rtx_t rtx;
|
|
const char* fmtp = "111 apt=97;rtx-time=3000";
|
|
assert(0 == sdp_a_fmtp_rtx(fmtp, &format, &rtx));
|
|
assert(111 == format && 97 == rtx.apt && 3000 == rtx.rtx_time);
|
|
}
|
|
|
|
void sdp_a_fmtp_test(void)
|
|
{
|
|
sdp_a_fmtp_h264_test();
|
|
sdp_a_fmtp_h265_test();
|
|
sdp_a_fmtp_mpeg4_test();
|
|
sdp_a_fmtp_mpeg4_aac_test();
|
|
sdp_a_fmtp_rtx_test();
|
|
}
|
|
#endif
|