decoder.c 15.6 KB
Newer Older
Jingning Han's avatar
Jingning Han committed
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
Jingning Han's avatar
Jingning Han committed
3
 *
4 5 6 7 8 9
 * This source code is subject to the terms of the BSD 2 Clause License and
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
 * was not distributed with this source code in the LICENSE file, you can
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
 * Media Patent License 1.0 was not distributed with this source code in the
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
Jingning Han's avatar
Jingning Han committed
10 11 12 13 14 15
 */

#include <assert.h>
#include <limits.h>
#include <stdio.h>

Yaowu Xu's avatar
Yaowu Xu committed
16
#include "./av1_rtcd.h"
Adrian Grange's avatar
Adrian Grange committed
17 18
#include "./aom_dsp_rtcd.h"
#include "./aom_scale_rtcd.h"
Jingning Han's avatar
Jingning Han committed
19

Adrian Grange's avatar
Adrian Grange committed
20
#include "aom_mem/aom_mem.h"
Yaowu Xu's avatar
Yaowu Xu committed
21
#include "aom_ports/system_state.h"
Adrian Grange's avatar
Adrian Grange committed
22 23 24 25
#include "aom_ports/aom_once.h"
#include "aom_ports/aom_timer.h"
#include "aom_scale/aom_scale.h"
#include "aom_util/aom_thread.h"
Jingning Han's avatar
Jingning Han committed
26

Yaowu Xu's avatar
Yaowu Xu committed
27 28 29 30 31 32 33 34 35
#include "av1/common/alloccommon.h"
#include "av1/common/loopfilter.h"
#include "av1/common/onyxc_int.h"
#include "av1/common/quant_common.h"
#include "av1/common/reconintra.h"

#include "av1/decoder/decodeframe.h"
#include "av1/decoder/decoder.h"
#include "av1/decoder/detokenize.h"
Jingning Han's avatar
Jingning Han committed
36 37 38 39 40

static void initialize_dec(void) {
  static volatile int init_done = 0;

  if (!init_done) {
Yaowu Xu's avatar
Yaowu Xu committed
41
    av1_rtcd();
Adrian Grange's avatar
Adrian Grange committed
42 43
    aom_dsp_rtcd();
    aom_scale_rtcd();
44
    av1_init_intra_predictors();
Jingning Han's avatar
Jingning Han committed
45 46 47 48
    init_done = 1;
  }
}

49
static void av1_dec_setup_mi(AV1_COMMON *cm) {
Jingning Han's avatar
Jingning Han committed
50 51 52 53 54 55
  cm->mi = cm->mip + cm->mi_stride + 1;
  cm->mi_grid_visible = cm->mi_grid_base + cm->mi_stride + 1;
  memset(cm->mi_grid_base, 0,
         cm->mi_stride * (cm->mi_rows + 1) * sizeof(*cm->mi_grid_base));
}

56
static int av1_dec_alloc_mi(AV1_COMMON *cm, int mi_size) {
Adrian Grange's avatar
Adrian Grange committed
57
  cm->mip = aom_calloc(mi_size, sizeof(*cm->mip));
clang-format's avatar
clang-format committed
58
  if (!cm->mip) return 1;
Jingning Han's avatar
Jingning Han committed
59
  cm->mi_alloc_size = mi_size;
Adrian Grange's avatar
Adrian Grange committed
60
  cm->mi_grid_base = (MODE_INFO **)aom_calloc(mi_size, sizeof(MODE_INFO *));
clang-format's avatar
clang-format committed
61
  if (!cm->mi_grid_base) return 1;
Jingning Han's avatar
Jingning Han committed
62 63 64
  return 0;
}

65
static void av1_dec_free_mi(AV1_COMMON *cm) {
Adrian Grange's avatar
Adrian Grange committed
66
  aom_free(cm->mip);
Jingning Han's avatar
Jingning Han committed
67
  cm->mip = NULL;
Adrian Grange's avatar
Adrian Grange committed
68
  aom_free(cm->mi_grid_base);
Jingning Han's avatar
Jingning Han committed
69 70 71
  cm->mi_grid_base = NULL;
}

