Skip to content

Commit

Permalink
fn dav1d_parse_sequence_header: backport simplification from dav1d …
Browse files Browse the repository at this point in the history
…1.2.1 (#897)

Avoid creating an entire decoder instance to just parse a sequence
header.
  • Loading branch information
kkysen authored Apr 11, 2024
2 parents 3b02e8d + eef4db8 commit 10404a7
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 165 deletions.
8 changes: 8 additions & 0 deletions src/getbits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,12 @@ impl<'a> GetBits<'a> {
pub const fn pos(&self) -> usize {
self.index * u8::BITS as usize - self.bits_left as usize
}

pub const fn byte_pos(&self) -> usize {
self.index
}

pub const fn has_pending_bits(&self) -> bool {
self.state != 0 || self.bits_left != 0
}
}
55 changes: 1 addition & 54 deletions src/lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,59 +297,6 @@ COLD int dav1d_open(Dav1dContext **const c_out, const Dav1dSettings *const s) {
return 0;
}

static void dummy_free(const uint8_t *const data, void *const user_data) {
assert(data && !user_data);
}

static int dav1d_parse_sequence_header_error(const int res, Dav1dContext *c, Dav1dData *const buf) {
dav1d_data_unref_internal(buf);
dav1d_close(&c);

return res;
}

int dav1d_parse_sequence_header(Dav1dSequenceHeader *const out,
const uint8_t *const ptr, const size_t sz)
{
Dav1dData buf = { 0 };
int res;

validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL));

Dav1dSettings s;
dav1d_default_settings(&s);
s.n_threads = 1;
s.logger.callback = NULL;

Dav1dContext *c;
res = dav1d_open(&c, &s);
if (res < 0) return res;

if (ptr) {
res = dav1d_data_wrap_internal(&buf, ptr, sz, dummy_free, NULL);
if (res < 0) return dav1d_parse_sequence_header_error(res, c, &buf);
}

while (buf.sz > 0) {
res = dav1d_parse_obus(c, &buf, 1);
if (res < 0) return dav1d_parse_sequence_header_error(res, c, &buf);

assert((size_t)res <= buf.sz);
buf.sz -= res;
buf.data += res;
}

if (!c->seq_hdr) {
res = DAV1D_ERR(ENOENT);
return dav1d_parse_sequence_header_error(res, c, &buf);
}

memcpy(out, c->seq_hdr, sizeof(*out));

res = 0;
return dav1d_parse_sequence_header_error(res, c, &buf);
}

