/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 1999 - 2006, Digium, Inc. * * Mark Spencer * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ /*! \file * * \brief Capabilty Management * * \author Mark Spencer */ #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 39057 $") #include "asterisk/capability.h" #include "asterisk/channel.h" struct videoSize videoSizes[MAX_VIDEO_SIZES] = { {"SQCIF", 48, 0}, {"QSIF", 75, 0}, {"QCIF", 99, 0}, {"QVGA", 300, 0}, {"SIF", 300, 0}, {"CIF", 396, 0}, {"VGA", 1200, 0}, {"CIF4", 1584, 0}, {"SVGA", 1875, 0}, {"XGA", 2304, 0}, {"CIF16", 6336, 0}, }; struct ast_capabilities ast_default_caps = { DEFAULT_MAX_CALL_BITRATE, DEFAULT_MAX_CALL_BITRATE, {0, 0, {0,0,30,0,0,30,0,0,0,0,0}, DEFAULT_MAX_CALL_BITRATE}, {0, 0, {0,0,30,0,0,30,0,0,0,0,0}, DEFAULT_MAX_CALL_BITRATE}, {0, 0, 0x42, 0x0c, 0x80, 7128, 0, DEFAULT_MAX_CALL_BITRATE}, }; struct ast_capabilities *ast_alloc_capabilities() { struct ast_capabilities *cap = ast_calloc(1, sizeof(struct ast_capabilities)); return cap; } int ast_are_caps_zero(struct ast_capabilities *caps) { int res = 0; if ((caps->maxcallbitrate == 0) && (caps->maxvideobitrate == 0) && (caps->h261.valid == 0) && (caps->h263.valid == 0)) res = 1; return res; } void ast_dump_h2613_video_cap(struct ast_h2613_video_cap *vidcap) { int i; if (vidcap->valid) { ast_verbose(" MaxBR: %dkbps\n", vidcap->maxbr/1000); for (i = 0; i < MAX_VIDEO_SIZES; i++) { if (vidcap->maxfr[i] > 0) ast_verbose(" %-11s %1dfps\n", videoSizes[i].type, vidcap->maxfr[i]); } if (vidcap->annexes) ast_verbose(" Annexes: 0x%08x\n",vidcap->annexes); } else ast_verbose("\n"); } void ast_dump_h264_video_cap(struct ast_h264_video_cap *vidcap) { if (vidcap->valid) { ast_verbose(" RTPnum: %03d\n",vidcap->rtpnum); ast_verbose(" Profile: %02x\n",vidcap->profile); ast_verbose(" Level: %02x\n",vidcap->level); ast_verbose(" Constraint: %02x\n",vidcap->constraint); ast_verbose(" PMode %d\n", vidcap->packet_mode); ast_verbose(" MaxBR: %dkbps\n", vidcap->maxbr/1000); ast_verbose(" MaxMBPS: %d\n", vidcap->maxmbps); } else ast_verbose("\n"); } void ast_dump_caps(struct ast_capabilities *caps) { ast_verbose(" Bitrates:\n MaxCall %dkbps\n MaxVideo %dkbps\n", caps->maxcallbitrate/1000,caps->maxvideobitrate/1000); if (caps->h261.valid) { ast_verbose(" H261:\n"); ast_dump_h2613_video_cap(&caps->h261); } if (caps->h263.valid) { ast_verbose(" H263:\n"); ast_dump_h2613_video_cap(&caps->h263); } if (caps->h264.valid) { ast_verbose(" H264:\n"); ast_dump_h264_video_cap(&caps->h264); } ast_verbose("\n"); } int ast_copy_capabilities(struct ast_capabilities *dstcap, struct ast_capabilities *srccap) { memcpy(dstcap, srccap, sizeof(struct ast_capabilities)); return 0; } /*! \brief Finds the highest common capability between the two source caps and applies it to the destination cap */ /* Can be called with dst same as one of the src caps */ void ast_resolve_h2613_video_cap(struct ast_h2613_video_cap *dstcap, struct ast_h2613_video_cap *s1cap, struct ast_h2613_video_cap *s2cap) { int i; /*... resolve valids */ if (s1cap->valid && s2cap->valid) dstcap->valid = 1; else { dstcap->valid = 0; memset(dstcap->maxfr, 0, sizeof(dstcap->maxfr[0]) * MAX_VIDEO_SIZES); dstcap->maxbr = 0; return; } /* Use the lowest frame rate even if zero */ for (i = 0; i < MAX_VIDEO_SIZES; i++) dstcap->maxfr[i] = MIN(s1cap->maxfr[i], s2cap->maxfr[i]); /* Union of annexes */ dstcap->annexes = s1cap->annexes & s2cap->annexes; /* Resolve bitrates */ dstcap->maxbr = MIN(s1cap->maxbr, s2cap->maxbr); } /*! \brief Finds the highest common capability between the two source caps and applies it to the destination cap */ /* Can be called with dst same as one of the src caps */ void ast_resolve_h264_video_cap(struct ast_h264_video_cap *dstcap, struct ast_h264_video_cap *s1cap, struct ast_h264_video_cap *s2cap) { /*... resolve valids */ if (s1cap->valid && s2cap->valid) dstcap->valid = 1; else { dstcap->valid = 0; dstcap->rtpnum = 0; dstcap->profile = 0; dstcap->constraint = 0; dstcap->level = 0; dstcap->maxbr = 0; dstcap->maxmbps = 0; return; } /* Resolve profiles */ dstcap->profile = MIN(s1cap->profile, s2cap->profile); /* Resolve levels */ dstcap->level = MIN(s1cap->level, s2cap->level); /* Resolve constraints */ dstcap->constraint = MIN(s1cap->constraint, s2cap->constraint); /* Resolve bitrates */ dstcap->maxbr = MIN(s1cap->maxbr, s2cap->maxbr); if (s1cap->rtpnum > 0) dstcap->rtpnum = s1cap->rtpnum; else if (s2cap->rtpnum > 0) dstcap->rtpnum = s2cap->rtpnum; } /*! \brief ast_resolve_capabilities: called when we need to have dst = MIN(s1cap, s2cap), or in set theory terms s1cap INTERSECT s2cap (s1 n s2) */ int ast_resolve_capabilities(struct ast_capabilities *dst, struct ast_capabilities *s1caps, struct ast_capabilities *s2caps, int debug) { int res = 0; if (debug) { ast_verbose("\nResolve: Input Caps1:\n"); ast_dump_caps(s1caps); ast_verbose("\nResolve: Input Caps2:\n"); ast_dump_caps(s2caps); } dst->maxcallbitrate = MIN(s1caps->maxcallbitrate, s2caps->maxcallbitrate); dst->maxvideobitrate = MIN(s1caps->maxvideobitrate, s2caps->maxvideobitrate); ast_resolve_h2613_video_cap(&dst->h261, &s1caps->h261, &s2caps->h261); ast_resolve_h2613_video_cap(&dst->h263, &s1caps->h263, &s2caps->h263); ast_resolve_h264_video_cap(&dst->h264, &s1caps->h264, &s2caps->h264); if (debug) { ast_verbose("\nResolve: Output Caps:\n"); ast_dump_caps(dst); } return res; } /*! \brief ast_set_capabilities_from_int: called when we need to make sure the complex caps are inline with old integer caps */ int ast_set_capabilities_from_int(struct ast_capabilities *caps, int simplecap) { caps->h261.valid = ((simplecap & AST_FORMAT_H261) == AST_FORMAT_H261) ? 1 : 0; caps->h263.valid = ((simplecap & AST_FORMAT_H263) == AST_FORMAT_H263) ? 1 : 0; caps->h264.valid = ((simplecap & AST_FORMAT_H264) == AST_FORMAT_H264) ? 1 : 0; return 0; } int ast_set_capabilities(struct ast_channel *chan, struct ast_capabilities *chancaps) { if (!chan) return -1; if (chan->tech->set_capabilities) chan->tech->set_capabilities(chan, chancaps); return 0; } /* Checks all the video bitrates to make sure they conform to the following: enforce maxcallbitrate >= maxvideobitrate >= maxbr This will be called when any bitrates can be set (incoming call, reload etc). The maxcallbitrate will override everything Callbitrate overides the media codec bitrates */ int check_set_video_bitrates(struct ast_capabilities *caps, int debug) { int maxctbr = caps->maxcallbitrate; int maxvbr = caps->maxvideobitrate; int maxh261 = caps->h261.valid ? caps->h261.maxbr : 0; int maxh263 = caps->h263.valid ? caps->h263.maxbr : 0; int maxh264 = caps->h264.valid ? caps->h264.maxbr : 0; int maxmedia = MAX(maxh261, MAX(maxh263, maxh264)); if(debug){ ast_verbose ("CHECK SET BITRATES (IN): ct=%d, vbr=%d, h261=%d, h263=%d, h264=%d, maxmedia=%d\n", maxctbr, maxvbr, maxh261, maxh263, maxh264, maxmedia); } /* Make sure ct and vbr are set to something sensible */ if (maxctbr) { if (maxvbr && maxvbr > maxctbr) { maxvbr = maxctbr; } else { maxvbr = maxctbr; } } else { if (maxvbr) { maxctbr = maxvbr; } else { maxvbr = maxctbr = maxmedia; } } if (!maxvbr || !maxctbr) { if(debug) ast_verbose("CHECK SET BITRATES Haven't got any video bitrates set\n"); return -1; } /* Now make sure all codec bitrates are below the max permissible */ if (maxh264 > maxvbr) maxh264 = maxvbr; if (maxh263 > maxvbr) maxh263 = maxvbr; if (maxh261 > maxvbr) maxh261 = maxvbr; caps->h264.maxbr = maxh264; caps->h263.maxbr = maxh263; caps->h261.maxbr = maxh261; caps->maxvideobitrate = maxvbr; caps->maxcallbitrate = maxctbr; if(debug) { maxmedia = MAX(maxh261, MAX(maxh263, maxh264)); ast_verbose ("CHECK SET BITRATES (OUT): ct=%d, vbr=%d, h261=%d, h263=%d, h264=%d, maxmedia=%d\n", maxctbr, maxvbr, maxh261, maxh263, maxh264, maxmedia); } return 0; } /* Returns bitrate if found else -1 */ int parse_h2613_conf_line(const char *fmtstr, struct ast_h2613_video_cap *vidcap, unsigned int framerate) { char *parse, *loop_str, *equals, *value_str; char *sep = " ;:/,"; int len; int i; unsigned int tmp; int found=0; memset(vidcap, 0, sizeof(struct ast_h2613_video_cap)); parse = ast_strdupa(fmtstr); /* Get the fmtp: and payload type out of the way */ for ((loop_str = strsep(&parse, sep)); loop_str; (loop_str = strsep(&parse, sep)) != NULL) { if((equals = strchr(loop_str, '='))==NULL) return -1; found = 0; len = equals - loop_str; value_str = loop_str+len+1; if (!strncasecmp(loop_str, "maxbr", len)){ if(sscanf(value_str, "%d", &tmp)) { found = 1; vidcap->maxbr = tmp*1000; /* Convert from *100 to *1 */ vidcap->valid = 1; /* ast_verbose("MaxBR=%d\n", vidcap->maxbr); */ } else ast_verbose("Failed to parse maxbr in process_h261_h261_fmtp()\n"); } else if (len == 1 && loop_str[0] >= 'A' && loop_str[0] <= 'Z') { /* this is an annex */ vidcap->annexes |= (1 << (loop_str[0] - 'A')); found = 1; ast_verbose("Found annex %c, we now have %08x\n", loop_str[0], vidcap->annexes); } else { /* Check video sizes */ for (i = 0; i < MAX_VIDEO_SIZES; i++) { if (strlen(videoSizes[i].type) == len && !strncasecmp(loop_str, videoSizes[i].type, len)) { if (sscanf(value_str, "%d", &tmp)){ found = 1; /* vidcap->maxfr[i] = framerate < (30 / tmp) ? framerate : (30 /tmp); */ vidcap->maxfr[i] = (tmp > 30) ? 30 : (tmp < 0) ? 0 : tmp; vidcap->valid = 1; } /* ast_verbose("%s->FR=%d, len=%d, string=%s\n", videoSizes[i].type, vidcap->maxfr[i], len, loop_str); */ } } } if (!found) ast_verbose("Unable to parse %s in fmtp\n", loop_str); } if (vidcap->valid && vidcap->maxbr) return vidcap->maxbr; else return -1; } /* Returns 0 on success else -1 */ int parse_h264_conf_line(const char *fmtstr, struct ast_h264_video_cap *vidcap, int framerate) { char *parse, *loop_str, *equals, *value_str; char *sep = " ;:/,"; int len; struct videoSize myVideoSizes[MAX_VIDEO_SIZES]; int found=0; int maxbr = 0; int level_maxbr = 0; int maxmbps = 0; int level_maxmbps = 0; int profile = 0; int level = 0; int constraint = 0; int packetmode = 0; int rtpnum=0; /* "a=fmtp:%d profile-level-id=42800C; packetization-mode=0; max-br=384; max-mbps=7128\r\n" */ memcpy(myVideoSizes, videoSizes, sizeof(myVideoSizes)); parse = ast_strdupa(fmtstr); /* Get the fmtp: and payload type out of the way */ for ((loop_str = strsep(&parse, sep)); loop_str; (loop_str = strsep(&parse, sep)) != NULL) { if (loop_str[0] == '\0') { loop_str++; continue; } if((equals = strchr(loop_str, '='))==NULL) return -1; found = 0; len = equals - loop_str; value_str = loop_str+len+1; /* Now check other parameters than can appear in a video fmtp */ if (!strncasecmp(loop_str, "max-br", len)) { if(sscanf(value_str, "%d", &maxbr)) found = 1; else ast_verbose("Failed to parse max-br in process_h264_fmtp()\n"); } else if (!strncasecmp(loop_str, "max-mbps", len)) { if(sscanf(value_str, "%d", &maxmbps)) found = 1; else ast_verbose("Failed to parse max-mbps in process_h264_fmtp()\n"); } else if (!strncasecmp(loop_str, "profile-level-id", len)) { if(sscanf(value_str, "%02x%02x%02x", &profile, &constraint, &level)) found = 1; else ast_verbose("Failed to parse profile-level-id in process_h264_fmtp()\n"); } else if (!strncasecmp(loop_str, "packetization-mode", len)) { if(sscanf(value_str, "%d", &packetmode)) found = 1; else ast_verbose("Failed to parse packetization-mode in process_h264_fmtp()\n"); } else if (!strncasecmp(loop_str, "rtpnum", len)) { if(sscanf(value_str, "%d", &rtpnum)) found = 1; else ast_verbose("Failed to parse rtpnum in process_h264_fmtp()\n"); } if (!found) ast_verbose("Unable to parse %s in fmtp\n", loop_str); } /* Work out picture sizes from supplied level */ switch (level) { case level_1 : level_maxbr = 64000; level_maxmbps = 1485; break; case level_1_1 : level_maxbr = 64000; level_maxmbps = 3000; break; case level_1_2 : level_maxbr = 128000; level_maxmbps = 6000; break; case level_1_3 : level_maxbr = 384000; level_maxmbps = 11880; break; case level_2 : level_maxbr = 768000; level_maxmbps = 11880; break; default : level_maxbr = 2000000; level_maxmbps = 11880; break; } maxbr = maxbr * 1000; if (maxbr > level_maxbr) vidcap->maxbr = maxbr; else vidcap->maxbr = level_maxbr; if (maxmbps > level_maxmbps) vidcap->maxmbps = maxmbps; else vidcap->maxmbps = level_maxmbps; vidcap->profile = profile; vidcap->level = level; vidcap->constraint = constraint; vidcap->valid = 1; vidcap->rtpnum = rtpnum; return 0; }