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