static int has_grain(const Dav1dPicture *const pic)
{
const Dav1dFilmGrainData *fgdata = &pic->frame_hdr->film_grain.data;
Expand Down Expand Up @@ -467,7 +414,7 @@ static int gen_picture(Dav1dContext *const c)
return 0;

while (in->sz > 0) {
res = dav1d_parse_obus(c, in, 0);
res = dav1d_parse_obus(c, in);
if (res < 0) {
dav1d_data_unref_internal(in);
} else {
Expand Down
61 changes: 5 additions & 56 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ use crate::include::dav1d::dav1d::Dav1dSettings;
use crate::include::dav1d::dav1d::Rav1dDecodeFrameType;
use crate::include::dav1d::dav1d::Rav1dInloopFilterType;
use crate::include::dav1d::dav1d::Rav1dSettings;
use crate::include::dav1d::headers::DRav1d;
use crate::include::dav1d::headers::Dav1dSequenceHeader;
use crate::include::dav1d::headers::Rav1dFilmGrainData;
use crate::include::dav1d::headers::Rav1dSequenceHeader;
use crate::include::dav1d::picture::Dav1dPicture;
use crate::include::dav1d::picture::Rav1dPicture;
use crate::src::cpu::rav1d_init_cpu;
Expand All @@ -24,7 +22,6 @@ use crate::src::error::Dav1dResult;
use crate::src::error::Rav1dError::EGeneric;
use crate::src::error::Rav1dError::EAGAIN;
use crate::src::error::Rav1dError::EINVAL;
use crate::src::error::Rav1dError::ENOENT;
use crate::src::error::Rav1dError::ENOMEM;
use crate::src::error::Rav1dResult;
use crate::src::fg_apply;
Expand All @@ -42,6 +39,7 @@ use crate::src::mem::rav1d_freep_aligned;
use crate::src::mem::rav1d_mem_pool_end;
use crate::src::mem::rav1d_mem_pool_init;
use crate::src::obu::rav1d_parse_obus;
use crate::src::obu::rav1d_parse_sequence_header;
use crate::src::picture::dav1d_default_picture_alloc;
use crate::src::picture::dav1d_default_picture_release;
use crate::src::picture::rav1d_picture_alloc_copy;
Expand Down Expand Up @@ -355,57 +353,6 @@ pub unsafe extern "C" fn dav1d_open(
.into()
}

unsafe extern "C" fn dummy_free(data: *const u8, user_data: *mut c_void) {
if !(!data.is_null() && user_data.is_null()) {
unreachable!();
}
}

pub(crate) unsafe fn rav1d_parse_sequence_header(
ptr: *const u8,
sz: usize,
) -> Rav1dResult<DRav1d<Rav1dSequenceHeader, Dav1dSequenceHeader>> {
let s = Rav1dSettings {
n_threads: 1,
logger: None,
..Default::default()
};
let mut c: *mut Rav1dContext = 0 as *mut Rav1dContext;
rav1d_open(&mut c, &s)?;
|| -> Rav1dResult<DRav1d<Rav1dSequenceHeader, Dav1dSequenceHeader>> {
let Rav1dData {
mut data,
m: mut props,
} = match NonNull::new(ptr.cast_mut()) {
None => Default::default(),
Some(ptr) => Rav1dData::wrap(
slice::from_raw_parts(ptr.as_ptr(), sz).into(),
Some(dummy_free),
ptr::null_mut(),
)?,
};
if let Some(data) = &mut data {
while !data.is_empty() {
let len = rav1d_parse_obus(&mut *c, data, &mut props, true)?;
data.slice_in_place(len..);
}
}

if (*c).seq_hdr.is_none() {
return Err(ENOENT);
}

(*c).seq_hdr
.take()
.and_then(Arc::into_inner)
.map(Ok)
.unwrap()
}()
.inspect_err(|_| {
rav1d_close(&mut c);
})
}

#[no_mangle]
pub unsafe extern "C" fn dav1d_parse_sequence_header(
out: *mut Dav1dSequenceHeader,
Expand All @@ -414,7 +361,9 @@ pub unsafe extern "C" fn dav1d_parse_sequence_header(
) -> Dav1dResult {
(|| {
validate_input!((!out.is_null(), EINVAL))?;
let seq_hdr = rav1d_parse_sequence_header(ptr, sz)?;
validate_input!((!ptr.is_null(), EINVAL))?;
validate_input!((sz > 0, EINVAL))?;
let seq_hdr = rav1d_parse_sequence_header(slice::from_raw_parts(ptr, sz))?;
out.write(seq_hdr.dav1d);
Ok(())
})()
Expand Down Expand Up @@ -564,7 +513,7 @@ unsafe fn gen_picture(c: &mut Rav1dContext) -> Rav1dResult {
} = mem::take(&mut c.in_0);
let Some(mut r#in) = r#in else { return Ok(()) };
while !r#in.is_empty() {
let len = rav1d_parse_obus(c, &r#in, &props, false);
let len = rav1d_parse_obus(c, &r#in, &props);
if let Ok(len) = len {
r#in.slice_in_place(len..);
}
Expand Down
90 changes: 65 additions & 25 deletions src/obu.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

#include "common/frame.h"
#include "common/intops.h"
#include "common/validate.h"

#include "src/decode.h"
#include "src/getbits.h"
Expand All @@ -44,13 +45,13 @@
#include "src/ref.h"
#include "src/thread_task.h"

static int parse_seq_hdr_error(Dav1dContext *const c) {
dav1d_log(c, "Error parsing sequence header\n");
static int parse_seq_hdr_error(void) {
return DAV1D_ERR(EINVAL);
}

static int parse_seq_hdr(Dav1dContext *const c, GetBits *const gb,
Dav1dSequenceHeader *const hdr)
static NOINLINE int parse_seq_hdr(Dav1dSequenceHeader *const hdr,
GetBits *const gb,
const int strict_std_compliance)
{
#define DEBUG_SEQ_HDR 0

Expand All @@ -60,15 +61,15 @@ static int parse_seq_hdr(Dav1dContext *const c, GetBits *const gb,

memset(hdr, 0, sizeof(*hdr));
hdr->profile = dav1d_get_bits(gb, 3);
if (hdr->profile > 2) return parse_seq_hdr_error(c);
if (hdr->profile > 2) return parse_seq_hdr_error();
#if DEBUG_SEQ_HDR
printf("SEQHDR: post-profile: off=%u\n",
dav1d_get_bits_pos(gb) - init_bit_pos);
#endif

hdr->still_picture = dav1d_get_bit(gb);
hdr->reduced_still_picture_header = dav1d_get_bit(gb);
if (hdr->reduced_still_picture_header && !hdr->still_picture) return parse_seq_hdr_error(c);
if (hdr->reduced_still_picture_header && !hdr->still_picture) return parse_seq_hdr_error();
#if DEBUG_SEQ_HDR
printf("SEQHDR: post-stillpicture_flags: off=%u\n",
dav1d_get_bits_pos(gb) - init_bit_pos);
Expand All @@ -84,22 +85,22 @@ static int parse_seq_hdr(Dav1dContext *const c, GetBits *const gb,
if (hdr->timing_info_present) {
hdr->num_units_in_tick = dav1d_get_bits(gb, 32);
hdr->time_scale = dav1d_get_bits(gb, 32);
if (c->strict_std_compliance && (!hdr->num_units_in_tick || !hdr->time_scale))
return parse_seq_hdr_error(c);
if (strict_std_compliance && (!hdr->num_units_in_tick || !hdr->time_scale))
return parse_seq_hdr_error();
hdr->equal_picture_interval = dav1d_get_bit(gb);
if (hdr->equal_picture_interval) {
const unsigned num_ticks_per_picture = dav1d_get_vlc(gb);
if (num_ticks_per_picture == 0xFFFFFFFFU)
return parse_seq_hdr_error(c);
return parse_seq_hdr_error();
hdr->num_ticks_per_picture = num_ticks_per_picture + 1;
}

hdr->decoder_model_info_present = dav1d_get_bit(gb);
if (hdr->decoder_model_info_present) {
hdr->encoder_decoder_buffer_delay_length = dav1d_get_bits(gb, 5) + 1;
hdr->num_units_in_decoding_tick = dav1d_get_bits(gb, 32);
if (c->strict_std_compliance && !hdr->num_units_in_decoding_tick)
return parse_seq_hdr_error(c);
if (strict_std_compliance && !hdr->num_units_in_decoding_tick)
return parse_seq_hdr_error();
hdr->buffer_removal_delay_length = dav1d_get_bits(gb, 5) + 1;
hdr->frame_presentation_delay_length = dav1d_get_bits(gb, 5) + 1;
}
Expand All @@ -116,7 +117,7 @@ static int parse_seq_hdr(Dav1dContext *const c, GetBits *const gb,
&hdr->operating_points[i];
op->idc = dav1d_get_bits(gb, 12);
if (op->idc && (!(op->idc & 0xff) || !(op->idc & 0xf00)))
return parse_seq_hdr_error(c);
return parse_seq_hdr_error();
op->major_level = 2 + dav1d_get_bits(gb, 3);
op->minor_level = dav1d_get_bits(gb, 2);
if (op->major_level > 3)
Expand Down Expand Up @@ -144,12 +145,6 @@ static int parse_seq_hdr(Dav1dContext *const c, GetBits *const gb,
#endif
}

const int op_idx =
c->operating_point < hdr->num_operating_points ? c->operating_point : 0;
c->operating_point_idc = hdr->operating_points[op_idx].idc;
const unsigned spatial_mask = c->operating_point_idc >> 8;
c->max_spatial_id = spatial_mask ? ulog2(spatial_mask) : 0;

hdr->width_n_bits = dav1d_get_bits(gb, 4) + 1;
hdr->height_n_bits = dav1d_get_bits(gb, 4) + 1;
hdr->max_width = dav1d_get_bits(gb, hdr->width_n_bits) + 1;
Expand Down Expand Up @@ -231,7 +226,7 @@ static int parse_seq_hdr(Dav1dContext *const c, GetBits *const gb,
hdr->layout = DAV1D_PIXEL_LAYOUT_I444;
hdr->color_range = 1;
if (hdr->profile != 1 && !(hdr->profile == 2 && hdr->hbd == 2))
return parse_seq_hdr_error(c);
return parse_seq_hdr_error();
} else {
hdr->color_range = dav1d_get_bit(gb);
switch (hdr->profile) {
Expand All @@ -256,10 +251,10 @@ static int parse_seq_hdr(Dav1dContext *const c, GetBits *const gb,
hdr->chr = (hdr->ss_hor & hdr->ss_ver) ?
dav1d_get_bits(gb, 2) : DAV1D_CHR_UNKNOWN;
}
if (c->strict_std_compliance &&
if (strict_std_compliance &&
hdr->mtrx == DAV1D_MC_IDENTITY && hdr->layout != DAV1D_PIXEL_LAYOUT_I444)
{
return parse_seq_hdr_error(c);
return parse_seq_hdr_error();
}
if (!hdr->monochrome)
hdr->separate_uv_delta_q = dav1d_get_bit(gb);
Expand All @@ -283,6 +278,45 @@ static int parse_seq_hdr(Dav1dContext *const c, GetBits *const gb,
return 0;
}

int dav1d_parse_sequence_header(Dav1dSequenceHeader *const out,
const uint8_t *const ptr, const size_t sz)
{
validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL));
validate_input_or_ret(ptr != NULL, DAV1D_ERR(EINVAL));
validate_input_or_ret(sz > 0, DAV1D_ERR(EINVAL));

GetBits gb;
dav1d_init_get_bits(&gb, ptr, sz);
int res = DAV1D_ERR(ENOENT);

do {
dav1d_get_bit(&gb); // obu_forbidden_bit
const enum Dav1dObuType type = dav1d_get_bits(&gb, 4);
const int has_extension = dav1d_get_bit(&gb);
const int has_length_field = dav1d_get_bit(&gb);
dav1d_get_bits(&gb, 1 + 8 * has_extension); // ignore

const uint8_t *obu_end = gb.ptr_end;
if (has_length_field) {
const size_t len = dav1d_get_uleb128(&gb);
if (len > (size_t)(obu_end - gb.ptr)) return DAV1D_ERR(EINVAL);
obu_end = gb.ptr + len;
}

if (type == DAV1D_OBU_SEQ_HDR) {
if ((res = parse_seq_hdr(out, &gb, 0)) < 0) return res;
if (gb.ptr > obu_end) return DAV1D_ERR(EINVAL);
dav1d_bytealign_get_bits(&gb);
}

if (gb.error) return DAV1D_ERR(EINVAL);
assert(gb.state == 0 && gb.bits_left == 0);
gb.ptr = obu_end;
} while (gb.ptr < gb.ptr_end);

return res;
}

static int read_frame_size(Dav1dContext *const c, GetBits *const gb,
const int use_ref)
{
Expand Down Expand Up @@ -1208,7 +1242,7 @@ static int dav1d_parse_obus_skip(Dav1dContext *const c, const unsigned len, cons
return len + init_byte_pos;
}

int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int global) {
int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in) {
GetBits gb;
int res;

Expand Down Expand Up @@ -1266,14 +1300,22 @@ int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int globa
sizeof(Dav1dSequenceHeader));
if (!ref) return DAV1D_ERR(ENOMEM);
Dav1dSequenceHeader *seq_hdr = ref->data;
if ((res = parse_seq_hdr(c, &gb, seq_hdr)) < 0) {
if ((res = parse_seq_hdr(seq_hdr, &gb, c->strict_std_compliance)) < 0) {
dav1d_log(c, "Error parsing sequence header\n");
dav1d_ref_dec(&ref);
return dav1d_parse_obus_error(c, in);
}
if (check_for_overrun(c, &gb, init_bit_pos, len)) {
dav1d_ref_dec(&ref);
return dav1d_parse_obus_error(c, in);
}

const int op_idx =
c->operating_point < seq_hdr->num_operating_points ? c->operating_point : 0;
c->operating_point_idc = seq_hdr->operating_points[op_idx].idc;
const unsigned spatial_mask = c->operating_point_idc >> 8;
c->max_spatial_id = spatial_mask ? ulog2(spatial_mask) : 0;

// If we have read a sequence header which is different from
// the old one, this is a new video sequence and can't use any
// previous state. Free that state.
Expand Down Expand Up @@ -1313,7 +1355,6 @@ int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int globa
// fall-through
case DAV1D_OBU_FRAME:
case DAV1D_OBU_FRAME_HDR:
if (global) break;
if (!c->seq_hdr) return dav1d_parse_obus_error(c, in);
if (!c->frame_hdr_ref) {
c->frame_hdr_ref = dav1d_ref_create_using_pool(c->frame_hdr_pool,
Expand Down Expand Up @@ -1369,7 +1410,6 @@ int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int globa
dav1d_bytealign_get_bits(&gb);
// fall-through
case DAV1D_OBU_TILE_GRP: {
if (global) break;
if (!c->frame_hdr) return dav1d_parse_obus_error(c, in);
if (c->n_tile_data_alloc < c->n_tile_data + 1) {
if ((c->n_tile_data + 1) > INT_MAX / (int)sizeof(*c->tile)) return dav1d_parse_obus_error(c, in);
Expand Down
Loading

0 comments on commit 10404a7

Please sign in to comment.