mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-11-14 05:18:31 +00:00
add libvpx 89cc6825 (matching current chrome canary) from repo https://chromium.googlesource.com/webm/libvpx
This commit is contained in:
1 parent
4f7a29de5e
commit
1ffc801957
1140 files changed
+491852
No files matched your search
@@ -0,0 +1,693 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* VP9 SVC encoding support via libvpx
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define VPX_DISABLE_CTRL_TYPECHECKS 1
|
||||
#include "./vpx_config.h"
|
||||
#include "vpx/svc_context.h"
|
||||
#include "vpx/vp8cx.h"
|
||||
#include "vpx/vpx_encoder.h"
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
#include "vp9/common/vp9_onyxc_int.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#define strtok_r strtok_s
|
||||
#ifndef MINGW_HAS_SECURE_API
|
||||
// proto from /usr/x86_64-w64-mingw32/include/sec_api/string_s.h
|
||||
_CRTIMP char *__cdecl strtok_s(char *str, const char *delim, char **context);
|
||||
#endif /* MINGW_HAS_SECURE_API */
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strdup _strdup
|
||||
#define strtok_r strtok_s
|
||||
#endif
|
||||
|
||||
#define SVC_REFERENCE_FRAMES 8
|
||||
#define SUPERFRAME_SLOTS (8)
|
||||
#define SUPERFRAME_BUFFER_SIZE (SUPERFRAME_SLOTS * sizeof(uint32_t) + 2)
|
||||
|
||||
#define MAX_QUANTIZER 63
|
||||
|
||||
static const int DEFAULT_SCALE_FACTORS_NUM[VPX_SS_MAX_LAYERS] = {
|
||||
4, 5, 7, 11, 16
|
||||
};
|
||||
|
||||
static const int DEFAULT_SCALE_FACTORS_DEN[VPX_SS_MAX_LAYERS] = {
|
||||
16, 16, 16, 16, 16
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
QUANTIZER = 0,
|
||||
BITRATE,
|
||||
SCALE_FACTOR,
|
||||
AUTO_ALT_REF,
|
||||
ALL_OPTION_TYPES
|
||||
} LAYER_OPTION_TYPE;
|
||||
|
||||
static const int option_max_values[ALL_OPTION_TYPES] = {
|
||||
63, INT_MAX, INT_MAX, 1
|
||||
};
|
||||
|
||||
static const int option_min_values[ALL_OPTION_TYPES] = {
|
||||
0, 0, 1, 0
|
||||
};
|
||||
|
||||
// One encoded frame
|
||||
typedef struct FrameData {
|
||||
void *buf; // compressed data buffer
|
||||
size_t size; // length of compressed data
|
||||
vpx_codec_frame_flags_t flags; /**< flags for this frame */
|
||||
struct FrameData *next;
|
||||
} FrameData;
|
||||
|
||||
static SvcInternal_t *get_svc_internal(SvcContext *svc_ctx) {
|
||||
if (svc_ctx == NULL) return NULL;
|
||||
if (svc_ctx->internal == NULL) {
|
||||
SvcInternal_t *const si = (SvcInternal_t *)malloc(sizeof(*si));
|
||||
if (si != NULL) {
|
||||
memset(si, 0, sizeof(*si));
|
||||
}
|
||||
svc_ctx->internal = si;
|
||||
}
|
||||
return (SvcInternal_t *)svc_ctx->internal;
|
||||
}
|
||||
|
||||
static const SvcInternal_t *get_const_svc_internal(
|
||||
const SvcContext *svc_ctx) {
|
||||
if (svc_ctx == NULL) return NULL;
|
||||
return (const SvcInternal_t *)svc_ctx->internal;
|
||||
}
|
||||
|
||||
static void svc_log_reset(SvcContext *svc_ctx) {
|
||||
SvcInternal_t *const si = (SvcInternal_t *)svc_ctx->internal;
|
||||
si->message_buffer[0] = '\0';
|
||||
}
|
||||
|
||||
static int svc_log(SvcContext *svc_ctx, SVC_LOG_LEVEL level,
|
||||
const char *fmt, ...) {
|
||||
char buf[512];
|
||||
int retval = 0;
|
||||
va_list ap;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
|
||||
if (level > svc_ctx->log_level) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
retval = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (svc_ctx->log_print) {
|
||||
printf("%s", buf);
|
||||
} else {
|
||||
strncat(si->message_buffer, buf,
|
||||
sizeof(si->message_buffer) - strlen(si->message_buffer) - 1);
|
||||
}
|
||||
|
||||
if (level == SVC_LOG_ERROR) {
|
||||
si->codec_ctx->err_detail = si->message_buffer;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static vpx_codec_err_t extract_option(LAYER_OPTION_TYPE type,
|
||||
char *input,
|
||||
int *value0,
|
||||
int *value1) {
|
||||
if (type == SCALE_FACTOR) {
|
||||
*value0 = strtol(input, &input, 10);
|
||||
if (*input++ != '/')
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
*value1 = strtol(input, &input, 10);
|
||||
|
||||
if (*value0 < option_min_values[SCALE_FACTOR] ||
|
||||
*value1 < option_min_values[SCALE_FACTOR] ||
|
||||
*value0 > option_max_values[SCALE_FACTOR] ||
|
||||
*value1 > option_max_values[SCALE_FACTOR] ||
|
||||
*value0 > *value1) // num shouldn't be greater than den
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
} else {
|
||||
*value0 = atoi(input);
|
||||
if (*value0 < option_min_values[type] ||
|
||||
*value0 > option_max_values[type])
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
static vpx_codec_err_t parse_layer_options_from_string(SvcContext *svc_ctx,
|
||||
LAYER_OPTION_TYPE type,
|
||||
const char *input,
|
||||
int *option0,
|
||||
int *option1) {
|
||||
int i;
|
||||
vpx_codec_err_t res = VPX_CODEC_OK;
|
||||
char *input_string;
|
||||
char *token;
|
||||
const char *delim = ",";
|
||||
char *save_ptr;
|
||||
|
||||
if (input == NULL || option0 == NULL ||
|
||||
(option1 == NULL && type == SCALE_FACTOR))
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
|
||||
input_string = strdup(input);
|
||||
token = strtok_r(input_string, delim, &save_ptr);
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
if (token != NULL) {
|
||||
res = extract_option(type, token, option0 + i, option1 + i);
|
||||
if (res != VPX_CODEC_OK)
|
||||
break;
|
||||
token = strtok_r(NULL, delim, &save_ptr);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (res == VPX_CODEC_OK && i != svc_ctx->spatial_layers) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"svc: layer params type: %d %d values required, "
|
||||
"but only %d specified\n", type, svc_ctx->spatial_layers, i);
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
free(input_string);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse SVC encoding options
|
||||
* Format: encoding-mode=<svc_mode>,layers=<layer_count>
|
||||
* scale-factors=<n1>/<d1>,<n2>/<d2>,...
|
||||
* quantizers=<q1>,<q2>,...
|
||||
* svc_mode = [i|ip|alt_ip|gf]
|
||||
*/
|
||||
static vpx_codec_err_t parse_options(SvcContext *svc_ctx, const char *options) {
|
||||
char *input_string;
|
||||
char *option_name;
|
||||
char *option_value;
|
||||
char *input_ptr;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
vpx_codec_err_t res = VPX_CODEC_OK;
|
||||
int i, alt_ref_enabled = 0;
|
||||
|
||||
if (options == NULL) return VPX_CODEC_OK;
|
||||
input_string = strdup(options);
|
||||
|
||||
// parse option name
|
||||
option_name = strtok_r(input_string, "=", &input_ptr);
|
||||
while (option_name != NULL) {
|
||||
// parse option value
|
||||
option_value = strtok_r(NULL, " ", &input_ptr);
|
||||
if (option_value == NULL) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "option missing value: %s\n",
|
||||
option_name);
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
if (strcmp("spatial-layers", option_name) == 0) {
|
||||
svc_ctx->spatial_layers = atoi(option_value);
|
||||
} else if (strcmp("temporal-layers", option_name) == 0) {
|
||||
svc_ctx->temporal_layers = atoi(option_value);
|
||||
} else if (strcmp("scale-factors", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, SCALE_FACTOR, option_value,
|
||||
si->svc_params.scaling_factor_num,
|
||||
si->svc_params.scaling_factor_den);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("max-quantizers", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
|
||||
si->svc_params.max_quantizers,
|
||||
NULL);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("min-quantizers", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, QUANTIZER, option_value,
|
||||
si->svc_params.min_quantizers,
|
||||
NULL);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("auto-alt-refs", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, AUTO_ALT_REF, option_value,
|
||||
si->enable_auto_alt_ref, NULL);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("bitrates", option_name) == 0) {
|
||||
res = parse_layer_options_from_string(svc_ctx, BITRATE, option_value,
|
||||
si->bitrates, NULL);
|
||||
if (res != VPX_CODEC_OK) break;
|
||||
} else if (strcmp("multi-frame-contexts", option_name) == 0) {
|
||||
si->use_multiple_frame_contexts = atoi(option_value);
|
||||
} else {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "invalid option: %s\n", option_name);
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
option_name = strtok_r(NULL, "=", &input_ptr);
|
||||
}
|
||||
free(input_string);
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
if (si->svc_params.max_quantizers[i] > MAX_QUANTIZER ||
|
||||
si->svc_params.max_quantizers[i] < 0 ||
|
||||
si->svc_params.min_quantizers[i] > si->svc_params.max_quantizers[i] ||
|
||||
si->svc_params.min_quantizers[i] < 0)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (si->use_multiple_frame_contexts &&
|
||||
(svc_ctx->spatial_layers > 3 ||
|
||||
svc_ctx->spatial_layers * svc_ctx->temporal_layers > 4))
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i)
|
||||
alt_ref_enabled += si->enable_auto_alt_ref[i];
|
||||
if (alt_ref_enabled > REF_FRAMES - svc_ctx->spatial_layers) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"svc: auto alt ref: Maxinum %d(REF_FRAMES - layers) layers could"
|
||||
"enabled auto alt reference frame, but % layers are enabled\n",
|
||||
REF_FRAMES - svc_ctx->spatial_layers, alt_ref_enabled);
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_svc_set_options(SvcContext *svc_ctx,
|
||||
const char *options) {
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || options == NULL || si == NULL) {
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
strncpy(si->options, options, sizeof(si->options));
|
||||
si->options[sizeof(si->options) - 1] = '\0';
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
void assign_layer_bitrates(const SvcContext *svc_ctx,
|
||||
vpx_codec_enc_cfg_t *const enc_cfg) {
|
||||
int i;
|
||||
const SvcInternal_t *const si = get_const_svc_internal(svc_ctx);
|
||||
int sl, tl, spatial_layer_target;
|
||||
|
||||
if (svc_ctx->temporal_layering_mode != 0) {
|
||||
if (si->bitrates[0] != 0) {
|
||||
enc_cfg->rc_target_bitrate = 0;
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
enc_cfg->ss_target_bitrate[sl*svc_ctx->temporal_layers] = 0;
|
||||
for (tl = 0; tl < svc_ctx->temporal_layers; ++tl) {
|
||||
enc_cfg->ss_target_bitrate[sl*svc_ctx->temporal_layers]
|
||||
+= (unsigned int)si->bitrates[sl * svc_ctx->temporal_layers + tl];
|
||||
enc_cfg->layer_target_bitrate[sl*svc_ctx->temporal_layers + tl]
|
||||
= si->bitrates[sl * svc_ctx->temporal_layers + tl];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float total = 0;
|
||||
float alloc_ratio[VPX_MAX_LAYERS] = {0};
|
||||
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
if (si->svc_params.scaling_factor_den[sl] > 0) {
|
||||
alloc_ratio[sl] = (float)( (sl+1) );
|
||||
total += alloc_ratio[sl];
|
||||
}
|
||||
}
|
||||
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
enc_cfg->ss_target_bitrate[sl] = spatial_layer_target =
|
||||
(unsigned int)(enc_cfg->rc_target_bitrate *
|
||||
alloc_ratio[sl] / total);
|
||||
if (svc_ctx->temporal_layering_mode == 3) {
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
|
||||
(spatial_layer_target*6)/10; // 60%
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
|
||||
(spatial_layer_target*8)/10; // 80%
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 2] =
|
||||
spatial_layer_target;
|
||||
} else if (svc_ctx->temporal_layering_mode == 2 ||
|
||||
svc_ctx->temporal_layering_mode == 1) {
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers] =
|
||||
spatial_layer_target * 2 / 3;
|
||||
enc_cfg->layer_target_bitrate[sl * svc_ctx->temporal_layers + 1] =
|
||||
spatial_layer_target;
|
||||
} else {
|
||||
// User should explicitly assign bitrates in this case.
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (si->bitrates[0] != 0) {
|
||||
enc_cfg->rc_target_bitrate = 0;
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
enc_cfg->ss_target_bitrate[i] = (unsigned int)si->bitrates[i];
|
||||
enc_cfg->rc_target_bitrate += si->bitrates[i];
|
||||
}
|
||||
} else {
|
||||
float total = 0;
|
||||
float alloc_ratio[VPX_MAX_LAYERS] = {0};
|
||||
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
if (si->svc_params.scaling_factor_den[i] > 0) {
|
||||
alloc_ratio[i] = (float)(si->svc_params.scaling_factor_num[i] * 1.0 /
|
||||
si->svc_params.scaling_factor_den[i]);
|
||||
|
||||
alloc_ratio[i] *= alloc_ratio[i];
|
||||
total += alloc_ratio[i];
|
||||
}
|
||||
}
|
||||
for (i = 0; i < VPX_SS_MAX_LAYERS; ++i) {
|
||||
if (total > 0) {
|
||||
enc_cfg->layer_target_bitrate[i] = (unsigned int)
|
||||
(enc_cfg->rc_target_bitrate * alloc_ratio[i] / total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_svc_init(SvcContext *svc_ctx, vpx_codec_ctx_t *codec_ctx,
|
||||
vpx_codec_iface_t *iface,
|
||||
vpx_codec_enc_cfg_t *enc_cfg) {
|
||||
vpx_codec_err_t res;
|
||||
int i, sl , tl;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || codec_ctx == NULL || iface == NULL ||
|
||||
enc_cfg == NULL) {
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
if (si == NULL) return VPX_CODEC_MEM_ERROR;
|
||||
|
||||
si->codec_ctx = codec_ctx;
|
||||
|
||||
si->width = enc_cfg->g_w;
|
||||
si->height = enc_cfg->g_h;
|
||||
|
||||
// wonkap: why is this necessary?
|
||||
/*if (enc_cfg->kf_max_dist < 2) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "key frame distance too small: %d\n",
|
||||
enc_cfg->kf_max_dist);
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}*/
|
||||
|
||||
si->kf_dist = enc_cfg->kf_max_dist;
|
||||
|
||||
if (svc_ctx->spatial_layers == 0)
|
||||
svc_ctx->spatial_layers = VPX_SS_DEFAULT_LAYERS;
|
||||
if (svc_ctx->spatial_layers < 1 ||
|
||||
svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "spatial layers: invalid value: %d\n",
|
||||
svc_ctx->spatial_layers);
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// Note: temporal_layering_mode only applies to one-pass CBR
|
||||
// si->svc_params.temporal_layering_mode = svc_ctx->temporal_layering_mode;
|
||||
if (svc_ctx->temporal_layering_mode == 3) {
|
||||
svc_ctx->temporal_layers = 3;
|
||||
} else if (svc_ctx->temporal_layering_mode == 2 ||
|
||||
svc_ctx->temporal_layering_mode == 1) {
|
||||
svc_ctx->temporal_layers = 2;
|
||||
}
|
||||
|
||||
for (sl = 0; sl < VPX_SS_MAX_LAYERS; ++sl) {
|
||||
si->svc_params.scaling_factor_num[sl] = DEFAULT_SCALE_FACTORS_NUM[sl];
|
||||
si->svc_params.scaling_factor_den[sl] = DEFAULT_SCALE_FACTORS_DEN[sl];
|
||||
}
|
||||
for (tl = 0; tl < svc_ctx->temporal_layers; ++tl) {
|
||||
for (sl = 0; sl < svc_ctx->spatial_layers; ++sl) {
|
||||
i = sl * svc_ctx->temporal_layers + tl;
|
||||
si->svc_params.max_quantizers[i] = MAX_QUANTIZER;
|
||||
si->svc_params.min_quantizers[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse aggregate command line options. Options must start with
|
||||
// "layers=xx" then followed by other options
|
||||
res = parse_options(svc_ctx, si->options);
|
||||
if (res != VPX_CODEC_OK) return res;
|
||||
|
||||
if (svc_ctx->spatial_layers < 1)
|
||||
svc_ctx->spatial_layers = 1;
|
||||
if (svc_ctx->spatial_layers > VPX_SS_MAX_LAYERS)
|
||||
svc_ctx->spatial_layers = VPX_SS_MAX_LAYERS;
|
||||
|
||||
if (svc_ctx->temporal_layers < 1)
|
||||
svc_ctx->temporal_layers = 1;
|
||||
if (svc_ctx->temporal_layers > VPX_TS_MAX_LAYERS)
|
||||
svc_ctx->temporal_layers = VPX_TS_MAX_LAYERS;
|
||||
|
||||
if (svc_ctx->temporal_layers * svc_ctx->spatial_layers > VPX_MAX_LAYERS) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR,
|
||||
"spatial layers * temporal layers exceeds the maximum number of "
|
||||
"allowed layers of %d\n",
|
||||
svc_ctx->spatial_layers * svc_ctx->temporal_layers,
|
||||
(int) VPX_MAX_LAYERS);
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
assign_layer_bitrates(svc_ctx, enc_cfg);
|
||||
|
||||
#if CONFIG_SPATIAL_SVC
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i)
|
||||
enc_cfg->ss_enable_auto_alt_ref[i] = si->enable_auto_alt_ref[i];
|
||||
#endif
|
||||
|
||||
if (svc_ctx->temporal_layers > 1) {
|
||||
int i;
|
||||
for (i = 0; i < svc_ctx->temporal_layers; ++i) {
|
||||
enc_cfg->ts_target_bitrate[i] = enc_cfg->rc_target_bitrate /
|
||||
svc_ctx->temporal_layers;
|
||||
enc_cfg->ts_rate_decimator[i] = 1 << (svc_ctx->temporal_layers - 1 - i);
|
||||
}
|
||||
}
|
||||
|
||||
if (svc_ctx->threads)
|
||||
enc_cfg->g_threads = svc_ctx->threads;
|
||||
|
||||
// Modify encoder configuration
|
||||
enc_cfg->ss_number_layers = svc_ctx->spatial_layers;
|
||||
enc_cfg->ts_number_layers = svc_ctx->temporal_layers;
|
||||
|
||||
if (enc_cfg->rc_end_usage == VPX_CBR) {
|
||||
enc_cfg->rc_resize_allowed = 0;
|
||||
enc_cfg->rc_min_quantizer = 2;
|
||||
enc_cfg->rc_max_quantizer = 56;
|
||||
enc_cfg->rc_undershoot_pct = 50;
|
||||
enc_cfg->rc_overshoot_pct = 50;
|
||||
enc_cfg->rc_buf_initial_sz = 500;
|
||||
enc_cfg->rc_buf_optimal_sz = 600;
|
||||
enc_cfg->rc_buf_sz = 1000;
|
||||
enc_cfg->rc_dropframe_thresh = 0;
|
||||
}
|
||||
|
||||
if (enc_cfg->g_error_resilient == 0 && si->use_multiple_frame_contexts == 0)
|
||||
enc_cfg->g_error_resilient = 1;
|
||||
|
||||
// Initialize codec
|
||||
res = vpx_codec_enc_init(codec_ctx, iface, enc_cfg, VPX_CODEC_USE_PSNR);
|
||||
if (res != VPX_CODEC_OK) {
|
||||
svc_log(svc_ctx, SVC_LOG_ERROR, "svc_enc_init error\n");
|
||||
return res;
|
||||
}
|
||||
if (svc_ctx->spatial_layers > 1 || svc_ctx->temporal_layers > 1) {
|
||||
vpx_codec_control(codec_ctx, VP9E_SET_SVC, 1);
|
||||
vpx_codec_control(codec_ctx, VP9E_SET_SVC_PARAMETERS, &si->svc_params);
|
||||
}
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a frame into multiple layers
|
||||
* Create a superframe containing the individual layers
|
||||
*/
|
||||
vpx_codec_err_t vpx_svc_encode(SvcContext *svc_ctx,
|
||||
vpx_codec_ctx_t *codec_ctx,
|
||||
struct vpx_image *rawimg,
|
||||
vpx_codec_pts_t pts,
|
||||
int64_t duration, int deadline) {
|
||||
vpx_codec_err_t res;
|
||||
vpx_codec_iter_t iter;
|
||||
const vpx_codec_cx_pkt_t *cx_pkt;
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || codec_ctx == NULL || si == NULL) {
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
}
|
||||
|
||||
svc_log_reset(svc_ctx);
|
||||
|
||||
res = vpx_codec_encode(codec_ctx, rawimg, pts, (uint32_t)duration, 0,
|
||||
deadline);
|
||||
if (res != VPX_CODEC_OK) {
|
||||
return res;
|
||||
}
|
||||
// save compressed data
|
||||
iter = NULL;
|
||||
while ((cx_pkt = vpx_codec_get_cx_data(codec_ctx, &iter))) {
|
||||
switch (cx_pkt->kind) {
|
||||
#if VPX_ENCODER_ABI_VERSION > (5 + VPX_CODEC_ABI_VERSION)
|
||||
#if CONFIG_SPATIAL_SVC
|
||||
case VPX_CODEC_SPATIAL_SVC_LAYER_PSNR: {
|
||||
int i;
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
int j;
|
||||
svc_log(svc_ctx, SVC_LOG_DEBUG,
|
||||
"SVC frame: %d, layer: %d, PSNR(Total/Y/U/V): "
|
||||
"%2.3f %2.3f %2.3f %2.3f \n",
|
||||
si->psnr_pkt_received, i,
|
||||
cx_pkt->data.layer_psnr[i].psnr[0],
|
||||
cx_pkt->data.layer_psnr[i].psnr[1],
|
||||
cx_pkt->data.layer_psnr[i].psnr[2],
|
||||
cx_pkt->data.layer_psnr[i].psnr[3]);
|
||||
svc_log(svc_ctx, SVC_LOG_DEBUG,
|
||||
"SVC frame: %d, layer: %d, SSE(Total/Y/U/V): "
|
||||
"%2.3f %2.3f %2.3f %2.3f \n",
|
||||
si->psnr_pkt_received, i,
|
||||
cx_pkt->data.layer_psnr[i].sse[0],
|
||||
cx_pkt->data.layer_psnr[i].sse[1],
|
||||
cx_pkt->data.layer_psnr[i].sse[2],
|
||||
cx_pkt->data.layer_psnr[i].sse[3]);
|
||||
|
||||
for (j = 0; j < COMPONENTS; ++j) {
|
||||
si->psnr_sum[i][j] +=
|
||||
cx_pkt->data.layer_psnr[i].psnr[j];
|
||||
si->sse_sum[i][j] += cx_pkt->data.layer_psnr[i].sse[j];
|
||||
}
|
||||
}
|
||||
++si->psnr_pkt_received;
|
||||
break;
|
||||
}
|
||||
case VPX_CODEC_SPATIAL_SVC_LAYER_SIZES: {
|
||||
int i;
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i)
|
||||
si->bytes_sum[i] += cx_pkt->data.layer_sizes[i];
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
case VPX_CODEC_PSNR_PKT:
|
||||
{
|
||||
#if VPX_ENCODER_ABI_VERSION > (5 + VPX_CODEC_ABI_VERSION)
|
||||
int j;
|
||||
svc_log(svc_ctx, SVC_LOG_DEBUG,
|
||||
"frame: %d, layer: %d, PSNR(Total/Y/U/V): "
|
||||
"%2.3f %2.3f %2.3f %2.3f \n",
|
||||
si->psnr_pkt_received, 0,
|
||||
cx_pkt->data.layer_psnr[0].psnr[0],
|
||||
cx_pkt->data.layer_psnr[0].psnr[1],
|
||||
cx_pkt->data.layer_psnr[0].psnr[2],
|
||||
cx_pkt->data.layer_psnr[0].psnr[3]);
|
||||
for (j = 0; j < COMPONENTS; ++j) {
|
||||
si->psnr_sum[0][j] +=
|
||||
cx_pkt->data.layer_psnr[0].psnr[j];
|
||||
si->sse_sum[0][j] += cx_pkt->data.layer_psnr[0].sse[j];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
++si->psnr_pkt_received;
|
||||
break;
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
const char *vpx_svc_get_message(const SvcContext *svc_ctx) {
|
||||
const SvcInternal_t *const si = get_const_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || si == NULL) return NULL;
|
||||
return si->message_buffer;
|
||||
}
|
||||
|
||||
static double calc_psnr(double d) {
|
||||
if (d == 0) return 100;
|
||||
return -10.0 * log(d) / log(10.0);
|
||||
}
|
||||
|
||||
// dump accumulated statistics and reset accumulated values
|
||||
const char *vpx_svc_dump_statistics(SvcContext *svc_ctx) {
|
||||
int number_of_frames;
|
||||
int i, j;
|
||||
uint32_t bytes_total = 0;
|
||||
double scale[COMPONENTS];
|
||||
double psnr[COMPONENTS];
|
||||
double mse[COMPONENTS];
|
||||
double y_scale;
|
||||
|
||||
SvcInternal_t *const si = get_svc_internal(svc_ctx);
|
||||
if (svc_ctx == NULL || si == NULL) return NULL;
|
||||
|
||||
svc_log_reset(svc_ctx);
|
||||
|
||||
number_of_frames = si->psnr_pkt_received;
|
||||
if (number_of_frames <= 0) return vpx_svc_get_message(svc_ctx);
|
||||
|
||||
svc_log(svc_ctx, SVC_LOG_INFO, "\n");
|
||||
for (i = 0; i < svc_ctx->spatial_layers; ++i) {
|
||||
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Average PSNR=[%2.3f, %2.3f, %2.3f, %2.3f], Bytes=[%u]\n",
|
||||
i, (double)si->psnr_sum[i][0] / number_of_frames,
|
||||
(double)si->psnr_sum[i][1] / number_of_frames,
|
||||
(double)si->psnr_sum[i][2] / number_of_frames,
|
||||
(double)si->psnr_sum[i][3] / number_of_frames, si->bytes_sum[i]);
|
||||
// the following psnr calculation is deduced from ffmpeg.c#print_report
|
||||
y_scale = si->width * si->height * 255.0 * 255.0 * number_of_frames;
|
||||
scale[1] = y_scale;
|
||||
scale[2] = scale[3] = y_scale / 4; // U or V
|
||||
scale[0] = y_scale * 1.5; // total
|
||||
|
||||
for (j = 0; j < COMPONENTS; j++) {
|
||||
psnr[j] = calc_psnr(si->sse_sum[i][j] / scale[j]);
|
||||
mse[j] = si->sse_sum[i][j] * 255.0 * 255.0 / scale[j];
|
||||
}
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Overall PSNR=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, psnr[0],
|
||||
psnr[1], psnr[2], psnr[3]);
|
||||
svc_log(svc_ctx, SVC_LOG_INFO,
|
||||
"Layer %d Overall MSE=[%2.3f, %2.3f, %2.3f, %2.3f]\n", i, mse[0],
|
||||
mse[1], mse[2], mse[3]);
|
||||
|
||||
bytes_total += si->bytes_sum[i];
|
||||
// Clear sums for next time.
|
||||
si->bytes_sum[i] = 0;
|
||||
for (j = 0; j < COMPONENTS; ++j) {
|
||||
si->psnr_sum[i][j] = 0;
|
||||
si->sse_sum[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// only display statistics once
|
||||
si->psnr_pkt_received = 0;
|
||||
|
||||
svc_log(svc_ctx, SVC_LOG_INFO, "Total Bytes=[%u]\n", bytes_total);
|
||||
return vpx_svc_get_message(svc_ctx);
|
||||
}
|
||||
|
||||
void vpx_svc_release(SvcContext *svc_ctx) {
|
||||
SvcInternal_t *si;
|
||||
if (svc_ctx == NULL) return;
|
||||
// do not use get_svc_internal as it will unnecessarily allocate an
|
||||
// SvcInternal_t if it was not already allocated
|
||||
si = (SvcInternal_t *)svc_ctx->internal;
|
||||
if (si != NULL) {
|
||||
free(si);
|
||||
svc_ctx->internal = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
|
||||
/*!\file
|
||||
* \brief Provides the high level interface to wrap decoder algorithms.
|
||||
*
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx/internal/vpx_codec_internal.h"
|
||||
#include "vpx_version.h"
|
||||
|
||||
#define SAVE_STATUS(ctx,var) (ctx?(ctx->err = var):var)
|
||||
|
||||
int vpx_codec_version(void) {
|
||||
return VERSION_PACKED;
|
||||
}
|
||||
|
||||
|
||||
const char *vpx_codec_version_str(void) {
|
||||
return VERSION_STRING_NOSP;
|
||||
}
|
||||
|
||||
|
||||
const char *vpx_codec_version_extra_str(void) {
|
||||
return VERSION_EXTRA;
|
||||
}
|
||||
|
||||
|
||||
const char *vpx_codec_iface_name(vpx_codec_iface_t *iface) {
|
||||
return iface ? iface->name : "<invalid interface>";
|
||||
}
|
||||
|
||||
const char *vpx_codec_err_to_string(vpx_codec_err_t err) {
|
||||
switch (err) {
|
||||
case VPX_CODEC_OK:
|
||||
return "Success";
|
||||
case VPX_CODEC_ERROR:
|
||||
return "Unspecified internal error";
|
||||
case VPX_CODEC_MEM_ERROR:
|
||||
return "Memory allocation error";
|
||||
case VPX_CODEC_ABI_MISMATCH:
|
||||
return "ABI version mismatch";
|
||||
case VPX_CODEC_INCAPABLE:
|
||||
return "Codec does not implement requested capability";
|
||||
case VPX_CODEC_UNSUP_BITSTREAM:
|
||||
return "Bitstream not supported by this decoder";
|
||||
case VPX_CODEC_UNSUP_FEATURE:
|
||||
return "Bitstream required feature not supported by this decoder";
|
||||
case VPX_CODEC_CORRUPT_FRAME:
|
||||
return "Corrupt frame detected";
|
||||
case VPX_CODEC_INVALID_PARAM:
|
||||
return "Invalid parameter";
|
||||
case VPX_CODEC_LIST_END:
|
||||
return "End of iterated list";
|
||||
}
|
||||
|
||||
return "Unrecognized error code";
|
||||
}
|
||||
|
||||
const char *vpx_codec_error(vpx_codec_ctx_t *ctx) {
|
||||
return (ctx) ? vpx_codec_err_to_string(ctx->err)
|
||||
: vpx_codec_err_to_string(VPX_CODEC_INVALID_PARAM);
|
||||
}
|
||||
|
||||
const char *vpx_codec_error_detail(vpx_codec_ctx_t *ctx) {
|
||||
if (ctx && ctx->err)
|
||||
return ctx->priv ? ctx->priv->err_detail : ctx->err_detail;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_destroy(vpx_codec_ctx_t *ctx) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!ctx)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!ctx->iface || !ctx->priv)
|
||||
res = VPX_CODEC_ERROR;
|
||||
else {
|
||||
ctx->iface->destroy((vpx_codec_alg_priv_t *)ctx->priv);
|
||||
|
||||
ctx->iface = NULL;
|
||||
ctx->name = NULL;
|
||||
ctx->priv = NULL;
|
||||
res = VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_caps_t vpx_codec_get_caps(vpx_codec_iface_t *iface) {
|
||||
return (iface) ? iface->caps : 0;
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_control_(vpx_codec_ctx_t *ctx,
|
||||
int ctrl_id,
|
||||
...) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!ctx || !ctrl_id)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!ctx->iface || !ctx->priv || !ctx->iface->ctrl_maps)
|
||||
res = VPX_CODEC_ERROR;
|
||||
else {
|
||||
vpx_codec_ctrl_fn_map_t *entry;
|
||||
|
||||
res = VPX_CODEC_ERROR;
|
||||
|
||||
for (entry = ctx->iface->ctrl_maps; entry && entry->fn; entry++) {
|
||||
if (!entry->ctrl_id || entry->ctrl_id == ctrl_id) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, ctrl_id);
|
||||
res = entry->fn((vpx_codec_alg_priv_t *)ctx->priv, ap);
|
||||
va_end(ap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
void vpx_internal_error(struct vpx_internal_error_info *info,
|
||||
vpx_codec_err_t error,
|
||||
const char *fmt,
|
||||
...) {
|
||||
va_list ap;
|
||||
|
||||
info->error_code = error;
|
||||
info->has_detail = 0;
|
||||
|
||||
if (fmt) {
|
||||
size_t sz = sizeof(info->detail);
|
||||
|
||||
info->has_detail = 1;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(info->detail, sz - 1, fmt, ap);
|
||||
va_end(ap);
|
||||
info->detail[sz - 1] = '\0';
|
||||
}
|
||||
|
||||
if (info->setjmp)
|
||||
longjmp(info->jmp, info->error_code);
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
|
||||
/*!\file
|
||||
* \brief Provides the high level interface to wrap decoder algorithms.
|
||||
*
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "vpx/internal/vpx_codec_internal.h"
|
||||
|
||||
#define SAVE_STATUS(ctx,var) (ctx?(ctx->err = var):var)
|
||||
|
||||
static vpx_codec_alg_priv_t *get_alg_priv(vpx_codec_ctx_t *ctx) {
|
||||
return (vpx_codec_alg_priv_t *)ctx->priv;
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_codec_dec_init_ver(vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_iface_t *iface,
|
||||
const vpx_codec_dec_cfg_t *cfg,
|
||||
vpx_codec_flags_t flags,
|
||||
int ver) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (ver != VPX_DECODER_ABI_VERSION)
|
||||
res = VPX_CODEC_ABI_MISMATCH;
|
||||
else if (!ctx || !iface)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (iface->abi_version != VPX_CODEC_INTERNAL_ABI_VERSION)
|
||||
res = VPX_CODEC_ABI_MISMATCH;
|
||||
else if ((flags & VPX_CODEC_USE_POSTPROC) && !(iface->caps & VPX_CODEC_CAP_POSTPROC))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else if ((flags & VPX_CODEC_USE_ERROR_CONCEALMENT) &&
|
||||
!(iface->caps & VPX_CODEC_CAP_ERROR_CONCEALMENT))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else if ((flags & VPX_CODEC_USE_INPUT_FRAGMENTS) &&
|
||||
!(iface->caps & VPX_CODEC_CAP_INPUT_FRAGMENTS))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else if (!(iface->caps & VPX_CODEC_CAP_DECODER))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else {
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->iface = iface;
|
||||
ctx->name = iface->name;
|
||||
ctx->priv = NULL;
|
||||
ctx->init_flags = flags;
|
||||
ctx->config.dec = cfg;
|
||||
|
||||
res = ctx->iface->init(ctx, NULL);
|
||||
if (res) {
|
||||
ctx->err_detail = ctx->priv ? ctx->priv->err_detail : NULL;
|
||||
vpx_codec_destroy(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_peek_stream_info(vpx_codec_iface_t *iface,
|
||||
const uint8_t *data,
|
||||
unsigned int data_sz,
|
||||
vpx_codec_stream_info_t *si) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!iface || !data || !data_sz || !si
|
||||
|| si->sz < sizeof(vpx_codec_stream_info_t))
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else {
|
||||
/* Set default/unknown values */
|
||||
si->w = 0;
|
||||
si->h = 0;
|
||||
|
||||
res = iface->dec.peek_si(data, data_sz, si);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_get_stream_info(vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_stream_info_t *si) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!ctx || !si || si->sz < sizeof(vpx_codec_stream_info_t))
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!ctx->iface || !ctx->priv)
|
||||
res = VPX_CODEC_ERROR;
|
||||
else {
|
||||
/* Set default/unknown values */
|
||||
si->w = 0;
|
||||
si->h = 0;
|
||||
|
||||
res = ctx->iface->dec.get_si(get_alg_priv(ctx), si);
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_decode(vpx_codec_ctx_t *ctx,
|
||||
const uint8_t *data,
|
||||
unsigned int data_sz,
|
||||
void *user_priv,
|
||||
long deadline) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
/* Sanity checks */
|
||||
/* NULL data ptr allowed if data_sz is 0 too */
|
||||
if (!ctx || (!data && data_sz) || (data && !data_sz))
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!ctx->iface || !ctx->priv)
|
||||
res = VPX_CODEC_ERROR;
|
||||
else {
|
||||
res = ctx->iface->dec.decode(get_alg_priv(ctx), data, data_sz, user_priv,
|
||||
deadline);
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
vpx_image_t *vpx_codec_get_frame(vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_iter_t *iter) {
|
||||
vpx_image_t *img;
|
||||
|
||||
if (!ctx || !iter || !ctx->iface || !ctx->priv)
|
||||
img = NULL;
|
||||
else
|
||||
img = ctx->iface->dec.get_frame(get_alg_priv(ctx), iter);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_register_put_frame_cb(vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_put_frame_cb_fn_t cb,
|
||||
void *user_priv) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!ctx || !cb)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!ctx->iface || !ctx->priv
|
||||
|| !(ctx->iface->caps & VPX_CODEC_CAP_PUT_FRAME))
|
||||
res = VPX_CODEC_ERROR;
|
||||
else {
|
||||
ctx->priv->dec.put_frame_cb.u.put_frame = cb;
|
||||
ctx->priv->dec.put_frame_cb.user_priv = user_priv;
|
||||
res = VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_register_put_slice_cb(vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_put_slice_cb_fn_t cb,
|
||||
void *user_priv) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!ctx || !cb)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!ctx->iface || !ctx->priv
|
||||
|| !(ctx->iface->caps & VPX_CODEC_CAP_PUT_SLICE))
|
||||
res = VPX_CODEC_ERROR;
|
||||
else {
|
||||
ctx->priv->dec.put_slice_cb.u.put_slice = cb;
|
||||
ctx->priv->dec.put_slice_cb.user_priv = user_priv;
|
||||
res = VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_codec_set_frame_buffer_functions(
|
||||
vpx_codec_ctx_t *ctx, vpx_get_frame_buffer_cb_fn_t cb_get,
|
||||
vpx_release_frame_buffer_cb_fn_t cb_release, void *cb_priv) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!ctx || !cb_get || !cb_release) {
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
} else if (!ctx->iface || !ctx->priv ||
|
||||
!(ctx->iface->caps & VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER)) {
|
||||
res = VPX_CODEC_ERROR;
|
||||
} else {
|
||||
res = ctx->iface->dec.set_fb_fn(get_alg_priv(ctx), cb_get, cb_release,
|
||||
cb_priv);
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
|
||||
/*!\file
|
||||
* \brief Provides the high level interface to wrap encoder algorithms.
|
||||
*
|
||||
*/
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include "vpx_config.h"
|
||||
#include "vpx/internal/vpx_codec_internal.h"
|
||||
|
||||
#define SAVE_STATUS(ctx,var) (ctx?(ctx->err = var):var)
|
||||
|
||||
static vpx_codec_alg_priv_t *get_alg_priv(vpx_codec_ctx_t *ctx) {
|
||||
return (vpx_codec_alg_priv_t *)ctx->priv;
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_codec_enc_init_ver(vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_iface_t *iface,
|
||||
const vpx_codec_enc_cfg_t *cfg,
|
||||
vpx_codec_flags_t flags,
|
||||
int ver) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (ver != VPX_ENCODER_ABI_VERSION)
|
||||
res = VPX_CODEC_ABI_MISMATCH;
|
||||
else if (!ctx || !iface || !cfg)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (iface->abi_version != VPX_CODEC_INTERNAL_ABI_VERSION)
|
||||
res = VPX_CODEC_ABI_MISMATCH;
|
||||
else if (!(iface->caps & VPX_CODEC_CAP_ENCODER))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else if ((flags & VPX_CODEC_USE_PSNR)
|
||||
&& !(iface->caps & VPX_CODEC_CAP_PSNR))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else if ((flags & VPX_CODEC_USE_OUTPUT_PARTITION)
|
||||
&& !(iface->caps & VPX_CODEC_CAP_OUTPUT_PARTITION))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else {
|
||||
ctx->iface = iface;
|
||||
ctx->name = iface->name;
|
||||
ctx->priv = NULL;
|
||||
ctx->init_flags = flags;
|
||||
ctx->config.enc = cfg;
|
||||
res = ctx->iface->init(ctx, NULL);
|
||||
|
||||
if (res) {
|
||||
ctx->err_detail = ctx->priv ? ctx->priv->err_detail : NULL;
|
||||
vpx_codec_destroy(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
vpx_codec_err_t vpx_codec_enc_init_multi_ver(vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_iface_t *iface,
|
||||
vpx_codec_enc_cfg_t *cfg,
|
||||
int num_enc,
|
||||
vpx_codec_flags_t flags,
|
||||
vpx_rational_t *dsf,
|
||||
int ver) {
|
||||
vpx_codec_err_t res = VPX_CODEC_OK;
|
||||
|
||||
if (ver != VPX_ENCODER_ABI_VERSION)
|
||||
res = VPX_CODEC_ABI_MISMATCH;
|
||||
else if (!ctx || !iface || !cfg || (num_enc > 16 || num_enc < 1))
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (iface->abi_version != VPX_CODEC_INTERNAL_ABI_VERSION)
|
||||
res = VPX_CODEC_ABI_MISMATCH;
|
||||
else if (!(iface->caps & VPX_CODEC_CAP_ENCODER))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else if ((flags & VPX_CODEC_USE_PSNR)
|
||||
&& !(iface->caps & VPX_CODEC_CAP_PSNR))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else if ((flags & VPX_CODEC_USE_OUTPUT_PARTITION)
|
||||
&& !(iface->caps & VPX_CODEC_CAP_OUTPUT_PARTITION))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else {
|
||||
int i;
|
||||
void *mem_loc = NULL;
|
||||
|
||||
if (!(res = iface->enc.mr_get_mem_loc(cfg, &mem_loc))) {
|
||||
for (i = 0; i < num_enc; i++) {
|
||||
vpx_codec_priv_enc_mr_cfg_t mr_cfg;
|
||||
|
||||
/* Validate down-sampling factor. */
|
||||
if (dsf->num < 1 || dsf->num > 4096 || dsf->den < 1 ||
|
||||
dsf->den > dsf->num) {
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
mr_cfg.mr_low_res_mode_info = mem_loc;
|
||||
mr_cfg.mr_total_resolutions = num_enc;
|
||||
mr_cfg.mr_encoder_id = num_enc - 1 - i;
|
||||
mr_cfg.mr_down_sampling_factor.num = dsf->num;
|
||||
mr_cfg.mr_down_sampling_factor.den = dsf->den;
|
||||
|
||||
/* Force Key-frame synchronization. Namely, encoder at higher
|
||||
* resolution always use the same frame_type chosen by the
|
||||
* lowest-resolution encoder.
|
||||
*/
|
||||
if (mr_cfg.mr_encoder_id)
|
||||
cfg->kf_mode = VPX_KF_DISABLED;
|
||||
|
||||
ctx->iface = iface;
|
||||
ctx->name = iface->name;
|
||||
ctx->priv = NULL;
|
||||
ctx->init_flags = flags;
|
||||
ctx->config.enc = cfg;
|
||||
res = ctx->iface->init(ctx, &mr_cfg);
|
||||
|
||||
if (res) {
|
||||
const char *error_detail =
|
||||
ctx->priv ? ctx->priv->err_detail : NULL;
|
||||
/* Destroy current ctx */
|
||||
ctx->err_detail = error_detail;
|
||||
vpx_codec_destroy(ctx);
|
||||
|
||||
/* Destroy already allocated high-level ctx */
|
||||
while (i) {
|
||||
ctx--;
|
||||
ctx->err_detail = error_detail;
|
||||
vpx_codec_destroy(ctx);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (res)
|
||||
break;
|
||||
|
||||
ctx++;
|
||||
cfg++;
|
||||
dsf++;
|
||||
}
|
||||
ctx--;
|
||||
}
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_enc_config_default(vpx_codec_iface_t *iface,
|
||||
vpx_codec_enc_cfg_t *cfg,
|
||||
unsigned int usage) {
|
||||
vpx_codec_err_t res;
|
||||
vpx_codec_enc_cfg_map_t *map;
|
||||
int i;
|
||||
|
||||
if (!iface || !cfg || usage > INT_MAX)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!(iface->caps & VPX_CODEC_CAP_ENCODER))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else {
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
|
||||
for (i = 0; i < iface->enc.cfg_map_count; ++i) {
|
||||
map = iface->enc.cfg_maps + i;
|
||||
if (map->usage == (int)usage) {
|
||||
*cfg = map->cfg;
|
||||
cfg->g_usage = usage;
|
||||
res = VPX_CODEC_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
#if ARCH_X86 || ARCH_X86_64
|
||||
/* On X86, disable the x87 unit's internal 80 bit precision for better
|
||||
* consistency with the SSE unit's 64 bit precision.
|
||||
*/
|
||||
#include "vpx_ports/x86.h"
|
||||
#define FLOATING_POINT_INIT() do {\
|
||||
unsigned short x87_orig_mode = x87_set_double_precision();
|
||||
#define FLOATING_POINT_RESTORE() \
|
||||
x87_set_control_word(x87_orig_mode); }while(0)
|
||||
|
||||
|
||||
#else
|
||||
static void FLOATING_POINT_INIT() {}
|
||||
static void FLOATING_POINT_RESTORE() {}
|
||||
#endif
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_encode(vpx_codec_ctx_t *ctx,
|
||||
const vpx_image_t *img,
|
||||
vpx_codec_pts_t pts,
|
||||
unsigned long duration,
|
||||
vpx_enc_frame_flags_t flags,
|
||||
unsigned long deadline) {
|
||||
vpx_codec_err_t res = VPX_CODEC_OK;
|
||||
|
||||
if (!ctx || (img && !duration))
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!ctx->iface || !ctx->priv)
|
||||
res = VPX_CODEC_ERROR;
|
||||
else if (!(ctx->iface->caps & VPX_CODEC_CAP_ENCODER))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else {
|
||||
unsigned int num_enc = ctx->priv->enc.total_encoders;
|
||||
|
||||
/* Execute in a normalized floating point environment, if the platform
|
||||
* requires it.
|
||||
*/
|
||||
FLOATING_POINT_INIT();
|
||||
|
||||
if (num_enc == 1)
|
||||
res = ctx->iface->enc.encode(get_alg_priv(ctx), img, pts,
|
||||
duration, flags, deadline);
|
||||
else {
|
||||
/* Multi-resolution encoding:
|
||||
* Encode multi-levels in reverse order. For example,
|
||||
* if mr_total_resolutions = 3, first encode level 2,
|
||||
* then encode level 1, and finally encode level 0.
|
||||
*/
|
||||
int i;
|
||||
|
||||
ctx += num_enc - 1;
|
||||
if (img) img += num_enc - 1;
|
||||
|
||||
for (i = num_enc - 1; i >= 0; i--) {
|
||||
if ((res = ctx->iface->enc.encode(get_alg_priv(ctx), img, pts,
|
||||
duration, flags, deadline)))
|
||||
break;
|
||||
|
||||
ctx--;
|
||||
if (img) img--;
|
||||
}
|
||||
ctx++;
|
||||
}
|
||||
|
||||
FLOATING_POINT_RESTORE();
|
||||
}
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
|
||||
const vpx_codec_cx_pkt_t *vpx_codec_get_cx_data(vpx_codec_ctx_t *ctx,
|
||||
vpx_codec_iter_t *iter) {
|
||||
const vpx_codec_cx_pkt_t *pkt = NULL;
|
||||
|
||||
if (ctx) {
|
||||
if (!iter)
|
||||
ctx->err = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!ctx->iface || !ctx->priv)
|
||||
ctx->err = VPX_CODEC_ERROR;
|
||||
else if (!(ctx->iface->caps & VPX_CODEC_CAP_ENCODER))
|
||||
ctx->err = VPX_CODEC_INCAPABLE;
|
||||
else
|
||||
pkt = ctx->iface->enc.get_cx_data(get_alg_priv(ctx), iter);
|
||||
}
|
||||
|
||||
if (pkt && pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||
// If the application has specified a destination area for the
|
||||
// compressed data, and the codec has not placed the data there,
|
||||
// and it fits, copy it.
|
||||
vpx_codec_priv_t *const priv = ctx->priv;
|
||||
char *const dst_buf = (char *)priv->enc.cx_data_dst_buf.buf;
|
||||
|
||||
if (dst_buf &&
|
||||
pkt->data.raw.buf != dst_buf &&
|
||||
pkt->data.raw.sz + priv->enc.cx_data_pad_before +
|
||||
priv->enc.cx_data_pad_after <= priv->enc.cx_data_dst_buf.sz) {
|
||||
vpx_codec_cx_pkt_t *modified_pkt = &priv->enc.cx_data_pkt;
|
||||
|
||||
memcpy(dst_buf + priv->enc.cx_data_pad_before, pkt->data.raw.buf,
|
||||
pkt->data.raw.sz);
|
||||
*modified_pkt = *pkt;
|
||||
modified_pkt->data.raw.buf = dst_buf;
|
||||
modified_pkt->data.raw.sz += priv->enc.cx_data_pad_before +
|
||||
priv->enc.cx_data_pad_after;
|
||||
pkt = modified_pkt;
|
||||
}
|
||||
|
||||
if (dst_buf == pkt->data.raw.buf) {
|
||||
priv->enc.cx_data_dst_buf.buf = dst_buf + pkt->data.raw.sz;
|
||||
priv->enc.cx_data_dst_buf.sz -= pkt->data.raw.sz;
|
||||
}
|
||||
}
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_set_cx_data_buf(vpx_codec_ctx_t *ctx,
|
||||
const vpx_fixed_buf_t *buf,
|
||||
unsigned int pad_before,
|
||||
unsigned int pad_after) {
|
||||
if (!ctx || !ctx->priv)
|
||||
return VPX_CODEC_INVALID_PARAM;
|
||||
|
||||
if (buf) {
|
||||
ctx->priv->enc.cx_data_dst_buf = *buf;
|
||||
ctx->priv->enc.cx_data_pad_before = pad_before;
|
||||
ctx->priv->enc.cx_data_pad_after = pad_after;
|
||||
} else {
|
||||
ctx->priv->enc.cx_data_dst_buf.buf = NULL;
|
||||
ctx->priv->enc.cx_data_dst_buf.sz = 0;
|
||||
ctx->priv->enc.cx_data_pad_before = 0;
|
||||
ctx->priv->enc.cx_data_pad_after = 0;
|
||||
}
|
||||
|
||||
return VPX_CODEC_OK;
|
||||
}
|
||||
|
||||
|
||||
const vpx_image_t *vpx_codec_get_preview_frame(vpx_codec_ctx_t *ctx) {
|
||||
vpx_image_t *img = NULL;
|
||||
|
||||
if (ctx) {
|
||||
if (!ctx->iface || !ctx->priv)
|
||||
ctx->err = VPX_CODEC_ERROR;
|
||||
else if (!(ctx->iface->caps & VPX_CODEC_CAP_ENCODER))
|
||||
ctx->err = VPX_CODEC_INCAPABLE;
|
||||
else if (!ctx->iface->enc.get_preview)
|
||||
ctx->err = VPX_CODEC_INCAPABLE;
|
||||
else
|
||||
img = ctx->iface->enc.get_preview(get_alg_priv(ctx));
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
||||
vpx_fixed_buf_t *vpx_codec_get_global_headers(vpx_codec_ctx_t *ctx) {
|
||||
vpx_fixed_buf_t *buf = NULL;
|
||||
|
||||
if (ctx) {
|
||||
if (!ctx->iface || !ctx->priv)
|
||||
ctx->err = VPX_CODEC_ERROR;
|
||||
else if (!(ctx->iface->caps & VPX_CODEC_CAP_ENCODER))
|
||||
ctx->err = VPX_CODEC_INCAPABLE;
|
||||
else if (!ctx->iface->enc.get_glob_hdrs)
|
||||
ctx->err = VPX_CODEC_INCAPABLE;
|
||||
else
|
||||
buf = ctx->iface->enc.get_glob_hdrs(get_alg_priv(ctx));
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
vpx_codec_err_t vpx_codec_enc_config_set(vpx_codec_ctx_t *ctx,
|
||||
const vpx_codec_enc_cfg_t *cfg) {
|
||||
vpx_codec_err_t res;
|
||||
|
||||
if (!ctx || !ctx->iface || !ctx->priv || !cfg)
|
||||
res = VPX_CODEC_INVALID_PARAM;
|
||||
else if (!(ctx->iface->caps & VPX_CODEC_CAP_ENCODER))
|
||||
res = VPX_CODEC_INCAPABLE;
|
||||
else
|
||||
res = ctx->iface->enc.cfg_set(get_alg_priv(ctx), cfg);
|
||||
|
||||
return SAVE_STATUS(ctx, res);
|
||||
}
|
||||
|
||||
|
||||
int vpx_codec_pkt_list_add(struct vpx_codec_pkt_list *list,
|
||||
const struct vpx_codec_cx_pkt *pkt) {
|
||||
if (list->cnt < list->max) {
|
||||
list->pkts[list->cnt++] = *pkt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
const vpx_codec_cx_pkt_t *vpx_codec_pkt_list_get(struct vpx_codec_pkt_list *list,
|
||||
vpx_codec_iter_t *iter) {
|
||||
const vpx_codec_cx_pkt_t *pkt;
|
||||
|
||||
if (!(*iter)) {
|
||||
*iter = list->pkts;
|
||||
}
|
||||
|
||||
pkt = (const vpx_codec_cx_pkt_t *)*iter;
|
||||
|
||||
if ((size_t)(pkt - list->pkts) < list->cnt)
|
||||
*iter = pkt + 1;
|
||||
else
|
||||
pkt = NULL;
|
||||
|
||||
return pkt;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "vpx/vpx_image.h"
|
||||
#include "vpx/vpx_integer.h"
|
||||
#include "vpx_mem/vpx_mem.h"
|
||||
|
||||
static vpx_image_t *img_alloc_helper(vpx_image_t *img,
|
||||
vpx_img_fmt_t fmt,
|
||||
unsigned int d_w,
|
||||
unsigned int d_h,
|
||||
unsigned int buf_align,
|
||||
unsigned int stride_align,
|
||||
unsigned char *img_data) {
|
||||
unsigned int h, w, s, xcs, ycs, bps;
|
||||
unsigned int stride_in_bytes;
|
||||
int align;
|
||||
|
||||
/* Treat align==0 like align==1 */
|
||||
if (!buf_align)
|
||||
buf_align = 1;
|
||||
|
||||
/* Validate alignment (must be power of 2) */
|
||||
if (buf_align & (buf_align - 1))
|
||||
goto fail;
|
||||
|
||||
/* Treat align==0 like align==1 */
|
||||
if (!stride_align)
|
||||
stride_align = 1;
|
||||
|
||||
/* Validate alignment (must be power of 2) */
|
||||
if (stride_align & (stride_align - 1))
|
||||
goto fail;
|
||||
|
||||
/* Get sample size for this format */
|
||||
switch (fmt) {
|
||||
case VPX_IMG_FMT_RGB32:
|
||||
case VPX_IMG_FMT_RGB32_LE:
|
||||
case VPX_IMG_FMT_ARGB:
|
||||
case VPX_IMG_FMT_ARGB_LE:
|
||||
bps = 32;
|
||||
break;
|
||||
case VPX_IMG_FMT_RGB24:
|
||||
case VPX_IMG_FMT_BGR24:
|
||||
bps = 24;
|
||||
break;
|
||||
case VPX_IMG_FMT_RGB565:
|
||||
case VPX_IMG_FMT_RGB565_LE:
|
||||
case VPX_IMG_FMT_RGB555:
|
||||
case VPX_IMG_FMT_RGB555_LE:
|
||||
case VPX_IMG_FMT_UYVY:
|
||||
case VPX_IMG_FMT_YUY2:
|
||||
case VPX_IMG_FMT_YVYU:
|
||||
bps = 16;
|
||||
break;
|
||||
case VPX_IMG_FMT_I420:
|
||||
case VPX_IMG_FMT_YV12:
|
||||
case VPX_IMG_FMT_VPXI420:
|
||||
case VPX_IMG_FMT_VPXYV12:
|
||||
bps = 12;
|
||||
break;
|
||||
case VPX_IMG_FMT_I422:
|
||||
case VPX_IMG_FMT_I440:
|
||||
bps = 16;
|
||||
break;
|
||||
case VPX_IMG_FMT_I444:
|
||||
bps = 24;
|
||||
break;
|
||||
case VPX_IMG_FMT_I42016:
|
||||
bps = 24;
|
||||
break;
|
||||
case VPX_IMG_FMT_I42216:
|
||||
case VPX_IMG_FMT_I44016:
|
||||
bps = 32;
|
||||
break;
|
||||
case VPX_IMG_FMT_I44416:
|
||||
bps = 48;
|
||||
break;
|
||||
default:
|
||||
bps = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get chroma shift values for this format */
|
||||
switch (fmt) {
|
||||
case VPX_IMG_FMT_I420:
|
||||
case VPX_IMG_FMT_YV12:
|
||||
case VPX_IMG_FMT_VPXI420:
|
||||
case VPX_IMG_FMT_VPXYV12:
|
||||
case VPX_IMG_FMT_I422:
|
||||
case VPX_IMG_FMT_I42016:
|
||||
case VPX_IMG_FMT_I42216:
|
||||
xcs = 1;
|
||||
break;
|
||||
default:
|
||||
xcs = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (fmt) {
|
||||
case VPX_IMG_FMT_I420:
|
||||
case VPX_IMG_FMT_I440:
|
||||
case VPX_IMG_FMT_YV12:
|
||||
case VPX_IMG_FMT_VPXI420:
|
||||
case VPX_IMG_FMT_VPXYV12:
|
||||
case VPX_IMG_FMT_I42016:
|
||||
case VPX_IMG_FMT_I44016:
|
||||
ycs = 1;
|
||||
break;
|
||||
default:
|
||||
ycs = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Calculate storage sizes given the chroma subsampling */
|
||||
align = (1 << xcs) - 1;
|
||||
w = (d_w + align) & ~align;
|
||||
align = (1 << ycs) - 1;
|
||||
h = (d_h + align) & ~align;
|
||||
s = (fmt & VPX_IMG_FMT_PLANAR) ? w : bps * w / 8;
|
||||
s = (s + stride_align - 1) & ~(stride_align - 1);
|
||||
stride_in_bytes = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s * 2 : s;
|
||||
|
||||
/* Allocate the new image */
|
||||
if (!img) {
|
||||
img = (vpx_image_t *)calloc(1, sizeof(vpx_image_t));
|
||||
|
||||
if (!img)
|
||||
goto fail;
|
||||
|
||||
img->self_allocd = 1;
|
||||
} else {
|
||||
memset(img, 0, sizeof(vpx_image_t));
|
||||
}
|
||||
|
||||
img->img_data = img_data;
|
||||
|
||||
if (!img_data) {
|
||||
const uint64_t alloc_size = (fmt & VPX_IMG_FMT_PLANAR) ?
|
||||
(uint64_t)h * s * bps / 8 : (uint64_t)h * s;
|
||||
|
||||
if (alloc_size != (size_t)alloc_size)
|
||||
goto fail;
|
||||
|
||||
img->img_data = (uint8_t *)vpx_memalign(buf_align, (size_t)alloc_size);
|
||||
img->img_data_owner = 1;
|
||||
}
|
||||
|
||||
if (!img->img_data)
|
||||
goto fail;
|
||||
|
||||
img->fmt = fmt;
|
||||
img->bit_depth = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
|
||||
img->w = w;
|
||||
img->h = h;
|
||||
img->x_chroma_shift = xcs;
|
||||
img->y_chroma_shift = ycs;
|
||||
img->bps = bps;
|
||||
|
||||
/* Calculate strides */
|
||||
img->stride[VPX_PLANE_Y] = img->stride[VPX_PLANE_ALPHA] = stride_in_bytes;
|
||||
img->stride[VPX_PLANE_U] = img->stride[VPX_PLANE_V] = stride_in_bytes >> xcs;
|
||||
|
||||
/* Default viewport to entire image */
|
||||
if (!vpx_img_set_rect(img, 0, 0, d_w, d_h))
|
||||
return img;
|
||||
|
||||
fail:
|
||||
vpx_img_free(img);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vpx_image_t *vpx_img_alloc(vpx_image_t *img,
|
||||
vpx_img_fmt_t fmt,
|
||||
unsigned int d_w,
|
||||
unsigned int d_h,
|
||||
unsigned int align) {
|
||||
return img_alloc_helper(img, fmt, d_w, d_h, align, align, NULL);
|
||||
}
|
||||
|
||||
vpx_image_t *vpx_img_wrap(vpx_image_t *img,
|
||||
vpx_img_fmt_t fmt,
|
||||
unsigned int d_w,
|
||||
unsigned int d_h,
|
||||
unsigned int stride_align,
|
||||
unsigned char *img_data) {
|
||||
/* By setting buf_align = 1, we don't change buffer alignment in this
|
||||
* function. */
|
||||
return img_alloc_helper(img, fmt, d_w, d_h, 1, stride_align, img_data);
|
||||
}
|
||||
|
||||
int vpx_img_set_rect(vpx_image_t *img,
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
unsigned int w,
|
||||
unsigned int h) {
|
||||
unsigned char *data;
|
||||
|
||||
if (x + w <= img->w && y + h <= img->h) {
|
||||
img->d_w = w;
|
||||
img->d_h = h;
|
||||
|
||||
/* Calculate plane pointers */
|
||||
if (!(img->fmt & VPX_IMG_FMT_PLANAR)) {
|
||||
img->planes[VPX_PLANE_PACKED] =
|
||||
img->img_data + x * img->bps / 8 + y * img->stride[VPX_PLANE_PACKED];
|
||||
} else {
|
||||
const int bytes_per_sample =
|
||||
(img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
|
||||
data = img->img_data;
|
||||
|
||||
if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) {
|
||||
img->planes[VPX_PLANE_ALPHA] =
|
||||
data + x * bytes_per_sample + y * img->stride[VPX_PLANE_ALPHA];
|
||||
data += img->h * img->stride[VPX_PLANE_ALPHA];
|
||||
}
|
||||
|
||||
img->planes[VPX_PLANE_Y] = data + x * bytes_per_sample +
|
||||
y * img->stride[VPX_PLANE_Y];
|
||||
data += img->h * img->stride[VPX_PLANE_Y];
|
||||
|
||||
if (!(img->fmt & VPX_IMG_FMT_UV_FLIP)) {
|
||||
img->planes[VPX_PLANE_U] =
|
||||
data + (x >> img->x_chroma_shift) * bytes_per_sample +
|
||||
(y >> img->y_chroma_shift) * img->stride[VPX_PLANE_U];
|
||||
data += (img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_U];
|
||||
img->planes[VPX_PLANE_V] =
|
||||
data + (x >> img->x_chroma_shift) * bytes_per_sample +
|
||||
(y >> img->y_chroma_shift) * img->stride[VPX_PLANE_V];
|
||||
} else {
|
||||
img->planes[VPX_PLANE_V] =
|
||||
data + (x >> img->x_chroma_shift) * bytes_per_sample +
|
||||
(y >> img->y_chroma_shift) * img->stride[VPX_PLANE_V];
|
||||
data += (img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_V];
|
||||
img->planes[VPX_PLANE_U] =
|
||||
data + (x >> img->x_chroma_shift) * bytes_per_sample +
|
||||
(y >> img->y_chroma_shift) * img->stride[VPX_PLANE_U];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void vpx_img_flip(vpx_image_t *img) {
|
||||
/* Note: In the calculation pointer adjustment calculation, we want the
|
||||
* rhs to be promoted to a signed type. Section 6.3.1.8 of the ISO C99
|
||||
* standard indicates that if the adjustment parameter is unsigned, the
|
||||
* stride parameter will be promoted to unsigned, causing errors when
|
||||
* the lhs is a larger type than the rhs.
|
||||
*/
|
||||
img->planes[VPX_PLANE_Y] += (signed)(img->d_h - 1) * img->stride[VPX_PLANE_Y];
|
||||
img->stride[VPX_PLANE_Y] = -img->stride[VPX_PLANE_Y];
|
||||
|
||||
img->planes[VPX_PLANE_U] += (signed)((img->d_h >> img->y_chroma_shift) - 1)
|
||||
* img->stride[VPX_PLANE_U];
|
||||
img->stride[VPX_PLANE_U] = -img->stride[VPX_PLANE_U];
|
||||
|
||||
img->planes[VPX_PLANE_V] += (signed)((img->d_h >> img->y_chroma_shift) - 1)
|
||||
* img->stride[VPX_PLANE_V];
|
||||
img->stride[VPX_PLANE_V] = -img->stride[VPX_PLANE_V];
|
||||
|
||||
img->planes[VPX_PLANE_ALPHA] += (signed)(img->d_h - 1) * img->stride[VPX_PLANE_ALPHA];
|
||||
img->stride[VPX_PLANE_ALPHA] = -img->stride[VPX_PLANE_ALPHA];
|
||||
}
|
||||
|
||||
void vpx_img_free(vpx_image_t *img) {
|
||||
if (img) {
|
||||
if (img->img_data && img->img_data_owner)
|
||||
vpx_free(img->img_data);
|
||||
|
||||
if (img->self_allocd)
|
||||
free(img);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The WebM project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "vpx/internal/vpx_psnr.h"
|
||||
|
||||
#define MAX_PSNR 100.0
|
||||
|
||||
double vpx_sse_to_psnr(double samples, double peak, double sse) {
|
||||
if (sse > 0.0) {
|
||||
const double psnr = 10.0 * log10(samples * peak * peak / sse);
|
||||
return psnr > MAX_PSNR ? MAX_PSNR : psnr;
|
||||
} else {
|
||||
return MAX_PSNR;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user