1/*
2 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
3 *
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.
9 */
10
11/**
12 * @file
13 * VP9 SVC encoding support via libvpx
14 */
15
16#include <assert.h>
17#include <math.h>
18#include <limits.h>
19#include <stdarg.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
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"
30
31#ifdef __MINGW32__
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__ */
38
39#ifdef _MSC_VER
40#define strdup _strdup
41#define strtok_r strtok_s
42#endif
43
44#define SVC_REFERENCE_FRAMES 8
45#define SUPERFRAME_SLOTS (8)
46#define SUPERFRAME_BUFFER_SIZE (SUPERFRAME_SLOTS * sizeof(uint32_t) + 2)
47
48#define MAX_QUANTIZER 63
49
50static const int DEFAULT_SCALE_FACTORS_NUM[VPX_SS_MAX_LAYERS] = {
51  4, 5, 7, 11, 16
52};
53
54static const int DEFAULT_SCALE_FACTORS_DEN[VPX_SS_MAX_LAYERS] = {
55  16, 16, 16, 16, 16
56};
57
58typedef enum {
59  QUANTIZER = 0,
60  BITRATE,
61  SCALE_FACTOR,
62  AUTO_ALT_REF,
63  ALL_OPTION_TYPES
64} LAYER_OPTION_TYPE;
65
66static const int option_max_values[ALL_OPTION_TYPES] = {
67  63, INT_MAX, INT_MAX, 1
68};
69
70static const int option_min_values[ALL_OPTION_TYPES] = {
71  0, 0, 1, 0
72};
73
74// One encoded frame
75typedef struct FrameData {
76  void                     *buf;    // compressed data buffer
77  size_t                    size;  // length of compressed data
78  vpx_codec_frame_flags_t   flags;    /**< flags for this frame */
79  struct FrameData         *next;
80} FrameData;
81
82static SvcInternal_t *get_svc_internal(SvcContext *svc_ctx) {
83  if (svc_ctx == NULL) return NULL;
84  if (svc_ctx->internal == NULL) {
85    SvcInternal_t *const si = (SvcInternal_t *)malloc(sizeof(*si));
86    if (si != NULL) {
87      memset(si, 0, sizeof(*si));
88    }
89    svc_ctx->internal = si;
90  }
91  return (SvcInternal_t *)svc_ctx->internal;
92}
93
94static const SvcInternal_t *get_const_svc_internal(
95    const SvcContext *svc_ctx) {
96  if (svc_ctx == NULL) return NULL;
97  return (const SvcInternal_t *)svc_ctx->internal;
98}
99
100static void svc_log_reset(SvcContext *svc_ctx) {
101  SvcInternal_t *const si = (SvcInternal_t *)svc_ctx->internal;
102  si->message_buffer[0] = '\0';
103}
104
105static int svc_log(SvcContext *svc_ctx, SVC_LOG_LEVEL level,
106                   const char *fmt, ...) {
107  char buf[512];
108  int retval = 0;
109  va_list ap;
110  SvcInternal_t *const si = get_svc_internal(svc_ctx);
111
112  if (level > svc_ctx->log_level) {
113    return retval;
114  }
115
116  va_start(ap, fmt);
117  retval = vsnprintf(buf, sizeof(buf), fmt, ap);
118  va_end(ap);
119
120  if (svc_ctx->log_print) {
121    printf("%s", buf);
122  } else {
123    strncat(si->message_buffer, buf,
124            sizeof(si->message_buffer) - strlen(si->message_buffer) - 1);
125  }
126
127  if (level == SVC_LOG_ERROR) {
128    si->codec_ctx->err_detail = si->message_buffer;
129  }
130  return retval;
131}
132
133static vpx_codec_err_t extract_option(LAYER_OPTION_TYPE type,
134                                      char *input,
135                                      int *value0,
136                                      int *value1) {
137  if (type == SCALE_FACTOR) {
138    *value0 = strtol(input, &input, 10);
139    if (*input++ != '/')
140      return VPX_CODEC_INVALID_PARAM;
141    *value1 = strtol(input, &input, 10);
142
143    if (*value0 < option_min_values[SCALE_FACTOR] ||
144        *value1 < option_min_values[SCALE_FACTOR] ||
145        *value0 > option_max_values[SCALE_FACTOR] ||
146        *value1 > option_max_values[SCALE_FACTOR] ||
147        *value0 > *value1)  // num shouldn't be greater than den
148      return VPX_CODEC_INVALID_PARAM;
149  } else {
150    *value0 = atoi(input);
151    if (*value0 < option_min_values[type] ||
152        *value0 > option_max_values[type])
153      return VPX_CODEC_INVALID_PARAM;
154  }
155  return VPX_CODEC_OK;
156}
157
158static vpx_codec_err_t parse_layer_options_from_string(SvcContext *svc_ctx,
159                                                       LAYER_OPTION_TYPE type,
160                                                       const char *input,
161                                                       int *option0,
162                                                       int *option1) {
163  int i;
164  vpx_codec_err_t res = VPX_CODEC_OK;
165  char *input_string;
166  char *token;
167  const char *delim = ",";
168  char *save_ptr;
169
170  if (input == NULL || option0 == NULL ||
171      (option1 == NULL && type == SCALE_FACTOR))
172    return VPX_CODEC_INVALID_PARAM;
173
174  input_string = strdup(input);
175  token = strtok_r(input_string, delim, &save_ptr);
176  for (i = 0; i < svc_ctx->spatial_layers; ++i) {
177    if (token != NULL) {
178      res = extract_option(type, token, option0 + i, option1 + i);
179      if (res != VPX_CODEC_OK)
180        break;
181      token = strtok_r(NULL, delim, &save_ptr);
182    } else {
183      break;
184    }
185  }
186  if (res == VPX_CODEC_OK && i != svc_ctx->spatial_layers) {
187    svc_log(svc_ctx, SVC_LOG_ERROR,
188            "svc: layer params type: %d    %d values required, "
189            "but only %d specified\n", type, svc_ctx->spatial_layers, i);
190    res = VPX_CODEC_INVALID_PARAM;
191  }
192  free(input_string);
193  return res;
194}
195
196/**
197 * Parse SVC encoding options
198 * Format: encoding-mode=<svc_mode>,layers=<layer_count>
199 *         scale-factors=<n1>/<d1>,<n2>/<d2>,...
200 *         quantizers=<q1>,<q2>,...
201 * svc_mode = [i|ip|alt_ip|gf]
202 */
203static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
204  char *input_string;
205  char *option_name;
206  char *option_value;
207  char *input_ptr;
208  SvcInternal_t *const si = get_svc_internal(svc_ctx);
209  vpx_codec_err_t res = VPX_CODEC_OK;
210  int i, alt_ref_enabled = 0;
211
212  if (options == NULL) return VPX_CODEC_OK;
213  input_string = strdup(options);
214
215  // parse option name
216  option_name = strtok_r(input_string, "=", &input_ptr);
217  while (option_name != NULL) {
218    // parse option value
219    option_value = strtok_r(NULL, " ", &input_ptr);
220    if (option_value == NULL) {
221      svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n",
222              option_name);
223      res = VPX_CODEC_INVALID_PARAM;
224      break;
225    }
226    if (strcmp("spatial-layers", option_name) == 0) {
227      svc_ctx->spatial_layers = atoi(option_value);
228    } else if (strcmp("temporal-layers", option_name) == 0) {
229      svc_ctx->temporal_layers = atoi(option_value);
230    } else if (strcmp("scale-factors", option_name) == 0) {
231      res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR, option_value,
232                                            si->svc_params.scaling_factor_num,
233                                            si->svc_params.scaling_factor_den);
234      if (res != VPX_CODEC_OK) break;
235    } else if (strcmp("max-quantizers", option_name) == 0) {
236      res = parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
237                                            si->svc_params.max_quantizers,
238                                            NULL);
239      if (res != VPX_CODEC_OK) break;
240    } else if (strcmp("min-quantizers", option_name) == 0) {
241      res = parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
242                                            si->svc_params.min_quantizers,
243                                            NULL);
244      if (res != VPX_CODEC_OK) break;
245    } else if (strcmp("auto-alt-refs", option_name) == 0) {
246      res = parse_layer_options_from_string(svc_ctx, AUTO_ALT_REF, option_value,
247                                            si->enable_auto_alt_ref, NULL);
248      if (res != VPX_CODEC_OK) break;
249    } else if (strcmp("bitrates", option_name) == 0) {
250      res = parse_layer_options_from_string(svc_ctx, BITRATE, option_value,
251                                            si->bitrates, NULL);
252      if (res != VPX_CODEC_OK) break;
253    } else if (strcmp("multi-frame-contexts", option_name) == 0) {
254      si->use_multiple_frame_contexts = atoi(option_value);
255    } else {
256      svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name);
257      res = VPX_CODEC_INVALID_PARAM;
258      break;
259    }
260    option_name = strtok_r(NULL, "=", &input_ptr);
261  }
262  free(input_string);
263
264  for (i = 0; i < svc_ctx->spatial_layers; ++i) {
265    if (si->svc_params.max_quantizers[i] > MAX_QUANTIZER ||
266        si->svc_params.max_quantizers[i] < 0 ||
267        si->svc_params.min_quantizers[i] > si->svc_params.max_quantizers[i] ||
268        si->svc_params.min_quantizers[i] < 0)
269      res = VPX_CODEC_INVALID_PARAM;
270  }
271
272  if (si->use_multiple_frame_contexts &&
273      (svc_ctx->spatial_layers > 3 ||
274       svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4))
275    res = VPX_CODEC_INVALID_PARAM;
276
277  for (i = 0; i < svc_ctx->spatial_layers; ++i)
278    alt_ref_enabled += si->enable_auto_alt_ref[i];
279  if (alt_ref_enabled > REF_FRAMES - svc_ctx->spatial_layers) {
280    svc_log(svc_ctx, SVC_LOG_ERROR,
281            "svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
282            "enabled auto alt reference frame, but % layers are enabled\n",
283            REF_FRAMES - svc_ctx->spatial_layers, alt_ref_enabled);
284    res = VPX_CODEC_INVALID_PARAM;
285  }
286
287  return res;
288}
289
290vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx,
291                                    const char *options) {
292  SvcInternal_t *const si = get_svc_internal(svc_ctx);
293  if (svc_ctx == NULL || options == NULL || si == NULL) {
294    return VPX_CODEC_INVALID_PARAM;
295  }
296  strncpy(si->options, options, sizeof(si->options));
297  si->options[sizeof(si->options) - 1] = '\0';
298  return VPX_CODEC_OK;
299}
300
301void assign_layer_bitrates(const SvcContext *svc_ctx,
302                           vpx_codec_enc_cfg_t *const enc_cfg) {
303  int i;
304  const SvcInternal_t *const si = get_const_svc_internal(svc_ctx);
305  int sl, tl, spatial_layer_target;
306
307  if (svc_ctx->temporal_layering_mode != 0) {
308    if (si->bitrates[0] != 0) {
309      enc_cfg->rc_target_bitrate = 0;
310      for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
311        enc_cfg->ss_target_bitrate[sl*svc_ctx->temporal_layers] = 0;
312        for (tl = 0; tl < svc_ctx->temporal_layers; ++tl) {
313          enc_cfg->ss_target_bitrate[sl*svc_ctx->temporal_layers]
314              += (unsigned int)si->bitrates[sl * svc_ctx->temporal_layers + tl];
315          enc_cfg->layer_target_bitrate[sl*svc_ctx->temporal_layers + tl]
316              = si->bitrates[sl * svc_ctx->temporal_layers + tl];
317        }
318      }
319    } else {
320      float total = 0;
321      float alloc_ratio[VPX_MAX_LAYERS] = {0};
322
323      for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
324        if (si->svc_params.scaling_factor_den[sl] > 0) {
325          alloc_ratio[sl] = (float)(si->svc_params.scaling_factor_num[sl] *
326              1.0 / si->svc_params.scaling_factor_den[sl]);
327          total += alloc_ratio[sl];
328        }
329      }
330
331      for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
332        enc_cfg->ss_target_bitrate[sl] = spatial_layer_target =
333            (unsigned int)(enc_cfg->rc_target_bitrate *
334                alloc_ratio[sl] / total);
335        if (svc_ctx->temporal_layering_mode == 3) {
336          enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
337              spatial_layer_target >> 1;
338          enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
339              (spatial_layer_target >> 1) + (spatial_layer_target >> 2);
340          enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 2] =
341              spatial_layer_target;
342        } else if (svc_ctx->temporal_layering_mode == 2 ||
343                   svc_ctx->temporal_layering_mode == 1) {
344          enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
345              spatial_layer_target * 2 / 3;
346          enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
347              spatial_layer_target;
348        } else {
349          // User should explicitly assign bitrates in this case.
350          assert(0);
351        }
352      }
353    }
354  } else {
355    if (si->bitrates[0] != 0) {
356      enc_cfg->rc_target_bitrate = 0;
357      for (i = 0; i < svc_ctx->spatial_layers; ++i) {
358        enc_cfg->ss_target_bitrate[i] = (unsigned int)si->bitrates[i];
359        enc_cfg->rc_target_bitrate += si->bitrates[i];
360      }
361    } else {
362      float total = 0;
363      float alloc_ratio[VPX_MAX_LAYERS] = {0};
364
365      for (i = 0; i < svc_ctx->spatial_layers; ++i) {
366        if (si->svc_params.scaling_factor_den[i] > 0) {
367          alloc_ratio[i] = (float)(si->svc_params.scaling_factor_num[i] * 1.0 /
368                                   si->svc_params.scaling_factor_den[i]);
369
370          alloc_ratio[i] *= alloc_ratio[i];
371          total += alloc_ratio[i];
372        }
373      }
374      for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
375        if (total > 0) {
376          enc_cfg->layer_target_bitrate[i] = (unsigned int)
377              (enc_cfg->rc_target_bitrate * alloc_ratio[i] / total);
378        }
379      }
380    }
381  }
382}
383
384vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
385                             vpx_codec_iface_t *iface,
386                             vpx_codec_enc_cfg_t *enc_cfg) {
387  vpx_codec_err_t res;
388  int i;
389  SvcInternal_t *const si = get_svc_internal(svc_ctx);
390  if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
391      enc_cfg == NULL) {
392    return VPX_CODEC_INVALID_PARAM;
393  }
394  if (si == NULL) return VPX_CODEC_MEM_ERROR;
395
396  si->codec_ctx = codec_ctx;
397
398  si->width = enc_cfg->g_w;
399  si->height = enc_cfg->g_h;
400
401  if (enc_cfg->kf_max_dist < 2) {
402    svc_log(svc_ctx, SVC_LOG_ERROR, "key frame distance too small: %d\n",
403            enc_cfg->kf_max_dist);
404    return VPX_CODEC_INVALID_PARAM;
405  }
406  si->kf_dist = enc_cfg->kf_max_dist;
407
408  if (svc_ctx->spatial_layers == 0)
409    svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS;
410  if (svc_ctx->spatial_layers < 1 ||
411      svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) {
412    svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
413            svc_ctx->spatial_layers);
414    return VPX_CODEC_INVALID_PARAM;
415  }
416
417  // Note: temporal_layering_mode only applies to one-pass CBR
418  // si->svc_params.temporal_layering_mode = svc_ctx->temporal_layering_mode;
419  if (svc_ctx->temporal_layering_mode == 3) {
420    svc_ctx->temporal_layers = 3;
421  } else if (svc_ctx->temporal_layering_mode == 2 ||
422             svc_ctx->temporal_layering_mode == 1) {
423    svc_ctx->temporal_layers = 2;
424  }
425
426  for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
427    si->svc_params.max_quantizers[i] = MAX_QUANTIZER;
428    si->svc_params.min_quantizers[i] = 0;
429    si->svc_params.scaling_factor_num[i] = DEFAULT_SCALE_FACTORS_NUM[i];
430    si->svc_params.scaling_factor_den[i] = DEFAULT_SCALE_FACTORS_DEN[i];
431  }
432
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;
437
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;
442
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;
447
448  if (svc_ctx->temporal_layers * svc_ctx->spatial_layers > VPX_MAX_LAYERS) {
449      svc_log(svc_ctx, SVC_LOG_ERROR,
450          "spatial layers * temporal layers exceeds the maximum number of "
451          "allowed layers of %d\n",
452          svc_ctx->spatial_layers * svc_ctx->temporal_layers,
453          (int) VPX_MAX_LAYERS);
454      return VPX_CODEC_INVALID_PARAM;
455  }
456  assign_layer_bitrates(svc_ctx, enc_cfg);
457
458#if CONFIG_SPATIAL_SVC
459  for (i = 0; i < svc_ctx->spatial_layers; ++i)
460    enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i];
461#endif
462
463  if (svc_ctx->temporal_layers > 1) {
464    int i;
465    for (i = 0; i < svc_ctx->temporal_layers; ++i) {
466      enc_cfg->ts_target_bitrate[i] = enc_cfg->rc_target_bitrate /
467                                      svc_ctx->temporal_layers;
468      enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i);
469    }
470  }
471
472  if (svc_ctx->threads)
473    enc_cfg->g_threads = svc_ctx->threads;
474
475  // Modify encoder configuration
476  enc_cfg->ss_number_layers = svc_ctx->spatial_layers;
477  enc_cfg->ts_number_layers = svc_ctx->temporal_layers;
478
479  if (enc_cfg->rc_end_usage == VPX_CBR) {
480    enc_cfg->rc_resize_allowed = 0;
481    enc_cfg->rc_min_quantizer = 2;
482    enc_cfg->rc_max_quantizer = 56;
483    enc_cfg->rc_undershoot_pct = 50;
484    enc_cfg->rc_overshoot_pct = 50;
485    enc_cfg->rc_buf_initial_sz = 500;
486    enc_cfg->rc_buf_optimal_sz = 600;
487    enc_cfg->rc_buf_sz = 1000;
488  }
489
490  if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0)
491    enc_cfg->g_error_resilient = 1;
492
493  // Initialize codec
494  res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR);
495  if (res != VPX_CODEC_OK) {
496    svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
497    return res;
498  }
499  if (svc_ctx->spatial_layers > 1 || svc_ctx->temporal_layers > 1) {
500    vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1);
501    vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &si->svc_params);
502  }
503  return VPX_CODEC_OK;
504}
505
506/**
507 * Encode a frame into multiple layers
508 * Create a superframe containing the individual layers
509 */
510vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx,
511                               vpx_codec_ctx_t *codec_ctx,
512                               struct vpx_image *rawimg,
513                               vpx_codec_pts_t pts,
514                               int64_t duration, int deadline) {
515  vpx_codec_err_t res;
516  vpx_codec_iter_t iter;
517  const vpx_codec_cx_pkt_t *cx_pkt;
518  SvcInternal_t *const si = get_svc_internal(svc_ctx);
519  if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) {
520    return VPX_CODEC_INVALID_PARAM;
521  }
522
523  svc_log_reset(svc_ctx);
524
525  res = vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0,
526                         deadline);
527  if (res != VPX_CODEC_OK) {
528    return res;
529  }
530  // save compressed data
531  iter = NULL;
532  while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) {
533    switch (cx_pkt->kind) {
534#if VPX_ENCODER_ABI_VERSION > (5 + VPX_CODEC_ABI_VERSION)
535#if CONFIG_SPATIAL_SVC
536      case VPX_CODEC_SPATIAL_SVC_LAYER_PSNR: {
537        int i;
538        for (i = 0; i < svc_ctx->spatial_layers; ++i) {
539          int j;
540          svc_log(svc_ctx, SVC_LOG_DEBUG,
541                  "SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): "
542                  "%2.3f  %2.3f  %2.3f  %2.3f \n",
543                  si->psnr_pkt_received, i,
544                  cx_pkt->data.layer_psnr[i].psnr[0],
545                  cx_pkt->data.layer_psnr[i].psnr[1],
546                  cx_pkt->data.layer_psnr[i].psnr[2],
547                  cx_pkt->data.layer_psnr[i].psnr[3]);
548          svc_log(svc_ctx, SVC_LOG_DEBUG,
549                  "SVC frame: %d, layer: %d, SSE(Total/Y/U/V): "
550                  "%2.3f  %2.3f  %2.3f  %2.3f \n",
551                  si->psnr_pkt_received, i,
552                  cx_pkt->data.layer_psnr[i].sse[0],
553                  cx_pkt->data.layer_psnr[i].sse[1],
554                  cx_pkt->data.layer_psnr[i].sse[2],
555                  cx_pkt->data.layer_psnr[i].sse[3]);
556
557          for (j = 0; j < COMPONENTS; ++j) {
558            si->psnr_sum[i][j] +=
559                cx_pkt->data.layer_psnr[i].psnr[j];
560            si->sse_sum[i][j] += cx_pkt->data.layer_psnr[i].sse[j];
561          }
562        }
563        ++si->psnr_pkt_received;
564        break;
565      }
566      case VPX_CODEC_SPATIAL_SVC_LAYER_SIZES: {
567        int i;
568        for (i = 0; i < svc_ctx->spatial_layers; ++i)
569          si->bytes_sum[i] += cx_pkt->data.layer_sizes[i];
570        break;
571      }
572#endif
573#endif
574      default: {
575        break;
576      }
577    }
578  }
579
580  return VPX_CODEC_OK;
581}
582
583const char *vpx_svc_get_message(const SvcContext *svc_ctx) {
584  const SvcInternal_t *const si = get_const_svc_internal(svc_ctx);
585  if (svc_ctx == NULL || si == NULL) return NULL;
586  return si->message_buffer;
587}
588
589static double calc_psnr(double d) {
590  if (d == 0) return 100;
591  return -10.0 * log(d) / log(10.0);
592}
593
594// dump accumulated statistics and reset accumulated values
595const char *vpx_svc_dump_statistics(SvcContext *svc_ctx) {
596  int number_of_frames;
597  int i, j;
598  uint32_t bytes_total = 0;
599  double scale[COMPONENTS];
600  double psnr[COMPONENTS];
601  double mse[COMPONENTS];
602  double y_scale;
603
604  SvcInternal_t *const si = get_svc_internal(svc_ctx);
605  if (svc_ctx == NULL || si == NULL) return NULL;
606
607  svc_log_reset(svc_ctx);
608
609  number_of_frames = si->psnr_pkt_received;
610  if (number_of_frames <= 0) return vpx_svc_get_message(svc_ctx);
611
612  svc_log(svc_ctx, SVC_LOG_INFO, "\n");
613  for (i = 0; i < svc_ctx->spatial_layers; ++i) {
614
615    svc_log(svc_ctx, SVC_LOG_INFO,
616            "Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n",
617            i, (double)si->psnr_sum[i][0] / number_of_frames,
618            (double)si->psnr_sum[i][1] / number_of_frames,
619            (double)si->psnr_sum[i][2] / number_of_frames,
620            (double)si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]);
621    // the following psnr calculation is deduced from ffmpeg.c#print_report
622    y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames;
623    scale[1] = y_scale;
624    scale[2] = scale[3] = y_scale / 4;  // U or V
625    scale[0] = y_scale * 1.5;           // total
626
627    for (j = 0; j < COMPONENTS; j++) {
628      psnr[j] = calc_psnr(si->sse_sum[i][j] / scale[j]);
629      mse[j] = si->sse_sum[i][j] * 255.0 * 255.0 / scale[j];
630    }
631    svc_log(svc_ctx, SVC_LOG_INFO,
632            "Layer %d Overall PSNR=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, psnr[0],
633            psnr[1], psnr[2], psnr[3]);
634    svc_log(svc_ctx, SVC_LOG_INFO,
635            "Layer %d Overall MSE=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, mse[0],
636            mse[1], mse[2], mse[3]);
637
638    bytes_total += si->bytes_sum[i];
639    // Clear sums for next time.
640    si->bytes_sum[i] = 0;
641    for (j = 0; j < COMPONENTS; ++j) {
642      si->psnr_sum[i][j] = 0;
643      si->sse_sum[i][j] = 0;
644    }
645  }
646
647  // only display statistics once
648  si->psnr_pkt_received = 0;
649
650  svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total);
651  return vpx_svc_get_message(svc_ctx);
652}
653
654void vpx_svc_release(SvcContext *svc_ctx) {
655  SvcInternal_t *si;
656  if (svc_ctx == NULL) return;
657  // do not use get_svc_internal as it will unnecessarily allocate an
658  // SvcInternal_t if it was not already allocated
659  si = (SvcInternal_t *)svc_ctx->internal;
660  if (si != NULL) {
661    free(si);
662    svc_ctx->internal = NULL;
663  }
664}
665
666