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
22 #define VPX_DISABLE_CTRL_TYPECHECKS 1
23 #define VPX_CODEC_DISABLE_COMPAT 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 char *DEFAULT_QUANTIZER_VALUES = "60,53,39,33,27";
51 static const char *DEFAULT_SCALE_FACTORS = "4/16,5/16,7/16,11/16,16/16";
54 typedef struct FrameData {
55 void *buf; // compressed data buffer
56 size_t size; // length of compressed data
57 vpx_codec_frame_flags_t flags; /**< flags for this frame */
58 struct FrameData *next;
61 typedef struct SvcInternal {
62 char options[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_options
63 char quantizers[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_quantizers
64 char scale_factors[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_scale_factors
66 // values extracted from option, quantizers
67 int scaling_factor_num[VPX_SS_MAX_LAYERS];
68 int scaling_factor_den[VPX_SS_MAX_LAYERS];
69 int quantizer[VPX_SS_MAX_LAYERS];
70 int enable_auto_alt_ref[VPX_SS_MAX_LAYERS];
72 // accumulated statistics
73 double psnr_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; // total/Y/U/V
74 uint64_t sse_sum[VPX_SS_MAX_LAYERS][COMPONENTS];
75 uint32_t bytes_sum[VPX_SS_MAX_LAYERS];
77 // codec encoding values
78 int width; // width of highest layer
79 int height; // height of highest layer
80 int kf_dist; // distance between keyframes
83 int encode_frame_count;
90 FrameData *frame_list;
91 FrameData *frame_temp;
94 size_t rc_stats_buf_size;
95 size_t rc_stats_buf_used;
97 char message_buffer[2048];
98 vpx_codec_ctx_t *codec_ctx;
101 // create FrameData from encoder output
102 static struct FrameData *fd_create(void *buf, size_t size,
103 vpx_codec_frame_flags_t flags) {
104 struct FrameData *const frame_data =
105 (struct FrameData *)vpx_malloc(sizeof(*frame_data));
106 if (frame_data == NULL) {
109 frame_data->buf = vpx_malloc(size);
110 if (frame_data->buf == NULL) {
111 vpx_free(frame_data);
114 vpx_memcpy(frame_data->buf, buf, size);
115 frame_data->size = size;
116 frame_data->flags = flags;
121 static void fd_free(struct FrameData *p) {
129 // add FrameData to list
130 static void fd_list_add(struct FrameData **list, struct FrameData *layer_data) {
131 struct FrameData **p = list;
133 while (*p != NULL) p = &(*p)->next;
135 layer_data->next = NULL;
138 // free FrameData list
139 static void fd_free_list(struct FrameData *list) {
140 struct FrameData *p = list;
149 static SvcInternal *get_svc_internal(SvcContext *svc_ctx) {
150 if (svc_ctx == NULL) return NULL;
151 if (svc_ctx->internal == NULL) {
152 SvcInternal *const si = (SvcInternal *)malloc(sizeof(*si));
154 memset(si, 0, sizeof(*si));
156 svc_ctx->internal = si;
158 return (SvcInternal *)svc_ctx->internal;
161 static const SvcInternal *get_const_svc_internal(const SvcContext *svc_ctx) {
162 if (svc_ctx == NULL) return NULL;
163 return (const SvcInternal *)svc_ctx->internal;
166 static void svc_log_reset(SvcContext *svc_ctx) {
167 SvcInternal *const si = (SvcInternal *)svc_ctx->internal;
168 si->message_buffer[0] = '\0';
171 static int svc_log(SvcContext *svc_ctx, SVC_LOG_LEVEL level,
172 const char *fmt, ...) {
176 SvcInternal *const si = get_svc_internal(svc_ctx);
178 if (level > svc_ctx->log_level) {
183 retval = vsnprintf(buf, sizeof(buf), fmt, ap);
186 if (svc_ctx->log_print) {
189 strncat(si->message_buffer, buf,
190 sizeof(si->message_buffer) - strlen(si->message_buffer) - 1);
193 if (level == SVC_LOG_ERROR) {
194 si->codec_ctx->err_detail = si->message_buffer;
199 static vpx_codec_err_t parse_quantizer_values(SvcContext *svc_ctx,
200 const char *quantizer_values) {
203 const char *delim = ",";
207 vpx_codec_err_t res = VPX_CODEC_OK;
208 SvcInternal *const si = get_svc_internal(svc_ctx);
210 if (quantizer_values == NULL || strlen(quantizer_values) == 0) {
211 input_string = strdup(DEFAULT_QUANTIZER_VALUES);
213 input_string = strdup(quantizer_values);
216 token = strtok_r(input_string, delim, &save_ptr);
217 for (i = 0; i < svc_ctx->spatial_layers; ++i) {
220 if (q <= 0 || q > 100) {
221 svc_log(svc_ctx, SVC_LOG_ERROR,
222 "svc-quantizer-values: invalid value %s\n", token);
223 res = VPX_CODEC_INVALID_PARAM;
226 token = strtok_r(NULL, delim, &save_ptr);
231 si->quantizer[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] = q;
233 if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) {
234 svc_log(svc_ctx, SVC_LOG_ERROR,
235 "svc: quantizers: %d values required, but only %d specified\n",
236 svc_ctx->spatial_layers, found);
237 res = VPX_CODEC_INVALID_PARAM;
243 static vpx_codec_err_t parse_auto_alt_ref(SvcContext *svc_ctx,
244 const char *alt_ref_options) {
247 const char *delim = ",";
249 int found = 0, enabled = 0;
251 vpx_codec_err_t res = VPX_CODEC_OK;
252 SvcInternal *const si = get_svc_internal(svc_ctx);
254 if (alt_ref_options == NULL || strlen(alt_ref_options) == 0) {
255 return VPX_CODEC_INVALID_PARAM;
257 input_string = strdup(alt_ref_options);
260 token = strtok_r(input_string, delim, &save_ptr);
261 for (i = 0; i < svc_ctx->spatial_layers; ++i) {
264 if (value < 0 || value > 1) {
265 svc_log(svc_ctx, SVC_LOG_ERROR,
266 "enable auto alt ref values: invalid value %s\n", token);
267 res = VPX_CODEC_INVALID_PARAM;
270 token = strtok_r(NULL, delim, &save_ptr);
275 si->enable_auto_alt_ref[i] = value;
279 if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) {
280 svc_log(svc_ctx, SVC_LOG_ERROR,
281 "svc: quantizers: %d values required, but only %d specified\n",
282 svc_ctx->spatial_layers, found);
283 res = VPX_CODEC_INVALID_PARAM;
285 if (enabled > REF_FRAMES - svc_ctx->spatial_layers) {
286 svc_log(svc_ctx, SVC_LOG_ERROR,
287 "svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
288 "enabled auto alt reference frame, but % layers are enabled\n",
289 REF_FRAMES - svc_ctx->spatial_layers, enabled);
290 res = VPX_CODEC_INVALID_PARAM;
296 static void log_invalid_scale_factor(SvcContext *svc_ctx, const char *value) {
297 svc_log(svc_ctx, SVC_LOG_ERROR, "svc scale-factors: invalid value %s\n",
301 static vpx_codec_err_t parse_scale_factors(SvcContext *svc_ctx,
302 const char *scale_factors) {
305 const char *delim = ",";
310 vpx_codec_err_t res = VPX_CODEC_OK;
311 SvcInternal *const si = get_svc_internal(svc_ctx);
313 if (scale_factors == NULL || strlen(scale_factors) == 0) {
314 input_string = strdup(DEFAULT_SCALE_FACTORS);
316 input_string = strdup(scale_factors);
318 token = strtok_r(input_string, delim, &save_ptr);
319 for (i = 0; i < svc_ctx->spatial_layers; ++i) {
322 num = strtol(token, &token, 10);
324 log_invalid_scale_factor(svc_ctx, token);
325 res = VPX_CODEC_INVALID_PARAM;
328 if (*token++ != '/') {
329 log_invalid_scale_factor(svc_ctx, token);
330 res = VPX_CODEC_INVALID_PARAM;
333 den = strtol(token, &token, 10);
335 log_invalid_scale_factor(svc_ctx, token);
336 res = VPX_CODEC_INVALID_PARAM;
339 token = strtok_r(NULL, delim, &save_ptr);
342 si->scaling_factor_num[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] =
344 si->scaling_factor_den[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] =
347 if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) {
348 svc_log(svc_ctx, SVC_LOG_ERROR,
349 "svc: scale-factors: %d values required, but only %d specified\n",
350 svc_ctx->spatial_layers, found);
351 res = VPX_CODEC_INVALID_PARAM;
358 * Parse SVC encoding options
359 * Format: encoding-mode=<svc_mode>,layers=<layer_count>
360 * scale-factors=<n1>/<d1>,<n2>/<d2>,...
361 * quantizers=<q1>,<q2>,...
362 * svc_mode = [i|ip|alt_ip|gf]
364 static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
369 vpx_codec_err_t res = VPX_CODEC_OK;
371 if (options == NULL) return VPX_CODEC_OK;
372 input_string = strdup(options);
375 option_name = strtok_r(input_string, "=", &input_ptr);
376 while (option_name != NULL) {
377 // parse option value
378 option_value = strtok_r(NULL, " ", &input_ptr);
379 if (option_value == NULL) {
380 svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n",
382 res = VPX_CODEC_INVALID_PARAM;
385 if (strcmp("layers", option_name) == 0) {
386 svc_ctx->spatial_layers = atoi(option_value);
387 } else if (strcmp("scale-factors", option_name) == 0) {
388 res = parse_scale_factors(svc_ctx, option_value);
389 if (res != VPX_CODEC_OK) break;
390 } else if (strcmp("quantizers", option_name) == 0) {
391 res = parse_quantizer_values(svc_ctx, option_value);
392 if (res != VPX_CODEC_OK) break;
393 } else if (strcmp("auto-alt-refs", option_name) == 0) {
394 res = parse_auto_alt_ref(svc_ctx, option_value);
395 if (res != VPX_CODEC_OK) break;
397 svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name);
398 res = VPX_CODEC_INVALID_PARAM;
401 option_name = strtok_r(NULL, "=", &input_ptr);
407 vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx, const char *options) {
408 SvcInternal *const si = get_svc_internal(svc_ctx);
409 if (svc_ctx == NULL || options == NULL || si == NULL) {
410 return VPX_CODEC_INVALID_PARAM;
412 strncpy(si->options, options, sizeof(si->options));
413 si->options[sizeof(si->options) - 1] = '\0';
417 vpx_codec_err_t vpx_svc_set_quantizers(SvcContext *svc_ctx,
418 const char *quantizers) {
419 SvcInternal *const si = get_svc_internal(svc_ctx);
420 if (svc_ctx == NULL || quantizers == NULL || si == NULL) {
421 return VPX_CODEC_INVALID_PARAM;
423 strncpy(si->quantizers, quantizers, sizeof(si->quantizers));
424 si->quantizers[sizeof(si->quantizers) - 1] = '\0';
428 vpx_codec_err_t vpx_svc_set_scale_factors(SvcContext *svc_ctx,
429 const char *scale_factors) {
430 SvcInternal *const si = get_svc_internal(svc_ctx);
431 if (svc_ctx == NULL || scale_factors == NULL || si == NULL) {
432 return VPX_CODEC_INVALID_PARAM;
434 strncpy(si->scale_factors, scale_factors, sizeof(si->scale_factors));
435 si->scale_factors[sizeof(si->scale_factors) - 1] = '\0';
439 vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
440 vpx_codec_iface_t *iface,
441 vpx_codec_enc_cfg_t *enc_cfg) {
444 SvcInternal *const si = get_svc_internal(svc_ctx);
445 if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
447 return VPX_CODEC_INVALID_PARAM;
449 if (si == NULL) return VPX_CODEC_MEM_ERROR;
451 si->codec_ctx = codec_ctx;
453 si->width = enc_cfg->g_w;
454 si->height = enc_cfg->g_h;
456 if (enc_cfg->kf_max_dist < 2) {
457 svc_log(svc_ctx, SVC_LOG_ERROR, "key frame distance too small: %d\n",
458 enc_cfg->kf_max_dist);
459 return VPX_CODEC_INVALID_PARAM;
461 si->kf_dist = enc_cfg->kf_max_dist;
463 if (svc_ctx->spatial_layers == 0)
464 svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS;
465 if (svc_ctx->spatial_layers < 1 ||
466 svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) {
467 svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
468 svc_ctx->spatial_layers);
469 return VPX_CODEC_INVALID_PARAM;
472 res = parse_quantizer_values(svc_ctx, si->quantizers);
473 if (res != VPX_CODEC_OK) return res;
475 res = parse_scale_factors(svc_ctx, si->scale_factors);
476 if (res != VPX_CODEC_OK) return res;
478 // Parse aggregate command line options. Options must start with
479 // "layers=xx" then followed by other options
480 res = parse_options(svc_ctx, si->options);
481 if (res != VPX_CODEC_OK) return res;
483 si->layers = svc_ctx->spatial_layers;
485 // Assign target bitrate for each layer. We calculate the ratio
486 // from the resolution for now.
487 // TODO(Minghai): Optimize the mechanism of allocating bits after
488 // implementing svc two pass rate control.
489 if (si->layers > 1) {
491 float alloc_ratio[VPX_SS_MAX_LAYERS] = {0};
493 assert(si->layers <= VPX_SS_MAX_LAYERS);
494 for (i = 0; i < si->layers; ++i) {
495 int pos = i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers;
496 if (pos < VPX_SS_MAX_LAYERS && si->scaling_factor_den[pos] > 0) {
497 alloc_ratio[i] = (float)(si->scaling_factor_num[pos] * 1.0 /
498 si->scaling_factor_den[pos]);
500 alloc_ratio[i] *= alloc_ratio[i];
501 total += alloc_ratio[i];
505 for (i = 0; i < si->layers; ++i) {
507 enc_cfg->ss_target_bitrate[i] = (unsigned int)
508 (enc_cfg->rc_target_bitrate * alloc_ratio[i] / total);
513 for (i = 0; i < si->layers; ++i)
514 enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i];
516 // modify encoder configuration
517 enc_cfg->ss_number_layers = si->layers;
518 enc_cfg->ts_number_layers = 1; // Temporal layers not used in this encoder.
520 // TODO(ivanmaltz): determine if these values need to be set explicitly for
521 // svc, or if the normal default/override mechanism can be used
522 enc_cfg->rc_dropframe_thresh = 0;
523 enc_cfg->rc_resize_allowed = 0;
525 if (enc_cfg->g_pass == VPX_RC_ONE_PASS) {
526 enc_cfg->rc_min_quantizer = 33;
527 enc_cfg->rc_max_quantizer = 33;
530 enc_cfg->rc_undershoot_pct = 100;
531 enc_cfg->rc_overshoot_pct = 15;
532 enc_cfg->rc_buf_initial_sz = 500;
533 enc_cfg->rc_buf_optimal_sz = 600;
534 enc_cfg->rc_buf_sz = 1000;
535 enc_cfg->g_error_resilient = 1;
538 res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR);
539 if (res != VPX_CODEC_OK) {
540 svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
544 vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1);
545 vpx_codec_control(codec_ctx, VP8E_SET_TOKEN_PARTITIONS, 1);
550 vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx,
553 unsigned int *height) {
554 int w, h, index, num, den;
555 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
557 if (svc_ctx == NULL || si == NULL || width == NULL || height == NULL) {
558 return VPX_CODEC_INVALID_PARAM;
560 if (layer < 0 || layer >= si->layers) return VPX_CODEC_INVALID_PARAM;
562 index = layer + VPX_SS_MAX_LAYERS - si->layers;
563 num = si->scaling_factor_num[index];
564 den = si->scaling_factor_den[index];
565 if (num == 0 || den == 0) return VPX_CODEC_INVALID_PARAM;
567 w = si->width * num / den;
568 h = si->height * num / den;
570 // make height and width even to make chrome player happy
580 static void set_svc_parameters(SvcContext *svc_ctx,
581 vpx_codec_ctx_t *codec_ctx) {
582 int layer, layer_index;
583 vpx_svc_parameters_t svc_params;
584 SvcInternal *const si = get_svc_internal(svc_ctx);
586 memset(&svc_params, 0, sizeof(svc_params));
587 svc_params.temporal_layer = 0;
588 svc_params.spatial_layer = si->layer;
591 if (VPX_CODEC_OK != vpx_svc_get_layer_resolution(svc_ctx, layer,
593 &svc_params.height)) {
594 svc_log(svc_ctx, SVC_LOG_ERROR, "vpx_svc_get_layer_resolution failed\n");
596 layer_index = layer + VPX_SS_MAX_LAYERS - si->layers;
598 if (codec_ctx->config.enc->g_pass == VPX_RC_ONE_PASS) {
599 svc_params.min_quantizer = si->quantizer[layer_index];
600 svc_params.max_quantizer = si->quantizer[layer_index];
602 svc_params.min_quantizer = codec_ctx->config.enc->rc_min_quantizer;
603 svc_params.max_quantizer = codec_ctx->config.enc->rc_max_quantizer;
606 svc_params.distance_from_i_frame = si->frame_within_gop;
607 vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &svc_params);
611 * Encode a frame into multiple layers
612 * Create a superframe containing the individual layers
614 vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
615 struct vpx_image *rawimg, vpx_codec_pts_t pts,
616 int64_t duration, int deadline) {
618 vpx_codec_iter_t iter;
619 const vpx_codec_cx_pkt_t *cx_pkt;
620 int layer_for_psnr = 0;
621 SvcInternal *const si = get_svc_internal(svc_ctx);
622 if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) {
623 return VPX_CODEC_INVALID_PARAM;
626 svc_log_reset(svc_ctx);
627 si->rc_stats_buf_used = 0;
629 si->layers = svc_ctx->spatial_layers;
630 if (si->encode_frame_count == 0) {
631 si->frame_within_gop = 0;
633 si->is_keyframe = (si->frame_within_gop == 0);
635 if (rawimg != NULL) {
636 svc_log(svc_ctx, SVC_LOG_DEBUG,
637 "vpx_svc_encode layers: %d, frame_count: %d, "
638 "frame_within_gop: %d\n", si->layers, si->encode_frame_count,
639 si->frame_within_gop);
642 if (rawimg != NULL) {
644 for (si->layer = 0; si->layer < si->layers; ++si->layer) {
645 set_svc_parameters(svc_ctx, codec_ctx);
649 res = vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0,
651 if (res != VPX_CODEC_OK) {
654 // save compressed data
656 while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) {
657 switch (cx_pkt->kind) {
658 case VPX_CODEC_CX_FRAME_PKT: {
659 fd_list_add(&si->frame_list, fd_create(cx_pkt->data.frame.buf,
660 cx_pkt->data.frame.sz,
661 cx_pkt->data.frame.flags));
663 svc_log(svc_ctx, SVC_LOG_DEBUG, "SVC frame: %d, kf: %d, size: %d, "
664 "pts: %d\n", si->frame_received,
665 (cx_pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? 1 : 0,
666 (int)cx_pkt->data.frame.sz, (int)cx_pkt->data.frame.pts);
668 ++si->frame_received;
672 case VPX_CODEC_PSNR_PKT: {
674 svc_log(svc_ctx, SVC_LOG_DEBUG,
675 "SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): "
676 "%2.3f %2.3f %2.3f %2.3f \n",
677 si->frame_received, layer_for_psnr,
678 cx_pkt->data.psnr.psnr[0], cx_pkt->data.psnr.psnr[1],
679 cx_pkt->data.psnr.psnr[2], cx_pkt->data.psnr.psnr[3]);
680 svc_log(svc_ctx, SVC_LOG_DEBUG,
681 "SVC frame: %d, layer: %d, SSE(Total/Y/U/V): "
682 "%2.3f %2.3f %2.3f %2.3f \n",
683 si->frame_received, layer_for_psnr,
684 cx_pkt->data.psnr.sse[0], cx_pkt->data.psnr.sse[1],
685 cx_pkt->data.psnr.sse[2], cx_pkt->data.psnr.sse[3]);
686 for (i = 0; i < COMPONENTS; i++) {
687 si->psnr_sum[layer_for_psnr][i] += cx_pkt->data.psnr.psnr[i];
688 si->sse_sum[layer_for_psnr][i] += cx_pkt->data.psnr.sse[i];
693 case VPX_CODEC_STATS_PKT: {
694 size_t new_size = si->rc_stats_buf_used +
695 cx_pkt->data.twopass_stats.sz;
697 if (new_size > si->rc_stats_buf_size) {
698 char *p = (char*)realloc(si->rc_stats_buf, new_size);
700 svc_log(svc_ctx, SVC_LOG_ERROR, "Error allocating stats buf\n");
701 return VPX_CODEC_MEM_ERROR;
703 si->rc_stats_buf = p;
704 si->rc_stats_buf_size = new_size;
707 memcpy(si->rc_stats_buf + si->rc_stats_buf_used,
708 cx_pkt->data.twopass_stats.buf, cx_pkt->data.twopass_stats.sz);
709 si->rc_stats_buf_used += cx_pkt->data.twopass_stats.sz;
712 case VPX_CODEC_SPATIAL_SVC_LAYER_SIZES: {
714 for (i = 0; i < si->layers; ++i)
715 si->bytes_sum[i] += cx_pkt->data.layer_sizes[i];
724 if (rawimg != NULL) {
725 ++si->frame_within_gop;
726 ++si->encode_frame_count;
732 const char *vpx_svc_get_message(const SvcContext *svc_ctx) {
733 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
734 if (svc_ctx == NULL || si == NULL) return NULL;
735 return si->message_buffer;
738 // We will maintain a list of output frame buffers since with lag_in_frame
739 // we need to output all frame buffers at the end. vpx_svc_get_buffer() will
740 // remove a frame buffer from the list the put it to a temporal pointer, which
741 // will be removed at the next vpx_svc_get_buffer() or when closing encoder.
742 void *vpx_svc_get_buffer(SvcContext *svc_ctx) {
743 SvcInternal *const si = get_svc_internal(svc_ctx);
744 if (svc_ctx == NULL || si == NULL || si->frame_list == NULL) return NULL;
747 fd_free(si->frame_temp);
749 si->frame_temp = si->frame_list;
750 si->frame_list = si->frame_list->next;
752 return si->frame_temp->buf;
755 size_t vpx_svc_get_frame_size(const SvcContext *svc_ctx) {
756 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
757 if (svc_ctx == NULL || si == NULL || si->frame_list == NULL) return 0;
758 return si->frame_list->size;
761 int vpx_svc_get_encode_frame_count(const SvcContext *svc_ctx) {
762 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
763 if (svc_ctx == NULL || si == NULL) return 0;
764 return si->encode_frame_count;
767 int vpx_svc_is_keyframe(const SvcContext *svc_ctx) {
768 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
769 if (svc_ctx == NULL || si == NULL || si->frame_list == NULL) return 0;
770 return (si->frame_list->flags & VPX_FRAME_IS_KEY) != 0;
773 void vpx_svc_set_keyframe(SvcContext *svc_ctx) {
774 SvcInternal *const si = get_svc_internal(svc_ctx);
775 if (svc_ctx == NULL || si == NULL) return;
776 si->frame_within_gop = 0;
779 static double calc_psnr(double d) {
780 if (d == 0) return 100;
781 return -10.0 * log(d) / log(10.0);
784 // dump accumulated statistics and reset accumulated values
785 const char *vpx_svc_dump_statistics(SvcContext *svc_ctx) {
786 int number_of_frames, encode_frame_count;
788 uint32_t bytes_total = 0;
789 double scale[COMPONENTS];
790 double psnr[COMPONENTS];
791 double mse[COMPONENTS];
794 SvcInternal *const si = get_svc_internal(svc_ctx);
795 if (svc_ctx == NULL || si == NULL) return NULL;
797 svc_log_reset(svc_ctx);
799 encode_frame_count = si->encode_frame_count;
800 if (si->encode_frame_count <= 0) return vpx_svc_get_message(svc_ctx);
802 svc_log(svc_ctx, SVC_LOG_INFO, "\n");
803 for (i = 0; i < si->layers; ++i) {
804 number_of_frames = encode_frame_count;
806 svc_log(svc_ctx, SVC_LOG_INFO,
807 "Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n",
808 i, (double)si->psnr_sum[i][0] / number_of_frames,
809 (double)si->psnr_sum[i][1] / number_of_frames,
810 (double)si->psnr_sum[i][2] / number_of_frames,
811 (double)si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]);
812 // the following psnr calculation is deduced from ffmpeg.c#print_report
813 y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames;
815 scale[2] = scale[3] = y_scale / 4; // U or V
816 scale[0] = y_scale * 1.5; // total
818 for (j = 0; j < COMPONENTS; j++) {
819 psnr[j] = calc_psnr(si->sse_sum[i][j] / scale[j]);
820 mse[j] = si->sse_sum[i][j] * 255.0 * 255.0 / scale[j];
822 svc_log(svc_ctx, SVC_LOG_INFO,
823 "Layer %d Overall PSNR=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, psnr[0],
824 psnr[1], psnr[2], psnr[3]);
825 svc_log(svc_ctx, SVC_LOG_INFO,
826 "Layer %d Overall MSE=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, mse[0],
827 mse[1], mse[2], mse[3]);
829 bytes_total += si->bytes_sum[i];
830 // clear sums for next time
831 si->bytes_sum[i] = 0;
832 for (j = 0; j < COMPONENTS; ++j) {
833 si->psnr_sum[i][j] = 0;
834 si->sse_sum[i][j] = 0;
838 // only display statistics once
839 si->encode_frame_count = 0;
841 svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total);
842 return vpx_svc_get_message(svc_ctx);
845 void vpx_svc_release(SvcContext *svc_ctx) {
847 if (svc_ctx == NULL) return;
848 // do not use get_svc_internal as it will unnecessarily allocate an
849 // SvcInternal if it was not already allocated
850 si = (SvcInternal *)svc_ctx->internal;
852 fd_free(si->frame_temp);
853 fd_free_list(si->frame_list);
854 if (si->rc_stats_buf) {
855 free(si->rc_stats_buf);
858 svc_ctx->internal = NULL;
862 size_t vpx_svc_get_rc_stats_buffer_size(const SvcContext *svc_ctx) {
863 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
864 if (svc_ctx == NULL || si == NULL) return 0;
865 return si->rc_stats_buf_used;
868 char *vpx_svc_get_rc_stats_buffer(const SvcContext *svc_ctx) {
869 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
870 if (svc_ctx == NULL || si == NULL) return NULL;
871 return si->rc_stats_buf;