]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/v4l2_m2m_enc.c
avcodec: Constify AVCodecs
[ffmpeg] / libavcodec / v4l2_m2m_enc.c
index 636e1a96dde014d1d32c04655860a7960bf3701a..043fe80c695fff5b4767b9c8226a5c2652f62793 100644 (file)
 #include <linux/videodev2.h>
 #include <sys/ioctl.h>
 #include <search.h>
+#include "encode.h"
 #include "libavcodec/avcodec.h"
+#include "libavcodec/internal.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/pixfmt.h"
 #include "libavutil/opt.h"
+#include "profiles.h"
 #include "v4l2_context.h"
 #include "v4l2_m2m.h"
+#include "v4l2_fmt.h"
 
 #define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x
 #define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x
@@ -46,7 +50,7 @@ static inline void v4l2_set_timeperframe(V4L2m2mContext *s, unsigned int num, un
         av_log(s->avctx, AV_LOG_WARNING, "Failed to set timeperframe");
 }
 
-static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int value, const char *name)
+static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int value, const char *name, int log_warning)
 {
     struct v4l2_ext_controls ctrls = { { 0 } };
     struct v4l2_ext_control ctrl = { 0 };
@@ -58,15 +62,16 @@ static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed
 
     /* set ctrl*/
     ctrl.value = value;
-    ctrl.id = id ;
+    ctrl.id = id;
 
     if (ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0)
-        av_log(s->avctx, AV_LOG_WARNING, "Failed to set %s\n", name);
+        av_log(s->avctx, log_warning || errno != EINVAL ? AV_LOG_WARNING : AV_LOG_DEBUG,
+               "Failed to set %s: %s\n", name, strerror(errno));
     else
         av_log(s->avctx, AV_LOG_DEBUG, "Encoder: %s = %d\n", name, value);
 }
 
-static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int *value, const char *name)
+static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int *value, const char *name, int log_warning)
 {
     struct v4l2_ext_controls ctrls = { { 0 } };
     struct v4l2_ext_control ctrl = { 0 };
@@ -82,7 +87,8 @@ static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed i
 
     ret = ioctl(s->fd, VIDIOC_G_EXT_CTRLS, &ctrls);
     if (ret < 0) {
-        av_log(s->avctx, AV_LOG_WARNING, "Failed to set %s\n", name);
+        av_log(s->avctx, log_warning || errno != EINVAL ? AV_LOG_WARNING : AV_LOG_DEBUG,
+               "Failed to get %s\n", name);
         return ret;
     }
 
@@ -144,8 +150,8 @@ static int v4l2_check_b_frame_support(V4L2m2mContext *s)
     if (s->avctx->max_b_frames)
         av_log(s->avctx, AV_LOG_WARNING, "Encoder does not support b-frames yet\n");
 
-    v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames");
-    v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames, "number of B-frames");
+    v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames", 0);
+    v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames, "number of B-frames", 0);
     if (s->avctx->max_b_frames == 0)
         return 0;
 
@@ -154,6 +160,17 @@ static int v4l2_check_b_frame_support(V4L2m2mContext *s)
     return AVERROR_PATCHWELCOME;
 }
 
+static inline void v4l2_subscribe_eos_event(V4L2m2mContext *s)
+{
+    struct v4l2_event_subscription sub;
+
+    memset(&sub, 0, sizeof(sub));
+    sub.type = V4L2_EVENT_EOS;
+    if (ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub) < 0)
+        av_log(s->avctx, AV_LOG_WARNING,
+               "the v4l2 driver does not support end of stream VIDIOC_SUBSCRIBE_EVENT\n");
+}
+
 static int v4l2_prepare_encoder(V4L2m2mContext *s)
 {
     AVCodecContext *avctx = s->avctx;
@@ -163,6 +180,8 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s)
     /**
      * requirements
      */
+    v4l2_subscribe_eos_event(s);
+
     ret = v4l2_check_b_frame_support(s);
     if (ret)
         return ret;
