vp9_dx_iface.c 27.6 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
11
12
 */

#include <stdlib.h>
#include <string.h>
13

14
#include "./vpx_version.h"
15
16
17
18
19

#include "vpx/internal/vpx_codec_internal.h"
#include "vpx/vp8dx.h"
#include "vpx/vpx_decoder.h"

20
#include "vp9/common/vp9_frame_buffers.h"
21

22
#include "vp9/decoder/vp9_decoder.h"
23
#include "vp9/decoder/vp9_read_bit_buffer.h"
24

John Koleszar's avatar
John Koleszar committed
25
#include "vp9/vp9_iface_common.h"
John Koleszar's avatar
John Koleszar committed
26

27
#define VP9_CAP_POSTPROC (CONFIG_VP9_POSTPROC ? VPX_CODEC_CAP_POSTPROC : 0)
28
29

typedef vpx_codec_stream_info_t vp9_stream_info_t;
John Koleszar's avatar
John Koleszar committed
30

John Koleszar's avatar
John Koleszar committed
31
32
33
struct vpx_codec_alg_priv {
  vpx_codec_priv_t        base;
  vpx_codec_dec_cfg_t     cfg;
34
  vp9_stream_info_t       si;
John Koleszar's avatar
John Koleszar committed
35
36
  int                     postproc_cfg_set;
  vp8_postproc_cfg_t      postproc_cfg;
37
38
  vpx_decrypt_cb          decrypt_cb;
  void                   *decrypt_state;
John Koleszar's avatar
John Koleszar committed
39
  vpx_image_t             img;
40
  int                     invert_tile_order;
41
  int                     frame_parallel_decode;  // frame-based threading.
42
43
44
45
46
47
  int                     last_show_frame;  // Index of last output frame.

  VP9Worker               *frame_workers;
  int                     num_frame_workers;
  int                     next_submit_thread_id;
  int                     next_output_thread_id;
hkuang's avatar
hkuang committed
48

49
50
51
52
  // External frame buffer info to save for VP9 common.
  void *ext_priv;  // Private data associated with the external frame buffers.
  vpx_get_frame_buffer_cb_fn_t get_ext_fb_cb;
  vpx_release_frame_buffer_cb_fn_t release_ext_fb_cb;
John Koleszar's avatar
John Koleszar committed
53
54
};

55
static vpx_codec_err_t decoder_init(vpx_codec_ctx_t *ctx,
56
                                    vpx_codec_priv_enc_mr_cfg_t *data) {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
57
58
59
  // This function only allocates space for the vpx_codec_alg_priv_t
  // structure. More memory may be required at the time the stream
  // information becomes known.
60
61
  (void)data;

John Koleszar's avatar
John Koleszar committed
62
  if (!ctx->priv) {
63
64
    vpx_codec_alg_priv_t *alg_priv = vpx_memalign(32, sizeof(*alg_priv));
    if (alg_priv == NULL)
65
66
      return VPX_CODEC_MEM_ERROR;

67
68
69
    vp9_zero(*alg_priv);

    ctx->priv = (vpx_codec_priv_t *)alg_priv;
70
71
    ctx->priv->sz = sizeof(*ctx->priv);
    ctx->priv->iface = ctx->iface;
72
    ctx->priv->alg_priv = alg_priv;
73
74
    ctx->priv->alg_priv->si.sz = sizeof(ctx->priv->alg_priv->si);
    ctx->priv->init_flags = ctx->init_flags;
75
76
77
78
79
    ctx->priv->alg_priv->frame_parallel_decode =
        (ctx->init_flags & VPX_CODEC_USE_FRAME_THREADING);

    // Disable frame parallel decoding for now.
    ctx->priv->alg_priv->frame_parallel_decode = 0;
80
81
82
83
84

    if (ctx->config.dec) {
      // Update the reference to the config structure to an internal copy.
      ctx->priv->alg_priv->cfg = *ctx->config.dec;
      ctx->config.dec = &ctx->priv->alg_priv->cfg;
John Koleszar's avatar
John Koleszar committed
85
    }
John Koleszar's avatar
John Koleszar committed
86
  }
John Koleszar's avatar
John Koleszar committed
87

88
  return VPX_CODEC_OK;
John Koleszar's avatar
John Koleszar committed
89
90
}

91
static vpx_codec_err_t decoder_destroy(vpx_codec_alg_priv_t *ctx) {
92
93
94
95
96
97
98
99
  if (ctx->frame_workers != NULL) {
    int i;
    for (i = 0; i < ctx->num_frame_workers; ++i) {
      VP9Worker *const worker = &ctx->frame_workers[i];
      FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
      vp9_decoder_remove(worker_data->pbi);
      vpx_free(worker_data);
    }
100
  }
John Koleszar's avatar
John Koleszar committed
101

102
  vpx_free(ctx->frame_workers);
103
104
  vpx_free(ctx);

John Koleszar's avatar
John Koleszar committed
105
  return VPX_CODEC_OK;
John Koleszar's avatar
John Koleszar committed
106
107
}

