vp9_decodeframe.c 46 KB
Newer Older
      setup_token_decoder(data, data_end, size, &cm->error,
                          &tile_data->bit_reader);
      setup_tile_context(pbi, &tile_data->xd, 0, tile_col);
      setup_tile_macroblockd(tile_data);

      worker->had_error = 0;
      if (i == num_workers - 1 || tile_col == tile_cols - 1) {
        vp9_worker_execute(worker);
      } else {
        vp9_worker_launch(worker);
      }

      data += size;
      ++tile_col;
    }

    for (; i > 0; --i) {
      VP9Worker *const worker = &pbi->tile_workers[i - 1];
      pbi->mb.corrupted |= !vp9_worker_sync(worker);
    }
  }

  {
    const int final_worker = (tile_cols + num_workers - 1) % num_workers;
    TileWorkerData *const tile_data =
        (TileWorkerData*)pbi->tile_workers[final_worker].data1;
    return vp9_reader_find_end(&tile_data->bit_reader);
  }
}

static void check_sync_code(VP9_COMMON *cm, struct vp9_read_bit_buffer *rb) {
  if (vp9_rb_read_literal(rb, 8) != VP9_SYNC_CODE_0 ||
      vp9_rb_read_literal(rb, 8) != VP9_SYNC_CODE_1 ||
      vp9_rb_read_literal(rb, 8) != VP9_SYNC_CODE_2) {
    vpx_internal_error(&cm->error, VPX_CODEC_UNSUP_BITSTREAM,
                       "Invalid frame sync code");
  }
}
John Koleszar's avatar
John Koleszar committed

Frank Galligan's avatar
Frank Galligan committed
static void error_handler(void *data, size_t bit_offset) {
  VP9_COMMON *const cm = (VP9_COMMON *)data;
  vpx_internal_error(&cm->error, VPX_CODEC_CORRUPT_FRAME, "Truncated packet");
}

#define RESERVED \
  if (vp9_rb_read_bit(rb)) \
      vpx_internal_error(&cm->error, VPX_CODEC_UNSUP_BITSTREAM, \
                         "Reserved bit must be unset")

