On Sun, 24 Jan 2016 14:20:54 +0000
Mark Thompson <s...@jkqxz.net> wrote:

> ...
> >> +static int vaapi_get_buffer(AVCodecContext *s, AVFrame *frame, int flags)
> >> +{
> >> +    InputStream *ist = s->opaque;
> >> +    VAAPIDecoderContext *ctx = ist->hwaccel_ctx;
> >> +    AVFrame *new_frame;
> >> +
> >> +    av_assert0(frame->format == AV_PIX_FMT_VAAPI);  
> > 
> > This can actually change on software decoding fallback, I think?  
> 
> But this function won't be called in that case?
> 
> It works on the switching cases in FATE, at least: 
> h264-reinit-small_420_8-to-large_444_10 and 
> h264-reinit-small_420_9-to-small_420_8 both pass.

OK, didn't realize ffmpeg.c already dispatches the call is needed.

> >> +
> >> +    new_frame = av_frame_alloc();
> >> +    if(!new_frame)
> >> +        return AVERROR(ENOMEM);
> >> +
> >> +    av_vaapi_surface_pool_get(&ctx->pool, new_frame);
> >> +
> >> +    av_log(ctx, AV_LOG_DEBUG, "Decoder given reference to surface %#x.\n",
> >> +           (VASurfaceID)new_frame->data[3]);
> >> +
> >> +    av_frame_copy_props(new_frame, frame);
> >> +    av_frame_unref(frame);
> >> +    av_frame_move_ref(frame, new_frame);  
> > 
> > What are these acrobatics for? Why not use frame directly?  
> 
> The AVFrame supplied by the decoder already has a lot of the metadata filled 
> in, but the surface pool needs to make a new reference to its own AVFrame.
> 
> Without this, you get display order == decode order (that was fun to track 
> down).

I still don't understand - AVFrames aren't referenced, AVBufferRefs
are.

> ...
> >> +        // If there isn't an exact match, we will choose the last 
> >> (highest)
> >> +        // profile in the mapping table.  This is unfortunate because it
> >> +        // could well be wrong, but it improves compatibility because 
> >> streams
> >> +        // and decoders do not necessarily conform to the requirements.
> >> +        // (Without this, a lot of streams in FATE are discarded when then
> >> +        // could have worked - H.264 extended profile which actually 
> >> decodes
> >> +        // with main, for example.)  
> > 
> > I still think this should not be done by default, and instead a user
> > option should enable it.  
> 
> I guess that works if the error message suggests the option they could try.  
> "-lax-codec-profile-constraints"?  Other hwaccels might want it too.

Yes, something like this. At least vdpau already checks it pretty
strictly (even verifies the level).

> >> +    }
> >> +    return result;
> >> +}
> >> +
> >> +static const struct {
> >> +    enum AVPixelFormat pix_fmt;
> >> +    unsigned int fourcc;
> >> +} vaapi_image_formats[] = {
> >> +    { AV_PIX_FMT_NV12,    VA_FOURCC_NV12 },
> >> +    { AV_PIX_FMT_YUV420P, VA_FOURCC_YV12 },
> >> +    { AV_PIX_FMT_GRAY8,   VA_FOURCC_Y800 },
> >> +};
> >> +
> >> +static int vaapi_get_pix_fmt(unsigned int fourcc)
> >> +{
> >> +    int i;
> >> +    for(i = 0; i < FF_ARRAY_ELEMS(vaapi_image_formats); i++)
> >> +        if(vaapi_image_formats[i].fourcc == fourcc)
> >> +            return vaapi_image_formats[i].pix_fmt;
> >> +    return 0;
> >> +}  
> > 
> > Again wondering why there's not just a single mapping table in the
> > libavutil code. (Just a suggestion; this is ok too.)  
> 
> Ok, that does make sense to go with the mapping now.  I'll change it.
> 
> >> +
> >> +static int vaapi_build_decoder_config(VAAPIDecoderContext *ctx,
> >> +                                      AVVAAPIPipelineConfig *config,
> >> +                                      AVVAAPISurfaceConfig *output,
> >> +                                      AVCodecContext *avctx)
> >> +{
> >> +    VAStatus vas;
> >> +    int i;
> >> +
> >> +    memset(config, 0, sizeof(*config));
> >> +
> >> +    // Pick codec profile to use.
> >> +    {
> >> +        VAProfile profile;
> >> +        int profile_count;
> >> +        VAProfile *profile_list;
> >> +
> >> +        profile = vaapi_find_profile(avctx);
> >> +        if(profile == VAProfileNone) {
> >> +            av_log(ctx, AV_LOG_ERROR, "VAAPI does not support codec 
> >> %s.\n",
> >> +                   avcodec_get_name(avctx->codec_id));
> >> +            return AVERROR(EINVAL);
> >> +        }
> >> +
> >> +        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, AV_LOG_ERROR, "Failed to query profiles: %d 
> >> (%s).\n",
> >> +                   vas, vaErrorStr(vas));
> >> +            av_free(profile_list);
> >> +            return AVERROR(EINVAL);
> >> +        }
> >> +
> >> +        while(1) {
> >> +            for(i = 0; i < profile_count; i++) {
> >> +                if(profile_list[i] == profile)
> >> +                    break;
> >> +            }
> >> +            if(i < profile_count)
> >> +                break;
> >> +
> >> +            if(profile == VAProfileH264Baseline ||
> >> +               profile == VAProfileH264ConstrainedBaseline) {
> >> +                // Promote both of these to improve compatibility.
> >> +                profile = VAProfileH264Main;
> >> +                continue;
> >> +            }
> >> +
> >> +            profile = VAProfileNone;
> >> +            break;
> >> +        }
> >> +
> >> +        av_free(profile_list);
> >> +
> >> +        if(profile == VAProfileNone) {
> >> +            av_log(ctx, AV_LOG_ERROR, "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, AV_LOG_ERROR, "Failed to fetch config attributes: 
> >> "
> >> +                   "%d (%s).\n", vas, vaErrorStr(vas));
> >> +            return AVERROR(EINVAL);
> >> +        }
> >> +        if(!(attr.value & VA_RT_FORMAT_YUV420)) {
> >> +            av_log(ctx, AV_LOG_ERROR, "Hardware does not support required 
> >> "
> >> +                   "chroma format (%#x).\n", attr.value);
> >> +            return AVERROR(EINVAL);
> >> +        }
> >> +
> >> +        output->rt_format = VA_RT_FORMAT_YUV420;  
> > 
> > I'm confused, shouldn't this set it to something retrieved by the
> > function above?  
> 
> Read again - it's just a check, the code errors out on all other 
> possibilities.
> 

