| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * V4L2 mem2mem encoders | ||
| 3 | * | ||
| 4 | * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org> | ||
| 5 | * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org> | ||
| 6 | * | ||
| 7 | * This file is part of FFmpeg. | ||
| 8 | * | ||
| 9 | * FFmpeg is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU Lesser General Public | ||
| 11 | * License as published by the Free Software Foundation; either | ||
| 12 | * version 2.1 of the License, or (at your option) any later version. | ||
| 13 | * | ||
| 14 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 17 | * Lesser General Public License for more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU Lesser General Public | ||
| 20 | * License along with FFmpeg; if not, write to the Free Software | ||
| 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/videodev2.h> | ||
| 25 | #include <sys/ioctl.h> | ||
| 26 | #include <search.h> | ||
| 27 | #include "encode.h" | ||
| 28 | #include "libavcodec/avcodec.h" | ||
| 29 | #include "libavutil/pixdesc.h" | ||
| 30 | #include "libavutil/pixfmt.h" | ||
| 31 | #include "libavutil/opt.h" | ||
| 32 | #include "codec_internal.h" | ||
| 33 | #include "profiles.h" | ||
| 34 | #include "v4l2_context.h" | ||
| 35 | #include "v4l2_m2m.h" | ||
| 36 | #include "v4l2_fmt.h" | ||
| 37 | |||
| 38 | #define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x | ||
| 39 | #define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x | ||
| 40 | |||
| 41 | ✗ | static inline void v4l2_set_timeperframe(V4L2m2mContext *s, unsigned int num, unsigned int den) | |
| 42 | { | ||
| 43 | ✗ | struct v4l2_streamparm parm = { 0 }; | |
| 44 | |||
| 45 | ✗ | parm.type = V4L2_TYPE_IS_MULTIPLANAR(s->output.type) ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT; | |
| 46 | ✗ | parm.parm.output.timeperframe.denominator = den; | |
| 47 | ✗ | parm.parm.output.timeperframe.numerator = num; | |
| 48 | |||
| 49 | ✗ | if (ioctl(s->fd, VIDIOC_S_PARM, &parm) < 0) | |
| 50 | ✗ | av_log(s->avctx, AV_LOG_WARNING, "Failed to set timeperframe"); | |
| 51 | ✗ | } | |
| 52 | |||
| 53 | ✗ | static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int value, const char *name, int log_warning) | |
| 54 | { | ||
| 55 | ✗ | struct v4l2_ext_controls ctrls = { { 0 } }; | |
| 56 | ✗ | struct v4l2_ext_control ctrl = { 0 }; | |
| 57 | |||
| 58 | /* set ctrls */ | ||
| 59 | ✗ | ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; | |
| 60 | ✗ | ctrls.controls = &ctrl; | |
| 61 | ✗ | ctrls.count = 1; | |
| 62 | |||
| 63 | /* set ctrl*/ | ||
| 64 | ✗ | ctrl.value = value; | |
| 65 | ✗ | ctrl.id = id; | |
| 66 | |||
| 67 | ✗ | if (ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0) | |
| 68 | ✗ | av_log(s->avctx, log_warning || errno != EINVAL ? AV_LOG_WARNING : AV_LOG_DEBUG, | |
| 69 | ✗ | "Failed to set %s: %s\n", name, strerror(errno)); | |
| 70 | else | ||
| 71 | ✗ | av_log(s->avctx, AV_LOG_DEBUG, "Encoder: %s = %d\n", name, value); | |
| 72 | ✗ | } | |
| 73 | |||
| 74 | ✗ | static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id, signed int *value, const char *name, int log_warning) | |
| 75 | { | ||
| 76 | ✗ | struct v4l2_ext_controls ctrls = { { 0 } }; | |
| 77 | ✗ | struct v4l2_ext_control ctrl = { 0 }; | |
| 78 | int ret; | ||
| 79 | |||
| 80 | /* set ctrls */ | ||
| 81 | ✗ | ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; | |
| 82 | ✗ | ctrls.controls = &ctrl; | |
| 83 | ✗ | ctrls.count = 1; | |
| 84 | |||
| 85 | /* set ctrl*/ | ||
| 86 | ✗ | ctrl.id = id ; | |
| 87 | |||
| 88 | ✗ | ret = ioctl(s->fd, VIDIOC_G_EXT_CTRLS, &ctrls); | |
| 89 | ✗ | if (ret < 0) { | |
| 90 | ✗ | av_log(s->avctx, log_warning || errno != EINVAL ? AV_LOG_WARNING : AV_LOG_DEBUG, | |
| 91 | "Failed to get %s\n", name); | ||
| 92 | ✗ | return ret; | |
| 93 | } | ||
| 94 | |||
| 95 | ✗ | *value = ctrl.value; | |
| 96 | |||
| 97 | ✗ | return 0; | |
| 98 | } | ||
| 99 | |||
| 100 | ✗ | static inline unsigned int v4l2_h264_profile_from_ff(int p) | |
| 101 | { | ||
| 102 | static const struct h264_profile { | ||
| 103 | unsigned int ffmpeg_val; | ||
| 104 | unsigned int v4l2_val; | ||
| 105 | } profile[] = { | ||
| 106 | { AV_PROFILE_H264_CONSTRAINED_BASELINE, MPEG_VIDEO(H264_PROFILE_CONSTRAINED_BASELINE) }, | ||
| 107 | { AV_PROFILE_H264_HIGH_444_PREDICTIVE, MPEG_VIDEO(H264_PROFILE_HIGH_444_PREDICTIVE) }, | ||
| 108 | { AV_PROFILE_H264_HIGH_422_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_422_INTRA) }, | ||
| 109 | { AV_PROFILE_H264_HIGH_444_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_444_INTRA) }, | ||
| 110 | { AV_PROFILE_H264_HIGH_10_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_10_INTRA) }, | ||
| 111 | { AV_PROFILE_H264_HIGH_422, MPEG_VIDEO(H264_PROFILE_HIGH_422) }, | ||
| 112 | { AV_PROFILE_H264_BASELINE, MPEG_VIDEO(H264_PROFILE_BASELINE) }, | ||
| 113 | { AV_PROFILE_H264_EXTENDED, MPEG_VIDEO(H264_PROFILE_EXTENDED) }, | ||
| 114 | { AV_PROFILE_H264_HIGH_10, MPEG_VIDEO(H264_PROFILE_HIGH_10) }, | ||
| 115 | { AV_PROFILE_H264_MAIN, MPEG_VIDEO(H264_PROFILE_MAIN) }, | ||
| 116 | { AV_PROFILE_H264_HIGH, MPEG_VIDEO(H264_PROFILE_HIGH) }, | ||
| 117 | }; | ||
| 118 | int i; | ||
| 119 | |||
| 120 | ✗ | for (i = 0; i < FF_ARRAY_ELEMS(profile); i++) { | |
| 121 | ✗ | if (profile[i].ffmpeg_val == p) | |
| 122 | ✗ | return profile[i].v4l2_val; | |
| 123 | } | ||
| 124 | ✗ | return AVERROR(ENOENT); | |
| 125 | } | ||
| 126 | |||
| 127 | ✗ | static inline int v4l2_mpeg4_profile_from_ff(int p) | |
| 128 | { | ||
| 129 | static const struct mpeg4_profile { | ||
| 130 | unsigned int ffmpeg_val; | ||
| 131 | unsigned int v4l2_val; | ||
| 132 | } profile[] = { | ||
| 133 | { AV_PROFILE_MPEG4_ADVANCED_CODING, MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY) }, | ||
| 134 | { AV_PROFILE_MPEG4_ADVANCED_SIMPLE, MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_SIMPLE) }, | ||
| 135 | { AV_PROFILE_MPEG4_SIMPLE_SCALABLE, MPEG_VIDEO(MPEG4_PROFILE_SIMPLE_SCALABLE) }, | ||
| 136 | { AV_PROFILE_MPEG4_SIMPLE, MPEG_VIDEO(MPEG4_PROFILE_SIMPLE) }, | ||
| 137 | { AV_PROFILE_MPEG4_CORE, MPEG_VIDEO(MPEG4_PROFILE_CORE) }, | ||
| 138 | }; | ||
| 139 | int i; | ||
| 140 | |||
| 141 | ✗ | for (i = 0; i < FF_ARRAY_ELEMS(profile); i++) { | |
| 142 | ✗ | if (profile[i].ffmpeg_val == p) | |
| 143 | ✗ | return profile[i].v4l2_val; | |
| 144 | } | ||
| 145 | ✗ | return AVERROR(ENOENT); | |
| 146 | } | ||
| 147 | |||
| 148 | ✗ | static int v4l2_check_b_frame_support(V4L2m2mContext *s) | |
| 149 | { | ||
| 150 | ✗ | if (s->avctx->max_b_frames) | |
| 151 | ✗ | av_log(s->avctx, AV_LOG_WARNING, "Encoder does not support b-frames yet\n"); | |
| 152 | |||
| 153 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames", 0); | |
| 154 | ✗ | v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames, "number of B-frames", 0); | |
| 155 | ✗ | if (s->avctx->max_b_frames == 0) | |
| 156 | ✗ | return 0; | |
| 157 | |||
| 158 | ✗ | avpriv_report_missing_feature(s->avctx, "DTS/PTS calculation for V4L2 encoding"); | |
| 159 | |||
| 160 | ✗ | return AVERROR_PATCHWELCOME; | |
| 161 | } | ||
| 162 | |||
| 163 | ✗ | static inline void v4l2_subscribe_eos_event(V4L2m2mContext *s) | |
| 164 | { | ||
| 165 | struct v4l2_event_subscription sub; | ||
| 166 | |||
| 167 | ✗ | memset(&sub, 0, sizeof(sub)); | |
| 168 | ✗ | sub.type = V4L2_EVENT_EOS; | |
| 169 | ✗ | if (ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub) < 0) | |
| 170 | ✗ | av_log(s->avctx, AV_LOG_WARNING, | |
| 171 | "the v4l2 driver does not support end of stream VIDIOC_SUBSCRIBE_EVENT\n"); | ||
| 172 | ✗ | } | |
| 173 | |||
| 174 | ✗ | static int v4l2_prepare_encoder(V4L2m2mContext *s) | |
| 175 | { | ||
| 176 | ✗ | AVCodecContext *avctx = s->avctx; | |
| 177 | int qmin_cid, qmax_cid, qmin, qmax; | ||
| 178 | int ret, val; | ||
| 179 | |||
| 180 | /** | ||
| 181 | * requirements | ||
| 182 | */ | ||
| 183 | ✗ | v4l2_subscribe_eos_event(s); | |
| 184 | |||
| 185 | ✗ | ret = v4l2_check_b_frame_support(s); | |
| 186 | ✗ | if (ret) | |
| 187 | ✗ | return ret; | |
| 188 | |||
| 189 | /** | ||
| 190 | * settings | ||
| 191 | */ | ||
| 192 | ✗ | if (avctx->framerate.num || avctx->framerate.den) | |
| 193 | ✗ | v4l2_set_timeperframe(s, avctx->framerate.den, avctx->framerate.num); | |
| 194 | |||
| 195 | /* set ext ctrls */ | ||
| 196 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(HEADER_MODE), MPEG_VIDEO(HEADER_MODE_SEPARATE), "header mode", 0); | |
| 197 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE) , avctx->bit_rate, "bit rate", 1); | |
| 198 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(FRAME_RC_ENABLE), 1, "frame level rate control", 0); | |
| 199 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(GOP_SIZE), avctx->gop_size,"gop size", 1); | |
| 200 | |||
| 201 | ✗ | av_log(avctx, AV_LOG_DEBUG, | |
| 202 | "Encoder Context: id (%d), profile (%d), frame rate(%d/%d), number b-frames (%d), " | ||
| 203 | "gop size (%d), bit rate (%"PRId64"), qmin (%d), qmax (%d)\n", | ||
| 204 | ✗ | avctx->codec_id, avctx->profile, avctx->framerate.num, avctx->framerate.den, | |
| 205 | avctx->max_b_frames, avctx->gop_size, avctx->bit_rate, avctx->qmin, avctx->qmax); | ||
| 206 | |||
| 207 | ✗ | switch (avctx->codec_id) { | |
| 208 | ✗ | case AV_CODEC_ID_H264: | |
| 209 | ✗ | if (avctx->profile != AV_PROFILE_UNKNOWN) { | |
| 210 | ✗ | val = v4l2_h264_profile_from_ff(avctx->profile); | |
| 211 | ✗ | if (val < 0) | |
| 212 | ✗ | av_log(avctx, AV_LOG_WARNING, "h264 profile not found\n"); | |
| 213 | else | ||
| 214 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(H264_PROFILE), val, "h264 profile", 1); | |
| 215 | } | ||
| 216 | ✗ | qmin_cid = MPEG_CID(H264_MIN_QP); | |
| 217 | ✗ | qmax_cid = MPEG_CID(H264_MAX_QP); | |
| 218 | ✗ | qmin = 0; | |
| 219 | ✗ | qmax = 51; | |
| 220 | ✗ | break; | |
| 221 | ✗ | case AV_CODEC_ID_MPEG4: | |
| 222 | ✗ | if (avctx->profile != AV_PROFILE_UNKNOWN) { | |
| 223 | ✗ | val = v4l2_mpeg4_profile_from_ff(avctx->profile); | |
| 224 | ✗ | if (val < 0) | |
| 225 | ✗ | av_log(avctx, AV_LOG_WARNING, "mpeg4 profile not found\n"); | |
| 226 | else | ||
| 227 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_PROFILE), val, "mpeg4 profile", 1); | |
| 228 | } | ||
| 229 | ✗ | qmin_cid = MPEG_CID(MPEG4_MIN_QP); | |
| 230 | ✗ | qmax_cid = MPEG_CID(MPEG4_MAX_QP); | |
| 231 | ✗ | if (avctx->flags & AV_CODEC_FLAG_QPEL) | |
| 232 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_QPEL), 1, "qpel", 1); | |
| 233 | ✗ | qmin = 1; | |
| 234 | ✗ | qmax = 31; | |
| 235 | ✗ | break; | |
| 236 | ✗ | case AV_CODEC_ID_H263: | |
| 237 | ✗ | qmin_cid = MPEG_CID(H263_MIN_QP); | |
| 238 | ✗ | qmax_cid = MPEG_CID(H263_MAX_QP); | |
| 239 | ✗ | qmin = 1; | |
| 240 | ✗ | qmax = 31; | |
| 241 | ✗ | break; | |
| 242 | ✗ | case AV_CODEC_ID_VP8: | |
| 243 | ✗ | qmin_cid = MPEG_CID(VPX_MIN_QP); | |
| 244 | ✗ | qmax_cid = MPEG_CID(VPX_MAX_QP); | |
| 245 | ✗ | qmin = 0; | |
| 246 | ✗ | qmax = 127; | |
| 247 | ✗ | break; | |
| 248 | ✗ | case AV_CODEC_ID_VP9: | |
| 249 | ✗ | qmin_cid = MPEG_CID(VPX_MIN_QP); | |
| 250 | ✗ | qmax_cid = MPEG_CID(VPX_MAX_QP); | |
| 251 | ✗ | qmin = 0; | |
| 252 | ✗ | qmax = 255; | |
| 253 | ✗ | break; | |
| 254 | ✗ | default: | |
| 255 | ✗ | return 0; | |
| 256 | } | ||
| 257 | |||
| 258 | ✗ | if (avctx->qmin >= 0 && avctx->qmax >= 0 && avctx->qmin > avctx->qmax) { | |
| 259 | ✗ | av_log(avctx, AV_LOG_WARNING, "Invalid qmin:%d qmax:%d. qmin should not " | |
| 260 | "exceed qmax\n", avctx->qmin, avctx->qmax); | ||
| 261 | } else { | ||
| 262 | ✗ | qmin = avctx->qmin >= 0 ? avctx->qmin : qmin; | |
| 263 | ✗ | qmax = avctx->qmax >= 0 ? avctx->qmax : qmax; | |
| 264 | } | ||
| 265 | |||
| 266 | ✗ | v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale", | |
| 267 | ✗ | avctx->qmin >= 0); | |
| 268 | ✗ | v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale", | |
| 269 | ✗ | avctx->qmax >= 0); | |
| 270 | |||
| 271 | ✗ | return 0; | |
| 272 | } | ||
| 273 | |||
| 274 | ✗ | static int v4l2_send_frame(AVCodecContext *avctx, const AVFrame *frame) | |
| 275 | { | ||
| 276 | ✗ | V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; | |
| 277 | ✗ | V4L2Context *const output = &s->output; | |
| 278 | |||
| 279 | #ifdef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME | ||
| 280 | ✗ | if (frame && frame->pict_type == AV_PICTURE_TYPE_I) | |
| 281 | ✗ | v4l2_set_ext_ctrl(s, MPEG_CID(FORCE_KEY_FRAME), 0, "force key frame", 1); | |
| 282 | #endif | ||
| 283 | |||
| 284 | ✗ | return ff_v4l2_context_enqueue_frame(output, frame); | |
| 285 | } | ||
| 286 | |||
| 287 | ✗ | static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) | |
| 288 | { | ||
| 289 | ✗ | V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; | |
| 290 | ✗ | V4L2Context *const capture = &s->capture; | |
| 291 | ✗ | V4L2Context *const output = &s->output; | |
| 292 | ✗ | AVFrame *frame = s->frame; | |
| 293 | int ret; | ||
| 294 | |||
| 295 | ✗ | if (s->draining) | |
| 296 | ✗ | goto dequeue; | |
| 297 | |||
| 298 | ✗ | if (!frame->buf[0]) { | |
| 299 | ✗ | ret = ff_encode_get_frame(avctx, frame); | |
| 300 | ✗ | if (ret < 0 && ret != AVERROR_EOF) | |
| 301 | ✗ | return ret; | |
| 302 | |||
| 303 | ✗ | if (ret == AVERROR_EOF) | |
| 304 | ✗ | frame = NULL; | |
| 305 | } | ||
| 306 | |||
| 307 | ✗ | ret = v4l2_send_frame(avctx, frame); | |
| 308 | ✗ | if (ret != AVERROR(EAGAIN)) | |
| 309 | ✗ | av_frame_unref(frame); | |
| 310 | |||
| 311 | ✗ | if (ret < 0 && ret != AVERROR(EAGAIN)) | |
| 312 | ✗ | return ret; | |
| 313 | |||
| 314 | ✗ | if (!output->streamon) { | |
| 315 | ✗ | ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON); | |
| 316 | ✗ | if (ret) { | |
| 317 | ✗ | av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on output context\n"); | |
| 318 | ✗ | return ret; | |
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | ✗ | if (!capture->streamon) { | |
| 323 | ✗ | ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON); | |
| 324 | ✗ | if (ret) { | |
| 325 | ✗ | av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on capture context\n"); | |
| 326 | ✗ | return ret; | |
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | ✗ | dequeue: | |
| 331 | ✗ | return ff_v4l2_context_dequeue_packet(capture, avpkt); | |
| 332 | } | ||
| 333 | |||
| 334 | ✗ | static av_cold int v4l2_encode_init(AVCodecContext *avctx) | |
| 335 | { | ||
| 336 | V4L2Context *capture, *output; | ||
| 337 | V4L2m2mContext *s; | ||
| 338 | ✗ | V4L2m2mPriv *priv = avctx->priv_data; | |
| 339 | enum AVPixelFormat pix_fmt_output; | ||
| 340 | uint32_t v4l2_fmt_output; | ||
| 341 | int ret; | ||
| 342 | |||
| 343 | ✗ | ret = ff_v4l2_m2m_create_context(priv, &s); | |
| 344 | ✗ | if (ret < 0) | |
| 345 | ✗ | return ret; | |
| 346 | |||
| 347 | ✗ | capture = &s->capture; | |
| 348 | ✗ | output = &s->output; | |
| 349 | |||
| 350 | /* common settings output/capture */ | ||
| 351 | ✗ | output->height = capture->height = avctx->height; | |
| 352 | ✗ | output->width = capture->width = avctx->width; | |
| 353 | |||
| 354 | /* output context */ | ||
| 355 | ✗ | output->av_codec_id = AV_CODEC_ID_RAWVIDEO; | |
| 356 | ✗ | output->av_pix_fmt = avctx->pix_fmt; | |
| 357 | |||
| 358 | /* capture context */ | ||
| 359 | ✗ | capture->av_codec_id = avctx->codec_id; | |
| 360 | ✗ | capture->av_pix_fmt = AV_PIX_FMT_NONE; | |
| 361 | |||
| 362 | ✗ | s->avctx = avctx; | |
| 363 | ✗ | ret = ff_v4l2_m2m_codec_init(priv); | |
| 364 | ✗ | if (ret) { | |
| 365 | ✗ | av_log(avctx, AV_LOG_ERROR, "can't configure encoder\n"); | |
| 366 | ✗ | return ret; | |
| 367 | } | ||
| 368 | |||
| 369 | ✗ | if (V4L2_TYPE_IS_MULTIPLANAR(output->type)) | |
| 370 | ✗ | v4l2_fmt_output = output->format.fmt.pix_mp.pixelformat; | |
| 371 | else | ||
| 372 | ✗ | v4l2_fmt_output = output->format.fmt.pix.pixelformat; | |
| 373 | |||
| 374 | ✗ | pix_fmt_output = ff_v4l2_format_v4l2_to_avfmt(v4l2_fmt_output, AV_CODEC_ID_RAWVIDEO); | |
| 375 | ✗ | if (pix_fmt_output != avctx->pix_fmt) { | |
| 376 | ✗ | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt_output); | |
| 377 | ✗ | av_log(avctx, AV_LOG_ERROR, "Encoder requires %s pixel format.\n", desc->name); | |
| 378 | ✗ | return AVERROR(EINVAL); | |
| 379 | } | ||
| 380 | |||
| 381 | ✗ | return v4l2_prepare_encoder(s); | |
| 382 | } | ||
| 383 | |||
| 384 | ✗ | static av_cold int v4l2_encode_close(AVCodecContext *avctx) | |
| 385 | { | ||
| 386 | ✗ | return ff_v4l2_m2m_codec_end(avctx->priv_data); | |
| 387 | } | ||
| 388 | |||
| 389 | #define OFFSET(x) offsetof(V4L2m2mPriv, x) | ||
| 390 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM | ||
| 391 | |||
| 392 | #define V4L_M2M_CAPTURE_OPTS \ | ||
| 393 | V4L_M2M_DEFAULT_OPTS,\ | ||
| 394 | { "num_capture_buffers", "Number of buffers in the capture context", \ | ||
| 395 | OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 4 }, 4, INT_MAX, FLAGS } | ||
| 396 | |||
| 397 | static const AVOption mpeg4_options[] = { | ||
| 398 | V4L_M2M_CAPTURE_OPTS, | ||
| 399 | FF_MPEG4_PROFILE_OPTS | ||
| 400 | { NULL }, | ||
| 401 | }; | ||
| 402 | |||
| 403 | static const AVOption options[] = { | ||
| 404 | V4L_M2M_CAPTURE_OPTS, | ||
| 405 | { NULL }, | ||
| 406 | }; | ||
| 407 | |||
| 408 | static const FFCodecDefault v4l2_m2m_defaults[] = { | ||
| 409 | { "qmin", "-1" }, | ||
| 410 | { "qmax", "-1" }, | ||
| 411 | { NULL }, | ||
| 412 | }; | ||
| 413 | |||
| 414 | #define M2MENC_CLASS(NAME, OPTIONS_NAME) \ | ||
| 415 | static const AVClass v4l2_m2m_ ## NAME ## _enc_class = { \ | ||
| 416 | .class_name = #NAME "_v4l2m2m_encoder", \ | ||
| 417 | .item_name = av_default_item_name, \ | ||
| 418 | .option = OPTIONS_NAME, \ | ||
| 419 | .version = LIBAVUTIL_VERSION_INT, \ | ||
| 420 | }; | ||
| 421 | |||
| 422 | #define M2MENC(NAME, LONGNAME, OPTIONS_NAME, CODEC) \ | ||
| 423 | M2MENC_CLASS(NAME, OPTIONS_NAME) \ | ||
| 424 | const FFCodec ff_ ## NAME ## _v4l2m2m_encoder = { \ | ||
| 425 | .p.name = #NAME "_v4l2m2m" , \ | ||
| 426 | CODEC_LONG_NAME("V4L2 mem2mem " LONGNAME " encoder wrapper"), \ | ||
| 427 | .p.type = AVMEDIA_TYPE_VIDEO, \ | ||
| 428 | .p.id = CODEC , \ | ||
| 429 | .priv_data_size = sizeof(V4L2m2mPriv), \ | ||
| 430 | .p.priv_class = &v4l2_m2m_ ## NAME ##_enc_class, \ | ||
| 431 | .init = v4l2_encode_init, \ | ||
| 432 | FF_CODEC_RECEIVE_PACKET_CB(v4l2_receive_packet), \ | ||
| 433 | .close = v4l2_encode_close, \ | ||
| 434 | .defaults = v4l2_m2m_defaults, \ | ||
| 435 | .p.capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY, \ | ||
| 436 | .color_ranges = AVCOL_RANGE_MPEG, \ | ||
| 437 | .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ | ||
| 438 | FF_CODEC_CAP_INIT_CLEANUP, \ | ||
| 439 | .p.wrapper_name = "v4l2m2m", \ | ||
| 440 | } | ||
| 441 | |||
| 442 | M2MENC(mpeg4,"MPEG4", mpeg4_options, AV_CODEC_ID_MPEG4); | ||
| 443 | M2MENC(h263, "H.263", options, AV_CODEC_ID_H263); | ||
| 444 | M2MENC(h264, "H.264", options, AV_CODEC_ID_H264); | ||
| 445 | M2MENC(hevc, "HEVC", options, AV_CODEC_ID_HEVC); | ||
| 446 | M2MENC(vp8, "VP8", options, AV_CODEC_ID_VP8); | ||
| 447 |