72 73 74
AV1Decoder *av1_decoder_create(BufferPool *const pool) {
  AV1Decoder *volatile const pbi = aom_memalign(32, sizeof(*pbi));
  AV1_COMMON *volatile const cm = pbi ? &pbi->common : NULL;
Jingning Han's avatar
Jingning Han committed
75

clang-format's avatar
clang-format committed
76
  if (!cm) return NULL;
Jingning Han's avatar
Jingning Han committed
77

78
  av1_zero(*pbi);
Jingning Han's avatar
Jingning Han committed
79 80 81

  if (setjmp(cm->error.jmp)) {
    cm->error.setjmp = 0;
82
    av1_decoder_remove(pbi);
Jingning Han's avatar
Jingning Han committed
83 84 85 86 87
    return NULL;
  }

  cm->error.setjmp = 1;

Adrian Grange's avatar
Adrian Grange committed
88
  CHECK_MEM_ERROR(cm, cm->fc, (FRAME_CONTEXT *)aom_calloc(1, sizeof(*cm->fc)));
clang-format's avatar
clang-format committed
89 90
  CHECK_MEM_ERROR(
      cm, cm->frame_contexts,
Adrian Grange's avatar
Adrian Grange committed
91
      (FRAME_CONTEXT *)aom_calloc(FRAME_CONTEXTS, sizeof(*cm->frame_contexts)));
Jingning Han's avatar
Jingning Han committed
92 93 94 95 96 97 98 99 100 101 102 103

  pbi->need_resync = 1;
  once(initialize_dec);

  // Initialize the references to not point to any frame buffers.
  memset(&cm->ref_frame_map, -1, sizeof(cm->ref_frame_map));
  memset(&cm->next_ref_frame_map, -1, sizeof(cm->next_ref_frame_map));

  cm->current_video_frame = 0;
  pbi->ready_for_new_data = 1;
  pbi->common.buffer_pool = pool;

Adrian Grange's avatar
Adrian Grange committed
104 105
  cm->bit_depth = AOM_BITS_8;
  cm->dequant_bit_depth = AOM_BITS_8;
Jingning Han's avatar
Jingning Han committed
106

107 108 109
  cm->alloc_mi = av1_dec_alloc_mi;
  cm->free_mi = av1_dec_free_mi;
  cm->setup_mi = av1_dec_setup_mi;
Jingning Han's avatar
Jingning Han committed
110

111
  av1_loop_filter_init(cm);
Jingning Han's avatar
Jingning Han committed
112

113 114 115 116
#if CONFIG_AOM_QM
  aom_qm_init(cm);
#endif

Jingning Han's avatar
Jingning Han committed
117 118
  cm->error.setjmp = 0;

Adrian Grange's avatar
Adrian Grange committed
119
  aom_get_worker_interface()->init(&pbi->lf_worker);
Jingning Han's avatar
Jingning Han committed
120 121 122 123

  return pbi;
}

124
void av1_decoder_remove(AV1Decoder *pbi) {
Jingning Han's avatar
Jingning Han committed
125 126
  int i;

clang-format's avatar
clang-format committed
127
  if (!pbi) return;
128

Adrian Grange's avatar
Adrian Grange committed
129 130 131
  aom_get_worker_interface()->end(&pbi->lf_worker);
  aom_free(pbi->lf_worker.data1);
  aom_free(pbi->tile_data);
Jingning Han's avatar
Jingning Han committed
132
  for (i = 0; i < pbi->num_tile_workers; ++i) {
Adrian Grange's avatar
Adrian Grange committed
133
    AVxWorker *const worker = &pbi->tile_workers[i];
Adrian Grange's avatar
Adrian Grange committed
134
    aom_get_worker_interface()->end(worker);
Jingning Han's avatar
Jingning Han committed
135
  }
Adrian Grange's avatar
Adrian Grange committed
136 137 138
  aom_free(pbi->tile_worker_data);
  aom_free(pbi->tile_worker_info);
  aom_free(pbi->tile_workers);
Jingning Han's avatar
Jingning Han committed
139 140

  if (pbi->num_tile_workers > 0) {
141
    av1_loop_filter_dealloc(&pbi->lf_row_sync);
Jingning Han's avatar
Jingning Han committed
142 143
  }

Adrian Grange's avatar
Adrian Grange committed
144
  aom_free(pbi);
Jingning Han's avatar
Jingning Han committed
145 146 147 148
}

