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          enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
344              spatial_layer_target * 2 / 3;
345          enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
346              spatial_layer_target;
347        } else {
348          // User should explicitly assign bitrates in this case.
349          assert(0);
350        }
351      }
352    }
353  } else {
354    if (si->bitrates[0] != 0) {
355      enc_cfg->rc_target_bitrate = 0;
356      for (i = 0; i < svc_ctx->spatial_layers; ++i) {
357        enc_cfg->ss_target_bitrate[i] = (unsigned int)si->bitrates[i];
358        enc_cfg->rc_target_bitrate += si->bitrates[i];
359      }
360    } else {
361      float total = 0;
362      float alloc_ratio[VPX_MAX_LAYERS] = {0};
363
364      for (i = 0; i < svc_ctx->spatial_layers; ++i) {
365        if (si->svc_params.scaling_factor_den[i] > 0) {
366          alloc_ratio[i] = (float)(si->svc_params.scaling_factor_num[i] * 1.0 /
367                                   si->svc_params.scaling_factor_den[i]);
368
369          alloc_ratio[i] *= alloc_ratio[i];
370          total += alloc_ratio[i];
371        }
372      }
373      for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
374        if (total > 0) {
375          enc_cfg->layer_target_bitrate[i] = (unsigned int)
376              (enc_cfg->rc_target_bitrate * alloc_ratio[i] / total);
377        }
378      }
379    }
380  }
381}
382
383vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
384                             vpx_codec_iface_t *iface,
385                             vpx_codec_enc_cfg_t *enc_cfg) {
386  vpx_codec_err_t res;
387  int i;
388  SvcInternal_t *const si = get_svc_internal(svc_ctx);
389  if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
390      enc_cfg == NULL) {
391    return VPX_CODEC_INVALID_PARAM;
392  }
393  if (si == NULL) return VPX_CODEC_MEM_ERROR;
394
395  si->codec_ctx = codec_ctx;
396
397  si->width = enc_cfg->g_w;
398  si->height = enc_cfg->g_h;
399
400  if (enc_cfg->kf_max_dist < 2) {
401    svc_log(svc_ctx, SVC_LOG_ERROR, "key frame distance too small: %d\n",
402            enc_cfg->kf_max_dist);
403    return VPX_CODEC_INVALID_PARAM;
404  }
405  si->kf_dist = enc_cfg->kf_max_dist;
406
407  if (svc_ctx->spatial_layers == 0)
408    svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS;
409  if (svc_ctx->spatial_layers < 1 ||
410      svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) {
411    svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
412            svc_ctx->spatial_layers);
413    return VPX_CODEC_INVALID_PARAM;
414  }
415
416  // Note: temporal_layering_mode only applies to one-pass CBR
417  // si->svc_params.temporal_layering_mode = svc_ctx->temporal_layering_mode;
418  if (svc_ctx->temporal_layering_mode == 3) {
419    svc_ctx->temporal_layers = 3;
420  } else if (svc_ctx->temporal_layering_mode == 2) {
421    svc_ctx->temporal_layers = 2;
422  }
423
424  for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
425    si->svc_params.max_quantizers[i] = MAX_QUANTIZER;
426    si->svc_params.min_quantizers[i] = 0;
427    si->svc_params.scaling_factor_num[i] = DEFAULT_SCALE_FACTORS_NUM[i];
428    si->svc_params.scaling_factor_den[i] = DEFAULT_SCALE_FACTORS_DEN[i];
429  }
430
431  // Parse aggregate command line options. Options must start with
432  // "layers=xx" then followed by other options
433  res = parse_options(svc_ctx, si->options);
434  if (res != VPX_CODEC_OK) return res;
435
436  if (svc_ctx->spatial_layers < 1)
437    svc_ctx->spatial_layers = 1;
438  if (svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS)
439    svc_ctx->spatial_layers = VPX_SS_MAX_LAYERS;
440
441  if (svc_ctx->temporal_layers < 1)
442    svc_ctx->temporal_layers = 1;
443  if (svc_ctx->temporal_layers > VPX_TS_MAX_LAYERS)
444    svc_ctx->temporal_layers = VPX_TS_MAX_LAYERS;
445
446  if (svc_ctx->temporal_layers * svc_ctx->spatial_layers > VPX_MAX_LAYERS) {
447      svc_log(svc_ctx, SVC_LOG_ERROR,
448          "spatial layers * temporal layers exceeds the maximum number of "
449          "allowed layers of %d\n",
450          svc_ctx->spatial_layers * svc_ctx->temporal_layers,
451          (int) VPX_MAX_LAYERS);
452      return VPX_CODEC_INVALID_PARAM;
453  }
454  assign_layer_bitrates(svc_ctx, enc_cfg);
455
456#if CONFIG_SPATIAL_SVC
457  for (i = 0; i < svc_ctx->spatial_layers; ++i)
458    enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i];
459#endif
460
461  if (svc_ctx->temporal_layers > 1) {
462    int i;
463    for (i = 0; i < svc_ctx->temporal_layers; ++i) {
464      enc_cfg->ts_target_bitrate[i] = enc_cfg->rc_target_bitrate /
465                                      svc_ctx->temporal_layers;
466      enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i);
467    }
468  }
469
470  if (svc_ctx->threads)
471    enc_cfg->g_threads = svc_ctx->threads;
472
473  // Modify encoder configuration
474  enc_cfg->ss_number_layers = svc_ctx->spatial_layers;
475  enc_cfg->ts_number_layers = svc_ctx->temporal_layers;
476
477  if (enc_cfg->rc_end_usage == VPX_CBR) {
478    enc_cfg->rc_resize_allowed = 0;
479    enc_cfg->rc_min_quantizer = 2;
480    enc_cfg->rc_max_quantizer = 63;
481    enc_cfg->rc_undershoot_pct = 50;
482    enc_cfg->rc_overshoot_pct = 50;
483    enc_cfg->rc_buf_initial_sz = 20;
484    enc_cfg->rc_buf_optimal_sz = 600;
485    enc_cfg->rc_buf_sz = 1000;
486  }
487
488  if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0)
489    enc_cfg->g_error_resilient = 1;
490
491  // Initialize codec
492  res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR);
493  if (res != VPX_CODEC_OK) {
494    svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
495    return res;
496  }
497
498  vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1);
499  vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &si->svc_params);
500
501  return VPX_CODEC_OK;
502}
503
504/**
505 * Encode a frame into multiple layers
506 * Create a superframe containing the individual layers
507 */
508vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx,
509                               vpx_codec_ctx_t *codec_ctx,
510                               struct vpx_image *rawimg,
511                               vpx_codec_pts_t pts,
512                               int64_t duration, int deadline) {
513  vpx_codec_err_t res;
514  vpx_codec_iter_t iter;
515  const vpx_codec_cx_pkt_t *cx_pkt;
516  SvcInternal_t *const si = get_svc_internal(svc_ctx);
517  if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) {
518    return VPX_CODEC_INVALID_PARAM;
519  }
520
521  svc_log_reset(svc_ctx);
522
523  res = vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0,
524                         deadline);
525  if (res != VPX_CODEC_OK) {
526    return res;
527  }
528  // save compressed data
529  iter = NULL;
530  while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) {
531    switch (cx_pkt->kind) {
532#if VPX_ENCODER_ABI_VERSION > (5 + VPX_CODEC_ABI_VERSION)
533#if CONFIG_SPATIAL_SVC
534      case VPX_CODEC_SPATIAL_SVC_LAYER_PSNR: {
535        int i;
536        for (i = 0; i < svc_ctx->spatial_layers; ++i) {
537          int j;
538          svc_log(svc_ctx, SVC_LOG_DEBUG,
539                  "SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): "
540                  "%2.3f  %2.3f  %2.3f  %2.3f \n",
541                  si->psnr_pkt_received, i,
542                  cx_pkt->data.layer_psnr[i].psnr[0],
543                  cx_pkt->data.layer_psnr[i].psnr[1],
544                  cx_pkt->data.layer_psnr[i].psnr[2],
545                  cx_pkt->data.layer_psnr[i].psnr[3]);
546          svc_log(svc_ctx, SVC_LOG_DEBUG,
547                  "SVC frame: %d, layer: %d, SSE(Total/Y/U/V): "
548                  "%2.3f  %2.3f  %2.3f  %2.3f \n",
549                  si->psnr_pkt_received, i,
550                  cx_pkt->data.layer_psnr[i].sse[0],
551                  cx_pkt->data.layer_psnr[i].sse[1],
552                  cx_pkt->data.layer_psnr[i].sse[2],
553                  cx_pkt->data.layer_psnr[i].sse[3]);
554
555          for (j = 0; j < COMPONENTS; ++j) {
556            si->psnr_sum[i][j] +=
557                cx_pkt->data.layer_psnr[i].psnr[j];
558            si->sse_sum[i][j] += cx_pkt->data.layer_psnr[i].sse[j];
559          }
560        }
561        ++si->psnr_pkt_received;
562        break;
563      }
564      case VPX_CODEC_SPATIAL_SVC_LAYER_SIZES: {
565        int i;
566        for (i = 0; i < svc_ctx->spatial_layers; ++i)
567          si->bytes_sum[i] += cx_pkt->data.layer_sizes[i];
568        break;
569      }
570#endif
571#endif
572      default: {
573        break;
574      }
575    }
576  }
577
578  return VPX_CODEC_OK;
579}
580
581const char *vpx_svc_get_message(const SvcContext *svc_ctx) {
582  const SvcInternal_t *const si = get_const_svc_internal(svc_ctx);
583  if (svc_ctx == NULL || si == NULL) return NULL;
584  return si->message_buffer;
585}
586
587static double calc_psnr(double d) {
588  if (d == 0) return 100;
589  return -10.0 * log(d) / log(10.0);
590}
591
592// dump accumulated statistics and reset accumulated values
593const char *vpx_svc_dump_statistics(SvcContext *svc_ctx) {
594  int number_of_frames;
595  int i, j;
596  uint32_t bytes_total = 0;
597  double scale[COMPONENTS];
598  double psnr[COMPONENTS];
599  double mse[COMPONENTS];
600  double y_scale;
601
602  SvcInternal_t *const si = get_svc_internal(svc_ctx);
603  if (svc_ctx == NULL || si == NULL) return NULL;
604
605  svc_log_reset(svc_ctx);
606
607  number_of_frames = si->psnr_pkt_received;
608  if (number_of_frames <= 0) return vpx_svc_get_message(svc_ctx);
609
610  svc_log(svc_ctx, SVC_LOG_INFO, "\n");
611  for (i = 0; i < svc_ctx->spatial_layers; ++i) {
612
613    svc_log(svc_ctx, SVC_LOG_INFO,
614            "Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n",
615            i, (double)si->psnr_sum[i][0] / number_of_frames,
616            (double)si->psnr_sum[i][1] / number_of_frames,
617            (double)si->psnr_sum[i][2] / number_of_frames,
618            (double)si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]);
619    // the following psnr calculation is deduced from ffmpeg.c#print_report
620    y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames;
621    scale[1] = y_scale;
622    scale[2] = scale[3] = y_scale / 4;  // U or V
623    scale[0] = y_scale * 1.5;           // total
624
625    for (j = 0; j < COMPONENTS; j++) {
626      psnr[j] = calc_psnr(si->sse_sum[i][j] / scale[j]);
627      mse[j] = si->sse_sum[i][j] * 255.0 * 255.0 / scale[j];
628    }
629    svc_log(svc_ctx, SVC_LOG_INFO,
630            "Layer %d Overall PSNR=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, psnr[0],
631            psnr[1], psnr[2], psnr[3]);
632    svc_log(svc_ctx, SVC_LOG_INFO,
633            "Layer %d Overall MSE=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, mse[0],
634            mse[1], mse[2], mse[3]);
635
636    bytes_total += si->bytes_sum[i];
637    // Clear sums for next time.
638    si->bytes_sum[i] = 0;
639    for (j = 0; j < COMPONENTS; ++j) {
640      si->psnr_sum[i][j] = 0;
641      si->sse_sum[i][j] = 0;
642    }
643  }
644
645  // only display statistics once
646  si->psnr_pkt_received = 0;
647
648  svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total);
649  return vpx_svc_get_message(svc_ctx);
650}
651
652void vpx_svc_release(SvcContext *svc_ctx) {
653  SvcInternal_t *si;
654  if (svc_ctx == NULL) return;
655  // do not use get_svc_internal as it will unnecessarily allocate an
656  // SvcInternal_t if it was not already allocated
657  si = (SvcInternal_t *)svc_ctx->internal;
658  if (si != NULL) {
659    free(si);
660    svc_ctx->internal = NULL;
661  }
662}
663
664