vp9_decoder.c 15.7 KB
Newer Older
John Koleszar's avatar
John Koleszar committed
1
/*
2
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
John Koleszar's avatar
John Koleszar committed
3
 *
4
 *  Use of this source code is governed by a BSD-style license
5 6
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
7
 *  in the file PATENTS.  All contributing project authors may
8
 *  be found in the AUTHORS file in the root of the source tree.
John Koleszar's avatar
John Koleszar committed
9 10
 */

Dmitry Kovalev's avatar
Dmitry Kovalev committed
11
#include <assert.h>
Dmitry Kovalev's avatar
Dmitry Kovalev committed
12 13
#include <limits.h>
#include <stdio.h>
Dmitry Kovalev's avatar
Dmitry Kovalev committed
14

15
#include "./vp9_rtcd.h"
Johann's avatar
Johann committed
16
#include "./vpx_dsp_rtcd.h"
17 18 19
#include "./vpx_scale_rtcd.h"

#include "vpx_mem/vpx_mem.h"
20
#include "vpx_ports/system_state.h"
21
#include "vpx_ports/vpx_once.h"
22 23
#include "vpx_ports/vpx_timer.h"
#include "vpx_scale/vpx_scale.h"
Jingning Han's avatar
Jingning Han committed
24
#include "vpx_util/vpx_thread.h"
25 26 27

#include "vp9/common/vp9_alloccommon.h"
#include "vp9/common/vp9_loopfilter.h"
28
#include "vp9/common/vp9_onyxc_int.h"
29
#if CONFIG_VP9_POSTPROC
30
#include "vp9/common/vp9_postproc.h"
John Koleszar's avatar
John Koleszar committed
31
#endif
32
#include "vp9/common/vp9_quant_common.h"
33
#include "vp9/common/vp9_reconintra.h"
34

Yaowu Xu's avatar
Yaowu Xu committed
35
#include "vp9/decoder/vp9_decodeframe.h"
36
#include "vp9/decoder/vp9_decoder.h"
37
#include "vp9/decoder/vp9_detokenize.h"
John Koleszar's avatar
John Koleszar committed
38

39
static void initialize_dec(void) {
40
  static volatile int init_done = 0;
John Koleszar's avatar
John Koleszar committed
41

John Koleszar's avatar
John Koleszar committed
42
  if (!init_done) {
43
    vp9_rtcd();
Johann's avatar
Johann committed
44
    vpx_dsp_rtcd();
45
    vpx_scale_rtcd();
46
    vp9_init_intra_predictors();
John Koleszar's avatar
John Koleszar committed
47 48
    init_done = 1;
  }
John Koleszar's avatar
John Koleszar committed
49 50
}

51 52
static void vp9_dec_setup_mi(VP9_COMMON *cm) {
  cm->mi = cm->mip + cm->mi_stride + 1;
53
  cm->mi_grid_visible = cm->mi_grid_base + cm->mi_stride + 1;
James Zern's avatar
James Zern committed
54 55
  memset(cm->mi_grid_base, 0,
         cm->mi_stride * (cm->mi_rows + 1) * sizeof(*cm->mi_grid_base));
56 57 58 59 60 61 62
}

static int vp9_dec_alloc_mi(VP9_COMMON *cm, int mi_size) {
  cm->mip = vpx_calloc(mi_size, sizeof(*cm->mip));
  if (!cm->mip)
    return 1;
  cm->mi_alloc_size = mi_size;
63 64 65
  cm->mi_grid_base = (MODE_INFO **)vpx_calloc(mi_size, sizeof(MODE_INFO*));
  if (!cm->mi_grid_base)
    return 1;
66 67 68 69 70 71
  return 0;
}

static void vp9_dec_free_mi(VP9_COMMON *cm) {
  vpx_free(cm->mip);
  cm->mip = NULL;
72 73
  vpx_free(cm->mi_grid_base);
  cm->mi_grid_base = NULL;
74 75
}