static size_t read_uncompressed_header(VP9D_COMP *pbi,
                                       struct vp9_read_bit_buffer *rb) {
  VP9_COMMON *const cm = &pbi->common;
  size_t sz;

  cm->last_frame_type = cm->frame_type;
  if (vp9_rb_read_literal(rb, 2) != VP9_FRAME_MARKER)
      vpx_internal_error(&cm->error, VPX_CODEC_UNSUP_BITSTREAM,
                         "Invalid frame marker");

  cm->version = vp9_rb_read_bit(rb);
  RESERVED;

  if (vp9_rb_read_bit(rb)) {
    // show an existing frame directly
    int frame_to_show = cm->ref_frame_map[vp9_rb_read_literal(rb, 3)];
    ref_cnt_fb(cm->fb_idx_ref_cnt, &cm->new_fb_idx, frame_to_show);
    pbi->refresh_frame_flags = 0;
    cm->lf.filter_level = 0;
  cm->frame_type = (FRAME_TYPE) vp9_rb_read_bit(rb);
  cm->show_frame = vp9_rb_read_bit(rb);
  cm->error_resilient_mode = vp9_rb_read_bit(rb);

  if (cm->frame_type == KEY_FRAME) {
    check_sync_code(cm, rb);
Dmitry Kovalev's avatar
Dmitry Kovalev committed
    cm->color_space = vp9_rb_read_literal(rb, 3);  // colorspace
    if (cm->color_space != SRGB) {
      vp9_rb_read_bit(rb);  // [16,235] (including xvycc) vs [0,255] range
      if (cm->version == 1) {
        cm->subsampling_x = vp9_rb_read_bit(rb);
        cm->subsampling_y = vp9_rb_read_bit(rb);
        vp9_rb_read_bit(rb);  // has extra plane
      } else {
        cm->subsampling_y = cm->subsampling_x = 1;
      }
      if (cm->version == 1) {
        cm->subsampling_y = cm->subsampling_x = 0;
        vp9_rb_read_bit(rb);  // has extra plane
      } else {
        vpx_internal_error(&cm->error, VPX_CODEC_UNSUP_BITSTREAM,
                           "RGB not supported in profile 0");
      }
John Koleszar's avatar
John Koleszar committed

Dmitry Kovalev's avatar
Dmitry Kovalev committed
    pbi->refresh_frame_flags = (1 << REF_FRAMES) - 1;
Dmitry Kovalev's avatar
Dmitry Kovalev committed
    for (i = 0; i < REFS_PER_FRAME; ++i)
      cm->active_ref_idx[i] = cm->new_fb_idx;

    setup_frame_size(pbi, rb);
    cm->intra_only = cm->show_frame ? 0 : vp9_rb_read_bit(rb);

    cm->reset_frame_context = cm->error_resilient_mode ?
        0 : vp9_rb_read_literal(rb, 2);
    if (cm->intra_only) {
      check_sync_code(cm, rb);
Dmitry Kovalev's avatar
Dmitry Kovalev committed
      pbi->refresh_frame_flags = vp9_rb_read_literal(rb, REF_FRAMES);
      setup_frame_size(pbi, rb);
Dmitry Kovalev's avatar
Dmitry Kovalev committed
      pbi->refresh_frame_flags = vp9_rb_read_literal(rb, REF_FRAMES);
Dmitry Kovalev's avatar
Dmitry Kovalev committed
      for (i = 0; i < REFS_PER_FRAME; ++i) {
        const int ref = vp9_rb_read_literal(rb, REF_FRAMES_LOG2);
        cm->active_ref_idx[i] = cm->ref_frame_map[ref];
        cm->ref_frame_sign_bias[LAST_FRAME + i] = vp9_rb_read_bit(rb);
      }
      setup_frame_size_with_refs(pbi, rb);
      cm->allow_high_precision_mv = vp9_rb_read_bit(rb);
      cm->mcomp_filter_type = read_interp_filter_type(rb);
Dmitry Kovalev's avatar
Dmitry Kovalev committed
      for (i = 0; i < REFS_PER_FRAME; ++i) {
        vp9_setup_scale_factors(cm, i);
        if (vp9_is_scaled(&cm->active_ref_scale_comm[i]))
          vp9_extend_frame_borders(&cm->yv12_fb[cm->active_ref_idx[i]],
                                   cm->subsampling_x, cm->subsampling_y);
      }
  if (!cm->error_resilient_mode) {
    cm->refresh_frame_context = vp9_rb_read_bit(rb);
    cm->frame_parallel_decoding_mode = vp9_rb_read_bit(rb);
  } else {
    cm->refresh_frame_context = 0;
    cm->frame_parallel_decoding_mode = 1;
  }

  // This flag will be overridden by the call to vp9_setup_past_independence
  // below, forcing the use of context 0 for those frame types.
Dmitry Kovalev's avatar
Dmitry Kovalev committed
  cm->frame_context_idx = vp9_rb_read_literal(rb, FRAME_CONTEXTS_LOG2);
  if (frame_is_intra_only(cm) || cm->error_resilient_mode)
    vp9_setup_past_independence(cm);
  setup_loopfilter(&cm->lf, rb);
  setup_quantization(cm, &pbi->mb, rb);
  setup_segmentation(&cm->seg, rb);
  sz = vp9_rb_read_literal(rb, 16);
  if (sz == 0)
    vpx_internal_error(&cm->error, VPX_CODEC_CORRUPT_FRAME,
                       "Invalid header size");

  return sz;
John Koleszar's avatar
John Koleszar committed

static int read_compressed_header(VP9D_COMP *pbi, const uint8_t *data,
                                  size_t partition_size) {
  VP9_COMMON *const cm = &pbi->common;
  MACROBLOCKD *const xd = &pbi->mb;
  FRAME_CONTEXT *const fc = &cm->fc;

  if (vp9_reader_init(&r, data, partition_size))
    vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
                       "Failed to allocate bool decoder 0");

  cm->tx_mode = xd->lossless ? ONLY_4X4 : read_tx_mode(&r);
  if (cm->tx_mode == TX_MODE_SELECT)
    read_tx_mode_probs(&fc->tx_probs, &r);
  read_coef_probs(fc, cm->tx_mode, &r);

  for (k = 0; k < MBSKIP_CONTEXTS; ++k)
    vp9_diff_update_prob(&r, &fc->mbskip_probs[k]);

  if (!frame_is_intra_only(cm)) {
    nmv_context *const nmvc = &fc->nmvc;
    int i, j;

    read_inter_mode_probs(fc, &r);

    if (cm->mcomp_filter_type == SWITCHABLE)
      read_switchable_interp_probs(fc, &r);
    for (i = 0; i < INTRA_INTER_CONTEXTS; i++)
      vp9_diff_update_prob(&r, &fc->intra_inter_prob[i]);

    cm->reference_mode = read_reference_mode(cm, &r);
    read_reference_mode_probs(cm, &r);

    for (j = 0; j < BLOCK_SIZE_GROUPS; j++)
      for (i = 0; i < INTRA_MODES - 1; ++i)
        vp9_diff_update_prob(&r, &fc->y_mode_prob[j][i]);

    for (j = 0; j < PARTITION_CONTEXTS; ++j)
      for (i = 0; i < PARTITION_TYPES - 1; ++i)
        vp9_diff_update_prob(&r, &fc->partition_prob[j][i]);
    read_mv_probs(nmvc, cm->allow_high_precision_mv, &r);

  return vp9_reader_has_error(&r);
}

void vp9_init_dequantizer(VP9_COMMON *cm) {
  int q;

  for (q = 0; q < QINDEX_RANGE; q++) {
    cm->y_dequant[q][0] = vp9_dc_quant(q, cm->y_dc_delta_q);
    cm->y_dequant[q][1] = vp9_ac_quant(q, 0);
    cm->uv_dequant[q][0] = vp9_dc_quant(q, cm->uv_dc_delta_q);
    cm->uv_dequant[q][1] = vp9_ac_quant(q, cm->uv_ac_delta_q);
#ifdef NDEBUG
#define debug_check_frame_counts(cm) (void)0
#else  // !NDEBUG
// Counts should only be incremented when frame_parallel_decoding_mode and
// error_resilient_mode are disabled.
static void debug_check_frame_counts(const VP9_COMMON *const cm) {
  FRAME_COUNTS zero_counts;
  vp9_zero(zero_counts);
  assert(cm->frame_parallel_decoding_mode || cm->error_resilient_mode);
  assert(!memcmp(cm->counts.y_mode, zero_counts.y_mode,
                 sizeof(cm->counts.y_mode)));
  assert(!memcmp(cm->counts.uv_mode, zero_counts.uv_mode,
                 sizeof(cm->counts.uv_mode)));
  assert(!memcmp(cm->counts.partition, zero_counts.partition,
                 sizeof(cm->counts.partition)));
  assert(!memcmp(cm->counts.coef, zero_counts.coef,
                 sizeof(cm->counts.coef)));
  assert(!memcmp(cm->counts.eob_branch, zero_counts.eob_branch,
                 sizeof(cm->counts.eob_branch)));
  assert(!memcmp(cm->counts.switchable_interp, zero_counts.switchable_interp,
                 sizeof(cm->counts.switchable_interp)));
  assert(!memcmp(cm->counts.inter_mode, zero_counts.inter_mode,
                 sizeof(cm->counts.inter_mode)));
  assert(!memcmp(cm->counts.intra_inter, zero_counts.intra_inter,
                 sizeof(cm->counts.intra_inter)));
  assert(!memcmp(cm->counts.comp_inter, zero_counts.comp_inter,
                 sizeof(cm->counts.comp_inter)));
  assert(!memcmp(cm->counts.single_ref, zero_counts.single_ref,
                 sizeof(cm->counts.single_ref)));
  assert(!memcmp(cm->counts.comp_ref, zero_counts.comp_ref,
                 sizeof(cm->counts.comp_ref)));
  assert(!memcmp(&cm->counts.tx, &zero_counts.tx, sizeof(cm->counts.tx)));
  assert(!memcmp(cm->counts.mbskip, zero_counts.mbskip,
                 sizeof(cm->counts.mbskip)));
  assert(!memcmp(&cm->counts.mv, &zero_counts.mv, sizeof(cm->counts.mv)));
}
#endif  // NDEBUG

int vp9_decode_frame(VP9D_COMP *pbi, const uint8_t **p_data_end) {
  int i;
  VP9_COMMON *const cm = &pbi->common;
  MACROBLOCKD *const xd = &pbi->mb;
  const uint8_t *data = pbi->source;
  const uint8_t *const data_end = pbi->source + pbi->source_sz;
Yaowu Xu's avatar
Yaowu Xu committed

  struct vp9_read_bit_buffer rb = { data, data_end, 0, cm, error_handler };
  const size_t first_partition_size = read_uncompressed_header(pbi, &rb);
  const int keyframe = cm->frame_type == KEY_FRAME;
  const int tile_rows = 1 << cm->log2_tile_rows;
  const int tile_cols = 1 << cm->log2_tile_cols;
  YV12_BUFFER_CONFIG *const new_fb = get_frame_new_buffer(cm);
  if (!first_partition_size) {
      // showing a frame directly
      *p_data_end = data + 1;
      return 0;
  if (!pbi->decoded_key_frame && !keyframe)
John Koleszar's avatar
John Koleszar committed
    return -1;
  data += vp9_rb_bytes_read(&rb);
  if (!read_is_valid(data, first_partition_size, data_end))
    vpx_internal_error(&cm->error, VPX_CODEC_CORRUPT_FRAME,
                       "Truncated packet or corrupt header length");
  pbi->do_loopfilter_inline =
      (cm->log2_tile_rows | cm->log2_tile_cols) == 0 && cm->lf.filter_level;
  if (pbi->do_loopfilter_inline && pbi->lf_worker.data1 == NULL) {
    CHECK_MEM_ERROR(cm, pbi->lf_worker.data1, vpx_malloc(sizeof(LFWorkerData)));
    pbi->lf_worker.hook = (VP9WorkerHook)vp9_loop_filter_worker;
    if (pbi->oxcf.max_threads > 1 && !vp9_worker_reset(&pbi->lf_worker)) {
      vpx_internal_error(&cm->error, VPX_CODEC_ERROR,
                         "Loop filter thread creation failed");
    }
  }

  alloc_tile_storage(pbi, tile_rows, tile_cols);
  xd->mode_info_stride = cm->mode_info_stride;
  setup_plane_dequants(cm, xd, cm->base_qindex);
  setup_block_dptrs(xd, cm->subsampling_x, cm->subsampling_y);
  cm->fc = cm->frame_contexts[cm->frame_context_idx];
  vp9_zero(cm->counts);
  for (i = 0; i < MAX_MB_PLANE; ++i)
    vpx_memset(xd->plane[i].dqcoeff, 0, 64 * 64 * sizeof(int16_t));
  xd->corrupted = 0;
  new_fb->corrupted = read_compressed_header(pbi, data, first_partition_size);
  // TODO(jzern): remove frame_parallel_decoding_mode restriction for
  // single-frame tile decoding.
  if (pbi->oxcf.max_threads > 1 && tile_rows == 1 && tile_cols > 1 &&
      cm->frame_parallel_decoding_mode) {
    *p_data_end = decode_tiles_mt(pbi, data + first_partition_size);
  } else {
    *p_data_end = decode_tiles(pbi, data + first_partition_size);
  }
John Koleszar's avatar
John Koleszar committed

  cm->last_width = cm->width;
  cm->last_height = cm->height;
  new_fb->corrupted |= xd->corrupted;
John Koleszar's avatar
John Koleszar committed

  if (!pbi->decoded_key_frame) {
    if (keyframe && !new_fb->corrupted)
John Koleszar's avatar
John Koleszar committed
      pbi->decoded_key_frame = 1;
    else
      vpx_internal_error(&cm->error, VPX_CODEC_CORRUPT_FRAME,
John Koleszar's avatar
John Koleszar committed
                         "A stream must start with a complete key frame");
  }
  if (!cm->error_resilient_mode && !cm->frame_parallel_decoding_mode) {
    vp9_adapt_coef_probs(cm);
    if (!frame_is_intra_only(cm)) {
      vp9_adapt_mode_probs(cm);
      vp9_adapt_mv_probs(cm, cm->allow_high_precision_mv);
  } else {
    debug_check_frame_counts(cm);
John Koleszar's avatar
John Koleszar committed
  }
  if (cm->refresh_frame_context)
    cm->frame_contexts[cm->frame_context_idx] = cm->fc;
John Koleszar's avatar
John Koleszar committed
  return 0;
John Koleszar's avatar
John Koleszar committed
}