/* * 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 vp8e_set_config() and vp8e_encode() only. Must not be called // by vp8e_init() because the `error` paramerer (cpi->common.error) will be // destroyed by vpx_codec_enc_init_ver() after vp8e_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;
/* VP8 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 ((int)(stats->count + 0.5) != n_packets - 1)
ERROR("rc_twopass_stats_in missing EOS stats packet");
} #endif
RANGE_CHECK(cfg, ts_number_layers, 1, 5);
if (cfg->ts_number_layers > 1) { unsignedint i;
RANGE_CHECK_HI(cfg, ts_periodicity, 16);
for (i = 1; i < cfg->ts_number_layers; ++i) { if (cfg->ts_target_bitrate[i] <= cfg->ts_target_bitrate[i - 1] &&
cfg->rc_target_bitrate > 0)
ERROR("ts_target_bitrate entries are not strictly increasing");
}
RANGE_CHECK(cfg, ts_rate_decimator[cfg->ts_number_layers - 1], 1, 1); for (i = cfg->ts_number_layers - 2; i > 0; i--) { if (cfg->ts_rate_decimator[i - 1] != 2 * cfg->ts_rate_decimator[i])
ERROR("ts_rate_decimator factors are not powers of 2");
}
#if (CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING) if (cfg->g_threads > (1 << vp8_cfg->token_partitions))
ERROR("g_threads cannot be bigger than number of token partitions"); #endif
// Cap the target rate to 1000 Mbps to avoid some integer overflows in // target bandwidth calculations.
oxcf->target_bandwidth = VPXMIN(cfg.rc_target_bitrate, 1000000);
oxcf->rc_max_intra_bitrate_pct = vp8_cfg.rc_max_intra_bitrate_pct;
oxcf->gf_cbr_boost_pct = vp8_cfg.gf_cbr_boost_pct;
#if CONFIG_MULTI_RES_ENCODING /* When mr_cfg is NULL, oxcf->mr_total_resolutions and oxcf->mr_encoder_id * are both memset to 0, which ensures the correct logic under this * situation.
*/ if (mr_cfg) {
oxcf->mr_total_resolutions = mr_cfg->mr_total_resolutions;
oxcf->mr_encoder_id = mr_cfg->mr_encoder_id;
oxcf->mr_down_sampling_factor.num = mr_cfg->mr_down_sampling_factor.num;
oxcf->mr_down_sampling_factor.den = mr_cfg->mr_down_sampling_factor.den;
oxcf->mr_low_res_mode_info = mr_cfg->mr_low_res_mode_info;
} #else
(void)mr_cfg; #endif
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"); if ((ctx->cpi->initial_width && (int)cfg->g_w > ctx->cpi->initial_width) ||
(ctx->cpi->initial_height && (int)cfg->g_h > ctx->cpi->initial_height))
ERROR("Cannot increase width or height larger than their initial values");
}
/* 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->vp8_cfg, 0); 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) /* Use best quality mode if no deadline is given. */
new_qc = MODE_BESTQUALITY;
if (deadline) { /* 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->timestamp_ratio.num) {
ERROR("duration is too big");
}
uint64_t duration_us =
duration * (uint64_t)ctx->timestamp_ratio.num /
((uint64_t)ctx->timestamp_ratio.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_qc = (deadline > duration_us) ? MODE_GOODQUALITY : MODE_REALTIME;
}
if (flags &
(VP8_EFLAG_NO_REF_LAST | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF)) { int ref = 7;
if (flags & VP8_EFLAG_NO_REF_LAST) ref ^= VP8_LAST_FRAME;
if (flags & VP8_EFLAG_NO_REF_GF) ref ^= VP8_GOLD_FRAME;
if (flags & VP8_EFLAG_NO_REF_ARF) ref ^= VP8_ALTR_FRAME;
vp8_use_as_reference(ctx->cpi, ref);
}
if (flags &
(VP8_EFLAG_NO_UPD_LAST | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
VP8_EFLAG_FORCE_GF | VP8_EFLAG_FORCE_ARF)) { int upd = 7;
if (flags & VP8_EFLAG_NO_UPD_LAST) upd ^= VP8_LAST_FRAME;
if (flags & VP8_EFLAG_NO_UPD_GF) upd ^= VP8_GOLD_FRAME;
if (flags & VP8_EFLAG_NO_UPD_ARF) upd ^= VP8_ALTR_FRAME;
vp8_update_reference(ctx->cpi, upd);
}
if (flags & VP8_EFLAG_NO_UPD_ENTROPY) {
vp8_update_entropy(ctx->cpi, 0);
}
return VPX_CODEC_OK;
}
static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx, const vpx_image_t *img, vpx_codec_pts_t pts, unsignedlong duration,
vpx_enc_frame_flags_t enc_flags,
vpx_enc_deadline_t deadline) { volatile vpx_codec_err_t res = VPX_CODEC_OK; // Make a copy as volatile to avoid -Wclobbered with longjmp. volatile vpx_enc_frame_flags_t flags = enc_flags; volatile vpx_codec_pts_t pts_val = pts;
if (!ctx->cfg.rc_target_bitrate) { #if CONFIG_MULTI_RES_ENCODING if (!ctx->cpi) return VPX_CODEC_ERROR; if (ctx->cpi->oxcf.mr_total_resolutions > 1) {
LOWER_RES_FRAME_INFO *low_res_frame_info =
(LOWER_RES_FRAME_INFO *)ctx->cpi->oxcf.mr_low_res_mode_info; if (!low_res_frame_info) return VPX_CODEC_ERROR;
low_res_frame_info->skip_encoding_prev_stream = 1; if (ctx->cpi->oxcf.mr_encoder_id == 0)
low_res_frame_info->skip_encoding_base_stream = 1;
} #endif return res;
}
if (img) res = validate_img(ctx, img);
if (!res) res = validate_config(ctx, &ctx->cfg, &ctx->vp8_cfg, 1);
if (!res) res = pick_quickcompress_mode(ctx, duration, deadline);
vpx_codec_pkt_list_init(&ctx->pkt_list);
// If no flags are set in the encode call, then use the frame flags as // defined via the control function: vp8e_set_frame_flags. if (!flags) {
flags = ctx->control_frame_flags;
}
ctx->control_frame_flags = 0;
if (!res) res = set_reference_and_update(ctx, flags);
/* Initialize the encoder instance on the first frame*/ if (!res && ctx->cpi) { unsignedint lib_flags;
int64_t dst_time_stamp, dst_end_time_stamp;
size_t size, cx_data_sz; unsignedchar *cx_data; unsignedchar *cx_data_end; int comp_data_state = 0;
if (setjmp(ctx->cpi->common.error.jmp)) {
ctx->cpi->common.error.setjmp = 0;
res = update_error_state(ctx, &ctx->cpi->common.error);
vpx_clear_system_state(); return res;
}
ctx->cpi->common.error.setjmp = 1;
/* Set up internal flags */ if (ctx->base.init_flags & VPX_CODEC_USE_PSNR) {
((VP8_COMP *)ctx->cpi)->b_calculate_psnr = 1;
}
if (ctx->base.init_flags & VPX_CODEC_USE_OUTPUT_PARTITION) {
((VP8_COMP *)ctx->cpi)->output_partition = 1;
}
/* Convert API flags to internal codec lib flags */
lib_flags = (flags & VPX_EFLAG_FORCE_KF) ? FRAMEFLAGS_KEY : 0;
if (img != NULL) {
YV12_BUFFER_CONFIG sd;
if (!ctx->pts_offset_initialized) {
ctx->pts_offset = pts_val;
ctx->pts_offset_initialized = 1;
} if (pts_val < ctx->pts_offset) {
vpx_internal_error(&ctx->cpi->common.error, VPX_CODEC_INVALID_PARAM, "pts is smaller than initial pts");
}
pts_val -= ctx->pts_offset; if (pts_val > INT64_MAX / ctx->timestamp_ratio.num) {
vpx_internal_error(
&ctx->cpi->common.error, VPX_CODEC_INVALID_PARAM, "conversion of relative pts to ticks would overflow");
}
dst_time_stamp =
pts_val * ctx->timestamp_ratio.num / ctx->timestamp_ratio.den; #if ULONG_MAX > INT64_MAX if (duration > INT64_MAX) {
vpx_internal_error(&ctx->cpi->common.error, VPX_CODEC_INVALID_PARAM, "duration is too big");
} #endif if (pts_val > INT64_MAX - (int64_t)duration) {
vpx_internal_error(&ctx->cpi->common.error, VPX_CODEC_INVALID_PARAM, "relative pts + duration is too big");
}
vpx_codec_pts_t pts_end = pts_val + (int64_t)duration; if (pts_end > INT64_MAX / ctx->timestamp_ratio.num) {
vpx_internal_error(
&ctx->cpi->common.error, VPX_CODEC_INVALID_PARAM, "conversion of relative pts + duration to ticks would overflow");
}
dst_end_time_stamp =
pts_end * ctx->timestamp_ratio.num / ctx->timestamp_ratio.den;
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 { if (vp8_receive_raw_frame(ctx->cpi, ctx->next_frame_flag | lib_flags,
&sd, dst_time_stamp, dst_end_time_stamp)) {
VP8_COMP *cpi = (VP8_COMP *)ctx->cpi;
res = update_error_state(ctx, &cpi->common.error);
}
}
/* reset for next frame */
ctx->next_frame_flag = 0;
}
/* Add the frame packet to the list of returned packets. */
round = (vpx_codec_pts_t)ctx->timestamp_ratio.num / 2; if (round > 0) --round;
delta = (dst_end_time_stamp - dst_time_stamp);
pkt.kind = VPX_CODEC_CX_FRAME_PKT;
pkt.data.frame.pts =
(dst_time_stamp * ctx->timestamp_ratio.den + round) /
ctx->timestamp_ratio.num +
ctx->pts_offset;
pkt.data.frame.duration =
(unsignedlong)((delta * ctx->timestamp_ratio.den + round) /
ctx->timestamp_ratio.num);
pkt.data.frame.flags = lib_flags << 16;
pkt.data.frame.width[0] = cpi->common.Width;
pkt.data.frame.height[0] = cpi->common.Height;
pkt.data.frame.spatial_layer_encoded[0] = 1;
if (lib_flags & FRAMEFLAGS_KEY) {
pkt.data.frame.flags |= VPX_FRAME_IS_KEY;
}
if (!cpi->common.show_frame) {
pkt.data.frame.flags |= VPX_FRAME_IS_INVISIBLE;
/* This timestamp should be as close as possible to the * prior PTS so that if a decoder uses pts to schedule when * to do this, we start right after last frame was decoded. * Invisible frames have no duration.
*/
pkt.data.frame.pts =
((cpi->last_time_stamp_seen * ctx->timestamp_ratio.den + round) /
ctx->timestamp_ratio.num) +
ctx->pts_offset + 1;
pkt.data.frame.duration = 0;
}
if (cpi->droppable) pkt.data.frame.flags |= VPX_FRAME_IS_DROPPABLE;
if (cpi->output_partition) { int i; constint num_partitions =
(1 << cpi->common.multi_token_partition) + 1;
pkt.data.frame.flags |= VPX_FRAME_IS_FRAGMENT;
for (i = 0; i < num_partitions; ++i) { #if CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING
pkt.data.frame.buf = cpi->partition_d[i]; #else
pkt.data.frame.buf = cx_data;
cx_data += cpi->partition_sz[i];
cx_data_sz -= cpi->partition_sz[i]; #endif
pkt.data.frame.sz = cpi->partition_sz[i];
pkt.data.frame.partition_id = i; /* don't set the fragment bit for the last partition */ if (i == (num_partitions - 1)) {
pkt.data.frame.flags &= ~VPX_FRAME_IS_FRAGMENT;
}
vpx_codec_pkt_list_add(&ctx->pkt_list.head, &pkt);
} #if CONFIG_REALTIME_ONLY & CONFIG_ONTHEFLY_BITPACKING /* In lagged mode the encoder can buffer multiple frames. * We don't want this in partitioned output because * partitions are spread all over the output buffer. * So, force an exit!
*/
cx_data_sz -= ctx->cx_data_sz / 2; #endif
} else {
pkt.data.frame.buf = cx_data;
pkt.data.frame.sz = size;
pkt.data.frame.partition_id = -1;
vpx_codec_pkt_list_add(&ctx->pkt_list.head, &pkt);
cx_data += size;
cx_data_sz -= size;
}
}
}
ctx->cpi->common.error.setjmp = 0;
}
if (data) { int res;
vpx_scaling_mode_t scalemode = *(vpx_scaling_mode_t *)data;
res = vp8_set_internal_size(ctx->cpi, scalemode.h_scaling_mode,
scalemode.v_scaling_mode);
if (!res) { /*force next frame a key frame to effect scaling mode */
ctx->next_frame_flag |= FRAMEFLAGS_KEY; return VPX_CODEC_OK;
} else { return VPX_CODEC_INVALID_PARAM;
}
} else { return VPX_CODEC_INVALID_PARAM;
}
}
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.