Skip to content

Commit ac110f1

Browse files
committed
vaapi dec.: deduce SW format to reported valid
Set AVHWFramesContext::sw_format to first of av_hwframe_transfer_get_formats(). This is consistent how MPV does that. Fixes NV12 being transmitted despite AVHWFramesContext::sw_format was set to yuv420p causing chroma channels corruption (because the nv12 data was misinterpreted as the latter one) occuring on AMD cards, steps to reproduce: ``` uv -t testcard -c lavc:enc=libx264:safe -d gl --param use-hw-accel=vaapi ``` See also: <mpv-player/mpv@66e30e7>
1 parent e5d628c commit ac110f1

File tree

4 files changed

+115
-16
lines changed

4 files changed

+115
-16
lines changed

src/hwaccel_vaapi.c

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,20 @@
4848
#include "config_win32.h"
4949
#endif // defined HAVE_CONFIG_H
5050

51-
#include "hwaccel_vaapi.h"
52-
53-
#include "debug.h"
54-
55-
#include "hwaccel_libav_common.h"
5651
#include <libavcodec/version.h>
52+
#include <libavutil/pixdesc.h>
5753
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 74, 100)
5854
#include <libavcodec/vaapi.h>
5955
#endif
6056
#include <libavutil/hwcontext_vaapi.h>
6157

58+
#include "debug.h"
59+
#include "hwaccel_libav_common.h"
60+
#include "hwaccel_vaapi.h"
61+
#include "libavcodec/lavc_common.h"
62+
6263
#define DEFAULT_SURFACES 20
64+
#define MOD_NAME "[vaapi] "
6365

6466
struct vaapi_ctx {
6567
AVBufferRef *device_ref;
@@ -196,6 +198,84 @@ static int vaapi_create_context(struct vaapi_ctx *ctx,
196198
}
197199
#endif //LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 74, 100)
198200

201+
/**
202+
* Returns first SW format from valid_sw_formats. This is usually
203+
* AV_PIX_FMT_YUV420P or AV_PIX_FMT_NV12.
204+
*
205+
* The code borrows heavily from mpv
206+
* <https://github.com/mpv-player/mpv/blob/master/video/out/hwdec/hwdec_vaapi.c>
207+
* namely from function try_format_config().
208+
*/
209+
static enum AVPixelFormat
210+
get_sw_format(VADisplay display, AVBufferRef *device_ref,
211+
enum AVPixelFormat fallback_fmt)
212+
{
213+
enum AVPixelFormat ret = AV_PIX_FMT_NONE;
214+
AVVAAPIHWConfig *hwconfig = NULL;
215+
VAConfigID config_id = 0;
216+
AVHWFramesConstraints *fc = NULL;
217+
218+
VAStatus status = vaCreateConfig(
219+
display, VAProfileNone, VAEntrypointVideoProc, NULL, 0, &config_id);
220+
if (status != VA_STATUS_SUCCESS) {
221+
MSG(ERROR, "cannot create config\n");
222+
goto fail;
223+
}
224+
fc = av_hwdevice_get_hwframe_constraints(device_ref, hwconfig);
225+
if (!fc) {
226+
MSG(ERROR, "failed to retrieve libavutil frame constraints\n");
227+
goto fail;
228+
}
229+
230+
/*
231+
* We need a hwframe_ctx to be able to get the valid formats, but to
232+
* initialise it, we need a format, so we get the first format from the
233+
* hwconfig. We don't care about the other formats in the config because
234+
* the transfer formats list will already include them.
235+
*/
236+
AVBufferRef *fref = NULL;
237+
fref = av_hwframe_ctx_alloc(device_ref);
238+
if (!fref) {
239+
MSG(ERROR, "failed to alloc libavutil frame context\n");
240+
goto fail;
241+
}
242+
AVHWFramesContext *fctx = (void *) fref->data;
243+
enum {
244+
INIT_SIZE = 128, ///< just some valid size
245+
};
246+
fctx->format = AV_PIX_FMT_VAAPI;
247+
fctx->sw_format = fc->valid_sw_formats[0];
248+
fctx->width = INIT_SIZE;
249+
fctx->height = INIT_SIZE;
250+
if (av_hwframe_ctx_init(fref) < 0) {
251+
MSG(ERROR, "failed to init libavutil frame context\n");
252+
goto fail;
253+
}
254+
255+
enum AVPixelFormat *fmts = NULL;
256+
int rc = av_hwframe_transfer_get_formats(
257+
fref, AV_HWFRAME_TRANSFER_DIRECTION_FROM, &fmts, 0);
258+
if (rc) {
259+
MSG(ERROR, "failed to get libavutil frame context supported "
260+
"formats\n");
261+
goto fail;
262+
}
263+
MSG(DEBUG, "Available HW layouts: %s\n", get_avpixfmts_names(fmts));
264+
ret = fmts[0];
265+
266+
fail:
267+
av_hwframe_constraints_free(&fc);
268+
av_buffer_unref(&fref);
269+
if (ret == AV_PIX_FMT_NONE) {
270+
MSG(WARNING, "Using fallback HW frames layout: %s\n",
271+
av_get_pix_fmt_name(ret));
272+
ret = fallback_fmt;
273+
}
274+
MSG(VERBOSE, "Selected HW frames layout: %s\n",
275+
av_get_pix_fmt_name(ret));
276+
return ret;
277+
}
278+
199279
int vaapi_init(struct AVCodecContext *s,
200280
struct hw_accel_state *state,
201281
codec_t out_codec)
@@ -224,11 +304,13 @@ int vaapi_init(struct AVCodecContext *s,
224304
if (s->active_thread_type & FF_THREAD_FRAME)
225305
decode_surfaces += s->thread_count;
226306

307+
const enum AVPixelFormat sw_format = get_sw_format(
308+
ctx->device_vaapi_ctx->display, ctx->device_ref, s->sw_pix_fmt);
227309
ret = create_hw_frame_ctx(ctx->device_ref,
228310
s->coded_width,
229311
s->coded_height,
230312
AV_PIX_FMT_VAAPI,
231-
s->sw_pix_fmt,
313+
sw_format,
232314
decode_surfaces,
233315
&ctx->hw_frames_ctx);
234316
if(ret < 0)

src/libavcodec/lavc_common.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656

5757
#include "host.h"
5858
#include "libavcodec/lavc_common.h"
59+
#include "utils/macros.h"
5960
#include "video.h"
6061

6162
#define MOD_NAME "[lavc_common] "
@@ -356,4 +357,25 @@ audio_bps_to_av_sample_fmt(int bps, bool planar)
356357
abort();
357358
}
358359
}
360+
361+
/**
362+
* Prints space-separated nammes of AVPixelFormats in AV_PIX_FMT_NONE-terminated
363+
* pixfmts list to given buf and returns pointer to given buf.
364+
*/
365+
const char *
366+
get_avpixfmts_names(const enum AVPixelFormat *pixfmts)
367+
{
368+
_Thread_local static char buf[STR_LEN];
369+
if (pixfmts == NULL || *pixfmts == AV_PIX_FMT_NONE) {
370+
snprintf(buf, sizeof buf, " (none)");
371+
return buf;
372+
}
373+
const enum AVPixelFormat *it = pixfmts;
374+
while (*it != AV_PIX_FMT_NONE) {
375+
snprintf(buf + strlen(buf), sizeof buf - strlen(buf), "%s%s",
376+
it != pixfmts ? " " : "", av_get_pix_fmt_name(*it));
377+
it++;
378+
}
379+
return buf;
380+
}
359381
/* vi: set expandtab sw=8: */

