1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2015, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Seven Du <dujinfang@gmail.com>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Anthony Minessale II <anthm@freeswitch.org>
27  * Seven Du <dujinfang@gmail.com>
28  * Sam Russell <sam.h.russell@gmail.com>
29  *
30  * mod_vpx.c -- VP8/9 Video Codec, with transcoding
31  *
32  */
33 
34 #include <switch.h>
35 #ifdef SWITCH_HAVE_YUV
36 #ifdef SWITCH_HAVE_VPX
37 #include <vpx/vpx_encoder.h>
38 #include <vpx/vpx_decoder.h>
39 #include <vpx/vp8cx.h>
40 #include <vpx/vp8dx.h>
41 #include <vpx/vp8.h>
42 
43 // #define DEBUG_VP9
44 
45 #ifdef DEBUG_VP9
46 #define VPX_SWITCH_LOG_LEVEL SWITCH_LOG_ERROR
47 #else
48 #define VPX_SWITCH_LOG_LEVEL SWITCH_LOG_DEBUG1
49 #endif
50 
51 #define SLICE_SIZE SWITCH_DEFAULT_VIDEO_SIZE
52 #define KEY_FRAME_MIN_FREQ 250000
53 
54 #define CODEC_TYPE_ANY 0
55 #define CODEC_TYPE_VP8 8
56 #define CODEC_TYPE_VP9 9
57 
58 typedef struct my_vpx_cfg_s {
59 	char name[64];
60 	int lossless;
61 	int cpuused;
62 	int token_parts;
63 	int static_thresh;
64 	int noise_sensitivity;
65 	int max_intra_bitrate_pct;
66 	vp9e_tune_content tune_content;
67 
68 	vpx_codec_enc_cfg_t enc_cfg;
69 	vpx_codec_dec_cfg_t dec_cfg;
70 	switch_event_t *codecs;
71 } my_vpx_cfg_t;
72 
73 #define SHOW(cfg, field) switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "    %-28s = %d\n", #field, cfg->field)
74 
show_config(my_vpx_cfg_t * my_cfg,vpx_codec_enc_cfg_t * cfg)75 static void show_config(my_vpx_cfg_t *my_cfg, vpx_codec_enc_cfg_t *cfg)
76 {
77 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "    %-28s = %s\n", "name", my_cfg->name);
78 	switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "    %-28s = %d\n", "decoder.threads", my_cfg->dec_cfg.threads);
79 
80 	SHOW(my_cfg, lossless);
81 	SHOW(my_cfg, cpuused);
82 	SHOW(my_cfg, token_parts);
83 	SHOW(my_cfg, static_thresh);
84 	SHOW(my_cfg, noise_sensitivity);
85 	SHOW(my_cfg, max_intra_bitrate_pct);
86 	SHOW(my_cfg, tune_content);
87 
88 	SHOW(cfg, g_usage);
89 	SHOW(cfg, g_threads);
90 	SHOW(cfg, g_profile);
91 	SHOW(cfg, g_w);
92 	SHOW(cfg, g_h);
93 	SHOW(cfg, g_bit_depth);
94 	SHOW(cfg, g_input_bit_depth);
95 	SHOW(cfg, g_timebase.num);
96 	SHOW(cfg, g_timebase.den);
97 	SHOW(cfg, g_error_resilient);
98 	SHOW(cfg, g_pass);
99 	SHOW(cfg, g_lag_in_frames);
100 	SHOW(cfg, rc_dropframe_thresh);
101 	SHOW(cfg, rc_resize_allowed);
102 	SHOW(cfg, rc_scaled_width);
103 	SHOW(cfg, rc_scaled_height);
104 	SHOW(cfg, rc_resize_up_thresh);
105 	SHOW(cfg, rc_resize_down_thresh);
106 	SHOW(cfg, rc_end_usage);
107 	SHOW(cfg, rc_target_bitrate);
108 	SHOW(cfg, rc_min_quantizer);
109 	SHOW(cfg, rc_max_quantizer);
110 	SHOW(cfg, rc_undershoot_pct);
111 	SHOW(cfg, rc_overshoot_pct);
112 	SHOW(cfg, rc_buf_sz);
113 	SHOW(cfg, rc_buf_initial_sz);
114 	SHOW(cfg, rc_buf_optimal_sz);
115 	SHOW(cfg, rc_2pass_vbr_bias_pct);
116 	SHOW(cfg, rc_2pass_vbr_minsection_pct);
117 	SHOW(cfg, rc_2pass_vbr_maxsection_pct);
118 	SHOW(cfg, kf_mode);
119 	SHOW(cfg, kf_min_dist);
120 	SHOW(cfg, kf_max_dist);
121 	SHOW(cfg, ss_number_layers);
122 	SHOW(cfg, ts_number_layers);
123 	SHOW(cfg, ts_periodicity);
124 	SHOW(cfg, temporal_layering_mode);
125 
126 	if (my_cfg->codecs) {
127 		switch_event_header_t *hp;
128 
129 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "======== Codec specific profiles ========\n");
130 
131 		for (hp = my_cfg->codecs->headers; hp; hp = hp->next) {
132 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "    %-28s = %s\n", hp->name, hp->value);
133 		}
134 	}
135 }
136 
137 /*	http://tools.ietf.org/html/draft-ietf-payload-vp8-10
138 
139 	The first octets after the RTP header are the VP8 payload descriptor, with the following structure.
140 #endif
141 
142 	     0 1 2 3 4 5 6 7
143 	    +-+-+-+-+-+-+-+-+
144 	    |X|R|N|S|R| PID | (REQUIRED)
145 	    +-+-+-+-+-+-+-+-+
146 	X:  |I|L|T|K| RSV   | (OPTIONAL)
147 	    +-+-+-+-+-+-+-+-+
148 	I:  |M| PictureID   | (OPTIONAL)
149 	    +-+-+-+-+-+-+-+-+
150 	L:  |   TL0PICIDX   | (OPTIONAL)
151 	    +-+-+-+-+-+-+-+-+
152 	T/K:|TID|Y| KEYIDX  | (OPTIONAL)
153 	    +-+-+-+-+-+-+-+-+
154 
155 
156 	VP8 Payload Header
157 
158 	 0 1 2 3 4 5 6 7
159 	+-+-+-+-+-+-+-+-+
160 	|Size0|H| VER |P|
161 	+-+-+-+-+-+-+-+-+
162 	|     Size1     |
163 	+-+-+-+-+-+-+-+-+
164 	|     Size2     |
165 	+-+-+-+-+-+-+-+-+
166 	| Bytes 4..N of |
167 	| VP8 payload   |
168 	:               :
169 	+-+-+-+-+-+-+-+-+
170 	| OPTIONAL RTP  |
171 	| padding       |
172 	:               :
173 	+-+-+-+-+-+-+-+-+
174 */
175 
176 
177 #ifdef _MSC_VER
178 #pragma pack(push, r1, 1)
179 #endif
180 
181 #if SWITCH_BYTE_ORDER == __BIG_ENDIAN
182 
183 typedef struct {
184 	unsigned extended:1;
185 	unsigned reserved1:1;
186 	unsigned non_referenced:1;
187 	unsigned start:1;
188 	unsigned reserved2:1;
189 	unsigned pid:3;
190 	unsigned I:1;
191 	unsigned L:1;
192 	unsigned T:1;
193 	unsigned K:1;
194 	unsigned RSV:4;
195 	unsigned M:1;
196 	unsigned PID:15;
197 	unsigned TL0PICIDX:8;
198 	unsigned TID:2;
199 	unsigned Y:1;
200 	unsigned KEYIDX:5;
201 } vp8_payload_descriptor_t;
202 
203 typedef struct {
204 	unsigned have_pid:1;
205 	unsigned have_p_layer:1;
206 	unsigned have_layer_ind:1;
207 	unsigned is_flexible:1;
208 	unsigned start:1;
209 	unsigned end:1;
210 	unsigned have_ss:1;
211 	unsigned zero:1;
212 } vp9_payload_descriptor_t;
213 
214 typedef struct {
215 	unsigned n_s:3;
216 	unsigned y:1;
217 	unsigned g:1;
218 	unsigned zero:3;
219 } vp9_ss_t;
220 
221 typedef struct {
222 	unsigned t:3;
223 	unsigned u:1;
224 	unsigned r:2;
225 	unsigned zero:2;
226 } vp9_n_g_t;
227 
228 typedef struct {
229 	unsigned temporal_id:3;
230 	unsigned temporal_up_switch:1;
231 	unsigned spatial_id:3;
232 	unsigned inter_layer_predicted:1;
233 } vp9_p_layer_t;
234 
235 #else /* ELSE LITTLE */
236 
237 typedef struct {
238 	unsigned pid:3;
239 	unsigned reserved2:1;
240 	unsigned start:1;
241 	unsigned non_referenced:1;
242 	unsigned reserved1:1;
243 	unsigned extended:1;
244 	unsigned RSV:4;
245 	unsigned K:1;
246 	unsigned T:1;
247 	unsigned L:1;
248 	unsigned I:1;
249 	unsigned PID:15;
250 	unsigned M:1;
251 	unsigned TL0PICIDX:8;
252 	unsigned KEYIDX:5;
253 	unsigned Y:1;
254 	unsigned TID:2;
255 } vp8_payload_descriptor_t;
256 
257 typedef struct {
258 	unsigned zero:1;
259 	unsigned have_ss:1;
260 	unsigned end:1;
261 	unsigned start:1;
262 	unsigned is_flexible:1;
263 	unsigned have_layer_ind:1;
264 	unsigned have_p_layer:1;
265 	unsigned have_pid:1;
266 } vp9_payload_descriptor_t;
267 
268 typedef struct {
269 	unsigned zero:3;
270 	unsigned g:1;
271 	unsigned y:1;
272 	unsigned n_s:3;
273 } vp9_ss_t;
274 
275 typedef struct {
276 	unsigned zero:2;
277 	unsigned r:2;
278 	unsigned u:1;
279 	unsigned t:3;
280 } vp9_n_g_t;
281 
282 typedef struct {
283 	unsigned inter_layer_predicted:1;
284 	unsigned spatial_id:3;
285 	unsigned temporal_up_switch:1;
286 	unsigned temporal_id:3;
287 } vp9_p_layer_t;
288 
289 #endif
290 
291 typedef union {
292 	vp8_payload_descriptor_t vp8;
293 	vp9_payload_descriptor_t vp9;
294 } vpx_payload_descriptor_t;
295 
296 #define kMaxVp9NumberOfSpatialLayers 16
297 
298 typedef struct {
299 	switch_bool_t has_received_sli;
300 	uint8_t picture_id_sli;
301 	switch_bool_t has_received_rpsi;
302 	uint64_t picture_id_rpsi;
303 	int16_t picture_id;  // Negative value to skip pictureId.
304 
305 	switch_bool_t inter_pic_predicted;  // This layer frame is dependent on previously
306 	                           // coded frame(s).
307 	switch_bool_t flexible_mode;
308 	switch_bool_t ss_data_available;
309 
310 	int tl0_pic_idx;  // Negative value to skip tl0PicIdx.
311 	uint8_t temporal_idx;
312 	uint8_t spatial_idx;
313 	switch_bool_t temporal_up_switch;
314 	switch_bool_t inter_layer_predicted;  // Frame is dependent on directly lower spatial
315 	                             // layer frame.
316 	uint8_t gof_idx;
317 
318 	// SS data.
319 	size_t num_spatial_layers;
320 	switch_bool_t spatial_layer_resolution_present;
321 	uint16_t width[kMaxVp9NumberOfSpatialLayers];
322 	uint16_t height[kMaxVp9NumberOfSpatialLayers];
323 	// GofInfoVP9 gof;
324 } vp9_info_t;
325 
326 
327 #ifdef _MSC_VER
328 #pragma pack(pop, r1)
329 #endif
330 
331 
332 #define __IS_VP8_KEY_FRAME(byte) !(((byte) & 0x01))
IS_VP8_KEY_FRAME(uint8_t * data)333 static inline int IS_VP8_KEY_FRAME(uint8_t *data)
334 {
335 	uint8_t S;
336 	uint8_t DES;
337 	uint8_t PID;
338 
339 	DES = *data;
340 	data++;
341 	S = DES & 0x10;
342 	PID = DES & 0x07;
343 
344 	if (DES & 0x80) { // X
345 		uint8_t X = *data;
346 		data++;
347 		if (X & 0x80) { // I
348 			uint8_t M = (*data) & 0x80;
349 			data++;
350 			if (M) data++;
351 		}
352 		if (X & 0x40) data++; // L
353 		if (X & 0x30) data++; // T/K
354 	}
355 
356 	if (S && (PID == 0)) {
357 		return __IS_VP8_KEY_FRAME(*data);
358 	} else {
359 		// if (PID > 0) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PID: %d\n", PID);
360 		return 0;
361 	}
362 }
363 
364 #define IS_VP9_KEY_FRAME(byte) ((((byte) & 0x40) == 0) && ((byte) & 0x0A))
365 #define IS_VP9_START_PKT(byte) ((byte) & 0x08)
366 
367 #ifdef WIN32
368 #undef SWITCH_MOD_DECLARE_DATA
369 #define SWITCH_MOD_DECLARE_DATA __declspec(dllexport)
370 #endif
371 SWITCH_MODULE_LOAD_FUNCTION(mod_vpx_load);
372 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vpx_shutdown);
373 SWITCH_MODULE_DEFINITION(CORE_VPX_MODULE, mod_vpx_load, mod_vpx_shutdown, NULL);
374 
375 struct vpx_context {
376 	int debug;
377 	switch_codec_t *codec;
378 	int is_vp9;
379 	vp9_info_t vp9;
380 	vpx_codec_iface_t *encoder_interface;
381 	vpx_codec_iface_t *decoder_interface;
382 	unsigned int flags;
383 	switch_codec_settings_t codec_settings;
384 	unsigned int bandwidth;
385 	vpx_codec_enc_cfg_t config;
386 	switch_time_t last_key_frame;
387 
388 	vpx_codec_ctx_t	encoder;
389 	uint8_t encoder_init;
390 	vpx_image_t *pic;
391 	switch_bool_t force_key_frame;
392 	int fps;
393 	int format;
394 	int intra_period;
395 	int num;
396 	int partition_index;
397 	const vpx_codec_cx_pkt_t *pkt;
398 	vpx_codec_iter_t enc_iter;
399 	vpx_codec_iter_t dec_iter;
400 	uint32_t last_ts;
401 	switch_time_t last_ms;
402 	vpx_codec_ctx_t	decoder;
403 	uint8_t decoder_init;
404 	int decoded_first_frame;
405 	switch_buffer_t *vpx_packet_buffer;
406 	int got_key_frame;
407 	int no_key_frame;
408 	int got_start_frame;
409 	uint32_t last_received_timestamp;
410 	switch_bool_t last_received_complete_picture;
411 	uint16_t last_received_seq;
412 	int need_key_frame;
413 	int need_encoder_reset;
414 	int need_decoder_reset;
415 	int32_t change_bandwidth;
416 	uint64_t framecount;
417 	switch_memory_pool_t *pool;
418 	switch_buffer_t *pbuffer;
419 	switch_time_t start_time;
420 	switch_image_t *patch_img;
421 	int16_t picture_id;
422 };
423 typedef struct vpx_context vpx_context_t;
424 
425 #define MAX_PROFILES 100
426 
427 struct vpx_globals {
428 	int debug;
429 	uint32_t max_bitrate;
430 	uint32_t rtp_slice_size;
431 	uint32_t key_frame_min_freq;
432 
433 	uint32_t dec_threads;
434 	uint32_t enc_threads;
435 
436 	my_vpx_cfg_t *profiles[MAX_PROFILES];
437 };
438 
439 struct vpx_globals vpx_globals = { 0 };
440 
441 static my_vpx_cfg_t *find_cfg_profile(const char *name, switch_bool_t reconfig);
442 static void parse_profile(my_vpx_cfg_t *my_cfg, switch_xml_t profile, int codec_type);
443 
init_decoder(switch_codec_t * codec)444 static switch_status_t init_decoder(switch_codec_t *codec)
445 {
446 	vpx_context_t *context = (vpx_context_t *)codec->private_info;
447 
448 	//if (context->decoder_init) {
449 	//	vpx_codec_destroy(&context->decoder);
450 	//	context->decoder_init = 0;
451 	//}
452 
453 	if (context->flags & SWITCH_CODEC_FLAG_DECODE && !context->decoder_init) {
454 		vpx_codec_dec_cfg_t cfg = {0, 0, 0};
455 		vpx_codec_flags_t dec_flags = 0;
456 		vp8_postproc_cfg_t ppcfg;
457 		my_vpx_cfg_t *my_cfg = NULL;
458 		vpx_codec_err_t err;
459 
460 		if (context->is_vp9) {
461 			my_cfg = find_cfg_profile("vp9", SWITCH_FALSE);
462 		} else {
463 			my_cfg = find_cfg_profile("vp8", SWITCH_FALSE);
464 		}
465 
466 		if (!my_cfg) return SWITCH_STATUS_FALSE;
467 
468 		cfg.threads = my_cfg->dec_cfg.threads;
469 
470 		if ((err = vpx_codec_dec_init(&context->decoder, context->decoder_interface, &cfg, dec_flags)) != VPX_CODEC_OK) {
471 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_ERROR,
472 				"VPX decoder %s codec init error: [%d:%s:%s]\n",
473 				vpx_codec_iface_name(context->decoder_interface),
474 				err, vpx_codec_error(&context->decoder), vpx_codec_error_detail(&context->decoder));
475 			return SWITCH_STATUS_FALSE;
476 		}
477 
478 		context->last_ts = 0;
479 		context->last_received_timestamp = 0;
480 		context->last_received_complete_picture = 0;
481 		context->last_received_seq = 0;
482 		context->decoder_init = 1;
483 		context->got_key_frame = 0;
484 		context->no_key_frame = 0;
485 		context->got_start_frame = 0;
486 		// the types of post processing to be done, should be combination of "vp8_postproc_level"
487 		ppcfg.post_proc_flag = VP8_DEBLOCK;//VP8_DEMACROBLOCK | VP8_DEBLOCK;
488 		// the strength of deblocking, valid range [0, 16]
489 		ppcfg.deblocking_level = 1;
490 		// Set deblocking settings
491 		vpx_codec_control(&context->decoder, VP8_SET_POSTPROC, &ppcfg);
492 
493 		if (context->vpx_packet_buffer) {
494 			switch_buffer_zero(context->vpx_packet_buffer);
495 		} else {
496 			switch_buffer_create_dynamic(&context->vpx_packet_buffer, 512, 512, 0);
497 		}
498 	}
499 
500 	return SWITCH_STATUS_SUCCESS;
501 }
502 
CODEC_TYPE(const char * string)503 static int CODEC_TYPE(const char *string)
504 {
505 	if (!strcmp(string, "vp8")) {
506 		return CODEC_TYPE_VP8;
507 	} else if (!strcmp(string, "vp9")) {
508 		return CODEC_TYPE_VP9;
509 	}
510 
511 	return CODEC_TYPE_ANY;
512 }
513 
parse_codec_specific_profile(my_vpx_cfg_t * my_cfg,const char * codec_name)514 static void parse_codec_specific_profile(my_vpx_cfg_t *my_cfg, const char *codec_name)
515 {
516 	switch_xml_t cfg = NULL;
517 	switch_xml_t xml = switch_xml_open_cfg("vpx.conf", &cfg, NULL);
518 	switch_xml_t profiles = cfg ? switch_xml_child(cfg, "profiles") : NULL;
519 
520 	// open config and find the profile to parse
521 	if (profiles) {
522 		switch_event_header_t *hp;
523 
524 		for (hp = my_cfg->codecs->headers; hp; hp = hp->next) {
525 			// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: %s\n", hp->name, hp->value);
526 			if (!strcmp(hp->name, codec_name)) {
527 				switch_xml_t profile;
528 				for (profile = switch_xml_child(profiles, "profile"); profile; profile = profile->next) {
529 					const char *name = switch_xml_attr(profile, "name");
530 
531 					if (!strcmp(hp->value, name)) {
532 						parse_profile(my_cfg, profile, CODEC_TYPE(codec_name));
533 					}
534 				}
535 			}
536 		}
537 	}
538 
539 	if (xml) switch_xml_free(xml);
540 }
541 
init_encoder(switch_codec_t * codec)542 static switch_status_t init_encoder(switch_codec_t *codec)
543 {
544 	vpx_context_t *context = (vpx_context_t *)codec->private_info;
545 	vpx_codec_enc_cfg_t *config = &context->config;
546 	my_vpx_cfg_t *my_cfg = NULL;
547 	vpx_codec_err_t err;
548 	char *codec_name = "vp8";
549 
550 	if (context->is_vp9) {
551 		codec_name = "vp9";
552 	}
553 
554 	if (!zstr(context->codec_settings.video.config_profile_name)) {
555 		my_cfg = find_cfg_profile(context->codec_settings.video.config_profile_name, SWITCH_FALSE);
556 	}
557 
558 	if (!my_cfg) {
559 		my_cfg = find_cfg_profile(codec_name, SWITCH_FALSE);
560 	}
561 
562 	if (!my_cfg) return SWITCH_STATUS_FALSE;
563 
564 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "config: %s\n", my_cfg->name);
565 
566 	if (context->is_vp9) {
567 		my_cfg->enc_cfg.g_profile = 0; // default build of VP9 only support 0, TODO: remove this
568 	}
569 
570 	if (my_cfg->codecs) {
571 		parse_codec_specific_profile(my_cfg, codec_name);
572 	}
573 
574 	if (vpx_globals.debug) show_config(my_cfg, &my_cfg->enc_cfg);
575 
576 	if (!context->codec_settings.video.width) {
577 		context->codec_settings.video.width = 1280;
578 	}
579 
580 	if (!context->codec_settings.video.height) {
581 		context->codec_settings.video.height = 720;
582 	}
583 
584 	if (context->codec_settings.video.bandwidth == -1) {
585 		context->codec_settings.video.bandwidth = 0;
586 	}
587 
588 	if (context->codec_settings.video.bandwidth) {
589 		context->bandwidth = context->codec_settings.video.bandwidth;
590 	} else {
591 		context->bandwidth = switch_calc_bitrate(context->codec_settings.video.width, context->codec_settings.video.height, 1, 15);
592 	}
593 
594 	if (context->bandwidth > vpx_globals.max_bitrate) {
595 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_WARNING, "REQUESTED BITRATE TRUNCATED FROM %d TO %d\n", context->bandwidth, vpx_globals.max_bitrate);
596 		context->bandwidth = vpx_globals.max_bitrate;
597 	}
598 
599 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_NOTICE,
600 		"VPX encoder reset (WxH/BW) from %dx%d/%u to %dx%d/%u\n",
601 		config->g_w, config->g_h, config->rc_target_bitrate,
602 		context->codec_settings.video.width, context->codec_settings.video.height, context->bandwidth);
603 
604 	context->pkt = NULL;
605 	context->start_time = switch_micro_time_now();
606 
607 	*config = my_cfg->enc_cfg; // reset whole config to current defaults
608 
609 	config->g_w = context->codec_settings.video.width;
610 	config->g_h = context->codec_settings.video.height;
611 	config->rc_target_bitrate = context->bandwidth;
612 
613 	if (context->is_vp9) {
614 		if (my_cfg->lossless) {
615 			config->rc_min_quantizer = 0;
616 			config->rc_max_quantizer = 0;
617 		}
618 	}
619 
620 	if (context->encoder_init) {
621 		if ((err = vpx_codec_enc_config_set(&context->encoder, config)) != VPX_CODEC_OK) {
622 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_ERROR,
623 				"VPX encoder %s codec reconf error: [%d:%s:%s]\n",
624 				vpx_codec_iface_name(context->encoder_interface),
625 				err, vpx_codec_error(&context->encoder), vpx_codec_error_detail(&context->encoder));
626 			return SWITCH_STATUS_FALSE;
627 		}
628 	} else if (context->flags & SWITCH_CODEC_FLAG_ENCODE) {
629 		if (vpx_globals.debug) {
630 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_INFO, "VPX encoder %s settings:\n", vpx_codec_iface_name(context->encoder_interface));
631 			show_config(my_cfg, config);
632 		}
633 
634 		if ((err = vpx_codec_enc_init(&context->encoder, context->encoder_interface, config, 0 & VPX_CODEC_USE_OUTPUT_PARTITION)) != VPX_CODEC_OK) {
635 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_ERROR,
636 				"VPX encoder %s codec init error: [%d:%s:%s]\n",
637 				vpx_codec_iface_name(context->encoder_interface),
638 				err, vpx_codec_error(&context->encoder), vpx_codec_error_detail(&context->encoder));
639 			return SWITCH_STATUS_FALSE;
640 		}
641 
642 		context->encoder_init = 1;
643 
644 		vpx_codec_control(&context->encoder, VP8E_SET_TOKEN_PARTITIONS, my_cfg->token_parts);
645 		vpx_codec_control(&context->encoder, VP8E_SET_CPUUSED, my_cfg->cpuused);
646 		vpx_codec_control(&context->encoder, VP8E_SET_STATIC_THRESHOLD, my_cfg->static_thresh);
647 
648 		if (context->is_vp9) {
649 			if (my_cfg->lossless) {
650 				vpx_codec_control(&context->encoder, VP9E_SET_LOSSLESS, 1);
651 			}
652 
653 			vpx_codec_control(&context->encoder, VP9E_SET_TUNE_CONTENT, my_cfg->tune_content);
654 		} else {
655 			vpx_codec_control(&context->encoder, VP8E_SET_NOISE_SENSITIVITY, my_cfg->noise_sensitivity);
656 
657 			if (my_cfg->max_intra_bitrate_pct) {
658 				vpx_codec_control(&context->encoder, VP8E_SET_MAX_INTRA_BITRATE_PCT, my_cfg->max_intra_bitrate_pct);
659 			}
660 		}
661 	}
662 
663 	return SWITCH_STATUS_SUCCESS;
664 }
665 
switch_vpx_init(switch_codec_t * codec,switch_codec_flag_t flags,const switch_codec_settings_t * codec_settings)666 static switch_status_t switch_vpx_init(switch_codec_t *codec, switch_codec_flag_t flags, const switch_codec_settings_t *codec_settings)
667 {
668 	vpx_context_t *context = NULL;
669 	int encoding, decoding;
670 
671 	encoding = (flags & SWITCH_CODEC_FLAG_ENCODE);
672 	decoding = (flags & SWITCH_CODEC_FLAG_DECODE);
673 
674 	if (!(encoding || decoding) || ((context = switch_core_alloc(codec->memory_pool, sizeof(*context))) == 0)) {
675 		return SWITCH_STATUS_FALSE;
676 	}
677 
678 	memset(context, 0, sizeof(*context));
679 	context->flags = flags;
680 	codec->private_info = context;
681 	context->pool = codec->memory_pool;
682 
683 	if (codec_settings) {
684 		context->codec_settings = *codec_settings;
685 	}
686 
687 	if (!strcmp(codec->implementation->iananame, "VP9")) {
688 		context->is_vp9 = 1;
689 		context->encoder_interface = vpx_codec_vp9_cx();
690 		context->decoder_interface = vpx_codec_vp9_dx();
691 	} else {
692 		context->encoder_interface = vpx_codec_vp8_cx();
693 		context->decoder_interface = vpx_codec_vp8_dx();
694 	}
695 
696 	if (codec->fmtp_in) {
697 		codec->fmtp_out = switch_core_strdup(codec->memory_pool, codec->fmtp_in);
698 	}
699 
700 	context->codec_settings.video.width = 320;
701 	context->codec_settings.video.height = 240;
702 
703 	switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_DEBUG, "VPX VER:%s VPX_IMAGE_ABI_VERSION:%d VPX_CODEC_ABI_VERSION:%d\n",
704 		vpx_codec_version_str(), VPX_IMAGE_ABI_VERSION, VPX_CODEC_ABI_VERSION);
705 
706 	if (!context->is_vp9) {
707 		context->picture_id =  13; // picture Id may start from random value and must be incremented on each frame
708 	} else {
709 		context->vp9.picture_id = 13;
710 	}
711 
712 	return SWITCH_STATUS_SUCCESS;
713 }
714 
consume_partition(vpx_context_t * context,switch_frame_t * frame)715 static switch_status_t consume_partition(vpx_context_t *context, switch_frame_t *frame)
716 {
717 	vpx_payload_descriptor_t *payload_descriptor;
718 	uint8_t *body, *c = NULL;
719 	uint32_t hdrlen = 0, payload_size = 0, max_payload_size = 0, start = 0, key = 0;
720 	switch_size_t remaining_bytes = 0;
721 	switch_status_t status;
722 
723 	if (!context->pkt) {
724 		if ((context->pkt = vpx_codec_get_cx_data(&context->encoder, &context->enc_iter))) {
725 			start = 1;
726 			if (!context->pbuffer) {
727 				switch_buffer_create_partition(context->pool, &context->pbuffer, context->pkt->data.frame.buf, context->pkt->data.frame.sz);
728 			} else {
729 				switch_buffer_set_partition_data(context->pbuffer, context->pkt->data.frame.buf, context->pkt->data.frame.sz);
730 			}
731 		}
732 	}
733 
734 	if (context->pbuffer) {
735 		remaining_bytes = switch_buffer_inuse(context->pbuffer);
736 	}
737 
738 	if (!context->pkt || context->pkt->kind != VPX_CODEC_CX_FRAME_PKT || !remaining_bytes) {
739 		frame->datalen = 0;
740 		frame->m = 1;
741 		context->pkt = NULL;
742 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "writing 0 bytes\n");
743 		return SWITCH_STATUS_SUCCESS;
744 	}
745 
746 	key = (context->pkt->data.frame.flags & VPX_FRAME_IS_KEY);
747 
748 #if 0
749 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "flags: %x pts: %lld duration:%lu partition_id: %d\n",
750 		context->pkt->data.frame.flags, context->pkt->data.frame.pts, context->pkt->data.frame.duration, context->pkt->data.frame.partition_id);
751 #endif
752 
753 	/* reset header */
754 	*(uint8_t *)frame->data = 0;
755 	payload_descriptor = (vpx_payload_descriptor_t *) frame->data;
756 	memset(payload_descriptor, 0, sizeof(*payload_descriptor));
757 
758 
759 	if (context->is_vp9) {
760 		hdrlen = 1;				/* Send VP9 with 1 byte REQUIRED header. */
761 	} else {
762 		hdrlen = 4;				/* Send VP8 with 4 byte extended header, includes 1 byte REQUIRED header, 1 byte X header and 2 bytes of I header with picture_id. */
763 	}
764 
765 	body = ((uint8_t *)frame->data) + hdrlen;
766 
767 	if (context->is_vp9) {
768 		payload_descriptor->vp9.start = start;
769 
770 		if (1) {
771 			// payload_descriptor->vp9.have_p_layer = key; // key?
772 			payload_descriptor->vp9.have_pid = 1;
773 
774 			if (payload_descriptor->vp9.have_pid) {
775 				if (context->vp9.picture_id > 0x7f) {
776 					*body++ = (context->vp9.picture_id >> 8) | 0x80;
777 					*body++ = context->vp9.picture_id & 0xff;
778 					hdrlen += 2;
779 				} else {
780 					*body++ = context->vp9.picture_id;
781 					hdrlen++;
782 				}
783 			}
784 
785 			if (key) {
786 				vp9_ss_t *ss = (vp9_ss_t *)body;
787 
788 				payload_descriptor->vp9.have_ss = 1;
789 				payload_descriptor->vp9.have_p_layer = 0;
790 				ss->n_s = 0;
791 				ss->g = 0;
792 				ss->y = 0;
793 				ss->zero = 0;
794 				body++;
795 				hdrlen++;
796 
797 				if (0) { // y ?
798 					uint16_t *w;
799 					uint16_t *h;
800 
801 					ss->y = 1;
802 
803 					w = (uint16_t *)body;
804 					body+=2;
805 					h = (uint16_t *)body;
806 					body+=2;
807 
808 					*w = (uint16_t)context->codec_settings.video.width;
809 					*h = (uint16_t)context->codec_settings.video.height;
810 
811 					hdrlen += (ss->n_s + 1) * 4;
812 				}
813 			} else {
814 				payload_descriptor->vp9.have_p_layer = 1;
815 			}
816 		}
817 	}
818 
819 	if (!context->is_vp9) {
820 		payload_descriptor->vp8.start = start;
821 
822 		payload_descriptor->vp8.extended = 1;	/* REQUIRED header. */
823 
824 		payload_descriptor->vp8.I = 1;			/* X header. */
825 
826 		payload_descriptor->vp8.M = 1;			/* I header. */
827 		c = ((uint8_t *)frame->data) + 2;
828 		*c++ = (context->picture_id >> 8) | 0x80;
829 		*c = context->picture_id & 0xff;
830 
831 		payload_descriptor->vp8.L = 0;
832 		payload_descriptor->vp8.TL0PICIDX = 0;
833 
834 		payload_descriptor->vp8.T = 0;
835 		payload_descriptor->vp8.TID = 0;
836 		payload_descriptor->vp8.Y = 0;
837 		payload_descriptor->vp8.K = 0;
838 		payload_descriptor->vp8.KEYIDX = 0;
839 	}
840 
841 	/*
842 		Try to split payload to packets evenly(with largest at the end) up to vpx_globals.rtp_slice_size,
843 		(assume hdrlen constant across all packets of the same picture).
844 		It keeps packets being transmitted in order.
845 		Without it last (and thus the smallest one) packet usually arrive out of order
846 		(before the previous one)
847 	*/
848 	max_payload_size = vpx_globals.rtp_slice_size - hdrlen;
849 	payload_size = remaining_bytes / ((remaining_bytes + max_payload_size - 1) / max_payload_size);
850 
851 	if (remaining_bytes <= payload_size) {
852 		switch_buffer_read(context->pbuffer, body, remaining_bytes);
853 		context->pkt = NULL;
854 		frame->datalen = hdrlen + remaining_bytes;
855 		frame->m = 1;
856 
857 		// increment and wrap picture_id (if needed) after the last picture's packet
858 		if (context->is_vp9) {
859 			context->vp9.picture_id++;
860 			if ((uint16_t)context->vp9.picture_id > 0x7fff) {
861 				context->vp9.picture_id = 0;
862 			}
863 		} else {
864 			context->picture_id++;
865 			if ((uint16_t)context->picture_id > 0x7fff) {
866 				context->picture_id = 0;
867 			}
868 		}
869 
870 		status = SWITCH_STATUS_SUCCESS;
871 	} else {
872 		switch_buffer_read(context->pbuffer, body, payload_size);
873 		frame->datalen = hdrlen + payload_size;
874 		frame->m = 0;
875 		status = SWITCH_STATUS_MORE_DATA;
876 	}
877 
878 	if (frame->m && context->is_vp9) {
879 		payload_descriptor->vp9.end = 1;
880 	}
881 
882 	return status;
883 }
884 
reset_codec_encoder(switch_codec_t * codec)885 static switch_status_t reset_codec_encoder(switch_codec_t *codec)
886 {
887 	vpx_context_t *context = (vpx_context_t *)codec->private_info;
888 
889 	if (context->encoder_init) {
890 		vpx_codec_destroy(&context->encoder);
891 	}
892 	context->last_ts = 0;
893 	context->last_ms = 0;
894 	context->framecount = 0;
895 	context->encoder_init = 0;
896 	context->pkt = NULL;
897 	return init_encoder(codec);
898 }
899 
switch_vpx_encode(switch_codec_t * codec,switch_frame_t * frame)900 static switch_status_t switch_vpx_encode(switch_codec_t *codec, switch_frame_t *frame)
901 {
902 	vpx_context_t *context = (vpx_context_t *)codec->private_info;
903 	int width = 0;
904 	int height = 0;
905 	uint32_t dur;
906 	int64_t pts;
907 	vpx_enc_frame_flags_t vpx_flags = 0;
908 	switch_time_t now;
909 	vpx_codec_err_t err;
910 
911 	if (frame->flags & SFF_SAME_IMAGE) {
912 		return consume_partition(context, frame);
913 	}
914 
915 	if (context->need_encoder_reset != 0) {
916 		if (reset_codec_encoder(codec) != SWITCH_STATUS_SUCCESS) {
917 			return SWITCH_STATUS_FALSE;
918 		}
919 		context->need_encoder_reset = 0;
920 	}
921 
922 	if (frame->img->d_h > 1) {
923 		width = frame->img->d_w;
924 		height = frame->img->d_h;
925 	} else {
926 		width = frame->img->w;
927 		height = frame->img->h;
928 	}
929 
930 	if (context->codec_settings.video.width != width || context->codec_settings.video.height != height) {
931 		context->codec_settings.video.width = width;
932 		context->codec_settings.video.height = height;
933 		reset_codec_encoder(codec);
934 		frame->flags |= SFF_PICTURE_RESET;
935 		context->need_key_frame = 3;
936 	}
937 
938 	if (!context->encoder_init) {
939 		if (init_encoder(codec) != SWITCH_STATUS_SUCCESS) {
940 			return SWITCH_STATUS_FALSE;
941 		}
942 	}
943 
944 	if (context->change_bandwidth) {
945 		context->codec_settings.video.bandwidth = context->change_bandwidth;
946 		context->change_bandwidth = 0;
947 		if (init_encoder(codec) != SWITCH_STATUS_SUCCESS) {
948 			return SWITCH_STATUS_FALSE;
949 		}
950 	}
951 
952 	now = switch_time_now();
953 
954 	if (context->need_key_frame > 0) {
955 		// force generate a key frame
956 		if (!context->last_key_frame || (now - context->last_key_frame) > vpx_globals.key_frame_min_freq) {
957 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), VPX_SWITCH_LOG_LEVEL,
958 				"VPX encoder keyframe request\n");
959 			vpx_flags |= VPX_EFLAG_FORCE_KF;
960 			context->need_key_frame = 0;
961 			context->last_key_frame = now;
962 		}
963 	}
964 
965 	context->framecount++;
966 
967 	pts = (now - context->start_time) / 1000;
968 	//pts = frame->timestamp;
969 
970 	dur = context->last_ms ? (now - context->last_ms) / 1000 : pts;
971 
972 	if ((err = vpx_codec_encode(&context->encoder,
973 						 (vpx_image_t *) frame->img,
974 						 pts,
975 						 dur,
976 						 vpx_flags,
977 						 VPX_DL_REALTIME)) != VPX_CODEC_OK) {
978 		switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_ERROR, "VPX encode error [%d:%s:%s]\n",
979 			err, vpx_codec_error(&context->encoder), vpx_codec_error_detail(&context->encoder));
980 		frame->datalen = 0;
981 		return SWITCH_STATUS_FALSE;
982 	}
983 
984 	context->enc_iter = NULL;
985 	context->last_ts = frame->timestamp;
986 	context->last_ms = now;
987 
988 	return consume_partition(context, frame);
989 }
990 
buffer_vp8_packets(vpx_context_t * context,switch_frame_t * frame)991 static switch_status_t buffer_vp8_packets(vpx_context_t *context, switch_frame_t *frame)
992 {
993 	uint8_t *data = frame->data;
994 	uint8_t S;
995 	uint8_t DES;
996 	//	uint8_t PID;
997 	int len;
998 
999 	if (context->debug > 0) {
1000 		switch_log_printf(SWITCH_CHANNEL_LOG, context->debug,
1001 					  "VIDEO VPX: seq: %d ts: %u len: %u %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x mark: %d\n",
1002 					  frame->seq, frame->timestamp, frame->datalen,
1003 					  *((uint8_t *)data), *((uint8_t *)data + 1),
1004 					  *((uint8_t *)data + 2), *((uint8_t *)data + 3),
1005 					  *((uint8_t *)data + 4), *((uint8_t *)data + 5),
1006 					  *((uint8_t *)data + 6), *((uint8_t *)data + 7),
1007 					  *((uint8_t *)data + 8), *((uint8_t *)data + 9),
1008 					  *((uint8_t *)data + 10), frame->m);
1009 	}
1010 
1011 	DES = *data;
1012 	data++;
1013 	S = (DES & 0x10);
1014 	//	PID = DES & 0x07;
1015 
1016 	//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DATA LEN %d S BIT %d PID: %d\n", frame->datalen, S, PID);
1017 
1018 	if (DES & 0x80) { // X
1019 		uint8_t X = *data;
1020 		//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "X BIT SET\n");
1021 		data++;
1022 		if (X & 0x80) { // I
1023 			uint8_t M = (*data) & 0x80;
1024 			//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "I BIT SET\n");
1025 			data++;
1026 			if (M) {
1027 				//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "M BIT SET\n");
1028 				data++;
1029 			}
1030 		}
1031 		if (X & 0x40) {
1032 			//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "L BIT SET\n");
1033 			data++; // L
1034 		}
1035 		if (X & 0x30) {
1036 			//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "T/K BIT SET\n");
1037 			data++; // T/K
1038 		}
1039 	}
1040 
1041 	if (!switch_buffer_inuse(context->vpx_packet_buffer) && !S) {
1042 		if (context->got_key_frame > 0) {
1043 			context->got_key_frame = 0;
1044 			context->got_start_frame = 0;
1045 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG2, "packet loss?\n");
1046 		}
1047 		return SWITCH_STATUS_MORE_DATA;
1048 	}
1049 
1050 	if (S) {
1051 		switch_buffer_zero(context->vpx_packet_buffer);
1052 		context->last_received_timestamp = frame->timestamp;
1053 #if 0
1054 		if (PID == 0) {
1055 			key = __IS_VP8_KEY_FRAME(*data);
1056 		}
1057 #endif
1058 	}
1059 
1060 	len = frame->datalen - (data - (uint8_t *)frame->data);
1061 	//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "POST PARSE: DATA LEN %d KEY %d KEYBYTE = %0x\n", len, key, *data);
1062 
1063 	if (len <= 0) {
1064 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid packet %d\n", len);
1065 		return SWITCH_STATUS_RESTART;
1066 	}
1067 
1068 	if (context->last_received_timestamp != frame->timestamp) {
1069 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG2, "wrong timestamp %u, expect %u, packet loss?\n", frame->timestamp, context->last_received_timestamp);
1070 		switch_buffer_zero(context->vpx_packet_buffer);
1071 		return SWITCH_STATUS_RESTART;
1072 	}
1073 
1074 	switch_buffer_write(context->vpx_packet_buffer, data, len);
1075 	return SWITCH_STATUS_SUCCESS;
1076 }
1077 
1078 // https://tools.ietf.org/id/draft-ietf-payload-vp9-01.txt
1079 
buffer_vp9_packets(vpx_context_t * context,switch_frame_t * frame)1080 static switch_status_t buffer_vp9_packets(vpx_context_t *context, switch_frame_t *frame)
1081 {
1082 	uint8_t *data = (uint8_t *)frame->data;
1083 	uint8_t *vp9  = (uint8_t *)frame->data;
1084 	vp9_payload_descriptor_t *desc = (vp9_payload_descriptor_t *)vp9;
1085 	int len = 0;
1086 
1087 	if (context->debug > 0) {
1088 		switch_log_printf(SWITCH_CHANNEL_LOG, frame->m ? SWITCH_LOG_ERROR : SWITCH_LOG_INFO,
1089 					"[%02x %02x %02x %02x] m=%d len=%4d seq=%d ts=%u ssrc=%u "
1090 					"have_pid=%d "
1091 					"have_p_layer=%d "
1092 					"have_layer_ind=%d "
1093 					"is_flexible=%d "
1094 					"start=%d "
1095 					"end=%d "
1096 					"have_ss=%d "
1097 					"zero=%d\n",
1098 					*data, *(data+1), *(data+2), *(data+3), frame->m, frame->datalen, frame->seq, frame->timestamp, frame->ssrc,
1099 					desc->have_pid,
1100 					desc->have_p_layer,
1101 					desc->have_layer_ind,
1102 					desc->is_flexible,
1103 					desc->start,
1104 					desc->end,
1105 					desc->have_ss,
1106 					desc->zero);
1107 	}
1108 
1109 	vp9++;
1110 
1111 	if (desc->have_pid) {
1112 #ifdef DEBUG_VP9
1113 		uint16_t pid = 0;
1114 		pid = *vp9 & 0x7f;	//0 bit is M , 1-7 bit is pid.
1115 #endif
1116 		if (*vp9 & 0x80) {	//if (M==1)
1117 			vp9++;
1118 #ifdef DEBUG_VP9
1119 			pid = (pid << 8) + *vp9;
1120 #endif
1121 		}
1122 
1123 		vp9++;
1124 
1125 #ifdef DEBUG_VP9
1126 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "have pid: %d start=%d end=%d\n", pid, desc->start, desc->end);
1127 #endif
1128 
1129 	}
1130 
1131 	if (desc->have_layer_ind) {
1132 #ifdef DEBUG_VP9
1133 		vp9_p_layer_t *layer = (vp9_p_layer_t *)vp9;
1134 
1135 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "temporal_id=%d temporal_up_switch=%d spatial_id=%d inter_layer_predicted=%d\n",
1136 			layer->temporal_id, layer->temporal_up_switch, layer->spatial_id, layer->inter_layer_predicted);
1137 #endif
1138 
1139 		vp9++;
1140 		if (!desc->is_flexible) {
1141 			vp9++; // TL0PICIDX
1142 		}
1143 	}
1144 
1145 	//When P and F are both set to one, indicating a non-key frame in flexible mode
1146 	if (desc->have_p_layer && desc->is_flexible) { // P & F set, P_DIFF
1147 		if (*vp9 & 1) { // N
1148 			vp9++;
1149 			if (*vp9 & 1) { // N
1150 				vp9++;
1151 				if (*vp9 & 1) {
1152 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid VP9 packet!");
1153 					switch_buffer_zero(context->vpx_packet_buffer);
1154 					goto end;
1155 				}
1156 			}
1157 		}
1158 		vp9++;
1159 	}
1160 
1161 	if (desc->have_ss) {
1162 		vp9_ss_t *ss = (vp9_ss_t *)(vp9++);
1163 
1164 #ifdef DEBUG_VP9
1165 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "have ss: %02x n_s: %d y:%d g:%d\n", *(uint8_t *)ss, ss->n_s, ss->y, ss->g);
1166 #endif
1167 		if (ss->y) {
1168 			int i;
1169 
1170 			for (i=0; i<=ss->n_s; i++) {
1171 #ifdef DEBUG_VP9
1172 				int width = ntohs(*(uint16_t *)vp9);
1173 				int height = ntohs(*(uint16_t *)(vp9 + 2));
1174 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "SS: %d %dx%d\n", i, width, height);
1175 #endif
1176 				vp9 += 4;
1177 			}
1178 		}
1179 
1180 		if (ss->g) {
1181 			int i;
1182 			uint8_t ng = *vp9++;	//N_G indicates the number of frames in a GOF
1183 
1184 			for (i = 0; ng > 0 && i < ng; i++) {
1185 				vp9_n_g_t *n_g = (vp9_n_g_t *)(vp9++);
1186 				vp9 += n_g->r;
1187 			}
1188 		}
1189 	}
1190 
1191 	if (vp9 - data >= frame->datalen) {
1192 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Invalid VP9 Packet %" SWITCH_SSIZE_T_FMT " > %d\n", vp9 - data, frame->datalen);
1193 		switch_buffer_zero(context->vpx_packet_buffer);
1194 		goto end;
1195 	}
1196 
1197 	if (!switch_buffer_inuse(context->vpx_packet_buffer)) { // start packet
1198 		if (!desc->start) {
1199 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "got invalid vp9 packet, packet loss? waiting for a start packet\n");
1200 			goto end;
1201 		}
1202 	}
1203 
1204 	len = frame->datalen - (vp9 - data);
1205 	switch_buffer_write(context->vpx_packet_buffer, vp9, len);
1206 
1207 end:
1208 
1209 #ifdef DEBUG_VP9
1210 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "buffered %d bytes, buffer size: %" SWITCH_SIZE_T_FMT "\n", len, switch_buffer_inuse(context->vpx_packet_buffer));
1211 #endif
1212 
1213 	return SWITCH_STATUS_SUCCESS;
1214 }
1215 
switch_vpx_decode(switch_codec_t * codec,switch_frame_t * frame)1216 static switch_status_t switch_vpx_decode(switch_codec_t *codec, switch_frame_t *frame)
1217 {
1218 	vpx_context_t *context = (vpx_context_t *)codec->private_info;
1219 	switch_size_t len;
1220 	vpx_codec_ctx_t *decoder = NULL;
1221 	switch_status_t status = SWITCH_STATUS_SUCCESS;
1222 	int is_start = 0, is_keyframe = 0, get_refresh = 0;
1223 
1224 	if (context->debug > 0 && context->debug < 4) {
1225 		vp9_payload_descriptor_t *desc = (vp9_payload_descriptor_t *)frame->data;
1226 		uint8_t *data = frame->data;
1227 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%02x %02x %02x %02x m=%d start=%d end=%d m=%d len=%d\n",
1228 			*data, *(data+1), *(data+2), *(data+3), frame->m, desc->start, desc->end, frame->m, frame->datalen);
1229 	}
1230 
1231 	if (context->is_vp9) {
1232 		is_keyframe = IS_VP9_KEY_FRAME(*(unsigned char *)frame->data);
1233 		is_start = IS_VP9_START_PKT(*(unsigned char *)frame->data);
1234 
1235 		if (is_keyframe) {
1236 			switch_log_printf(SWITCH_CHANNEL_LOG, VPX_SWITCH_LOG_LEVEL, "================Got a key frame!!!!========================\n");
1237 		}
1238 
1239 		if (context->last_received_seq && context->last_received_seq + 1 != frame->seq) {
1240 			switch_log_printf(SWITCH_CHANNEL_LOG, VPX_SWITCH_LOG_LEVEL, "Packet loss detected last=%d got=%d lost=%d\n", context->last_received_seq, frame->seq, frame->seq - context->last_received_seq);
1241 			if (is_keyframe && context->vpx_packet_buffer) switch_buffer_zero(context->vpx_packet_buffer);
1242 		}
1243 
1244 		context->last_received_seq = frame->seq;
1245 	} else { // vp8
1246 		is_start = (*(unsigned char *)frame->data & 0x10);
1247 		is_keyframe = IS_VP8_KEY_FRAME((uint8_t *)frame->data);
1248 	}
1249 
1250     if (!is_keyframe && context->got_key_frame <= 0) {
1251 		context->no_key_frame++;
1252 		//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "no keyframe, %d\n", context->no_key_frame);
1253 		if (context->no_key_frame > 50) {
1254 			if ((is_keyframe = is_start)) {
1255 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "no keyframe, treating start as key. frames=%d\n", context->no_key_frame);
1256 			}
1257 		}
1258     }
1259 
1260 	if (context->debug > 0 && is_keyframe) {
1261 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "GOT KEY FRAME %d\n", context->got_key_frame);
1262 	}
1263 
1264 	if (context->need_decoder_reset != 0) {
1265 		vpx_codec_destroy(&context->decoder);
1266 		context->decoder_init = 0;
1267 		status = init_decoder(codec);
1268 		context->need_decoder_reset = 0;
1269 	}
1270 
1271 	if (status != SWITCH_STATUS_SUCCESS) goto end;
1272 
1273 	if (!context->decoder_init) {
1274 		status = init_decoder(codec);
1275 	}
1276 
1277 	if (status != SWITCH_STATUS_SUCCESS) goto end;
1278 
1279 	if (!context->decoder_init) {
1280 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "VPX decoder is not initialized!\n");
1281 		return SWITCH_STATUS_FALSE;
1282 	}
1283 
1284 	decoder = &context->decoder;
1285 
1286 	// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "len: %d ts: %u mark:%d\n", frame->datalen, frame->timestamp, frame->m);
1287 
1288 	// context->last_received_timestamp = frame->timestamp;
1289 	context->last_received_complete_picture = frame->m ? SWITCH_TRUE : SWITCH_FALSE;
1290 
1291 	if (is_start) {
1292 		context->got_start_frame = 1;
1293 	}
1294 
1295 	if (is_keyframe) {
1296 		switch_set_flag(frame, SFF_IS_KEYFRAME);
1297 		if (context->got_key_frame <= 0) {
1298 			context->got_key_frame = 1;
1299 			context->no_key_frame = 0;
1300 		} else {
1301 			context->got_key_frame++;
1302 		}
1303 	} else if (context->got_key_frame <= 0) {
1304 		if ((--context->got_key_frame % 200) == 0) {
1305 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Waiting for key frame %d\n", context->got_key_frame);
1306 		}
1307 
1308 		get_refresh = 1;
1309 
1310 		if (!context->got_start_frame) {
1311 			switch_goto_status(SWITCH_STATUS_MORE_DATA, end);
1312 		}
1313 	}
1314 
1315 
1316 	status = context->is_vp9 ? buffer_vp9_packets(context, frame) : buffer_vp8_packets(context, frame);
1317 
1318 
1319 	if (context->dec_iter && (frame->img = (switch_image_t *) vpx_codec_get_frame(decoder, &context->dec_iter))) {
1320 		switch_goto_status(SWITCH_STATUS_SUCCESS, end);
1321 	}
1322 
1323 	// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "====READ buf:%ld got_key:%d st:%d m:%d\n", switch_buffer_inuse(context->vpx_packet_buffer), context->got_key_frame, status, frame->m);
1324 
1325 	len = switch_buffer_inuse(context->vpx_packet_buffer);
1326 
1327 	//if (frame->m && (status != SWITCH_STATUS_SUCCESS || !len)) {
1328 		//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "WTF????? %d %ld\n", status, len);
1329 	//}
1330 
1331 	if (status == SWITCH_STATUS_SUCCESS && frame->m && len) {
1332 		uint8_t *data;
1333 		int corrupted = 0;
1334 		vpx_codec_err_t err;
1335 
1336 		switch_buffer_peek_zerocopy(context->vpx_packet_buffer, (void *)&data);
1337 
1338 		context->dec_iter = NULL;
1339 		err = vpx_codec_decode(decoder, data, (unsigned int)len, NULL, 0);
1340 
1341 		if (err != VPX_CODEC_OK) {
1342 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), (context->decoded_first_frame ? SWITCH_LOG_ERROR : VPX_SWITCH_LOG_LEVEL),
1343 				"VPX error decoding %" SWITCH_SIZE_T_FMT " bytes, [%d:%s:%s]\n",
1344 				len, err, vpx_codec_error(decoder), vpx_codec_error_detail(decoder));
1345 
1346 			if (err == VPX_CODEC_MEM_ERROR) {
1347 				switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_WARNING, "VPX MEM ERROR, resetting decoder!\n");
1348 				context->need_decoder_reset = 1;
1349 			}
1350 
1351 			switch_goto_status(SWITCH_STATUS_RESTART, end);
1352 		} else {
1353 			if (!context->decoded_first_frame) context->decoded_first_frame = 1;
1354 		}
1355 
1356 		if (vpx_codec_control(decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted) != VPX_CODEC_OK) {
1357 			switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(codec->session), SWITCH_LOG_WARNING, "VPX control error!\n");
1358 			switch_goto_status(SWITCH_STATUS_RESTART, end);
1359 		}
1360 
1361 		if (corrupted) {
1362 			frame->img = NULL;
1363 #ifdef DEBUG_VP9
1364 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted!!\n");
1365 #endif
1366 		} else {
1367 			frame->img = (switch_image_t *) vpx_codec_get_frame(decoder, &context->dec_iter);
1368 
1369 #ifdef DEBUG_VP9
1370 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "decoded: %dx%d\n", frame->img->d_w, frame->img->d_h);
1371 #endif
1372 		}
1373 
1374 		switch_buffer_zero(context->vpx_packet_buffer);
1375 
1376 		if (!frame->img) {
1377 			//context->need_decoder_reset = 1;
1378 			context->got_key_frame = 0;
1379 			context->got_start_frame = 0;
1380 			status = SWITCH_STATUS_RESTART;
1381 		}
1382 	}
1383 
1384 end:
1385 
1386 	if (status == SWITCH_STATUS_RESTART) {
1387 		switch_buffer_zero(context->vpx_packet_buffer);
1388 		//context->need_decoder_reset = 1;
1389 		context->got_key_frame = 0;
1390 		context->got_start_frame = 0;
1391 		//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "RESET VPX\n");
1392 	}
1393 
1394 	if (!frame->img || status == SWITCH_STATUS_RESTART) {
1395 		status = SWITCH_STATUS_MORE_DATA;
1396 	}
1397 
1398 	if (context->got_key_frame <= 0 || get_refresh) {
1399 		switch_set_flag(frame, SFF_WAIT_KEY_FRAME);
1400 	}
1401 
1402 	if (frame->img && (codec->flags & SWITCH_CODEC_FLAG_VIDEO_PATCHING)) {
1403 		switch_img_free(&context->patch_img);
1404 		switch_img_copy(frame->img, &context->patch_img);
1405 		frame->img = context->patch_img;
1406 	}
1407 
1408 	return status;
1409 }
1410 
1411 
switch_vpx_control(switch_codec_t * codec,switch_codec_control_command_t cmd,switch_codec_control_type_t ctype,void * cmd_data,switch_codec_control_type_t atype,void * cmd_arg,switch_codec_control_type_t * rtype,void ** ret_data)1412 static switch_status_t switch_vpx_control(switch_codec_t *codec,
1413 										  switch_codec_control_command_t cmd,
1414 										  switch_codec_control_type_t ctype,
1415 										  void *cmd_data,
1416 										  switch_codec_control_type_t atype,
1417 										  void *cmd_arg,
1418 										  switch_codec_control_type_t *rtype,
1419 										  void **ret_data)
1420 {
1421 
1422 	vpx_context_t *context = (vpx_context_t *)codec->private_info;
1423 
1424 	switch(cmd) {
1425 	case SCC_VIDEO_RESET:
1426 		{
1427 			int mask = *((int *) cmd_data);
1428 			if (mask & 1) {
1429 				context->need_encoder_reset = 1;
1430 			}
1431 			if (mask & 2) {
1432 				context->need_decoder_reset = 1;
1433 			}
1434 		}
1435 		break;
1436 	case SCC_VIDEO_GEN_KEYFRAME:
1437 		context->need_key_frame = 1;
1438 		break;
1439 	case SCC_VIDEO_BANDWIDTH:
1440 		{
1441 			switch(ctype) {
1442 			case SCCT_INT:
1443 				context->change_bandwidth = *((int *) cmd_data);
1444 				break;
1445 			case SCCT_STRING:
1446 				{
1447 					char *bwv = (char *) cmd_data;
1448 					context->change_bandwidth = switch_parse_bandwidth_string(bwv);
1449 				}
1450 				break;
1451 			default:
1452 				break;
1453 			}
1454 		}
1455 		break;
1456 	case SCC_CODEC_SPECIFIC:
1457 		{
1458 			const char *command = (const char *)cmd_data;
1459 
1460 			if (ctype == SCCT_INT) {
1461 			} else if (ctype == SCCT_STRING && !zstr(command)) {
1462 				if (!strcasecmp(command, "VP8E_SET_CPUUSED")) {
1463 					vpx_codec_control(&context->encoder, VP8E_SET_CPUUSED, *(int *)cmd_arg);
1464 				} else if (!strcasecmp(command, "VP8E_SET_TOKEN_PARTITIONS")) {
1465 					vpx_codec_control(&context->encoder, VP8E_SET_TOKEN_PARTITIONS, *(int *)cmd_arg);
1466 				} else if (!strcasecmp(command, "VP8E_SET_NOISE_SENSITIVITY")) {
1467 					vpx_codec_control(&context->encoder, VP8E_SET_NOISE_SENSITIVITY, *(int *)cmd_arg);
1468 				}
1469 			}
1470 
1471 		}
1472 		break;
1473 	case SCC_DEBUG:
1474 		{
1475 			int32_t level = *((uint32_t *) cmd_data);
1476 			context->debug = level;
1477 		}
1478 		break;
1479 	default:
1480 		break;
1481 	}
1482 
1483 
1484 	return SWITCH_STATUS_SUCCESS;
1485 }
1486 
1487 
switch_vpx_destroy(switch_codec_t * codec)1488 static switch_status_t switch_vpx_destroy(switch_codec_t *codec)
1489 {
1490 	vpx_context_t *context = (vpx_context_t *)codec->private_info;
1491 
1492 	if (context) {
1493 
1494 		switch_img_free(&context->patch_img);
1495 
1496 		if ((codec->flags & SWITCH_CODEC_FLAG_ENCODE)) {
1497 			vpx_codec_destroy(&context->encoder);
1498 		}
1499 
1500 		if ((codec->flags & SWITCH_CODEC_FLAG_DECODE)) {
1501 			vpx_codec_destroy(&context->decoder);
1502 		}
1503 
1504 		if (context->pic) {
1505 			vpx_img_free(context->pic);
1506 			context->pic = NULL;
1507 		}
1508 		if (context->vpx_packet_buffer) {
1509 			switch_buffer_destroy(&context->vpx_packet_buffer);
1510 			context->vpx_packet_buffer = NULL;
1511 		}
1512 	}
1513 	return SWITCH_STATUS_SUCCESS;
1514 }
1515 
init_vp8(my_vpx_cfg_t * my_cfg)1516 static void init_vp8(my_vpx_cfg_t *my_cfg)
1517 {
1518 	vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &my_cfg->enc_cfg, 0);
1519 
1520 	my_cfg->dec_cfg.threads = vpx_globals.dec_threads;
1521 	my_cfg->enc_cfg.g_threads = vpx_globals.enc_threads;
1522 	my_cfg->static_thresh = 100;
1523 	my_cfg->noise_sensitivity = 1;
1524 
1525 	my_cfg->cpuused = -6;
1526 	my_cfg->enc_cfg.g_profile = 2;
1527 	my_cfg->enc_cfg.g_timebase.num = 1;
1528 	my_cfg->enc_cfg.g_timebase.den = 1000;
1529 	my_cfg->enc_cfg.g_error_resilient = VPX_ERROR_RESILIENT_PARTITIONS;
1530 	my_cfg->enc_cfg.rc_resize_allowed = 1;
1531 	my_cfg->enc_cfg.rc_end_usage = VPX_CBR;
1532 	my_cfg->enc_cfg.rc_target_bitrate = switch_parse_bandwidth_string("1mb");
1533 	my_cfg->enc_cfg.rc_min_quantizer = 4;
1534 	my_cfg->enc_cfg.rc_max_quantizer = 63;
1535 	my_cfg->enc_cfg.rc_overshoot_pct = 50;
1536 	my_cfg->enc_cfg.rc_buf_sz = 5000;
1537 	my_cfg->enc_cfg.rc_buf_initial_sz = 1000;
1538 	my_cfg->enc_cfg.rc_buf_optimal_sz = 1000;
1539 	my_cfg->enc_cfg.kf_max_dist = 360;
1540 }
1541 
init_vp9(my_vpx_cfg_t * my_cfg)1542 static void init_vp9(my_vpx_cfg_t *my_cfg)
1543 {
1544 	vpx_codec_enc_config_default(vpx_codec_vp9_cx(), &my_cfg->enc_cfg, 0);
1545 
1546 	my_cfg->dec_cfg.threads = vpx_globals.dec_threads;
1547 	my_cfg->enc_cfg.g_threads = vpx_globals.enc_threads;
1548 	my_cfg->static_thresh = 1000;
1549 
1550 	my_cfg->cpuused = -8;
1551 	my_cfg->enc_cfg.g_profile = 0;
1552 	my_cfg->enc_cfg.g_lag_in_frames = 0;
1553 	my_cfg->enc_cfg.g_timebase.den = 1000;
1554 	my_cfg->enc_cfg.g_error_resilient = VPX_ERROR_RESILIENT_PARTITIONS;
1555 	my_cfg->enc_cfg.rc_resize_allowed = 1;
1556 	my_cfg->enc_cfg.rc_end_usage = VPX_CBR;
1557 	my_cfg->enc_cfg.rc_target_bitrate = switch_parse_bandwidth_string("1mb");
1558 	my_cfg->enc_cfg.rc_min_quantizer = 4;
1559 	my_cfg->enc_cfg.rc_max_quantizer = 63;
1560 	my_cfg->enc_cfg.rc_overshoot_pct = 50;
1561 	my_cfg->enc_cfg.rc_buf_sz = 5000;
1562 	my_cfg->enc_cfg.rc_buf_initial_sz = 1000;
1563 	my_cfg->enc_cfg.rc_buf_optimal_sz = 1000;
1564 	my_cfg->enc_cfg.kf_max_dist = 360;
1565 	my_cfg->tune_content = VP9E_CONTENT_SCREEN;
1566 }
1567 
find_cfg_profile(const char * name,switch_bool_t reconfig)1568 static my_vpx_cfg_t *find_cfg_profile(const char *name, switch_bool_t reconfig)
1569 {
1570 	int i;
1571 
1572 	for (i = 0; i < MAX_PROFILES; i++) {
1573 		if (!vpx_globals.profiles[i]) {
1574 			vpx_globals.profiles[i] = malloc(sizeof(my_vpx_cfg_t));
1575 			switch_assert(vpx_globals.profiles[i]);
1576 			memset(vpx_globals.profiles[i], 0, sizeof(my_vpx_cfg_t));
1577 			switch_set_string(vpx_globals.profiles[i]->name, name);
1578 
1579 			if (!strcmp(name, "vp9")) {
1580 				init_vp9(vpx_globals.profiles[i]);
1581 			} else {
1582 				init_vp8(vpx_globals.profiles[i]);
1583 			}
1584 
1585 			vpx_globals.profiles[i]->token_parts = switch_core_cpu_count() > 1 ? 3 : 0;
1586 
1587 			return vpx_globals.profiles[i];
1588 		}
1589 
1590 		if (!strcmp(name, vpx_globals.profiles[i]->name)) {
1591 			if (reconfig) {
1592 				memset(vpx_globals.profiles[i], 0, sizeof(my_vpx_cfg_t));
1593 				switch_set_string(vpx_globals.profiles[i]->name, name);
1594 			}
1595 
1596 			return vpx_globals.profiles[i];
1597 		}
1598 	}
1599 
1600 	return NULL;
1601 }
1602 
1603 #define UINTVAL(v) (v > 0 ? v : 0);
1604 #define _VPX_CHECK_ERR(fmt, ...) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "VPX config param \"%s\" -> \"%s\" value \"%s\" " fmt "\n", profile_name, name, value, __VA_ARGS__)
1605 #define _VPX_CHECK_ERRDEF(var, fmt, ...) _VPX_CHECK_ERR(fmt ", leave default %d", __VA_ARGS__, var)
1606 #define _VPX_CHECK_ERRDEF_INVL(var) _VPX_CHECK_ERRDEF(var, "%s", "is invalid")
1607 #define _VPX_CHECK_ERRDEF_NOTAPPL(var) _VPX_CHECK_ERRDEF(var, "%s", "is not applicable")
1608 #define _VPX_CHECK_MIN(var, val, min) do { int lval = val; if (lval < (min)) _VPX_CHECK_ERRDEF(var, "is lower than %d", min); else var = lval; } while(0)
1609 #define _VPX_CHECK_MAX(var, val, max) do { int lval = val; if (lval > (max)) _VPX_CHECK_ERRDEF(var, "is larger than %d", max); else var = lval; } while(0)
1610 #define _VPX_CHECK_MIN_MAX(var, val, min, max) do { int lval = val; if ((lval < (min)) || (lval > (max))) _VPX_CHECK_ERRDEF(var, "not in [%d..%d]", min, max); else var = lval; } while(0)
1611 
parse_profile(my_vpx_cfg_t * my_cfg,switch_xml_t profile,int codec_type)1612 static void parse_profile(my_vpx_cfg_t *my_cfg, switch_xml_t profile, int codec_type)
1613 {
1614 	switch_xml_t param = NULL;
1615 	const char *profile_name = profile->name;
1616 
1617 	vpx_codec_dec_cfg_t *dec_cfg = NULL;
1618 	vpx_codec_enc_cfg_t *enc_cfg = NULL;
1619 
1620 	dec_cfg = &my_cfg->dec_cfg;
1621 	enc_cfg = &my_cfg->enc_cfg;
1622 
1623 	for (param = switch_xml_child(profile, "param"); param; param = param->next) {
1624 		const char *name = switch_xml_attr(param, "name");
1625 		const char *value = switch_xml_attr(param, "value");
1626 		int val;
1627 
1628 		if (!enc_cfg || !dec_cfg) break;
1629 		if (zstr(name) || zstr(value)) continue;
1630 
1631 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: %s = %s\n", my_cfg->name, name, value);
1632 
1633 		val = atoi(value);
1634 
1635 		if (!strcmp(name, "dec-threads")) {
1636 			_VPX_CHECK_MIN(dec_cfg->threads, switch_parse_cpu_string(value), 1);
1637 		} else if (!strcmp(name, "enc-threads")) {
1638 			_VPX_CHECK_MIN(enc_cfg->g_threads, switch_parse_cpu_string(value), 1);
1639 		} else if (!strcmp(name, "g-profile")) {
1640 			_VPX_CHECK_MIN_MAX(enc_cfg->g_profile, val, 0, 3);
1641 #if 0
1642 		} else if (!strcmp(name, "g-timebase")) {
1643 			int num = 0;
1644 			int den = 0;
1645 			char *slash = strchr(value, '/');
1646 
1647 			num = UINTVAL(val);
1648 
1649 			if (slash) {
1650 				slash++;
1651 				den = atoi(slash);
1652 
1653 				if (den < 0) den = 0;
1654 			}
1655 
1656 			if (num && den) {
1657 				enc_cfg->g_timebase.num = num;
1658 				enc_cfg->g_timebase.den = den;
1659 			}
1660 #endif
1661 		} else if (!strcmp(name, "g-error-resilient")) {
1662 			char *s = strdup(value);
1663 			if (s) {
1664 				vpx_codec_er_flags_t res = 0;
1665 				int argc;
1666 				char *argv[10];
1667 				int i;
1668 
1669 				argc = switch_separate_string(s, '|', argv, (sizeof(argv) / sizeof(argv[0])));
1670 				for (i = 0; i < argc; i++) {
1671 					if (!strcasecmp(argv[i], "DEFAULT")) {
1672 						res |= VPX_ERROR_RESILIENT_DEFAULT;
1673 					} else if (!strcasecmp(argv[i], "PARTITIONS")) {
1674 						res |= VPX_ERROR_RESILIENT_PARTITIONS;
1675 					} else {
1676 						_VPX_CHECK_ERR("has invalid token \"%s\"", argv[i]);
1677 					}
1678 				}
1679 
1680 				free(s);
1681 				enc_cfg->g_error_resilient = res;
1682 			}
1683 		} else if (!strcmp(name, "g-pass")) {
1684 			if (!strcasecmp(value, "ONE_PASS")) {
1685 				enc_cfg->g_pass = VPX_RC_ONE_PASS;
1686 			} else if (!strcasecmp(value, "FIRST_PASS")) {
1687 				enc_cfg->g_pass = VPX_RC_FIRST_PASS;
1688 			} else if (!strcasecmp(value, "LAST_PASS")) {
1689 				enc_cfg->g_pass = VPX_RC_FIRST_PASS;
1690 			} else {
1691 				_VPX_CHECK_ERRDEF_INVL(enc_cfg->g_pass);
1692 			}
1693 		} else if (!strcmp(name, "g-lag-in-frames")) {
1694 			_VPX_CHECK_MIN_MAX(enc_cfg->g_lag_in_frames, val, 0, 25);
1695 		} else if (!strcmp(name, "rc_dropframe_thresh")) {
1696 			_VPX_CHECK_MIN_MAX(enc_cfg->rc_dropframe_thresh, val, 0, 100);
1697 		} else if (!strcmp(name, "rc-resize-allowed")) {
1698 			_VPX_CHECK_MIN_MAX(enc_cfg->rc_resize_allowed, val, 0, 1);
1699 		} else if (!strcmp(name, "rc-scaled-width")) {
1700 			_VPX_CHECK_MIN(enc_cfg->rc_scaled_width, val, 0);
1701 		} else if (!strcmp(name, "rc-scaled-height")) {
1702 			_VPX_CHECK_MIN(enc_cfg->rc_scaled_height, val, 0);
1703 		} else if (!strcmp(name, "rc-resize-up-thresh")) {
1704 			_VPX_CHECK_MIN_MAX(enc_cfg->rc_resize_up_thresh, val, 0, 100);
1705 		} else if (!strcmp(name, "rc-resize-down-thresh")) {
1706 			_VPX_CHECK_MIN_MAX(enc_cfg->rc_resize_down_thresh, val, 0, 100);
1707 		} else if (!strcmp(name, "rc-end-usage")) {
1708 			if (!strcasecmp(value, "VBR")) {
1709 				enc_cfg->rc_end_usage = VPX_VBR;
1710 			} else if (!strcasecmp(value, "CBR")) {
1711 				enc_cfg->rc_end_usage = VPX_CBR;
1712 			} else if (!strcasecmp(value, "CQ")) {
1713 				enc_cfg->rc_end_usage = VPX_CQ;
1714 			} else if (!strcasecmp(value, "Q")) {
1715 				enc_cfg->rc_end_usage = VPX_Q;
1716 			} else {
1717 				_VPX_CHECK_ERRDEF_INVL(enc_cfg->rc_end_usage);
1718 			}
1719 		} else if (!strcmp(name, "rc-target-bitrate")) {
1720 			_VPX_CHECK_MIN(enc_cfg->rc_target_bitrate, switch_parse_bandwidth_string(value), 1);
1721 		} else if (!strcmp(name, "rc-min-quantizer")) {
1722 			_VPX_CHECK_MIN_MAX(enc_cfg->rc_min_quantizer, val, 0, 63);
1723 		} else if (!strcmp(name, "rc-max-quantizer")) {
1724 			_VPX_CHECK_MIN_MAX(enc_cfg->rc_max_quantizer, val, 0, 63);
1725 		} else if (!strcmp(name, "rc-undershoot-pct")) {
1726 			if (codec_type == CODEC_TYPE_VP9) {
1727 				_VPX_CHECK_MIN_MAX(enc_cfg->rc_undershoot_pct, val, 0, 100);
1728 			} else {
1729 				_VPX_CHECK_MIN_MAX(enc_cfg->rc_undershoot_pct, val, 0, 1000);
1730 			}
1731 		} else if (!strcmp(name, "rc-overshoot-pct")) {
1732 			if (codec_type == CODEC_TYPE_VP9) {
1733 				_VPX_CHECK_MIN_MAX(enc_cfg->rc_overshoot_pct, val, 0, 100);
1734 			} else {
1735 				_VPX_CHECK_MIN_MAX(enc_cfg->rc_overshoot_pct, val, 0, 1000);
1736 			}
1737 		} else if (!strcmp(name, "rc-buf-sz")) {
1738 			_VPX_CHECK_MIN(enc_cfg->rc_buf_sz, val, 1);
1739 		} else if (!strcmp(name, "rc-buf-initial-sz")) {
1740 			_VPX_CHECK_MIN(enc_cfg->rc_buf_initial_sz, val, 1);
1741 		} else if (!strcmp(name, "rc-buf-optimal-sz")) {
1742 			_VPX_CHECK_MIN(enc_cfg->rc_buf_optimal_sz, val, 1);
1743 		} else if (!strcmp(name, "rc-2pass-vbr-bias-pct")) {
1744 			_VPX_CHECK_MIN_MAX(enc_cfg->rc_2pass_vbr_bias_pct, val, 0, 100);
1745 		} else if (!strcmp(name, "rc-2pass-vbr-minsection-pct")) {
1746 			_VPX_CHECK_MIN(enc_cfg->rc_2pass_vbr_minsection_pct, val, 1);
1747 		} else if (!strcmp(name, "rc-2pass-vbr-maxsection-pct")) {
1748 			_VPX_CHECK_MIN(enc_cfg->rc_2pass_vbr_maxsection_pct, val, 1);
1749 		} else if (!strcmp(name, "kf-mode")) {
1750 			if (!strcasecmp(value, "AUTO")) {
1751 				enc_cfg->kf_mode = VPX_KF_AUTO;
1752 			} else if (!strcasecmp(value, "DISABLED")) {
1753 				enc_cfg->kf_mode = VPX_KF_DISABLED;
1754 			} else {
1755 				_VPX_CHECK_ERRDEF_INVL(enc_cfg->kf_mode);
1756 			}
1757 		} else if (!strcmp(name, "kf-min-dist")) {
1758 			_VPX_CHECK_MIN(enc_cfg->kf_min_dist, val, 0);
1759 		} else if (!strcmp(name, "kf-max-dist")) {
1760 			_VPX_CHECK_MIN(enc_cfg->kf_max_dist, val, 0);
1761 		} else if (!strcmp(name, "ss-number-layers")) {
1762 			_VPX_CHECK_MIN_MAX(enc_cfg->ss_number_layers, val, 0, VPX_SS_MAX_LAYERS);
1763 		} else if (!strcmp(name, "ts-number-layers")) {
1764 			if (codec_type == CODEC_TYPE_VP8) {
1765 				_VPX_CHECK_MIN_MAX(enc_cfg->ts_number_layers, val, 0, VPX_SS_MAX_LAYERS);
1766 			} else if (codec_type == CODEC_TYPE_VP9) {
1767 				_VPX_CHECK_MIN_MAX(enc_cfg->ts_number_layers, val, enc_cfg->ts_number_layers, enc_cfg->ts_number_layers); // lock it
1768 			} else {
1769 				_VPX_CHECK_ERRDEF_NOTAPPL(enc_cfg->ts_number_layers);
1770 			}
1771 		} else if (!strcmp(name, "ts-periodicity")) {
1772 			if (codec_type == CODEC_TYPE_VP9) {
1773 				_VPX_CHECK_MIN_MAX(enc_cfg->ts_periodicity, val, enc_cfg->ts_periodicity, enc_cfg->ts_periodicity); // lock it
1774 			} else {
1775 				_VPX_CHECK_MIN_MAX(enc_cfg->ts_periodicity, val, 0, 16);
1776 			}
1777 		} else if (!strcmp(name, "temporal-layering-mode")) {
1778 			if (codec_type == CODEC_TYPE_VP9) {
1779 				_VPX_CHECK_MIN_MAX(enc_cfg->temporal_layering_mode, val, enc_cfg->temporal_layering_mode, enc_cfg->temporal_layering_mode); // lock it
1780 			} else {
1781 				_VPX_CHECK_MIN_MAX(enc_cfg->temporal_layering_mode, val, 0, 3);
1782 			}
1783 		} else if (!strcmp(name, "lossless")) {
1784 			if (codec_type == CODEC_TYPE_VP9) {
1785 				_VPX_CHECK_MIN_MAX(my_cfg->lossless, val, 0, 1);
1786 			} else {
1787 				_VPX_CHECK_ERRDEF_NOTAPPL(my_cfg->lossless);
1788 			}
1789 		} else if (!strcmp(name, "cpuused")) {
1790 			if (codec_type == CODEC_TYPE_VP8) {
1791 				_VPX_CHECK_MIN_MAX(my_cfg->cpuused, val, -16, 16);
1792 			} else {
1793 				_VPX_CHECK_MIN_MAX(my_cfg->cpuused, val, -8, 8);
1794 			}
1795 		} else if (!strcmp(name, "token-parts")) {
1796 			_VPX_CHECK_MIN_MAX(my_cfg->token_parts, switch_parse_cpu_string(value), VP8_ONE_TOKENPARTITION, VP8_EIGHT_TOKENPARTITION);
1797 		} else if (!strcmp(name, "static-thresh")) {
1798 			_VPX_CHECK_MIN(my_cfg->static_thresh, val, 0);
1799 		} else if (!strcmp(name, "noise-sensitivity")) {
1800 			if (codec_type == CODEC_TYPE_VP8) {
1801 				_VPX_CHECK_MIN_MAX(my_cfg->noise_sensitivity, val, 0, 6);
1802 			} else {
1803 				_VPX_CHECK_ERRDEF_NOTAPPL(my_cfg->noise_sensitivity);
1804 			}
1805 		} else if (!strcmp(name, "max-intra-bitrate-pct")) {
1806 			if (codec_type == CODEC_TYPE_VP8) {
1807 				_VPX_CHECK_MIN(my_cfg->max_intra_bitrate_pct, val, 0);
1808 			} else {
1809 				_VPX_CHECK_ERRDEF_NOTAPPL(my_cfg->max_intra_bitrate_pct);
1810 			}
1811 		} else if (!strcmp(name, "vp9e-tune-content")) {
1812 			if (codec_type == CODEC_TYPE_VP9) {
1813 				if (!strcasecmp(value, "DEFAULT")) {
1814 					my_cfg->tune_content = VP9E_CONTENT_DEFAULT;
1815 				} else if (!strcasecmp(value, "SCREEN")) {
1816 					my_cfg->tune_content = VP9E_CONTENT_SCREEN;
1817 				} else {
1818 					_VPX_CHECK_ERRDEF_INVL(my_cfg->tune_content);
1819 				}
1820 			} else {
1821 				_VPX_CHECK_ERRDEF_NOTAPPL(my_cfg->tune_content);
1822 			}
1823 		}
1824 	} // for param
1825 }
1826 
parse_codecs(my_vpx_cfg_t * my_cfg,switch_xml_t codecs)1827 static void parse_codecs(my_vpx_cfg_t *my_cfg, switch_xml_t codecs)
1828 {
1829 	switch_xml_t codec = NULL;
1830 
1831 	if (!codecs) {
1832 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "no codecs in %s\n", my_cfg->name);
1833 		return;
1834 	}
1835 
1836 	codec = switch_xml_child(codecs, "codec");
1837 
1838 	if (my_cfg->codecs) {
1839 		switch_event_destroy(&my_cfg->codecs);
1840 	}
1841 
1842 	switch_event_create(&my_cfg->codecs, SWITCH_EVENT_CLONE);
1843 
1844 	for (; codec; codec = codec->next) {
1845 		const char *name = switch_xml_attr(codec, "name");
1846 		const char *profile = switch_xml_attr(codec, "profile");
1847 
1848 		if (zstr(name) || zstr(profile)) continue;
1849 
1850 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "codec: %s, profile: %s\n", name, profile);
1851 
1852 		switch_event_add_header_string(my_cfg->codecs, SWITCH_STACK_BOTTOM, name, profile);
1853 	}
1854 }
1855 
load_config()1856 static void load_config()
1857 {
1858 	switch_xml_t cfg = NULL, xml = NULL;
1859 	my_vpx_cfg_t *my_cfg = NULL;
1860 
1861 	memset(&vpx_globals, 0, sizeof(vpx_globals));
1862 
1863 	vpx_globals.max_bitrate = switch_calc_bitrate(1920, 1080, 5, 60);
1864 	vpx_globals.rtp_slice_size = SLICE_SIZE;
1865 	vpx_globals.key_frame_min_freq = KEY_FRAME_MIN_FREQ;
1866 
1867 	xml = switch_xml_open_cfg("vpx.conf", &cfg, NULL);
1868 
1869 	if (xml) {
1870 		switch_xml_t settings = switch_xml_child(cfg, "settings");
1871 		switch_xml_t profiles = switch_xml_child(cfg, "profiles");
1872 
1873 		if (settings) {
1874 			switch_xml_t param;
1875 
1876 			for (param = switch_xml_child(settings, "param"); param; param = param->next) {
1877 				const char *profile_name = "settings"; // for _VPX_CHECK_*() macroses only
1878 				const char *name = switch_xml_attr(param, "name");
1879 				const char *value = switch_xml_attr(param, "value");
1880 
1881 				if (zstr(name) || zstr(value)) continue;
1882 
1883 				if (!strcmp(name, "debug")) {
1884 					vpx_globals.debug = atoi(value);
1885 				} else if (!strcmp(name, "max-bitrate")) {
1886 					_VPX_CHECK_MIN(vpx_globals.max_bitrate, switch_parse_bandwidth_string(value), 1);
1887 				} else if (!strcmp(name, "rtp-slice-size")) {
1888 					_VPX_CHECK_MIN_MAX(vpx_globals.rtp_slice_size, atoi(value), 500, 1500);
1889 				} else if (!strcmp(name, "key-frame-min-freq")) {
1890 					_VPX_CHECK_MIN_MAX(vpx_globals.key_frame_min_freq, atoi(value) * 1000, 10 * 1000, 3000 * 1000);
1891 				} else if (!strcmp(name, "dec-threads")) {
1892 					int val = switch_parse_cpu_string(value);
1893 					_VPX_CHECK_MIN(vpx_globals.dec_threads, val, 1);
1894 				} else if (!strcmp(name, "enc-threads")) {
1895 					int val = switch_parse_cpu_string(value);
1896 					_VPX_CHECK_MIN(vpx_globals.enc_threads, val, 1);
1897 				}
1898 			}
1899 		}
1900 
1901 		if (profiles) {
1902 			switch_xml_t profile = switch_xml_child(profiles, "profile");
1903 
1904 			for (; profile; profile = profile->next) {
1905 				switch_xml_t codecs = switch_xml_child(profile, "codecs");
1906 				const char *profile_name = switch_xml_attr(profile, "name");
1907 				my_vpx_cfg_t *my_cfg = NULL;
1908 
1909 				if (zstr(profile_name)) continue;
1910 
1911 				my_cfg = find_cfg_profile(profile_name, SWITCH_TRUE);
1912 
1913 				if (!my_cfg) continue;
1914 
1915 				parse_profile(my_cfg, profile, CODEC_TYPE(profile_name));
1916 				parse_codecs(my_cfg, codecs);
1917 			} // for profile
1918 		} // profiles
1919 
1920 		switch_xml_free(xml);
1921 	} // xml
1922 
1923 	if (vpx_globals.max_bitrate <= 0) {
1924 		vpx_globals.max_bitrate = switch_calc_bitrate(1920, 1080, 5, 60);
1925 	}
1926 
1927 	if (vpx_globals.rtp_slice_size < 500 || vpx_globals.rtp_slice_size > 1500) {
1928 		vpx_globals.rtp_slice_size = SLICE_SIZE;
1929 	}
1930 
1931 	if (vpx_globals.key_frame_min_freq < 10000 || vpx_globals.key_frame_min_freq > 3 * 1000000) {
1932 		vpx_globals.key_frame_min_freq = KEY_FRAME_MIN_FREQ;
1933 	}
1934 
1935 	my_cfg = find_cfg_profile("vp8", SWITCH_FALSE);
1936 
1937 	if (my_cfg) {
1938 		if (!my_cfg->enc_cfg.g_threads) my_cfg->enc_cfg.g_threads = 1;
1939 		if (!my_cfg->dec_cfg.threads) my_cfg->dec_cfg.threads = switch_parse_cpu_string("cpu/2/4");
1940 	}
1941 
1942 	my_cfg = find_cfg_profile("vp9", SWITCH_FALSE);
1943 
1944 	if (my_cfg) {
1945 		if (!my_cfg->enc_cfg.g_threads) my_cfg->enc_cfg.g_threads = 1;
1946 		if (!my_cfg->dec_cfg.threads) my_cfg->dec_cfg.threads = switch_parse_cpu_string("cpu/2/4");
1947 	}
1948 }
1949 
1950 #define VPX_API_SYNTAX "<reload|debug <on|off>>"
SWITCH_STANDARD_API(vpx_api_function)1951 SWITCH_STANDARD_API(vpx_api_function)
1952 {
1953 	if (session) {
1954 		return SWITCH_STATUS_FALSE;
1955 	}
1956 
1957 	if (zstr(cmd)) {
1958 		goto usage;
1959 	}
1960 
1961 	if (!strcasecmp(cmd, "reload")) {
1962 		const char *err;
1963 		my_vpx_cfg_t *my_cfg;
1964 		int i;
1965 
1966 		switch_xml_reload(&err);
1967 		stream->write_function(stream, "Reload XML [%s]\n", err);
1968 
1969 		load_config();
1970 
1971 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "    %-26s = %d\n", "rtp-slice-size", vpx_globals.rtp_slice_size);
1972 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "    %-26s = %d\n", "key-frame-min-freq", vpx_globals.key_frame_min_freq);
1973 
1974 		for (i = 0; i < MAX_PROFILES; i++) {
1975 			my_cfg = vpx_globals.profiles[i];
1976 
1977 			if (!my_cfg) break;
1978 
1979 			if (!strcmp(my_cfg->name, "vp8")) {
1980 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Codec: %s\n", vpx_codec_iface_name(vpx_codec_vp8_cx()));
1981 			} else if (!strcmp(my_cfg->name, "vp9")) {
1982 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Codec: %s\n", vpx_codec_iface_name(vpx_codec_vp9_cx()));
1983 			} else {
1984 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Codec: %s\n", my_cfg->name);
1985 			}
1986 
1987 			show_config(my_cfg, &my_cfg->enc_cfg);
1988 		}
1989 
1990 		stream->write_function(stream, "+OK\n");
1991 	} else if (!strcasecmp(cmd, "debug")) {
1992 		stream->write_function(stream, "+OK debug %s\n", vpx_globals.debug ? "on" : "off");
1993 	} else if (!strcasecmp(cmd, "debug on")) {
1994 		vpx_globals.debug = 1;
1995 		stream->write_function(stream, "+OK debug on\n");
1996 	} else if (!strcasecmp(cmd, "debug off")) {
1997 		vpx_globals.debug = 0;
1998 		stream->write_function(stream, "+OK debug off\n");
1999 	}
2000 
2001 	return SWITCH_STATUS_SUCCESS;
2002 
2003   usage:
2004 	stream->write_function(stream, "USAGE: %s\n", VPX_API_SYNTAX);
2005 	return SWITCH_STATUS_SUCCESS;
2006 }
2007 
SWITCH_MODULE_LOAD_FUNCTION(mod_vpx_load)2008 SWITCH_MODULE_LOAD_FUNCTION(mod_vpx_load)
2009 {
2010 	switch_codec_interface_t *codec_interface;
2011 	switch_api_interface_t *vpx_api_interface;
2012 
2013 	memset(&vpx_globals, 0, sizeof(struct vpx_globals));
2014 	load_config();
2015 
2016 	/* connect my internal structure to the blank pointer passed to me */
2017 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
2018 
2019 	SWITCH_ADD_CODEC(codec_interface, "VP8 Video");
2020 	switch_core_codec_add_video_implementation(pool, codec_interface, 99, "VP8", NULL,
2021 											   switch_vpx_init, switch_vpx_encode, switch_vpx_decode, switch_vpx_control, switch_vpx_destroy);
2022 	SWITCH_ADD_CODEC(codec_interface, "VP9 Video");
2023 	switch_core_codec_add_video_implementation(pool, codec_interface, 99, "VP9", NULL,
2024 											   switch_vpx_init, switch_vpx_encode, switch_vpx_decode, switch_vpx_control, switch_vpx_destroy);
2025 
2026 	SWITCH_ADD_API(vpx_api_interface, "vpx",
2027 				   "VPX API", vpx_api_function, VPX_API_SYNTAX);
2028 
2029 	switch_console_set_complete("add vpx reload");
2030 	switch_console_set_complete("add vpx debug");
2031 	switch_console_set_complete("add vpx debug on");
2032 	switch_console_set_complete("add vpx debug off");
2033 
2034 	/* indicate that the module should continue to be loaded */
2035 	return SWITCH_STATUS_SUCCESS;
2036 }
2037 
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vpx_shutdown)2038 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vpx_shutdown)
2039 {
2040 	int i;
2041 
2042 	for (i = 0; i < MAX_PROFILES; i++) {
2043 		my_vpx_cfg_t *my_cfg = vpx_globals.profiles[i];
2044 
2045 		if (!my_cfg) break;
2046 
2047 		if (my_cfg->codecs) {
2048 			switch_event_destroy(&my_cfg->codecs);
2049 		}
2050 
2051 		free(my_cfg);
2052 	}
2053 
2054 	return SWITCH_STATUS_SUCCESS;
2055 }
2056 #endif
2057 #endif
2058 /* For Emacs:
2059  * Local Variables:
2060  * mode:c
2061  * indent-tabs-mode:t
2062  * tab-width:4
2063  * c-basic-offset:4
2064  * End:
2065  * For VIM:
2066  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
2067  */
2068