#include "transcoding/codec/internals.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <drm/drm.h>
#define AV_DICT_SET_QP(d, v, a) \
} tvh_codec_profile_vaapi_t;
+static int
+probe_vaapi_device(const char *device, char *name, size_t namelen)
+{
+ drm_version_t dv;
+ char dname[128];
+ int fd;
+
+ if ((fd = open(device, O_RDWR)) < 0)
+ return -1;
+ memset(&dv, 0, sizeof(dv));
+ memset(dname, 0, sizeof(dname));
+ dv.name = dname;
+ dv.name_len = sizeof(dname)-1;
+ if (ioctl(fd, DRM_IOCTL_VERSION, &dv) < 0) {
+ close(fd);
+ return -1;
+ }
+ snprintf(name, namelen, "%s v%d.%d.%d (%s)",
+ dv.name, dv.version_major, dv.version_minor,
+ dv.version_patchlevel, device);
+ close(fd);
+ return 0;
+}
+
+static htsmsg_t *
+tvh_codec_profile_vaapi_device_list(void *obj, const char *lang)
+{
+ static const char *renderD_fmt = "/dev/dri/renderD%d";
+ static const char *card_fmt = "/dev/dri/card%d";
+ htsmsg_t *result = htsmsg_create_list();
+ char device[PATH_MAX];
+ char name[128];
+ int i, dev_num;
+
+ for (i = 0; i < 32; i++) {
+ dev_num = i + 128;
+ snprintf(device, sizeof(device), renderD_fmt, dev_num);
+ if (probe_vaapi_device(device, name, sizeof(name)) == 0)
+ htsmsg_add_msg(result, NULL, htsmsg_create_key_val(device, name));
+ }
+ for (i = 0; i < 32; i++) {
+ dev_num = i + 128;
+ snprintf(device, sizeof(device), card_fmt, dev_num);
+ if (probe_vaapi_device(device, name, sizeof(name)) == 0)
+ htsmsg_add_msg(result, NULL, htsmsg_create_key_val(device, name));
+ }
+ return result;
+}
+
static int
tvh_codec_profile_vaapi_open(tvh_codec_profile_vaapi_t *self,
AVDictionary **opts)
return 0;
}
-
static const codec_profile_class_t codec_profile_vaapi_class = {
{
.ic_super = (idclass_t *)&codec_profile_video_class,
.ic_class = "codec_profile_vaapi",
.ic_caption = N_("vaapi"),
.ic_properties = (const property_t[]){
+ {
+ .type = PT_STR,
+ .id = "device",
+ .name = N_("Device name"),
+ .desc = N_("Device name (e.g. /dev/dri/renderD129)."),
+ .group = 3,
+ .off = offsetof(tvh_codec_profile_vaapi_t, device),
+ .list = tvh_codec_profile_vaapi_device_list,
+ },
{
.type = PT_DBL,
.id = "bit_rate",
*/
+#include "../internals.h"
#include "vaapi.h"
#include <libavcodec/vaapi.h>
#include <va/va.h>
-static AVBufferRef *hw_device_ref = NULL;
+typedef struct tvh_vaapi_device {
+ char *hw_device_name;
+ AVBufferRef *hw_device_ref;
+ LIST_ENTRY(tvh_vaapi_device) link;
+} TVHVADevice;
typedef struct tvh_vaapi_context_t {
struct vaapi_context;
+ const char *logpref;
VAEntrypoint entrypoint;
enum AVPixelFormat io_format;
enum AVPixelFormat sw_format;
} TVHVAContext;
-static int
-tvhva_init()
+static LIST_HEAD(, tvh_vaapi_device) tvhva_devices;
+
+
+static AVBufferRef *
+tvhva_init(const char *device)
{
- static const char *renderD_fmt = "/dev/dri/renderD%d";
- static const char *card_fmt = "/dev/dri/card%d";
- int i, dev_num;
- char device[32];
-
- //search for valid graphics device
- for (i = 0; i < 6; i++) {
- memset(device, 0, sizeof(device));
- if (i < 3) {
- dev_num = i + 128;
- snprintf(device, sizeof(device), renderD_fmt, dev_num);
- }
- else {
- dev_num = i - 3;
- snprintf(device, sizeof(device), card_fmt, dev_num);
- }
- tvhdebug(LS_VAAPI, "trying device: %s", device);
- if (av_hwdevice_ctx_create(&hw_device_ref, AV_HWDEVICE_TYPE_VAAPI,
- device, NULL, 0)) {
- tvhdebug(LS_VAAPI,
- "failed to create a context for device: %s", device);
- continue;
- }
- else {
- tvhinfo(LS_VAAPI,
- "successful context creation for device: %s", device);
- return 0;
- }
+ TVHVADevice *vad;
+
+ if (device == NULL || *device == '\0')
+ device = "/dev/dri/renderD128";
+ LIST_FOREACH(vad, &tvhva_devices, link) {
+ if (strcmp(vad->hw_device_name, device) == 0)
+ break;
+ }
+ if (vad == NULL) {
+ vad = calloc(1, sizeof(*vad));
+ vad->hw_device_name = strdup(device);
+ LIST_INSERT_HEAD(&tvhva_devices, vad, link);
+ }
+ if (vad->hw_device_ref)
+ return vad->hw_device_ref;
+ tvhtrace(LS_VAAPI, "trying device: %s", device);
+ if (av_hwdevice_ctx_create(&vad->hw_device_ref, AV_HWDEVICE_TYPE_VAAPI,
+ device, NULL, 0)) {
+ tvherror(LS_VAAPI,
+ "failed to create a context for device: %s", device);
+ return NULL;
}
- tvherror(LS_VAAPI, "failed to find suitable VAAPI device");
- return -1;
+ tvhtrace(LS_VAAPI, "successful context creation for device: %s", device);
+ return vad->hw_device_ref;
}
static void
tvhva_done()
{
- if (hw_device_ref) {
- av_buffer_unref(&hw_device_ref);
- hw_device_ref = NULL;
+ TVHVADevice *vad;
+
+ while ((vad = LIST_FIRST(&tvhva_devices)) != NULL) {
+ LIST_REMOVE(vad, link);
+ av_buffer_unref(&vad->hw_device_ref);
+ free(vad->hw_device_name);
+ free(vad);
}
}
static VADisplay *
-tvhva_context_display(TVHVAContext *self)
+tvhva_context_display(TVHVAContext *self, AVCodecContext *avctx)
{
+ TVHContext *ctx = avctx->opaque;
AVHWDeviceContext *hw_device_ctx = NULL;
- if (!hw_device_ref && tvhva_init()) {
+ if (!ctx->hw_device_ref &&
+ !(ctx->hw_device_ref = tvhva_init(ctx->hw_accel_device))) {
return NULL;
}
- if (!(self->hw_device_ref = av_buffer_ref(hw_device_ref))) {
+ if (!(self->hw_device_ref = av_buffer_ref(ctx->hw_device_ref))) {
return NULL;
}
hw_device_ctx = (AVHWDeviceContext*)self->hw_device_ref->data;
va_res = vaGetConfigAttributes(self->display, profile, self->entrypoint,
&attrib, 1);
if (va_res != VA_STATUS_SUCCESS) {
- tvherror(LS_VAAPI, "vaGetConfigAttributes: %s", vaErrorStr(va_res));
+ tvherror(LS_VAAPI, "%s: vaGetConfigAttributes: %s",
+ self->logpref, vaErrorStr(va_res));
return -1;
}
if (attrib.value == VA_ATTRIB_NOT_SUPPORTED || !(attrib.value & format)) {
- tvherror(LS_VAAPI, "unsupported VA_RT_FORMAT");
+ tvherror(LS_VAAPI, "%s: unsupported VA_RT_FORMAT", self->logpref);
return -1;
}
attrib.value = format;
va_res = vaCreateConfig(self->display, profile, self->entrypoint,
&attrib, 1, &self->config_id);
if (va_res != VA_STATUS_SUCCESS) {
- tvherror(LS_VAAPI, "vaCreateConfig: %s", vaErrorStr(va_res));
+ tvherror(LS_VAAPI, "%s: vaCreateConfig: %s",
+ self->logpref, vaErrorStr(va_res));
return -1;
}
return 0;
int i, ret = 0;
if (!(va_config = av_hwdevice_hwconfig_alloc(self->hw_device_ref))) {
- tvherror(LS_VAAPI, "failed to allocate hwconfig");
+ tvherror(LS_VAAPI, "%s: failed to allocate hwconfig", self->logpref);
return AVERROR(ENOMEM);
}
va_config->config_id = self->config_id;
hw_constraints =
av_hwdevice_get_hwframe_constraints(self->hw_device_ref, va_config);
if (!hw_constraints) {
- tvherror(LS_VAAPI, "failed to get constraints");
+ tvherror(LS_VAAPI, "%s: failed to get constraints", self->logpref);
av_freep(&va_config);
return -1;
}
}
}
if (self->sw_format == AV_PIX_FMT_NONE) {
- tvherror(LS_VAAPI, "VAAPI hardware does not support pixel format: %s",
- av_get_pix_fmt_name(self->io_format));
+ tvherror(LS_VAAPI, "%s: VAAPI hardware does not support pixel format: %s",
+ self->logpref, av_get_pix_fmt_name(self->io_format));
ret = AVERROR(EINVAL);
goto end;
}
self->height < hw_constraints->min_height ||
self->width > hw_constraints->max_width ||
self->height > hw_constraints->max_height) {
- tvherror(LS_VAAPI, "VAAPI hardware does not support image "
+ tvherror(LS_VAAPI, "%s: VAAPI hardware does not support image "
"size %dx%d (constraints: width %d-%d height %d-%d).",
- self->width, self->height,
+ self->logpref, self->width, self->height,
hw_constraints->min_width, hw_constraints->max_width,
hw_constraints->min_height, hw_constraints->max_height);
ret = AVERROR(EINVAL);
AVHWFramesContext *hw_frames_ctx = NULL;
AVVAAPIFramesContext *va_frames = NULL;
- if (!(self->display = tvhva_context_display(self))) {
+ if (!(self->display = tvhva_context_display(self, avctx))) {
return -1;
}
if ((profile = tvhva_context_profile(self, avctx)) == VAProfileNone ||
tvhva_context_check_profile(self, profile)) {
- tvherror(LS_VAAPI, "unsupported codec: %s and/or profile: %s",
+ tvherror(LS_VAAPI, "%s: unsupported codec: %s and/or profile: %s",
+ self->logpref,
avctx->codec->name,
av_get_profile_name(avctx->codec, avctx->profile));
return -1;
}
if (!(format = tvhva_get_format(self->io_format))) {
- tvherror(LS_VAAPI, "unsupported pixel format: %s",
+ tvherror(LS_VAAPI, "%s: unsupported pixel format: %s",
+ self->logpref,
av_get_pix_fmt_name(self->io_format));
return -1;
}
}
if (!(self->hw_frames_ref = av_hwframe_ctx_alloc(self->hw_device_ref))) {
- tvherror(LS_VAAPI, "failed to create VAAPI frame context.");
+ tvherror(LS_VAAPI, "%s: failed to create VAAPI frame context.",
+ self->logpref);
return AVERROR(ENOMEM);
}
hw_frames_ctx = (AVHWFramesContext*)self->hw_frames_ref->data;
hw_frames_ctx->initial_pool_size = 32;
if (av_hwframe_ctx_init(self->hw_frames_ref) < 0) {
- tvherror(LS_VAAPI, "failed to initialise VAAPI frame context");
+ tvherror(LS_VAAPI, "%s: failed to initialise VAAPI frame context",
+ self->logpref);
return -1;
}
va_frames->surface_ids, va_frames->nb_surfaces,
&self->context_id);
if (va_res != VA_STATUS_SUCCESS) {
- tvherror(LS_VAAPI, "vaCreateContext: %s", vaErrorStr(va_res));
+ tvherror(LS_VAAPI, "%s: vaCreateContext: %s",
+ self->logpref, vaErrorStr(va_res));
return -1;
}
}
static TVHVAContext *
-tvhva_context_create(AVCodecContext *avctx, VAEntrypoint entrypoint)
+tvhva_context_create(const char *logpref,
+ AVCodecContext *avctx, VAEntrypoint entrypoint)
{
TVHVAContext *self = NULL;
+ enum AVPixelFormat pix_fmt;
if (!(self = calloc(1, sizeof(TVHVAContext)))) {
- tvherror(LS_VAAPI, "failed to allocate vaapi context");
+ tvherror(LS_VAAPI, "%s: failed to allocate vaapi context", logpref);
return NULL;
}
+ self->logpref = logpref;
self->display = NULL;
self->config_id = VA_INVALID_ID;
self->context_id = VA_INVALID_ID;
self->entrypoint = entrypoint;
- if (avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P) {
+ pix_fmt = self->entrypoint == VAEntrypointVLD ?
+ avctx->sw_pix_fmt : avctx->pix_fmt;
+ if (pix_fmt == AV_PIX_FMT_YUV420P ||
+ pix_fmt == AV_PIX_FMT_VAAPI) {
self->io_format = AV_PIX_FMT_NV12;
}
else {
- self->io_format = avctx->sw_pix_fmt;
+ self->io_format = pix_fmt;
+ }
+ if (self->io_format == AV_PIX_FMT_NONE) {
+ tvherror(LS_VAAPI, "%s: failed to get pix_fmt for vaapi context "
+ "(sw_pix_fmt: %s, pix_fmt: %s)",
+ logpref,
+ av_get_pix_fmt_name(avctx->sw_pix_fmt),
+ av_get_pix_fmt_name(avctx->pix_fmt));
+ free(self);
+ return NULL;
}
self->sw_format = AV_PIX_FMT_NONE;
self->width = avctx->coded_width;
vaapi_decode_setup_context(AVCodecContext *avctx)
{
if (!(avctx->hwaccel_context =
- tvhva_context_create(avctx, VAEntrypointVLD))) {
+ tvhva_context_create("decode", avctx, VAEntrypointVLD))) {
return -1;
}
int
vaapi_encode_setup_context(AVCodecContext *avctx)
{
+ TVHContext *ctx = avctx->opaque;
TVHVAContext *hwaccel_context = NULL;
if (!(hwaccel_context =
- tvhva_context_create(avctx, VAEntrypointEncSlice))) {
+ tvhva_context_create("encode", avctx, VAEntrypointEncSlice))) {
return -1;
}
- if (!(avctx->opaque = av_buffer_ref(hwaccel_context->hw_device_ref))) {
+ if (!(ctx->hw_device_ctx = av_buffer_ref(hwaccel_context->hw_device_ref))) {
tvhva_context_destroy(hwaccel_context);
return AVERROR(ENOMEM);
}
av_buffer_unref(&avctx->hw_frames_ctx);
avctx->hw_frames_ctx = NULL;
}*/
- if (avctx->opaque) {
- AVBufferRef *hw_device_ctx = avctx->opaque;
- av_buffer_unref(&hw_device_ctx);
- avctx->opaque = NULL;
- }
+ TVHContext *ctx = avctx->opaque;
+ av_buffer_unref(&ctx->hw_device_ctx);
+ ctx->hw_device_ctx = NULL;
}