src/libavcodec/lavc_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ void lavd_flush(AVCodecContext *codec_ctx);
123123
const char *lavc_thread_type_to_str(int thread_type);
124124
struct audio_desc audio_desc_from_av_frame(const AVFrame *frm);
125125
enum AVSampleFormat audio_bps_to_av_sample_fmt(int bps, bool planar);
126+
const char *get_avpixfmts_names(const enum AVPixelFormat *pixfmts);
126127

127128
#ifdef __cplusplus
128129
}

src/video_compress/libavcodec.cpp

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -672,16 +672,10 @@ static int vaapi_init(struct AVCodecContext *s){
672672
#endif
673673

674674
void print_codec_supp_pix_fmts(const enum AVPixelFormat *first) {
675-
char out[STR_LEN] = MOD_NAME "Codec supported pixel formats:" TERM_BOLD;
676-
if (first == nullptr) {
677-
snprintf(out + strlen(out), sizeof out - strlen(out),
678-
" (none)");
679-
}
680-
const enum AVPixelFormat *it = first;
681-
while (it != nullptr && *it != AV_PIX_FMT_NONE) {
682-
snprintf(out + strlen(out), sizeof out - strlen(out), " %s",
683-
av_get_pix_fmt_name(*it++));
684-
}
675+
char out[STR_LEN];
676+
snprintf(out, sizeof out,
677+
MOD_NAME "Codec supported pixel formats: " TBOLD("%s"),
678+
get_avpixfmts_names(first));
685679
LOG(LOG_LEVEL_VERBOSE) << wrap_paragraph(out) << TERM_RESET "\n";
686680
}
687681

0 commit comments

Comments
 (0)