diff options
author | James Zern <jzern@google.com> | 2020-01-15 17:20:52 -0800 |
---|---|---|
committer | James Zern <jzern@google.com> | 2020-01-17 21:00:28 -0800 |
commit | e49e9629abf962ea496d4761caf49f880a5a8924 (patch) | |
tree | 3ad62ed60696295ea2a29cbf26bf0d3ae25a30d5 | |
parent | 148886ab41fed444b221d84a9b6dd67d29cacd75 (diff) | |
download | libvpx-e49e9629abf962ea496d4761caf49f880a5a8924.tar.gz |
libvpx: cherry-pick timestamp overflow fixes | DO NOT MERGE
(files affecting the core library only)
* https://chromium-review.googlesource.com/q/Ic6f5eacd9a7c21b95707d31ee2da77dc8ac7dccf
(11de1b838 Fix timestamp overflow issues)
* https://chromium-review.googlesource.com/q/I1d8a6e263fddb9e4cc6265a313011a18d18bbf9e
(04383393e Add missing typecast and re-enable timestamp test)
Bug: b/144361434
Test: upstream test coverage, repro test case on coral-userdebug
Change-Id: I094862039d64288ad26b9db35c5dee9bd22083df
-rw-r--r-- | README.version | 5 | ||||
-rw-r--r-- | libvpx/vp8/encoder/onyx_int.h | 3 | ||||
-rw-r--r-- | libvpx/vp8/vp8_cx_iface.c | 68 | ||||
-rw-r--r-- | libvpx/vp9/vp9_cx_iface.c | 74 | ||||
-rw-r--r-- | libvpx/vpx_util/vpx_timestamp.h | 45 |
5 files changed, 161 insertions, 34 deletions
diff --git a/README.version b/README.version index 6bc385584..dfc6a897b 100644 --- a/README.version +++ b/README.version @@ -3,3 +3,8 @@ Version: v1.8.0 BugComponent: 42195 Owners: johannkoenig Local Modifications: + - cherry-pick: + * https://chromium-review.googlesource.com/q/Ic6f5eacd9a7c21b95707d31ee2da77dc8ac7dccf + (11de1b838 Fix timestamp overflow issues) + * https://chromium-review.googlesource.com/q/I1d8a6e263fddb9e4cc6265a313011a18d18bbf9e + (04383393e Add missing typecast and re-enable timestamp test) diff --git a/libvpx/vp8/encoder/onyx_int.h b/libvpx/vp8/encoder/onyx_int.h index 603de8bcb..7d1c6ab73 100644 --- a/libvpx/vp8/encoder/onyx_int.h +++ b/libvpx/vp8/encoder/onyx_int.h @@ -57,6 +57,9 @@ extern "C" { #define VP8_TEMPORAL_ALT_REF !CONFIG_REALTIME_ONLY +/* vp8 uses 10,000,000 ticks/second as time stamp */ +#define TICKS_PER_SEC 10000000 + typedef struct { int kf_indicated; unsigned int frames_since_key; diff --git a/libvpx/vp8/vp8_cx_iface.c b/libvpx/vp8/vp8_cx_iface.c index d01d2095f..1394f2adc 100644 --- a/libvpx/vp8/vp8_cx_iface.c +++ b/libvpx/vp8/vp8_cx_iface.c @@ -18,6 +18,7 @@ #include "vpx_mem/vpx_mem.h" #include "vpx_ports/system_state.h" #include "vpx_ports/vpx_once.h" +#include "vpx_util/vpx_timestamp.h" #include "vp8/encoder/onyx_int.h" #include "vpx/vp8cx.h" #include "vp8/encoder/firstpass.h" @@ -75,6 +76,9 @@ struct vpx_codec_alg_priv { vpx_codec_priv_t base; vpx_codec_enc_cfg_t cfg; struct vp8_extracfg vp8_cfg; + vpx_rational64_t timestamp_ratio; + vpx_codec_pts_t pts_offset; + unsigned char pts_offset_initialized; VP8_CONFIG oxcf; struct VP8_COMP *cpi; unsigned char *cx_data; @@ -127,6 +131,22 @@ static vpx_codec_err_t update_error_state( if (!!((p)->memb) != (p)->memb) ERROR(#memb " expected boolean"); \ } while (0) +#if defined(_MSC_VER) +#define COMPILE_TIME_ASSERT(boolexp) \ + do { \ + char compile_time_assert[(boolexp) ? 1 : -1]; \ + (void)compile_time_assert; \ + } while (0) +#else /* !_MSC_VER */ +#define COMPILE_TIME_ASSERT(boolexp) \ + do { \ + struct { \ + unsigned int compile_time_assert : (boolexp) ? 1 : -1; \ + } compile_time_assert; \ + (void)compile_time_assert; \ + } while (0) +#endif /* _MSC_VER */ + static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, const vpx_codec_enc_cfg_t *cfg, const struct vp8_extracfg *vp8_cfg, @@ -654,6 +674,12 @@ static vpx_codec_err_t vp8e_init(vpx_codec_ctx_t *ctx, res = validate_config(priv, &priv->cfg, &priv->vp8_cfg, 0); if (!res) { + priv->pts_offset_initialized = 0; + priv->timestamp_ratio.den = priv->cfg.g_timebase.den; + priv->timestamp_ratio.num = (int64_t)priv->cfg.g_timebase.num; + priv->timestamp_ratio.num *= TICKS_PER_SEC; + reduce_ratio(&priv->timestamp_ratio); + set_vp8e_config(&priv->oxcf, priv->cfg, priv->vp8_cfg, mr_cfg); priv->cpi = vp8_create_compressor(&priv->oxcf); if (!priv->cpi) res = VPX_CODEC_MEM_ERROR; @@ -718,12 +744,14 @@ static void pick_quickcompress_mode(vpx_codec_alg_priv_t *ctx, new_qc = MODE_BESTQUALITY; if (deadline) { + /* Convert duration parameter from stream timebase to microseconds */ uint64_t duration_us; - /* Convert duration parameter from stream timebase to microseconds */ - duration_us = (uint64_t)duration * 1000000 * - (uint64_t)ctx->cfg.g_timebase.num / - (uint64_t)ctx->cfg.g_timebase.den; + COMPILE_TIME_ASSERT(TICKS_PER_SEC > 1000000 && + (TICKS_PER_SEC % 1000000) == 0); + + duration_us = duration * (uint64_t)ctx->timestamp_ratio.num / + (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. @@ -802,6 +830,7 @@ static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx, 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 @@ -822,6 +851,12 @@ static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx, if (!res) res = validate_config(ctx, &ctx->cfg, &ctx->vp8_cfg, 1); + if (!ctx->pts_offset_initialized) { + ctx->pts_offset = pts_val; + ctx->pts_offset_initialized = 1; + } + pts_val -= ctx->pts_offset; + pick_quickcompress_mode(ctx, duration, deadline); vpx_codec_pkt_list_init(&ctx->pkt_list); @@ -871,11 +906,10 @@ static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx, /* Convert API flags to internal codec lib flags */ lib_flags = (flags & VPX_EFLAG_FORCE_KF) ? FRAMEFLAGS_KEY : 0; - /* vp8 use 10,000,000 ticks/second as time stamp */ dst_time_stamp = - pts * 10000000 * ctx->cfg.g_timebase.num / ctx->cfg.g_timebase.den; - dst_end_time_stamp = (pts + duration) * 10000000 * ctx->cfg.g_timebase.num / - ctx->cfg.g_timebase.den; + pts_val * ctx->timestamp_ratio.num / ctx->timestamp_ratio.den; + dst_end_time_stamp = (pts_val + (int64_t)duration) * + ctx->timestamp_ratio.num / ctx->timestamp_ratio.den; if (img != NULL) { res = image2yuvconfig(img, &sd); @@ -914,15 +948,17 @@ static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx, VP8_COMP *cpi = (VP8_COMP *)ctx->cpi; /* Add the frame packet to the list of returned packets. */ - round = (vpx_codec_pts_t)10000000 * ctx->cfg.g_timebase.num / 2 - 1; + 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->cfg.g_timebase.den + round) / - ctx->cfg.g_timebase.num / 10000000; + (dst_time_stamp * ctx->timestamp_ratio.den + round) / + ctx->timestamp_ratio.num + + ctx->pts_offset; pkt.data.frame.duration = - (unsigned long)((delta * ctx->cfg.g_timebase.den + round) / - ctx->cfg.g_timebase.num / 10000000); + (unsigned long)((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; @@ -941,9 +977,9 @@ static vpx_codec_err_t vp8e_encode(vpx_codec_alg_priv_t *ctx, * Invisible frames have no duration. */ pkt.data.frame.pts = - ((cpi->last_time_stamp_seen * ctx->cfg.g_timebase.den + round) / - ctx->cfg.g_timebase.num / 10000000) + - 1; + ((cpi->last_time_stamp_seen * ctx->timestamp_ratio.den + round) / + ctx->timestamp_ratio.num) + + ctx->pts_offset + 1; pkt.data.frame.duration = 0; } diff --git a/libvpx/vp9/vp9_cx_iface.c b/libvpx/vp9/vp9_cx_iface.c index 85f83a662..374f862f9 100644 --- a/libvpx/vp9/vp9_cx_iface.c +++ b/libvpx/vp9/vp9_cx_iface.c @@ -15,6 +15,7 @@ #include "vpx/vpx_encoder.h" #include "vpx_ports/vpx_once.h" #include "vpx_ports/system_state.h" +#include "vpx_util/vpx_timestamp.h" #include "vpx/internal/vpx_codec_internal.h" #include "./vpx_version.h" #include "vp9/encoder/vp9_encoder.h" @@ -94,6 +95,9 @@ struct vpx_codec_alg_priv { vpx_codec_priv_t base; vpx_codec_enc_cfg_t cfg; struct vp9_extracfg extra_cfg; + vpx_rational64_t timestamp_ratio; + vpx_codec_pts_t pts_offset; + unsigned char pts_offset_initialized; VP9EncoderConfig oxcf; VP9_COMP *cpi; unsigned char *cx_data; @@ -151,6 +155,22 @@ static vpx_codec_err_t update_error_state( if (!!((p)->memb) != (p)->memb) ERROR(#memb " expected boolean"); \ } while (0) +#if defined(_MSC_VER) +#define COMPILE_TIME_ASSERT(boolexp) \ + do { \ + char compile_time_assert[(boolexp) ? 1 : -1]; \ + (void)compile_time_assert; \ + } while (0) +#else // !_MSC_VER +#define COMPILE_TIME_ASSERT(boolexp) \ + do { \ + struct { \ + unsigned int compile_time_assert : (boolexp) ? 1 : -1; \ + } compile_time_assert; \ + (void)compile_time_assert; \ + } while (0) +#endif // _MSC_VER + static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx, const vpx_codec_enc_cfg_t *cfg, const struct vp9_extracfg *extra_cfg) { @@ -915,6 +935,12 @@ static vpx_codec_err_t encoder_init(vpx_codec_ctx_t *ctx, res = validate_config(priv, &priv->cfg, &priv->extra_cfg); if (res == VPX_CODEC_OK) { + priv->pts_offset_initialized = 0; + priv->timestamp_ratio.den = priv->cfg.g_timebase.den; + priv->timestamp_ratio.num = (int64_t)priv->cfg.g_timebase.num; + priv->timestamp_ratio.num *= TICKS_PER_SEC; + reduce_ratio(&priv->timestamp_ratio); + set_encoder_config(&priv->oxcf, &priv->cfg, &priv->extra_cfg); #if CONFIG_VP9_HIGHBITDEPTH priv->oxcf.use_highbitdepth = @@ -951,12 +977,14 @@ static void pick_quickcompress_mode(vpx_codec_alg_priv_t *ctx, switch (ctx->cfg.g_pass) { case VPX_RC_ONE_PASS: if (deadline > 0) { - const vpx_codec_enc_cfg_t *const cfg = &ctx->cfg; - // Convert duration parameter from stream timebase to microseconds. - const uint64_t duration_us = (uint64_t)duration * 1000000 * - (uint64_t)cfg->g_timebase.num / - (uint64_t)cfg->g_timebase.den; + uint64_t duration_us; + + COMPILE_TIME_ASSERT(TICKS_PER_SEC > 1000000 && + (TICKS_PER_SEC % 1000000) == 0); + + duration_us = duration * (uint64_t)ctx->timestamp_ratio.num / + (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. @@ -1040,15 +1068,16 @@ static int write_superframe_index(vpx_codec_alg_priv_t *ctx) { return index_sz; } -static int64_t timebase_units_to_ticks(const vpx_rational_t *timebase, +static int64_t timebase_units_to_ticks(const vpx_rational64_t *timestamp_ratio, int64_t n) { - return n * TICKS_PER_SEC * timebase->num / timebase->den; + return n * timestamp_ratio->num / timestamp_ratio->den; } -static int64_t ticks_to_timebase_units(const vpx_rational_t *timebase, +static int64_t ticks_to_timebase_units(const vpx_rational64_t *timestamp_ratio, int64_t n) { - const int64_t round = (int64_t)TICKS_PER_SEC * timebase->num / 2 - 1; - return (n * timebase->den + round) / timebase->num / TICKS_PER_SEC; + int64_t round = timestamp_ratio->num / 2; + if (round > 0) --round; + return (n * timestamp_ratio->den + round) / timestamp_ratio->num; } static vpx_codec_frame_flags_t get_frame_pkt_flags(const VP9_COMP *cpi, @@ -1078,7 +1107,7 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, volatile vpx_codec_err_t res = VPX_CODEC_OK; volatile vpx_enc_frame_flags_t flags = enc_flags; VP9_COMP *const cpi = ctx->cpi; - const vpx_rational_t *const timebase = &ctx->cfg.g_timebase; + const vpx_rational64_t *const timestamp_ratio = &ctx->timestamp_ratio; size_t data_sz; if (cpi == NULL) return VPX_CODEC_INVALID_PARAM; @@ -1112,6 +1141,12 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, } } + if (!ctx->pts_offset_initialized) { + ctx->pts_offset = pts; + ctx->pts_offset_initialized = 1; + } + pts -= ctx->pts_offset; + pick_quickcompress_mode(ctx, duration, deadline); vpx_codec_pkt_list_init(&ctx->pkt_list); @@ -1144,13 +1179,13 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, if (res == VPX_CODEC_OK) { unsigned int lib_flags = 0; YV12_BUFFER_CONFIG sd; - int64_t dst_time_stamp = timebase_units_to_ticks(timebase, pts); + int64_t dst_time_stamp = timebase_units_to_ticks(timestamp_ratio, pts); int64_t dst_end_time_stamp = - timebase_units_to_ticks(timebase, pts + duration); + timebase_units_to_ticks(timestamp_ratio, pts + duration); size_t size, cx_data_sz; unsigned char *cx_data; - cpi->svc.timebase_fac = timebase_units_to_ticks(timebase, 1); + cpi->svc.timebase_fac = timebase_units_to_ticks(timestamp_ratio, 1); cpi->svc.time_stamp_superframe = dst_time_stamp; // Set up internal flags @@ -1213,9 +1248,10 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, if (ctx->output_cx_pkt_cb.output_cx_pkt) { pkt.kind = VPX_CODEC_CX_FRAME_PKT; pkt.data.frame.pts = - ticks_to_timebase_units(timebase, dst_time_stamp); + ticks_to_timebase_units(timestamp_ratio, dst_time_stamp) + + ctx->pts_offset; pkt.data.frame.duration = (unsigned long)ticks_to_timebase_units( - timebase, dst_end_time_stamp - dst_time_stamp); + timestamp_ratio, dst_end_time_stamp - dst_time_stamp); pkt.data.frame.flags = get_frame_pkt_flags(cpi, lib_flags); pkt.data.frame.buf = ctx->pending_cx_data; pkt.data.frame.sz = size; @@ -1231,9 +1267,11 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, // Add the frame packet to the list of returned packets. pkt.kind = VPX_CODEC_CX_FRAME_PKT; - pkt.data.frame.pts = ticks_to_timebase_units(timebase, dst_time_stamp); + pkt.data.frame.pts = + ticks_to_timebase_units(timestamp_ratio, dst_time_stamp) + + ctx->pts_offset; pkt.data.frame.duration = (unsigned long)ticks_to_timebase_units( - timebase, dst_end_time_stamp - dst_time_stamp); + timestamp_ratio, dst_end_time_stamp - dst_time_stamp); pkt.data.frame.flags = get_frame_pkt_flags(cpi, lib_flags); pkt.data.frame.width[cpi->svc.spatial_layer_id] = cpi->common.width; pkt.data.frame.height[cpi->svc.spatial_layer_id] = cpi->common.height; diff --git a/libvpx/vpx_util/vpx_timestamp.h b/libvpx/vpx_util/vpx_timestamp.h new file mode 100644 index 000000000..c210de5e5 --- /dev/null +++ b/libvpx/vpx_util/vpx_timestamp.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifndef VPX_VPX_UTIL_VPX_TIMESTAMP_H_ +#define VPX_VPX_UTIL_VPX_TIMESTAMP_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Rational Number with an int64 numerator +typedef struct vpx_rational64 { + int64_t num; // fraction numerator + int den; // fraction denominator +} vpx_rational64_t; // alias for struct vpx_rational64_t + +static INLINE int gcd(int64_t a, int b) { + int r; // remainder + while (b > 0) { + r = (int)(a % b); + a = b; + b = r; + } + + return (int)a; +} + +static INLINE void reduce_ratio(vpx_rational64_t *ratio) { + const int denom = gcd(ratio->num, ratio->den); + ratio->num /= denom; + ratio->den /= denom; +} + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // VPX_VPX_UTIL_VPX_TIMESTAMP_H_ |