/* * 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.
*/
// Called by encoder_set_config() and encoder_encode() only. Must not be called // by encoder_init() because the `error` paramerer (cpi->common.error) will be // destroyed by vpx_codec_enc_init_ver() after encoder_init() returns an error. // See the "IMPORTANT" comment in vpx_codec_enc_init_ver(). static vpx_codec_err_t update_error_state(
vpx_codec_alg_priv_t *ctx, conststruct vpx_internal_error_info *error) { const vpx_codec_err_t res = error->error_code;
// For formation of valid ARF groups lag_in _frames should be 0 or greater // than the max_gf_interval + 2 if (cfg->g_lag_in_frames > 0 && extra_cfg->max_gf_interval > 0 &&
cfg->g_lag_in_frames < extra_cfg->max_gf_interval + 2) {
ERROR("Set lag in frames to 0 (low delay) or >= (max-gf-interval + 2)");
}
if (cfg->ss_number_layers * cfg->ts_number_layers > VPX_MAX_LAYERS)
ERROR("ss_number_layers * ts_number_layers is out of range"); if (cfg->ts_number_layers > 1) { unsignedint sl, tl; for (sl = 1; sl < cfg->ss_number_layers; ++sl) { for (tl = 1; tl < cfg->ts_number_layers; ++tl) { constint layer = LAYER_IDS_TO_IDX(sl, tl, cfg->ts_number_layers); if (cfg->layer_target_bitrate[layer] <
cfg->layer_target_bitrate[layer - 1])
ERROR("ts_target_bitrate entries are not increasing");
}
}
RANGE_CHECK(cfg, ts_rate_decimator[cfg->ts_number_layers - 1], 1, 1); for (tl = cfg->ts_number_layers - 2; tl > 0; --tl) if (cfg->ts_rate_decimator[tl - 1] != 2 * cfg->ts_rate_decimator[tl])
ERROR("ts_rate_decimator factors are not powers of 2");
}
// VP9 does not support a lower bound on the keyframe interval in // automatic keyframe placement mode. if (cfg->kf_mode != VPX_KF_DISABLED && cfg->kf_min_dist != cfg->kf_max_dist &&
cfg->kf_min_dist > 0)
ERROR( "kf_min_dist not supported in auto mode, use 0 " "or kf_max_dist instead.");
if (cfg->rc_twopass_stats_in.buf == NULL)
ERROR("rc_twopass_stats_in.buf not set.");
if (cfg->rc_twopass_stats_in.sz % packet_sz)
ERROR("rc_twopass_stats_in.sz indicates truncated packet.");
if (cfg->ss_number_layers > 1 || cfg->ts_number_layers > 1) { int i; unsignedint n_packets_per_layer[VPX_SS_MAX_LAYERS] = { 0 };
stats = cfg->rc_twopass_stats_in.buf; for (i = 0; i < n_packets; ++i) { constint layer_id = (int)stats[i].spatial_layer_id; if (layer_id >= 0 && layer_id < (int)cfg->ss_number_layers) {
++n_packets_per_layer[layer_id];
}
}
for (i = 0; i < (int)cfg->ss_number_layers; ++i) { unsignedint layer_id; if (n_packets_per_layer[i] < 2) {
ERROR( "rc_twopass_stats_in requires at least two packets for each " "layer.");
}
static vpx_codec_err_t validate_img(vpx_codec_alg_priv_t *ctx, const vpx_image_t *img) { switch (img->fmt) { case VPX_IMG_FMT_YV12: case VPX_IMG_FMT_I420: case VPX_IMG_FMT_I42016: case VPX_IMG_FMT_NV12: break; case VPX_IMG_FMT_I422: case VPX_IMG_FMT_I444: case VPX_IMG_FMT_I440: if (ctx->cfg.g_profile != (unsignedint)PROFILE_1) {
ERROR( "Invalid image format. I422, I444, I440 images are not supported " "in profile.");
} break; case VPX_IMG_FMT_I42216: case VPX_IMG_FMT_I44416: case VPX_IMG_FMT_I44016: if (ctx->cfg.g_profile != (unsignedint)PROFILE_1 &&
ctx->cfg.g_profile != (unsignedint)PROFILE_3) {
ERROR( "Invalid image format. 16-bit I422, I444, I440 images are " "not supported in profile.");
} break; default:
ERROR( "Invalid image format. Only YV12, I420, I422, I444, I440, NV12 " "images are supported."); break;
}
if (img->d_w != ctx->cfg.g_w || img->d_h != ctx->cfg.g_h)
ERROR("Image size must match encoder init configuration size");
return VPX_CODEC_OK;
}
staticint get_image_bps(const vpx_image_t *img) { switch (img->fmt) { case VPX_IMG_FMT_YV12: case VPX_IMG_FMT_NV12: case VPX_IMG_FMT_I420: return 12; case VPX_IMG_FMT_I422: return 16; case VPX_IMG_FMT_I444: return 24; case VPX_IMG_FMT_I440: return 16; case VPX_IMG_FMT_I42016: return 24; case VPX_IMG_FMT_I42216: return 32; case VPX_IMG_FMT_I44416: return 48; case VPX_IMG_FMT_I44016: return 32; default: assert(0 && "Invalid image format"); break;
} return 0;
}
// Modify the encoder config for the target level. staticvoid config_target_level(VP9EncoderConfig *oxcf) { double max_average_bitrate; // in bits per second int max_over_shoot_pct; constint target_level_index = get_level_index(oxcf->target_level);
// Adjust minimum art-ref distance. // min_gf_interval should be no less than min_altref_distance + 1, // as the encoder may produce bitstream with alt-ref distance being // min_gf_interval - 1. if (oxcf->min_gf_interval <=
(int)vp9_level_defs[target_level_index].min_altref_distance) {
oxcf->min_gf_interval =
(int)vp9_level_defs[target_level_index].min_altref_distance + 1; // If oxcf->max_gf_interval == 0, it will be assigned with a default value // in vp9_rc_set_gf_interval_range(). if (oxcf->max_gf_interval != 0) {
oxcf->max_gf_interval =
VPXMAX(oxcf->max_gf_interval, oxcf->min_gf_interval);
}
}
// Adjust maximum column tiles. if (vp9_level_defs[target_level_index].max_col_tiles <
(1 << oxcf->tile_columns)) { while (oxcf->tile_columns > 0 &&
vp9_level_defs[target_level_index].max_col_tiles <
(1 << oxcf->tile_columns))
--oxcf->tile_columns;
}
}
raw_target_rate =
(unsignedint)((int64_t)oxcf->width * oxcf->height * oxcf->bit_depth * 3 *
oxcf->init_framerate / 1000); // Cap target bitrate to raw rate or 1000Mbps, whichever is less
cfg->rc_target_bitrate =
VPXMIN(VPXMIN(raw_target_rate, cfg->rc_target_bitrate), 1000000);
// TODO(yunqing): The dependencies between row tiles cause error in multi- // threaded encoding. For now, tile_rows is forced to be 0 in this case. // The further fix can be done by adding synchronizations after a tile row // is encoded. But this will hurt multi-threaded encoder performance. So, // it is recommended to use tile-rows=0 while encoding with threads > 1. if (oxcf->max_threads > 1 && oxcf->tile_columns > 0)
oxcf->tile_rows = 0; else
oxcf->tile_rows = extra_cfg->tile_rows;
// The values set here are factors that will be applied to default values // to get the final value used in the two pass code. Hence 1.0 will // match the default behaviour when not using passed in values. // We also apply limits here to prevent the user from applying settings // that make no sense.
cpi->twopass.active_wq_factor =
(double)cfg->active_wq_factor.num / (double)cfg->active_wq_factor.den; if (cpi->twopass.active_wq_factor < 0.25)
cpi->twopass.active_wq_factor = 0.25; elseif (cpi->twopass.active_wq_factor > 16.0)
cpi->twopass.active_wq_factor = 16.0;
cpi->twopass.sr_default_decay_limit =
(double)cfg->sr_default_decay_limit.num /
(double)cfg->sr_default_decay_limit.den; if (cpi->twopass.sr_default_decay_limit < 0.25)
cpi->twopass.sr_default_decay_limit = 0.25; // If the default changes this will need to change. elseif (cpi->twopass.sr_default_decay_limit > 1.33)
cpi->twopass.sr_default_decay_limit = 1.33;
if (cfg->g_w != ctx->cfg.g_w || cfg->g_h != ctx->cfg.g_h) { if (cfg->g_lag_in_frames > 1 || cfg->g_pass != VPX_RC_ONE_PASS)
ERROR("Cannot change width or height after initialization"); // Note: function encoder_set_config() is allowed to be called multiple // times. However, when the original frame width or height is less than two // times of the new frame width or height, a forced key frame should be // used. To make sure the correct detection of a forced key frame, we need // to update the frame width and height only when the actual encoding is // performed. cpi->last_coded_width and cpi->last_coded_height are used to // track the actual coded frame size. if ((ctx->cpi->last_coded_width && ctx->cpi->last_coded_height &&
!valid_ref_frame_size(ctx->cpi->last_coded_width,
ctx->cpi->last_coded_height, cfg->g_w,
cfg->g_h)) ||
(ctx->cpi->initial_width && (int)cfg->g_w > ctx->cpi->initial_width) ||
(ctx->cpi->initial_height &&
(int)cfg->g_h > ctx->cpi->initial_height)) {
force_key = 1;
}
}
// Prevent increasing lag_in_frames. This check is stricter than it needs // to be -- the limit is not increasing past the first lag_in_frames // value, but we don't track the initial config, only the last successful // config. if (cfg->g_lag_in_frames > ctx->cfg.g_lag_in_frames)
ERROR("Cannot increase lag_in_frames");
res = validate_config(ctx, cfg, &ctx->extra_cfg); if (res != VPX_CODEC_OK) return res;
if (ctx->config.enc) { // Update the reference to the config structure to an internal copy.
priv->cfg = *ctx->config.enc;
ctx->config.enc = &priv->cfg;
}
#if CONFIG_REALTIME_ONLY
(void)duration;
deadline = VPX_DL_REALTIME; #else switch (ctx->cfg.g_pass) { case VPX_RC_ONE_PASS: if (deadline > 0) { // Convert duration parameter from stream timebase to microseconds.
VPX_STATIC_ASSERT(TICKS_PER_SEC > 1000000 &&
(TICKS_PER_SEC % 1000000) == 0);
if (duration > UINT64_MAX / (uint64_t)ctx->oxcf.g_timebase_in_ts.num) {
ERROR("duration is too big");
}
uint64_t duration_us = duration *
(uint64_t)ctx->oxcf.g_timebase_in_ts.num /
((uint64_t)ctx->oxcf.g_timebase_in_ts.den *
(TICKS_PER_SEC / 1000000));
// If the deadline is more that the duration this frame is to be shown, // use good quality mode. Otherwise use realtime mode.
new_mode = (deadline > duration_us) ? GOOD : REALTIME;
} else {
new_mode = BEST;
} break; case VPX_RC_FIRST_PASS: break; case VPX_RC_LAST_PASS: new_mode = deadline > 0 ? GOOD : BEST; break;
} #endif// CONFIG_REALTIME_ONLY
// Turn on to test if supplemental superframe data breaks decoding // #define TEST_SUPPLEMENTAL_SUPERFRAME_DATA staticint write_superframe_index(vpx_codec_alg_priv_t *ctx) {
uint8_t marker = 0xc0; unsignedint mask; int mag, index_sz;
#if !CONFIG_REALTIME_ONLY staticINLINE vpx_codec_cx_pkt_t
get_first_pass_stats_pkt(FIRSTPASS_STATS *stats) { // WARNNING: This function assumes that stats will // exist and not be changed until the packet is processed // TODO(angiebird): Refactor the code to avoid using the assumption.
vpx_codec_cx_pkt_t pkt;
pkt.kind = VPX_CODEC_STATS_PKT;
pkt.data.twopass_stats.buf = stats;
pkt.data.twopass_stats.sz = sizeof(*stats); return pkt;
} #endif
if (img != NULL) {
res = validate_img(ctx, img); if (res == VPX_CODEC_OK) { // There's no codec control for multiple alt-refs so check the encoder // instance for its status to determine the compressed data size.
data_sz = ctx->cfg.g_w * ctx->cfg.g_h * get_image_bps(img) / 8 *
(cpi->multi_layer_arf ? 8 : 2); if (data_sz < kMinCompressedSize) data_sz = kMinCompressedSize; if (ctx->cx_data == NULL || ctx->cx_data_sz < data_sz) {
ctx->cx_data_sz = data_sz;
free(ctx->cx_data);
ctx->cx_data = (unsignedchar *)malloc(ctx->cx_data_sz); if (ctx->cx_data == NULL) { return VPX_CODEC_MEM_ERROR;
}
}
#if ULONG_MAX > INT64_MAX if (duration > INT64_MAX) {
vpx_internal_error(&cpi->common.error, VPX_CODEC_INVALID_PARAM, "duration is too big");
} #endif if (pts > INT64_MAX - (int64_t)duration) {
vpx_internal_error(&cpi->common.error, VPX_CODEC_INVALID_PARAM, "relative pts + duration is too big");
}
vpx_codec_pts_t pts_end = pts + (int64_t)duration; if (pts_end > INT64_MAX / timebase_in_ts->num) {
vpx_internal_error(
&cpi->common.error, VPX_CODEC_INVALID_PARAM, "conversion of relative pts + duration to ticks would overflow");
} const int64_t dst_end_time_stamp =
timebase_units_to_ticks(timebase_in_ts, pts_end);
res = image2yuvconfig(img, &sd);
if (sd.y_width != ctx->cfg.g_w || sd.y_height != ctx->cfg.g_h) { /* from vpx_encoder.h for g_w/g_h: "Note that the frames passed as input to the encoder must have this resolution"
*/
ctx->base.err_detail = "Invalid input frame resolution";
res = VPX_CODEC_INVALID_PARAM;
} else { // Store the original flags in to the frame buffer. Will extract the // key frame flag when we actually encode this frame. if (vp9_receive_raw_frame(cpi, flags | ctx->next_frame_flags, &sd,
dst_time_stamp, dst_end_time_stamp)) {
res = update_error_state(ctx, &cpi->common.error);
}
ctx->next_frame_flags = 0;
}
}
/* TODO(webm:1844): this is a minimal check, the underlying codec doesn't * respect the buffer size anyway.
*/ if (cx_data_sz < ctx->cx_data_sz / 2) {
vpx_internal_error(&cpi->common.error, VPX_CODEC_ERROR, "Compressed data buffer too small");
}
}
if (cpi->oxcf.pass == 1 && !cpi->use_svc) { #if !CONFIG_REALTIME_ONLY // compute first pass stats if (img) { int ret;
int64_t dst_time_stamp;
int64_t dst_end_time_stamp;
vpx_codec_cx_pkt_t fps_pkt;
ENCODE_FRAME_RESULT encode_frame_result;
vp9_init_encode_frame_result(&encode_frame_result); // TODO(angiebird): Call vp9_first_pass directly
ret = vp9_get_compressed_data(
cpi, &lib_flags, &size, cx_data, cx_data_sz, &dst_time_stamp,
&dst_end_time_stamp, !img, &encode_frame_result);
assert(size == 0); // There is no compressed data in the first pass
(void)ret;
assert(ret == 0);
fps_pkt = get_first_pass_stats_pkt(&cpi->twopass.this_frame_stats);
vpx_codec_pkt_list_add(&ctx->pkt_list.head, &fps_pkt);
} else { if (!cpi->twopass.first_pass_done) {
vpx_codec_cx_pkt_t fps_pkt;
vp9_end_first_pass(cpi);
fps_pkt = get_first_pass_stats_pkt(&cpi->twopass.total_stats);
vpx_codec_pkt_list_add(&ctx->pkt_list.head, &fps_pkt);
}
} #else// !CONFIG_REALTIME_ONLY
assert(0); #endif// !CONFIG_REALTIME_ONLY
} else {
ENCODE_FRAME_RESULT encode_frame_result;
int64_t dst_time_stamp;
int64_t dst_end_time_stamp;
vp9_init_encode_frame_result(&encode_frame_result); while (cx_data_sz >= ctx->cx_data_sz / 2 &&
-1 != vp9_get_compressed_data(cpi, &lib_flags, &size, cx_data,
cx_data_sz, &dst_time_stamp,
&dst_end_time_stamp, !img,
&encode_frame_result)) { // Pack psnr pkt if (size > 0 && !cpi->use_svc) { // TODO(angiebird): Figure out while we don't need psnr pkt when // use_svc is on
PSNR_STATS psnr; if (vp9_get_psnr(cpi, &psnr)) {
vpx_codec_cx_pkt_t psnr_pkt = get_psnr_pkt(&psnr);
vpx_codec_pkt_list_add(&ctx->pkt_list.head, &psnr_pkt);
}
}
if (size || (cpi->use_svc && cpi->svc.skip_enhancement_layer)) { // Pack invisible frames with the next visible frame if (!cpi->common.show_frame ||
(cpi->use_svc && cpi->svc.spatial_layer_id <
cpi->svc.number_spatial_layers - 1)) { if (ctx->pending_cx_data == NULL) ctx->pending_cx_data = cx_data;
ctx->pending_cx_data_sz += size; if (size)
ctx->pending_frame_sizes[ctx->pending_frame_count++] = size;
ctx->pending_frame_magnitude |= size;
cx_data += size;
cx_data_sz -= size;
pkt.data.frame.width[cpi->svc.spatial_layer_id] = cpi->common.width;
pkt.data.frame.height[cpi->svc.spatial_layer_id] =
cpi->common.height;
pkt.data.frame.spatial_layer_encoded[cpi->svc.spatial_layer_id] =
1 - cpi->svc.drop_spatial_layer[cpi->svc.spatial_layer_id];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.