static int equal_dimensions(const YV12_BUFFER_CONFIG *a,
                            const YV12_BUFFER_CONFIG *b) {
clang-format's avatar
clang-format committed
149 150
  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;
Jingning Han's avatar
Jingning Han committed
151 152
}

153
aom_codec_err_t av1_copy_reference_dec(AV1Decoder *pbi,
Adrian Grange's avatar
Adrian Grange committed
154
                                        AOM_REFFRAME ref_frame_flag,
Yaowu Xu's avatar
Yaowu Xu committed
155
                                        YV12_BUFFER_CONFIG *sd) {
156
  AV1_COMMON *cm = &pbi->common;
Jingning Han's avatar
Jingning Han committed
157 158 159

  /* 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
Adrian Grange's avatar
Adrian Grange committed
160
   * aomenc --test-decode functionality working, and will be replaced in a
161
   * later commit that adds AV1-specific controls for this functionality.
Jingning Han's avatar
Jingning Han committed
162
   */
Adrian Grange's avatar
Adrian Grange committed
163
  if (ref_frame_flag == AOM_LAST_FLAG) {
Jingning Han's avatar
Jingning Han committed
164 165
    const YV12_BUFFER_CONFIG *const cfg = get_ref_frame(cm, 0);
    if (cfg == NULL) {
Adrian Grange's avatar
Adrian Grange committed
166
      aom_internal_error(&cm->error, AOM_CODEC_ERROR,
Jingning Han's avatar
Jingning Han committed
167
                         "No 'last' reference frame");
Adrian Grange's avatar
Adrian Grange committed
168
      return AOM_CODEC_ERROR;
Jingning Han's avatar
Jingning Han committed
169 170
    }
    if (!equal_dimensions(cfg, sd))
Adrian Grange's avatar
Adrian Grange committed
171
      aom_internal_error(&cm->error, AOM_CODEC_ERROR,
Jingning Han's avatar
Jingning Han committed
172 173
                         "Incorrect buffer dimensions");
    else
Adrian Grange's avatar
Adrian Grange committed
174
      aom_yv12_copy_frame(cfg, sd);
Jingning Han's avatar
Jingning Han committed
175
  } else {
Adrian Grange's avatar
Adrian Grange committed
176
    aom_internal_error(&cm->error, AOM_CODEC_ERROR, "Invalid reference frame");
Jingning Han's avatar
Jingning Han committed
177 178 179 180 181
  }

  return cm->error.error_code;
}