@@ -171,12 +190,13 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s)
      * settingss
      */
     if (avctx->framerate.num || avctx->framerate.den)
-        v4l2_set_timeperframe(s, avctx->framerate.num, avctx->framerate.den);
+        v4l2_set_timeperframe(s, avctx->framerate.den, avctx->framerate.num);
 
     /* set ext ctrls */
-    v4l2_set_ext_ctrl(s, MPEG_CID(HEADER_MODE), MPEG_VIDEO(HEADER_MODE_SEPARATE), "header mode");
-    v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE) , avctx->bit_rate, "bit rate");
-    v4l2_set_ext_ctrl(s, MPEG_CID(GOP_SIZE), avctx->gop_size,"gop size");
+    v4l2_set_ext_ctrl(s, MPEG_CID(HEADER_MODE), MPEG_VIDEO(HEADER_MODE_SEPARATE), "header mode", 0);
+    v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE) , avctx->bit_rate, "bit rate", 1);
+    v4l2_set_ext_ctrl(s, MPEG_CID(FRAME_RC_ENABLE), 1, "frame level rate control", 0);
+    v4l2_set_ext_ctrl(s, MPEG_CID(GOP_SIZE), avctx->gop_size,"gop size", 1);
 
     av_log(avctx, AV_LOG_DEBUG,
         "Encoder Context: id (%d), profile (%d), frame rate(%d/%d), number b-frames (%d), "
@@ -186,26 +206,30 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s)
 
     switch (avctx->codec_id) {
     case AV_CODEC_ID_H264:
-        val = v4l2_h264_profile_from_ff(avctx->profile);
-        if (val < 0)
-            av_log(avctx, AV_LOG_WARNING, "h264 profile not found\n");
-        else
-            v4l2_set_ext_ctrl(s, MPEG_CID(H264_PROFILE), val, "h264 profile");
+        if (avctx->profile != FF_PROFILE_UNKNOWN) {
+            val = v4l2_h264_profile_from_ff(avctx->profile);
+            if (val < 0)
+                av_log(avctx, AV_LOG_WARNING, "h264 profile not found\n");
+            else
+                v4l2_set_ext_ctrl(s, MPEG_CID(H264_PROFILE), val, "h264 profile", 1);
+        }
         qmin_cid = MPEG_CID(H264_MIN_QP);
         qmax_cid = MPEG_CID(H264_MAX_QP);
         qmin = 0;
         qmax = 51;
         break;
     case AV_CODEC_ID_MPEG4:
-        val = v4l2_mpeg4_profile_from_ff(avctx->profile);
-        if (val < 0)
-            av_log(avctx, AV_LOG_WARNING, "mpeg4 profile not found\n");
-        else
-            v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_PROFILE), val, "mpeg4 profile");
+        if (avctx->profile != FF_PROFILE_UNKNOWN) {
+            val = v4l2_mpeg4_profile_from_ff(avctx->profile);
+            if (val < 0)
+                av_log(avctx, AV_LOG_WARNING, "mpeg4 profile not found\n");
+            else
+                v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_PROFILE), val, "mpeg4 profile", 1);
+        }
         qmin_cid = MPEG_CID(MPEG4_MIN_QP);
         qmax_cid = MPEG_CID(MPEG4_MAX_QP);
         if (avctx->flags & AV_CODEC_FLAG_QPEL)
-            v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_QPEL), 1, "qpel");
+            v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_QPEL), 1, "qpel", 1);
         qmin = 1;
         qmax = 31;
         break;
@@ -231,11 +255,18 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s)
         return 0;
     }
 
-    if (qmin != avctx->qmin || qmax != avctx->qmax)
-        av_log(avctx, AV_LOG_WARNING, "Encoder adjusted: qmin (%d), qmax (%d)\n", qmin, qmax);
+    if (avctx->qmin >= 0 && avctx->qmax >= 0 && avctx->qmin > avctx->qmax) {
+        av_log(avctx, AV_LOG_WARNING, "Invalid qmin:%d qmax:%d. qmin should not "
+                                      "exceed qmax\n", avctx->qmin, avctx->qmax);
+    } else {
+        qmin = avctx->qmin >= 0 ? avctx->qmin : qmin;
+        qmax = avctx->qmax >= 0 ? avctx->qmax : qmax;
+    }
 