108
109
110
111
112
113
114
static vpx_codec_err_t decoder_peek_si_internal(const uint8_t *data,
                                                unsigned int data_sz,
                                                vpx_codec_stream_info_t *si,
                                                vpx_decrypt_cb decrypt_cb,
                                                void *decrypt_state) {
  uint8_t clear_buffer[9];

115
116
  if (data + data_sz <= data)
    return VPX_CODEC_INVALID_PARAM;
117
118
119

  si->is_kf = 0;
  si->w = si->h = 0;
John Koleszar's avatar
John Koleszar committed
120

121
122
123
124
125
126
  if (decrypt_cb) {
    data_sz = MIN(sizeof(clear_buffer), data_sz);
    decrypt_cb(decrypt_state, data, clear_buffer, data_sz);
    data = clear_buffer;
  }

127
128
129
  {
    struct vp9_read_bit_buffer rb = { data, data + data_sz, 0, NULL, NULL };
    const int frame_marker = vp9_rb_read_literal(&rb, 2);
130
131
132
    const int version = vp9_rb_read_bit(&rb);
    (void) vp9_rb_read_bit(&rb);  // unused version bit

133
134
    if (frame_marker != VP9_FRAME_MARKER)
      return VPX_CODEC_UNSUP_BITSTREAM;
135

136
137
138
139
140
    if (version > 1) return VPX_CODEC_UNSUP_BITSTREAM;

    if (vp9_rb_read_bit(&rb)) {  // show an existing frame
      return VPX_CODEC_OK;
    }
James Zern's avatar
James Zern committed
141

142
143
144
    if (data_sz <= 8)
      return VPX_CODEC_UNSUP_BITSTREAM;

145
    si->is_kf = !vp9_rb_read_bit(&rb);
James Zern's avatar
James Zern committed
146
    if (si->is_kf) {
147
148
      const int sRGB = 7;
      int colorspace;
John Koleszar's avatar
John Koleszar committed
149

150
151
      rb.bit_offset += 1;  // show frame
      rb.bit_offset += 1;  // error resilient
John Koleszar's avatar
John Koleszar committed
152

153
154
155
      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) {
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
        return VPX_CODEC_UNSUP_BITSTREAM;
      }

      colorspace = vp9_rb_read_literal(&rb, 3);
      if (colorspace != sRGB) {
        rb.bit_offset += 1;  // [16,235] (including xvycc) vs [0,255] range
        if (version == 1) {
          rb.bit_offset += 2;  // subsampling x/y
          rb.bit_offset += 1;  // has extra plane
        }
      } else {
        if (version == 1) {
          rb.bit_offset += 1;  // has extra plane
        } else {
          // RGB is only available in version 1
          return VPX_CODEC_UNSUP_BITSTREAM;
        }
      }

      // TODO(jzern): these are available on non-keyframes in intra only mode.
      si->w = vp9_rb_read_literal(&rb, 16) + 1;
      si->h = vp9_rb_read_literal(&rb, 16) + 1;
James Zern's avatar
James Zern committed
178
    }
John Koleszar's avatar
John Koleszar committed
179
180
  }

181
  return VPX_CODEC_OK;
John Koleszar's avatar
John Koleszar committed
182
183
}

184
185
186
187
188
189
static vpx_codec_err_t decoder_peek_si(const uint8_t *data,
                                       unsigned int data_sz,
                                       vpx_codec_stream_info_t *si) {
  return decoder_peek_si_internal(data, data_sz, si, NULL, NULL);
}

190
191
static vpx_codec_err_t decoder_get_si(vpx_codec_alg_priv_t *ctx,
                                      vpx_codec_stream_info_t *si) {
Dmitry Kovalev's avatar
Dmitry Kovalev committed
192
193
194
  const size_t sz = (si->sz >= sizeof(vp9_stream_info_t))
                       ? sizeof(vp9_stream_info_t)
                       : sizeof(vpx_codec_stream_info_t);
John Koleszar's avatar
John Koleszar committed
195
  memcpy(si, &ctx->si, sz);
196
  si->sz = (unsigned int)sz;
John Koleszar's avatar
John Koleszar committed
197

John Koleszar's avatar
John Koleszar committed
198
  return VPX_CODEC_OK;
John Koleszar's avatar
John Koleszar committed
199
200
}

201
202
203
204
205
static void set_error_detail(vpx_codec_alg_priv_t *ctx,
                             const char *const error) {
  ctx->base.err_detail = error;
}

