Asterisk - The Open Source Telephony Project  18.5.0
res_format_attr_h264.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012, Digium, Inc.
5  *
6  * Joshua Colp <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  *
18  */
19 
20 /*! \file
21  *
22  * \brief H.264 Format Attribute Module
23  *
24  * \author\verbatim Joshua Colp <[email protected]> \endverbatim
25  *
26  * This is a format attribute module for the H.264 codec.
27  * \ingroup applications
28  */
29 
30 /*** MODULEINFO
31  <support_level>core</support_level>
32  ***/
33 
34 #include "asterisk.h"
35 
36 #include "asterisk/module.h"
37 #include "asterisk/format.h"
38 
39 /*! \brief Value that indicates an attribute is actually unset */
40 #define H264_ATTR_KEY_UNSET UINT8_MAX
41 
42 /*! \brief Maximum size for SPS / PPS values in sprop-parameter-sets attribute
43  * if you change this value then you must change H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT
44  * as well. */
45 #define H264_MAX_SPS_PPS_SIZE 16
46 /*! \brief This is used when executing sscanf on buffers of H264_MAX_SPS_PPS_SIZE
47  * length. It must ALWAYS be a string literal representation of one less than
48  * H264_MAX_SPS_PPS_SIZE */
49 #define H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT "15"
50 
51 struct h264_attr {
52  unsigned int PROFILE_IDC;
53  unsigned int PROFILE_IOP;
54  unsigned int LEVEL;
55  unsigned int MAX_MBPS;
56  unsigned int MAX_FS;
57  unsigned int MAX_CPB;
58  unsigned int MAX_DPB;
59  unsigned int MAX_BR;
60  unsigned int MAX_SMBPS;
61  unsigned int MAX_FPS;
62  unsigned int REDUNDANT_PIC_CAP;
63  unsigned int PARAMETER_ADD;
64  unsigned int PACKETIZATION_MODE;
66  unsigned int SPROP_DEINT_BUF_REQ;
67  unsigned int DEINT_BUF_CAP;
68  unsigned int SPROP_INIT_BUF_TIME;
69  unsigned int SPROP_MAX_DON_DIFF;
70  unsigned int MAX_RCMD_NALU_SIZE;
74 };
75 
76 static void h264_destroy(struct ast_format *format)
77 {
78  struct h264_attr *attr = ast_format_get_attribute_data(format);
79 
80  ast_free(attr);
81 }
82 
83 static int h264_clone(const struct ast_format *src, struct ast_format *dst)
84 {
85  struct h264_attr *original = ast_format_get_attribute_data(src);
86  struct h264_attr *attr = ast_calloc(1, sizeof(*attr));
87 
88  if (!attr) {
89  return -1;
90  }
91 
92  if (original) {
93  *attr = *original;
94  }
95 
97 
98  return 0;
99 }
100 
101 static enum ast_format_cmp_res h264_cmp(const struct ast_format *format1, const struct ast_format *format2)
102 {
103  struct h264_attr *attr1 = ast_format_get_attribute_data(format1);
104  struct h264_attr *attr2 = ast_format_get_attribute_data(format2);
105 
106  if (!attr1 || !attr1->PROFILE_IDC || !attr2 || !attr2->PROFILE_IDC ||
107  (attr1->PROFILE_IDC == attr2->PROFILE_IDC)) {
108  return AST_FORMAT_CMP_EQUAL;
109  }
110 
112 }
113 
114 #define DETERMINE_JOINT(joint, attr1, attr2, field) (joint->field = (attr1 && attr1->field) ? attr1->field : (attr2 && attr2->field) ? attr2->field : 0)
115 
116 static struct ast_format *h264_getjoint(const struct ast_format *format1, const struct ast_format *format2)
117 {
118  struct ast_format *cloned;
119  struct h264_attr *attr, *attr1, *attr2;
120 
121  cloned = ast_format_clone(format1);
122  if (!cloned) {
123  return NULL;
124  }
125  attr = ast_format_get_attribute_data(cloned);
126 
127  attr1 = ast_format_get_attribute_data(format1);
128  attr2 = ast_format_get_attribute_data(format2);
129 
130  DETERMINE_JOINT(attr, attr1, attr2, PROFILE_IDC);
131  DETERMINE_JOINT(attr, attr1, attr2, PROFILE_IOP);
132  DETERMINE_JOINT(attr, attr1, attr2, LEVEL);
133  DETERMINE_JOINT(attr, attr1, attr2, MAX_MBPS);
134  DETERMINE_JOINT(attr, attr1, attr2, MAX_FS);
135  DETERMINE_JOINT(attr, attr1, attr2, MAX_CPB);
136  DETERMINE_JOINT(attr, attr1, attr2, MAX_DPB);
137  DETERMINE_JOINT(attr, attr1, attr2, MAX_BR);
138  DETERMINE_JOINT(attr, attr1, attr2, MAX_SMBPS);
139  DETERMINE_JOINT(attr, attr1, attr2, MAX_FPS);
140  DETERMINE_JOINT(attr, attr1, attr2, REDUNDANT_PIC_CAP);
141  DETERMINE_JOINT(attr, attr1, attr2, PARAMETER_ADD);
142  DETERMINE_JOINT(attr, attr1, attr2, SPROP_INTERLEAVING_DEPTH);
143  DETERMINE_JOINT(attr, attr1, attr2, SPROP_DEINT_BUF_REQ);
144  DETERMINE_JOINT(attr, attr1, attr2, DEINT_BUF_CAP);
145  DETERMINE_JOINT(attr, attr1, attr2, SPROP_INIT_BUF_TIME);
146  DETERMINE_JOINT(attr, attr1, attr2, SPROP_MAX_DON_DIFF);
147  DETERMINE_JOINT(attr, attr1, attr2, MAX_RCMD_NALU_SIZE);
148  DETERMINE_JOINT(attr, attr1, attr2, LEVEL_ASYMMETRY_ALLOWED);
149  DETERMINE_JOINT(attr, attr1, attr2, PACKETIZATION_MODE);
150 
151  if (attr1 && !ast_strlen_zero(attr1->SPS)) {
152  ast_copy_string(attr->SPS, attr1->SPS, sizeof(attr->SPS));
153  } else if (attr2 && !ast_strlen_zero(attr2->SPS)) {
154  ast_copy_string(attr->SPS, attr2->SPS, sizeof(attr->SPS));
155  }
156 
157  if (attr1 && !ast_strlen_zero(attr1->PPS)) {
158  ast_copy_string(attr->PPS, attr1->PPS, sizeof(attr->PPS));
159  } else if (attr2 && !ast_strlen_zero(attr2->PPS)) {
160  ast_copy_string(attr->PPS, attr2->PPS, sizeof(attr->PPS));
161  }
162 
163  return cloned;
164 }
165 
166 static struct ast_format *h264_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
167 {
168  char *attribs = ast_strdupa(attributes), *attrib;
169  struct ast_format *cloned;
170  struct h264_attr *attr;
171 
172  cloned = ast_format_clone(format);
173  if (!cloned) {
174  return NULL;
175  }
176  attr = ast_format_get_attribute_data(cloned);
177 
182 
183  while ((attrib = strsep(&attribs, ";"))) {
184  unsigned int val;
185  unsigned long int val2;
186 
187  attrib = ast_strip(attrib);
188 
189  if (sscanf(attrib, "profile-level-id=%lx", &val2) == 1) {
190  attr->PROFILE_IDC = ((val2 >> 16) & 0xFF);
191  attr->PROFILE_IOP = ((val2 >> 8) & 0xFF);
192  attr->LEVEL = (val2 & 0xFF);
193  } else if (sscanf(attrib, "sprop-parameter-sets=%" H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT "[^','],%" H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT "s", attr->SPS, attr->PPS) == 2) {
194  /* XXX sprop-parameter-sets can actually be of unlimited length. This may need to be addressed later. */
195  } else if (sscanf(attrib, "max-mbps=%30u", &val) == 1) {
196  attr->MAX_MBPS = val;
197  } else if (sscanf(attrib, "max-fs=%30u", &val) == 1) {
198  attr->MAX_FS = val;
199  } else if (sscanf(attrib, "max-cpb=%30u", &val) == 1) {
200  attr->MAX_CPB = val;
201  } else if (sscanf(attrib, "max-dpb=%30u", &val) == 1) {
202  attr->MAX_DPB = val;
203  } else if (sscanf(attrib, "max-br=%30u", &val) == 1) {
204  attr->MAX_BR = val;
205  } else if (sscanf(attrib, "max-smbps=%30u", &val) == 1) {
206  attr->MAX_SMBPS = val;
207  } else if (sscanf(attrib, "max-fps=%30u", &val) == 1) {
208  attr->MAX_FPS = val;
209  } else if (sscanf(attrib, "redundant-pic-cap=%30u", &val) == 1) {
210  attr->REDUNDANT_PIC_CAP = val;
211  } else if (sscanf(attrib, "parameter-add=%30u", &val) == 1) {
212  attr->PARAMETER_ADD = val;
213  } else if (sscanf(attrib, "packetization-mode=%30u", &val) == 1) {
214  attr->PACKETIZATION_MODE = val;
215  } else if (sscanf(attrib, "sprop-interleaving-depth=%30u", &val) == 1) {
216  attr->SPROP_INTERLEAVING_DEPTH = val;
217  } else if (sscanf(attrib, "sprop-deint-buf-req=%30u", &val) == 1) {
218  attr->SPROP_DEINT_BUF_REQ = val;
219  } else if (sscanf(attrib, "deint-buf-cap=%30u", &val) == 1) {
220  attr->DEINT_BUF_CAP = val;
221  } else if (sscanf(attrib, "sprop-init-buf-time=%30u", &val) == 1) {
222  attr->SPROP_INIT_BUF_TIME = val;
223  } else if (sscanf(attrib, "sprop-max-don-diff=%30u", &val) == 1) {
224  attr->SPROP_MAX_DON_DIFF = val;
225  } else if (sscanf(attrib, "max-rcmd-nalu-size=%30u", &val) == 1) {
226  attr->MAX_RCMD_NALU_SIZE = val;
227  } else if (sscanf(attrib, "level-asymmetry-allowed=%30u", &val) == 1) {
228  attr->LEVEL_ASYMMETRY_ALLOWED = val;
229  }
230  }
231 
232  return cloned;
233 }
234 
235 #define APPEND_IF_NOT_H264_UNSET(field, str, name) do { \
236  if (field != H264_ATTR_KEY_UNSET) { \
237  if (added) { \
238  ast_str_append(str, 0, ";"); \
239  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { \
240  added = 1; \
241  } \
242  ast_str_append(str, 0, "%s=%u", name, field); \
243  } \
244 } while (0)
245 
246 #define APPEND_IF_NONZERO(field, str, name) do { \
247  if (field) { \
248  if (added) { \
249  ast_str_append(str, 0, ";"); \
250  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) { \
251  added = 1; \
252  } \
253  ast_str_append(str, 0, "%s=%u", name, field); \
254  } \
255 } while (0)
256 
257 static void h264_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
258 {
259  struct h264_attr *attr = ast_format_get_attribute_data(format);
260  int added = 0;
261 
262  if (!attr) {
263  return;
264  }
265 
266  APPEND_IF_NONZERO(attr->MAX_MBPS, str, "max-mbps");
267  APPEND_IF_NONZERO(attr->MAX_FS, str, "max-fs");
268  APPEND_IF_NONZERO(attr->MAX_CPB, str, "max-cpb");
269  APPEND_IF_NONZERO(attr->MAX_DPB, str, "max-dpb");
270  APPEND_IF_NONZERO(attr->MAX_BR, str, "max-br");
271  APPEND_IF_NONZERO(attr->MAX_SMBPS, str, "max-smbps");
272  APPEND_IF_NONZERO(attr->MAX_FPS, str, "max-fps");
273  APPEND_IF_NONZERO(attr->SPROP_INTERLEAVING_DEPTH, str, "sprop-interleaving-depth");
274  APPEND_IF_NONZERO(attr->SPROP_DEINT_BUF_REQ, str, "sprop-deint-buf-req");
275  APPEND_IF_NONZERO(attr->DEINT_BUF_CAP, str, "deint-buf-cap");
276  APPEND_IF_NONZERO(attr->SPROP_INIT_BUF_TIME, str, "sprop-init-buf-time");
277  APPEND_IF_NONZERO(attr->SPROP_MAX_DON_DIFF, str, "sprop-max-don-diff");
278  APPEND_IF_NONZERO(attr->MAX_RCMD_NALU_SIZE, str, "max-rcmd-nalu-size");
279 
280  APPEND_IF_NOT_H264_UNSET(attr->REDUNDANT_PIC_CAP, str, "redundant-pic-cap");
281  APPEND_IF_NOT_H264_UNSET(attr->PARAMETER_ADD, str, "parameter-add");
282  APPEND_IF_NOT_H264_UNSET(attr->PACKETIZATION_MODE, str, "packetization-mode");
283  APPEND_IF_NOT_H264_UNSET(attr->LEVEL_ASYMMETRY_ALLOWED, str, "level-asymmetry-allowed");
284 
285  if (attr->PROFILE_IDC && attr->LEVEL) {
286  if (added) {
287  ast_str_append(str, 0, ";");
288  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
289  added = 1;
290  }
291  ast_str_append(str, 0, "profile-level-id=%02X%02X%02X", attr->PROFILE_IDC, attr->PROFILE_IOP, attr->LEVEL);
292  }
293 
294  if (!ast_strlen_zero(attr->SPS) && !ast_strlen_zero(attr->PPS)) {
295  if (added) {
296  ast_str_append(str, 0, ";");
297  } else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
298  added = 1;
299  }
300  ast_str_append(str, 0, "sprop-parameter-sets=%s,%s", attr->SPS, attr->PPS);
301  }
302 
303  if (added) {
304  ast_str_append(str, 0, "\r\n");
305  }
306 
307  return;
308 }
309 
312  .format_clone = h264_clone,
313  .format_cmp = h264_cmp,
314  .format_get_joint = h264_getjoint,
315  .format_parse_sdp_fmtp = h264_parse_sdp_fmtp,
316  .format_generate_sdp_fmtp = h264_generate_sdp_fmtp,
317 };
318 
319 static int unload_module(void)
320 {
321  return 0;
322 }
323 
324 static int load_module(void)
325 {
326  if (ast_format_interface_register("h264", &h264_interface)) {
328  }
329 
331 }
332 
333 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "H.264 Format Attribute Module",
334  .support_level = AST_MODULE_SUPPORT_CORE,
335  .load = load_module,
336  .unload = unload_module,
337 );
#define APPEND_IF_NONZERO(field, str, name)
#define APPEND_IF_NOT_H264_UNSET(field, str, name)
Asterisk main include file. File version handling, generic pbx functions.
static struct ast_format * h264_getjoint(const struct ast_format *format1, const struct ast_format *format2)
Optional format interface to extend format operations.
Definition: format.h:44
unsigned int MAX_FPS
Definition: ast_expr2.c:325
char SPS[H264_MAX_SPS_PPS_SIZE]
unsigned int MAX_RCMD_NALU_SIZE
static enum ast_format_cmp_res h264_cmp(const struct ast_format *format1, const struct ast_format *format2)
unsigned int DEINT_BUF_CAP
unsigned int PROFILE_IOP
unsigned int SPROP_INTERLEAVING_DEPTH
#define H264_MAX_SPS_PPS_SIZE
Maximum size for SPS / PPS values in sprop-parameter-sets attribute if you change this value then you...
Definition of a media format.
Definition: format.c:43
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
void * ast_format_get_attribute_data(const struct ast_format *format)
Get the attribute data on a format.
Definition: format.c:125
const char * str
Definition: app_jack.c:147
#define NULL
Definition: resample.c:96
char PPS[H264_MAX_SPS_PPS_SIZE]
Media Format API.
#define ast_strlen_zero(foo)
Definition: strings.h:52
static struct ast_format * h264_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
unsigned int MAX_DPB
static void h264_destroy(struct ast_format *format)
unsigned int PARAMETER_ADD
struct ast_format * ast_format_clone(const struct ast_format *format)
Clone an existing media format so it can be modified.
Definition: format.c:180
unsigned int SPROP_DEINT_BUF_REQ
void(*const format_destroy)(struct ast_format *format)
Callback for when the format is destroyed, used to release attribute resources.
Definition: format.h:50
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
ast_format_cmp_res
Format comparison results.
Definition: format.h:34
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
unsigned int PROFILE_IDC
#define H264_MAX_SPS_PPS_SIZE_SCAN_LIMIT
This is used when executing sscanf on buffers of H264_MAX_SPS_PPS_SIZE length. It must ALWAYS be a st...
unsigned int LEVEL_ASYMMETRY_ALLOWED
unsigned int MAX_CPB
static int load_module(void)
unsigned int MAX_MBPS
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define H264_ATTR_KEY_UNSET
Value that indicates an attribute is actually unset.
#define ast_format_interface_register(codec, interface)
Register a format interface for use with the provided codec.
Definition: format.h:273
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static struct ast_format_interface h264_interface
unsigned int PACKETIZATION_MODE
unsigned int MAX_FS
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
unsigned int SPROP_INIT_BUF_TIME
unsigned int REDUNDANT_PIC_CAP
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
static int h264_clone(const struct ast_format *src, struct ast_format *dst)
char * strsep(char **str, const char *delims)
void ast_format_set_attribute_data(struct ast_format *format, void *attribute_data)
Set the attribute data on a format.
Definition: format.c:130
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
unsigned int MAX_BR
static int unload_module(void)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
#define DETERMINE_JOINT(joint, attr1, attr2, field)
Asterisk module definitions.
static snd_pcm_format_t format
Definition: chan_alsa.c:102
unsigned int MAX_SMBPS
unsigned int SPROP_MAX_DON_DIFF
static void h264_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
unsigned int LEVEL