Commit 02606969 authored by James Almer's avatar James Almer

implement a decoupled decode API

parent ec53ec6d
......@@ -62,8 +62,8 @@ DAV1D_API void dav1d_default_settings(Dav1dSettings *s);
/**
* Allocate and open a decoder instance.
*
* @param c_out The decoder instance to open. To be used in iterative calls to
* dav1d_decode(). *c_out will be set to the allocated context.
* @param c_out The decoder instance to open. *c_out will be set to the
* allocated context.
* @param s Input settings context.
*
* @note The context must be freed using dav1d_close() when decoding is
......@@ -74,25 +74,40 @@ DAV1D_API void dav1d_default_settings(Dav1dSettings *s);
DAV1D_API int dav1d_open(Dav1dContext **c_out, const Dav1dSettings *s);
/**
* Decode one frame.
* Feed bitstream data to the decoder.
*
* @param c Input decoder instance.
* @param in Input bitstream data. On success, ownership of the reference is
* passed to the library.
*
* @return
* 0: Success, and the data was consumed.
* -EAGAIN: The data can't be consumed. dav1d_get_picture() should be called
* to get one or more frames before the function can consume new
* data.
* other negative errno codes: Error during decoding or because of invalid
* passed-in arguments.
*/
DAV1D_API int dav1d_send_data(Dav1dContext *c, Dav1dData *in);
/**
* Return a decoded picture.
*
* @param c Input decoder instance.
* @param in Input bitstream data. On success, the caller retains ownership of
* the input reference if the data was not fully consumed.
* @param out Output frame. The caller assumes ownership of the returned
* reference.
*
* @return
* 0: Success, and a frame is returned.
* -EAGAIN: Not enough data to output a frame. The fuction should be called
* again with new input.
* -EAGAIN: Not enough data to output a frame. dav1d_send_data() should be
* called with new input.
* other negative errno codes: Error during decoding or because of invalid
* passed-in arguments.
*
* @note To flush the decoder (i.e. all input is finished), feed it NULL input
* data until it returns -EAGAIN.
* @note To drain buffered frames from the decoder (i.e. on end of stream),
* call this function until it returns -EAGAIN.
*/
DAV1D_API int dav1d_decode(Dav1dContext *c, Dav1dData *in, Dav1dPicture *out);
DAV1D_API int dav1d_get_picture(Dav1dContext *c, Dav1dPicture *out);
/**
* Close a decoder instance and free all associated memory.
......
......@@ -35,6 +35,7 @@
#include "common/validate.h"
#include "src/data.h"
#include "src/ref.h"
uint8_t * dav1d_data_create(Dav1dData *const buf, const size_t sz) {
......@@ -64,6 +65,18 @@ int dav1d_data_wrap(Dav1dData *const buf, const uint8_t *const ptr, const size_t
return 0;
}
void dav1d_data_move_ref(Dav1dData *const dst, Dav1dData *const src) {
validate_input(dst != NULL);
validate_input(dst->data == NULL);
validate_input(src != NULL);
if (src->ref)
validate_input(src->data != NULL);
*dst = *src;
memset(src, 0, sizeof(*src));
}
void dav1d_data_unref(Dav1dData *const buf) {
validate_input(buf != NULL);
......
/*
* 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_SRC_DATA_H__
#define __DAV1D_SRC_DATA_H__
#include "dav1d/data.h"
/**
* Move a data reference.
*/
void dav1d_data_move_ref(Dav1dData *dst, Dav1dData *src);
#endif /* __DAV1D_SRC_DATA_H__ */
......@@ -40,6 +40,7 @@ typedef struct Dav1dTileContext Dav1dTileContext;
#include "src/cdef.h"
#include "src/cdf.h"
#include "src/data.h"
#include "src/env.h"
#include "src/intra_edge.h"
#include "src/ipred.h"
......@@ -80,6 +81,7 @@ struct Dav1dContext {
Av1FrameHeader frame_hdr; // FIXME make ref?
// decoded output picture queue
Dav1dData in;
Dav1dPicture out;
struct {
Dav1dThreadPicture *out_delayed;
......
......@@ -157,15 +157,28 @@ error:
return -ENOMEM;
}
int dav1d_decode(Dav1dContext *const c,
Dav1dData *const in, Dav1dPicture *const out)
int dav1d_send_data(Dav1dContext *const c, Dav1dData *const in)
{
validate_input_or_ret(c != NULL, -EINVAL);
validate_input_or_ret(in != NULL, -EINVAL);
validate_input_or_ret(in->data == NULL || in->sz, -EINVAL);
if (c->in.data)
return -EAGAIN;
dav1d_data_move_ref(&c->in, in);
return 0;
}
int dav1d_get_picture(Dav1dContext *const c, Dav1dPicture *const out)
{
int res;
validate_input_or_ret(c != NULL, -EINVAL);
validate_input_or_ret(out != NULL, -EINVAL);
if (!in) {
Dav1dData *const in = &c->in;
if (!in->data) {
if (c->n_fc == 1) return -EAGAIN;
// flush
......@@ -198,8 +211,10 @@ int dav1d_decode(Dav1dContext *const c,
}
while (in->sz > 0) {
if ((res = dav1d_parse_obus(c, in)) < 0)
if ((res = dav1d_parse_obus(c, in)) < 0) {
dav1d_data_unref(in);
return res;
}
assert((size_t)res <= in->sz);
in->sz -= res;
......@@ -220,6 +235,8 @@ int dav1d_decode(Dav1dContext *const c,
}
void dav1d_flush(Dav1dContext *const c) {
dav1d_data_unref(&c->in);
if (c->n_fc == 1) return;
for (unsigned n = 0; n < c->n_fc; n++)
......@@ -298,6 +315,7 @@ void dav1d_close(Dav1dContext **const c_out) {
dav1d_free_aligned(f->lf.lr_lpf_line);
}
dav1d_free_aligned(c->fc);
dav1d_data_unref(&c->in);
if (c->n_fc > 1) {
for (unsigned n = 0; n < c->n_fc; n++)
if (c->frame_thread.out_delayed[n].p.data[0])
......
......@@ -97,8 +97,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
ptr += frame_size;
do {
if ((err = dav1d_send_data(ctx, &buf)) < 0) {
if (err != -EAGAIN)
break;
}
memset(&pic, 0, sizeof(pic));
err = dav1d_decode(ctx, &buf, &pic);
err = dav1d_get_picture(ctx, &pic);
if (err == 0) {
dav1d_picture_unref(&pic);
} else if (err != -EAGAIN) {
......@@ -112,7 +116,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
do {
memset(&pic, 0, sizeof(pic));
err = dav1d_decode(ctx, NULL, &pic);
err = dav1d_get_picture(ctx, &pic);
if (err == 0)
dav1d_picture_unref(&pic);
} while (err == 0);
......
......@@ -108,7 +108,15 @@ int main(const int argc, char *const *const argv) {
do {
memset(&p, 0, sizeof(p));
if ((res = dav1d_decode(c, &data, &p)) < 0) {
if ((res = dav1d_send_data(c, &data)) < 0) {
if (res != -EAGAIN) {
fprintf(stderr, "Error decoding frame: %s\n",
strerror(-res));
break;
}
}
if ((res = dav1d_get_picture(c, &p)) < 0) {
if (res != -EAGAIN) {
fprintf(stderr, "Error decoding frame: %s\n",
strerror(-res));
......@@ -139,7 +147,7 @@ int main(const int argc, char *const *const argv) {
// flush
if (res == 0) while (!cli_settings.limit || n_out < cli_settings.limit) {
if ((res = dav1d_decode(c, NULL, &p)) < 0) {
if ((res = dav1d_get_picture(c, &p)) < 0) {
if (res != -EAGAIN) {
fprintf(stderr, "Error decoding frame: %s\n",
strerror(-res));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment