| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebM project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 /** | 11 /** |
| 12 * @file | 12 * @file |
| 13 * VP9 SVC encoding support via libvpx | 13 * VP9 SVC encoding support via libvpx |
| 14 */ | 14 */ |
| 15 | 15 |
| 16 #include <assert.h> | 16 #include <assert.h> |
| 17 #include <math.h> | 17 #include <math.h> |
| 18 #include <limits.h> |
| 18 #include <stdarg.h> | 19 #include <stdarg.h> |
| 19 #include <stdio.h> | 20 #include <stdio.h> |
| 20 #include <stdlib.h> | 21 #include <stdlib.h> |
| 21 #include <string.h> | 22 #include <string.h> |
| 22 #define VPX_DISABLE_CTRL_TYPECHECKS 1 | 23 #define VPX_DISABLE_CTRL_TYPECHECKS 1 |
| 23 #define VPX_CODEC_DISABLE_COMPAT 1 | |
| 24 #include "./vpx_config.h" | 24 #include "./vpx_config.h" |
| 25 #include "vpx/svc_context.h" | 25 #include "vpx/svc_context.h" |
| 26 #include "vpx/vp8cx.h" | 26 #include "vpx/vp8cx.h" |
| 27 #include "vpx/vpx_encoder.h" | 27 #include "vpx/vpx_encoder.h" |
| 28 #include "vpx_mem/vpx_mem.h" | 28 #include "vpx_mem/vpx_mem.h" |
| 29 #include "vp9/common/vp9_onyxc_int.h" | 29 #include "vp9/common/vp9_onyxc_int.h" |
| 30 | 30 |
| 31 #ifdef __MINGW32__ | 31 #ifdef __MINGW32__ |
| 32 #define strtok_r strtok_s | 32 #define strtok_r strtok_s |
| 33 #ifndef MINGW_HAS_SECURE_API | 33 #ifndef MINGW_HAS_SECURE_API |
| 34 // proto from /usr/x86_64-w64-mingw32/include/sec_api/string_s.h | 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); | 35 _CRTIMP char *__cdecl strtok_s(char *str, const char *delim, char **context); |
| 36 #endif /* MINGW_HAS_SECURE_API */ | 36 #endif /* MINGW_HAS_SECURE_API */ |
| 37 #endif /* __MINGW32__ */ | 37 #endif /* __MINGW32__ */ |
| 38 | 38 |
| 39 #ifdef _MSC_VER | 39 #ifdef _MSC_VER |
| 40 #define strdup _strdup | 40 #define strdup _strdup |
| 41 #define strtok_r strtok_s | 41 #define strtok_r strtok_s |
| 42 #endif | 42 #endif |
| 43 | 43 |
| 44 #define SVC_REFERENCE_FRAMES 8 | 44 #define SVC_REFERENCE_FRAMES 8 |
| 45 #define SUPERFRAME_SLOTS (8) | 45 #define SUPERFRAME_SLOTS (8) |
| 46 #define SUPERFRAME_BUFFER_SIZE (SUPERFRAME_SLOTS * sizeof(uint32_t) + 2) | 46 #define SUPERFRAME_BUFFER_SIZE (SUPERFRAME_SLOTS * sizeof(uint32_t) + 2) |
| 47 #define OPTION_BUFFER_SIZE 256 | 47 #define OPTION_BUFFER_SIZE 1024 |
| 48 #define COMPONENTS 4 // psnr & sse statistics maintained for total, y, u, v | 48 #define COMPONENTS 4 // psnr & sse statistics maintained for total, y, u, v |
| 49 | 49 |
| 50 static const char *DEFAULT_QUANTIZER_VALUES = "60,53,39,33,27"; | 50 #define MAX_QUANTIZER 63 |
| 51 static const char *DEFAULT_SCALE_FACTORS = "4/16,5/16,7/16,11/16,16/16"; | 51 |
| 52 static const int DEFAULT_SCALE_FACTORS_NUM[VPX_SS_MAX_LAYERS] = { |
| 53 4, 5, 7, 11, 16 |
| 54 }; |
| 55 |
| 56 static const int DEFAULT_SCALE_FACTORS_DEN[VPX_SS_MAX_LAYERS] = { |
| 57 16, 16, 16, 16, 16 |
| 58 }; |
| 59 |
| 60 typedef enum { |
| 61 QUANTIZER = 0, |
| 62 BITRATE, |
| 63 SCALE_FACTOR, |
| 64 AUTO_ALT_REF, |
| 65 ALL_OPTION_TYPES |
| 66 } LAYER_OPTION_TYPE; |
| 67 |
| 68 static const int option_max_values[ALL_OPTION_TYPES] = { |
| 69 63, INT_MAX, INT_MAX, 1 |
| 70 }; |
| 71 |
| 72 static const int option_min_values[ALL_OPTION_TYPES] = { |
| 73 0, 0, 1, 0 |
| 74 }; |
| 52 | 75 |
| 53 // One encoded frame | 76 // One encoded frame |
| 54 typedef struct FrameData { | 77 typedef struct FrameData { |
| 55 void *buf; // compressed data buffer | 78 void *buf; // compressed data buffer |
| 56 size_t size; // length of compressed data | 79 size_t size; // length of compressed data |
| 57 vpx_codec_frame_flags_t flags; /**< flags for this frame */ | 80 vpx_codec_frame_flags_t flags; /**< flags for this frame */ |
| 58 struct FrameData *next; | 81 struct FrameData *next; |
| 59 } FrameData; | 82 } FrameData; |
| 60 | 83 |
| 61 typedef struct SvcInternal { | 84 typedef struct SvcInternal { |
| 62 char options[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_options | 85 char options[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_options |
| 63 char quantizers[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_quantizers | |
| 64 char scale_factors[OPTION_BUFFER_SIZE]; // set by vpx_svc_set_scale_factors | |
| 65 | 86 |
| 66 // values extracted from option, quantizers | 87 // values extracted from option, quantizers |
| 67 int scaling_factor_num[VPX_SS_MAX_LAYERS]; | 88 vpx_svc_extra_cfg_t svc_params; |
| 68 int scaling_factor_den[VPX_SS_MAX_LAYERS]; | |
| 69 int quantizer[VPX_SS_MAX_LAYERS]; | |
| 70 int enable_auto_alt_ref[VPX_SS_MAX_LAYERS]; | 89 int enable_auto_alt_ref[VPX_SS_MAX_LAYERS]; |
| 90 int bitrates[VPX_SS_MAX_LAYERS]; |
| 71 | 91 |
| 72 // accumulated statistics | 92 // accumulated statistics |
| 73 double psnr_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; // total/Y/U/V | 93 double psnr_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; // total/Y/U/V |
| 74 uint64_t sse_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; | 94 uint64_t sse_sum[VPX_SS_MAX_LAYERS][COMPONENTS]; |
| 75 uint32_t bytes_sum[VPX_SS_MAX_LAYERS]; | 95 uint32_t bytes_sum[VPX_SS_MAX_LAYERS]; |
| 76 | 96 |
| 77 // codec encoding values | 97 // codec encoding values |
| 78 int width; // width of highest layer | 98 int width; // width of highest layer |
| 79 int height; // height of highest layer | 99 int height; // height of highest layer |
| 80 int kf_dist; // distance between keyframes | 100 int kf_dist; // distance between keyframes |
| 81 | 101 |
| 82 // state variables | 102 // state variables |
| 83 int encode_frame_count; | 103 int psnr_pkt_received; |
| 84 int frame_received; | |
| 85 int frame_within_gop; | |
| 86 int layers; | |
| 87 int layer; | 104 int layer; |
| 88 int is_keyframe; | |
| 89 int use_multiple_frame_contexts; | 105 int use_multiple_frame_contexts; |
| 90 | 106 |
| 91 FrameData *frame_list; | |
| 92 FrameData *frame_temp; | |
| 93 | |
| 94 char *rc_stats_buf; | |
| 95 size_t rc_stats_buf_size; | |
| 96 size_t rc_stats_buf_used; | |
| 97 | |
| 98 char message_buffer[2048]; | 107 char message_buffer[2048]; |
| 99 vpx_codec_ctx_t *codec_ctx; | 108 vpx_codec_ctx_t *codec_ctx; |
| 100 } SvcInternal; | 109 } SvcInternal; |
| 101 | 110 |
| 102 // create FrameData from encoder output | |
| 103 static struct FrameData *fd_create(void *buf, size_t size, | |
| 104 vpx_codec_frame_flags_t flags) { | |
| 105 struct FrameData *const frame_data = | |
| 106 (struct FrameData *)vpx_malloc(sizeof(*frame_data)); | |
| 107 if (frame_data == NULL) { | |
| 108 return NULL; | |
| 109 } | |
| 110 frame_data->buf = vpx_malloc(size); | |
| 111 if (frame_data->buf == NULL) { | |
| 112 vpx_free(frame_data); | |
| 113 return NULL; | |
| 114 } | |
| 115 vpx_memcpy(frame_data->buf, buf, size); | |
| 116 frame_data->size = size; | |
| 117 frame_data->flags = flags; | |
| 118 return frame_data; | |
| 119 } | |
| 120 | |
| 121 // free FrameData | |
| 122 static void fd_free(struct FrameData *p) { | |
| 123 if (p) { | |
| 124 if (p->buf) | |
| 125 vpx_free(p->buf); | |
| 126 vpx_free(p); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 // add FrameData to list | |
| 131 static void fd_list_add(struct FrameData **list, struct FrameData *layer_data) { | |
| 132 struct FrameData **p = list; | |
| 133 | |
| 134 while (*p != NULL) p = &(*p)->next; | |
| 135 *p = layer_data; | |
| 136 layer_data->next = NULL; | |
| 137 } | |
| 138 | |
| 139 // free FrameData list | |
| 140 static void fd_free_list(struct FrameData *list) { | |
| 141 struct FrameData *p = list; | |
| 142 | |
| 143 while (p) { | |
| 144 list = list->next; | |
| 145 fd_free(p); | |
| 146 p = list; | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 static SvcInternal *get_svc_internal(SvcContext *svc_ctx) { | 111 static SvcInternal *get_svc_internal(SvcContext *svc_ctx) { |
| 151 if (svc_ctx == NULL) return NULL; | 112 if (svc_ctx == NULL) return NULL; |
| 152 if (svc_ctx->internal == NULL) { | 113 if (svc_ctx->internal == NULL) { |
| 153 SvcInternal *const si = (SvcInternal *)malloc(sizeof(*si)); | 114 SvcInternal *const si = (SvcInternal *)malloc(sizeof(*si)); |
| 154 if (si != NULL) { | 115 if (si != NULL) { |
| 155 memset(si, 0, sizeof(*si)); | 116 memset(si, 0, sizeof(*si)); |
| 156 } | 117 } |
| 157 svc_ctx->internal = si; | 118 svc_ctx->internal = si; |
| 158 } | 119 } |
| 159 return (SvcInternal *)svc_ctx->internal; | 120 return (SvcInternal *)svc_ctx->internal; |
| (...skipping 30 matching lines...) Expand all Loading... |
| 190 strncat(si->message_buffer, buf, | 151 strncat(si->message_buffer, buf, |
| 191 sizeof(si->message_buffer) - strlen(si->message_buffer) - 1); | 152 sizeof(si->message_buffer) - strlen(si->message_buffer) - 1); |
| 192 } | 153 } |
| 193 | 154 |
| 194 if (level == SVC_LOG_ERROR) { | 155 if (level == SVC_LOG_ERROR) { |
| 195 si->codec_ctx->err_detail = si->message_buffer; | 156 si->codec_ctx->err_detail = si->message_buffer; |
| 196 } | 157 } |
| 197 return retval; | 158 return retval; |
| 198 } | 159 } |
| 199 | 160 |
| 200 static vpx_codec_err_t parse_quantizer_values(SvcContext *svc_ctx, | 161 static vpx_codec_err_t extract_option(LAYER_OPTION_TYPE type, |
| 201 const char *quantizer_values) { | 162 char *input, |
| 163 int *value0, |
| 164 int *value1) { |
| 165 if (type == SCALE_FACTOR) { |
| 166 *value0 = strtol(input, &input, 10); |
| 167 if (*input++ != '/') |
| 168 return VPX_CODEC_INVALID_PARAM; |
| 169 *value1 = strtol(input, &input, 10); |
| 170 |
| 171 if (*value0 < option_min_values[SCALE_FACTOR] || |
| 172 *value1 < option_min_values[SCALE_FACTOR] || |
| 173 *value0 > option_max_values[SCALE_FACTOR] || |
| 174 *value1 > option_max_values[SCALE_FACTOR] || |
| 175 *value0 > *value1) // num shouldn't be greater than den |
| 176 return VPX_CODEC_INVALID_PARAM; |
| 177 } else { |
| 178 *value0 = atoi(input); |
| 179 if (*value0 < option_min_values[type] || |
| 180 *value0 > option_max_values[type]) |
| 181 return VPX_CODEC_INVALID_PARAM; |
| 182 } |
| 183 return VPX_CODEC_OK; |
| 184 } |
| 185 |
| 186 static vpx_codec_err_t parse_layer_options_from_string(SvcContext *svc_ctx, |
| 187 LAYER_OPTION_TYPE type, |
| 188 const char *input, |
| 189 int *option0, |
| 190 int *option1) { |
| 191 int i; |
| 192 vpx_codec_err_t res = VPX_CODEC_OK; |
| 202 char *input_string; | 193 char *input_string; |
| 203 char *token; | 194 char *token; |
| 204 const char *delim = ","; | 195 const char *delim = ","; |
| 205 char *save_ptr; | 196 char *save_ptr; |
| 206 int found = 0; | |
| 207 int i, q; | |
| 208 vpx_codec_err_t res = VPX_CODEC_OK; | |
| 209 SvcInternal *const si = get_svc_internal(svc_ctx); | |
| 210 | 197 |
| 211 if (quantizer_values == NULL || strlen(quantizer_values) == 0) { | 198 if (input == NULL || option0 == NULL || |
| 212 input_string = strdup(DEFAULT_QUANTIZER_VALUES); | 199 (option1 == NULL && type == SCALE_FACTOR)) |
| 213 } else { | 200 return VPX_CODEC_INVALID_PARAM; |
| 214 input_string = strdup(quantizer_values); | |
| 215 } | |
| 216 | 201 |
| 202 input_string = strdup(input); |
| 217 token = strtok_r(input_string, delim, &save_ptr); | 203 token = strtok_r(input_string, delim, &save_ptr); |
| 218 for (i = 0; i < svc_ctx->spatial_layers; ++i) { | 204 for (i = 0; i < svc_ctx->spatial_layers; ++i) { |
| 219 if (token != NULL) { | 205 if (token != NULL) { |
| 220 q = atoi(token); | 206 res = extract_option(type, token, option0 + i, option1 + i); |
| 221 if (q <= 0 || q > 100) { | 207 if (res != VPX_CODEC_OK) |
| 222 svc_log(svc_ctx, SVC_LOG_ERROR, | |
| 223 "svc-quantizer-values: invalid value %s\n", token); | |
| 224 res = VPX_CODEC_INVALID_PARAM; | |
| 225 break; | 208 break; |
| 226 } | |
| 227 token = strtok_r(NULL, delim, &save_ptr); | 209 token = strtok_r(NULL, delim, &save_ptr); |
| 228 found = i + 1; | |
| 229 } else { | 210 } else { |
| 230 q = 0; | 211 break; |
| 231 } | 212 } |
| 232 si->quantizer[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] = q; | |
| 233 } | 213 } |
| 234 if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) { | 214 if (res == VPX_CODEC_OK && i != svc_ctx->spatial_layers) { |
| 235 svc_log(svc_ctx, SVC_LOG_ERROR, | 215 svc_log(svc_ctx, SVC_LOG_ERROR, |
| 236 "svc: quantizers: %d values required, but only %d specified\n", | 216 "svc: layer params type: %d %d values required, " |
| 237 svc_ctx->spatial_layers, found); | 217 "but only %d specified\n", type, svc_ctx->spatial_layers, i); |
| 238 res = VPX_CODEC_INVALID_PARAM; | |
| 239 } | |
| 240 free(input_string); | |
| 241 return res; | |
| 242 } | |
| 243 | |
| 244 static vpx_codec_err_t parse_auto_alt_ref(SvcContext *svc_ctx, | |
| 245 const char *alt_ref_options) { | |
| 246 char *input_string; | |
| 247 char *token; | |
| 248 const char *delim = ","; | |
| 249 char *save_ptr; | |
| 250 int found = 0, enabled = 0; | |
| 251 int i, value; | |
| 252 vpx_codec_err_t res = VPX_CODEC_OK; | |
| 253 SvcInternal *const si = get_svc_internal(svc_ctx); | |
| 254 | |
| 255 if (alt_ref_options == NULL || strlen(alt_ref_options) == 0) { | |
| 256 return VPX_CODEC_INVALID_PARAM; | |
| 257 } else { | |
| 258 input_string = strdup(alt_ref_options); | |
| 259 } | |
| 260 | |
| 261 token = strtok_r(input_string, delim, &save_ptr); | |
| 262 for (i = 0; i < svc_ctx->spatial_layers; ++i) { | |
| 263 if (token != NULL) { | |
| 264 value = atoi(token); | |
| 265 if (value < 0 || value > 1) { | |
| 266 svc_log(svc_ctx, SVC_LOG_ERROR, | |
| 267 "enable auto alt ref values: invalid value %s\n", token); | |
| 268 res = VPX_CODEC_INVALID_PARAM; | |
| 269 break; | |
| 270 } | |
| 271 token = strtok_r(NULL, delim, &save_ptr); | |
| 272 found = i + 1; | |
| 273 } else { | |
| 274 value = 0; | |
| 275 } | |
| 276 si->enable_auto_alt_ref[i] = value; | |
| 277 if (value > 0) | |
| 278 ++enabled; | |
| 279 } | |
| 280 if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) { | |
| 281 svc_log(svc_ctx, SVC_LOG_ERROR, | |
| 282 "svc: quantizers: %d values required, but only %d specified\n", | |
| 283 svc_ctx->spatial_layers, found); | |
| 284 res = VPX_CODEC_INVALID_PARAM; | |
| 285 } | |
| 286 if (enabled > REF_FRAMES - svc_ctx->spatial_layers) { | |
| 287 svc_log(svc_ctx, SVC_LOG_ERROR, | |
| 288 "svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could" | |
| 289 "enabled auto alt reference frame, but % layers are enabled\n", | |
| 290 REF_FRAMES - svc_ctx->spatial_layers, enabled); | |
| 291 res = VPX_CODEC_INVALID_PARAM; | |
| 292 } | |
| 293 free(input_string); | |
| 294 return res; | |
| 295 } | |
| 296 | |
| 297 static void log_invalid_scale_factor(SvcContext *svc_ctx, const char *value) { | |
| 298 svc_log(svc_ctx, SVC_LOG_ERROR, "svc scale-factors: invalid value %s\n", | |
| 299 value); | |
| 300 } | |
| 301 | |
| 302 static vpx_codec_err_t parse_scale_factors(SvcContext *svc_ctx, | |
| 303 const char *scale_factors) { | |
| 304 char *input_string; | |
| 305 char *token; | |
| 306 const char *delim = ","; | |
| 307 char *save_ptr; | |
| 308 int found = 0; | |
| 309 int i; | |
| 310 int64_t num, den; | |
| 311 vpx_codec_err_t res = VPX_CODEC_OK; | |
| 312 SvcInternal *const si = get_svc_internal(svc_ctx); | |
| 313 | |
| 314 if (scale_factors == NULL || strlen(scale_factors) == 0) { | |
| 315 input_string = strdup(DEFAULT_SCALE_FACTORS); | |
| 316 } else { | |
| 317 input_string = strdup(scale_factors); | |
| 318 } | |
| 319 token = strtok_r(input_string, delim, &save_ptr); | |
| 320 for (i = 0; i < svc_ctx->spatial_layers; ++i) { | |
| 321 num = den = 0; | |
| 322 if (token != NULL) { | |
| 323 num = strtol(token, &token, 10); | |
| 324 if (num <= 0) { | |
| 325 log_invalid_scale_factor(svc_ctx, token); | |
| 326 res = VPX_CODEC_INVALID_PARAM; | |
| 327 break; | |
| 328 } | |
| 329 if (*token++ != '/') { | |
| 330 log_invalid_scale_factor(svc_ctx, token); | |
| 331 res = VPX_CODEC_INVALID_PARAM; | |
| 332 break; | |
| 333 } | |
| 334 den = strtol(token, &token, 10); | |
| 335 if (den <= 0) { | |
| 336 log_invalid_scale_factor(svc_ctx, token); | |
| 337 res = VPX_CODEC_INVALID_PARAM; | |
| 338 break; | |
| 339 } | |
| 340 token = strtok_r(NULL, delim, &save_ptr); | |
| 341 found = i + 1; | |
| 342 } | |
| 343 si->scaling_factor_num[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] = | |
| 344 (int)num; | |
| 345 si->scaling_factor_den[i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers] = | |
| 346 (int)den; | |
| 347 } | |
| 348 if (res == VPX_CODEC_OK && found != svc_ctx->spatial_layers) { | |
| 349 svc_log(svc_ctx, SVC_LOG_ERROR, | |
| 350 "svc: scale-factors: %d values required, but only %d specified\n", | |
| 351 svc_ctx->spatial_layers, found); | |
| 352 res = VPX_CODEC_INVALID_PARAM; | 218 res = VPX_CODEC_INVALID_PARAM; |
| 353 } | 219 } |
| 354 free(input_string); | 220 free(input_string); |
| 355 return res; | 221 return res; |
| 356 } | 222 } |
| 357 | 223 |
| 358 /** | 224 /** |
| 359 * Parse SVC encoding options | 225 * Parse SVC encoding options |
| 360 * Format: encoding-mode=<svc_mode>,layers=<layer_count> | 226 * Format: encoding-mode=<svc_mode>,layers=<layer_count> |
| 361 * scale-factors=<n1>/<d1>,<n2>/<d2>,... | 227 * scale-factors=<n1>/<d1>,<n2>/<d2>,... |
| 362 * quantizers=<q1>,<q2>,... | 228 * quantizers=<q1>,<q2>,... |
| 363 * svc_mode = [i|ip|alt_ip|gf] | 229 * svc_mode = [i|ip|alt_ip|gf] |
| 364 */ | 230 */ |
| 365 static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) { | 231 static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) { |
| 366 char *input_string; | 232 char *input_string; |
| 367 char *option_name; | 233 char *option_name; |
| 368 char *option_value; | 234 char *option_value; |
| 369 char *input_ptr; | 235 char *input_ptr; |
| 370 SvcInternal *const si = get_svc_internal(svc_ctx); | 236 SvcInternal *const si = get_svc_internal(svc_ctx); |
| 371 vpx_codec_err_t res = VPX_CODEC_OK; | 237 vpx_codec_err_t res = VPX_CODEC_OK; |
| 238 int i, alt_ref_enabled = 0; |
| 372 | 239 |
| 373 if (options == NULL) return VPX_CODEC_OK; | 240 if (options == NULL) return VPX_CODEC_OK; |
| 374 input_string = strdup(options); | 241 input_string = strdup(options); |
| 375 | 242 |
| 376 // parse option name | 243 // parse option name |
| 377 option_name = strtok_r(input_string, "=", &input_ptr); | 244 option_name = strtok_r(input_string, "=", &input_ptr); |
| 378 while (option_name != NULL) { | 245 while (option_name != NULL) { |
| 379 // parse option value | 246 // parse option value |
| 380 option_value = strtok_r(NULL, " ", &input_ptr); | 247 option_value = strtok_r(NULL, " ", &input_ptr); |
| 381 if (option_value == NULL) { | 248 if (option_value == NULL) { |
| 382 svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n", | 249 svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n", |
| 383 option_name); | 250 option_name); |
| 384 res = VPX_CODEC_INVALID_PARAM; | 251 res = VPX_CODEC_INVALID_PARAM; |
| 385 break; | 252 break; |
| 386 } | 253 } |
| 387 if (strcmp("spatial-layers", option_name) == 0) { | 254 if (strcmp("spatial-layers", option_name) == 0) { |
| 388 svc_ctx->spatial_layers = atoi(option_value); | 255 svc_ctx->spatial_layers = atoi(option_value); |
| 389 } else if (strcmp("temporal-layers", option_name) == 0) { | 256 } else if (strcmp("temporal-layers", option_name) == 0) { |
| 390 svc_ctx->temporal_layers = atoi(option_value); | 257 svc_ctx->temporal_layers = atoi(option_value); |
| 391 } else if (strcmp("scale-factors", option_name) == 0) { | 258 } else if (strcmp("scale-factors", option_name) == 0) { |
| 392 res = parse_scale_factors(svc_ctx, option_value); | 259 res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR, option_value, |
| 260 si->svc_params.scaling_factor_num, |
| 261 si->svc_params.scaling_factor_den); |
| 393 if (res != VPX_CODEC_OK) break; | 262 if (res != VPX_CODEC_OK) break; |
| 394 } else if (strcmp("quantizers", option_name) == 0) { | 263 } else if (strcmp("max-quantizers", option_name) == 0) { |
| 395 res = parse_quantizer_values(svc_ctx, option_value); | 264 res = parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value, |
| 265 si->svc_params.max_quantizers, |
| 266 NULL); |
| 267 if (res != VPX_CODEC_OK) break; |
| 268 } else if (strcmp("min-quantizers", option_name) == 0) { |
| 269 res = parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value, |
| 270 si->svc_params.min_quantizers, |
| 271 NULL); |
| 396 if (res != VPX_CODEC_OK) break; | 272 if (res != VPX_CODEC_OK) break; |
| 397 } else if (strcmp("auto-alt-refs", option_name) == 0) { | 273 } else if (strcmp("auto-alt-refs", option_name) == 0) { |
| 398 res = parse_auto_alt_ref(svc_ctx, option_value); | 274 res = parse_layer_options_from_string(svc_ctx, AUTO_ALT_REF, option_value, |
| 275 si->enable_auto_alt_ref, NULL); |
| 276 if (res != VPX_CODEC_OK) break; |
| 277 } else if (strcmp("bitrates", option_name) == 0) { |
| 278 res = parse_layer_options_from_string(svc_ctx, BITRATE, option_value, |
| 279 si->bitrates, NULL); |
| 399 if (res != VPX_CODEC_OK) break; | 280 if (res != VPX_CODEC_OK) break; |
| 400 } else if (strcmp("multi-frame-contexts", option_name) == 0) { | 281 } else if (strcmp("multi-frame-contexts", option_name) == 0) { |
| 401 si->use_multiple_frame_contexts = atoi(option_value); | 282 si->use_multiple_frame_contexts = atoi(option_value); |
| 402 } else { | 283 } else { |
| 403 svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name); | 284 svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name); |
| 404 res = VPX_CODEC_INVALID_PARAM; | 285 res = VPX_CODEC_INVALID_PARAM; |
| 405 break; | 286 break; |
| 406 } | 287 } |
| 407 option_name = strtok_r(NULL, "=", &input_ptr); | 288 option_name = strtok_r(NULL, "=", &input_ptr); |
| 408 } | 289 } |
| 409 free(input_string); | 290 free(input_string); |
| 410 | 291 |
| 292 for (i = 0; i < svc_ctx->spatial_layers; ++i) { |
| 293 if (si->svc_params.max_quantizers[i] > MAX_QUANTIZER || |
| 294 si->svc_params.max_quantizers[i] < 0 || |
| 295 si->svc_params.min_quantizers[i] > si->svc_params.max_quantizers[i] || |
| 296 si->svc_params.min_quantizers[i] < 0) |
| 297 res = VPX_CODEC_INVALID_PARAM; |
| 298 } |
| 299 |
| 411 if (si->use_multiple_frame_contexts && | 300 if (si->use_multiple_frame_contexts && |
| 412 (svc_ctx->spatial_layers > 3 || | 301 (svc_ctx->spatial_layers > 3 || |
| 413 svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4)) | 302 svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4)) |
| 414 res = VPX_CODEC_INVALID_PARAM; | 303 res = VPX_CODEC_INVALID_PARAM; |
| 415 | 304 |
| 305 for (i = 0; i < svc_ctx->spatial_layers; ++i) |
| 306 alt_ref_enabled += si->enable_auto_alt_ref[i]; |
| 307 if (alt_ref_enabled > REF_FRAMES - svc_ctx->spatial_layers) { |
| 308 svc_log(svc_ctx, SVC_LOG_ERROR, |
| 309 "svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could" |
| 310 "enabled auto alt reference frame, but % layers are enabled\n", |
| 311 REF_FRAMES - svc_ctx->spatial_layers, alt_ref_enabled); |
| 312 res = VPX_CODEC_INVALID_PARAM; |
| 313 } |
| 314 |
| 416 return res; | 315 return res; |
| 417 } | 316 } |
| 418 | 317 |
| 419 vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx, const char *options) { | 318 vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx, const char *options) { |
| 420 SvcInternal *const si = get_svc_internal(svc_ctx); | 319 SvcInternal *const si = get_svc_internal(svc_ctx); |
| 421 if (svc_ctx == NULL || options == NULL || si == NULL) { | 320 if (svc_ctx == NULL || options == NULL || si == NULL) { |
| 422 return VPX_CODEC_INVALID_PARAM; | 321 return VPX_CODEC_INVALID_PARAM; |
| 423 } | 322 } |
| 424 strncpy(si->options, options, sizeof(si->options)); | 323 strncpy(si->options, options, sizeof(si->options)); |
| 425 si->options[sizeof(si->options) - 1] = '\0'; | 324 si->options[sizeof(si->options) - 1] = '\0'; |
| 426 return VPX_CODEC_OK; | 325 return VPX_CODEC_OK; |
| 427 } | 326 } |
| 428 | 327 |
| 429 vpx_codec_err_t vpx_svc_set_quantizers(SvcContext *svc_ctx, | 328 void assign_layer_bitrates(const SvcContext *svc_ctx, |
| 430 const char *quantizers) { | 329 vpx_codec_enc_cfg_t *const enc_cfg) { |
| 431 SvcInternal *const si = get_svc_internal(svc_ctx); | 330 int i; |
| 432 if (svc_ctx == NULL || quantizers == NULL || si == NULL) { | 331 const SvcInternal *const si = get_const_svc_internal(svc_ctx); |
| 433 return VPX_CODEC_INVALID_PARAM; | 332 |
| 333 if (si->bitrates[0] != 0) { |
| 334 enc_cfg->rc_target_bitrate = 0; |
| 335 for (i = 0; i < svc_ctx->spatial_layers; ++i) { |
| 336 enc_cfg->ss_target_bitrate[i] = (unsigned int)si->bitrates[i]; |
| 337 enc_cfg->rc_target_bitrate += si->bitrates[i]; |
| 338 } |
| 339 } else { |
| 340 float total = 0; |
| 341 float alloc_ratio[VPX_SS_MAX_LAYERS] = {0}; |
| 342 |
| 343 for (i = 0; i < svc_ctx->spatial_layers; ++i) { |
| 344 if (si->svc_params.scaling_factor_den[i] > 0) { |
| 345 alloc_ratio[i] = (float)(si->svc_params.scaling_factor_num[i] * 1.0 / |
| 346 si->svc_params.scaling_factor_den[i]); |
| 347 |
| 348 alloc_ratio[i] *= alloc_ratio[i]; |
| 349 total += alloc_ratio[i]; |
| 350 } |
| 351 } |
| 352 |
| 353 for (i = 0; i < svc_ctx->spatial_layers; ++i) { |
| 354 if (total > 0) { |
| 355 enc_cfg->ss_target_bitrate[i] = (unsigned int) |
| 356 (enc_cfg->rc_target_bitrate * alloc_ratio[i] / total); |
| 357 } |
| 358 } |
| 434 } | 359 } |
| 435 strncpy(si->quantizers, quantizers, sizeof(si->quantizers)); | |
| 436 si->quantizers[sizeof(si->quantizers) - 1] = '\0'; | |
| 437 return VPX_CODEC_OK; | |
| 438 } | |
| 439 | |
| 440 vpx_codec_err_t vpx_svc_set_scale_factors(SvcContext *svc_ctx, | |
| 441 const char *scale_factors) { | |
| 442 SvcInternal *const si = get_svc_internal(svc_ctx); | |
| 443 if (svc_ctx == NULL || scale_factors == NULL || si == NULL) { | |
| 444 return VPX_CODEC_INVALID_PARAM; | |
| 445 } | |
| 446 strncpy(si->scale_factors, scale_factors, sizeof(si->scale_factors)); | |
| 447 si->scale_factors[sizeof(si->scale_factors) - 1] = '\0'; | |
| 448 return VPX_CODEC_OK; | |
| 449 } | 360 } |
| 450 | 361 |
| 451 vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx, | 362 vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx, |
| 452 vpx_codec_iface_t *iface, | 363 vpx_codec_iface_t *iface, |
| 453 vpx_codec_enc_cfg_t *enc_cfg) { | 364 vpx_codec_enc_cfg_t *enc_cfg) { |
| 454 vpx_codec_err_t res; | 365 vpx_codec_err_t res; |
| 455 int i; | 366 int i; |
| 456 SvcInternal *const si = get_svc_internal(svc_ctx); | 367 SvcInternal *const si = get_svc_internal(svc_ctx); |
| 457 if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL || | 368 if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL || |
| 458 enc_cfg == NULL) { | 369 enc_cfg == NULL) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 474 | 385 |
| 475 if (svc_ctx->spatial_layers == 0) | 386 if (svc_ctx->spatial_layers == 0) |
| 476 svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS; | 387 svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS; |
| 477 if (svc_ctx->spatial_layers < 1 || | 388 if (svc_ctx->spatial_layers < 1 || |
| 478 svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) { | 389 svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) { |
| 479 svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n", | 390 svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n", |
| 480 svc_ctx->spatial_layers); | 391 svc_ctx->spatial_layers); |
| 481 return VPX_CODEC_INVALID_PARAM; | 392 return VPX_CODEC_INVALID_PARAM; |
| 482 } | 393 } |
| 483 | 394 |
| 484 res = parse_quantizer_values(svc_ctx, si->quantizers); | 395 for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) { |
| 485 if (res != VPX_CODEC_OK) return res; | 396 si->svc_params.max_quantizers[i] = MAX_QUANTIZER; |
| 486 | 397 si->svc_params.min_quantizers[i] = 0; |
| 487 res = parse_scale_factors(svc_ctx, si->scale_factors); | 398 si->svc_params.scaling_factor_num[i] = DEFAULT_SCALE_FACTORS_NUM[i]; |
| 488 if (res != VPX_CODEC_OK) return res; | 399 si->svc_params.scaling_factor_den[i] = DEFAULT_SCALE_FACTORS_DEN[i]; |
| 400 } |
| 489 | 401 |
| 490 // Parse aggregate command line options. Options must start with | 402 // Parse aggregate command line options. Options must start with |
| 491 // "layers=xx" then followed by other options | 403 // "layers=xx" then followed by other options |
| 492 res = parse_options(svc_ctx, si->options); | 404 res = parse_options(svc_ctx, si->options); |
| 493 if (res != VPX_CODEC_OK) return res; | 405 if (res != VPX_CODEC_OK) return res; |
| 494 | 406 |
| 495 if (svc_ctx->spatial_layers < 1) | 407 if (svc_ctx->spatial_layers < 1) |
| 496 svc_ctx->spatial_layers = 1; | 408 svc_ctx->spatial_layers = 1; |
| 497 if (svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) | 409 if (svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) |
| 498 svc_ctx->spatial_layers = VPX_SS_MAX_LAYERS; | 410 svc_ctx->spatial_layers = VPX_SS_MAX_LAYERS; |
| 499 | 411 |
| 500 if (svc_ctx->temporal_layers < 1) | 412 if (svc_ctx->temporal_layers < 1) |
| 501 svc_ctx->temporal_layers = 1; | 413 svc_ctx->temporal_layers = 1; |
| 502 if (svc_ctx->temporal_layers > VPX_TS_MAX_LAYERS) | 414 if (svc_ctx->temporal_layers > VPX_TS_MAX_LAYERS) |
| 503 svc_ctx->temporal_layers = VPX_TS_MAX_LAYERS; | 415 svc_ctx->temporal_layers = VPX_TS_MAX_LAYERS; |
| 504 | 416 |
| 505 si->layers = svc_ctx->spatial_layers; | 417 assign_layer_bitrates(svc_ctx, enc_cfg); |
| 506 | |
| 507 // Assign target bitrate for each layer. We calculate the ratio | |
| 508 // from the resolution for now. | |
| 509 // TODO(Minghai): Optimize the mechanism of allocating bits after | |
| 510 // implementing svc two pass rate control. | |
| 511 if (si->layers > 1) { | |
| 512 float total = 0; | |
| 513 float alloc_ratio[VPX_SS_MAX_LAYERS] = {0}; | |
| 514 | |
| 515 assert(si->layers <= VPX_SS_MAX_LAYERS); | |
| 516 for (i = 0; i < si->layers; ++i) { | |
| 517 int pos = i + VPX_SS_MAX_LAYERS - svc_ctx->spatial_layers; | |
| 518 if (pos < VPX_SS_MAX_LAYERS && si->scaling_factor_den[pos] > 0) { | |
| 519 alloc_ratio[i] = (float)(si->scaling_factor_num[pos] * 1.0 / | |
| 520 si->scaling_factor_den[pos]); | |
| 521 | |
| 522 alloc_ratio[i] *= alloc_ratio[i]; | |
| 523 total += alloc_ratio[i]; | |
| 524 } | |
| 525 } | |
| 526 | |
| 527 for (i = 0; i < si->layers; ++i) { | |
| 528 if (total > 0) { | |
| 529 enc_cfg->ss_target_bitrate[i] = (unsigned int) | |
| 530 (enc_cfg->rc_target_bitrate * alloc_ratio[i] / total); | |
| 531 } | |
| 532 } | |
| 533 } | |
| 534 | 418 |
| 535 #if CONFIG_SPATIAL_SVC | 419 #if CONFIG_SPATIAL_SVC |
| 536 for (i = 0; i < si->layers; ++i) | 420 for (i = 0; i < svc_ctx->spatial_layers; ++i) |
| 537 enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i]; | 421 enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i]; |
| 538 #endif | 422 #endif |
| 539 | 423 |
| 540 if (svc_ctx->temporal_layers > 1) { | 424 if (svc_ctx->temporal_layers > 1) { |
| 541 int i; | 425 int i; |
| 542 for (i = 0; i < svc_ctx->temporal_layers; ++i) { | 426 for (i = 0; i < svc_ctx->temporal_layers; ++i) { |
| 543 enc_cfg->ts_target_bitrate[i] = enc_cfg->rc_target_bitrate / | 427 enc_cfg->ts_target_bitrate[i] = enc_cfg->rc_target_bitrate / |
| 544 svc_ctx->temporal_layers; | 428 svc_ctx->temporal_layers; |
| 545 enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i); | 429 enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i); |
| 546 } | 430 } |
| 547 } | 431 } |
| 548 | 432 |
| 549 // modify encoder configuration | 433 // modify encoder configuration |
| 550 enc_cfg->ss_number_layers = si->layers; | 434 enc_cfg->ss_number_layers = svc_ctx->spatial_layers; |
| 551 enc_cfg->ts_number_layers = svc_ctx->temporal_layers; | 435 enc_cfg->ts_number_layers = svc_ctx->temporal_layers; |
| 552 | 436 |
| 553 // TODO(ivanmaltz): determine if these values need to be set explicitly for | |
| 554 // svc, or if the normal default/override mechanism can be used | |
| 555 enc_cfg->rc_dropframe_thresh = 0; | |
| 556 enc_cfg->rc_resize_allowed = 0; | |
| 557 | |
| 558 if (enc_cfg->g_pass == VPX_RC_ONE_PASS) { | |
| 559 enc_cfg->rc_min_quantizer = 33; | |
| 560 enc_cfg->rc_max_quantizer = 33; | |
| 561 } | |
| 562 | |
| 563 enc_cfg->rc_undershoot_pct = 100; | |
| 564 enc_cfg->rc_overshoot_pct = 15; | |
| 565 enc_cfg->rc_buf_initial_sz = 500; | |
| 566 enc_cfg->rc_buf_optimal_sz = 600; | |
| 567 enc_cfg->rc_buf_sz = 1000; | |
| 568 if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0) | 437 if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0) |
| 569 enc_cfg->g_error_resilient = 1; | 438 enc_cfg->g_error_resilient = 1; |
| 570 | 439 |
| 571 // Initialize codec | 440 // Initialize codec |
| 572 res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR); | 441 res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR); |
| 573 if (res != VPX_CODEC_OK) { | 442 if (res != VPX_CODEC_OK) { |
| 574 svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n"); | 443 svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n"); |
| 575 return res; | 444 return res; |
| 576 } | 445 } |
| 577 | 446 |
| 578 vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1); | 447 vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1); |
| 579 vpx_codec_control(codec_ctx, VP8E_SET_TOKEN_PARTITIONS, 1); | 448 vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &si->svc_params); |
| 580 | 449 |
| 581 return VPX_CODEC_OK; | 450 return VPX_CODEC_OK; |
| 582 } | 451 } |
| 583 | 452 |
| 584 vpx_codec_err_t vpx_svc_get_layer_resolution(const SvcContext *svc_ctx, | |
| 585 int layer, | |
| 586 unsigned int *width, | |
| 587 unsigned int *height) { | |
| 588 int w, h, index, num, den; | |
| 589 const SvcInternal *const si = get_const_svc_internal(svc_ctx); | |
| 590 | |
| 591 if (svc_ctx == NULL || si == NULL || width == NULL || height == NULL) { | |
| 592 return VPX_CODEC_INVALID_PARAM; | |
| 593 } | |
| 594 if (layer < 0 || layer >= si->layers) return VPX_CODEC_INVALID_PARAM; | |
| 595 | |
| 596 index = layer + VPX_SS_MAX_LAYERS - si->layers; | |
| 597 num = si->scaling_factor_num[index]; | |
| 598 den = si->scaling_factor_den[index]; | |
| 599 if (num == 0 || den == 0) return VPX_CODEC_INVALID_PARAM; | |
| 600 | |
| 601 w = si->width * num / den; | |
| 602 h = si->height * num / den; | |
| 603 | |
| 604 // make height and width even to make chrome player happy | |
| 605 w += w % 2; | |
| 606 h += h % 2; | |
| 607 | |
| 608 *width = w; | |
| 609 *height = h; | |
| 610 | |
| 611 return VPX_CODEC_OK; | |
| 612 } | |
| 613 | |
| 614 static void set_svc_parameters(SvcContext *svc_ctx, | |
| 615 vpx_codec_ctx_t *codec_ctx) { | |
| 616 int layer, layer_index; | |
| 617 vpx_svc_parameters_t svc_params; | |
| 618 SvcInternal *const si = get_svc_internal(svc_ctx); | |
| 619 | |
| 620 memset(&svc_params, 0, sizeof(svc_params)); | |
| 621 svc_params.temporal_layer = 0; | |
| 622 svc_params.spatial_layer = si->layer; | |
| 623 | |
| 624 layer = si->layer; | |
| 625 if (VPX_CODEC_OK != vpx_svc_get_layer_resolution(svc_ctx, layer, | |
| 626 &svc_params.width, | |
| 627 &svc_params.height)) { | |
| 628 svc_log(svc_ctx, SVC_LOG_ERROR, "vpx_svc_get_layer_resolution failed\n"); | |
| 629 } | |
| 630 layer_index = layer + VPX_SS_MAX_LAYERS - si->layers; | |
| 631 | |
| 632 if (codec_ctx->config.enc->g_pass == VPX_RC_ONE_PASS) { | |
| 633 svc_params.min_quantizer = si->quantizer[layer_index]; | |
| 634 svc_params.max_quantizer = si->quantizer[layer_index]; | |
| 635 } else { | |
| 636 svc_params.min_quantizer = codec_ctx->config.enc->rc_min_quantizer; | |
| 637 svc_params.max_quantizer = codec_ctx->config.enc->rc_max_quantizer; | |
| 638 } | |
| 639 | |
| 640 svc_params.distance_from_i_frame = si->frame_within_gop; | |
| 641 vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &svc_params); | |
| 642 } | |
| 643 | |
| 644 /** | 453 /** |
| 645 * Encode a frame into multiple layers | 454 * Encode a frame into multiple layers |
| 646 * Create a superframe containing the individual layers | 455 * Create a superframe containing the individual layers |
| 647 */ | 456 */ |
| 648 vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx, | 457 vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx, |
| 649 struct vpx_image *rawimg, vpx_codec_pts_t pts, | 458 struct vpx_image *rawimg, vpx_codec_pts_t pts, |
| 650 int64_t duration, int deadline) { | 459 int64_t duration, int deadline) { |
| 651 vpx_codec_err_t res; | 460 vpx_codec_err_t res; |
| 652 vpx_codec_iter_t iter; | 461 vpx_codec_iter_t iter; |
| 653 const vpx_codec_cx_pkt_t *cx_pkt; | 462 const vpx_codec_cx_pkt_t *cx_pkt; |
| 654 int layer_for_psnr = 0; | |
| 655 SvcInternal *const si = get_svc_internal(svc_ctx); | 463 SvcInternal *const si = get_svc_internal(svc_ctx); |
| 656 if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) { | 464 if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) { |
| 657 return VPX_CODEC_INVALID_PARAM; | 465 return VPX_CODEC_INVALID_PARAM; |
| 658 } | 466 } |
| 659 | 467 |
| 660 svc_log_reset(svc_ctx); | 468 svc_log_reset(svc_ctx); |
| 661 si->rc_stats_buf_used = 0; | |
| 662 | |
| 663 si->layers = svc_ctx->spatial_layers; | |
| 664 if (si->encode_frame_count == 0) { | |
| 665 si->frame_within_gop = 0; | |
| 666 } | |
| 667 si->is_keyframe = (si->frame_within_gop == 0); | |
| 668 | |
| 669 if (rawimg != NULL) { | |
| 670 svc_log(svc_ctx, SVC_LOG_DEBUG, | |
| 671 "vpx_svc_encode layers: %d, frame_count: %d, " | |
| 672 "frame_within_gop: %d\n", si->layers, si->encode_frame_count, | |
| 673 si->frame_within_gop); | |
| 674 } | |
| 675 | |
| 676 if (rawimg != NULL) { | |
| 677 // encode each layer | |
| 678 for (si->layer = 0; si->layer < si->layers; ++si->layer) { | |
| 679 set_svc_parameters(svc_ctx, codec_ctx); | |
| 680 } | |
| 681 } | |
| 682 | 469 |
| 683 res = vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0, | 470 res = vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0, |
| 684 deadline); | 471 deadline); |
| 685 if (res != VPX_CODEC_OK) { | 472 if (res != VPX_CODEC_OK) { |
| 686 return res; | 473 return res; |
| 687 } | 474 } |
| 688 // save compressed data | 475 // save compressed data |
| 689 iter = NULL; | 476 iter = NULL; |
| 690 while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) { | 477 while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) { |
| 691 switch (cx_pkt->kind) { | 478 switch (cx_pkt->kind) { |
| 692 case VPX_CODEC_CX_FRAME_PKT: { | 479 #if CONFIG_SPATIAL_SVC |
| 693 fd_list_add(&si->frame_list, fd_create(cx_pkt->data.frame.buf, | 480 case VPX_CODEC_SPATIAL_SVC_LAYER_PSNR: { |
| 694 cx_pkt->data.frame.sz, | 481 int i; |
| 695 cx_pkt->data.frame.flags)); | 482 for (i = 0; i < svc_ctx->spatial_layers; ++i) { |
| 483 int j; |
| 484 svc_log(svc_ctx, SVC_LOG_DEBUG, |
| 485 "SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): " |
| 486 "%2.3f %2.3f %2.3f %2.3f \n", |
| 487 si->psnr_pkt_received, i, |
| 488 cx_pkt->data.layer_psnr[i].psnr[0], |
| 489 cx_pkt->data.layer_psnr[i].psnr[1], |
| 490 cx_pkt->data.layer_psnr[i].psnr[2], |
| 491 cx_pkt->data.layer_psnr[i].psnr[3]); |
| 492 svc_log(svc_ctx, SVC_LOG_DEBUG, |
| 493 "SVC frame: %d, layer: %d, SSE(Total/Y/U/V): " |
| 494 "%2.3f %2.3f %2.3f %2.3f \n", |
| 495 si->psnr_pkt_received, i, |
| 496 cx_pkt->data.layer_psnr[i].sse[0], |
| 497 cx_pkt->data.layer_psnr[i].sse[1], |
| 498 cx_pkt->data.layer_psnr[i].sse[2], |
| 499 cx_pkt->data.layer_psnr[i].sse[3]); |
| 696 | 500 |
| 697 svc_log(svc_ctx, SVC_LOG_DEBUG, "SVC frame: %d, kf: %d, size: %d, " | 501 for (j = 0; j < COMPONENTS; ++j) { |
| 698 "pts: %d\n", si->frame_received, | 502 si->psnr_sum[i][j] += |
| 699 (cx_pkt->data.frame.flags & VPX_FRAME_IS_KEY) ? 1 : 0, | 503 cx_pkt->data.layer_psnr[i].psnr[j]; |
| 700 (int)cx_pkt->data.frame.sz, (int)cx_pkt->data.frame.pts); | 504 si->sse_sum[i][j] += cx_pkt->data.layer_psnr[i].sse[j]; |
| 701 | 505 } |
| 702 ++si->frame_received; | 506 } |
| 703 layer_for_psnr = 0; | 507 ++si->psnr_pkt_received; |
| 704 break; | 508 break; |
| 705 } | 509 } |
| 706 case VPX_CODEC_PSNR_PKT: { | |
| 707 int i; | |
| 708 svc_log(svc_ctx, SVC_LOG_DEBUG, | |
| 709 "SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): " | |
| 710 "%2.3f %2.3f %2.3f %2.3f \n", | |
| 711 si->frame_received, layer_for_psnr, | |
| 712 cx_pkt->data.psnr.psnr[0], cx_pkt->data.psnr.psnr[1], | |
| 713 cx_pkt->data.psnr.psnr[2], cx_pkt->data.psnr.psnr[3]); | |
| 714 svc_log(svc_ctx, SVC_LOG_DEBUG, | |
| 715 "SVC frame: %d, layer: %d, SSE(Total/Y/U/V): " | |
| 716 "%2.3f %2.3f %2.3f %2.3f \n", | |
| 717 si->frame_received, layer_for_psnr, | |
| 718 cx_pkt->data.psnr.sse[0], cx_pkt->data.psnr.sse[1], | |
| 719 cx_pkt->data.psnr.sse[2], cx_pkt->data.psnr.sse[3]); | |
| 720 for (i = 0; i < COMPONENTS; i++) { | |
| 721 si->psnr_sum[layer_for_psnr][i] += cx_pkt->data.psnr.psnr[i]; | |
| 722 si->sse_sum[layer_for_psnr][i] += cx_pkt->data.psnr.sse[i]; | |
| 723 } | |
| 724 ++layer_for_psnr; | |
| 725 break; | |
| 726 } | |
| 727 case VPX_CODEC_STATS_PKT: { | |
| 728 size_t new_size = si->rc_stats_buf_used + | |
| 729 cx_pkt->data.twopass_stats.sz; | |
| 730 | |
| 731 if (new_size > si->rc_stats_buf_size) { | |
| 732 char *p = (char*)realloc(si->rc_stats_buf, new_size); | |
| 733 if (p == NULL) { | |
| 734 svc_log(svc_ctx, SVC_LOG_ERROR, "Error allocating stats buf\n"); | |
| 735 return VPX_CODEC_MEM_ERROR; | |
| 736 } | |
| 737 si->rc_stats_buf = p; | |
| 738 si->rc_stats_buf_size = new_size; | |
| 739 } | |
| 740 | |
| 741 memcpy(si->rc_stats_buf + si->rc_stats_buf_used, | |
| 742 cx_pkt->data.twopass_stats.buf, cx_pkt->data.twopass_stats.sz); | |
| 743 si->rc_stats_buf_used += cx_pkt->data.twopass_stats.sz; | |
| 744 break; | |
| 745 } | |
| 746 #if CONFIG_SPATIAL_SVC | |
| 747 case VPX_CODEC_SPATIAL_SVC_LAYER_SIZES: { | 510 case VPX_CODEC_SPATIAL_SVC_LAYER_SIZES: { |
| 748 int i; | 511 int i; |
| 749 for (i = 0; i < si->layers; ++i) | 512 for (i = 0; i < svc_ctx->spatial_layers; ++i) |
| 750 si->bytes_sum[i] += cx_pkt->data.layer_sizes[i]; | 513 si->bytes_sum[i] += cx_pkt->data.layer_sizes[i]; |
| 751 break; | 514 break; |
| 752 } | 515 } |
| 753 #endif | 516 #endif |
| 754 default: { | 517 default: { |
| 755 break; | 518 break; |
| 756 } | 519 } |
| 757 } | 520 } |
| 758 } | 521 } |
| 759 | 522 |
| 760 if (rawimg != NULL) { | |
| 761 ++si->frame_within_gop; | |
| 762 ++si->encode_frame_count; | |
| 763 } | |
| 764 | |
| 765 return VPX_CODEC_OK; | 523 return VPX_CODEC_OK; |
| 766 } | 524 } |
| 767 | 525 |
| 768 const char *vpx_svc_get_message(const SvcContext *svc_ctx) { | 526 const char *vpx_svc_get_message(const SvcContext *svc_ctx) { |
| 769 const SvcInternal *const si = get_const_svc_internal(svc_ctx); | 527 const SvcInternal *const si = get_const_svc_internal(svc_ctx); |
| 770 if (svc_ctx == NULL || si == NULL) return NULL; | 528 if (svc_ctx == NULL || si == NULL) return NULL; |
| 771 return si->message_buffer; | 529 return si->message_buffer; |
| 772 } | 530 } |
| 773 | 531 |
| 774 // We will maintain a list of output frame buffers since with lag_in_frame | |
| 775 // we need to output all frame buffers at the end. vpx_svc_get_buffer() will | |
| 776 // remove a frame buffer from the list the put it to a temporal pointer, which | |
| 777 // will be removed at the next vpx_svc_get_buffer() or when closing encoder. | |
| 778 void *vpx_svc_get_buffer(SvcContext *svc_ctx) { | |
| 779 SvcInternal *const si = get_svc_internal(svc_ctx); | |
| 780 if (svc_ctx == NULL || si == NULL || si->frame_list == NULL) return NULL; | |
| 781 | |
| 782 if (si->frame_temp) | |
| 783 fd_free(si->frame_temp); | |
| 784 | |
| 785 si->frame_temp = si->frame_list; | |
| 786 si->frame_list = si->frame_list->next; | |
| 787 | |
| 788 return si->frame_temp->buf; | |
| 789 } | |
| 790 | |
| 791 size_t vpx_svc_get_frame_size(const SvcContext *svc_ctx) { | |
| 792 const SvcInternal *const si = get_const_svc_internal(svc_ctx); | |
| 793 if (svc_ctx == NULL || si == NULL || si->frame_list == NULL) return 0; | |
| 794 return si->frame_list->size; | |
| 795 } | |
| 796 | |
| 797 int vpx_svc_get_encode_frame_count(const SvcContext *svc_ctx) { | |
| 798 const SvcInternal *const si = get_const_svc_internal(svc_ctx); | |
| 799 if (svc_ctx == NULL || si == NULL) return 0; | |
| 800 return si->encode_frame_count; | |
| 801 } | |
| 802 | |
| 803 int vpx_svc_is_keyframe(const SvcContext *svc_ctx) { | |
| 804 const SvcInternal *const si = get_const_svc_internal(svc_ctx); | |
| 805 if (svc_ctx == NULL || si == NULL || si->frame_list == NULL) return 0; | |
| 806 return (si->frame_list->flags & VPX_FRAME_IS_KEY) != 0; | |
| 807 } | |
| 808 | |
| 809 void vpx_svc_set_keyframe(SvcContext *svc_ctx) { | |
| 810 SvcInternal *const si = get_svc_internal(svc_ctx); | |
| 811 if (svc_ctx == NULL || si == NULL) return; | |
| 812 si->frame_within_gop = 0; | |
| 813 } | |
| 814 | |
| 815 static double calc_psnr(double d) { | 532 static double calc_psnr(double d) { |
| 816 if (d == 0) return 100; | 533 if (d == 0) return 100; |
| 817 return -10.0 * log(d) / log(10.0); | 534 return -10.0 * log(d) / log(10.0); |
| 818 } | 535 } |
| 819 | 536 |
| 820 // dump accumulated statistics and reset accumulated values | 537 // dump accumulated statistics and reset accumulated values |
| 821 const char *vpx_svc_dump_statistics(SvcContext *svc_ctx) { | 538 const char *vpx_svc_dump_statistics(SvcContext *svc_ctx) { |
| 822 int number_of_frames, encode_frame_count; | 539 int number_of_frames; |
| 823 int i, j; | 540 int i, j; |
| 824 uint32_t bytes_total = 0; | 541 uint32_t bytes_total = 0; |
| 825 double scale[COMPONENTS]; | 542 double scale[COMPONENTS]; |
| 826 double psnr[COMPONENTS]; | 543 double psnr[COMPONENTS]; |
| 827 double mse[COMPONENTS]; | 544 double mse[COMPONENTS]; |
| 828 double y_scale; | 545 double y_scale; |
| 829 | 546 |
| 830 SvcInternal *const si = get_svc_internal(svc_ctx); | 547 SvcInternal *const si = get_svc_internal(svc_ctx); |
| 831 if (svc_ctx == NULL || si == NULL) return NULL; | 548 if (svc_ctx == NULL || si == NULL) return NULL; |
| 832 | 549 |
| 833 svc_log_reset(svc_ctx); | 550 svc_log_reset(svc_ctx); |
| 834 | 551 |
| 835 encode_frame_count = si->encode_frame_count; | 552 number_of_frames = si->psnr_pkt_received; |
| 836 if (si->encode_frame_count <= 0) return vpx_svc_get_message(svc_ctx); | 553 if (number_of_frames <= 0) return vpx_svc_get_message(svc_ctx); |
| 837 | 554 |
| 838 svc_log(svc_ctx, SVC_LOG_INFO, "\n"); | 555 svc_log(svc_ctx, SVC_LOG_INFO, "\n"); |
| 839 for (i = 0; i < si->layers; ++i) { | 556 for (i = 0; i < svc_ctx->spatial_layers; ++i) { |
| 840 number_of_frames = encode_frame_count; | |
| 841 | 557 |
| 842 svc_log(svc_ctx, SVC_LOG_INFO, | 558 svc_log(svc_ctx, SVC_LOG_INFO, |
| 843 "Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n", | 559 "Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n", |
| 844 i, (double)si->psnr_sum[i][0] / number_of_frames, | 560 i, (double)si->psnr_sum[i][0] / number_of_frames, |
| 845 (double)si->psnr_sum[i][1] / number_of_frames, | 561 (double)si->psnr_sum[i][1] / number_of_frames, |
| 846 (double)si->psnr_sum[i][2] / number_of_frames, | 562 (double)si->psnr_sum[i][2] / number_of_frames, |
| 847 (double)si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]); | 563 (double)si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]); |
| 848 // the following psnr calculation is deduced from ffmpeg.c#print_report | 564 // the following psnr calculation is deduced from ffmpeg.c#print_report |
| 849 y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames; | 565 y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames; |
| 850 scale[1] = y_scale; | 566 scale[1] = y_scale; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 865 bytes_total += si->bytes_sum[i]; | 581 bytes_total += si->bytes_sum[i]; |
| 866 // clear sums for next time | 582 // clear sums for next time |
| 867 si->bytes_sum[i] = 0; | 583 si->bytes_sum[i] = 0; |
| 868 for (j = 0; j < COMPONENTS; ++j) { | 584 for (j = 0; j < COMPONENTS; ++j) { |
| 869 si->psnr_sum[i][j] = 0; | 585 si->psnr_sum[i][j] = 0; |
| 870 si->sse_sum[i][j] = 0; | 586 si->sse_sum[i][j] = 0; |
| 871 } | 587 } |
| 872 } | 588 } |
| 873 | 589 |
| 874 // only display statistics once | 590 // only display statistics once |
| 875 si->encode_frame_count = 0; | 591 si->psnr_pkt_received = 0; |
| 876 | 592 |
| 877 svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total); | 593 svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total); |
| 878 return vpx_svc_get_message(svc_ctx); | 594 return vpx_svc_get_message(svc_ctx); |
| 879 } | 595 } |
| 880 | 596 |
| 881 void vpx_svc_release(SvcContext *svc_ctx) { | 597 void vpx_svc_release(SvcContext *svc_ctx) { |
| 882 SvcInternal *si; | 598 SvcInternal *si; |
| 883 if (svc_ctx == NULL) return; | 599 if (svc_ctx == NULL) return; |
| 884 // do not use get_svc_internal as it will unnecessarily allocate an | 600 // do not use get_svc_internal as it will unnecessarily allocate an |
| 885 // SvcInternal if it was not already allocated | 601 // SvcInternal if it was not already allocated |
| 886 si = (SvcInternal *)svc_ctx->internal; | 602 si = (SvcInternal *)svc_ctx->internal; |
| 887 if (si != NULL) { | 603 if (si != NULL) { |
| 888 fd_free(si->frame_temp); | |
| 889 fd_free_list(si->frame_list); | |
| 890 if (si->rc_stats_buf) { | |
| 891 free(si->rc_stats_buf); | |
| 892 } | |
| 893 free(si); | 604 free(si); |
| 894 svc_ctx->internal = NULL; | 605 svc_ctx->internal = NULL; |
| 895 } | 606 } |
| 896 } | 607 } |
| 897 | 608 |
| 898 size_t vpx_svc_get_rc_stats_buffer_size(const SvcContext *svc_ctx) { | |
| 899 const SvcInternal *const si = get_const_svc_internal(svc_ctx); | |
| 900 if (svc_ctx == NULL || si == NULL) return 0; | |
| 901 return si->rc_stats_buf_used; | |
| 902 } | |
| 903 | |
| 904 char *vpx_svc_get_rc_stats_buffer(const SvcContext *svc_ctx) { | |
| 905 const SvcInternal *const si = get_const_svc_internal(svc_ctx); | |
| 906 if (svc_ctx == NULL || si == NULL) return NULL; | |
| 907 return si->rc_stats_buf; | |
| 908 } | |
| 909 | |
| 910 | |
| OLD | NEW |