|
- // 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;
- 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))
- {
- }
- else if (0 == strncasecmp("sprop-sps", p, nc))
- {
- }
- else if (0 == strncasecmp("sprop-pps", p, nc))
- {
- }
- else if (0 == strncasecmp("sprop-sei", p, nc))
- {
- }
- 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_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_mpeg4_test();
- sdp_a_fmtp_mpeg4_aac_test();
- sdp_a_fmtp_rtx_test();
- }
- #endif
|