76
VP9Decoder *vp9_decoder_create(BufferPool *const pool) {
77 78
  VP9Decoder *volatile const pbi = vpx_memalign(32, sizeof(*pbi));
  VP9_COMMON *volatile const cm = pbi ? &pbi->common : NULL;
John Koleszar's avatar
John Koleszar committed
79

80
  if (!cm)
John Koleszar's avatar
John Koleszar committed
81
    return NULL;
John Koleszar's avatar
John Koleszar committed
82

Dmitry Kovalev's avatar
Dmitry Kovalev committed
83
  vp9_zero(*pbi);
John Koleszar's avatar
John Koleszar committed
84

85 86
  if (setjmp(cm->error.jmp)) {
    cm->error.setjmp = 0;
87
    vp9_decoder_remove(pbi);
88
    return NULL;
John Koleszar's avatar
John Koleszar committed
89
  }
John Koleszar's avatar
John Koleszar committed
90

91
  cm->error.setjmp = 1;
92 93 94 95 96 97 98

  CHECK_MEM_ERROR(cm, cm->fc,
                  (FRAME_CONTEXT *)vpx_calloc(1, sizeof(*cm->fc)));
  CHECK_MEM_ERROR(cm, cm->frame_contexts,
                  (FRAME_CONTEXT *)vpx_calloc(FRAME_CONTEXTS,
                  sizeof(*cm->frame_contexts)));

99
  pbi->need_resync = 1;
100
  once(initialize_dec);
John Koleszar's avatar
John Koleszar committed
101

102
  // Initialize the references to not point to any frame buffers.
James Zern's avatar
James Zern committed
103 104
  memset(&cm->ref_frame_map, -1, sizeof(cm->ref_frame_map));
  memset(&cm->next_ref_frame_map, -1, sizeof(cm->next_ref_frame_map));
105 106

  cm->current_video_frame = 0;
John Koleszar's avatar
John Koleszar committed
107
  pbi->ready_for_new_data = 1;
108 109
  pbi->common.buffer_pool = pool;

110
  cm->bit_depth = VPX_BITS_8;
111
  cm->dequant_bit_depth = VPX_BITS_8;
John Koleszar's avatar
John Koleszar committed
112

113 114 115 116
  cm->alloc_mi = vp9_dec_alloc_mi;
  cm->free_mi = vp9_dec_free_mi;
  cm->setup_mi = vp9_dec_setup_mi;

117
  vp9_loop_filter_init(cm);
John Koleszar's avatar
John Koleszar committed
118

119
  cm->error.setjmp = 0;
120

121
  vpx_get_worker_interface()->init(&pbi->lf_worker);
122

123
  return pbi;
John Koleszar's avatar
John Koleszar committed
124 125
}

126
void vp9_decoder_remove(VP9Decoder *pbi) {
127
  int i;
John Koleszar's avatar
John Koleszar committed
128

129 130 131
  if (!pbi)
    return;

132
  vpx_get_worker_interface()->end(&pbi->lf_worker);
133
  vpx_free(pbi->lf_worker.data1);
134
  vpx_free(pbi->tile_data);
135
  for (i = 0; i < pbi->num_tile_workers; ++i) {
136 137
    VPxWorker *const worker = &pbi->tile_workers[i];
    vpx_get_worker_interface()->end(worker);
138
  }
139
  vpx_free(pbi->tile_worker_data);
140
  vpx_free(pbi->tile_workers);
141

142 143
  if (pbi->num_tile_workers > 0) {
    vp9_loop_filter_dealloc(&pbi->lf_row_sync);
144 145
  }

John Koleszar's avatar
John Koleszar committed
146
  vpx_free(pbi);
John Koleszar's avatar
John Koleszar committed
147 148
}

149 150
static int equal_dimensions(const YV12_BUFFER_CONFIG *a,
                            const YV12_BUFFER_CONFIG *b) {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
151 152 153
    return a->y_height == b->y_height && a->y_width == b->y_width &&
           a->uv_height == b->uv_height && a->uv_width == b->uv_width;
}
John Koleszar's avatar
John Koleszar committed
154

