In this case, the user only supplies a device and the frame context
is allocated internally by lavc.
---
On 30/01/17 12:48, wm4 wrote:
> On Mon, 30 Jan 2017 10:58:07 +0000
> Mark Thompson <[email protected]> wrote:
>
>> On 30/01/17 09:32, wm4 wrote:
>>> Last but not least, I'm wondering if it would be possible to not unref
>>> and recreate the hw frames pool if the surface format/size doesn't
>>> change. This would be useful for e.g. seeks. Seeks imply a
>>> avcodec_flush_buffers() call, which destroys the hwdec state and causes
>>> get_format() to be called again.
>>
>> The API requires us to call get_format(), and that implies that we have to
>> clear AVCodecContext.hw_frames_ctx (for current users, that API feature is
>> already fixed).
>>
>> So, this code could cache the frames context that it allocated last time and
>> then reuse it if the user offers the same device and the parameters are
>> identical? I think that would work, but it would also transiently allocate
>> more memory than it otherwise would when things change - is that ok?
>>
>
> Isn't the user disallowed to touch hw_frames_ctx if hw_device_ctx is
> set in the AVCodecContext? Which would mean the vaapi-specific init
> function could unref it before allocating a new frames context.
>
> On the other hand, it might be possible that most potential users of
> this feature will be fine with inefficient seeks. They can always go
> further by implementing it manually by setting get_format if more
> optimization is required. (With the main value of this new API being
> ease of entry, instead of having to implement semi-obscure callbacks.)
How about this? We save a reference to the frames context and then reuse it if
the parameters match. (Not well tested, and not in the seek case: there might
be some obvious error here. I will test it more if people like the idea.)
libavcodec/vaapi_decode.c | 167 +++++++++++++++++++++++++++++++++++++++++-----
libavcodec/vaapi_decode.h | 5 ++
2 files changed, 155 insertions(+), 17 deletions(-)
diff --git a/libavcodec/vaapi_decode.c b/libavcodec/vaapi_decode.c
index 42f03ab14..5f7b0eca1 100644
--- a/libavcodec/vaapi_decode.c
+++ b/libavcodec/vaapi_decode.c
@@ -18,6 +18,7 @@
#include "libavutil/avassert.h"
#include "libavutil/common.h"
+#include "libavutil/pixdesc.h"
#include "avcodec.h"
#include "internal.h"
@@ -280,6 +281,7 @@ static int vaapi_decode_make_config(AVCodecContext *avctx)
const AVCodecDescriptor *codec_desc;
VAProfile profile, *profile_list = NULL;
int profile_count, exact_match, alt_profile;
+ const AVPixFmtDescriptor *sw_desc, *desc;
// Allowing a profile mismatch can be useful because streams may
// over-declare their required capabilities - in particular, many
@@ -373,7 +375,9 @@ static int vaapi_decode_make_config(AVCodecContext *avctx)
goto fail;
}
- hwconfig = av_hwdevice_hwconfig_alloc(ctx->frames->device_ref);
+ hwconfig = av_hwdevice_hwconfig_alloc(avctx->hw_device_ctx ?
+ avctx->hw_device_ctx :
+ ctx->frames->device_ref);
if (!hwconfig) {
err = AVERROR(ENOMEM);
goto fail;
@@ -381,24 +385,77 @@ static int vaapi_decode_make_config(AVCodecContext *avctx)
hwconfig->config_id = ctx->va_config;
constraints =
- av_hwdevice_get_hwframe_constraints(ctx->frames->device_ref,
+ av_hwdevice_get_hwframe_constraints(avctx->hw_device_ctx ?
+ avctx->hw_device_ctx :
+ ctx->frames->device_ref,
hwconfig);
if (!constraints) {
- // Ignore.
- } else {
- if (avctx->coded_width < constraints->min_width ||
- avctx->coded_height < constraints->min_height ||
- avctx->coded_width > constraints->max_width ||
- avctx->coded_height > constraints->max_height) {
- av_log(avctx, AV_LOG_ERROR, "Hardware does not support image "
- "size %dx%d (constraints: width %d-%d height %d-%d).\n",
- avctx->coded_width, avctx->coded_height,
- constraints->min_width, constraints->max_width,
- constraints->min_height, constraints->max_height);
- err = AVERROR(EINVAL);
- goto fail;
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if (avctx->coded_width < constraints->min_width ||
+ avctx->coded_height < constraints->min_height ||
+ avctx->coded_width > constraints->max_width ||
+ avctx->coded_height > constraints->max_height) {
+ av_log(avctx, AV_LOG_ERROR, "Hardware does not support image "
+ "size %dx%d (constraints: width %d-%d height %d-%d).\n",
+ avctx->coded_width, avctx->coded_height,
+ constraints->min_width, constraints->max_width,
+ constraints->min_height, constraints->max_height);
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+ if (!constraints->valid_sw_formats ||
+ constraints->valid_sw_formats[0] == AV_PIX_FMT_NONE) {
+ av_log(avctx, AV_LOG_ERROR, "Hardware does not offer any "
+ "usable surface formats.\n");
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ // Find the first format in the list which matches the expected
+ // bit depth and subsampling. If none are found (this can happen
+ // when 10-bit streams are decoded to 8-bit surfaces, for example)
+ // then just take the first format on the list.
+ ctx->surface_format = constraints->valid_sw_formats[0];
+ sw_desc = av_pix_fmt_desc_get(avctx->sw_pix_fmt);
+ for (i = 0; constraints->valid_sw_formats[i] != AV_PIX_FMT_NONE; i++) {
+ desc = av_pix_fmt_desc_get(constraints->valid_sw_formats[i]);
+ if (desc->nb_components != sw_desc->nb_components ||
+ desc->log2_chroma_w != sw_desc->log2_chroma_w ||
+ desc->log2_chroma_h != sw_desc->log2_chroma_h)
+ continue;
+ for (j = 0; j < desc->nb_components; j++) {
+ if (desc->comp[j].depth != sw_desc->comp[j].depth)
+ break;
}
+ if (j < desc->nb_components)
+ continue;
+ ctx->surface_format = constraints->valid_sw_formats[i];
+ break;
+ }
+
+ // Start with at least four surfaces.
+ ctx->surface_count = 4;
+ // Add per-codec number of surfaces used for storing reference frames.
+ switch (avctx->codec_id) {
+ case AV_CODEC_ID_H264:
+ case AV_CODEC_ID_HEVC:
+ ctx->surface_count += 16;
+ break;
+ case AV_CODEC_ID_VP9:
+ ctx->surface_count += 8;
+ break;
+ case AV_CODEC_ID_VP8:
+ ctx->surface_count += 3;
+ break;
+ default:
+ ctx->surface_count += 2;
}
+ // Add an additional surface per thread is frame threading is enabled.
+ if (avctx->active_thread_type & FF_THREAD_FRAME)
+ ctx->surface_count += avctx->thread_count;
av_hwframe_constraints_free(&constraints);
av_freep(&hwconfig);
@@ -462,12 +519,32 @@ int ff_vaapi_decode_init(AVCodecContext *avctx)
ctx->frames = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
ctx->hwfc = ctx->frames->hwctx;
+ if (ctx->frames->format != AV_PIX_FMT_VAAPI) {
+ av_log(avctx, AV_LOG_ERROR, "Frames supplied for VAAPI "
+ "decoding must be VAAPI frames (not %d).\n",
+ ctx->frames->format);
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+
ctx->device = ctx->frames->device_ctx;
ctx->hwctx = ctx->device->hwctx;
+ } else if (avctx->hw_device_ctx) {
+ ctx->device = (AVHWDeviceContext*)avctx->hw_device_ctx->data;
+ ctx->hwctx = ctx->device->hwctx;
+
+ if (ctx->device->type != AV_HWDEVICE_TYPE_VAAPI) {
+ av_log(avctx, AV_LOG_ERROR, "Device supplied for VAAPI "
+ "decoding must be a VAAPI device (not %d).\n",
+ ctx->device->type);
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+
} else {
- av_log(avctx, AV_LOG_ERROR, "A hardware frames context is "
- "required for VAAPI decoding.\n");
+ av_log(avctx, AV_LOG_ERROR, "A hardware device or frames context "
+ "is required for VAAPI decoding.\n");
err = AVERROR(EINVAL);
goto fail;
}
@@ -486,6 +563,60 @@ int ff_vaapi_decode_init(AVCodecContext *avctx)
if (err)
goto fail;
+ if (!avctx->hw_frames_ctx) {
+ int reuse_saved_frames = 0;
+
+ if (ctx->saved_frames_ref) {
+ ctx->frames = (AVHWFramesContext*)ctx->saved_frames_ref->data;
+
+ if (ctx->frames->width == avctx->coded_width &&
+ ctx->frames->height == avctx->coded_height &&
+ ctx->frames->sw_format == ctx->surface_format &&
+ ctx->frames->initial_pool_size == ctx->surface_count) {
+ reuse_saved_frames = 1;
+ }
+ }
+
+ if (reuse_saved_frames) {
+ avctx->hw_frames_ctx = av_buffer_ref(ctx->saved_frames_ref);
+ if (!avctx->hw_frames_ctx) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ ctx->hwfc = ctx->frames->hwctx;
+ } else {
+ av_buffer_unref(&ctx->saved_frames_ref);
+
+ avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx);
+ ctx->frames = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
+
+ ctx->frames->format = AV_PIX_FMT_VAAPI;
+ ctx->frames->width = avctx->coded_width;
+ ctx->frames->height = avctx->coded_height;
+
+ ctx->frames->sw_format = ctx->surface_format;
+ ctx->frames->initial_pool_size = ctx->surface_count;
+
+ err = av_hwframe_ctx_init(avctx->hw_frames_ctx);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to initialise internal "
+ "frames context: %d.\n", err);
+ goto fail;
+ }
+
+ ctx->hwfc = ctx->frames->hwctx;
+
+ ctx->saved_frames_ref = av_buffer_ref(avctx->hw_frames_ctx);
+ if (!ctx->saved_frames_ref) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ }
+ } else {
+ av_buffer_unref(&ctx->saved_frames_ref);
+ }
+
vas = vaCreateContext(ctx->hwctx->display, ctx->va_config,
avctx->coded_width, avctx->coded_height,
VA_PROGRESSIVE,
@@ -540,6 +671,8 @@ int ff_vaapi_decode_uninit(AVCodecContext *avctx)
}
}
+ av_buffer_unref(&ctx->saved_frames_ref);
+
#if FF_API_VAAPI_CONTEXT
}
#endif
diff --git a/libavcodec/vaapi_decode.h b/libavcodec/vaapi_decode.h
index 08b212d03..1211c3ff3 100644
--- a/libavcodec/vaapi_decode.h
+++ b/libavcodec/vaapi_decode.h
@@ -69,6 +69,11 @@ typedef struct VAAPIDecodeContext {
AVHWFramesContext *frames;
AVVAAPIFramesContext *hwfc;
+
+ enum AVPixelFormat surface_format;
+ int surface_count;
+
+ AVBufferRef *saved_frames_ref;
} VAAPIDecodeContext;
--
2.11.0
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel