shithub: dav1d

Download patch

ref: c371907ffea1cafa9ee5867df4127a53b836d68f
parent: e071d257cae01c07d2204478dcc489200df66750
author: Ronald S. Bultje <rsbultje@gmail.com>
date: Sat Nov 17 14:13:23 EST 2018

Make frame_hdr a pointer

--- a/src/cdef_apply_tmpl.c
+++ b/src/cdef_apply_tmpl.c
@@ -88,7 +88,7 @@
     pixel *ptrs[3] = { p[0], p[1], p[2] };
     const int sbsz = 16;
     const int sb64w = f->sb128w << 1;
-    const int damping = f->frame_hdr.cdef.damping + BITDEPTH - 8;
+    const int damping = f->frame_hdr->cdef.damping + BITDEPTH - 8;
     const enum Dav1dPixelLayout layout = f->cur.p.layout;
     const int uv_idx = DAV1D_PIXEL_LAYOUT_I444 - layout;
     const int has_chroma = layout != DAV1D_PIXEL_LAYOUT_I400;
@@ -119,15 +119,15 @@
             const int sb64_idx = ((by & sbsz) >> 3) + (sbx & 1);
             const int cdef_idx = lflvl[sb128x].cdef_idx[sb64_idx];
             if (cdef_idx == -1 ||
-                (!f->frame_hdr.cdef.y_strength[cdef_idx] &&
-                 !f->frame_hdr.cdef.uv_strength[cdef_idx]))
+                (!f->frame_hdr->cdef.y_strength[cdef_idx] &&
+                 !f->frame_hdr->cdef.uv_strength[cdef_idx]))
             {
                 last_skip = 1;
                 goto next_sb;
             }
 
-            const int y_lvl = f->frame_hdr.cdef.y_strength[cdef_idx];
-            const int uv_lvl = f->frame_hdr.cdef.uv_strength[cdef_idx];
+            const int y_lvl = f->frame_hdr->cdef.y_strength[cdef_idx];
+            const int uv_lvl = f->frame_hdr->cdef.uv_strength[cdef_idx];
             pixel *bptrs[3] = { iptrs[0], iptrs[1], iptrs[2] };
             for (int bx = sbx * sbsz; bx < imin((sbx + 1) * sbsz, f->bw);
                  bx += 2, edges |= HAVE_LEFT)