182
aom_codec_err_t av1_set_reference_dec(AV1_COMMON *cm,
Adrian Grange's avatar
Adrian Grange committed
183
                                       AOM_REFFRAME ref_frame_flag,
clang-format's avatar
clang-format committed
184
                                       YV12_BUFFER_CONFIG *sd) {
Jingning Han's avatar
Jingning Han committed
185 186 187 188 189
  RefBuffer *ref_buf = NULL;
  RefCntBuffer *const frame_bufs = cm->buffer_pool->frame_bufs;

  // 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
Adrian Grange's avatar
Adrian Grange committed
190
  // aomenc --test-decode functionality working, and will be replaced in a
191
  // later commit that adds AV1-specific controls for this functionality.
Adrian Grange's avatar
Adrian Grange committed
192
  if (ref_frame_flag == AOM_LAST_FLAG) {
Jingning Han's avatar
Jingning Han committed
193
    ref_buf = &cm->frame_refs[0];
Adrian Grange's avatar
Adrian Grange committed
194
  } else if (ref_frame_flag == AOM_GOLD_FLAG) {
Jingning Han's avatar
Jingning Han committed
195
    ref_buf = &cm->frame_refs[1];
Adrian Grange's avatar
Adrian Grange committed
196
  } else if (ref_frame_flag == AOM_ALT_FLAG) {
Jingning Han's avatar
Jingning Han committed
197 198
    ref_buf = &cm->frame_refs[2];
  } else {
Adrian Grange's avatar
Adrian Grange committed
199
    aom_internal_error(&cm->error, AOM_CODEC_ERROR, "Invalid reference frame");
Jingning Han's avatar
Jingning Han committed
200 201 202 203
    return cm->error.error_code;
  }

  if (!equal_dimensions(ref_buf->buf, sd)) {
Adrian Grange's avatar
Adrian Grange committed
204
    aom_internal_error(&cm->error, AOM_CODEC_ERROR,
Jingning Han's avatar
Jingning Han committed
205 206 207 208 209 210
                       "Incorrect buffer dimensions");
  } else {
    int *ref_fb_ptr = &ref_buf->idx;

    // Find an empty frame buffer.
    const int free_fb = get_free_fb(cm);
Adrian Grange's avatar
Adrian Grange committed
211
    if (cm->new_fb_idx == INVALID_IDX) return AOM_CODEC_MEM_ERROR;
Jingning Han's avatar
Jingning Han committed
212 213 214 215 216 217 218 219

    // Decrease ref_count since it will be increased again in
    // ref_cnt_fb() below.
    --frame_bufs[free_fb].ref_count;

    // Manage the reference counters and copy image.
    ref_cnt_fb(frame_bufs, ref_fb_ptr, free_fb);
    ref_buf->buf = &frame_bufs[*ref_fb_ptr].buf;
Adrian Grange's avatar
Adrian Grange committed
220
    aom_yv12_copy_frame(sd, ref_buf->buf);
Jingning Han's avatar
Jingning Han committed
221 222 223 224 225 226
  }

  return cm->error.error_code;
}

/* If any buffer updating is signaled it should be done here. */
227
static void swap_frame_buffers(AV1Decoder *pbi) {
Jingning Han's avatar
Jingning Han committed
228
  int ref_index = 0, mask;
229
  AV1_COMMON *const cm = &pbi->common;
Jingning Han's avatar
Jingning Han committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
  BufferPool *const pool = cm->buffer_pool;
  RefCntBuffer *const frame_bufs = cm->buffer_pool->frame_bufs;

  lock_buffer_pool(pool);
  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);
    }
    cm->ref_frame_map[ref_index] = cm->next_ref_frame_map[ref_index];
    ++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);
    cm->ref_frame_map[ref_index] = cm->next_ref_frame_map[ref_index];
  }
  unlock_buffer_pool(pool);
  pbi->hold_ref_buf = 0;
  cm->frame_to_show = get_frame_new_buffer(cm);

257
  if (!cm->frame_parallel_decode || !cm->show_frame) {
Jingning Han's avatar
Jingning Han committed
258 259 260 261 262 263 264 265 266 267
    lock_buffer_pool(pool);
    --frame_bufs[cm->new_fb_idx].ref_count;
    unlock_buffer_pool(pool);
  }

  // Invalidate these references until the next frame starts.
  for (ref_index = 0; ref_index < 3; ref_index++)
    cm->frame_refs[ref_index].idx = -1;
}

