1442 lines
49 KiB
C
1442 lines
49 KiB
C
/*
|
|
Copyright (C) 2024 John Cox john.cox@raspberrypi.com
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
obtaining a copy of this software and associated documentation
|
|
files (the "Software"), to deal in the Software without
|
|
restriction, including without limitation the rights to use, copy,
|
|
modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
// File included by v4l2_req_hevc_v* - not compiled on its own
|
|
|
|
#include "decode.h"
|
|
#include "hevc/hevcdec.h"
|
|
#include "hwconfig.h"
|
|
#include "internal.h"
|
|
#include "thread.h"
|
|
#include "v4l2_fmt.h"
|
|
|
|
#include "libavutil/mem.h"
|
|
|
|
#if HEVC_CTRLS_VERSION == 1
|
|
#include "hevc-ctrls-v1.h"
|
|
|
|
// Fixup renamed entries
|
|
#define V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT
|
|
|
|
#elif HEVC_CTRLS_VERSION == 2
|
|
#include "hevc-ctrls-v2.h"
|
|
#elif HEVC_CTRLS_VERSION == 3
|
|
#include "hevc-ctrls-v3.h"
|
|
#elif HEVC_CTRLS_VERSION == 4
|
|
#include <linux/v4l2-controls.h>
|
|
#if !defined(V4L2_CID_STATELESS_HEVC_SPS)
|
|
#include "hevc-ctrls-v4.h"
|
|
#endif
|
|
#else
|
|
#error Unknown HEVC_CTRLS_VERSION
|
|
#endif
|
|
|
|
#ifndef V4L2_CID_STATELESS_HEVC_SPS
|
|
#define V4L2_CID_STATELESS_HEVC_SPS V4L2_CID_MPEG_VIDEO_HEVC_SPS
|
|
#define V4L2_CID_STATELESS_HEVC_PPS V4L2_CID_MPEG_VIDEO_HEVC_PPS
|
|
#define V4L2_CID_STATELESS_HEVC_SLICE_PARAMS V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS
|
|
#define V4L2_CID_STATELESS_HEVC_SCALING_MATRIX V4L2_CID_MPEG_VIDEO_HEVC_SCALING_MATRIX
|
|
#define V4L2_CID_STATELESS_HEVC_DECODE_PARAMS V4L2_CID_MPEG_VIDEO_HEVC_DECODE_PARAMS
|
|
#define V4L2_CID_STATELESS_HEVC_DECODE_MODE V4L2_CID_MPEG_VIDEO_HEVC_DECODE_MODE
|
|
#define V4L2_CID_STATELESS_HEVC_START_CODE V4L2_CID_MPEG_VIDEO_HEVC_START_CODE
|
|
|
|
#define V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_SLICE_BASED
|
|
#define V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED V4L2_MPEG_VIDEO_HEVC_DECODE_MODE_FRAME_BASED
|
|
#define V4L2_STATELESS_HEVC_START_CODE_NONE V4L2_MPEG_VIDEO_HEVC_START_CODE_NONE
|
|
#define V4L2_STATELESS_HEVC_START_CODE_ANNEX_B V4L2_MPEG_VIDEO_HEVC_START_CODE_ANNEX_B
|
|
#endif
|
|
|
|
#include "v4l2_request_hevc.h"
|
|
|
|
#include "libavutil/hwcontext_drm.h"
|
|
|
|
#include <semaphore.h>
|
|
#include <pthread.h>
|
|
|
|
#include "v4l2_req_devscan.h"
|
|
#include "v4l2_req_dmabufs.h"
|
|
#include "v4l2_req_pollqueue.h"
|
|
#include "v4l2_req_media.h"
|
|
#include "v4l2_req_utils.h"
|
|
|
|
// Attached to buf[0] in frame
|
|
// Pooled in hwcontext so generally create once - 1/frame
|
|
typedef struct V4L2MediaReqDescriptor {
|
|
AVDRMFrameDescriptor drm;
|
|
|
|
// Media
|
|
uint64_t timestamp;
|
|
struct qent_dst * qe_dst;
|
|
|
|
// Refs to source frames
|
|
AVBufferRef * refs[18]; // 16 + 1 + 1
|
|
|
|
// Decode only - should be NULL by the time we emit the frame
|
|
struct req_decode_ent decode_ent;
|
|
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
struct v4l2_ctrl_hevc_decode_params dec;
|
|
#endif
|
|
|
|
size_t num_slices;
|
|
size_t alloced_slices;
|
|
struct v4l2_ctrl_hevc_slice_params * slice_params;
|
|
struct slice_info * slices;
|
|
|
|
size_t num_offsets;
|
|
size_t alloced_offsets;
|
|
uint32_t *offsets;
|
|
|
|
} V4L2MediaReqDescriptor;
|
|
|
|
struct slice_info {
|
|
const uint8_t * ptr;
|
|
size_t len; // bytes
|
|
size_t n_offsets;
|
|
};
|
|
|
|
// Handy container for accumulating controls before setting
|
|
struct req_controls {
|
|
int has_scaling;
|
|
struct timeval tv;
|
|
struct v4l2_ctrl_hevc_sps sps;
|
|
struct v4l2_ctrl_hevc_pps pps;
|
|
struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix;
|
|
};
|
|
|
|
//static uint8_t nalu_slice_start_code[] = { 0x00, 0x00, 0x01 };
|
|
|
|
|
|
// Get an FFmpeg format from the v4l2 format
|
|
static enum AVPixelFormat pixel_format_from_format(const struct v4l2_format *const format)
|
|
{
|
|
const uint32_t vfmt = V4L2_TYPE_IS_MULTIPLANAR(format->type) ?
|
|
format->fmt.pix_mp.pixelformat : format->fmt.pix.pixelformat;
|
|
switch (vfmt) {
|
|
#if CONFIG_SAND
|
|
case V4L2_PIX_FMT_NV12_COL128:
|
|
case V4L2_PIX_FMT_NV12_COL128M:
|
|
return AV_PIX_FMT_RPI4_8;
|
|
case V4L2_PIX_FMT_NV12_10_COL128:
|
|
case V4L2_PIX_FMT_NV12_10_COL128M:
|
|
return AV_PIX_FMT_RPI4_10;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
return ff_v4l2_format_v4l2_to_avfmt(vfmt, AV_CODEC_ID_RAWVIDEO);
|
|
}
|
|
|
|
static inline uint64_t frame_capture_dpb(const AVFrame * const frame)
|
|
{
|
|
const V4L2MediaReqDescriptor *const rd = (V4L2MediaReqDescriptor *)frame->data[0];
|
|
return rd->timestamp;
|
|
}
|
|
|
|
static inline void frame_set_capture_dpb(AVFrame * const frame, const uint64_t dpb_stamp)
|
|
{
|
|
V4L2MediaReqDescriptor *const rd = (V4L2MediaReqDescriptor *)frame->data[0];
|
|
rd->timestamp = dpb_stamp;
|
|
}
|
|
|
|
static void fill_pred_table(const HEVCContext *h, struct v4l2_hevc_pred_weight_table *table)
|
|
{
|
|
int32_t luma_weight_denom, chroma_weight_denom;
|
|
const SliceHeader * const sh = &h->sh;
|
|
const HEVCPPS * const pps = h->pps;
|
|
const HEVCSPS * const sps = pps->sps;
|
|
|
|
if (sh->slice_type == HEVC_SLICE_I ||
|
|
(sh->slice_type == HEVC_SLICE_P && !pps->weighted_pred_flag) ||
|
|
(sh->slice_type == HEVC_SLICE_B && !pps->weighted_bipred_flag))
|
|
return;
|
|
|
|
table->luma_log2_weight_denom = sh->luma_log2_weight_denom;
|
|
|
|
if (sps->chroma_format_idc)
|
|
table->delta_chroma_log2_weight_denom = sh->chroma_log2_weight_denom - sh->luma_log2_weight_denom;
|
|
|
|
luma_weight_denom = (1 << sh->luma_log2_weight_denom);
|
|
chroma_weight_denom = (1 << sh->chroma_log2_weight_denom);
|
|
|
|
for (int i = 0; i < 15 && i < sh->nb_refs[L0]; i++) {
|
|
table->delta_luma_weight_l0[i] = sh->luma_weight_l0[i] - luma_weight_denom;
|
|
table->luma_offset_l0[i] = sh->luma_offset_l0[i];
|
|
table->delta_chroma_weight_l0[i][0] = sh->chroma_weight_l0[i][0] - chroma_weight_denom;
|
|
table->delta_chroma_weight_l0[i][1] = sh->chroma_weight_l0[i][1] - chroma_weight_denom;
|
|
table->chroma_offset_l0[i][0] = sh->chroma_offset_l0[i][0];
|
|
table->chroma_offset_l0[i][1] = sh->chroma_offset_l0[i][1];
|
|
}
|
|
|
|
if (sh->slice_type != HEVC_SLICE_B)
|
|
return;
|
|
|
|
for (int i = 0; i < 15 && i < sh->nb_refs[L1]; i++) {
|
|
table->delta_luma_weight_l1[i] = sh->luma_weight_l1[i] - luma_weight_denom;
|
|
table->luma_offset_l1[i] = sh->luma_offset_l1[i];
|
|
table->delta_chroma_weight_l1[i][0] = sh->chroma_weight_l1[i][0] - chroma_weight_denom;
|
|
table->delta_chroma_weight_l1[i][1] = sh->chroma_weight_l1[i][1] - chroma_weight_denom;
|
|
table->chroma_offset_l1[i][0] = sh->chroma_offset_l1[i][0];
|
|
table->chroma_offset_l1[i][1] = sh->chroma_offset_l1[i][1];
|
|
}
|
|
}
|
|
|
|
#if HEVC_CTRLS_VERSION <= 2
|
|
static int find_frame_rps_type(const HEVCContext *h, uint64_t timestamp)
|
|
{
|
|
const HEVCFrame *frame;
|
|
int i;
|
|
|
|
for (i = 0; i < h->rps[ST_CURR_BEF].nb_refs; i++) {
|
|
frame = h->rps[ST_CURR_BEF].ref[i];
|
|
if (frame && timestamp == frame_capture_dpb(frame->f))
|
|
return V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE;
|
|
}
|
|
|
|
for (i = 0; i < h->rps[ST_CURR_AFT].nb_refs; i++) {
|
|
frame = h->rps[ST_CURR_AFT].ref[i];
|
|
if (frame && timestamp == frame_capture_dpb(frame->f))
|
|
return V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_AFTER;
|
|
}
|
|
|
|
for (i = 0; i < h->rps[LT_CURR].nb_refs; i++) {
|
|
frame = h->rps[LT_CURR].ref[i];
|
|
if (frame && timestamp == frame_capture_dpb(frame->f))
|
|
return V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static unsigned int
|
|
get_ref_pic_index(const HEVCContext *h, const HEVCFrame *frame,
|
|
const struct v4l2_hevc_dpb_entry * const entries,
|
|
const unsigned int num_entries)
|
|
{
|
|
uint64_t timestamp;
|
|
|
|
if (!frame)
|
|
return 0;
|
|
|
|
timestamp = frame_capture_dpb(frame->f);
|
|
|
|
for (unsigned int i = 0; i < num_entries; i++) {
|
|
if (entries[i].timestamp == timestamp)
|
|
return i;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const uint8_t * ptr_from_index(const uint8_t * b, unsigned int idx)
|
|
{
|
|
unsigned int z = 0;
|
|
while (idx--) {
|
|
if (*b++ == 0) {
|
|
++z;
|
|
if (z >= 2 && *b == 3) {
|
|
++b;
|
|
z = 0;
|
|
}
|
|
}
|
|
else {
|
|
z = 0;
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
static int slice_add(V4L2MediaReqDescriptor * const rd)
|
|
{
|
|
if (rd->num_slices >= rd->alloced_slices) {
|
|
size_t n2 = rd->alloced_slices == 0 ? 8 : rd->alloced_slices * 2;
|
|
|
|
if (av_reallocp_array(&rd->slice_params, n2, sizeof(*rd->slice_params)))
|
|
goto fail;
|
|
if (av_reallocp_array(&rd->slices, n2, sizeof(*rd->slices)))
|
|
goto fail;
|
|
rd->alloced_slices = n2;
|
|
}
|
|
++rd->num_slices;
|
|
return 0;
|
|
|
|
fail:
|
|
av_freep(&rd->slices);
|
|
rd->alloced_slices = 0;
|
|
rd->num_slices = 0;
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
|
|
static int offsets_add(V4L2MediaReqDescriptor *const rd, const size_t n, const unsigned * const offsets)
|
|
{
|
|
if (rd->num_offsets + n > rd->alloced_offsets) {
|
|
size_t n2 = rd->alloced_slices == 0 ? 128 : rd->alloced_slices * 2;
|
|
void * p2;
|
|
while (rd->num_offsets + n > n2)
|
|
n2 *= 2;
|
|
if (av_reallocp_array(&rd->offsets, n2, sizeof(*rd->offsets))) {
|
|
rd->alloced_offsets = 0;
|
|
rd->num_offsets = 0;
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
rd->offsets = p2;
|
|
rd->alloced_offsets = n2;
|
|
}
|
|
for (size_t i = 0; i != n; ++i)
|
|
rd->offsets[rd->num_offsets++] = offsets[i] - 1;
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int
|
|
fill_dpb_entries(const HEVCContext * const h, struct v4l2_hevc_dpb_entry * const entries)
|
|
{
|
|
unsigned int i;
|
|
unsigned int n = 0;
|
|
const HEVCFrame * const pic = h->cur_frame;
|
|
const HEVCLayerContext * const layer = &h->layers[h->cur_layer];
|
|
|
|
for (i = 0; i < FF_ARRAY_ELEMS(layer->DPB); i++) {
|
|
const HEVCFrame * const frame = &layer->DPB[i];
|
|
if (frame != pic && (frame->flags & (HEVC_FRAME_FLAG_LONG_REF | HEVC_FRAME_FLAG_SHORT_REF))) {
|
|
struct v4l2_hevc_dpb_entry * const entry = entries + n++;
|
|
|
|
entry->timestamp = frame_capture_dpb(frame->f);
|
|
#if HEVC_CTRLS_VERSION <= 2
|
|
entry->rps = find_frame_rps_type(h, entry->timestamp);
|
|
#else
|
|
entry->flags = (frame->flags & HEVC_FRAME_FLAG_LONG_REF) == 0 ? 0 :
|
|
V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE;
|
|
#endif
|
|
entry->field_pic = (frame->f->flags & AV_FRAME_FLAG_INTERLACED) != 0;
|
|
|
|
#if HEVC_CTRLS_VERSION <= 3
|
|
/* TODO: Interleaved: Get the POC for each field. */
|
|
entry->pic_order_cnt[0] = frame->poc;
|
|
entry->pic_order_cnt[1] = frame->poc;
|
|
#else
|
|
entry->pic_order_cnt_val = frame->poc;
|
|
#endif
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
static void fill_slice_params(const HEVCContext * const h,
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
const struct v4l2_ctrl_hevc_decode_params * const dec,
|
|
#endif
|
|
struct v4l2_ctrl_hevc_slice_params *slice_params,
|
|
uint32_t bit_size, uint32_t bit_offset)
|
|
{
|
|
const SliceHeader * const sh = &h->sh;
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
const struct v4l2_hevc_dpb_entry *const dpb = dec->dpb;
|
|
const unsigned int dpb_n = dec->num_active_dpb_entries;
|
|
#else
|
|
struct v4l2_hevc_dpb_entry *const dpb = slice_params->dpb;
|
|
unsigned int dpb_n;
|
|
#endif
|
|
unsigned int i;
|
|
RefPicList *rpl;
|
|
|
|
*slice_params = (struct v4l2_ctrl_hevc_slice_params) {
|
|
.bit_size = bit_size,
|
|
#if HEVC_CTRLS_VERSION <= 3
|
|
.data_bit_offset = bit_offset,
|
|
#else
|
|
.data_byte_offset = bit_offset / 8 + 1,
|
|
#endif
|
|
/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
|
|
.slice_segment_addr = sh->slice_segment_addr,
|
|
|
|
/* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */
|
|
.nal_unit_type = h->nal_unit_type,
|
|
.nuh_temporal_id_plus1 = h->temporal_id + 1,
|
|
|
|
/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
|
|
.slice_type = sh->slice_type,
|
|
.colour_plane_id = sh->colour_plane_id,
|
|
.slice_pic_order_cnt = h->cur_frame->poc,
|
|
.num_ref_idx_l0_active_minus1 = sh->nb_refs[L0] ? sh->nb_refs[L0] - 1 : 0,
|
|
.num_ref_idx_l1_active_minus1 = sh->nb_refs[L1] ? sh->nb_refs[L1] - 1 : 0,
|
|
.collocated_ref_idx = sh->slice_temporal_mvp_enabled_flag ? sh->collocated_ref_idx : 0,
|
|
.five_minus_max_num_merge_cand = sh->slice_type == HEVC_SLICE_I ? 0 : 5 - sh->max_num_merge_cand,
|
|
.slice_qp_delta = sh->slice_qp_delta,
|
|
.slice_cb_qp_offset = sh->slice_cb_qp_offset,
|
|
.slice_cr_qp_offset = sh->slice_cr_qp_offset,
|
|
.slice_act_y_qp_offset = 0,
|
|
.slice_act_cb_qp_offset = 0,
|
|
.slice_act_cr_qp_offset = 0,
|
|
.slice_beta_offset_div2 = sh->beta_offset / 2,
|
|
.slice_tc_offset_div2 = sh->tc_offset / 2,
|
|
|
|
/* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture timing SEI message */
|
|
.pic_struct = h->sei.picture_timing.picture_struct,
|
|
|
|
#if HEVC_CTRLS_VERSION < 2
|
|
/* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */
|
|
.num_rps_poc_st_curr_before = h->rps[ST_CURR_BEF].nb_refs,
|
|
.num_rps_poc_st_curr_after = h->rps[ST_CURR_AFT].nb_refs,
|
|
.num_rps_poc_lt_curr = h->rps[LT_CURR].nb_refs,
|
|
#endif
|
|
};
|
|
|
|
if (sh->slice_sample_adaptive_offset_flag[0])
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA;
|
|
|
|
if (sh->slice_sample_adaptive_offset_flag[1])
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA;
|
|
|
|
if (sh->slice_temporal_mvp_enabled_flag)
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED;
|
|
|
|
if (sh->mvd_l1_zero_flag)
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO;
|
|
|
|
if (sh->cabac_init_flag)
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT;
|
|
|
|
if (sh->collocated_list == L0)
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0;
|
|
|
|
if (sh->disable_deblocking_filter_flag)
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED;
|
|
|
|
if (sh->slice_loop_filter_across_slices_enabled_flag)
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED;
|
|
|
|
if (sh->dependent_slice_segment_flag)
|
|
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT;
|
|
|
|
#if HEVC_CTRLS_VERSION < 2
|
|
dpb_n = fill_dpb_entries(h, dpb);
|
|
slice_params->num_active_dpb_entries = dpb_n;
|
|
#endif
|
|
|
|
if (sh->slice_type != HEVC_SLICE_I) {
|
|
rpl = &h->cur_frame->refPicList[0];
|
|
for (i = 0; i < rpl->nb_refs; i++)
|
|
slice_params->ref_idx_l0[i] = get_ref_pic_index(h, rpl->ref[i], dpb, dpb_n);
|
|
}
|
|
|
|
if (sh->slice_type == HEVC_SLICE_B) {
|
|
rpl = &h->cur_frame->refPicList[1];
|
|
for (i = 0; i < rpl->nb_refs; i++)
|
|
slice_params->ref_idx_l1[i] = get_ref_pic_index(h, rpl->ref[i], dpb, dpb_n);
|
|
}
|
|
|
|
fill_pred_table(h, &slice_params->pred_weight_table);
|
|
|
|
slice_params->num_entry_point_offsets = sh->num_entry_point_offsets;
|
|
#if HEVC_CTRLS_VERSION <= 3
|
|
if (slice_params->num_entry_point_offsets > 256) {
|
|
slice_params->num_entry_point_offsets = 256;
|
|
av_log(NULL, AV_LOG_ERROR, "%s: Currently only 256 entry points are supported, but slice has %d entry points.\n", __func__, sh->num_entry_point_offsets);
|
|
}
|
|
|
|
for (i = 0; i < slice_params->num_entry_point_offsets; i++)
|
|
slice_params->entry_point_offset_minus1[i] = sh->entry_point_offset[i] - 1;
|
|
#endif
|
|
}
|
|
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
static void
|
|
fill_decode_params(const HEVCContext * const h,
|
|
struct v4l2_ctrl_hevc_decode_params * const dec)
|
|
{
|
|
unsigned int i;
|
|
|
|
*dec = (struct v4l2_ctrl_hevc_decode_params){
|
|
.pic_order_cnt_val = h->poc,
|
|
.num_poc_st_curr_before = h->rps[ST_CURR_BEF].nb_refs,
|
|
.num_poc_st_curr_after = h->rps[ST_CURR_AFT].nb_refs,
|
|
.num_poc_lt_curr = h->rps[LT_CURR].nb_refs,
|
|
};
|
|
|
|
dec->num_active_dpb_entries = fill_dpb_entries(h, dec->dpb);
|
|
|
|
// The docn does seem to ask that we fit our 32 bit signed POC into
|
|
// a U8 so... (To be fair 16 bits would be enough)
|
|
// Luckily we (Pi) don't use these fields
|
|
for (i = 0; i != h->rps[ST_CURR_BEF].nb_refs; ++i)
|
|
dec->poc_st_curr_before[i] = h->rps[ST_CURR_BEF].ref[i]->poc;
|
|
for (i = 0; i != h->rps[ST_CURR_AFT].nb_refs; ++i)
|
|
dec->poc_st_curr_after[i] = h->rps[ST_CURR_AFT].ref[i]->poc;
|
|
for (i = 0; i != h->rps[LT_CURR].nb_refs; ++i)
|
|
dec->poc_lt_curr[i] = h->rps[LT_CURR].ref[i]->poc;
|
|
|
|
if (IS_IRAP(h))
|
|
dec->flags |= V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC;
|
|
if (IS_IDR(h))
|
|
dec->flags |= V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC;
|
|
if (h->sh.no_output_of_prior_pics_flag)
|
|
dec->flags |= V4L2_HEVC_DECODE_PARAM_FLAG_NO_OUTPUT_OF_PRIOR;
|
|
|
|
}
|
|
#endif
|
|
|
|
static void fill_sps(struct v4l2_ctrl_hevc_sps *ctrl, const HEVCSPS *sps)
|
|
{
|
|
/* ISO/IEC 23008-2, ITU-T Rec. H.265: Sequence parameter set */
|
|
*ctrl = (struct v4l2_ctrl_hevc_sps) {
|
|
.chroma_format_idc = sps->chroma_format_idc,
|
|
.pic_width_in_luma_samples = sps->width,
|
|
.pic_height_in_luma_samples = sps->height,
|
|
.bit_depth_luma_minus8 = sps->bit_depth - 8,
|
|
.bit_depth_chroma_minus8 = sps->bit_depth - 8,
|
|
.log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_poc_lsb - 4,
|
|
.sps_max_dec_pic_buffering_minus1 = sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering - 1,
|
|
.sps_max_num_reorder_pics = sps->temporal_layer[sps->max_sub_layers - 1].num_reorder_pics,
|
|
.sps_max_latency_increase_plus1 = sps->temporal_layer[sps->max_sub_layers - 1].max_latency_increase + 1,
|
|
.log2_min_luma_coding_block_size_minus3 = sps->log2_min_cb_size - 3,
|
|
.log2_diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_coding_block_size,
|
|
.log2_min_luma_transform_block_size_minus2 = sps->log2_min_tb_size - 2,
|
|
.log2_diff_max_min_luma_transform_block_size = sps->log2_max_trafo_size - sps->log2_min_tb_size,
|
|
.max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter,
|
|
.max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra,
|
|
.pcm_sample_bit_depth_luma_minus1 = sps->pcm.bit_depth - 1,
|
|
.pcm_sample_bit_depth_chroma_minus1 = sps->pcm.bit_depth_chroma - 1,
|
|
.log2_min_pcm_luma_coding_block_size_minus3 = sps->pcm.log2_min_pcm_cb_size - 3,
|
|
.log2_diff_max_min_pcm_luma_coding_block_size = sps->pcm.log2_max_pcm_cb_size - sps->pcm.log2_min_pcm_cb_size,
|
|
.num_short_term_ref_pic_sets = sps->nb_st_rps,
|
|
.num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps,
|
|
.chroma_format_idc = sps->chroma_format_idc,
|
|
.sps_max_sub_layers_minus1 = sps->max_sub_layers - 1,
|
|
};
|
|
|
|
if (sps->separate_colour_plane)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE;
|
|
|
|
if (sps->scaling_list_enabled)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED;
|
|
|
|
if (sps->amp_enabled)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_AMP_ENABLED;
|
|
|
|
if (sps->sao_enabled)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET;
|
|
|
|
if (sps->pcm_enabled)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_PCM_ENABLED;
|
|
|
|
if (sps->pcm_loop_filter_disabled)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED;
|
|
|
|
if (sps->long_term_ref_pics_present)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT;
|
|
|
|
if (sps->temporal_mvp_enabled)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED;
|
|
|
|
if (sps->strong_intra_smoothing_enabled)
|
|
ctrl->flags |= V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED;
|
|
}
|
|
|
|
static void fill_scaling_matrix(const ScalingList * const sl,
|
|
struct v4l2_ctrl_hevc_scaling_matrix * const sm)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
unsigned int j;
|
|
|
|
for (j = 0; j < 16; j++)
|
|
sm->scaling_list_4x4[i][j] = sl->sl[0][i][j];
|
|
for (j = 0; j < 64; j++) {
|
|
sm->scaling_list_8x8[i][j] = sl->sl[1][i][j];
|
|
sm->scaling_list_16x16[i][j] = sl->sl[2][i][j];
|
|
if (i < 2)
|
|
sm->scaling_list_32x32[i][j] = sl->sl[3][i * 3][j];
|
|
}
|
|
sm->scaling_list_dc_coef_16x16[i] = sl->sl_dc[0][i];
|
|
if (i < 2)
|
|
sm->scaling_list_dc_coef_32x32[i] = sl->sl_dc[1][i * 3];
|
|
}
|
|
}
|
|
|
|
static void fill_pps(struct v4l2_ctrl_hevc_pps * const ctrl, const HEVCPPS * const pps)
|
|
{
|
|
uint64_t flags = 0;
|
|
|
|
if (pps->dependent_slice_segments_enabled_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED;
|
|
|
|
if (pps->output_flag_present_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT;
|
|
|
|
if (pps->sign_data_hiding_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED;
|
|
|
|
if (pps->cabac_init_present_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT;
|
|
|
|
if (pps->constrained_intra_pred_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED;
|
|
|
|
if (pps->transform_skip_enabled_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED;
|
|
|
|
if (pps->cu_qp_delta_enabled_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED;
|
|
|
|
if (pps->pic_slice_level_chroma_qp_offsets_present_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT;
|
|
|
|
if (pps->weighted_pred_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED;
|
|
|
|
if (pps->weighted_bipred_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED;
|
|
|
|
if (pps->transquant_bypass_enable_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED;
|
|
|
|
if (pps->tiles_enabled_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_TILES_ENABLED;
|
|
|
|
if (pps->entropy_coding_sync_enabled_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED;
|
|
|
|
if (pps->loop_filter_across_tiles_enabled_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED;
|
|
|
|
if (pps->seq_loop_filter_across_slices_enabled_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED;
|
|
|
|
if (pps->deblocking_filter_override_enabled_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED;
|
|
|
|
if (pps->disable_dbf)
|
|
flags |= V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER;
|
|
|
|
if (pps->lists_modification_present_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT;
|
|
|
|
if (pps->slice_header_extension_present_flag)
|
|
flags |= V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT;
|
|
|
|
/* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture parameter set */
|
|
*ctrl = (struct v4l2_ctrl_hevc_pps) {
|
|
.num_extra_slice_header_bits = pps->num_extra_slice_header_bits,
|
|
.init_qp_minus26 = pps->pic_init_qp_minus26,
|
|
.diff_cu_qp_delta_depth = pps->diff_cu_qp_delta_depth,
|
|
.pps_cb_qp_offset = pps->cb_qp_offset,
|
|
.pps_cr_qp_offset = pps->cr_qp_offset,
|
|
.pps_beta_offset_div2 = pps->beta_offset / 2,
|
|
.pps_tc_offset_div2 = pps->tc_offset / 2,
|
|
.log2_parallel_merge_level_minus2 = pps->log2_parallel_merge_level - 2,
|
|
.flags = flags
|
|
};
|
|
|
|
|
|
if (pps->tiles_enabled_flag) {
|
|
ctrl->num_tile_columns_minus1 = pps->num_tile_columns - 1;
|
|
ctrl->num_tile_rows_minus1 = pps->num_tile_rows - 1;
|
|
|
|
for (int i = 0; i < pps->num_tile_columns; i++)
|
|
ctrl->column_width_minus1[i] = pps->column_width[i] - 1;
|
|
|
|
for (int i = 0; i < pps->num_tile_rows; i++)
|
|
ctrl->row_height_minus1[i] = pps->row_height[i] - 1;
|
|
}
|
|
}
|
|
|
|
static int frame_finish(V4L2MediaReqDescriptor * const rd)
|
|
{
|
|
int rv = 0;
|
|
|
|
if (rd->qe_dst) {
|
|
MediaBufsStatus stat = qent_dst_wait(rd->qe_dst);
|
|
if (stat != MEDIABUFS_STATUS_SUCCESS)
|
|
rv = -1;
|
|
}
|
|
|
|
{
|
|
AVBufferRef **p = rd->refs;
|
|
for (; *p != NULL; ++p)
|
|
av_buffer_unref(p);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Called before finally returning the frame to the user
|
|
// Set corrupt flag here as this is actually the frame structure that
|
|
// is going to the user (in MT land each thread has its own pool)
|
|
static int frame_post_process(void *logctx, AVFrame *frame)
|
|
{
|
|
V4L2MediaReqDescriptor *rd = (V4L2MediaReqDescriptor*)frame->data[0];
|
|
|
|
// av_log(NULL, AV_LOG_INFO, "%s\n", __func__);
|
|
frame->flags &= ~AV_FRAME_FLAG_CORRUPT;
|
|
if (frame_finish(rd) != 0) {
|
|
av_log(logctx, AV_LOG_ERROR, "%s: Decode fail\n", __func__);
|
|
frame->flags |= AV_FRAME_FLAG_CORRUPT;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline struct timeval cvt_dpb_to_tv(uint64_t t)
|
|
{
|
|
t /= 1000;
|
|
return (struct timeval){
|
|
.tv_usec = t % 1000000,
|
|
.tv_sec = t / 1000000
|
|
};
|
|
}
|
|
|
|
static inline uint64_t cvt_timestamp_to_dpb(const unsigned int t)
|
|
{
|
|
return (uint64_t)t * 1000;
|
|
}
|
|
|
|
static int v4l2_request_hevc_start_frame(AVCodecContext *avctx,
|
|
V4L2RequestContextHEVC *const ctx,
|
|
av_unused const uint8_t *buffer,
|
|
av_unused uint32_t size)
|
|
{
|
|
const HEVCContext *h = avctx->priv_data;
|
|
V4L2MediaReqDescriptor *const rd = (V4L2MediaReqDescriptor *)h->cur_frame->f->data[0];
|
|
|
|
// av_log(NULL, AV_LOG_INFO, "%s\n", __func__);
|
|
decode_q_add(&ctx->decode_q, &rd->decode_ent);
|
|
|
|
rd->num_slices = 0;
|
|
ctx->timestamp++;
|
|
rd->timestamp = cvt_timestamp_to_dpb(ctx->timestamp);
|
|
|
|
{
|
|
FrameDecodeData * const fdd = (FrameDecodeData*)h->cur_frame->f->private_ref->data;
|
|
fdd->post_process = frame_post_process;
|
|
}
|
|
|
|
// qe_dst needs to be bound to the data buffer and only returned when that is
|
|
if (!rd->qe_dst)
|
|
{
|
|
if ((rd->qe_dst = mediabufs_dst_qent_alloc(ctx->mbufs, ctx->dbufs)) == NULL) {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: Failed to get dst buffer\n", __func__);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
}
|
|
|
|
// ff_thread_finish_setup by caller
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Object fd & size will be zapped by this & need setting later
|
|
static int drm_from_format(AVDRMFrameDescriptor * const desc, const struct v4l2_format * const format)
|
|
{
|
|
AVDRMLayerDescriptor *layer = &desc->layers[0];
|
|
unsigned int width;
|
|
unsigned int height;
|
|
unsigned int bpl;
|
|
unsigned int bpl2;
|
|
uint32_t pixelformat;
|
|
uint64_t mod = DRM_FORMAT_MOD_LINEAR;
|
|
unsigned int object_count = 1;
|
|
|
|
if (V4L2_TYPE_IS_MULTIPLANAR(format->type)) {
|
|
width = format->fmt.pix_mp.width;
|
|
height = format->fmt.pix_mp.height;
|
|
pixelformat = format->fmt.pix_mp.pixelformat;
|
|
bpl = format->fmt.pix_mp.plane_fmt[0].bytesperline;
|
|
}
|
|
else {
|
|
width = format->fmt.pix.width;
|
|
height = format->fmt.pix.height;
|
|
pixelformat = format->fmt.pix.pixelformat;
|
|
bpl = format->fmt.pix.bytesperline;
|
|
}
|
|
bpl2 = bpl;
|
|
|
|
switch (pixelformat) {
|
|
case V4L2_PIX_FMT_NV12:
|
|
layer->format = DRM_FORMAT_NV12;
|
|
break;
|
|
case V4L2_PIX_FMT_P010:
|
|
layer->format = DRM_FORMAT_P010;
|
|
break;
|
|
#if CONFIG_SAND
|
|
case V4L2_PIX_FMT_NV12_COL128:
|
|
layer->format = DRM_FORMAT_NV12;
|
|
mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(bpl);
|
|
break;
|
|
case V4L2_PIX_FMT_NV12_10_COL128:
|
|
layer->format = DRM_FORMAT_P030;
|
|
mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(bpl);
|
|
break;
|
|
case V4L2_PIX_FMT_NV12_COL128M:
|
|
layer->format = DRM_FORMAT_NV12;
|
|
mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0);
|
|
bpl = height;
|
|
bpl2 = height / 2;
|
|
object_count = 2;
|
|
break;
|
|
case V4L2_PIX_FMT_NV12_10_COL128M:
|
|
layer->format = DRM_FORMAT_P030;
|
|
mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0);
|
|
bpl = height;
|
|
bpl2 = height / 2;
|
|
object_count = 2;
|
|
break;
|
|
#endif
|
|
#ifdef DRM_FORMAT_MOD_ALLWINNER_TILED
|
|
case V4L2_PIX_FMT_SUNXI_TILED_NV12:
|
|
layer->format = DRM_FORMAT_NV12;
|
|
mod = DRM_FORMAT_MOD_ALLWINNER_TILED;
|
|
break;
|
|
#endif
|
|
#if defined(V4L2_PIX_FMT_NV15) && defined(DRM_FORMAT_NV15)
|
|
case V4L2_PIX_FMT_NV15:
|
|
layer->format = DRM_FORMAT_NV15;
|
|
break;
|
|
#endif
|
|
case V4L2_PIX_FMT_NV16:
|
|
layer->format = DRM_FORMAT_NV16;
|
|
break;
|
|
#if defined(V4L2_PIX_FMT_NV20) && defined(DRM_FORMAT_NV20)
|
|
case V4L2_PIX_FMT_NV20:
|
|
layer->format = DRM_FORMAT_NV20;
|
|
break;
|
|
#endif
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
desc->nb_objects = object_count;
|
|
for (unsigned int i = 0; i != AV_DRM_MAX_PLANES; ++i) {
|
|
desc->objects[i].fd = -1;
|
|
desc->objects[i].size = 0;
|
|
desc->objects[i].format_modifier = (i >= object_count) ? DRM_FORMAT_MOD_INVALID : mod;
|
|
}
|
|
|
|
desc->nb_layers = 1;
|
|
layer->nb_planes = 2;
|
|
|
|
layer->planes[0].object_index = 0;
|
|
layer->planes[0].offset = 0;
|
|
layer->planes[0].pitch = bpl;
|
|
#if CONFIG_SAND
|
|
if (pixelformat == V4L2_PIX_FMT_NV12_COL128) {
|
|
layer->planes[1].object_index = 0;
|
|
layer->planes[1].offset = height * 128;
|
|
layer->planes[0].pitch = width;
|
|
layer->planes[1].pitch = width;
|
|
}
|
|
else if (pixelformat == V4L2_PIX_FMT_NV12_10_COL128) {
|
|
layer->planes[1].object_index = 0;
|
|
layer->planes[1].offset = height * 128;
|
|
layer->planes[0].pitch = width * 2; // Lies but it keeps DRM import happy
|
|
layer->planes[1].pitch = width * 2;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
layer->planes[1].object_index = (object_count > 1) ? 1 : 0;
|
|
layer->planes[1].offset = (object_count > 1) ? 0 : layer->planes[0].pitch * height;
|
|
layer->planes[1].pitch = bpl2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_req_ctls(V4L2RequestContextHEVC *ctx, struct media_request * const mreq,
|
|
struct req_controls *const controls,
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
struct v4l2_ctrl_hevc_decode_params * const dec,
|
|
#endif
|
|
struct v4l2_ctrl_hevc_slice_params * const slices, const unsigned int slice_count,
|
|
void * const offsets, const size_t offset_count)
|
|
{
|
|
int rv;
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
unsigned int n = 3;
|
|
#else
|
|
unsigned int n = 2;
|
|
#endif
|
|
|
|
struct v4l2_ext_control control[6] = {
|
|
{
|
|
.id = V4L2_CID_STATELESS_HEVC_SPS,
|
|
.ptr = &controls->sps,
|
|
.size = sizeof(controls->sps),
|
|
},
|
|
{
|
|
.id = V4L2_CID_STATELESS_HEVC_PPS,
|
|
.ptr = &controls->pps,
|
|
.size = sizeof(controls->pps),
|
|
},
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
{
|
|
.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
|
|
.ptr = dec,
|
|
.size = sizeof(*dec),
|
|
},
|
|
#endif
|
|
};
|
|
|
|
if (slices)
|
|
control[n++] = (struct v4l2_ext_control) {
|
|
.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS,
|
|
.ptr = slices,
|
|
.size = sizeof(*slices) * slice_count,
|
|
};
|
|
|
|
if (controls->has_scaling)
|
|
control[n++] = (struct v4l2_ext_control) {
|
|
.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
|
|
.ptr = &controls->scaling_matrix,
|
|
.size = sizeof(controls->scaling_matrix),
|
|
};
|
|
|
|
#if HEVC_CTRLS_VERSION >= 4
|
|
if (offsets)
|
|
control[n++] = (struct v4l2_ext_control) {
|
|
.id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS,
|
|
.ptr = offsets,
|
|
.size = sizeof(((struct V4L2MediaReqDescriptor *)0)->offsets[0]) * offset_count,
|
|
};
|
|
#endif
|
|
|
|
rv = mediabufs_ctl_set_ext_ctrls(ctx->mbufs, mreq, control, n);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static void
|
|
add_ref_once(V4L2MediaReqDescriptor * const rd, struct HEVCFrame * const ref)
|
|
{
|
|
AVBufferRef **p = rd->refs;
|
|
int i = 0;
|
|
while (*p != NULL) {
|
|
if (ref->f->buf[0]->data == (*p)->data)
|
|
return;
|
|
++p;
|
|
av_assert0(++i < 16);
|
|
}
|
|
*p = av_buffer_ref(ref->f->buf[0]);
|
|
}
|
|
|
|
// This only works because we started out from a single coded frame buffer
|
|
// that will remain intact until after end_frame
|
|
static int v4l2_request_hevc_decode_slice(AVCodecContext *avctx, V4L2RequestContextHEVC *const ctx, const uint8_t *buffer, uint32_t size)
|
|
{
|
|
const HEVCContext * const h = avctx->priv_data;
|
|
const SliceHeader * const sh = &h->sh;
|
|
V4L2MediaReqDescriptor * const rd = (V4L2MediaReqDescriptor*)h->cur_frame->f->data[0];
|
|
uint32_t boff = (ptr_from_index(buffer, sh->data_offset) - buffer) * 8 - 1;
|
|
|
|
const unsigned int n = rd->num_slices;
|
|
const unsigned int block_start = (n / ctx->max_slices) * ctx->max_slices;
|
|
|
|
int rv;
|
|
struct slice_info * si;
|
|
|
|
// This looks dodgy but we know that FFmpeg has parsed this from a buffer
|
|
// that contains the entire frame including the start code
|
|
if (ctx->start_code == V4L2_STATELESS_HEVC_START_CODE_ANNEX_B) {
|
|
buffer -= 3;
|
|
size += 3;
|
|
boff += 24;
|
|
if (buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 1) {
|
|
av_log(avctx, AV_LOG_ERROR, "Start code requested but missing %02x:%02x:%02x\n",
|
|
buffer[0], buffer[1], buffer[2]);
|
|
}
|
|
}
|
|
|
|
if ((rv = slice_add(rd)) != 0)
|
|
return rv;
|
|
|
|
si = rd->slices + n;
|
|
si->ptr = buffer;
|
|
si->len = size;
|
|
si->n_offsets = rd->num_offsets;
|
|
|
|
if (n != block_start) {
|
|
struct slice_info *const si0 = rd->slices + block_start;
|
|
const size_t offset = (buffer - si0->ptr);
|
|
boff += offset * 8;
|
|
size += offset;
|
|
si0->len = si->len + offset;
|
|
}
|
|
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
if (n == 0)
|
|
fill_decode_params(h, &rd->dec);
|
|
fill_slice_params(h, &rd->dec, rd->slice_params + n, size * 8, boff);
|
|
#else
|
|
fill_slice_params(h, rd->slice_params + n, size * 8, boff);
|
|
#endif
|
|
|
|
{
|
|
RefPicList *rpl;
|
|
int i;
|
|
|
|
if (sh->slice_type != HEVC_SLICE_I) {
|
|
rpl = &h->cur_frame->refPicList[0];
|
|
for (i = 0; i < rpl->nb_refs; i++)
|
|
add_ref_once(rd, rpl->ref[i]);
|
|
}
|
|
|
|
if (sh->slice_type == HEVC_SLICE_B) {
|
|
rpl = &h->cur_frame->refPicList[1];
|
|
for (i = 0; i < rpl->nb_refs; i++)
|
|
add_ref_once(rd, rpl->ref[i]);
|
|
}
|
|
}
|
|
|
|
if (ctx->max_offsets != 0 &&
|
|
(rv = offsets_add(rd, h->sh.num_entry_point_offsets, h->sh.entry_point_offset)) != 0)
|
|
return rv;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void v4l2_request_hevc_abort_frame(AVCodecContext * const avctx, V4L2RequestContextHEVC *const ctx)
|
|
{
|
|
const HEVCContext * const h = avctx->priv_data;
|
|
if (h->cur_frame != NULL) {
|
|
V4L2MediaReqDescriptor *const rd = (V4L2MediaReqDescriptor *)h->cur_frame->f->data[0];
|
|
|
|
decode_q_remove(&ctx->decode_q, &rd->decode_ent);
|
|
}
|
|
}
|
|
|
|
static int send_slice(AVCodecContext * const avctx,
|
|
V4L2RequestContextHEVC * const ctx,
|
|
V4L2MediaReqDescriptor * const rd,
|
|
struct req_controls *const controls,
|
|
const unsigned int i, const unsigned int j)
|
|
{
|
|
const int is_last = (j == rd->num_slices);
|
|
struct slice_info *const si = rd->slices + i;
|
|
struct media_request * req = NULL;
|
|
struct qent_src * src = NULL;
|
|
MediaBufsStatus stat;
|
|
void * offsets = rd->offsets + rd->slices[i].n_offsets;
|
|
size_t n_offsets = (is_last ? rd->num_offsets : rd->slices[j].n_offsets) - rd->slices[i].n_offsets;
|
|
|
|
if ((req = media_request_get(ctx->mpool)) == NULL) {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: Failed to alloc media request\n", __func__);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
|
|
if (set_req_ctls(ctx, req,
|
|
controls,
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
&rd->dec,
|
|
#endif
|
|
rd->slice_params + i, j - i,
|
|
offsets, n_offsets)) {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: Failed to set req ctls\n", __func__);
|
|
goto fail1;
|
|
}
|
|
|
|
if ((src = mediabufs_src_qent_get(ctx->mbufs)) == NULL) {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: Failed to get src buffer\n", __func__);
|
|
goto fail1;
|
|
}
|
|
|
|
if (qent_src_data_copy(src, 0, si->ptr, si->len, ctx->dbufs) != 0) {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: Failed data copy\n", __func__);
|
|
goto fail2;
|
|
}
|
|
|
|
if (qent_src_params_set(src, &controls->tv)) {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: Failed src param set\n", __func__);
|
|
goto fail2;
|
|
}
|
|
|
|
stat = mediabufs_start_request(ctx->mbufs, &req, &src,
|
|
i == 0 ? rd->qe_dst : NULL,
|
|
is_last);
|
|
|
|
if (stat != MEDIABUFS_STATUS_SUCCESS) {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: Failed to start request\n", __func__);
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
return 0;
|
|
|
|
fail2:
|
|
mediabufs_src_qent_abort(ctx->mbufs, &src);
|
|
fail1:
|
|
media_request_abort(&req);
|
|
return AVERROR_UNKNOWN;
|
|
}
|
|
|
|
static int v4l2_request_hevc_end_frame(AVCodecContext *avctx, V4L2RequestContextHEVC *const ctx)
|
|
{
|
|
const HEVCContext * const h = avctx->priv_data;
|
|
V4L2MediaReqDescriptor *rd = (V4L2MediaReqDescriptor*)h->cur_frame->f->data[0];
|
|
struct req_controls rc;
|
|
unsigned int i;
|
|
int rv;
|
|
|
|
// It is possible, though maybe a bug, to get an end_frame without
|
|
// a previous start_frame. If we do then give up.
|
|
if (!decode_q_in_q(&rd->decode_ent)) {
|
|
av_log(avctx, AV_LOG_DEBUG, "%s: Frame not in decode Q\n", __func__);
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
|
|
{
|
|
const HEVCPPS *pps = h->pps;
|
|
const HEVCSPS *sps = pps->sps;
|
|
const ScalingList *sl = pps->scaling_list_data_present_flag ?
|
|
&pps->scaling_list :
|
|
sps->scaling_list_enabled ?
|
|
&sps->scaling_list : NULL;
|
|
|
|
|
|
memset(&rc, 0, sizeof(rc));
|
|
rc.tv = cvt_dpb_to_tv(rd->timestamp);
|
|
fill_sps(&rc.sps, sps);
|
|
fill_pps(&rc.pps, pps);
|
|
if (sl) {
|
|
rc.has_scaling = 1;
|
|
fill_scaling_matrix(sl, &rc.scaling_matrix);
|
|
}
|
|
}
|
|
|
|
decode_q_wait(&ctx->decode_q, &rd->decode_ent);
|
|
|
|
// qe_dst needs to be bound to the data buffer and only returned when that is
|
|
// Alloc almost certainly wants to be serialised if there is any chance of blocking
|
|
// so we get the next frame to be free in the thread that needs it for decode first.
|
|
//
|
|
// In our current world this probably isn't a concern but put it here anyway
|
|
if (!rd->qe_dst)
|
|
{
|
|
if ((rd->qe_dst = mediabufs_dst_qent_alloc(ctx->mbufs, ctx->dbufs)) == NULL) {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: Failed to get dst buffer\n", __func__);
|
|
rv = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
// Send as slices
|
|
for (i = 0; i < rd->num_slices; i += ctx->max_slices) {
|
|
const unsigned int e = FFMIN(rd->num_slices, i + ctx->max_slices);
|
|
if ((rv = send_slice(avctx, ctx, rd, &rc, i, e)) != 0)
|
|
goto fail;
|
|
}
|
|
|
|
// Set the drm_prime desriptor
|
|
drm_from_format(&rd->drm, mediabufs_dst_fmt(ctx->mbufs));
|
|
for (i = 0; i != rd->drm.nb_objects; ++i) {
|
|
rd->drm.objects[i].fd = dmabuf_fd(qent_dst_dmabuf(rd->qe_dst, i));
|
|
rd->drm.objects[i].size = dmabuf_size(qent_dst_dmabuf(rd->qe_dst, i));
|
|
}
|
|
|
|
decode_q_remove(&ctx->decode_q, &rd->decode_ent);
|
|
return 0;
|
|
|
|
fail:
|
|
decode_q_remove(&ctx->decode_q, &rd->decode_ent);
|
|
return rv;
|
|
}
|
|
|
|
static inline int
|
|
ctrl_valid(const struct v4l2_query_ext_ctrl * const c, const int64_t v)
|
|
{
|
|
return v >= c->minimum && v <= c->maximum;
|
|
}
|
|
|
|
// Initial check & init
|
|
static int
|
|
probe(AVCodecContext * const avctx, V4L2RequestContextHEVC * const ctx)
|
|
{
|
|
const HEVCContext *h = avctx->priv_data;
|
|
const HEVCSPS * const sps = h->pps->sps;
|
|
struct v4l2_ctrl_hevc_sps ctrl_sps;
|
|
unsigned int i;
|
|
|
|
// Check for var slice array
|
|
struct v4l2_query_ext_ctrl qc[] = {
|
|
{ .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS },
|
|
{ .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, },
|
|
{ .id = V4L2_CID_STATELESS_HEVC_SPS },
|
|
{ .id = V4L2_CID_STATELESS_HEVC_PPS },
|
|
{ .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX },
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
{ .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS },
|
|
#endif
|
|
};
|
|
// Order & size must match!
|
|
static const size_t ctrl_sizes[] = {
|
|
sizeof(struct v4l2_ctrl_hevc_slice_params),
|
|
sizeof(int32_t),
|
|
sizeof(struct v4l2_ctrl_hevc_sps),
|
|
sizeof(struct v4l2_ctrl_hevc_pps),
|
|
sizeof(struct v4l2_ctrl_hevc_scaling_matrix),
|
|
#if HEVC_CTRLS_VERSION >= 2
|
|
sizeof(struct v4l2_ctrl_hevc_decode_params),
|
|
#endif
|
|
};
|
|
const unsigned int noof_ctrls = FF_ARRAY_ELEMS(qc);
|
|
|
|
#if HEVC_CTRLS_VERSION == 2
|
|
if (mediabufs_ctl_driver_version(ctx->mbufs) >= MEDIABUFS_DRIVER_VERSION(5, 18, 0))
|
|
return AVERROR(EINVAL);
|
|
#elif HEVC_CTRLS_VERSION == 3
|
|
if (mediabufs_ctl_driver_version(ctx->mbufs) < MEDIABUFS_DRIVER_VERSION(5, 18, 0))
|
|
return AVERROR(EINVAL);
|
|
#endif
|
|
|
|
mediabufs_ctl_query_ext_ctrls(ctx->mbufs, qc, noof_ctrls);
|
|
i = 0;
|
|
#if HEVC_CTRLS_VERSION < 4
|
|
// Fail frame mode silently for anything prior to V4
|
|
if (qc[1].type == 0 || !ctrl_valid(qc + 1, V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED))
|
|
return AVERROR(EINVAL);
|
|
#endif
|
|
for (; i != noof_ctrls; ++i) {
|
|
if (qc[i].type == 0) {
|
|
av_log(avctx, AV_LOG_DEBUG, "Probed V%d control %#x missing\n", HEVC_CTRLS_VERSION, qc[i].id);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
if (ctrl_sizes[i] != (size_t)qc[i].elem_size) {
|
|
av_log(avctx, AV_LOG_DEBUG, "Probed V%d control %d size mismatch %zu != %zu\n",
|
|
HEVC_CTRLS_VERSION, i, ctrl_sizes[i], (size_t)qc[i].elem_size);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
}
|
|
|
|
fill_sps(&ctrl_sps, sps);
|
|
|
|
if (mediabufs_set_ext_ctrl(ctx->mbufs, NULL, V4L2_CID_STATELESS_HEVC_SPS, &ctrl_sps, sizeof(ctrl_sps))) {
|
|
av_log(avctx, AV_LOG_ERROR, "Failed to set initial SPS\n");
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Final init
|
|
static int
|
|
set_controls(AVCodecContext * const avctx, V4L2RequestContextHEVC * const ctx)
|
|
{
|
|
int ret;
|
|
|
|
struct v4l2_query_ext_ctrl querys[] = {
|
|
{ .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, },
|
|
{ .id = V4L2_CID_STATELESS_HEVC_START_CODE, },
|
|
{ .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, },
|
|
#if HEVC_CTRLS_VERSION >= 4
|
|
{ .id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS, },
|
|
#endif
|
|
};
|
|
|
|
struct v4l2_ext_control ctrls[] = {
|
|
{ .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, },
|
|
{ .id = V4L2_CID_STATELESS_HEVC_START_CODE, },
|
|
};
|
|
|
|
mediabufs_ctl_query_ext_ctrls(ctx->mbufs, querys, FF_ARRAY_ELEMS(querys));
|
|
|
|
ctx->max_slices = (!(querys[2].flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) ||
|
|
querys[2].nr_of_dims != 1 || querys[2].dims[0] == 0) ?
|
|
1 : querys[2].dims[0];
|
|
av_log(avctx, AV_LOG_DEBUG, "%s: Max slices %d\n", __func__, ctx->max_slices);
|
|
|
|
#if HEVC_CTRLS_VERSION >= 4
|
|
ctx->max_offsets = (querys[3].type == 0 || querys[3].nr_of_dims != 1) ?
|
|
0 : querys[3].dims[0];
|
|
av_log(avctx, AV_LOG_DEBUG, "%s: Entry point offsets %d\n", __func__, ctx->max_offsets);
|
|
#else
|
|
ctx->max_offsets = 0;
|
|
#endif
|
|
|
|
if (querys[0].default_value == V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED ||
|
|
querys[0].default_value == V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED)
|
|
ctx->decode_mode = querys[0].default_value;
|
|
else if (ctrl_valid(querys + 0, V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED))
|
|
ctx->decode_mode = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED;
|
|
else if (ctrl_valid(querys + 0, V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED))
|
|
ctx->decode_mode = V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED;
|
|
else {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: unsupported decode mode\n", __func__);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
if (querys[1].default_value == V4L2_STATELESS_HEVC_START_CODE_NONE ||
|
|
querys[1].default_value == V4L2_STATELESS_HEVC_START_CODE_ANNEX_B)
|
|
ctx->start_code = querys[1].default_value;
|
|
else if (ctrl_valid(querys + 1, V4L2_STATELESS_HEVC_START_CODE_ANNEX_B))
|
|
ctx->start_code = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B;
|
|
else if (ctrl_valid(querys + 1, V4L2_STATELESS_HEVC_START_CODE_NONE))
|
|
ctx->start_code = V4L2_STATELESS_HEVC_START_CODE_NONE;
|
|
else {
|
|
av_log(avctx, AV_LOG_ERROR, "%s: unsupported start code\n", __func__);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
// If we are in slice mode & START_CODE_NONE supported then pick that
|
|
// as it doesn't require the slightly dodgy look backwards in our raw buffer
|
|
if (ctx->decode_mode == V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED &&
|
|
ctrl_valid(querys + 1, V4L2_STATELESS_HEVC_START_CODE_NONE))
|
|
ctx->start_code = V4L2_STATELESS_HEVC_START_CODE_NONE;
|
|
|
|
ctrls[0].value = ctx->decode_mode;
|
|
ctrls[1].value = ctx->start_code;
|
|
|
|
ret = mediabufs_ctl_set_ext_ctrls(ctx->mbufs, NULL, ctrls, FF_ARRAY_ELEMS(ctrls));
|
|
return !ret ? 0 : AVERROR(-ret);
|
|
}
|
|
|
|
static void v4l2_req_frame_free(void *opaque, uint8_t *data)
|
|
{
|
|
AVCodecContext *avctx = opaque;
|
|
V4L2MediaReqDescriptor * const rd = (V4L2MediaReqDescriptor*)data;
|
|
|
|
av_log(NULL, AV_LOG_DEBUG, "%s: avctx=%p data=%p\n", __func__, avctx, data);
|
|
|
|
frame_finish(rd);
|
|
|
|
qent_dst_unref(&rd->qe_dst);
|
|
|
|
av_freep(&rd->slices);
|
|
av_freep(&rd->slice_params);
|
|
av_freep(&rd->offsets);
|
|
|
|
av_free(rd);
|
|
}
|
|
|
|
static AVBufferRef *v4l2_req_frame_alloc(void *opaque, int size)
|
|
{
|
|
AVCodecContext *avctx = opaque;
|
|
// V4L2RequestContextHEVC *ctx = avctx->internal->hwaccel_priv_data;
|
|
// V4L2MediaReqDescriptor *req;
|
|
AVBufferRef *ref;
|
|
uint8_t *data;
|
|
// int ret;
|
|
|
|
data = av_mallocz(size);
|
|
if (!data)
|
|
return NULL;
|
|
|
|
av_log(avctx, AV_LOG_DEBUG, "%s: avctx=%p size=%d data=%p\n", __func__, avctx, size, data);
|
|
ref = av_buffer_create(data, size, v4l2_req_frame_free, avctx, 0);
|
|
if (!ref) {
|
|
av_freep(&data);
|
|
return NULL;
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
#if 0
|
|
static void v4l2_req_pool_free(void *opaque)
|
|
{
|
|
av_log(NULL, AV_LOG_DEBUG, "%s: opaque=%p\n", __func__, opaque);
|
|
}
|
|
|
|
static void v4l2_req_hwframe_ctx_free(AVHWFramesContext *hwfc)
|
|
{
|
|
av_log(NULL, AV_LOG_DEBUG, "%s: hwfc=%p pool=%p\n", __func__, hwfc, hwfc->pool);
|
|
|
|
av_buffer_pool_uninit(&hwfc->pool);
|
|
}
|
|
#endif
|
|
|
|
static int frame_params(AVCodecContext *avctx, V4L2RequestContextHEVC *const ctx, AVBufferRef *hw_frames_ctx)
|
|
{
|
|
AVHWFramesContext *hwfc = (AVHWFramesContext*)hw_frames_ctx->data;
|
|
const struct v4l2_format *vfmt = mediabufs_dst_fmt(ctx->mbufs);
|
|
|
|
hwfc->format = AV_PIX_FMT_DRM_PRIME;
|
|
hwfc->sw_format = pixel_format_from_format(vfmt);
|
|
if (V4L2_TYPE_IS_MULTIPLANAR(vfmt->type)) {
|
|
hwfc->width = vfmt->fmt.pix_mp.width;
|
|
hwfc->height = vfmt->fmt.pix_mp.height;
|
|
} else {
|
|
hwfc->width = vfmt->fmt.pix.width;
|
|
hwfc->height = vfmt->fmt.pix.height;
|
|
}
|
|
#if 0
|
|
hwfc->pool = av_buffer_pool_init2(sizeof(V4L2MediaReqDescriptor), avctx, v4l2_req_frame_alloc, v4l2_req_pool_free);
|
|
if (!hwfc->pool)
|
|
return AVERROR(ENOMEM);
|
|
|
|
hwfc->free = v4l2_req_hwframe_ctx_free;
|
|
|
|
hwfc->initial_pool_size = 1;
|
|
|
|
switch (avctx->codec_id) {
|
|
case AV_CODEC_ID_VP9:
|
|
hwfc->initial_pool_size += 8;
|
|
break;
|
|
case AV_CODEC_ID_VP8:
|
|
hwfc->initial_pool_size += 3;
|
|
break;
|
|
default:
|
|
hwfc->initial_pool_size += 2;
|
|
}
|
|
#endif
|
|
av_log(avctx, AV_LOG_DEBUG, "%s: avctx=%p ctx=%p hw_frames_ctx=%p hwfc=%p pool=%p width=%d height=%d initial_pool_size=%d\n", __func__, avctx, ctx, hw_frames_ctx, hwfc, hwfc->pool, hwfc->width, hwfc->height, hwfc->initial_pool_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int alloc_frame(AVCodecContext * avctx, V4L2RequestContextHEVC *const ctx, AVFrame *frame)
|
|
{
|
|
int rv;
|
|
|
|
frame->buf[0] = v4l2_req_frame_alloc(avctx, sizeof(V4L2MediaReqDescriptor));
|
|
if (!frame->buf[0])
|
|
return AVERROR(ENOMEM);
|
|
|
|
frame->data[0] = frame->buf[0]->data;
|
|
|
|
frame->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx);
|
|
// Cropping will be applied by hevc_refs.c:ff_hevc_set_new_ref
|
|
// Mirrors hwaccel path in avcodec_default_get_buffer2
|
|
frame->width = avctx->coded_width;
|
|
frame->height = avctx->coded_height;
|
|
|
|
if ((rv = ff_attach_decode_data(frame)) != 0) {
|
|
av_log(avctx, AV_LOG_ERROR, "Failed to attach decode data to frame\n");
|
|
av_frame_unref(frame);
|
|
return rv;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const v4l2_req_decode_fns V(ff_v4l2_req_hevc) = {
|
|
.src_pix_fmt_v4l2 = V4L2_PIX_FMT_HEVC_SLICE,
|
|
.name = "V4L2 HEVC stateless V" STR(HEVC_CTRLS_VERSION),
|
|
.probe = probe,
|
|
.set_controls = set_controls,
|
|
|
|
.start_frame = v4l2_request_hevc_start_frame,
|
|
.decode_slice = v4l2_request_hevc_decode_slice,
|
|
.end_frame = v4l2_request_hevc_end_frame,
|
|
.abort_frame = v4l2_request_hevc_abort_frame,
|
|
.frame_params = frame_params,
|
|
.alloc_frame = alloc_frame,
|
|
};
|
|
|