Oh, ok.

> This section is only present because more could be added.  Currently the only 
> other possibility in the Intel driver is greyscale for H.264 decoding only, 
> so it didn't seem worth it now.
> 

What about conversion to RGB? I think 10 bit will also have a different
RT format, not sure.

> >> +    }
> >> +
> >> +    // Decide on the image format.
> >> +    if(avctx->pix_fmt == 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 {
> >> +        int image_format_count;
> >> +        VAImageFormat *image_format_list;
> >> +        int pix_fmt;
> >> +
> >> +        // We might want to force a change to the output format here
> >> +        // if we are intending to use VADeriveImage?
> >> +
> >> +        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, AV_LOG_ERROR, "Failed to query image formats: "
> >> +                   "%d (%s).\n", vas, vaErrorStr(vas));
> >> +            return AVERROR(EINVAL);
> >> +        }
> >> +
> >> +        for(i = 0; i < image_format_count; i++) {
> >> +            pix_fmt = vaapi_get_pix_fmt(image_format_list[i].fourcc);
> >> +            if(pix_fmt == AV_PIX_FMT_NONE)
> >> +                continue;
> >> +            if(pix_fmt == avctx->pix_fmt)
> >> +                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 {
> >> +            for(i = 0; i < image_format_count; i++) {
> >> +                pix_fmt = vaapi_get_pix_fmt(image_format_list[i].fourcc);
> >> +                if(pix_fmt != AV_PIX_FMT_NONE)
> >> +                    break;
> >> +            }
> >> +            if(i >= image_format_count) {
> >> +                av_log(ctx, AV_LOG_ERROR, "No supported output format 
> >> found.\n");
> >> +                av_free(image_format_list);
> >> +                return AVERROR(EINVAL);
> >> +            }
> >> +            av_log(ctx, AV_LOG_DEBUG, "Using alternate 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);
> >> +    }  
> > 
> > Seems to pick a format randomly (from the unordered image_format_list).
> > Maybe it'd just be better to force NV12, and if that doesn't work,
> > retry with yuv420p, or so.  
> 
> It picks the format you asked for (avctx->pix_fmt), or if not present takes 
> the first usable one on the list.  In practice you ask for YUV420P and 
> probably get it, with NV12 otherwise.
> 

The first from the list returned by libva is still arbitrary.

What's the format "you asked for"? Does this refer to the -pix_fmt
command line option, or whatever it was?

> >> +
> >> +    // 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;
> >> +    }
> >> +
> >> +    // 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;
> >> +    }  
> > 
> > Forgot what the conclusion about this was last time.  
> 
> Check later, and the pipeline initialisation will fail.  I forgot to add the 
> explicit check (call vaQuerySurfaceAttributes()) to improve the message, I'll 
> do that now.
> 

Hm, ok.

> ...
> There is currently has no uninitialisation on any of this (vaTerminate, close 
> X display or drm fd) because I don't have a sensible place to call it from.  
> I'm hoping that's ok in the ffmpeg application.

Should be fine.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel

Reply via email to