268
int av1_receive_compressed_data(AV1Decoder *pbi, size_t size,
clang-format's avatar
clang-format committed
269
                                 const uint8_t **psource) {
270
  AV1_COMMON *volatile const cm = &pbi->common;
Jingning Han's avatar
Jingning Han committed
271 272 273 274
  BufferPool *volatile const pool = cm->buffer_pool;
  RefCntBuffer *volatile const frame_bufs = cm->buffer_pool->frame_bufs;
  const uint8_t *source = *psource;
  int retcode = 0;
Adrian Grange's avatar
Adrian Grange committed
275
  cm->error.error_code = AOM_CODEC_OK;
Jingning Han's avatar
Jingning Han committed
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295

  if (size == 0) {
    // 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.
    if (cm->frame_refs[0].idx > 0) {
      assert(cm->frame_refs[0].buf != NULL);
      cm->frame_refs[0].buf->corrupted = 1;
    }
  }

  pbi->ready_for_new_data = 0;

  // Check if the previous frame was a frame without any references to it.
  // Release frame buffer if not decoding in frame parallel mode.
clang-format's avatar
clang-format committed
296 297
  if (!cm->frame_parallel_decode && cm->new_fb_idx >= 0 &&
      frame_bufs[cm->new_fb_idx].ref_count == 0)
Jingning Han's avatar
Jingning Han committed
298 299 300 301
    pool->release_fb_cb(pool->cb_priv,
                        &frame_bufs[cm->new_fb_idx].raw_frame_buffer);
  // Find a free frame buffer. Return error if can not find any.
  cm->new_fb_idx = get_free_fb(cm);
Adrian Grange's avatar
Adrian Grange committed
302
  if (cm->new_fb_idx == INVALID_IDX) return AOM_CODEC_MEM_ERROR;
Jingning Han's avatar
Jingning Han committed
303 304 305 306 307

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

  pbi->hold_ref_buf = 0;
308
  if (cm->frame_parallel_decode) {
Adrian Grange's avatar
Adrian Grange committed
309
    AVxWorker *const worker = pbi->frame_worker_owner;
310
    av1_frameworker_lock_stats(worker);
Jingning Han's avatar
Jingning Han committed
311 312 313 314 315
    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;
316
    av1_frameworker_unlock_stats(worker);
Jingning Han's avatar
Jingning Han committed
317 318 319 320 321
  } else {
    pbi->cur_buf = &frame_bufs[cm->new_fb_idx];
  }

  if (setjmp(cm->error.jmp)) {
Adrian Grange's avatar
Adrian Grange committed
322
    const AVxWorkerInterface *const winterface = aom_get_worker_interface();
Jingning Han's avatar
Jingning Han committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    int i;

    cm->error.setjmp = 0;
    pbi->ready_for_new_data = 1;

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

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

Adrian Grange's avatar
Adrian Grange committed
362
    aom_clear_system_state();
Jingning Han's avatar
Jingning Han committed
363 364 365 366
    return -1;
  }

  cm->error.setjmp = 1;
367
  av1_decode_frame(pbi, source, source + size, psource);
Jingning Han's avatar
Jingning Han committed
368 369 370

  swap_frame_buffers(pbi);

Adrian Grange's avatar
Adrian Grange committed
371
  aom_clear_system_state();
Jingning Han's avatar
Jingning Han committed
372 373 374 375

  if (!cm->show_existing_frame) {
    cm->last_show_frame = cm->show_frame;
    cm->prev_frame = cm->cur_frame;
376
    if (cm->seg.enabled && !cm->frame_parallel_decode)
377
      av1_swap_current_and_last_seg_map(cm);
Jingning Han's avatar
Jingning Han committed
378 379 380
  }

  // Update progress in frame parallel decode.
381
  if (cm->frame_parallel_decode) {
Jingning Han's avatar
Jingning Han committed
382 383
    // Need to lock the mutex here as another thread may
    // be accessing this buffer.
Adrian Grange's avatar
Adrian Grange committed
384
    AVxWorker *const worker = pbi->frame_worker_owner;
Jingning Han's avatar
Jingning Han committed
385
    FrameWorkerData *const frame_worker_data = worker->data1;
386
    av1_frameworker_lock_stats(worker);
Jingning Han's avatar
Jingning Han committed
387 388 389 390 391 392

    if (cm->show_frame) {
      cm->current_video_frame++;
    }
    frame_worker_data->frame_decoded = 1;
    frame_worker_data->frame_context_ready = 1;
393 394
    av1_frameworker_signal_stats(worker);
    av1_frameworker_unlock_stats(worker);
Jingning Han's avatar
Jingning Han committed
395 396 397 398 399 400 401 402 403 404 405 406
  } else {
    cm->last_width = cm->width;
    cm->last_height = cm->height;
    if (cm->show_frame) {
      cm->current_video_frame++;
    }
  }

  cm->error.setjmp = 0;
  return retcode;
}

