Add --step for temporal subsampling If --step is set to a value N, then every Nth frame will be encoded. The input stream "I" should be treated as having the following filters applied to it, in this order: 1. I is fed to the skip filter. If --skip is set, the first N frames are dropped, producing a new stream I'. 2. I' is fed to the step filter. If --step is set, every Nth frame is output into a new stream I''. 3. I'' is fed through the limit filter. If --limit is set, then this filter lets through the first N frames and drops the rest. This final stream is sent to the encoder. Examples (assuming the first frame is "frame 1"): --limit=5 --skip=0 --step=1 Frames 1 through 5 are encoded. --limit=5 --skip=5 --step=1 Frames 6 through 10 are encoded. --limit=5 --skip=5 --step=2 Frames 6, 8, 10, 12, and 14 are encoded. Change-Id: I08448a1e720904ae4b1cf96e99e8e6ec6678ce09
diff --git a/CMakeLists.txt b/CMakeLists.txt index ce84d45..9be8f4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -155,6 +155,7 @@ "${AOM_ROOT}/common/warnings.h" "${AOM_ROOT}/common/y4minput.c" "${AOM_ROOT}/common/y4minput.h" + "${AOM_ROOT}/common/stream_iter.c" "${AOM_ROOT}/examples/encoder_util.h" "${AOM_ROOT}/examples/encoder_util.c")
diff --git a/apps/aomenc.c b/apps/aomenc.c index 3ec6bdc..2a05757 100644 --- a/apps/aomenc.c +++ b/apps/aomenc.c
@@ -15,6 +15,7 @@ #include <limits.h> #include <math.h> #include <stdarg.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -32,6 +33,7 @@ #include "aom_ports/mem_ops.h" #include "common/args.h" #include "common/ivfenc.h" +#include "common/stream_iter.h" #include "common/tools_common.h" #include "common/warnings.h" @@ -103,20 +105,6 @@ va_end(ap); } -static int read_frame(struct AvxInputContext *input_ctx, aom_image_t *img) { - FILE *f = input_ctx->file; - y4m_input *y4m = &input_ctx->y4m; - int shortread = 0; - - if (input_ctx->file_type == FILE_TYPE_Y4M) { - if (y4m_input_fetch_frame(y4m, f, img) < 1) return 0; - } else { - shortread = read_yuv_frame(input_ctx, img); - } - - return !shortread; -} - static int file_is_y4m(const char detect[4]) { if (memcmp(detect, "YUV4", 4) == 0) { return 1; @@ -156,6 +144,8 @@ ARG_DEF(NULL, "limit", 1, "Stop encoding after n input frames"); static const arg_def_t skip = ARG_DEF(NULL, "skip", 1, "Skip the first n input frames"); +static const arg_def_t step = ARG_DEF( + NULL, "step", 1, "Encode every n-th frame (after the --skip frames)"); static const arg_def_t good_dl = ARG_DEF(NULL, "good", 0, "Use Good Quality Deadline"); static const arg_def_t rt_dl = @@ -218,6 +208,7 @@ &fpf_name, &limit, &skip, + &step, &good_dl, &rt_dl, &quietarg, @@ -1195,6 +1186,7 @@ global->passes = 0; global->color_type = I420; global->csp = AOM_CSP_UNKNOWN; + global->step_frames = 1; int cfg_included = 0; init_config(&global->encoder_config); @@ -1261,7 +1253,12 @@ global->limit = arg_parse_uint(&arg); else if (arg_match(&arg, &skip, argi)) global->skip_frames = arg_parse_uint(&arg); - else if (arg_match(&arg, &psnrarg, argi)) + else if (arg_match(&arg, &step, argi)) { + global->step_frames = arg_parse_uint(&arg); + if (global->step_frames == 0) { + die("--step must be positive"); + } + } else if (arg_match(&arg, &psnrarg, argi)) global->show_psnr = 1; else if (arg_match(&arg, &recontest, argi)) global->test_decode = arg_parse_enum_or_int(&arg); @@ -1822,6 +1819,25 @@ } } +static void print_frames_to_code(FILE *f, struct stream_state *stream, + struct AvxEncoderConfig *global) { + const struct aom_codec_enc_cfg *cfg = &stream->config.cfg; + int num_frames = cfg->g_limit / global->step_frames; + // E.g., if step == 3 and limit == 4, then 2 frames are encoded. + // Similar for all cases where limit % step != 0. + if (cfg->g_limit % global->step_frames != 0) { + ++num_frames; + } + fprintf(f, "Frames to be coded : %d - %d (%d frames", + global->skip_frames, global->skip_frames + cfg->g_limit - 1, + num_frames); + if (global->step_frames == 1) { + fprintf(f, ")\n"); + } else { + fprintf(f, ", step size:%d)\n", global->step_frames); + } +} + static void show_stream_config(struct stream_state *stream, struct AvxEncoderConfig *global, struct AvxInputContext *input) { @@ -1840,9 +1856,7 @@ (double)global->framerate.num / (double)global->framerate.den, input->bit_depth); fprintf(stdout, "Number of threads : %d\n", cfg->g_threads); - fprintf(stdout, "Frames to be coded : %d - %d (%d frames)\n", - global->skip_frames, global->skip_frames + cfg->g_limit - 1, - cfg->g_limit); + print_frames_to_code(stdout, stream, global); fprintf(stdout, "Operating bit depth : %d\n", cfg->g_bit_depth); fprintf(stdout, "Num of coding passes : %d\n", global->passes); #if !CONFIG_SINGLEPASS @@ -2366,7 +2380,6 @@ int allocated_raw_shift = 0; int do_16bit_internal = 0; int input_shift = 0; - int frame_avail, got_data; struct AvxInputContext input; struct AvxEncoderConfig global; @@ -2443,10 +2456,6 @@ input.only_i420 = 0; for (pass = global.pass ? global.pass - 1 : 0; pass < global.passes; pass++) { - int frames_in = 0, seen_frames = 0; - int64_t average_rate = -1; - int64_t lagged_count = 0; - open_input_file(&input, global.csp); /* If the input file doesn't specify its w/h (raw files), try to get @@ -2698,92 +2707,85 @@ }; } - frame_avail = 1; - got_data = 0; + // Keep track of the total number of frames passed to the encoder. + int seen_frames = 0; + // Does the encoder have queued data that needs retrieval? + int got_data = 0; + // Is there a frame available for processing? + int frame_avail = 1; + // Wrap the original stream of frames in an "object" that returns the + // same set of streams. + StreamIter orig_stream; + copy_stream_iter_init(&orig_stream, &input); + + // The skip iterator will skip the first N frames. Wrap the original + // stream in the skip iterator. + StreamIter skip_stream; + skip_stream_iter_init(&skip_stream, &orig_stream, global.skip_frames); + + // The step iterator will only return every N-th frame. + StreamIter step_stream; + step_stream_iter_init(&step_stream, &skip_stream, global.step_frames); + + // The limit iterator will stop returning frames after the N-th. + StreamIter limit_stream; + limit_stream_iter_init(&limit_stream, &step_stream, global.limit); while (frame_avail || got_data) { - struct aom_usec_timer timer; + frame_avail = read_stream_iter(&limit_stream, &raw); + if (frame_avail) { + seen_frames++; + } + fflush(stdout); - if (!global.limit || frames_in < global.limit) { - frame_avail = read_frame(&input, &raw); - - if (frame_avail) frames_in++; - seen_frames = - frames_in > global.skip_frames ? frames_in - global.skip_frames : 0; + aom_image_t *frame_to_encode; + if (input_shift || (do_16bit_internal && input.bit_depth == 8)) { + assert(do_16bit_internal); + // Input bit depth and stream bit depth do not match, so up + // shift frame to stream bit depth + if (!allocated_raw_shift) { + aom_img_alloc(&raw_shift, raw.fmt | AOM_IMG_FMT_HIGHBITDEPTH, + input.width, input.height, 32); + allocated_raw_shift = 1; + } + aom_img_upshift(&raw_shift, &raw, input_shift); + frame_to_encode = &raw_shift; } else { - frame_avail = 0; + frame_to_encode = &raw; } - - if (frames_in > global.skip_frames) { - aom_image_t *frame_to_encode; - if (input_shift || (do_16bit_internal && input.bit_depth == 8)) { - assert(do_16bit_internal); - // Input bit depth and stream bit depth do not match, so up - // shift frame to stream bit depth - if (!allocated_raw_shift) { - aom_img_alloc(&raw_shift, raw.fmt | AOM_IMG_FMT_HIGHBITDEPTH, - input.width, input.height, 32); - allocated_raw_shift = 1; - } - aom_img_upshift(&raw_shift, &raw, input_shift); - frame_to_encode = &raw_shift; - } else { - frame_to_encode = &raw; - } - aom_usec_timer_start(&timer); - if (do_16bit_internal) { - assert(frame_to_encode->fmt & AOM_IMG_FMT_HIGHBITDEPTH); - FOREACH_STREAM(stream, streams) { - if (stream->config.use_16bit_internal) - encode_frame(stream, &global, - frame_avail ? frame_to_encode : NULL, frames_in); - else - assert(0); - }; - } else { - assert((frame_to_encode->fmt & AOM_IMG_FMT_HIGHBITDEPTH) == 0); - FOREACH_STREAM(stream, streams) { - encode_frame(stream, &global, frame_avail ? frame_to_encode : NULL, - frames_in); - } - } - aom_usec_timer_mark(&timer); - cx_time += aom_usec_timer_elapsed(&timer); - - FOREACH_STREAM(stream, streams) { update_quantizer_histogram(stream); } - - got_data = 0; + struct aom_usec_timer timer; + aom_usec_timer_start(&timer); + if (do_16bit_internal) { + assert(frame_to_encode->fmt & AOM_IMG_FMT_HIGHBITDEPTH); FOREACH_STREAM(stream, streams) { - get_cx_data(stream, &global, &got_data); - } - - if (!got_data && input.length && streams != NULL && - !streams->frames_out) { - lagged_count = global.limit ? seen_frames : ftello(input.file); - } else if (input.length) { - int64_t rate; - - if (global.limit) { - const int64_t frame_in_lagged = (seen_frames - lagged_count) * 1000; - - rate = cx_time ? frame_in_lagged * (int64_t)1000000 / cx_time : 0; - } else { - const int64_t input_pos = ftello(input.file); - const int64_t input_pos_lagged = input_pos - lagged_count; - rate = cx_time ? input_pos_lagged * (int64_t)1000000 / cx_time : 0; - } - - average_rate = - (average_rate <= 0) ? rate : (average_rate * 7 + rate) / 8; - } - - if (got_data && global.test_decode != TEST_DECODE_OFF) { - FOREACH_STREAM(stream, streams) { - test_decode(stream, global.test_decode); - } + if (stream->config.use_16bit_internal) + encode_frame(stream, &global, frame_avail ? frame_to_encode : NULL, + seen_frames); + else + assert(0); + }; + } else { + assert((frame_to_encode->fmt & AOM_IMG_FMT_HIGHBITDEPTH) == 0); + FOREACH_STREAM(stream, streams) { + encode_frame(stream, &global, frame_avail ? frame_to_encode : NULL, + seen_frames); } } + aom_usec_timer_mark(&timer); + cx_time += aom_usec_timer_elapsed(&timer); + FOREACH_STREAM(stream, streams) { update_quantizer_histogram(stream); } + + got_data = 0; + FOREACH_STREAM(stream, streams) { + get_cx_data(stream, &global, &got_data); + } + + if (got_data && global.test_decode != TEST_DECODE_OFF) { + FOREACH_STREAM(stream, streams) { + test_decode(stream, global.test_decode); + } + } fflush(stdout); }
diff --git a/apps/aomenc.h b/apps/aomenc.h index ceaf954..b0982db 100644 --- a/apps/aomenc.h +++ b/apps/aomenc.h
@@ -43,6 +43,7 @@ int verbose; int limit; int skip_frames; + int step_frames; int show_psnr; enum TestDecodeFatality test_decode; int have_framerate;
diff --git a/common/stream_iter.c b/common/stream_iter.c new file mode 100644 index 0000000..bf523d8 --- /dev/null +++ b/common/stream_iter.c
@@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020, Alliance for Open Media. All rights reserved + * + * This source code is subject to the terms of the BSD 2 Clause License and + * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License + * was not distributed with this source code in the LICENSE file, you can + * obtain it at www.aomedia.org/license/software. If the Alliance for Open + * Media Patent License 1.0 was not distributed with this source code in the + * PATENTS file, you can obtain it at www.aomedia.org/license/patent. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> + +#include "common/stream_iter.h" +#include "common/y4minput.h" + +static int copy_reader(StreamIter *iter, aom_image_t *img) { + struct AvxInputContext *input_ctx = iter->input.avx; + FILE *f = input_ctx->file; + y4m_input *y4m = &input_ctx->y4m; + int shortread = 0; + + if (input_ctx->file_type == FILE_TYPE_Y4M) { + if (y4m_input_fetch_frame(y4m, f, img) < 1) return 0; + } else { + shortread = read_yuv_frame(input_ctx, img); + } + return !shortread; +} + +void copy_stream_iter_init(StreamIter *iter, struct AvxInputContext *input) { + iter->input.avx = input; + iter->reader = copy_reader; +} + +static int skip_reader(StreamIter *iter, aom_image_t *raw) { + // While we haven't skipped enough frames, read from the underlying + // stream and throw the result away. If no frame is available, early + // exit. + while (iter->current < iter->n) { + ++iter->current; + int frame_avail = read_stream_iter(iter->input.stream, raw); + if (!frame_avail) { + return frame_avail; + } + } + // If we're past the skip region, copy the remaining output. + return read_stream_iter(iter->input.stream, raw); +} + +void skip_stream_iter_init(StreamIter *iter, StreamIter *input, int num_skip) { + assert(num_skip >= 0); + iter->input.stream = input; + iter->current = 0; + iter->n = num_skip; + iter->reader = skip_reader; +} + +static int step_reader(StreamIter *iter, aom_image_t *raw) { + while (true) { + int frame_avail = read_stream_iter(iter->input.stream, raw); + // If at end of stream, no need to read further. + if (!frame_avail) { + return frame_avail; + } + + bool should_encode = iter->current == 0; + iter->current = (iter->current + 1) % (iter->n); + if (should_encode) { + return frame_avail; + } + } +} + +void step_stream_iter_init(StreamIter *iter, StreamIter *input, int step_size) { + assert(step_size > 0); + iter->input.stream = input; + iter->current = 0; + iter->n = step_size; + iter->reader = step_reader; +} + +static int limit_reader(StreamIter *iter, aom_image_t *raw) { + // limit of 0 is a special case meaning "no limit". + if (iter->n != 0 && iter->current >= iter->n) { + return 0; + } + ++iter->current; + return read_stream_iter(iter->input.stream, raw); +} + +void limit_stream_iter_init(StreamIter *iter, StreamIter *input, int limit) { + assert(limit >= 0); + iter->input.stream = input; + iter->current = 0; + iter->n = limit; + iter->reader = limit_reader; +} + +int read_stream_iter(StreamIter *iter, aom_image_t *raw) { + return iter->reader(iter, raw); +}
diff --git a/common/stream_iter.h b/common/stream_iter.h new file mode 100644 index 0000000..af98a2a --- /dev/null +++ b/common/stream_iter.h
@@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, Alliance for Open Media. All rights reserved + * + * This source code is subject to the terms of the BSD 2 Clause License and + * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License + * was not distributed with this source code in the LICENSE file, you can + * obtain it at www.aomedia.org/license/software. If the Alliance for Open + * Media Patent License 1.0 was not distributed with this source code in the + * PATENTS file, you can obtain it at www.aomedia.org/license/patent. + */ +#ifndef AOM_COMMON_STREAM_ITER_H_ +#define AOM_COMMON_STREAM_ITER_H_ + +#include "aom/aom_image.h" +#include "common/tools_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct StreamIter; + +// The input to each stream iterator can either be an AvxInputContext (when +// reading from a file or a pipe like STDIN) or another stream iterator. +union StreamInput { + struct AvxInputContext *avx; + struct StreamIter *stream; +}; + +typedef struct StreamIter { + union StreamInput input; + // For simplicity, all streams have two additional numbers associated + // with them, e.g., for counting the number of frames returned and the + // number of frames that should be skipped. Some iterators, like the copy + // iterator, do not use either field. + int current; + int n; + // Pointer to the function that performs the read. Returns whether a frame + // is available. If a frame is returned, it is written into *raw. + int (*reader)(struct StreamIter *iter, aom_image_t *raw); +} StreamIter; + +// Iterator that simply copies the data from the AvxInputContext. +void copy_stream_iter_init(StreamIter *iter, struct AvxInputContext *input); + +// Iterator that skips the first N frames. Takes another stream as input. +void skip_stream_iter_init(StreamIter *iter, StreamIter *input, int num_skip); + +// Iterator that only returns every N-th frame. Takes another stream as input. +void step_stream_iter_init(StreamIter *iter, StreamIter *input, int step_size); + +// Iterator that stops returning frames after the N-th. Takes another stream +// as input. +void limit_stream_iter_init(StreamIter *iter, StreamIter *input, int limit); + +// Invokes the iterator's specialized read function to read data from the +// stream. Returns if a frame was read. If so, writes the data into *raw. +int read_stream_iter(StreamIter *iter, aom_image_t *raw); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // AOM_COMMON_STREAM_ITER_H_
diff --git a/test/stream_iter_test.cc b/test/stream_iter_test.cc new file mode 100644 index 0000000..f7376ac --- /dev/null +++ b/test/stream_iter_test.cc
@@ -0,0 +1,285 @@ +/* + * Copyright (c) 2020, Alliance for Open Media. All rights reserved + * + * This source code is subject to the terms of the BSD 2 Clause License and + * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License + * was not distributed with this source code in the LICENSE file, you can + * obtain it at www.aomedia.org/license/software. If the Alliance for Open + * Media Patent License 1.0 was not distributed with this source code in the + * PATENTS file, you can obtain it at www.aomedia.org/license/patent. + */ + +#include <stdbool.h> +#include "common/stream_iter.h" +#include "third_party/googletest/src/googletest/include/gtest/gtest.h" + +namespace { + +// Dummy class that increments the width field in the image, which is used +// as a placeholder for the frame number. Stops iterating after N frames. +int dummy_reader(StreamIter *iter, aom_image_t *raw) { + if (iter->current >= iter->n) { + return 0; + } + ++iter->current; + raw->w = iter->current; + return 1; +} + +void dummy_stream_iter_init(StreamIter *iter, int total) { + iter->current = 0; + iter->n = total; + iter->reader = dummy_reader; +} + +class StreamIterTest : public ::testing::Test {}; + +TEST_F(StreamIterTest, Skip0) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter skip; + skip_stream_iter_init(&skip, &input, 0); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&skip, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_TRUE(read_stream_iter(&skip, &img)); + EXPECT_EQ(2U, img.w); + + EXPECT_TRUE(read_stream_iter(&skip, &img)); + EXPECT_EQ(3U, img.w); + + EXPECT_FALSE(read_stream_iter(&skip, &img)); +} + +TEST_F(StreamIterTest, Skip1) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter skip; + skip_stream_iter_init(&skip, &input, 1); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&skip, &img)); + EXPECT_EQ(2U, img.w); + + EXPECT_TRUE(read_stream_iter(&skip, &img)); + EXPECT_EQ(3U, img.w); + + EXPECT_FALSE(read_stream_iter(&skip, &img)); +} + +TEST_F(StreamIterTest, Skip2) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter skip; + skip_stream_iter_init(&skip, &input, 2); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&skip, &img)); + EXPECT_EQ(3U, img.w); + + EXPECT_FALSE(read_stream_iter(&skip, &img)); +} + +TEST_F(StreamIterTest, Skip3) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter skip; + skip_stream_iter_init(&skip, &input, 3); + + aom_image_t img; + EXPECT_FALSE(read_stream_iter(&skip, &img)); +} + +TEST_F(StreamIterTest, Skip4) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter skip; + skip_stream_iter_init(&skip, &input, 4); + + aom_image_t img; + EXPECT_FALSE(read_stream_iter(&skip, &img)); +} + +// limit=0 is a special case meaning "no limit." +TEST_F(StreamIterTest, Limit0) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter limit; + limit_stream_iter_init(&limit, &input, 0); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(2U, img.w); + + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(3U, img.w); + + EXPECT_FALSE(read_stream_iter(&limit, &img)); +} + +TEST_F(StreamIterTest, Limit1) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter limit; + limit_stream_iter_init(&limit, &input, 1); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_FALSE(read_stream_iter(&limit, &img)); +} + +TEST_F(StreamIterTest, Limit2) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter limit; + limit_stream_iter_init(&limit, &input, 2); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(2U, img.w); + + EXPECT_FALSE(read_stream_iter(&limit, &img)); +} + +TEST_F(StreamIterTest, Limit3) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter limit; + limit_stream_iter_init(&limit, &input, 3); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(2U, img.w); + + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(3U, img.w); + + EXPECT_FALSE(read_stream_iter(&limit, &img)); +} + +TEST_F(StreamIterTest, Limit4) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter limit; + limit_stream_iter_init(&limit, &input, 4); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(2U, img.w); + + EXPECT_TRUE(read_stream_iter(&limit, &img)); + EXPECT_EQ(3U, img.w); + + EXPECT_FALSE(read_stream_iter(&limit, &img)); +} + +TEST_F(StreamIterTest, Step1) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter step; + step_stream_iter_init(&step, &input, 1); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(2U, img.w); + + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(3U, img.w); + + EXPECT_FALSE(read_stream_iter(&step, &img)); +} + +TEST_F(StreamIterTest, Step2) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter step; + step_stream_iter_init(&step, &input, 2); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(3U, img.w); + + EXPECT_FALSE(read_stream_iter(&step, &img)); +} + +TEST_F(StreamIterTest, Step3) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter step; + step_stream_iter_init(&step, &input, 3); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_FALSE(read_stream_iter(&step, &img)); +} + +TEST_F(StreamIterTest, Step4) { + StreamIter input; + dummy_stream_iter_init(&input, 3); + + StreamIter step; + step_stream_iter_init(&step, &input, 4); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_FALSE(read_stream_iter(&step, &img)); +} + +// Test when the step size is an even divisor of the number of frames. +TEST_F(StreamIterTest, Step5) { + StreamIter input; + dummy_stream_iter_init(&input, 10); + + StreamIter step; + step_stream_iter_init(&step, &input, 5); + + aom_image_t img; + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(1U, img.w); + + EXPECT_TRUE(read_stream_iter(&step, &img)); + EXPECT_EQ(6U, img.w); + + EXPECT_FALSE(read_stream_iter(&step, &img)); +} + +} // namespace
diff --git a/test/test.cmake b/test/test.cmake index 2ca7c64..8c6dfa3 100644 --- a/test/test.cmake +++ b/test/test.cmake
@@ -218,6 +218,7 @@ "${AOM_ROOT}/test/pickrst_test.cc" "${AOM_ROOT}/test/quantize_func_test.cc" "${AOM_ROOT}/test/sad_test.cc" + "${AOM_ROOT}/test/stream_iter_test.cc" "${AOM_ROOT}/test/subtract_test.cc" "${AOM_ROOT}/test/reconinter_test.cc" "${AOM_ROOT}/test/sum_squares_test.cc"