Dmitry Kovalev's avatar
Dmitry Kovalev committed
206
207
208
static vpx_codec_err_t update_error_state(vpx_codec_alg_priv_t *ctx,
                           const struct vpx_internal_error_info *error) {
  if (error->error_code)
209
    set_error_detail(ctx, error->has_detail ? error->detail : NULL);
John Koleszar's avatar
John Koleszar committed
210

Dmitry Kovalev's avatar
Dmitry Kovalev committed
211
  return error->error_code;
John Koleszar's avatar
John Koleszar committed
212
213
}

214
static void init_buffer_callbacks(vpx_codec_alg_priv_t *ctx) {
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  int i;

  for (i = 0; i < ctx->num_frame_workers; ++i) {
    VP9Worker *const worker = &ctx->frame_workers[i];
    FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
    VP9_COMMON *const cm = &worker_data->pbi->common;

    cm->new_fb_idx = -1;
    if (ctx->get_ext_fb_cb != NULL && ctx->release_ext_fb_cb != NULL) {
      cm->get_fb_cb = ctx->get_ext_fb_cb;
      cm->release_fb_cb = ctx->release_ext_fb_cb;
      cm->cb_priv = ctx->ext_priv;
    } else {
      cm->get_fb_cb = vp9_get_frame_buffer;
      cm->release_fb_cb = vp9_release_frame_buffer;
230

231
232
233
      if (vp9_alloc_internal_frame_buffers(&cm->int_frame_buffers))
        vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
                           "Failed to initialize internal frame buffers");
234

235
236
      cm->cb_priv = &cm->int_frame_buffers;
    }
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  }
}

static void set_default_ppflags(vp8_postproc_cfg_t *cfg) {
  cfg->post_proc_flag = VP8_DEBLOCK | VP8_DEMACROBLOCK;
  cfg->deblocking_level = 4;
  cfg->noise_level = 0;
}

static void set_ppflags(const vpx_codec_alg_priv_t *ctx,
                        vp9_ppflags_t *flags) {
  flags->post_proc_flag =
      ctx->postproc_cfg.post_proc_flag;

  flags->deblocking_level = ctx->postproc_cfg.deblocking_level;
  flags->noise_level = ctx->postproc_cfg.noise_level;
}

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
static int frame_worker_hook(void *arg1, void *arg2) {
  FrameWorkerData *const worker_data = (FrameWorkerData *)arg1;
  const uint8_t *data = worker_data->data;
  (void)arg2;
  worker_data->result = vp9_receive_compressed_data(worker_data->pbi,
                                                    worker_data->data_size,
                                                    &data);
  worker_data->data_end = data;
  return !worker_data->result;
}

static vpx_codec_err_t init_decoder(vpx_codec_alg_priv_t *ctx) {
  int i;
  const VP9WorkerInterface *const winterface = vp9_get_worker_interface();

  ctx->last_show_frame = -1;
  ctx->next_submit_thread_id = 0;
  ctx->next_output_thread_id = 0;
  ctx->num_frame_workers =
      (ctx->frame_parallel_decode == 1) ? ctx->cfg.threads: 1;

  ctx->frame_workers = (VP9Worker *)
      vpx_malloc(ctx->num_frame_workers * sizeof(*ctx->frame_workers));
  if (ctx->frame_workers == NULL) {
    set_error_detail(ctx, "Failed to allocate frame_workers");
    return VPX_CODEC_MEM_ERROR;
  }
hkuang's avatar
hkuang committed
282

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  for (i = 0; i < ctx->num_frame_workers; ++i) {
    VP9Worker *const worker = &ctx->frame_workers[i];
    FrameWorkerData *worker_data = NULL;
    winterface->init(worker);
    worker->data1 = vpx_memalign(32, sizeof(FrameWorkerData));
    if (worker->data1 == NULL) {
      set_error_detail(ctx, "Failed to allocate worker_data");
      return VPX_CODEC_MEM_ERROR;
    }
    worker_data = (FrameWorkerData *)worker->data1;
    worker_data->pbi = vp9_decoder_create();
    if (worker_data->pbi == NULL) {
      set_error_detail(ctx, "Failed to allocate worker_data");
      return VPX_CODEC_MEM_ERROR;
    }

    // If decoding in serial mode, FrameWorker thread could create tile worker
    // thread or loopfilter thread.
    worker_data->pbi->max_threads =
        (ctx->frame_parallel_decode == 0) ? ctx->cfg.threads : 0;

    worker_data->pbi->inv_tile_order = ctx->invert_tile_order;
    worker_data->pbi->frame_parallel_decode = ctx->frame_parallel_decode;
    worker->hook = (VP9WorkerHook)frame_worker_hook;
  }
Dmitry Kovalev's avatar
Dmitry Kovalev committed
308

309
310
311
312
313
314
315
  // If postprocessing was enabled by the application and a
  // configuration has not been provided, default it.
  if (!ctx->postproc_cfg_set &&
      (ctx->base.init_flags & VPX_CODEC_USE_POSTPROC))
    set_default_ppflags(&ctx->postproc_cfg);

  init_buffer_callbacks(ctx);
316
317

  return VPX_CODEC_OK;
318
319
}

