On Sat, 30 Jan 2016 22:12:36 +0000 Mark Thompson <s...@jkqxz.net> wrote:
> --- > Makefile | 1 + > configure | 5 + > ffmpeg.c | 6 + > ffmpeg.h | 9 + > ffmpeg_opt.c | 38 +++- > ffmpeg_vaapi.c | 642 > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 6 files changed, 700 insertions(+), 1 deletion(-) > create mode 100644 ffmpeg_vaapi.c > > diff --git a/Makefile b/Makefile > index e484249..b2173bb 100644 > --- a/Makefile > +++ b/Makefile > @@ -37,6 +37,7 @@ OBJS-ffmpeg-$(CONFIG_VDA) += ffmpeg_videotoolbox.o > endif > OBJS-ffmpeg-$(CONFIG_VIDEOTOOLBOX) += ffmpeg_videotoolbox.o > OBJS-ffmpeg-$(CONFIG_LIBMFX) += ffmpeg_qsv.o > +OBJS-ffmpeg-$(CONFIG_VAAPI_RECENT) += ffmpeg_vaapi.o > OBJS-ffserver += ffserver_config.o > > TESTTOOLS = audiogen videogen rotozoom tiny_psnr tiny_ssim base64 > diff --git a/configure b/configure > index e7f53af..d429cbb 100755 > --- a/configure > +++ b/configure > @@ -1965,6 +1965,7 @@ HAVE_LIST=" > section_data_rel_ro > texi2html > threads > + vaapi_drm > vaapi_x11 > vdpau_x11 > winrt > @@ -5777,6 +5778,10 @@ enabled vaapi && enabled xlib && > check_lib2 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 && > enable vaapi_x11 > > +enabled vaapi && > + check_lib2 "va/va.h va/va_drm.h" vaGetDisplayDRM -lva -lva-drm && > + enable vaapi_drm > + > enabled vdpau && > check_cpp_condition vdpau/vdpau.h "defined > VDP_DECODER_PROFILE_MPEG4_PART2_ASP" || > disable vdpau > diff --git a/ffmpeg.c b/ffmpeg.c > index a5ec3c3..6616a07 100644 > --- a/ffmpeg.c > +++ b/ffmpeg.c > @@ -2603,6 +2603,12 @@ static int init_output_stream(OutputStream *ost, char > *error, int error_len) > !av_dict_get(ost->encoder_opts, "ab", NULL, 0)) > av_dict_set(&ost->encoder_opts, "b", "128000", 0); > > +#if CONFIG_VAAPI_RECENT > + if(ost->enc->type == AVMEDIA_TYPE_VIDEO && > + strstr(ost->enc->name, "vaapi")) > + vaapi_hardware_set_options(&ost->encoder_opts); > +#endif > + > if ((ret = avcodec_open2(ost->enc_ctx, codec, &ost->encoder_opts)) < > 0) { > if (ret == AVERROR_EXPERIMENTAL) > abort_codec_experimental(codec, 1); > diff --git a/ffmpeg.h b/ffmpeg.h > index 20322b0..85173a8 100644 > --- a/ffmpeg.h > +++ b/ffmpeg.h > @@ -65,6 +65,7 @@ enum HWAccelID { > HWACCEL_VDA, > HWACCEL_VIDEOTOOLBOX, > HWACCEL_QSV, > + HWACCEL_VAAPI, > }; > > typedef struct HWAccel { > @@ -126,6 +127,8 @@ typedef struct OptionsContext { > int nb_hwaccels; > SpecifierOpt *hwaccel_devices; > int nb_hwaccel_devices; > + SpecifierOpt *hwaccel_output_formats; > + int nb_hwaccel_output_formats; > SpecifierOpt *autorotate; > int nb_autorotate; > > @@ -325,6 +328,7 @@ typedef struct InputStream { > /* hwaccel options */ > enum HWAccelID hwaccel_id; > char *hwaccel_device; > + enum AVPixelFormat hwaccel_output_format; > > /* hwaccel context */ > enum HWAccelID active_hwaccel_id; > @@ -540,6 +544,7 @@ extern AVIOContext *progress_avio; > extern float max_error_rate; > extern int vdpau_api_ver; > extern char *videotoolbox_pixfmt; > +extern int hwaccel_lax_profile_check; > > extern const AVIOInterruptCB int_cb; > > @@ -577,5 +582,9 @@ int vda_init(AVCodecContext *s); > int videotoolbox_init(AVCodecContext *s); > int qsv_init(AVCodecContext *s); > int qsv_transcode_init(OutputStream *ost); > +int vaapi_decode_init(AVCodecContext *s); > + > +int vaapi_hardware_init(const char *device); > +int vaapi_hardware_set_options(AVDictionary **dict); > > #endif /* FFMPEG_H */ > diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c > index 669976b..d65293e 100644 > --- a/ffmpeg_opt.c > +++ b/ffmpeg_opt.c > @@ -82,8 +82,12 @@ const HWAccel hwaccels[] = { > #if CONFIG_LIBMFX > { "qsv", qsv_init, HWACCEL_QSV, AV_PIX_FMT_QSV }, > #endif > +#if CONFIG_VAAPI_RECENT > + { "vaapi", vaapi_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI }, > +#endif > { 0 }, > }; > +int hwaccel_lax_profile_check = 0; > > char *vstats_filename; > char *sdp_filename; > @@ -442,6 +446,15 @@ static int opt_sdp_file(void *optctx, const char *opt, > const char *arg) > return 0; > } > > +#if CONFIG_VAAPI_RECENT > +static int opt_vaapi(void *optctx, const char *opt, const char *arg) > +{ > + if(vaapi_hardware_init(arg)) > + exit_program(1); > + return 0; > +} > +#endif > + > /** > * Parse a metadata specifier passed as 'arg' parameter. > * @param arg metadata string to parse > @@ -633,7 +646,8 @@ static void add_input_streams(OptionsContext *o, > AVFormatContext *ic) > AVStream *st = ic->streams[i]; > AVCodecContext *dec = st->codec; > InputStream *ist = av_mallocz(sizeof(*ist)); > - char *framerate = NULL, *hwaccel = NULL, *hwaccel_device = NULL; > + char *framerate = NULL; > + char *hwaccel = NULL, *hwaccel_device = NULL, *hwaccel_output_format > = NULL; > char *codec_tag = NULL; > char *next; > char *discard_str = NULL; > @@ -753,6 +767,18 @@ static void add_input_streams(OptionsContext *o, > AVFormatContext *ic) > if (!ist->hwaccel_device) > exit_program(1); > } > + > + MATCH_PER_STREAM_OPT(hwaccel_output_formats, str, > hwaccel_output_format, ic, st); > + if (hwaccel_output_format) { > + ist->hwaccel_output_format = > av_get_pix_fmt(hwaccel_output_format); > + if (ist->hwaccel_output_format == AV_PIX_FMT_NONE) { > + av_log(NULL, AV_LOG_FATAL, "Unrecognised hwaccel output " > + "format: %s", hwaccel_output_format); > + } > + } else { > + ist->hwaccel_output_format = AV_PIX_FMT_NONE; > + } > + > ist->hwaccel_pix_fmt = AV_PIX_FMT_NONE; > > break; > @@ -3351,6 +3377,9 @@ const OptionDef options[] = { > { "hwaccel_device", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | > OPT_SPEC | OPT_INPUT, > { .off = OFFSET(hwaccel_devices) }, > "select a device for HW acceleration", "devicename" }, > + { "hwaccel_output_format", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT > | > + OPT_SPEC | OPT_INPUT, > { .off = OFFSET(hwaccel_output_formats) }, > + "select output format used with HW accelerated decoding", "format" }, > #if HAVE_VDPAU_X11 > { "vdpau_api_ver", HAS_ARG | OPT_INT | OPT_EXPERT, { &vdpau_api_ver }, > "" }, > #endif > @@ -3359,6 +3388,8 @@ const OptionDef options[] = { > #endif > { "hwaccels", OPT_EXIT, > { .func_arg = show_hwaccels }, > "show available HW acceleration methods" }, > + { "hwaccel_lax_profile_check", OPT_BOOL | OPT_EXPERT, { > &hwaccel_lax_profile_check}, > + "attempt to decode anyway if HW accelerated decoder's supported > profiles do not exactly match the stream" }, > { "autorotate", HAS_ARG | OPT_BOOL | OPT_SPEC | > OPT_EXPERT | OPT_INPUT, > { .off = OFFSET(autorotate) }, > "automatically insert correct rotate filters" }, > @@ -3445,5 +3476,10 @@ const OptionDef options[] = { > { "dn", OPT_BOOL | OPT_VIDEO | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { > .off = OFFSET(data_disable) }, > "disable data" }, > > +#if CONFIG_VAAPI_RECENT > + { "vaapi", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vaapi }, > + "set VAAPI hardware context (DRM path or X11 display name)" }, > +#endif > + > { NULL, }, > }; > diff --git a/ffmpeg_vaapi.c b/ffmpeg_vaapi.c > new file mode 100644 > index 0000000..a6a2018 > --- /dev/null > +++ b/ffmpeg_vaapi.c > @@ -0,0 +1,642 @@ > +/* > + * VAAPI helper for hardware-accelerated decoding. > + * > + * Copyright (C) 2016 Mark Thompson <m...@jkqxz.net> > + * > + * 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 <fcntl.h> > +#include <pthread.h> > +#include <unistd.h> > + > +#include "ffmpeg.h" > + > +#include "libavutil/avassert.h" > +#include "libavutil/avconfig.h" > +#include "libavutil/buffer.h" > +#include "libavutil/frame.h" > +#include "libavutil/imgutils.h" > +#include "libavutil/opt.h" > +#include "libavutil/pixfmt.h" > + > +#include "libavcodec/vaapi.h" > +#include "libavcodec/vaapi_support.h" > + > +#include <va/va_x11.h> > +#include <va/va_drm.h> > + > + > +static AVClass vaapi_class = { > + .class_name = "vaapi_ffmpeg", > + .item_name = av_default_item_name, > + .version = LIBAVUTIL_VERSION_INT, > +}; > + > + > +#define DEFAULT_SURFACES 20 > + > +typedef struct VAAPIDecoderContext { > + const AVClass *class; > + > + AVVAAPIHardwareContext *hardware_context; > + AVVAAPIPipelineConfig config; > + AVVAAPIPipelineContext codec; > + AVVAAPISurfaceConfig output; > + AVVAAPISurfacePool pool; > + > + enum AVPixelFormat output_format; > + int codec_initialised; > +} VAAPIDecoderContext; > + > + > +static int vaapi_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) > +{ > + InputStream *ist = s->opaque; > + VAAPIDecoderContext *ctx = ist->hwaccel_ctx; > + > + av_assert0(frame->format == AV_PIX_FMT_VAAPI); > + > + av_vaapi_surface_pool_get(&ctx->pool, frame); > + > + av_log(ctx, AV_LOG_DEBUG, "Decoder given reference to surface %#x.\n", > + (VASurfaceID)frame->data[3]); > + > + return 0; > +} > + > +static int vaapi_retrieve_data(AVCodecContext *avctx, AVFrame *input_image) > +{ > + InputStream *ist = avctx->opaque; > + VAAPIDecoderContext *ctx = ist->hwaccel_ctx; > + AVVAAPISurfaceConfig *output = &ctx->output; > + AVFrame *output_image; > + int err; > + > + av_log(ctx, AV_LOG_DEBUG, "Decoder output in surface %#x.\n", > + (VASurfaceID)input_image->data[3]); > + > + if(ctx->output_format == AV_PIX_FMT_VAAPI) { > + // Nothing to do, the hardware surface is passed to the output. > + goto done; > + } > + > + output_image = av_frame_alloc(); > + if(!output_image) > + return AVERROR(ENOMEM); > + > + output_image->format = output->av_format; > + output_image->width = input_image->width; > + output_image->height = input_image->height; > + > + err = av_frame_get_buffer(output_image, 32); > + if(err < 0) { > + av_log(ctx, AV_LOG_ERROR, "Failed to get buffer for output frame: " > + "%d (%s).\n", err, av_err2str(err)); > + return err; > + } > + > + err = av_vaapi_copy_from_surface(output_image, input_image); > + if(err < 0) { > + av_log(ctx, AV_LOG_ERROR, "Failed to copy from output surface: " > + "%d (%s).\n", err, av_err2str(err)); > + return err; > + } > + > + av_frame_copy_props(output_image, input_image); > + av_frame_unref(input_image); > + av_frame_move_ref(input_image, output_image); > + > + done: > + av_log(ctx, AV_LOG_DEBUG, "Decoder output: %s, %ux%u.\n", > + av_get_pix_fmt_name(input_image->format), > + input_image->width, input_image->height); > + return 0; > +} > + > + > +static const struct { > + enum AVCodecID codec_id; > + int codec_profile; > + VAProfile va_profile; > +} vaapi_profile_map[] = { > +#define MAP(c, p, v) { AV_CODEC_ID_ ## c, FF_PROFILE_ ## p, VAProfile ## v } > + MAP(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple ), > + MAP(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main ), > + MAP(H263, UNKNOWN, H263Baseline), > + MAP(MPEG4, MPEG4_SIMPLE, MPEG4Simple ), > + MAP(MPEG4, MPEG4_ADVANCED_SIMPLE, > + MPEG4AdvancedSimple), > + MAP(MPEG4, MPEG4_MAIN, MPEG4Main ), > + MAP(H264, H264_CONSTRAINED_BASELINE, > + H264ConstrainedBaseline), > + MAP(H264, H264_BASELINE, H264Baseline), > + MAP(H264, H264_MAIN, H264Main ), > + MAP(H264, H264_HIGH, H264High ), > +#if CONFIG_HEVC_VAAPI_HWACCEL > + MAP(HEVC, HEVC_MAIN, HEVCMain ), > +#endif > + MAP(WMV3, VC1_SIMPLE, VC1Simple ), > + MAP(WMV3, VC1_MAIN, VC1Main ), > + MAP(WMV3, VC1_COMPLEX, VC1Advanced ), > + MAP(WMV3, VC1_ADVANCED, VC1Advanced ), > + MAP(VC1, VC1_SIMPLE, VC1Simple ), > + MAP(VC1, VC1_MAIN, VC1Main ), > + MAP(VC1, VC1_COMPLEX, VC1Advanced ), > + MAP(VC1, VC1_ADVANCED, VC1Advanced ), > +#if CONFIG_VP9_VAAPI_HWACCEL > + MAP(VP9, VP9_0, VP9Profile0 ), > +#endif > +#undef MAP > +}; > + > +static int vaapi_build_decoder_config(VAAPIDecoderContext *ctx, > + AVVAAPIPipelineConfig *config, > + AVVAAPISurfaceConfig *output, > + AVCodecContext *avctx, > + int fallback_allowed) > +{ > + VAStatus vas; > + int i; > + int loglevel = fallback_allowed ? AV_LOG_VERBOSE : AV_LOG_ERROR; > + > + memset(config, 0, sizeof(*config)); > + > + // Pick codec profile to use. > + { > + VAProfile profile; > + int profile_count, exact_match, alt_profile; > + VAProfile *profile_list; > + > + profile = VAProfileNone; > + exact_match = 0; > + > + for(i = 0; i < FF_ARRAY_ELEMS(vaapi_profile_map); i++) { > + if(avctx->codec_id != vaapi_profile_map[i].codec_id) > + continue; > + profile = vaapi_profile_map[i].va_profile; > + if(avctx->profile == vaapi_profile_map[i].codec_profile) { > + exact_match = 1; > + break; > + } > + alt_profile = vaapi_profile_map[i].codec_profile; > + } > + if(profile == VAProfileNone) { > + av_log(ctx, loglevel, "VAAPI does not support codec %s.\n", > + avcodec_get_name(avctx->codec_id)); > + return AVERROR(EINVAL); > + } > + if(!exact_match) { > + if(fallback_allowed || !hwaccel_lax_profile_check) { > + av_log(ctx, loglevel, "VAAPI does not support codec %s " > + "profile %d.\n", avcodec_get_name(avctx->codec_id), > + avctx->profile); > + if(!fallback_allowed) { > + av_log(ctx, AV_LOG_WARNING, "If you want attempt > decoding " > + "anyway with a possibly-incompatible profile, add > " > + "the option -hwaccel_lax_profile_check.\n"); > + } > + return AVERROR(EINVAL); > + } else { > + av_log(ctx, AV_LOG_WARNING, "VAAPI does not support codec %s > " > + "profile %d: trying instead with profile %d.\n", > + avcodec_get_name(avctx->codec_id), > + avctx->profile, alt_profile); > + av_log(ctx, AV_LOG_WARNING, "This may not fail or give " > + "incorrect results, depending on your hardware.\n"); > + } > + } > + > + profile_count = vaMaxNumProfiles(ctx->hardware_context->display); > + profile_list = av_calloc(profile_count, sizeof(VAProfile)); > + if(!profile_list) > + return AVERROR(ENOMEM); > + > + vas = vaQueryConfigProfiles(ctx->hardware_context->display, > + profile_list, &profile_count); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(ctx, loglevel, "Failed to query profiles: %d (%s).\n", > + vas, vaErrorStr(vas)); > + av_free(profile_list); > + return AVERROR_EXTERNAL; > + } > + > + for(i = 0; i < profile_count; i++) { > + if(profile_list[i] == profile) > + break; > + } > + if(i >= profile_count) > + profile = VAProfileNone; > + > + av_free(profile_list); > + > + if(profile == VAProfileNone) { > + av_log(ctx, loglevel, "Hardware does not support codec: " > + "%s / %d.\n", avcodec_get_name(avctx->codec_id), > + avctx->profile); > + return AVERROR(EINVAL); > + } else { > + av_log(ctx, AV_LOG_DEBUG, "Hardware supports codec: " > + "%s / %d -> VAProfile %d.\n", > + avcodec_get_name(avctx->codec_id), avctx->profile, > + profile); > + } > + > + config->profile = profile; > + config->entrypoint = VAEntrypointVLD; > + } > + > + // Decide on the internal chroma format. > + { > + VAConfigAttrib attr; > + > + // Currently the software only supports YUV420, so just make sure > + // that the hardware we have does too. > + > + memset(&attr, 0, sizeof(attr)); > + attr.type = VAConfigAttribRTFormat; > + vas = vaGetConfigAttributes(ctx->hardware_context->display, > config->profile, > + VAEntrypointVLD, &attr, 1); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(ctx, loglevel, "Failed to fetch config attributes: " > + "%d (%s).\n", vas, vaErrorStr(vas)); > + return AVERROR_EXTERNAL; > + } > + if(!(attr.value & VA_RT_FORMAT_YUV420)) { > + av_log(ctx, loglevel, "Hardware does not support required " > + "chroma format (%#x).\n", attr.value); > + return AVERROR(EINVAL); > + } > + > + output->rt_format = VA_RT_FORMAT_YUV420; > + } > + > + // Decide on the image format. > + if(ctx->output_format == AV_PIX_FMT_VAAPI) { > + // We are going to be passing through a VAAPI surface directly: > + // they will stay as whatever opaque internal format for that time, > + // and we never need to make VAImages from them. > + > + av_log(ctx, AV_LOG_DEBUG, "Using VAAPI opaque output format.\n"); > + > + output->av_format = AV_PIX_FMT_VAAPI; > + memset(&output->image_format, 0, sizeof(output->image_format)); > + > + } else { > + // Use the user-specified format (-hwaccel_output_format) if given, > + // otherwise try to get NV12 and if not possible just use the first > + // known one in the list (random something, the user receiving it > will > + // just have to cope - hopefully a swscale instance can fix it up). > + > + int default_format = AV_PIX_FMT_NV12; > + int image_format_count; > + VAImageFormat *image_format_list; > + int pix_fmt; > + > + image_format_count = > vaMaxNumImageFormats(ctx->hardware_context->display); > + image_format_list = av_calloc(image_format_count, > + sizeof(VAImageFormat)); > + if(!image_format_list) > + return AVERROR(ENOMEM); > + > + vas = vaQueryImageFormats(ctx->hardware_context->display, > image_format_list, > + &image_format_count); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(ctx, loglevel, "Failed to query image formats: " > + "%d (%s).\n", vas, vaErrorStr(vas)); > + return AVERROR_EXTERNAL; > + } > + > + if(ctx->output_format != AV_PIX_FMT_NONE) { > + for(i = 0; i < image_format_count; i++) { > + pix_fmt = av_vaapi_pix_fmt(image_format_list[i].fourcc); > + if(pix_fmt == AV_PIX_FMT_NONE) > + continue; > + if(pix_fmt == ctx->output_format) > + break; > + } > + if(i < image_format_count) { > + av_log(ctx, AV_LOG_DEBUG, "Using desired output format %s " > + "(%#x).\n", av_get_pix_fmt_name(pix_fmt), > + image_format_list[i].fourcc); > + } else { > + av_log(ctx, loglevel, "Unable to use specified output " > + "format %s.", av_get_pix_fmt_name(pix_fmt)); > + av_free(image_format_list); > + return AVERROR(EINVAL); > + } > + } else { > + for(i = 0; i < image_format_count; i++) { > + pix_fmt = av_vaapi_pix_fmt(image_format_list[i].fourcc); > + if(pix_fmt == default_format) > + break; > + } > + if(i < image_format_count) { > + av_log(ctx, AV_LOG_DEBUG, "Using default output format %s " > + "(%#x).\n", av_get_pix_fmt_name(pix_fmt), > + image_format_list[i].fourcc); > + } else { > + for(i = 0; i < image_format_count; i++) { > + pix_fmt = av_vaapi_pix_fmt(image_format_list[i].fourcc); > + if(pix_fmt != AV_PIX_FMT_NONE) > + break; > + } > + if(i >= image_format_count) { > + av_log(ctx, loglevel, "No supported output format.\n"); > + av_free(image_format_list); > + return AVERROR(EINVAL); > + } > + av_log(ctx, AV_LOG_DEBUG, "Using random output format %s " > + "(%#x).\n", av_get_pix_fmt_name(pix_fmt), > + image_format_list[i].fourcc); > + } > + } > + > + output->av_format = pix_fmt; > + memcpy(&output->image_format, &image_format_list[i], > + sizeof(VAImageFormat)); > + > + av_free(image_format_list); > + } > + > + // Decide how many reference frames we need. > + { > + // We should be able to do this in a more sensible way by looking > + // at how many reference frames the input stream requires. > + //output->count = DEFAULT_SURFACES; If this is disabled, where does the default count come from? I don't think it's possible to get the value from the stream at this point, only when the per-hwaccel entrypoints in libavcodec are actually called. (Not sure if I commented on this before.) > + } > + > + // Test whether the width and height are within allowable limits. > + { > + // It would be nice if we could check this here before starting > + // anything, but unfortunately we need an active VA config to test. > + // Hence just copy. If it isn't supproted, the pipeline > + // initialisation below will fail below instead. > + config->width = output->width = avctx->coded_width; > + config->height = output->height = avctx->coded_height; > + } > + > + return 0; > +} > + > +static void vaapi_decode_uninit(AVCodecContext *avctx) > +{ > + InputStream *ist = avctx->opaque; > + VAAPIDecoderContext *ctx = ist->hwaccel_ctx; > + > + if(ctx) { > + if(ctx->codec_initialised) { > + av_vaapi_lock_hardware_context(ctx->hardware_context); > + av_vaapi_surface_pool_uninit(&ctx->pool); > + av_vaapi_pipeline_uninit(&ctx->codec); > + av_vaapi_unlock_hardware_context(ctx->hardware_context); > + ctx->codec_initialised = 0; > + } > + > + av_free(ctx); > + } > + > + > + ist->hwaccel_ctx = 0; > + ist->hwaccel_uninit = 0; > + ist->hwaccel_get_buffer = 0; > + ist->hwaccel_retrieve_data = 0; > +} > + > + > +AVVAAPIHardwareContext *vaapi_context; Should probably be at the start of the file. And be made static. > + > +int vaapi_decode_init(AVCodecContext *avctx) > +{ > + InputStream *ist = avctx->opaque; > + VAAPIDecoderContext *ctx; > + int err; > + int loglevel = (ist->hwaccel_id != HWACCEL_VAAPI ? AV_LOG_VERBOSE : > AV_LOG_ERROR); > + > + // We have -hwaccel without -vaapi, so just initialise here with the > + // device passed as -hwaccel_device (if -vaapi was passed, it will > + // always have been called before now). > + if(!vaapi_context) { > + err = vaapi_hardware_init(ist->hwaccel_device); > + if(err < 0) > + return err; > + } > + > + if(ist->hwaccel_ctx) { > + ctx = ist->hwaccel_ctx; > + > + av_vaapi_lock_hardware_context(ctx->hardware_context); > + > + err = av_vaapi_pipeline_uninit(&ctx->codec); > + if(err < 0) { > + av_log(ctx, loglevel, "Unable to reinit; failed to uninit " > + "old codec context: %d (%s).\n", err, av_err2str(err)); > + goto fail; > + } > + > + err = av_vaapi_surface_pool_uninit(&ctx->pool); > + if(err < 0) { > + av_log(ctx, loglevel, "Unable to reinit; failed to uninit " > + "old surface pool: %d (%s).\n", err, av_err2str(err)); > + goto fail; > + } > + > + } else { > + if(vaapi_context->decoder_pipeline_config_id != VA_INVALID_ID) { > + av_log(0, loglevel, "Multiple simultaneous VAAPI decode " > + "pipelines are not supported!\n"); > + return AVERROR(EINVAL); > + } > + > + ctx = av_mallocz(sizeof(*ctx)); > + if(!ctx) > + return AVERROR(ENOMEM); > + ctx->class = &vaapi_class; > + > + ctx->hardware_context = vaapi_context; > + > + av_vaapi_lock_hardware_context(ctx->hardware_context); > + } > + > + ctx->output_format = ist->hwaccel_output_format; > + > + err = vaapi_build_decoder_config(ctx, &ctx->config, &ctx->output, avctx, > + ist->hwaccel_id != HWACCEL_VAAPI); > + if(err < 0) { > + av_log(ctx, loglevel, "No supported configuration for this codec."); > + goto fail; > + } > + > + avctx->pix_fmt = ctx->output_format; > + > + err = av_vaapi_surface_pool_init(&ctx->pool, ctx->hardware_context, > + &ctx->output, DEFAULT_SURFACES); > + if(err < 0) { > + av_log(ctx, loglevel, "Failed to initialise surface pool: " > + "%d (%s).\n", err, av_err2str(err)); > + goto fail; > + } > + > + err = av_vaapi_pipeline_init(&ctx->codec, ctx->hardware_context, > + &ctx->config, &ctx->pool); > + if(err < 0) { > + av_log(ctx, loglevel, "Failed to initialise codec context: " > + "%d (%s).\n", err, av_err2str(err)); > + goto fail; > + } > + ctx->codec_initialised = 1; > + > + av_vaapi_unlock_hardware_context(ctx->hardware_context); > + > + av_log(ctx, AV_LOG_DEBUG, "VAAPI decoder (re)init complete.\n"); > + > + ist->hwaccel_ctx = ctx; > + ist->hwaccel_uninit = vaapi_decode_uninit; > + ist->hwaccel_get_buffer = vaapi_get_buffer; > + ist->hwaccel_retrieve_data = vaapi_retrieve_data; > + > + avctx->hwaccel_context = ctx->hardware_context; > + > + ctx->hardware_context->decoder_pipeline_config_id = > ctx->codec.config_id; > + ctx->hardware_context->decoder_pipeline_context_id = > ctx->codec.context_id; > + > + return 0; > + > + fail: > + av_vaapi_unlock_hardware_context(ctx->hardware_context); > + vaapi_decode_uninit(avctx); > + return err; > +} > + > +int vaapi_hardware_set_options(AVDictionary **dict) > +{ > + av_log(0, AV_LOG_DEBUG, "Setting VAAPI hardware_context.\n"); > + av_dict_set_int(dict, "hardware_context", (int64_t)vaapi_context, 0); > + return 0; > +} > + > + > +static AVClass *vaapi_log = &vaapi_class; > +static pthread_mutex_t vaapi_mutex; > + > +static void vaapi_mutex_lock(void *ignored) > +{ > + pthread_mutex_lock(&vaapi_mutex); > +} > + > +static void vaapi_mutex_unlock(void *ignored) > +{ > + pthread_mutex_unlock(&vaapi_mutex); > +} > + > +int vaapi_hardware_init(const char *device) > +{ > + VADisplay display; > + int drm_fd; > + Display *x11_display; > + VAStatus vas; > + int major, minor, err; > + pthread_mutexattr_t mutex_attr; > + > + err = pthread_mutexattr_init(&mutex_attr); > + if(!err) > + err = pthread_mutexattr_settype(&mutex_attr, > PTHREAD_MUTEX_RECURSIVE); > + if(!err) > + err = pthread_mutex_init(&vaapi_mutex, &mutex_attr); > + pthread_mutexattr_destroy(&mutex_attr); > + if(err) { > + err = AVERROR(err); > + av_log(&vaapi_log, AV_LOG_ERROR, "Failed to initialise VAAPI mutex: " > + "%s.\n", av_err2str(err)); > + return err; > + } > + > + display = 0; > + > +#if HAVE_VAAPI_X11 > + if(!display) { > + // Try to open the device as an X11 display. > + x11_display = XOpenDisplay(device); > + if(!x11_display) { > + av_log(&vaapi_log, AV_LOG_WARNING, "Cannot open X11 display " > + "%s.\n", XDisplayName(device)); > + } else { > + display = vaGetDisplay(x11_display); > + if(!display) { > + av_log(&vaapi_log, AV_LOG_WARNING, "Cannot open a VA display > " > + "from X11 display %s.\n", XDisplayName(device)); > + XCloseDisplay(x11_display); > + } else { > + av_log(&vaapi_log, AV_LOG_VERBOSE, "Opened VA display via " > + "X11 display %s.\n", XDisplayName(device)); > + } > + } > + } > +#endif > + > +#if HAVE_VAAPI_DRM > + if(!display && device) { > + // Try to open the device as a DRM path. > + drm_fd = open(device, O_RDWR); > + if(drm_fd < 0) { > + err = errno; > + av_log(&vaapi_log, AV_LOG_WARNING, "Cannot open DRM device > %s.\n", > + device); > + } else { > + display = vaGetDisplayDRM(drm_fd); > + if(!display) { > + av_log(&vaapi_log, AV_LOG_WARNING, "Cannot open a VA display > " > + "from DRM device %s.\n", device); > + close(drm_fd); > + } else { > + av_log(&vaapi_log, AV_LOG_VERBOSE, "Opened VA display via " > + "DRM device %s.\n", device); > + } > + } > + } > +#endif > + > + if(!display) { > + av_log(&vaapi_log, AV_LOG_ERROR, "No VA display found for " > + "device %s.\n", device); > + return AVERROR(EINVAL); > + } > + > + vas = vaInitialize(display, &major, &minor); > + if(vas != VA_STATUS_SUCCESS) { > + av_log(&vaapi_log, AV_LOG_ERROR, "Failed to initialise VAAPI " > + "connection: %d (%s).\n", vas, vaErrorStr(vas)); > + return AVERROR(EINVAL); > + } > + av_log(&vaapi_log, AV_LOG_VERBOSE, "Initialised VAAPI connection: " > + "version %d.%d\n", major, minor); > + > + vaapi_context = av_vaapi_alloc_hardware_context(); > + if(!vaapi_context) > + return AVERROR(ENOMEM); > + > + vaapi_context->display = display; > + vaapi_context->lock = &vaapi_mutex_lock; > + vaapi_context->unlock = &vaapi_mutex_unlock; > + > + vaapi_context->decoder_pipeline_config_id = VA_INVALID_ID; > + vaapi_context->decoder_pipeline_context_id = VA_INVALID_ID; > + > + return 0; > +} _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel