Bug 1729481: Exclude more things from dav1d to make it easier to update r=jbauman

Depends on D124940

Differential Revision: https://phabricator.services.mozilla.com/D124941
This commit is contained in:
Tom Ritter 2021-09-17 19:31:36 +00:00
parent 4fe1475a38
commit 3b40e004e9
63 changed files with 5 additions and 10993 deletions

View File

@ -50,6 +50,11 @@ vendoring:
exclude:
- build/.gitattributes
- build/.gitignore
- doc
- examples
- package
- tests
- tools
update-actions:
- action: copy-file

View File

@ -1,19 +0,0 @@
PROJECT_NAME = dav1d
OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT@
STRIP_FROM_PATH = @DOXYGEN_STRIP@
OUTPUT_LANGUAGE = English
TAB_SIZE = 4
EXTRACT_ALL = YES
OPTIMIZE_OUTPUT_FOR_C = YES
DOXYFILE_ENCODING = UTF-8
TYPEDEF_HIDES_STRUCT = YES
QUIET = YES
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
INPUT = @DOXYGEN_INPUT@
FILE_PATTERNS = *.h
GENERATE_HTML = YES
GENERATE_LATEX = NO

View File

@ -1,108 +0,0 @@
Alliance for Open Media Patent License 1.0
1. License Terms.
1.1. Patent License. Subject to the terms and conditions of this License, each
Licensor, on behalf of itself and successors in interest and assigns,
grants Licensee a non-sublicensable, perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as expressly stated in this
License) patent license to its Necessary Claims to make, use, sell, offer
for sale, import or distribute any Implementation.
1.2. Conditions.
1.2.1. Availability. As a condition to the grant of rights to Licensee to make,
sell, offer for sale, import or distribute an Implementation under
Section 1.1, Licensee must make its Necessary Claims available under
this License, and must reproduce this License with any Implementation
as follows:
a. For distribution in source code, by including this License in the
root directory of the source code with its Implementation.
b. For distribution in any other form (including binary, object form,
and/or hardware description code (e.g., HDL, RTL, Gate Level Netlist,
GDSII, etc.)), by including this License in the documentation, legal
notices, and/or other written materials provided with the
Implementation.
1.2.2. Additional Conditions. This license is directly from Licensor to
Licensee. Licensee acknowledges as a condition of benefiting from it
that no rights from Licensor are received from suppliers, distributors,
or otherwise in connection with this License.
1.3. Defensive Termination. If any Licensee, its Affiliates, or its agents
initiates patent litigation or files, maintains, or voluntarily
participates in a lawsuit against another entity or any person asserting
that any Implementation infringes Necessary Claims, any patent licenses
granted under this License directly to the Licensee are immediately
terminated as of the date of the initiation of action unless 1) that suit
was in response to a corresponding suit regarding an Implementation first
brought against an initiating entity, or 2) that suit was brought to
enforce the terms of this License (including intervention in a third-party
action by a Licensee).
1.4. Disclaimers. The Reference Implementation and Specification are provided
"AS IS" and without warranty. The entire risk as to implementing or
otherwise using the Reference Implementation or Specification is assumed
by the implementer and user. Licensor expressly disclaims any warranties
(express, implied, or otherwise), including implied warranties of
merchantability, non-infringement, fitness for a particular purpose, or
title, related to the material. IN NO EVENT WILL LICENSOR BE LIABLE TO
ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL,
INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF
ACTION OF ANY KIND WITH RESPECT TO THIS LICENSE, WHETHER BASED ON BREACH
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR
NOT THE OTHER PARTRY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2. Definitions.
2.1. Affiliate. “Affiliate” means an entity that directly or indirectly
Controls, is Controlled by, or is under common Control of that party.
2.2. Control. “Control” means direct or indirect control of more than 50% of
the voting power to elect directors of that corporation, or for any other
entity, the power to direct management of such entity.
2.3. Decoder. "Decoder" means any decoder that conforms fully with all
non-optional portions of the Specification.
2.4. Encoder. "Encoder" means any encoder that produces a bitstream that can
be decoded by a Decoder only to the extent it produces such a bitstream.
2.5. Final Deliverable. “Final Deliverable” means the final version of a
deliverable approved by the Alliance for Open Media as a Final
Deliverable.
2.6. Implementation. "Implementation" means any implementation, including the
Reference Implementation, that is an Encoder and/or a Decoder. An
Implementation also includes components of an Implementation only to the
extent they are used as part of an Implementation.
2.7. License. “License” means this license.
2.8. Licensee. “Licensee” means any person or entity who exercises patent
rights granted under this License.
2.9. Licensor. "Licensor" means (i) any Licensee that makes, sells, offers
for sale, imports or distributes any Implementation, or (ii) a person
or entity that has a licensing obligation to the Implementation as a
result of its membership and/or participation in the Alliance for Open
Media working group that developed the Specification.
2.10. Necessary Claims. "Necessary Claims" means all claims of patents or
patent applications, (a) that currently or at any time in the future,
are owned or controlled by the Licensor, and (b) (i) would be an
Essential Claim as defined by the W3C Policy as of February 5, 2004
(https://www.w3.org/Consortium/Patent-Policy-20040205/#def-essential)
as if the Specification was a W3C Recommendation; or (ii) are infringed
by the Reference Implementation.
2.11. Reference Implementation. “Reference Implementation” means an Encoder
and/or Decoder released by the Alliance for Open Media as a Final
Deliverable.
2.12. Specification. “Specification” means the specification designated by
the Alliance for Open Media as a Final Deliverable for which this
License was issued.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,43 +0,0 @@
# 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.
doxygen = find_program('doxygen', required: false)
dot = find_program('dot', required: false)
if doxygen.found() and dot.found()
conf_data = configuration_data()
conf_data.set('DOXYGEN_INPUT', join_paths(dav1d_src_root, 'include/dav1d'))
conf_data.set('DOXYGEN_STRIP', join_paths(dav1d_src_root, 'include'))
conf_data.set('DOXYGEN_OUTPUT', meson.current_build_dir())
doxyfile = configure_file(input: 'Doxyfile.in',
output: 'Doxyfile',
configuration: conf_data)
custom_target('doc',
build_by_default: false,
command: [doxygen, doxyfile],
output: ['html']
)
endif

View File

@ -1,786 +0,0 @@
/*
* Copyright © 2019, 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.
*/
#include "config.h"
#include "vcs_version.h"
#include <getopt.h>
#include <stdbool.h>
#include <SDL.h>
#include "dav1d/dav1d.h"
#include "common/attributes.h"
#include "tools/input/input.h"
#include "dp_fifo.h"
#include "dp_renderer.h"
#define FRAME_OFFSET_TO_PTS(foff) \
(uint64_t)(((foff) * rd_ctx->spf) * 1000000000.0 + .5)
#define TS_TO_PTS(ts) \
(uint64_t)(((ts) * rd_ctx->timebase) * 1000000000.0 + .5)
// Selected renderer callbacks and cookie
static const Dav1dPlayRenderInfo *renderer_info = { NULL };
/**
* Render context structure
* This structure contains informations necessary
* to be shared between the decoder and the renderer
* threads.
*/
typedef struct render_context
{
Dav1dPlaySettings settings;
Dav1dSettings lib_settings;
// Renderer private data (passed to callbacks)
void *rd_priv;
// Lock to protect access to the context structure
SDL_mutex *lock;
// Timestamp of last displayed frame (in timebase unit)
int64_t last_ts;
// Timestamp of last decoded frame (in timebase unit)
int64_t current_ts;
// Ticks when last frame was received
uint32_t last_ticks;
// PTS time base
double timebase;
// Seconds per frame
double spf;
// Number of frames
uint32_t total;
// Fifo
Dav1dPlayPtrFifo *fifo;
// Custom SDL2 event types
uint32_t event_types;
// User pause state
uint8_t user_paused;
// Internal pause state
uint8_t paused;
// Start of internal pause state
uint32_t pause_start;
// Duration of internal pause state
uint32_t pause_time;
// Seek accumulator
int seek;
// Indicates if termination of the decoder thread was requested
uint8_t dec_should_terminate;
} Dav1dPlayRenderContext;
static void dp_settings_print_usage(const char *const app,
const char *const reason, ...)
{
if (reason) {
va_list args;
va_start(args, reason);
vfprintf(stderr, reason, args);
va_end(args);
fprintf(stderr, "\n\n");
}
fprintf(stderr, "Usage: %s [options]\n\n", app);
fprintf(stderr, "Supported options:\n"
" --input/-i $file: input file\n"
" --untimed/-u: ignore PTS, render as fast as possible\n"
" --framethreads $num: number of frame threads (default: 1)\n"
" --tilethreads $num: number of tile threads (default: 1)\n"
" --pfthreads $num: number of postfilter threads(default: 1)\n"
" --highquality: enable high quality rendering\n"
" --zerocopy/-z: enable zero copy upload path\n"
" --gpugrain/-g: enable GPU grain synthesis\n"
" --version/-v: print version and exit\n"
" --renderer/-r: select renderer backend (default: auto)\n");
exit(1);
}
static unsigned parse_unsigned(const char *const optarg, const int option,
const char *const app)
{
char *end;
const unsigned res = (unsigned) strtoul(optarg, &end, 0);
if (*end || end == optarg)
dp_settings_print_usage(app, "Invalid argument \"%s\" for option %s; should be an integer",
optarg, option);
return res;
}
static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx,
const int argc, char *const *const argv)
{
int o;
Dav1dPlaySettings *settings = &rd_ctx->settings;
Dav1dSettings *lib_settings = &rd_ctx->lib_settings;
// Short options
static const char short_opts[] = "i:vuzgr:";
enum {
ARG_FRAME_THREADS = 256,
ARG_TILE_THREADS,
ARG_POSTFILTER_THREADS,
ARG_HIGH_QUALITY,
};
// Long options
static const struct option long_opts[] = {
{ "input", 1, NULL, 'i' },
{ "version", 0, NULL, 'v' },
{ "untimed", 0, NULL, 'u' },
{ "framethreads", 1, NULL, ARG_FRAME_THREADS },
{ "tilethreads", 1, NULL, ARG_TILE_THREADS },
{ "pfthreads", 1, NULL, ARG_POSTFILTER_THREADS },
{ "highquality", 0, NULL, ARG_HIGH_QUALITY },
{ "zerocopy", 0, NULL, 'z' },
{ "gpugrain", 0, NULL, 'g' },
{ "renderer", 0, NULL, 'r'},
{ NULL, 0, NULL, 0 },
};
while ((o = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (o) {
case 'i':
settings->inputfile = optarg;
break;
case 'v':
fprintf(stderr, "%s\n", dav1d_version());
exit(0);
case 'u':
settings->untimed = true;
break;
case ARG_HIGH_QUALITY:
settings->highquality = true;
break;
case 'z':
settings->zerocopy = true;
break;
case 'g':
settings->gpugrain = true;
break;
case 'r':
settings->renderer_name = optarg;
break;
case ARG_FRAME_THREADS:
lib_settings->n_frame_threads =
parse_unsigned(optarg, ARG_FRAME_THREADS, argv[0]);
break;
case ARG_TILE_THREADS:
lib_settings->n_tile_threads =
parse_unsigned(optarg, ARG_TILE_THREADS, argv[0]);
break;
case ARG_POSTFILTER_THREADS:
lib_settings->n_postfilter_threads =
parse_unsigned(optarg, ARG_POSTFILTER_THREADS, argv[0]);
break;
default:
dp_settings_print_usage(argv[0], NULL);
}
}
if (optind < argc)
dp_settings_print_usage(argv[0],
"Extra/unused arguments found, e.g. '%s'\n", argv[optind]);
if (!settings->inputfile)
dp_settings_print_usage(argv[0], "Input file (-i/--input) is required");
if (settings->renderer_name && strcmp(settings->renderer_name, "auto") == 0)
settings->renderer_name = NULL;
}
/**
* Destroy a Dav1dPlayRenderContext
*/
static void dp_rd_ctx_destroy(Dav1dPlayRenderContext *rd_ctx)
{
assert(rd_ctx != NULL);
renderer_info->destroy_renderer(rd_ctx->rd_priv);
dp_fifo_destroy(rd_ctx->fifo);
SDL_DestroyMutex(rd_ctx->lock);
free(rd_ctx);
}
/**
* Create a Dav1dPlayRenderContext
*
* \note The Dav1dPlayRenderContext must be destroyed
* again by using dp_rd_ctx_destroy.
*/
static Dav1dPlayRenderContext *dp_rd_ctx_create(int argc, char **argv)
{
Dav1dPlayRenderContext *rd_ctx;
// Alloc
rd_ctx = calloc(1, sizeof(Dav1dPlayRenderContext));
if (rd_ctx == NULL) {
return NULL;
}
// Register a custom event to notify our SDL main thread
// about new frames
rd_ctx->event_types = SDL_RegisterEvents(3);
if (rd_ctx->event_types == UINT32_MAX) {
fprintf(stderr, "Failure to create custom SDL event types!\n");
free(rd_ctx);
return NULL;
}
rd_ctx->fifo = dp_fifo_create(5);
if (rd_ctx->fifo == NULL) {
fprintf(stderr, "Failed to create FIFO for output pictures!\n");
free(rd_ctx);
return NULL;
}
rd_ctx->lock = SDL_CreateMutex();
if (rd_ctx->lock == NULL) {
fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError());
dp_fifo_destroy(rd_ctx->fifo);
free(rd_ctx);
return NULL;
}
// Parse and validate arguments
dav1d_default_settings(&rd_ctx->lib_settings);
memset(&rd_ctx->settings, 0, sizeof(rd_ctx->settings));
dp_rd_ctx_parse_args(rd_ctx, argc, argv);
// Select renderer
renderer_info = dp_get_renderer(rd_ctx->settings.renderer_name);
if (renderer_info == NULL) {
printf("No suitable rendered matching %s found.\n",
(rd_ctx->settings.renderer_name) ? rd_ctx->settings.renderer_name : "auto");
} else {
printf("Using %s renderer\n", renderer_info->name);
}
rd_ctx->rd_priv = (renderer_info) ? renderer_info->create_renderer() : NULL;
if (rd_ctx->rd_priv == NULL) {
SDL_DestroyMutex(rd_ctx->lock);
dp_fifo_destroy(rd_ctx->fifo);
free(rd_ctx);
return NULL;
}
return rd_ctx;
}
/**
* Notify about new event
*/
static void dp_rd_ctx_post_event(Dav1dPlayRenderContext *rd_ctx, uint32_t type)
{
SDL_Event event;
SDL_zero(event);
event.type = type;
SDL_PushEvent(&event);
}
/**
* Update the decoder context with a new dav1d picture
*
* Once the decoder decoded a new picture, this call can be used
* to update the internal texture of the render context with the
* new picture.
*/
static void dp_rd_ctx_update_with_dav1d_picture(Dav1dPlayRenderContext *rd_ctx,
Dav1dPicture *dav1d_pic)
{
rd_ctx->current_ts = dav1d_pic->m.timestamp;
renderer_info->update_frame(rd_ctx->rd_priv, dav1d_pic, &rd_ctx->settings);
}
/**
* Toggle pause state
*/
static void dp_rd_ctx_toggle_pause(Dav1dPlayRenderContext *rd_ctx)
{
SDL_LockMutex(rd_ctx->lock);
rd_ctx->user_paused = !rd_ctx->user_paused;
if (rd_ctx->seek)
goto out;
rd_ctx->paused = rd_ctx->user_paused;
uint32_t now = SDL_GetTicks();
if (rd_ctx->paused)
rd_ctx->pause_start = now;
else {
rd_ctx->pause_time += now - rd_ctx->pause_start;
rd_ctx->pause_start = 0;
rd_ctx->last_ticks = now;
}
out:
SDL_UnlockMutex(rd_ctx->lock);
}
/**
* Query pause state
*/
static int dp_rd_ctx_is_paused(Dav1dPlayRenderContext *rd_ctx)
{
int ret;
SDL_LockMutex(rd_ctx->lock);
ret = rd_ctx->paused;
SDL_UnlockMutex(rd_ctx->lock);
return ret;
}
/**
* Request seeking, in seconds
*/
static void dp_rd_ctx_seek(Dav1dPlayRenderContext *rd_ctx, int sec)
{
SDL_LockMutex(rd_ctx->lock);
rd_ctx->seek += sec;
if (!rd_ctx->paused)
rd_ctx->pause_start = SDL_GetTicks();
rd_ctx->paused = 1;
SDL_UnlockMutex(rd_ctx->lock);
}
static int decode_frame(Dav1dPicture **p, Dav1dContext *c,
Dav1dData *data, DemuxerContext *in_ctx);
static inline void destroy_pic(void *a);
/**
* Seek the stream, if requested
*/
static int dp_rd_ctx_handle_seek(Dav1dPlayRenderContext *rd_ctx,
DemuxerContext *in_ctx,
Dav1dContext *c, Dav1dData *data)
{
int res = 0;
SDL_LockMutex(rd_ctx->lock);
if (!rd_ctx->seek)
goto out;
int64_t seek = rd_ctx->seek * 1000000000ULL;
uint64_t pts = TS_TO_PTS(rd_ctx->current_ts);
pts = ((int64_t)pts > -seek) ? pts + seek : 0;
int end = pts >= FRAME_OFFSET_TO_PTS(rd_ctx->total);
if (end)
pts = FRAME_OFFSET_TO_PTS(rd_ctx->total - 1);
uint64_t target_pts = pts;
dav1d_flush(c);
uint64_t shift = FRAME_OFFSET_TO_PTS(5);
while (1) {
if (shift > pts)
shift = pts;
if ((res = input_seek(in_ctx, pts - shift)))
goto out;
Dav1dSequenceHeader seq;
uint64_t cur_pts;
do {
if ((res = input_read(in_ctx, data)))
break;
cur_pts = TS_TO_PTS(data->m.timestamp);
res = dav1d_parse_sequence_header(&seq, data->data, data->sz);
} while (res && cur_pts < pts);
if (!res && cur_pts <= pts)
break;
if (shift > pts)
shift = pts;
pts -= shift;
}
if (!res) {
pts = TS_TO_PTS(data->m.timestamp);
while (pts < target_pts) {
Dav1dPicture *p;
if ((res = decode_frame(&p, c, data, in_ctx)))
break;
if (p) {
pts = TS_TO_PTS(p->m.timestamp);
if (pts < target_pts)
destroy_pic(p);
else {
dp_fifo_push(rd_ctx->fifo, p);
uint32_t type = rd_ctx->event_types + DAV1D_EVENT_SEEK_FRAME;
dp_rd_ctx_post_event(rd_ctx, type);
}
}
}
if (!res) {
rd_ctx->last_ts = data->m.timestamp - rd_ctx->spf / rd_ctx->timebase;
rd_ctx->current_ts = data->m.timestamp;
}
}
out:
rd_ctx->paused = rd_ctx->user_paused;
if (!rd_ctx->paused && rd_ctx->seek) {
uint32_t now = SDL_GetTicks();
rd_ctx->pause_time += now - rd_ctx->pause_start;
rd_ctx->pause_start = 0;
rd_ctx->last_ticks = now;
}
rd_ctx->seek = 0;
SDL_UnlockMutex(rd_ctx->lock);
if (res)
fprintf(stderr, "Error seeking, aborting\n");
return res;
}
/**
* Terminate decoder thread (async)
*/
static void dp_rd_ctx_request_shutdown(Dav1dPlayRenderContext *rd_ctx)
{
SDL_LockMutex(rd_ctx->lock);
rd_ctx->dec_should_terminate = 1;
SDL_UnlockMutex(rd_ctx->lock);
}
/**
* Query state of decoder shutdown request
*/
static int dp_rd_ctx_should_terminate(Dav1dPlayRenderContext *rd_ctx)
{
int ret = 0;
SDL_LockMutex(rd_ctx->lock);
ret = rd_ctx->dec_should_terminate;
SDL_UnlockMutex(rd_ctx->lock);
return ret;
}
/**
* Render the currently available texture
*
* Renders the currently available texture, if any.
*/
static void dp_rd_ctx_render(Dav1dPlayRenderContext *rd_ctx)
{
SDL_LockMutex(rd_ctx->lock);
// Calculate time since last frame was received
uint32_t ticks_now = SDL_GetTicks();
uint32_t ticks_diff = (rd_ctx->last_ticks != 0) ? ticks_now - rd_ctx->last_ticks : 0;
// Calculate when to display the frame
int64_t ts_diff = rd_ctx->current_ts - rd_ctx->last_ts;
int32_t pts_diff = (ts_diff * rd_ctx->timebase) * 1000.0 + .5;
int32_t wait_time = pts_diff - ticks_diff;
// In untimed mode, simply don't wait
if (rd_ctx->settings.untimed)
wait_time = 0;
// This way of timing the playback is not accurate, as there is no guarantee
// that SDL_Delay will wait for exactly the requested amount of time so in a
// accurate player this would need to be done in a better way.
if (wait_time > 0) {
SDL_Delay(wait_time);
} else if (wait_time < -10 && !rd_ctx->paused) { // Do not warn for minor time drifts
fprintf(stderr, "Frame displayed %f seconds too late\n", wait_time / 1000.0);
}
renderer_info->render(rd_ctx->rd_priv, &rd_ctx->settings);
rd_ctx->last_ts = rd_ctx->current_ts;
rd_ctx->last_ticks = SDL_GetTicks();
SDL_UnlockMutex(rd_ctx->lock);
}
static int decode_frame(Dav1dPicture **p, Dav1dContext *c,
Dav1dData *data, DemuxerContext *in_ctx)
{
int res;
// Send data packets we got from the demuxer to dav1d
if ((res = dav1d_send_data(c, data)) < 0) {
// On EAGAIN, dav1d can not consume more data and
// dav1d_get_picture needs to be called first, which
// will happen below, so just keep going in that case
// and do not error out.
if (res != DAV1D_ERR(EAGAIN)) {
dav1d_data_unref(data);
goto err;
}
}
*p = calloc(1, sizeof(**p));
// Try to get a decoded frame
if ((res = dav1d_get_picture(c, *p)) < 0) {
// In all error cases, even EAGAIN, p needs to be freed as
// it is never added to the queue and would leak.
free(*p);
*p = NULL;
// On EAGAIN, it means dav1d has not enough data to decode
// therefore this is not a decoding error but just means
// we need to feed it more data, which happens in the next
// run of the decoder loop.
if (res != DAV1D_ERR(EAGAIN))
goto err;
}
return data->sz == 0 ? input_read(in_ctx, data) : 0;
err:
fprintf(stderr, "Error decoding frame: %s\n",
strerror(-res));
return res;
}
static inline void destroy_pic(void *a)
{
Dav1dPicture *p = (Dav1dPicture *)a;
dav1d_picture_unref(p);
free(p);
}
/* Decoder thread "main" function */
static int decoder_thread_main(void *cookie)
{
Dav1dPlayRenderContext *rd_ctx = cookie;
Dav1dPicture *p;
Dav1dContext *c = NULL;
Dav1dData data;
DemuxerContext *in_ctx = NULL;
int res = 0;
unsigned total, timebase[2], fps[2];
Dav1dPlaySettings settings = rd_ctx->settings;
if ((res = input_open(&in_ctx, "ivf",
settings.inputfile,
fps, &total, timebase)) < 0)
{
fprintf(stderr, "Failed to open demuxer\n");
res = 1;
goto cleanup;
}
rd_ctx->timebase = (double)timebase[1] / timebase[0];
rd_ctx->spf = (double)fps[1] / fps[0];
rd_ctx->total = total;
if ((res = dav1d_open(&c, &rd_ctx->lib_settings))) {
fprintf(stderr, "Failed opening dav1d decoder\n");
res = 1;
goto cleanup;
}
if ((res = input_read(in_ctx, &data)) < 0) {
fprintf(stderr, "Failed demuxing input\n");
res = 1;
goto cleanup;
}
// Decoder loop
while (1) {
if (dp_rd_ctx_should_terminate(rd_ctx) ||
(res = dp_rd_ctx_handle_seek(rd_ctx, in_ctx, c, &data)) ||
(res = decode_frame(&p, c, &data, in_ctx)))
{
break;
}
else if (p) {
// Queue frame
SDL_LockMutex(rd_ctx->lock);
int seek = rd_ctx->seek;
SDL_UnlockMutex(rd_ctx->lock);
if (!seek) {
dp_fifo_push(rd_ctx->fifo, p);
uint32_t type = rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME;
dp_rd_ctx_post_event(rd_ctx, type);
}
}
}
// Release remaining data
if (data.sz > 0)
dav1d_data_unref(&data);
// Do not drain in case an error occured and caused us to leave the
// decoding loop early.
if (res < 0)
goto cleanup;
// Drain decoder
// When there is no more data to feed to the decoder, for example
// because the file ended, we still need to request pictures, as
// even though we do not have more data, there can be frames decoded
// from data we sent before. So we need to call dav1d_get_picture until
// we get an EAGAIN error.
do {
if (dp_rd_ctx_should_terminate(rd_ctx))
break;
p = calloc(1, sizeof(*p));
res = dav1d_get_picture(c, p);
if (res < 0) {
free(p);
if (res != DAV1D_ERR(EAGAIN)) {
fprintf(stderr, "Error decoding frame: %s\n",
strerror(-res));
break;
}
} else {
// Queue frame
dp_fifo_push(rd_ctx->fifo, p);
uint32_t type = rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME;
dp_rd_ctx_post_event(rd_ctx, type);
}
} while (res != DAV1D_ERR(EAGAIN));
cleanup:
dp_rd_ctx_post_event(rd_ctx, rd_ctx->event_types + DAV1D_EVENT_DEC_QUIT);
if (in_ctx)
input_close(in_ctx);
if (c)
dav1d_close(&c);
return (res != DAV1D_ERR(EAGAIN) && res < 0);
}
int main(int argc, char **argv)
{
SDL_Thread *decoder_thread;
// Check for version mismatch between library and tool
const char *version = dav1d_version();
if (strcmp(version, DAV1D_VERSION)) {
fprintf(stderr, "Version mismatch (library: %s, executable: %s)\n",
version, DAV1D_VERSION);
return 1;
}
// Init SDL2 library
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
return 10;
// Create render context
Dav1dPlayRenderContext *rd_ctx = dp_rd_ctx_create(argc, argv);
if (rd_ctx == NULL) {
fprintf(stderr, "Failed creating render context\n");
return 5;
}
if (rd_ctx->settings.zerocopy) {
if (renderer_info->alloc_pic) {
rd_ctx->lib_settings.allocator = (Dav1dPicAllocator) {
.cookie = rd_ctx->rd_priv,
.alloc_picture_callback = renderer_info->alloc_pic,
.release_picture_callback = renderer_info->release_pic,
};
} else {
fprintf(stderr, "--zerocopy unsupported by selected renderer\n");
}
}
if (rd_ctx->settings.gpugrain) {
if (renderer_info->supports_gpu_grain) {
rd_ctx->lib_settings.apply_grain = 0;
} else {
fprintf(stderr, "--gpugrain unsupported by selected renderer\n");
}
}
// Start decoder thread
decoder_thread = SDL_CreateThread(decoder_thread_main, "Decoder thread", rd_ctx);
// Main loop
#define NUM_MAX_EVENTS 8
SDL_Event events[NUM_MAX_EVENTS];
int num_frame_events = 0;
uint32_t start_time = 0, n_out = 0;
while (1) {
int num_events = 0;
SDL_WaitEvent(NULL);
while (num_events < NUM_MAX_EVENTS && SDL_PollEvent(&events[num_events++]))
break;
for (int i = 0; i < num_events; ++i) {
SDL_Event *e = &events[i];
if (e->type == SDL_QUIT) {
dp_rd_ctx_request_shutdown(rd_ctx);
dp_fifo_flush(rd_ctx->fifo, destroy_pic);
SDL_FlushEvent(rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME);
SDL_FlushEvent(rd_ctx->event_types + DAV1D_EVENT_SEEK_FRAME);
num_frame_events = 0;
} else if (e->type == SDL_WINDOWEVENT) {
if (e->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
// TODO: Handle window resizes
} else if(e->window.event == SDL_WINDOWEVENT_EXPOSED) {
dp_rd_ctx_render(rd_ctx);
}
} else if (e->type == SDL_KEYDOWN) {
SDL_KeyboardEvent *kbde = (SDL_KeyboardEvent *)e;
if (kbde->keysym.sym == SDLK_SPACE) {
dp_rd_ctx_toggle_pause(rd_ctx);
} else if (kbde->keysym.sym == SDLK_LEFT ||
kbde->keysym.sym == SDLK_RIGHT)
{
if (kbde->keysym.sym == SDLK_LEFT)
dp_rd_ctx_seek(rd_ctx, -5);
else if (kbde->keysym.sym == SDLK_RIGHT)
dp_rd_ctx_seek(rd_ctx, +5);
dp_fifo_flush(rd_ctx->fifo, destroy_pic);
SDL_FlushEvent(rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME);
num_frame_events = 0;
}
} else if (e->type == rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME) {
num_frame_events++;
// Store current ticks for stats calculation
if (start_time == 0)
start_time = SDL_GetTicks();
} else if (e->type == rd_ctx->event_types + DAV1D_EVENT_SEEK_FRAME) {
// Dequeue frame and update the render context with it
Dav1dPicture *p = dp_fifo_shift(rd_ctx->fifo);
// Do not update textures during termination
if (!dp_rd_ctx_should_terminate(rd_ctx)) {
dp_rd_ctx_update_with_dav1d_picture(rd_ctx, p);
n_out++;
}
destroy_pic(p);
} else if (e->type == rd_ctx->event_types + DAV1D_EVENT_DEC_QUIT) {
goto out;
}
}
if (num_frame_events && !dp_rd_ctx_is_paused(rd_ctx)) {
// Dequeue frame and update the render context with it
Dav1dPicture *p = dp_fifo_shift(rd_ctx->fifo);
// Do not update textures during termination
if (!dp_rd_ctx_should_terminate(rd_ctx)) {
dp_rd_ctx_update_with_dav1d_picture(rd_ctx, p);
dp_rd_ctx_render(rd_ctx);
n_out++;
}
destroy_pic(p);
num_frame_events--;
}
}
out:;
// Print stats
uint32_t time_ms = SDL_GetTicks() - start_time - rd_ctx->pause_time;
printf("Decoded %u frames in %d seconds, avg %.02f fps\n",
n_out, time_ms / 1000, n_out/ (time_ms / 1000.0));
int decoder_ret = 0;
SDL_WaitThread(decoder_thread, &decoder_ret);
dp_rd_ctx_destroy(rd_ctx);
return decoder_ret;
}

View File

@ -1,147 +0,0 @@
/*
* Copyright © 2019, 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.
*/
#include <SDL.h>
#include <assert.h>
#include "dp_fifo.h"
// FIFO structure
struct dp_fifo
{
SDL_mutex *lock;
SDL_cond *cond_change;
size_t capacity;
size_t count;
void **entries;
int push_wait;
int flush;
};
Dav1dPlayPtrFifo *dp_fifo_create(size_t capacity)
{
Dav1dPlayPtrFifo *fifo;
assert(capacity > 0);
if (capacity <= 0)
return NULL;
fifo = malloc(sizeof(*fifo));
if (fifo == NULL)
return NULL;
fifo->capacity = capacity;
fifo->count = 0;
fifo->push_wait = 0;
fifo->flush = 0;
fifo->lock = SDL_CreateMutex();
if (fifo->lock == NULL) {
free(fifo);
return NULL;
}
fifo->cond_change = SDL_CreateCond();
if (fifo->cond_change == NULL) {
SDL_DestroyMutex(fifo->lock);
free(fifo);
return NULL;
}
fifo->entries = calloc(capacity, sizeof(void*));
if (fifo->entries == NULL) {
dp_fifo_destroy(fifo);
return NULL;
}
return fifo;
}
// Destroy FIFO
void dp_fifo_destroy(Dav1dPlayPtrFifo *fifo)
{
assert(fifo->count == 0);
SDL_DestroyMutex(fifo->lock);
SDL_DestroyCond(fifo->cond_change);
free(fifo->entries);
free(fifo);
}
// Push to FIFO
void dp_fifo_push(Dav1dPlayPtrFifo *fifo, void *element)
{
SDL_LockMutex(fifo->lock);
while (fifo->count == fifo->capacity) {
fifo->push_wait = 1;
SDL_CondWait(fifo->cond_change, fifo->lock);
fifo->push_wait = 0;
if (fifo->flush) {
SDL_CondSignal(fifo->cond_change);
SDL_UnlockMutex(fifo->lock);
return;
}
}
fifo->entries[fifo->count++] = element;
if (fifo->count == 1)
SDL_CondSignal(fifo->cond_change);
SDL_UnlockMutex(fifo->lock);
}
// Helper that shifts the FIFO array
static void *dp_fifo_array_shift(void **arr, size_t len)
{
void *shifted_element = arr[0];
for (size_t i = 1; i < len; ++i)
arr[i-1] = arr[i];
return shifted_element;
}
// Get item from FIFO
void *dp_fifo_shift(Dav1dPlayPtrFifo *fifo)
{
SDL_LockMutex(fifo->lock);
while (fifo->count == 0)
SDL_CondWait(fifo->cond_change, fifo->lock);
void *res = dp_fifo_array_shift(fifo->entries, fifo->count--);
if (fifo->count == fifo->capacity - 1)
SDL_CondSignal(fifo->cond_change);
SDL_UnlockMutex(fifo->lock);
return res;
}
void dp_fifo_flush(Dav1dPlayPtrFifo *fifo, void (*destroy_elem)(void *))
{
SDL_LockMutex(fifo->lock);
fifo->flush = 1;
if (fifo->push_wait) {
SDL_CondSignal(fifo->cond_change);
SDL_CondWait(fifo->cond_change, fifo->lock);
}
while (fifo->count)
destroy_elem(fifo->entries[--fifo->count]);
fifo->flush = 0;
SDL_UnlockMutex(fifo->lock);
}

View File

@ -1,63 +0,0 @@
/*
* Copyright © 2019, 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.
*/
/*
* Dav1dPlay FIFO helper
*/
typedef struct dp_fifo Dav1dPlayPtrFifo;
/* Create a FIFO
*
* Creates a FIFO with the given capacity.
* If the capacity is reached, new inserts into the FIFO
* will block until enough space is available again.
*/
Dav1dPlayPtrFifo *dp_fifo_create(size_t capacity);
/* Destroy a FIFO
*
* The FIFO must be empty before it is destroyed!
*/
void dp_fifo_destroy(Dav1dPlayPtrFifo *fifo);
/* Shift FIFO
*
* Return the first item from the FIFO, thereby removing it from
* the FIFO and making room for new entries.
*/
void *dp_fifo_shift(Dav1dPlayPtrFifo *fifo);
/* Push to FIFO
*
* Add an item to the end of the FIFO.
* If the FIFO is full, this call will block until there is again enough
* space in the FIFO, so calling this from the "consumer" thread if no
* other thread will call dp_fifo_shift will lead to a deadlock.
*/
void dp_fifo_push(Dav1dPlayPtrFifo *fifo, void *element);
void dp_fifo_flush(Dav1dPlayPtrFifo *fifo, void (*destroy_elem)(void *));

View File