407 408
int av1_get_raw_frame(AV1Decoder *pbi, YV12_BUFFER_CONFIG *sd) {
  AV1_COMMON *const cm = &pbi->common;
Jingning Han's avatar
Jingning Han committed
409 410
  int ret = -1;

clang-format's avatar
clang-format committed
411
  if (pbi->ready_for_new_data == 1) return ret;
Jingning Han's avatar
Jingning Han committed
412 413 414 415

  pbi->ready_for_new_data = 1;

  /* no raw frame to show!!! */
clang-format's avatar
clang-format committed
416
  if (!cm->show_frame) return ret;
Jingning Han's avatar
Jingning Han committed
417 418 419 420 421

  pbi->ready_for_new_data = 1;

  *sd = *cm->frame_to_show;
  ret = 0;
Adrian Grange's avatar
Adrian Grange committed
422
  aom_clear_system_state();
Jingning Han's avatar
Jingning Han committed
423 424 425
  return ret;
}

426
aom_codec_err_t av1_parse_superframe_index(const uint8_t *data, size_t data_sz,
clang-format's avatar
clang-format committed
427
                                            uint32_t sizes[8], int *count,
Adrian Grange's avatar
Adrian Grange committed
428
                                            aom_decrypt_cb decrypt_cb,
clang-format's avatar
clang-format committed
429
                                            void *decrypt_state) {
Jingning Han's avatar
Jingning Han committed
430 431 432 433 434 435 436
  // 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;
437 438 439
#if CONFIG_MISC_FIXES
  size_t frame_sz_sum = 0;
#endif
Jingning Han's avatar
Jingning Han committed
440 441 442 443 444 445 446 447

  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;
448
    const size_t index_sz = 2 + mag * (frames - CONFIG_MISC_FIXES);
Jingning Han's avatar
Jingning Han committed
449 450 451

    // This chunk is marked as having a superframe index but doesn't have
    // enough data for it, thus it's an invalid superframe index.
Adrian Grange's avatar
Adrian Grange committed
452
    if (data_sz < index_sz) return AOM_CODEC_CORRUPT_FRAME;
Jingning Han's avatar
Jingning Han committed
453 454

    {
clang-format's avatar
clang-format committed
455 456
      const uint8_t marker2 =
          read_marker(decrypt_cb, decrypt_state, data + data_sz - index_sz);
Jingning Han's avatar
Jingning Han committed
457 458 459 460

      // 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.
Adrian Grange's avatar
Adrian Grange committed
461
      if (marker != marker2) return AOM_CODEC_CORRUPT_FRAME;
Jingning Han's avatar
Jingning Han committed
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
    }

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

477
      for (i = 0; i < frames - CONFIG_MISC_FIXES; ++i) {
Jingning Han's avatar
Jingning Han committed
478 479
        uint32_t this_sz = 0;

clang-format's avatar
clang-format committed
480
        for (j = 0; j < mag; ++j) this_sz |= (*x++) << (j * 8);
481
        this_sz += CONFIG_MISC_FIXES;
Jingning Han's avatar
Jingning Han committed
482
        sizes[i] = this_sz;
483 484 485
#if CONFIG_MISC_FIXES
        frame_sz_sum += this_sz;
#endif
Jingning Han's avatar
Jingning Han committed
486
      }
487 488 489
#if CONFIG_MISC_FIXES
      sizes[i] = data_sz - index_sz - frame_sz_sum;
#endif
Jingning Han's avatar
Jingning Han committed
490 491 492
      *count = frames;
    }
  }
Adrian Grange's avatar
Adrian Grange committed
493
  return AOM_CODEC_OK;
Jingning Han's avatar
Jingning Han committed
494
}