Dmitry Kovalev's avatar
Dmitry Kovalev committed
320
321
322
static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx,
                                  const uint8_t **data, unsigned int data_sz,
                                  void *user_priv, int64_t deadline) {
323
324
  vp9_ppflags_t flags = {0};
  const VP9WorkerInterface *const winterface = vp9_get_worker_interface();
325
326
  (void)deadline;

327
328
329
  // Determine the stream parameters. Note that we rely on peek_si to
  // validate that we have a buffer that does not wrap around the top
  // of the heap.
330
331
  if (!ctx->si.h) {
    const vpx_codec_err_t res =
332
333
        decoder_peek_si_internal(*data, data_sz, &ctx->si, ctx->decrypt_cb,
                                 ctx->decrypt_state);
334
335
    if (res != VPX_CODEC_OK)
      return res;
336
337
338

    if (!ctx->si.is_kf)
      return VPX_CODEC_ERROR;
339
  }
340

341
342
343
344
345
  // Initialize the decoder workers on the first frame
  if (ctx->frame_workers == NULL) {
    const vpx_codec_err_t res = init_decoder(ctx);
    if (res != VPX_CODEC_OK)
      return res;
John Koleszar's avatar
John Koleszar committed
346
347
  }

348
349
350
351
352
353
  if (!ctx->frame_parallel_decode) {
    VP9Worker *const worker = ctx->frame_workers;
    FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
    worker_data->data = *data;
    worker_data->data_size = data_sz;
    worker_data->user_priv = user_priv;
354

355
356
357
358
    // Set these even if already initialized.  The caller may have changed the
    // decrypt config between frames.
    worker_data->pbi->decrypt_cb = ctx->decrypt_cb;
    worker_data->pbi->decrypt_state = ctx->decrypt_state;
John Koleszar's avatar
John Koleszar committed
359

360
361
    worker->had_error = 0;
    winterface->execute(worker);
John Koleszar's avatar
John Koleszar committed
362

363
364
    // Update data pointer after decode.
    *data = worker_data->data_end;
John Koleszar's avatar
John Koleszar committed
365

366
367
368
369
370
371
    if (worker->had_error)
      return update_error_state(ctx, &worker_data->pbi->common.error);
  } else {
    // TODO(hkuang): Implement frame parallel decode.
    return VPX_CODEC_INCAPABLE;
  }
372

373
374
  if (ctx->base.init_flags & VPX_CODEC_USE_POSTPROC)
    set_ppflags(ctx, &flags);
375

376
  return VPX_CODEC_OK;
John Koleszar's avatar
John Koleszar committed
377
378
}

379
380
381
382
383
384
385
386
387
388
389
static INLINE uint8_t read_marker(vpx_decrypt_cb decrypt_cb,
                                  void *decrypt_state,
                                  const uint8_t *data) {
  if (decrypt_cb) {
    uint8_t marker;
    decrypt_cb(decrypt_state, data, &marker, 1);
    return marker;
  }
  return *data;
}

390
391
392
393
394
395
396
397
398
399
400
static vpx_codec_err_t 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.

John Koleszar's avatar
John Koleszar committed
401
402
403
  uint8_t marker;

  assert(data_sz);
404
  marker = read_marker(decrypt_cb, decrypt_state, data + data_sz - 1);
John Koleszar's avatar
John Koleszar committed
405
406
  *count = 0;

407
  if ((marker & 0xe0) == 0xc0) {
Johann's avatar
Johann committed
408
409
410
    const uint32_t frames = (marker & 0x7) + 1;
    const uint32_t mag = ((marker >> 3) & 0x3) + 1;
    const size_t index_sz = 2 + mag * frames;
John Koleszar's avatar
John Koleszar committed
411

412
413
414
415
    // 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;
416

417
418
419
    {
      const uint8_t marker2 = read_marker(decrypt_cb, decrypt_state,
                                          data + data_sz - index_sz);
John Koleszar's avatar
John Koleszar committed
420

421
422
423
424
425
426
      // 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;
    }
John Koleszar's avatar
John Koleszar committed
427

428
429
430
431
432
433
434
435
436
437
438
    {
      // 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;
439
      }
440
441
442
443
444
445
446
447
448

      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;
John Koleszar's avatar
John Koleszar committed
449
450
    }
  }
451
  return VPX_CODEC_OK;
John Koleszar's avatar
John Koleszar committed
452
453
}

