[FFmpeg-devel,16/25] avcodec/v4l2_m2m_dec: add support for AV_PIX_FMT_DRM_PRIME

Submitted by Aman Gupta on Sept. 3, 2019, 1:02 a.m.

Details

Message ID 20190903010230.96236-17-ffmpeg@tmm1.net
State New
Headers show

Commit Message

Aman Gupta Sept. 3, 2019, 1:02 a.m.
From: Aman Gupta <aman@tmm1.net>

Based on patchset submitted to ffmpeg-devel by Lukas Rusak <lorusak@gmail.com>

Signed-off-by: Aman Gupta <aman@tmm1.net>
---
 libavcodec/v4l2_m2m_dec.c | 85 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 80 insertions(+), 5 deletions(-)

Comments

Mark Thompson Sept. 13, 2019, 12:01 a.m.
On 03/09/2019 02:02, Aman Gupta wrote:
> From: Aman Gupta <aman@tmm1.net>
> 
> Based on patchset submitted to ffmpeg-devel by Lukas Rusak <lorusak@gmail.com>
> 
> Signed-off-by: Aman Gupta <aman@tmm1.net>
> ---
>  libavcodec/v4l2_m2m_dec.c | 85 ++++++++++++++++++++++++++++++++++++---
>  1 file changed, 80 insertions(+), 5 deletions(-)
> 
> diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c
> index a3744208f3..6b73d2ffd9 100644
> --- a/libavcodec/v4l2_m2m_dec.c
> +++ b/libavcodec/v4l2_m2m_dec.c
> @@ -23,11 +23,16 @@
>  
>  #include <linux/videodev2.h>
>  #include <sys/ioctl.h>
> +
> +#include "libavutil/hwcontext.h"
> +#include "libavutil/hwcontext_drm.h"
>  #include "libavutil/pixfmt.h"
>  #include "libavutil/pixdesc.h"
>  #include "libavutil/opt.h"
>  #include "libavcodec/avcodec.h"
>  #include "libavcodec/decode.h"
> +#include "libavcodec/hwaccel.h"
> +#include "libavcodec/internal.h"
>  
>  #include "v4l2_context.h"
>  #include "v4l2_m2m.h"
> @@ -39,7 +44,7 @@ static int v4l2_try_start(AVCodecContext *avctx)
>      V4L2Context *const capture = &s->capture;
>      V4L2Context *const output = &s->output;
>      struct v4l2_selection selection;
> -    int ret;
> +    int ret, pix_fmt;
>  
>      /* 1. start the output process */
>      if (!output->streamon) {
> @@ -62,8 +67,13 @@ static int v4l2_try_start(AVCodecContext *avctx)
>      }
>  
>      /* 2.1 update the AVCodecContext */
> -    avctx->pix_fmt = ff_v4l2_format_v4l2_to_avfmt(capture->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
> +    pix_fmt = ff_v4l2_format_v4l2_to_avfmt(capture->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
> +    if (avctx->pix_fmt != AV_PIX_FMT_DRM_PRIME)
> +        avctx->pix_fmt = pix_fmt;
> +    else
> +        avctx->sw_pix_fmt = pix_fmt;
>      capture->av_pix_fmt = avctx->pix_fmt;
> +    capture->sw_pix_fmt = avctx->sw_pix_fmt;
>  
>      /* 3. set the crop parameters */
>      selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> @@ -189,15 +199,42 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx)
>      output->av_codec_id = avctx->codec_id;
>      output->av_pix_fmt  = AV_PIX_FMT_NONE;
>  
> +    /* negotiate drm vs software pixel formats */
> +    avctx->pix_fmt = ff_get_format(avctx, avctx->codec->pix_fmts);

The codec array allows choice between NV12 and YUV420P, but probably only one of those is actually supported?  You need to make this array with the actually-supported format.

Do all V4L2 M2M devices necessarily support VIDIOC_EXPBUF?

> +    switch (avctx->pix_fmt) {
> +    case AV_PIX_FMT_DRM_PRIME:
> +        avctx->sw_pix_fmt = AV_PIX_FMT_NV12;

The previous patch strongly suggests that this is not always true.

> +        break;
> +
> +    case AV_PIX_FMT_NONE:
> +        return 0;

I think this should be an error code - zero indicates success.

> +        break;
> +
> +    default:
> +        break;
> +    }
> +
>      capture->av_codec_id = AV_CODEC_ID_RAWVIDEO;
>      capture->av_pix_fmt = avctx->pix_fmt;
> +    capture->sw_pix_fmt = avctx->sw_pix_fmt;
> +
> +    if (avctx->hw_device_ctx) {
> +        s->device_ref = av_buffer_ref(avctx->hw_device_ctx);
> +    } else {
> +        s->device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM);
> +        if (!s->device_ref)
> +            return AVERROR(ENOMEM);
> +
> +        ret = av_hwdevice_ctx_init(s->device_ref);
> +        if (ret < 0) {
> +            av_buffer_unref(&s->device_ref);
> +            return ret;
> +        }
> +    }
>  
>      ret = ff_v4l2_m2m_codec_init(priv);
>      if (ret) {
>          av_log(avctx, AV_LOG_ERROR, "can't configure decoder\n");
> -        s->self_ref = NULL;
> -        av_buffer_unref(&priv->context_ref);
> -
>          return ret;
>      }
>      s->avctx = avctx;
> @@ -210,6 +247,25 @@ static av_cold int v4l2_decode_close(AVCodecContext *avctx)
>      return ff_v4l2_m2m_codec_end(avctx->priv_data);
>  }
>  
> +static void v4l2_flush(AVCodecContext *avctx)
> +{
> +    V4L2m2mPriv *priv = avctx->priv_data;
> +    V4L2m2mContext* s = priv->context;
> +    int ret;
> +
> +    /* wait for pending buffer references */
> +    if (atomic_load(&s->refcount))
> +        while(sem_wait(&s->refsync) == -1 && errno == EINTR);
> +
> +    ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
> +    if (ret)
> +        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
> +
> +    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
> +    if (ret)
> +        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
> +}
> +
>  #define OFFSET(x) offsetof(V4L2m2mPriv, x)
>  #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
>  
> @@ -220,6 +276,19 @@ static const AVOption options[] = {
>      { NULL},
>  };
>  
> +static const AVCodecHWConfigInternal *v4l2_m2m_hw_configs[] = {
> +    &(const AVCodecHWConfigInternal) {
> +        .public = {
> +            .pix_fmt     = AV_PIX_FMT_DRM_PRIME,
> +            .methods     = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX |
> +                           AV_CODEC_HW_CONFIG_METHOD_INTERNAL,
> +            .device_type = AV_HWDEVICE_TYPE_DRM

The external device doesn't do anything here - this should just be METHOD_INTERNAL and not offer a device type.

(The device is always guessed inside the code, I suppose with the assumption that no system will ever have two decoders.  Possibly some sort of V4L2 device would make sense for proper selection, though I'm not sure how that would work.)

> +        },
> +        .hwaccel = NULL,
> +    },
> +    NULL
> +};
> +
>  #define M2MDEC_CLASS(NAME) \
>      static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \
>          .class_name = #NAME "_v4l2m2m_decoder", \
> @@ -240,7 +309,13 @@ static const AVOption options[] = {
>          .init           = v4l2_decode_init, \
>          .receive_frame  = v4l2_receive_frame, \
>          .close          = v4l2_decode_close, \
> +        .flush          = v4l2_flush, \
> +        .pix_fmts       = (const enum AVPixelFormat[]) { AV_PIX_FMT_DRM_PRIME, \
> +                                                         AV_PIX_FMT_NV12, \
> +                                                         AV_PIX_FMT_YUV420P, \
> +                                                         AV_PIX_FMT_NONE}, \

As above, this is likely to be wrong.  It's probably better to leave this empty like other decoders which don't have a fixed format.

>          .bsfs           = bsf_name, \
> +        .hw_configs     = v4l2_m2m_hw_configs, \
>          .capabilities   = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \
>          .caps_internal  = FF_CODEC_CAP_SETS_PKT_DTS, \
>          .wrapper_name   = "v4l2m2m", \
> 

- Mark

Patch hide | download patch | download mbox

diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c
index a3744208f3..6b73d2ffd9 100644
--- a/libavcodec/v4l2_m2m_dec.c
+++ b/libavcodec/v4l2_m2m_dec.c
@@ -23,11 +23,16 @@ 
 
 #include <linux/videodev2.h>
 #include <sys/ioctl.h>
+
+#include "libavutil/hwcontext.h"
+#include "libavutil/hwcontext_drm.h"
 #include "libavutil/pixfmt.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/opt.h"
 #include "libavcodec/avcodec.h"
 #include "libavcodec/decode.h"
+#include "libavcodec/hwaccel.h"
+#include "libavcodec/internal.h"
 
 #include "v4l2_context.h"
 #include "v4l2_m2m.h"
@@ -39,7 +44,7 @@  static int v4l2_try_start(AVCodecContext *avctx)
     V4L2Context *const capture = &s->capture;
     V4L2Context *const output = &s->output;
     struct v4l2_selection selection;
-    int ret;
+    int ret, pix_fmt;
 
     /* 1. start the output process */
     if (!output->streamon) {
@@ -62,8 +67,13 @@  static int v4l2_try_start(AVCodecContext *avctx)
     }
 
     /* 2.1 update the AVCodecContext */
-    avctx->pix_fmt = ff_v4l2_format_v4l2_to_avfmt(capture->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
+    pix_fmt = ff_v4l2_format_v4l2_to_avfmt(capture->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO);
+    if (avctx->pix_fmt != AV_PIX_FMT_DRM_PRIME)
+        avctx->pix_fmt = pix_fmt;
+    else
+        avctx->sw_pix_fmt = pix_fmt;
     capture->av_pix_fmt = avctx->pix_fmt;
+    capture->sw_pix_fmt = avctx->sw_pix_fmt;
 
     /* 3. set the crop parameters */
     selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -189,15 +199,42 @@  static av_cold int v4l2_decode_init(AVCodecContext *avctx)
     output->av_codec_id = avctx->codec_id;
     output->av_pix_fmt  = AV_PIX_FMT_NONE;
 
+    /* negotiate drm vs software pixel formats */
+    avctx->pix_fmt = ff_get_format(avctx, avctx->codec->pix_fmts);
+    switch (avctx->pix_fmt) {
+    case AV_PIX_FMT_DRM_PRIME:
+        avctx->sw_pix_fmt = AV_PIX_FMT_NV12;
+        break;
+
+    case AV_PIX_FMT_NONE:
+        return 0;
+        break;
+
+    default:
+        break;
+    }
+
     capture->av_codec_id = AV_CODEC_ID_RAWVIDEO;
     capture->av_pix_fmt = avctx->pix_fmt;
+    capture->sw_pix_fmt = avctx->sw_pix_fmt;
+
+    if (avctx->hw_device_ctx) {
+        s->device_ref = av_buffer_ref(avctx->hw_device_ctx);
+    } else {
+        s->device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM);
+        if (!s->device_ref)
+            return AVERROR(ENOMEM);
+
+        ret = av_hwdevice_ctx_init(s->device_ref);
+        if (ret < 0) {
+            av_buffer_unref(&s->device_ref);
+            return ret;
+        }
+    }
 
     ret = ff_v4l2_m2m_codec_init(priv);
     if (ret) {
         av_log(avctx, AV_LOG_ERROR, "can't configure decoder\n");
-        s->self_ref = NULL;
-        av_buffer_unref(&priv->context_ref);
-
         return ret;
     }
     s->avctx = avctx;
@@ -210,6 +247,25 @@  static av_cold int v4l2_decode_close(AVCodecContext *avctx)
     return ff_v4l2_m2m_codec_end(avctx->priv_data);
 }
 
+static void v4l2_flush(AVCodecContext *avctx)
+{
+    V4L2m2mPriv *priv = avctx->priv_data;
+    V4L2m2mContext* s = priv->context;
+    int ret;
+
+    /* wait for pending buffer references */
+    if (atomic_load(&s->refcount))
+        while(sem_wait(&s->refsync) == -1 && errno == EINTR);
+
+    ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
+    if (ret)
+        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
+
+    ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
+    if (ret)
+        av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
+}
+
 #define OFFSET(x) offsetof(V4L2m2mPriv, x)
 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
 
@@ -220,6 +276,19 @@  static const AVOption options[] = {
     { NULL},
 };
 
+static const AVCodecHWConfigInternal *v4l2_m2m_hw_configs[] = {
+    &(const AVCodecHWConfigInternal) {
+        .public = {
+            .pix_fmt     = AV_PIX_FMT_DRM_PRIME,
+            .methods     = AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX |
+                           AV_CODEC_HW_CONFIG_METHOD_INTERNAL,
+            .device_type = AV_HWDEVICE_TYPE_DRM
+        },
+        .hwaccel = NULL,
+    },
+    NULL
+};
+
 #define M2MDEC_CLASS(NAME) \
     static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \
         .class_name = #NAME "_v4l2m2m_decoder", \
@@ -240,7 +309,13 @@  static const AVOption options[] = {
         .init           = v4l2_decode_init, \
         .receive_frame  = v4l2_receive_frame, \
         .close          = v4l2_decode_close, \
+        .flush          = v4l2_flush, \
+        .pix_fmts       = (const enum AVPixelFormat[]) { AV_PIX_FMT_DRM_PRIME, \
+                                                         AV_PIX_FMT_NV12, \
+                                                         AV_PIX_FMT_YUV420P, \
+                                                         AV_PIX_FMT_NONE}, \
         .bsfs           = bsf_name, \
+        .hw_configs     = v4l2_m2m_hw_configs, \
         .capabilities   = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \
         .caps_internal  = FF_CODEC_CAP_SETS_PKT_DTS, \
         .wrapper_name   = "v4l2m2m", \