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
  vpx_get_worker_interface()->end(&pbi->lf_worker);
130
  vpx_free(pbi->lf_worker.data1);
131
  vpx_free(pbi->tile_data);
132
  for (i = 0; i < pbi->num_tile_workers; ++i) {
133
134
    VPxWorker *const worker = &pbi->tile_workers[i];
    vpx_get_worker_interface()->end(worker);
135
  }
136
137
  vpx_free(pbi->tile_worker_data);
  vpx_free(pbi->tile_worker_info);
138
  vpx_free(pbi->tile_workers);
139

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

John Koleszar's avatar
John Koleszar committed
144
  vpx_free(pbi);
John Koleszar's avatar
John Koleszar committed
145
146
}

147
148
static int equal_dimensions(const YV12_BUFFER_CONFIG *a,
                            const YV12_BUFFER_CONFIG *b) {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
149
150
151
    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
152

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

158
159
160
161
162
  /* 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
163
  if (ref_frame_flag == VP9_LAST_FLAG) {
164
165
166
167
168
169
    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;
    }
170
171
172
173
174
    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
175
  } else {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
176
    vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
John Koleszar's avatar
John Koleszar committed
177
                       "Invalid reference frame");
Dmitry Kovalev's avatar
Dmitry Kovalev committed
178
  }
179

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


184
vpx_codec_err_t vp9_set_reference_dec(VP9_COMMON *cm,
Dmitry Kovalev's avatar
Dmitry Kovalev committed
185
                                      VP9_REFFRAME ref_frame_flag,
186
                                      YV12_BUFFER_CONFIG *sd) {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
187
  RefBuffer *ref_buf = NULL;
188
  RefCntBuffer *const frame_bufs = cm->buffer_pool->frame_bufs;
John Koleszar's avatar
John Koleszar committed
189

190
191
192
193
  // 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.
194
  if (ref_frame_flag == VP9_LAST_FLAG) {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
195
    ref_buf = &cm->frame_refs[0];
196
  } else if (ref_frame_flag == VP9_GOLD_FLAG) {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
197
    ref_buf = &cm->frame_refs[1];
198
  } else if (ref_frame_flag == VP9_ALT_FLAG) {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
199
    ref_buf = &cm->frame_refs[2];
200
  } else {
201
    vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
John Koleszar's avatar
John Koleszar committed
202
                       "Invalid reference frame");
203
    return cm->error.error_code;
John Koleszar's avatar
John Koleszar committed
204
  }
205

Dmitry Kovalev's avatar
Dmitry Kovalev committed
206
  if (!equal_dimensions(ref_buf->buf, sd)) {
207
    vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
John Koleszar's avatar
John Koleszar committed
208
209
                       "Incorrect buffer dimensions");
  } else {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
210
211
    int *ref_fb_ptr = &ref_buf->idx;

Dmitry Kovalev's avatar
Dmitry Kovalev committed
212
213
    // Find an empty frame buffer.
    const int free_fb = get_free_fb(cm);
214
215
216
    if (cm->new_fb_idx == INVALID_IDX)
      return VPX_CODEC_MEM_ERROR;

217
    // Decrease ref_count since it will be increased again in
Dmitry Kovalev's avatar
Dmitry Kovalev committed
218
    // ref_cnt_fb() below.
219
    --frame_bufs[free_fb].ref_count;
John Koleszar's avatar
John Koleszar committed
220

Dmitry Kovalev's avatar
Dmitry Kovalev committed
221
    // Manage the reference counters and copy image.
222
223
    ref_cnt_fb(frame_bufs, ref_fb_ptr, free_fb);
    ref_buf->buf = &frame_bufs[*ref_fb_ptr].buf;
Dmitry Kovalev's avatar
Dmitry Kovalev committed
224
    vp8_yv12_copy_frame(sd, ref_buf->buf);
John Koleszar's avatar
John Koleszar committed
225
  }
226

227
  return cm->error.error_code;
John Koleszar's avatar
John Koleszar committed
228
229
}

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

237
  lock_buffer_pool(pool);
238
  for (mask = pbi->refresh_frame_flags; mask; mask >>= 1) {
239
240
241
242
243
244
245
    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.
    if ((mask & 1) && old_idx >= 0) {
      decrease_ref_count(old_idx, frame_bufs, pool);
246
    }
247
    cm->ref_frame_map[ref_index] = cm->next_ref_frame_map[ref_index];
248
249
    ++ref_index;
  }
250

251
252
253
254
255
256
257
258
  // 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;
259
  cm->frame_to_show = get_frame_new_buffer(cm);
260
261
262
263
264
265

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

Dmitry Kovalev's avatar
Dmitry Kovalev committed
267
  // Invalidate these references until the next frame starts.
268
  for (ref_index = 0; ref_index < 3; ref_index++)
269
    cm->frame_refs[ref_index].idx = -1;
270
271
}

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

281
  if (size == 0) {
282
283
284
285
286
287
288
289
    // 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.
290
291
    if (cm->frame_refs[0].idx > 0) {
      assert(cm->frame_refs[0].buf != NULL);
Dmitry Kovalev's avatar
Dmitry Kovalev committed
292
      cm->frame_refs[0].buf->corrupted = 1;
293
    }
John Koleszar's avatar
John Koleszar committed
294
  }
295

296
297
  pbi->ready_for_new_data = 0;

298
  // Check if the previous frame was a frame without any references to it.
299
300
301
302
303
  // 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);
304
  // Find a free frame buffer. Return error if can not find any.
John Koleszar's avatar
John Koleszar committed
305
  cm->new_fb_idx = get_free_fb(cm);
306
307
  if (cm->new_fb_idx == INVALID_IDX)
    return VPX_CODEC_MEM_ERROR;
308

309
  // Assign a MV array to the frame buffer.
310
311
312
313
  cm->cur_frame = &pool->frame_bufs[cm->new_fb_idx];

  pbi->hold_ref_buf = 0;
  if (pbi->frame_parallel_decode) {
314
    VPxWorker *const worker = pbi->frame_worker_owner;
315
316
317
318
319
320
321
322
323
324
325
    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];
  }

326

Dmitry Kovalev's avatar
Dmitry Kovalev committed
327
  if (setjmp(cm->error.jmp)) {
328
    const VPxWorkerInterface *const winterface = vpx_get_worker_interface();
James Zern's avatar
James Zern committed
329
330
    int i;

Dmitry Kovalev's avatar
Dmitry Kovalev committed
331
    cm->error.setjmp = 0;
332
    pbi->ready_for_new_data = 1;
James Zern's avatar
James Zern committed
333
334
335
336
337
338
339
340

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

341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
    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.
        if ((mask & 1) && old_idx >= 0) {
          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);
367

368
    vpx_clear_system_state();
John Koleszar's avatar
John Koleszar committed
369
370
    return -1;
  }
John Koleszar's avatar
John Koleszar committed
371

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

375
  swap_frame_buffers(pbi);
John Koleszar's avatar
John Koleszar committed
376

377
  vpx_clear_system_state();
John Koleszar's avatar
John Koleszar committed
378

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

386
387
388
389
  // 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.
390
    VPxWorker *const worker = pbi->frame_worker_owner;
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
    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++;
    }
  }
408

Dmitry Kovalev's avatar
Dmitry Kovalev committed
409
  cm->error.setjmp = 0;
John Koleszar's avatar
John Koleszar committed
410
  return retcode;
John Koleszar's avatar
John Koleszar committed
411
}
412

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

John Koleszar's avatar
John Koleszar committed
421
422
  if (pbi->ready_for_new_data == 1)
    return ret;
John Koleszar's avatar
John Koleszar committed
423

424
425
  pbi->ready_for_new_data = 1;

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

430
431
  pbi->ready_for_new_data = 1;

432
#if CONFIG_VP9_POSTPROC
433
434
435
436
437
438
  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
439
#else
440
  *sd = *cm->frame_to_show;
Yaowu Xu's avatar
Yaowu Xu committed
441
  ret = 0;
442
#endif /*!CONFIG_POSTPROC*/
443
  vpx_clear_system_state();
John Koleszar's avatar
John Koleszar committed
444
  return ret;
John Koleszar's avatar
John Koleszar committed
445
}
446
447
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

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