155
vpx_codec_err_t vp9_copy_reference_dec(VP9Decoder *pbi,
John Koleszar's avatar
John Koleszar committed
156 157
                                       VP9_REFFRAME ref_frame_flag,
                                       YV12_BUFFER_CONFIG *sd) {
158
  VP9_COMMON *cm = &pbi->common;
John Koleszar's avatar
John Koleszar committed
159

160 161 162 163 164
  /* TODO(jkoleszar): The decoder doesn't have any real knowledge of what the
   * encoder is using the frame buffers for. This is just a stub to keep the
   * vpxenc --test-decode functionality working, and will be replaced in a
   * later commit that adds VP9-specific controls for this functionality.
   */
Dmitry Kovalev's avatar
Dmitry Kovalev committed
165
  if (ref_frame_flag == VP9_LAST_FLAG) {
166 167 168 169 170 171
    const YV12_BUFFER_CONFIG *const cfg = get_ref_frame(cm, 0);
    if (cfg == NULL) {
      vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
                         "No 'last' reference frame");
      return VPX_CODEC_ERROR;
    }
172 173 174 175 176
    if (!equal_dimensions(cfg, sd))
      vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
                         "Incorrect buffer dimensions");
    else
      vp8_yv12_copy_frame(cfg, sd);
Dmitry Kovalev's avatar
Dmitry Kovalev committed
177
  } else {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
178
    vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
John Koleszar's avatar
John Koleszar committed
179
                       "Invalid reference frame");
Dmitry Kovalev's avatar
Dmitry Kovalev committed
180
  }
181

Dmitry Kovalev's avatar
Dmitry Kovalev committed
182
  return cm->error.error_code;
John Koleszar's avatar
John Koleszar committed
183
}
184 185


186
vpx_codec_err_t vp9_set_reference_dec(VP9_COMMON *cm,
Dmitry Kovalev's avatar
Dmitry Kovalev committed
187
                                      VP9_REFFRAME ref_frame_flag,
188
                                      YV12_BUFFER_CONFIG *sd) {
189 190
  int idx;
  YV12_BUFFER_CONFIG *ref_buf = NULL;
John Koleszar's avatar
John Koleszar committed
191

192 193 194 195
  // TODO(jkoleszar): The decoder doesn't have any real knowledge of what the
  // encoder is using the frame buffers for. This is just a stub to keep the
  // vpxenc --test-decode functionality working, and will be replaced in a
  // later commit that adds VP9-specific controls for this functionality.
196 197 198 199 200 201

  // (Yunqing) The set_reference control depends on the following setting in
  // encoder.
  // cpi->lst_fb_idx = 0;
  // cpi->gld_fb_idx = 1;
  // cpi->alt_fb_idx = 2;
202
  if (ref_frame_flag == VP9_LAST_FLAG) {
203
    idx = cm->ref_frame_map[0];
204
  } else if (ref_frame_flag == VP9_GOLD_FLAG) {
205
    idx = cm->ref_frame_map[1];
206
  } else if (ref_frame_flag == VP9_ALT_FLAG) {
207
    idx = cm->ref_frame_map[2];
208
  } else {
209
    vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
John Koleszar's avatar
John Koleszar committed
210
                       "Invalid reference frame");
211
    return cm->error.error_code;
John Koleszar's avatar
John Koleszar committed
212
  }
213

214
  if (idx < 0 || idx >= FRAME_BUFFERS) {
215
    vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
216 217 218
                       "Invalid reference frame map");
    return cm->error.error_code;
  }
219

220 221
  // Get the destination reference buffer.
  ref_buf = &cm->buffer_pool->frame_bufs[idx].buf;
John Koleszar's avatar
John Koleszar committed
222

223 224 225 226 227 228
  if (!equal_dimensions(ref_buf, sd)) {
    vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
                       "Incorrect buffer dimensions");
  } else {
    // Overwrite the reference frame buffer.
    vp8_yv12_copy_frame(sd, ref_buf);
John Koleszar's avatar
John Koleszar committed
229
  }
230

231
  return cm->error.error_code;
John Koleszar's avatar
John Koleszar committed
232 233
}

