diff --git a/include/dav1d/dav1d.h b/include/dav1d/dav1d.h index 237ce3eab2f99a5bab13a89e2f435f307513a769..d5ec001086abb078b72e44a16cbff8b34b48dd72 100644 --- a/include/dav1d/dav1d.h +++ b/include/dav1d/dav1d.h @@ -68,6 +68,13 @@ enum Dav1dInloopFilterType { DAV1D_INLOOPFILTER_RESTORATION, }; +enum Dav1dDecodeFrameType { + DAV1D_DECODEFRAMETYPE_ALL = 0, ///< decode and return all frames + DAV1D_DECODEFRAMETYPE_REFERENCE = 1,///< decode and return frames referenced by other frames only + DAV1D_DECODEFRAMETYPE_INTRA = 2, ///< decode and return intra frames only (includes keyframes) + DAV1D_DECODEFRAMETYPE_KEY = 3, ///< decode and return keyframes only +}; + typedef struct Dav1dSettings { int n_threads; ///< number of threads (0 = number of logical cores in host system, default 0) int max_frame_delay; ///< Set to 1 for low-latency decoding (0 = ceil(sqrt(n_threads)), default 0) @@ -86,7 +93,9 @@ typedef struct Dav1dSettings { ///< once when shown, default 0) enum Dav1dInloopFilterType inloop_filters; ///< postfilters to enable during decoding (default ///< DAV1D_INLOOPFILTER_ALL) - uint8_t reserved[20]; ///< reserved for future use + enum Dav1dDecodeFrameType decode_frame_type; ///< frame types to decode (default + ///< DAV1D_DECODEFRAMETYPE_ALL) + uint8_t reserved[16]; ///< reserved for future use } Dav1dSettings; /** diff --git a/meson.build b/meson.build index 52d8ef0109c0487fbae5c816da517242f8cab569..cee8b791eedbe2adbf0268167c2be0633a2394c1 100644 --- a/meson.build +++ b/meson.build @@ -30,7 +30,7 @@ project('dav1d', ['c'], 'b_ndebug=if-release'], meson_version: '>= 0.49.0') -dav1d_soname_version = '6.7.0' +dav1d_soname_version = '6.8.0' dav1d_api_version_array = dav1d_soname_version.split('.') dav1d_api_version_major = dav1d_api_version_array[0] dav1d_api_version_minor = dav1d_api_version_array[1] diff --git a/src/internal.h b/src/internal.h index 29d07b80386921035ceaa7504670cc1b8bf3f01a..b5fd1e18ef351814bda5667f75598a93e612ecab 100644 --- a/src/internal.h +++ b/src/internal.h @@ -194,6 +194,7 @@ struct Dav1dContext { int strict_std_compliance; int output_invisible_frames; enum Dav1dInloopFilterType inloop_filters; + enum Dav1dDecodeFrameType decode_frame_type; int drain; enum PictureFlags frame_flags; enum Dav1dEventFlags event_flags; diff --git a/src/lib.c b/src/lib.c index ea66ae4645280ce9b7411932b92083dcf2ea85ff..396a57c98f4ec00e4f3dc7cdc3a6c5b11c498aae 100644 --- a/src/lib.c +++ b/src/lib.c @@ -77,6 +77,7 @@ COLD void dav1d_default_settings(Dav1dSettings *const s) { s->strict_std_compliance = 0; s->output_invisible_frames = 0; s->inloop_filters = DAV1D_INLOOPFILTER_ALL; + s->decode_frame_type = DAV1D_DECODEFRAMETYPE_ALL; } static void close_internal(Dav1dContext **const c_out, int flush); @@ -144,6 +145,8 @@ COLD int dav1d_open(Dav1dContext **const c_out, const Dav1dSettings *const s) { DAV1D_ERR(EINVAL)); validate_input_or_ret(s->operating_point >= 0 && s->operating_point <= 31, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->decode_frame_type >= DAV1D_DECODEFRAMETYPE_ALL && + s->decode_frame_type <= DAV1D_DECODEFRAMETYPE_KEY, DAV1D_ERR(EINVAL)); pthread_attr_t thread_attr; if (pthread_attr_init(&thread_attr)) return DAV1D_ERR(ENOMEM); @@ -164,6 +167,7 @@ COLD int dav1d_open(Dav1dContext **const c_out, const Dav1dSettings *const s) { c->strict_std_compliance = s->strict_std_compliance; c->output_invisible_frames = s->output_invisible_frames; c->inloop_filters = s->inloop_filters; + c->decode_frame_type = s->decode_frame_type; dav1d_data_props_set_defaults(&c->cached_error_props); diff --git a/src/obu.c b/src/obu.c index 47ab11779b65927fb844b1fe0b7b3ebf6320b910..b6c2b6990bc9aea3b72be60e47921e7634f68990 100644 --- a/src/obu.c +++ b/src/obu.c @@ -289,9 +289,9 @@ static int read_frame_size(Dav1dContext *const c, GetBits *const gb, if (dav1d_get_bit(gb)) { const Dav1dThreadPicture *const ref = &c->refs[c->frame_hdr->refidx[i]].p; - if (!ref->p.data[0]) return -1; - hdr->width[1] = ref->p.p.w; - hdr->height = ref->p.p.h; + if (!ref->p.frame_hdr) return -1; + hdr->width[1] = ref->p.frame_hdr->width[1]; + hdr->height = ref->p.frame_hdr->height; hdr->render_width = ref->p.frame_hdr->render_width; hdr->render_height = ref->p.frame_hdr->render_height; hdr->super_res.enabled = seqhdr->super_res && dav1d_get_bit(gb); @@ -923,7 +923,7 @@ static int parse_frame_hdr(Dav1dContext *const c, GetBits *const gb) { int off_after = -1; int off_before_idx, off_after_idx; for (int i = 0; i < 7; i++) { - if (!c->refs[hdr->refidx[i]].p.p.data[0]) goto error; + if (!c->refs[hdr->refidx[i]].p.p.frame_hdr) goto error; const unsigned refpoc = c->refs[hdr->refidx[i]].p.p.frame_hdr->frame_offset; const int diff = get_poc_diff(seqhdr->order_hint_n_bits, refpoc, poc); @@ -951,7 +951,7 @@ static int parse_frame_hdr(Dav1dContext *const c, GetBits *const gb) { unsigned off_before2 = 0xFFFFFFFFU; int off_before2_idx; for (int i = 0; i < 7; i++) { - if (!c->refs[hdr->refidx[i]].p.p.data[0]) goto error; + if (!c->refs[hdr->refidx[i]].p.p.frame_hdr) goto error; const unsigned refpoc = c->refs[hdr->refidx[i]].p.p.frame_hdr->frame_offset; if (get_poc_diff(seqhdr->order_hint_n_bits, refpoc, off_before) < 0) { @@ -1537,6 +1537,20 @@ int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int globa if (c->seq_hdr && c->frame_hdr) { if (c->frame_hdr->show_existing_frame) { + if (!c->refs[c->frame_hdr->existing_frame_idx].p.p.frame_hdr) goto error; + switch (c->refs[c->frame_hdr->existing_frame_idx].p.p.frame_hdr->frame_type) { + case DAV1D_FRAME_TYPE_INTER: + case DAV1D_FRAME_TYPE_SWITCH: + if (c->decode_frame_type > DAV1D_DECODEFRAMETYPE_REFERENCE) + goto skip; + break; + case DAV1D_FRAME_TYPE_INTRA: + if (c->decode_frame_type > DAV1D_DECODEFRAMETYPE_INTRA) + goto skip; + // fall-through + default: + break; + } if (!c->refs[c->frame_hdr->existing_frame_idx].p.p.data[0]) goto error; if (c->strict_std_compliance && !c->refs[c->frame_hdr->existing_frame_idx].p.showable) @@ -1617,6 +1631,23 @@ int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int globa } c->frame_hdr = NULL; } else if (c->n_tiles == c->frame_hdr->tiling.cols * c->frame_hdr->tiling.rows) { + switch (c->frame_hdr->frame_type) { + case DAV1D_FRAME_TYPE_INTER: + case DAV1D_FRAME_TYPE_SWITCH: + if (c->decode_frame_type > DAV1D_DECODEFRAMETYPE_REFERENCE || + (c->decode_frame_type == DAV1D_DECODEFRAMETYPE_REFERENCE && + !c->frame_hdr->refresh_frame_flags)) + goto skip; + break; + case DAV1D_FRAME_TYPE_INTRA: + if (c->decode_frame_type > DAV1D_DECODEFRAMETYPE_INTRA || + (c->decode_frame_type == DAV1D_DECODEFRAMETYPE_REFERENCE && + !c->frame_hdr->refresh_frame_flags)) + goto skip; + // fall-through + default: + break; + } if (!c->n_tile_data) goto error; if ((res = dav1d_submit_frame(c)) < 0) @@ -1629,6 +1660,26 @@ int dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in, const int globa return len + init_byte_pos; +skip: + // update refs with only the headers in case we skip the frame + for (int i = 0; i < 8; i++) { + if (c->frame_hdr->refresh_frame_flags & (1 << i)) { + dav1d_thread_picture_unref(&c->refs[i].p); + c->refs[i].p.p.frame_hdr = c->frame_hdr; + c->refs[i].p.p.seq_hdr = c->seq_hdr; + c->refs[i].p.p.frame_hdr_ref = c->frame_hdr_ref; + c->refs[i].p.p.seq_hdr_ref = c->seq_hdr_ref; + dav1d_ref_inc(c->frame_hdr_ref); + dav1d_ref_inc(c->seq_hdr_ref); + } + } + + dav1d_ref_dec(&c->frame_hdr_ref); + c->frame_hdr = NULL; + c->n_tiles = 0; + + return len + init_byte_pos; + error: dav1d_data_props_copy(&c->cached_error_props, &in->m); dav1d_log(c, "Error parsing OBU data\n"); diff --git a/tools/dav1d_cli_parse.c b/tools/dav1d_cli_parse.c index a1819651e8061e8a7f39a7ec2cc4805247667c4d..4d747c03271fe33ced6983183b0d522c9d85b0ba 100644 --- a/tools/dav1d_cli_parse.c +++ b/tools/dav1d_cli_parse.c @@ -62,6 +62,7 @@ enum { ARG_NEG_STRIDE, ARG_OUTPUT_INVISIBLE, ARG_INLOOP_FILTERS, + ARG_DECODE_FRAME_TYPE, }; static const struct option long_opts[] = { @@ -88,6 +89,7 @@ static const struct option long_opts[] = { { "negstride", 0, NULL, ARG_NEG_STRIDE }, { "outputinvisible", 1, NULL, ARG_OUTPUT_INVISIBLE }, { "inloopfilters", 1, NULL, ARG_INLOOP_FILTERS }, + { "decodeframetype", 1, NULL, ARG_DECODE_FRAME_TYPE }, { NULL, 0, NULL, 0 }, }; @@ -145,7 +147,9 @@ static void usage(const char *const app, const char *const reason, ...) { " --negstride: use negative picture strides\n" " this is mostly meant as a developer option\n" " --outputinvisible $num: whether to output invisible (alt-ref) frames (default: 0)\n" - " --inloopfilters $str: which in-loop filters to enable (none, (no)deblock, (no)cdef, (no)restoration or all; default: all)\n"); + " --inloopfilters $str: which in-loop filters to enable (none, (no)deblock, (no)cdef, (no)restoration or all; default: all)\n" + " --decodeframetype $str: which frame types to decode (reference, intra, key or all; default: all)\n" + ); exit(1); } @@ -235,6 +239,13 @@ static const EnumParseTable inloop_filters_tbl[] = { { "all", DAV1D_INLOOPFILTER_ALL }, }; +static const EnumParseTable decode_frame_type_tbl[] = { + { "all", DAV1D_DECODEFRAMETYPE_ALL }, + { "reference", DAV1D_DECODEFRAMETYPE_REFERENCE }, + { "intra", DAV1D_DECODEFRAMETYPE_INTRA }, + { "key", DAV1D_DECODEFRAMETYPE_KEY }, +}; + #define ARRAY_SIZE(n) (sizeof(n)/sizeof(*(n))) static unsigned parse_enum(char *optarg, const EnumParseTable *const tbl, @@ -381,6 +392,11 @@ void parse(const int argc, char *const *const argv, parse_enum(optarg, inloop_filters_tbl, ARRAY_SIZE(inloop_filters_tbl),ARG_INLOOP_FILTERS, argv[0]); break; + case ARG_DECODE_FRAME_TYPE: + lib_settings->decode_frame_type = + parse_enum(optarg, decode_frame_type_tbl, + ARRAY_SIZE(decode_frame_type_tbl), ARG_DECODE_FRAME_TYPE, argv[0]); + break; default: usage(argv[0], NULL); }