| /* |
| * 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 <string.h> |
| |
| #include "libavutil/avassert.h" |
| #include "libavutil/pixdesc.h" |
| #include "formats.h" |
| #include "internal.h" |
| #include "vaapi_vpp.h" |
| |
| int ff_vaapi_vpp_query_formats(AVFilterContext *avctx) |
| { |
| enum AVPixelFormat pix_fmts[] = { |
| AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE, |
| }; |
| int err; |
| |
| if ((err = ff_formats_ref(ff_make_format_list(pix_fmts), |
| &avctx->inputs[0]->outcfg.formats)) < 0) |
| return err; |
| if ((err = ff_formats_ref(ff_make_format_list(pix_fmts), |
| &avctx->outputs[0]->incfg.formats)) < 0) |
| return err; |
| |
| return 0; |
| } |
| |
| void ff_vaapi_vpp_pipeline_uninit(AVFilterContext *avctx) |
| { |
| VAAPIVPPContext *ctx = avctx->priv; |
| int i; |
| for (i = 0; i < ctx->nb_filter_buffers; i++) { |
| if (ctx->filter_buffers[i] != VA_INVALID_ID) { |
| vaDestroyBuffer(ctx->hwctx->display, ctx->filter_buffers[i]); |
| ctx->filter_buffers[i] = VA_INVALID_ID; |
| } |
| } |
| ctx->nb_filter_buffers = 0; |
| |
| if (ctx->va_context != VA_INVALID_ID) { |
| vaDestroyContext(ctx->hwctx->display, ctx->va_context); |
| ctx->va_context = VA_INVALID_ID; |
| } |
| |
| if (ctx->va_config != VA_INVALID_ID) { |
| vaDestroyConfig(ctx->hwctx->display, ctx->va_config); |
| ctx->va_config = VA_INVALID_ID; |
| } |
| |
| av_buffer_unref(&ctx->device_ref); |
| ctx->hwctx = NULL; |
| } |
| |
| int ff_vaapi_vpp_config_input(AVFilterLink *inlink) |
| { |
| AVFilterContext *avctx = inlink->dst; |
| VAAPIVPPContext *ctx = avctx->priv; |
| |
| if (ctx->pipeline_uninit) |
| ctx->pipeline_uninit(avctx); |
| |
| if (!inlink->hw_frames_ctx) { |
| av_log(avctx, AV_LOG_ERROR, "A hardware frames reference is " |
| "required to associate the processing device.\n"); |
| return AVERROR(EINVAL); |
| } |
| |
| ctx->input_frames_ref = av_buffer_ref(inlink->hw_frames_ctx); |
| if (!ctx->input_frames_ref) { |
| av_log(avctx, AV_LOG_ERROR, "A input frames reference create " |
| "failed.\n"); |
| return AVERROR(ENOMEM); |
| } |
| ctx->input_frames = (AVHWFramesContext*)ctx->input_frames_ref->data; |
| |
| return 0; |
| } |
| |
| int ff_vaapi_vpp_config_output(AVFilterLink *outlink) |
| { |
| AVFilterContext *avctx = outlink->src; |
| VAAPIVPPContext *ctx = avctx->priv; |
| AVVAAPIHWConfig *hwconfig = NULL; |
| AVHWFramesConstraints *constraints = NULL; |
| AVHWFramesContext *output_frames; |
| AVVAAPIFramesContext *va_frames; |
| VAStatus vas; |
| int err, i; |
| |
| if (ctx->pipeline_uninit) |
| ctx->pipeline_uninit(avctx); |
| |
| if (!ctx->output_width) |
| ctx->output_width = avctx->inputs[0]->w; |
| if (!ctx->output_height) |
| ctx->output_height = avctx->inputs[0]->h; |
| |
| av_assert0(ctx->input_frames); |
| ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref); |
| if (!ctx->device_ref) { |
| av_log(avctx, AV_LOG_ERROR, "A device reference create " |
| "failed.\n"); |
| return AVERROR(ENOMEM); |
| } |
| ctx->hwctx = ((AVHWDeviceContext*)ctx->device_ref->data)->hwctx; |
| |
| av_assert0(ctx->va_config == VA_INVALID_ID); |
| vas = vaCreateConfig(ctx->hwctx->display, VAProfileNone, |
| VAEntrypointVideoProc, NULL, 0, &ctx->va_config); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to create processing pipeline " |
| "config: %d (%s).\n", vas, vaErrorStr(vas)); |
| err = AVERROR(EIO); |
| goto fail; |
| } |
| |
| hwconfig = av_hwdevice_hwconfig_alloc(ctx->device_ref); |
| if (!hwconfig) { |
| err = AVERROR(ENOMEM); |
| goto fail; |
| } |
| hwconfig->config_id = ctx->va_config; |
| |
| constraints = av_hwdevice_get_hwframe_constraints(ctx->device_ref, |
| hwconfig); |
| if (!constraints) { |
| err = AVERROR(ENOMEM); |
| goto fail; |
| } |
| |
| if (ctx->output_format == AV_PIX_FMT_NONE) |
| ctx->output_format = ctx->input_frames->sw_format; |
| if (constraints->valid_sw_formats) { |
| for (i = 0; constraints->valid_sw_formats[i] != AV_PIX_FMT_NONE; i++) { |
| if (ctx->output_format == constraints->valid_sw_formats[i]) |
| break; |
| } |
| if (constraints->valid_sw_formats[i] == AV_PIX_FMT_NONE) { |
| av_log(avctx, AV_LOG_ERROR, "Hardware does not support output " |
| "format %s.\n", av_get_pix_fmt_name(ctx->output_format)); |
| err = AVERROR(EINVAL); |
| goto fail; |
| } |
| } |
| |
| if (ctx->output_width < constraints->min_width || |
| ctx->output_height < constraints->min_height || |
| ctx->output_width > constraints->max_width || |
| ctx->output_height > constraints->max_height) { |
| av_log(avctx, AV_LOG_ERROR, "Hardware does not support scaling to " |
| "size %dx%d (constraints: width %d-%d height %d-%d).\n", |
| ctx->output_width, ctx->output_height, |
| constraints->min_width, constraints->max_width, |
| constraints->min_height, constraints->max_height); |
| err = AVERROR(EINVAL); |
| goto fail; |
| } |
| |
| outlink->hw_frames_ctx = av_hwframe_ctx_alloc(ctx->device_ref); |
| if (!outlink->hw_frames_ctx) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to create HW frame context " |
| "for output.\n"); |
| err = AVERROR(ENOMEM); |
| goto fail; |
| } |
| |
| output_frames = (AVHWFramesContext*)outlink->hw_frames_ctx->data; |
| |
| output_frames->format = AV_PIX_FMT_VAAPI; |
| output_frames->sw_format = ctx->output_format; |
| output_frames->width = ctx->output_width; |
| output_frames->height = ctx->output_height; |
| |
| output_frames->initial_pool_size = 4; |
| |
| err = ff_filter_init_hw_frames(avctx, outlink, 10); |
| if (err < 0) |
| goto fail; |
| |
| err = av_hwframe_ctx_init(outlink->hw_frames_ctx); |
| if (err < 0) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to initialise VAAPI frame " |
| "context for output: %d\n", err); |
| goto fail; |
| } |
| |
| va_frames = output_frames->hwctx; |
| |
| av_assert0(ctx->va_context == VA_INVALID_ID); |
| vas = vaCreateContext(ctx->hwctx->display, ctx->va_config, |
| ctx->output_width, ctx->output_height, |
| VA_PROGRESSIVE, |
| va_frames->surface_ids, va_frames->nb_surfaces, |
| &ctx->va_context); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to create processing pipeline " |
| "context: %d (%s).\n", vas, vaErrorStr(vas)); |
| return AVERROR(EIO); |
| } |
| |
| outlink->w = ctx->output_width; |
| outlink->h = ctx->output_height; |
| |
| if (ctx->build_filter_params) { |
| err = ctx->build_filter_params(avctx); |
| if (err < 0) |
| goto fail; |
| } |
| |
| av_freep(&hwconfig); |
| av_hwframe_constraints_free(&constraints); |
| return 0; |
| |
| fail: |
| av_buffer_unref(&outlink->hw_frames_ctx); |
| av_freep(&hwconfig); |
| av_hwframe_constraints_free(&constraints); |
| return err; |
| } |
| |
| typedef struct VAAPIColourProperties { |
| VAProcColorStandardType va_color_standard; |
| |
| enum AVColorPrimaries color_primaries; |
| enum AVColorTransferCharacteristic color_trc; |
| enum AVColorSpace colorspace; |
| |
| uint8_t va_chroma_sample_location; |
| uint8_t va_color_range; |
| |
| enum AVColorRange color_range; |
| enum AVChromaLocation chroma_sample_location; |
| } VAAPIColourProperties; |
| |
| static const VAAPIColourProperties vaapi_colour_standard_map[] = { |
| { VAProcColorStandardBT601, 5, 6, 5 }, |
| { VAProcColorStandardBT601, 6, 6, 6 }, |
| { VAProcColorStandardBT709, 1, 1, 1 }, |
| { VAProcColorStandardBT470M, 4, 4, 4 }, |
| { VAProcColorStandardBT470BG, 5, 5, 5 }, |
| { VAProcColorStandardSMPTE170M, 6, 6, 6 }, |
| { VAProcColorStandardSMPTE240M, 7, 7, 7 }, |
| { VAProcColorStandardGenericFilm, 8, 1, 1 }, |
| #if VA_CHECK_VERSION(1, 1, 0) |
| { VAProcColorStandardSRGB, 1, 13, 0 }, |
| { VAProcColorStandardXVYCC601, 1, 11, 5 }, |
| { VAProcColorStandardXVYCC709, 1, 11, 1 }, |
| { VAProcColorStandardBT2020, 9, 14, 9 }, |
| #endif |
| }; |
| |
| static void vaapi_vpp_fill_colour_standard(VAAPIColourProperties *props, |
| VAProcColorStandardType *vacs, |
| int nb_vacs) |
| { |
| const VAAPIColourProperties *t; |
| int i, j, score, best_score, worst_score; |
| VAProcColorStandardType best_standard; |
| |
| #if VA_CHECK_VERSION(1, 3, 0) |
| // If the driver supports explicit use of the standard values then just |
| // use them and avoid doing any mapping. (The driver may not support |
| // some particular code point, but it still has enough information to |
| // make a better fallback choice than we do in that case.) |
| for (i = 0; i < nb_vacs; i++) { |
| if (vacs[i] == VAProcColorStandardExplicit) { |
| props->va_color_standard = VAProcColorStandardExplicit; |
| return; |
| } |
| } |
| #endif |
| |
| // Give scores to the possible options and choose the lowest one. |
| // An exact match will score zero and therefore always be chosen, as |
| // will a partial match where all unmatched elements are explicitly |
| // unspecified. If no options match at all then just pass "none" to |
| // the driver and let it make its own choice. |
| best_standard = VAProcColorStandardNone; |
| best_score = -1; |
| worst_score = 4 * (props->colorspace != AVCOL_SPC_UNSPECIFIED && |
| props->colorspace != AVCOL_SPC_RGB) + |
| 2 * (props->color_trc != AVCOL_TRC_UNSPECIFIED) + |
| (props->color_primaries != AVCOL_PRI_UNSPECIFIED); |
| |
| if (worst_score == 0) { |
| // No properties are specified, so we aren't going to be able to |
| // make a useful choice. |
| props->va_color_standard = VAProcColorStandardNone; |
| return; |
| } |
| |
| for (i = 0; i < nb_vacs; i++) { |
| for (j = 0; j < FF_ARRAY_ELEMS(vaapi_colour_standard_map); j++) { |
| t = &vaapi_colour_standard_map[j]; |
| if (t->va_color_standard != vacs[i]) |
| continue; |
| |
| score = 0; |
| if (props->colorspace != AVCOL_SPC_UNSPECIFIED && |
| props->colorspace != AVCOL_SPC_RGB) |
| score += 4 * (props->colorspace != t->colorspace); |
| if (props->color_trc != AVCOL_TRC_UNSPECIFIED) |
| score += 2 * (props->color_trc != t->color_trc); |
| if (props->color_primaries != AVCOL_PRI_UNSPECIFIED) |
| score += (props->color_primaries != t->color_primaries); |
| |
| // Only include choices which matched something. |
| if (score < worst_score && |
| (best_score == -1 || score < best_score)) { |
| best_score = score; |
| best_standard = t->va_color_standard; |
| } |
| } |
| } |
| props->va_color_standard = best_standard; |
| } |
| |
| static void vaapi_vpp_fill_chroma_sample_location(VAAPIColourProperties *props) |
| { |
| #if VA_CHECK_VERSION(1, 1, 0) |
| static const struct { |
| enum AVChromaLocation av; |
| uint8_t va; |
| } csl_map[] = { |
| { AVCHROMA_LOC_UNSPECIFIED, VA_CHROMA_SITING_UNKNOWN }, |
| { AVCHROMA_LOC_LEFT, VA_CHROMA_SITING_VERTICAL_CENTER | |
| VA_CHROMA_SITING_HORIZONTAL_LEFT }, |
| { AVCHROMA_LOC_CENTER, VA_CHROMA_SITING_VERTICAL_CENTER | |
| VA_CHROMA_SITING_HORIZONTAL_CENTER }, |
| { AVCHROMA_LOC_TOPLEFT, VA_CHROMA_SITING_VERTICAL_TOP | |
| VA_CHROMA_SITING_HORIZONTAL_LEFT }, |
| { AVCHROMA_LOC_TOP, VA_CHROMA_SITING_VERTICAL_TOP | |
| VA_CHROMA_SITING_HORIZONTAL_CENTER }, |
| { AVCHROMA_LOC_BOTTOMLEFT, VA_CHROMA_SITING_VERTICAL_BOTTOM | |
| VA_CHROMA_SITING_HORIZONTAL_LEFT }, |
| { AVCHROMA_LOC_BOTTOM, VA_CHROMA_SITING_VERTICAL_BOTTOM | |
| VA_CHROMA_SITING_HORIZONTAL_CENTER }, |
| }; |
| int i; |
| |
| for (i = 0; i < FF_ARRAY_ELEMS(csl_map); i++) { |
| if (props->chroma_sample_location == csl_map[i].av) { |
| props->va_chroma_sample_location = csl_map[i].va; |
| return; |
| } |
| } |
| props->va_chroma_sample_location = VA_CHROMA_SITING_UNKNOWN; |
| #else |
| props->va_chroma_sample_location = 0; |
| #endif |
| } |
| |
| static void vaapi_vpp_fill_colour_range(VAAPIColourProperties *props) |
| { |
| #if VA_CHECK_VERSION(1, 1, 0) |
| switch (props->color_range) { |
| case AVCOL_RANGE_MPEG: |
| props->va_color_range = VA_SOURCE_RANGE_REDUCED; |
| break; |
| case AVCOL_RANGE_JPEG: |
| props->va_color_range = VA_SOURCE_RANGE_FULL; |
| break; |
| case AVCOL_RANGE_UNSPECIFIED: |
| default: |
| props->va_color_range = VA_SOURCE_RANGE_UNKNOWN; |
| } |
| #else |
| props->va_color_range = 0; |
| #endif |
| } |
| |
| static void vaapi_vpp_fill_colour_properties(AVFilterContext *avctx, |
| VAAPIColourProperties *props, |
| VAProcColorStandardType *vacs, |
| int nb_vacs) |
| { |
| vaapi_vpp_fill_colour_standard(props, vacs, nb_vacs); |
| vaapi_vpp_fill_chroma_sample_location(props); |
| vaapi_vpp_fill_colour_range(props); |
| |
| av_log(avctx, AV_LOG_DEBUG, "Mapped colour properties %s %s/%s/%s %s " |
| "to VA standard %d chroma siting %#x range %#x.\n", |
| av_color_range_name(props->color_range), |
| av_color_space_name(props->colorspace), |
| av_color_primaries_name(props->color_primaries), |
| av_color_transfer_name(props->color_trc), |
| av_chroma_location_name(props->chroma_sample_location), |
| props->va_color_standard, |
| props->va_chroma_sample_location, props->va_color_range); |
| } |
| |
| static int vaapi_vpp_frame_is_rgb(const AVFrame *frame) |
| { |
| const AVHWFramesContext *hwfc; |
| const AVPixFmtDescriptor *desc; |
| av_assert0(frame->format == AV_PIX_FMT_VAAPI && |
| frame->hw_frames_ctx); |
| hwfc = (const AVHWFramesContext*)frame->hw_frames_ctx->data; |
| desc = av_pix_fmt_desc_get(hwfc->sw_format); |
| av_assert0(desc); |
| return !!(desc->flags & AV_PIX_FMT_FLAG_RGB); |
| } |
| |
| static int vaapi_vpp_colour_properties(AVFilterContext *avctx, |
| VAProcPipelineParameterBuffer *params, |
| const AVFrame *input_frame, |
| AVFrame *output_frame) |
| { |
| VAAPIVPPContext *ctx = avctx->priv; |
| VAAPIColourProperties input_props, output_props; |
| VAProcPipelineCaps caps; |
| VAStatus vas; |
| |
| vas = vaQueryVideoProcPipelineCaps(ctx->hwctx->display, ctx->va_context, |
| ctx->filter_buffers, ctx->nb_filter_buffers, |
| &caps); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to query capabilities for " |
| "colour standard support: %d (%s).\n", vas, vaErrorStr(vas)); |
| return AVERROR_EXTERNAL; |
| } |
| |
| input_props = (VAAPIColourProperties) { |
| .colorspace = vaapi_vpp_frame_is_rgb(input_frame) |
| ? AVCOL_SPC_RGB : input_frame->colorspace, |
| .color_primaries = input_frame->color_primaries, |
| .color_trc = input_frame->color_trc, |
| .color_range = input_frame->color_range, |
| .chroma_sample_location = input_frame->chroma_location, |
| }; |
| |
| vaapi_vpp_fill_colour_properties(avctx, &input_props, |
| caps.input_color_standards, |
| caps.num_input_color_standards); |
| |
| output_props = (VAAPIColourProperties) { |
| .colorspace = vaapi_vpp_frame_is_rgb(output_frame) |
| ? AVCOL_SPC_RGB : output_frame->colorspace, |
| .color_primaries = output_frame->color_primaries, |
| .color_trc = output_frame->color_trc, |
| .color_range = output_frame->color_range, |
| .chroma_sample_location = output_frame->chroma_location, |
| }; |
| vaapi_vpp_fill_colour_properties(avctx, &output_props, |
| caps.output_color_standards, |
| caps.num_output_color_standards); |
| |
| // If the properties weren't filled completely in the output frame and |
| // we chose a fixed standard then fill the known values in here. |
| #if VA_CHECK_VERSION(1, 3, 0) |
| if (output_props.va_color_standard != VAProcColorStandardExplicit) |
| #endif |
| { |
| const VAAPIColourProperties *output_standard = NULL; |
| int i; |
| |
| for (i = 0; i < FF_ARRAY_ELEMS(vaapi_colour_standard_map); i++) { |
| if (output_props.va_color_standard == |
| vaapi_colour_standard_map[i].va_color_standard) { |
| output_standard = &vaapi_colour_standard_map[i]; |
| break; |
| } |
| } |
| if (output_standard) { |
| output_frame->colorspace = vaapi_vpp_frame_is_rgb(output_frame) |
| ? AVCOL_SPC_RGB : output_standard->colorspace; |
| output_frame->color_primaries = output_standard->color_primaries; |
| output_frame->color_trc = output_standard->color_trc; |
| } |
| } |
| |
| params->surface_color_standard = input_props.va_color_standard; |
| params->output_color_standard = output_props.va_color_standard; |
| |
| #if VA_CHECK_VERSION(1, 1, 0) |
| params->input_color_properties = (VAProcColorProperties) { |
| .chroma_sample_location = input_props.va_chroma_sample_location, |
| .color_range = input_props.va_color_range, |
| #if VA_CHECK_VERSION(1, 3, 0) |
| .colour_primaries = input_props.color_primaries, |
| .transfer_characteristics = input_props.color_trc, |
| .matrix_coefficients = input_props.colorspace, |
| #endif |
| }; |
| params->output_color_properties = (VAProcColorProperties) { |
| .chroma_sample_location = output_props.va_chroma_sample_location, |
| .color_range = output_props.va_color_range, |
| #if VA_CHECK_VERSION(1, 3, 0) |
| .colour_primaries = output_props.color_primaries, |
| .transfer_characteristics = output_props.color_trc, |
| .matrix_coefficients = output_props.colorspace, |
| #endif |
| }; |
| #endif |
| |
| return 0; |
| } |
| |
| int ff_vaapi_vpp_init_params(AVFilterContext *avctx, |
| VAProcPipelineParameterBuffer *params, |
| const AVFrame *input_frame, |
| AVFrame *output_frame) |
| { |
| VAAPIVPPContext *ctx = avctx->priv; |
| VASurfaceID input_surface; |
| int err; |
| |
| ctx->input_region = (VARectangle) { |
| .x = input_frame->crop_left, |
| .y = input_frame->crop_top, |
| .width = input_frame->width - |
| (input_frame->crop_left + input_frame->crop_right), |
| .height = input_frame->height - |
| (input_frame->crop_top + input_frame->crop_bottom), |
| }; |
| output_frame->crop_top = 0; |
| output_frame->crop_bottom = 0; |
| output_frame->crop_left = 0; |
| output_frame->crop_right = 0; |
| |
| input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3], |
| |
| *params = (VAProcPipelineParameterBuffer) { |
| .surface = input_surface, |
| .surface_region = &ctx->input_region, |
| .output_region = NULL, |
| .output_background_color = VAAPI_VPP_BACKGROUND_BLACK, |
| .pipeline_flags = 0, |
| .filter_flags = VA_FRAME_PICTURE, |
| |
| // Filter and reference data filled by the filter itself. |
| |
| #if VA_CHECK_VERSION(1, 1, 0) |
| .rotation_state = VA_ROTATION_NONE, |
| .mirror_state = VA_MIRROR_NONE, |
| #endif |
| }; |
| |
| err = vaapi_vpp_colour_properties(avctx, params, |
| input_frame, output_frame); |
| if (err < 0) |
| return err; |
| |
| return 0; |
| } |
| |
| int ff_vaapi_vpp_make_param_buffers(AVFilterContext *avctx, |
| int type, |
| const void *data, |
| size_t size, |
| int count) |
| { |
| VAStatus vas; |
| VABufferID buffer; |
| VAAPIVPPContext *ctx = avctx->priv; |
| |
| av_assert0(ctx->nb_filter_buffers + 1 <= VAProcFilterCount); |
| |
| vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, |
| type, size, count, (void*)data, &buffer); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to create parameter " |
| "buffer (type %d): %d (%s).\n", |
| type, vas, vaErrorStr(vas)); |
| return AVERROR(EIO); |
| } |
| |
| ctx->filter_buffers[ctx->nb_filter_buffers++] = buffer; |
| |
| av_log(avctx, AV_LOG_DEBUG, "Param buffer (type %d, %zu bytes, count %d) " |
| "is %#x.\n", type, size, count, buffer); |
| return 0; |
| } |
| |
| |
| int ff_vaapi_vpp_render_picture(AVFilterContext *avctx, |
| VAProcPipelineParameterBuffer *params, |
| AVFrame *output_frame) |
| { |
| VAAPIVPPContext *ctx = avctx->priv; |
| VASurfaceID output_surface; |
| VABufferID params_id; |
| VAStatus vas; |
| int err; |
| |
| output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3]; |
| |
| vas = vaBeginPicture(ctx->hwctx->display, |
| ctx->va_context, output_surface); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: " |
| "%d (%s).\n", vas, vaErrorStr(vas)); |
| err = AVERROR(EIO); |
| goto fail; |
| } |
| |
| vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, |
| VAProcPipelineParameterBufferType, |
| sizeof(*params), 1, params, ¶ms_id); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer: " |
| "%d (%s).\n", vas, vaErrorStr(vas)); |
| err = AVERROR(EIO); |
| goto fail_after_begin; |
| } |
| av_log(avctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n", |
| params_id); |
| |
| vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, |
| ¶ms_id, 1); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to render parameter buffer: " |
| "%d (%s).\n", vas, vaErrorStr(vas)); |
| err = AVERROR(EIO); |
| goto fail_after_begin; |
| } |
| |
| vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to start picture processing: " |
| "%d (%s).\n", vas, vaErrorStr(vas)); |
| err = AVERROR(EIO); |
| goto fail_after_render; |
| } |
| |
| if (CONFIG_VAAPI_1 || ctx->hwctx->driver_quirks & |
| AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) { |
| vas = vaDestroyBuffer(ctx->hwctx->display, params_id); |
| if (vas != VA_STATUS_SUCCESS) { |
| av_log(avctx, AV_LOG_ERROR, "Failed to free parameter buffer: " |
| "%d (%s).\n", vas, vaErrorStr(vas)); |
| // And ignore. |
| } |
| } |
| |
| return 0; |
| |
| // We want to make sure that if vaBeginPicture has been called, we also |
| // call vaRenderPicture and vaEndPicture. These calls may well fail or |
| // do something else nasty, but once we're in this failure case there |
| // isn't much else we can do. |
| fail_after_begin: |
| vaRenderPicture(ctx->hwctx->display, ctx->va_context, ¶ms_id, 1); |
| fail_after_render: |
| vaEndPicture(ctx->hwctx->display, ctx->va_context); |
| fail: |
| return err; |
| } |
| |
| void ff_vaapi_vpp_ctx_init(AVFilterContext *avctx) |
| { |
| int i; |
| VAAPIVPPContext *ctx = avctx->priv; |
| |
| ctx->va_config = VA_INVALID_ID; |
| ctx->va_context = VA_INVALID_ID; |
| ctx->valid_ids = 1; |
| |
| for (i = 0; i < VAProcFilterCount; i++) |
| ctx->filter_buffers[i] = VA_INVALID_ID; |
| ctx->nb_filter_buffers = 0; |
| } |
| |
| void ff_vaapi_vpp_ctx_uninit(AVFilterContext *avctx) |
| { |
| VAAPIVPPContext *ctx = avctx->priv; |
| if (ctx->valid_ids && ctx->pipeline_uninit) |
| ctx->pipeline_uninit(avctx); |
| |
| av_buffer_unref(&ctx->input_frames_ref); |
| av_buffer_unref(&ctx->device_ref); |
| } |