-    v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale");
-    v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale");
+    v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale",
+                      avctx->qmin >= 0);
+    v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale",
+                      avctx->qmax >= 0);
 
     return 0;
 }
@@ -245,6 +276,11 @@ static int v4l2_send_frame(AVCodecContext *avctx, const AVFrame *frame)
     V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
     V4L2Context *const output = &s->output;
 
+#ifdef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME
+    if (frame && frame->pict_type == AV_PICTURE_TYPE_I)
+        v4l2_set_ext_ctrl(s, MPEG_CID(FORCE_KEY_FRAME), 0, "force key frame", 1);
+#endif
+
     return ff_v4l2_context_enqueue_frame(output, frame);
 }
 
@@ -253,15 +289,32 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
     V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
     V4L2Context *const capture = &s->capture;
     V4L2Context *const output = &s->output;
+    AVFrame *frame = s->frame;
     int ret;
 
     if (s->draining)
         goto dequeue;
 
+    if (!frame->buf[0]) {
+        ret = ff_encode_get_frame(avctx, frame);
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+
+        if (ret == AVERROR_EOF)
+            frame = NULL;
+    }
+
+    ret = v4l2_send_frame(avctx, frame);
+    if (ret != AVERROR(EAGAIN))
+        av_frame_unref(frame);
+
+    if (ret < 0 && ret != AVERROR(EAGAIN))
+        return ret;
+
     if (!output->streamon) {
         ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON);
         if (ret) {
-            av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF failed on output context\n");
+            av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on output context\n");
             return ret;
         }
     }
@@ -282,9 +335,12 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx)
 {
     V4L2Context *capture, *output;
     V4L2m2mContext *s;
+    V4L2m2mPriv *priv = avctx->priv_data;
+    enum AVPixelFormat pix_fmt_output;
+    uint32_t v4l2_fmt_output;
     int ret;
 
-    ret = ff_v4l2_m2m_create_context(avctx, &s);
+    ret = ff_v4l2_m2m_create_context(priv, &s);
     if (ret < 0)
         return ret;
 
@@ -303,50 +359,86 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx)
     capture->av_codec_id = avctx->codec_id;
     capture->av_pix_fmt = AV_PIX_FMT_NONE;
 
-    ret = ff_v4l2_m2m_codec_init(avctx);
+    s->avctx = avctx;
+    ret = ff_v4l2_m2m_codec_init(priv);
     if (ret) {
         av_log(avctx, AV_LOG_ERROR, "can't configure encoder\n");
         return ret;
     }
 
+    if (V4L2_TYPE_IS_MULTIPLANAR(output->type))
+        v4l2_fmt_output = output->format.fmt.pix_mp.pixelformat;
+    else
+        v4l2_fmt_output = output->format.fmt.pix.pixelformat;
+
+    pix_fmt_output = ff_v4l2_format_v4l2_to_avfmt(v4l2_fmt_output, AV_CODEC_ID_RAWVIDEO);
+    if (pix_fmt_output != avctx->pix_fmt) {
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt_output);
+        av_log(avctx, AV_LOG_ERROR, "Encoder requires %s pixel format.\n", desc->name);
+        return AVERROR(EINVAL);
+    }
+
     return v4l2_prepare_encoder(s);
 }
 
+static av_cold int v4l2_encode_close(AVCodecContext *avctx)
+{
+    return ff_v4l2_m2m_codec_end(avctx->priv_data);
+}
+
 #define OFFSET(x) offsetof(V4L2m2mPriv, x)
 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 