454
455
456
static vpx_codec_err_t decoder_decode(vpx_codec_alg_priv_t *ctx,
                                      const uint8_t *data, unsigned int data_sz,
                                      void *user_priv, long deadline) {
457
  const uint8_t *data_start = data;
458
459
  const uint8_t * const data_end = data + data_sz;
  vpx_codec_err_t res;
460
461
  uint32_t frame_sizes[8];
  int frame_count;
John Koleszar's avatar
John Koleszar committed
462

463
464
  if (data == NULL || data_sz == 0)
    return VPX_CODEC_INVALID_PARAM;
465

466
467
468
469
  res = parse_superframe_index(data, data_sz, frame_sizes, &frame_count,
                               ctx->decrypt_cb, ctx->decrypt_state);
  if (res != VPX_CODEC_OK)
    return res;
470

471
472
473
474
475
476
477
478
479
480
481
482
483
484
  if (ctx->frame_parallel_decode) {
    // Decode in frame parallel mode. When decoding in this mode, the frame
    // passed to the decoder must be either a normal frame or a superframe with
    // superframe index so the decoder could get each frame's start position
    // in the superframe.
    if (frame_count > 0) {
      int i;

      for (i = 0; i < frame_count; ++i) {
        const uint8_t *data_start_copy = data_start;
        const uint32_t frame_size = frame_sizes[i];
        vpx_codec_err_t res;
        if (data_start < data
            || frame_size > (uint32_t) (data_end - data_start)) {
485
          set_error_detail(ctx, "Invalid frame size in index");
486
487
488
489
490
491
492
          return VPX_CODEC_CORRUPT_FRAME;
        }

        res = decode_one(ctx, &data_start_copy, frame_size, user_priv,
                         deadline);
        if (res != VPX_CODEC_OK)
          return res;
John Koleszar's avatar
John Koleszar committed
493

494
495
496
497
        data_start += frame_size;
      }
    } else {
      res = decode_one(ctx, &data_start, data_sz, user_priv, deadline);
498
499
      if (res != VPX_CODEC_OK)
        return res;
500

501
502
      // Extra data detected after the frame.
      if (data_start < data_end - 1) {
503
        set_error_detail(ctx, "Fail to decode frame in parallel mode");
504
505
        return VPX_CODEC_INCAPABLE;
      }
John Koleszar's avatar
John Koleszar committed
506
    }
507
  } else {
508
509
510
511
512
513
514
515
516
517
    // Decode in serial mode.
    if (frame_count > 0) {
      int i;

      for (i = 0; i < frame_count; ++i) {
        const uint8_t *data_start_copy = data_start;
        const uint32_t frame_size = frame_sizes[i];
        vpx_codec_err_t res;
        if (data_start < data
            || frame_size > (uint32_t) (data_end - data_start)) {
518
          set_error_detail(ctx, "Invalid frame size in index");
519
520
521
522
523
524
525
          return VPX_CODEC_CORRUPT_FRAME;
        }

        res = decode_one(ctx, &data_start_copy, frame_size, user_priv,
                         deadline);
        if (res != VPX_CODEC_OK)
          return res;
526

527
528
529
        data_start += frame_size;
      }
    } else {
530
      while (data_start < data_end) {
531
532
533
534
535
536
537
538
539
540
541
542
543
544
        const uint32_t frame_size = (uint32_t) (data_end - data_start);
        const vpx_codec_err_t res = decode_one(ctx, &data_start, frame_size,
                                               user_priv, deadline);
        if (res != VPX_CODEC_OK)
          return res;

        // Account for suboptimal termination by the encoder.
        while (data_start < data_end) {
          const uint8_t marker = read_marker(ctx->decrypt_cb,
                                             ctx->decrypt_state, data_start);
          if (marker)
            break;
          ++data_start;
        }
545
      }
546
    }
547
  }
548

549
  return VPX_CODEC_OK;
550
551
}

552
553
static vpx_image_t *decoder_get_frame(vpx_codec_alg_priv_t *ctx,
                                      vpx_codec_iter_t *iter) {
John Koleszar's avatar
John Koleszar committed
554
555
  vpx_image_t *img = NULL;

556
557
558
559
560
561
562
563
564
565
566
567
  // iter acts as a flip flop, so an image is only returned on the first
  // call to get_frame.
  if (*iter == NULL && ctx->frame_workers != NULL) {
    YV12_BUFFER_CONFIG sd;
    vp9_ppflags_t flags = {0, 0, 0};

    VP9Worker *const worker = &ctx->frame_workers[ctx->next_output_thread_id];
    FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
    if (vp9_get_raw_frame(worker_data->pbi, &sd, &flags) == 0) {
      VP9_COMMON *const cm = &worker_data->pbi->common;
      yuvconfig2image(&ctx->img, &sd, worker_data->user_priv);
      ctx->img.fb_priv = cm->frame_bufs[cm->new_fb_idx].raw_frame_buffer.priv;
John Koleszar's avatar
John Koleszar committed
568
569
      img = &ctx->img;
      *iter = img;
570
571
572
573
574
575
576
577
578
      // Decrease reference count of last output frame in frame parallel mode.
      if (ctx->frame_parallel_decode && ctx->last_show_frame >= 0) {
        --cm->frame_bufs[ctx->last_show_frame].ref_count;
        if (cm->frame_bufs[ctx->last_show_frame].ref_count == 0) {
          cm->release_fb_cb(cm->cb_priv,
              &cm->frame_bufs[ctx->last_show_frame].raw_frame_buffer);
        }
      }
      ctx->last_show_frame = worker_data->pbi->common.new_fb_idx;
John Koleszar's avatar
John Koleszar committed
579
    }
John Koleszar's avatar
John Koleszar committed
580
  }
John Koleszar's avatar
John Koleszar committed
581

John Koleszar's avatar
John Koleszar committed
582
  return img;
John Koleszar's avatar
John Koleszar committed
583
584
}