234
/* If any buffer updating is signaled it should be done here. */
235
static void swap_frame_buffers(VP9Decoder *pbi) {
236
  int ref_index = 0, mask;
Dmitry Kovalev's avatar
Dmitry Kovalev committed
237
  VP9_COMMON *const cm = &pbi->common;
238 239
  BufferPool *const pool = cm->buffer_pool;
  RefCntBuffer *const frame_bufs = cm->buffer_pool->frame_bufs;
240

241
  lock_buffer_pool(pool);
242

243
  for (mask = pbi->refresh_frame_flags; mask; mask >>= 1) {
244 245 246 247 248
    const int old_idx = cm->ref_frame_map[ref_index];
    // Current thread releases the holding of reference frame.
    decrease_ref_count(old_idx, frame_bufs, pool);

    // Release the reference frame in reference map.
249
    if (mask & 1)
250
      decrease_ref_count(old_idx, frame_bufs, pool);
251

252
    cm->ref_frame_map[ref_index] = cm->next_ref_frame_map[ref_index];
253 254
    ++ref_index;
  }
255

256 257 258 259 260 261 262 263
  // Current thread releases the holding of reference frame.
  for (; ref_index < REF_FRAMES && !cm->show_existing_frame; ++ref_index) {
    const int old_idx = cm->ref_frame_map[ref_index];
    decrease_ref_count(old_idx, frame_bufs, pool);
    cm->ref_frame_map[ref_index] = cm->next_ref_frame_map[ref_index];
  }
  unlock_buffer_pool(pool);
  pbi->hold_ref_buf = 0;
264
  cm->frame_to_show = get_frame_new_buffer(cm);
265 266 267 268 269 270

  if (!pbi->frame_parallel_decode || !cm->show_frame) {
    lock_buffer_pool(pool);
    --frame_bufs[cm->new_fb_idx].ref_count;
    unlock_buffer_pool(pool);
  }
271

Dmitry Kovalev's avatar
Dmitry Kovalev committed
272
  // Invalidate these references until the next frame starts.
273
  for (ref_index = 0; ref_index < REFS_PER_FRAME; ref_index++)
274
    cm->frame_refs[ref_index].idx = -1;
275 276
}