+#define V4L_M2M_CAPTURE_OPTS \
+    V4L_M2M_DEFAULT_OPTS,\
+    { "num_capture_buffers", "Number of buffers in the capture context", \
+        OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 4 }, 4, INT_MAX, FLAGS }
+
+static const AVOption mpeg4_options[] = {
+    V4L_M2M_CAPTURE_OPTS,
+    FF_MPEG4_PROFILE_OPTS
+    { NULL },
+};
+
 static const AVOption options[] = {
-    V4L_M2M_DEFAULT_OPTS,
-    { "num_capture_buffers", "Number of buffers in the capture context",
-        OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 4 }, 4, INT_MAX, FLAGS },
+    V4L_M2M_CAPTURE_OPTS,
     { NULL },
 };
 
-#define M2MENC(NAME, LONGNAME, CODEC) \
-static const AVClass v4l2_m2m_ ## NAME ## _enc_class = {\
-    .class_name = #NAME "_v4l2_m2m_encoder",\
-    .item_name  = av_default_item_name,\
-    .option     = options,\
-    .version    = LIBAVUTIL_VERSION_INT,\
-};\
-\
-AVCodec ff_ ## NAME ## _v4l2m2m_encoder = { \
-    .name           = #NAME "_v4l2m2m" ,\
-    .long_name      = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " encoder wrapper"),\
-    .type           = AVMEDIA_TYPE_VIDEO,\
-    .id             = CODEC ,\
-    .priv_data_size = sizeof(V4L2m2mPriv),\
-    .priv_class     = &v4l2_m2m_ ## NAME ##_enc_class,\
-    .init           = v4l2_encode_init,\
-    .send_frame     = v4l2_send_frame,\
-    .receive_packet = v4l2_receive_packet,\
-    .close          = ff_v4l2_m2m_codec_end,\
-    .capabilities   = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY, \
-    .wrapper_name   = "v4l2m2m", \
+static const AVCodecDefault v4l2_m2m_defaults[] = {
+    { "qmin", "-1" },
+    { "qmax", "-1" },
+    { NULL },
 };
 
-M2MENC(mpeg4,"MPEG4", AV_CODEC_ID_MPEG4);
-M2MENC(h263, "H.263", AV_CODEC_ID_H263);
-M2MENC(h264, "H.264", AV_CODEC_ID_H264);
-M2MENC(hevc, "HEVC",  AV_CODEC_ID_HEVC);
-M2MENC(vp8,  "VP8",   AV_CODEC_ID_VP8);
+#define M2MENC_CLASS(NAME, OPTIONS_NAME) \
+    static const AVClass v4l2_m2m_ ## NAME ## _enc_class = { \
+        .class_name = #NAME "_v4l2m2m_encoder", \
+        .item_name  = av_default_item_name, \
+        .option     = OPTIONS_NAME, \
+        .version    = LIBAVUTIL_VERSION_INT, \
+    };
+
+#define M2MENC(NAME, LONGNAME, OPTIONS_NAME, CODEC) \
+    M2MENC_CLASS(NAME, OPTIONS_NAME) \
+    const AVCodec ff_ ## NAME ## _v4l2m2m_encoder = { \
+        .name           = #NAME "_v4l2m2m" , \
+        .long_name      = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " encoder wrapper"), \
+        .type           = AVMEDIA_TYPE_VIDEO, \
+        .id             = CODEC , \
+        .priv_data_size = sizeof(V4L2m2mPriv), \
+        .priv_class     = &v4l2_m2m_ ## NAME ##_enc_class, \
+        .init           = v4l2_encode_init, \
+        .receive_packet = v4l2_receive_packet, \
+        .close          = v4l2_encode_close, \
+        .defaults       = v4l2_m2m_defaults, \
+        .capabilities   = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY, \
+        .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP, \
+        .wrapper_name   = "v4l2m2m", \
+    }
+
+M2MENC(mpeg4,"MPEG4", mpeg4_options, AV_CODEC_ID_MPEG4);
+M2MENC(h263, "H.263", options,       AV_CODEC_ID_H263);
+M2MENC(h264, "H.264", options,       AV_CODEC_ID_H264);
+M2MENC(hevc, "HEVC",  options,       AV_CODEC_ID_HEVC);
+M2MENC(vp8,  "VP8",   options,       AV_CODEC_ID_VP8);