Thu, 15 Jan 2015 15:59:08 +0100
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 | } |