277
int vp9_receive_compressed_data(VP9Decoder *pbi,
278
                                size_t size, const uint8_t **psource) {
279
  VP9_COMMON *volatile const cm = &pbi->common;
280 281
  BufferPool *volatile const pool = cm->buffer_pool;
  RefCntBuffer *volatile const frame_bufs = cm->buffer_pool->frame_bufs;
282
  const uint8_t *source = *psource;
John Koleszar's avatar
John Koleszar committed
283
  int retcode = 0;
Dmitry Kovalev's avatar
Dmitry Kovalev committed
284
  cm->error.error_code = VPX_CODEC_OK;
John Koleszar's avatar
John Koleszar committed
285

286
  if (size == 0) {
287 288 289 290 291 292 293 294
    // This is used to signal that we are missing frames.
    // We do not know if the missing frame(s) was supposed to update
    // any of the reference buffers, but we act conservative and
    // mark only the last buffer as corrupted.
    //
    // TODO(jkoleszar): Error concealment is undefined and non-normative
    // at this point, but if it becomes so, [0] may not always be the correct
    // thing to do here.
295 296
    if (cm->frame_refs[0].idx > 0) {
      assert(cm->frame_refs[0].buf != NULL);
Dmitry Kovalev's avatar
Dmitry Kovalev committed
297
      cm->frame_refs[0].buf->corrupted = 1;
298
    }
John Koleszar's avatar
John Koleszar committed
299
  }
300

301 302
  pbi->ready_for_new_data = 0;

303
  // Check if the previous frame was a frame without any references to it.
304 305 306 307 308
  // Release frame buffer if not decoding in frame parallel mode.
  if (!pbi->frame_parallel_decode && cm->new_fb_idx >= 0
      && frame_bufs[cm->new_fb_idx].ref_count == 0)
    pool->release_fb_cb(pool->cb_priv,
                        &frame_bufs[cm->new_fb_idx].raw_frame_buffer);
309
  // Find a free frame buffer. Return error if can not find any.
John Koleszar's avatar
John Koleszar committed
310
  cm->new_fb_idx = get_free_fb(cm);
311 312
  if (cm->new_fb_idx == INVALID_IDX)
    return VPX_CODEC_MEM_ERROR;
313

314
  // Assign a MV array to the frame buffer.
315 316 317 318
  cm->cur_frame = &pool->frame_bufs[cm->new_fb_idx];

  pbi->hold_ref_buf = 0;
  if (pbi->frame_parallel_decode) {
319
    VPxWorker *const worker = pbi->frame_worker_owner;
320 321 322 323 324 325 326 327 328 329 330
    vp9_frameworker_lock_stats(worker);
    frame_bufs[cm->new_fb_idx].frame_worker_owner = worker;
    // Reset decoding progress.
    pbi->cur_buf = &frame_bufs[cm->new_fb_idx];
    pbi->cur_buf->row = -1;
    pbi->cur_buf->col = -1;
    vp9_frameworker_unlock_stats(worker);
  } else {
    pbi->cur_buf = &frame_bufs[cm->new_fb_idx];
  }

Dmitry Kovalev's avatar
Dmitry Kovalev committed
331
  if (setjmp(cm->error.jmp)) {
332
    const VPxWorkerInterface *const winterface = vpx_get_worker_interface();
James Zern's avatar
James Zern committed
333 334
    int i;

Dmitry Kovalev's avatar
Dmitry Kovalev committed
335
    cm->error.setjmp = 0;
336
    pbi->ready_for_new_data = 1;
James Zern's avatar
James Zern committed
337 338 339 340 341 342 343 344

    // Synchronize all threads immediately as a subsequent decode call may
    // cause a resize invalidating some allocations.
    winterface->sync(&pbi->lf_worker);
    for (i = 0; i < pbi->num_tile_workers; ++i) {
      winterface->sync(&pbi->tile_workers[i]);
    }

345 346 347 348 349 350 351 352 353 354
    lock_buffer_pool(pool);
    // Release all the reference buffers if worker thread is holding them.
    if (pbi->hold_ref_buf == 1) {
      int ref_index = 0, mask;
      for (mask = pbi->refresh_frame_flags; mask; mask >>= 1) {
        const int old_idx = cm->ref_frame_map[ref_index];
        // Current thread releases the holding of reference frame.
        decrease_ref_count(old_idx, frame_bufs, pool);

        // Release the reference frame in reference map.
355
        if (mask & 1)
356 357 358 359 360 361 362 363 364 365 366 367 368 369
          decrease_ref_count(old_idx, frame_bufs, pool);
        ++ref_index;
      }

      // Current thread releases the holding of reference frame.
      for (; ref_index < REF_FRAMES && !cm->show_existing_frame; ++ref_index) {
        const int old_idx = cm->ref_frame_map[ref_index];
        decrease_ref_count(old_idx, frame_bufs, pool);
      }
      pbi->hold_ref_buf = 0;
    }
    // Release current frame.
    decrease_ref_count(cm->new_fb_idx, frame_bufs, pool);
    unlock_buffer_pool(pool);
370

371
    vpx_clear_system_state();
John Koleszar's avatar
John Koleszar committed
372 373
    return -1;
  }
John Koleszar's avatar
John Koleszar committed
374

Dmitry Kovalev's avatar
Dmitry Kovalev committed
375
  cm->error.setjmp = 1;
376
  vp9_decode_frame(pbi, source, source + size, psource);
John Koleszar's avatar
John Koleszar committed
377

378
  swap_frame_buffers(pbi);
John Koleszar's avatar
John Koleszar committed
379

380
  vpx_clear_system_state();
John Koleszar's avatar
John Koleszar committed
381

382
  if (!cm->show_existing_frame) {
383
    cm->last_show_frame = cm->show_frame;
384
    cm->prev_frame = cm->cur_frame;
385 386
    if (cm->seg.enabled && !pbi->frame_parallel_decode)
      vp9_swap_current_and_last_seg_map(cm);
387
  }
388

389 390 391 392
  // Update progress in frame parallel decode.
  if (pbi->frame_parallel_decode) {
    // Need to lock the mutex here as another thread may
    // be accessing this buffer.
393
    VPxWorker *const worker = pbi->frame_worker_owner;
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
    FrameWorkerData *const frame_worker_data = worker->data1;
    vp9_frameworker_lock_stats(worker);

    if (cm->show_frame) {
      cm->current_video_frame++;
    }
    frame_worker_data->frame_decoded = 1;
    frame_worker_data->frame_context_ready = 1;
    vp9_frameworker_signal_stats(worker);
    vp9_frameworker_unlock_stats(worker);
  } else {
    cm->last_width = cm->width;
    cm->last_height = cm->height;
    if (cm->show_frame) {
      cm->current_video_frame++;
    }
  }
411

Dmitry Kovalev's avatar
Dmitry Kovalev committed
412
  cm->error.setjmp = 0;
John Koleszar's avatar
John Koleszar committed
413
  return retcode;
John Koleszar's avatar
John Koleszar committed
414
}
415