585
static vpx_codec_err_t decoder_set_fb_fn(
586
587
588
589
590
    vpx_codec_alg_priv_t *ctx,
    vpx_get_frame_buffer_cb_fn_t cb_get,
    vpx_release_frame_buffer_cb_fn_t cb_release, void *cb_priv) {
  if (cb_get == NULL || cb_release == NULL) {
    return VPX_CODEC_INVALID_PARAM;
591
  } else if (ctx->frame_workers == NULL) {
592
593
594
595
596
597
598
599
600
601
602
    // If the decoder has already been initialized, do not accept changes to
    // the frame buffer functions.
    ctx->get_ext_fb_cb = cb_get;
    ctx->release_ext_fb_cb = cb_release;
    ctx->ext_priv = cb_priv;
    return VPX_CODEC_OK;
  }

  return VPX_CODEC_ERROR;
}

603
static vpx_codec_err_t ctrl_set_reference(vpx_codec_alg_priv_t *ctx,
604
                                          va_list args) {
605
  vpx_ref_frame_t *const data = va_arg(args, vpx_ref_frame_t *);
John Koleszar's avatar
John Koleszar committed
606

607
608
609
610
611
612
  // Only support this function in serial decode.
  if (ctx->frame_parallel_decode) {
    set_error_detail(ctx, "Not supported in frame parallel decode");
    return VPX_CODEC_INCAPABLE;
  }

John Koleszar's avatar
John Koleszar committed
613
  if (data) {
614
    vpx_ref_frame_t *const frame = (vpx_ref_frame_t *)data;
John Koleszar's avatar
John Koleszar committed
615
    YV12_BUFFER_CONFIG sd;
616
617
    VP9Worker *const worker = ctx->frame_workers;
    FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
John Koleszar's avatar
John Koleszar committed
618
    image2yuvconfig(&frame->img, &sd);
619
    return vp9_set_reference_dec(&worker_data->pbi->common,
620
                                 (VP9_REFFRAME)frame->frame_type, &sd);
621
  } else {
John Koleszar's avatar
John Koleszar committed
622
    return VPX_CODEC_INVALID_PARAM;
623
  }
John Koleszar's avatar
John Koleszar committed
624
625
}

626
static vpx_codec_err_t ctrl_copy_reference(vpx_codec_alg_priv_t *ctx,
627
                                           va_list args) {
John Koleszar's avatar
John Koleszar committed
628
  vpx_ref_frame_t *data = va_arg(args, vpx_ref_frame_t *);
John Koleszar's avatar
John Koleszar committed
629

630
631
632
633
634
635
  // Only support this function in serial decode.
  if (ctx->frame_parallel_decode) {
    set_error_detail(ctx, "Not supported in frame parallel decode");
    return VPX_CODEC_INCAPABLE;
  }

John Koleszar's avatar
John Koleszar committed
636
  if (data) {
637
    vpx_ref_frame_t *frame = (vpx_ref_frame_t *) data;
John Koleszar's avatar
John Koleszar committed
638
    YV12_BUFFER_CONFIG sd;
639
640
    VP9Worker *const worker = ctx->frame_workers;
    FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
John Koleszar's avatar
John Koleszar committed
641
    image2yuvconfig(&frame->img, &sd);
642
    return vp9_copy_reference_dec(worker_data->pbi,
John Koleszar's avatar
John Koleszar committed
643
                                  (VP9_REFFRAME)frame->frame_type, &sd);
644
  } else {
John Koleszar's avatar
John Koleszar committed
645
    return VPX_CODEC_INVALID_PARAM;
646
  }
John Koleszar's avatar
John Koleszar committed
647
648
}

649
static vpx_codec_err_t ctrl_get_reference(vpx_codec_alg_priv_t *ctx,
650
                                          va_list args) {
John Koleszar's avatar
John Koleszar committed
651
652
  vp9_ref_frame_t *data = va_arg(args, vp9_ref_frame_t *);

653
654
655
656
657
658
  // Only support this function in serial decode.
  if (ctx->frame_parallel_decode) {
    set_error_detail(ctx, "Not supported in frame parallel decode");
    return VPX_CODEC_INCAPABLE;
  }

John Koleszar's avatar
John Koleszar committed
659
660
  if (data) {
    YV12_BUFFER_CONFIG* fb;
661
662
663
664
    VP9Worker *const worker = ctx->frame_workers;
    FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
    vp9_get_reference_dec(worker_data->pbi, data->idx, &fb);
    yuvconfig2image(&data->img, fb, worker_data->user_priv);
John Koleszar's avatar
John Koleszar committed
665
666
667
668
669
670
    return VPX_CODEC_OK;
  } else {
    return VPX_CODEC_INVALID_PARAM;
  }
}

