diff --git a/hve.c b/hve.c index 77466bc..b2ee2e6 100644 --- a/hve.c +++ b/hve.c @@ -31,6 +31,7 @@ struct hve }; static int init_hwframes_context(struct hve* h, const struct hve_config *config); +static int hve_pixel_format_depth( enum AVPixelFormat pix_fmt, int *depth); static struct hve *hve_close_and_return_null(struct hve *h, const char *msg); static int HVE_ERROR_MSG(const char *msg); @@ -165,10 +166,25 @@ static int init_hwframes_context(struct hve* h, const struct hve_config *config) frames_ctx = (AVHWFramesContext*)(hw_frames_ref->data); frames_ctx->format = AV_PIX_FMT_VAAPI; - frames_ctx->sw_format = h->sw_pix_fmt; frames_ctx->width = config->width; frames_ctx->height = config->height; frames_ctx->initial_pool_size = 20; + frames_ctx->sw_format = AV_PIX_FMT_NV12; + + // Starting from FFmpeg 4.1, avcodec will not fall back to NV12 automatically + // when using non 4:2:0 software pixel format not supported by codec. + // Here, instead of using h->sw_pix_fmt we always fall to P010LE for 10 bit + // input and NV12 otherwise which may possibly lead to some loss of information + // on modern hardware supporting 4:2:2 and 4:4:4 chroma subsampling + // (e.g. HEVC with >= IceLake) + // See: + // https://github.com/bmegli/hardware-video-encoder/issues/26 + int depth; + if(hve_pixel_format_depth(h->sw_pix_fmt, &depth) != HVE_OK) + return HVE_ERROR_MSG("failed to get pixel format depth"); + + if(depth == 10) + frames_ctx->sw_format = AV_PIX_FMT_P010LE; if((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) { @@ -185,6 +201,22 @@ static int init_hwframes_context(struct hve* h, const struct hve_config *config) return err == 0 ? HVE_OK : HVE_ERROR; } +static int hve_pixel_format_depth(enum AVPixelFormat pix_fmt, int *depth) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); + int i; + + if (!desc || !desc->nb_components) + return HVE_ERROR; + + *depth = -INT_MAX; + + for (i = 0; i < desc->nb_components; i++) + *depth = FFMAX(desc->comp[i].depth, *depth); + + return HVE_OK; +} + int hve_send_frame(struct hve *h,struct hve_frame *frame) { AVCodecContext* avctx=h->avctx;