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);
         }