@ -1,135 +0,0 @@
/*
* 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.
*/
#include <inttypes.h>
#include <string.h>
#include "dav1d/dav1d.h"
#include <SDL.h>
#ifdef HAVE_PLACEBO
# include <libplacebo/config.h>
#endif
// Check libplacebo Vulkan rendering
#if defined(HAVE_VULKAN) && defined(SDL_VIDEO_VULKAN)
# if defined(PL_HAVE_VULKAN) && PL_HAVE_VULKAN
# define HAVE_RENDERER_PLACEBO
# define HAVE_PLACEBO_VULKAN
# endif
#endif
// Check libplacebo OpenGL rendering
#if defined(PL_HAVE_OPENGL) && PL_HAVE_OPENGL
# define HAVE_RENDERER_PLACEBO
# define HAVE_PLACEBO_OPENGL
#endif
/**
* Settings structure
* Hold all settings available for the player,
* this is usually filled by parsing arguments
* from the console.
*/
typedef struct {
const char *inputfile;
const char *renderer_name;
int highquality;
int untimed;
int zerocopy;
int gpugrain;
} Dav1dPlaySettings;
#define WINDOW_WIDTH 910
#define WINDOW_HEIGHT 512
enum {
DAV1D_EVENT_NEW_FRAME,
DAV1D_EVENT_SEEK_FRAME,
DAV1D_EVENT_DEC_QUIT
};
/**
* Renderer info
*/
typedef struct rdr_info
{
// Renderer name
const char *name;
// Cookie passed to the renderer implementation callbacks
void *cookie;
// Callback to create the renderer
void* (*create_renderer)();
// Callback to destroy the renderer
void (*destroy_renderer)(void *cookie);
// Callback to the render function that renders a prevously sent frame
void (*render)(void *cookie, const Dav1dPlaySettings *settings);
// Callback to the send frame function, _may_ also unref dav1d_pic!
int (*update_frame)(void *cookie, Dav1dPicture *dav1d_pic,
const Dav1dPlaySettings *settings);
// Callback for alloc/release pictures (optional)
int (*alloc_pic)(Dav1dPicture *pic, void *cookie);
void (*release_pic)(Dav1dPicture *pic, void *cookie);
// Whether or not this renderer can apply on-GPU film grain synthesis
int supports_gpu_grain;
} Dav1dPlayRenderInfo;
extern const Dav1dPlayRenderInfo rdr_placebo_vk;
extern const Dav1dPlayRenderInfo rdr_placebo_gl;
extern const Dav1dPlayRenderInfo rdr_sdl;
// Available renderes ordered by priority
static const Dav1dPlayRenderInfo* const dp_renderers[] = {
&rdr_placebo_vk,
&rdr_placebo_gl,
&rdr_sdl,
};
static inline const Dav1dPlayRenderInfo *dp_get_renderer(const char *name)
{
for (size_t i = 0; i < (sizeof(dp_renderers)/sizeof(*dp_renderers)); ++i)
{
if (dp_renderers[i]->name == NULL)
continue;
if (name == NULL || strcmp(name, dp_renderers[i]->name) == 0) {
return dp_renderers[i];
}
}
return NULL;
}
static inline SDL_Window *dp_create_sdl_window(int window_flags)
{
SDL_Window *win;
window_flags |= SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI;
win = SDL_CreateWindow("Dav1dPlay", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH, WINDOW_HEIGHT, window_flags);
SDL_SetWindowResizable(win, SDL_TRUE);
return win;
}

View File

@ -1,423 +0,0 @@
/*
* 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.
*/
#include "dp_renderer.h"
#ifdef HAVE_RENDERER_PLACEBO
#include <assert.h>
#include <libplacebo/renderer.h>
#include <libplacebo/utils/dav1d.h>
#ifdef HAVE_PLACEBO_VULKAN
# include <libplacebo/vulkan.h>
# include <SDL_vulkan.h>
#endif
#ifdef HAVE_PLACEBO_OPENGL
# include <libplacebo/opengl.h>
# include <SDL_opengl.h>
#endif
/**
* Renderer context for libplacebo
*/
typedef struct renderer_priv_ctx
{
// SDL window
SDL_Window *win;
// Placebo context
struct pl_context *ctx;
// Placebo renderer
struct pl_renderer *renderer;
#ifdef HAVE_PLACEBO_VULKAN
// Placebo Vulkan handle
const struct pl_vulkan *vk;
// Placebo Vulkan instance
const struct pl_vk_inst *vk_inst;
// Vulkan surface
VkSurfaceKHR surf;
#endif
#ifdef HAVE_PLACEBO_OPENGL
// Placebo OpenGL handle
const struct pl_opengl *gl;
#endif
// Placebo GPU
const struct pl_gpu *gpu;
// Placebo swapchain
const struct pl_swapchain *swapchain;
// Lock protecting access to the texture
SDL_mutex *lock;
// Image to render, and planes backing them
struct pl_frame image;
const struct pl_tex *plane_tex[3];
} Dav1dPlayRendererPrivateContext;
static Dav1dPlayRendererPrivateContext*
placebo_renderer_create_common(int window_flags)
{
// Create Window
SDL_Window *sdlwin = dp_create_sdl_window(window_flags | SDL_WINDOW_RESIZABLE);
if (sdlwin == NULL)
return NULL;
// Alloc
Dav1dPlayRendererPrivateContext *rd_priv_ctx = malloc(sizeof(Dav1dPlayRendererPrivateContext));
if (rd_priv_ctx == NULL) {
return NULL;
}
*rd_priv_ctx = (Dav1dPlayRendererPrivateContext) {0};
rd_priv_ctx->win = sdlwin;
// Init libplacebo
rd_priv_ctx->ctx = pl_context_create(PL_API_VER, &(struct pl_context_params) {
.log_cb = pl_log_color,
#ifndef NDEBUG
.log_level = PL_LOG_DEBUG,
#else
.log_level = PL_LOG_WARN,
#endif
});
if (rd_priv_ctx->ctx == NULL) {
free(rd_priv_ctx);
return NULL;
}
// Create Mutex
rd_priv_ctx->lock = SDL_CreateMutex();
if (rd_priv_ctx->lock == NULL) {
fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError());
pl_context_destroy(&(rd_priv_ctx->ctx));
free(rd_priv_ctx);
return NULL;
}
return rd_priv_ctx;
}
#ifdef HAVE_PLACEBO_OPENGL
static void *placebo_renderer_create_gl()
{
SDL_Window *sdlwin = NULL;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
// Common init
Dav1dPlayRendererPrivateContext *rd_priv_ctx =
placebo_renderer_create_common(SDL_WINDOW_OPENGL);
if (rd_priv_ctx == NULL)
return NULL;
sdlwin = rd_priv_ctx->win;
// Init OpenGL
struct pl_opengl_params params = pl_opengl_default_params;
# ifndef NDEBUG
params.debug = true;
# endif
SDL_GLContext glcontext = SDL_GL_CreateContext(sdlwin);
SDL_GL_MakeCurrent(sdlwin, glcontext);
rd_priv_ctx->gl = pl_opengl_create(rd_priv_ctx->ctx, &params);
if (!rd_priv_ctx->gl) {
fprintf(stderr, "Failed creating opengl device!\n");
exit(2);
}
rd_priv_ctx->swapchain = pl_opengl_create_swapchain(rd_priv_ctx->gl,
&(struct pl_opengl_swapchain_params) {
.swap_buffers = (void (*)(void *)) SDL_GL_SwapWindow,
.priv = sdlwin,
});
if (!rd_priv_ctx->swapchain) {
fprintf(stderr, "Failed creating opengl swapchain!\n");
exit(2);
}
int w = WINDOW_WIDTH, h = WINDOW_HEIGHT;
SDL_GL_GetDrawableSize(sdlwin, &w, &h);
if (!pl_swapchain_resize(rd_priv_ctx->swapchain, &w, &h)) {
fprintf(stderr, "Failed resizing vulkan swapchain!\n");
exit(2);
}
rd_priv_ctx->gpu = rd_priv_ctx->gl->gpu;
if (w != WINDOW_WIDTH || h != WINDOW_HEIGHT)
printf("Note: window dimensions differ (got %dx%d)\n", w, h);
return rd_priv_ctx;
}
#endif
#ifdef HAVE_PLACEBO_VULKAN
static void *placebo_renderer_create_vk()
{
SDL_Window *sdlwin = NULL;
// Common init
Dav1dPlayRendererPrivateContext *rd_priv_ctx =
placebo_renderer_create_common(SDL_WINDOW_VULKAN);
if (rd_priv_ctx == NULL)
return NULL;
sdlwin = rd_priv_ctx->win;
// Init Vulkan
unsigned num = 0;
if (!SDL_Vulkan_GetInstanceExtensions(sdlwin, &num, NULL)) {
fprintf(stderr, "Failed enumerating Vulkan extensions: %s\n", SDL_GetError());
exit(1);
}
const char **extensions = malloc(num * sizeof(const char *));
assert(extensions);
SDL_bool ok = SDL_Vulkan_GetInstanceExtensions(sdlwin, &num, extensions);
if (!ok) {
fprintf(stderr, "Failed getting Vk instance extensions\n");
exit(1);
}
if (num > 0) {
printf("Requesting %d additional Vulkan extensions:\n", num);
for (unsigned i = 0; i < num; i++)
printf(" %s\n", extensions[i]);
}
struct pl_vk_inst_params iparams = pl_vk_inst_default_params;
iparams.extensions = extensions;
iparams.num_extensions = num;
rd_priv_ctx->vk_inst = pl_vk_inst_create(rd_priv_ctx->ctx, &iparams);
if (!rd_priv_ctx->vk_inst) {
fprintf(stderr, "Failed creating Vulkan instance!\n");
exit(1);
}
free(extensions);
if (!SDL_Vulkan_CreateSurface(sdlwin, rd_priv_ctx->vk_inst->instance, &rd_priv_ctx->surf)) {
fprintf(stderr, "Failed creating vulkan surface: %s\n", SDL_GetError());
exit(1);
}
struct pl_vulkan_params params = pl_vulkan_default_params;
params.instance = rd_priv_ctx->vk_inst->instance;
params.surface = rd_priv_ctx->surf;
params.allow_software = true;
rd_priv_ctx->vk = pl_vulkan_create(rd_priv_ctx->ctx, &params);
if (!rd_priv_ctx->vk) {
fprintf(stderr, "Failed creating vulkan device!\n");
exit(2);
}
// Create swapchain
rd_priv_ctx->swapchain = pl_vulkan_create_swapchain(rd_priv_ctx->vk,
&(struct pl_vulkan_swapchain_params) {
.surface = rd_priv_ctx->surf,
.present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR,
});
if (!rd_priv_ctx->swapchain) {
fprintf(stderr, "Failed creating vulkan swapchain!\n");
exit(2);
}
int w = WINDOW_WIDTH, h = WINDOW_HEIGHT;
if (!pl_swapchain_resize(rd_priv_ctx->swapchain, &w, &h)) {
fprintf(stderr, "Failed resizing vulkan swapchain!\n");
exit(2);
}
rd_priv_ctx->gpu = rd_priv_ctx->vk->gpu;
if (w != WINDOW_WIDTH || h != WINDOW_HEIGHT)
printf("Note: window dimensions differ (got %dx%d)\n", w, h);
return rd_priv_ctx;
}
#endif
static void placebo_renderer_destroy(void *cookie)
{
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL);
pl_renderer_destroy(&(rd_priv_ctx->renderer));
pl_swapchain_destroy(&(rd_priv_ctx->swapchain));
for (int i = 0; i < 3; i++)
pl_tex_destroy(rd_priv_ctx->gpu, &(rd_priv_ctx->plane_tex[i]));
#ifdef HAVE_PLACEBO_VULKAN
if (rd_priv_ctx->vk) {
pl_vulkan_destroy(&(rd_priv_ctx->vk));
vkDestroySurfaceKHR(rd_priv_ctx->vk_inst->instance, rd_priv_ctx->surf, NULL);
pl_vk_inst_destroy(&(rd_priv_ctx->vk_inst));
}
#endif
#ifdef HAVE_PLACEBO_OPENGL
if (rd_priv_ctx->gl)
pl_opengl_destroy(&(rd_priv_ctx->gl));
#endif
SDL_DestroyWindow(rd_priv_ctx->win);
pl_context_destroy(&(rd_priv_ctx->ctx));
}
static void placebo_render(void *cookie, const Dav1dPlaySettings *settings)
{
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL);
SDL_LockMutex(rd_priv_ctx->lock);
if (!rd_priv_ctx->image.num_planes) {
SDL_UnlockMutex(rd_priv_ctx->lock);
return;
}
// Prepare rendering
if (rd_priv_ctx->renderer == NULL) {
rd_priv_ctx->renderer = pl_renderer_create(rd_priv_ctx->ctx, rd_priv_ctx->gpu);
}
struct pl_swapchain_frame frame;
bool ok = pl_swapchain_start_frame(rd_priv_ctx->swapchain, &frame);
if (!ok) {
SDL_UnlockMutex(rd_priv_ctx->lock);
return;
}
struct pl_render_params render_params = {0};
if (settings->highquality)
render_params = pl_render_default_params;
struct pl_frame target;
pl_frame_from_swapchain(&target, &frame);
pl_rect2df_aspect_copy(&target.crop, &rd_priv_ctx->image.crop, 0.0);
if (pl_frame_is_cropped(&target))
pl_tex_clear(rd_priv_ctx->gpu, frame.fbo, (float[4]){ 0.0 });
if (!pl_render_image(rd_priv_ctx->renderer, &rd_priv_ctx->image, &target, &render_params)) {
fprintf(stderr, "Failed rendering frame!\n");
pl_tex_clear(rd_priv_ctx->gpu, frame.fbo, (float[4]){ 1.0 });
}
ok = pl_swapchain_submit_frame(rd_priv_ctx->swapchain);
if (!ok) {
fprintf(stderr, "Failed submitting frame!\n");
SDL_UnlockMutex(rd_priv_ctx->lock);
return;
}
pl_swapchain_swap_buffers(rd_priv_ctx->swapchain);
SDL_UnlockMutex(rd_priv_ctx->lock);
}
static int placebo_upload_image(void *cookie, Dav1dPicture *dav1d_pic,
const Dav1dPlaySettings *settings)
{
Dav1dPlayRendererPrivateContext *p = cookie;
assert(p != NULL);
int ret = 0;
if (!dav1d_pic)
return ret;
struct pl_dav1d_upload_params params = {
.picture = dav1d_pic,
.film_grain = settings->gpugrain,
.gpu_allocated = settings->zerocopy,
.asynchronous = true,
};
SDL_LockMutex(p->lock);
if (!pl_upload_dav1dpicture(p->gpu, &p->image, p->plane_tex, &params)) {
fprintf(stderr, "Failed uploading planes!\n");
p->image = (struct pl_frame) {0};
ret = -1;
}
SDL_UnlockMutex(p->lock);
return ret;
}
static int placebo_alloc_pic(Dav1dPicture *const pic, void *cookie)
{
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL);
SDL_LockMutex(rd_priv_ctx->lock);
int ret = pl_allocate_dav1dpicture(pic, rd_priv_ctx->gpu);
SDL_UnlockMutex(rd_priv_ctx->lock);
return ret;
}
static void placebo_release_pic(Dav1dPicture *pic, void *cookie)
{
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL);
SDL_LockMutex(rd_priv_ctx->lock);
pl_release_dav1dpicture(pic, rd_priv_ctx->gpu);
SDL_UnlockMutex(rd_priv_ctx->lock);
}
#ifdef HAVE_PLACEBO_VULKAN
const Dav1dPlayRenderInfo rdr_placebo_vk = {
.name = "placebo-vk",
.create_renderer = placebo_renderer_create_vk,
.destroy_renderer = placebo_renderer_destroy,
.render = placebo_render,
.update_frame = placebo_upload_image,
.alloc_pic = placebo_alloc_pic,
.release_pic = placebo_release_pic,
.supports_gpu_grain = 1,
};
#else
const Dav1dPlayRenderInfo rdr_placebo_vk = { NULL };
#endif
#ifdef HAVE_PLACEBO_OPENGL
const Dav1dPlayRenderInfo rdr_placebo_gl = {
.name = "placebo-gl",
.create_renderer = placebo_renderer_create_gl,
.destroy_renderer = placebo_renderer_destroy,
.render = placebo_render,
.update_frame = placebo_upload_image,
.supports_gpu_grain = 1,
};
#else
const Dav1dPlayRenderInfo rdr_placebo_gl = { NULL };
#endif
#else
const Dav1dPlayRenderInfo rdr_placebo_vk = { NULL };
const Dav1dPlayRenderInfo rdr_placebo_gl = { NULL };
#endif

View File

@ -1,164 +0,0 @@
/*
* 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.
*/
#include "dp_renderer.h"
#include <assert.h>
/**
* Renderer context for SDL
*/
typedef struct renderer_priv_ctx
{
// SDL window
SDL_Window *win;
// SDL renderer
SDL_Renderer *renderer;
// Lock protecting access to the texture
SDL_mutex *lock;
// Texture to render
SDL_Texture *tex;
} Dav1dPlayRendererPrivateContext;
static void *sdl_renderer_create()
{
SDL_Window *win = dp_create_sdl_window(0);
if (win == NULL)
return NULL;
// Alloc
Dav1dPlayRendererPrivateContext *rd_priv_ctx = malloc(sizeof(Dav1dPlayRendererPrivateContext));
if (rd_priv_ctx == NULL) {
return NULL;
}
rd_priv_ctx->win = win;
// Create renderer
rd_priv_ctx->renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
// Set scale quality
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
// Create Mutex
rd_priv_ctx->lock = SDL_CreateMutex();
if (rd_priv_ctx->lock == NULL) {
fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError());
free(rd_priv_ctx);
return NULL;
}
rd_priv_ctx->tex = NULL;
return rd_priv_ctx;
}
static void sdl_renderer_destroy(void *cookie)
{
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL);
SDL_DestroyRenderer(rd_priv_ctx->renderer);
SDL_DestroyMutex(rd_priv_ctx->lock);
free(rd_priv_ctx);
}
static void sdl_render(void *cookie, const Dav1dPlaySettings *settings)
{
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL);
SDL_LockMutex(rd_priv_ctx->lock);
if (rd_priv_ctx->tex == NULL) {
SDL_UnlockMutex(rd_priv_ctx->lock);
return;
}
// Display the frame
SDL_RenderClear(rd_priv_ctx->renderer);
SDL_RenderCopy(rd_priv_ctx->renderer, rd_priv_ctx->tex, NULL, NULL);
SDL_RenderPresent(rd_priv_ctx->renderer);
SDL_UnlockMutex(rd_priv_ctx->lock);
}
static int sdl_update_texture(void *cookie, Dav1dPicture *dav1d_pic,
const Dav1dPlaySettings *settings)
{
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL);
SDL_LockMutex(rd_priv_ctx->lock);
if (dav1d_pic == NULL) {
rd_priv_ctx->tex = NULL;
SDL_UnlockMutex(rd_priv_ctx->lock);
return 0;
}
int width = dav1d_pic->p.w;
int height = dav1d_pic->p.h;
int tex_w = width;
int tex_h = height;
enum Dav1dPixelLayout dav1d_layout = dav1d_pic->p.layout;
if (DAV1D_PIXEL_LAYOUT_I420 != dav1d_layout || dav1d_pic->p.bpc != 8) {
fprintf(stderr, "Unsupported pixel format, only 8bit 420 supported so far.\n");
exit(50);
}
SDL_Texture *texture = rd_priv_ctx->tex;
if (texture != NULL) {
SDL_QueryTexture(texture, NULL, NULL, &tex_w, &tex_h);
if (tex_w != width || tex_h != height) {
SDL_DestroyTexture(texture);
texture = NULL;
}
}
if (texture == NULL) {
texture = SDL_CreateTexture(rd_priv_ctx->renderer, SDL_PIXELFORMAT_IYUV,
SDL_TEXTUREACCESS_STREAMING, width, height);
}
SDL_UpdateYUVTexture(texture, NULL,
dav1d_pic->data[0], (int)dav1d_pic->stride[0], // Y
dav1d_pic->data[1], (int)dav1d_pic->stride[1], // U
dav1d_pic->data[2], (int)dav1d_pic->stride[1] // V
);
rd_priv_ctx->tex = texture;
SDL_UnlockMutex(rd_priv_ctx->lock);
return 0;
}
const Dav1dPlayRenderInfo rdr_sdl = {
.name = "sdl",
.create_renderer = sdl_renderer_create,
.destroy_renderer = sdl_renderer_destroy,
.render = sdl_render,
.update_frame = sdl_update_texture
};

View File

@ -1,74 +0,0 @@
# 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 examples
#
# Leave subdir if examples are disabled
if not get_option('enable_examples')
subdir_done()
endif
# dav1d player sources
dav1dplay_sources = files(
'dav1dplay.c',
'dp_fifo.c',
'dp_renderer_placebo.c',
'dp_renderer_sdl.c',
)
sdl2_dependency = dependency('sdl2', version: '>= 2.0.1', required: true)
if sdl2_dependency.found()
dav1dplay_deps = [sdl2_dependency, libm_dependency]
dav1dplay_cflags = []
placebo_dependency = dependency('libplacebo', version: '>= 3.110.0', required: false)
if placebo_dependency.found()
dav1dplay_deps += placebo_dependency
dav1dplay_cflags += '-DHAVE_PLACEBO'
# If libplacebo is found, we might be able to use Vulkan
# with it, in which case we need the Vulkan library too.
vulkan_dependency = dependency('vulkan', required: false)
if vulkan_dependency.found()
dav1dplay_deps += vulkan_dependency
dav1dplay_cflags += '-DHAVE_VULKAN'
endif
endif
dav1dplay = executable('dav1dplay',
dav1dplay_sources,
rev_target,
link_with : [libdav1d, dav1d_input_objs],
include_directories : [dav1d_inc_dirs],
dependencies : [getopt_dependency, dav1dplay_deps],
install : true,
c_args : dav1dplay_cflags,
)
endif

View File

@ -1,16 +0,0 @@
[binaries]
c = 'aarch64-linux-android21-clang'
cpp = 'aarch64-linux-android21-clang++'
ar = 'aarch64-linux-android-ar'
strip = 'aarch64-linux-android-strip'
pkgconfig = 'pkg-config'
windres = 'aarch64-linux-android-windres'
[properties]
needs_exe_wrapper = true
[host_machine]
system = 'android'
cpu_family = 'aarch64'
endian = 'little'
cpu = 'aarch64'

View File

@ -1,16 +0,0 @@
[binaries]
c = 'armv7a-linux-androideabi16-clang'
cpp = 'armv7a-linux-androideabi16-clang++'
ar = 'arm-linux-androideabi-ar'
strip = 'arm-linux-androideabi-strip'
pkgconfig = 'pkg-config'
windres = 'arm-linux-androideabi-windres'
[properties]
needs_exe_wrapper = true
[host_machine]
system = 'android'
cpu_family = 'arm'
endian = 'little'
cpu = 'arm'

View File

@ -1,15 +0,0 @@
[binaries]
c = 'gcc'
cpp = 'g++'
ar = 'ar'
strip = 'strip'
[properties]
c_link_args = ['-m32', '-Wl,-z,text']
c_args = ['-m32']
[host_machine]
system = 'linux'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'

View File

