1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
|
From f227da5a137d45d44d37e1d2be6427e9f7c247b3 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Tue, 6 Aug 2024 09:06:03 +0000
Subject: [PATCH 04/13] avcodec/v4l2request: Add common decode support for
hwaccels
Add common support for decoding using the V4L2 Request API.
Basic flow for decoding follows the kernel Memory-to-memory Stateless
Video Decoder Interface > Decoding [1].
A codec hwaccel typically handle decoding as follow:
In start_frame next OUTPUT buffer and its related request object is
picked from a circular queue and any codec specific CONTROLs are
prepared.
In decode_slice the slice bitstream data is appended to the OUTPUT
buffer.
In end_frame a CAPTURE buffer tied to the AVFrame is queued, it will be
used as the decoding target by the driver / hw decoder. The prepared
codec specific CONTROLs are queued as part of the request object.
Finally, the request object is submitted for decoding.
In post_process the AVFrame is only returned once the CAPTURE buffer
tied to the AVFrame has been made available for dequeuing by the driver.
For slice based hw decoders only the request for the final slice of the
frame is submitted in end_frame, remaining is submitted in decode_slice.
[1] https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/dev-stateless-decoder.html#decoding
Co-developed-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Signed-off-by: Jernej Skrabec <jernej.skrabec@gmail.com>
Co-developed-by: Alex Bee <knaerzche@gmail.com>
Signed-off-by: Alex Bee <knaerzche@gmail.com>
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
configure | 3 +-
libavcodec/hwconfig.h | 2 +
libavcodec/v4l2_request.c | 537 +++++++++++++++++++++++++++++++++++++-
libavcodec/v4l2_request.h | 41 +++
4 files changed, 581 insertions(+), 2 deletions(-)
--- a/configure
+++ b/configure
@@ -3403,7 +3403,7 @@ dxva2_deps="dxva2api_h DXVA2_ConfigPictu
ffnvcodec_deps_any="libdl LoadLibrary"
mediacodec_deps="android mediandk pthreads"
nvdec_deps="ffnvcodec"
-v4l2_request_deps="linux_media_h v4l2_timeval_to_ns libdrm libudev"
+v4l2_request_deps="linux_media_h v4l2_timeval_to_ns v4l2_m2m_hold_capture_buf libdrm libudev"
v4l2_request_suggest="libdrm libudev"
vaapi_x11_deps="xlib_x11"
videotoolbox_hwaccel_deps="videotoolbox pthreads"
@@ -7662,6 +7662,7 @@ if enabled v4l2_m2m; then
fi
if enabled v4l2_request; then
+ check_cc v4l2_m2m_hold_capture_buf linux/videodev2.h "int i = V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF"
check_func_headers "linux/media.h linux/videodev2.h" v4l2_timeval_to_ns
check_pkg_config libudev libudev libudev.h udev_new
v4l2_request_extralibs="$libudev_extralibs"
--- a/libavcodec/hwconfig.h
+++ b/libavcodec/hwconfig.h
@@ -79,6 +79,8 @@ void ff_hwaccel_uninit(AVCodecContext *a
HW_CONFIG_HWACCEL(0, 0, 1, D3D11VA_VLD, NONE, ff_ ## codec ## _d3d11va_hwaccel)
#define HWACCEL_D3D12VA(codec) \
HW_CONFIG_HWACCEL(1, 1, 0, D3D12, D3D12VA, ff_ ## codec ## _d3d12va_hwaccel)
+#define HWACCEL_V4L2REQUEST(codec) \
+ HW_CONFIG_HWACCEL(1, 0, 0, DRM_PRIME, V4L2REQUEST, ff_ ## codec ## _v4l2request_hwaccel)
#define HW_CONFIG_ENCODER(device, frames, ad_hoc, format, device_type_) \
&(const AVCodecHWConfigInternal) { \
--- a/libavcodec/v4l2_request.c
+++ b/libavcodec/v4l2_request.c
@@ -19,6 +19,7 @@
#include "config.h"
#include <linux/media.h>
+#include <poll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
@@ -29,6 +30,8 @@
#include "internal.h"
#include "v4l2_request.h"
+#define V4L2_PLANES_MAX 2
+
static const AVClass v4l2_request_context_class = {
.class_name = "V4L2RequestContext",
.item_name = av_default_item_name,
@@ -40,6 +43,537 @@ static inline V4L2RequestContext *v4l2_r
return (V4L2RequestContext *)avctx->internal->hwaccel_priv_data;
}
+static inline uint32_t v4l2_request_frameindex(AVFrame *frame)
+{
+ return (uint32_t)(uintptr_t)frame->data[1];
+}
+
+uint64_t ff_v4l2_request_get_capture_timestamp(AVFrame *frame)
+{
+ /*
+ * The CAPTURE buffer index is used as a base for V4L2 frame reference.
+ * This works because frames are decoded into a CAPTURE buffer that is
+ * closely tied to an AVFrame.
+ */
+ struct timeval timestamp = {
+ .tv_sec = 0,
+ .tv_usec = v4l2_request_frameindex(frame) + 1,
+ };
+ return v4l2_timeval_to_ns(×tamp);
+}
+
+int ff_v4l2_request_query_control(AVCodecContext *avctx,
+ struct v4l2_query_ext_ctrl *control)
+{
+ V4L2RequestContext *ctx = v4l2_request_context(avctx);
+
+ if (ioctl(ctx->fctxi->video_fd, VIDIOC_QUERY_EXT_CTRL, control) < 0) {
+ int ret = AVERROR(errno);
+ // Skip error logging when driver does not support control id (EINVAL)
+ if (errno != EINVAL)
+ av_log(ctx, AV_LOG_ERROR, "Failed to query control %u: %s (%d)\n",
+ control->id, strerror(errno), errno);
+ return ret;
+ }
+
+ return 0;
+}
+
+int ff_v4l2_request_query_control_default_value(AVCodecContext *avctx,
+ uint32_t id)
+{
+ struct v4l2_query_ext_ctrl control = {
+ .id = id,
+ };
+ int ret;
+
+ ret = ff_v4l2_request_query_control(avctx, &control);
+ if (ret < 0)
+ return ret;
+
+ return control.default_value;
+}
+
+static int v4l2_request_set_controls(V4L2RequestContext *ctx, int request_fd,
+ struct v4l2_ext_control *control, int count)
+{
+ struct v4l2_ext_controls controls = {
+ .controls = control,
+ .count = count,
+ .request_fd = request_fd,
+ .which = (request_fd >= 0) ? V4L2_CTRL_WHICH_REQUEST_VAL : 0,
+ };
+
+ if (!control || !count)
+ return 0;
+
+ if (ioctl(ctx->fctxi->video_fd, VIDIOC_S_EXT_CTRLS, &controls) < 0)
+ return AVERROR(errno);
+
+ return 0;
+}
+
+int ff_v4l2_request_set_controls(AVCodecContext *avctx,
+ struct v4l2_ext_control *control, int count)
+{
+ V4L2RequestContext *ctx = v4l2_request_context(avctx);
+ int ret;
+
+ ret = v4l2_request_set_controls(ctx, -1, control, count);
+ if (ret < 0)
+ av_log(ctx, AV_LOG_ERROR, "Failed to set %d control(s): %s (%d)\n",
+ count, strerror(errno), errno);
+
+ return ret;
+}
+
+static int v4l2_request_queue_buffer(V4L2RequestContext *ctx,
+ struct v4l2_buffer *buffer)
+{
+ struct v4l2_plane planes[V4L2_PLANES_MAX] = {};
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(buffer->type)) {
+ planes[0].bytesused = buffer->bytesused;
+ buffer->bytesused = 0;
+ buffer->length = FF_ARRAY_ELEMS(planes);
+ buffer->m.planes = planes;
+ }
+
+ // Queue the buffer
+ if (ioctl(ctx->fctxi->video_fd, VIDIOC_QBUF, buffer) < 0)
+ return AVERROR(errno);
+
+ // Mark the buffer as queued
+ if (V4L2_TYPE_IS_OUTPUT(buffer->type))
+ ctx->queued_output |= 1 << buffer->index;
+ else
+ ctx->queued_capture |= 1 << buffer->index;
+
+ return 0;
+}
+
+static int v4l2_request_queue_capture_buffer(V4L2RequestContext *ctx,
+ uint32_t index)
+{
+ struct v4l2_buffer buffer = {
+ .index = index,
+ .type = ctx->fctxi->capture.format.type,
+ .memory = V4L2_MEMORY_MMAP,
+ };
+ return v4l2_request_queue_buffer(ctx, &buffer);
+}
+
+static int v4l2_request_queue_output_buffer(V4L2RequestContext *ctx,
+ V4L2RequestOutputBuffer *output,
+ uint32_t flags)
+{
+ struct v4l2_buffer buffer = {
+ .index = output->index,
+ .type = ctx->fctxi->output.format.type,
+ .memory = V4L2_MEMORY_MMAP,
+ .timestamp = output->timestamp,
+ .bytesused = output->bytesused,
+ .request_fd = output->request_fd,
+ .flags = V4L2_BUF_FLAG_REQUEST_FD | flags,
+ };
+ return v4l2_request_queue_buffer(ctx, &buffer);
+}
+
+static int v4l2_request_dequeue_buffer(V4L2RequestContext *ctx,
+ enum v4l2_buf_type type)
+{
+ struct v4l2_plane planes[V4L2_PLANES_MAX] = {};
+ struct v4l2_buffer buffer = {
+ .type = type,
+ .memory = V4L2_MEMORY_MMAP,
+ };
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(buffer.type)) {
+ buffer.length = FF_ARRAY_ELEMS(planes);
+ buffer.m.planes = planes;
+ }
+
+ // Dequeue next completed buffer
+ if (ioctl(ctx->fctxi->video_fd, VIDIOC_DQBUF, &buffer) < 0)
+ return AVERROR(errno);
+
+ // Mark the buffer as dequeued
+ if (V4L2_TYPE_IS_OUTPUT(buffer.type))
+ ctx->queued_output &= ~(1 << buffer.index);
+ else
+ ctx->queued_capture &= ~(1 << buffer.index);
+
+ return 0;
+}
+
+static inline int v4l2_request_dequeue_completed_buffers(V4L2RequestContext *ctx,
+ enum v4l2_buf_type type)
+{
+ int ret;
+
+ do {
+ ret = v4l2_request_dequeue_buffer(ctx, type);
+ } while (!ret);
+
+ return ret;
+}
+
+static int v4l2_request_wait_on_capture(V4L2RequestContext *ctx, uint32_t index)
+{
+ enum v4l2_buf_type type = ctx->fctxi->capture.format.type;
+ struct pollfd pollfd = {
+ .fd = ctx->fctxi->video_fd,
+ .events = POLLIN,
+ };
+
+ ff_mutex_lock(&ctx->mutex);
+
+ // Dequeue all completed CAPTURE buffers
+ if (ctx->queued_capture)
+ v4l2_request_dequeue_completed_buffers(ctx, type);
+
+ // Wait on the specific CAPTURE buffer
+ while (ctx->queued_capture & (1 << index)) {
+ int ret = poll(&pollfd, 1, 2000);
+ if (ret <= 0)
+ goto fail;
+
+ ret = v4l2_request_dequeue_buffer(ctx, type);
+ if (ret < 0 && ret != AVERROR(EAGAIN))
+ goto fail;
+ }
+
+ ff_mutex_unlock(&ctx->mutex);
+ return 0;
+
+fail:
+ ff_mutex_unlock(&ctx->mutex);
+ av_log(ctx, AV_LOG_ERROR, "Failed waiting on CAPTURE buffer %d\n", index);
+ return AVERROR(EINVAL);
+}
+
+static V4L2RequestOutputBuffer *v4l2_request_next_output(V4L2RequestContext *ctx)
+{
+ enum v4l2_buf_type type = ctx->fctxi->output.format.type;
+ V4L2RequestOutputBuffer *output;
+ struct pollfd pollfd = {
+ .fd = ctx->fctxi->video_fd,
+ .events = POLLOUT,
+ };
+ uint8_t index;
+
+ ff_mutex_lock(&ctx->mutex);
+
+ // Use next OUTPUT buffer in the circular queue
+ index = ctx->next_output;
+ output = &ctx->output[index];
+ ctx->next_output = (index + 1) % FF_ARRAY_ELEMS(ctx->output);
+
+ // Dequeue all completed OUTPUT buffers
+ if (ctx->queued_output)
+ v4l2_request_dequeue_completed_buffers(ctx, type);
+
+ // Wait on the specific OUTPUT buffer
+ while (ctx->queued_output & (1 << output->index)) {
+ int ret = poll(&pollfd, 1, 2000);
+ if (ret <= 0)
+ goto fail;
+
+ ret = v4l2_request_dequeue_buffer(ctx, type);
+ if (ret < 0 && ret != AVERROR(EAGAIN))
+ goto fail;
+ }
+
+ ff_mutex_unlock(&ctx->mutex);
+
+ // Reset bytesused state
+ output->bytesused = 0;
+
+ return output;
+
+fail:
+ ff_mutex_unlock(&ctx->mutex);
+ av_log(ctx, AV_LOG_ERROR, "Failed waiting on OUTPUT buffer %d\n",
+ output->index);
+ return NULL;
+}
+
+static int v4l2_request_wait_on_request(V4L2RequestContext *ctx,
+ V4L2RequestOutputBuffer *output)
+{
+ struct pollfd pollfd = {
+ .fd = output->request_fd,
+ .events = POLLPRI,
+ };
+
+ // Wait on the specific request to complete
+ while (ctx->queued_request & (1 << output->index)) {
+ int ret = poll(&pollfd, 1, 2000);
+ if (ret <= 0)
+ break;
+
+ // Mark request as dequeued
+ if (pollfd.revents & (POLLPRI | POLLERR)) {
+ ctx->queued_request &= ~(1 << output->index);
+ break;
+ }
+ }
+
+ // Reinit the request object
+ if (ioctl(output->request_fd, MEDIA_REQUEST_IOC_REINIT) < 0) {
+ int ret = AVERROR(errno);
+ av_log(ctx, AV_LOG_ERROR, "Failed to reinit request object %d: %s (%d)\n",
+ output->request_fd, strerror(errno), errno);
+ return ret;
+ }
+
+ // Ensure request is marked as dequeued
+ ctx->queued_request &= ~(1 << output->index);
+
+ return 0;
+}
+
+int ff_v4l2_request_append_output(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic,
+ const uint8_t *data, uint32_t size)
+{
+ V4L2RequestContext *ctx = v4l2_request_context(avctx);
+
+ // Append data to OUTPUT buffer and ensure there is enough space for padding
+ if (pic->output->bytesused + size + AV_INPUT_BUFFER_PADDING_SIZE <= pic->output->size) {
+ memcpy(pic->output->addr + pic->output->bytesused, data, size);
+ pic->output->bytesused += size;
+ return 0;
+ } else {
+ av_log(ctx, AV_LOG_ERROR,
+ "Failed to append %u bytes data to OUTPUT buffer %d (%u of %u used)\n",
+ size, pic->output->index, pic->output->bytesused, pic->output->size);
+ return AVERROR(ENOMEM);
+ }
+}
+
+static int v4l2_request_queue_decode(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic,
+ struct v4l2_ext_control *control, int count,
+ bool first_slice, bool last_slice)
+{
+ V4L2RequestContext *ctx = v4l2_request_context(avctx);
+ uint32_t flags;
+ int ret;
+
+ if (first_slice) {
+ /*
+ * Wait on dequeue of the target CAPTURE buffer. Otherwise V4L2 decoder
+ * may use a different CAPTURE buffer than hwaccel expects.
+ *
+ * Normally decoding has already completed when a CAPTURE buffer is
+ * reused so this is more or less a no-op, however in some situations
+ * FFmpeg may reuse an AVFrame early, i.e. when no output frame was
+ * produced prior time, and a synchronization is necessary.
+ */
+ ret = v4l2_request_wait_on_capture(ctx, pic->capture_index);
+ if (ret < 0)
+ return ret;
+ }
+
+ ff_mutex_lock(&ctx->mutex);
+
+ /*
+ * The OUTPUT buffer tied to prior use of current request object can
+ * independently be dequeued before the full decode request has been
+ * completed. This may happen when a decoder use multi stage decoding,
+ * e.g. rpi-hevc-dec. In such case we can start reusing the OUTPUT buffer,
+ * however we must wait on the prior request to fully complete before we
+ * can reuse the request object, and a synchronization is necessary.
+ */
+ ret = v4l2_request_wait_on_request(ctx, pic->output);
+ if (ret < 0)
+ goto fail;
+
+ /*
+ * Dequeue any completed OUTPUT buffers, this is strictly not necessary,
+ * however if a synchronization was necessary for the CAPTURE and/or request
+ * there is more than likely one or more OUTPUT buffers that can be dequeued.
+ */
+ if (ctx->queued_output)
+ v4l2_request_dequeue_completed_buffers(ctx, ctx->fctxi->output.format.type);
+
+ // Set codec controls for current request
+ ret = v4l2_request_set_controls(ctx, pic->output->request_fd, control, count);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to set %d control(s) for request %d: %s (%d)\n",
+ count, pic->output->request_fd, strerror(errno), errno);
+ goto fail;
+ }
+
+ // Ensure there is zero padding at the end of bitstream data
+ memset(pic->output->addr + pic->output->bytesused, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
+ /*
+ * Use CAPTURE buffer index as base for V4L2 frame reference.
+ * This works because a CAPTURE buffer is closely tied to a AVFrame
+ * and FFmpeg handle all frame reference tracking for us.
+ */
+ pic->output->timestamp = (struct timeval) {
+ .tv_sec = 0,
+ .tv_usec = pic->capture_index + 1,
+ };
+
+ /*
+ * Queue the OUTPUT buffer of current request. The CAPTURE buffer may be
+ * hold by the V4L2 decoder unless this is the last slice of a frame.
+ */
+ flags = last_slice ? 0 : V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF;
+ ret = v4l2_request_queue_output_buffer(ctx, pic->output, flags);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to queue OUTPUT buffer %d for request %d: %s (%d)\n",
+ pic->output->index, pic->output->request_fd, strerror(errno), errno);
+ goto fail;
+ }
+
+ if (first_slice) {
+ /*
+ * Queue the target CAPTURE buffer, hwaccel expect and depend on that
+ * this specific CAPTURE buffer will be used as decode target for
+ * current request, otherwise frames may be output in wrong order or
+ * wrong CAPTURE buffer could get used as a reference frame.
+ */
+ ret = v4l2_request_queue_capture_buffer(ctx, pic->capture_index);
+ if (ret < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to queue CAPTURE buffer %d for request %d: %s (%d)\n",
+ pic->capture_index, pic->output->request_fd, strerror(errno), errno);
+ goto fail;
+ }
+ }
+
+ // Queue current request
+ ret = ioctl(pic->output->request_fd, MEDIA_REQUEST_IOC_QUEUE);
+ if (ret < 0) {
+ ret = AVERROR(errno);
+ av_log(ctx, AV_LOG_ERROR, "Failed to queue request object %d: %s (%d)\n",
+ pic->output->request_fd, strerror(errno), errno);
+ goto fail;
+ }
+
+ // Mark current request as queued
+ ctx->queued_request |= 1 << pic->output->index;
+
+ ret = 0;
+fail:
+ ff_mutex_unlock(&ctx->mutex);
+ return ret;
+}
+
+int ff_v4l2_request_decode_slice(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic,
+ struct v4l2_ext_control *control, int count,
+ bool first_slice, bool last_slice)
+{
+ V4L2RequestContext *ctx = v4l2_request_context(avctx);
+
+ /*
+ * Fallback to queue each slice as a full frame when holding CAPTURE
+ * buffers is not supported by the driver.
+ */
+ if ((ctx->fctxi->output.capabilities & V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF) !=
+ V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF)
+ return v4l2_request_queue_decode(avctx, pic, control, count, true, true);
+
+ return v4l2_request_queue_decode(avctx, pic, control, count,
+ first_slice, last_slice);
+}
+
+int ff_v4l2_request_decode_frame(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic,
+ struct v4l2_ext_control *control, int count)
+{
+ return v4l2_request_queue_decode(avctx, pic, control, count, true, true);
+}
+
+static int v4l2_request_post_process(void *logctx, AVFrame *frame)
+{
+ uint32_t index = v4l2_request_frameindex(frame);
+ FrameDecodeData *fdd = frame->private_ref;
+ V4L2RequestContext *ctx = fdd->hwaccel_priv;
+
+ // Wait on CAPTURE buffer before returning the frame to application
+ return v4l2_request_wait_on_capture(ctx, index);
+}
+
+int ff_v4l2_request_reset_picture(AVCodecContext *avctx, V4L2RequestPictureContext *pic)
+{
+ V4L2RequestContext *ctx = v4l2_request_context(avctx);
+
+ // Get and wait on next OUTPUT buffer from circular queue
+ pic->output = v4l2_request_next_output(ctx);
+ if (!pic->output)
+ return AVERROR(EINVAL);
+
+ return 0;
+}
+
+int ff_v4l2_request_start_frame(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic,
+ AVFrame *frame)
+{
+ V4L2RequestContext *ctx = v4l2_request_context(avctx);
+ uint32_t index = v4l2_request_frameindex(frame);
+ FrameDecodeData *fdd = frame->private_ref;
+ int ret;
+
+ // Get next OUTPUT buffer from circular queue
+ ret = ff_v4l2_request_reset_picture(avctx, pic);
+ if (ret)
+ return ret;
+
+ // Ensure CAPTURE buffer is dequeued before reuse
+ ret = v4l2_request_wait_on_capture(ctx, index);
+ if (ret)
+ return ret;
+
+ // Wait on CAPTURE buffer in post_process() before returning to application
+ fdd->hwaccel_priv = ctx;
+ fdd->post_process = v4l2_request_post_process;
+
+ // CAPTURE buffer used for current frame
+ pic->capture_index = index;
+
+ return 0;
+}
+
+void ff_v4l2_request_flush(AVCodecContext *avctx)
+{
+ V4L2RequestContext *ctx = v4l2_request_context(avctx);
+ enum v4l2_buf_type type = ctx->fctxi->output.format.type;
+ struct pollfd pollfd = {
+ .fd = ctx->fctxi->video_fd,
+ .events = POLLOUT,
+ };
+
+ ff_mutex_lock(&ctx->mutex);
+
+ // Dequeue all completed OUTPUT buffers
+ if (ctx->queued_output)
+ v4l2_request_dequeue_completed_buffers(ctx, type);
+
+ // Wait on any remaining OUTPUT buffer
+ while (ctx->queued_output) {
+ int ret = poll(&pollfd, 1, 2000);
+ if (ret <= 0)
+ break;
+
+ ret = v4l2_request_dequeue_buffer(ctx, type);
+ if (ret < 0 && ret != AVERROR(EAGAIN))
+ break;
+ }
+
+ // Dequeue all completed CAPTURE buffers
+ if (ctx->queued_capture)
+ v4l2_request_dequeue_completed_buffers(ctx, ctx->fctxi->capture.format.type);
+
+ ff_mutex_unlock(&ctx->mutex);
+}
+
static void v4l2_request_output_buffer_uninit(V4L2RequestOutputBuffer *output)
{
// Close the request associated with the OUTPUT buffer
@@ -142,7 +676,8 @@ int ff_v4l2_request_uninit(AVCodecContex
enum v4l2_buf_type type;
if (ctx->fctxi) {
- // TODO: Flush and wait on all pending requests
+ // Flush and wait on all pending requests
+ ff_v4l2_request_flush(avctx);
// Stop streaming on OUTPUT queue
type = ctx->fctxi->output.format.type;
--- a/libavcodec/v4l2_request.h
+++ b/libavcodec/v4l2_request.h
@@ -46,10 +46,51 @@ typedef struct V4L2RequestContext {
AVV4L2RequestFramesContextInternal *fctxi;
AVMutex mutex;
V4L2RequestOutputBuffer output[4];
+ uint8_t next_output;
+ uint32_t queued_output;
+ uint32_t queued_request;
+ uint64_t queued_capture;
struct v4l2_ext_control *init_controls;
int nb_init_controls;
} V4L2RequestContext;
+typedef struct V4L2RequestPictureContext {
+ V4L2RequestOutputBuffer *output;
+ uint32_t capture_index;
+} V4L2RequestPictureContext;
+
+uint64_t ff_v4l2_request_get_capture_timestamp(AVFrame *frame);
+
+int ff_v4l2_request_query_control(AVCodecContext *avctx,
+ struct v4l2_query_ext_ctrl *control);
+
+int ff_v4l2_request_query_control_default_value(AVCodecContext *avctx,
+ uint32_t id);
+
+int ff_v4l2_request_set_controls(AVCodecContext *avctx,
+ struct v4l2_ext_control *control, int count);
+
+int ff_v4l2_request_append_output(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic,
+ const uint8_t *data, uint32_t size);
+
+int ff_v4l2_request_decode_slice(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic,
+ struct v4l2_ext_control *control, int count,
+ bool first_slice, bool last_slice);
+
+int ff_v4l2_request_decode_frame(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic,
+ struct v4l2_ext_control *control, int count);
+
+int ff_v4l2_request_reset_picture(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic);
+
+int ff_v4l2_request_start_frame(AVCodecContext *avctx,
+ V4L2RequestPictureContext *pic, AVFrame *frame);
+
+void ff_v4l2_request_flush(AVCodecContext *avctx);
+
int ff_v4l2_request_frame_params(AVCodecContext *avctx,
AVBufferRef *hw_frames_ctx,
uint32_t pixelformat,
|