Skip to content
Snippets Groups Projects
vaapi_encode_h264.c 46.2 KiB
Newer Older
  • Learn to ignore specific revisions
  •     pic->nb_slices = 1;
    
        return 0;
    }
    
    static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,
                                                   VAAPIEncodePicture *pic,
                                                   VAAPIEncodeSlice *slice)
    {
        VAAPIEncodeContext                 *ctx = avctx->priv_data;
        VAEncSequenceParameterBufferH264  *vseq = ctx->codec_sequence_params;
        VAEncPictureParameterBufferH264   *vpic = pic->codec_picture_params;
        VAEncSliceParameterBufferH264   *vslice = slice->codec_slice_params;
        VAAPIEncodeH264Context            *priv = ctx->priv_data;
        VAAPIEncodeH264Slice            *pslice;
        VAAPIEncodeH264MiscSliceParams  *mslice;
        int i;
    
        slice->priv_data = av_mallocz(sizeof(*pslice));
        if (!slice->priv_data)
            return AVERROR(ENOMEM);
        pslice = slice->priv_data;
        mslice = &pslice->misc_slice_params;
    
        if (pic->type == PICTURE_TYPE_IDR)
    
            mslice->nal_unit_type = H264_NAL_IDR_SLICE;
    
            mslice->nal_unit_type = H264_NAL_SLICE;
    
    
        switch (pic->type) {
        case PICTURE_TYPE_IDR:
            vslice->slice_type  = SLICE_TYPE_I;
            mslice->nal_ref_idc = 3;
            break;
        case PICTURE_TYPE_I:
            vslice->slice_type  = SLICE_TYPE_I;
            mslice->nal_ref_idc = 2;
            break;
        case PICTURE_TYPE_P:
            vslice->slice_type  = SLICE_TYPE_P;
            mslice->nal_ref_idc = 1;
            break;
        case PICTURE_TYPE_B:
            vslice->slice_type  = SLICE_TYPE_B;
            mslice->nal_ref_idc = 0;
            break;
        default:
            av_assert0(0 && "invalid picture type");
        }
    
        // Only one slice per frame.
        vslice->macroblock_address = 0;
        vslice->num_macroblocks = priv->mb_width * priv->mb_height;
    
        vslice->macroblock_info = VA_INVALID_ID;
    
        vslice->pic_parameter_set_id = vpic->pic_parameter_set_id;
        vslice->idr_pic_id = priv->idr_pic_count++;
    
        vslice->pic_order_cnt_lsb = pic->display_order &
            ((1 << (4 + vseq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4)) - 1);
    
        for (i = 0; i < FF_ARRAY_ELEMS(vslice->RefPicList0); i++) {
            vslice->RefPicList0[i].picture_id = VA_INVALID_ID;
            vslice->RefPicList0[i].flags      = VA_PICTURE_H264_INVALID;
            vslice->RefPicList1[i].picture_id = VA_INVALID_ID;
            vslice->RefPicList1[i].flags      = VA_PICTURE_H264_INVALID;
        }
    
        av_assert0(pic->nb_refs <= 2);
        if (pic->nb_refs >= 1) {
    
            // Backward reference for P- or B-frame.
    
            av_assert0(pic->type == PICTURE_TYPE_P ||
                       pic->type == PICTURE_TYPE_B);
    
            vslice->num_ref_idx_l0_active_minus1 = 0;
            vslice->RefPicList0[0] = vpic->ReferenceFrames[0];
        }
        if (pic->nb_refs >= 2) {
    
            // Forward reference for B-frame.
    
            av_assert0(pic->type == PICTURE_TYPE_B);
    
            vslice->num_ref_idx_l1_active_minus1 = 0;
            vslice->RefPicList1[0] = vpic->ReferenceFrames[1];
        }
    
        if (pic->type == PICTURE_TYPE_B)
            vslice->slice_qp_delta = priv->fixed_qp_b - vpic->pic_init_qp;
        else if (pic->type == PICTURE_TYPE_P)
            vslice->slice_qp_delta = priv->fixed_qp_p - vpic->pic_init_qp;
        else
            vslice->slice_qp_delta = priv->fixed_qp_idr - vpic->pic_init_qp;
    
        vslice->direct_spatial_mv_pred_flag = 1;
    
        return 0;
    }
    
    
    static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
    
    {
        VAAPIEncodeContext      *ctx = avctx->priv_data;
        VAAPIEncodeH264Context *priv = ctx->priv_data;
    
        VAAPIEncodeH264Options  *opt = ctx->codec_options;
    
        priv->mb_width  = FFALIGN(avctx->width,  16) / 16;
        priv->mb_height = FFALIGN(avctx->height, 16) / 16;
    
        if (ctx->va_rc_mode == VA_RC_CQP) {
            priv->fixed_qp_p = opt->qp;
            if (avctx->i_quant_factor > 0.0)
                priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor +
                                            avctx->i_quant_offset) + 0.5);
            else
                priv->fixed_qp_idr = priv->fixed_qp_p;
            if (avctx->b_quant_factor > 0.0)
                priv->fixed_qp_b = (int)((priv->fixed_qp_p * avctx->b_quant_factor +
                                          avctx->b_quant_offset) + 0.5);
            else
                priv->fixed_qp_b = priv->fixed_qp_p;
    
            av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = "
                   "%d / %d / %d for IDR- / P- / B-frames.\n",
                   priv->fixed_qp_idr, priv->fixed_qp_p, priv->fixed_qp_b);
    
        } else if (ctx->va_rc_mode == VA_RC_CBR) {
            // These still need to be  set for pic_init_qp/slice_qp_delta.
            priv->fixed_qp_idr = 26;
            priv->fixed_qp_p   = 26;
            priv->fixed_qp_b   = 26;
    
            av_log(avctx, AV_LOG_DEBUG, "Using constant-bitrate = %d bps.\n",
                   avctx->bit_rate);
    
        } else {
            av_assert0(0 && "Invalid RC mode.");
        }
    
        if (opt->quality > 0) {
    #if VA_CHECK_VERSION(0, 36, 0)
            priv->quality_params.misc.type =
                VAEncMiscParameterTypeQualityLevel;
            priv->quality_params.quality.quality_level = opt->quality;
    
            ctx->global_params[ctx->nb_global_params] =
                &priv->quality_params.misc;
            ctx->global_params_size[ctx->nb_global_params++] =
                sizeof(priv->quality_params);
    #else
            av_log(avctx, AV_LOG_WARNING, "The encode quality option is not "
                   "supported with this VAAPI version.\n");
    #endif
        }
    
    static const VAAPIEncodeType vaapi_encode_type_h264 = {
        .priv_data_size        = sizeof(VAAPIEncodeH264Context),
    
        .configure             = &vaapi_encode_h264_configure,
    
        .sequence_params_size  = sizeof(VAEncSequenceParameterBufferH264),
        .init_sequence_params  = &vaapi_encode_h264_init_sequence_params,
    
        .picture_params_size   = sizeof(VAEncPictureParameterBufferH264),
        .init_picture_params   = &vaapi_encode_h264_init_picture_params,
    
        .slice_params_size     = sizeof(VAEncSliceParameterBufferH264),
        .init_slice_params     = &vaapi_encode_h264_init_slice_params,
    
        .sequence_header_type  = VAEncPackedHeaderSequence,
        .write_sequence_header = &vaapi_encode_h264_write_sequence_header,
    
        .slice_header_type     = VAEncPackedHeaderH264_Slice,
        .write_slice_header    = &vaapi_encode_h264_write_slice_header,
    
        .write_extra_header    = &vaapi_encode_h264_write_extra_header,
    };
    
    static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
    
        VAAPIEncodeContext     *ctx = avctx->priv_data;
        VAAPIEncodeH264Options *opt =
            (VAAPIEncodeH264Options*)ctx->codec_options_data;
    
        ctx->codec = &vaapi_encode_type_h264;
    
    
        switch (avctx->profile) {
        case FF_PROFILE_H264_CONSTRAINED_BASELINE:
            ctx->va_profile = VAProfileH264ConstrainedBaseline;
            break;
        case FF_PROFILE_H264_BASELINE:
            ctx->va_profile = VAProfileH264Baseline;
            break;
        case FF_PROFILE_H264_MAIN:
            ctx->va_profile = VAProfileH264Main;
            break;
        case FF_PROFILE_H264_EXTENDED:
            av_log(avctx, AV_LOG_ERROR, "H.264 extended profile "
                   "is not supported.\n");
            return AVERROR_PATCHWELCOME;
        case FF_PROFILE_UNKNOWN:
        case FF_PROFILE_H264_HIGH:
            ctx->va_profile = VAProfileH264High;
            break;
        case FF_PROFILE_H264_HIGH_10:
        case FF_PROFILE_H264_HIGH_10_INTRA:
            av_log(avctx, AV_LOG_ERROR, "H.264 10-bit profiles "
                   "are not supported.\n");
            return AVERROR_PATCHWELCOME;
        case FF_PROFILE_H264_HIGH_422:
        case FF_PROFILE_H264_HIGH_422_INTRA:
        case FF_PROFILE_H264_HIGH_444:
        case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
        case FF_PROFILE_H264_HIGH_444_INTRA:
        case FF_PROFILE_H264_CAVLC_444:
            av_log(avctx, AV_LOG_ERROR, "H.264 non-4:2:0 profiles "
                   "are not supported.\n");
            return AVERROR_PATCHWELCOME;
        default:
            av_log(avctx, AV_LOG_ERROR, "Unknown H.264 profile %d.\n",
                   avctx->profile);
            return AVERROR(EINVAL);
        }
    
    #if VA_CHECK_VERSION(0, 39, 2)
    
            ctx->va_entrypoint = VAEntrypointEncSliceLP;
    #else
            av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
                   "supported with this VAAPI version.\n");
            return AVERROR(EINVAL);
    #endif
        } else {
            ctx->va_entrypoint = VAEntrypointEncSlice;
        }
    
        // Only 8-bit encode is supported.
        ctx->va_rt_format = VA_RT_FORMAT_YUV420;
    
        if (avctx->bit_rate > 0)
    
            ctx->va_rc_mode = VA_RC_CBR;
    
            ctx->va_rc_mode = VA_RC_CQP;
    
        ctx->va_packed_headers =
            VA_ENC_PACKED_HEADER_SEQUENCE | // SPS and PPS.
            VA_ENC_PACKED_HEADER_SLICE    | // Slice headers.
            VA_ENC_PACKED_HEADER_MISC;      // SEI.
    
        ctx->surface_width  = FFALIGN(avctx->width,  16);
        ctx->surface_height = FFALIGN(avctx->height, 16);
    
        return ff_vaapi_encode_init(avctx);
    
    #define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \
                       offsetof(VAAPIEncodeH264Options, x))
    #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
    static const AVOption vaapi_encode_h264_options[] = {
    
        { "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
    
          OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 20 }, 0, 52, FLAGS },
    
        { "quality", "Set encode quality (trades off against speed, higher is faster)",
    
          OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 8, FLAGS },
        { "low_power", "Use low-power encoding mode (experimental: only supported "
          "on some platforms, does not support all features)",
          OFFSET(low_power), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
    
    static const AVCodecDefault vaapi_encode_h264_defaults[] = {
        { "profile",        "100" },
        { "level",          "51"  },
        { "b",              "0"   },
        { "bf",             "2"   },
        { "g",              "120" },
        { "i_qfactor",      "1.0" },
        { "i_qoffset",      "0.0" },
        { "b_qfactor",      "1.2" },
        { "b_qoffset",      "0.0" },
        { NULL },
    };
    
    static const AVClass vaapi_encode_h264_class = {
        .class_name = "h264_vaapi",
        .item_name  = av_default_item_name,
    
        .option     = vaapi_encode_h264_options,
    
        .version    = LIBAVUTIL_VERSION_INT,
    };
    
    AVCodec ff_h264_vaapi_encoder = {
        .name           = "h264_vaapi",
        .long_name      = NULL_IF_CONFIG_SMALL("H.264/AVC (VAAPI)"),
        .type           = AVMEDIA_TYPE_VIDEO,
        .id             = AV_CODEC_ID_H264,
    
        .priv_data_size = (sizeof(VAAPIEncodeContext) +
                           sizeof(VAAPIEncodeH264Options)),
    
        .init           = &vaapi_encode_h264_init,
        .encode2        = &ff_vaapi_encode2,
        .close          = &ff_vaapi_encode_close,
        .priv_class     = &vaapi_encode_h264_class,
        .capabilities   = AV_CODEC_CAP_DELAY,
        .defaults       = vaapi_encode_h264_defaults,
        .pix_fmts = (const enum AVPixelFormat[]) {
            AV_PIX_FMT_VAAPI,
            AV_PIX_FMT_NONE,
        },
    };