]>
Commit | Line | Data |
---|---|---|
6838be8a JP |
1 | From 40a990827399c05ad3ce3f8242321bd8a67aa0bd Mon Sep 17 00:00:00 2001 |
2 | From: Lukas Rusak <lorusak@gmail.com> | |
3 | Date: Tue, 24 Apr 2018 23:00:23 -0700 | |
4 | Subject: [PATCH 1/9] libavcodec: v4l2m2m: output AVDRMFrameDescriptor | |
5 | ||
6 | This allows for a zero-copy output by exporting the v4l2 buffer then wrapping that buffer | |
7 | in the AVDRMFrameDescriptor like it is done in rkmpp. | |
8 | ||
9 | This has been in use for quite some time with great success on many platforms including: | |
10 | - Amlogic S905 | |
11 | - Raspberry Pi | |
12 | - i.MX6 | |
13 | - Dragonboard 410c | |
14 | ||
15 | This was developed in conjunction with Kodi to allow handling the zero-copy buffer rendering. | |
16 | A simply utility for testing is also available here: https://github.com/BayLibre/ffmpeg-drm | |
17 | ||
18 | todo: | |
19 | - allow selecting pixel format output from decoder | |
20 | - allow configuring amount of output and capture buffers | |
21 | ||
22 | V2: | |
23 | - allow selecting AV_PIX_FMT_DRM_PRIME | |
24 | ||
25 | V3: | |
26 | - use get_format to select AV_PIX_FMT_DRM_PRIME | |
27 | - use hw_configs | |
28 | - add handling of AV_PIX_FMT_YUV420P format (for raspberry pi) | |
29 | - add handling of AV_PIX_FMT_YUYV422 format (for i.MX6 coda decoder) | |
30 | ||
31 | V4: | |
32 | - rebased on 4.2.2 | |
33 | ||
34 | V5: | |
35 | - rebased on 4.3 | |
36 | --- | |
37 | libavcodec/v4l2_buffers.c | 155 ++++++++++++++++++++++++++++++++++++-- | |
38 | libavcodec/v4l2_buffers.h | 4 + | |
39 | libavcodec/v4l2_context.c | 40 +++++++++- | |
40 | libavcodec/v4l2_m2m.h | 3 + | |
41 | libavcodec/v4l2_m2m_dec.c | 23 ++++++ | |
42 | 5 files changed, 213 insertions(+), 12 deletions(-) | |
43 | ||
44 | diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c | |
45 | index 4b2679eb38..cbd3e5680d 100644 | |
46 | --- a/libavcodec/v4l2_buffers.c | |
47 | +++ b/libavcodec/v4l2_buffers.c | |
48 | @@ -21,6 +21,7 @@ | |
49 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
50 | */ | |
51 | ||
52 | +#include <drm/drm_fourcc.h> | |
53 | #include <linux/videodev2.h> | |
54 | #include <sys/ioctl.h> | |
55 | #include <sys/mman.h> | |
56 | @@ -30,6 +31,7 @@ | |
57 | #include "libavcodec/avcodec.h" | |
58 | #include "libavcodec/internal.h" | |
59 | #include "libavutil/pixdesc.h" | |
60 | +#include "libavutil/hwcontext.h" | |
61 | #include "v4l2_context.h" | |
62 | #include "v4l2_buffers.h" | |
63 | #include "v4l2_m2m.h" | |
64 | @@ -210,7 +212,79 @@ static enum AVColorTransferCharacteristic v4l2_get_color_trc(V4L2Buffer *buf) | |
65 | return AVCOL_TRC_UNSPECIFIED; | |
66 | } | |
67 | ||
68 | -static void v4l2_free_buffer(void *opaque, uint8_t *unused) | |
69 | +static uint8_t * v4l2_get_drm_frame(V4L2Buffer *avbuf) | |
70 | +{ | |
71 | + AVDRMFrameDescriptor *drm_desc = &avbuf->drm_frame; | |
72 | + AVDRMLayerDescriptor *layer; | |
73 | + | |
74 | + /* fill the DRM frame descriptor */ | |
75 | + drm_desc->nb_objects = avbuf->num_planes; | |
76 | + drm_desc->nb_layers = 1; | |
77 | + | |
78 | + layer = &drm_desc->layers[0]; | |
79 | + layer->nb_planes = avbuf->num_planes; | |
80 | + | |
81 | + for (int i = 0; i < avbuf->num_planes; i++) { | |
82 | + layer->planes[i].object_index = i; | |
83 | + layer->planes[i].offset = 0; | |
84 | + layer->planes[i].pitch = avbuf->plane_info[i].bytesperline; | |
85 | + } | |
86 | + | |
87 | + switch (avbuf->context->av_pix_fmt) { | |
88 | + case AV_PIX_FMT_YUYV422: | |
89 | + | |
90 | + layer->format = DRM_FORMAT_YUYV; | |
91 | + layer->nb_planes = 1; | |
92 | + | |
93 | + break; | |
94 | + | |
95 | + case AV_PIX_FMT_NV12: | |
96 | + case AV_PIX_FMT_NV21: | |
97 | + | |
98 | + layer->format = avbuf->context->av_pix_fmt == AV_PIX_FMT_NV12 ? | |
99 | + DRM_FORMAT_NV12 : DRM_FORMAT_NV21; | |
100 | + | |
101 | + if (avbuf->num_planes > 1) | |
102 | + break; | |
103 | + | |
104 | + layer->nb_planes = 2; | |
105 | + | |
106 | + layer->planes[1].object_index = 0; | |
107 | + layer->planes[1].offset = avbuf->plane_info[0].bytesperline * | |
108 | + avbuf->context->format.fmt.pix.height; | |
109 | + layer->planes[1].pitch = avbuf->plane_info[0].bytesperline; | |
110 | + break; | |
111 | + | |
112 | + case AV_PIX_FMT_YUV420P: | |
113 | + | |
114 | + layer->format = DRM_FORMAT_YUV420; | |
115 | + | |
116 | + if (avbuf->num_planes > 1) | |
117 | + break; | |
118 | + | |
119 | + layer->nb_planes = 3; | |
120 | + | |
121 | + layer->planes[1].object_index = 0; | |
122 | + layer->planes[1].offset = avbuf->plane_info[0].bytesperline * | |
123 | + avbuf->context->format.fmt.pix.height; | |
124 | + layer->planes[1].pitch = avbuf->plane_info[0].bytesperline >> 1; | |
125 | + | |
126 | + layer->planes[2].object_index = 0; | |
127 | + layer->planes[2].offset = layer->planes[1].offset + | |
128 | + ((avbuf->plane_info[0].bytesperline * | |
129 | + avbuf->context->format.fmt.pix.height) >> 2); | |
130 | + layer->planes[2].pitch = avbuf->plane_info[0].bytesperline >> 1; | |
131 | + break; | |
132 | + | |
133 | + default: | |
134 | + drm_desc->nb_layers = 0; | |
135 | + break; | |
136 | + } | |
137 | + | |
138 | + return (uint8_t *) drm_desc; | |
139 | +} | |
140 | + | |
141 | +static void v4l2_free_buffer(void *opaque, uint8_t *data) | |
142 | { | |
143 | V4L2Buffer* avbuf = opaque; | |
144 | V4L2m2mContext *s = buf_to_m2mctx(avbuf); | |
145 | @@ -234,6 +308,36 @@ static void v4l2_free_buffer(void *opaque, uint8_t *unused) | |
146 | } | |
147 | } | |
148 | ||
149 | +static int v4l2_buffer_export_drm(V4L2Buffer* avbuf) | |
150 | +{ | |
151 | + struct v4l2_exportbuffer expbuf; | |
152 | + int i, ret; | |
153 | + | |
154 | + for (i = 0; i < avbuf->num_planes; i++) { | |
155 | + memset(&expbuf, 0, sizeof(expbuf)); | |
156 | + | |
157 | + expbuf.index = avbuf->buf.index; | |
158 | + expbuf.type = avbuf->buf.type; | |
159 | + expbuf.plane = i; | |
160 | + | |
161 | + ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_EXPBUF, &expbuf); | |
162 | + if (ret < 0) | |
163 | + return AVERROR(errno); | |
164 | + | |
165 | + if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->buf.type)) { | |
166 | + /* drm frame */ | |
167 | + avbuf->drm_frame.objects[i].size = avbuf->buf.m.planes[i].length; | |
168 | + avbuf->drm_frame.objects[i].fd = expbuf.fd; | |
169 | + } else { | |
170 | + /* drm frame */ | |
171 | + avbuf->drm_frame.objects[0].size = avbuf->buf.length; | |
172 | + avbuf->drm_frame.objects[0].fd = expbuf.fd; | |
173 | + } | |
174 | + } | |
175 | + | |
176 | + return 0; | |
177 | +} | |
178 | + | |
179 | static int v4l2_buf_increase_ref(V4L2Buffer *in) | |
180 | { | |
181 | V4L2m2mContext *s = buf_to_m2mctx(in); | |
182 | @@ -254,6 +358,24 @@ static int v4l2_buf_increase_ref(V4L2Buffer *in) | |
183 | return 0; | |
184 | } | |
185 | ||
186 | +static int v4l2_buf_to_bufref_drm(V4L2Buffer *in, AVBufferRef **buf) | |
187 | +{ | |
188 | + int ret; | |
189 | + | |
190 | + *buf = av_buffer_create((uint8_t *) &in->drm_frame, | |
191 | + sizeof(in->drm_frame), | |
192 | + v4l2_free_buffer, | |
193 | + in, AV_BUFFER_FLAG_READONLY); | |
194 | + if (!*buf) | |
195 | + return AVERROR(ENOMEM); | |
196 | + | |
197 | + ret = v4l2_buf_increase_ref(in); | |
198 | + if (ret) | |
199 | + av_buffer_unref(buf); | |
200 | + | |
201 | + return ret; | |
202 | +} | |
203 | + | |
204 | static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf) | |
205 | { | |
206 | int ret; | |
207 | @@ -303,13 +425,24 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) | |
208 | ||
209 | frame->format = avbuf->context->av_pix_fmt; | |
210 | ||
211 | - for (i = 0; i < avbuf->num_planes; i++) { | |
212 | - ret = v4l2_buf_to_bufref(avbuf, i, &frame->buf[i]); | |
213 | + if (buf_to_m2mctx(avbuf)->output_drm) { | |
214 | + /* 1. get references to the actual data */ | |
215 | + ret = v4l2_buf_to_bufref_drm(avbuf, &frame->buf[0]); | |
216 | if (ret) | |
217 | return ret; | |
218 | ||
219 | - frame->linesize[i] = avbuf->plane_info[i].bytesperline; | |
220 | - frame->data[i] = frame->buf[i]->data; | |
221 | + frame->data[0] = (uint8_t *) v4l2_get_drm_frame(avbuf); | |
222 | + frame->format = AV_PIX_FMT_DRM_PRIME; | |
223 | + } else { | |
224 | + /* 1. get references to the actual data */ | |
225 | + for (i = 0; i < avbuf->num_planes; i++) { | |
226 | + ret = v4l2_buf_to_bufref(avbuf, i, &frame->buf[i]); | |
227 | + if (ret) | |
228 | + return ret; | |
229 | + | |
230 | + frame->linesize[i] = avbuf->plane_info[i].bytesperline; | |
231 | + frame->data[i] = frame->buf[i]->data; | |
232 | + } | |
233 | } | |
234 | ||
235 | /* fixup special cases */ | |
236 | @@ -543,9 +676,6 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) | |
237 | ||
238 | avbuf->status = V4L2BUF_AVAILABLE; | |
239 | ||
240 | - if (V4L2_TYPE_IS_OUTPUT(ctx->type)) | |
241 | - return 0; | |
242 | - | |
243 | if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { | |
244 | avbuf->buf.m.planes = avbuf->planes; | |
245 | avbuf->buf.length = avbuf->num_planes; | |
246 | @@ -555,6 +685,15 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) | |
247 | avbuf->buf.length = avbuf->planes[0].length; | |
248 | } | |
249 | ||
250 | + if (V4L2_TYPE_IS_OUTPUT(ctx->type)) | |
251 | + return 0; | |
252 | + | |
253 | + if (buf_to_m2mctx(avbuf)->output_drm) { | |
254 | + ret = v4l2_buffer_export_drm(avbuf); | |
255 | + if (ret) | |
256 | + return ret; | |
257 | + } | |
258 | + | |
259 | return ff_v4l2_buffer_enqueue(avbuf); | |
260 | } | |
261 | ||
262 | diff --git a/libavcodec/v4l2_buffers.h b/libavcodec/v4l2_buffers.h | |
263 | index 8dbc7fc104..037e667997 100644 | |
264 | --- a/libavcodec/v4l2_buffers.h | |
265 | +++ b/libavcodec/v4l2_buffers.h | |
266 | @@ -27,6 +27,7 @@ | |
267 | #include <stdatomic.h> | |
268 | #include <linux/videodev2.h> | |
269 | ||
270 | +#include "libavutil/hwcontext_drm.h" | |
271 | #include "avcodec.h" | |
272 | ||
273 | enum V4L2Buffer_status { | |
274 | @@ -42,6 +43,9 @@ typedef struct V4L2Buffer { | |
275 | /* each buffer needs to have a reference to its context */ | |
276 | struct V4L2Context *context; | |
277 | ||
278 | + /* DRM descriptor */ | |
279 | + AVDRMFrameDescriptor drm_frame; | |
280 | + | |
281 | /* This object is refcounted per-plane, so we need to keep track | |
282 | * of how many context-refs we are holding. */ | |
283 | AVBufferRef *context_ref; | |
284 | diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c | |
285 | index ff1ea8e57b..e9e8c27a54 100644 | |
286 | --- a/libavcodec/v4l2_context.c | |
287 | +++ b/libavcodec/v4l2_context.c | |
288 | @@ -455,22 +455,54 @@ static int v4l2_release_buffers(V4L2Context* ctx) | |
289 | struct v4l2_requestbuffers req = { | |
290 | .memory = V4L2_MEMORY_MMAP, | |
291 | .type = ctx->type, | |
292 | - .count = 0, /* 0 -> unmaps buffers from the driver */ | |
293 | + .count = 0, /* 0 -> unmap all buffers from the driver */ | |
294 | }; | |
295 | - int i, j; | |
296 | + int ret, i, j; | |
297 | ||
298 | for (i = 0; i < ctx->num_buffers; i++) { | |
299 | V4L2Buffer *buffer = &ctx->buffers[i]; | |
300 | ||
301 | for (j = 0; j < buffer->num_planes; j++) { | |
302 | struct V4L2Plane_info *p = &buffer->plane_info[j]; | |
303 | + | |
304 | + if (V4L2_TYPE_IS_OUTPUT(ctx->type)) { | |
305 | + /* output buffers are not EXPORTED */ | |
306 | + goto unmap; | |
307 | + } | |
308 | + | |
309 | + if (ctx_to_m2mctx(ctx)->output_drm) { | |
310 | + /* use the DRM frame to close */ | |
311 | + if (buffer->drm_frame.objects[j].fd >= 0) { | |
312 | + if (close(buffer->drm_frame.objects[j].fd) < 0) { | |
313 | + av_log(logger(ctx), AV_LOG_ERROR, "%s close drm fd " | |
314 | + "[buffer=%2d, plane=%d, fd=%2d] - %s \n", | |
315 | + ctx->name, i, j, buffer->drm_frame.objects[j].fd, | |
316 | + av_err2str(AVERROR(errno))); | |
317 | + } | |
318 | + } | |
319 | + } | |
320 | +unmap: | |
321 | if (p->mm_addr && p->length) | |
322 | if (munmap(p->mm_addr, p->length) < 0) | |
323 | - av_log(logger(ctx), AV_LOG_ERROR, "%s unmap plane (%s))\n", ctx->name, av_err2str(AVERROR(errno))); | |
324 | + av_log(logger(ctx), AV_LOG_ERROR, "%s unmap plane (%s))\n", | |
325 | + ctx->name, av_err2str(AVERROR(errno))); | |
326 | } | |
327 | } | |
328 | ||
329 | - return ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_REQBUFS, &req); | |
330 | + ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_REQBUFS, &req); | |
331 | + if (ret < 0) { | |
332 | + av_log(logger(ctx), AV_LOG_ERROR, "release all %s buffers (%s)\n", | |
333 | + ctx->name, av_err2str(AVERROR(errno))); | |
334 | + | |
335 | + if (ctx_to_m2mctx(ctx)->output_drm) | |
336 | + av_log(logger(ctx), AV_LOG_ERROR, | |
337 | + "Make sure the DRM client releases all FB/GEM objects before closing the codec (ie):\n" | |
338 | + "for all buffers: \n" | |
339 | + " 1. drmModeRmFB(..)\n" | |
340 | + " 2. drmIoctl(.., DRM_IOCTL_GEM_CLOSE,... )\n"); | |
341 | + } | |
342 | + | |
343 | + return ret; | |
344 | } | |
345 | ||
346 | static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfmt) | |
347 | diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h | |
348 | index b67b216331..0fbd19a013 100644 | |
349 | --- a/libavcodec/v4l2_m2m.h | |
350 | +++ b/libavcodec/v4l2_m2m.h | |
351 | @@ -66,6 +66,9 @@ typedef struct V4L2m2mContext { | |
352 | ||
353 | /* reference back to V4L2m2mPriv */ | |
354 | void *priv; | |
355 | + | |
356 | + /* generate DRM frames */ | |
357 | + int output_drm; | |
358 | } V4L2m2mContext; | |
359 | ||
360 | typedef struct V4L2m2mPriv { | |
361 | diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c | |
362 | index ab07c0a24a..6bc7442702 100644 | |
363 | --- a/libavcodec/v4l2_m2m_dec.c | |
364 | +++ b/libavcodec/v4l2_m2m_dec.c | |
365 | @@ -23,6 +23,9 @@ | |
366 | ||
367 | #include <linux/videodev2.h> | |
368 | #include <sys/ioctl.h> | |
369 | + | |
370 | +#include "libavutil/hwcontext.h" | |
371 | +#include "libavutil/hwcontext_drm.h" | |
372 | #include "libavutil/pixfmt.h" | |
373 | #include "libavutil/pixdesc.h" | |
374 | #include "libavutil/opt.h" | |
375 | @@ -30,6 +33,9 @@ | |
376 | #include "libavcodec/decode.h" | |
377 | #include "libavcodec/internal.h" | |
378 | ||
379 | +#include "libavcodec/hwaccels.h" | |
380 | +#include "libavcodec/internal.h" | |
381 | + | |
382 | #include "v4l2_context.h" | |
383 | #include "v4l2_m2m.h" | |
384 | #include "v4l2_fmt.h" | |
385 | @@ -201,6 +207,15 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) | |
386 | capture->av_codec_id = AV_CODEC_ID_RAWVIDEO; | |
387 | capture->av_pix_fmt = avctx->pix_fmt; | |
388 | ||
389 | + /* the client requests the codec to generate DRM frames: | |
390 | + * - data[0] will therefore point to the returned AVDRMFrameDescriptor | |
391 | + * check the ff_v4l2_buffer_to_avframe conversion function. | |
392 | + * - the DRM frame format is passed in the DRM frame descriptor layer. | |
393 | + * check the v4l2_get_drm_frame function. | |
394 | + */ | |
395 | + if (ff_get_format(avctx, avctx->codec->pix_fmts) == AV_PIX_FMT_DRM_PRIME) | |
396 | + s->output_drm = 1; | |
397 | + | |
398 | s->avctx = avctx; | |
399 | ret = ff_v4l2_m2m_codec_init(priv); | |
400 | if (ret) { | |
401 | @@ -226,6 +241,11 @@ static const AVOption options[] = { | |
402 | { NULL}, | |
403 | }; | |
404 | ||
405 | +static const AVCodecHWConfigInternal *v4l2_m2m_hw_configs[] = { | |
406 | + HW_CONFIG_INTERNAL(DRM_PRIME), | |
407 | + NULL | |
408 | +}; | |
409 | + | |
410 | #define M2MDEC_CLASS(NAME) \ | |
411 | static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \ | |
412 | .class_name = #NAME "_v4l2m2m_decoder", \ | |
413 | @@ -249,6 +269,9 @@ static const AVOption options[] = { | |
414 | .bsfs = bsf_name, \ | |
415 | .capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ | |
416 | .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, \ | |
417 | + .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_DRM_PRIME, \ | |
418 | + AV_PIX_FMT_NONE}, \ | |
419 | + .hw_configs = v4l2_m2m_hw_configs, \ | |
420 | .wrapper_name = "v4l2m2m", \ | |
421 | } | |
422 | ||
423 | ||
424 | From 9a2a361c2c84c8da54cd3a74b0d0bb966df8fe69 Mon Sep 17 00:00:00 2001 | |
425 | From: Lukas Rusak <lorusak@gmail.com> | |
426 | Date: Thu, 16 Aug 2018 21:09:40 -0700 | |
427 | Subject: [PATCH 2/9] libavcodec: v4l2m2m: depends on libdrm | |
428 | ||
429 | --- | |
430 | configure | 1 + | |
431 | libavcodec/v4l2_buffers.c | 2 +- | |
432 | 2 files changed, 2 insertions(+), 1 deletion(-) | |
433 | ||
434 | diff --git a/configure b/configure | |
435 | index d7a3f507e8..d203f6f7da 100755 | |
436 | --- a/configure | |
437 | +++ b/configure | |
438 | @@ -3437,6 +3437,7 @@ sndio_indev_deps="sndio" | |
439 | sndio_outdev_deps="sndio" | |
440 | v4l2_indev_deps_any="linux_videodev2_h sys_videoio_h" | |
441 | v4l2_indev_suggest="libv4l2" | |
442 | +v4l2_outdev_deps="libdrm" | |
443 | v4l2_outdev_deps_any="linux_videodev2_h sys_videoio_h" | |
444 | v4l2_outdev_suggest="libv4l2" | |
445 | vfwcap_indev_deps="vfw32 vfwcap_defines" | |
446 | diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c | |
447 | index cbd3e5680d..bebe2c1796 100644 | |
448 | --- a/libavcodec/v4l2_buffers.c | |
449 | +++ b/libavcodec/v4l2_buffers.c | |
450 | @@ -21,7 +21,7 @@ | |
451 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
452 | */ | |
453 | ||
454 | -#include <drm/drm_fourcc.h> | |
455 | +#include <drm_fourcc.h> | |
456 | #include <linux/videodev2.h> | |
457 | #include <sys/ioctl.h> | |
458 | #include <sys/mman.h> | |
459 | ||
460 | From 7b0fa2d859c12a8a129c884d16673ca731336c06 Mon Sep 17 00:00:00 2001 | |
461 | From: Lukas Rusak <lorusak@gmail.com> | |
462 | Date: Thu, 16 Aug 2018 21:10:13 -0700 | |
463 | Subject: [PATCH 3/9] libavcodec: v4l2m2m: set format_modifier to | |
464 | DRM_FORMAT_MOD_LINEAR | |
465 | ||
466 | --- | |
467 | libavcodec/v4l2_buffers.c | 2 ++ | |
468 | 1 file changed, 2 insertions(+) | |
469 | ||
470 | diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c | |
471 | index bebe2c1796..12037d5d66 100644 | |
472 | --- a/libavcodec/v4l2_buffers.c | |
473 | +++ b/libavcodec/v4l2_buffers.c | |
474 | @@ -328,10 +328,12 @@ static int v4l2_buffer_export_drm(V4L2Buffer* avbuf) | |
475 | /* drm frame */ | |
476 | avbuf->drm_frame.objects[i].size = avbuf->buf.m.planes[i].length; | |
477 | avbuf->drm_frame.objects[i].fd = expbuf.fd; | |
478 | + avbuf->drm_frame.objects[i].format_modifier = DRM_FORMAT_MOD_LINEAR; | |
479 | } else { | |
480 | /* drm frame */ | |
481 | avbuf->drm_frame.objects[0].size = avbuf->buf.length; | |
482 | avbuf->drm_frame.objects[0].fd = expbuf.fd; | |
483 | + avbuf->drm_frame.objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR; | |
484 | } | |
485 | } | |
486 | ||
487 | ||
488 | From e1857456c8f24e40d5c898886f2f51014e59ee9d Mon Sep 17 00:00:00 2001 | |
489 | From: Lukas Rusak <lorusak@gmail.com> | |
490 | Date: Thu, 16 Aug 2018 21:10:53 -0700 | |
491 | Subject: [PATCH 4/9] libavcodec: v4l2m2m: only mmap the buffer when it is | |
492 | output type and drm prime is used | |
493 | ||
494 | --- | |
495 | libavcodec/v4l2_buffers.c | 20 ++++++++++++++------ | |
496 | 1 file changed, 14 insertions(+), 6 deletions(-) | |
497 | ||
498 | diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c | |
499 | index 12037d5d66..1adf518ab9 100644 | |
500 | --- a/libavcodec/v4l2_buffers.c | |
501 | +++ b/libavcodec/v4l2_buffers.c | |
502 | @@ -662,14 +662,22 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) | |
503 | ||
504 | if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { | |
505 | avbuf->plane_info[i].length = avbuf->buf.m.planes[i].length; | |
506 | - avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length, | |
507 | - PROT_READ | PROT_WRITE, MAP_SHARED, | |
508 | - buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.planes[i].m.mem_offset); | |
509 | + | |
510 | + if ((V4L2_TYPE_IS_OUTPUT(ctx->type) && buf_to_m2mctx(avbuf)->output_drm) || | |
511 | + !buf_to_m2mctx(avbuf)->output_drm) { | |
512 | + avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length, | |
513 | + PROT_READ | PROT_WRITE, MAP_SHARED, | |
514 | + buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.planes[i].m.mem_offset); | |
515 | + } | |
516 | } else { | |
517 | avbuf->plane_info[i].length = avbuf->buf.length; | |
518 | - avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.length, | |
519 | - PROT_READ | PROT_WRITE, MAP_SHARED, | |
520 | - buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.offset); | |
521 | + | |
522 | + if ((V4L2_TYPE_IS_OUTPUT(ctx->type) && buf_to_m2mctx(avbuf)->output_drm) || | |
523 | + !buf_to_m2mctx(avbuf)->output_drm) { | |
524 | + avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.length, | |
525 | + PROT_READ | PROT_WRITE, MAP_SHARED, | |
526 | + buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.offset); | |
527 | + } | |
528 | } | |
529 | ||
530 | if (avbuf->plane_info[i].mm_addr == MAP_FAILED) | |
531 | ||
532 | From c8fc3ea1b5777546f7ec72a54b053a2d4fa9fd59 Mon Sep 17 00:00:00 2001 | |
533 | From: Lukas Rusak <lorusak@gmail.com> | |
534 | Date: Thu, 16 Aug 2018 21:11:38 -0700 | |
535 | Subject: [PATCH 5/9] libavcodec: v4l2m2m: allow using software pixel formats | |
536 | ||
537 | --- | |
538 | libavcodec/v4l2_m2m_dec.c | 11 ++++++++++- | |
539 | 1 file changed, 10 insertions(+), 1 deletion(-) | |
540 | ||
541 | diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c | |
542 | index 6bc7442702..4b9baf833c 100644 | |
543 | --- a/libavcodec/v4l2_m2m_dec.c | |
544 | +++ b/libavcodec/v4l2_m2m_dec.c | |
545 | @@ -213,8 +213,16 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) | |
546 | * - the DRM frame format is passed in the DRM frame descriptor layer. | |
547 | * check the v4l2_get_drm_frame function. | |
548 | */ | |
549 | - if (ff_get_format(avctx, avctx->codec->pix_fmts) == AV_PIX_FMT_DRM_PRIME) | |
550 | + switch (ff_get_format(avctx, avctx->codec->pix_fmts)) { | |
551 | + case AV_PIX_FMT_DRM_PRIME: | |
552 | s->output_drm = 1; | |
553 | + break; | |
554 | + case AV_PIX_FMT_NONE: | |
555 | + return 0; | |
556 | + break; | |
557 | + default: | |
558 | + break; | |
559 | + } | |
560 | ||
561 | s->avctx = avctx; | |
562 | ret = ff_v4l2_m2m_codec_init(priv); | |
563 | @@ -270,6 +278,7 @@ static const AVCodecHWConfigInternal *v4l2_m2m_hw_configs[] = { | |
564 | .capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ | |
565 | .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, \ | |
566 | .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_DRM_PRIME, \ | |
567 | + AV_PIX_FMT_NV12, \ | |
568 | AV_PIX_FMT_NONE}, \ | |
569 | .hw_configs = v4l2_m2m_hw_configs, \ | |
570 | .wrapper_name = "v4l2m2m", \ | |
571 | ||
572 | From 13f02e940f083f19dbe8b9ac8fc7df45700dd36e Mon Sep 17 00:00:00 2001 | |
573 | From: Lukas Rusak <lorusak@gmail.com> | |
574 | Date: Mon, 24 Sep 2018 13:39:31 -0700 | |
575 | Subject: [PATCH 6/9] libavcodec: v4l2m2m: implement hwcontext | |
576 | ||
577 | --- | |
578 | libavcodec/v4l2_buffers.c | 22 ++++++++++++++++++++++ | |
579 | libavcodec/v4l2_context.h | 2 ++ | |
580 | libavcodec/v4l2_m2m.h | 2 ++ | |
581 | libavcodec/v4l2_m2m_dec.c | 11 +++++++++++ | |
582 | 4 files changed, 37 insertions(+) | |
583 | ||
584 | diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c | |
585 | index 1adf518ab9..6e2a544394 100644 | |
586 | --- a/libavcodec/v4l2_buffers.c | |
587 | +++ b/libavcodec/v4l2_buffers.c | |
588 | @@ -435,6 +435,7 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) | |
589 | ||
590 | frame->data[0] = (uint8_t *) v4l2_get_drm_frame(avbuf); | |
591 | frame->format = AV_PIX_FMT_DRM_PRIME; | |
592 | + frame->hw_frames_ctx = av_buffer_ref(avbuf->context->frames_ref); | |
593 | } else { | |
594 | /* 1. get references to the actual data */ | |
595 | for (i = 0; i < avbuf->num_planes; i++) { | |
596 | @@ -635,6 +636,27 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) | |
597 | avbuf->buf.type = ctx->type; | |
598 | avbuf->buf.index = index; | |
599 | ||
600 | + if (buf_to_m2mctx(avbuf)->output_drm) { | |
601 | + AVHWFramesContext *hwframes; | |
602 | + | |
603 | + av_buffer_unref(&ctx->frames_ref); | |
604 | + | |
605 | + ctx->frames_ref = av_hwframe_ctx_alloc(buf_to_m2mctx(avbuf)->device_ref); | |
606 | + if (!ctx->frames_ref) { | |
607 | + ret = AVERROR(ENOMEM); | |
608 | + return ret; | |
609 | + } | |
610 | + | |
611 | + hwframes = (AVHWFramesContext*)ctx->frames_ref->data; | |
612 | + hwframes->format = AV_PIX_FMT_DRM_PRIME; | |
613 | + hwframes->sw_format = ctx->av_pix_fmt; | |
614 | + hwframes->width = ctx->width; | |
615 | + hwframes->height = ctx->height; | |
616 | + ret = av_hwframe_ctx_init(ctx->frames_ref); | |
617 | + if (ret < 0) | |
618 | + return ret; | |
619 | + } | |
620 | + | |
621 | if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { | |
622 | avbuf->buf.length = VIDEO_MAX_PLANES; | |
623 | avbuf->buf.m.planes = avbuf->planes; | |
624 | diff --git a/libavcodec/v4l2_context.h b/libavcodec/v4l2_context.h | |
625 | index 22a9532444..e804e94131 100644 | |
626 | --- a/libavcodec/v4l2_context.h | |
627 | +++ b/libavcodec/v4l2_context.h | |
628 | @@ -92,6 +92,8 @@ typedef struct V4L2Context { | |
629 | */ | |
630 | int done; | |
631 | ||
632 | + AVBufferRef *frames_ref; | |
633 | + | |
634 | } V4L2Context; | |
635 | ||
636 | /** | |
637 | diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h | |
638 | index 0fbd19a013..adf5997bb5 100644 | |
639 | --- a/libavcodec/v4l2_m2m.h | |
640 | +++ b/libavcodec/v4l2_m2m.h | |
641 | @@ -67,6 +67,8 @@ typedef struct V4L2m2mContext { | |
642 | /* reference back to V4L2m2mPriv */ | |
643 | void *priv; | |
644 | ||
645 | + AVBufferRef *device_ref; | |
646 | + | |
647 | /* generate DRM frames */ | |
648 | int output_drm; | |
649 | } V4L2m2mContext; | |
650 | diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c | |
651 | index 4b9baf833c..6c23693137 100644 | |
652 | --- a/libavcodec/v4l2_m2m_dec.c | |
653 | +++ b/libavcodec/v4l2_m2m_dec.c | |
654 | @@ -35,6 +35,7 @@ | |
655 | ||
656 | #include "libavcodec/hwaccels.h" | |
657 | #include "libavcodec/internal.h" | |
658 | +#include "libavcodec/hwconfig.h" | |
659 | ||
660 | #include "v4l2_context.h" | |
661 | #include "v4l2_m2m.h" | |
662 | @@ -224,6 +225,16 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) | |
663 | break; | |
664 | } | |
665 | ||
666 | + s->device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); | |
667 | + if (!s->device_ref) { | |
668 | + ret = AVERROR(ENOMEM); | |
669 | + return ret; | |
670 | + } | |
671 | + | |
672 | + ret = av_hwdevice_ctx_init(s->device_ref); | |
673 | + if (ret < 0) | |
674 | + return ret; | |
675 | + | |
676 | s->avctx = avctx; | |
677 | ret = ff_v4l2_m2m_codec_init(priv); | |
678 | if (ret) { | |
679 | ||
680 | From 34be198b8039c9df434792f19f0985e45419407e Mon Sep 17 00:00:00 2001 | |
681 | From: Lukas Rusak <lorusak@gmail.com> | |
682 | Date: Mon, 4 May 2020 13:01:29 -0700 | |
683 | Subject: [PATCH 7/9] libavcodec: v4l2m2m: allow lower minimum buffer values | |
684 | ||
685 | There is no reason to enforce a high minimum. In the context | |
686 | of streaming only a few output buffers and capture buffers | |
687 | are even needed for continuous playback. This also helps | |
688 | alleviate memory pressure when decoding 4K media. | |
689 | --- | |
690 | libavcodec/v4l2_m2m.h | 2 +- | |
691 | libavcodec/v4l2_m2m_dec.c | 2 +- | |
692 | 2 files changed, 2 insertions(+), 2 deletions(-) | |
693 | ||
694 | diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h | |
695 | index adf5997bb5..1082b9dad2 100644 | |
696 | --- a/libavcodec/v4l2_m2m.h | |
697 | +++ b/libavcodec/v4l2_m2m.h | |
698 | @@ -38,7 +38,7 @@ | |
699 | ||
700 | #define V4L_M2M_DEFAULT_OPTS \ | |
701 | { "num_output_buffers", "Number of buffers in the output context",\ | |
702 | - OFFSET(num_output_buffers), AV_OPT_TYPE_INT, { .i64 = 16 }, 6, INT_MAX, FLAGS } | |
703 | + OFFSET(num_output_buffers), AV_OPT_TYPE_INT, { .i64 = 16 }, 2, INT_MAX, FLAGS } | |
704 | ||
705 | typedef struct V4L2m2mContext { | |
706 | char devname[PATH_MAX]; | |
707 | diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c | |
708 | index 6c23693137..e323c37052 100644 | |
709 | --- a/libavcodec/v4l2_m2m_dec.c | |
710 | +++ b/libavcodec/v4l2_m2m_dec.c | |
711 | @@ -256,7 +256,7 @@ static av_cold int v4l2_decode_close(AVCodecContext *avctx) | |
712 | static const AVOption options[] = { | |
713 | V4L_M2M_DEFAULT_OPTS, | |
714 | { "num_capture_buffers", "Number of buffers in the capture context", | |
715 | - OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 20}, 20, INT_MAX, FLAGS }, | |
716 | + OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 20}, 2, INT_MAX, FLAGS }, | |
717 | { NULL}, | |
718 | }; | |
719 | ||
720 | ||
721 | From 2956fd1881d28abf6bf77bd9a57866c4ba81d199 Mon Sep 17 00:00:00 2001 | |
722 | From: Lukas Rusak <lorusak@gmail.com> | |
723 | Date: Wed, 6 May 2020 11:12:58 -0700 | |
724 | Subject: [PATCH 8/9] libavcodec: v4l2m2m: add option to specify pixel format | |
725 | used by the decoder | |
726 | ||
727 | --- | |
728 | libavcodec/v4l2_context.c | 9 +++++++++ | |
729 | libavcodec/v4l2_m2m.h | 2 ++ | |
730 | libavcodec/v4l2_m2m_dec.c | 1 + | |
731 | 3 files changed, 12 insertions(+) | |
732 | ||
733 | diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c | |
734 | index e9e8c27a54..a97b70e836 100644 | |
735 | --- a/libavcodec/v4l2_context.c | |
736 | +++ b/libavcodec/v4l2_context.c | |
737 | @@ -531,6 +531,8 @@ static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfm | |
738 | ||
739 | static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p) | |
740 | { | |
741 | + V4L2m2mContext* s = ctx_to_m2mctx(ctx); | |
742 | + V4L2m2mPriv *priv = s->avctx->priv_data; | |
743 | enum AVPixelFormat pixfmt = ctx->av_pix_fmt; | |
744 | struct v4l2_fmtdesc fdesc; | |
745 | int ret; | |
746 | @@ -549,6 +551,13 @@ static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p) | |
747 | if (ret) | |
748 | return AVERROR(EINVAL); | |
749 | ||
750 | + if (priv->pix_fmt != AV_PIX_FMT_NONE) { | |
751 | + if (fdesc.pixelformat != ff_v4l2_format_avfmt_to_v4l2(priv->pix_fmt)) { | |
752 | + fdesc.index++; | |
753 | + continue; | |
754 | + } | |
755 | + } | |
756 | + | |
757 | pixfmt = ff_v4l2_format_v4l2_to_avfmt(fdesc.pixelformat, AV_CODEC_ID_RAWVIDEO); | |
758 | ret = v4l2_try_raw_format(ctx, pixfmt); | |
759 | if (ret){ | |
760 | diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h | |
761 | index 1082b9dad2..943a8923c4 100644 | |
762 | --- a/libavcodec/v4l2_m2m.h | |
763 | +++ b/libavcodec/v4l2_m2m.h | |
764 | @@ -30,6 +30,7 @@ | |
765 | #include <linux/videodev2.h> | |
766 | ||
767 | #include "libavcodec/avcodec.h" | |
768 | +#include "libavutil/pixfmt.h" | |
769 | #include "v4l2_context.h" | |
770 | ||
771 | #define container_of(ptr, type, member) ({ \ | |
772 | @@ -81,6 +82,7 @@ typedef struct V4L2m2mPriv { | |
773 | ||
774 | int num_output_buffers; | |
775 | int num_capture_buffers; | |
776 | + enum AVPixelFormat pix_fmt; | |
777 | } V4L2m2mPriv; | |
778 | ||
779 | /** | |
780 | diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c | |
781 | index e323c37052..363e998142 100644 | |
782 | --- a/libavcodec/v4l2_m2m_dec.c | |
783 | +++ b/libavcodec/v4l2_m2m_dec.c | |
784 | @@ -257,6 +257,7 @@ static const AVOption options[] = { | |
785 | V4L_M2M_DEFAULT_OPTS, | |
786 | { "num_capture_buffers", "Number of buffers in the capture context", | |
787 | OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 20}, 2, INT_MAX, FLAGS }, | |
788 | + { "pixel_format", "Pixel format to be used by the decoder", OFFSET(pix_fmt), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_NONE}, AV_PIX_FMT_NONE, AV_PIX_FMT_NB, FLAGS }, | |
789 | { NULL}, | |
790 | }; | |
791 | ||
792 | ||
793 | From 2bb6d0cb244590f0c70dd111ed978cd87fa3bee1 Mon Sep 17 00:00:00 2001 | |
794 | From: Lukas Rusak <lorusak@gmail.com> | |
795 | Date: Mon, 24 Sep 2018 13:39:56 -0700 | |
796 | Subject: [PATCH 9/9] libavcodec: v4l2m2m: implement flush | |
797 | ||
798 | --- | |
799 | libavcodec/v4l2_m2m_dec.c | 36 ++++++++++++++++++++++++++++++++++++ | |
800 | 1 file changed, 36 insertions(+) | |
801 | ||
802 | diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c | |
803 | index 363e998142..52ec67cb59 100644 | |
804 | --- a/libavcodec/v4l2_m2m_dec.c | |
805 | +++ b/libavcodec/v4l2_m2m_dec.c | |
806 | @@ -250,6 +250,41 @@ static av_cold int v4l2_decode_close(AVCodecContext *avctx) | |
807 | return ff_v4l2_m2m_codec_end(avctx->priv_data); | |
808 | } | |
809 | ||
810 | +static void v4l2_decode_flush(AVCodecContext *avctx) | |
811 | +{ | |
812 | + V4L2m2mPriv *priv = avctx->priv_data; | |
813 | + V4L2m2mContext* s = priv->context; | |
814 | + V4L2Context* output = &s->output; | |
815 | + V4L2Context* capture = &s->capture; | |
816 | + int ret, i; | |
817 | + | |
818 | + ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMOFF); | |
819 | + if (ret < 0) | |
820 | + av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s error: %d\n", output->name, ret); | |
821 | + | |
822 | + ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON); | |
823 | + if (ret < 0) | |
824 | + av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON %s error: %d\n", output->name, ret); | |
825 | + | |
826 | + for (i = 0; i < output->num_buffers; i++) { | |
827 | + if (output->buffers[i].status == V4L2BUF_IN_DRIVER) | |
828 | + output->buffers[i].status = V4L2BUF_AVAILABLE; | |
829 | + } | |
830 | + | |
831 | + struct v4l2_decoder_cmd cmd = { | |
832 | + .cmd = V4L2_DEC_CMD_START, | |
833 | + .flags = 0, | |
834 | + }; | |
835 | + | |
836 | + ret = ioctl(s->fd, VIDIOC_DECODER_CMD, &cmd); | |
837 | + if (ret < 0) | |
838 | + av_log(avctx, AV_LOG_ERROR, "VIDIOC_DECODER_CMD start error: %d\n", errno); | |
839 | + | |
840 | + s->draining = 0; | |
841 | + output->done = 0; | |
842 | + capture->done = 0; | |
843 | +} | |
844 | + | |
845 | #define OFFSET(x) offsetof(V4L2m2mPriv, x) | |
846 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM | |
847 | ||
848 | @@ -286,6 +321,7 @@ static const AVCodecHWConfigInternal *v4l2_m2m_hw_configs[] = { | |
849 | .init = v4l2_decode_init, \ | |
850 | .receive_frame = v4l2_receive_frame, \ | |
851 | .close = v4l2_decode_close, \ | |
852 | + .flush = v4l2_decode_flush, \ | |
853 | .bsfs = bsf_name, \ | |
854 | .capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ | |
855 | .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, \ |