diff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c
index 02f168b58d..0b54b5820c 100644
--- a/libavcodec/cbs_av1.c
+++ b/libavcodec/cbs_av1.c
@@ -776,13 +776,15 @@ static int cbs_av1_get_relative_dist(const AV1RawSequenceHeader *seq,
 #undef byte_alignment
 
 
-static int cbs_av1_split_fragment(CodedBitstreamContext *ctx,
-                                  CodedBitstreamFragment *frag,
-                                  int header)
+typedef int (*cbs_av1_split_obu_callback)(CodedBitstreamContext *ctx,
+                                          void *priv, int obu_type,
+                                          const uint8_t *data, size_t size);
+
+static int cbs_av1_split_obus(CodedBitstreamContext *ctx,
+                              void *priv, cbs_av1_split_obu_callback cb,
+                              const uint8_t *data, size_t size)
 {
     GetBitContext gbc;
-    uint8_t *data;
-    size_t size;
     uint64_t obu_length;
     int pos, err, trace;
 
@@ -790,9 +792,6 @@ static int cbs_av1_split_fragment(CodedBitstreamContext *ctx,
     trace = ctx->trace_enable;
     ctx->trace_enable = 0;
 
-    data = frag->data;
-    size = frag->data_size;
-
     if (INT_MAX / 8 < size) {
         av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid fragment: "
                "too large (%"SIZE_SPECIFIER" bytes).\n", size);
@@ -837,8 +836,7 @@ static int cbs_av1_split_fragment(CodedBitstreamContext *ctx,
             goto fail;
         }
 
-        err = ff_cbs_insert_unit_data(ctx, frag, -1, header.obu_type,
-                                      data, obu_length, frag->data_ref);
+        err = cb(ctx, priv, header.obu_type, data, obu_length);
         if (err < 0)
             goto fail;
 
@@ -852,6 +850,23 @@ fail:
     return err;
 }
 
+static int cbs_av1_insert_obu(CodedBitstreamContext *ctx,
+                              void *priv, int obu_type,
+                              const uint8_t *data, size_t size)
+{
+    CodedBitstreamFragment *frag = priv;
+    return ff_cbs_insert_unit_data(ctx, frag, -1, obu_type,
+                                   (uint8_t*)data, size, frag->data_ref);
+}
+
+static int cbs_av1_split_fragment(CodedBitstreamContext *ctx,
+                                  CodedBitstreamFragment *frag,
+                                  int header)
+{
+    return cbs_av1_split_obus(ctx, frag, &cbs_av1_insert_obu,
+                              frag->data, frag->data_size);
+}
+
 static void cbs_av1_free_tile_data(AV1RawTileData *td)
 {
     av_buffer_unref(&td->data_ref);
@@ -895,6 +910,12 @@ static int cbs_av1_ref_tile_data(CodedBitstreamContext *ctx,
 {
     int pos;
 
+    if (!unit->data_ref) {
+        // Not refcounted - only parsing headers, so tile data will
+        // not be needed.
+        return 0;
+    }
+
     pos = get_bits_count(gbc);
     if (pos >= 8 * unit->data_size) {
         av_log(ctx->log_ctx, AV_LOG_ERROR, "Bitstream ended before "
@@ -1324,6 +1345,52 @@ static void cbs_av1_close(CodedBitstreamContext *ctx)
     av_freep(&priv->write_buffer);
 }
 
+static int cbs_av1_parse_obu(CodedBitstreamContext *ctx,
+                             void *priv, int obu_type,
+                             const uint8_t *data, size_t data_size)
+{
+    CodedBitstreamUnit unit;
+    int err;
+
+    // These OBU types will not affect parsing.
+    if (obu_type == AV1_OBU_METADATA  ||
+        obu_type == AV1_OBU_TILE_LIST ||
+        obu_type == AV1_OBU_PADDING)
+        return 0;
+
+    unit = (CodedBitstreamUnit) {
+        .type      = obu_type,
+        .data      = (uint8_t*)data,
+        .data_size = data_size,
+    };
+
+    err = cbs_av1_read_unit(ctx, &unit);
+    if (err >= 0 && priv) {
+        AV1RawOBU *obu = unit.content;
+        switch (obu->header.obu_type) {
+        case AV1_OBU_FRAME_HEADER:
+        case AV1_OBU_REDUNDANT_FRAME_HEADER:
+            memcpy(priv, &obu->obu.frame_header,
+                   sizeof(obu->obu.frame_header));
+            break;
+        case AV1_OBU_FRAME:
+            memcpy(priv, &obu->obu.frame.header,
+                   sizeof(obu->obu.frame.header));
+            break;
+        }
+    }
+
+    av_buffer_unref(&unit.content_ref);
+
+    return err;
+}
+
+static int cbs_av1_parse_headers(CodedBitstreamContext *ctx, void *header,
+                                 const uint8_t *data, size_t size)
+{
+    return cbs_av1_split_obus(ctx, header, &cbs_av1_parse_obu, data, size);
+}
+
 const CodedBitstreamType ff_cbs_type_av1 = {
     .codec_id          = AV_CODEC_ID_AV1,
 
@@ -1335,4 +1402,6 @@ const CodedBitstreamType ff_cbs_type_av1 = {
     .assemble_fragment = &cbs_av1_assemble_fragment,
 
     .close             = &cbs_av1_close,
+
+    .parse_headers     = &cbs_av1_parse_headers,
 };
