Add a V4L2 Request API hwaccel for VP9. Support for VP9 is enabled when Linux kernel headers declare the control id V4L2_CID_STATELESS_VP9_FRAME, added in v5.17. Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com> Co-developed-by: Jernej Skrabec <jernej.skrabec@gmail.com> Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com> Co-developed-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
530 lines
19 KiB
C
530 lines
19 KiB
C
/*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "hwaccel_internal.h"
|
|
#include "hwconfig.h"
|
|
#include "internal.h"
|
|
#include "v4l2_request.h"
|
|
#include "vp89_rac.h"
|
|
#include "vp9dec.h"
|
|
|
|
#define V4L2_VP9_CONTROLS_MAX 2
|
|
|
|
typedef struct V4L2RequestContextVP9 {
|
|
V4L2RequestContext base;
|
|
bool has_compressed_hdr;
|
|
} V4L2RequestContextVP9;
|
|
|
|
typedef struct V4L2RequestControlsVP9 {
|
|
V4L2RequestPictureContext pic;
|
|
struct v4l2_ctrl_vp9_frame frame;
|
|
struct v4l2_ctrl_vp9_compressed_hdr compressed_hdr;
|
|
} V4L2RequestControlsVP9;
|
|
|
|
// differential forward probability updates
|
|
static int read_prob_delta(VPXRangeCoder *c)
|
|
{
|
|
static const uint8_t inv_map_table[255] = {
|
|
7, 20, 33, 46, 59, 72, 85, 98, 111, 124, 137, 150, 163, 176,
|
|
189, 202, 215, 228, 241, 254, 1, 2, 3, 4, 5, 6, 8, 9,
|
|
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24,
|
|
25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38, 39,
|
|
40, 41, 42, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54,
|
|
55, 56, 57, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
|
|
70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
|
|
86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 99, 100,
|
|
101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115,
|
|
116, 117, 118, 119, 120, 121, 122, 123, 125, 126, 127, 128, 129, 130,
|
|
131, 132, 133, 134, 135, 136, 138, 139, 140, 141, 142, 143, 144, 145,
|
|
146, 147, 148, 149, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
|
|
161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
|
|
177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 190, 191,
|
|
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 203, 204, 205, 206,
|
|
207, 208, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, 220, 221,
|
|
222, 223, 224, 225, 226, 227, 229, 230, 231, 232, 233, 234, 235, 236,
|
|
237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
|
|
252, 253, 253,
|
|
};
|
|
int d;
|
|
|
|
/* This code is trying to do a differential probability update. For a
|
|
* current probability A in the range [1, 255], the difference to a new
|
|
* probability of any value can be expressed differentially as 1-A, 255-A
|
|
* where some part of this (absolute range) exists both in positive as
|
|
* well as the negative part, whereas another part only exists in one
|
|
* half. We're trying to code this shared part differentially, i.e.
|
|
* times two where the value of the lowest bit specifies the sign, and
|
|
* the single part is then coded on top of this. This absolute difference
|
|
* then again has a value of [0, 254], but a bigger value in this range
|
|
* indicates that we're further away from the original value A, so we
|
|
* can code this as a VLC code, since higher values are increasingly
|
|
* unlikely. The first 20 values in inv_map_table[] allow 'cheap, rough'
|
|
* updates vs. the 'fine, exact' updates further down the range, which
|
|
* adds one extra dimension to this differential update model. */
|
|
|
|
if (!vp89_rac_get(c)) {
|
|
d = vp89_rac_get_uint(c, 4) + 0;
|
|
} else if (!vp89_rac_get(c)) {
|
|
d = vp89_rac_get_uint(c, 4) + 16;
|
|
} else if (!vp89_rac_get(c)) {
|
|
d = vp89_rac_get_uint(c, 5) + 32;
|
|
} else {
|
|
d = vp89_rac_get_uint(c, 7);
|
|
if (d >= 65)
|
|
d = (d << 1) - 65 + vp89_rac_get(c);
|
|
d += 64;
|
|
av_assert2(d < FF_ARRAY_ELEMS(inv_map_table));
|
|
}
|
|
|
|
return inv_map_table[d];
|
|
}
|
|
|
|
static void fill_compressed_hdr(struct v4l2_ctrl_vp9_compressed_hdr *ctrl,
|
|
const uint8_t *buffer, uint32_t size,
|
|
AVCodecContext *avctx)
|
|
{
|
|
const VP9Context *s = avctx->priv_data;
|
|
enum CompPredMode comppredmode;
|
|
int ret, i, j, k, l, m, n;
|
|
VPXRangeCoder c;
|
|
|
|
ret = ff_vpx_init_range_decoder(&c, buffer + s->s.h.uncompressed_header_size,
|
|
s->s.h.compressed_header_size);
|
|
if (ret < 0)
|
|
return;
|
|
|
|
if (vpx_rac_get_prob_branchy(&c, 128)) // marker bit
|
|
return;
|
|
|
|
// txfm updates
|
|
if (s->s.h.lossless) {
|
|
ctrl->tx_mode = V4L2_VP9_TX_MODE_ONLY_4X4;
|
|
} else {
|
|
ctrl->tx_mode = vp89_rac_get_uint(&c, 2);
|
|
if (ctrl->tx_mode == V4L2_VP9_TX_MODE_ALLOW_32X32)
|
|
ctrl->tx_mode += vp89_rac_get(&c);
|
|
|
|
if (ctrl->tx_mode == V4L2_VP9_TX_MODE_SELECT) {
|
|
for (i = 0; i < 2; i++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->tx8[i][0] = read_prob_delta(&c);
|
|
for (i = 0; i < 2; i++)
|
|
for (j = 0; j < 2; j++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->tx16[i][j] = read_prob_delta(&c);
|
|
for (i = 0; i < 2; i++)
|
|
for (j = 0; j < 3; j++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->tx32[i][j] = read_prob_delta(&c);
|
|
}
|
|
}
|
|
|
|
// coef updates
|
|
for (i = 0; i < 4; i++) {
|
|
if (vp89_rac_get(&c)) {
|
|
for (j = 0; j < 2; j++)
|
|
for (k = 0; k < 2; k++)
|
|
for (l = 0; l < 6; l++)
|
|
for (m = 0; m < 6; m++) {
|
|
if (m >= 3 && l == 0) // dc only has 3 pt
|
|
break;
|
|
for (n = 0; n < 3; n++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->coef[i][j][k][l][m][n] =
|
|
read_prob_delta(&c);
|
|
}
|
|
}
|
|
if (ctrl->tx_mode == i)
|
|
break;
|
|
}
|
|
|
|
// mode updates
|
|
for (i = 0; i < 3; i++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->skip[i] = read_prob_delta(&c);
|
|
if (!s->s.h.keyframe && !s->s.h.intraonly) {
|
|
for (i = 0; i < 7; i++)
|
|
for (j = 0; j < 3; j++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->inter_mode[i][j] = read_prob_delta(&c);
|
|
|
|
if (s->s.h.filtermode == FILTER_SWITCHABLE)
|
|
for (i = 0; i < 4; i++)
|
|
for (j = 0; j < 2; j++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->interp_filter[i][j] = read_prob_delta(&c);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->is_inter[i] = read_prob_delta(&c);
|
|
|
|
if (s->s.h.allowcompinter) {
|
|
comppredmode = vp89_rac_get(&c);
|
|
if (comppredmode)
|
|
comppredmode += vp89_rac_get(&c);
|
|
if (comppredmode == PRED_SWITCHABLE)
|
|
for (i = 0; i < 5; i++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->comp_mode[i] = read_prob_delta(&c);
|
|
} else {
|
|
comppredmode = PRED_SINGLEREF;
|
|
}
|
|
|
|
if (comppredmode != PRED_COMPREF) {
|
|
for (i = 0; i < 5; i++) {
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->single_ref[i][0] = read_prob_delta(&c);
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->single_ref[i][1] = read_prob_delta(&c);
|
|
}
|
|
}
|
|
|
|
if (comppredmode != PRED_SINGLEREF) {
|
|
for (i = 0; i < 5; i++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->comp_ref[i] = read_prob_delta(&c);
|
|
}
|
|
|
|
for (i = 0; i < 4; i++)
|
|
for (j = 0; j < 9; j++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->y_mode[i][j] = read_prob_delta(&c);
|
|
|
|
for (i = 0; i < 4; i++)
|
|
for (j = 0; j < 4; j++)
|
|
for (k = 0; k < 3; k++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->partition[(i * 4) + j][k] = read_prob_delta(&c);
|
|
|
|
// mv fields
|
|
for (i = 0; i < 3; i++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.joint[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.sign[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
|
|
for (j = 0; j < 10; j++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.classes[i][j] = (vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.class0_bit[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
|
|
for (j = 0; j < 10; j++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.bits[i][j] = (vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
}
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
for (j = 0; j < 2; j++)
|
|
for (k = 0; k < 3; k++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.class0_fr[i][j][k] =
|
|
(vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
|
|
for (j = 0; j < 3; j++)
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.fr[i][j] = (vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
}
|
|
|
|
if (s->s.h.highprecisionmvs) {
|
|
for (i = 0; i < 2; i++) {
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.class0_hp[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
|
|
if (vpx_rac_get_prob_branchy(&c, 252))
|
|
ctrl->mv.hp[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy(ctrl->uv_mode, s->prob.p.uv_mode, sizeof(ctrl->uv_mode));
|
|
}
|
|
|
|
static void fill_frame(struct v4l2_ctrl_vp9_frame *ctrl, AVCodecContext *avctx)
|
|
{
|
|
const VP9Context *s = avctx->priv_data;
|
|
AVFrame *ref;
|
|
int i;
|
|
|
|
*ctrl = (struct v4l2_ctrl_vp9_frame) {
|
|
.lf = {
|
|
.level = s->s.h.filter.level,
|
|
.sharpness = s->s.h.filter.sharpness,
|
|
},
|
|
|
|
.quant = {
|
|
.base_q_idx = s->s.h.yac_qi,
|
|
.delta_q_y_dc = s->s.h.ydc_qdelta,
|
|
.delta_q_uv_dc = s->s.h.uvdc_qdelta,
|
|
.delta_q_uv_ac = s->s.h.uvac_qdelta,
|
|
},
|
|
|
|
.compressed_header_size = s->s.h.compressed_header_size,
|
|
.uncompressed_header_size = s->s.h.uncompressed_header_size,
|
|
.frame_width_minus_1 = avctx->width - 1,
|
|
.frame_height_minus_1 = avctx->height - 1,
|
|
.render_width_minus_1 = s->w - 1,
|
|
.render_height_minus_1 = s->h - 1,
|
|
.reset_frame_context = s->s.h.resetctx > 0 ? s->s.h.resetctx - 1 : 0,
|
|
.frame_context_idx = s->s.h.framectxid,
|
|
.profile = s->s.h.profile,
|
|
.bit_depth = s->s.h.bpp,
|
|
.interpolation_filter = s->s.h.filtermode ^ (s->s.h.filtermode <= 1),
|
|
.tile_cols_log2 = s->s.h.tiling.log2_tile_cols,
|
|
.tile_rows_log2 = s->s.h.tiling.log2_tile_rows,
|
|
.reference_mode = s->s.h.comppredmode,
|
|
};
|
|
|
|
for (i = 0; i < 4; i++)
|
|
ctrl->lf.ref_deltas[i] = s->s.h.lf_delta.ref[i];
|
|
|
|
for (i = 0; i < 2; i++)
|
|
ctrl->lf.mode_deltas[i] = s->s.h.lf_delta.mode[i];
|
|
|
|
if (s->s.h.lf_delta.enabled)
|
|
ctrl->lf.flags |= V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED;
|
|
|
|
if (s->s.h.lf_delta.updated)
|
|
ctrl->lf.flags |= V4L2_VP9_LOOP_FILTER_FLAG_DELTA_UPDATE;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
if (s->s.h.segmentation.feat[i].q_enabled) {
|
|
ctrl->seg.feature_data[i][V4L2_VP9_SEG_LVL_ALT_Q] =
|
|
s->s.h.segmentation.feat[i].q_val;
|
|
ctrl->seg.feature_enabled[i] |= 1 << V4L2_VP9_SEG_LVL_ALT_Q;
|
|
}
|
|
|
|
if (s->s.h.segmentation.feat[i].lf_enabled) {
|
|
ctrl->seg.feature_data[i][V4L2_VP9_SEG_LVL_ALT_L] =
|
|
s->s.h.segmentation.feat[i].lf_val;
|
|
ctrl->seg.feature_enabled[i] |= 1 << V4L2_VP9_SEG_LVL_ALT_L;
|
|
}
|
|
|
|
if (s->s.h.segmentation.feat[i].ref_enabled) {
|
|
ctrl->seg.feature_data[i][V4L2_VP9_SEG_LVL_REF_FRAME] =
|
|
s->s.h.segmentation.feat[i].ref_val;
|
|
ctrl->seg.feature_enabled[i] |= 1 << V4L2_VP9_SEG_LVL_REF_FRAME;
|
|
}
|
|
|
|
if (s->s.h.segmentation.feat[i].skip_enabled)
|
|
ctrl->seg.feature_enabled[i] |= 1 << V4L2_VP9_SEG_LVL_SKIP;
|
|
}
|
|
|
|
for (i = 0; i < 7; i++)
|
|
ctrl->seg.tree_probs[i] = s->s.h.segmentation.prob[i];
|
|
|
|
if (s->s.h.segmentation.temporal) {
|
|
for (i = 0; i < 3; i++)
|
|
ctrl->seg.pred_probs[i] = s->s.h.segmentation.pred_prob[i];
|
|
} else {
|
|
memset(ctrl->seg.pred_probs, 255, sizeof(ctrl->seg.pred_probs));
|
|
}
|
|
|
|
if (s->s.h.segmentation.enabled)
|
|
ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_ENABLED;
|
|
|
|
if (s->s.h.segmentation.update_map)
|
|
ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP;
|
|
|
|
if (s->s.h.segmentation.temporal)
|
|
ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE;
|
|
|
|
if (s->frame_header->segmentation_update_data)
|
|
ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA;
|
|
|
|
if (s->s.h.segmentation.absolute_vals)
|
|
ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE;
|
|
|
|
if (s->s.h.keyframe)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_KEY_FRAME;
|
|
|
|
if (!s->s.h.invisible)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_SHOW_FRAME;
|
|
|
|
if (s->s.h.errorres)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT;
|
|
|
|
if (s->s.h.intraonly)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_INTRA_ONLY;
|
|
|
|
if (!s->s.h.keyframe && s->s.h.highprecisionmvs)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV;
|
|
|
|
if (s->s.h.refreshctx)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX;
|
|
|
|
if (s->s.h.parallelmode)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE;
|
|
|
|
if (s->ss_h)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_X_SUBSAMPLING;
|
|
|
|
if (s->ss_v)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_Y_SUBSAMPLING;
|
|
|
|
if (avctx->color_range == AVCOL_RANGE_JPEG)
|
|
ctrl->flags |= V4L2_VP9_FRAME_FLAG_COLOR_RANGE_FULL_SWING;
|
|
|
|
ref = s->s.refs[s->s.h.refidx[0]].f;
|
|
if (ref && ref->private_ref)
|
|
ctrl->last_frame_ts = ff_v4l2_request_get_capture_timestamp(ref);
|
|
|
|
ref = s->s.refs[s->s.h.refidx[1]].f;
|
|
if (ref && ref->private_ref)
|
|
ctrl->golden_frame_ts = ff_v4l2_request_get_capture_timestamp(ref);
|
|
|
|
ref = s->s.refs[s->s.h.refidx[2]].f;
|
|
if (ref && ref->private_ref)
|
|
ctrl->alt_frame_ts = ff_v4l2_request_get_capture_timestamp(ref);
|
|
|
|
if (s->s.h.signbias[0])
|
|
ctrl->ref_frame_sign_bias |= V4L2_VP9_SIGN_BIAS_LAST;
|
|
|
|
if (s->s.h.signbias[1])
|
|
ctrl->ref_frame_sign_bias |= V4L2_VP9_SIGN_BIAS_GOLDEN;
|
|
|
|
if (s->s.h.signbias[2])
|
|
ctrl->ref_frame_sign_bias |= V4L2_VP9_SIGN_BIAS_ALT;
|
|
}
|
|
|
|
static int v4l2_request_vp9_start_frame(AVCodecContext *avctx,
|
|
av_unused const AVBufferRef *buf_ref,
|
|
const uint8_t *buffer,
|
|
uint32_t size)
|
|
{
|
|
const VP9SharedContext *h = avctx->priv_data;
|
|
const VP9Frame *f = &h->frames[CUR_FRAME];
|
|
V4L2RequestContextVP9 *ctx = avctx->internal->hwaccel_priv_data;
|
|
V4L2RequestControlsVP9 *controls = f->hwaccel_picture_private;
|
|
int ret;
|
|
|
|
ret = ff_v4l2_request_start_frame(avctx, &controls->pic, f->tf.f);
|
|
if (ret)
|
|
return ret;
|
|
|
|
fill_frame(&controls->frame, avctx);
|
|
|
|
if (ctx->has_compressed_hdr)
|
|
fill_compressed_hdr(&controls->compressed_hdr, buffer, size, avctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int v4l2_request_vp9_decode_slice(AVCodecContext *avctx,
|
|
const uint8_t *buffer, uint32_t size)
|
|
{
|
|
const VP9SharedContext *h = avctx->priv_data;
|
|
V4L2RequestControlsVP9 *controls = h->frames[CUR_FRAME].hwaccel_picture_private;
|
|
|
|
return ff_v4l2_request_append_output(avctx, &controls->pic, buffer, size);
|
|
}
|
|
|
|
static int v4l2_request_vp9_end_frame(AVCodecContext *avctx)
|
|
{
|
|
const VP9SharedContext *h = avctx->priv_data;
|
|
V4L2RequestContextVP9 *ctx = avctx->internal->hwaccel_priv_data;
|
|
V4L2RequestControlsVP9 *controls = h->frames[CUR_FRAME].hwaccel_picture_private;
|
|
int count = 0;
|
|
|
|
struct v4l2_ext_control control[V4L2_VP9_CONTROLS_MAX] = {};
|
|
|
|
control[count++] = (struct v4l2_ext_control) {
|
|
.id = V4L2_CID_STATELESS_VP9_FRAME,
|
|
.ptr = &controls->frame,
|
|
.size = sizeof(controls->frame),
|
|
};
|
|
|
|
if (ctx->has_compressed_hdr) {
|
|
control[count++] = (struct v4l2_ext_control) {
|
|
.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR,
|
|
.ptr = &controls->compressed_hdr,
|
|
.size = sizeof(controls->compressed_hdr),
|
|
};
|
|
}
|
|
|
|
return ff_v4l2_request_decode_frame(avctx, &controls->pic, control, count);
|
|
}
|
|
|
|
static int v4l2_request_vp9_post_frames_ctx(AVCodecContext *avctx)
|
|
{
|
|
V4L2RequestContextVP9 *ctx = avctx->internal->hwaccel_priv_data;
|
|
|
|
struct v4l2_query_ext_ctrl compressed_hdr = {
|
|
.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR,
|
|
};
|
|
|
|
// TODO: check V4L2_CID_MPEG_VIDEO_VP9_PROFILE
|
|
// TODO: check V4L2_CID_MPEG_VIDEO_VP9_LEVEL
|
|
|
|
if (!ff_v4l2_request_query_control(avctx, &compressed_hdr))
|
|
ctx->has_compressed_hdr = true;
|
|
else
|
|
ctx->has_compressed_hdr = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int v4l2_request_vp9_init(AVCodecContext *avctx)
|
|
{
|
|
struct v4l2_ctrl_vp9_frame frame;
|
|
|
|
struct v4l2_ext_control control[] = {
|
|
{
|
|
.id = V4L2_CID_STATELESS_VP9_FRAME,
|
|
.ptr = &frame,
|
|
.size = sizeof(frame),
|
|
},
|
|
};
|
|
|
|
fill_frame(&frame, avctx);
|
|
|
|
return ff_v4l2_request_init(avctx, control, FF_ARRAY_ELEMS(control),
|
|
v4l2_request_vp9_post_frames_ctx);
|
|
}
|
|
|
|
static int v4l2_request_vp9_frame_params(AVCodecContext *avctx,
|
|
AVBufferRef *hw_frames_ctx)
|
|
{
|
|
const VP9Context *s = avctx->priv_data;
|
|
uint8_t bit_depth = s ? s->s.h.bpp : 0;
|
|
|
|
return ff_v4l2_request_frame_params(avctx, hw_frames_ctx,
|
|
V4L2_PIX_FMT_VP9_FRAME, bit_depth);
|
|
}
|
|
|
|
const FFHWAccel ff_vp9_v4l2request_hwaccel = {
|
|
.p.name = "vp9_v4l2request",
|
|
.p.type = AVMEDIA_TYPE_VIDEO,
|
|
.p.id = AV_CODEC_ID_VP9,
|
|
.p.pix_fmt = AV_PIX_FMT_DRM_PRIME,
|
|
.start_frame = v4l2_request_vp9_start_frame,
|
|
.decode_slice = v4l2_request_vp9_decode_slice,
|
|
.end_frame = v4l2_request_vp9_end_frame,
|
|
.flush = ff_v4l2_request_flush,
|
|
.frame_priv_data_size = sizeof(V4L2RequestControlsVP9),
|
|
.init = v4l2_request_vp9_init,
|
|
.uninit = ff_v4l2_request_uninit,
|
|
.priv_data_size = sizeof(V4L2RequestContextVP9),
|
|
.frame_params = v4l2_request_vp9_frame_params,
|
|
.caps_internal = HWACCEL_CAP_ASYNC_SAFE,
|
|
};
|