--- a/src/decode.c
+++ b/src/decode.c
@@ -77,7 +77,7 @@
 {
     Dav1dTileState *const ts = t->ts;
     const Dav1dFrameContext *const f = t->f;
-    const int have_hp = f->frame_hdr.hp;
+    const int have_hp = f->frame_hdr->hp;
     const int sign = msac_decode_bool_adapt(&ts->msac, mv_comp->sign);
     const int cl = msac_decode_symbol_adapt(&ts->msac, mv_comp->classes, 11);
     int up, fp, hp;
@@ -613,11 +613,11 @@
     // var-tx tree coding
     b->tx_split[0] = b->tx_split[1] = 0;
     b->max_ytx = dav1d_max_txfm_size_for_bs[bs][0];
-    if (f->frame_hdr.segmentation.lossless[b->seg_id] ||
+    if (f->frame_hdr->segmentation.lossless[b->seg_id] ||
         b->max_ytx == TX_4X4)
     {
         b->max_ytx = b->uvtx = TX_4X4;
-        if (f->frame_hdr.txfm_mode == TX_SWITCHABLE) {
+        if (f->frame_hdr->txfm_mode == TX_SWITCHABLE) {
 #define set_ctx(type, dir, diridx, off, mul, rep_macro) \
             rep_macro(type, t->dir tx, off, TX_4X4)
             case_set(bh4, l., 1, by4);
@@ -624,8 +624,8 @@
             case_set(bw4, a->, 0, bx4);
 #undef set_ctx
         }
-    } else if (f->frame_hdr.txfm_mode != TX_SWITCHABLE || b->skip) {
-        if (f->frame_hdr.txfm_mode == TX_SWITCHABLE) {
+    } else if (f->frame_hdr->txfm_mode != TX_SWITCHABLE || b->skip) {
+        if (f->frame_hdr->txfm_mode == TX_SWITCHABLE) {
 #define set_ctx(type, dir, diridx, off, mul, rep_macro) \
             rep_macro(type, t->dir tx, off, mul * b_dim[2 + diridx])
             case_set(bh4, l., 1, by4);
@@ -632,7 +632,7 @@
             case_set(bw4, a->, 0, bx4);
 #undef set_ctx
         } else {
-            assert(f->frame_hdr.txfm_mode == TX_LARGEST);
+            assert(f->frame_hdr->txfm_mode == TX_LARGEST);
         }
         b->uvtx = dav1d_max_txfm_size_for_bs[bs][f->cur.p.layout];
     } else {
@@ -664,8 +664,8 @@
 {
     unsigned seg_id = 8;
 
-    assert(f->frame_hdr.primary_ref_frame != PRIMARY_REF_NONE);
-    if (dav1d_thread_picture_wait(&f->refp[f->frame_hdr.primary_ref_frame],
+    assert(f->frame_hdr->primary_ref_frame != PRIMARY_REF_NONE);
+    if (dav1d_thread_picture_wait(&f->refp[f->frame_hdr->primary_ref_frame],
                                   (by + h4) * 4, PLANE_TYPE_BLOCK))
     {
         return 8;
@@ -727,7 +727,7 @@
 #undef set_ctx
             }
         } else {
-            if (f->frame_hdr.frame_type & 1 /* not intrabc */ &&
+            if (f->frame_hdr->frame_type & 1 /* not intrabc */ &&
                 b->comp_type == COMP_INTER_NONE && b->motion_mode == MM_WARP)
             {
                 uint64_t mask[2] = { 0, 0 };
@@ -767,8 +767,8 @@
 
     // segment_id (if seg_feature for skip/ref/gmv is enabled)
     int seg_pred = 0;
-    if (f->frame_hdr.segmentation.enabled) {
-        if (!f->frame_hdr.segmentation.update_map) {
+    if (f->frame_hdr->segmentation.enabled) {
+        if (!f->frame_hdr->segmentation.update_map) {
             if (f->prev_segmap) {
                 unsigned seg_id = get_prev_frame_segid(f, t->by, t->bx, w4, h4,
                                                        f->prev_segmap,
@@ -778,9 +778,9 @@
             } else {
                 b->seg_id = 0;
             }
-            seg = &f->frame_hdr.segmentation.seg_data.d[b->seg_id];
-        } else if (f->frame_hdr.segmentation.seg_data.preskip) {
-            if (f->frame_hdr.segmentation.temporal &&
+            seg = &f->frame_hdr->segmentation.seg_data.d[b->seg_id];
+        } else if (f->frame_hdr->segmentation.seg_data.preskip) {
+            if (f->frame_hdr->segmentation.temporal &&
                 (seg_pred = msac_decode_bool_adapt(&ts->msac,
                                        ts->cdf.m.seg_pred[t->a->seg_pred[bx4] +
                                                           t->l.seg_pred[by4]])))
@@ -805,7 +805,7 @@
                                                    ts->cdf.m.seg_id[seg_ctx],
                                                    NUM_SEGMENTS);
                 const unsigned last_active_seg_id =
-                    f->frame_hdr.segmentation.seg_data.last_active_segid;
+                    f->frame_hdr->segmentation.seg_data.last_active_segid;
                 b->seg_id = neg_deinterleave(diff, pred_seg_id,
                                              last_active_seg_id + 1);
                 if (b->seg_id > last_active_seg_id) b->seg_id = 0; // error?
@@ -816,7 +816,7 @@
                 printf("Post-segid[preskip;%d]: r=%d\n",
                        b->seg_id, ts->msac.rng);
 
-            seg = &f->frame_hdr.segmentation.seg_data.d[b->seg_id];
+            seg = &f->frame_hdr->segmentation.seg_data.d[b->seg_id];
         }
     } else {
         b->seg_id = 0;
@@ -824,7 +824,7 @@
 
     // skip_mode
     if ((!seg || (!seg->globalmv && seg->ref == -1 && !seg->skip)) &&
-        f->frame_hdr.skip_mode_enabled && imin(bw4, bh4) > 1)
+        f->frame_hdr->skip_mode_enabled && imin(bw4, bh4) > 1)
     {
         const int smctx = t->a->skip_mode[bx4] + t->l.skip_mode[by4];
         b->skip_mode = msac_decode_bool_adapt(&ts->msac,
@@ -846,11 +846,11 @@
     }
 
     // segment_id
-    if (f->frame_hdr.segmentation.enabled &&
-        f->frame_hdr.segmentation.update_map &&
-        !f->frame_hdr.segmentation.seg_data.preskip)
+    if (f->frame_hdr->segmentation.enabled &&
+        f->frame_hdr->segmentation.update_map &&
+        !f->frame_hdr->segmentation.seg_data.preskip)
     {
-        if (!b->skip && f->frame_hdr.segmentation.temporal &&
+        if (!b->skip && f->frame_hdr->segmentation.temporal &&
             (seg_pred = msac_decode_bool_adapt(&ts->msac,
                                    ts->cdf.m.seg_pred[t->a->seg_pred[bx4] +
                                                       t->l.seg_pred[by4]])))
@@ -877,7 +877,7 @@
                                                    ts->cdf.m.seg_id[seg_ctx],
                                                    NUM_SEGMENTS);
                 const unsigned last_active_seg_id =
-                    f->frame_hdr.segmentation.seg_data.last_active_segid;
+                    f->frame_hdr->segmentation.seg_data.last_active_segid;
                 b->seg_id = neg_deinterleave(diff, pred_seg_id,
                                              last_active_seg_id + 1);
                 if (b->seg_id > last_active_seg_id) b->seg_id = 0; // error?
@@ -885,7 +885,7 @@
             if (b->seg_id >= NUM_SEGMENTS) b->seg_id = 0; // error?
         }
 
-        seg = &f->frame_hdr.segmentation.seg_data.d[b->seg_id];
+        seg = &f->frame_hdr->segmentation.seg_data.d[b->seg_id];
 
         if (DEBUG_BLOCK_INFO)
             printf("Post-segid[postskip;%d]: r=%d\n",
@@ -897,7 +897,7 @@
         const int idx = f->seq_hdr->sb128 ? ((t->bx & 16) >> 4) +
                                            ((t->by & 16) >> 3) : 0;
         if (t->cur_sb_cdef_idx_ptr[idx] == -1) {
-            const int v = msac_decode_bools(&ts->msac, f->frame_hdr.cdef.n_bits);
+            const int v = msac_decode_bools(&ts->msac, f->frame_hdr->cdef.n_bits);
             t->cur_sb_cdef_idx_ptr[idx] = v;
             if (bw4 > 16) t->cur_sb_cdef_idx_ptr[idx + 1] = v;
             if (bh4 > 16) t->cur_sb_cdef_idx_ptr[idx + 2] = v;
@@ -914,7 +914,7 @@
         !(t->by & (31 >> !f->seq_hdr->sb128)))
     {
         const int prev_qidx = ts->last_qidx;
-        const int have_delta_q = f->frame_hdr.delta.q.present &&
+        const int have_delta_q = f->frame_hdr->delta.q.present &&
             (bs != (f->seq_hdr->sb128 ? BS_128x128 : BS_64x64) || !b->skip);
 
         int8_t prev_delta_lf[4];
@@ -928,7 +928,7 @@
             }
             if (delta_q) {
                 if (msac_decode_bool(&ts->msac, EC_BOOL_EPROB)) delta_q = -delta_q;
-                delta_q *= 1 << f->frame_hdr.delta.q.res_log2;
+                delta_q *= 1 << f->frame_hdr->delta.q.res_log2;
             }
             ts->last_qidx = iclip(ts->last_qidx + delta_q, 1, 255);
             if (have_delta_q && DEBUG_BLOCK_INFO)
@@ -935,14 +935,14 @@
                 printf("Post-delta_q[%d->%d]: r=%d\n",
                        delta_q, ts->last_qidx, ts->msac.rng);
 
-            if (f->frame_hdr.delta.lf.present) {
-                const int n_lfs = f->frame_hdr.delta.lf.multi ?
+            if (f->frame_hdr->delta.lf.present) {
+                const int n_lfs = f->frame_hdr->delta.lf.multi ?
                     f->seq_hdr->layout != DAV1D_PIXEL_LAYOUT_I400 ? 4 : 2 : 1;
 
                 for (int i = 0; i < n_lfs; i++) {
                     int delta_lf =
                         msac_decode_symbol_adapt(&ts->msac,
-                        ts->cdf.m.delta_lf[i + f->frame_hdr.delta.lf.multi], 4);
+                        ts->cdf.m.delta_lf[i + f->frame_hdr->delta.lf.multi], 4);
                     if (delta_lf == 3) {
                         const int n_bits = 1 + msac_decode_bools(&ts->msac, 3);
                         delta_lf = msac_decode_bools(&ts->msac, n_bits) +
@@ -951,7 +951,7 @@
                     if (delta_lf) {
                         if (msac_decode_bool(&ts->msac, EC_BOOL_EPROB))
                             delta_lf = -delta_lf;
-                        delta_lf *= 1 << f->frame_hdr.delta.lf.res_log2;
+                        delta_lf *= 1 << f->frame_hdr->delta.lf.res_log2;
                     }
                     ts->last_delta_lf[i] =
                         iclip(ts->last_delta_lf[i] + delta_lf, -63, 63);
@@ -961,12 +961,12 @@
                 }
             }
         }
-        if (ts->last_qidx == f->frame_hdr.quant.yac) {
+        if (ts->last_qidx == f->frame_hdr->quant.yac) {
             // assign frame-wide q values to this sb
             ts->dq = f->dq;
         } else if (ts->last_qidx != prev_qidx) {
             // find sb-specific quant parameters
-            init_quant_tables(f->seq_hdr, &f->frame_hdr, ts->last_qidx, ts->dqmem);
+            init_quant_tables(f->seq_hdr, f->frame_hdr, ts->last_qidx, ts->dqmem);
             ts->dq = ts->dqmem;
         }
         if (!memcmp(ts->last_delta_lf, (int8_t[4]) { 0, 0, 0, 0 }, 4)) {
@@ -974,7 +974,7 @@
             ts->lflvl = f->lf.lvl;
         } else if (memcmp(ts->last_delta_lf, prev_delta_lf, 4)) {
             // find sb-specific lf lvl parameters
-            dav1d_calc_lf_values(ts->lflvlmem, &f->frame_hdr, ts->last_delta_lf);
+            dav1d_calc_lf_values(ts->lflvlmem, f->frame_hdr, ts->last_delta_lf);
             ts->lflvl = ts->lflvlmem;
         }
     }
@@ -981,7 +981,7 @@
 
     if (b->skip_mode) {
         b->intra = 0;
-    } else if (f->frame_hdr.frame_type & 1) {
+    } else if (f->frame_hdr->frame_type & 1) {
         if (seg && (seg->ref >= 0 || seg->globalmv)) {
             b->intra = !seg->ref;
         } else {
@@ -991,7 +991,7 @@
             if (DEBUG_BLOCK_INFO)
                 printf("Post-intra[%d]: r=%d\n", b->intra, ts->msac.rng);
         }
-    } else if (f->frame_hdr.allow_intrabc) {
+    } else if (f->frame_hdr->allow_intrabc) {
         b->intra = !msac_decode_bool_adapt(&ts->msac, ts->cdf.m.intrabc);
         if (DEBUG_BLOCK_INFO)
             printf("Post-intrabcflag[%d]: r=%d\n", b->intra, ts->msac.rng);
@@ -1001,7 +1001,7 @@
 
     // intra/inter-specific stuff
     if (b->intra) {
-        uint16_t *const ymode_cdf = f->frame_hdr.frame_type & 1 ?
+        uint16_t *const ymode_cdf = f->frame_hdr->frame_type & 1 ?
             ts->cdf.m.y_mode[dav1d_ymode_size_context[bs]] :
             ts->cdf.kfym[dav1d_intra_mode_context[t->a->mode[bx4]]]
                         [dav1d_intra_mode_context[t->l.mode[by4]]];
@@ -1022,7 +1022,7 @@
         }
 
         if (has_chroma) {
-            const int cfl_allowed = f->frame_hdr.segmentation.lossless[b->seg_id] ?
+            const int cfl_allowed = f->frame_hdr->segmentation.lossless[b->seg_id] ?
                 cbw4 == 1 && cbh4 == 1 : !!(cfl_allowed_mask & (1 << bs));
             uint16_t *const uvmode_cdf = ts->cdf.m.uv_mode[cfl_allowed][b->y_mode];
             b->uv_mode = msac_decode_symbol_adapt(&ts->msac, uvmode_cdf,
@@ -1068,7 +1068,7 @@
         }
 
         b->pal_sz[0] = b->pal_sz[1] = 0;
-        if (f->frame_hdr.allow_screen_content_tools &&
+        if (f->frame_hdr->allow_screen_content_tools &&
             imax(bw4, bh4) <= 16 && bw4 + bh4 >= 4)
         {
             const int sz_ctx = b_dim[2] + b_dim[3] - 2;
@@ -1133,7 +1133,7 @@
         }
 
         const TxfmInfo *t_dim;
-        if (f->frame_hdr.segmentation.lossless[b->seg_id]) {
+        if (f->frame_hdr->segmentation.lossless[b->seg_id]) {
             b->tx = b->uvtx = (int) TX_4X4;
             t_dim = &dav1d_txfm_dimensions[TX_4X4];
         } else {
@@ -1140,7 +1140,7 @@
             b->tx = dav1d_max_txfm_size_for_bs[bs][0];
             b->uvtx = dav1d_max_txfm_size_for_bs[bs][f->cur.p.layout];
             t_dim = &dav1d_txfm_dimensions[b->tx];
-            if (f->frame_hdr.txfm_mode == TX_SWITCHABLE && t_dim->max > TX_4X4) {
+            if (f->frame_hdr->txfm_mode == TX_SWITCHABLE && t_dim->max > TX_4X4) {
                 const int tctx = get_tx_ctx(t->a, &t->l, t_dim, by4, bx4);
                 uint16_t *const tx_cdf = ts->cdf.m.txsz[t_dim->max - 1][tctx];
                 int depth = msac_decode_symbol_adapt(&ts->msac, tx_cdf,
@@ -1163,7 +1163,7 @@
         }
 
         dav1d_create_lf_mask_intra(t->lf_mask, f->lf.level, f->b4_stride,
-                                   &f->frame_hdr, (const uint8_t (*)[8][2])
+                                   f->frame_hdr, (const uint8_t (*)[8][2])
                                    &ts->lflvl[b->seg_id][0][0][0],
                                    t->bx, t->by, f->w4, f->h4, bs,
                                    b->tx, b->uvtx, f->cur.p.layout,
@@ -1183,7 +1183,7 @@
         rep_macro(type, t->dir skip, off, mul * b->skip); \
         /* see aomedia bug 2183 for why we use luma coordinates here */ \
         rep_macro(type, t->pal_sz_uv[diridx], off, mul * (has_chroma ? b->pal_sz[1] : 0)); \
-        if (f->frame_hdr.frame_type & 1) { \
+        if (f->frame_hdr->frame_type & 1) { \
             rep_macro(type, t->dir comp_type, off, mul * COMP_INTER_NONE); \
             rep_macro(type, t->dir ref[0], off, mul * ((uint8_t) -1)); \
             rep_macro(type, t->dir ref[1], off, mul * ((uint8_t) -1)); \
@@ -1221,11 +1221,11 @@
                     memcpy(t->al_pal[1][by4 + y][pl], pal, 16);
             }
         }
-        if ((f->frame_hdr.frame_type & 1) || f->frame_hdr.allow_intrabc) {
+        if ((f->frame_hdr->frame_type & 1) || f->frame_hdr->allow_intrabc) {
             splat_intraref(f->mvs, f->b4_stride, t->by, t->bx, bs,
                            y_mode_nofilt);
         }
-    } else if (!(f->frame_hdr.frame_type & 1)) {
+    } else if (!(f->frame_hdr->frame_type & 1)) {
         // intra block copy
         candidate_mv mvstack[8];
         int n_mvs;
@@ -1351,7 +1351,7 @@
         if (b->skip_mode) {
             is_comp = 1;
         } else if ((!seg || (seg->ref == -1 && !seg->globalmv && !seg->skip)) &&
-                   f->frame_hdr.switchable_comp_refs && imin(bw4, bh4) > 1)
+                   f->frame_hdr->switchable_comp_refs && imin(bw4, bh4) > 1)
         {
             const int ctx = get_comp_ctx(t->a, &t->l, by4, bx4,
                                          have_top, have_left);
@@ -1363,8 +1363,8 @@
         }
 
         if (b->skip_mode) {
-            b->ref[0] = f->frame_hdr.skip_mode_refs[0];
-            b->ref[1] = f->frame_hdr.skip_mode_refs[1];
+            b->ref[0] = f->frame_hdr->skip_mode_refs[0];
+            b->ref[1] = f->frame_hdr->skip_mode_refs[1];
             b->comp_type = COMP_INTER_AVG;
             b->inter_mode = NEARESTMV_NEARESTMV;
             b->drl_idx = 0;
@@ -1381,8 +1381,8 @@
 
             b->mv[0] = mvstack[0].this_mv;
             b->mv[1] = mvstack[0].comp_mv;
-            fix_mv_precision(&f->frame_hdr, &b->mv[0]);
-            fix_mv_precision(&f->frame_hdr, &b->mv[1]);
+            fix_mv_precision(f->frame_hdr, &b->mv[0]);
+            fix_mv_precision(f->frame_hdr, &b->mv[1]);
             if (DEBUG_BLOCK_INFO)
                 printf("Post-skipmodeblock[mv=1:y=%d,x=%d,2:y=%d,x=%d,refs=%d+%d\n",
                        b->mv[0].y, b->mv[0].x, b->mv[1].y, b->mv[1].x,
@@ -1502,19 +1502,19 @@
             case NEARMV: \
             case NEARESTMV: \
                 b->mv[idx] = mvstack[b->drl_idx].pfx##_mv; \
-                fix_mv_precision(&f->frame_hdr, &b->mv[idx]); \
+                fix_mv_precision(f->frame_hdr, &b->mv[idx]); \
                 break; \
             case GLOBALMV: \
                 has_subpel_filter |= \
-                    f->frame_hdr.gmv[b->ref[idx]].type == WM_TYPE_TRANSLATION; \
-                b->mv[idx] = get_gmv_2d(&f->frame_hdr.gmv[b->ref[idx]], \
-                                        t->bx, t->by, bw4, bh4, &f->frame_hdr); \
-                fix_mv_precision(&f->frame_hdr, &b->mv[idx]); \
+                    f->frame_hdr->gmv[b->ref[idx]].type == WM_TYPE_TRANSLATION; \
+                b->mv[idx] = get_gmv_2d(&f->frame_hdr->gmv[b->ref[idx]], \
+                                        t->bx, t->by, bw4, bh4, f->frame_hdr); \
+                fix_mv_precision(f->frame_hdr, &b->mv[idx]); \
                 break; \
             case NEWMV: \
                 b->mv[idx] = mvstack[b->drl_idx].pfx##_mv; \
                 read_mv_residual(t, &b->mv[idx], &ts->cdf.mv, \
-                                 !f->frame_hdr.force_integer_mv); \
+                                 !f->frame_hdr->force_integer_mv); \
                 break; \
             }
             has_subpel_filter = imin(bw4, bh4) == 1 ||
@@ -1636,11 +1636,11 @@
                                         ts->cdf.m.globalmv_mode[(ctx >> 3) & 1]))
                 {
                     b->inter_mode = GLOBALMV;
-                    b->mv[0] = get_gmv_2d(&f->frame_hdr.gmv[b->ref[0]],
-                                          t->bx, t->by, bw4, bh4, &f->frame_hdr);
-                    fix_mv_precision(&f->frame_hdr, &b->mv[0]);
+                    b->mv[0] = get_gmv_2d(&f->frame_hdr->gmv[b->ref[0]],
+                                          t->bx, t->by, bw4, bh4, f->frame_hdr);
+                    fix_mv_precision(f->frame_hdr, &b->mv[0]);
                     has_subpel_filter = imin(bw4, bh4) == 1 ||
-                        f->frame_hdr.gmv[b->ref[0]].type == WM_TYPE_TRANSLATION;
+                        f->frame_hdr->gmv[b->ref[0]].type == WM_TYPE_TRANSLATION;
                 } else {
                     has_subpel_filter = 1;
                     if (msac_decode_bool_adapt(&ts->msac,
@@ -1667,7 +1667,7 @@
                         b->mv[0] = mvstack[b->drl_idx].this_mv;
                     } else {
                         b->mv[0] = mvlist[0][b->drl_idx];
-                        fix_mv_precision(&f->frame_hdr, &b->mv[0]);
+                        fix_mv_precision(f->frame_hdr, &b->mv[0]);
                     }
                 }
 
@@ -1693,13 +1693,13 @@
                     b->mv[0] = mvstack[b->drl_idx].this_mv;
                 } else {
                     b->mv[0] = mvlist[0][0];
-                    fix_mv_precision(&f->frame_hdr, &b->mv[0]);
+                    fix_mv_precision(f->frame_hdr, &b->mv[0]);
                 }
                 if (DEBUG_BLOCK_INFO)
                     printf("Post-intermode[%d,drl=%d]: r=%d\n",
                            b->inter_mode, b->drl_idx, ts->msac.rng);
                 read_mv_residual(t, &b->mv[0], &ts->cdf.mv,
-                                 !f->frame_hdr.force_integer_mv);
+                                 !f->frame_hdr->force_integer_mv);
                 if (DEBUG_BLOCK_INFO)
                     printf("Post-residualmv[mv=y:%d,x:%d]: r=%d\n",
                            b->mv[0].y, b->mv[0].x, ts->msac.rng);
@@ -1733,11 +1733,11 @@
             }
 
             // motion variation
-            if (f->frame_hdr.switchable_motion_mode &&
+            if (f->frame_hdr->switchable_motion_mode &&
                 b->interintra_type == INTER_INTRA_NONE && imin(bw4, bh4) >= 2 &&
                 // is not warped global motion
-                !(!f->frame_hdr.force_integer_mv && b->inter_mode == GLOBALMV &&
-                  f->frame_hdr.gmv[b->ref[0]].type > WM_TYPE_TRANSLATION) &&
+                !(!f->frame_hdr->force_integer_mv && b->inter_mode == GLOBALMV &&
+                  f->frame_hdr->gmv[b->ref[0]].type > WM_TYPE_TRANSLATION) &&
                 // has overlappable neighbours
                 ((have_left && findoddzero(&t->l.intra[by4 + 1], h4 >> 1)) ||
                  (have_top && findoddzero(&t->a->intra[bx4 + 1], w4 >> 1))))
@@ -1748,8 +1748,8 @@
                 find_matching_ref(t, intra_edge_flags, bw4, bh4, w4, h4,
                                   have_left, have_top, b->ref[0], mask);
                 const int allow_warp = !f->svc[b->ref[0]][0].scale &&
-                    !f->frame_hdr.force_integer_mv &&
-                    f->frame_hdr.warp_motion && (mask[0] | mask[1]);
+                    !f->frame_hdr->force_integer_mv &&
+                    f->frame_hdr->warp_motion && (mask[0] | mask[1]);
 
                 b->motion_mode = allow_warp ?
                     msac_decode_symbol_adapt(&ts->msac, ts->cdf.m.motion_mode[bs], 3) :
@@ -1785,7 +1785,7 @@
 
         // subpel filter
         enum FilterMode filter[2];
-        if (f->frame_hdr.subpel_filter_mode == FILTER_SWITCHABLE) {
+        if (f->frame_hdr->subpel_filter_mode == FILTER_SWITCHABLE) {
             if (has_subpel_filter) {
                 const int comp = b->comp_type != COMP_INTER_NONE;
                 const int ctx1 = get_filter_ctx(t->a, &t->l, comp, 0, b->ref[0],
@@ -1813,7 +1813,7 @@
                 filter[0] = filter[1] = FILTER_8TAP_REGULAR;
             }
         } else {
-            filter[0] = filter[1] = f->frame_hdr.subpel_filter_mode;
+            filter[0] = filter[1] = f->frame_hdr->subpel_filter_mode;
         }
         b->filter2d = dav1d_filter_2d[filter[1]][filter[0]];
 
@@ -1831,7 +1831,7 @@
         const uint8_t (*const lf_lvls)[8][2] = (const uint8_t (*)[8][2])
             &ts->lflvl[b->seg_id][0][b->ref[0] + 1][!is_globalmv];
         dav1d_create_lf_mask_inter(t->lf_mask, f->lf.level, f->b4_stride,
-                                   &f->frame_hdr, lf_lvls, t->bx, t->by,
+                                   f->frame_hdr, lf_lvls, t->bx, t->by,
                                    f->w4, f->h4, b->skip, bs, b->tx_split,
                                    b->uvtx, f->cur.p.layout,
                                    &t->a->tx_lpf_y[bx4], &t->l.tx_lpf_y[by4],
@@ -1878,8 +1878,8 @@
     }
 
     // update contexts
-    if (f->frame_hdr.segmentation.enabled &&
-        f->frame_hdr.segmentation.update_map)
+    if (f->frame_hdr->segmentation.enabled &&
+        f->frame_hdr->segmentation.update_map)
     {
         uint8_t *seg_ptr = &f->cur_segmap[t->by * f->b4_stride + t->bx];
 #define set_ctx(type, dir, diridx, off, mul, rep_macro) \
@@ -1923,7 +1923,7 @@
     if (f->frame_thread.pass != 2) {
         if (0 && bl == BL_64X64)
             printf("poc=%d,y=%d,x=%d,bl=%d,r=%d\n",
-                   f->frame_hdr.frame_offset, t->by, t->bx, bl, t->ts->msac.rng);
+                   f->frame_hdr->frame_offset, t->by, t->bx, bl, t->ts->msac.rng);
         bx8 = (t->bx & 31) >> 1;
         by8 = (t->by & 31) >> 1;
         ctx = get_partition_ctx(t->a, &t->l, bl, by8, bx8);
@@ -1946,7 +1946,7 @@
             }
             if (DEBUG_BLOCK_INFO)
                 printf("poc=%d,y=%d,x=%d,bl=%d,ctx=%d,bp=%d: r=%d\n",
-                       f->frame_hdr.frame_offset, t->by, t->bx, bl, ctx, bp,
+                       f->frame_hdr->frame_offset, t->by, t->bx, bl, ctx, bp,
                        t->ts->msac.rng);
         }
         const uint8_t *const b = dav1d_block_sizes[bl][bp];
@@ -2111,7 +2111,7 @@
             is_split = msac_decode_bool(&t->ts->msac, gather_top_partition_prob(pc, bl) >> EC_PROB_SHIFT);
             if (DEBUG_BLOCK_INFO)
                 printf("poc=%d,y=%d,x=%d,bl=%d,ctx=%d,bp=%d: r=%d\n",
-                       f->frame_hdr.frame_offset, t->by, t->bx, bl, ctx,
+                       f->frame_hdr->frame_offset, t->by, t->bx, bl, ctx,
                        is_split ? PARTITION_SPLIT : PARTITION_H, t->ts->msac.rng);
         }
 
@@ -2141,7 +2141,7 @@
                 return 1;
             if (DEBUG_BLOCK_INFO)
                 printf("poc=%d,y=%d,x=%d,bl=%d,ctx=%d,bp=%d: r=%d\n",
-                       f->frame_hdr.frame_offset, t->by, t->bx, bl, ctx,
+                       f->frame_hdr->frame_offset, t->by, t->bx, bl, ctx,
                        is_split ? PARTITION_SPLIT : PARTITION_V, t->ts->msac.rng);
         }
 
@@ -2205,20 +2205,20 @@
                        const int tile_row, const int tile_col,
                        const int tile_start_off)
 {
-    const int col_sb_start = f->frame_hdr.tiling.col_start_sb[tile_col];
+    const int col_sb_start = f->frame_hdr->tiling.col_start_sb[tile_col];
     const int col_sb128_start = col_sb_start >> !f->seq_hdr->sb128;
-    const int col_sb_end = f->frame_hdr.tiling.col_start_sb[tile_col + 1];
-    const int row_sb_start = f->frame_hdr.tiling.row_start_sb[tile_row];
-    const int row_sb_end = f->frame_hdr.tiling.row_start_sb[tile_row + 1];
+    const int col_sb_end = f->frame_hdr->tiling.col_start_sb[tile_col + 1];
+    const int row_sb_start = f->frame_hdr->tiling.row_start_sb[tile_row];
+    const int row_sb_end = f->frame_hdr->tiling.row_start_sb[tile_row + 1];
     const int sb_shift = f->sb_shift;
 
     ts->frame_thread.pal_idx = &f->frame_thread.pal_idx[tile_start_off * 2];
     ts->frame_thread.cf = &((int32_t *) f->frame_thread.cf)[tile_start_off * 3];
     ts->cdf = *f->in_cdf.cdf;
-    ts->last_qidx = f->frame_hdr.quant.yac;
+    ts->last_qidx = f->frame_hdr->quant.yac;
     memset(ts->last_delta_lf, 0, sizeof(ts->last_delta_lf));
 
-    msac_init(&ts->msac, data, sz, f->frame_hdr.disable_cdf_update);
+    msac_init(&ts->msac, data, sz, f->frame_hdr->disable_cdf_update);
 
     ts->tiling.row = tile_row;
     ts->tiling.col = tile_col;
@@ -2229,7 +2229,7 @@
 
     // Reference Restoration Unit (used for exp coding)
     int sb_idx, unit_idx;
-    if (f->frame_hdr.super_res.enabled) {
+    if (f->frame_hdr->super_res.enabled) {
         // vertical components only
         sb_idx = (ts->tiling.row_start >> 5) * f->sr_sb128w;
         unit_idx = (ts->tiling.row_start & 16) >> 3;
@@ -2239,13 +2239,13 @@
                    ((ts->tiling.col_start & 16) >> 4);
     }
     for (int p = 0; p < 3; p++) {
-        if (f->frame_hdr.restoration.type[p] == RESTORATION_NONE)
+        if (f->frame_hdr->restoration.type[p] == RESTORATION_NONE)
             continue;
 
-        if (f->frame_hdr.super_res.enabled) {
+        if (f->frame_hdr->super_res.enabled) {
             const int ss_hor = p && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444;
-            const int d = f->frame_hdr.super_res.width_scale_denominator;
-            const int unit_size_log2 = f->frame_hdr.restoration.unit_size[!!p];
+            const int d = f->frame_hdr->super_res.width_scale_denominator;
+            const int unit_size_log2 = f->frame_hdr->restoration.unit_size[!!p];
             const int rnd = (8 << unit_size_log2) - 1, shift = unit_size_log2 + 3;
             const int x = ((4 * ts->tiling.col_start * d >> ss_hor) + rnd) >> shift;
             const int px_x = x << (unit_size_log2 + ss_hor);
@@ -2359,10 +2359,10 @@
     const Dav1dContext *const c = f->c;
     const int sb_step = f->sb_step;
     const int tile_row = ts->tiling.row, tile_col = ts->tiling.col;
-    const int col_sb_start = f->frame_hdr.tiling.col_start_sb[tile_col];
+    const int col_sb_start = f->frame_hdr->tiling.col_start_sb[tile_col];
     const int col_sb128_start = col_sb_start >> !f->seq_hdr->sb128;
 
-    reset_context(&t->l, !(f->frame_hdr.frame_type & 1), f->frame_thread.pass);
+    reset_context(&t->l, !(f->frame_hdr->frame_type & 1), f->frame_thread.pass);
     if (f->frame_thread.pass == 2) {
         for (t->bx = ts->tiling.col_start,
              t->a = f->a + col_sb128_start + tile_row * f->sb128w;
@@ -2382,7 +2382,7 @@
     // error out on symbol decoder overread
     if (ts->msac.cnt < -15) return 1;
 
-    if (c->n_fc > 1 && f->frame_hdr.use_ref_frame_mvs) {
+    if (c->n_fc > 1 && f->frame_hdr->use_ref_frame_mvs) {
         for (int n = 0; n < 7; n++)
             if (dav1d_thread_picture_wait(&f->refp[n], 4 * (t->by + sb_step),
                                           PLANE_TYPE_BLOCK))
@@ -2415,12 +2415,12 @@
         }
         // Restoration filter
         for (int p = 0; p < 3; p++) {
-            if (f->frame_hdr.restoration.type[p] == RESTORATION_NONE)
+            if (f->frame_hdr->restoration.type[p] == RESTORATION_NONE)
                 continue;
 
             const int ss_ver = p && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420;
             const int ss_hor = p && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444;
-            const int unit_size_log2 = f->frame_hdr.restoration.unit_size[!!p];
+            const int unit_size_log2 = f->frame_hdr->restoration.unit_size[!!p];
             const int y = t->by * 4 >> ss_ver;
             const int h = (f->cur.p.h + ss_ver) >> ss_ver;
 
@@ -2432,13 +2432,13 @@
             // restoration unit
             if (y && y + half_unit > h) continue;
 
-            const enum RestorationType frame_type = f->frame_hdr.restoration.type[p];
+            const enum RestorationType frame_type = f->frame_hdr->restoration.type[p];
 
-            if (f->frame_hdr.super_res.enabled) {
+            if (f->frame_hdr->super_res.enabled) {
                 const int w = (f->sr_cur.p.p.w + ss_hor) >> ss_hor;
                 const int n_units = imax(1, (w + half_unit) >> unit_size_log2);
 
-                const int d = f->frame_hdr.super_res.width_scale_denominator;
+                const int d = f->frame_hdr->super_res.width_scale_denominator;
                 const int rnd = unit_size * 8 - 1, shift = unit_size_log2 + 3;
                 const int x0 = ((4 *  t->bx            * d >> ss_hor) + rnd) >> shift;
                 const int x1 = ((4 * (t->bx + sb_step) * d >> ss_hor) + rnd) >> shift;
@@ -2495,43 +2495,43 @@
     int retval = -EINVAL;
 
     if (f->n_tc > 1) {
-        if (f->frame_hdr.tiling.cols * f->sbh > f->tile_thread.titsati_sz) {
+        if (f->frame_hdr->tiling.cols * f->sbh > f->tile_thread.titsati_sz) {
             freep(&f->tile_thread.task_idx_to_sby_and_tile_idx);
             f->tile_thread.task_idx_to_sby_and_tile_idx =
                 malloc(sizeof(*f->tile_thread.task_idx_to_sby_and_tile_idx) *
-                       f->frame_hdr.tiling.cols * f->sbh);
+                       f->frame_hdr->tiling.cols * f->sbh);
             if (!f->tile_thread.task_idx_to_sby_and_tile_idx) return -ENOMEM;
-            f->tile_thread.titsati_sz = f->frame_hdr.tiling.cols * f->sbh;
+            f->tile_thread.titsati_sz = f->frame_hdr->tiling.cols * f->sbh;
         }
-        if (f->tile_thread.titsati_init[0] != f->frame_hdr.tiling.cols ||
+        if (f->tile_thread.titsati_init[0] != f->frame_hdr->tiling.cols ||
             f->tile_thread.titsati_init[1] != f->sbh)
         {
             for (int tile_row = 0, tile_idx = 0;
-                 tile_row < f->frame_hdr.tiling.rows; tile_row++)
+                 tile_row < f->frame_hdr->tiling.rows; tile_row++)
             {
-                for (int sby = f->frame_hdr.tiling.row_start_sb[tile_row];
-                     sby < f->frame_hdr.tiling.row_start_sb[tile_row + 1]; sby++)
+                for (int sby = f->frame_hdr->tiling.row_start_sb[tile_row];
+                     sby < f->frame_hdr->tiling.row_start_sb[tile_row + 1]; sby++)
                 {
-                    for (int tile_col = 0; tile_col < f->frame_hdr.tiling.cols;
+                    for (int tile_col = 0; tile_col < f->frame_hdr->tiling.cols;
                          tile_col++, tile_idx++)
                     {
                         f->tile_thread.task_idx_to_sby_and_tile_idx[tile_idx][0] = sby;
                         f->tile_thread.task_idx_to_sby_and_tile_idx[tile_idx][1] =
-                            tile_row * f->frame_hdr.tiling.cols + tile_col;
+                            tile_row * f->frame_hdr->tiling.cols + tile_col;
                     }
                 }
             }
-            f->tile_thread.titsati_init[0] = f->frame_hdr.tiling.cols;
+            f->tile_thread.titsati_init[0] = f->frame_hdr->tiling.cols;
             f->tile_thread.titsati_init[1] = f->sbh;
         }
     }
 
-    if (f->frame_hdr.tiling.cols * f->frame_hdr.tiling.rows > f->n_ts) {
-        f->ts = realloc(f->ts, f->frame_hdr.tiling.cols *
-                               f->frame_hdr.tiling.rows * sizeof(*f->ts));
+    if (f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows > f->n_ts) {
+        f->ts = realloc(f->ts, f->frame_hdr->tiling.cols *
+                               f->frame_hdr->tiling.rows * sizeof(*f->ts));
         if (!f->ts) return -ENOMEM;
         for (int n = f->n_ts;
-             n < f->frame_hdr.tiling.cols * f->frame_hdr.tiling.rows; n++)
+             n < f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows; n++)
         {
             Dav1dTileState *const ts = &f->ts[n];
             pthread_mutex_init(&ts->tile_thread.lock, NULL);
@@ -2541,31 +2541,31 @@
             freep(&f->frame_thread.tile_start_off);
             f->frame_thread.tile_start_off =
                 malloc(sizeof(*f->frame_thread.tile_start_off) *
-                       f->frame_hdr.tiling.cols * f->frame_hdr.tiling.rows);
+                       f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows);
             if (!f->frame_thread.tile_start_off) return -ENOMEM;
         }
-        f->n_ts = f->frame_hdr.tiling.cols * f->frame_hdr.tiling.rows;
+        f->n_ts = f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows;
     }
 
     if (c->n_fc > 1) {
         int tile_idx = 0;
-        for (int tile_row = 0; tile_row < f->frame_hdr.tiling.rows; tile_row++) {
-            int row_off = f->frame_hdr.tiling.row_start_sb[tile_row] *
+        for (int tile_row = 0; tile_row < f->frame_hdr->tiling.rows; tile_row++) {
+            int row_off = f->frame_hdr->tiling.row_start_sb[tile_row] *
                           f->sb_step * 4 * f->sb128w * 128;
-            int b_diff = (f->frame_hdr.tiling.row_start_sb[tile_row + 1] -
-                          f->frame_hdr.tiling.row_start_sb[tile_row]) * f->sb_step * 4;
-            for (int tile_col = 0; tile_col < f->frame_hdr.tiling.cols; tile_col++) {
+            int b_diff = (f->frame_hdr->tiling.row_start_sb[tile_row + 1] -
+                          f->frame_hdr->tiling.row_start_sb[tile_row]) * f->sb_step * 4;
+            for (int tile_col = 0; tile_col < f->frame_hdr->tiling.cols; tile_col++) {
                 f->frame_thread.tile_start_off[tile_idx++] = row_off + b_diff *
-                    f->frame_hdr.tiling.col_start_sb[tile_col] * f->sb_step * 4;
+                    f->frame_hdr->tiling.col_start_sb[tile_col] * f->sb_step * 4;
             }
         }
     }
 
-    if (f->sb128w * f->frame_hdr.tiling.rows > f->a_sz) {
+    if (f->sb128w * f->frame_hdr->tiling.rows > f->a_sz) {
         freep(&f->a);
-        f->a = malloc(f->sb128w * f->frame_hdr.tiling.rows * sizeof(*f->a));
+        f->a = malloc(f->sb128w * f->frame_hdr->tiling.rows * sizeof(*f->a));
         if (!f->a) return -ENOMEM;
-        f->a_sz = f->sb128w * f->frame_hdr.tiling.rows;
+        f->a_sz = f->sb128w * f->frame_hdr->tiling.rows;
     }
 
     // update allocation of block contexts for above
@@ -2650,11 +2650,11 @@
         if (!f->lf.lr_mask) return -ENOMEM;
         f->lf.lr_mask_sz = f->sr_sb128w * f->sb128h;
     }
-    if (f->frame_hdr.loopfilter.sharpness != f->lf.last_sharpness) {
-        dav1d_calc_eih(&f->lf.lim_lut, f->frame_hdr.loopfilter.sharpness);
-        f->lf.last_sharpness = f->frame_hdr.loopfilter.sharpness;
+    if (f->frame_hdr->loopfilter.sharpness != f->lf.last_sharpness) {
+        dav1d_calc_eih(&f->lf.lim_lut, f->frame_hdr->loopfilter.sharpness);
+        f->lf.last_sharpness = f->frame_hdr->loopfilter.sharpness;
     }
-    dav1d_calc_lf_values(f->lf.lvl, &f->frame_hdr, (int8_t[4]) { 0, 0, 0, 0 });
+    dav1d_calc_lf_values(f->lf.lvl, f->frame_hdr, (int8_t[4]) { 0, 0, 0, 0 });
     memset(f->lf.mask, 0, sizeof(*f->lf.mask) * f->sb128w * f->sb128h);
 
     if (f->sbh * f->sb128w * 128 > f->ipred_edge_sz) {
@@ -2667,47 +2667,47 @@
         f->ipred_edge[2] = &ptr[f->ipred_edge_sz * 2];
     }
 
-    if (f->sb128h * f->frame_hdr.tiling.cols > f->lf.re_sz) {
+    if (f->sb128h * f->frame_hdr->tiling.cols > f->lf.re_sz) {
         freep(&f->lf.tx_lpf_right_edge[0]);
         f->lf.tx_lpf_right_edge[0] = malloc((f->sb128h * 32 * 2) *
-                                            f->frame_hdr.tiling.cols);
+                                            f->frame_hdr->tiling.cols);
         if (!f->lf.tx_lpf_right_edge[0]) return -ENOMEM;
         f->lf.tx_lpf_right_edge[1] = f->lf.tx_lpf_right_edge[0] +
-                                     f->sb128h * 32 * f->frame_hdr.tiling.cols;
-        f->lf.re_sz = f->sb128h * f->frame_hdr.tiling.cols;
+                                     f->sb128h * 32 * f->frame_hdr->tiling.cols;
+        f->lf.re_sz = f->sb128h * f->frame_hdr->tiling.cols;
     }
 
     // init ref mvs
-    if ((f->frame_hdr.frame_type & 1) || f->frame_hdr.allow_intrabc) {
+    if ((f->frame_hdr->frame_type & 1) || f->frame_hdr->allow_intrabc) {
         f->mvs = f->mvs_ref->data;
         const int order_hint_n_bits = f->seq_hdr->order_hint * f->seq_hdr->order_hint_n_bits;
         const int ret = av1_init_ref_mv_common(f->libaom_cm, f->bw >> 1, f->bh >> 1,
                                                f->b4_stride, f->seq_hdr->sb128,
                                                f->mvs, f->ref_mvs, f->cur.poc, f->refpoc,
-                                               f->refrefpoc, f->frame_hdr.gmv,
-                                               f->frame_hdr.hp, f->frame_hdr.force_integer_mv,
-                                               f->frame_hdr.use_ref_frame_mvs,
+                                               f->refrefpoc, f->frame_hdr->gmv,
+                                               f->frame_hdr->hp, f->frame_hdr->force_integer_mv,
+                                               f->frame_hdr->use_ref_frame_mvs,
                                                order_hint_n_bits);
         if (ret < 0) return -ENOMEM;
-        if (c->n_fc == 1 && f->frame_hdr.use_ref_frame_mvs)
+        if (c->n_fc == 1 && f->frame_hdr->use_ref_frame_mvs)
             av1_init_ref_mv_tile_row(f->libaom_cm, 0, f->bw, 0, f->bh);
     }
 
     // setup dequant tables
-    init_quant_tables(f->seq_hdr, &f->frame_hdr, f->frame_hdr.quant.yac, f->dq);
-    if (f->frame_hdr.quant.qm)
+    init_quant_tables(f->seq_hdr, f->frame_hdr, f->frame_hdr->quant.yac, f->dq);
+    if (f->frame_hdr->quant.qm)
         for (int j = 0; j < N_RECT_TX_SIZES; j++) {
-            f->qm[0][j][0] = dav1d_qm_tbl[f->frame_hdr.quant.qm_y][0][j];
-            f->qm[0][j][1] = dav1d_qm_tbl[f->frame_hdr.quant.qm_u][1][j];
-            f->qm[0][j][2] = dav1d_qm_tbl[f->frame_hdr.quant.qm_v][1][j];
+            f->qm[0][j][0] = dav1d_qm_tbl[f->frame_hdr->quant.qm_y][0][j];
+            f->qm[0][j][1] = dav1d_qm_tbl[f->frame_hdr->quant.qm_u][1][j];
+            f->qm[0][j][2] = dav1d_qm_tbl[f->frame_hdr->quant.qm_v][1][j];
         }
-    for (int i = f->frame_hdr.quant.qm; i < 2; i++)
+    for (int i = f->frame_hdr->quant.qm; i < 2; i++)
         for (int tx = 0; tx < N_RECT_TX_SIZES; tx++)
             for (int pl = 0; pl < 3; pl++)
                 f->qm[i][tx][pl] = dav1d_qm_tbl[15][!!pl][tx];
 
     // setup jnt_comp weights
-    if (f->frame_hdr.switchable_comp_refs) {
+    if (f->frame_hdr->switchable_comp_refs) {
         for (int i = 0; i < 7; i++) {
             const unsigned ref0poc = f->refp[i].p.poc;
 
@@ -2752,7 +2752,7 @@
     f->lf.tile_row = 1;
 
     dav1d_cdf_thread_wait(&f->in_cdf);
-    if (f->frame_hdr.refresh_context)
+    if (f->frame_hdr->refresh_context)
         memcpy(f->out_cdf.cdf, f->in_cdf.cdf, sizeof(*f->in_cdf.cdf));
 
     // parse individual tiles per tile group
@@ -2766,12 +2766,12 @@
             if (j == f->tile[i].end) {
                 tile_sz = size;
             } else {
-                if (f->frame_hdr.tiling.n_bytes > size) goto error;
+                if (f->frame_hdr->tiling.n_bytes > size) goto error;
                 tile_sz = 0;
-                for (unsigned k = 0; k < f->frame_hdr.tiling.n_bytes; k++)
+                for (unsigned k = 0; k < f->frame_hdr->tiling.n_bytes; k++)
                     tile_sz |= (unsigned)*data++ << (k * 8);
                 tile_sz++;
-                size -= f->frame_hdr.tiling.n_bytes;
+                size -= f->frame_hdr->tiling.n_bytes;
                 if (tile_sz > size) goto error;
             }
 
@@ -2778,11 +2778,11 @@
             setup_tile(&f->ts[j], f, data, tile_sz, tile_row, tile_col++,
                        c->n_fc > 1 ? f->frame_thread.tile_start_off[j] : 0);
 
-            if (tile_col == f->frame_hdr.tiling.cols) {
+            if (tile_col == f->frame_hdr->tiling.cols) {
                 tile_col = 0;
                 tile_row++;
             }
-            if (j == f->frame_hdr.tiling.update && f->frame_hdr.refresh_context)
+            if (j == f->frame_hdr->tiling.update && f->frame_hdr->refresh_context)
                 update_set = 1;
             data += tile_sz;
             size -= tile_sz;
@@ -2799,7 +2799,7 @@
     // - pass 1 means symbol parsing only;
     // - pass 2 means reconstruction and loop filtering.
 
-    const int uses_2pass = c->n_fc > 1 && f->frame_hdr.refresh_context;
+    const int uses_2pass = c->n_fc > 1 && f->frame_hdr->refresh_context;
     for (f->frame_thread.pass = uses_2pass;
          f->frame_thread.pass <= 2 * uses_2pass; f->frame_thread.pass++)
     {
@@ -2807,8 +2807,8 @@
             f->frame_thread.pass == 0 ? PLANE_TYPE_ALL :
             f->frame_thread.pass == 1 ? PLANE_TYPE_BLOCK : PLANE_TYPE_Y;
 
-        for (int n = 0; n < f->sb128w * f->frame_hdr.tiling.rows; n++)
-            reset_context(&f->a[n], !(f->frame_hdr.frame_type & 1), f->frame_thread.pass);
+        for (int n = 0; n < f->sb128w * f->frame_hdr->tiling.rows; n++)
+            reset_context(&f->a[n], !(f->frame_hdr->frame_type & 1), f->frame_thread.pass);
 
         if (f->n_tc == 1) {
             Dav1dTileContext *const t = f->tc;
@@ -2816,15 +2816,15 @@
             // no tile threading - we explicitly interleave tile/sbrow decoding
             // and post-filtering, so that the full process runs in-line, so
             // that frame threading is still possible
-            for (int tile_row = 0; tile_row < f->frame_hdr.tiling.rows; tile_row++) {
+            for (int tile_row = 0; tile_row < f->frame_hdr->tiling.rows; tile_row++) {
                 const int sbh_end =
-                    imin(f->frame_hdr.tiling.row_start_sb[tile_row + 1], f->sbh);
-                for (int sby = f->frame_hdr.tiling.row_start_sb[tile_row];
+                    imin(f->frame_hdr->tiling.row_start_sb[tile_row + 1], f->sbh);
+                for (int sby = f->frame_hdr->tiling.row_start_sb[tile_row];
                      sby < sbh_end; sby++)
                 {
                     t->by = sby << (4 + f->seq_hdr->sb128);
-                    for (int tile_col = 0; tile_col < f->frame_hdr.tiling.cols; tile_col++) {
-                        t->ts = &f->ts[tile_row * f->frame_hdr.tiling.cols + tile_col];
+                    for (int tile_col = 0; tile_col < f->frame_hdr->tiling.cols; tile_col++) {
+                        t->ts = &f->ts[tile_row * f->frame_hdr->tiling.cols + tile_col];
 
                         if (dav1d_decode_tile_sbrow(t)) goto error;
                     }
@@ -2842,15 +2842,15 @@
 
             pthread_mutex_lock(&f->tile_thread.lock);
             assert(!f->tile_thread.tasks_left);
-            if (f->frame_thread.pass == 1 || f->n_tc >= f->frame_hdr.tiling.cols) {
+            if (f->frame_thread.pass == 1 || f->n_tc >= f->frame_hdr->tiling.cols) {
                 // we can (or in fact, if >, we need to) do full tile decoding.
                 // loopfilter happens below
-                num_tasks = f->frame_hdr.tiling.cols * f->frame_hdr.tiling.rows;
+                num_tasks = f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows;
             } else {
                 // we need to interleave sbrow decoding for all tile cols in a
                 // tile row, since otherwise subsequent threads will be blocked
                 // waiting for the post-filter to complete
-                num_tasks = f->sbh * f->frame_hdr.tiling.cols;
+                num_tasks = f->sbh * f->frame_hdr->tiling.cols;
             }
             f->tile_thread.num_tasks = f->tile_thread.tasks_left = num_tasks;
             pthread_cond_broadcast(&f->tile_thread.cond);
@@ -2857,16 +2857,16 @@
             pthread_mutex_unlock(&f->tile_thread.lock);
 
             // loopfilter + cdef + restoration
-            for (int tile_row = 0; tile_row < f->frame_hdr.tiling.rows; tile_row++) {
-                for (int sby = f->frame_hdr.tiling.row_start_sb[tile_row];
-                     sby < f->frame_hdr.tiling.row_start_sb[tile_row + 1]; sby++)
+            for (int tile_row = 0; tile_row < f->frame_hdr->tiling.rows; tile_row++) {
+                for (int sby = f->frame_hdr->tiling.row_start_sb[tile_row];
+                     sby < f->frame_hdr->tiling.row_start_sb[tile_row + 1]; sby++)
                 {
-                    for (int tile_col = 0; tile_col < f->frame_hdr.tiling.cols;
+                    for (int tile_col = 0; tile_col < f->frame_hdr->tiling.cols;
                          tile_col++)
                     {
                         int progress;
                         Dav1dTileState *const ts =
-                            &f->ts[tile_row * f->frame_hdr.tiling.cols + tile_col];
+                            &f->ts[tile_row * f->frame_hdr->tiling.cols + tile_col];
 
                         if ((progress = atomic_load(&ts->progress)) <= sby) {
                             pthread_mutex_lock(&ts->tile_thread.lock);
@@ -2902,17 +2902,17 @@
             pthread_mutex_unlock(&f->tile_thread.lock);
         }
 
-        if (f->frame_thread.pass <= 1 && f->frame_hdr.refresh_context) {
+        if (f->frame_thread.pass <= 1 && f->frame_hdr->refresh_context) {
             // cdf update
             if (update_set)
-                dav1d_update_tile_cdf(&f->frame_hdr, f->out_cdf.cdf,
-                                      &f->ts[f->frame_hdr.tiling.update].cdf);
+                dav1d_update_tile_cdf(f->frame_hdr, f->out_cdf.cdf,
+                                      &f->ts[f->frame_hdr->tiling.update].cdf);
             dav1d_cdf_thread_signal(&f->out_cdf);
         }
         if (f->frame_thread.pass == 1) {
             assert(c->n_fc > 1);
             for (int tile_idx = 0;
-                 tile_idx < f->frame_hdr.tiling.rows * f->frame_hdr.tiling.cols;
+                 tile_idx < f->frame_hdr->tiling.rows * f->frame_hdr->tiling.cols;
                  tile_idx++)
             {
                 Dav1dTileState *const ts = &f->ts[tile_idx];
@@ -2920,7 +2920,7 @@
                 ts->frame_thread.pal_idx = &f->frame_thread.pal_idx[tile_start_off * 2];
                 ts->frame_thread.cf = &((int32_t *) f->frame_thread.cf)[tile_start_off * 3];
                 if (f->n_tc > 0) {
-                    unsigned row_sb_start = f->frame_hdr.tiling.row_start_sb[ts->tiling.row];
+                    unsigned row_sb_start = f->frame_hdr->tiling.row_start_sb[ts->tiling.row];
                     atomic_init(&ts->progress, row_sb_start);
                 }
             }
@@ -2940,7 +2940,7 @@
     dav1d_picture_unref(&f->cur);
     dav1d_thread_picture_unref(&f->sr_cur);
     dav1d_cdf_thread_unref(&f->in_cdf);
-    if (f->frame_hdr.refresh_context) {
+    if (f->frame_hdr->refresh_context) {
         dav1d_cdf_thread_signal(&f->out_cdf);
         dav1d_cdf_thread_unref(&f->out_cdf);
     }
@@ -2990,7 +2990,7 @@
     f->seq_hdr = c->seq_hdr;
     f->seq_hdr_ref = c->seq_hdr_ref;
     dav1d_ref_inc(f->seq_hdr_ref);
-    f->frame_hdr = c->frame_hdr;
+    *f->frame_hdr = *c->frame_hdr;
     const int bd_idx = (f->seq_hdr->bpc - 8) >> 1;
     f->dsp = &c->dsp[bd_idx];
 
@@ -3039,9 +3039,9 @@
     }
 #undef assign_bitdepth_case
 
-    if (f->frame_hdr.frame_type & 1) {
-        if (f->frame_hdr.primary_ref_frame != PRIMARY_REF_NONE) {
-            const int pri_ref = f->frame_hdr.refidx[f->frame_hdr.primary_ref_frame];
+    if (f->frame_hdr->frame_type & 1) {
+        if (f->frame_hdr->primary_ref_frame != PRIMARY_REF_NONE) {
+            const int pri_ref = f->frame_hdr->refidx[f->frame_hdr->primary_ref_frame];
             if (!c->refs[pri_ref].p.p.data[0]) {
                 res = -EINVAL;
                 goto error;
@@ -3048,12 +3048,12 @@
             }
         }
         for (int i = 0; i < 7; i++) {
-            const int refidx = f->frame_hdr.refidx[i];
+            const int refidx = f->frame_hdr->refidx[i];
             if (!c->refs[refidx].p.p.data[0] ||
-                f->frame_hdr.width[0] * 2 < c->refs[refidx].p.p.p.w ||
-                f->frame_hdr.height * 2 < c->refs[refidx].p.p.p.h ||
-                f->frame_hdr.width[0] > c->refs[refidx].p.p.p.w * 16 ||
-                f->frame_hdr.height > c->refs[refidx].p.p.p.h * 16 ||
+                f->frame_hdr->width[0] * 2 < c->refs[refidx].p.p.p.w ||
+                f->frame_hdr->height * 2 < c->refs[refidx].p.p.p.h ||
+                f->frame_hdr->width[0] > c->refs[refidx].p.p.p.w * 16 ||
+                f->frame_hdr->height > c->refs[refidx].p.p.p.h * 16 ||
                 f->seq_hdr->layout != c->refs[refidx].p.p.p.layout ||
                 f->seq_hdr->bpc != c->refs[refidx].p.p.p.bpc)
             {
@@ -3064,34 +3064,34 @@
             }
             dav1d_thread_picture_ref(&f->refp[i], &c->refs[refidx].p);
             f->ref_coded_width[i] = c->refs[refidx].coded_width;
-            if (f->frame_hdr.width[0] != c->refs[refidx].p.p.p.w ||
-                f->frame_hdr.height != c->refs[refidx].p.p.p.h)
+            if (f->frame_hdr->width[0] != c->refs[refidx].p.p.p.w ||
+                f->frame_hdr->height != c->refs[refidx].p.p.p.h)
             {
 #define scale_fac(ref_sz, this_sz) \
     ((((ref_sz) << 14) + ((this_sz) >> 1)) / (this_sz))
                 f->svc[i][0].scale = scale_fac(c->refs[refidx].p.p.p.w,
-                                               f->frame_hdr.width[0]);
+                                               f->frame_hdr->width[0]);
                 f->svc[i][1].scale = scale_fac(c->refs[refidx].p.p.p.h,
-                                               f->frame_hdr.height);
+                                               f->frame_hdr->height);
                 f->svc[i][0].step = (f->svc[i][0].scale + 8) >> 4;
                 f->svc[i][1].step = (f->svc[i][1].scale + 8) >> 4;
             } else {
                 f->svc[i][0].scale = 0;
             }
-            f->gmv_warp_allowed[i] = f->frame_hdr.gmv[i].type > WM_TYPE_TRANSLATION &&
-                                     !f->frame_hdr.force_integer_mv &&
-                                     !dav1d_get_shear_params(&f->frame_hdr.gmv[i]);
+            f->gmv_warp_allowed[i] = f->frame_hdr->gmv[i].type > WM_TYPE_TRANSLATION &&
+                                     !f->frame_hdr->force_integer_mv &&
+                                     !dav1d_get_shear_params(&f->frame_hdr->gmv[i]);
         }
     }
 
     // setup entropy
-    if (f->frame_hdr.primary_ref_frame == PRIMARY_REF_NONE) {
-        dav1d_init_states(&f->in_cdf, f->frame_hdr.quant.yac);
+    if (f->frame_hdr->primary_ref_frame == PRIMARY_REF_NONE) {
+        dav1d_init_states(&f->in_cdf, f->frame_hdr->quant.yac);
     } else {
-        const int pri_ref = f->frame_hdr.refidx[f->frame_hdr.primary_ref_frame];
+        const int pri_ref = f->frame_hdr->refidx[f->frame_hdr->primary_ref_frame];
         dav1d_cdf_thread_ref(&f->in_cdf, &c->cdf[pri_ref]);
     }
-    if (f->frame_hdr.refresh_context) {
+    if (f->frame_hdr->refresh_context) {
         dav1d_cdf_thread_alloc(&f->out_cdf, c->n_fc > 1 ? &f->frame_thread.td : NULL);
     }
 
@@ -3102,16 +3102,16 @@
     c->n_tile_data = 0;
 
     // allocate frame
-    res = dav1d_thread_picture_alloc(&f->sr_cur, f->frame_hdr.width[1],
-                                     f->frame_hdr.height,
+    res = dav1d_thread_picture_alloc(&f->sr_cur, f->frame_hdr->width[1],
+                                     f->frame_hdr->height,
                                      f->seq_hdr->layout, f->seq_hdr->bpc,
                                      c->n_fc > 1 ? &f->frame_thread.td : NULL,
-                                     f->frame_hdr.show_frame, &c->allocator);
+                                     f->frame_hdr->show_frame, &c->allocator);
     if (res < 0) goto error;
 
-    f->sr_cur.p.poc = f->frame_hdr.frame_offset;
-    f->sr_cur.p.p.type = f->frame_hdr.frame_type;
-    f->sr_cur.p.p.film_grain = f->frame_hdr.film_grain.data;
+    f->sr_cur.p.poc = f->frame_hdr->frame_offset;
+    f->sr_cur.p.p.type = f->frame_hdr->frame_type;
+    f->sr_cur.p.p.film_grain = f->frame_hdr->film_grain.data;
     f->sr_cur.p.p.pri = f->seq_hdr->pri;
     f->sr_cur.p.p.trc = f->seq_hdr->trc;
     f->sr_cur.p.p.mtrx = f->seq_hdr->mtrx;
@@ -3118,16 +3118,16 @@
     f->sr_cur.p.p.chr = f->seq_hdr->chr;
     f->sr_cur.p.p.fullrange = f->seq_hdr->color_range;
     f->sr_cur.p.m = f->tile[0].data.m;
-    f->sr_cur.p.p.spatial_id = f->frame_hdr.spatial_id;
+    f->sr_cur.p.p.spatial_id = f->frame_hdr->spatial_id;
 
-    if (f->frame_hdr.super_res.enabled) {
-        res = dav1d_picture_alloc_copy(&f->cur, f->frame_hdr.width[0], &f->sr_cur.p);
+    if (f->frame_hdr->super_res.enabled) {
+        res = dav1d_picture_alloc_copy(&f->cur, f->frame_hdr->width[0], &f->sr_cur.p);
         if (res < 0) goto error;
     } else {
         dav1d_picture_ref(&f->cur, &f->sr_cur.p);
     }
 
-    if (f->frame_hdr.super_res.enabled) {
+    if (f->frame_hdr->super_res.enabled) {
         f->resize_step[0] = scale_fac(f->cur.p.w, f->sr_cur.p.p.w);
         const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444;
         const int in_cw = (f->cur.p.w + ss_hor) >> ss_hor;
@@ -3140,16 +3140,16 @@
 
     // move f->cur into output queue
     if (c->n_fc == 1) {
-        if (f->frame_hdr.show_frame)
+        if (f->frame_hdr->show_frame)
             dav1d_picture_ref(&c->out, &f->sr_cur.p);
     } else {
         dav1d_thread_picture_ref(out_delayed, &f->sr_cur);
     }
 
-    f->w4 = (f->frame_hdr.width[0] + 3) >> 2;
-    f->h4 = (f->frame_hdr.height + 3) >> 2;
-    f->bw = ((f->frame_hdr.width[0] + 7) >> 3) << 1;
-    f->bh = ((f->frame_hdr.height + 7) >> 3) << 1;
+    f->w4 = (f->frame_hdr->width[0] + 3) >> 2;
+    f->h4 = (f->frame_hdr->height + 3) >> 2;
+    f->bw = ((f->frame_hdr->width[0] + 7) >> 3) << 1;
+    f->bh = ((f->frame_hdr->height + 7) >> 3) << 1;
     f->sb128w = (f->bw + 31) >> 5;
     f->sb128h = (f->bh + 31) >> 5;
     f->sb_shift = 4 + f->seq_hdr->sb128;
@@ -3158,19 +3158,19 @@
     f->b4_stride = (f->bw + 31) & ~31;
 
     // ref_mvs
-    if ((f->frame_hdr.frame_type & 1) || f->frame_hdr.allow_intrabc) {
+    if ((f->frame_hdr->frame_type & 1) || f->frame_hdr->allow_intrabc) {
         f->mvs_ref = dav1d_ref_create(f->sb128h * 32 * f->b4_stride *
                                       sizeof(*f->mvs));
         f->mvs = f->mvs_ref->data;
-        if (!f->frame_hdr.allow_intrabc) {
+        if (!f->frame_hdr->allow_intrabc) {
             for (int i = 0; i < 7; i++)
                 f->refpoc[i] = f->refp[i].p.poc;
         } else {
             memset(f->refpoc, 0, sizeof(f->refpoc));
         }
-        if (f->frame_hdr.use_ref_frame_mvs) {
+        if (f->frame_hdr->use_ref_frame_mvs) {
             for (int i = 0; i < 7; i++) {
-                const int refidx = f->frame_hdr.refidx[i];
+                const int refidx = f->frame_hdr->refidx[i];
                 if (c->refs[refidx].refmvs != NULL &&
                     f->ref_coded_width[i] == f->cur.p.w &&
                     f->refp[i].p.p.h == f->cur.p.h)
@@ -3194,7 +3194,7 @@
     }
 
     // segmap
-    if (f->frame_hdr.segmentation.enabled) {
+    if (f->frame_hdr->segmentation.enabled) {
         // By default, the previous segmentation map is not initialised.
         f->prev_segmap_ref = NULL;
         f->prev_segmap = NULL;
@@ -3201,13 +3201,13 @@
 
         // We might need a previous frame's segmentation map. This
         // happens if there is either no update or a temporal update.
-        if (f->frame_hdr.segmentation.temporal || !f->frame_hdr.segmentation.update_map) {
-            const int pri_ref = f->frame_hdr.primary_ref_frame;
+        if (f->frame_hdr->segmentation.temporal || !f->frame_hdr->segmentation.update_map) {
+            const int pri_ref = f->frame_hdr->primary_ref_frame;
             assert(pri_ref != PRIMARY_REF_NONE);
             const int ref_w = ((f->ref_coded_width[pri_ref] + 7) >> 3) << 1;
             const int ref_h = ((f->refp[pri_ref].p.p.h + 7) >> 3) << 1;
             if (ref_w == f->bw && ref_h == f->bh) {
-                f->prev_segmap_ref = c->refs[f->frame_hdr.refidx[pri_ref]].segmap;
+                f->prev_segmap_ref = c->refs[f->frame_hdr->refidx[pri_ref]].segmap;
                 if (f->prev_segmap_ref) {
                     dav1d_ref_inc(f->prev_segmap_ref);
                     f->prev_segmap = f->prev_segmap_ref->data;
@@ -3215,7 +3215,7 @@
             }
         }
 
-        if (f->frame_hdr.segmentation.update_map) {
+        if (f->frame_hdr->segmentation.update_map) {
             // We're updating an existing map, but need somewhere to
             // put the new values. Allocate them here (the data
             // actually gets set elsewhere)
@@ -3241,23 +3241,23 @@
 
     // update references etc.
     for (int i = 0; i < 8; i++) {
-        if (f->frame_hdr.refresh_frame_flags & (1 << i)) {
+        if (f->frame_hdr->refresh_frame_flags & (1 << i)) {
             if (c->refs[i].p.p.data[0])
                 dav1d_thread_picture_unref(&c->refs[i].p);
             dav1d_thread_picture_ref(&c->refs[i].p, &f->sr_cur);
-            c->refs[i].coded_width = f->frame_hdr.width[0];
+            c->refs[i].coded_width = f->frame_hdr->width[0];
 
             if (c->cdf[i].cdf) dav1d_cdf_thread_unref(&c->cdf[i]);
-            if (f->frame_hdr.refresh_context) {
+            if (f->frame_hdr->refresh_context) {
                 dav1d_cdf_thread_ref(&c->cdf[i], &f->out_cdf);
             } else {
                 dav1d_cdf_thread_ref(&c->cdf[i], &f->in_cdf);
             }
             c->refs[i].lf_mode_ref_deltas =
-                f->frame_hdr.loopfilter.mode_ref_deltas;
-            c->refs[i].seg_data = f->frame_hdr.segmentation.seg_data;
-            memcpy(c->refs[i].gmv, f->frame_hdr.gmv, sizeof(c->refs[i].gmv));
-            c->refs[i].film_grain = f->frame_hdr.film_grain.data;
+                f->frame_hdr->loopfilter.mode_ref_deltas;
+            c->refs[i].seg_data = f->frame_hdr->segmentation.seg_data;
+            memcpy(c->refs[i].gmv, f->frame_hdr->gmv, sizeof(c->refs[i].gmv));
+            c->refs[i].film_grain = f->frame_hdr->film_grain.data;
 
             dav1d_ref_dec(&c->refs[i].segmap);
             c->refs[i].segmap = f->cur_segmap_ref;
@@ -3264,13 +3264,13 @@
             if (f->cur_segmap_ref)
                 dav1d_ref_inc(f->cur_segmap_ref);
             dav1d_ref_dec(&c->refs[i].refmvs);
-            if (!f->frame_hdr.allow_intrabc) {
+            if (!f->frame_hdr->allow_intrabc) {
                 c->refs[i].refmvs = f->mvs_ref;
                 if (f->mvs_ref)
                     dav1d_ref_inc(f->mvs_ref);
             }
             memcpy(c->refs[i].refpoc, f->refpoc, sizeof(f->refpoc));
-            c->refs[i].qidx = f->frame_hdr.quant.yac;
+            c->refs[i].qidx = f->frame_hdr->quant.yac;
         }
     }
 
@@ -3278,7 +3278,7 @@
         if ((res = dav1d_decode_frame(f)) < 0) {
             dav1d_picture_unref(&c->out);
             for (int i = 0; i < 8; i++) {
-                if (f->frame_hdr.refresh_frame_flags & (1 << i)) {
+                if (f->frame_hdr->refresh_frame_flags & (1 << i)) {
                     if (c->refs[i].p.p.data[0])
                         dav1d_thread_picture_unref(&c->refs[i].p);
                     if (c->cdf[i].cdf)
@@ -3297,7 +3297,7 @@
     return 0;
 error:
     dav1d_cdf_thread_unref(&f->in_cdf);
-    if (f->frame_hdr.refresh_context)
+    if (f->frame_hdr->refresh_context)
         dav1d_cdf_thread_unref(&f->out_cdf);
     for (int i = 0; i < 7; i++) {
         if (f->refp[i].p.data[0])
--- a/src/internal.h
+++ b/src/internal.h
@@ -79,7 +79,7 @@
     int n_tiles;
     Dav1dRef *seq_hdr_ref;
     Av1SequenceHeader *seq_hdr;
-    Av1FrameHeader frame_hdr; // FIXME make ref?
+    Av1FrameHeader *frame_hdr, frame_hdr_mem; // FIXME make ref?
 
     // decoded output picture queue
     Dav1dData in;
@@ -125,7 +125,7 @@
 struct Dav1dFrameContext {
     Dav1dRef *seq_hdr_ref;
     Av1SequenceHeader *seq_hdr;
-    Av1FrameHeader frame_hdr;
+    Av1FrameHeader *frame_hdr, frame_hdr_mem;
     Dav1dThreadPicture refp[7];
     Dav1dPicture cur; // during block coding / reconstruction
     Dav1dThreadPicture sr_cur; // after super-resolution upscaling
--- a/src/lf_apply_tmpl.c
+++ b/src/lf_apply_tmpl.c
@@ -194,7 +194,7 @@
     const uint8_t *lpf_y = &f->lf.tx_lpf_right_edge[0][sby << sbl2];
     const uint8_t *lpf_uv = &f->lf.tx_lpf_right_edge[1][sby << (sbl2 - ss_ver)];
     for (int tile_col = 1;; tile_col++) {
-        x = f->frame_hdr.tiling.col_start_sb[tile_col];
+        x = f->frame_hdr->tiling.col_start_sb[tile_col];
         if ((x << sbl2) >= f->bw) break;
         const int bx4 = x & is_sb64 ? 16 : 0, cbx4 = bx4 >> ss_hor;
         x >>= is_sb64;
@@ -279,7 +279,7 @@
                             imin(32, f->w4 - x * 32), starty4, endy4);
     }
 
-    if (!f->frame_hdr.loopfilter.level_u && !f->frame_hdr.loopfilter.level_v)
+    if (!f->frame_hdr->loopfilter.level_u && !f->frame_hdr->loopfilter.level_v)
         return;
 
     ptrdiff_t uv_off;
--- a/src/lib.c
+++ b/src/lib.c
@@ -89,6 +89,7 @@
     if (!c) goto error;
     memset(c, 0, sizeof(*c));
 
+    c->frame_hdr = &c->frame_hdr_mem;
     c->allocator = s->allocator;
     c->apply_grain = s->apply_grain;
     c->operating_point = s->operating_point;
@@ -107,6 +108,7 @@
     for (int n = 0; n < s->n_frame_threads; n++) {
         Dav1dFrameContext *const f = &c->fc[n];
         f->c = c;
+        f->frame_hdr = &f->frame_hdr_mem;
         f->lf.last_sharpness = -1;
         f->n_tc = s->n_tile_threads;
         f->tc = dav1d_alloc_aligned(sizeof(*f->tc) * s->n_tile_threads, 32);
--- a/src/lr_apply_tmpl.c
+++ b/src/lr_apply_tmpl.c
@@ -49,8 +49,8 @@
                        const int ss_ver, const int sb128,
                        int row, const int row_h, const int src_w, const int ss_hor)
 {
-    const int dst_w = f->frame_hdr.super_res.enabled ?
-                      (f->frame_hdr.width[1] + ss_hor) >> ss_hor : src_w;
+    const int dst_w = f->frame_hdr->super_res.enabled ?
+                      (f->frame_hdr->width[1] + ss_hor) >> ss_hor : src_w;
 
     // The first stripe of the frame is shorter by 8 luma pixel rows.
     int stripe_h = (64 - 8 * !row) >> ss_ver;
@@ -72,7 +72,7 @@
     dst += 4 * PXSTRIDE(dst_stride);
     src += (stripe_h - 2) * PXSTRIDE(src_stride);
 
-    if (f->frame_hdr.super_res.enabled) {
+    if (f->frame_hdr->super_res.enabled) {
         for (; row + stripe_h <= row_h; row += stripe_h) {
             f->dsp->mc.resize(dst, dst_stride, src, src_stride,
                               dst_w, src_w, 4, f->resize_step[ss_hor],
@@ -103,9 +103,9 @@
 
     // TODO Also check block level restore type to reduce copying.
     const int restore_planes =
-        ((f->frame_hdr.restoration.type[0] != RESTORATION_NONE) << 0) +
-        ((f->frame_hdr.restoration.type[1] != RESTORATION_NONE) << 1) +
-        ((f->frame_hdr.restoration.type[2] != RESTORATION_NONE) << 2);
+        ((f->frame_hdr->restoration.type[0] != RESTORATION_NONE) << 0) +
+        ((f->frame_hdr->restoration.type[1] != RESTORATION_NONE) << 1) +
+        ((f->frame_hdr->restoration.type[2] != RESTORATION_NONE) << 2);
 
     if (restore_planes & LR_RESTORE_Y) {
         const int h = f->bh << 2;
@@ -211,7 +211,7 @@
     const int ss_hor = chroma & (f->sr_cur.p.p.layout != DAV1D_PIXEL_LAYOUT_I444);
     const ptrdiff_t p_stride = f->sr_cur.p.stride[chroma];
 
-    const int unit_size_log2 = f->frame_hdr.restoration.unit_size[!!plane];
+    const int unit_size_log2 = f->frame_hdr->restoration.unit_size[!!plane];
     const int unit_size = 1 << unit_size_log2;
     const int half_unit_size = unit_size >> 1;
     const int max_unit_size = unit_size + half_unit_size;
@@ -271,9 +271,9 @@
     const ptrdiff_t *const dst_stride = f->sr_cur.p.stride;
 
     const int restore_planes =
-        ((f->frame_hdr.restoration.type[0] != RESTORATION_NONE) << 0) +
-        ((f->frame_hdr.restoration.type[1] != RESTORATION_NONE) << 1) +
-        ((f->frame_hdr.restoration.type[2] != RESTORATION_NONE) << 2);
+        ((f->frame_hdr->restoration.type[0] != RESTORATION_NONE) << 0) +
+        ((f->frame_hdr->restoration.type[1] != RESTORATION_NONE) << 1) +
+        ((f->frame_hdr->restoration.type[2] != RESTORATION_NONE) << 2);
 
     if (restore_planes & LR_RESTORE_Y) {
         const int h = f->sr_cur.p.p.h;
--- a/src/obu.c
+++ b/src/obu.c
@@ -275,13 +275,13 @@
                            const int use_ref)
 {
     const Av1SequenceHeader *const seqhdr = c->seq_hdr;
-    Av1FrameHeader *const hdr = &c->frame_hdr;
+    Av1FrameHeader *const hdr = c->frame_hdr;
 
     if (use_ref) {
         for (int i = 0; i < 7; i++) {
             if (dav1d_get_bits(gb, 1)) {
                 Dav1dThreadPicture *const ref =
-                    &c->refs[c->frame_hdr.refidx[i]].p;
+                    &c->refs[c->frame_hdr->refidx[i]].p;
                 if (!ref->p.data[0]) return -1;
                 // FIXME render_* may be wrong
                 hdr->render_width = hdr->width[1] = ref->p.p.w;
@@ -345,7 +345,7 @@
     const uint8_t *const init_ptr = gb->ptr;
 #endif
     const Av1SequenceHeader *const seqhdr = c->seq_hdr;
-    Av1FrameHeader *const hdr = &c->frame_hdr;
+    Av1FrameHeader *const hdr = c->frame_hdr;
     int res;
 
     hdr->show_existing_frame =
@@ -1097,13 +1097,13 @@
 
 static void parse_tile_hdr(Dav1dContext *const c, GetBits *const gb) {
     int have_tile_pos = 0;
-    const int n_tiles = c->frame_hdr.tiling.cols * c->frame_hdr.tiling.rows;
+    const int n_tiles = c->frame_hdr->tiling.cols * c->frame_hdr->tiling.rows;
     if (n_tiles > 1)
         have_tile_pos = dav1d_get_bits(gb, 1);
 
     if (have_tile_pos) {
-        const int n_bits = c->frame_hdr.tiling.log2_cols +
-                           c->frame_hdr.tiling.log2_rows;
+        const int n_bits = c->frame_hdr->tiling.log2_cols +
+                           c->frame_hdr->tiling.log2_rows;
         c->tile[c->n_tile_data].start = dav1d_get_bits(gb, n_bits);
         c->tile[c->n_tile_data].end = dav1d_get_bits(gb, n_bits);
     } else {
@@ -1241,8 +1241,8 @@
     case OBU_FRAME_HDR:
         c->have_frame_hdr = 0;
         if (!c->seq_hdr) goto error;
-        c->frame_hdr.temporal_id = temporal_id;
-        c->frame_hdr.spatial_id = spatial_id;
+        c->frame_hdr->temporal_id = temporal_id;
+        c->frame_hdr->spatial_id = spatial_id;
         if ((res = parse_frame_hdr(c, &gb)) < 0)
             return res;
         for (int n = 0; n < c->n_tile_data; n++)
@@ -1260,7 +1260,7 @@
             break;
         }
         // OBU_FRAMEs shouldn't be signalled with show_existing_frame
-        if (c->frame_hdr.show_existing_frame) goto error;
+        if (c->frame_hdr->show_existing_frame) goto error;
 
         c->have_frame_hdr = 1;
 
@@ -1314,7 +1314,7 @@
     }
 
     if (c->seq_hdr && c->have_frame_hdr &&
-        c->n_tiles == c->frame_hdr.tiling.cols * c->frame_hdr.tiling.rows)
+        c->n_tiles == c->frame_hdr->tiling.cols * c->frame_hdr->tiling.rows)
     {
         if (!c->n_tile_data)
             return -EINVAL;
@@ -1324,11 +1324,11 @@
         c->have_frame_hdr = 0;
         c->n_tiles = 0;
     } else if (c->seq_hdr && c->have_frame_hdr &&
-               c->frame_hdr.show_existing_frame)
+               c->frame_hdr->show_existing_frame)
     {
         if (c->n_fc == 1) {
             dav1d_picture_ref(&c->out,
-                              &c->refs[c->frame_hdr.existing_frame_idx].p.p);
+                              &c->refs[c->frame_hdr->existing_frame_idx].p.p);
             c->out.m = in->m;
         } else {
             // need to append this to the frame output queue
@@ -1351,16 +1351,16 @@
                 dav1d_thread_picture_unref(out_delayed);
             }
             dav1d_thread_picture_ref(out_delayed,
-                                     &c->refs[c->frame_hdr.existing_frame_idx].p);
+                                     &c->refs[c->frame_hdr->existing_frame_idx].p);
             out_delayed->visible = 1;
             out_delayed->p.m = in->m;
             pthread_mutex_unlock(&f->frame_thread.td.lock);
         }
         c->have_frame_hdr = 0;
-        if (c->refs[c->frame_hdr.existing_frame_idx].p.p.p.type == DAV1D_FRAME_TYPE_KEY) {
-            const int r = c->frame_hdr.existing_frame_idx;
+        if (c->refs[c->frame_hdr->existing_frame_idx].p.p.p.type == DAV1D_FRAME_TYPE_KEY) {
+            const int r = c->frame_hdr->existing_frame_idx;
             for (int i = 0; i < 8; i++) {
-                if (i == c->frame_hdr.existing_frame_idx) continue;
+                if (i == c->frame_hdr->existing_frame_idx) continue;
 
                 if (c->refs[i].p.p.data[0])
                     dav1d_thread_picture_unref(&c->refs[i].p);
--- a/src/recon.h
+++ b/src/recon.h
@@ -32,7 +32,7 @@
 #include "src/levels.h"
 
 #define DEBUG_BLOCK_INFO 0 && \
-        f->frame_hdr.frame_offset == 2 && t->by >= 0 && t->by < 4 && \
+        f->frame_hdr->frame_offset == 2 && t->by >= 0 && t->by < 4 && \
         t->bx >= 8 && t->bx < 12
 #define DEBUG_B_PIXELS 0
 
--- a/src/recon_tmpl.c
+++ b/src/recon_tmpl.c
@@ -80,7 +80,7 @@
            t_dim->ctx, sctx, all_skip, ts->msac.rng);
     if (all_skip) {
         *res_ctx = 0x40;
-        *txtp = f->frame_hdr.segmentation.lossless[b->seg_id] ? WHT_WHT :
+        *txtp = f->frame_hdr->segmentation.lossless[b->seg_id] ? WHT_WHT :
                                                                 DCT_DCT;
         return -1;
     }
@@ -88,14 +88,14 @@
     // transform type (chroma: derived, luma: explicitly coded)
     if (chroma) {
         if (intra) {
-            *txtp = get_uv_intra_txtp(b->uv_mode, tx, &f->frame_hdr, b->seg_id);
+            *txtp = get_uv_intra_txtp(b->uv_mode, tx, f->frame_hdr, b->seg_id);
         } else {
             const enum TxfmType y_txtp = *txtp;
-            *txtp = get_uv_inter_txtp(t_dim, y_txtp, &f->frame_hdr, b->seg_id);
+            *txtp = get_uv_inter_txtp(t_dim, y_txtp, f->frame_hdr, b->seg_id);
         }
     } else {
         const enum TxfmTypeSet set = get_ext_txtp_set(tx, !intra,
-                                                      &f->frame_hdr, b->seg_id);
+                                                      f->frame_hdr, b->seg_id);
         const unsigned set_cnt = dav1d_tx_type_count[set];
         unsigned idx;
         if (set_cnt == 1) {
@@ -1152,9 +1152,9 @@
         4 * (t->by * PXSTRIDE(f->cur.stride[0]) + t->bx);
     const ptrdiff_t uvdstoff =
         4 * ((t->bx >> ss_hor) + (t->by >> ss_ver) * PXSTRIDE(f->cur.stride[1]));
-    if (!(f->frame_hdr.frame_type & 1)) {
+    if (!(f->frame_hdr->frame_type & 1)) {
         // intrabc
-        assert(!f->frame_hdr.super_res.enabled);
+        assert(!f->frame_hdr->super_res.enabled);
         res = mc(t, dst, NULL, f->cur.stride[0], bw4, bh4, t->bx, t->by, 0,
                  b->mv[0], &f->sr_cur, 0 /* unused */, FILTER_2D_BILINEAR);
         if (res) return res;
@@ -1175,7 +1175,7 @@
         {
             res = warp_affine(t, dst, NULL, f->cur.stride[0], b_dim, 0, refp,
                               b->motion_mode == MM_WARP ? &t->warpmv :
-                                  &f->frame_hdr.gmv[b->ref[0]]);
+                                  &f->frame_hdr->gmv[b->ref[0]]);
             if (res) return res;
         } else {
             res = mc(t, dst, NULL, f->cur.stride[0],
@@ -1291,7 +1291,7 @@
                     res = warp_affine(t, ((pixel *) f->cur.data[1 + pl]) + uvdstoff, NULL,
                                       f->cur.stride[1], b_dim, 1 + pl, refp,
                                       b->motion_mode == MM_WARP ? &t->warpmv :
-                                          &f->frame_hdr.gmv[b->ref[0]]);
+                                          &f->frame_hdr->gmv[b->ref[0]]);
                     if (res) return res;
                 }
             } else {
@@ -1367,7 +1367,7 @@
 
             if (b->inter_mode == GLOBALMV_GLOBALMV && f->gmv_warp_allowed[b->ref[i]]) {
                 res = warp_affine(t, NULL, tmp[i], bw4 * 4, b_dim, 0, refp,
-                                  &f->frame_hdr.gmv[b->ref[i]]);
+                                  &f->frame_hdr->gmv[b->ref[i]]);
                 if (res) return res;
             } else {
                 res = mc(t, NULL, tmp[i], 0, bw4, bh4, t->bx, t->by, 0,
@@ -1411,7 +1411,7 @@
                     imin(cbw4, cbh4) > 1 && f->gmv_warp_allowed[b->ref[i]])
                 {
                     res = warp_affine(t, NULL, tmp[i], bw4 * 2, b_dim, 1 + pl,
-                                      refp, &f->frame_hdr.gmv[b->ref[i]]);
+                                      refp, &f->frame_hdr->gmv[b->ref[i]]);
                     if (res) return res;
                 } else {
                     res = mc(t, NULL, tmp[i], 0, bw4, bh4, t->bx, t->by,
@@ -1569,11 +1569,11 @@
 void bytefn(dav1d_filter_sbrow)(Dav1dFrameContext *const f, const int sby) {
     const int sbsz = f->sb_step, sbh = f->sbh;
 
-    if (f->frame_hdr.loopfilter.level_y[0] ||
-        f->frame_hdr.loopfilter.level_y[1])
+    if (f->frame_hdr->loopfilter.level_y[0] ||
+        f->frame_hdr->loopfilter.level_y[1])
     {
         int start_of_tile_row = 0;
-        if (f->frame_hdr.tiling.row_start_sb[f->lf.tile_row] == sby)
+        if (f->frame_hdr->tiling.row_start_sb[f->lf.tile_row] == sby)
             start_of_tile_row = f->lf.tile_row++;
         bytefn(dav1d_loopfilter_sbrow)(f, f->lf.p, f->lf.mask_ptr, sby,
                                        start_of_tile_row);
@@ -1598,7 +1598,7 @@
         bytefn(dav1d_cdef_brow)(f, f->lf.p, f->lf.mask_ptr, sby * sbsz,
                                 imin(sby * sbsz + n_blks, f->bh));
     }
-    if (f->frame_hdr.super_res.enabled) {
+    if (f->frame_hdr->super_res.enabled) {
         const int has_chroma = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400;
         for (int pl = 0; pl < 1 + 2 * has_chroma; pl++) {
             const int ss_ver = pl && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420;
--- a/src/thread_task.c
+++ b/src/thread_task.c
@@ -79,7 +79,7 @@
         const int task_idx = fttd->num_tasks - fttd->tasks_left--;
         pthread_mutex_unlock(&fttd->lock);
 
-        if (f->frame_thread.pass == 1 || f->n_tc >= f->frame_hdr.tiling.cols) {
+        if (f->frame_thread.pass == 1 || f->n_tc >= f->frame_hdr->tiling.cols) {
             // we can (or in fact, if >, we need to) do full tile decoding.
             // loopfilter happens in the main thread
             Dav1dTileState *const ts = t->ts = &f->ts[task_idx];