From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- third_party/dav1d/tests/dav1d_argon.bash | 176 ++++++++++++++++ third_party/dav1d/tests/header_test.c | 33 +++ third_party/dav1d/tests/libfuzzer/alloc_fail.c | 102 ++++++++++ third_party/dav1d/tests/libfuzzer/alloc_fail.h | 35 ++++ third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c | 199 +++++++++++++++++++ third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h | 37 ++++ third_party/dav1d/tests/libfuzzer/main.c | 100 ++++++++++ third_party/dav1d/tests/libfuzzer/meson.build | 101 ++++++++++ third_party/dav1d/tests/meson.build | 155 +++++++++++++++ third_party/dav1d/tests/seek_stress.c | 243 +++++++++++++++++++++++ 10 files changed, 1181 insertions(+) create mode 100755 third_party/dav1d/tests/dav1d_argon.bash create mode 100644 third_party/dav1d/tests/header_test.c create mode 100644 third_party/dav1d/tests/libfuzzer/alloc_fail.c create mode 100644 third_party/dav1d/tests/libfuzzer/alloc_fail.h create mode 100644 third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c create mode 100644 third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h create mode 100644 third_party/dav1d/tests/libfuzzer/main.c create mode 100644 third_party/dav1d/tests/libfuzzer/meson.build create mode 100644 third_party/dav1d/tests/meson.build create mode 100644 third_party/dav1d/tests/seek_stress.c (limited to 'third_party/dav1d/tests') diff --git a/third_party/dav1d/tests/dav1d_argon.bash b/third_party/dav1d/tests/dav1d_argon.bash new file mode 100755 index 0000000000..27a8d61911 --- /dev/null +++ b/third_party/dav1d/tests/dav1d_argon.bash @@ -0,0 +1,176 @@ +#!/usr/bin/env bash + +DAV1D="tools/dav1d" +ARGON_DIR='.' +FILMGRAIN=1 +CPUMASK=-1 +THREADS=0 +JOBS=1 + +usage() { + NAME=$(basename "$0") + { + printf "Usage: %s [-d dav1d] [-a argondir] [-g \$filmgrain] [-c \$cpumask] [-t threads] [-j jobs] [DIRECTORY]...\n" "$NAME" + printf "Example: %s -d /path/to/dav1d -a /path/to/argon/ -g 0 -c avx2 profile0_core\n" "$NAME" + printf "Used to verify that dav1d can decode the Argon AV1 test vectors correctly.\n\n" + printf " DIRECTORY one or more dirs in the argon folder to check against\n" + printf " (default: everything except large scale tiles and stress files)\n" + printf " -d dav1d path to dav1d executable (default: tools/dav1d)\n" + printf " -a dir path to argon dir (default: 'tests/argon' if found; '.' otherwise)\n" + printf " -g \$num enable filmgrain (default: 1)\n" + printf " -c \$mask use restricted cpumask (default: -1)\n" + printf " -t \$num number of threads per dav1d (default: 0)\n" + printf " -j \$num number of parallel dav1d processes (default: 1)\n\n" + } >&2 + exit 1 +} + +error() { + printf "\033[1;91m%s\033[0m\n" "$*" >&2 + exit 1 +} + +fail() { + printf "\033[1K\rMismatch in %s\n" "$1" + (( failed++ )) +} + +check_pids() { + new_pids=() + done_pids=() + for p in "${pids[@]}"; do + if kill -0 "$p" 2>/dev/null; then + new_pids+=("$p") + else + done_pids+=("$p") + fi + done + pids=("${new_pids[@]}") +} + +wait_pids() { + pid_list=("$@") + for p in "${pid_list[@]}"; do + if ! wait "$p"; then + local file_varname="file$p" + fail "${!file_varname}" + fi + done +} + +block_pids() { + while [ ${#pids[@]} -ge "$JOBS" ]; do + check_pids + if [ ${#done_pids} -eq 0 ]; then + sleep 0.2 + else + wait_pids "${done_pids[@]}" + fi + done +} + +wait_all_pids() { + wait_pids "${pids[@]}" +} + +# find tests/argon +tests_dir=$(dirname "$(readlink -f "$0")") +if [ -d "$tests_dir/argon" ]; then + ARGON_DIR="$tests_dir/argon" +fi + +while getopts ":d:a:g:c:t:j:" opt; do + case "$opt" in + d) + DAV1D="$OPTARG" + ;; + a) + ARGON_DIR="$OPTARG" + ;; + g) + FILMGRAIN="$OPTARG" + ;; + c) + CPUMASK="$OPTARG" + ;; + t) + THREADS="$OPTARG" + ;; + j) + JOBS="$OPTARG" + ;; + \?) + printf "Error! Invalid option: -%s\n" "$OPTARG" >&2 + usage + ;; + *) + usage + ;; + esac +done +shift $((OPTIND-1)) + +if [ "$#" -eq 0 ]; then + # Everything except large scale tiles and stress files. + dirs=("$ARGON_DIR/profile0_core" "$ARGON_DIR/profile0_core_special" + "$ARGON_DIR/profile0_not_annexb" "$ARGON_DIR/profile0_not_annexb_special" + "$ARGON_DIR/profile1_core" "$ARGON_DIR/profile1_core_special" + "$ARGON_DIR/profile1_not_annexb" "$ARGON_DIR/profile1_not_annexb_special" + "$ARGON_DIR/profile2_core" "$ARGON_DIR/profile2_core_special" + "$ARGON_DIR/profile2_not_annexb" "$ARGON_DIR/profile2_not_annexb_special" + "$ARGON_DIR/profile_switching") +else + mapfile -t dirs < <(printf "${ARGON_DIR}/%s\n" "$@" | sort -u) +fi + +ver_info="dav1d $("$DAV1D" -v 2>&1) filmgrain=$FILMGRAIN cpumask=$CPUMASK" || error "Error! Can't run $DAV1D" +files=() + +for d in "${dirs[@]}"; do + if [ -d "$d/streams" ]; then + files+=("${d/%\//}"/streams/*.obu) + fi +done + +num_files="${#files[@]}" +if [ "$num_files" -eq 0 ]; then + error "Error! No files found at ${dirs[*]}" +fi + +failed=0 +pids=() +for i in "${!files[@]}"; do + f="${files[i]}" + if [ "$FILMGRAIN" -eq 0 ]; then + md5=${f/\/streams\//\/md5_no_film_grain\/} + else + md5=${f/\/streams\//\/md5_ref\/} + fi + md5=$(<"${md5/%obu/md5}") || error "Error! Can't read md5 ${md5} for file ${f}" + md5=${md5/ */} + + printf '\033[1K\r[%3d%% %*d/%d] Verifying %s' "$(((i+1)*100/num_files))" "${#num_files}" "$((i+1))" "$num_files" "${f#"$ARGON_DIR"/}" + cmd=("$DAV1D" -i "$f" --filmgrain "$FILMGRAIN" --verify "$md5" --cpumask "$CPUMASK" --threads "$THREADS" -q) + if [ "$JOBS" -gt 1 ]; then + "${cmd[@]}" 2>/dev/null & + p=$! + pids+=("$p") + declare "file$p=${f#"$ARGON_DIR"/}" + block_pids + else + if ! "${cmd[@]}" 2>/dev/null; then + fail "${f#"$ARGON_DIR"/}" + fi + fi +done + +wait_all_pids + +if [ "$failed" -ne 0 ]; then + printf "\033[1K\r%d/%d files \033[1;91mfailed\033[0m to verify" "$failed" "$num_files" +else + printf "\033[1K\r%d files \033[1;92msuccessfully\033[0m verified" "$num_files" +fi +printf " in %dm%ds (%s)\n" "$((SECONDS/60))" "$((SECONDS%60))" "$ver_info" + +exit $failed diff --git a/third_party/dav1d/tests/header_test.c b/third_party/dav1d/tests/header_test.c new file mode 100644 index 0000000000..2cd9eca7f9 --- /dev/null +++ b/third_party/dav1d/tests/header_test.c @@ -0,0 +1,33 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include DAV1D_TEST_HEADER + +int main(void) +{ + return 0; +} diff --git a/third_party/dav1d/tests/libfuzzer/alloc_fail.c b/third_party/dav1d/tests/libfuzzer/alloc_fail.c new file mode 100644 index 0000000000..ddd1dd71ab --- /dev/null +++ b/third_party/dav1d/tests/libfuzzer/alloc_fail.c @@ -0,0 +1,102 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Janne Grunau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "alloc_fail.h" + +static int fail_probability; + +void dav1d_setup_alloc_fail(unsigned seed, unsigned probability) { + srand(seed); + + while (probability >= RAND_MAX) + probability >>= 1; + + fail_probability = probability; +} + +void * __wrap_malloc(size_t); + +void * __wrap_malloc(size_t sz) { + if (rand() < fail_probability) + return NULL; + return malloc(sz); +} + +#if defined(HAVE_POSIX_MEMALIGN) +int __wrap_posix_memalign(void **memptr, size_t alignment, size_t size); + +int __wrap_posix_memalign(void **memptr, size_t alignment, size_t size) { + if (rand() < fail_probability) + return ENOMEM; + return posix_memalign(memptr, alignment, size); +} +#else +#error "HAVE_POSIX_MEMALIGN required" +#endif + +int __wrap_pthread_create(pthread_t *, const pthread_attr_t *, + void *(*) (void *), void *); + +int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) +{ + if (rand() < (fail_probability + RAND_MAX/16)) + return EAGAIN; + + return pthread_create(thread, attr, start_routine, arg); +} + +int __wrap_pthread_mutex_init(pthread_mutex_t *, + const pthread_mutexattr_t *); + +int __wrap_pthread_mutex_init(pthread_mutex_t *restrict mutex, + const pthread_mutexattr_t *restrict attr) +{ + if (rand() < (fail_probability + RAND_MAX/8)) + return ENOMEM; + + return pthread_mutex_init(mutex, attr); +} + +int __wrap_pthread_cond_init(pthread_cond_t *, + const pthread_condattr_t *); + +int __wrap_pthread_cond_init(pthread_cond_t *restrict cond, + const pthread_condattr_t *restrict attr) +{ + if (rand() < (fail_probability + RAND_MAX/16)) + return ENOMEM; + + return pthread_cond_init(cond, attr); +} diff --git a/third_party/dav1d/tests/libfuzzer/alloc_fail.h b/third_party/dav1d/tests/libfuzzer/alloc_fail.h new file mode 100644 index 0000000000..5ace870beb --- /dev/null +++ b/third_party/dav1d/tests/libfuzzer/alloc_fail.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Janne Grunau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DAV1D_TESTS_LIBFUZZER_ALLOC_FAIL_H +#define DAV1D_TESTS_LIBFUZZER_ALLOC_FAIL_H + +#include + +DAV1D_API void dav1d_setup_alloc_fail(unsigned seed, unsigned probability); + +#endif /* DAV1D_TESTS_LIBFUZZER_ALLOC_FAIL_H */ diff --git a/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c b/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c new file mode 100644 index 0000000000..502ee22b06 --- /dev/null +++ b/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.c @@ -0,0 +1,199 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Janne Grunau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include "src/cpu.h" +#include "dav1d_fuzzer.h" + +#ifdef DAV1D_ALLOC_FAIL + +#include "alloc_fail.h" + +static unsigned djb_xor(const uint8_t * c, size_t len) { + unsigned hash = 5381; + for(size_t i = 0; i < len; i++) + hash = hash * 33 ^ c[i]; + return hash; +} +#endif + +static unsigned r32le(const uint8_t *const p) { + return ((uint32_t)p[3] << 24U) | (p[2] << 16U) | (p[1] << 8U) | p[0]; +} + +#define DAV1D_FUZZ_MAX_SIZE 4096 * 4096 + +// search for "--cpumask xxx" in argv and remove both parameters +int LLVMFuzzerInitialize(int *argc, char ***argv) { + int i = 1; + for (; i < *argc; i++) { + if (!strcmp((*argv)[i], "--cpumask")) { + const char * cpumask = (*argv)[i+1]; + if (cpumask) { + char *end; + unsigned res; + if (!strncmp(cpumask, "0x", 2)) { + cpumask += 2; + res = (unsigned) strtoul(cpumask, &end, 16); + } else { + res = (unsigned) strtoul(cpumask, &end, 0); + } + if (end != cpumask && !end[0]) { + dav1d_set_cpu_flags_mask(res); + } + } + break; + } + } + + for (; i < *argc - 2; i++) { + (*argv)[i] = (*argv)[i + 2]; + } + + *argc = i; + + return 0; +} + + +// expects ivf input + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + Dav1dSettings settings = { 0 }; + Dav1dContext * ctx = NULL; + Dav1dPicture pic; + const uint8_t *ptr = data; + int have_seq_hdr = 0; + int err; + + dav1d_version(); + + if (size < 32) goto end; +#ifdef DAV1D_ALLOC_FAIL + unsigned h = djb_xor(ptr, 32); + unsigned seed = h; + unsigned probability = h > (RAND_MAX >> 5) ? RAND_MAX >> 5 : h; + int max_frame_delay = (h & 0xf) + 1; + int n_threads = ((h >> 4) & 0x7) + 1; + if (max_frame_delay > 5) max_frame_delay = 1; + if (n_threads > 3) n_threads = 1; +#endif + ptr += 32; // skip ivf header + + dav1d_default_settings(&settings); + +#ifdef DAV1D_MT_FUZZING + settings.max_frame_delay = settings.n_threads = 4; +#elif defined(DAV1D_ALLOC_FAIL) + settings.max_frame_delay = max_frame_delay; + settings.n_threads = n_threads; + dav1d_setup_alloc_fail(seed, probability); +#else + settings.max_frame_delay = settings.n_threads = 1; +#endif +#if defined(DAV1D_FUZZ_MAX_SIZE) + settings.frame_size_limit = DAV1D_FUZZ_MAX_SIZE; +#endif + + err = dav1d_open(&ctx, &settings); + if (err < 0) goto end; + + while (ptr <= data + size - 12) { + Dav1dData buf; + uint8_t *p; + + size_t frame_size = r32le(ptr); + ptr += 12; + + if (frame_size > size || ptr > data + size - frame_size) + break; + + if (!frame_size) continue; + + if (!have_seq_hdr) { + Dav1dSequenceHeader seq; + int err = dav1d_parse_sequence_header(&seq, ptr, frame_size); + // skip frames until we see a sequence header + if (err != 0) { + ptr += frame_size; + continue; + } + have_seq_hdr = 1; + } + + // copy frame data to a new buffer to catch reads past the end of input + p = dav1d_data_create(&buf, frame_size); + if (!p) goto cleanup; + memcpy(p, ptr, frame_size); + ptr += frame_size; + + do { + if ((err = dav1d_send_data(ctx, &buf)) < 0) { + if (err != DAV1D_ERR(EAGAIN)) + break; + } + memset(&pic, 0, sizeof(pic)); + err = dav1d_get_picture(ctx, &pic); + if (err == 0) { + dav1d_picture_unref(&pic); + } else if (err != DAV1D_ERR(EAGAIN)) { + break; + } + } while (buf.sz > 0); + + if (buf.sz > 0) + dav1d_data_unref(&buf); + } + + memset(&pic, 0, sizeof(pic)); + if ((err = dav1d_get_picture(ctx, &pic)) == 0) { + /* Test calling dav1d_picture_unref() after dav1d_close() */ + do { + Dav1dPicture pic2 = { 0 }; + if ((err = dav1d_get_picture(ctx, &pic2)) == 0) + dav1d_picture_unref(&pic2); + } while (err != DAV1D_ERR(EAGAIN)); + + dav1d_close(&ctx); + dav1d_picture_unref(&pic); + return 0; + } + +cleanup: + dav1d_close(&ctx); +end: + return 0; +} diff --git a/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h b/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h new file mode 100644 index 0000000000..0cbbad46b0 --- /dev/null +++ b/third_party/dav1d/tests/libfuzzer/dav1d_fuzzer.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Janne Grunau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DAV1D_TESTS_LIBFUZZER_DAV1D_FUZZER_H +#define DAV1D_TESTS_LIBFUZZER_DAV1D_FUZZER_H + +#include +#include + +int LLVMFuzzerInitialize(int *argc, char ***argv); +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +#endif /* DAV1D_TESTS_LIBFUZZER_DAV1D_FUZZER_H */ diff --git a/third_party/dav1d/tests/libfuzzer/main.c b/third_party/dav1d/tests/libfuzzer/main.c new file mode 100644 index 0000000000..8647738666 --- /dev/null +++ b/third_party/dav1d/tests/libfuzzer/main.c @@ -0,0 +1,100 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Janne Grunau + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dav1d_fuzzer.h" + +// expects ivf input + +int main(int argc, char *argv[]) { + int ret = -1; + FILE *f = NULL; + int64_t fsize; + const char *filename = NULL; + uint8_t *data = NULL; + size_t size = 0; + + if (LLVMFuzzerInitialize(&argc, &argv)) { + return 1; + } + + if (argc != 2) { + fprintf(stdout, "Usage:\n%s fuzzing_testcase.ivf\n", argv[0]); + return -1; + } + filename = argv[1]; + + if (!(f = fopen(filename, "rb"))) { + fprintf(stderr, "failed to open %s: %s\n", filename, strerror(errno)); + goto error; + } + + if (fseeko(f, 0, SEEK_END) == -1) { + fprintf(stderr, "fseek(%s, 0, SEEK_END) failed: %s\n", filename, + strerror(errno)); + goto error; + } + if ((fsize = ftello(f)) == -1) { + fprintf(stderr, "ftell(%s) failed: %s\n", filename, strerror(errno)); + goto error; + } + rewind(f); + + if (fsize < 0 || fsize > INT_MAX) { + fprintf(stderr, "%s is too large: %"PRId64"\n", filename, fsize); + goto error; + } + size = (size_t)fsize; + + if (!(data = malloc(size))) { + fprintf(stderr, "failed to allocate: %zu bytes\n", size); + goto error; + } + + if (fread(data, size, 1, f) == size) { + fprintf(stderr, "failed to read %zu bytes from %s: %s\n", size, + filename, strerror(errno)); + goto error; + } + + ret = LLVMFuzzerTestOneInput(data, size); + +error: + free(data); + if (f) fclose(f); + return ret; +} diff --git a/third_party/dav1d/tests/libfuzzer/meson.build b/third_party/dav1d/tests/libfuzzer/meson.build new file mode 100644 index 0000000000..45d28562c1 --- /dev/null +++ b/third_party/dav1d/tests/libfuzzer/meson.build @@ -0,0 +1,101 @@ +# Copyright © 2020, VideoLAN and dav1d authors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# +# Build definition for the dav1d fuzzing binaries +# + +if fuzzing_engine == 'none' and not have_fseeko + subdir_done() +endif + +dav1d_fuzzer_sources = files('dav1d_fuzzer.c') +fuzzer_ldflags = [] +fuzzer_link_lang = {} + +if get_option('fuzzer_ldflags') != '' + fuzzer_ldflags += [get_option('fuzzer_ldflags')] +endif + +if fuzzing_engine == 'none' + dav1d_fuzzer_sources += files('main.c') +elif fuzzing_engine == 'libfuzzer' + fuzzer_ldflags += ['-fsanitize=fuzzer'] +elif fuzzing_engine == 'oss-fuzz' + # libFuzzingEngine needs c++ + add_languages('cpp') + fuzzer_link_lang = {'link_language': 'cpp'} +endif + +dav1d_fuzzer = executable('dav1d_fuzzer', + dav1d_fuzzer_sources, + include_directories: dav1d_inc_dirs, + link_args: fuzzer_ldflags, + link_with : libdav1d, + build_by_default: true, + dependencies : [thread_dependency], + kwargs: fuzzer_link_lang + ) + +dav1d_fuzzer_mt = executable('dav1d_fuzzer_mt', + dav1d_fuzzer_sources, + include_directories: dav1d_inc_dirs, + c_args: ['-DDAV1D_MT_FUZZING'], + link_args: fuzzer_ldflags, + link_with : libdav1d, + build_by_default: true, + dependencies : [thread_dependency], + kwargs: fuzzer_link_lang + ) + +objcopy = find_program('objcopy', + required: false) +if (objcopy.found() and + not get_option('b_lto') and + get_option('default_library') == 'static' and + cc.has_function('posix_memalign', prefix : '#include ', args : test_args)) + + libdav1d_af = custom_target('libdav1d_af', + input: libdav1d, + output: 'libdav1d_af.a', + depends: libdav1d, + command: [objcopy, + '--redefine-sym', 'malloc=__wrap_malloc', + '--redefine-sym', 'posix_memalign=__wrap_posix_memalign', + '--redefine-sym', 'pthread_create=__wrap_pthread_create', + '--redefine-sym', 'pthread_cond_init=__wrap_pthread_cond_init', + '--redefine-sym', 'pthread_mutex_init=__wrap_pthread_mutex_init', + '@INPUT@', '@OUTPUT@']) + + dav1d_fuzzer_mem = executable('dav1d_fuzzer_mem', + dav1d_fuzzer_sources + ['alloc_fail.c'], + include_directories: dav1d_inc_dirs, + c_args: ['-DDAV1D_ALLOC_FAIL'], + link_args: fuzzer_ldflags + [join_paths(libdav1d_af.full_path())], + link_depends: libdav1d_af, + build_by_default: false, + dependencies : [thread_dependency], + kwargs: fuzzer_link_lang + ) +endif diff --git a/third_party/dav1d/tests/meson.build b/third_party/dav1d/tests/meson.build new file mode 100644 index 0000000000..11db0a56e9 --- /dev/null +++ b/third_party/dav1d/tests/meson.build @@ -0,0 +1,155 @@ +# Copyright © 2018, VideoLAN and dav1d authors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# +# Build definition for the dav1d tests +# + +# Leave subdir if tests are disabled +if not get_option('enable_tests') + subdir_done() +endif + +if is_asm_enabled + checkasm_sources = files( + 'checkasm/checkasm.c', + 'checkasm/msac.c', + 'checkasm/pal.c', + 'checkasm/refmvs.c', + ) + + checkasm_tmpl_sources = files( + 'checkasm/cdef.c', + 'checkasm/filmgrain.c', + 'checkasm/ipred.c', + 'checkasm/itx.c', + 'checkasm/loopfilter.c', + 'checkasm/looprestoration.c', + 'checkasm/mc.c', + ) + + checkasm_bitdepth_objs = [] + foreach bitdepth : dav1d_bitdepths + checkasm_bitdepth_lib = static_library( + 'checkasm_bitdepth_@0@'.format(bitdepth), + checkasm_tmpl_sources, + include_directories: dav1d_inc_dirs, + dependencies : [stdatomic_dependencies], + c_args: ['-DBITDEPTH=@0@'.format(bitdepth)], + install: false, + build_by_default: false, + ) + checkasm_bitdepth_objs += checkasm_bitdepth_lib.extract_all_objects(recursive: true) + endforeach + + checkasm_asm_objs = [] + checkasm_asm_sources = [] + if host_machine.cpu_family() == 'aarch64' or host_machine.cpu() == 'arm64' + checkasm_asm_sources += files('checkasm/arm/checkasm_64.S') + elif host_machine.cpu_family().startswith('arm') + checkasm_asm_sources += files('checkasm/arm/checkasm_32.S') + elif host_machine.cpu_family() == 'riscv64' + checkasm_asm_sources += files('checkasm/riscv/checkasm_64.S') + elif host_machine.cpu_family().startswith('x86') + checkasm_asm_objs += nasm_gen.process(files('checkasm/x86/checkasm.asm')) + endif + + if use_gaspp + checkasm_asm_objs += gaspp_gen.process(checkasm_asm_sources) + else + checkasm_sources += checkasm_asm_sources + endif + + checkasm = executable('checkasm', + checkasm_sources, + checkasm_asm_objs, + + objects: [ + checkasm_bitdepth_objs, + libdav1d.extract_all_objects(recursive: true), + ], + + include_directories: dav1d_inc_dirs, + build_by_default: false, + dependencies : [ + thread_dependency, + rt_dependency, + libdl_dependency, + libm_dependency, + ], + ) + + test('checkasm', checkasm, suite: 'checkasm', timeout: 180, is_parallel: false) + benchmark('checkasm', checkasm, suite: 'checkasm', timeout: 3600, args: '--bench') +endif + +c99_extension_flag = cc.first_supported_argument( + '-Werror=c11-extensions', + '-Werror=c99-c11-compat', + '-Wc11-extensions', + '-Wc99-c11-compat', +) + +# dav1d_api_headers +foreach header : dav1d_api_headers + target = header + '_test' + + header_test_exe = executable(target, + 'header_test.c', + include_directories: dav1d_inc_dirs, + c_args: ['-DDAV1D_TEST_HEADER="@0@"'.format(header), c99_extension_flag], + build_by_default: true + ) + + test(target, header_test_exe, suite: 'headers') +endforeach + + +# fuzzing binaries +subdir('libfuzzer') + +# seek stress test binary, depends on dav1d cli tool +if (get_option('enable_tools') and get_option('enable_seek_stress')) + seek_stress_sources = files('seek_stress.c') + seek_stress = executable('seek_stress', + seek_stress_sources, rev_target, + objects: [ + dav1d.extract_objects('dav1d_cli_parse.c'), + dav1d_input_objs.extract_objects('input/input.c', 'input/ivf.c'), + ], + include_directories: [dav1d_inc_dirs, include_directories('../tools')], + link_with: libdav1d, + dependencies: [ + thread_dependency, + rt_dependency, + getopt_dependency, + libm_dependency, + ], + ) +endif + +# Include dav1d test data repository with additional tests +if get_option('testdata_tests') + subdir('dav1d-test-data') +endif diff --git a/third_party/dav1d/tests/seek_stress.c b/third_party/dav1d/tests/seek_stress.c new file mode 100644 index 0000000000..a85ec86886 --- /dev/null +++ b/third_party/dav1d/tests/seek_stress.c @@ -0,0 +1,243 @@ +/* + * Copyright © 2020, VideoLAN and dav1d authors + * Copyright © 2020, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "vcs_version.h" +#include "cli_config.h" + +#include +#include +#include +#include + +#include "dav1d/dav1d.h" +#include "input/input.h" +#include "input/demuxer.h" +#include "dav1d_cli_parse.h" + +#define NUM_RAND_SEEK 3 +#define NUM_REL_SEEK 4 +#define NUM_END_SEEK 2 + +const Demuxer annexb_demuxer = { .name = "" }; +const Demuxer section5_demuxer = { .name = "" }; + +#ifdef _WIN32 +#include +static unsigned get_seed(void) { + return GetTickCount(); +} +#else +#ifdef __APPLE__ +#include +#else +#include +#endif +static unsigned get_seed(void) { +#ifdef __APPLE__ + return (unsigned) mach_absolute_time(); +#elif defined(HAVE_CLOCK_GETTIME) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (unsigned) (1000000000ULL * ts.tv_sec + ts.tv_nsec); +#endif +} +#endif + +static uint32_t xs_state[4]; + +static void xor128_srand(unsigned seed) { + xs_state[0] = seed; + xs_state[1] = ( seed & 0xffff0000) | (~seed & 0x0000ffff); + xs_state[2] = (~seed & 0xffff0000) | ( seed & 0x0000ffff); + xs_state[3] = ~seed; +} + +// xor128 from Marsaglia, George (July 2003). "Xorshift RNGs". +// Journal of Statistical Software. 8 (14). +// doi:10.18637/jss.v008.i14. +static int xor128_rand(void) { + const uint32_t x = xs_state[0]; + const uint32_t t = x ^ (x << 11); + + xs_state[0] = xs_state[1]; + xs_state[1] = xs_state[2]; + xs_state[2] = xs_state[3]; + uint32_t w = xs_state[3]; + + w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); + xs_state[3] = w; + + return w >> 1; +} + +static inline int decode_frame(Dav1dPicture *const p, + Dav1dContext *const c, Dav1dData *const data) +{ + int res; + memset(p, 0, sizeof(*p)); + if ((res = dav1d_send_data(c, data)) < 0) { + if (res != DAV1D_ERR(EAGAIN)) { + fprintf(stderr, "Error decoding frame: %s\n", + strerror(DAV1D_ERR(res))); + return res; + } + } + if ((res = dav1d_get_picture(c, p)) < 0) { + if (res != DAV1D_ERR(EAGAIN)) { + fprintf(stderr, "Error decoding frame: %s\n", + strerror(DAV1D_ERR(res))); + return res; + } + } else dav1d_picture_unref(p); + return 0; +} + +static int decode_rand(DemuxerContext *const in, Dav1dContext *const c, + Dav1dData *const data, const double fps) +{ + int res = 0; + Dav1dPicture p; + const int num_frames = xor128_rand() % (int)(fps * 5); + for (int i = 0; i < num_frames; i++) { + if ((res = decode_frame(&p, c, data))) break; + if (input_read(in, data) || data->sz == 0) break; + } + return res; +} + +static int decode_all(DemuxerContext *const in, + Dav1dContext *const c, Dav1dData *const data) +{ + int res = 0; + Dav1dPicture p; + do { if ((res = decode_frame(&p, c, data))) break; + } while (!input_read(in, data) && data->sz > 0); + return res; +} + +static int seek(DemuxerContext *const in, Dav1dContext *const c, + const uint64_t pts, Dav1dData *const data) +{ + int res; + if ((res = input_seek(in, pts))) return res; + Dav1dSequenceHeader seq; + do { if ((res = input_read(in, data))) break; + } while (dav1d_parse_sequence_header(&seq, data->data, data->sz)); + dav1d_flush(c); + return res; +} + +int main(const int argc, char *const *const argv) { + const char *version = dav1d_version(); + if (strcmp(version, DAV1D_VERSION)) { + fprintf(stderr, "Version mismatch (library: %s, executable: %s)\n", + version, DAV1D_VERSION); + return EXIT_FAILURE; + } + + CLISettings cli_settings; + Dav1dSettings lib_settings; + DemuxerContext *in; + Dav1dContext *c; + Dav1dData data; + unsigned total, i_fps[2], i_timebase[2]; + double timebase, spf, fps; + uint64_t pts; + + xor128_srand(get_seed()); + parse(argc, argv, &cli_settings, &lib_settings); + + if (input_open(&in, "ivf", cli_settings.inputfile, + i_fps, &total, i_timebase) < 0 || + !i_timebase[0] || !i_timebase[1] || !i_fps[0] || !i_fps[1]) + { + return EXIT_SUCCESS; + } + if (dav1d_open(&c, &lib_settings)) + return EXIT_FAILURE; + + timebase = (double)i_timebase[1] / i_timebase[0]; + spf = (double)i_fps[1] / i_fps[0]; + fps = (double)i_fps[0] / i_fps[1]; + if (fps < 1) goto end; + +#define FRAME_OFFSET_TO_PTS(foff) \ + (uint64_t)llround(((foff) * spf) * 1000000000.0) +#define TS_TO_PTS(ts) \ + (uint64_t)llround(((ts) * timebase) * 1000000000.0) + + // seek at random pts + for (int i = 0; i < NUM_RAND_SEEK; i++) { + pts = FRAME_OFFSET_TO_PTS(xor128_rand() % total); + if (seek(in, c, pts, &data)) continue; + if (decode_rand(in, c, &data, fps)) goto end; + } + pts = TS_TO_PTS(data.m.timestamp); + + // seek left / right randomly with random intervals within 1s + for (int i = 0, tries = 0; + i - tries < NUM_REL_SEEK && tries < NUM_REL_SEEK / 2; + i++) + { + const int sign = xor128_rand() & 1 ? -1 : +1; + const float diff = (xor128_rand() % 100) / 100.f; + int64_t new_pts = pts + sign * FRAME_OFFSET_TO_PTS(diff * fps); + const int64_t new_ts = llround(new_pts / (timebase * 1000000000.0)); + new_pts = TS_TO_PTS(new_ts); + if (new_pts < 0 || (uint64_t)new_pts >= FRAME_OFFSET_TO_PTS(total)) { + if (seek(in, c, FRAME_OFFSET_TO_PTS(total / 2), &data)) break; + pts = TS_TO_PTS(data.m.timestamp); + tries++; + continue; + } + if (seek(in, c, new_pts, &data)) + if (seek(in, c, 0, &data)) goto end; + if (decode_rand(in, c, &data, fps)) goto end; + pts = TS_TO_PTS(data.m.timestamp); + } + + unsigned shift = 0; + do { + shift += 5; + if (shift > total) + shift = total; + } while (seek(in, c, FRAME_OFFSET_TO_PTS(total - shift), &data)); + + // simulate seeking after the end of the file + for (int i = 0; i < NUM_END_SEEK; i++) { + if (seek(in, c, FRAME_OFFSET_TO_PTS(total - shift), &data)) goto end; + if (decode_all(in, c, &data)) goto end; + int num_flush = 1 + 64 + xor128_rand() % 64; + while (num_flush--) dav1d_flush(c); + } + +end: + input_close(in); + dav1d_close(&c); + return EXIT_SUCCESS; +} -- cgit v1.2.3