@ -1,17 +0,0 @@
[binaries]
c = 'i686-w64-mingw32-gcc'
cpp = 'i686-w64-mingw32-g++'
ar = 'i686-w64-mingw32-ar'
strip = 'i686-w64-mingw32-strip'
windres = 'i686-w64-mingw32-windres'
exe_wrapper = ['wine']
[properties]
c_link_args = ['-static-libgcc']
needs_exe_wrapper = true
[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'

View File

@ -1,17 +0,0 @@
[binaries]
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
windres = 'x86_64-w64-mingw32-windres'
exe_wrapper = ['wine']
[properties]
c_link_args = ['-static-libgcc']
needs_exe_wrapper = true
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'

View File

@ -1,24 +0,0 @@
name: dav1d
base: core18
version: git
version-script: git describe HEAD --always
summary: AV1 decoder from VideoLAN
description: |
A small and fast AV1 decoder from the people who brought you VLC.
grade: stable
confinement: strict # use 'strict' once you have the right plugs and slots
apps:
dav1d:
command: usr/bin/dav1d
plugs: [ 'home' ]
parts:
dav1d:
plugin: meson
source: ../../
build-packages: [ 'nasm' ]
meson-parameters:
- --prefix=/usr
- --buildtype=release

View File

@ -1,201 +0,0 @@
/******************************************************************************
* Copyright © 2018, VideoLAN and dav1d authors
* Copyright © 2015 Martin Storsjo
* Copyright © 2015 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.
*****************************************************************************/
#define PRIVATE_PREFIX checkasm_
#include "src/arm/asm.S"
#include "src/arm/32/util.S"
const register_init, align=3
.quad 0x21f86d66c8ca00ce
.quad 0x75b6ba21077c48ad
.quad 0xed56bb2dcb3c7736
.quad 0x8bda43d3fd1a7e06
.quad 0xb64a9c9e5d318408
.quad 0xdf9a54b303f1d3a3
.quad 0x4a75479abd64e097
.quad 0x249214109d5d1c88
endconst
const error_message_fpscr
.asciz "failed to preserve register FPSCR, changed bits: %x"
error_message_gpr:
.asciz "failed to preserve register r%d"
error_message_vfp:
.asciz "failed to preserve register d%d"
error_message_stack:
.asciz "failed to preserve stack"
endconst
@ max number of args used by any asm function.
#define MAX_ARGS 15
#define ARG_STACK 4*(MAX_ARGS - 4)
@ Align the used stack space to 8 to preserve the stack alignment.
@ +8 for stack canary reference.
#define ARG_STACK_A (((ARG_STACK + pushed + 7) & ~7) - pushed + 8)
.macro clobbercheck variant
.equ pushed, 4*9
function checked_call_\variant, export=1
push {r4-r11, lr}
.ifc \variant, vfp
vpush {d8-d15}
fmrx r4, FPSCR
push {r4}
.equ pushed, pushed + 16*4 + 4
.endif
movrel r12, register_init
.ifc \variant, vfp
vldm r12, {d8-d15}
.endif
ldm r12, {r4-r11}
sub sp, sp, #ARG_STACK_A
.equ pos, 0
.rept MAX_ARGS-4
ldr r12, [sp, #ARG_STACK_A + pushed + 8 + pos]
str r12, [sp, #pos]
.equ pos, pos + 4
.endr
@ For stack overflows, the callee is free to overwrite the parameters
@ that were passed on the stack (if any), so we can only check after
@ that point. First figure out how many parameters the function
@ really took on the stack:
ldr r12, [sp, #ARG_STACK_A + pushed + 8 + 4*(MAX_ARGS-4)]
@ Load the first non-parameter value from the stack, that should be
@ left untouched by the function. Store a copy of it inverted, so that
@ e.g. overwriting everything with zero would be noticed.
ldr r12, [sp, r12, lsl #2]
mvn r12, r12
str r12, [sp, #ARG_STACK_A - 4]
mov r12, r0
mov r0, r2
mov r1, r3
ldrd r2, r3, [sp, #ARG_STACK_A + pushed]
@ Call the target function
blx r12
@ Load the number of stack parameters, stack canary and its reference
ldr r12, [sp, #ARG_STACK_A + pushed + 8 + 4*(MAX_ARGS-4)]
ldr r2, [sp, r12, lsl #2]
ldr r3, [sp, #ARG_STACK_A - 4]
add sp, sp, #ARG_STACK_A
push {r0, r1}
mvn r3, r3
cmp r2, r3
bne 5f
movrel r12, register_init
.ifc \variant, vfp
.macro check_reg_vfp, dreg, offset
ldrd r2, r3, [r12, #8 * (\offset)]
vmov r0, lr, \dreg
eor r2, r2, r0
eor r3, r3, lr
orrs r2, r2, r3
bne 4f
.endm
.irp n, 8, 9, 10, 11, 12, 13, 14, 15
@ keep track of the checked double/SIMD register
mov r1, #\n
check_reg_vfp d\n, \n-8
.endr
.purgem check_reg_vfp
fmrx r1, FPSCR
ldr r3, [sp, #8]
eor r1, r1, r3
@ Ignore changes in bits 0-4 and 7
bic r1, r1, #0x9f
@ Ignore changes in the topmost 5 bits
bics r1, r1, #0xf8000000
bne 3f
.endif
@ keep track of the checked GPR
mov r1, #4
.macro check_reg reg1, reg2=
ldrd r2, r3, [r12], #8
eors r2, r2, \reg1
bne 2f
add r1, r1, #1
.ifnb \reg2
eors r3, r3, \reg2
bne 2f
.endif
add r1, r1, #1
.endm
check_reg r4, r5
check_reg r6, r7
@ r9 is a volatile register in the ios ABI
#ifdef __APPLE__
check_reg r8
#else
check_reg r8, r9
#endif
check_reg r10, r11
.purgem check_reg
b 0f
5:
movrel r0, error_message_stack
b 1f
4:
movrel r0, error_message_vfp
b 1f
3:
movrel r0, error_message_fpscr
b 1f
2:
movrel r0, error_message_gpr
1:
#ifdef PREFIX
bl _checkasm_fail_func
#else
bl checkasm_fail_func
#endif
0:
pop {r0, r1}
.ifc \variant, vfp
pop {r2}
fmxr FPSCR, r2
vpop {d8-d15}
.endif
pop {r4-r11, pc}
endfunc
.endm
clobbercheck vfp

View File

@ -1,211 +0,0 @@
/******************************************************************************
* Copyright © 2018, VideoLAN and dav1d authors
* Copyright © 2015 Martin Storsjo
* Copyright © 2015 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.
*****************************************************************************/
#define PRIVATE_PREFIX checkasm_
#include "src/arm/asm.S"
#include "src/arm/64/util.S"
const register_init, align=4
.quad 0x21f86d66c8ca00ce
.quad 0x75b6ba21077c48ad
.quad 0xed56bb2dcb3c7736
.quad 0x8bda43d3fd1a7e06
.quad 0xb64a9c9e5d318408
.quad 0xdf9a54b303f1d3a3
.quad 0x4a75479abd64e097
.quad 0x249214109d5d1c88
.quad 0x1a1b2550a612b48c
.quad 0x79445c159ce79064
.quad 0x2eed899d5a28ddcd
.quad 0x86b2536fcd8cf636
.quad 0xb0856806085e7943
.quad 0x3f2bf84fc0fcca4e
.quad 0xacbd382dcf5b8de2
.quad 0xd229e1f5b281303f
.quad 0x71aeaff20b095fd9
.quad 0xab63e2e11fa38ed9
endconst
const error_message_register
.asciz "failed to preserve register"
error_message_stack:
.asciz "stack clobbered"
endconst
// max number of args used by any asm function.
#define MAX_ARGS 15
#define CLOBBER_STACK ((8*MAX_ARGS + 15) & ~15)
function stack_clobber, export=1
mov x3, sp
mov x2, #CLOBBER_STACK
1:
stp x0, x1, [sp, #-16]!
subs x2, x2, #16
b.gt 1b
mov sp, x3
ret
endfunc
// + 16 for stack canary reference
#define ARG_STACK ((8*(MAX_ARGS - 8) + 15) & ~15 + 16)
function checked_call, export=1
stp x29, x30, [sp, #-16]!
mov x29, sp
stp x19, x20, [sp, #-16]!
stp x21, x22, [sp, #-16]!
stp x23, x24, [sp, #-16]!
stp x25, x26, [sp, #-16]!
stp x27, x28, [sp, #-16]!
stp d8, d9, [sp, #-16]!
stp d10, d11, [sp, #-16]!
stp d12, d13, [sp, #-16]!
stp d14, d15, [sp, #-16]!
movrel x9, register_init
ldp d8, d9, [x9], #16
ldp d10, d11, [x9], #16
ldp d12, d13, [x9], #16
ldp d14, d15, [x9], #16
ldp x19, x20, [x9], #16
ldp x21, x22, [x9], #16
ldp x23, x24, [x9], #16
ldp x25, x26, [x9], #16
ldp x27, x28, [x9], #16
sub sp, sp, #ARG_STACK
.equ pos, 0
.rept MAX_ARGS-8
// Skip the first 8 args, that are loaded into registers
ldr x9, [x29, #16 + 8*8 + pos]
str x9, [sp, #pos]
.equ pos, pos + 8
.endr
// Fill x8-x17 with garbage. This doesn't have to be preserved,
// but avoids relying on them having any particular value.
movrel x9, register_init
ldp x10, x11, [x9], #32
ldp x12, x13, [x9], #32
ldp x14, x15, [x9], #32
ldp x16, x17, [x9], #32
ldp x8, x9, [x9]
// For stack overflows, the callee is free to overwrite the parameters
// that were passed on the stack (if any), so we can only check after
// that point. First figure out how many parameters the function
// really took on the stack:
ldr w2, [x29, #16 + 8*8 + (MAX_ARGS-8)*8]
// Load the first non-parameter value from the stack, that should be
// left untouched by the function. Store a copy of it inverted, so that
// e.g. overwriting everything with zero would be noticed.
ldr x2, [sp, x2, lsl #3]
mvn x2, x2
str x2, [sp, #ARG_STACK-8]
// Load the in-register arguments
mov x12, x0
ldp x0, x1, [x29, #16]
ldp x2, x3, [x29, #32]
ldp x4, x5, [x29, #48]
ldp x6, x7, [x29, #64]
// Call the target function
blr x12
// Load the number of stack parameters, stack canary and its reference
ldr w2, [x29, #16 + 8*8 + (MAX_ARGS-8)*8]
ldr x2, [sp, x2, lsl #3]
ldr x3, [sp, #ARG_STACK-8]
add sp, sp, #ARG_STACK
stp x0, x1, [sp, #-16]!
mvn x3, x3
cmp x2, x3
b.ne 2f
movrel x9, register_init
movi v3.8h, #0
.macro check_reg_neon reg1, reg2
ldr q1, [x9], #16
uzp1 v2.2d, v\reg1\().2d, v\reg2\().2d
eor v1.16b, v1.16b, v2.16b
orr v3.16b, v3.16b, v1.16b
.endm
check_reg_neon 8, 9
check_reg_neon 10, 11
check_reg_neon 12, 13
check_reg_neon 14, 15
uqxtn v3.8b, v3.8h
umov x3, v3.d[0]
.macro check_reg reg1, reg2
ldp x0, x1, [x9], #16
eor x0, x0, \reg1
eor x1, x1, \reg2
orr x3, x3, x0
orr x3, x3, x1
.endm
check_reg x19, x20
check_reg x21, x22
check_reg x23, x24
check_reg x25, x26
check_reg x27, x28
cbz x3, 0f
movrel x0, error_message_register
b 1f
2:
movrel x0, error_message_stack
1:
#ifdef PREFIX
bl _checkasm_fail_func
#else
bl checkasm_fail_func
#endif
0:
ldp x0, x1, [sp], #16
ldp d14, d15, [sp], #16
ldp d12, d13, [sp], #16
ldp d10, d11, [sp], #16
ldp d8, d9, [sp], #16
ldp x27, x28, [sp], #16
ldp x25, x26, [sp], #16
ldp x23, x24, [sp], #16
ldp x21, x22, [sp], #16
ldp x19, x20, [sp], #16
ldp x29, x30, [sp], #16
ret
endfunc

View File

@ -1,150 +0,0 @@
/*
* 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 "tests/checkasm/checkasm.h"
#include <string.h>
#include <stdio.h>
#include "common/dump.h"
#include "src/levels.h"
#include "src/cdef.h"
static int to_binary(int x) { /* 0-15 -> 0000-1111 */
return (x & 1) + 5 * (x & 2) + 25 * (x & 4) + 125 * (x & 8);
}
static void init_tmp(pixel *buf, int n, const int bitdepth_max) {
const int fill_type = rnd() & 7;
if (fill_type == 0)
while (n--) /* check for cdef_filter underflows */
*buf++ = rnd() & 1;
else if (fill_type == 1)
while (n--) /* check for cdef_filter overflows */
*buf++ = bitdepth_max - (rnd() & 1);
else
while (n--)
*buf++ = rnd() & bitdepth_max;
}
static void check_cdef_filter(const cdef_fn fn, const int w, const int h) {
ALIGN_STK_64(pixel, c_src, 16 * 10 + 16, ), *const c_dst = c_src + 8;
ALIGN_STK_64(pixel, a_src, 16 * 10 + 16, ), *const a_dst = a_src + 8;
ALIGN_STK_64(pixel, top_buf, 16 * 2 + 16, ), *const top = top_buf + 8;
ALIGN_STK_16(pixel, left, 8,[2]);
const ptrdiff_t stride = 16 * sizeof(pixel);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel (*left)[2],
const pixel *top, int pri_strength, int sec_strength,
int dir, int damping, enum CdefEdgeFlags edges HIGHBD_DECL_SUFFIX);
if (check_func(fn, "cdef_filter_%dx%d_%dbpc", w, h, BITDEPTH)) {
for (int dir = 0; dir < 8; dir++) {
for (enum CdefEdgeFlags edges = 0x0; edges <= 0xf; edges++) {
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8;
init_tmp(c_src, 16 * 10 + 16, bitdepth_max);
init_tmp(top_buf, 16 * 2 + 16, bitdepth_max);
init_tmp((pixel *) left, 8 * 2, bitdepth_max);
memcpy(a_src, c_src, (16 * 10 + 16) * sizeof(pixel));
const int lvl = 1 + (rnd() % 62);
const int damping = 3 + (rnd() & 3) + bitdepth_min_8 - (w == 4 || (rnd() & 1));
int pri_strength = (lvl >> 2) << bitdepth_min_8;
int sec_strength = lvl & 3;
sec_strength += sec_strength == 3;
sec_strength <<= bitdepth_min_8;
call_ref(c_dst, stride, left, top, pri_strength, sec_strength,
dir, damping, edges HIGHBD_TAIL_SUFFIX);
call_new(a_dst, stride, left, top, pri_strength, sec_strength,
dir, damping, edges HIGHBD_TAIL_SUFFIX);
if (checkasm_check_pixel(c_dst, stride, a_dst, stride, w, h, "dst")) {
fprintf(stderr, "strength = %d:%d, dir = %d, damping = %d, edges = %04d\n",
pri_strength, sec_strength, dir, damping, to_binary(edges));
return;
}
if (dir == 7 && (edges == 0x5 || edges == 0xa || edges == 0xf)) {
/* Benchmark a fixed set of cases to get consistent results:
* 1) top/left edges and pri_strength only
* 2) bottom/right edges and sec_strength only
* 3) all edges and both pri_strength and sec_strength
*/
pri_strength = (edges & 1) << bitdepth_min_8;
sec_strength = (edges & 2) << bitdepth_min_8;
bench_new(a_dst, stride, left, top, pri_strength, sec_strength,
dir, damping, edges HIGHBD_TAIL_SUFFIX);
}
}
}
}
}
static void check_cdef_direction(const cdef_dir_fn fn) {
ALIGN_STK_64(pixel, src, 8 * 8,);
declare_func(int, pixel *src, ptrdiff_t dst_stride, unsigned *var
HIGHBD_DECL_SUFFIX);
if (check_func(fn, "cdef_dir_%dbpc", BITDEPTH)) {
unsigned c_var, a_var;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
init_tmp(src, 64, bitdepth_max);
const int c_dir = call_ref(src, 8 * sizeof(pixel), &c_var HIGHBD_TAIL_SUFFIX);
const int a_dir = call_new(src, 8 * sizeof(pixel), &a_var HIGHBD_TAIL_SUFFIX);
if (c_var != a_var || c_dir != a_dir) {
if (fail()) {
hex_fdump(stderr, src, 8 * sizeof(pixel), 8, 8, "src");
fprintf(stderr, "c_dir %d a_dir %d\n", c_dir, a_dir);
}
}
bench_new(src, 8 * sizeof(pixel), &a_var HIGHBD_TAIL_SUFFIX);
}
report("cdef_dir");
}
void bitfn(checkasm_check_cdef)(void) {
Dav1dCdefDSPContext c;
bitfn(dav1d_cdef_dsp_init)(&c);
check_cdef_direction(c.dir);
check_cdef_filter(c.fb[0], 8, 8);
check_cdef_filter(c.fb[1], 4, 8);
check_cdef_filter(c.fb[2], 4, 4);
report("cdef_filter");
}

View File

@ -1,869 +0,0 @@
/*
* 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 "tests/checkasm/checkasm.h"
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "src/cpu.h"
#ifdef _WIN32
#include <windows.h>
#define COLOR_RED FOREGROUND_RED
#define COLOR_GREEN FOREGROUND_GREEN
#define COLOR_YELLOW (FOREGROUND_RED|FOREGROUND_GREEN)
static unsigned get_seed(void) {
return GetTickCount();
}
#else
#include <unistd.h>
#include <signal.h>
#include <time.h>
#ifdef __APPLE__
#include <mach/mach_time.h>
#endif
#define COLOR_RED 1
#define COLOR_GREEN 2
#define COLOR_YELLOW 3
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
/* List of tests to invoke */
static const struct {
const char *name;
void (*func)(void);
} tests[] = {
{ "msac", checkasm_check_msac },
#if CONFIG_8BPC
{ "cdef_8bpc", checkasm_check_cdef_8bpc },
{ "filmgrain_8bpc", checkasm_check_filmgrain_8bpc },
{ "ipred_8bpc", checkasm_check_ipred_8bpc },
{ "itx_8bpc", checkasm_check_itx_8bpc },
{ "loopfilter_8bpc", checkasm_check_loopfilter_8bpc },
{ "looprestoration_8bpc", checkasm_check_looprestoration_8bpc },
{ "mc_8bpc", checkasm_check_mc_8bpc },
#endif
#if CONFIG_16BPC
{ "cdef_16bpc", checkasm_check_cdef_16bpc },
{ "filmgrain_16bpc", checkasm_check_filmgrain_16bpc },
{ "ipred_16bpc", checkasm_check_ipred_16bpc },
{ "itx_16bpc", checkasm_check_itx_16bpc },
{ "loopfilter_16bpc", checkasm_check_loopfilter_16bpc },
{ "looprestoration_16bpc", checkasm_check_looprestoration_16bpc },
{ "mc_16bpc", checkasm_check_mc_16bpc },
#endif
{ 0 }
};
/* List of cpu flags to check */
static const struct {
const char *name;
const char *suffix;
unsigned flag;
} cpus[] = {
#if ARCH_X86
{ "SSE2", "sse2", DAV1D_X86_CPU_FLAG_SSE2 },
{ "SSSE3", "ssse3", DAV1D_X86_CPU_FLAG_SSSE3 },
{ "SSE4.1", "sse4", DAV1D_X86_CPU_FLAG_SSE41 },
{ "AVX2", "avx2", DAV1D_X86_CPU_FLAG_AVX2 },
{ "AVX-512 (Ice Lake)", "avx512icl", DAV1D_X86_CPU_FLAG_AVX512ICL },
#elif ARCH_AARCH64 || ARCH_ARM
{ "NEON", "neon", DAV1D_ARM_CPU_FLAG_NEON },
#elif ARCH_PPC64LE
{ "VSX", "vsx", DAV1D_PPC_CPU_FLAG_VSX },
#endif
{ 0 }
};
typedef struct CheckasmFuncVersion {
struct CheckasmFuncVersion *next;
void *func;
int ok;
unsigned cpu;
int iterations;
uint64_t cycles;
} CheckasmFuncVersion;
/* Binary search tree node */
typedef struct CheckasmFunc {
struct CheckasmFunc *child[2];
CheckasmFuncVersion versions;
uint8_t color; /* 0 = red, 1 = black */
char name[];
} CheckasmFunc;
/* Internal state */
static struct {
CheckasmFunc *funcs;
CheckasmFunc *current_func;
CheckasmFuncVersion *current_func_ver;
const char *current_test_name;
const char *bench_pattern;
size_t bench_pattern_len;
int num_checked;
int num_failed;
int nop_time;
unsigned cpu_flag;
const char *cpu_flag_name;
const char *test_name;
unsigned seed;
int bench_c;
int verbose;
int function_listing;
#if ARCH_X86_64
void (*simd_warmup)(void);
#endif
} state;
/* float compare support code */
typedef union {
float f;
uint32_t i;
} intfloat;
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.
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 int is_negative(const intfloat u) {
return u.i >> 31;
}
int float_near_ulp(const float a, const float b, const unsigned max_ulp) {
intfloat x, y;
x.f = a;
y.f = b;
if (is_negative(x) != is_negative(y)) {
// handle -0.0 == +0.0
return a == b;
}
if (llabs((int64_t)x.i - y.i) <= max_ulp)
return 1;
return 0;
}
int float_near_ulp_array(const float *const a, const float *const b,
const unsigned max_ulp, const int len)
{
for (int i = 0; i < len; i++)
if (!float_near_ulp(a[i], b[i], max_ulp))
return 0;
return 1;
}
int float_near_abs_eps(const float a, const float b, const float eps) {
return fabsf(a - b) < eps;
}
int float_near_abs_eps_array(const float *const a, const float *const b,
const float eps, const int len)
{
for (int i = 0; i < len; i++)
if (!float_near_abs_eps(a[i], b[i], eps))
return 0;
return 1;
}
int float_near_abs_eps_ulp(const float a, const float b, const float eps,
const unsigned max_ulp)
{
return float_near_ulp(a, b, max_ulp) || float_near_abs_eps(a, b, eps);
}
int float_near_abs_eps_array_ulp(const float *const a, const float *const b,
const float eps, const unsigned max_ulp,
const int len)
{
for (int i = 0; i < len; i++)
if (!float_near_abs_eps_ulp(a[i], b[i], eps, max_ulp))
return 0;
return 1;
}
/* Print colored text to stderr if the terminal supports it */
static void color_printf(const int color, const char *const fmt, ...) {
static int8_t use_color = -1;
va_list arg;
#ifdef _WIN32
static HANDLE con;
static WORD org_attributes;
if (use_color < 0) {
CONSOLE_SCREEN_BUFFER_INFO con_info;
con = GetStdHandle(STD_ERROR_HANDLE);
if (con && con != INVALID_HANDLE_VALUE &&
GetConsoleScreenBufferInfo(con, &con_info))
{
org_attributes = con_info.wAttributes;
use_color = 1;
} else
use_color = 0;
}
if (use_color)
SetConsoleTextAttribute(con, (org_attributes & 0xfff0) |
(color & 0x0f));
#else
if (use_color < 0) {
const char *const term = getenv("TERM");
use_color = term && strcmp(term, "dumb") && isatty(2);
}
if (use_color)
fprintf(stderr, "\x1b[%d;3%dm", (color & 0x08) >> 3, color & 0x07);
#endif
va_start(arg, fmt);
vfprintf(stderr, fmt, arg);
va_end(arg);
if (use_color) {
#ifdef _WIN32
SetConsoleTextAttribute(con, org_attributes);
#else
fprintf(stderr, "\x1b[0m");
#endif
}
}
/* Deallocate a tree */
static void destroy_func_tree(CheckasmFunc *const f) {
if (f) {
CheckasmFuncVersion *v = f->versions.next;
while (v) {
CheckasmFuncVersion *next = v->next;
free(v);
v = next;
}
destroy_func_tree(f->child[0]);
destroy_func_tree(f->child[1]);
free(f);
}
}
/* Allocate a zero-initialized block, clean up and exit on failure */
static void *checkasm_malloc(const size_t size) {
void *const ptr = calloc(1, size);
if (!ptr) {
fprintf(stderr, "checkasm: malloc failed\n");
destroy_func_tree(state.funcs);
exit(1);
}
return ptr;
}
/* Get the suffix of the specified cpu flag */
static const char *cpu_suffix(const unsigned cpu) {
for (int i = (int)(sizeof(cpus) / sizeof(*cpus)) - 2; i >= 0; i--)
if (cpu & cpus[i].flag)
return cpus[i].suffix;
return "c";
}
#ifdef readtime
static int cmp_nop(const void *a, const void *b) {
return *(const uint16_t*)a - *(const uint16_t*)b;
}
/* Measure the overhead of the timing code (in decicycles) */
static int measure_nop_time(void) {
uint16_t nops[10000];
int nop_sum = 0;
for (int i = 0; i < 10000; i++) {
uint64_t t = readtime();
nops[i] = (uint16_t) (readtime() - t);
}
qsort(nops, 10000, sizeof(uint16_t), cmp_nop);
for (int i = 2500; i < 7500; i++)
nop_sum += nops[i];
return nop_sum / 500;
}
/* Print benchmark results */
static void print_benchs(const CheckasmFunc *const f) {
if (f) {
print_benchs(f->child[0]);
/* Only print functions with at least one assembly version */
if (state.bench_c || f->versions.cpu || f->versions.next) {
const CheckasmFuncVersion *v = &f->versions;
do {
if (v->iterations) {
const int decicycles = (int) (10*v->cycles/v->iterations -
state.nop_time) / 4;
printf("%s_%s: %d.%d\n", f->name, cpu_suffix(v->cpu),
decicycles/10, decicycles%10);
}
} while ((v = v->next));
}
print_benchs(f->child[1]);
}
}
#endif
static void print_functions(const CheckasmFunc *const f) {
if (f) {
print_functions(f->child[0]);
printf("%s\n", f->name);
print_functions(f->child[1]);
}
}
#define is_digit(x) ((x) >= '0' && (x) <= '9')
/* ASCIIbetical sort except preserving natural order for numbers */
static int cmp_func_names(const char *a, const char *b) {
const char *const start = a;
int ascii_diff, digit_diff;
for (; !(ascii_diff = *(const unsigned char*)a -
*(const unsigned char*)b) && *a; a++, b++);
for (; is_digit(*a) && is_digit(*b); a++, b++);
if (a > start && is_digit(a[-1]) &&
(digit_diff = is_digit(*a) - is_digit(*b)))
{
return digit_diff;
}
return ascii_diff;
}
/* Perform a tree rotation in the specified direction and return the new root */
static CheckasmFunc *rotate_tree(CheckasmFunc *const f, const int dir) {
CheckasmFunc *const r = f->child[dir^1];
f->child[dir^1] = r->child[dir];
r->child[dir] = f;
r->color = f->color;
f->color = 0;
return r;
}
#define is_red(f) ((f) && !(f)->color)
/* Balance a left-leaning red-black tree at the specified node */
static void balance_tree(CheckasmFunc **const root) {
CheckasmFunc *const f = *root;
if (is_red(f->child[0]) && is_red(f->child[1])) {
f->color ^= 1;
f->child[0]->color = f->child[1]->color = 1;
}
else if (!is_red(f->child[0]) && is_red(f->child[1]))
*root = rotate_tree(f, 0); /* Rotate left */
else if (is_red(f->child[0]) && is_red(f->child[0]->child[0]))
*root = rotate_tree(f, 1); /* Rotate right */
}
/* Get a node with the specified name, creating it if it doesn't exist */
static CheckasmFunc *get_func(CheckasmFunc **const root, const char *const name) {
CheckasmFunc *f = *root;
if (f) {
/* Search the tree for a matching node */
const int cmp = cmp_func_names(name, f->name);
if (cmp) {
f = get_func(&f->child[cmp > 0], name);
/* Rebalance the tree on the way up if a new node was inserted */
if (!f->versions.func)
balance_tree(root);
}
} else {
/* Allocate and insert a new node into the tree */
const size_t name_length = strlen(name) + 1;
f = *root = checkasm_malloc(offsetof(CheckasmFunc, name) + name_length);
memcpy(f->name, name, name_length);
}
return f;
}
checkasm_context checkasm_context_buf;
/* Crash handling: attempt to catch crashes and handle them
* gracefully instead of just aborting abruptly. */
#ifdef _WIN32
static LONG NTAPI signal_handler(EXCEPTION_POINTERS *const e) {
switch (e->ExceptionRecord->ExceptionCode) {
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_DIVIDE_BY_ZERO:
checkasm_fail_func("fatal arithmetic error");
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
case EXCEPTION_PRIV_INSTRUCTION:
checkasm_fail_func("illegal instruction");
break;
case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_IN_PAGE_ERROR:
case EXCEPTION_STACK_OVERFLOW:
checkasm_fail_func("segmentation fault");
break;
default:
return EXCEPTION_CONTINUE_SEARCH;
}
checkasm_load_context();
return EXCEPTION_CONTINUE_EXECUTION; /* never reached, but shuts up gcc */
}
#else
static void signal_handler(const int s) {
checkasm_set_signal_handler_state(0);
checkasm_fail_func(s == SIGFPE ? "fatal arithmetic error" :
s == SIGILL ? "illegal instruction" :
"segmentation fault");
checkasm_load_context();
}
#endif
/* Perform tests and benchmarks for the specified
* cpu flag if supported by the host */
static void check_cpu_flag(const char *const name, unsigned flag) {
const unsigned old_cpu_flag = state.cpu_flag;
flag |= old_cpu_flag;
dav1d_set_cpu_flags_mask(flag);
state.cpu_flag = dav1d_get_cpu_flags();
if (!flag || state.cpu_flag != old_cpu_flag) {
state.cpu_flag_name = name;
for (int i = 0; tests[i].func; i++) {
if (state.test_name && strcmp(tests[i].name, state.test_name))
continue;
xor128_srand(state.seed);
state.current_test_name = tests[i].name;
tests[i].func();
}
}
}
/* Print the name of the current CPU flag, but only do it once */
static void print_cpu_name(void) {
if (state.cpu_flag_name) {
color_printf(COLOR_YELLOW, "%s:\n", state.cpu_flag_name);
state.cpu_flag_name = NULL;
}
}
int main(int argc, char *argv[]) {
state.seed = get_seed();
while (argc > 1) {
if (!strncmp(argv[1], "--help", 6)) {
fprintf(stdout,
"checkasm [options] <random seed>\n"
" <random seed> Numeric value to seed the rng\n"
"Options:\n"
" --test=<test_name> Test only <test_name>\n"
" --bench=<pattern> Test and benchmark the functions matching <pattern>\n"
" --list-functions List available functions\n"
" --list-tests List available tests\n"
" --bench-c Benchmark the C-only functions\n"
" --verbose -v Print failures verbosely\n");
return 0;
} else if (!strncmp(argv[1], "--bench-c", 9)) {
state.bench_c = 1;
} else if (!strncmp(argv[1], "--bench", 7)) {
#ifndef readtime
fprintf(stderr,
"checkasm: --bench is not supported on your system\n");
return 1;
#endif
if (argv[1][7] == '=') {
state.bench_pattern = argv[1] + 8;
state.bench_pattern_len = strlen(state.bench_pattern);
} else
state.bench_pattern = "";
} else if (!strncmp(argv[1], "--test=", 7)) {
state.test_name = argv[1] + 7;
} else if (!strcmp(argv[1], "--list-functions")) {
state.function_listing = 1;
} else if (!strcmp(argv[1], "--list-tests")) {
for (int i = 0; tests[i].name; i++)
printf("%s\n", tests[i].name);
return 0;
} else if (!strcmp(argv[1], "--verbose") || !strcmp(argv[1], "-v")) {
state.verbose = 1;
} else {
state.seed = (unsigned) strtoul(argv[1], NULL, 10);
}
argc--;
argv++;
}
dav1d_init_cpu();
#ifdef readtime
if (state.bench_pattern) {
static int testing = 0;
checkasm_save_context();
if (!testing) {
checkasm_set_signal_handler_state(1);
testing = 1;
readtime();
checkasm_set_signal_handler_state(0);
} else {
fprintf(stderr, "checkasm: unable to access cycle counter\n");
return 1;
}
}
#endif
int ret = 0;
if (!state.function_listing) {
fprintf(stderr, "checkasm: using random seed %u\n", state.seed);
#if ARCH_X86_64
void checkasm_warmup_avx2(void);
void checkasm_warmup_avx512(void);
const unsigned cpu_flags = dav1d_get_cpu_flags();
if (cpu_flags & DAV1D_X86_CPU_FLAG_AVX512ICL)
state.simd_warmup = checkasm_warmup_avx512;
else if (cpu_flags & DAV1D_X86_CPU_FLAG_AVX2)
state.simd_warmup = checkasm_warmup_avx2;
checkasm_simd_warmup();
#endif
}
check_cpu_flag(NULL, 0);
if (state.function_listing) {
print_functions(state.funcs);
} else {
for (int i = 0; cpus[i].flag; i++)
check_cpu_flag(cpus[i].name, cpus[i].flag);
if (!state.num_checked) {
fprintf(stderr, "checkasm: no tests to perform\n");
} else if (state.num_failed) {
fprintf(stderr, "checkasm: %d of %d tests have failed\n",
state.num_failed, state.num_checked);
ret = 1;
} else {
fprintf(stderr, "checkasm: all %d tests passed\n", state.num_checked);
#ifdef readtime
if (state.bench_pattern) {
state.nop_time = measure_nop_time();
printf("nop: %d.%d\n", state.nop_time/10, state.nop_time%10);
print_benchs(state.funcs);
}
#endif
}
}
destroy_func_tree(state.funcs);
return ret;
}
/* Decide whether or not the specified function needs to be tested and
* allocate/initialize data structures if needed. Returns a pointer to a
* reference function if the function should be tested, otherwise NULL */
void *checkasm_check_func(void *const func, const char *const name, ...) {
char name_buf[256];
va_list arg;
va_start(arg, name);
const int name_length = vsnprintf(name_buf, sizeof(name_buf), name, arg);
va_end(arg);
if (!func || name_length <= 0 || (size_t)name_length >= sizeof(name_buf))
return NULL;
state.current_func = get_func(&state.funcs, name_buf);
if (state.function_listing) /* Save function names without running tests */
return NULL;
state.funcs->color = 1;
CheckasmFuncVersion *v = &state.current_func->versions;
void *ref = func;
if (v->func) {
CheckasmFuncVersion *prev;
do {
/* Only test functions that haven't already been tested */
if (v->func == func)
return NULL;
if (v->ok)
ref = v->func;
prev = v;
} while ((v = v->next));
v = prev->next = checkasm_malloc(sizeof(CheckasmFuncVersion));
}
v->func = func;
v->ok = 1;
v->cpu = state.cpu_flag;
state.current_func_ver = v;
xor128_srand(state.seed);
if (state.cpu_flag || state.bench_c)
state.num_checked++;
return ref;
}
/* Decide whether or not the current function needs to be benchmarked */
int checkasm_bench_func(void) {
return !state.num_failed && state.bench_pattern &&
!strncmp(state.current_func->name, state.bench_pattern,
state.bench_pattern_len);
}
/* Indicate that the current test has failed, return whether verbose printing
* is requested. */
int checkasm_fail_func(const char *const msg, ...) {
if (state.current_func_ver && state.current_func_ver->cpu &&
state.current_func_ver->ok)
{
va_list arg;
print_cpu_name();
fprintf(stderr, " %s_%s (", state.current_func->name,
cpu_suffix(state.current_func_ver->cpu));
va_start(arg, msg);
vfprintf(stderr, msg, arg);
va_end(arg);
fprintf(stderr, ")\n");
state.current_func_ver->ok = 0;
state.num_failed++;
}
return state.verbose;
}
/* Update benchmark results of the current function */
void checkasm_update_bench(const int iterations, const uint64_t cycles) {
state.current_func_ver->iterations += iterations;
state.current_func_ver->cycles += cycles;
}
/* Print the outcome of all tests performed since
* the last time this function was called */
void checkasm_report(const char *const name, ...) {
static int prev_checked, prev_failed;
static size_t max_length;
if (state.num_checked > prev_checked) {
int pad_length = (int) max_length + 4;
va_list arg;
print_cpu_name();
pad_length -= fprintf(stderr, " - %s.", state.current_test_name);
va_start(arg, name);
pad_length -= vfprintf(stderr, name, arg);
va_end(arg);
fprintf(stderr, "%*c", imax(pad_length, 0) + 2, '[');
if (state.num_failed == prev_failed)
color_printf(COLOR_GREEN, "OK");
else
color_printf(COLOR_RED, "FAILED");
fprintf(stderr, "]\n");
prev_checked = state.num_checked;
prev_failed = state.num_failed;
} else if (!state.cpu_flag) {
/* Calculate the amount of padding required
* to make the output vertically aligned */
size_t length = strlen(state.current_test_name);
va_list arg;
va_start(arg, name);
length += vsnprintf(NULL, 0, name, arg);
va_end(arg);
if (length > max_length)
max_length = length;
}
}
void checkasm_set_signal_handler_state(const int enabled) {
#ifdef _WIN32
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
if (enabled)
AddVectoredExceptionHandler(0, signal_handler);
else
RemoveVectoredExceptionHandler(signal_handler);
#endif
#else
void (*const handler)(int) = enabled ? signal_handler : SIG_DFL;
signal(SIGBUS, handler);
signal(SIGFPE, handler);
signal(SIGILL, handler);
signal(SIGSEGV, handler);
#endif
}
static int check_err(const char *const file, const int line,
const char *const name, const int w, const int h,
int *const err)
{
if (*err)
return 0;
if (!checkasm_fail_func("%s:%d", file, line))
return 1;
*err = 1;
fprintf(stderr, "%s (%dx%d):\n", name, w, h);
return 0;
}
#define DEF_CHECKASM_CHECK_FUNC(type, fmt) \
int checkasm_check_##type(const char *const file, const int line, \
const type *buf1, ptrdiff_t stride1, \
const type *buf2, ptrdiff_t stride2, \
const int w, int h, const char *const name, \
const int align_w, const int align_h, \
const int padding) \
{ \
int aligned_w = (w + align_w - 1) & ~(align_w - 1); \
int aligned_h = (h + align_h - 1) & ~(align_h - 1); \
int err = 0; \
stride1 /= sizeof(*buf1); \
stride2 /= sizeof(*buf2); \
int y = 0; \
for (y = 0; y < h; y++) \
if (memcmp(&buf1[y*stride1], &buf2[y*stride2], w*sizeof(*buf1))) \
break; \
if (y != h) { \
if (check_err(file, line, name, w, h, &err)) \
return 1; \
for (y = 0; y < h; y++) { \
for (int x = 0; x < w; x++) \
fprintf(stderr, " " fmt, buf1[x]); \
fprintf(stderr, " "); \
for (int x = 0; x < w; x++) \
fprintf(stderr, " " fmt, buf2[x]); \
fprintf(stderr, " "); \
for (int x = 0; x < w; x++) \
fprintf(stderr, "%c", buf1[x] != buf2[x] ? 'x' : '.'); \
buf1 += stride1; \
buf2 += stride2; \
fprintf(stderr, "\n"); \
} \
buf1 -= h*stride1; \
buf2 -= h*stride2; \
} \
for (y = -padding; y < 0; y++) \
if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
(w + 2*padding)*sizeof(*buf1))) { \
if (check_err(file, line, name, w, h, &err)) \
return 1; \
fprintf(stderr, " overwrite above\n"); \
break; \
} \
for (y = aligned_h; y < aligned_h + padding; y++) \
if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
(w + 2*padding)*sizeof(*buf1))) { \
if (check_err(file, line, name, w, h, &err)) \
return 1; \
fprintf(stderr, " overwrite below\n"); \
break; \
} \
for (y = 0; y < h; y++) \
if (memcmp(&buf1[y*stride1 - padding], &buf2[y*stride2 - padding], \
padding*sizeof(*buf1))) { \
if (check_err(file, line, name, w, h, &err)) \
return 1; \
fprintf(stderr, " overwrite left\n"); \
break; \
} \
for (y = 0; y < h; y++) \
if (memcmp(&buf1[y*stride1 + aligned_w], &buf2[y*stride2 + aligned_w], \
padding*sizeof(*buf1))) { \
if (check_err(file, line, name, w, h, &err)) \
return 1; \
fprintf(stderr, " overwrite right\n"); \
break; \
} \
return err; \
}
DEF_CHECKASM_CHECK_FUNC(int8_t, "%4d")
DEF_CHECKASM_CHECK_FUNC(uint8_t, "%02x")
DEF_CHECKASM_CHECK_FUNC(uint16_t, "%04x")
DEF_CHECKASM_CHECK_FUNC(int16_t, "%6d")
DEF_CHECKASM_CHECK_FUNC(int32_t, "%9d")
#if ARCH_X86_64
void checkasm_simd_warmup(void)
{
if (state.simd_warmup)
state.simd_warmup();
}
#endif

View File

@ -1,349 +0,0 @@
/*
* 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.
*/
#ifndef DAV1D_TESTS_CHECKASM_CHECKASM_H
#define DAV1D_TESTS_CHECKASM_CHECKASM_H
#include "config.h"
#include <stdint.h>
#include <stdlib.h>
#if ARCH_X86_64 && defined(_WIN32)
/* setjmp/longjmp on 64-bit Windows will try to use SEH to unwind the stack,
* which doesn't work for assembly functions without unwind information. */
#include <windows.h>
#define checkasm_context CONTEXT
#define checkasm_save_context() RtlCaptureContext(&checkasm_context_buf)
#define checkasm_load_context() RtlRestoreContext(&checkasm_context_buf, NULL)
#else
#include <setjmp.h>
#define checkasm_context jmp_buf
#define checkasm_save_context() setjmp(checkasm_context_buf)
#define checkasm_load_context() longjmp(checkasm_context_buf, 1)
#endif
#include "include/common/attributes.h"
#include "include/common/bitdepth.h"
#include "include/common/intops.h"
int xor128_rand(void);
#define rnd xor128_rand
#define decl_check_bitfns(name) \
name##_8bpc(void); \
name##_16bpc(void)
void checkasm_check_msac(void);
decl_check_bitfns(void checkasm_check_cdef);
decl_check_bitfns(void checkasm_check_filmgrain);
decl_check_bitfns(void checkasm_check_ipred);
decl_check_bitfns(void checkasm_check_itx);
decl_check_bitfns(void checkasm_check_loopfilter);
decl_check_bitfns(void checkasm_check_looprestoration);
decl_check_bitfns(void checkasm_check_mc);
void *checkasm_check_func(void *func, const char *name, ...);
int checkasm_bench_func(void);
int checkasm_fail_func(const char *msg, ...);
void checkasm_update_bench(int iterations, uint64_t cycles);
void checkasm_report(const char *name, ...);
void checkasm_set_signal_handler_state(int enabled);
extern checkasm_context checkasm_context_buf;
/* float compare utilities */
int float_near_ulp(float a, float b, unsigned max_ulp);
int float_near_abs_eps(float a, float b, float eps);
int float_near_abs_eps_ulp(float a, float b, float eps, unsigned max_ulp);
int float_near_ulp_array(const float *a, const float *b, unsigned max_ulp,
int len);
int float_near_abs_eps_array(const float *a, const float *b, float eps,
int len);
int float_near_abs_eps_array_ulp(const float *a, const float *b, float eps,
unsigned max_ulp, int len);
#define BENCH_RUNS (1 << 12) /* Trade-off between accuracy and speed */
/* Decide whether or not the specified function needs to be tested */
#define check_func(func, ...)\
(func_ref = checkasm_check_func((func_new = func), __VA_ARGS__))
/* Declare the function prototype. The first argument is the return value,
* the remaining arguments are the function parameters. Naming parameters
* is optional. */
#define declare_func(ret, ...)\
declare_new(ret, __VA_ARGS__)\
void *func_ref, *func_new;\
typedef ret func_type(__VA_ARGS__);\
checkasm_save_context()
/* Indicate that the current test has failed */
#define fail() checkasm_fail_func("%s:%d", __FILE__, __LINE__)
/* Print the test outcome */
#define report checkasm_report
/* Call the reference function */
#define call_ref(...)\
(checkasm_set_signal_handler_state(1),\
((func_type *)func_ref)(__VA_ARGS__));\
checkasm_set_signal_handler_state(0)
#if HAVE_ASM
#if ARCH_X86
#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
#define readtime() (_mm_lfence(), __rdtsc())
#else
static inline uint64_t readtime(void) {
uint32_t eax, edx;
__asm__ __volatile__("lfence\nrdtsc" : "=a"(eax), "=d"(edx));
return (((uint64_t)edx) << 32) | eax;
}
#define readtime readtime
#endif
#elif (ARCH_AARCH64 || ARCH_ARM) && defined(__APPLE__)
#include <mach/mach_time.h>
#define readtime() mach_absolute_time()
#elif ARCH_AARCH64
#ifdef _MSC_VER
#include <windows.h>
#define readtime() (_InstructionSynchronizationBarrier(), ReadTimeStampCounter())
#else
static inline uint64_t readtime(void) {
uint64_t cycle_counter;
/* This requires enabling user mode access to the cycle counter (which
* can only be done from kernel space).
* This could also read cntvct_el0 instead of pmccntr_el0; that register
* might also be readable (depending on kernel version), but it has much
* worse precision (it's a fixed 50 MHz timer). */
__asm__ __volatile__("isb\nmrs %0, pmccntr_el0"
: "=r"(cycle_counter)
:: "memory");
return cycle_counter;
}
#define readtime readtime
#endif
#elif ARCH_ARM && !defined(_MSC_VER) && __ARM_ARCH >= 7
static inline uint64_t readtime(void) {
uint32_t cycle_counter;
/* This requires enabling user mode access to the cycle counter (which
* can only be done from kernel space). */
__asm__ __volatile__("isb\nmrc p15, 0, %0, c9, c13, 0"
: "=r"(cycle_counter)
:: "memory");
return cycle_counter;
}
#define readtime readtime
#elif ARCH_PPC64LE
static inline uint64_t readtime(void) {
uint32_t tbu, tbl, temp;
__asm__ __volatile__(
"1:\n"
"mfspr %2,269\n"
"mfspr %0,268\n"
"mfspr %1,269\n"
"cmpw %2,%1\n"
"bne 1b\n"
: "=r"(tbl), "=r"(tbu), "=r"(temp)
:
: "cc");
return (((uint64_t)tbu) << 32) | (uint64_t)tbl;
}
#define readtime readtime
#endif
/* Verifies that clobbered callee-saved registers
* are properly saved and restored */
void checkasm_checked_call(void *func, ...);
#if ARCH_X86_64
/* Evil hack: detect incorrect assumptions that 32-bit ints are zero-extended
* to 64-bit. This is done by clobbering the stack with junk around the stack
* pointer and calling the assembly function through checked_call() with added
* dummy arguments which forces all real arguments to be passed on the stack
* and not in registers. For 32-bit arguments the upper half of the 64-bit
* register locations on the stack will now contain junk which will cause
* misbehaving functions to either produce incorrect output or segfault. Note
* that even though this works extremely well in practice, it's technically
* not guaranteed and false negatives is theoretically possible, but there
* can never be any false positives. */
void checkasm_stack_clobber(uint64_t clobber, ...);
/* YMM and ZMM registers on x86 are turned off to save power when they haven't
* been used for some period of time. When they are used there will be a
* "warmup" period during which performance will be reduced and inconsistent
* which is problematic when trying to benchmark individual functions. We can
* work around this by periodically issuing "dummy" instructions that uses
* those registers to keep them powered on. */
void checkasm_simd_warmup(void);
#define declare_new(ret, ...)\
ret (*checked_call)(void *, int, int, int, int, int, __VA_ARGS__,\
int, int, int, int, int, int, int, int,\
int, int, int, int, int, int, int) =\
(void *)checkasm_checked_call;
#define CLOB (UINT64_C(0xdeadbeefdeadbeef))
#ifdef _WIN32
#define STACKARGS 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0
#else
#define STACKARGS 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0
#endif
#define call_new(...)\
(checkasm_set_signal_handler_state(1),\
checkasm_simd_warmup(),\
checkasm_stack_clobber(CLOB, CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB, CLOB, CLOB),\
checked_call(func_new, 0, 0, 0, 0, 0, __VA_ARGS__, STACKARGS));\
checkasm_set_signal_handler_state(0)
#elif ARCH_X86_32
#define declare_new(ret, ...)\
ret (*checked_call)(void *, __VA_ARGS__, int, int, int, int, int, int,\
int, int, int, int, int, int, int, int, int) =\
(void *)checkasm_checked_call;
#define call_new(...)\
(checkasm_set_signal_handler_state(1),\
checked_call(func_new, __VA_ARGS__, 15, 14, 13, 12,\
11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1));\
checkasm_set_signal_handler_state(0)
#elif ARCH_ARM
/* Use a dummy argument, to offset the real parameters by 2, not only 1.
* This makes sure that potential 8-byte-alignment of parameters is kept
* the same even when the extra parameters have been removed. */
void checkasm_checked_call_vfp(void *func, int dummy, ...);
#define declare_new(ret, ...)\
ret (*checked_call)(void *, int dummy, __VA_ARGS__,\
int, int, int, int, int, int, int, int,\
int, int, int, int, int, int, int) =\
(void *)checkasm_checked_call_vfp;
#define call_new(...)\
(checkasm_set_signal_handler_state(1),\
checked_call(func_new, 0, __VA_ARGS__, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0));\
checkasm_set_signal_handler_state(0)
#elif ARCH_AARCH64 && !defined(__APPLE__)
void checkasm_stack_clobber(uint64_t clobber, ...);
#define declare_new(ret, ...)\
ret (*checked_call)(void *, int, int, int, int, int, int, int,\
__VA_ARGS__, int, int, int, int, int, int, int, int,\
int, int, int, int, int, int, int) =\
(void *)checkasm_checked_call;
#define CLOB (UINT64_C(0xdeadbeefdeadbeef))
#define call_new(...)\
(checkasm_set_signal_handler_state(1),\
checkasm_stack_clobber(CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB, CLOB,\
CLOB, CLOB, CLOB, CLOB, CLOB),\
checked_call(func_new, 0, 0, 0, 0, 0, 0, 0, __VA_ARGS__,\
7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0));\
checkasm_set_signal_handler_state(0)
#else
#define declare_new(ret, ...)
#define call_new(...)\
(checkasm_set_signal_handler_state(1),\
((func_type *)func_new)(__VA_ARGS__));\
checkasm_set_signal_handler_state(0)
#endif
#else /* HAVE_ASM */
#define declare_new(ret, ...)
/* Call the function */
#define call_new(...)\
(checkasm_set_signal_handler_state(1),\
((func_type *)func_new)(__VA_ARGS__));\
checkasm_set_signal_handler_state(0)
#endif /* HAVE_ASM */
/* Benchmark the function */
#ifdef readtime
#define bench_new(...)\
do {\
func_type *tfunc = func_new;\
checkasm_set_signal_handler_state(1);\
if (checkasm_bench_func()) {\
uint64_t tsum = 0;\
int tcount = 0;\
for (int ti = 0; ti < BENCH_RUNS; ti++) {\
uint64_t t = readtime();\
tfunc(__VA_ARGS__);\
tfunc(__VA_ARGS__);\
tfunc(__VA_ARGS__);\
tfunc(__VA_ARGS__);\
t = readtime() - t;\
if (t*tcount <= tsum*4 && ti > 0) {\
tsum += t;\
tcount++;\
}\
}\
checkasm_update_bench(tcount, tsum);\
} else {\
tfunc(__VA_ARGS__);\
}\
checkasm_set_signal_handler_state(0);\
} while (0)
#else
#define bench_new(...) do {} while (0)
#endif
#define PIXEL_RECT(name, w, h) \
ALIGN_STK_64(pixel, name##_buf, ((h)+32)*((w)+64) + 64,); \
ptrdiff_t name##_stride = sizeof(pixel)*((w)+64); \
(void)name##_stride; \
pixel *name = name##_buf + ((w)+64)*16 + 64
#define CLEAR_PIXEL_RECT(name) \
memset(name##_buf, 0x99, sizeof(name##_buf)) \
#define DECL_CHECKASM_CHECK_FUNC(type) \
int checkasm_check_##type(const char *const file, const int line, \
const type *const buf1, const ptrdiff_t stride1, \
const type *const buf2, const ptrdiff_t stride2, \
const int w, const int h, const char *const name, \
const int align_w, const int align_h, \
const int padding)
DECL_CHECKASM_CHECK_FUNC(int8_t);
DECL_CHECKASM_CHECK_FUNC(uint8_t);
DECL_CHECKASM_CHECK_FUNC(uint16_t);
DECL_CHECKASM_CHECK_FUNC(int16_t);
DECL_CHECKASM_CHECK_FUNC(int32_t);
#define CONCAT(a,b) a ## b
#define checkasm_check2(prefix, ...) CONCAT(checkasm_check_, prefix)(__FILE__, __LINE__, __VA_ARGS__)
#define checkasm_check(prefix, ...) checkasm_check2(prefix, __VA_ARGS__, 0, 0, 0)
#ifdef BITDEPTH
#define checkasm_check_pixel(...) checkasm_check(PIXEL_TYPE, __VA_ARGS__)
#define checkasm_check_pixel_padded(...) checkasm_check2(PIXEL_TYPE, __VA_ARGS__, 1, 1, 8)
#define checkasm_check_pixel_padded_align(...) checkasm_check2(PIXEL_TYPE, __VA_ARGS__, 8)
#define checkasm_check_coef(...) checkasm_check(COEF_TYPE, __VA_ARGS__)
#endif
#endif /* DAV1D_TESTS_CHECKASM_CHECKASM_H */

View File

@ -1,401 +0,0 @@
/*
* Copyright © 2019, VideoLAN and dav1d authors
* Copyright © 2019, 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 "tests/checkasm/checkasm.h"
#include <string.h>
#include "src/levels.h"
#include "src/film_grain.h"
#define UNIT_TEST 1
#include "src/fg_apply_tmpl.c"
#if BITDEPTH == 8
#define checkasm_check_entry(...) checkasm_check(int8_t, __VA_ARGS__)
#else
#define checkasm_check_entry(...) checkasm_check(int16_t, __VA_ARGS__)
#endif
static const char ss_name[][4] = {
[DAV1D_PIXEL_LAYOUT_I420 - 1] = "420",
[DAV1D_PIXEL_LAYOUT_I422 - 1] = "422",
[DAV1D_PIXEL_LAYOUT_I444 - 1] = "444",
};
static void check_gen_grny(const Dav1dFilmGrainDSPContext *const dsp) {
entry grain_lut_c[GRAIN_HEIGHT][GRAIN_WIDTH];
entry grain_lut_a[GRAIN_HEIGHT + 1][GRAIN_WIDTH];
declare_func(void, entry grain_lut[][GRAIN_WIDTH],
const Dav1dFilmGrainData *data HIGHBD_DECL_SUFFIX);
for (int i = 0; i < 4; i++) {
if (check_func(dsp->generate_grain_y, "gen_grain_y_ar%d_%dbpc", i, BITDEPTH)) {
ALIGN_STK_16(Dav1dFilmGrainData, fg_data, 1,);
fg_data[0].seed = rnd() & 0xFFFF;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#endif
fg_data[0].grain_scale_shift = rnd() & 3;
fg_data[0].ar_coeff_shift = (rnd() & 3) + 6;
fg_data[0].ar_coeff_lag = i;
const int num_y_pos = 2 * fg_data[0].ar_coeff_lag * (fg_data[0].ar_coeff_lag + 1);
for (int n = 0; n < num_y_pos; n++)
fg_data[0].ar_coeffs_y[n] = (rnd() & 0xff) - 128;
call_ref(grain_lut_c, fg_data HIGHBD_TAIL_SUFFIX);
call_new(grain_lut_a, fg_data HIGHBD_TAIL_SUFFIX);
checkasm_check_entry(grain_lut_c[0], sizeof(entry) * GRAIN_WIDTH,
grain_lut_a[0], sizeof(entry) * GRAIN_WIDTH,
GRAIN_WIDTH, GRAIN_HEIGHT, "grain_lut");
bench_new(grain_lut_a, fg_data HIGHBD_TAIL_SUFFIX);
}
}
report("gen_grain_y");
}
static void check_gen_grnuv(const Dav1dFilmGrainDSPContext *const dsp) {
entry grain_lut_y[GRAIN_HEIGHT + 1][GRAIN_WIDTH];
entry grain_lut_c[GRAIN_HEIGHT][GRAIN_WIDTH];
entry grain_lut_a[GRAIN_HEIGHT + 1][GRAIN_WIDTH];
declare_func(void, entry grain_lut[][GRAIN_WIDTH],
const entry grain_lut_y[][GRAIN_WIDTH],
const Dav1dFilmGrainData *data, intptr_t uv HIGHBD_DECL_SUFFIX);
for (int layout_idx = 0; layout_idx < 3; layout_idx++) {
const enum Dav1dPixelLayout layout = layout_idx + 1;
const int ss_x = layout != DAV1D_PIXEL_LAYOUT_I444;
const int ss_y = layout == DAV1D_PIXEL_LAYOUT_I420;
for (int i = 0; i < 4; i++) {
if (check_func(dsp->generate_grain_uv[layout_idx],
"gen_grain_uv_ar%d_%dbpc_%s",
i, BITDEPTH, ss_name[layout_idx]))
{
ALIGN_STK_16(Dav1dFilmGrainData, fg_data, 1,);
fg_data[0].seed = rnd() & 0xFFFF;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#endif
fg_data[0].num_y_points = rnd() & 1;
fg_data[0].grain_scale_shift = rnd() & 3;
fg_data[0].ar_coeff_shift = (rnd() & 3) + 6;
fg_data[0].ar_coeff_lag = i;
const int num_y_pos = 2 * fg_data[0].ar_coeff_lag * (fg_data[0].ar_coeff_lag + 1);
for (int n = 0; n < num_y_pos; n++)
fg_data[0].ar_coeffs_y[n] = (rnd() & 0xff) - 128;
dsp->generate_grain_y(grain_lut_y, fg_data HIGHBD_TAIL_SUFFIX);
const int uv = rnd() & 1;
const int num_uv_pos = num_y_pos + !!fg_data[0].num_y_points;
for (int n = 0; n < num_uv_pos; n++)
fg_data[0].ar_coeffs_uv[uv][n] = (rnd() & 0xff) - 128;
if (!fg_data[0].num_y_points)
fg_data[0].ar_coeffs_uv[uv][num_uv_pos] = 0;
memset(grain_lut_c, 0xff, sizeof(grain_lut_c));
memset(grain_lut_a, 0xff, sizeof(grain_lut_a));
call_ref(grain_lut_c, grain_lut_y, fg_data, uv HIGHBD_TAIL_SUFFIX);
call_new(grain_lut_a, grain_lut_y, fg_data, uv HIGHBD_TAIL_SUFFIX);
int w = ss_x ? 44 : GRAIN_WIDTH;
int h = ss_y ? 38 : GRAIN_HEIGHT;
checkasm_check_entry(grain_lut_c[0], sizeof(entry) * GRAIN_WIDTH,
grain_lut_a[0], sizeof(entry) * GRAIN_WIDTH,
w, h, "grain_lut");
bench_new(grain_lut_a, grain_lut_y, fg_data, uv HIGHBD_TAIL_SUFFIX);
}
}
}
report("gen_grain_uv");
}
static void check_fgy_sbrow(const Dav1dFilmGrainDSPContext *const dsp) {
PIXEL_RECT(c_dst, 128, 32);
PIXEL_RECT(a_dst, 128, 32);
PIXEL_RECT(src, 128, 32);
const ptrdiff_t stride = c_dst_stride;
declare_func(void, pixel *dst_row, const pixel *src_row, ptrdiff_t stride,
const Dav1dFilmGrainData *data, size_t pw,
const uint8_t scaling[SCALING_SIZE],
const entry grain_lut[][GRAIN_WIDTH],
int bh, int row_num HIGHBD_DECL_SUFFIX);
if (check_func(dsp->fgy_32x32xn, "fgy_32x32xn_%dbpc", BITDEPTH)) {
ALIGN_STK_16(Dav1dFilmGrainData, fg_data, 16,);
fg_data[0].seed = rnd() & 0xFFFF;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
uint8_t scaling[SCALING_SIZE];
entry grain_lut[GRAIN_HEIGHT + 1][GRAIN_WIDTH];
fg_data[0].grain_scale_shift = rnd() & 3;
fg_data[0].ar_coeff_shift = (rnd() & 3) + 6;
fg_data[0].ar_coeff_lag = rnd() & 3;
const int num_y_pos = 2 * fg_data[0].ar_coeff_lag * (fg_data[0].ar_coeff_lag + 1);
for (int n = 0; n < num_y_pos; n++)
fg_data[0].ar_coeffs_y[n] = (rnd() & 0xff) - 128;
dsp->generate_grain_y(grain_lut, fg_data HIGHBD_TAIL_SUFFIX);
fg_data[0].num_y_points = 2 + (rnd() % 13);
const int pad = 0xff / fg_data[0].num_y_points;
for (int n = 0; n < fg_data[0].num_y_points; n++) {
fg_data[0].y_points[n][0] = 0xff * n / fg_data[0].num_y_points;
fg_data[0].y_points[n][0] += rnd() % pad;
fg_data[0].y_points[n][1] = rnd() & 0xff;
}
generate_scaling(bitdepth_from_max(bitdepth_max), fg_data[0].y_points,
fg_data[0].num_y_points, scaling);
fg_data[0].clip_to_restricted_range = rnd() & 1;
fg_data[0].scaling_shift = (rnd() & 3) + 8;
for (fg_data[0].overlap_flag = 0; fg_data[0].overlap_flag <= 1;
fg_data[0].overlap_flag++)
{
for (int i = 0; i <= 2 * fg_data[0].overlap_flag; i++) {
int w, h, row_num;
if (fg_data[0].overlap_flag) {
w = 35 + (rnd() % 93);
if (i == 0) {
row_num = 0;
h = 1 + (rnd() % 31);
} else {
row_num = 1 + (rnd() & 0x7ff);
if (i == 1) {
h = 3 + (rnd() % 30);
} else {
h = 1 + (rnd() & 1);
}
}
} else {
w = 1 + (rnd() & 127);
h = 1 + (rnd() & 31);
row_num = rnd() & 0x7ff;
}
for (int y = 0; y < 32; y++) {
// Src pixels past the right edge can be uninitialized
for (int x = 0; x < 128; x++)
src[y * PXSTRIDE(stride) + x] = rnd();
for (int x = 0; x < w; x++)
src[y * PXSTRIDE(stride) + x] &= bitdepth_max;
}
CLEAR_PIXEL_RECT(c_dst);
CLEAR_PIXEL_RECT(a_dst);
call_ref(c_dst, src, stride, fg_data, w, scaling, grain_lut, h,
row_num HIGHBD_TAIL_SUFFIX);
call_new(a_dst, src, stride, fg_data, w, scaling, grain_lut, h,
row_num HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel_padded_align(c_dst, stride, a_dst, stride,
w, h, "dst", 32, 2);
}
}
fg_data[0].overlap_flag = 1;
for (int y = 0; y < 32; y++) {
// Make sure all pixels are in range
for (int x = 0; x < 128; x++)
src[y * PXSTRIDE(stride) + x] &= bitdepth_max;
}
bench_new(a_dst, src, stride, fg_data, 64, scaling, grain_lut, 32,
1 HIGHBD_TAIL_SUFFIX);
}
report("fgy_32x32xn");
}
static void check_fguv_sbrow(const Dav1dFilmGrainDSPContext *const dsp) {
PIXEL_RECT(c_dst, 128, 32);
PIXEL_RECT(a_dst, 128, 32);
PIXEL_RECT(src, 128, 32);
PIXEL_RECT(luma_src, 128, 32);
const ptrdiff_t lstride = luma_src_stride;
declare_func(void, pixel *dst_row, const pixel *src_row, ptrdiff_t stride,
const Dav1dFilmGrainData *data, size_t pw,
const uint8_t scaling[SCALING_SIZE],
const entry grain_lut[][GRAIN_WIDTH], int bh, int row_num,
const pixel *luma_row, ptrdiff_t luma_stride, int uv_pl,
int is_identity HIGHBD_DECL_SUFFIX);
for (int layout_idx = 0; layout_idx < 3; layout_idx++) {
const enum Dav1dPixelLayout layout = layout_idx + 1;
const int ss_x = layout != DAV1D_PIXEL_LAYOUT_I444;
const int ss_y = layout == DAV1D_PIXEL_LAYOUT_I420;
const ptrdiff_t stride = c_dst_stride;
for (int csfl = 0; csfl <= 1; csfl++) {
if (check_func(dsp->fguv_32x32xn[layout_idx],
"fguv_32x32xn_%dbpc_%s_csfl%d",
BITDEPTH, ss_name[layout_idx], csfl))
{
ALIGN_STK_16(Dav1dFilmGrainData, fg_data, 1,);
fg_data[0].seed = rnd() & 0xFFFF;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
const int uv_pl = rnd() & 1;
const int is_identity = rnd() & 1;
uint8_t scaling[SCALING_SIZE];
entry grain_lut[2][GRAIN_HEIGHT + 1][GRAIN_WIDTH];
fg_data[0].grain_scale_shift = rnd() & 3;
fg_data[0].ar_coeff_shift = (rnd() & 3) + 6;
fg_data[0].ar_coeff_lag = rnd() & 3;
const int num_y_pos = 2 * fg_data[0].ar_coeff_lag * (fg_data[0].ar_coeff_lag + 1);
for (int n = 0; n < num_y_pos; n++)
fg_data[0].ar_coeffs_y[n] = (rnd() & 0xff) - 128;
const int num_uv_pos = num_y_pos + 1;
for (int n = 0; n < num_uv_pos; n++)
fg_data[0].ar_coeffs_uv[uv_pl][n] = (rnd() & 0xff) - 128;
dsp->generate_grain_y(grain_lut[0], fg_data HIGHBD_TAIL_SUFFIX);
dsp->generate_grain_uv[layout_idx](grain_lut[1], grain_lut[0],
fg_data, uv_pl HIGHBD_TAIL_SUFFIX);
if (csfl) {
fg_data[0].num_y_points = 2 + (rnd() % 13);
const int pad = 0xff / fg_data[0].num_y_points;
for (int n = 0; n < fg_data[0].num_y_points; n++) {
fg_data[0].y_points[n][0] = 0xff * n / fg_data[0].num_y_points;
fg_data[0].y_points[n][0] += rnd() % pad;
fg_data[0].y_points[n][1] = rnd() & 0xff;
}
generate_scaling(bitdepth_from_max(bitdepth_max), fg_data[0].y_points,
fg_data[0].num_y_points, scaling);
} else {
fg_data[0].num_uv_points[uv_pl] = 2 + (rnd() % 9);
const int pad = 0xff / fg_data[0].num_uv_points[uv_pl];
for (int n = 0; n < fg_data[0].num_uv_points[uv_pl]; n++) {
fg_data[0].uv_points[uv_pl][n][0] = 0xff * n / fg_data[0].num_uv_points[uv_pl];
fg_data[0].uv_points[uv_pl][n][0] += rnd() % pad;
fg_data[0].uv_points[uv_pl][n][1] = rnd() & 0xff;
}
generate_scaling(bitdepth_from_max(bitdepth_max), fg_data[0].uv_points[uv_pl],
fg_data[0].num_uv_points[uv_pl], scaling);
fg_data[0].uv_mult[uv_pl] = (rnd() & 0xff) - 128;
fg_data[0].uv_luma_mult[uv_pl] = (rnd() & 0xff) - 128;
fg_data[0].uv_offset[uv_pl] = (rnd() & 0x1ff) - 256;
}
fg_data[0].clip_to_restricted_range = rnd() & 1;
fg_data[0].scaling_shift = (rnd() & 3) + 8;
fg_data[0].chroma_scaling_from_luma = csfl;
for (fg_data[0].overlap_flag = 0; fg_data[0].overlap_flag <= 1;
fg_data[0].overlap_flag++)
{
for (int i = 0; i <= 2 * fg_data[0].overlap_flag; i++) {
int w, h, row_num;
if (fg_data[0].overlap_flag) {
w = (36 >> ss_x) + (rnd() % (92 >> ss_x));
if (i == 0) {
row_num = 0;
h = 1 + (rnd() & (31 >> ss_y));
} else {
row_num = 1 + (rnd() & 0x7ff);
if (i == 1) {
h = (ss_y ? 2 : 3) + (rnd() % (ss_y ? 15 : 30));
} else {
h = ss_y ? 1 : 1 + (rnd() & 1);
}
}
} else {
w = 1 + (rnd() & (127 >> ss_x));
h = 1 + (rnd() & (31 >> ss_y));
row_num = rnd() & 0x7ff;
}
for (int y = 0; y < 32; y++) {
// Src pixels past the right edge can be uninitialized
for (int x = 0; x < 128; x++) {
src[y * PXSTRIDE(stride) + x] = rnd();
luma_src[y * PXSTRIDE(lstride) + x] = rnd();
}
for (int x = 0; x < w; x++)
src[y * PXSTRIDE(stride) + x] &= bitdepth_max;
for (int x = 0; x < (w << ss_x); x++)
luma_src[y * PXSTRIDE(lstride) + x] &= bitdepth_max;
}
CLEAR_PIXEL_RECT(c_dst);
CLEAR_PIXEL_RECT(a_dst);
call_ref(c_dst, src, stride, fg_data, w, scaling, grain_lut[1], h,
row_num, luma_src, lstride, uv_pl, is_identity HIGHBD_TAIL_SUFFIX);
call_new(a_dst, src, stride, fg_data, w, scaling, grain_lut[1], h,
row_num, luma_src, lstride, uv_pl, is_identity HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel_padded_align(c_dst, stride,
a_dst, stride,
w, h, "dst",
32 >> ss_x, 2);
}
}
fg_data[0].overlap_flag = 1;
for (int y = 0; y < 32; y++) {
// Make sure all pixels are in range
for (int x = 0; x < 128; x++) {
src[y * PXSTRIDE(stride) + x] &= bitdepth_max;
luma_src[y * PXSTRIDE(lstride) + x] &= bitdepth_max;
}
}
bench_new(a_dst, src, stride, fg_data, 32, scaling, grain_lut[1], 16,
1, luma_src, lstride, uv_pl, is_identity HIGHBD_TAIL_SUFFIX);
}
}
}
report("fguv_32x32xn");
}
void bitfn(checkasm_check_filmgrain)(void) {
Dav1dFilmGrainDSPContext c;
bitfn(dav1d_film_grain_dsp_init)(&c);
check_gen_grny(&c);
check_gen_grnuv(&c);
check_fgy_sbrow(&c);
check_fguv_sbrow(&c);
}

View File

@ -1,289 +0,0 @@
/*
* 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 "tests/checkasm/checkasm.h"
#include "src/ipred.h"
#include "src/levels.h"
#include <stdio.h>
static const char *const intra_pred_mode_names[N_IMPL_INTRA_PRED_MODES] = {
[DC_PRED] = "dc",
[DC_128_PRED] = "dc_128",
[TOP_DC_PRED] = "dc_top",
[LEFT_DC_PRED] = "dc_left",
[HOR_PRED] = "h",
[VERT_PRED] = "v",
[PAETH_PRED] = "paeth",
[SMOOTH_PRED] = "smooth",
[SMOOTH_V_PRED] = "smooth_v",
[SMOOTH_H_PRED] = "smooth_h",
[Z1_PRED] = "z1",
[Z2_PRED] = "z2",
[Z3_PRED] = "z3",
[FILTER_PRED] = "filter"
};
static const char *const cfl_ac_names[3] = { "420", "422", "444" };
static const char *const cfl_pred_mode_names[DC_128_PRED + 1] = {
[DC_PRED] = "cfl",
[DC_128_PRED] = "cfl_128",
[TOP_DC_PRED] = "cfl_top",
[LEFT_DC_PRED] = "cfl_left",
};
static const uint8_t z_angles[27] = {
3, 6, 9,
14, 17, 20, 23, 26, 29, 32,
36, 39, 42, 45, 48, 51, 54,
58, 61, 64, 67, 70, 73, 76,
81, 84, 87
};
static void check_intra_pred(Dav1dIntraPredDSPContext *const c) {
PIXEL_RECT(c_dst, 64, 64);
PIXEL_RECT(a_dst, 64, 64);
ALIGN_STK_64(pixel, topleft_buf, 257,);
pixel *const topleft = topleft_buf + 128;
declare_func(void, pixel *dst, ptrdiff_t stride, const pixel *topleft,
int width, int height, int angle, int max_width, int max_height
HIGHBD_DECL_SUFFIX);
for (int mode = 0; mode < N_IMPL_INTRA_PRED_MODES; mode++) {
int bpc_min = BITDEPTH, bpc_max = BITDEPTH;
if (mode == FILTER_PRED && BITDEPTH == 16) {
bpc_min = 10;
bpc_max = 12;
}
for (int bpc = bpc_min; bpc <= bpc_max; bpc += 2)
for (int w = 4; w <= (mode == FILTER_PRED ? 32 : 64); w <<= 1)
if (check_func(c->intra_pred[mode], "intra_pred_%s_w%d_%dbpc",
intra_pred_mode_names[mode], w, bpc))
{
for (int h = imax(w / 4, 4); h <= imin(w * 4,
(mode == FILTER_PRED ? 32 : 64)); h <<= 1)
{
const ptrdiff_t stride = c_dst_stride;
int a = 0, maxw = 0, maxh = 0;
if (mode >= Z1_PRED && mode <= Z3_PRED) { /* angle */
a = (90 * (mode - Z1_PRED) + z_angles[rnd() % 27]) |
(rnd() & 0x600);
if (mode == Z2_PRED) {
maxw = rnd(), maxh = rnd();
maxw = 1 + (maxw & (maxw & 4096 ? 4095 : w - 1));
maxh = 1 + (maxh & (maxh & 4096 ? 4095 : h - 1));
}
} else if (mode == FILTER_PRED) /* filter_idx */
a = (rnd() % 5) | (rnd() & ~511);
int bitdepth_max;
if (bpc == 16)
bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
else
bitdepth_max = (1 << bpc) - 1;
for (int i = -h * 2; i <= w * 2; i++)
topleft[i] = rnd() & bitdepth_max;
CLEAR_PIXEL_RECT(c_dst);
CLEAR_PIXEL_RECT(a_dst);
call_ref(c_dst, stride, topleft, w, h, a, maxw, maxh
HIGHBD_TAIL_SUFFIX);
call_new(a_dst, stride, topleft, w, h, a, maxw, maxh
HIGHBD_TAIL_SUFFIX);
if (checkasm_check_pixel_padded(c_dst, stride,
a_dst, stride,
w, h, "dst"))
{
if (mode == Z1_PRED || mode == Z3_PRED)
fprintf(stderr, "angle = %d (0x%03x)\n",
a & 0x1ff, a & 0x600);
else if (mode == Z2_PRED)
fprintf(stderr, "angle = %d (0x%03x), "
"max_width = %d, max_height = %d\n",
a & 0x1ff, a & 0x600, maxw, maxh);
else if (mode == FILTER_PRED)
fprintf(stderr, "filter_idx = %d\n", a & 0x1ff);
}
bench_new(a_dst, stride, topleft, w, h, a, 128, 128
HIGHBD_TAIL_SUFFIX);
}
}
}
report("intra_pred");
}
static void check_cfl_ac(Dav1dIntraPredDSPContext *const c) {
ALIGN_STK_64(int16_t, c_dst, 32 * 32,);
ALIGN_STK_64(int16_t, a_dst, 32 * 32,);
ALIGN_STK_64(pixel, luma, 32 * 32,);
declare_func(void, int16_t *ac, const pixel *y, ptrdiff_t stride,
int w_pad, int h_pad, int cw, int ch);
for (int layout = 1; layout <= DAV1D_PIXEL_LAYOUT_I444; layout++) {
const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444;
const int h_step = 2 >> ss_hor, v_step = 2 >> ss_ver;
for (int w = 4; w <= (32 >> ss_hor); w <<= 1)
if (check_func(c->cfl_ac[layout - 1], "cfl_ac_%s_w%d_%dbpc",
cfl_ac_names[layout - 1], w, BITDEPTH))
{
for (int h = imax(w / 4, 4);
h <= imin(w * 4, (32 >> ss_ver)); h <<= 1)
{
const ptrdiff_t stride = 32 * sizeof(pixel);
for (int w_pad = imax((w >> 2) - h_step, 0);
w_pad >= 0; w_pad -= h_step)
{
for (int h_pad = imax((h >> 2) - v_step, 0);
h_pad >= 0; h_pad -= v_step)
{
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int y = 0; y < (h << ss_ver); y++)
for (int x = 0; x < (w << ss_hor); x++)
luma[y * 32 + x] = rnd() & bitdepth_max;
call_ref(c_dst, luma, stride, w_pad, h_pad, w, h);
call_new(a_dst, luma, stride, w_pad, h_pad, w, h);
checkasm_check(int16_t, c_dst, w * sizeof(*c_dst),
a_dst, w * sizeof(*a_dst),
w, h, "dst");
}
}
bench_new(a_dst, luma, stride, 0, 0, w, h);
}
}
}
report("cfl_ac");
}
static void check_cfl_pred(Dav1dIntraPredDSPContext *const c) {
ALIGN_STK_64(pixel, c_dst, 32 * 32,);
ALIGN_STK_64(pixel, a_dst, 32 * 32,);
ALIGN_STK_64(int16_t, ac, 32 * 32,);
ALIGN_STK_64(pixel, topleft_buf, 257,);
pixel *const topleft = topleft_buf + 128;
declare_func(void, pixel *dst, ptrdiff_t stride, const pixel *topleft,
int width, int height, const int16_t *ac, int alpha
HIGHBD_DECL_SUFFIX);
for (int mode = 0; mode <= DC_128_PRED; mode += 1 + 2 * !mode)
for (int w = 4; w <= 32; w <<= 1)
if (check_func(c->cfl_pred[mode], "cfl_pred_%s_w%d_%dbpc",
cfl_pred_mode_names[mode], w, BITDEPTH))
{
for (int h = imax(w / 4, 4); h <= imin(w * 4, 32); h <<= 1)
{
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
const ptrdiff_t stride = w * sizeof(pixel);
int alpha = ((rnd() & 15) + 1) * (1 - (rnd() & 2));
for (int i = -h * 2; i <= w * 2; i++)
topleft[i] = rnd() & bitdepth_max;
int luma_avg = w * h >> 1;
for (int i = 0; i < w * h; i++)
luma_avg += ac[i] = rnd() & (bitdepth_max << 3);
luma_avg /= w * h;
for (int i = 0; i < w * h; i++)
ac[i] -= luma_avg;
call_ref(c_dst, stride, topleft, w, h, ac, alpha
HIGHBD_TAIL_SUFFIX);
call_new(a_dst, stride, topleft, w, h, ac, alpha
HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, stride, a_dst, stride,
w, h, "dst");
bench_new(a_dst, stride, topleft, w, h, ac, alpha
HIGHBD_TAIL_SUFFIX);
}
}
report("cfl_pred");
}
static void check_pal_pred(Dav1dIntraPredDSPContext *const c) {
ALIGN_STK_64(pixel, c_dst, 64 * 64,);
ALIGN_STK_64(pixel, a_dst, 64 * 64,);
ALIGN_STK_64(uint8_t, idx, 64 * 64,);
ALIGN_STK_16(uint16_t, pal, 8,);
declare_func(void, pixel *dst, ptrdiff_t stride, const uint16_t *pal,
const uint8_t *idx, int w, int h);
for (int w = 4; w <= 64; w <<= 1)
if (check_func(c->pal_pred, "pal_pred_w%d_%dbpc", w, BITDEPTH))
for (int h = imax(w / 4, 4); h <= imin(w * 4, 64); h <<= 1)
{
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
const ptrdiff_t stride = w * sizeof(pixel);
for (int i = 0; i < 8; i++)
pal[i] = rnd() & bitdepth_max;
for (int i = 0; i < w * h; i++)
idx[i] = rnd() & 7;
call_ref(c_dst, stride, pal, idx, w, h);
call_new(a_dst, stride, pal, idx, w, h);
checkasm_check_pixel(c_dst, stride, a_dst, stride, w, h, "dst");
bench_new(a_dst, stride, pal, idx, w, h);
}
report("pal_pred");
}
void bitfn(checkasm_check_ipred)(void) {
Dav1dIntraPredDSPContext c;
bitfn(dav1d_intra_pred_dsp_init)(&c);
check_intra_pred(&c);
check_cfl_ac(&c);
check_cfl_pred(&c);
check_pal_pred(&c);
}

View File

@ -1,308 +0,0 @@
/*
* 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 "tests/checkasm/checkasm.h"
#include <math.h>
#include "src/itx.h"
#include "src/levels.h"
#include "src/scan.h"
#include "src/tables.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.707106781186547524401
#endif
enum Tx1D { DCT, ADST, FLIPADST, IDENTITY, WHT };
static const uint8_t itx_1d_types[N_TX_TYPES_PLUS_LL][2] = {
[DCT_DCT] = { DCT, DCT },
[ADST_DCT] = { DCT, ADST },
[DCT_ADST] = { ADST, DCT },
[ADST_ADST] = { ADST, ADST },
[FLIPADST_DCT] = { DCT, FLIPADST },
[DCT_FLIPADST] = { FLIPADST, DCT },
[FLIPADST_FLIPADST] = { FLIPADST, FLIPADST },
[ADST_FLIPADST] = { FLIPADST, ADST },
[FLIPADST_ADST] = { ADST, FLIPADST },
[IDTX] = { IDENTITY, IDENTITY },
[V_DCT] = { IDENTITY, DCT },
[H_DCT] = { DCT, IDENTITY },
[V_ADST] = { IDENTITY, ADST },
[H_ADST] = { ADST, IDENTITY },
[V_FLIPADST] = { IDENTITY, FLIPADST },
[H_FLIPADST] = { FLIPADST, IDENTITY },
[WHT_WHT] = { WHT, WHT },
};
static const char *const itx_1d_names[5] = {
[DCT] = "dct",
[ADST] = "adst",
[FLIPADST] = "flipadst",
[IDENTITY] = "identity",
[WHT] = "wht"
};
static const double scaling_factors[9] = {
4.0000, /* 4x4 */
4.0000 * M_SQRT1_2, /* 4x8 8x4 */
2.0000, /* 4x16 8x8 16x4 */
2.0000 * M_SQRT1_2, /* 8x16 16x8 */
1.0000, /* 8x32 16x16 32x8 */
0.5000 * M_SQRT1_2, /* 16x32 32x16 */
0.2500, /* 16x64 32x32 64x16 */
0.1250 * M_SQRT1_2, /* 32x64 64x32 */
0.0625, /* 64x64 */
};
/* FIXME: Ensure that those forward transforms are similar to the real AV1
* transforms. The FLIPADST currently uses the ADST forward transform for
* example which is obviously "incorrect", but we're just using it for now
* since it does produce coefficients in the correct range at least. */
/* DCT-II */
static void fdct_1d(double *const out, const double *const in, const int sz) {
for (int i = 0; i < sz; i++) {
out[i] = 0.0;
for (int j = 0; j < sz; j++)
out[i] += in[j] * cos(M_PI * (2 * j + 1) * i / (sz * 2.0));
}
out[0] *= M_SQRT1_2;
}
/* See "Towards jointly optimal spatial prediction and adaptive transform in
* video/image coding", by J. Han, A. Saxena, and K. Rose
* IEEE Proc. ICASSP, pp. 726-729, Mar. 2010.
* and "A Butterfly Structured Design of The Hybrid Transform Coding Scheme",
* by Jingning Han, Yaowu Xu, and Debargha Mukherjee
* http://research.google.com/pubs/archive/41418.pdf
*/
static void fadst_1d(double *const out, const double *const in, const int sz) {
for (int i = 0; i < sz; i++) {
out[i] = 0.0;
for (int j = 0; j < sz; j++)
out[i] += in[j] * sin(M_PI *
(sz == 4 ? ( j + 1) * (2 * i + 1) / (8.0 + 1.0) :
(2 * j + 1) * (2 * i + 1) / (sz * 4.0)));
}
}
static void fwht4_1d(double *const out, const double *const in)
{
const double t0 = in[0] + in[1];
const double t3 = in[3] - in[2];
const double t4 = (t0 - t3) * 0.5;
const double t1 = t4 - in[1];
const double t2 = t4 - in[2];
out[0] = t0 - t2;
out[1] = t2;
out[2] = t3 + t1;
out[3] = t1;
}
static int copy_subcoefs(coef *coeff,
const enum RectTxfmSize tx, const enum TxfmType txtp,
const int sw, const int sh, const int subsh)
{
/* copy the topleft coefficients such that the return value (being the
* coefficient scantable index for the eob token) guarantees that only
* the topleft $sub out of $sz (where $sz >= $sub) coefficients in both
* dimensions are non-zero. This leads to braching to specific optimized
* simd versions (e.g. dc-only) so that we get full asm coverage in this
* test */
const enum TxClass tx_class = dav1d_tx_type_class[txtp];
const uint16_t *const scan = dav1d_scans[tx];
const int sub_high = subsh > 0 ? subsh * 8 - 1 : 0;
const int sub_low = subsh > 1 ? sub_high - 8 : 0;
int n, eob;
for (n = 0, eob = 0; n < sw * sh; n++) {
int rc, rcx, rcy;
if (tx_class == TX_CLASS_2D)
rc = scan[n], rcx = rc % sh, rcy = rc / sh;
else if (tx_class == TX_CLASS_H)
rcx = n % sh, rcy = n / sh, rc = n;
else /* tx_class == TX_CLASS_V */
rcx = n / sw, rcy = n % sw, rc = rcy * sh + rcx;
/* Pick a random eob within this sub-itx */
if (rcx > sub_high || rcy > sub_high) {
break; /* upper boundary */
} else if (!eob && (rcx > sub_low || rcy > sub_low))
eob = n; /* lower boundary */
}
if (eob)
eob += rnd() % (n - eob - 1);
if (tx_class == TX_CLASS_2D)
for (n = eob + 1; n < sw * sh; n++)
coeff[scan[n]] = 0;
else if (tx_class == TX_CLASS_H)
for (n = eob + 1; n < sw * sh; n++)
coeff[n] = 0;
else /* tx_class == TX_CLASS_V */ {
for (int rcx = eob / sw, rcy = eob % sw; rcx < sh; rcx++, rcy = -1)
while (++rcy < sw)
coeff[rcy * sh + rcx] = 0;
n = sw * sh;
}
for (; n < 32 * 32; n++)
coeff[n] = rnd();
return eob;
}
static int ftx(coef *const buf, const enum RectTxfmSize tx,
const enum TxfmType txtp, const int w, const int h,
const int subsh, const int bitdepth_max)
{
double out[64 * 64], temp[64 * 64];
const double scale = scaling_factors[ctz(w * h) - 4];
const int sw = imin(w, 32), sh = imin(h, 32);
for (int i = 0; i < h; i++) {
double in[64], temp_out[64];
for (int i = 0; i < w; i++)
in[i] = (rnd() & (2 * bitdepth_max + 1)) - bitdepth_max;
switch (itx_1d_types[txtp][0]) {
case DCT:
fdct_1d(temp_out, in, w);
break;
case ADST:
case FLIPADST:
fadst_1d(temp_out, in, w);
break;
case WHT:
fwht4_1d(temp_out, in);
break;
case IDENTITY:
memcpy(temp_out, in, w * sizeof(*temp_out));
break;
}
for (int j = 0; j < w; j++)
temp[j * h + i] = temp_out[j] * scale;
}
for (int i = 0; i < w; i++) {
switch (itx_1d_types[txtp][0]) {
case DCT:
fdct_1d(&out[i * h], &temp[i * h], h);
break;
case ADST:
case FLIPADST:
fadst_1d(&out[i * h], &temp[i * h], h);
break;
case WHT:
fwht4_1d(&out[i * h], &temp[i * h]);
break;
case IDENTITY:
memcpy(&out[i * h], &temp[i * h], h * sizeof(*out));
break;
}
}
for (int y = 0; y < sh; y++)
for (int x = 0; x < sw; x++)
buf[y * sw + x] = (coef) (out[y * w + x] + 0.5);
return copy_subcoefs(buf, tx, txtp, sw, sh, subsh);
}
void bitfn(checkasm_check_itx)(void) {
#if BITDEPTH == 16
const int bpc_min = 10, bpc_max = 12;
#else
const int bpc_min = 8, bpc_max = 8;
#endif
ALIGN_STK_64(coef, coeff, 2, [32 * 32]);
ALIGN_STK_64(pixel, c_dst, 64 * 64,);
ALIGN_STK_64(pixel, a_dst, 64 * 64,);
Dav1dInvTxfmDSPContext c = { { { 0 } } }; /* Zero unused function pointer elements. */
static const uint8_t txfm_size_order[N_RECT_TX_SIZES] = {
TX_4X4, RTX_4X8, RTX_4X16,
RTX_8X4, TX_8X8, RTX_8X16, RTX_8X32,
RTX_16X4, RTX_16X8, TX_16X16, RTX_16X32, RTX_16X64,
RTX_32X8, RTX_32X16, TX_32X32, RTX_32X64,
RTX_64X16, RTX_64X32, TX_64X64
};
static const uint8_t subsh_iters[5] = { 2, 2, 3, 5, 5 };
declare_func(void, pixel *dst, ptrdiff_t dst_stride, coef *coeff, int eob
HIGHBD_DECL_SUFFIX);
for (int i = 0; i < N_RECT_TX_SIZES; i++) {
const enum RectTxfmSize tx = txfm_size_order[i];
const int w = dav1d_txfm_dimensions[tx].w * 4;
const int h = dav1d_txfm_dimensions[tx].h * 4;
const int subsh_max = subsh_iters[imax(dav1d_txfm_dimensions[tx].lw,
dav1d_txfm_dimensions[tx].lh)];
for (int bpc = bpc_min; bpc <= bpc_max; bpc += 2) {
bitfn(dav1d_itx_dsp_init)(&c, bpc);
for (enum TxfmType txtp = 0; txtp < N_TX_TYPES_PLUS_LL; txtp++)
for (int subsh = 0; subsh < subsh_max; subsh++)
if (check_func(c.itxfm_add[tx][txtp],
"inv_txfm_add_%dx%d_%s_%s_%d_%dbpc",
w, h, itx_1d_names[itx_1d_types[txtp][0]],
itx_1d_names[itx_1d_types[txtp][1]], subsh,
bpc))
{
const int bitdepth_max = (1 << bpc) - 1;
const int eob = ftx(coeff[0], tx, txtp, w, h, subsh, bitdepth_max);
memcpy(coeff[1], coeff[0], sizeof(*coeff));
for (int j = 0; j < w * h; j++)
c_dst[j] = a_dst[j] = rnd() & bitdepth_max;
call_ref(c_dst, w * sizeof(*c_dst), coeff[0], eob
HIGHBD_TAIL_SUFFIX);
call_new(a_dst, w * sizeof(*c_dst), coeff[1], eob
HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, w * sizeof(*c_dst),
a_dst, w * sizeof(*a_dst),
w, h, "dst");
if (memcmp(coeff[0], coeff[1], sizeof(*coeff)))
fail();
bench_new(a_dst, w * sizeof(*c_dst), coeff[0], eob
HIGHBD_TAIL_SUFFIX);
}
}
report("add_%dx%d", w, h);
}
}

View File

@ -1,203 +0,0 @@
/*
* 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 "tests/checkasm/checkasm.h"
#include <string.h>
#include "src/levels.h"
#include "src/loopfilter.h"
static void init_lpf_border(pixel *const dst, const ptrdiff_t stride,
int E, int I, const int bitdepth_max)
{
const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8;
const int F = 1 << bitdepth_min_8;
E <<= bitdepth_min_8;
I <<= bitdepth_min_8;
const int filter_type = rnd() % 4;
const int edge_diff = rnd() % ((E + 2) * 4) - 2 * (E + 2);
switch (filter_type) {
case 0: // random, unfiltered
for (int i = -8; i < 8; i++)
dst[i * stride] = rnd() & bitdepth_max;
break;
case 1: // long flat
dst[-8 * stride] = rnd() & bitdepth_max;
dst[+7 * stride] = rnd() & bitdepth_max;
dst[+0 * stride] = rnd() & bitdepth_max;
dst[-1 * stride] = iclip_pixel(dst[+0 * stride] + edge_diff);
for (int i = 1; i < 7; i++) {
dst[-(1 + i) * stride] = iclip_pixel(dst[-1 * stride] +
rnd() % (2 * (F + 1)) - (F + 1));
dst[+(0 + i) * stride] = iclip_pixel(dst[+0 * stride] +
rnd() % (2 * (F + 1)) - (F + 1));
}
break;
case 2: // short flat
for (int i = 4; i < 8; i++) {
dst[-(1 + i) * stride] = rnd() & bitdepth_max;
dst[+(0 + i) * stride] = rnd() & bitdepth_max;
}
dst[+0 * stride] = rnd() & bitdepth_max;
dst[-1 * stride] = iclip_pixel(dst[+0 * stride] + edge_diff);
for (int i = 1; i < 4; i++) {
dst[-(1 + i) * stride] = iclip_pixel(dst[-1 * stride] +
rnd() % (2 * (F + 1)) - (F + 1));
dst[+(0 + i) * stride] = iclip_pixel(dst[+0 * stride] +
rnd() % (2 * (F + 1)) - (F + 1));
}
break;
case 3: // normal or hev
for (int i = 4; i < 8; i++) {
dst[-(1 + i) * stride] = rnd() & bitdepth_max;
dst[+(0 + i) * stride] = rnd() & bitdepth_max;
}
dst[+0 * stride] = rnd() & bitdepth_max;
dst[-1 * stride] = iclip_pixel(dst[+0 * stride] + edge_diff);
for (int i = 1; i < 4; i++) {
dst[-(1 + i) * stride] = iclip_pixel(dst[-(0 + i) * stride] +
rnd() % (2 * (I + 1)) - (I + 1));
dst[+(0 + i) * stride] = iclip_pixel(dst[+(i - 1) * stride] +
rnd() % (2 * (I + 1)) - (I + 1));
}
break;
}
}
static void check_lpf_sb(loopfilter_sb_fn fn, const char *const name,
const int n_blks, const int lf_idx,
const int is_chroma, const int dir)
{
ALIGN_STK_64(pixel, c_dst_mem, 128 * 16,);
ALIGN_STK_64(pixel, a_dst_mem, 128 * 16,);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const uint32_t *mask,
const uint8_t (*l)[4], ptrdiff_t b4_stride,
const Av1FilterLUT *lut, int w HIGHBD_DECL_SUFFIX);
pixel *a_dst, *c_dst;
ptrdiff_t stride, b4_stride;
int w, h;
if (dir) {
a_dst = a_dst_mem + 128 * 8;
c_dst = c_dst_mem + 128 * 8;
w = 128;
h = 16;
b4_stride = 32;
} else {
a_dst = a_dst_mem + 8;
c_dst = c_dst_mem + 8;
w = 16;
h = 128;
b4_stride = 2;
}
stride = w * sizeof(pixel);
Av1FilterLUT lut;
const int sharp = rnd() & 7;
for (int level = 0; level < 64; level++) {
int limit = level;
if (sharp > 0) {
limit >>= (sharp + 3) >> 2;
limit = imin(limit, 9 - sharp);
}
limit = imax(limit, 1);
lut.i[level] = limit;
lut.e[level] = 2 * (level + 2) + limit;
}
lut.sharp[0] = (sharp + 3) >> 2;
lut.sharp[1] = sharp ? 9 - sharp : 0xff;
const int n_strengths = is_chroma ? 2 : 3;
for (int i = 0; i < n_strengths; i++) {
if (check_func(fn, "%s_w%d_%dbpc", name,
is_chroma ? 4 + 2 * i : 4 << i, BITDEPTH))
{
uint32_t vmask[4] = { 0 };
uint8_t l[32 * 2][4];
for (int j = 0; j < n_blks; j++) {
const int idx = rnd() % (i + 2);
if (idx) vmask[idx - 1] |= 1U << j;
if (dir) {
l[j][lf_idx] = rnd() & 63;
l[j + 32][lf_idx] = rnd() & 63;
} else {
l[j * 2][lf_idx] = rnd() & 63;
l[j * 2 + 1][lf_idx] = rnd() & 63;
}
}
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int i = 0; i < 4 * n_blks; i++) {
const int x = i >> 2;
int L;
if (dir) {
L = l[32 + x][lf_idx] ? l[32 + x][lf_idx] : l[x][lf_idx];
} else {
L = l[2 * x + 1][lf_idx] ? l[2 * x + 1][lf_idx] : l[2 * x][lf_idx];
}
init_lpf_border(c_dst + i * (dir ? 1 : 16), dir ? 128 : 1,
lut.e[L], lut.i[L], bitdepth_max);
}
memcpy(a_dst_mem, c_dst_mem, 128 * sizeof(pixel) * 16);
call_ref(c_dst, stride,
vmask, (const uint8_t(*)[4]) &l[dir ? 32 : 1][lf_idx], b4_stride,
&lut, n_blks HIGHBD_TAIL_SUFFIX);
call_new(a_dst, stride,
vmask, (const uint8_t(*)[4]) &l[dir ? 32 : 1][lf_idx], b4_stride,
&lut, n_blks HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst_mem, stride, a_dst_mem, stride,
w, h, "dst");
bench_new(a_dst, stride,
vmask, (const uint8_t(*)[4]) &l[dir ? 32 : 1][lf_idx], b4_stride,
&lut, n_blks HIGHBD_TAIL_SUFFIX);
}
}
report(name);
}
void bitfn(checkasm_check_loopfilter)(void) {
Dav1dLoopFilterDSPContext c;
bitfn(dav1d_loop_filter_dsp_init)(&c);
check_lpf_sb(c.loop_filter_sb[0][0], "lpf_h_sb_y", 32, 0, 0, 0);
check_lpf_sb(c.loop_filter_sb[0][1], "lpf_v_sb_y", 32, 1, 0, 1);
check_lpf_sb(c.loop_filter_sb[1][0], "lpf_h_sb_uv", 16, 2, 1, 0);
check_lpf_sb(c.loop_filter_sb[1][1], "lpf_v_sb_uv", 16, 2, 1, 1);
}

View File

@ -1,202 +0,0 @@
/*
* 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 "tests/checkasm/checkasm.h"
#include <stdio.h>
#include <string.h>
#include "src/levels.h"
#include "src/looprestoration.h"
#include "src/tables.h"
static int to_binary(int x) { /* 0-15 -> 0000-1111 */
return (x & 1) + 5 * (x & 2) + 25 * (x & 4) + 125 * (x & 8);
}
static void init_tmp(pixel *buf, const ptrdiff_t stride,
const int w, const int h, const int bitdepth_max)
{
const int noise_mask = bitdepth_max >> 4;
const int x_off = rnd() & 7, y_off = rnd() & 7;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
buf[x] = (((x + x_off) ^ (y + y_off)) & 8 ? bitdepth_max : 0) ^
(rnd() & noise_mask);
}
buf += PXSTRIDE(stride);
}
}
static void check_wiener(Dav1dLoopRestorationDSPContext *const c, const int bpc) {
ALIGN_STK_64(pixel, c_src, 448 * 64,), *const c_dst = c_src + 32;
ALIGN_STK_64(pixel, a_src, 448 * 64,), *const a_dst = a_src + 32;
ALIGN_STK_64(pixel, edge_buf, 448 * 8,), *const h_edge = edge_buf + 32;
pixel left[64][4];
LooprestorationParams params;
int16_t (*const filter)[8] = params.filter;
declare_func(void, pixel *dst, ptrdiff_t dst_stride,
const pixel (*const left)[4],
const pixel *lpf, ptrdiff_t lpf_stride,
int w, int h, const LooprestorationParams *params,
enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX);
for (int t = 0; t < 2; t++) {
if (check_func(c->wiener[t], "wiener_%dtap_%dbpc", t ? 5 : 7, bpc)) {
filter[0][0] = filter[0][6] = t ? 0 : (rnd() & 15) - 5;
filter[0][1] = filter[0][5] = (rnd() & 31) - 23;
filter[0][2] = filter[0][4] = (rnd() & 63) - 17;
filter[0][3] = -(filter[0][0] + filter[0][1] + filter[0][2]) * 2;
#if BITDEPTH != 8
filter[0][3] += 128;
#endif
filter[1][0] = filter[1][6] = t ? 0 : (rnd() & 15) - 5;
filter[1][1] = filter[1][5] = (rnd() & 31) - 23;
filter[1][2] = filter[1][4] = (rnd() & 63) - 17;
filter[1][3] = 128 - (filter[1][0] + filter[1][1] + filter[1][2]) * 2;
const int base_w = 1 + (rnd() % 384);
const int base_h = 1 + (rnd() & 63);
const int bitdepth_max = (1 << bpc) - 1;
init_tmp(c_src, 448 * sizeof(pixel), 448, 64, bitdepth_max);
init_tmp(edge_buf, 448 * sizeof(pixel), 448, 8, bitdepth_max);
init_tmp((pixel *) left, 4 * sizeof(pixel), 4, 64, bitdepth_max);
for (enum LrEdgeFlags edges = 0; edges <= 0xf; edges++) {
const int w = edges & LR_HAVE_RIGHT ? 256 : base_w;
const int h = edges & LR_HAVE_BOTTOM ? 64 : base_h;
memcpy(a_src, c_src, 448 * 64 * sizeof(pixel));
call_ref(c_dst, 448 * sizeof(pixel), left,
h_edge, 448 * sizeof(pixel),
w, h, &params, edges HIGHBD_TAIL_SUFFIX);
call_new(a_dst, 448 * sizeof(pixel), left,
h_edge, 448 * sizeof(pixel),
w, h, &params, edges HIGHBD_TAIL_SUFFIX);
if (checkasm_check_pixel(c_dst, 448 * sizeof(pixel),
a_dst, 448 * sizeof(pixel),
w, h, "dst"))
{
fprintf(stderr, "size = %dx%d, edges = %04d\n",
w, h, to_binary(edges));
break;
}
}
bench_new(a_dst, 448 * sizeof(pixel), left,
h_edge, 448 * sizeof(pixel),
256, 64, &params, 0xf HIGHBD_TAIL_SUFFIX);
}
}
}
static void check_sgr(Dav1dLoopRestorationDSPContext *const c, const int bpc) {
ALIGN_STK_64(pixel, c_src, 448 * 64,), *const c_dst = c_src + 32;
ALIGN_STK_64(pixel, a_src, 448 * 64,), *const a_dst = a_src + 32;
ALIGN_STK_64(pixel, edge_buf, 448 * 8,), *const h_edge = edge_buf + 32;
pixel left[64][4];
LooprestorationParams params;
declare_func(void, pixel *dst, ptrdiff_t dst_stride,
const pixel (*const left)[4],
const pixel *lpf, ptrdiff_t lpf_stride,
int w, int h, const LooprestorationParams *params,
enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX);
static const struct { char name[4]; uint8_t idx; } sgr_data[3] = {
{ "5x5", 14 },
{ "3x3", 10 },
{ "mix", 0 },
};
for (int i = 0; i < 3; i++) {
if (check_func(c->sgr[i], "sgr_%s_%dbpc", sgr_data[i].name, bpc)) {
const uint16_t *const sgr_params = dav1d_sgr_params[sgr_data[i].idx];
params.sgr.s0 = sgr_params[0];
params.sgr.s1 = sgr_params[1];
params.sgr.w0 = sgr_params[0] ? (rnd() & 127) - 96 : 0;
params.sgr.w1 = (sgr_params[1] ? 160 - (rnd() & 127) : 33) - params.sgr.w0;
const int base_w = 1 + (rnd() % 384);
const int base_h = 1 + (rnd() & 63);
const int bitdepth_max = (1 << bpc) - 1;
init_tmp(c_src, 448 * sizeof(pixel), 448, 64, bitdepth_max);
init_tmp(edge_buf, 448 * sizeof(pixel), 448, 8, bitdepth_max);
init_tmp((pixel *) left, 4 * sizeof(pixel), 4, 64, bitdepth_max);
for (enum LrEdgeFlags edges = 0; edges <= 0xf; edges++) {
const int w = edges & LR_HAVE_RIGHT ? 256 : base_w;
const int h = edges & LR_HAVE_BOTTOM ? 64 : base_h;
memcpy(a_src, c_src, 448 * 64 * sizeof(pixel));
call_ref(c_dst, 448 * sizeof(pixel), left,
h_edge, 448 * sizeof(pixel),
w, h, &params, edges HIGHBD_TAIL_SUFFIX);
call_new(a_dst, 448 * sizeof(pixel), left,
h_edge, 448 * sizeof(pixel),
w, h, &params, edges HIGHBD_TAIL_SUFFIX);
if (checkasm_check_pixel(c_dst, 448 * sizeof(pixel),
a_dst, 448 * sizeof(pixel),
w, h, "dst"))
{
fprintf(stderr, "size = %dx%d, edges = %04d\n",
w, h, to_binary(edges));
break;
}
}
bench_new(a_dst, 448 * sizeof(pixel), left,
h_edge, 448 * sizeof(pixel),
256, 64, &params, 0xf HIGHBD_TAIL_SUFFIX);
}
}
}
void bitfn(checkasm_check_looprestoration)(void) {
#if BITDEPTH == 16
const int bpc_min = 10, bpc_max = 12;
#else
const int bpc_min = 8, bpc_max = 8;
#endif
for (int bpc = bpc_min; bpc <= bpc_max; bpc += 2) {
Dav1dLoopRestorationDSPContext c;
bitfn(dav1d_loop_restoration_dsp_init)(&c, bpc);
check_wiener(&c, bpc);
}
report("wiener");
for (int bpc = bpc_min; bpc <= bpc_max; bpc += 2) {
Dav1dLoopRestorationDSPContext c;
bitfn(dav1d_loop_restoration_dsp_init)(&c, bpc);
check_sgr(&c, bpc);
}
report("sgr");
}

View File

@ -1,756 +0,0 @@
/*
* 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 "tests/checkasm/checkasm.h"
#include "src/levels.h"
#include "src/mc.h"
static const char *const filter_names[] = {
"8tap_regular", "8tap_regular_smooth", "8tap_regular_sharp",
"8tap_sharp_regular", "8tap_sharp_smooth", "8tap_sharp",
"8tap_smooth_regular", "8tap_smooth", "8tap_smooth_sharp",
"bilinear"
};
static const char *const mxy_names[] = { "0", "h", "v", "hv" };
static const char *const scaled_paths[] = { "", "_dy1", "_dy2" };
static int mc_h_next(const int h) {
switch (h) {
case 4:
case 8:
case 16:
return (h * 3) >> 1;
case 6:
case 12:
case 24:
return (h & (h - 1)) * 2;
default:
return h * 2;
}
}
static void check_mc(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, src_buf, 135 * 135,);
ALIGN_STK_64(pixel, c_dst, 128 * 128,);
ALIGN_STK_64(pixel, a_dst, 128 * 128,);
const pixel *src = src_buf + 135 * 3 + 3;
const ptrdiff_t src_stride = 135 * sizeof(pixel);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *src,
ptrdiff_t src_stride, int w, int h, int mx, int my
HIGHBD_DECL_SUFFIX);
for (int filter = 0; filter < N_2D_FILTERS; filter++)
for (int w = 2; w <= 128; w <<= 1) {
const ptrdiff_t dst_stride = w * sizeof(pixel);
for (int mxy = 0; mxy < 4; mxy++)
if (check_func(c->mc[filter], "mc_%s_w%d_%s_%dbpc",
filter_names[filter], w, mxy_names[mxy], BITDEPTH))
{
const int h_min = w <= 32 ? 2 : w / 4;
const int h_max = imax(imin(w * 4, 128), 32);
for (int h = h_min; h <= h_max; h = mc_h_next(h)) {
const int mx = (mxy & 1) ? rnd() % 15 + 1 : 0;
const int my = (mxy & 2) ? rnd() % 15 + 1 : 0;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int i = 0; i < 135 * 135; i++)
src_buf[i] = rnd() & bitdepth_max;
call_ref(c_dst, dst_stride, src, src_stride, w, h,
mx, my HIGHBD_TAIL_SUFFIX);
call_new(a_dst, dst_stride, src, src_stride, w, h,
mx, my HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, dst_stride,
a_dst, dst_stride,
w, h, "dst");
if (filter == FILTER_2D_8TAP_REGULAR ||
filter == FILTER_2D_BILINEAR)
{
bench_new(a_dst, dst_stride, src, src_stride, w, h,
mx, my HIGHBD_TAIL_SUFFIX);
}
}
}
}
report("mc");
}
/* Generate worst case input in the topleft corner, randomize the rest */
static void generate_mct_input(pixel *const buf, const int bitdepth_max) {
static const int8_t pattern[8] = { -1, 0, -1, 0, 0, -1, 0, -1 };
const int sign = -(rnd() & 1);
for (int y = 0; y < 135; y++)
for (int x = 0; x < 135; x++)
buf[135*y+x] = ((x | y) < 8 ? (pattern[x] ^ pattern[y] ^ sign)
: rnd()) & bitdepth_max;
}
static void check_mct(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, src_buf, 135 * 135,);
ALIGN_STK_64(int16_t, c_tmp, 128 * 128,);
ALIGN_STK_64(int16_t, a_tmp, 128 * 128,);
const pixel *src = src_buf + 135 * 3 + 3;
const ptrdiff_t src_stride = 135 * sizeof(pixel);
declare_func(void, int16_t *tmp, const pixel *src, ptrdiff_t src_stride,
int w, int h, int mx, int my HIGHBD_DECL_SUFFIX);
for (int filter = 0; filter < N_2D_FILTERS; filter++)
for (int w = 4; w <= 128; w <<= 1)
for (int mxy = 0; mxy < 4; mxy++)
if (check_func(c->mct[filter], "mct_%s_w%d_%s_%dbpc",
filter_names[filter], w, mxy_names[mxy], BITDEPTH))
for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
{
const int mx = (mxy & 1) ? rnd() % 15 + 1 : 0;
const int my = (mxy & 2) ? rnd() % 15 + 1 : 0;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
generate_mct_input(src_buf, bitdepth_max);
call_ref(c_tmp, src, src_stride, w, h,
mx, my HIGHBD_TAIL_SUFFIX);
call_new(a_tmp, src, src_stride, w, h,
mx, my HIGHBD_TAIL_SUFFIX);
checkasm_check(int16_t, c_tmp, w * sizeof(*c_tmp),
a_tmp, w * sizeof(*a_tmp),
w, h, "tmp");
if (filter == FILTER_2D_8TAP_REGULAR ||
filter == FILTER_2D_BILINEAR)
{
bench_new(a_tmp, src, src_stride, w, h,
mx, my HIGHBD_TAIL_SUFFIX);
}
}
report("mct");
}
static void check_mc_scaled(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, src_buf, 263 * 263,);
ALIGN_STK_64(pixel, c_dst, 128 * 128,);
ALIGN_STK_64(pixel, a_dst, 128 * 128,);
const pixel *src = src_buf + 263 * 3 + 3;
const ptrdiff_t src_stride = 263 * sizeof(pixel);
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *src,
ptrdiff_t src_stride, int w, int h,
int mx, int my, int dx, int dy HIGHBD_DECL_SUFFIX);
for (int filter = 0; filter < N_2D_FILTERS; filter++)
for (int w = 2; w <= 128; w <<= 1) {
const ptrdiff_t dst_stride = w * sizeof(pixel);
for (int p = 0; p < 3; ++p) {
if (check_func(c->mc_scaled[filter], "mc_scaled_%s_w%d%s_%dbpc",
filter_names[filter], w, scaled_paths[p], BITDEPTH))
{
const int h_min = w <= 32 ? 2 : w / 4;
const int h_max = imax(imin(w * 4, 128), 32);
for (int h = h_min; h <= h_max; h = mc_h_next(h)) {
const int mx = rnd() % 1024;
const int my = rnd() % 1024;
const int dx = rnd() % 2048 + 1;
const int dy = !p
? rnd() % 2048 + 1
: p << 10; // ystep=1.0 and ystep=2.0 paths
for (int k = 0; k < 263 * 263; k++)
src_buf[k] = rnd() & bitdepth_max;
call_ref(c_dst, dst_stride, src, src_stride,
w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
call_new(a_dst, dst_stride, src, src_stride,
w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, dst_stride,
a_dst, dst_stride, w, h, "dst");
if (filter == FILTER_2D_8TAP_REGULAR ||
filter == FILTER_2D_BILINEAR)
bench_new(a_dst, dst_stride, src, src_stride,
w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
}
}
}
}
report("mc_scaled");
}
static void check_mct_scaled(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, src_buf, 263 * 263,);
ALIGN_STK_64(int16_t, c_tmp, 128 * 128,);
ALIGN_STK_64(int16_t, a_tmp, 128 * 128,);
const pixel *src = src_buf + 263 * 3 + 3;
const ptrdiff_t src_stride = 263 * sizeof(pixel);
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
declare_func(void, int16_t *tmp, const pixel *src, ptrdiff_t src_stride,
int w, int h, int mx, int my, int dx, int dy HIGHBD_DECL_SUFFIX);
for (int filter = 0; filter < N_2D_FILTERS; filter++)
for (int w = 4; w <= 128; w <<= 1)
for (int p = 0; p < 3; ++p) {
if (check_func(c->mct_scaled[filter], "mct_scaled_%s_w%d%s_%dbpc",
filter_names[filter], w, scaled_paths[p], BITDEPTH))
{
const int h_min = imax(w / 4, 4);
const int h_max = imin(w * 4, 128);
for (int h = h_min; h <= h_max; h = mc_h_next(h)) {
const int mx = rnd() % 1024;
const int my = rnd() % 1024;
const int dx = rnd() % 2048 + 1;
const int dy = !p
? rnd() % 2048 + 1
: p << 10; // ystep=1.0 and ystep=2.0 paths
for (int k = 0; k < 263 * 263; k++)
src_buf[k] = rnd() & bitdepth_max;
call_ref(c_tmp, src, src_stride,
w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
call_new(a_tmp, src, src_stride,
w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
checkasm_check(int16_t, c_tmp, w * sizeof(*c_tmp),
a_tmp, w * sizeof(*a_tmp),
w, h, "tmp");
if (filter == FILTER_2D_8TAP_REGULAR ||
filter == FILTER_2D_BILINEAR)
bench_new(a_tmp, src, src_stride,
w, h, mx, my, dx, dy HIGHBD_TAIL_SUFFIX);
}
}
}
report("mct_scaled");
}
static void init_tmp(Dav1dMCDSPContext *const c, pixel *const buf,
int16_t (*const tmp)[128 * 128], const int bitdepth_max)
{
for (int i = 0; i < 2; i++) {
generate_mct_input(buf, bitdepth_max);
c->mct[FILTER_2D_8TAP_SHARP](tmp[i], buf + 135 * 3 + 3,
135 * sizeof(pixel), 128, 128,
8, 8 HIGHBD_TAIL_SUFFIX);
}
}
static void check_avg(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(int16_t, tmp, 2, [128 * 128]);
ALIGN_STK_64(pixel, c_dst, 135 * 135,);
ALIGN_STK_64(pixel, a_dst, 128 * 128,);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const int16_t *tmp1,
const int16_t *tmp2, int w, int h HIGHBD_DECL_SUFFIX);
for (int w = 4; w <= 128; w <<= 1)
if (check_func(c->avg, "avg_w%d_%dbpc", w, BITDEPTH)) {
ptrdiff_t dst_stride = w * sizeof(pixel);
for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
{
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
init_tmp(c, c_dst, tmp, bitdepth_max);
call_ref(c_dst, dst_stride, tmp[0], tmp[1], w, h HIGHBD_TAIL_SUFFIX);
call_new(a_dst, dst_stride, tmp[0], tmp[1], w, h HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, dst_stride, a_dst, dst_stride,
w, h, "dst");
bench_new(a_dst, dst_stride, tmp[0], tmp[1], w, h HIGHBD_TAIL_SUFFIX);
}
}
report("avg");
}
static void check_w_avg(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(int16_t, tmp, 2, [128 * 128]);
ALIGN_STK_64(pixel, c_dst, 135 * 135,);
ALIGN_STK_64(pixel, a_dst, 128 * 128,);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const int16_t *tmp1,
const int16_t *tmp2, int w, int h, int weight HIGHBD_DECL_SUFFIX);
for (int w = 4; w <= 128; w <<= 1)
if (check_func(c->w_avg, "w_avg_w%d_%dbpc", w, BITDEPTH)) {
ptrdiff_t dst_stride = w * sizeof(pixel);
for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
{
int weight = rnd() % 15 + 1;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
init_tmp(c, c_dst, tmp, bitdepth_max);
call_ref(c_dst, dst_stride, tmp[0], tmp[1], w, h, weight HIGHBD_TAIL_SUFFIX);
call_new(a_dst, dst_stride, tmp[0], tmp[1], w, h, weight HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, dst_stride, a_dst, dst_stride,
w, h, "dst");
bench_new(a_dst, dst_stride, tmp[0], tmp[1], w, h, weight HIGHBD_TAIL_SUFFIX);
}
}
report("w_avg");
}
static void check_mask(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(int16_t, tmp, 2, [128 * 128]);
ALIGN_STK_64(pixel, c_dst, 135 * 135,);
ALIGN_STK_64(pixel, a_dst, 128 * 128,);
ALIGN_STK_64(uint8_t, mask, 128 * 128,);
for (int i = 0; i < 128 * 128; i++)
mask[i] = rnd() % 65;
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const int16_t *tmp1,
const int16_t *tmp2, int w, int h, const uint8_t *mask
HIGHBD_DECL_SUFFIX);
for (int w = 4; w <= 128; w <<= 1)
if (check_func(c->mask, "mask_w%d_%dbpc", w, BITDEPTH)) {
ptrdiff_t dst_stride = w * sizeof(pixel);
for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
{
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
init_tmp(c, c_dst, tmp, bitdepth_max);
call_ref(c_dst, dst_stride, tmp[0], tmp[1], w, h, mask HIGHBD_TAIL_SUFFIX);
call_new(a_dst, dst_stride, tmp[0], tmp[1], w, h, mask HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, dst_stride, a_dst, dst_stride,
w, h, "dst");
bench_new(a_dst, dst_stride, tmp[0], tmp[1], w, h, mask HIGHBD_TAIL_SUFFIX);
}
}
report("mask");
}
static void check_w_mask(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(int16_t, tmp, 2, [128 * 128]);
ALIGN_STK_64(pixel, c_dst, 135 * 135,);
ALIGN_STK_64(pixel, a_dst, 128 * 128,);
ALIGN_STK_64(uint8_t, c_mask, 128 * 128,);
ALIGN_STK_64(uint8_t, a_mask, 128 * 128,);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const int16_t *tmp1,
const int16_t *tmp2, int w, int h, uint8_t *mask, int sign
HIGHBD_DECL_SUFFIX);
static const uint16_t ss[] = { 444, 422, 420 };
static const uint8_t ss_hor[] = { 0, 1, 1 };
static const uint8_t ss_ver[] = { 0, 0, 1 };
for (int i = 0; i < 3; i++)
for (int w = 4; w <= 128; w <<= 1)
if (check_func(c->w_mask[i], "w_mask_%d_w%d_%dbpc", ss[i], w,
BITDEPTH))
{
ptrdiff_t dst_stride = w * sizeof(pixel);
for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1)
{
int sign = rnd() & 1;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
init_tmp(c, c_dst, tmp, bitdepth_max);
call_ref(c_dst, dst_stride, tmp[0], tmp[1], w, h,
c_mask, sign HIGHBD_TAIL_SUFFIX);
call_new(a_dst, dst_stride, tmp[0], tmp[1], w, h,
a_mask, sign HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, dst_stride,
a_dst, dst_stride,
w, h, "dst");
checkasm_check(uint8_t, c_mask, w >> ss_hor[i],
a_mask, w >> ss_hor[i],
w >> ss_hor[i], h >> ss_ver[i],
"mask");
bench_new(a_dst, dst_stride, tmp[0], tmp[1], w, h,
a_mask, sign HIGHBD_TAIL_SUFFIX);
}
}
report("w_mask");
}
static void check_blend(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, tmp, 32 * 32,);
ALIGN_STK_64(pixel, c_dst, 32 * 32,);
ALIGN_STK_64(pixel, a_dst, 32 * 32,);
ALIGN_STK_64(uint8_t, mask, 32 * 32,);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *tmp,
int w, int h, const uint8_t *mask);
for (int w = 4; w <= 32; w <<= 1) {
const ptrdiff_t dst_stride = w * sizeof(pixel);
if (check_func(c->blend, "blend_w%d_%dbpc", w, BITDEPTH))
for (int h = imax(w / 2, 4); h <= imin(w * 2, 32); h <<= 1) {
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int i = 0; i < 32 * 32; i++) {
tmp[i] = rnd() & bitdepth_max;
mask[i] = rnd() % 65;
}
for (int i = 0; i < w * h; i++)
c_dst[i] = a_dst[i] = rnd() & bitdepth_max;
call_ref(c_dst, dst_stride, tmp, w, h, mask);
call_new(a_dst, dst_stride, tmp, w, h, mask);
checkasm_check_pixel(c_dst, dst_stride, a_dst, dst_stride,
w, h, "dst");
bench_new(a_dst, dst_stride, tmp, w, h, mask);
}
}
report("blend");
}
static void check_blend_v(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, tmp, 32 * 128,);
ALIGN_STK_64(pixel, c_dst, 32 * 128,);
ALIGN_STK_64(pixel, a_dst, 32 * 128,);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *tmp,
int w, int h);
for (int w = 2; w <= 32; w <<= 1) {
const ptrdiff_t dst_stride = w * sizeof(pixel);
if (check_func(c->blend_v, "blend_v_w%d_%dbpc", w, BITDEPTH))
for (int h = 2; h <= (w == 2 ? 64 : 128); h <<= 1) {
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int i = 0; i < w * h; i++)
c_dst[i] = a_dst[i] = rnd() & bitdepth_max;
for (int i = 0; i < 32 * 128; i++)
tmp[i] = rnd() & bitdepth_max;
call_ref(c_dst, dst_stride, tmp, w, h);
call_new(a_dst, dst_stride, tmp, w, h);
checkasm_check_pixel(c_dst, dst_stride, a_dst, dst_stride,
w, h, "dst");
bench_new(a_dst, dst_stride, tmp, w, h);
}
}
report("blend_v");
}
static void check_blend_h(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, tmp, 128 * 32,);
ALIGN_STK_64(pixel, c_dst, 128 * 32,);
ALIGN_STK_64(pixel, a_dst, 128 * 32,);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *tmp,
int w, int h);
for (int w = 2; w <= 128; w <<= 1) {
const ptrdiff_t dst_stride = w * sizeof(pixel);
if (check_func(c->blend_h, "blend_h_w%d_%dbpc", w, BITDEPTH))
for (int h = (w == 128 ? 4 : 2); h <= 32; h <<= 1) {
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int i = 0; i < w * h; i++)
c_dst[i] = a_dst[i] = rnd() & bitdepth_max;
for (int i = 0; i < 128 * 32; i++)
tmp[i] = rnd() & bitdepth_max;
call_ref(c_dst, dst_stride, tmp, w, h);
call_new(a_dst, dst_stride, tmp, w, h);
checkasm_check_pixel(c_dst, dst_stride, a_dst, dst_stride,
w, h, "dst");
bench_new(a_dst, dst_stride, tmp, w, h);
}
}
report("blend_h");
}
static void check_warp8x8(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, src_buf, 15 * 15,);
ALIGN_STK_64(pixel, c_dst, 8 * 8,);
ALIGN_STK_64(pixel, a_dst, 8 * 8,);
int16_t abcd[4];
const pixel *src = src_buf + 15 * 3 + 3;
const ptrdiff_t dst_stride = 8 * sizeof(pixel);
const ptrdiff_t src_stride = 15 * sizeof(pixel);
declare_func(void, pixel *dst, ptrdiff_t dst_stride, const pixel *src,
ptrdiff_t src_stride, const int16_t *abcd, int mx, int my
HIGHBD_DECL_SUFFIX);
if (check_func(c->warp8x8, "warp_8x8_%dbpc", BITDEPTH)) {
const int mx = (rnd() & 0x1fff) - 0xa00;
const int my = (rnd() & 0x1fff) - 0xa00;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int i = 0; i < 4; i++)
abcd[i] = (rnd() & 0x1fff) - 0xa00;
for (int i = 0; i < 15 * 15; i++)
src_buf[i] = rnd() & bitdepth_max;
call_ref(c_dst, dst_stride, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
call_new(a_dst, dst_stride, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, dst_stride, a_dst, dst_stride,
8, 8, "dst");
bench_new(a_dst, dst_stride, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
}
report("warp8x8");
}
static void check_warp8x8t(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, src_buf, 15 * 15,);
ALIGN_STK_64(int16_t, c_tmp, 8 * 8,);
ALIGN_STK_64(int16_t, a_tmp, 8 * 8,);
int16_t abcd[4];
const pixel *src = src_buf + 15 * 3 + 3;
const ptrdiff_t src_stride = 15 * sizeof(pixel);
declare_func(void, int16_t *tmp, ptrdiff_t tmp_stride, const pixel *src,
ptrdiff_t src_stride, const int16_t *abcd, int mx, int my
HIGHBD_DECL_SUFFIX);
if (check_func(c->warp8x8t, "warp_8x8t_%dbpc", BITDEPTH)) {
const int mx = (rnd() & 0x1fff) - 0xa00;
const int my = (rnd() & 0x1fff) - 0xa00;
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int i = 0; i < 4; i++)
abcd[i] = (rnd() & 0x1fff) - 0xa00;
for (int i = 0; i < 15 * 15; i++)
src_buf[i] = rnd() & bitdepth_max;
call_ref(c_tmp, 8, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
call_new(a_tmp, 8, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
checkasm_check(int16_t, c_tmp, 8 * sizeof(*c_tmp),
a_tmp, 8 * sizeof(*a_tmp),
8, 8, "tmp");
bench_new(a_tmp, 8, src, src_stride, abcd, mx, my HIGHBD_TAIL_SUFFIX);
}
report("warp8x8t");
}
enum EdgeFlags {
HAVE_TOP = 1,
HAVE_BOTTOM = 2,
HAVE_LEFT = 4,
HAVE_RIGHT = 8,
};
static void random_offset_for_edge(int *const x, int *const y,
const int bw, const int bh,
int *const iw, int *const ih,
const enum EdgeFlags edge)
{
#define set_off(edge1, edge2, pos, dim) \
*i##dim = edge & (HAVE_##edge1 | HAVE_##edge2) ? 160 : 1 + (rnd() % (b##dim - 2)); \
switch (edge & (HAVE_##edge1 | HAVE_##edge2)) { \
case HAVE_##edge1 | HAVE_##edge2: \
assert(b##dim <= *i##dim); \
*pos = rnd() % (*i##dim - b##dim + 1); \
break; \
case HAVE_##edge1: \
*pos = (*i##dim - b##dim) + 1 + (rnd() % (b##dim - 1)); \
break; \
case HAVE_##edge2: \
*pos = -(1 + (rnd() % (b##dim - 1))); \
break; \
case 0: \
assert(b##dim - 1 > *i##dim); \
*pos = -(1 + (rnd() % (b##dim - *i##dim - 1))); \
break; \
}
set_off(LEFT, RIGHT, x, w);
set_off(TOP, BOTTOM, y, h);
}
static void check_emuedge(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, c_dst, 135 * 192,);
ALIGN_STK_64(pixel, a_dst, 135 * 192,);
ALIGN_STK_64(pixel, src, 160 * 160,);
for (int i = 0; i < 160 * 160; i++)
src[i] = rnd() & ((1U << BITDEPTH) - 1);
declare_func(void, intptr_t bw, intptr_t bh, intptr_t iw, intptr_t ih,
intptr_t x, intptr_t y,
pixel *dst, ptrdiff_t dst_stride,
const pixel *src, ptrdiff_t src_stride);
int x, y, iw, ih;
for (int w = 4; w <= 128; w <<= 1)
if (check_func(c->emu_edge, "emu_edge_w%d_%dbpc", w, BITDEPTH)) {
for (int h = imax(w / 4, 4); h <= imin(w * 4, 128); h <<= 1) {
// we skip 0xf, since it implies that we don't need emu_edge
for (enum EdgeFlags edge = 0; edge < 0xf; edge++) {
const int bw = w + (rnd() & 7);
const int bh = h + (rnd() & 7);
random_offset_for_edge(&x, &y, bw, bh, &iw, &ih, edge);
call_ref(bw, bh, iw, ih, x, y,
c_dst, 192 * sizeof(pixel), src, 160 * sizeof(pixel));
call_new(bw, bh, iw, ih, x, y,
a_dst, 192 * sizeof(pixel), src, 160 * sizeof(pixel));
checkasm_check_pixel(c_dst, 192 * sizeof(pixel),
a_dst, 192 * sizeof(pixel),
bw, bh, "dst");
}
}
for (enum EdgeFlags edge = 1; edge < 0xf; edge <<= 1) {
random_offset_for_edge(&x, &y, w + 7, w + 7, &iw, &ih, edge);
bench_new(w + 7, w + 7, iw, ih, x, y,
a_dst, 192 * sizeof(pixel), src, 160 * sizeof(pixel));
}
}
report("emu_edge");
}
static int get_upscale_x0(const int in_w, const int out_w, const int step) {
const int err = out_w * step - (in_w << 14);
const int x0 = (-((out_w - in_w) << 13) + (out_w >> 1)) / out_w + 128 - (err >> 1);
return x0 & 0x3fff;
}
static void check_resize(Dav1dMCDSPContext *const c) {
ALIGN_STK_64(pixel, c_dst, 1024 * 64,);
ALIGN_STK_64(pixel, a_dst, 1024 * 64,);
ALIGN_STK_64(pixel, src, 512 * 64,);
const int height = 64;
const int max_src_width = 512;
const ptrdiff_t dst_stride = 1024 * sizeof(pixel);
const ptrdiff_t src_stride = 512 * sizeof(pixel);
declare_func(void, pixel *dst, ptrdiff_t dst_stride,
const pixel *src, ptrdiff_t src_stride,
int dst_w, int src_w, int h, int dx, int mx0
HIGHBD_DECL_SUFFIX);
if (check_func(c->resize, "resize_%dbpc", BITDEPTH)) {
#if BITDEPTH == 16
const int bitdepth_max = rnd() & 1 ? 0x3ff : 0xfff;
#else
const int bitdepth_max = 0xff;
#endif
for (int i = 0; i < max_src_width * height; i++)
src[i] = rnd() & bitdepth_max;
const int w_den = 9 + (rnd() & 7);
const int src_w = 16 + (rnd() % (max_src_width - 16 + 1));
const int dst_w = w_den * src_w >> 3;
#define scale_fac(ref_sz, this_sz) \
((((ref_sz) << 14) + ((this_sz) >> 1)) / (this_sz))
const int dx = scale_fac(src_w, dst_w);
#undef scale_fac
const int mx0 = get_upscale_x0(src_w, dst_w, dx);
call_ref(c_dst, dst_stride, src, src_stride,
dst_w, height, src_w, dx, mx0 HIGHBD_TAIL_SUFFIX);
call_new(a_dst, dst_stride, src, src_stride,
dst_w, height, src_w, dx, mx0 HIGHBD_TAIL_SUFFIX);
checkasm_check_pixel(c_dst, dst_stride, a_dst, dst_stride,
dst_w, height, "dst");
bench_new(a_dst, dst_stride, src, src_stride,
512, height, 512 * 8 / w_den, dx, mx0 HIGHBD_TAIL_SUFFIX);
}
report("resize");
}
void bitfn(checkasm_check_mc)(void) {
Dav1dMCDSPContext c;
bitfn(dav1d_mc_dsp_init)(&c);
check_mc(&c);
check_mct(&c);
check_mc_scaled(&c);
check_mct_scaled(&c);
check_avg(&c);
check_w_avg(&c);
check_mask(&c);
check_w_mask(&c);
check_blend(&c);
check_blend_v(&c);
check_blend_h(&c);
check_warp8x8(&c);
check_warp8x8t(&c);
check_emuedge(&c);
check_resize(&c);
}

View File

@ -1,291 +0,0 @@
/*
* Copyright © 2019, VideoLAN and dav1d authors
* Copyright © 2019, 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 "tests/checkasm/checkasm.h"
#include "src/cpu.h"
#include "src/msac.h"
#include <stdio.h>
#include <string.h>
#define BUF_SIZE 8192
/* The normal code doesn't use function pointers */
typedef unsigned (*decode_symbol_adapt_fn)(MsacContext *s, uint16_t *cdf,
size_t n_symbols);
typedef unsigned (*decode_adapt_fn)(MsacContext *s, uint16_t *cdf);
typedef unsigned (*decode_bool_equi_fn)(MsacContext *s);
typedef unsigned (*decode_bool_fn)(MsacContext *s, unsigned f);
typedef struct {
decode_symbol_adapt_fn symbol_adapt4;
decode_symbol_adapt_fn symbol_adapt8;
decode_symbol_adapt_fn symbol_adapt16;
decode_adapt_fn bool_adapt;
decode_bool_equi_fn bool_equi;
decode_bool_fn bool;
decode_adapt_fn hi_tok;
} MsacDSPContext;
static void randomize_cdf(uint16_t *const cdf, const int n) {
int i;
for (i = 15; i > n; i--)
cdf[i] = rnd(); // padding
cdf[i] = 0; // count
do {
cdf[i - 1] = cdf[i] + rnd() % (32768 - cdf[i] - i) + 1;
} while (--i > 0);
}
/* memcmp() on structs can have weird behavior due to padding etc. */
static int msac_cmp(const MsacContext *const a, const MsacContext *const b) {
return a->buf_pos != b->buf_pos || a->buf_end != b->buf_end ||
a->dif != b->dif || a->rng != b->rng || a->cnt != b->cnt ||
a->allow_update_cdf != b->allow_update_cdf;
}
static void msac_dump(unsigned c_res, unsigned a_res,
const MsacContext *const a, const MsacContext *const b,
const uint16_t *const cdf_a, const uint16_t *const cdf_b,
const int num_cdf)
{
if (c_res != a_res)
fprintf(stderr, "c_res %u a_res %u\n", c_res, a_res);
if (a->buf_pos != b->buf_pos)
fprintf(stderr, "buf_pos %p vs %p\n", a->buf_pos, b->buf_pos);
if (a->buf_end != b->buf_end)
fprintf(stderr, "buf_end %p vs %p\n", a->buf_end, b->buf_end);
if (a->dif != b->dif)
fprintf(stderr, "dif %zx vs %zx\n", a->dif, b->dif);
if (a->rng != b->rng)
fprintf(stderr, "rng %u vs %u\n", a->rng, b->rng);
if (a->cnt != b->cnt)
fprintf(stderr, "cnt %d vs %d\n", a->cnt, b->cnt);
if (a->allow_update_cdf)
fprintf(stderr, "allow_update_cdf %d vs %d\n",
a->allow_update_cdf, b->allow_update_cdf);
if (num_cdf && memcmp(cdf_a, cdf_b, sizeof(*cdf_a) * (num_cdf + 1))) {
fprintf(stderr, "cdf:\n");
for (int i = 0; i <= num_cdf; i++)
fprintf(stderr, " %5u", cdf_a[i]);
fprintf(stderr, "\n");
for (int i = 0; i <= num_cdf; i++)
fprintf(stderr, " %5u", cdf_b[i]);
fprintf(stderr, "\n");
for (int i = 0; i <= num_cdf; i++)
fprintf(stderr, " %c", cdf_a[i] != cdf_b[i] ? 'x' : '.');
fprintf(stderr, "\n");
}
}
#define CHECK_SYMBOL_ADAPT(n, n_min, n_max) do { \
if (check_func(c->symbol_adapt##n, "msac_decode_symbol_adapt%d", n)) { \
for (int cdf_update = 0; cdf_update <= 1; cdf_update++) { \
for (int ns = n_min; ns <= n_max; ns++) { \
dav1d_msac_init(&s_c, buf, BUF_SIZE, !cdf_update); \
s_a = s_c; \
randomize_cdf(cdf[0], ns); \
memcpy(cdf[1], cdf[0], sizeof(*cdf)); \
for (int i = 0; i < 64; i++) { \
unsigned c_res = call_ref(&s_c, cdf[0], ns); \
unsigned a_res = call_new(&s_a, cdf[1], ns); \
if (c_res != a_res || msac_cmp(&s_c, &s_a) || \
memcmp(cdf[0], cdf[1], sizeof(**cdf) * (ns + 1))) \
{ \
if (fail()) \
msac_dump(c_res, a_res, &s_c, &s_a, \
cdf[0], cdf[1], ns); \
} \
} \
if (cdf_update && ns == n - 1) \
bench_new(&s_a, cdf[1], ns); \
} \
} \
} \
} while (0)
static void check_decode_symbol(MsacDSPContext *const c, uint8_t *const buf) {
ALIGN_STK_32(uint16_t, cdf, 2, [16]);
MsacContext s_c, s_a;
declare_func(unsigned, MsacContext *s, uint16_t *cdf, size_t n_symbols);
CHECK_SYMBOL_ADAPT( 4, 1, 4);
CHECK_SYMBOL_ADAPT( 8, 1, 7);
CHECK_SYMBOL_ADAPT(16, 3, 15);
report("decode_symbol");
}
static void check_decode_bool_adapt(MsacDSPContext *const c, uint8_t *const buf) {
MsacContext s_c, s_a;
declare_func(unsigned, MsacContext *s, uint16_t *cdf);
if (check_func(c->bool_adapt, "msac_decode_bool_adapt")) {
uint16_t cdf[2][2];
for (int cdf_update = 0; cdf_update <= 1; cdf_update++) {
dav1d_msac_init(&s_c, buf, BUF_SIZE, !cdf_update);
s_a = s_c;
cdf[0][0] = cdf[1][0] = rnd() % 32767 + 1;
cdf[0][1] = cdf[1][1] = 0;
for (int i = 0; i < 64; i++) {
unsigned c_res = call_ref(&s_c, cdf[0]);
unsigned a_res = call_new(&s_a, cdf[1]);
if (c_res != a_res || msac_cmp(&s_c, &s_a) ||
memcmp(cdf[0], cdf[1], sizeof(*cdf)))
{
if (fail())
msac_dump(c_res, a_res, &s_c, &s_a, cdf[0], cdf[1], 1);
}
}
if (cdf_update)
bench_new(&s_a, cdf[1]);
}
}
}
static void check_decode_bool_equi(MsacDSPContext *const c, uint8_t *const buf) {
MsacContext s_c, s_a;
declare_func(unsigned, MsacContext *s);
if (check_func(c->bool_equi, "msac_decode_bool_equi")) {
dav1d_msac_init(&s_c, buf, BUF_SIZE, 1);
s_a = s_c;
for (int i = 0; i < 64; i++) {
unsigned c_res = call_ref(&s_c);
unsigned a_res = call_new(&s_a);
if (c_res != a_res || msac_cmp(&s_c, &s_a)) {
if (fail())
msac_dump(c_res, a_res, &s_c, &s_a, NULL, NULL, 0);
}
}
bench_new(&s_a);
}
}
static void check_decode_bool(MsacDSPContext *const c, uint8_t *const buf) {
MsacContext s_c, s_a;
declare_func(unsigned, MsacContext *s, unsigned f);
if (check_func(c->bool, "msac_decode_bool")) {
dav1d_msac_init(&s_c, buf, BUF_SIZE, 1);
s_a = s_c;
for (int i = 0; i < 64; i++) {
const unsigned f = rnd() & 0x7fff;
unsigned c_res = call_ref(&s_c, f);
unsigned a_res = call_new(&s_a, f);
if (c_res != a_res || msac_cmp(&s_c, &s_a)) {
if (fail())
msac_dump(c_res, a_res, &s_c, &s_a, NULL, NULL, 0);
}
}
bench_new(&s_a, 16384);
}
}
static void check_decode_bool_funcs(MsacDSPContext *const c, uint8_t *const buf) {
check_decode_bool_adapt(c, buf);
check_decode_bool_equi(c, buf);
check_decode_bool(c, buf);
report("decode_bool");
}
static void check_decode_hi_tok(MsacDSPContext *const c, uint8_t *const buf) {
ALIGN_STK_16(uint16_t, cdf, 2, [16]);
MsacContext s_c, s_a;
declare_func(unsigned, MsacContext *s, uint16_t *cdf);
if (check_func(c->hi_tok, "msac_decode_hi_tok")) {
for (int cdf_update = 0; cdf_update <= 1; cdf_update++) {
dav1d_msac_init(&s_c, buf, BUF_SIZE, !cdf_update);
s_a = s_c;
randomize_cdf(cdf[0], 3);
memcpy(cdf[1], cdf[0], sizeof(*cdf));
for (int i = 0; i < 64; i++) {
unsigned c_res = call_ref(&s_c, cdf[0]);
unsigned a_res = call_new(&s_a, cdf[1]);
if (c_res != a_res || msac_cmp(&s_c, &s_a) ||
memcmp(cdf[0], cdf[1], sizeof(*cdf)))
{
if (fail())
msac_dump(c_res, a_res, &s_c, &s_a, cdf[0], cdf[1], 3);
break;
}
}
if (cdf_update)
bench_new(&s_a, cdf[1]);
}
}
report("decode_hi_tok");
}
void checkasm_check_msac(void) {
MsacDSPContext c;
c.symbol_adapt4 = dav1d_msac_decode_symbol_adapt_c;
c.symbol_adapt8 = dav1d_msac_decode_symbol_adapt_c;
c.symbol_adapt16 = dav1d_msac_decode_symbol_adapt_c;
c.bool_adapt = dav1d_msac_decode_bool_adapt_c;
c.bool_equi = dav1d_msac_decode_bool_equi_c;
c.bool = dav1d_msac_decode_bool_c;
c.hi_tok = dav1d_msac_decode_hi_tok_c;
#if (ARCH_AARCH64 || ARCH_ARM) && HAVE_ASM
if (dav1d_get_cpu_flags() & DAV1D_ARM_CPU_FLAG_NEON) {
c.symbol_adapt4 = dav1d_msac_decode_symbol_adapt4_neon;
c.symbol_adapt8 = dav1d_msac_decode_symbol_adapt8_neon;
c.symbol_adapt16 = dav1d_msac_decode_symbol_adapt16_neon;
c.bool_adapt = dav1d_msac_decode_bool_adapt_neon;
c.bool_equi = dav1d_msac_decode_bool_equi_neon;
c.bool = dav1d_msac_decode_bool_neon;
c.hi_tok = dav1d_msac_decode_hi_tok_neon;
}
#elif ARCH_X86 && HAVE_ASM
if (dav1d_get_cpu_flags() & DAV1D_X86_CPU_FLAG_SSE2) {
c.symbol_adapt4 = dav1d_msac_decode_symbol_adapt4_sse2;
c.symbol_adapt8 = dav1d_msac_decode_symbol_adapt8_sse2;
c.symbol_adapt16 = dav1d_msac_decode_symbol_adapt16_sse2;
c.bool_adapt = dav1d_msac_decode_bool_adapt_sse2;
c.bool_equi = dav1d_msac_decode_bool_equi_sse2;
c.bool = dav1d_msac_decode_bool_sse2;
c.hi_tok = dav1d_msac_decode_hi_tok_sse2;
}
#if ARCH_X86_64
if (dav1d_get_cpu_flags() & DAV1D_X86_CPU_FLAG_AVX2) {
c.symbol_adapt16 = dav1d_msac_decode_symbol_adapt16_avx2;
}
#endif
#endif
uint8_t buf[BUF_SIZE];
for (int i = 0; i < BUF_SIZE; i++)
buf[i] = rnd();
check_decode_symbol(&c, buf);
check_decode_bool_funcs(&c, buf);
check_decode_hi_tok(&c, buf);
}

View File

@ -1,287 +0,0 @@
; 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 "config.asm"
%undef private_prefix
%define private_prefix checkasm
%include "ext/x86/x86inc.asm"
SECTION_RODATA 16
%if ARCH_X86_64
; just random numbers to reduce the chance of incidental match
%if WIN64
x6: dq 0x1a1b2550a612b48c,0x79445c159ce79064
x7: dq 0x2eed899d5a28ddcd,0x86b2536fcd8cf636
x8: dq 0xb0856806085e7943,0x3f2bf84fc0fcca4e
x9: dq 0xacbd382dcf5b8de2,0xd229e1f5b281303f
x10: dq 0x71aeaff20b095fd9,0xab63e2e11fa38ed9
x11: dq 0x89b0c0765892729a,0x77d410d5c42c882d
x12: dq 0xc45ea11a955d8dd5,0x24b3c1d2a024048b
x13: dq 0x2e8ec680de14b47c,0xdd7b8919edd42786
x14: dq 0x135ce6888fa02cbf,0x11e53e2b2ac655ef
x15: dq 0x011ff554472a7a10,0x6de8f4c914c334d5
n7: dq 0x21f86d66c8ca00ce
n8: dq 0x75b6ba21077c48ad
%endif
n9: dq 0xed56bb2dcb3c7736
n10: dq 0x8bda43d3fd1a7e06
n11: dq 0xb64a9c9e5d318408
n12: dq 0xdf9a54b303f1d3a3
n13: dq 0x4a75479abd64e097
n14: dq 0x249214109d5d1c88
%endif
errmsg_reg: db "failed to preserve register", 0
errmsg_stack: db "stack corruption", 0
SECTION .text
cextern fail_func
; max number of args used by any asm function.
; (max_args % 4) must equal 3 for stack alignment
%define max_args 15
%if ARCH_X86_64
;-----------------------------------------------------------------------------
; int checkasm_stack_clobber(uint64_t clobber, ...)
;-----------------------------------------------------------------------------
cglobal stack_clobber, 1, 2
; Clobber the stack with junk below the stack pointer
%define argsize (max_args+6)*8
SUB rsp, argsize
mov r1, argsize-8
.loop:
mov [rsp+r1], r0
sub r1, 8
jge .loop
ADD rsp, argsize
RET
%if WIN64
%assign free_regs 7
%define stack_param rsp+32 ; shadow space
%define num_stack_params rsp+stack_offset+22*8
DECLARE_REG_TMP 4
%else
%assign free_regs 9
%define stack_param rsp
%define num_stack_params rsp+stack_offset+16*8
DECLARE_REG_TMP 7
%endif
;-----------------------------------------------------------------------------
; void checkasm_checked_call(void *func, ...)
;-----------------------------------------------------------------------------
INIT_XMM
cglobal checked_call, 2, 15, 16, max_args*8+64+8
mov t0, r0
; All arguments have been pushed on the stack instead of registers in
; order to test for incorrect assumptions that 32-bit ints are
; zero-extended to 64-bit.
mov r0, r6mp
mov r1, r7mp
mov r2, r8mp
mov r3, r9mp
%if UNIX64
mov r4, r10mp
mov r5, r11mp
%else ; WIN64
; Move possible floating-point arguments to the correct registers
movq m0, r0
movq m1, r1
movq m2, r2
movq m3, r3
%assign i 6
%rep 16-6
mova m %+ i, [x %+ i]
%assign i i+1
%endrep
%endif
; write stack canaries to the area above parameters passed on the stack
mov r9d, [num_stack_params]
mov r8, [rsp+stack_offset] ; return address
not r8
%assign i 0
%rep 8 ; 64 bytes
mov [stack_param+(r9+i)*8], r8
%assign i i+1
%endrep
dec r9d
jl .stack_setup_done ; no stack parameters
.copy_stack_parameter:
mov r8, [stack_param+stack_offset+7*8+r9*8]
mov [stack_param+r9*8], r8
dec r9d
jge .copy_stack_parameter
.stack_setup_done:
%assign i 14
%rep 15-free_regs
mov r %+ i, [n %+ i]
%assign i i-1
%endrep
call t0
; check for failure to preserve registers
xor r14, [n14]
lea r0, [errmsg_reg]
%assign i 13
%rep 14-free_regs
xor r %+ i, [n %+ i]
or r14, r %+ i
%assign i i-1
%endrep
%if WIN64
pxor m6, [x6]
%assign i 7
%rep 16-7
pxor m %+ i, [x %+ i]
por m6, m %+ i
%assign i i+1
%endrep
packsswb m6, m6
movq r5, m6
or r14, r5
%endif
jnz .fail
; check for stack corruption
mov r9d, [num_stack_params]
mov r8, [rsp+stack_offset]
mov r4, [stack_param+r9*8]
not r8
xor r4, r8
%assign i 1
%rep 6
mov r5, [stack_param+(r9+i)*8]
xor r5, r8
or r4, r5
%assign i i+1
%endrep
xor r8, [stack_param+(r9+7)*8]
or r4, r8
jz .ok
add r0, errmsg_stack-errmsg_reg
.fail:
; Call fail_func() with a descriptive message to mark it as a failure.
; Save the return value located in rdx:rax first to prevent clobbering.
mov r9, rax
mov r10, rdx
xor eax, eax
call fail_func
mov rdx, r10
mov rax, r9
.ok:
RET
; trigger a warmup of vector units
%macro WARMUP 0
cglobal warmup, 0, 0
xorps m0, m0
mulps m0, m0
RET
%endmacro
INIT_YMM avx2
WARMUP
INIT_ZMM avx512
WARMUP
%else
; just random numbers to reduce the chance of incidental match
%assign n3 0x6549315c
%assign n4 0xe02f3e23
%assign n5 0xb78d0d1d
%assign n6 0x33627ba7
;-----------------------------------------------------------------------------
; void checkasm_checked_call(void *func, ...)
;-----------------------------------------------------------------------------
cglobal checked_call, 1, 7
mov r3, [esp+stack_offset] ; return address
mov r1, [esp+stack_offset+17*4] ; num_stack_params
mov r2, 27
not r3
sub r2, r1
.push_canary:
push r3
dec r2
jg .push_canary
.push_parameter:
push dword [esp+32*4]
dec r1
jg .push_parameter
mov r3, n3
mov r4, n4
mov r5, n5
mov r6, n6
call r0
; check for failure to preserve registers
xor r3, n3
xor r4, n4
xor r5, n5
xor r6, n6
or r3, r4
or r5, r6
LEA r1, errmsg_reg
or r3, r5
jnz .fail
; check for stack corruption
mov r3, [esp+48*4] ; num_stack_params
mov r6, [esp+31*4] ; return address
mov r4, [esp+r3*4]
sub r3, 26
not r6
xor r4, r6
.check_canary:
mov r5, [esp+(r3+27)*4]
xor r5, r6
or r4, r5
inc r3
jl .check_canary
test r4, r4
jz .ok
add r1, errmsg_stack-errmsg_reg
.fail:
mov r3, eax
mov r4, edx
mov [esp], r1
call fail_func
mov edx, r4
mov eax, r3
.ok:
add esp, 27*4
RET
%endif ; ARCH_X86_64

View File

@ -1,33 +0,0 @@
/*
* 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()
{
return 0;
}

View File

@ -1,102 +0,0 @@
/*
* 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 <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#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);
}

View File

@ -1,35 +0,0 @@
/*
* 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/common.h>
DAV1D_API void dav1d_setup_alloc_fail(unsigned seed, unsigned probability);
#endif /* DAV1D_TESTS_LIBFUZZER_ALLOC_FAIL_H */

View File

@ -1,199 +0,0 @@
/*
* 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 <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <dav1d/dav1d.h>
#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 n_frame_threads = (h & 0xf) + 1;
int n_tile_threads = ((h >> 4) & 0x7) + 1;
if (n_frame_threads > 5) n_frame_threads = 1;
if (n_tile_threads > 3) n_tile_threads = 1;
#endif
ptr += 32; // skip ivf header
dav1d_default_settings(&settings);
#ifdef DAV1D_MT_FUZZING
settings.n_frame_threads = settings.n_tile_threads = 2;
#elif defined(DAV1D_ALLOC_FAIL)
settings.n_frame_threads = n_frame_threads;
settings.n_tile_threads = n_tile_threads;
dav1d_setup_alloc_fail(seed, probability);
#else
settings.n_frame_threads = settings.n_tile_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 = { 0 };
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;
}

View File

@ -1,37 +0,0 @@
/*
* 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 <stddef.h>
#include <stdint.h>
int LLVMFuzzerInitialize(int *argc, char ***argv);
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
#endif /* DAV1D_TESTS_LIBFUZZER_DAV1D_FUZZER_H */

View File

@ -1,100 +0,0 @@
/*
* 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 <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#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;
}

View File

@ -1,102 +0,0 @@
# 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,
c_args: [stackalign_flag, stackrealign_flag],
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: [stackalign_flag, stackrealign_flag, '-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 <stdlib.h>', 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: [stackalign_flag, stackrealign_flag, '-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

View File

@ -1,151 +0,0 @@
# 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_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,
c_args: ['-DBITDEPTH=@0@'.format(bitdepth), stackalign_flag],
install: false,
build_by_default: false,
)
checkasm_bitdepth_objs += checkasm_bitdepth_lib.extract_all_objects()
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().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,
c_args: [stackalign_flag, stackrealign_flag],
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')
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

View File

@ -1,243 +0,0 @@
/*
* 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 <windows.h>
static unsigned get_seed(void) {
return GetTickCount();
}
#else
#ifdef __APPLE__
#include <mach/mach_time.h>
#else
#include <time.h>
#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;
}

View File

@ -1,562 +0,0 @@
/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
/*
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Sponsored in part by the Defense Advanced Research Projects
* Agency (DARPA) and Air Force Research Laboratory, Air Force
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
*/
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Dieter Baron and Thomas Klausner.
*
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <errno.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <windows.h>
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
#ifdef REPLACE_GETOPT
int opterr = 1; /* if error message should be printed */
int optind = 1; /* index into parent argv vector */
int optopt = '?'; /* character checked for validity */
#undef optreset /* see getopt.h */
#define optreset __mingw_optreset
int optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#endif
#define PRINT_ERROR ((opterr) && (*options != ':'))
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
/* return values */
#define BADCH (int)'?'
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
#define INORDER (int)1
#ifndef __CYGWIN__
#define __progname __argv[0]
#else
extern char __declspec(dllimport) *__progname;
#endif
#ifdef __CYGWIN__
static char EMSG[] = "";
#else
#define EMSG ""
#endif
static int getopt_internal(int, char * const *, const char *,
const struct option *, int *, int);
static int parse_long_options(char * const *, const char *,
const struct option *, int *, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);
static char *place = EMSG; /* option letter processing */
/* XXX: set optreset to 1 rather than these two */
static int nonopt_start = -1; /* first non option argument (for permute) */
static int nonopt_end = -1; /* first option after non options (for permute) */
/* Error messages */
static const char recargchar[] = "option requires an argument -- %c";
static const char recargstring[] = "option requires an argument -- %s";
static const char ambig[] = "ambiguous option -- %.*s";
static const char noarg[] = "option doesn't take an argument -- %.*s";
static const char illoptchar[] = "unknown option -- %c";
static const char illoptstring[] = "unknown option -- %s";
static void
_vwarnx(const char *fmt,va_list ap)
{
(void)fprintf(stderr,"%s: ",__progname);
if (fmt != NULL)
(void)vfprintf(stderr,fmt,ap);
(void)fprintf(stderr,"\n");
}
static void
warnx(const char *fmt,...)
{
va_list ap;
va_start(ap,fmt);
_vwarnx(fmt,ap);
va_end(ap);
}
/*
* Compute the greatest common divisor of a and b.
*/
static int
gcd(int a, int b)
{
int c;
c = a % b;
while (c != 0) {
a = b;
b = c;
c = a % b;
}
return (b);
}
/*
* Exchange the block from nonopt_start to nonopt_end with the block
* from nonopt_end to opt_end (keeping the same order of arguments
* in each block).
*/
static void
permute_args(int panonopt_start, int panonopt_end, int opt_end,
char * const *nargv)
{
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
char *swap;
/*
* compute lengths of blocks and number and size of cycles
*/
nnonopts = panonopt_end - panonopt_start;
nopts = opt_end - panonopt_end;
ncycle = gcd(nnonopts, nopts);
cyclelen = (opt_end - panonopt_start) / ncycle;
for (i = 0; i < ncycle; i++) {
cstart = panonopt_end+i;
pos = cstart;
for (j = 0; j < cyclelen; j++) {
if (pos >= panonopt_end)
pos -= nnonopts;
else
pos += nopts;
swap = nargv[pos];
/* LINTED const cast */
((char **) nargv)[pos] = nargv[cstart];
/* LINTED const cast */
((char **)nargv)[cstart] = swap;
}
}
}
/*
* parse_long_options --
* Parse long options in argc/argv argument vector.
* Returns -1 if short_too is set and the option does not match long_options.
*/
static int
parse_long_options(char * const *nargv, const char *options,
const struct option *long_options, int *idx, int short_too)
{
char *current_argv, *has_equal;
size_t current_argv_len;
int i, ambiguous, match;
#define IDENTICAL_INTERPRETATION(_x, _y) \
(long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
long_options[(_x)].flag == long_options[(_y)].flag && \
long_options[(_x)].val == long_options[(_y)].val)
current_argv = place;
match = -1;
ambiguous = 0;
optind++;
if ((has_equal = strchr(current_argv, '=')) != NULL) {
/* argument found (--option=arg) */
current_argv_len = has_equal - current_argv;
has_equal++;
} else
current_argv_len = strlen(current_argv);
for (i = 0; long_options[i].name; i++) {
/* find matching long option */
if (strncmp(current_argv, long_options[i].name,
current_argv_len))
continue;
if (strlen(long_options[i].name) == current_argv_len) {
/* exact match */
match = i;
ambiguous = 0;
break;
}
/*
* If this is a known short option, don't allow
* a partial match of a single character.
*/
if (short_too && current_argv_len == 1)
continue;
if (match == -1) /* partial match */
match = i;
else if (!IDENTICAL_INTERPRETATION(i, match))
ambiguous = 1;
}
if (ambiguous) {
/* ambiguous abbreviation */
if (PRINT_ERROR)
warnx(ambig, (int)current_argv_len,
current_argv);
optopt = 0;
return (BADCH);
}
if (match != -1) { /* option found */
if (long_options[match].has_arg == no_argument
&& has_equal) {
if (PRINT_ERROR)
warnx(noarg, (int)current_argv_len,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
return (BADARG);
}
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument) {
if (has_equal)
optarg = has_equal;
else if (long_options[match].has_arg ==
required_argument) {
/*
* optional argument doesn't use next nargv
*/
optarg = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument)
&& (optarg == NULL)) {
/*
* Missing argument; leading ':' indicates no error
* should be generated.
*/
if (PRINT_ERROR)
warnx(recargstring,
current_argv);
/*
* XXX: GNU sets optopt to val regardless of flag
*/
if (long_options[match].flag == NULL)
optopt = long_options[match].val;
else
optopt = 0;
--optind;
return (BADARG);
}
} else { /* unknown option */
if (short_too) {
--optind;
return (-1);
}
if (PRINT_ERROR)
warnx(illoptstring, current_argv);
optopt = 0;
return (BADCH);
}
if (idx)
*idx = match;
if (long_options[match].flag) {
*long_options[match].flag = long_options[match].val;
return (0);
} else
return (long_options[match].val);
#undef IDENTICAL_INTERPRETATION
}
/*
* getopt_internal --
* Parse argc/argv argument vector. Called by user level routines.
*/
static int
getopt_internal(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx, int flags)
{
char *oli; /* option letter list index */
int optchar, short_too;
static int posixly_correct = -1;
if (options == NULL)
return (-1);
/*
* XXX Some GNU programs (like cvs) set optind to 0 instead of
* XXX using optreset. Work around this braindamage.
*/
if (optind == 0)
optind = optreset = 1;
/*
* Disable GNU extensions if POSIXLY_CORRECT is set or options
* string begins with a '+'.
*
* CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or
* optreset != 0 for GNU compatibility.
*/
if (posixly_correct == -1 || optreset != 0)
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
if (*options == '-')
flags |= FLAG_ALLARGS;
else if (posixly_correct || *options == '+')
flags &= ~FLAG_PERMUTE;
if (*options == '+' || *options == '-')
options++;
optarg = NULL;
if (optreset)
nonopt_start = nonopt_end = -1;
start:
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc) { /* end of argument vector */
place = EMSG;
if (nonopt_end != -1) {
/* do permutation, if we have to */
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
else if (nonopt_start != -1) {
/*
* If we skipped non-options, set optind
* to the first of them.
*/
optind = nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
if (*(place = nargv[optind]) != '-' ||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
place = EMSG; /* found non-option */
if (flags & FLAG_ALLARGS) {
/*
* GNU extension:
* return non-option as argument to option 1
*/
optarg = nargv[optind++];
return (INORDER);
}
if (!(flags & FLAG_PERMUTE)) {
/*
* If no permutation wanted, stop parsing
* at first non-option.
*/
return (-1);
}
/* do permutation */
if (nonopt_start == -1)
nonopt_start = optind;
else if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
nonopt_start = optind -
(nonopt_end - nonopt_start);
nonopt_end = -1;
}
optind++;
/* process next argument */
goto start;
}
if (nonopt_start != -1 && nonopt_end == -1)
nonopt_end = optind;
/*
* If we have "-" do nothing, if "--" we are done.
*/
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
optind++;
place = EMSG;
/*
* We found an option (--), so if we skipped
* non-options, we have to permute.
*/
if (nonopt_end != -1) {
permute_args(nonopt_start, nonopt_end,
optind, nargv);
optind -= nonopt_end - nonopt_start;
}
nonopt_start = nonopt_end = -1;
return (-1);
}
}
/*
* Check long options if:
* 1) we were passed some
* 2) the arg is not just "-"
* 3) either the arg starts with -- we are getopt_long_only()
*/
if (long_options != NULL && place != nargv[optind] &&
(*place == '-' || (flags & FLAG_LONGONLY))) {
short_too = 0;
if (*place == '-')
place++; /* --foo long option */
else if (*place != ':' && strchr(options, *place) != NULL)
short_too = 1; /* could be short option too */
optchar = parse_long_options(nargv, options, long_options,
idx, short_too);
if (optchar != -1) {
place = EMSG;
return (optchar);
}
}
if ((optchar = (int)*place++) == (int)':' ||
(optchar == (int)'-' && *place != '\0') ||
(oli = strchr(options, optchar)) == NULL) {
/*
* If the user specified "-" and '-' isn't listed in
* options, return -1 (non-option) as per POSIX.
* Otherwise, it is an unknown option character (or ':').
*/
if (optchar == (int)'-' && *place == '\0')
return (-1);
if (!*place)
++optind;
if (PRINT_ERROR)
warnx(illoptchar, optchar);
optopt = optchar;
return (BADCH);
}
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
/* -W long-option */
if (*place) /* no space */
/* NOTHING */;
else if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else /* white space */
place = nargv[optind];
optchar = parse_long_options(nargv, options, long_options,
idx, 0);
place = EMSG;
return (optchar);
}
if (*++oli != ':') { /* doesn't take argument */
if (!*place)
++optind;
} else { /* takes (optional) argument */
optarg = NULL;
if (*place) /* no white space */
optarg = place;
else if (oli[1] != ':') { /* arg not optional */
if (++optind >= nargc) { /* no arg */
place = EMSG;
if (PRINT_ERROR)
warnx(recargchar, optchar);
optopt = optchar;
return (BADARG);
} else
optarg = nargv[optind];
}
place = EMSG;
++optind;
}
/* dump back option letter */
return (optchar);
}
#ifdef REPLACE_GETOPT
/*
* getopt --
* Parse argc/argv argument vector.
*
* [eventually this will replace the BSD getopt]
*/
int
getopt(int nargc, char * const *nargv, const char *options)
{
/*
* We don't pass FLAG_PERMUTE to getopt_internal() since
* the BSD getopt(3) (unlike GNU) has never done this.
*
* Furthermore, since many privileged programs call getopt()
* before dropping privileges it makes sense to keep things
* as simple (and bug-free) as possible.
*/
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
}
#endif /* REPLACE_GETOPT */
/*
* getopt_long --
* Parse argc/argv argument vector.
*/
int
getopt_long(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE));
}
/*
* getopt_long_only --
* Parse argc/argv argument vector.
*/
int
getopt_long_only(int nargc, char * const *nargv, const char *options,
const struct option *long_options, int *idx)
{
return (getopt_internal(nargc, nargv, options, long_options, idx,
FLAG_PERMUTE|FLAG_LONGONLY));
}

View File

@ -1,316 +0,0 @@
/*
* 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 "config.h"
#include "vcs_version.h"
#include "cli_config.h"
#include <errno.h>
#include <inttypes.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_IO_H
# include <io.h>
#endif
#ifdef _WIN32
# include <windows.h>
#endif
#ifdef __APPLE__
#include <mach/mach_time.h>
#endif
#include "dav1d/dav1d.h"
#include "input/input.h"
#include "output/output.h"
#include "dav1d_cli_parse.h"
static uint64_t get_time_nanos(void) {
#ifdef _WIN32
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
LARGE_INTEGER t;
QueryPerformanceCounter(&t);
uint64_t seconds = t.QuadPart / frequency.QuadPart;
uint64_t fractions = t.QuadPart % frequency.QuadPart;
return 1000000000 * seconds + 1000000000 * fractions / frequency.QuadPart;
#elif defined(HAVE_CLOCK_GETTIME)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return 1000000000ULL * ts.tv_sec + ts.tv_nsec;
#elif defined(__APPLE__)
mach_timebase_info_data_t info;
mach_timebase_info(&info);
return mach_absolute_time() * info.numer / info.denom;
#endif
}
static void sleep_nanos(uint64_t d) {
#ifdef _WIN32
Sleep((unsigned)(d / 1000000));
#else
const struct timespec ts = {
.tv_sec = (time_t)(d / 1000000000),
.tv_nsec = d % 1000000000,
};
nanosleep(&ts, NULL);
#endif
}
static void synchronize(const int realtime, const unsigned cache,
const unsigned n_out, const uint64_t nspf,
const uint64_t tfirst, uint64_t *const elapsed,
FILE *const frametimes)
{
const uint64_t tcurr = get_time_nanos();
const uint64_t last = *elapsed;
*elapsed = tcurr - tfirst;
if (realtime) {
const uint64_t deadline = nspf * n_out;
if (*elapsed < deadline) {
const uint64_t remaining = deadline - *elapsed;
if (remaining > nspf * cache) sleep_nanos(remaining - nspf * cache);
*elapsed = deadline;
}
}
if (frametimes) {
const uint64_t frametime = *elapsed - last;
fprintf(frametimes, "%" PRIu64 "\n", frametime);
fflush(frametimes);
}
}
static void print_stats(const int istty, const unsigned n, const unsigned num,
const uint64_t elapsed, const double i_fps)
{
char buf[80], *b = buf, *const end = buf + 80;
if (istty)
*b++ = '\r';
if (num == 0xFFFFFFFF)
b += snprintf(b, end - b, "Decoded %u frames", n);
else
b += snprintf(b, end - b, "Decoded %u/%u frames (%.1lf%%)",
n, num, 100.0 * n / num);
if (b < end) {
const double d_fps = 1e9 * n / elapsed;
if (i_fps) {
const double speed = d_fps / i_fps;
b += snprintf(b, end - b, " - %.2lf/%.2lf fps (%.2lfx)",
d_fps, i_fps, speed);
} else {
b += snprintf(b, end - b, " - %.2lf fps", d_fps);
}
}
if (!istty)
strcpy(b > end - 2 ? end - 2 : b, "\n");
fputs(buf, stderr);
}
int main(const int argc, char *const *const argv) {
const int istty = isatty(fileno(stderr));
int res = 0;
CLISettings cli_settings;
Dav1dSettings lib_settings;
DemuxerContext *in;
MuxerContext *out = NULL;
Dav1dPicture p;
Dav1dContext *c;
Dav1dData data;
unsigned n_out = 0, total, fps[2], timebase[2];
uint64_t nspf, tfirst, elapsed;
double i_fps;
FILE *frametimes = NULL;
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;
}
parse(argc, argv, &cli_settings, &lib_settings);
if ((res = input_open(&in, cli_settings.demuxer,
cli_settings.inputfile,
fps, &total, timebase)) < 0)
{
return EXIT_FAILURE;
}
for (unsigned i = 0; i <= cli_settings.skip; i++) {
if ((res = input_read(in, &data)) < 0) {
input_close(in);
return EXIT_FAILURE;
}
if (i < cli_settings.skip) dav1d_data_unref(&data);
}
if (!cli_settings.quiet)
fprintf(stderr, "dav1d %s - by VideoLAN\n", dav1d_version());
// skip frames until a sequence header is found
if (cli_settings.skip) {
Dav1dSequenceHeader seq;
unsigned seq_skip = 0;
while (dav1d_parse_sequence_header(&seq, data.data, data.sz)) {
if ((res = input_read(in, &data)) < 0) {
input_close(in);
return EXIT_FAILURE;
}
seq_skip++;
}
if (seq_skip && !cli_settings.quiet)
fprintf(stderr,
"skipped %u packets due to missing sequence header\n",
seq_skip);
}
if (cli_settings.limit != 0 && cli_settings.limit < total)
total = cli_settings.limit;
if ((res = dav1d_open(&c, &lib_settings)))
return EXIT_FAILURE;
if (cli_settings.frametimes)
frametimes = fopen(cli_settings.frametimes, "w");
if (cli_settings.realtime != REALTIME_CUSTOM) {
if (fps[1] == 0) {
i_fps = 0;
nspf = 0;
} else {
i_fps = (double)fps[0] / fps[1];
nspf = 1000000000ULL * fps[1] / fps[0];
}
} else {
i_fps = cli_settings.realtime_fps;
nspf = (uint64_t)(1000000000.0 / cli_settings.realtime_fps);
}
tfirst = get_time_nanos();
do {
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)));
break;
}
}
if ((res = dav1d_get_picture(c, &p)) < 0) {
if (res != DAV1D_ERR(EAGAIN)) {
fprintf(stderr, "Error decoding frame: %s\n",
strerror(DAV1D_ERR(res)));
break;
}
res = 0;
} else {
if (!n_out) {
if ((res = output_open(&out, cli_settings.muxer,
cli_settings.outputfile,
&p.p, fps)) < 0)
{
if (frametimes) fclose(frametimes);
return EXIT_FAILURE;
}
}
if ((res = output_write(out, &p)) < 0)
break;
n_out++;
if (nspf || !cli_settings.quiet) {
synchronize(cli_settings.realtime, cli_settings.realtime_cache,
n_out, nspf, tfirst, &elapsed, frametimes);
}
if (!cli_settings.quiet)
print_stats(istty, n_out, total, elapsed, i_fps);
}
if (cli_settings.limit && n_out == cli_settings.limit)
break;
} while (data.sz > 0 || !input_read(in, &data));
if (data.sz > 0) dav1d_data_unref(&data);
// flush
if (res == 0) while (!cli_settings.limit || n_out < cli_settings.limit) {
if ((res = dav1d_get_picture(c, &p)) < 0) {
if (res != DAV1D_ERR(EAGAIN)) {
fprintf(stderr, "Error decoding frame: %s\n",
strerror(DAV1D_ERR(res)));
} else {
res = 0;
break;
}
} else {
if (!n_out) {
if ((res = output_open(&out, cli_settings.muxer,
cli_settings.outputfile,
&p.p, fps)) < 0)
{
if (frametimes) fclose(frametimes);
return EXIT_FAILURE;
}
}
if ((res = output_write(out, &p)) < 0)
break;
n_out++;
if (nspf || !cli_settings.quiet) {
synchronize(cli_settings.realtime, cli_settings.realtime_cache,
n_out, nspf, tfirst, &elapsed, frametimes);
}
if (!cli_settings.quiet)
print_stats(istty, n_out, total, elapsed, i_fps);
}
}
if (frametimes) fclose(frametimes);
input_close(in);
if (out) {
if (!cli_settings.quiet && istty)
fprintf(stderr, "\n");
if (cli_settings.verify)
res |= output_verify(out, cli_settings.verify);
else
output_close(out);
} else {
fprintf(stderr, "No data decoded\n");
res = 1;
}
dav1d_close(&c);
return (res == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="VideoLAN.dav1d" version="1.0.0.0"/>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>
</assembly>

View File

@ -1,33 +0,0 @@
#define API_VERSION_NUMBER @API_VERSION_MAJOR@,@API_VERSION_MINOR@,@API_VERSION_REVISION@,0
#define API_VERSION_NUMBER_STR "@API_VERSION_MAJOR@.@API_VERSION_MINOR@.@API_VERSION_REVISION@"
#define PROJECT_VERSION_NUMBER @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_REVISION@,0
#define PROJECT_VERSION_NUMBER_STR "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_REVISION@"
#include <windows.h>
1 RT_MANIFEST "dav1d.manifest"
1 VERSIONINFO
FILETYPE VFT_APP
FILEOS VOS_NT_WINDOWS32
PRODUCTVERSION PROJECT_VERSION_NUMBER
FILEVERSION API_VERSION_NUMBER
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "CompanyName", "VideoLAN"
VALUE "ProductName", "dav1d"
VALUE "ProductVersion", PROJECT_VERSION_NUMBER_STR
VALUE "FileVersion", API_VERSION_NUMBER_STR
VALUE "FileDescription", "dav1d " PROJECT_VERSION_NUMBER_STR " - AV1 decoder"
VALUE "InternalName", "dav1d"
VALUE "OriginalFilename", "dav1d.exe"
VALUE "LegalCopyright", "Copyright \251 @COPYRIGHT_YEARS@ VideoLAN and dav1d Authors"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

View File

@ -1,382 +0,0 @@
/*
* 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 "config.h"
#include "cli_config.h"
#include <getopt.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "dav1d_cli_parse.h"
#include "src/cpu.h"
static const char short_opts[] = "i:o:vql:s:";
enum {
ARG_DEMUXER = 256,
ARG_MUXER,
ARG_FRAME_TIMES,
ARG_REALTIME,
ARG_REALTIME_CACHE,
ARG_FRAME_THREADS,
ARG_TILE_THREADS,
ARG_POSTFILTER_THREADS,
ARG_VERIFY,
ARG_FILM_GRAIN,
ARG_OPPOINT,
ARG_ALL_LAYERS,
ARG_SIZE_LIMIT,
ARG_CPU_MASK,
};
static const struct option long_opts[] = {
{ "input", 1, NULL, 'i' },
{ "output", 1, NULL, 'o' },
{ "quiet", 0, NULL, 'q' },
{ "demuxer", 1, NULL, ARG_DEMUXER },
{ "muxer", 1, NULL, ARG_MUXER },
{ "version", 0, NULL, 'v' },
{ "frametimes", 1, NULL, ARG_FRAME_TIMES },
{ "limit", 1, NULL, 'l' },
{ "skip", 1, NULL, 's' },
{ "realtime", 2, NULL, ARG_REALTIME },
{ "realtimecache", 1, NULL, ARG_REALTIME_CACHE },
{ "framethreads", 1, NULL, ARG_FRAME_THREADS },
{ "tilethreads", 1, NULL, ARG_TILE_THREADS },
{ "pfthreads", 1, NULL, ARG_POSTFILTER_THREADS },
{ "verify", 1, NULL, ARG_VERIFY },
{ "filmgrain", 1, NULL, ARG_FILM_GRAIN },
{ "oppoint", 1, NULL, ARG_OPPOINT },
{ "alllayers", 1, NULL, ARG_ALL_LAYERS },
{ "sizelimit", 1, NULL, ARG_SIZE_LIMIT },
{ "cpumask", 1, NULL, ARG_CPU_MASK },
{ NULL, 0, NULL, 0 },
};
#if HAVE_XXHASH_H
#define AVAILABLE_MUXERS "'md5', 'xxh3', 'yuv', 'yuv4mpeg2' or 'null'"
#else
#define AVAILABLE_MUXERS "'md5', 'yuv', 'yuv4mpeg2' or 'null'"
#endif
#if ARCH_AARCH64 || ARCH_ARM
#define ALLOWED_CPU_MASKS " or 'neon'"
#elif ARCH_PPC64LE
#define ALLOWED_CPU_MASKS " or 'vsx'"
#elif ARCH_X86
#define ALLOWED_CPU_MASKS \
", 'sse2', 'ssse3', 'sse41', 'avx2' or 'avx512icl'"
#else
#define ALLOWED_CPU_MASKS "not yet implemented for this architecture"
#endif
static void usage(const char *const app, const char *const reason, ...) {
if (reason) {
va_list args;
va_start(args, reason);
vfprintf(stderr, reason, args);
va_end(args);
fprintf(stderr, "\n\n");
}
fprintf(stderr, "Usage: %s [options]\n\n", app);
fprintf(stderr, "Supported options:\n"
" --input/-i $file: input file\n"
" --output/-o $file: output file\n"
" --demuxer $name: force demuxer type ('ivf', 'section5' or 'annexb'; default: detect from content)\n"
" --muxer $name: force muxer type (" AVAILABLE_MUXERS "; default: detect from extension)\n"
" --quiet/-q: disable status messages\n"
" --frametimes $file: dump frame times to file\n"
" --limit/-l $num: stop decoding after $num frames\n"
" --skip/-s $num: skip decoding of the first $num frames\n"
" --realtime [$fract]: limit framerate, optional argument to override input framerate\n"
" --realtimecache $num: set the size of the cache in realtime mode (default: 0)\n"
" --version/-v: print version and exit\n"
" --framethreads $num: number of frame threads (default: 1)\n"
" --tilethreads $num: number of tile threads (default: 1)\n"
" --pfthreads $num: number of postfilter threads (default: 1)\n"
" --filmgrain $num: enable film grain application (default: 1, except if muxer is md5 or xxh3)\n"
" --oppoint $num: select an operating point of a scalable AV1 bitstream (0 - 31)\n"
" --alllayers $num: output all spatial layers of a scalable AV1 bitstream (default: 1)\n"
" --sizelimit $num: stop decoding if the frame size exceeds the specified limit\n"
" --verify $md5: verify decoded md5. implies --muxer md5, no output\n"
" --cpumask $mask: restrict permitted CPU instruction sets (0" ALLOWED_CPU_MASKS "; default: -1)\n");
exit(1);
}
static void error(const char *const app, const char *const optarg,
const int option, const char *const shouldbe)
{
char optname[256];
int n;
for (n = 0; long_opts[n].name; n++)
if (long_opts[n].val == option)
break;
assert(long_opts[n].name);
if (long_opts[n].val < 256) {
sprintf(optname, "-%c/--%s", long_opts[n].val, long_opts[n].name);
} else {
sprintf(optname, "--%s", long_opts[n].name);
}
usage(app, "Invalid argument \"%s\" for option %s; should be %s",
optarg, optname, shouldbe);
}
static unsigned parse_unsigned(const char *const optarg, const int option,
const char *const app)
{
char *end;
const unsigned res = (unsigned) strtoul(optarg, &end, 0);
if (*end || end == optarg) error(app, optarg, option, "an integer");
return res;
}
static int parse_optional_fraction(const char *const optarg, const int option,
const char *const app, double *value)
{
if (optarg == NULL) return 0;
char *end;
*value = strtod(optarg, &end);
if (*end == '/' && end != optarg) {
const char *optarg2 = end + 1;
*value /= strtod(optarg2, &end);
if (*end || end == optarg2) error(app, optarg, option, "a fraction");
} else if (*end || end == optarg) {
error(app, optarg, option, "a fraction");
}
return 1;
}
typedef struct EnumParseTable {
const char *str;
const int val;
} EnumParseTable;
#if ARCH_X86
enum CpuMask {
X86_CPU_MASK_SSE2 = DAV1D_X86_CPU_FLAG_SSE2,
X86_CPU_MASK_SSSE3 = DAV1D_X86_CPU_FLAG_SSSE3 | X86_CPU_MASK_SSE2,
X86_CPU_MASK_SSE41 = DAV1D_X86_CPU_FLAG_SSE41 | X86_CPU_MASK_SSSE3,
X86_CPU_MASK_AVX2 = DAV1D_X86_CPU_FLAG_AVX2 | X86_CPU_MASK_SSE41,
X86_CPU_MASK_AVX512ICL = DAV1D_X86_CPU_FLAG_AVX512ICL | X86_CPU_MASK_AVX2,
};
#endif
static const EnumParseTable cpu_mask_tbl[] = {
#if ARCH_AARCH64 || ARCH_ARM
{ "neon", DAV1D_ARM_CPU_FLAG_NEON },
#elif ARCH_PPC64LE
{ "vsx", DAV1D_PPC_CPU_FLAG_VSX },
#elif ARCH_X86
{ "sse2", X86_CPU_MASK_SSE2 },
{ "ssse3", X86_CPU_MASK_SSSE3 },
{ "sse41", X86_CPU_MASK_SSE41 },
{ "avx2", X86_CPU_MASK_AVX2 },
{ "avx512icl", X86_CPU_MASK_AVX512ICL },
#endif
{ "none", 0 },
};
#define ARRAY_SIZE(n) (sizeof(n)/sizeof(*(n)))
static unsigned parse_enum(char *optarg, const EnumParseTable *const tbl,
const int tbl_sz, const int option, const char *app)
{
char str[1024];
strcpy(str, "any of ");
for (int n = 0; n < tbl_sz; n++) {
if (!strcmp(tbl[n].str, optarg))
return tbl[n].val;
if (n) {
if (n < tbl_sz - 1)
strcat(str, ", ");
else
strcat(str, " or ");
}
strcat(str, tbl[n].str);
}
char *end;
unsigned res;
if (!strncmp(optarg, "0x", 2)) {
res = (unsigned) strtoul(&optarg[2], &end, 16);
} else {
res = (unsigned) strtoul(optarg, &end, 0);
}
if (*end || end == optarg) {
strcat(str, ", a hexadecimal (starting with 0x), or an integer");
error(app, optarg, option, str);
}
return res;
}
void parse(const int argc, char *const *const argv,
CLISettings *const cli_settings, Dav1dSettings *const lib_settings)
{
int o;
memset(cli_settings, 0, sizeof(*cli_settings));
dav1d_default_settings(lib_settings);
int grain_specified = 0;
while ((o = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (o) {
case 'o':
cli_settings->outputfile = optarg;
break;
case 'i':
cli_settings->inputfile = optarg;
break;
case 'q':
cli_settings->quiet = 1;
break;
case 'l':
cli_settings->limit = parse_unsigned(optarg, 'l', argv[0]);
break;
case 's':
cli_settings->skip = parse_unsigned(optarg, 's', argv[0]);
break;
case ARG_DEMUXER:
cli_settings->demuxer = optarg;
break;
case ARG_MUXER:
cli_settings->muxer = optarg;
break;
case ARG_FRAME_TIMES:
cli_settings->frametimes = optarg;
break;
case ARG_REALTIME:
// workaround to parse an optional argument of the form `--a b`
// (getopt only allows `--a=b`)
if (optarg == NULL && optind < argc && argv[optind] != NULL &&
argv[optind][0] != '-')
{
optarg = argv[optind];
optind++;
}
cli_settings->realtime = 1 + parse_optional_fraction(optarg,
ARG_REALTIME, argv[0], &cli_settings->realtime_fps);
break;
case ARG_REALTIME_CACHE:
cli_settings->realtime_cache =
parse_unsigned(optarg, ARG_REALTIME_CACHE, argv[0]);
break;
case ARG_FRAME_THREADS:
lib_settings->n_frame_threads =
parse_unsigned(optarg, ARG_FRAME_THREADS, argv[0]);
break;
case ARG_TILE_THREADS:
lib_settings->n_tile_threads =
parse_unsigned(optarg, ARG_TILE_THREADS, argv[0]);
break;
case ARG_POSTFILTER_THREADS:
lib_settings->n_postfilter_threads =
parse_unsigned(optarg, ARG_POSTFILTER_THREADS, argv[0]);
break;
case ARG_VERIFY:
cli_settings->verify = optarg;
break;
case ARG_FILM_GRAIN:
lib_settings->apply_grain =
!!parse_unsigned(optarg, ARG_FILM_GRAIN, argv[0]);
grain_specified = 1;
break;
case ARG_OPPOINT:
lib_settings->operating_point =
parse_unsigned(optarg, ARG_OPPOINT, argv[0]);
break;
case ARG_ALL_LAYERS:
lib_settings->all_layers =
!!parse_unsigned(optarg, ARG_ALL_LAYERS, argv[0]);
break;
case ARG_SIZE_LIMIT: {
char *arg = optarg, *end;
uint64_t res = strtoul(arg, &end, 0);
if (*end == 'x') // NxM
res *= strtoul((arg = end + 1), &end, 0);
if (*end || end == arg || res >= UINT_MAX)
error(argv[0], optarg, ARG_SIZE_LIMIT, "an integer or dimension");
lib_settings->frame_size_limit = (unsigned) res;
break;
}
case 'v':
fprintf(stderr, "%s\n", dav1d_version());
exit(0);
case ARG_CPU_MASK:
dav1d_set_cpu_flags_mask(parse_enum(optarg, cpu_mask_tbl, ARRAY_SIZE(cpu_mask_tbl),
ARG_CPU_MASK, argv[0]));
break;
default:
usage(argv[0], NULL);
}
}
if (optind < argc)
usage(argv[0], "Extra/unused arguments found, e.g. '%s'\n", argv[optind]);
if (cli_settings->verify) {
if (cli_settings->outputfile)
usage(argv[0], "Verification (--verify) requires output file (-o/--output) to not set");
if (cli_settings->muxer && strcmp(cli_settings->muxer, "md5") &&
strcmp(cli_settings->muxer, "xxh3"))
{
usage(argv[0], "Verification (--verify) requires a checksum muxer (md5 or xxh3)");
}
cli_settings->outputfile = "-";
if (!cli_settings->muxer)
cli_settings->muxer = "md5";
}
if (!grain_specified && cli_settings->muxer &&
(!strcmp(cli_settings->muxer, "md5") ||
!strcmp(cli_settings->muxer, "xxh3")))
{
lib_settings->apply_grain = 0;
}
if (!cli_settings->inputfile)
usage(argv[0], "Input file (-i/--input) is required");
if ((!cli_settings->muxer || strcmp(cli_settings->muxer, "null")) &&
!cli_settings->outputfile)
{
usage(argv[0], "Output file (-o/--output) is required");
}
}

View File

@ -1,54 +0,0 @@
/*
* 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.
*/
#ifndef DAV1D_CLI_PARSE_H
#define DAV1D_CLI_PARSE_H
#include "dav1d/dav1d.h"
typedef struct {
const char *outputfile;
const char *inputfile;
const char *demuxer;
const char *muxer;
const char *frametimes;
const char *verify;
unsigned limit, skip;
int quiet;
enum {
REALTIME_DISABLE = 0,
REALTIME_INPUT,
REALTIME_CUSTOM,
} realtime;
double realtime_fps;
unsigned realtime_cache;
} CLISettings;
void parse(const int argc, char *const *const argv,
CLISettings *const cli_settings, Dav1dSettings *const lib_settings);
#endif /* DAV1D_CLI_PARSE_H */

View File

@ -1,196 +0,0 @@
/*
* Copyright © 2018, VideoLAN and dav1d authors
* Copyright © 2018, Two Orioles, LLC
* Copyright © 2019, James Almer <jamrial@gmail.com>
* 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "common/intops.h"
#include "dav1d/headers.h"
#include "input/demuxer.h"
#include "input/parse.h"
// these functions are based on an implementation from FFmpeg, and relicensed
// with author's permission
#define PROBE_SIZE 1024
static int annexb_probe(const uint8_t *data) {
int ret, cnt = 0;
size_t temporal_unit_size;
ret = leb(data + cnt, PROBE_SIZE - cnt, &temporal_unit_size);
if (ret < 0)
return 0;
cnt += ret;
size_t frame_unit_size;
ret = leb(data + cnt, PROBE_SIZE - cnt, &frame_unit_size);
if (ret < 0 || ((uint64_t)frame_unit_size + ret) > temporal_unit_size)
return 0;
cnt += ret;
temporal_unit_size -= ret;
size_t obu_unit_size;
ret = leb(data + cnt, PROBE_SIZE - cnt, &obu_unit_size);
if (ret < 0 || ((uint64_t)obu_unit_size + ret) >= frame_unit_size)
return 0;
cnt += ret;
temporal_unit_size -= obu_unit_size + ret;
frame_unit_size -= obu_unit_size + ret;
// Check that the first OBU is a Temporal Delimiter.
size_t obu_size;
enum Dav1dObuType type;
ret = parse_obu_header(data + cnt, imin(PROBE_SIZE - cnt, (int) obu_unit_size),
&obu_size, &type, 1);
if (ret < 0 || type != DAV1D_OBU_TD || obu_size > 0)
return 0;
cnt += (int)obu_unit_size;
// look for first frame and accompanying sequence header
int seq = 0;
while (cnt < PROBE_SIZE) {
ret = leb(data + cnt, PROBE_SIZE - cnt, &obu_unit_size);
if (ret < 0 || ((uint64_t)obu_unit_size + ret) > frame_unit_size)
return 0;
cnt += ret;
temporal_unit_size -= ret;
frame_unit_size -= ret;
ret = parse_obu_header(data + cnt, imin(PROBE_SIZE - cnt, (int) obu_unit_size),
&obu_size, &type, 1);
if (ret < 0)
return 0;
cnt += (int)obu_unit_size;
switch (type) {
case DAV1D_OBU_SEQ_HDR:
seq = 1;
break;
case DAV1D_OBU_FRAME:
case DAV1D_OBU_FRAME_HDR:
return seq;
case DAV1D_OBU_TD:
case DAV1D_OBU_TILE_GRP:
return 0;
default:
break;
}
temporal_unit_size -= obu_unit_size;
frame_unit_size -= obu_unit_size;
if (frame_unit_size <= 0)
break;
}
return 0;
}
typedef struct DemuxerPriv {
FILE *f;
size_t temporal_unit_size;
size_t frame_unit_size;
} AnnexbInputContext;
static int annexb_open(AnnexbInputContext *const c, const char *const file,
unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
{
int res;
size_t len;
if (!(c->f = fopen(file, "rb"))) {
fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
return -1;
}
// TODO: Parse sequence header and read timing info if any.
fps[0] = 25;
fps[1] = 1;
timebase[0] = 25;
timebase[1] = 1;
for (*num_frames = 0;; (*num_frames)++) {
res = leb128(c->f, &len);
if (res < 0)
break;
fseeko(c->f, len, SEEK_CUR);
}
fseeko(c->f, 0, SEEK_SET);
return 0;
}
static int annexb_read(AnnexbInputContext *const c, Dav1dData *const data) {
size_t len;
int res;
if (!c->temporal_unit_size) {
res = leb128(c->f, &c->temporal_unit_size);
if (res < 0) return -1;
}
if (!c->frame_unit_size) {
res = leb128(c->f, &c->frame_unit_size);
if (res < 0 || (c->frame_unit_size + res) > c->temporal_unit_size) return -1;
c->temporal_unit_size -= res;
}
res = leb128(c->f, &len);
if (res < 0 || (len + res) > c->frame_unit_size) return -1;
uint8_t *ptr = dav1d_data_create(data, len);
if (!ptr) return -1;
c->temporal_unit_size -= len + res;
c->frame_unit_size -= len + res;
if (fread(ptr, len, 1, c->f) != 1) {
fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
dav1d_data_unref(data);
return -1;
}
return 0;
}
static void annexb_close(AnnexbInputContext *const c) {
fclose(c->f);
}
const Demuxer annexb_demuxer = {
.priv_data_size = sizeof(AnnexbInputContext),
.name = "annexb",
.probe = annexb_probe,
.probe_sz = PROBE_SIZE,
.open = annexb_open,
.read = annexb_read,
.seek = NULL,
.close = annexb_close,
};

View File

@ -1,46 +0,0 @@
/*
* 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.
*/
#ifndef DAV1D_INPUT_DEMUXER_H
#define DAV1D_INPUT_DEMUXER_H
#include "data.h"
typedef struct DemuxerPriv DemuxerPriv;
typedef struct Demuxer {
int priv_data_size;
const char *name;
int probe_sz;
int (*probe)(const uint8_t *data);
int (*open)(DemuxerPriv *ctx, const char *filename,
unsigned fps[2], unsigned *num_frames, unsigned timebase[2]);
int (*read)(DemuxerPriv *ctx, Dav1dData *data);
int (*seek)(DemuxerPriv *ctx, uint64_t pts);
void (*close)(DemuxerPriv *ctx);
} Demuxer;
#endif /* DAV1D_INPUT_DEMUXER_H */

View File

@ -1,138 +0,0 @@
/*
* 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 "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common/attributes.h"
#include "common/intops.h"
#include "input/input.h"
#include "input/demuxer.h"
struct DemuxerContext {
DemuxerPriv *data;
const Demuxer *impl;
};
extern const Demuxer ivf_demuxer;
extern const Demuxer annexb_demuxer;
extern const Demuxer section5_demuxer;
static const Demuxer *const demuxers[] = {
&ivf_demuxer,
&annexb_demuxer,
&section5_demuxer,
NULL
};
int input_open(DemuxerContext **const c_out,
const char *const name, const char *const filename,
unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
{
const Demuxer *impl;
DemuxerContext *c;
int res, i;
if (name) {
for (i = 0; demuxers[i]; i++) {
if (!strcmp(demuxers[i]->name, name)) {
impl = demuxers[i];
break;
}
}
if (!demuxers[i]) {
fprintf(stderr, "Failed to find demuxer named \"%s\"\n", name);
return DAV1D_ERR(ENOPROTOOPT);
}
} else {
int probe_sz = 0;
for (i = 0; demuxers[i]; i++)
probe_sz = imax(probe_sz, demuxers[i]->probe_sz);
uint8_t *const probe_data = malloc(probe_sz);
if (!probe_data) {
fprintf(stderr, "Failed to allocate memory\n");
return DAV1D_ERR(ENOMEM);
}
FILE *f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Failed to open input file %s: %s\n", filename, strerror(errno));
return errno ? DAV1D_ERR(errno) : DAV1D_ERR(EIO);
}
res = !!fread(probe_data, 1, probe_sz, f);
fclose(f);
if (!res) {
free(probe_data);
fprintf(stderr, "Failed to read probe data\n");
return errno ? DAV1D_ERR(errno) : DAV1D_ERR(EIO);
}
for (i = 0; demuxers[i]; i++) {
if (demuxers[i]->probe(probe_data)) {
impl = demuxers[i];
break;
}
}
free(probe_data);
if (!demuxers[i]) {
fprintf(stderr,
"Failed to probe demuxer for file %s\n",
filename);
return DAV1D_ERR(ENOPROTOOPT);
}
}
if (!(c = calloc(1, sizeof(DemuxerContext) + impl->priv_data_size))) {
fprintf(stderr, "Failed to allocate memory\n");
return DAV1D_ERR(ENOMEM);
}
c->impl = impl;
c->data = (DemuxerPriv *) &c[1];
if ((res = impl->open(c->data, filename, fps, num_frames, timebase)) < 0) {
free(c);
return res;
}
*c_out = c;
return 0;
}
int input_read(DemuxerContext *const ctx, Dav1dData *const data) {
return ctx->impl->read(ctx->data, data);
}
int input_seek(DemuxerContext *const ctx, const uint64_t pts) {
return ctx->impl->seek ? ctx->impl->seek(ctx->data, pts) : -1;
}
void input_close(DemuxerContext *const ctx) {
ctx->impl->close(ctx->data);
free(ctx);
}

View File

@ -1,42 +0,0 @@
/*
* 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.
*/
#ifndef DAV1D_INPUT_INPUT_H
#define DAV1D_INPUT_INPUT_H
#include "data.h"
typedef struct DemuxerContext DemuxerContext;
int input_open(DemuxerContext **const c_out,
const char *const name, const char *const filename,
unsigned fps[2], unsigned *num_frames, unsigned timebase[2]);
int input_read(DemuxerContext *ctx, Dav1dData *data);
int input_seek(DemuxerContext *ctx, uint64_t pts);
void input_close(DemuxerContext *ctx);
#endif /* DAV1D_INPUT_INPUT_H */

View File

@ -1,203 +0,0 @@
/*
* 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 "config.h"
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "input/demuxer.h"
typedef struct DemuxerPriv {
FILE *f;
int broken;
double timebase;
uint64_t last_ts;
uint64_t step;
} IvfInputContext;
static const uint8_t probe_data[] = {
'D', 'K', 'I', 'F',
0, 0, 0x20, 0,
'A', 'V', '0', '1',
};
static int ivf_probe(const uint8_t *const data) {
return !memcmp(data, probe_data, sizeof(probe_data));
}
static unsigned rl32(const uint8_t *const p) {
return ((uint32_t)p[3] << 24U) | (p[2] << 16U) | (p[1] << 8U) | p[0];
}
static int64_t rl64(const uint8_t *const p) {
return (((uint64_t) rl32(&p[4])) << 32) | rl32(p);
}
static int ivf_open(IvfInputContext *const c, const char *const file,
unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
{
uint8_t hdr[32];
if (!(c->f = fopen(file, "rb"))) {
fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
return -1;
} else if (fread(hdr, 32, 1, c->f) != 1) {
fprintf(stderr, "Failed to read stream header: %s\n", strerror(errno));
fclose(c->f);
return -1;
} else if (memcmp(hdr, "DKIF", 4)) {
fprintf(stderr, "%s is not an IVF file [tag=%.4s|0x%02x%02x%02x%02x]\n",
file, hdr, hdr[0], hdr[1], hdr[2], hdr[3]);
fclose(c->f);
return -1;
} else if (memcmp(&hdr[8], "AV01", 4)) {
fprintf(stderr, "%s is not an AV1 file [tag=%.4s|0x%02x%02x%02x%02x]\n",
file, &hdr[8], hdr[8], hdr[9], hdr[10], hdr[11]);
fclose(c->f);
return -1;
}
timebase[0] = rl32(&hdr[16]);
timebase[1] = rl32(&hdr[20]);
const unsigned duration = rl32(&hdr[24]);
uint8_t data[8];
c->broken = 0;
for (*num_frames = 0;; (*num_frames)++) {
if (fread(data, 4, 1, c->f) != 1) break; // EOF
size_t sz = rl32(data);
if (fread(data, 8, 1, c->f) != 1) break; // EOF
const uint64_t ts = rl64(data);
if (*num_frames && ts <= c->last_ts)
c->broken = 1;
c->last_ts = ts;
fseeko(c->f, sz, SEEK_CUR);
}
uint64_t fps_num = (uint64_t) timebase[0] * *num_frames;
uint64_t fps_den = (uint64_t) timebase[1] * duration;
if (fps_num && fps_den) { /* Reduce fraction */
uint64_t gcd = fps_num;
for (uint64_t a = fps_den, b; (b = a % gcd); a = gcd, gcd = b);
fps_num /= gcd;
fps_den /= gcd;
while ((fps_num | fps_den) > UINT_MAX) {
fps_num >>= 1;
fps_den >>= 1;
}
}
if (fps_num && fps_den) {
fps[0] = (unsigned) fps_num;
fps[1] = (unsigned) fps_den;
} else {
fps[0] = fps[1] = 0;
}
c->timebase = (double)timebase[0] / timebase[1];
c->step = duration / *num_frames;
fseeko(c->f, 32, SEEK_SET);
c->last_ts = 0;
return 0;
}
static inline int ivf_read_header(IvfInputContext *const c, ptrdiff_t *const sz,
int64_t *const off_, uint64_t *const ts)
{
uint8_t data[8];
int64_t const off = ftello(c->f);
if (off_) *off_ = off;
if (fread(data, 4, 1, c->f) != 1) return -1; // EOF
*sz = rl32(data);
if (!c->broken) {
if (fread(data, 8, 1, c->f) != 1) return -1;
*ts = rl64(data);
} else {
if (fseeko(c->f, 8, SEEK_CUR)) return -1;
*ts = off > 32 ? c->last_ts + c->step : 0;
}
return 0;
}
static int ivf_read(IvfInputContext *const c, Dav1dData *const buf) {
uint8_t *ptr;
ptrdiff_t sz;
int64_t off;
uint64_t ts;
if (ivf_read_header(c, &sz, &off, &ts)) return -1;
if (!(ptr = dav1d_data_create(buf, sz))) return -1;
if (fread(ptr, sz, 1, c->f) != 1) {
fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
dav1d_data_unref(buf);
return -1;
}
buf->m.offset = off;
buf->m.timestamp = ts;
c->last_ts = ts;
return 0;
}
static int ivf_seek(IvfInputContext *const c, const uint64_t pts) {
uint64_t cur;
const uint64_t ts = llround((pts * c->timebase) / 1000000000.0);
if (ts <= c->last_ts)
if (fseeko(c->f, 32, SEEK_SET)) goto error;
while (1) {
ptrdiff_t sz;
if (ivf_read_header(c, &sz, NULL, &cur)) goto error;
if (cur >= ts) break;
if (fseeko(c->f, sz, SEEK_CUR)) goto error;
c->last_ts = cur;
}
if (fseeko(c->f, -12, SEEK_CUR)) goto error;
return 0;
error:
fprintf(stderr, "Failed to seek: %s\n", strerror(errno));
return -1;
}
static void ivf_close(IvfInputContext *const c) {
fclose(c->f);
}
const Demuxer ivf_demuxer = {
.priv_data_size = sizeof(IvfInputContext),
.name = "ivf",
.probe = ivf_probe,
.probe_sz = sizeof(probe_data),
.open = ivf_open,
.read = ivf_read,
.seek = ivf_seek,
.close = ivf_close,
};

View File

@ -1,109 +0,0 @@
/*
* Copyright © 2018, VideoLAN and dav1d authors
* Copyright © 2018, Two Orioles, LLC
* Copyright © 2019, James Almer <jamrial@gmail.com>
* 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_INPUT_PARSE_H
#define DAV1D_INPUT_PARSE_H
#include <limits.h>
#include "dav1d/headers.h"
static int leb128(FILE *const f, size_t *const len) {
uint64_t val = 0;
unsigned i = 0, more;
do {
uint8_t v;
if (fread(&v, 1, 1, f) < 1)
return -1;
more = v & 0x80;
val |= ((uint64_t) (v & 0x7F)) << (i * 7);
i++;
} while (more && i < 8);
if (val > UINT_MAX || more)
return -1;
*len = (size_t) val;
return i;
}
// these functions are based on an implementation from FFmpeg, and relicensed
// with author's permission
static int leb(const uint8_t *ptr, int sz, size_t *const len) {
uint64_t val = 0;
unsigned i = 0, more;
do {
if (!sz--) return -1;
const int v = *ptr++;
more = v & 0x80;
val |= ((uint64_t) (v & 0x7F)) << (i * 7);
i++;
} while (more && i < 8);
if (val > UINT_MAX || more)
return -1;
*len = (size_t) val;
return i;
}
static inline int parse_obu_header(const uint8_t *buf, int buf_size,
size_t *const obu_size,
enum Dav1dObuType *const type,
const int allow_implicit_size)
{
int ret, extension_flag, has_size_flag;
if (!buf_size)
return -1;
if (*buf & 0x80) // obu_forbidden_bit
return -1;
*type = (*buf & 0x78) >> 3;
extension_flag = (*buf & 0x4) >> 2;
has_size_flag = (*buf & 0x2) >> 1;
// ignore obu_reserved_1bit
buf++;
buf_size--;
if (extension_flag) {
buf++;
buf_size--;
// ignore fields
}
if (has_size_flag) {
ret = leb(buf, buf_size, obu_size);
if (ret < 0)
return -1;
return (int) *obu_size + ret + 1 + extension_flag;
} else if (!allow_implicit_size)
return -1;
*obu_size = buf_size;
return buf_size + 1 + extension_flag;
}
#endif /* DAV1D_INPUT_PARSE_H */

View File

@ -1,186 +0,0 @@
/*
* Copyright © 2019, VideoLAN and dav1d authors
* Copyright © 2019, Two Orioles, LLC
* Copyright © 2019, James Almer <jamrial@gmail.com>
* 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 <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "dav1d/headers.h"
#include "input/demuxer.h"
#include "input/parse.h"
#define PROBE_SIZE 1024
static int section5_probe(const uint8_t *data) {
int ret, cnt = 0;
// Check that the first OBU is a Temporal Delimiter.
size_t obu_size;
enum Dav1dObuType type;
ret = parse_obu_header(data + cnt, PROBE_SIZE - cnt,
&obu_size, &type, 0);
if (ret < 0 || type != DAV1D_OBU_TD || obu_size > 0)
return 0;
cnt += ret;
// look for first frame and accompanying sequence header
int seq = 0;
while (cnt < PROBE_SIZE) {
ret = parse_obu_header(data + cnt, PROBE_SIZE - cnt,
&obu_size, &type, 0);
if (ret < 0)
return 0;
cnt += ret;
switch (type) {
case DAV1D_OBU_SEQ_HDR:
seq = 1;
break;
case DAV1D_OBU_FRAME:
case DAV1D_OBU_FRAME_HDR:
return seq;
case DAV1D_OBU_TD:
case DAV1D_OBU_TILE_GRP:
return 0;
default:
break;
}
}
return 0;
}
typedef struct DemuxerPriv {
FILE *f;
} Section5InputContext;
static int section5_open(Section5InputContext *const c, const char *const file,
unsigned fps[2], unsigned *const num_frames, unsigned timebase[2])
{
if (!(c->f = fopen(file, "rb"))) {
fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
return -1;
}
// TODO: Parse sequence header and read timing info if any.
fps[0] = 25;
fps[1] = 1;
timebase[0] = 25;
timebase[1] = 1;
*num_frames = 0;
for (;;) {
uint8_t byte[2];
if (fread(&byte[0], 1, 1, c->f) < 1)
break;
const enum Dav1dObuType obu_type = (byte[0] >> 3) & 0xf;
if (obu_type == DAV1D_OBU_TD)
(*num_frames)++;
const int has_length_field = byte[0] & 0x2;
if (!has_length_field)
return -1;
const int has_extension = byte[0] & 0x4;
if (has_extension && fread(&byte[1], 1, 1, c->f) < 1)
return -1;
size_t len;
const int res = leb128(c->f, &len);
if (res < 0)
return -1;
fseeko(c->f, len, SEEK_CUR); // skip packet
}
fseeko(c->f, 0, SEEK_SET);
return 0;
}
static int section5_read(Section5InputContext *const c, Dav1dData *const data) {
size_t total_bytes = 0;
for (int first = 1;; first = 0) {
uint8_t byte[2];
if (fread(&byte[0], 1, 1, c->f) < 1) {
if (!first && feof(c->f)) break;
return -1;
}
const enum Dav1dObuType obu_type = (byte[0] >> 3) & 0xf;
if (first) {
if (obu_type != DAV1D_OBU_TD)
return -1;
} else {
if (obu_type == DAV1D_OBU_TD) {
// include TD in next packet
fseeko(c->f, -1, SEEK_CUR);
break;
}
}
const int has_length_field = byte[0] & 0x2;
if (!has_length_field)
return -1;
const int has_extension = !!(byte[0] & 0x4);
if (has_extension && fread(&byte[1], 1, 1, c->f) < 1)
return -1;
size_t len;
const int res = leb128(c->f, &len);
if (res < 0)
return -1;
total_bytes += 1 + has_extension + res + len;
fseeko(c->f, len, SEEK_CUR); // skip packet, we'll read it below
}
fseeko(c->f, -(off_t)total_bytes, SEEK_CUR);
uint8_t *ptr = dav1d_data_create(data, total_bytes);
if (!ptr) return -1;
if (fread(ptr, total_bytes, 1, c->f) != 1) {
fprintf(stderr, "Failed to read frame data: %s\n", strerror(errno));
dav1d_data_unref(data);
return -1;
}
return 0;
}
static void section5_close(Section5InputContext *const c) {
fclose(c->f);
}
const Demuxer section5_demuxer = {
.priv_data_size = sizeof(Section5InputContext),
.name = "section5",
.probe = section5_probe,
.probe_sz = PROBE_SIZE,
.open = section5_open,
.read = section5_read,
.seek = NULL,
.close = section5_close,
};

View File

@ -1,123 +0,0 @@
# 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.
# Common source files used by tools and examples
dav1d_input_sources = files(
'input/input.c',
'input/annexb.c',
'input/ivf.c',
'input/section5.c',
)
dav1d_output_sources = files(
'output/md5.c',
'output/null.c',
'output/output.c',
'output/y4m2.c',
'output/yuv.c',
)
# hacky check for xxhash.h to allow copying it to tools/output
if not get_option('xxhash_muxer').disabled()
xxhash_include = '-I' + meson.current_source_dir() / 'output'
if cc.has_header_symbol('xxhash.h', 'XXH3_createState', args : xxhash_include)
dav1d_output_sources += 'output/xxhash.c'
xxh3_found = true
elif get_option('xxhash_muxer').enabled()
# manual error since 'required' kw arg in has_header_symbol() was only added in meson 0.50
error( 'C symbol XXH3_createState not found in header xxhash.h')
endif
endif
dav1d_input_objs = static_library('dav1d_input',
dav1d_input_sources,
include_directories : dav1d_inc_dirs,
install : false,
build_by_default : false,
)
dav1d_output_objs = static_library('dav1d_output',
dav1d_output_sources,
include_directories : dav1d_inc_dirs,
install : false,
build_by_default : false,
)
# Leave subdir if tools are disabled
if not get_option('enable_tools')
subdir_done()
endif
#
# Build definition for the dav1d tools
#
# Configuratin data for cli_config.h
cli_cdata = configuration_data()
cli_cdata.set10('HAVE_XXHASH_H', get_variable('xxh3_found', false))
cli_config_h_target = configure_file(output: 'cli_config.h', configuration: cli_cdata)
# dav1d cli tool sources
dav1d_sources = files(
'dav1d.c',
'dav1d_cli_parse.c',
)
if host_machine.system() == 'windows'
rc_file = configure_file(
input : 'dav1d.rc.in',
output : 'dav1d.rc',
configuration : rc_data
)
dav1d_rc_obj = winmod.compile_resources(rc_file,
depend_files : files('dav1d.manifest'),
include_directories : include_directories('.')
)
else
dav1d_rc_obj = []
endif
dav1d = executable('dav1d',
dav1d_sources,
dav1d_rc_obj,
rev_target, cli_config_h_target,
link_with : [libdav1d, dav1d_input_objs, dav1d_output_objs],
include_directories : [dav1d_inc_dirs],
dependencies : [
getopt_dependency,
thread_dependency,
rt_dependency,
libm_dependency,
],
install : true,
)

View File

@ -1,315 +0,0 @@
/*
* 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 "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "common/intops.h"
#include "output/muxer.h"
static const uint32_t k[64] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
};
#if ENDIANNESS_BIG
#define NE2LE_32(x) (((x & 0x00ff) << 24) |\
((x & 0xff00) << 8) |\
((x >> 8) & 0xff00) |\
((x >> 24) & 0x00ff))
#define NE2LE_64(x) (((x & 0x000000ff) << 56) |\
((x & 0x0000ff00) << 40) |\
((x & 0x00ff0000) << 24) |\
((x & 0xff000000) << 8) |\
((x >> 8) & 0xff000000) |\
((x >> 24) & 0x00ff0000) |\
((x >> 40) & 0x0000ff00) |\
((x >> 56) & 0x000000ff))
#else
#define NE2LE_32(x) (x)
#define NE2LE_64(x) (x)
#endif
typedef struct MuxerPriv {
uint32_t abcd[4];
union {
uint8_t data[64];
uint32_t data32[16];
};
uint64_t len;
FILE *f;
#if ENDIANNESS_BIG
uint8_t *bswap;
int bswap_w;
#endif
} MD5Context;
static int md5_open(MD5Context *const md5, const char *const file,
const Dav1dPictureParameters *const p,
const unsigned fps[2])
{
if (!strcmp(file, "-")) {
md5->f = stdout;
} else if (!(md5->f = fopen(file, "wb"))) {
fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
return -1;
}
#if ENDIANNESS_BIG
md5->bswap = NULL;
md5->bswap_w = 0;
#endif
md5->abcd[0] = 0x67452301;
md5->abcd[1] = 0xefcdab89;
md5->abcd[2] = 0x98badcfe;
md5->abcd[3] = 0x10325476;
md5->len = 0;
return 0;
}
static inline uint32_t leftrotate(const uint32_t x, const int c) {
return (x << c) | (x >> (32 - c));
}
#define F(i) do { \
a = b + leftrotate(a + ((b & c) | (~b & d)) + k[i + 0] + NE2LE_32(data[i + 0]), 7); \
d = a + leftrotate(d + ((a & b) | (~a & c)) + k[i + 1] + NE2LE_32(data[i + 1]), 12); \
c = d + leftrotate(c + ((d & a) | (~d & b)) + k[i + 2] + NE2LE_32(data[i + 2]), 17); \
b = c + leftrotate(b + ((c & d) | (~c & a)) + k[i + 3] + NE2LE_32(data[i + 3]), 22); \
} while (0)
#define G(i) do { \
a = b + leftrotate(a + ((d & b) | (~d & c)) + k[i + 0] + NE2LE_32(data[(i + 1) & 15]), 5); \
d = a + leftrotate(d + ((c & a) | (~c & b)) + k[i + 1] + NE2LE_32(data[(i + 6) & 15]), 9); \
c = d + leftrotate(c + ((b & d) | (~b & a)) + k[i + 2] + NE2LE_32(data[(i + 11) & 15]), 14); \
b = c + leftrotate(b + ((a & c) | (~a & d)) + k[i + 3] + NE2LE_32(data[(i + 0) & 15]), 20); \
} while (0)
#define H(i) do { \
a = b + leftrotate(a + (b ^ c ^ d) + k[i + 0] + NE2LE_32(data[( 5 - i) & 15]), 4); \
d = a + leftrotate(d + (a ^ b ^ c) + k[i + 1] + NE2LE_32(data[( 8 - i) & 15]), 11); \
c = d + leftrotate(c + (d ^ a ^ b) + k[i + 2] + NE2LE_32(data[(11 - i) & 15]), 16); \
b = c + leftrotate(b + (c ^ d ^ a) + k[i + 3] + NE2LE_32(data[(14 - i) & 15]), 23); \
} while (0)
#define I(i) do { \
a = b + leftrotate(a + (c ^ (b | ~d)) + k[i + 0] + NE2LE_32(data[( 0 - i) & 15]), 6); \
d = a + leftrotate(d + (b ^ (a | ~c)) + k[i + 1] + NE2LE_32(data[( 7 - i) & 15]), 10); \
c = d + leftrotate(c + (a ^ (d | ~b)) + k[i + 2] + NE2LE_32(data[(14 - i) & 15]), 15); \
b = c + leftrotate(b + (d ^ (c | ~a)) + k[i + 3] + NE2LE_32(data[( 5 - i) & 15]), 21); \
} while (0)
static void md5_body(MD5Context *const md5, const uint32_t *const data) {
uint32_t a = md5->abcd[0];
uint32_t b = md5->abcd[1];
uint32_t c = md5->abcd[2];
uint32_t d = md5->abcd[3];
F( 0); F( 4); F( 8); F(12);
G(16); G(20); G(24); G(28);
H(32); H(36); H(40); H(44);
I(48); I(52); I(56); I(60);
md5->abcd[0] += a;
md5->abcd[1] += b;
md5->abcd[2] += c;
md5->abcd[3] += d;
}
static void md5_update(MD5Context *const md5, const uint8_t *data, unsigned len) {
if (!len) return;
if (md5->len & 63) {
const unsigned tmp = umin(len, 64 - (md5->len & 63));
memcpy(&md5->data[md5->len & 63], data, tmp);
len -= tmp;
data += tmp;
md5->len += tmp;
if (!(md5->len & 63))
md5_body(md5, md5->data32);
}
while (len >= 64) {
memcpy(md5->data, data, 64);
md5_body(md5, md5->data32);
md5->len += 64;
data += 64;
len -= 64;
}
if (len) {
memcpy(md5->data, data, len);
md5->len += len;
}
}
static int md5_write(MD5Context *const md5, Dav1dPicture *const p) {
const int hbd = p->p.bpc > 8;
const int w = p->p.w, h = p->p.h;
uint8_t *yptr = p->data[0];
#if ENDIANNESS_BIG
if (hbd && (!md5->bswap || md5->bswap_w < p->p.w)) {
free(md5->bswap);
md5->bswap_w = 0;
md5->bswap = malloc(p->p.w << 1);
if (!md5->bswap) return -1;
md5->bswap_w = p->p.w;
}
#endif
for (int y = 0; y < h; y++) {
#if ENDIANNESS_BIG
if (hbd) {
for (int x = 0; x < w; x++) {
md5->bswap[2 * x + 1] = yptr[2 * x];
md5->bswap[2 * x] = yptr[2 * x + 1];
}
md5_update(md5, md5->bswap, w << hbd);
} else
#endif
md5_update(md5, yptr, w << hbd);
yptr += p->stride[0];
}
if (p->p.layout != DAV1D_PIXEL_LAYOUT_I400) {
const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
const int cw = (w + ss_hor) >> ss_hor;
const int ch = (h + ss_ver) >> ss_ver;
for (int pl = 1; pl <= 2; pl++) {
uint8_t *uvptr = p->data[pl];
for (int y = 0; y < ch; y++) {
#if ENDIANNESS_BIG
if (hbd) {
for (int x = 0; x < cw; x++){
md5->bswap[2 * x + 1] = uvptr[2 * x];
md5->bswap[2 * x] = uvptr[2 * x + 1];
}
md5_update(md5, md5->bswap, cw << hbd);
} else
#endif
md5_update(md5, uvptr, cw << hbd);
uvptr += p->stride[1];
}
}
}
dav1d_picture_unref(p);
return 0;
}
static void md5_finish(MD5Context *const md5) {
static const uint8_t bit[2] = { 0x80, 0x00 };
const uint64_t len = NE2LE_64(md5->len << 3);
md5_update(md5, &bit[0], 1);
while ((md5->len & 63) != 56)
md5_update(md5, &bit[1], 1);
md5_update(md5, (const uint8_t *) &len, 8);
}
static void md5_close(MD5Context *const md5) {
md5_finish(md5);
for (int i = 0; i < 4; i++)
fprintf(md5->f, "%2.2x%2.2x%2.2x%2.2x",
md5->abcd[i] & 0xff,
(md5->abcd[i] >> 8) & 0xff,
(md5->abcd[i] >> 16) & 0xff,
md5->abcd[i] >> 24);
fprintf(md5->f, "\n");
#if ENDIANNESS_BIG
free(md5->bswap);
md5->bswap_w = 0;
#endif
if (md5->f != stdout)
fclose(md5->f);
}
static int md5_verify(MD5Context *const md5, const char *md5_str) {
md5_finish(md5);
if (strlen(md5_str) < 32)
return -1;
uint32_t abcd[4] = { 0 };
char t[3] = { 0 };
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 32; j += 8) {
char *ignore;
memcpy(t, md5_str, 2);
md5_str += 2;
abcd[i] |= (uint32_t) strtoul(t, &ignore, 16) << j;
}
}
#if ENDIANNESS_BIG
free(md5->bswap);
md5->bswap_w = 0;
#endif
return !!memcmp(abcd, md5->abcd, sizeof(abcd));
}
const Muxer md5_muxer = {
.priv_data_size = sizeof(MD5Context),
.name = "md5",
.extension = "md5",
.write_header = md5_open,
.write_picture = md5_write,
.write_trailer = md5_close,
.verify = md5_verify,
};

View File

@ -1,52 +0,0 @@
/*
* 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.
*/
#ifndef DAV1D_OUTPUT_MUXER_H
#define DAV1D_OUTPUT_MUXER_H
#include "picture.h"
typedef struct MuxerPriv MuxerPriv;
typedef struct Muxer {
int priv_data_size;
const char *name;
const char *extension;
int (*write_header)(MuxerPriv *ctx, const char *filename,
const Dav1dPictureParameters *p, const unsigned fps[2]);
int (*write_picture)(MuxerPriv *ctx, Dav1dPicture *p);
void (*write_trailer)(MuxerPriv *ctx);
/**
* Verifies the muxed data (for example in the md5 muxer). Replaces write_trailer.
*
* @param hash_string Muxer specific reference value.
*
* @return 0 on success.
*/
int (*verify)(MuxerPriv *ctx, const char *hash_string);
} Muxer;
#endif /* DAV1D_OUTPUT_MUXER_H */

View File

@ -1,44 +0,0 @@
/*
* 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 "config.h"
#include "output/muxer.h"
typedef struct MuxerPriv NullOutputContext;
static int null_write(NullOutputContext *const c, Dav1dPicture *const p) {
dav1d_picture_unref(p);
return 0;
}
const Muxer null_muxer = {
.priv_data_size = 0,
.name = "null",
.extension = "null",
.write_picture = null_write,
};

View File

@ -1,150 +0,0 @@
/*
* 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 "config.h"
#include "cli_config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common/attributes.h"
#include "output/output.h"
#include "output/muxer.h"
struct MuxerContext {
MuxerPriv *data;
const Muxer *impl;
};
extern const Muxer null_muxer;
extern const Muxer md5_muxer;
extern const Muxer xxh3_muxer;
extern const Muxer yuv_muxer;
extern const Muxer y4m2_muxer;
static const Muxer *muxers[] = {
&null_muxer,
&md5_muxer,
#if HAVE_XXHASH_H
&xxh3_muxer,
#endif
&yuv_muxer,
&y4m2_muxer,
NULL
};
static const char *find_extension(const char *const f) {
const size_t l = strlen(f);
if (l == 0) return NULL;
const char *const end = &f[l - 1], *step = end;
while ((*step >= 'a' && *step <= 'z') ||
(*step >= 'A' && *step <= 'Z') ||
(*step >= '0' && *step <= '9'))
{
step--;
}
return (step < end && step > f && *step == '.' && step[-1] != '/') ?
&step[1] : NULL;
}
int output_open(MuxerContext **const c_out,
const char *const name, const char *const filename,
const Dav1dPictureParameters *const p, const unsigned fps[2])
{
const Muxer *impl;
MuxerContext *c;
unsigned i;
int res;
if (name) {
for (i = 0; muxers[i]; i++) {
if (!strcmp(muxers[i]->name, name)) {
impl = muxers[i];
break;
}
}
if (!muxers[i]) {
fprintf(stderr, "Failed to find muxer named \"%s\"\n", name);
return DAV1D_ERR(ENOPROTOOPT);
}
} else if (!strcmp(filename, "/dev/null")) {
impl = muxers[0];
} else {
const char *const ext = find_extension(filename);
if (!ext) {
fprintf(stderr, "No extension found for file %s\n", filename);
return -1;
}
for (i = 0; muxers[i]; i++) {
if (!strcmp(muxers[i]->extension, ext)) {
impl = muxers[i];
break;
}
}
if (!muxers[i]) {
fprintf(stderr, "Failed to find muxer for extension \"%s\"\n", ext);
return DAV1D_ERR(ENOPROTOOPT);
}
}
if (!(c = malloc(sizeof(MuxerContext) + impl->priv_data_size))) {
fprintf(stderr, "Failed to allocate memory\n");
return DAV1D_ERR(ENOMEM);
}
c->impl = impl;
c->data = (MuxerPriv *) &c[1];
if (impl->write_header && (res = impl->write_header(c->data, filename, p, fps)) < 0) {
free(c);
return res;
}
*c_out = c;
return 0;
}
int output_write(MuxerContext *const ctx, Dav1dPicture *const p) {
const int res = ctx->impl->write_picture(ctx->data, p);
return res < 0 ? res : 0;
}
void output_close(MuxerContext *const ctx) {
if (ctx->impl->write_trailer)
ctx->impl->write_trailer(ctx->data);
free(ctx);
}
int output_verify(MuxerContext *const ctx, const char *const md5_str) {
const int res = ctx->impl->verify ?
ctx->impl->verify(ctx->data, md5_str) : 0;
free(ctx);
return res;
}

View File

@ -1,48 +0,0 @@
/*
* 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.
*/
#ifndef DAV1D_OUTPUT_OUTPUT_H
#define DAV1D_OUTPUT_OUTPUT_H
#include "picture.h"
typedef struct MuxerContext MuxerContext;
int output_open(MuxerContext **c, const char *name, const char *filename,
const Dav1dPictureParameters *p, const unsigned fps[2]);
int output_write(MuxerContext *ctx, Dav1dPicture *pic);
void output_close(MuxerContext *ctx);
/**
* Verifies the muxed data (for example in the md5 muxer). Replaces output_close.
*
* @param hash_string Muxer specific reference value.
*
* @return 0 on success.
*/
int output_verify(MuxerContext *ctx, const char *hash_string);
#endif /* DAV1D_OUTPUT_OUTPUT_H */

View File

@ -1,142 +0,0 @@
/*
* Copyright © 2018-2021, VideoLAN and dav1d authors
* Copyright © 2018-2021, 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define XXH_INLINE_ALL
#include "xxhash.h"
#include "output/muxer.h"
typedef struct MuxerPriv {
XXH3_state_t* state;
FILE *f;
} xxh3Context;
static int xxh3_open(xxh3Context *const xxh3, const char *const file,
const Dav1dPictureParameters *const p,
const unsigned fps[2])
{
xxh3->state = XXH3_createState();
if (!xxh3->state) return DAV1D_ERR(ENOMEM);
XXH_errorcode err = XXH3_128bits_reset(xxh3->state);
if (err != XXH_OK) {
XXH3_freeState(xxh3->state);
xxh3->state = NULL;
return DAV1D_ERR(ENOMEM);
}
if (!strcmp(file, "-")) {
xxh3->f = stdout;
} else if (!(xxh3->f = fopen(file, "wb"))) {
XXH3_freeState(xxh3->state);
xxh3->state = NULL;
fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
return -1;
}
return 0;
}
static int xxh3_write(xxh3Context *const xxh3, Dav1dPicture *const p) {
const int hbd = p->p.bpc > 8;
const int w = p->p.w, h = p->p.h;
uint8_t *yptr = p->data[0];
for (int y = 0; y < h; y++) {
XXH3_128bits_update(xxh3->state, yptr, w << hbd);
yptr += p->stride[0];
}
if (p->p.layout != DAV1D_PIXEL_LAYOUT_I400) {
const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
const int cw = (w + ss_hor) >> ss_hor;
const int ch = (h + ss_ver) >> ss_ver;
for (int pl = 1; pl <= 2; pl++) {
uint8_t *uvptr = p->data[pl];
for (int y = 0; y < ch; y++) {
XXH3_128bits_update(xxh3->state, uvptr, cw << hbd);
uvptr += p->stride[1];
}
}
}
dav1d_picture_unref(p);
return 0;
}
static void xxh3_close(xxh3Context *const xxh3) {
XXH128_hash_t hash = XXH3_128bits_digest(xxh3->state);
XXH3_freeState(xxh3->state);
XXH128_canonical_t c;
XXH128_canonicalFromHash(&c, hash);
for (int i = 0; i < 16; i++)
fprintf(xxh3->f, "%2.2x", c.digest[i]);
fprintf(xxh3->f, "\n");
if (xxh3->f != stdout)
fclose(xxh3->f);
}
static int xxh3_verify(xxh3Context *const xxh3, const char * xxh3_str) {
XXH128_hash_t hash = XXH3_128bits_digest(xxh3->state);
XXH3_freeState(xxh3->state);
if (strlen(xxh3_str) < 32)
return -1;
XXH128_canonical_t c;
char t[3] = { 0 };
for (int i = 0; i < 16; i++) {
char *ignore;
memcpy(t, xxh3_str, 2);
xxh3_str += 2;
c.digest[i] = (unsigned char) strtoul(t, &ignore, 16);
}
XXH128_hash_t verify = XXH128_hashFromCanonical(&c);
return !XXH128_isEqual(hash, verify);
}
const Muxer xxh3_muxer = {
.priv_data_size = sizeof(xxh3Context),
.name = "xxh3",
.extension = "xxh3",
.write_header = xxh3_open,
.write_picture = xxh3_write,
.write_trailer = xxh3_close,
.verify = xxh3_verify,
};

View File

@ -1,151 +0,0 @@
/*
* 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 "config.h"
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "output/muxer.h"
typedef struct MuxerPriv {
FILE *f;
int first;
unsigned fps[2];
} Y4m2OutputContext;
static int y4m2_open(Y4m2OutputContext *const c, const char *const file,
const Dav1dPictureParameters *p, const unsigned fps[2])
{
if (!strcmp(file, "-")) {
c->f = stdout;
} else if (!(c->f = fopen(file, "wb"))) {
fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
return -1;
}
c->first = 1;
c->fps[0] = fps[0];
c->fps[1] = fps[1];
return 0;
}
static int write_header(Y4m2OutputContext *const c, const Dav1dPicture *const p) {
static const char *const ss_names[][3] = {
[DAV1D_PIXEL_LAYOUT_I400] = { "mono", "mono10", "mono12" },
[DAV1D_PIXEL_LAYOUT_I420] = { NULL, "420p10", "420p12" },
[DAV1D_PIXEL_LAYOUT_I422] = { "422", "422p10", "422p12" },
[DAV1D_PIXEL_LAYOUT_I444] = { "444", "444p10", "444p12" }
};
static const char *const chr_names_8bpc_i420[] = {
[DAV1D_CHR_UNKNOWN] = "420jpeg",
[DAV1D_CHR_VERTICAL] = "420mpeg2",
[DAV1D_CHR_COLOCATED] = "420"
};
const char *const ss_name =
p->p.layout == DAV1D_PIXEL_LAYOUT_I420 && p->p.bpc == 8 ?
chr_names_8bpc_i420[p->seq_hdr->chr > 2 ? DAV1D_CHR_UNKNOWN : p->seq_hdr->chr] :
ss_names[p->p.layout][p->seq_hdr->hbd];
const unsigned fw = p->p.w;
const unsigned fh = p->p.h;
uint64_t aw = (uint64_t)fh * p->frame_hdr->render_width;
uint64_t ah = (uint64_t)fw * p->frame_hdr->render_height;
uint64_t gcd = ah;
for (uint64_t a = aw, b; (b = a % gcd); a = gcd, gcd = b);
aw /= gcd;
ah /= gcd;
fprintf(c->f, "YUV4MPEG2 W%u H%u F%u:%u Ip A%"PRIu64":%"PRIu64" C%s\n",
fw, fh, c->fps[0], c->fps[1], aw, ah, ss_name);
return 0;
}
static int y4m2_write(Y4m2OutputContext *const c, Dav1dPicture *const p) {
if (c->first) {
c->first = 0;
const int res = write_header(c, p);
if (res < 0) return res;
}
fprintf(c->f, "FRAME\n");
uint8_t *ptr;
const int hbd = p->p.bpc > 8;
ptr = p->data[0];
for (int y = 0; y < p->p.h; y++) {
if (fwrite(ptr, p->p.w << hbd, 1, c->f) != 1)
goto error;
ptr += p->stride[0];
}
if (p->p.layout != DAV1D_PIXEL_LAYOUT_I400) {
// u/v
const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
const int cw = (p->p.w + ss_hor) >> ss_hor;
const int ch = (p->p.h + ss_ver) >> ss_ver;
for (int pl = 1; pl <= 2; pl++) {
ptr = p->data[pl];
for (int y = 0; y < ch; y++) {
if (fwrite(ptr, cw << hbd, 1, c->f) != 1)
goto error;
ptr += p->stride[1];
}
}
}
dav1d_picture_unref(p);
return 0;
error:
dav1d_picture_unref(p);
fprintf(stderr, "Failed to write frame data: %s\n", strerror(errno));
return -1;
}
static void y4m2_close(Y4m2OutputContext *const c) {
if (c->f != stdout)
fclose(c->f);
}
const Muxer y4m2_muxer = {
.priv_data_size = sizeof(Y4m2OutputContext),
.name = "yuv4mpeg2",
.extension = "y4m",
.write_header = y4m2_open,
.write_picture = y4m2_write,
.write_trailer = y4m2_close,
};

View File

@ -1,104 +0,0 @@
/*
* 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 "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "output/muxer.h"
typedef struct MuxerPriv {
FILE *f;
} YuvOutputContext;
static int yuv_open(YuvOutputContext *const c, const char *const file,
const Dav1dPictureParameters *const p,
const unsigned fps[2])
{
if (!strcmp(file, "-")) {
c->f = stdout;
} else if (!(c->f = fopen(file, "wb"))) {
fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
return -1;
}
return 0;
}
static int yuv_write(YuvOutputContext *const c, Dav1dPicture *const p) {
uint8_t *ptr;
const int hbd = p->p.bpc > 8;
ptr = p->data[0];
for (int y = 0; y < p->p.h; y++) {
if (fwrite(ptr, p->p.w << hbd, 1, c->f) != 1)
goto error;
ptr += p->stride[0];
}
if (p->p.layout != DAV1D_PIXEL_LAYOUT_I400) {
// u/v
const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
const int cw = (p->p.w + ss_hor) >> ss_hor;
const int ch = (p->p.h + ss_ver) >> ss_ver;
for (int pl = 1; pl <= 2; pl++) {
ptr = p->data[pl];
for (int y = 0; y < ch; y++) {
if (fwrite(ptr, cw << hbd, 1, c->f) != 1)
goto error;
ptr += p->stride[1];
}
}
}
dav1d_picture_unref(p);
return 0;
error:
dav1d_picture_unref(p);
fprintf(stderr, "Failed to write frame data: %s\n", strerror(errno));
return -1;
}
static void yuv_close(YuvOutputContext *const c) {
if (c->f != stdout)
fclose(c->f);
}
const Muxer yuv_muxer = {
.priv_data_size = sizeof(YuvOutputContext),
.name = "yuv",
.extension = "yuv",
.write_header = yuv_open,
.write_picture = yuv_write,
.write_trailer = yuv_close,
};