]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
transcoding: allow to select vaapi device, fix pix_fmt handling?
authorJaroslav Kysela <perex@perex.cz>
Fri, 1 Sep 2017 17:23:46 +0000 (19:23 +0200)
committerJaroslav Kysela <perex@perex.cz>
Fri, 1 Sep 2017 17:23:46 +0000 (19:23 +0200)
src/transcoding/codec.h
src/transcoding/codec/codecs/libs/vaapi.c
src/transcoding/codec/profile_audio_class.c
src/transcoding/transcode/context.c
src/transcoding/transcode/hwaccels/vaapi.c
src/transcoding/transcode/internals.h
src/transcoding/transcode/video.c

index b0e5433ef23619ccd827d3194db9a3604ccf595f..148dfd30497d8b1f69ff5503230e3ba6f43ffefb 100644 (file)
@@ -96,6 +96,7 @@ typedef struct tvh_codec_profile_t {
     double bit_rate;
     double qscale;
     int profile;
+    char *device; // for hardware acceleration
     LIST_ENTRY(tvh_codec_profile_t) link;
 } TVHCodecProfile;
 
index 037bde8d7cfb2f652739037c7c511ec00124abd9..ee5012aba910b66a111f7af2f94d54167d4f64aa 100644 (file)
@@ -19,6 +19,9 @@
 
 
 #include "transcoding/codec/internals.h"
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <drm/drm.h>
 
 
 #define AV_DICT_SET_QP(d, v, a) \
@@ -34,6 +37,55 @@ typedef struct {
 } 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)
@@ -43,13 +95,21 @@ tvh_codec_profile_vaapi_open(tvh_codec_profile_vaapi_t *self,
     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",
index 6287949e494477f8b7ff59cd0c48541b527d762d..e036f52751be83027ffcd4906d06dc4ddbdc4692 100644 (file)
@@ -168,16 +168,14 @@ codec_profile_audio_class_language_list(void *obj, const char *lang)
     static char lc_buf[128];
 
     while (lc->code2b) {
-        htsmsg_t *map = htsmsg_create_map();
+        htsmsg_t *map = NULL;
         if (!strcmp(lc->code2b, "und")) {
-            htsmsg_add_str(map, "key", "");
-            htsmsg_add_str(map, "val", tvh_gettext_lang(lang, N_("Use original")));
+            map = htsmsg_create_key_val("", tvh_gettext_lang(lang, N_("Use original")));
         }
         else {
             memset(lc_buf, 0, sizeof(lc_buf));
             if (!str_snprintf(lc_buf, sizeof(lc_buf), "%s (%s)", lc->desc, lc->code2b)) {
-                htsmsg_add_str(map, "key", lc->code2b);
-                htsmsg_add_str(map, "val", lc_buf);
+                map = htsmsg_create_key_val(lc->code2b, lc_buf);
             }
             else {
                 htsmsg_destroy(list);
index 3c4f50d423df6c36fbb30c874207b41435aa0c58..c29b1c16a60f7de8cb4514fa9e02696d46f81747 100644 (file)
@@ -179,13 +179,14 @@ _context_wrap(TVHContext *self, AVPacket *avpkt, th_pkt_t *pkt)
 // creation
 
 static AVCodecContext *
-tvh_context_alloc_avctx(AVCodec *avcodec)
+tvh_context_alloc_avctx(TVHContext *context, AVCodec *avcodec)
 {
     AVCodecContext *avctx = NULL;
 
     if ((avctx = avcodec_alloc_context3(avcodec))) {
         avctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
         avctx->refcounted_frames = 1;
+        avctx->opaque = context;
     }
     return avctx;
 }
@@ -203,8 +204,8 @@ tvh_context_setup(TVHContext *self, AVCodec *iavcodec, AVCodec *oavcodec)
                        media_type_name ? media_type_name : "<unknown>");
         return -1;
     }
-    if (!(self->iavctx = tvh_context_alloc_avctx(iavcodec)) ||
-        !(self->oavctx = tvh_context_alloc_avctx(oavcodec)) ||
+    if (!(self->iavctx = tvh_context_alloc_avctx(self, iavcodec)) ||
+        !(self->oavctx = tvh_context_alloc_avctx(self, oavcodec)) ||
         !(self->iavframe = av_frame_alloc()) ||
         !(self->oavframe = av_frame_alloc())) {
         tvh_stream_log(self->stream, LOG_ERR,
@@ -577,11 +578,10 @@ tvh_context_open_filters(TVHContext *self,
     }
 
     // additional filtergraph params
-    if (!strcmp("buffer", source_name) && self->oavctx->opaque) { // hmm...
-        AVBufferRef *hw_device_ctx = self->oavctx->opaque;
+    if (!strcmp("buffer", source_name) && self->hw_device_ctx) {
         for (i = 0; i < self->avfltgraph->nb_filters; i++) {
             if (!(self->avfltgraph->filters[i]->hw_device_ctx =
-                      av_buffer_ref(hw_device_ctx))) {
+                      av_buffer_ref(self->hw_device_ctx))) {
                 ret = -1;
                 goto finish;
             }
@@ -720,6 +720,7 @@ tvh_context_destroy(TVHContext *self)
         self->type = NULL;
         self->profile = NULL;
         self->stream = NULL;
+        free(self->hw_accel_device);
         free(self);
         self = NULL;
     }
index 6978446ee8a7f6d1f5bd34f6ad29dfe3e7eafc83..11652fce142401242bc142ddf8a0429be0c953e3 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 
+#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;
@@ -43,49 +49,49 @@ typedef struct tvh_vaapi_context_t {
 } 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);
     }
 }
 
@@ -120,14 +126,16 @@ tvhva_context_destroy(TVHVAContext *self)
 
 
 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;
@@ -269,18 +277,20 @@ tvhva_context_config(TVHVAContext *self, VAProfile profile, unsigned int format)
     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;
@@ -297,7 +307,7 @@ tvhva_context_check_constraints(TVHVAContext *self)
     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;
@@ -305,7 +315,7 @@ tvhva_context_check_constraints(TVHVAContext *self)
     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;
     }
@@ -333,8 +343,8 @@ tvhva_context_check_constraints(TVHVAContext *self)
         }
     }
     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;
     }
