2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved.
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
13 * VP9 SVC encoding support via libvpx
23 #define VPX_DISABLE_CTRL_TYPECHECKS 1
24 #include "./vpx_config.h"
25 #include "vpx/svc_context.h"
26 #include "vpx/vp8cx.h"
27 #include "vpx/vpx_encoder.h"
28 #include "vpx_mem/vpx_mem.h"
29 #include "vp9/common/vp9_onyxc_int.h"
32 #define strtok_r strtok_s
33 #ifndef MINGW_HAS_SECURE_API
34 // proto from /usr/x86_64-w64-mingw32/include/sec_api/string_s.h
35 _CRTIMP char *__cdecl strtok_s(char *str, const char *delim, char **context);
36 #endif /* MINGW_HAS_SECURE_API */
37 #endif /* __MINGW32__ */
40 #define strdup _strdup
41 #define strtok_r strtok_s
44 #define SVC_REFERENCE_FRAMES 8
45 #define SUPERFRAME_SLOTS (8)
46 #define SUPERFRAME_BUFFER_SIZE (SUPERFRAME_SLOTS * sizeof(uint32_t) + 2)
47 #define OPTION_BUFFER_SIZE 256
48 #define COMPONENTS 4 // psnr & sse statistics maintained for total, y, u, v
50 static const int DEFAULT_QUANTIZER_VALUES[VPX_SS_MAX_LAYERS] = {
54 static const int DEFAULT_SCALE_FACTORS_NUM[VPX_SS_MAX_LAYERS] = {
58 static const int DEFAULT_SCALE_FACTORS_DEN[VPX_SS_MAX_LAYERS] = {
70 static const int option_max_values[ALL_OPTION_TYPES] = {
71 63, INT_MAX, INT_MAX, 1
74 static const int option_min_values[ALL_OPTION_TYPES] = {
79 typedef struct FrameData {
80 void *buf; // compressed data buffer
81 size_t size; // length of compressed data
82 vpx_codec_frame_flags_t flags; /**< flags for this frame */
83 struct FrameData *next;
86 typedef struct SvcInternal {
87 char options[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_options
88 char quantizers[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_quantizers
89 char scale_factors[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_scale_factors
91 // values extracted from option, quantizers
92 int scaling_factor_num[VPX_SS_MAX_LAYERS];
93 int scaling_factor_den[VPX_SS_MAX_LAYERS];
94 int quantizer[VPX_SS_MAX_LAYERS];
95 int enable_auto_alt_ref[VPX_SS_MAX_LAYERS];
96 int bitrates[VPX_SS_MAX_LAYERS];
98 // accumulated statistics
99 double psnr_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; // total/Y/U/V
100 uint64_t sse_sum[VPX_SS_MAX_LAYERS][COMPONENTS];
101 uint32_t bytes_sum[VPX_SS_MAX_LAYERS];
103 // codec encoding values
104 int width; // width of highest layer
105 int height; // height of highest layer
106 int kf_dist; // distance between keyframes
109 int encode_frame_count;
111 int frame_within_gop;
115 int use_multiple_frame_contexts;
117 char message_buffer[2048];
118 vpx_codec_ctx_t *codec_ctx;
121 static SvcInternal *get_svc_internal(SvcContext *svc_ctx) {
122 if (svc_ctx == NULL) return NULL;
123 if (svc_ctx->internal == NULL) {
124 SvcInternal *const si = (SvcInternal *)malloc(sizeof(*si));
126 memset(si, 0, sizeof(*si));
128 svc_ctx->internal = si;
130 return (SvcInternal *)svc_ctx->internal;
133 static const SvcInternal *get_const_svc_internal(const SvcContext *svc_ctx) {
134 if (svc_ctx == NULL) return NULL;
135 return (const SvcInternal *)svc_ctx->internal;
138 static void svc_log_reset(SvcContext *svc_ctx) {
139 SvcInternal *const si = (SvcInternal *)svc_ctx->internal;
140 si->message_buffer[0] = '\0';
143 static int svc_log(SvcContext *svc_ctx, SVC_LOG_LEVEL level,
144 const char *fmt, ...) {
148 SvcInternal *const si = get_svc_internal(svc_ctx);
150 if (level > svc_ctx->log_level) {
155 retval = vsnprintf(buf, sizeof(buf), fmt, ap);
158 if (svc_ctx->log_print) {
161 strncat(si->message_buffer, buf,
162 sizeof(si->message_buffer) - strlen(si->message_buffer) - 1);
165 if (level == SVC_LOG_ERROR) {
166 si->codec_ctx->err_detail = si->message_buffer;
171 static vpx_codec_err_t extract_option(LAYER_OPTION_TYPE type,
175 if (type == SCALE_FACTOR) {
176 *value0 = strtol(input, &input, 10);
178 return VPX_CODEC_INVALID_PARAM;
179 *value1 = strtol(input, &input, 10);
181 if (*value0 < option_min_values[SCALE_FACTOR] ||
182 *value1 < option_min_values[SCALE_FACTOR] ||
183 *value0 > option_max_values[SCALE_FACTOR] ||
184 *value1 > option_max_values[SCALE_FACTOR])
185 return VPX_CODEC_INVALID_PARAM;
187 *value0 = atoi(input);
188 if (*value0 < option_min_values[type] ||
189 *value0 > option_max_values[type])
190 return VPX_CODEC_INVALID_PARAM;
195 static vpx_codec_err_t parse_layer_options_from_string(SvcContext *svc_ctx,
196 LAYER_OPTION_TYPE type,
201 vpx_codec_err_t res = VPX_CODEC_OK;
204 const char *delim = ",";
207 if (input == NULL || option0 == NULL ||
208 (option1 == NULL && type == SCALE_FACTOR))
209 return VPX_CODEC_INVALID_PARAM;
211 input_string = strdup(input);
212 token = strtok_r(input_string, delim, &save_ptr);
213 for (i = 0; i < svc_ctx->spatial_layers; ++i) {
215 res = extract_option(type, token, option0 + i, option1 + i);
216 if (res != VPX_CODEC_OK)
218 token = strtok_r(NULL, delim, &save_ptr);
223 if (res == VPX_CODEC_OK && i != svc_ctx->spatial_layers) {
224 svc_log(svc_ctx, SVC_LOG_ERROR,
225 "svc: layer params type: %d %d values required, "
226 "but only %d specified\n", type, svc_ctx->spatial_layers, i);
227 res = VPX_CODEC_INVALID_PARAM;
234 * Parse SVC encoding options
235 * Format: encoding-mode=<svc_mode>,layers=<layer_count>
236 * scale-factors=<n1>/<d1>,<n2>/<d2>,...
237 * quantizers=<q1>,<q2>,...
238 * svc_mode = [i|ip|alt_ip|gf]
240 static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
245 SvcInternal *const si = get_svc_internal(svc_ctx);
246 vpx_codec_err_t res = VPX_CODEC_OK;
247 int i, alt_ref_enabled = 0;
249 if (options == NULL) return VPX_CODEC_OK;
250 input_string = strdup(options);
253 option_name = strtok_r(input_string, "=", &input_ptr);
254 while (option_name != NULL) {
255 // parse option value
256 option_value = strtok_r(NULL, " ", &input_ptr);
257 if (option_value == NULL) {
258 svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n",
260 res = VPX_CODEC_INVALID_PARAM;
263 if (strcmp("spatial-layers", option_name) == 0) {
264 svc_ctx->spatial_layers = atoi(option_value);
265 } else if (strcmp("temporal-layers", option_name) == 0) {
266 svc_ctx->temporal_layers = atoi(option_value);
267 } else if (strcmp("scale-factors", option_name) == 0) {
268 res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR, option_value,
269 si->scaling_factor_num,
270 si->scaling_factor_den);
271 if (res != VPX_CODEC_OK) break;
272 } else if (strcmp("quantizers", option_name) == 0) {
273 res = parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
274 si->quantizer, NULL);
275 if (res != VPX_CODEC_OK) break;
276 } else if (strcmp("auto-alt-refs", option_name) == 0) {
277 res = parse_layer_options_from_string(svc_ctx, AUTO_ALT_REF, option_value,
278 si->enable_auto_alt_ref, NULL);
279 if (res != VPX_CODEC_OK) break;
280 } else if (strcmp("bitrates", option_name) == 0) {
281 res = parse_layer_options_from_string(svc_ctx, BITRATE, option_value,
283 if (res != VPX_CODEC_OK) break;
284 } else if (strcmp("multi-frame-contexts", option_name) == 0) {
285 si->use_multiple_frame_contexts = atoi(option_value);
287 svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name);
288 res = VPX_CODEC_INVALID_PARAM;
291 option_name = strtok_r(NULL, "=", &input_ptr);
295 if (si->use_multiple_frame_contexts &&
296 (svc_ctx->spatial_layers > 3 ||
297 svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4))
298 res = VPX_CODEC_INVALID_PARAM;
300 for (i = 0; i < svc_ctx->spatial_layers; ++i)
301 alt_ref_enabled += si->enable_auto_alt_ref[i];
302 if (alt_ref_enabled > REF_FRAMES - svc_ctx->spatial_layers) {
303 svc_log(svc_ctx, SVC_LOG_ERROR,
304 "svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
305 "enabled auto alt reference frame, but % layers are enabled\n",
306 REF_FRAMES - svc_ctx->spatial_layers, alt_ref_enabled);
307 res = VPX_CODEC_INVALID_PARAM;
313 vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx, const char *options) {
314 SvcInternal *const si = get_svc_internal(svc_ctx);
315 if (svc_ctx == NULL || options == NULL || si == NULL) {
316 return VPX_CODEC_INVALID_PARAM;
318 strncpy(si->options, options, sizeof(si->options));
319 si->options[sizeof(si->options) - 1] = '\0';
323 vpx_codec_err_t vpx_svc_set_quantizers(SvcContext *svc_ctx,
324 const char *quantizers) {
325 SvcInternal *const si = get_svc_internal(svc_ctx);
326 if (svc_ctx == NULL || quantizers == NULL || si == NULL) {
327 return VPX_CODEC_INVALID_PARAM;
329 strncpy(si->quantizers, quantizers, sizeof(si->quantizers));
330 si->quantizers[sizeof(si->quantizers) - 1] = '\0';
334 vpx_codec_err_t vpx_svc_set_scale_factors(SvcContext *svc_ctx,
335 const char *scale_factors) {
336 SvcInternal *const si = get_svc_internal(svc_ctx);
337 if (svc_ctx == NULL || scale_factors == NULL || si == NULL) {
338 return VPX_CODEC_INVALID_PARAM;
340 strncpy(si->scale_factors, scale_factors, sizeof(si->scale_factors));
341 si->scale_factors[sizeof(si->scale_factors) - 1] = '\0';
345 void assign_layer_bitrates(const SvcInternal *const si,
346 vpx_codec_enc_cfg_t *const enc_cfg) {
349 if (si->bitrates[0] != 0) {
350 enc_cfg->rc_target_bitrate = 0;
351 for (i = 0; i < si->layers; ++i) {
352 enc_cfg->ss_target_bitrate[i] = (unsigned int)si->bitrates[i];
353 enc_cfg->rc_target_bitrate += si->bitrates[i];
357 float alloc_ratio[VPX_SS_MAX_LAYERS] = {0};
359 for (i = 0; i < si->layers; ++i) {
360 if (si->scaling_factor_den[i] > 0) {
361 alloc_ratio[i] = (float)(si->scaling_factor_num[i] * 1.0 /
362 si->scaling_factor_den[i]);
364 alloc_ratio[i] *= alloc_ratio[i];
365 total += alloc_ratio[i];
369 for (i = 0; i < si->layers; ++i) {
371 enc_cfg->ss_target_bitrate[i] = (unsigned int)
372 (enc_cfg->rc_target_bitrate * alloc_ratio[i] / total);
378 vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
379 vpx_codec_iface_t *iface,
380 vpx_codec_enc_cfg_t *enc_cfg) {
383 SvcInternal *const si = get_svc_internal(svc_ctx);
384 if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
386 return VPX_CODEC_INVALID_PARAM;
388 if (si == NULL) return VPX_CODEC_MEM_ERROR;
390 si->codec_ctx = codec_ctx;
392 si->width = enc_cfg->g_w;
393 si->height = enc_cfg->g_h;
395 if (enc_cfg->kf_max_dist < 2) {
396 svc_log(svc_ctx, SVC_LOG_ERROR, "key frame distance too small: %d\n",
397 enc_cfg->kf_max_dist);
398 return VPX_CODEC_INVALID_PARAM;
400 si->kf_dist = enc_cfg->kf_max_dist;
402 if (svc_ctx->spatial_layers == 0)
403 svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS;
404 if (svc_ctx->spatial_layers < 1 ||
405 svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) {
406 svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
407 svc_ctx->spatial_layers);
408 return VPX_CODEC_INVALID_PARAM;
411 for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
412 si->quantizer[i] = DEFAULT_QUANTIZER_VALUES[i];
413 si->scaling_factor_num[i] = DEFAULT_SCALE_FACTORS_NUM[i];
414 si->scaling_factor_den[i] = DEFAULT_SCALE_FACTORS_DEN[i];
417 if (strlen(si->quantizers) > 0) {
418 res = parse_layer_options_from_string(svc_ctx, QUANTIZER, si->quantizers,
419 si->quantizer, NULL);
420 if (res != VPX_CODEC_OK)
424 if (strlen(si->scale_factors) > 0) {
425 res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR,
427 si->scaling_factor_num,
428 si->scaling_factor_den);
429 if (res != VPX_CODEC_OK)
433 // Parse aggregate command line options. Options must start with
434 // "layers=xx" then followed by other options
435 res = parse_options(svc_ctx, si->options);
436 if (res != VPX_CODEC_OK) return res;
438 if (svc_ctx->spatial_layers < 1)
439 svc_ctx->spatial_layers = 1;
440 if (svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS)
441 svc_ctx->spatial_layers = VPX_SS_MAX_LAYERS;
443 if (svc_ctx->temporal_layers < 1)
444 svc_ctx->temporal_layers = 1;
445 if (svc_ctx->temporal_layers > VPX_TS_MAX_LAYERS)
446 svc_ctx->temporal_layers = VPX_TS_MAX_LAYERS;
448 si->layers = svc_ctx->spatial_layers;
450 assign_layer_bitrates(si, enc_cfg);
452 #if CONFIG_SPATIAL_SVC
453 for (i = 0; i < si->layers; ++i)
454 enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i];
457 if (svc_ctx->temporal_layers > 1) {
459 for (i = 0; i < svc_ctx->temporal_layers; ++i) {
460 enc_cfg->ts_target_bitrate[i] = enc_cfg->rc_target_bitrate /
461 svc_ctx->temporal_layers;
462 enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i);
466 // modify encoder configuration
467 enc_cfg->ss_number_layers = si->layers;
468 enc_cfg->ts_number_layers = svc_ctx->temporal_layers;
470 // TODO(ivanmaltz): determine if these values need to be set explicitly for
471 // svc, or if the normal default/override mechanism can be used
472 enc_cfg->rc_dropframe_thresh = 0;
473 enc_cfg->rc_resize_allowed = 0;
475 if (enc_cfg->g_pass == VPX_RC_ONE_PASS) {
476 enc_cfg->rc_min_quantizer = 33;
477 enc_cfg->rc_max_quantizer = 33;
480 enc_cfg->rc_undershoot_pct = 100;
481 enc_cfg->rc_overshoot_pct = 15;
482 enc_cfg->rc_buf_initial_sz = 500;
483 enc_cfg->rc_buf_optimal_sz = 600;
484 enc_cfg->rc_buf_sz = 1000;
485 if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0)
486 enc_cfg->g_error_resilient = 1;
489 res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR);
490 if (res != VPX_CODEC_OK) {
491 svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
495 vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1);
496 vpx_codec_control(codec_ctx, VP8E_SET_TOKEN_PARTITIONS, 1);
501 vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx,
504 unsigned int *height) {
506 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
508 if (svc_ctx == NULL || si == NULL || width == NULL || height == NULL) {
509 return VPX_CODEC_INVALID_PARAM;
511 if (layer < 0 || layer >= si->layers) return VPX_CODEC_INVALID_PARAM;
513 num = si->scaling_factor_num[layer];
514 den = si->scaling_factor_den[layer];
515 if (num == 0 || den == 0) return VPX_CODEC_INVALID_PARAM;
517 w = si->width * num / den;
518 h = si->height * num / den;
520 // make height and width even to make chrome player happy
530 static void set_svc_parameters(SvcContext *svc_ctx,
531 vpx_codec_ctx_t *codec_ctx) {
533 vpx_svc_parameters_t svc_params;
534 SvcInternal *const si = get_svc_internal(svc_ctx);
536 memset(&svc_params, 0, sizeof(svc_params));
537 svc_params.temporal_layer = 0;
538 svc_params.spatial_layer = si->layer;
541 if (VPX_CODEC_OK != vpx_svc_get_layer_resolution(svc_ctx, layer,
543 &svc_params.height)) {
544 svc_log(svc_ctx, SVC_LOG_ERROR, "vpx_svc_get_layer_resolution failed\n");
547 if (codec_ctx->config.enc->g_pass == VPX_RC_ONE_PASS) {
548 svc_params.min_quantizer = si->quantizer[layer];
549 svc_params.max_quantizer = si->quantizer[layer];
551 svc_params.min_quantizer = codec_ctx->config.enc->rc_min_quantizer;
552 svc_params.max_quantizer = codec_ctx->config.enc->rc_max_quantizer;
555 svc_params.distance_from_i_frame = si->frame_within_gop;
556 vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &svc_params);
560 * Encode a frame into multiple layers
561 * Create a superframe containing the individual layers
563 vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
564 struct vpx_image *rawimg, vpx_codec_pts_t pts,
565 int64_t duration, int deadline) {
567 vpx_codec_iter_t iter;
568 const vpx_codec_cx_pkt_t *cx_pkt;
569 int layer_for_psnr = 0;
570 SvcInternal *const si = get_svc_internal(svc_ctx);
571 if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) {
572 return VPX_CODEC_INVALID_PARAM;
575 svc_log_reset(svc_ctx);
577 si->layers = svc_ctx->spatial_layers;
578 if (si->encode_frame_count == 0) {
579 si->frame_within_gop = 0;
581 si->is_keyframe = (si->frame_within_gop == 0);
583 if (rawimg != NULL) {
584 svc_log(svc_ctx, SVC_LOG_DEBUG,
585 "vpx_svc_encode layers: %d, frame_count: %d, "
586 "frame_within_gop: %d\n", si->layers, si->encode_frame_count,
587 si->frame_within_gop);
590 if (rawimg != NULL) {
592 for (si->layer = 0; si->layer < si->layers; ++si->layer) {
593 set_svc_parameters(svc_ctx, codec_ctx);
597 res = vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0,
599 if (res != VPX_CODEC_OK) {
602 // save compressed data
604 while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) {
605 switch (cx_pkt->kind) {
606 case VPX_CODEC_PSNR_PKT: {
608 svc_log(svc_ctx, SVC_LOG_DEBUG,
609 "SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): "
610 "%2.3f %2.3f %2.3f %2.3f \n",
611 si->frame_received, layer_for_psnr,
612 cx_pkt->data.psnr.psnr[0], cx_pkt->data.psnr.psnr[1],
613 cx_pkt->data.psnr.psnr[2], cx_pkt->data.psnr.psnr[3]);
614 svc_log(svc_ctx, SVC_LOG_DEBUG,
615 "SVC frame: %d, layer: %d, SSE(Total/Y/U/V): "
616 "%2.3f %2.3f %2.3f %2.3f \n",
617 si->frame_received, layer_for_psnr,
618 cx_pkt->data.psnr.sse[0], cx_pkt->data.psnr.sse[1],
619 cx_pkt->data.psnr.sse[2], cx_pkt->data.psnr.sse[3]);
620 for (i = 0; i < COMPONENTS; i++) {
621 si->psnr_sum[layer_for_psnr][i] += cx_pkt->data.psnr.psnr[i];
622 si->sse_sum[layer_for_psnr][i] += cx_pkt->data.psnr.sse[i];
625 if (layer_for_psnr == svc_ctx->spatial_layers)
629 #if CONFIG_SPATIAL_SVC
630 case VPX_CODEC_SPATIAL_SVC_LAYER_SIZES: {
632 for (i = 0; i < si->layers; ++i)
633 si->bytes_sum[i] += cx_pkt->data.layer_sizes[i];
643 if (rawimg != NULL) {
644 ++si->frame_within_gop;
645 ++si->encode_frame_count;
651 const char *vpx_svc_get_message(const SvcContext *svc_ctx) {
652 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
653 if (svc_ctx == NULL || si == NULL) return NULL;
654 return si->message_buffer;
657 int vpx_svc_get_encode_frame_count(const SvcContext *svc_ctx) {
658 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
659 if (svc_ctx == NULL || si == NULL) return 0;
660 return si->encode_frame_count;
663 void vpx_svc_set_keyframe(SvcContext *svc_ctx) {
664 SvcInternal *const si = get_svc_internal(svc_ctx);
665 if (svc_ctx == NULL || si == NULL) return;
666 si->frame_within_gop = 0;
669 static double calc_psnr(double d) {
670 if (d == 0) return 100;
671 return -10.0 * log(d) / log(10.0);
674 // dump accumulated statistics and reset accumulated values
675 const char *vpx_svc_dump_statistics(SvcContext *svc_ctx) {
676 int number_of_frames, encode_frame_count;
678 uint32_t bytes_total = 0;
679 double scale[COMPONENTS];
680 double psnr[COMPONENTS];
681 double mse[COMPONENTS];
684 SvcInternal *const si = get_svc_internal(svc_ctx);
685 if (svc_ctx == NULL || si == NULL) return NULL;
687 svc_log_reset(svc_ctx);
689 encode_frame_count = si->encode_frame_count;
690 if (si->encode_frame_count <= 0) return vpx_svc_get_message(svc_ctx);
692 svc_log(svc_ctx, SVC_LOG_INFO, "\n");
693 for (i = 0; i < si->layers; ++i) {
694 number_of_frames = encode_frame_count;
696 svc_log(svc_ctx, SVC_LOG_INFO,
697 "Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n",
698 i, (double)si->psnr_sum[i][0] / number_of_frames,
699 (double)si->psnr_sum[i][1] / number_of_frames,
700 (double)si->psnr_sum[i][2] / number_of_frames,
701 (double)si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]);
702 // the following psnr calculation is deduced from ffmpeg.c#print_report
703 y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames;
705 scale[2] = scale[3] = y_scale / 4; // U or V
706 scale[0] = y_scale * 1.5; // total
708 for (j = 0; j < COMPONENTS; j++) {
709 psnr[j] = calc_psnr(si->sse_sum[i][j] / scale[j]);
710 mse[j] = si->sse_sum[i][j] * 255.0 * 255.0 / scale[j];
712 svc_log(svc_ctx, SVC_LOG_INFO,
713 "Layer %d Overall PSNR=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, psnr[0],
714 psnr[1], psnr[2], psnr[3]);
715 svc_log(svc_ctx, SVC_LOG_INFO,
716 "Layer %d Overall MSE=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, mse[0],
717 mse[1], mse[2], mse[3]);
719 bytes_total += si->bytes_sum[i];
720 // clear sums for next time
721 si->bytes_sum[i] = 0;
722 for (j = 0; j < COMPONENTS; ++j) {
723 si->psnr_sum[i][j] = 0;
724 si->sse_sum[i][j] = 0;
728 // only display statistics once
729 si->encode_frame_count = 0;
731 svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total);
732 return vpx_svc_get_message(svc_ctx);
735 void vpx_svc_release(SvcContext *svc_ctx) {
737 if (svc_ctx == NULL) return;
738 // do not use get_svc_internal as it will unnecessarily allocate an
739 // SvcInternal if it was not already allocated
740 si = (SvcInternal *)svc_ctx->internal;
743 svc_ctx->internal = NULL;