671
static vpx_codec_err_t ctrl_set_postproc(vpx_codec_alg_priv_t *ctx,
672
                                         va_list args) {
673
#if CONFIG_VP9_POSTPROC
John Koleszar's avatar
John Koleszar committed
674
  vp8_postproc_cfg_t *data = va_arg(args, vp8_postproc_cfg_t *);
John Koleszar's avatar
John Koleszar committed
675

John Koleszar's avatar
John Koleszar committed
676
677
678
679
  if (data) {
    ctx->postproc_cfg_set = 1;
    ctx->postproc_cfg = *((vp8_postproc_cfg_t *)data);
    return VPX_CODEC_OK;
680
  } else {
John Koleszar's avatar
John Koleszar committed
681
    return VPX_CODEC_INVALID_PARAM;
682
  }
John Koleszar's avatar
John Koleszar committed
683
#else
684
685
  (void)ctx;
  (void)args;
John Koleszar's avatar
John Koleszar committed
686
  return VPX_CODEC_INCAPABLE;
John Koleszar's avatar
John Koleszar committed
687
688
689
#endif
}

690
static vpx_codec_err_t ctrl_set_dbg_options(vpx_codec_alg_priv_t *ctx,
691
692
693
                                            va_list args) {
  (void)ctx;
  (void)args;
John Koleszar's avatar
John Koleszar committed
694
  return VPX_CODEC_INCAPABLE;
695
}
John Koleszar's avatar
John Koleszar committed
696

697
static vpx_codec_err_t ctrl_get_last_ref_updates(vpx_codec_alg_priv_t *ctx,
698
                                                 va_list args) {
699
  int *const update_info = va_arg(args, int *);
700

701
702
703
704
705
706
  // Only support this function in serial decode.
  if (ctx->frame_parallel_decode) {
    set_error_detail(ctx, "Not supported in frame parallel decode");
    return VPX_CODEC_INCAPABLE;
  }

John Koleszar's avatar
John Koleszar committed
707
  if (update_info) {
708
709
710
711
712
    if (ctx->frame_workers) {
      VP9Worker *const worker = ctx->frame_workers;
      FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
      *update_info = worker_data->pbi->refresh_frame_flags;
    } else {
713
      return VPX_CODEC_ERROR;
714
    }
John Koleszar's avatar
John Koleszar committed
715
    return VPX_CODEC_OK;
716
  } else {
John Koleszar's avatar
John Koleszar committed
717
    return VPX_CODEC_INVALID_PARAM;
718
  }
719
720
721
}


722
static vpx_codec_err_t ctrl_get_frame_corrupted(vpx_codec_alg_priv_t *ctx,
723
                                                va_list args) {
John Koleszar's avatar
John Koleszar committed
724
  int *corrupted = va_arg(args, int *);
725

726
727
728
729
730
731
  // Only support this function in serial decode.
  if (ctx->frame_parallel_decode) {
    set_error_detail(ctx, "Not supported in frame parallel decode");
    return VPX_CODEC_INCAPABLE;
  }

John Koleszar's avatar
John Koleszar committed
732
  if (corrupted) {
733
734
735
736
737
    if (ctx->frame_workers) {
      VP9Worker *const worker = ctx->frame_workers;
      FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
      *corrupted = worker_data->pbi->common.frame_to_show->corrupted;
    } else {
Yaowu Xu's avatar
Yaowu Xu committed
738
      return VPX_CODEC_ERROR;
739
    }
John Koleszar's avatar
John Koleszar committed
740
    return VPX_CODEC_OK;
741
  } else {
John Koleszar's avatar
John Koleszar committed
742
    return VPX_CODEC_INVALID_PARAM;
743
  }
744
745
}

746
static vpx_codec_err_t ctrl_get_display_size(vpx_codec_alg_priv_t *ctx,
747
                                             va_list args) {
748
749
  int *const display_size = va_arg(args, int *);

750
751
752
753
754
755
  // Only support this function in serial decode.
  if (ctx->frame_parallel_decode) {
    set_error_detail(ctx, "Not supported in frame parallel decode");
    return VPX_CODEC_INCAPABLE;
  }

756
  if (display_size) {
757
758
759
760
    if (ctx->frame_workers) {
      VP9Worker *const worker = ctx->frame_workers;
      FrameWorkerData *const worker_data = (FrameWorkerData *)worker->data1;
      const VP9_COMMON *const cm = &worker_data->pbi->common;
761
762
      display_size[0] = cm->display_width;
      display_size[1] = cm->display_height;
763
764
765
766
767
768
769
770
771
    } else {
      return VPX_CODEC_ERROR;
    }
    return VPX_CODEC_OK;
  } else {
    return VPX_CODEC_INVALID_PARAM;
  }
}

