Sat Apr 26 2014 22:01:40

Asterisk developer's documentation


res_format_attr_h264.c
Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2012, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  *
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief H.264 Format Attribute Module
00023  *
00024  * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
00025  *
00026  * This is a format attribute module for the H.264 codec.
00027  * \ingroup applications
00028  */
00029 
00030 /*** MODULEINFO
00031    <support_level>core</support_level>
00032  ***/
00033 
00034 #include "asterisk.h"
00035 
00036 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 383973 $")
00037 
00038 #include "asterisk/module.h"
00039 #include "asterisk/format.h"
00040 
00041 /*! \brief Value that indicates an attribute is actually unset */
00042 #define H264_ATTR_KEY_UNSET UINT8_MAX
00043 
00044 /*! \brief Maximum size for SPS / PPS values in sprop-parameter-sets attribute
00045  *   if you change this value then you must change H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT
00046  *   as well. */
00047 #define H264_MAX_SPS_PPS_SIZE 16
00048 /*! \brief This is used when executing sscanf on buffers of H264_MAX_SPS_PPS_SIZE
00049  *   length. It must ALWAYS be a string literal representation of one less than
00050  *   H264_MAX_SPS_PPS_SIZE */
00051 #define H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT "15"
00052 
00053 enum h264_attr_keys {
00054    H264_ATTR_KEY_PROFILE_IDC,
00055    H264_ATTR_KEY_PROFILE_IOP,
00056    H264_ATTR_KEY_LEVEL,
00057    H264_ATTR_KEY_MAX_MBPS,
00058    H264_ATTR_KEY_MAX_FS,
00059    H264_ATTR_KEY_MAX_CPB,
00060    H264_ATTR_KEY_MAX_DPB,
00061    H264_ATTR_KEY_MAX_BR,
00062    H264_ATTR_KEY_MAX_SMBPS,
00063    H264_ATTR_KEY_MAX_FPS,
00064    H264_ATTR_KEY_REDUNDANT_PIC_CAP,
00065    H264_ATTR_KEY_PARAMETER_ADD,
00066    H264_ATTR_KEY_PACKETIZATION_MODE,
00067    H264_ATTR_KEY_SPROP_INTERLEAVING_DEPTH,
00068    H264_ATTR_KEY_SPROP_DEINT_BUF_REQ,
00069    H264_ATTR_KEY_DEINT_BUF_CAP,
00070    H264_ATTR_KEY_SPROP_INIT_BUF_TIME,
00071    H264_ATTR_KEY_SPROP_MAX_DON_DIFF,
00072    H264_ATTR_KEY_MAX_RCMD_NALU_SIZE,
00073    H264_ATTR_KEY_LEVEL_ASYMMETRY_ALLOWED,
00074    H264_ATTR_KEY_SPS_LEN,
00075    H264_ATTR_KEY_PPS_LEN,
00076    H264_ATTR_KEY_SPS,
00077    H264_ATTR_KEY_PPS = H264_ATTR_KEY_SPS + H264_MAX_SPS_PPS_SIZE,
00078    H264_ATTR_KEY_END = H264_ATTR_KEY_PPS + H264_MAX_SPS_PPS_SIZE,
00079 };
00080 
00081 static enum ast_format_cmp_res h264_format_attr_cmp(const struct ast_format_attr *fattr1, const struct ast_format_attr *fattr2)
00082 {
00083    if (!fattr1->format_attr[H264_ATTR_KEY_PROFILE_IDC] || !fattr2->format_attr[H264_ATTR_KEY_PROFILE_IDC] ||
00084        (fattr1->format_attr[H264_ATTR_KEY_PROFILE_IDC] == fattr2->format_attr[H264_ATTR_KEY_PROFILE_IDC])) {
00085       return AST_FORMAT_CMP_EQUAL;
00086    }
00087 
00088    return AST_FORMAT_CMP_NOT_EQUAL;
00089 }
00090 
00091 static int h264_format_attr_get_joint(const struct ast_format_attr *fattr1, const struct ast_format_attr *fattr2, struct ast_format_attr *result)
00092 {
00093    int i;
00094 
00095    for (i = H264_ATTR_KEY_PROFILE_IDC; i < H264_ATTR_KEY_END; i++) {
00096       result->format_attr[i] = fattr1->format_attr[i] ? fattr1->format_attr[i] : fattr2->format_attr[i];
00097    }
00098 
00099    return 0;
00100 }
00101 
00102 static int h264_format_attr_sdp_parse(struct ast_format_attr *format_attr, const char *attributes)
00103 {
00104    char *attribs = ast_strdupa(attributes), *attrib;
00105 
00106    format_attr->format_attr[H264_ATTR_KEY_REDUNDANT_PIC_CAP] = H264_ATTR_KEY_UNSET;
00107    format_attr->format_attr[H264_ATTR_KEY_PARAMETER_ADD] = H264_ATTR_KEY_UNSET;
00108    format_attr->format_attr[H264_ATTR_KEY_PACKETIZATION_MODE] = H264_ATTR_KEY_UNSET;
00109    format_attr->format_attr[H264_ATTR_KEY_LEVEL_ASYMMETRY_ALLOWED] = H264_ATTR_KEY_UNSET;
00110 
00111    while ((attrib = strsep(&attribs, ";"))) {
00112       unsigned int val;
00113       unsigned long int val2;
00114       char sps[H264_MAX_SPS_PPS_SIZE], pps[H264_MAX_SPS_PPS_SIZE];
00115 
00116       if (sscanf(attrib, "profile-level-id=%lx", &val2) == 1) {
00117          format_attr->format_attr[H264_ATTR_KEY_PROFILE_IDC] = ((val2 >> 16) & 0xFF);
00118          format_attr->format_attr[H264_ATTR_KEY_PROFILE_IOP] = ((val2 >> 8) & 0xFF);
00119          format_attr->format_attr[H264_ATTR_KEY_LEVEL] = (val2 & 0xFF);
00120       } else if (sscanf(attrib, "sprop-parameter-sets=%" H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT "[^','],%" H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT "s", sps, pps) == 2) {
00121          /* XXX sprop-parameter-sets can actually be of unlimited length. This may need to be addressed later. */
00122          unsigned char spsdecoded[H264_MAX_SPS_PPS_SIZE] = { 0, }, ppsdecoded[H264_MAX_SPS_PPS_SIZE] = { 0, };
00123          int i;
00124 
00125          ast_base64decode(spsdecoded, sps, sizeof(spsdecoded));
00126          ast_base64decode(ppsdecoded, pps, sizeof(ppsdecoded));
00127 
00128          format_attr->format_attr[H264_ATTR_KEY_SPS_LEN] = 0;
00129          format_attr->format_attr[H264_ATTR_KEY_PPS_LEN] = 0;
00130 
00131          for (i = 0; i < H264_MAX_SPS_PPS_SIZE; i++) {
00132             if (spsdecoded[i]) {
00133                format_attr->format_attr[H264_ATTR_KEY_SPS + i] = spsdecoded[i];
00134                format_attr->format_attr[H264_ATTR_KEY_SPS_LEN]++;
00135             }
00136             if (ppsdecoded[i]) {
00137                format_attr->format_attr[H264_ATTR_KEY_PPS + i] = ppsdecoded[i];
00138                format_attr->format_attr[H264_ATTR_KEY_PPS_LEN]++;
00139             }
00140          }
00141       } else if (sscanf(attrib, "max-mbps=%30u", &val) == 1) {
00142          format_attr->format_attr[H264_ATTR_KEY_MAX_MBPS] = val;
00143       } else if (sscanf(attrib, "max-fs=%30u", &val) == 1) {
00144          format_attr->format_attr[H264_ATTR_KEY_MAX_FS] = val;
00145       } else if (sscanf(attrib, "max-cpb=%30u", &val) == 1) {
00146          format_attr->format_attr[H264_ATTR_KEY_MAX_CPB] = val;
00147       } else if (sscanf(attrib, "max-dpb=%30u", &val) == 1) {
00148          format_attr->format_attr[H264_ATTR_KEY_MAX_DPB] = val;
00149       } else if (sscanf(attrib, "max-br=%30u", &val) == 1) {
00150          format_attr->format_attr[H264_ATTR_KEY_MAX_BR] = val;
00151       } else if (sscanf(attrib, "max-smbps=%30u", &val) == 1) {
00152          format_attr->format_attr[H264_ATTR_KEY_MAX_SMBPS] = val;
00153       } else if (sscanf(attrib, "max-fps=%30u", &val) == 1) {
00154          format_attr->format_attr[H264_ATTR_KEY_MAX_FPS] = val;
00155       } else if (sscanf(attrib, "redundant-pic-cap=%30u", &val) == 1) {
00156          format_attr->format_attr[H264_ATTR_KEY_REDUNDANT_PIC_CAP] = val;
00157       } else if (sscanf(attrib, "parameter-add=%30u", &val) == 1) {
00158          format_attr->format_attr[H264_ATTR_KEY_PARAMETER_ADD] = val;
00159       } else if (sscanf(attrib, "packetization-mode=%30u", &val) == 1) {
00160          format_attr->format_attr[H264_ATTR_KEY_PACKETIZATION_MODE] = val;
00161       } else if (sscanf(attrib, "sprop-interleaving-depth=%30u", &val) == 1) {
00162          format_attr->format_attr[H264_ATTR_KEY_SPROP_INTERLEAVING_DEPTH] = val;
00163       } else if (sscanf(attrib, "sprop-deint-buf-req=%30u", &val) == 1) {
00164          format_attr->format_attr[H264_ATTR_KEY_SPROP_DEINT_BUF_REQ] = val;
00165       } else if (sscanf(attrib, "deint-buf-cap=%30u", &val) == 1) {
00166          format_attr->format_attr[H264_ATTR_KEY_DEINT_BUF_CAP] = val;
00167       } else if (sscanf(attrib, "sprop-init-buf-time=%30u", &val) == 1) {
00168          format_attr->format_attr[H264_ATTR_KEY_SPROP_INIT_BUF_TIME] = val;
00169       } else if (sscanf(attrib, "sprop-max-don-diff=%30u", &val) == 1) {
00170          format_attr->format_attr[H264_ATTR_KEY_SPROP_MAX_DON_DIFF] = val;
00171       } else if (sscanf(attrib, "max-rcmd-nalu-size=%30u", &val) == 1) {
00172          format_attr->format_attr[H264_ATTR_KEY_MAX_RCMD_NALU_SIZE] = val;
00173       } else if (sscanf(attrib, "level-asymmetry-allowed=%30u", &val) == 1) {
00174          format_attr->format_attr[H264_ATTR_KEY_LEVEL_ASYMMETRY_ALLOWED] = val;
00175       }
00176    }
00177 
00178    return 0;
00179 }
00180 
00181 /*! \brief Helper function which converts a key enum into a string value for SDP */
00182 static const char *h264_attr_key_to_str(enum h264_attr_keys key)
00183 {
00184    switch (key) {
00185    case H264_ATTR_KEY_MAX_MBPS:
00186       return "max-mbps";
00187    case H264_ATTR_KEY_MAX_FS:
00188       return "max-fs";
00189    case H264_ATTR_KEY_MAX_CPB:
00190       return "max-cpb";
00191    case H264_ATTR_KEY_MAX_DPB:
00192       return "max-dpb";
00193    case H264_ATTR_KEY_MAX_BR:
00194       return "max-br";
00195    case H264_ATTR_KEY_MAX_SMBPS:
00196       return "max-smbps";
00197    case H264_ATTR_KEY_MAX_FPS:
00198       return "max-fps";
00199    case H264_ATTR_KEY_REDUNDANT_PIC_CAP:
00200       return "redundant-pic-cap";
00201    case H264_ATTR_KEY_PARAMETER_ADD:
00202       return "parameter-add";
00203    case H264_ATTR_KEY_PACKETIZATION_MODE:
00204       return "packetization-mode";
00205    case H264_ATTR_KEY_SPROP_INTERLEAVING_DEPTH:
00206       return "sprop-interleaving-depth";
00207    case H264_ATTR_KEY_SPROP_DEINT_BUF_REQ:
00208       return "sprop-deint-buf-req";
00209    case H264_ATTR_KEY_DEINT_BUF_CAP:
00210       return "deint-buf-cap";
00211    case H264_ATTR_KEY_SPROP_INIT_BUF_TIME:
00212       return "sprop-init-buf-time";
00213    case H264_ATTR_KEY_SPROP_MAX_DON_DIFF:
00214       return "sprop-max-don-diff";
00215    case H264_ATTR_KEY_MAX_RCMD_NALU_SIZE:
00216       return "max-rcmd-nalu-size";
00217    case H264_ATTR_KEY_LEVEL_ASYMMETRY_ALLOWED:
00218       return "level-asymmetry-allowed";
00219    default:
00220       return NULL;
00221    }
00222 
00223    return NULL;
00224 }
00225 
00226 /*! \brief Helper function which determines if the value of an attribute can be placed into the SDP */
00227 static int h264_attr_key_addable(const struct ast_format_attr *format_attr, enum h264_attr_keys key)
00228 {
00229    switch (key) {
00230    case H264_ATTR_KEY_REDUNDANT_PIC_CAP:
00231    case H264_ATTR_KEY_PARAMETER_ADD:
00232    case H264_ATTR_KEY_PACKETIZATION_MODE:
00233    case H264_ATTR_KEY_LEVEL_ASYMMETRY_ALLOWED:
00234       return (format_attr->format_attr[key] != H264_ATTR_KEY_UNSET) ? 1 : 0;
00235    default:
00236       return format_attr->format_attr[key] ? 1 : 0;
00237    }
00238 
00239    return 1;
00240 }
00241 
00242 static void h264_format_attr_sdp_generate(const struct ast_format_attr *format_attr, unsigned int payload, struct ast_str **str)
00243 {
00244    int i, added = 0;
00245 
00246         for (i = H264_ATTR_KEY_PROFILE_IDC; i < H264_ATTR_KEY_END; i++) {
00247                 const char *name;
00248 
00249       if (i == H264_ATTR_KEY_SPS && format_attr->format_attr[H264_ATTR_KEY_SPS] && format_attr->format_attr[H264_ATTR_KEY_PPS]) {
00250          unsigned char spsdecoded[H264_MAX_SPS_PPS_SIZE] = { 0, }, ppsdecoded[H264_MAX_SPS_PPS_SIZE] = { 0, };
00251          int pos;
00252          char sps[H264_MAX_SPS_PPS_SIZE], pps[H264_MAX_SPS_PPS_SIZE];
00253 
00254          for (pos = 0; pos < H264_MAX_SPS_PPS_SIZE; pos++) {
00255             spsdecoded[pos] = format_attr->format_attr[H264_ATTR_KEY_SPS + pos];
00256             ppsdecoded[pos] = format_attr->format_attr[H264_ATTR_KEY_PPS + pos];
00257          }
00258 
00259          ast_base64encode(sps, spsdecoded, format_attr->format_attr[H264_ATTR_KEY_SPS_LEN], H264_MAX_SPS_PPS_SIZE);
00260          ast_base64encode(pps, ppsdecoded, format_attr->format_attr[H264_ATTR_KEY_PPS_LEN], H264_MAX_SPS_PPS_SIZE);
00261 
00262          if (!added) {
00263             ast_str_append(str, 0, "a=fmtp:%d sprop-parameter-sets=%s,%s", payload, sps, pps);
00264             added = 1;
00265          } else {
00266             ast_str_append(str, 0, ";sprop-parameter-sets=%s,%s", sps, pps);
00267          }
00268       } else if (i == H264_ATTR_KEY_PROFILE_IDC && format_attr->format_attr[H264_ATTR_KEY_PROFILE_IDC] &&
00269           format_attr->format_attr[H264_ATTR_KEY_PROFILE_IOP] && format_attr->format_attr[H264_ATTR_KEY_LEVEL]) {
00270          if (!added) {
00271             ast_str_append(str, 0, "a=fmtp:%d profile-level-id=%X%X%X", payload, format_attr->format_attr[H264_ATTR_KEY_PROFILE_IDC],
00272                       format_attr->format_attr[H264_ATTR_KEY_PROFILE_IOP], format_attr->format_attr[H264_ATTR_KEY_LEVEL]);
00273             added = 1;
00274          } else {
00275             ast_str_append(str, 0, ";profile-level-id=%X%X%X", format_attr->format_attr[H264_ATTR_KEY_PROFILE_IDC],
00276                       format_attr->format_attr[H264_ATTR_KEY_PROFILE_IOP], format_attr->format_attr[H264_ATTR_KEY_LEVEL]);
00277          }
00278       } else if ((name = h264_attr_key_to_str(i)) && h264_attr_key_addable(format_attr, i)) {
00279          if (!added) {
00280             ast_str_append(str, 0, "a=fmtp:%d %s=%u", payload, name, format_attr->format_attr[i]);
00281             added = 1;
00282          } else {
00283             ast_str_append(str, 0, ";%s=%u", name, format_attr->format_attr[i]);
00284          }
00285       }
00286    }
00287    
00288    if (added) {
00289       ast_str_append(str, 0, "\r\n");
00290    }
00291 
00292    return;
00293 }
00294 
00295 static struct ast_format_attr_interface h264_format_attr_interface = {
00296    .id = AST_FORMAT_H264,
00297    .format_attr_cmp = h264_format_attr_cmp,
00298    .format_attr_get_joint = h264_format_attr_get_joint,
00299    .format_attr_sdp_parse = h264_format_attr_sdp_parse,
00300    .format_attr_sdp_generate = h264_format_attr_sdp_generate,
00301 };
00302 
00303 static int unload_module(void)
00304 {
00305    ast_format_attr_unreg_interface(&h264_format_attr_interface);
00306 
00307    return 0;
00308 }
00309 
00310 static int load_module(void)
00311 {
00312    if (ast_format_attr_reg_interface(&h264_format_attr_interface)) {
00313       return AST_MODULE_LOAD_DECLINE;
00314    }
00315 
00316    return AST_MODULE_LOAD_SUCCESS;
00317 }
00318 
00319 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "H.264 Format Attribute Module",
00320    .load = load_module,
00321    .unload = unload_module,
00322    .load_pri = AST_MODPRI_DEFAULT,
00323 );