416
int vp9_get_raw_frame(VP9Decoder *pbi, YV12_BUFFER_CONFIG *sd,
417
                      vp9_ppflags_t *flags) {
418
  VP9_COMMON *const cm = &pbi->common;
John Koleszar's avatar
John Koleszar committed
419
  int ret = -1;
Yaowu Xu's avatar
Yaowu Xu committed
420 421 422
#if !CONFIG_VP9_POSTPROC
  (void)*flags;
#endif
John Koleszar's avatar
John Koleszar committed
423

John Koleszar's avatar
John Koleszar committed
424 425
  if (pbi->ready_for_new_data == 1)
    return ret;
John Koleszar's avatar
John Koleszar committed
426

427 428
  pbi->ready_for_new_data = 1;

Yaowu Xu's avatar
Yaowu Xu committed
429
  /* no raw frame to show!!! */
430
  if (!cm->show_frame)
John Koleszar's avatar
John Koleszar committed
431
    return ret;
John Koleszar's avatar
John Koleszar committed
432

433 434
  pbi->ready_for_new_data = 1;

435
#if CONFIG_VP9_POSTPROC
436 437 438 439 440 441
  if (!cm->show_existing_frame) {
    ret = vp9_post_proc_frame(cm, sd, flags);
  } else {
    *sd = *cm->frame_to_show;
    ret = 0;
  }
John Koleszar's avatar
John Koleszar committed
442
#else
443
  *sd = *cm->frame_to_show;
Yaowu Xu's avatar
Yaowu Xu committed
444
  ret = 0;
445
#endif /*!CONFIG_POSTPROC*/
446
  vpx_clear_system_state();
John Koleszar's avatar
John Koleszar committed
447
  return ret;
John Koleszar's avatar
John Koleszar committed
448
}
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512

vpx_codec_err_t vp9_parse_superframe_index(const uint8_t *data,
                                           size_t data_sz,
                                           uint32_t sizes[8], int *count,
                                           vpx_decrypt_cb decrypt_cb,
                                           void *decrypt_state) {
  // A chunk ending with a byte matching 0xc0 is an invalid chunk unless
  // it is a super frame index. If the last byte of real video compression
  // data is 0xc0 the encoder must add a 0 byte. If we have the marker but
  // not the associated matching marker byte at the front of the index we have
  // an invalid bitstream and need to return an error.

  uint8_t marker;

  assert(data_sz);
  marker = read_marker(decrypt_cb, decrypt_state, data + data_sz - 1);
  *count = 0;

  if ((marker & 0xe0) == 0xc0) {
    const uint32_t frames = (marker & 0x7) + 1;
    const uint32_t mag = ((marker >> 3) & 0x3) + 1;
    const size_t index_sz = 2 + mag * frames;

    // This chunk is marked as having a superframe index but doesn't have
    // enough data for it, thus it's an invalid superframe index.
    if (data_sz < index_sz)
      return VPX_CODEC_CORRUPT_FRAME;

    {
      const uint8_t marker2 = read_marker(decrypt_cb, decrypt_state,
                                          data + data_sz - index_sz);

      // This chunk is marked as having a superframe index but doesn't have
      // the matching marker byte at the front of the index therefore it's an
      // invalid chunk.
      if (marker != marker2)
        return VPX_CODEC_CORRUPT_FRAME;
    }

    {
      // Found a valid superframe index.
      uint32_t i, j;
      const uint8_t *x = &data[data_sz - index_sz + 1];

      // Frames has a maximum of 8 and mag has a maximum of 4.
      uint8_t clear_buffer[32];
      assert(sizeof(clear_buffer) >= frames * mag);
      if (decrypt_cb) {
        decrypt_cb(decrypt_state, x, clear_buffer, frames * mag);
        x = clear_buffer;
      }

      for (i = 0; i < frames; ++i) {
        uint32_t this_sz = 0;

        for (j = 0; j < mag; ++j)
          this_sz |= (*x++) << (j * 8);
        sizes[i] = this_sz;
      }
      *count = frames;
    }
  }
  return VPX_CODEC_OK;
}