772
static vpx_codec_err_t ctrl_set_invert_tile_order(vpx_codec_alg_priv_t *ctx,
773
                                                  va_list args) {
774
775
776
777
  ctx->invert_tile_order = va_arg(args, int);
  return VPX_CODEC_OK;
}

778
779
780
781
782
783
784
785
static vpx_codec_err_t ctrl_set_decryptor(vpx_codec_alg_priv_t *ctx,
                                          va_list args) {
  vpx_decrypt_init *init = va_arg(args, vpx_decrypt_init *);
  ctx->decrypt_cb = init ? init->decrypt_cb : NULL;
  ctx->decrypt_state = init ? init->decrypt_state : NULL;
  return VPX_CODEC_OK;
}

786
787
788
789
790
791
792
793
794
795
796
static vpx_codec_ctrl_fn_map_t decoder_ctrl_maps[] = {
  {VP8_COPY_REFERENCE,            ctrl_copy_reference},

  // Setters
  {VP8_SET_REFERENCE,             ctrl_set_reference},
  {VP8_SET_POSTPROC,              ctrl_set_postproc},
  {VP8_SET_DBG_COLOR_REF_FRAME,   ctrl_set_dbg_options},
  {VP8_SET_DBG_COLOR_MB_MODES,    ctrl_set_dbg_options},
  {VP8_SET_DBG_COLOR_B_MODES,     ctrl_set_dbg_options},
  {VP8_SET_DBG_DISPLAY_MV,        ctrl_set_dbg_options},
  {VP9_INVERT_TILE_DECODE_ORDER,  ctrl_set_invert_tile_order},
797
  {VPXD_SET_DECRYPTOR,            ctrl_set_decryptor},
798
799
800
801
802
803
804

  // Getters
  {VP8D_GET_LAST_REF_UPDATES,     ctrl_get_last_ref_updates},
  {VP8D_GET_FRAME_CORRUPTED,      ctrl_get_frame_corrupted},
  {VP9_GET_REFERENCE,             ctrl_get_reference},
  {VP9D_GET_DISPLAY_SIZE,         ctrl_get_display_size},

John Koleszar's avatar
John Koleszar committed
805
  { -1, NULL},
John Koleszar's avatar
John Koleszar committed
806
807
808
809
810
};

#ifndef VERSION_STRING
#define VERSION_STRING
#endif
811
812
CODEC_INTERFACE(vpx_codec_vp9_dx) = {
  "WebM Project VP9 Decoder" VERSION_STRING,
John Koleszar's avatar
John Koleszar committed
813
  VPX_CODEC_INTERNAL_ABI_VERSION,
814
  VPX_CODEC_CAP_DECODER | VP9_CAP_POSTPROC |
815
816
817
818
819
820
      VPX_CODEC_CAP_EXTERNAL_FRAME_BUFFER,  // vpx_codec_caps_t
  decoder_init,       // vpx_codec_init_fn_t
  decoder_destroy,    // vpx_codec_destroy_fn_t
  decoder_ctrl_maps,  // vpx_codec_ctrl_fn_map_t
  NOT_IMPLEMENTED,    // vpx_codec_get_mmap_fn_t
  NOT_IMPLEMENTED,    // vpx_codec_set_mmap_fn_t
821
  { // NOLINT
822
823
824
825
826
    decoder_peek_si,    // vpx_codec_peek_si_fn_t
    decoder_get_si,     // vpx_codec_get_si_fn_t
    decoder_decode,     // vpx_codec_decode_fn_t
    decoder_get_frame,  // vpx_codec_frame_get_fn_t
    decoder_set_fb_fn,  // vpx_codec_set_fb_fn_t
John Koleszar's avatar
John Koleszar committed
827
  },
828
  { // NOLINT
829
830
831
832
833
834
835
    NOT_IMPLEMENTED,  // vpx_codec_enc_cfg_map_t
    NOT_IMPLEMENTED,  // vpx_codec_encode_fn_t
    NOT_IMPLEMENTED,  // vpx_codec_get_cx_data_fn_t
    NOT_IMPLEMENTED,  // vpx_codec_enc_config_set_fn_t
    NOT_IMPLEMENTED,  // vpx_codec_get_global_headers_fn_t
    NOT_IMPLEMENTED,  // vpx_codec_get_preview_frame_fn_t
    NOT_IMPLEMENTED   // vpx_codec_enc_mr_get_mem_loc_fn_t
John Koleszar's avatar
John Koleszar committed
836
  }
John Koleszar's avatar
John Koleszar committed
837
};