@@ -344,9 +354,9 @@ tvhva_context_check_constraints(TVHVAContext *self)
         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);
@@ -368,18 +378,20 @@ tvhva_context_setup(TVHVAContext *self, AVCodecContext *avctx)
     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;
     }
@@ -390,7 +402,8 @@ tvhva_context_setup(TVHVAContext *self, AVCodecContext *avctx)
     }
 
     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;
@@ -401,7 +414,8 @@ tvhva_context_setup(TVHVAContext *self, AVCodecContext *avctx)
     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;
     }
 
@@ -414,7 +428,8 @@ tvhva_context_setup(TVHVAContext *self, AVCodecContext *avctx)
                                  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;
         }
     }
@@ -431,23 +446,38 @@ tvhva_context_setup(TVHVAContext *self, AVCodecContext *avctx)
 
 
 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;
@@ -478,7 +508,7 @@ int
 vaapi_decode_setup_context(AVCodecContext *avctx)
 {
     if (!(avctx->hwaccel_context =
-          tvhva_context_create(avctx, VAEntrypointVLD))) {
+          tvhva_context_create("decode", avctx, VAEntrypointVLD))) {
         return -1;
     }
 
@@ -505,13 +535,14 @@ vaapi_decode_close_context(AVCodecContext *avctx)
 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);
     }
@@ -527,11 +558,9 @@ vaapi_encode_close_context(AVCodecContext *avctx)
         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;
 }
 
 
index cdc4714efc7bc5f5c9299f2fa9be18b97d84d27f..a274575c3a1b4301c04a673dd58e313cdeda8823 100644 (file)
@@ -168,6 +168,10 @@ typedef struct tvh_context_t {
     int64_t duration;
     int64_t delta;
     uint8_t sri;
+    // hardware acceleration
+    char *hw_accel_device;
+    AVBufferRef *hw_device_ref;
+    AVBufferRef *hw_device_ctx;
 } TVHContext;
 
 int
index 0dda4fce8ad0d3f63d0a9c17623f737b35f4ad45..ce73b2d5c052a4a4ac62ea6c0eeeb113bfc32a3e 100644 (file)
@@ -102,6 +102,7 @@ tvh_video_context_open_decoder(TVHContext *self, AVDictionary **opts)
     if (hwaccel) {
         self->iavctx->get_format = hwaccels_decode_get_format;
     }
+    mystrset(&self->hw_accel_device, self->profile->device);
 #endif
     return 0;
 }