media/libvpx/vpx/src/svc_encodeframe.c

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /*
michael@0 2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved.
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license
michael@0 5 * that can be found in the LICENSE file in the root of the source
michael@0 6 * tree. An additional intellectual property rights grant can be found
michael@0 7 * in the file PATENTS. All contributing project authors may
michael@0 8 * be found in the AUTHORS file in the root of the source tree.
michael@0 9 */
michael@0 10
michael@0 11 /**
michael@0 12 * @file
michael@0 13 * VP9 SVC encoding support via libvpx
michael@0 14 */
michael@0 15
michael@0 16 #include <stdarg.h>
michael@0 17 #include <stdio.h>
michael@0 18 #include <stdlib.h>
michael@0 19 #include <string.h>
michael@0 20 #define VPX_DISABLE_CTRL_TYPECHECKS 1
michael@0 21 #define VPX_CODEC_DISABLE_COMPAT 1
michael@0 22 #include "vpx/svc_context.h"
michael@0 23 #include "vpx/vp8cx.h"
michael@0 24 #include "vpx/vpx_encoder.h"
michael@0 25
michael@0 26 #ifdef __MINGW32__
michael@0 27 #define strtok_r strtok_s
michael@0 28 #ifndef MINGW_HAS_SECURE_API
michael@0 29 // proto from /usr/x86_64-w64-mingw32/include/sec_api/string_s.h
michael@0 30 _CRTIMP char *__cdecl strtok_s(char *str, const char *delim, char **context);
michael@0 31 #endif /* MINGW_HAS_SECURE_API */
michael@0 32 #endif /* __MINGW32__ */
michael@0 33
michael@0 34 #ifdef _MSC_VER
michael@0 35 #define strdup _strdup
michael@0 36 #define strtok_r strtok_s
michael@0 37 #endif
michael@0 38
michael@0 39 #define SVC_REFERENCE_FRAMES 8
michael@0 40 #define SUPERFRAME_SLOTS (8)
michael@0 41 #define SUPERFRAME_BUFFER_SIZE (SUPERFRAME_SLOTS * sizeof(uint32_t) + 2)
michael@0 42 #define OPTION_BUFFER_SIZE 256
michael@0 43
michael@0 44 static const char *DEFAULT_QUANTIZER_VALUES = "60,53,39,33,27";
michael@0 45 static const char *DEFAULT_SCALE_FACTORS = "4/16,5/16,7/16,11/16,16/16";
michael@0 46
michael@0 47 typedef struct SvcInternal {
michael@0 48 char options[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_options
michael@0 49 char quantizers[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_quantizers
michael@0 50 char scale_factors[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_scale_factors
michael@0 51
michael@0 52 // values extracted from option, quantizers
michael@0 53 int scaling_factor_num[VPX_SS_MAX_LAYERS];
michael@0 54 int scaling_factor_den[VPX_SS_MAX_LAYERS];
michael@0 55 int quantizer[VPX_SS_MAX_LAYERS];
michael@0 56
michael@0 57 // accumulated statistics
michael@0 58 double psnr_in_layer[VPX_SS_MAX_LAYERS];
michael@0 59 uint32_t bytes_in_layer[VPX_SS_MAX_LAYERS];
michael@0 60
michael@0 61 // codec encoding values
michael@0 62 int width; // width of highest layer
michael@0 63 int height; // height of highest layer
michael@0 64 int kf_dist; // distance between keyframes
michael@0 65
michael@0 66 // state variables
michael@0 67 int encode_frame_count;
michael@0 68 int frame_within_gop;
michael@0 69 vpx_enc_frame_flags_t enc_frame_flags;
michael@0 70 int layers;
michael@0 71 int layer;
michael@0 72 int is_keyframe;
michael@0 73
michael@0 74 size_t frame_size;
michael@0 75 size_t buffer_size;
michael@0 76 void *buffer;
michael@0 77
michael@0 78 char message_buffer[2048];
michael@0 79 vpx_codec_ctx_t *codec_ctx;
michael@0 80 } SvcInternal;
michael@0 81
michael@0 82 // Superframe is used to generate an index of individual frames (i.e., layers)
michael@0 83 struct Superframe {
michael@0 84 int count;
michael@0 85 uint32_t sizes[SUPERFRAME_SLOTS];
michael@0 86 uint32_t magnitude;
michael@0 87 uint8_t buffer[SUPERFRAME_BUFFER_SIZE];
michael@0 88 size_t index_size;
michael@0 89 };
michael@0 90
michael@0 91 // One encoded frame layer
michael@0 92 struct LayerData {
michael@0 93 void *buf; // compressed data buffer
michael@0 94 size_t size; // length of compressed data
michael@0 95 struct LayerData *next;
michael@0 96 };
michael@0 97
michael@0 98 // create LayerData from encoder output
michael@0 99 static struct LayerData *ld_create(void *buf, size_t size) {
michael@0 100 struct LayerData *const layer_data = malloc(sizeof(*layer_data));
michael@0 101 if (layer_data == NULL) {
michael@0 102 return NULL;
michael@0 103 }
michael@0 104 layer_data->buf = malloc(size);
michael@0 105 if (layer_data->buf == NULL) {
michael@0 106 free(layer_data);
michael@0 107 return NULL;
michael@0 108 }
michael@0 109 memcpy(layer_data->buf, buf, size);
michael@0 110 layer_data->size = size;
michael@0 111 return layer_data;
michael@0 112 }
michael@0 113
michael@0 114 // free LayerData
michael@0 115 static void ld_free(struct LayerData *layer_data) {
michael@0 116 if (layer_data) {
michael@0 117 if (layer_data->buf) {
michael@0 118 free(layer_data->buf);
michael@0 119 layer_data->buf = NULL;
michael@0 120 }
michael@0 121 free(layer_data);
michael@0 122 }
michael@0 123 }
michael@0 124
michael@0 125 // add layer data to list
michael@0 126 static void ld_list_add(struct LayerData **list, struct LayerData *layer_data) {
michael@0 127 struct LayerData **p = list;
michael@0 128
michael@0 129 while (*p != NULL) p = &(*p)->next;
michael@0 130 *p = layer_data;
michael@0 131 layer_data->next = NULL;
michael@0 132 }
michael@0 133
michael@0 134 // get accumulated size of layer data
michael@0 135 static size_t ld_list_get_buffer_size(struct LayerData *list) {
michael@0 136 struct LayerData *p;
michael@0 137 size_t size = 0;
michael@0 138
michael@0 139 for (p = list; p != NULL; p = p->next) {
michael@0 140 size += p->size;
michael@0 141 }
michael@0 142 return size;
michael@0 143 }
michael@0 144
michael@0 145 // copy layer data to buffer
michael@0 146 static void ld_list_copy_to_buffer(struct LayerData *list, uint8_t *buffer) {
michael@0 147 struct LayerData *p;
michael@0 148
michael@0 149 for (p = list; p != NULL; p = p->next) {
michael@0 150 buffer[0] = 1;
michael@0 151 memcpy(buffer, p->buf, p->size);
michael@0 152 buffer += p->size;
michael@0 153 }
michael@0 154 }
michael@0 155
michael@0 156 // free layer data list
michael@0 157 static void ld_list_free(struct LayerData *list) {
michael@0 158 struct LayerData *p = list;
michael@0 159
michael@0 160 while (p) {
michael@0 161 list = list->next;
michael@0 162 ld_free(p);
michael@0 163 p = list;
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 static void sf_create_index(struct Superframe *sf) {
michael@0 168 uint8_t marker = 0xc0;
michael@0 169 int i;
michael@0 170 uint32_t mag, mask;
michael@0 171 uint8_t *bufp;
michael@0 172
michael@0 173 if (sf->count == 0 || sf->count >= 8) return;
michael@0 174
michael@0 175 // Add the number of frames to the marker byte
michael@0 176 marker |= sf->count - 1;
michael@0 177
michael@0 178 // Choose the magnitude
michael@0 179 for (mag = 0, mask = 0xff; mag < 4; ++mag) {
michael@0 180 if (sf->magnitude < mask) break;
michael@0 181 mask <<= 8;
michael@0 182 mask |= 0xff;
michael@0 183 }
michael@0 184 marker |= mag << 3;
michael@0 185
michael@0 186 // Write the index
michael@0 187 sf->index_size = 2 + (mag + 1) * sf->count;
michael@0 188 bufp = sf->buffer;
michael@0 189
michael@0 190 *bufp++ = marker;
michael@0 191 for (i = 0; i < sf->count; ++i) {
michael@0 192 int this_sz = sf->sizes[i];
michael@0 193 uint32_t j;
michael@0 194
michael@0 195 for (j = 0; j <= mag; ++j) {
michael@0 196 *bufp++ = this_sz & 0xff;
michael@0 197 this_sz >>= 8;
michael@0 198 }
michael@0 199 }
michael@0 200 *bufp++ = marker;
michael@0 201 }
michael@0 202
michael@0 203 static SvcInternal *get_svc_internal(SvcContext *svc_ctx) {
michael@0 204 if (svc_ctx == NULL) return NULL;
michael@0 205 if (svc_ctx->internal == NULL) {
michael@0 206 SvcInternal *const si = malloc(sizeof(*si));
michael@0 207 if (si != NULL) {
michael@0 208 memset(si, 0, sizeof(*si));
michael@0 209 }
michael@0 210 svc_ctx->internal = si;
michael@0 211 }
michael@0 212 return svc_ctx->internal;
michael@0 213 }
michael@0 214
michael@0 215 static const SvcInternal *get_const_svc_internal(const SvcContext *svc_ctx) {
michael@0 216 if (svc_ctx == NULL) return NULL;
michael@0 217 return svc_ctx->internal;
michael@0 218 }
michael@0 219
michael@0 220 static void svc_log_reset(SvcContext *svc_ctx) {
michael@0 221 SvcInternal *const si = (SvcInternal *)svc_ctx->internal;
michael@0 222 si->message_buffer[0] = '\0';
michael@0 223 }
michael@0 224
michael@0 225 static int svc_log(SvcContext *svc_ctx, int level, const char *fmt, ...) {
michael@0 226 char buf[512];
michael@0 227 int retval = 0;
michael@0 228 va_list ap;
michael@0 229 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 230
michael@0 231 if (level > svc_ctx->log_level) {
michael@0 232 return retval;
michael@0 233 }
michael@0 234
michael@0 235 va_start(ap, fmt);
michael@0 236 retval = vsnprintf(buf, sizeof(buf), fmt, ap);
michael@0 237 va_end(ap);
michael@0 238
michael@0 239 if (svc_ctx->log_print) {
michael@0 240 printf("%s", buf);
michael@0 241 } else {
michael@0 242 strncat(si->message_buffer, buf,
michael@0 243 sizeof(si->message_buffer) - strlen(si->message_buffer) - 1);
michael@0 244 }
michael@0 245
michael@0 246 if (level == SVC_LOG_ERROR) {
michael@0 247 si->codec_ctx->err_detail = si->message_buffer;
michael@0 248 }
michael@0 249 return retval;
michael@0 250 }
michael@0 251
michael@0 252 static vpx_codec_err_t set_option_encoding_mode(SvcContext *svc_ctx,
michael@0 253 const char *value_str) {
michael@0 254 if (strcmp(value_str, "i") == 0) {
michael@0 255 svc_ctx->encoding_mode = INTER_LAYER_PREDICTION_I;
michael@0 256 } else if (strcmp(value_str, "alt-ip") == 0) {
michael@0 257 svc_ctx->encoding_mode = ALT_INTER_LAYER_PREDICTION_IP;
michael@0 258 } else if (strcmp(value_str, "ip") == 0) {
michael@0 259 svc_ctx->encoding_mode = INTER_LAYER_PREDICTION_IP;
michael@0 260 } else if (strcmp(value_str, "gf") == 0) {
michael@0 261 svc_ctx->encoding_mode = USE_GOLDEN_FRAME;
michael@0 262 } else {
michael@0 263 svc_log(svc_ctx, SVC_LOG_ERROR, "invalid encoding mode: %s", value_str);
michael@0 264 return VPX_CODEC_INVALID_PARAM;
michael@0 265 }
michael@0 266 return VPX_CODEC_OK;
michael@0 267 }
michael@0 268
michael@0 269 static vpx_codec_err_t parse_quantizer_values(SvcContext *svc_ctx,
michael@0 270 const char *quantizer_values) {
michael@0 271 char *input_string;
michael@0 272 char *token;
michael@0 273 const char *delim = ",";
michael@0 274 char *save_ptr;
michael@0 275 int found = 0;
michael@0 276 int i, q;
michael@0 277 int res = VPX_CODEC_OK;
michael@0 278 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 279
michael@0 280 if (quantizer_values == NULL || strlen(quantizer_values) == 0) {
michael@0 281 input_string = strdup(DEFAULT_QUANTIZER_VALUES);
michael@0 282 } else {
michael@0 283 input_string = strdup(quantizer_values);
michael@0 284 }
michael@0 285
michael@0 286 token = strtok_r(input_string, delim, &save_ptr);
michael@0 287 for (i = 0; i < svc_ctx->spatial_layers; ++i) {
michael@0 288 if (token != NULL) {
michael@0 289 q = atoi(token);
michael@0 290 if (q <= 0 || q > 100) {
michael@0 291 svc_log(svc_ctx, SVC_LOG_ERROR,
michael@0 292 "svc-quantizer-values: invalid value %s\n", token);
michael@0 293 res = VPX_CODEC_INVALID_PARAM;
michael@0 294 break;
michael@0 295 }
michael@0 296 token = strtok_r(NULL, delim, &save_ptr);
michael@0 297 found = i + 1;
michael@0 298 } else {
michael@0 299 q = 0;
michael@0 300 }
michael@0 301 si->quantizer[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] = q;
michael@0 302 }
michael@0 303 if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) {
michael@0 304 svc_log(svc_ctx, SVC_LOG_ERROR,
michael@0 305 "svc: quantizers: %d values required, but only %d specified\n",
michael@0 306 svc_ctx->spatial_layers, found);
michael@0 307 res = VPX_CODEC_INVALID_PARAM;
michael@0 308 }
michael@0 309 free(input_string);
michael@0 310 return res;
michael@0 311 }
michael@0 312
michael@0 313 static void log_invalid_scale_factor(SvcContext *svc_ctx, const char *value) {
michael@0 314 svc_log(svc_ctx, SVC_LOG_ERROR, "svc scale-factors: invalid value %s\n",
michael@0 315 value);
michael@0 316 }
michael@0 317
michael@0 318 static vpx_codec_err_t parse_scale_factors(SvcContext *svc_ctx,
michael@0 319 const char *scale_factors) {
michael@0 320 char *input_string;
michael@0 321 char *token;
michael@0 322 const char *delim = ",";
michael@0 323 char *save_ptr;
michael@0 324 int found = 0;
michael@0 325 int i;
michael@0 326 int64_t num, den;
michael@0 327 int res = VPX_CODEC_OK;
michael@0 328 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 329
michael@0 330 if (scale_factors == NULL || strlen(scale_factors) == 0) {
michael@0 331 input_string = strdup(DEFAULT_SCALE_FACTORS);
michael@0 332 } else {
michael@0 333 input_string = strdup(scale_factors);
michael@0 334 }
michael@0 335 token = strtok_r(input_string, delim, &save_ptr);
michael@0 336 for (i = 0; i < svc_ctx->spatial_layers; ++i) {
michael@0 337 num = den = 0;
michael@0 338 if (token != NULL) {
michael@0 339 num = strtol(token, &token, 10);
michael@0 340 if (num <= 0) {
michael@0 341 log_invalid_scale_factor(svc_ctx, token);
michael@0 342 res = VPX_CODEC_INVALID_PARAM;
michael@0 343 break;
michael@0 344 }
michael@0 345 if (*token++ != '/') {
michael@0 346 log_invalid_scale_factor(svc_ctx, token);
michael@0 347 res = VPX_CODEC_INVALID_PARAM;
michael@0 348 break;
michael@0 349 }
michael@0 350 den = strtol(token, &token, 10);
michael@0 351 if (den <= 0) {
michael@0 352 log_invalid_scale_factor(svc_ctx, token);
michael@0 353 res = VPX_CODEC_INVALID_PARAM;
michael@0 354 break;
michael@0 355 }
michael@0 356 token = strtok_r(NULL, delim, &save_ptr);
michael@0 357 found = i + 1;
michael@0 358 }
michael@0 359 si->scaling_factor_num[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] =
michael@0 360 (int)num;
michael@0 361 si->scaling_factor_den[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] =
michael@0 362 (int)den;
michael@0 363 }
michael@0 364 if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) {
michael@0 365 svc_log(svc_ctx, SVC_LOG_ERROR,
michael@0 366 "svc: scale-factors: %d values required, but only %d specified\n",
michael@0 367 svc_ctx->spatial_layers, found);
michael@0 368 res = VPX_CODEC_INVALID_PARAM;
michael@0 369 }
michael@0 370 free(input_string);
michael@0 371 return res;
michael@0 372 }
michael@0 373
michael@0 374 /**
michael@0 375 * Parse SVC encoding options
michael@0 376 * Format: encoding-mode=<svc_mode>,layers=<layer_count>
michael@0 377 * scale-factors=<n1>/<d1>,<n2>/<d2>,...
michael@0 378 * quantizers=<q1>,<q2>,...
michael@0 379 * svc_mode = [i|ip|alt_ip|gf]
michael@0 380 */
michael@0 381 static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
michael@0 382 char *input_string;
michael@0 383 char *option_name;
michael@0 384 char *option_value;
michael@0 385 char *input_ptr;
michael@0 386 int res = VPX_CODEC_OK;
michael@0 387
michael@0 388 if (options == NULL) return VPX_CODEC_OK;
michael@0 389 input_string = strdup(options);
michael@0 390
michael@0 391 // parse option name
michael@0 392 option_name = strtok_r(input_string, "=", &input_ptr);
michael@0 393 while (option_name != NULL) {
michael@0 394 // parse option value
michael@0 395 option_value = strtok_r(NULL, " ", &input_ptr);
michael@0 396 if (option_value == NULL) {
michael@0 397 svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n",
michael@0 398 option_name);
michael@0 399 res = VPX_CODEC_INVALID_PARAM;
michael@0 400 break;
michael@0 401 }
michael@0 402 if (strcmp("encoding-mode", option_name) == 0) {
michael@0 403 res = set_option_encoding_mode(svc_ctx, option_value);
michael@0 404 if (res != VPX_CODEC_OK) break;
michael@0 405 } else if (strcmp("layers", option_name) == 0) {
michael@0 406 svc_ctx->spatial_layers = atoi(option_value);
michael@0 407 } else if (strcmp("scale-factors", option_name) == 0) {
michael@0 408 res = parse_scale_factors(svc_ctx, option_value);
michael@0 409 if (res != VPX_CODEC_OK) break;
michael@0 410 } else if (strcmp("quantizers", option_name) == 0) {
michael@0 411 res = parse_quantizer_values(svc_ctx, option_value);
michael@0 412 if (res != VPX_CODEC_OK) break;
michael@0 413 } else {
michael@0 414 svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name);
michael@0 415 res = VPX_CODEC_INVALID_PARAM;
michael@0 416 break;
michael@0 417 }
michael@0 418 option_name = strtok_r(NULL, "=", &input_ptr);
michael@0 419 }
michael@0 420 free(input_string);
michael@0 421 return res;
michael@0 422 }
michael@0 423
michael@0 424 vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx, const char *options) {
michael@0 425 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 426 if (svc_ctx == NULL || options == NULL || si == NULL) {
michael@0 427 return VPX_CODEC_INVALID_PARAM;
michael@0 428 }
michael@0 429 strncpy(si->options, options, sizeof(si->options));
michael@0 430 si->options[sizeof(si->options) - 1] = '\0';
michael@0 431 return VPX_CODEC_OK;
michael@0 432 }
michael@0 433
michael@0 434 vpx_codec_err_t vpx_svc_set_quantizers(SvcContext *svc_ctx,
michael@0 435 const char *quantizers) {
michael@0 436 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 437 if (svc_ctx == NULL || quantizers == NULL || si == NULL) {
michael@0 438 return VPX_CODEC_INVALID_PARAM;
michael@0 439 }
michael@0 440 strncpy(si->quantizers, quantizers, sizeof(si->quantizers));
michael@0 441 si->quantizers[sizeof(si->quantizers) - 1] = '\0';
michael@0 442 return VPX_CODEC_OK;
michael@0 443 }
michael@0 444
michael@0 445 vpx_codec_err_t vpx_svc_set_scale_factors(SvcContext *svc_ctx,
michael@0 446 const char *scale_factors) {
michael@0 447 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 448 if (svc_ctx == NULL || scale_factors == NULL || si == NULL) {
michael@0 449 return VPX_CODEC_INVALID_PARAM;
michael@0 450 }
michael@0 451 strncpy(si->scale_factors, scale_factors, sizeof(si->scale_factors));
michael@0 452 si->scale_factors[sizeof(si->scale_factors) - 1] = '\0';
michael@0 453 return VPX_CODEC_OK;
michael@0 454 }
michael@0 455
michael@0 456 vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
michael@0 457 vpx_codec_iface_t *iface,
michael@0 458 vpx_codec_enc_cfg_t *enc_cfg) {
michael@0 459 int max_intra_size_pct;
michael@0 460 vpx_codec_err_t res;
michael@0 461 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 462 if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
michael@0 463 enc_cfg == NULL) {
michael@0 464 return VPX_CODEC_INVALID_PARAM;
michael@0 465 }
michael@0 466 if (si == NULL) return VPX_CODEC_MEM_ERROR;
michael@0 467
michael@0 468 si->codec_ctx = codec_ctx;
michael@0 469
michael@0 470 si->width = enc_cfg->g_w;
michael@0 471 si->height = enc_cfg->g_h;
michael@0 472
michael@0 473 if (enc_cfg->kf_max_dist < 2) {
michael@0 474 svc_log(svc_ctx, SVC_LOG_ERROR, "key frame distance too small: %d\n",
michael@0 475 enc_cfg->kf_max_dist);
michael@0 476 return VPX_CODEC_INVALID_PARAM;
michael@0 477 }
michael@0 478 si->kf_dist = enc_cfg->kf_max_dist;
michael@0 479
michael@0 480 if (svc_ctx->spatial_layers == 0)
michael@0 481 svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS;
michael@0 482 if (svc_ctx->spatial_layers < 1 ||
michael@0 483 svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) {
michael@0 484 svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
michael@0 485 svc_ctx->spatial_layers);
michael@0 486 return VPX_CODEC_INVALID_PARAM;
michael@0 487 }
michael@0 488 // use SvcInternal value for number of layers to enable forcing single layer
michael@0 489 // for first frame
michael@0 490 si->layers = svc_ctx->spatial_layers;
michael@0 491
michael@0 492 res = parse_quantizer_values(svc_ctx, si->quantizers);
michael@0 493 if (res != VPX_CODEC_OK) return res;
michael@0 494
michael@0 495 res = parse_scale_factors(svc_ctx, si->scale_factors);
michael@0 496 if (res != VPX_CODEC_OK) return res;
michael@0 497
michael@0 498 // parse aggregate command line options
michael@0 499 res = parse_options(svc_ctx, si->options);
michael@0 500 if (res != VPX_CODEC_OK) return res;
michael@0 501
michael@0 502 // modify encoder configuration
michael@0 503 enc_cfg->ss_number_layers = si->layers;
michael@0 504 enc_cfg->kf_mode = VPX_KF_DISABLED;
michael@0 505 enc_cfg->g_pass = VPX_RC_ONE_PASS;
michael@0 506 // Lag in frames not currently supported
michael@0 507 enc_cfg->g_lag_in_frames = 0;
michael@0 508
michael@0 509 // TODO(ivanmaltz): determine if these values need to be set explicitly for
michael@0 510 // svc, or if the normal default/override mechanism can be used
michael@0 511 enc_cfg->rc_dropframe_thresh = 0;
michael@0 512 enc_cfg->rc_end_usage = VPX_CBR;
michael@0 513 enc_cfg->rc_resize_allowed = 0;
michael@0 514 enc_cfg->rc_min_quantizer = 33;
michael@0 515 enc_cfg->rc_max_quantizer = 33;
michael@0 516 enc_cfg->rc_undershoot_pct = 100;
michael@0 517 enc_cfg->rc_overshoot_pct = 15;
michael@0 518 enc_cfg->rc_buf_initial_sz = 500;
michael@0 519 enc_cfg->rc_buf_optimal_sz = 600;
michael@0 520 enc_cfg->rc_buf_sz = 1000;
michael@0 521 enc_cfg->g_error_resilient = 1;
michael@0 522
michael@0 523 // Initialize codec
michael@0 524 res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR);
michael@0 525 if (res != VPX_CODEC_OK) {
michael@0 526 svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
michael@0 527 return res;
michael@0 528 }
michael@0 529
michael@0 530 vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1);
michael@0 531 vpx_codec_control(codec_ctx, VP8E_SET_CPUUSED, 1);
michael@0 532 vpx_codec_control(codec_ctx, VP8E_SET_STATIC_THRESHOLD, 1);
michael@0 533 vpx_codec_control(codec_ctx, VP8E_SET_NOISE_SENSITIVITY, 1);
michael@0 534 vpx_codec_control(codec_ctx, VP8E_SET_TOKEN_PARTITIONS, 1);
michael@0 535
michael@0 536 max_intra_size_pct =
michael@0 537 (int)(((double)enc_cfg->rc_buf_optimal_sz * 0.5) *
michael@0 538 ((double)enc_cfg->g_timebase.den / enc_cfg->g_timebase.num) / 10.0);
michael@0 539 vpx_codec_control(codec_ctx, VP8E_SET_MAX_INTRA_BITRATE_PCT,
michael@0 540 max_intra_size_pct);
michael@0 541 return VPX_CODEC_OK;
michael@0 542 }
michael@0 543
michael@0 544 // SVC Algorithm flags - these get mapped to VP8_EFLAG_* defined in vp8cx.h
michael@0 545
michael@0 546 // encoder should reference the last frame
michael@0 547 #define USE_LAST (1 << 0)
michael@0 548
michael@0 549 // encoder should reference the alt ref frame
michael@0 550 #define USE_ARF (1 << 1)
michael@0 551
michael@0 552 // encoder should reference the golden frame
michael@0 553 #define USE_GF (1 << 2)
michael@0 554
michael@0 555 // encoder should copy current frame to the last frame buffer
michael@0 556 #define UPDATE_LAST (1 << 3)
michael@0 557
michael@0 558 // encoder should copy current frame to the alt ref frame buffer
michael@0 559 #define UPDATE_ARF (1 << 4)
michael@0 560
michael@0 561 // encoder should copy current frame to the golden frame
michael@0 562 #define UPDATE_GF (1 << 5)
michael@0 563
michael@0 564 static int map_vp8_flags(int svc_flags) {
michael@0 565 int flags = 0;
michael@0 566
michael@0 567 if (!(svc_flags & USE_LAST)) flags |= VP8_EFLAG_NO_REF_LAST;
michael@0 568 if (!(svc_flags & USE_ARF)) flags |= VP8_EFLAG_NO_REF_ARF;
michael@0 569 if (!(svc_flags & USE_GF)) flags |= VP8_EFLAG_NO_REF_GF;
michael@0 570
michael@0 571 if (svc_flags & UPDATE_LAST) {
michael@0 572 // last is updated automatically
michael@0 573 } else {
michael@0 574 flags |= VP8_EFLAG_NO_UPD_LAST;
michael@0 575 }
michael@0 576 if (svc_flags & UPDATE_ARF) {
michael@0 577 flags |= VP8_EFLAG_FORCE_ARF;
michael@0 578 } else {
michael@0 579 flags |= VP8_EFLAG_NO_UPD_ARF;
michael@0 580 }
michael@0 581 if (svc_flags & UPDATE_GF) {
michael@0 582 flags |= VP8_EFLAG_FORCE_GF;
michael@0 583 } else {
michael@0 584 flags |= VP8_EFLAG_NO_UPD_GF;
michael@0 585 }
michael@0 586 return flags;
michael@0 587 }
michael@0 588
michael@0 589 /**
michael@0 590 * Helper to check if the current frame is the first, full resolution dummy.
michael@0 591 */
michael@0 592 static int vpx_svc_dummy_frame(SvcContext *svc_ctx) {
michael@0 593 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 594 return svc_ctx->first_frame_full_size == 1 && si->encode_frame_count == 0;
michael@0 595 }
michael@0 596
michael@0 597 static void calculate_enc_frame_flags(SvcContext *svc_ctx) {
michael@0 598 vpx_enc_frame_flags_t flags = VPX_EFLAG_FORCE_KF;
michael@0 599 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 600 const int is_keyframe = (si->frame_within_gop == 0);
michael@0 601
michael@0 602 // keyframe layer zero is identical for all modes
michael@0 603 if ((is_keyframe && si->layer == 0) || vpx_svc_dummy_frame(svc_ctx)) {
michael@0 604 si->enc_frame_flags = VPX_EFLAG_FORCE_KF;
michael@0 605 return;
michael@0 606 }
michael@0 607
michael@0 608 switch (svc_ctx->encoding_mode) {
michael@0 609 case ALT_INTER_LAYER_PREDICTION_IP:
michael@0 610 if (si->layer == 0) {
michael@0 611 flags = map_vp8_flags(USE_LAST | UPDATE_LAST);
michael@0 612 } else if (is_keyframe) {
michael@0 613 if (si->layer == si->layers - 1) {
michael@0 614 flags = map_vp8_flags(USE_ARF | UPDATE_LAST);
michael@0 615 } else {
michael@0 616 flags = map_vp8_flags(USE_ARF | UPDATE_LAST | UPDATE_GF);
michael@0 617 }
michael@0 618 } else {
michael@0 619 flags = map_vp8_flags(USE_LAST | USE_ARF | UPDATE_LAST);
michael@0 620 }
michael@0 621 break;
michael@0 622 case INTER_LAYER_PREDICTION_I:
michael@0 623 if (si->layer == 0) {
michael@0 624 flags = map_vp8_flags(USE_LAST | UPDATE_LAST);
michael@0 625 } else if (is_keyframe) {
michael@0 626 flags = map_vp8_flags(USE_ARF | UPDATE_LAST);
michael@0 627 } else {
michael@0 628 flags = map_vp8_flags(USE_LAST | UPDATE_LAST);
michael@0 629 }
michael@0 630 break;
michael@0 631 case INTER_LAYER_PREDICTION_IP:
michael@0 632 if (si->layer == 0) {
michael@0 633 flags = map_vp8_flags(USE_LAST | UPDATE_LAST);
michael@0 634 } else if (is_keyframe) {
michael@0 635 flags = map_vp8_flags(USE_ARF | UPDATE_LAST);
michael@0 636 } else {
michael@0 637 flags = map_vp8_flags(USE_LAST | USE_ARF | UPDATE_LAST);
michael@0 638 }
michael@0 639 break;
michael@0 640 case USE_GOLDEN_FRAME:
michael@0 641 if (2 * si->layers - SVC_REFERENCE_FRAMES <= si->layer) {
michael@0 642 if (si->layer == 0) {
michael@0 643 flags = map_vp8_flags(USE_LAST | USE_GF | UPDATE_LAST);
michael@0 644 } else if (is_keyframe) {
michael@0 645 flags = map_vp8_flags(USE_ARF | UPDATE_LAST | UPDATE_GF);
michael@0 646 } else {
michael@0 647 flags = map_vp8_flags(USE_LAST | USE_ARF | USE_GF | UPDATE_LAST);
michael@0 648 }
michael@0 649 } else {
michael@0 650 if (si->layer == 0) {
michael@0 651 flags = map_vp8_flags(USE_LAST | UPDATE_LAST);
michael@0 652 } else if (is_keyframe) {
michael@0 653 flags = map_vp8_flags(USE_ARF | UPDATE_LAST);
michael@0 654 } else {
michael@0 655 flags = map_vp8_flags(USE_LAST | UPDATE_LAST);
michael@0 656 }
michael@0 657 }
michael@0 658 break;
michael@0 659 default:
michael@0 660 svc_log(svc_ctx, SVC_LOG_ERROR, "unexpected encoding mode: %d\n",
michael@0 661 svc_ctx->encoding_mode);
michael@0 662 break;
michael@0 663 }
michael@0 664 si->enc_frame_flags = flags;
michael@0 665 }
michael@0 666
michael@0 667 vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx,
michael@0 668 int layer,
michael@0 669 unsigned int *width,
michael@0 670 unsigned int *height) {
michael@0 671 int w, h, index, num, den;
michael@0 672 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
michael@0 673
michael@0 674 if (svc_ctx == NULL || si == NULL || width == NULL || height == NULL) {
michael@0 675 return VPX_CODEC_INVALID_PARAM;
michael@0 676 }
michael@0 677 if (layer < 0 || layer >= si->layers) return VPX_CODEC_INVALID_PARAM;
michael@0 678
michael@0 679 index = layer + VPX_SS_MAX_LAYERS - si->layers;
michael@0 680 num = si->scaling_factor_num[index];
michael@0 681 den = si->scaling_factor_den[index];
michael@0 682 if (num == 0 || den == 0) return VPX_CODEC_INVALID_PARAM;
michael@0 683
michael@0 684 w = si->width * num / den;
michael@0 685 h = si->height * num / den;
michael@0 686
michael@0 687 // make height and width even to make chrome player happy
michael@0 688 w += w % 2;
michael@0 689 h += h % 2;
michael@0 690
michael@0 691 *width = w;
michael@0 692 *height = h;
michael@0 693
michael@0 694 return VPX_CODEC_OK;
michael@0 695 }
michael@0 696
michael@0 697 static void set_svc_parameters(SvcContext *svc_ctx,
michael@0 698 vpx_codec_ctx_t *codec_ctx) {
michael@0 699 int layer, layer_index;
michael@0 700 vpx_svc_parameters_t svc_params;
michael@0 701 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 702
michael@0 703 memset(&svc_params, 0, sizeof(svc_params));
michael@0 704 svc_params.layer = si->layer;
michael@0 705 svc_params.flags = si->enc_frame_flags;
michael@0 706
michael@0 707 layer = si->layer;
michael@0 708 if (svc_ctx->encoding_mode == ALT_INTER_LAYER_PREDICTION_IP &&
michael@0 709 si->frame_within_gop == 0) {
michael@0 710 // layers 1 & 3 don't exist in this mode, use the higher one
michael@0 711 if (layer == 0 || layer == 2) {
michael@0 712 layer += 1;
michael@0 713 }
michael@0 714 }
michael@0 715 if (VPX_CODEC_OK != vpx_svc_get_layer_resolution(svc_ctx, layer,
michael@0 716 &svc_params.width,
michael@0 717 &svc_params.height)) {
michael@0 718 svc_log(svc_ctx, SVC_LOG_ERROR, "vpx_svc_get_layer_resolution failed\n");
michael@0 719 }
michael@0 720 layer_index = layer + VPX_SS_MAX_LAYERS - si->layers;
michael@0 721 svc_params.min_quantizer = si->quantizer[layer_index];
michael@0 722 svc_params.max_quantizer = si->quantizer[layer_index];
michael@0 723 svc_params.distance_from_i_frame = si->frame_within_gop;
michael@0 724
michael@0 725 // Use buffer i for layer i LST
michael@0 726 svc_params.lst_fb_idx = si->layer;
michael@0 727
michael@0 728 // Use buffer i-1 for layer i Alt (Inter-layer prediction)
michael@0 729 if (si->layer != 0) {
michael@0 730 const int use_higher_layer =
michael@0 731 svc_ctx->encoding_mode == ALT_INTER_LAYER_PREDICTION_IP &&
michael@0 732 si->frame_within_gop == 0;
michael@0 733 svc_params.alt_fb_idx = use_higher_layer ? si->layer - 2 : si->layer - 1;
michael@0 734 }
michael@0 735
michael@0 736 if (svc_ctx->encoding_mode == ALT_INTER_LAYER_PREDICTION_IP) {
michael@0 737 svc_params.gld_fb_idx = si->layer + 1;
michael@0 738 } else {
michael@0 739 if (si->layer < 2 * si->layers - SVC_REFERENCE_FRAMES)
michael@0 740 svc_params.gld_fb_idx = svc_params.lst_fb_idx;
michael@0 741 else
michael@0 742 svc_params.gld_fb_idx = 2 * si->layers - 1 - si->layer;
michael@0 743 }
michael@0 744
michael@0 745 svc_log(svc_ctx, SVC_LOG_DEBUG, "SVC frame: %d, layer: %d, %dx%d, q: %d\n",
michael@0 746 si->encode_frame_count, si->layer, svc_params.width,
michael@0 747 svc_params.height, svc_params.min_quantizer);
michael@0 748
michael@0 749 if (svc_params.flags == VPX_EFLAG_FORCE_KF) {
michael@0 750 svc_log(svc_ctx, SVC_LOG_DEBUG, "flags == VPX_EFLAG_FORCE_KF\n");
michael@0 751 } else {
michael@0 752 svc_log(
michael@0 753 svc_ctx, SVC_LOG_DEBUG, "Using: LST/GLD/ALT [%2d|%2d|%2d]\n",
michael@0 754 svc_params.flags & VP8_EFLAG_NO_REF_LAST ? -1 : svc_params.lst_fb_idx,
michael@0 755 svc_params.flags & VP8_EFLAG_NO_REF_GF ? -1 : svc_params.gld_fb_idx,
michael@0 756 svc_params.flags & VP8_EFLAG_NO_REF_ARF ? -1 : svc_params.alt_fb_idx);
michael@0 757 svc_log(
michael@0 758 svc_ctx, SVC_LOG_DEBUG, "Updating: LST/GLD/ALT [%2d|%2d|%2d]\n",
michael@0 759 svc_params.flags & VP8_EFLAG_NO_UPD_LAST ? -1 : svc_params.lst_fb_idx,
michael@0 760 svc_params.flags & VP8_EFLAG_NO_UPD_GF ? -1 : svc_params.gld_fb_idx,
michael@0 761 svc_params.flags & VP8_EFLAG_NO_UPD_ARF ? -1 : svc_params.alt_fb_idx);
michael@0 762 }
michael@0 763
michael@0 764 vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &svc_params);
michael@0 765 }
michael@0 766
michael@0 767 /**
michael@0 768 * Encode a frame into multiple layers
michael@0 769 * Create a superframe containing the individual layers
michael@0 770 */
michael@0 771 vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
michael@0 772 struct vpx_image *rawimg, vpx_codec_pts_t pts,
michael@0 773 int64_t duration, int deadline) {
michael@0 774 vpx_codec_err_t res;
michael@0 775 vpx_codec_iter_t iter;
michael@0 776 const vpx_codec_cx_pkt_t *cx_pkt;
michael@0 777 struct LayerData *cx_layer_list = NULL;
michael@0 778 struct LayerData *layer_data;
michael@0 779 struct Superframe superframe;
michael@0 780 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 781 if (svc_ctx == NULL || codec_ctx == NULL || rawimg == NULL || si == NULL) {
michael@0 782 return VPX_CODEC_INVALID_PARAM;
michael@0 783 }
michael@0 784
michael@0 785 memset(&superframe, 0, sizeof(superframe));
michael@0 786 svc_log_reset(svc_ctx);
michael@0 787
michael@0 788 si->layers = vpx_svc_dummy_frame(svc_ctx) ? 1 : svc_ctx->spatial_layers;
michael@0 789 if (si->frame_within_gop >= si->kf_dist ||
michael@0 790 si->encode_frame_count == 0 ||
michael@0 791 (si->encode_frame_count == 1 && svc_ctx->first_frame_full_size == 1)) {
michael@0 792 si->frame_within_gop = 0;
michael@0 793 }
michael@0 794 si->is_keyframe = (si->frame_within_gop == 0);
michael@0 795 si->frame_size = 0;
michael@0 796
michael@0 797 svc_log(svc_ctx, SVC_LOG_DEBUG,
michael@0 798 "vpx_svc_encode layers: %d, frame_count: %d, frame_within_gop: %d\n",
michael@0 799 si->layers, si->encode_frame_count, si->frame_within_gop);
michael@0 800
michael@0 801 // encode each layer
michael@0 802 for (si->layer = 0; si->layer < si->layers; ++si->layer) {
michael@0 803 if (svc_ctx->encoding_mode == ALT_INTER_LAYER_PREDICTION_IP &&
michael@0 804 si->is_keyframe && (si->layer == 1 || si->layer == 3)) {
michael@0 805 svc_log(svc_ctx, SVC_LOG_DEBUG, "Skip encoding layer %d\n", si->layer);
michael@0 806 continue;
michael@0 807 }
michael@0 808 calculate_enc_frame_flags(svc_ctx);
michael@0 809
michael@0 810 if (vpx_svc_dummy_frame(svc_ctx)) {
michael@0 811 // do not set svc parameters, use normal encode
michael@0 812 svc_log(svc_ctx, SVC_LOG_DEBUG, "encoding full size first frame\n");
michael@0 813 } else {
michael@0 814 set_svc_parameters(svc_ctx, codec_ctx);
michael@0 815 }
michael@0 816 res = vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration,
michael@0 817 si->enc_frame_flags, deadline);
michael@0 818 if (res != VPX_CODEC_OK) {
michael@0 819 return res;
michael@0 820 }
michael@0 821 // save compressed data
michael@0 822 iter = NULL;
michael@0 823 while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) {
michael@0 824 switch (cx_pkt->kind) {
michael@0 825 case VPX_CODEC_CX_FRAME_PKT: {
michael@0 826 const uint32_t frame_pkt_size = (uint32_t)(cx_pkt->data.frame.sz);
michael@0 827 if (!vpx_svc_dummy_frame(svc_ctx)) {
michael@0 828 si->bytes_in_layer[si->layer] += frame_pkt_size;
michael@0 829 svc_log(svc_ctx, SVC_LOG_DEBUG,
michael@0 830 "SVC frame: %d, layer: %d, size: %u\n",
michael@0 831 si->encode_frame_count, si->layer, frame_pkt_size);
michael@0 832 }
michael@0 833 layer_data =
michael@0 834 ld_create(cx_pkt->data.frame.buf, (size_t)frame_pkt_size);
michael@0 835 if (layer_data == NULL) {
michael@0 836 svc_log(svc_ctx, SVC_LOG_ERROR, "Error allocating LayerData\n");
michael@0 837 return 0;
michael@0 838 }
michael@0 839 ld_list_add(&cx_layer_list, layer_data);
michael@0 840
michael@0 841 // save layer size in superframe index
michael@0 842 superframe.sizes[superframe.count++] = frame_pkt_size;
michael@0 843 superframe.magnitude |= frame_pkt_size;
michael@0 844 break;
michael@0 845 }
michael@0 846 case VPX_CODEC_PSNR_PKT: {
michael@0 847 if (!vpx_svc_dummy_frame(svc_ctx)) {
michael@0 848 svc_log(svc_ctx, SVC_LOG_DEBUG,
michael@0 849 "SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): "
michael@0 850 "%2.3f %2.3f %2.3f %2.3f \n",
michael@0 851 si->encode_frame_count, si->layer,
michael@0 852 cx_pkt->data.psnr.psnr[0], cx_pkt->data.psnr.psnr[1],
michael@0 853 cx_pkt->data.psnr.psnr[2], cx_pkt->data.psnr.psnr[3]);
michael@0 854 si->psnr_in_layer[si->layer] += cx_pkt->data.psnr.psnr[0];
michael@0 855 }
michael@0 856 break;
michael@0 857 }
michael@0 858 default: {
michael@0 859 break;
michael@0 860 }
michael@0 861 }
michael@0 862 }
michael@0 863 }
michael@0 864 // add superframe index to layer data list
michael@0 865 if (!vpx_svc_dummy_frame(svc_ctx)) {
michael@0 866 sf_create_index(&superframe);
michael@0 867 layer_data = ld_create(superframe.buffer, superframe.index_size);
michael@0 868 ld_list_add(&cx_layer_list, layer_data);
michael@0 869 }
michael@0 870 // get accumulated size of layer data
michael@0 871 si->frame_size = ld_list_get_buffer_size(cx_layer_list);
michael@0 872 if (si->frame_size == 0) return VPX_CODEC_ERROR;
michael@0 873
michael@0 874 // all layers encoded, create single buffer with concatenated layers
michael@0 875 if (si->frame_size > si->buffer_size) {
michael@0 876 free(si->buffer);
michael@0 877 si->buffer = malloc(si->frame_size);
michael@0 878 if (si->buffer == NULL) {
michael@0 879 ld_list_free(cx_layer_list);
michael@0 880 return VPX_CODEC_MEM_ERROR;
michael@0 881 }
michael@0 882 si->buffer_size = si->frame_size;
michael@0 883 }
michael@0 884 // copy layer data into packet
michael@0 885 ld_list_copy_to_buffer(cx_layer_list, si->buffer);
michael@0 886
michael@0 887 ld_list_free(cx_layer_list);
michael@0 888
michael@0 889 svc_log(svc_ctx, SVC_LOG_DEBUG, "SVC frame: %d, kf: %d, size: %d, pts: %d\n",
michael@0 890 si->encode_frame_count, si->is_keyframe, (int)si->frame_size,
michael@0 891 (int)pts);
michael@0 892 ++si->frame_within_gop;
michael@0 893 ++si->encode_frame_count;
michael@0 894
michael@0 895 return VPX_CODEC_OK;
michael@0 896 }
michael@0 897
michael@0 898 const char *vpx_svc_get_message(const SvcContext *svc_ctx) {
michael@0 899 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
michael@0 900 if (svc_ctx == NULL || si == NULL) return NULL;
michael@0 901 return si->message_buffer;
michael@0 902 }
michael@0 903
michael@0 904 void *vpx_svc_get_buffer(const SvcContext *svc_ctx) {
michael@0 905 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
michael@0 906 if (svc_ctx == NULL || si == NULL) return NULL;
michael@0 907 return si->buffer;
michael@0 908 }
michael@0 909
michael@0 910 size_t vpx_svc_get_frame_size(const SvcContext *svc_ctx) {
michael@0 911 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
michael@0 912 if (svc_ctx == NULL || si == NULL) return 0;
michael@0 913 return si->frame_size;
michael@0 914 }
michael@0 915
michael@0 916 int vpx_svc_get_encode_frame_count(const SvcContext *svc_ctx) {
michael@0 917 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
michael@0 918 if (svc_ctx == NULL || si == NULL) return 0;
michael@0 919 return si->encode_frame_count;
michael@0 920 }
michael@0 921
michael@0 922 int vpx_svc_is_keyframe(const SvcContext *svc_ctx) {
michael@0 923 const SvcInternal *const si = get_const_svc_internal(svc_ctx);
michael@0 924 if (svc_ctx == NULL || si == NULL) return 0;
michael@0 925 return si->is_keyframe;
michael@0 926 }
michael@0 927
michael@0 928 void vpx_svc_set_keyframe(SvcContext *svc_ctx) {
michael@0 929 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 930 if (svc_ctx == NULL || si == NULL) return;
michael@0 931 si->frame_within_gop = 0;
michael@0 932 }
michael@0 933
michael@0 934 // dump accumulated statistics and reset accumulated values
michael@0 935 const char *vpx_svc_dump_statistics(SvcContext *svc_ctx) {
michael@0 936 int number_of_frames, number_of_keyframes, encode_frame_count;
michael@0 937 int i;
michael@0 938 uint32_t bytes_total = 0;
michael@0 939 SvcInternal *const si = get_svc_internal(svc_ctx);
michael@0 940 if (svc_ctx == NULL || si == NULL) return NULL;
michael@0 941
michael@0 942 svc_log_reset(svc_ctx);
michael@0 943
michael@0 944 encode_frame_count = si->encode_frame_count;
michael@0 945 if (svc_ctx->first_frame_full_size) encode_frame_count--;
michael@0 946 if (si->encode_frame_count <= 0) return vpx_svc_get_message(svc_ctx);
michael@0 947
michael@0 948 svc_log(svc_ctx, SVC_LOG_INFO, "\n");
michael@0 949 number_of_keyframes = encode_frame_count / si->kf_dist + 1;
michael@0 950 for (i = 0; i < si->layers; ++i) {
michael@0 951 number_of_frames = encode_frame_count;
michael@0 952
michael@0 953 if (svc_ctx->encoding_mode == ALT_INTER_LAYER_PREDICTION_IP &&
michael@0 954 (i == 1 || i == 3)) {
michael@0 955 number_of_frames -= number_of_keyframes;
michael@0 956 }
michael@0 957 svc_log(svc_ctx, SVC_LOG_INFO, "Layer %d PSNR=[%2.3f], Bytes=[%u]\n", i,
michael@0 958 (double)si->psnr_in_layer[i] / number_of_frames,
michael@0 959 si->bytes_in_layer[i]);
michael@0 960 bytes_total += si->bytes_in_layer[i];
michael@0 961 si->psnr_in_layer[i] = 0;
michael@0 962 si->bytes_in_layer[i] = 0;
michael@0 963 }
michael@0 964
michael@0 965 // only display statistics once
michael@0 966 si->encode_frame_count = 0;
michael@0 967
michael@0 968 svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total);
michael@0 969 return vpx_svc_get_message(svc_ctx);
michael@0 970 }
michael@0 971
michael@0 972 void vpx_svc_release(SvcContext *svc_ctx) {
michael@0 973 SvcInternal *si;
michael@0 974 if (svc_ctx == NULL) return;
michael@0 975 // do not use get_svc_internal as it will unnecessarily allocate an
michael@0 976 // SvcInternal if it was not already allocated
michael@0 977 si = (SvcInternal *)svc_ctx->internal;
michael@0 978 if (si != NULL) {
michael@0 979 free(si->buffer);
michael@0 980 free(si);
michael@0 981 svc_ctx->internal = NULL;
michael@0 982 }
michael@0 983 }

mercurial