diff --git a/packages/multimedia/ffmpeg/patches/rpi/ffmpeg-001-rpi.patch b/packages/multimedia/ffmpeg/patches/rpi/ffmpeg-001-rpi.patch index 5e46eb7261..87a16f85db 100644 --- a/packages/multimedia/ffmpeg/patches/rpi/ffmpeg-001-rpi.patch +++ b/packages/multimedia/ffmpeg/patches/rpi/ffmpeg-001-rpi.patch @@ -1,5 +1,5 @@ diff --git a/configure b/configure -index d7a3f507e8..7c2dd7161c 100755 +index d7a3f507e8..83383b0317 100755 --- a/configure +++ b/configure @@ -207,6 +207,7 @@ External library support: @@ -192,23 +192,27 @@ index d7a3f507e8..7c2dd7161c 100755 if enabled gcrypt; then GCRYPT_CONFIG="${cross_prefix}libgcrypt-config" -@@ -6617,6 +6667,16 @@ if enabled v4l2_m2m; then +@@ -6617,6 +6667,10 @@ if enabled v4l2_m2m; then check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" fi +check_func_headers "linux/media.h linux/videodev2.h" v4l2_timeval_to_ns +check_cc hevc_v4l2_request linux/videodev2.h "int i = V4L2_PIX_FMT_HEVC_SLICE;" +disable v4l2_req_hevc_vx -+if enabled hevc_v4l2request_hwaccel; then -+ enable v4l2_req_hevc_vx -+fi -+if enabled hevc_v4l2_request; then -+ disable v4l2_req_hevc_vx -+fi + check_headers sys/videoio.h test_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete +@@ -7104,6 +7158,9 @@ check_deps $CONFIG_LIST \ + enabled threads && ! enabled pthreads && ! enabled atomics_native && die "non pthread threading without atomics not supported, try adding --enable-pthreads or --cpu=i486 or higher if you are on x86" + enabled avresample && warn "Building with deprecated library libavresample" + ++# Sub-feature of hevc_v4l2request_hwaccel - can only be set once deps are done ++enabled hevc_v4l2request_hwaccel && disabled hevc_v4l2_request && enable v4l2_req_hevc_vx ++ + case $target_os in + haiku) + disable memalign diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index 46bb014de8..0502ff71b8 100644 --- a/fftools/ffmpeg.c @@ -384,7 +388,7 @@ index 807e783422..456d4f349b 100644 "write program-readable progress information", "url" }, { "stdin", OPT_BOOL | OPT_EXPERT, { &stdin_interaction }, diff --git a/libavcodec/Makefile b/libavcodec/Makefile -index 33a280cf69..1372d3981d 100644 +index 33a280cf69..e93c842047 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -19,6 +19,7 @@ HEADERS = ac3_parser.h \ @@ -409,7 +413,7 @@ index 33a280cf69..1372d3981d 100644 OBJS-$(CONFIG_VP8DSP) += vp8dsp.o -OBJS-$(CONFIG_V4L2_M2M) += v4l2_m2m.o v4l2_context.o v4l2_buffers.o v4l2_fmt.o +OBJS-$(CONFIG_V4L2_M2M) += v4l2_m2m.o v4l2_context.o v4l2_buffers.o v4l2_fmt.o\ -+ weak_link.o ++ weak_link.o v4l2_req_dmabufs.o +OBJS-$(CONFIG_V4L2_REQUEST) += v4l2_req_media.o v4l2_req_pollqueue.o v4l2_req_dmabufs.o\ + v4l2_req_devscan.o weak_link.o OBJS-$(CONFIG_WMA_FREQS) += wma_freqs.o @@ -50022,7 +50026,7 @@ index 0000000000..85c5b46d75 +}; + diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c -index 4b2679eb38..9ef2f40e39 100644 +index 4b2679eb38..8d80d19788 100644 --- a/libavcodec/v4l2_buffers.c +++ b/libavcodec/v4l2_buffers.c @@ -21,6 +21,7 @@ @@ -50033,7 +50037,7 @@ index 4b2679eb38..9ef2f40e39 100644 #include #include #include -@@ -29,57 +30,82 @@ +@@ -29,57 +30,88 @@ #include #include "libavcodec/avcodec.h" #include "libavcodec/internal.h" @@ -50043,6 +50047,7 @@ index 4b2679eb38..9ef2f40e39 100644 #include "v4l2_context.h" #include "v4l2_buffers.h" #include "v4l2_m2m.h" ++#include "v4l2_req_dmabufs.h" +#include "weak_link.h" #define USEC_PER_SEC 1000000 @@ -50050,27 +50055,35 @@ index 4b2679eb38..9ef2f40e39 100644 +static const AVRational v4l2_timebase = { 1, USEC_PER_SEC }; -static inline V4L2m2mContext *buf_to_m2mctx(V4L2Buffer *buf) -+static inline V4L2m2mContext *buf_to_m2mctx(const V4L2Buffer * const buf) ++static inline V4L2m2mContext *ctx_to_m2mctx(const V4L2Context *ctx) { - return V4L2_TYPE_IS_OUTPUT(buf->context->type) ? - container_of(buf->context, V4L2m2mContext, output) : - container_of(buf->context, V4L2m2mContext, capture); +- return V4L2_TYPE_IS_OUTPUT(buf->context->type) ? +- container_of(buf->context, V4L2m2mContext, output) : +- container_of(buf->context, V4L2m2mContext, capture); ++ return V4L2_TYPE_IS_OUTPUT(ctx->type) ? ++ container_of(ctx, V4L2m2mContext, output) : ++ container_of(ctx, V4L2m2mContext, capture); } -static inline AVCodecContext *logger(V4L2Buffer *buf) -+static inline AVCodecContext *logger(const V4L2Buffer * const buf) ++static inline V4L2m2mContext *buf_to_m2mctx(const V4L2Buffer * const buf) { - return buf_to_m2mctx(buf)->avctx; +- return buf_to_m2mctx(buf)->avctx; ++ return ctx_to_m2mctx(buf->context); } -static inline AVRational v4l2_get_timebase(V4L2Buffer *avbuf) -+static inline AVRational v4l2_get_timebase(const V4L2Buffer * const avbuf) ++static inline AVCodecContext *logger(const V4L2Buffer * const buf) { - V4L2m2mContext *s = buf_to_m2mctx(avbuf); -- ++ return buf_to_m2mctx(buf)->avctx; ++} + - if (s->avctx->pkt_timebase.num) - return s->avctx->pkt_timebase; - return s->avctx->time_base; ++static inline AVRational v4l2_get_timebase(const V4L2Buffer * const avbuf) ++{ + const V4L2m2mContext *s = buf_to_m2mctx(avbuf); + const AVRational tb = s->avctx->pkt_timebase.num ? + s->avctx->pkt_timebase : @@ -50138,7 +50151,7 @@ index 4b2679eb38..9ef2f40e39 100644 } static enum AVColorPrimaries v4l2_get_color_primaries(V4L2Buffer *buf) -@@ -116,6 +142,105 @@ static enum AVColorPrimaries v4l2_get_color_primaries(V4L2Buffer *buf) +@@ -116,6 +148,105 @@ static enum AVColorPrimaries v4l2_get_color_primaries(V4L2Buffer *buf) return AVCOL_PRI_UNSPECIFIED; } @@ -50244,7 +50257,7 @@ index 4b2679eb38..9ef2f40e39 100644 static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf) { enum v4l2_quantization qt; -@@ -134,6 +259,20 @@ static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf) +@@ -134,6 +265,20 @@ static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf) return AVCOL_RANGE_UNSPECIFIED; } @@ -50265,7 +50278,7 @@ index 4b2679eb38..9ef2f40e39 100644 static enum AVColorSpace v4l2_get_color_space(V4L2Buffer *buf) { enum v4l2_ycbcr_encoding ycbcr; -@@ -210,73 +349,165 @@ static enum AVColorTransferCharacteristic v4l2_get_color_trc(V4L2Buffer *buf) +@@ -210,73 +355,178 @@ static enum AVColorTransferCharacteristic v4l2_get_color_trc(V4L2Buffer *buf) return AVCOL_TRC_UNSPECIFIED; } @@ -50300,13 +50313,13 @@ index 4b2679eb38..9ef2f40e39 100644 + buf->buf.field = !is_interlaced ? V4L2_FIELD_NONE : + is_tff ? V4L2_FIELD_INTERLACED_TB : V4L2_FIELD_INTERLACED_BT; +} -+ + +- av_buffer_unref(&avbuf->context_ref); +static uint8_t * v4l2_get_drm_frame(V4L2Buffer *avbuf) +{ + AVDRMFrameDescriptor *drm_desc = &avbuf->drm_frame; + AVDRMLayerDescriptor *layer; - -- av_buffer_unref(&avbuf->context_ref); ++ + /* fill the DRM frame descriptor */ + drm_desc->nb_objects = avbuf->num_planes; + drm_desc->nb_layers = 1; @@ -50316,7 +50329,7 @@ index 4b2679eb38..9ef2f40e39 100644 + + for (int i = 0; i < avbuf->num_planes; i++) { + layer->planes[i].object_index = i; -+ layer->planes[i].offset = 0; ++ layer->planes[i].offset = avbuf->plane_info[i].offset; + layer->planes[i].pitch = avbuf->plane_info[i].bytesperline; } + @@ -50423,47 +50436,60 @@ index 4b2679eb38..9ef2f40e39 100644 } -static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf) -+static int v4l2_buffer_export_drm(V4L2Buffer* avbuf) ++static inline uint32_t ff_v4l2_buf_len(const struct v4l2_buffer * b, unsigned int i) { - int ret; -+ struct v4l2_exportbuffer expbuf; -+ int i, ret; ++ return V4L2_TYPE_IS_MULTIPLANAR(b->type) ? b->m.planes[i].length : b->length; ++} - if (plane >= in->num_planes) - return AVERROR(EINVAL); -+ for (i = 0; i < avbuf->num_planes; i++) { -+ memset(&expbuf, 0, sizeof(expbuf)); ++static int v4l2_buffer_export_drm(V4L2Buffer* avbuf) ++{ ++ int i, ret; ++ const V4L2m2mContext * const s = buf_to_m2mctx(avbuf); - /* even though most encoders return 0 in data_offset encoding vp8 does require this value */ - *buf = av_buffer_create((char *)in->plane_info[plane].mm_addr + in->planes[plane].data_offset, - in->plane_info[plane].length, v4l2_free_buffer, in, 0); - if (!*buf) - return AVERROR(ENOMEM); -+ expbuf.index = avbuf->buf.index; -+ expbuf.type = avbuf->buf.type; -+ expbuf.plane = i; ++ for (i = 0; i < avbuf->num_planes; i++) { ++ int dma_fd = -1; ++ const uint32_t blen = ff_v4l2_buf_len(&avbuf->buf, i); ++ ++ if (s->db_ctl != NULL) { ++ if ((avbuf->dmabuf[i] = dmabuf_alloc(s->db_ctl, blen)) == NULL) ++ return AVERROR(ENOMEM); ++ dma_fd = dmabuf_fd(avbuf->dmabuf[i]); ++ if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->buf.type)) ++ avbuf->buf.m.planes[i].m.fd = dma_fd; ++ else ++ avbuf->buf.m.fd = dma_fd; ++ } ++ else { ++ struct v4l2_exportbuffer expbuf; ++ memset(&expbuf, 0, sizeof(expbuf)); ++ ++ expbuf.index = avbuf->buf.index; ++ expbuf.type = avbuf->buf.type; ++ expbuf.plane = i; ++ ++ ret = ioctl(s->fd, VIDIOC_EXPBUF, &expbuf); ++ if (ret < 0) ++ return AVERROR(errno); ++ dma_fd = expbuf.fd; ++ } - ret = v4l2_buf_increase_ref(in); - if (ret) - av_buffer_unref(buf); -+ ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_EXPBUF, &expbuf); -+ if (ret < 0) -+ return AVERROR(errno); ++ avbuf->drm_frame.objects[i].size = blen; ++ avbuf->drm_frame.objects[i].fd = dma_fd; ++ avbuf->drm_frame.objects[i].format_modifier = DRM_FORMAT_MOD_LINEAR; ++ } - return ret; -+ if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->buf.type)) { -+ /* drm frame */ -+ avbuf->drm_frame.objects[i].size = avbuf->buf.m.planes[i].length; -+ avbuf->drm_frame.objects[i].fd = expbuf.fd; -+ avbuf->drm_frame.objects[i].format_modifier = DRM_FORMAT_MOD_LINEAR; -+ } else { -+ /* drm frame */ -+ avbuf->drm_frame.objects[0].size = avbuf->buf.length; -+ avbuf->drm_frame.objects[0].fd = expbuf.fd; -+ avbuf->drm_frame.objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR; -+ } -+ } -+ + return 0; } @@ -50474,7 +50500,7 @@ index 4b2679eb38..9ef2f40e39 100644 if (plane >= out->num_planes) return AVERROR(EINVAL); -@@ -284,32 +515,57 @@ static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, i +@@ -284,32 +534,57 @@ static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, i length = out->plane_info[plane].length; bytesused = FFMIN(size+offset, length); @@ -50529,7 +50555,7 @@ index 4b2679eb38..9ef2f40e39 100644 + frame->buf[0] = wrap_avbuf(avbuf); + if (frame->buf[0] == NULL) + return AVERROR(ENOMEM); -+ + + if (buf_to_m2mctx(avbuf)->output_drm) { + /* 1. get references to the actual data */ + frame->data[0] = (uint8_t *) v4l2_get_drm_frame(avbuf); @@ -50537,7 +50563,7 @@ index 4b2679eb38..9ef2f40e39 100644 + frame->hw_frames_ctx = av_buffer_ref(avbuf->context->frames_ref); + return 0; + } - ++ + + /* 1. get references to the actual data */ + for (i = 0; i < avbuf->num_planes; i++) { @@ -50547,7 +50573,7 @@ index 4b2679eb38..9ef2f40e39 100644 } /* fixup special cases */ -@@ -318,17 +574,17 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) +@@ -318,17 +593,17 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) case AV_PIX_FMT_NV21: if (avbuf->num_planes > 1) break; @@ -50571,7 +50597,7 @@ index 4b2679eb38..9ef2f40e39 100644 break; default: -@@ -338,68 +594,127 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) +@@ -338,68 +613,127 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) return 0; } @@ -50753,7 +50779,7 @@ index 4b2679eb38..9ef2f40e39 100644 return 0; } -@@ -409,16 +724,31 @@ static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out) +@@ -409,16 +743,31 @@ static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out) * ******************************************************************************/ @@ -50789,7 +50815,7 @@ index 4b2679eb38..9ef2f40e39 100644 av_frame_unref(frame); -@@ -429,17 +759,32 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) +@@ -429,17 +778,32 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) /* 2. get frame information */ frame->key_frame = !!(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME); @@ -50825,7 +50851,7 @@ index 4b2679eb38..9ef2f40e39 100644 /* 3. report errors upstream */ if (avbuf->buf.flags & V4L2_BUF_FLAG_ERROR) { -@@ -452,15 +797,15 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) +@@ -452,15 +816,15 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *avbuf) { @@ -50847,7 +50873,7 @@ index 4b2679eb38..9ef2f40e39 100644 if (avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME) pkt->flags |= AV_PKT_FLAG_KEY; -@@ -475,31 +820,91 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *avbuf) +@@ -475,39 +839,107 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *avbuf) return 0; } @@ -50904,9 +50930,16 @@ index 4b2679eb38..9ef2f40e39 100644 + munmap(p->mm_addr, p->length); + } + -+ for (i = 0; i != FF_ARRAY_ELEMS(avbuf->drm_frame.objects); ++i) { -+ if (avbuf->drm_frame.objects[i].fd != -1) -+ close(avbuf->drm_frame.objects[i].fd); ++ if (avbuf->dmabuf[0] == NULL) { ++ for (i = 0; i != FF_ARRAY_ELEMS(avbuf->drm_frame.objects); ++i) { ++ if (avbuf->drm_frame.objects[i].fd != -1) ++ close(avbuf->drm_frame.objects[i].fd); ++ } ++ } ++ else { ++ for (i = 0; i != FF_ARRAY_ELEMS(avbuf->dmabuf); ++i) { ++ dmabuf_free(avbuf->dmabuf[i]); ++ } + } + + av_buffer_unref(&avbuf->ref_buf); @@ -50923,7 +50956,9 @@ index 4b2679eb38..9ef2f40e39 100644 int ret, i; + V4L2Buffer * const avbuf = av_mallocz(sizeof(*avbuf)); + AVBufferRef * bufref; -+ ++ V4L2m2mContext * const s = ctx_to_m2mctx(ctx); + +- avbuf->buf.memory = V4L2_MEMORY_MMAP; + *pbufref = NULL; + if (avbuf == NULL) + return AVERROR(ENOMEM); @@ -50933,8 +50968,7 @@ index 4b2679eb38..9ef2f40e39 100644 + av_free(avbuf); + return AVERROR(ENOMEM); + } - -- avbuf->buf.memory = V4L2_MEMORY_MMAP; ++ + avbuf->context = ctx; + avbuf->buf.memory = mem; avbuf->buf.type = ctx->type; @@ -50949,16 +50983,17 @@ index 4b2679eb38..9ef2f40e39 100644 if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->buf.length = VIDEO_MAX_PLANES; avbuf->buf.m.planes = avbuf->planes; -@@ -507,7 +912,7 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) + } - ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_QUERYBUF, &avbuf->buf); +- ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_QUERYBUF, &avbuf->buf); ++ ret = ioctl(s->fd, VIDIOC_QUERYBUF, &avbuf->buf); if (ret < 0) - return AVERROR(errno); + goto fail; if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->num_planes = 0; -@@ -520,6 +925,8 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) +@@ -520,6 +952,8 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) avbuf->num_planes = 1; for (i = 0; i < avbuf->num_planes; i++) { @@ -50967,13 +51002,14 @@ index 4b2679eb38..9ef2f40e39 100644 avbuf->plane_info[i].bytesperline = V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? ctx->format.fmt.pix_mp.plane_fmt[i].bytesperline : -@@ -527,25 +934,29 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) +@@ -527,25 +961,31 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->plane_info[i].length = avbuf->buf.m.planes[i].length; - avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length, - PROT_READ | PROT_WRITE, MAP_SHARED, - buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.planes[i].m.mem_offset); ++ avbuf->plane_info[i].offset = avbuf->buf.m.planes[i].data_offset; + + if (want_mmap) + avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length, @@ -50984,6 +51020,7 @@ index 4b2679eb38..9ef2f40e39 100644 - avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.length, - PROT_READ | PROT_WRITE, MAP_SHARED, - buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.offset); ++ avbuf->plane_info[i].offset = 0; + + if (want_mmap) + avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.length, @@ -51008,16 +51045,18 @@ index 4b2679eb38..9ef2f40e39 100644 if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->buf.m.planes = avbuf->planes; avbuf->buf.length = avbuf->num_planes; -@@ -555,20 +966,51 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) +@@ -555,20 +995,53 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) avbuf->buf.length = avbuf->planes[0].length; } - return ff_v4l2_buffer_enqueue(avbuf); + if (!V4L2_TYPE_IS_OUTPUT(ctx->type)) { -+ if (buf_to_m2mctx(avbuf)->output_drm) { ++ if (s->output_drm) { + ret = v4l2_buffer_export_drm(avbuf); -+ if (ret) -+ goto fail; ++ if (ret) { ++ av_log(logger(avbuf), AV_LOG_ERROR, "Failed to get exported drm handles\n"); ++ goto fail; ++ } + } + } + @@ -51065,10 +51104,10 @@ index 4b2679eb38..9ef2f40e39 100644 return 0; } diff --git a/libavcodec/v4l2_buffers.h b/libavcodec/v4l2_buffers.h -index 8dbc7fc104..e64441ec9b 100644 +index 8dbc7fc104..0bda4dd06b 100644 --- a/libavcodec/v4l2_buffers.h +++ b/libavcodec/v4l2_buffers.h -@@ -27,25 +27,38 @@ +@@ -27,29 +27,44 @@ #include #include @@ -51087,6 +51126,7 @@ index 8dbc7fc104..e64441ec9b 100644 */ +struct V4L2Context; +struct ff_weak_link_client; ++struct dmabuf_h; + typedef struct V4L2Buffer { - /* each buffer needs to have a reference to its context */ @@ -51112,15 +51152,24 @@ index 8dbc7fc104..e64441ec9b 100644 /* keep track of the mmap address and mmap length */ struct V4L2Plane_info { -@@ -60,7 +73,6 @@ typedef struct V4L2Buffer { +- int bytesperline; ++ size_t bytesperline; ++ size_t offset; + void * mm_addr; + size_t length; + } plane_info[VIDEO_MAX_PLANES]; +@@ -60,9 +75,9 @@ typedef struct V4L2Buffer { struct v4l2_buffer buf; struct v4l2_plane planes[VIDEO_MAX_PLANES]; - int flags; enum V4L2Buffer_status status; ++ struct dmabuf_h * dmabuf[VIDEO_MAX_PLANES]; // If externally alloced dmabufs - stash other info here } V4L2Buffer; -@@ -98,6 +110,10 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *buf); + + /** +@@ -98,6 +113,10 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *buf); */ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out); @@ -51131,7 +51180,7 @@ index 8dbc7fc104..e64441ec9b 100644 /** * Extracts the data from an AVFrame to a V4L2Buffer * -@@ -106,7 +122,7 @@ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out); +@@ -106,7 +125,7 @@ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out); * * @returns 0 in case of success, a negative AVERROR code otherwise */ @@ -51140,7 +51189,7 @@ index 8dbc7fc104..e64441ec9b 100644 /** * Initializes a V4L2Buffer -@@ -116,7 +132,7 @@ int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out); +@@ -116,7 +135,7 @@ int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out); * * @returns 0 in case of success, a negative AVERROR code otherwise */ @@ -51149,7 +51198,7 @@ index 8dbc7fc104..e64441ec9b 100644 /** * Enqueues a V4L2Buffer -@@ -127,5 +143,12 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index); +@@ -127,5 +146,12 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index); */ int ff_v4l2_buffer_enqueue(V4L2Buffer* avbuf); @@ -51163,7 +51212,7 @@ index 8dbc7fc104..e64441ec9b 100644 #endif // AVCODEC_V4L2_BUFFERS_H diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c -index ff1ea8e57b..eaaec44666 100644 +index ff1ea8e57b..fcd5fdf359 100644 --- a/libavcodec/v4l2_context.c +++ b/libavcodec/v4l2_context.c @@ -27,11 +27,13 @@ @@ -51234,13 +51283,11 @@ index ff1ea8e57b..eaaec44666 100644 + .track_pts = track_pts + }; + return track_pts; - } - --static inline unsigned int v4l2_get_height(struct v4l2_format *fmt) ++} ++ +static int64_t +xlat_pts_frame_in(AVCodecContext *const avctx, xlat_track_t *const x, const AVFrame *const frame) - { -- return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height; ++{ + int64_t track_pts; + + // Avoid 0 @@ -51262,15 +51309,17 @@ index ff1ea8e57b..eaaec44666 100644 + .track_pts = track_pts + }; + return track_pts; -+} -+ + } + +-static inline unsigned int v4l2_get_height(struct v4l2_format *fmt) + +// Returns -1 if we should discard the frame +static int +xlat_pts_frame_out(AVCodecContext *const avctx, + xlat_track_t * const x, + AVFrame *const frame) -+{ + { +- return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height; + unsigned int n = pts_to_track(avctx, frame->pts) % FF_V4L2_M2M_TRACK_SIZE; + V4L2m2mTrackEl *const t = x->track_els + n; + if (frame->pts == AV_NOPTS_VALUE || frame->pts != t->track_pts) @@ -51471,12 +51520,12 @@ index ff1ea8e57b..eaaec44666 100644 - s->output.sample_aspect_ratio = v4l2_get_sar(&s->output); - } + get_default_selection(&s->capture, &s->capture.selection); -+ + +- reinit = v4l2_resolution_changed(&s->capture, &cap_fmt); + reinit = ctx_resolution_changed(&s->capture, &cap_fmt); + if ((s->quirks & FF_V4L2_QUIRK_REINIT_ALWAYS) != 0) + reinit = 1; - -- reinit = v4l2_resolution_changed(&s->capture, &cap_fmt); ++ + s->capture.format = cap_fmt; if (reinit) { - s->capture.height = v4l2_get_height(&cap_fmt); @@ -51525,16 +51574,16 @@ index ff1ea8e57b..eaaec44666 100644 if (ret) { - av_log(logger(ctx), AV_LOG_ERROR, "v4l2_m2m_codec_reinit\n"); + av_log(avctx, AV_LOG_ERROR, "v4l2_m2m_codec_reinit failed\n"); - return AVERROR(EINVAL); - } ++ return AVERROR(EINVAL); ++ } + + if (s->capture.width > ff_v4l2_get_format_width(&s->capture.format) || + s->capture.height > ff_v4l2_get_format_height(&s->capture.format)) { + av_log(avctx, AV_LOG_ERROR, "Format post reinit too small: wanted %dx%d > got %dx%d\n", + s->capture.width, s->capture.height, + ff_v4l2_get_format_width(&s->capture.format), ff_v4l2_get_format_height(&s->capture.format)); -+ return AVERROR(EINVAL); -+ } + return AVERROR(EINVAL); + } + + // Update pixel format - should only actually do something on initial change + s->capture.av_pix_fmt = @@ -51565,7 +51614,7 @@ index ff1ea8e57b..eaaec44666 100644 return 1; } -@@ -280,171 +452,282 @@ static int v4l2_stop_encode(V4L2Context *ctx) +@@ -280,171 +452,293 @@ static int v4l2_stop_encode(V4L2Context *ctx) return 0; } @@ -51767,6 +51816,11 @@ index ff1ea8e57b..eaaec44666 100644 + return 0; +} + ++static inline int ++dq_ok(const V4L2Context * const c) ++{ ++ return c->streamon && atomic_load(&c->q_count) != 0; ++} + +// Get a buffer +// If output then just gets the buffer in the expected way @@ -51805,7 +51859,7 @@ index ff1ea8e57b..eaaec44666 100644 - /* 2. dequeue the buffer */ - if (pfd.revents & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) { + // If capture && timeout == -1 then also wait for rx buffer free -+ if (is_cap && timeout == -1 && m->output.streamon && !m->draining) ++ if (is_cap && timeout == -1 && dq_ok(&m->output) && !m->draining) + pfd.events |= poll_out; - if (!V4L2_TYPE_IS_OUTPUT(ctx->type)) { @@ -51813,9 +51867,9 @@ index ff1ea8e57b..eaaec44666 100644 - if (pfd.revents & (POLLIN | POLLRDNORM)) - goto dequeue; + // If nothing Qed all we will get is POLLERR - avoid that -+ if ((pfd.events == poll_out && atomic_load(&m->output.q_count) == 0) || -+ (pfd.events == poll_cap && atomic_load(&m->capture.q_count) == 0) || -+ (pfd.events == (poll_cap | poll_out) && atomic_load(&m->capture.q_count) == 0 && atomic_load(&m->output.q_count) == 0)) { ++ if ((pfd.events == poll_out && !dq_ok(&m->output)) || ++ (pfd.events == poll_cap && !dq_ok(&m->capture)) || ++ (pfd.events == (poll_cap | poll_out) && !dq_ok(&m->capture) && !dq_ok(&m->output))) { + av_log(avctx, AV_LOG_TRACE, "V4L2 poll %s empty\n", ctx->name); + return AVERROR(ENOSPC); + } @@ -51874,17 +51928,17 @@ index ff1ea8e57b..eaaec44666 100644 } - return NULL; + return AVERROR(EAGAIN); ++ } ++ ++ if ((pfd.revents & POLLERR) != 0) { ++ av_log(avctx, AV_LOG_WARNING, "V4L2 %s POLLERR\n", ctx->name); ++ return AVERROR_UNKNOWN; } - if (ctx_to_m2mctx(ctx)->draining && !V4L2_TYPE_IS_OUTPUT(ctx->type)) { - int bytesused = V4L2_TYPE_IS_MULTIPLANAR(buf.type) ? - buf.m.planes[0].bytesused : buf.bytesused; - if (bytesused == 0) { -+ if ((pfd.revents & POLLERR) != 0) { -+ av_log(avctx, AV_LOG_WARNING, "V4L2 %s POLLERR\n", ctx->name); -+ return AVERROR_UNKNOWN; -+ } -+ + if ((pfd.revents & poll_event) != 0) { + ret = get_event(m); + if (ret < 0) { @@ -51897,6 +51951,13 @@ index ff1ea8e57b..eaaec44666 100644 - ctx->done = 1; -#endif + continue; ++ } ++ ++ if ((pfd.revents & poll_cap) != 0) { ++ ret = dq_buf(ctx, ppavbuf); ++ if (ret == AVERROR(EPIPE)) ++ continue; ++ return ret; } - avbuf = &ctx->buffers[buf.index]; @@ -51905,19 +51966,12 @@ index ff1ea8e57b..eaaec44666 100644 - if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { - memcpy(avbuf->planes, planes, sizeof(planes)); - avbuf->buf.m.planes = avbuf->planes; -+ if ((pfd.revents & poll_cap) != 0) { -+ ret = dq_buf(ctx, ppavbuf); -+ if (ret == AVERROR(EPIPE)) -+ continue; -+ return ret; - } -- return avbuf; -+ + if ((pfd.revents & poll_out) != 0) { + if (is_cap) + return AVERROR(EAGAIN); + return dq_buf(ctx, ppavbuf); -+ } + } +- return avbuf; + + av_log(avctx, AV_LOG_ERROR, "V4L2 poll unexpected events=%#x, revents=%#x\n", pfd.events, pfd.revents); + return AVERROR_UNKNOWN; @@ -51941,13 +51995,19 @@ index ff1ea8e57b..eaaec44666 100644 + return avbuf; +} + -+void -+ff_v4l2_dq_all(V4L2Context *const ctx) ++int ++ff_v4l2_dq_all(V4L2Context *const ctx, int timeout1) +{ + V4L2Buffer * avbuf; ++ if (timeout1 != 0) { ++ int rv = get_qbuf(ctx, &avbuf, timeout1); ++ if (rv != 0) ++ return rv; ++ } + do { + get_qbuf(ctx, &avbuf, 0); + } while (avbuf); ++ return 0; } static V4L2Buffer* v4l2_getfree_v4l2buf(V4L2Context *ctx) @@ -51961,7 +52021,7 @@ index ff1ea8e57b..eaaec44666 100644 - } while (v4l2_dequeue_v4l2buf(ctx, timeout)); - } + if (V4L2_TYPE_IS_OUTPUT(ctx->type)) -+ ff_v4l2_dq_all(ctx); ++ ff_v4l2_dq_all(ctx, 0); for (i = 0; i < ctx->num_buffers; i++) { - if (ctx->buffers[i].status == V4L2BUF_AVAILABLE) @@ -51972,7 +52032,7 @@ index ff1ea8e57b..eaaec44666 100644 } return NULL; -@@ -452,25 +735,45 @@ static V4L2Buffer* v4l2_getfree_v4l2buf(V4L2Context *ctx) +@@ -452,25 +746,45 @@ static V4L2Buffer* v4l2_getfree_v4l2buf(V4L2Context *ctx) static int v4l2_release_buffers(V4L2Context* ctx) { @@ -52002,18 +52062,18 @@ index ff1ea8e57b..eaaec44666 100644 + .type = ctx->type, + .count = 0, /* 0 -> unmap all buffers from the driver */ + }; ++ ++ while ((ret = ioctl(fd, VIDIOC_REQBUFS, &req)) == -1) { ++ if (errno == EINTR) ++ continue; ++ ++ ret = AVERROR(errno); - for (j = 0; j < buffer->num_planes; j++) { - struct V4L2Plane_info *p = &buffer->plane_info[j]; - if (p->mm_addr && p->length) - if (munmap(p->mm_addr, p->length) < 0) - av_log(logger(ctx), AV_LOG_ERROR, "%s unmap plane (%s))\n", ctx->name, av_err2str(AVERROR(errno))); -+ while ((ret = ioctl(fd, VIDIOC_REQBUFS, &req)) == -1) { -+ if (errno == EINTR) -+ continue; -+ -+ ret = AVERROR(errno); -+ + av_log(logger(ctx), AV_LOG_ERROR, "release all %s buffers (%s)\n", + ctx->name, av_err2str(AVERROR(errno))); + @@ -52032,7 +52092,7 @@ index ff1ea8e57b..eaaec44666 100644 } static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfmt) -@@ -499,6 +802,8 @@ static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfm +@@ -499,6 +813,8 @@ static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfm static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p) { @@ -52041,21 +52101,38 @@ index ff1ea8e57b..eaaec44666 100644 enum AVPixelFormat pixfmt = ctx->av_pix_fmt; struct v4l2_fmtdesc fdesc; int ret; -@@ -517,6 +822,13 @@ static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p) +@@ -512,21 +828,22 @@ static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p) + return 0; + } + +- for (;;) { ++ for (;; ++fdesc.index) { + ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_ENUM_FMT, &fdesc); if (ret) return AVERROR(EINVAL); + if (priv->pix_fmt != AV_PIX_FMT_NONE) { -+ if (fdesc.pixelformat != ff_v4l2_format_avfmt_to_v4l2(priv->pix_fmt)) { -+ fdesc.index++; ++ if (fdesc.pixelformat != ff_v4l2_format_avfmt_to_v4l2(priv->pix_fmt)) + continue; -+ } + } + pixfmt = ff_v4l2_format_v4l2_to_avfmt(fdesc.pixelformat, AV_CODEC_ID_RAWVIDEO); ret = v4l2_try_raw_format(ctx, pixfmt); - if (ret){ -@@ -569,30 +881,99 @@ static int v4l2_get_coded_format(V4L2Context* ctx, uint32_t *p) +- if (ret){ +- fdesc.index++; +- continue; ++ if (ret == 0) { ++ *p = pixfmt; ++ return 0; + } +- +- *p = pixfmt; +- +- return 0; + } + + return AVERROR(EINVAL); +@@ -569,30 +886,99 @@ static int v4l2_get_coded_format(V4L2Context* ctx, uint32_t *p) * *****************************************************************************/ @@ -52163,7 +52240,7 @@ index ff1ea8e57b..eaaec44666 100644 s->draining= 1; return 0; } -@@ -601,23 +982,29 @@ int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* frame) +@@ -601,23 +987,29 @@ int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* frame) if (!avbuf) return AVERROR(EAGAIN); @@ -52196,7 +52273,7 @@ index ff1ea8e57b..eaaec44666 100644 s->draining = 1; return 0; } -@@ -626,8 +1013,13 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) +@@ -626,8 +1018,13 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) if (!avbuf) return AVERROR(EAGAIN); @@ -52212,7 +52289,7 @@ index ff1ea8e57b..eaaec44666 100644 return ret; return ff_v4l2_buffer_enqueue(avbuf); -@@ -635,42 +1027,36 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) +@@ -635,42 +1032,36 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame, int timeout) { @@ -52244,7 +52321,8 @@ index ff1ea8e57b..eaaec44666 100644 + return 0; } - int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt) +-int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt) ++int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt, int timeout) { + V4L2m2mContext *s = ctx_to_m2mctx(ctx); + AVCodecContext *const avctx = s->avctx; @@ -52260,22 +52338,22 @@ index ff1ea8e57b..eaaec44666 100644 - if (!avbuf) { - if (ctx->done) - return AVERROR_EOF; -- -- return AVERROR(EAGAIN); -- } + do { -+ if ((rv = get_qbuf(ctx, &avbuf, -1)) != 0) ++ if ((rv = get_qbuf(ctx, &avbuf, timeout)) != 0) + return rv == AVERROR(ENOSPC) ? AVERROR(EAGAIN) : rv; // Caller not currently expecting ENOSPC + if ((rv = ff_v4l2_buffer_buf_to_avpkt(pkt, avbuf)) != 0) + return rv; + } while (xlat_pts_pkt_out(avctx, &s->xlat, pkt) != 0); +- return AVERROR(EAGAIN); +- } +- - return ff_v4l2_buffer_buf_to_avpkt(pkt, avbuf); + return 0; } int ff_v4l2_context_get_format(V4L2Context* ctx, int probe) -@@ -702,78 +1088,179 @@ int ff_v4l2_context_get_format(V4L2Context* ctx, int probe) +@@ -702,78 +1093,179 @@ int ff_v4l2_context_get_format(V4L2Context* ctx, int probe) int ff_v4l2_context_set_format(V4L2Context* ctx) { @@ -52425,12 +52503,12 @@ index ff1ea8e57b..eaaec44666 100644 + av_log(logger(ctx), AV_LOG_ERROR, "type %i not supported\n", ctx->type); + return AVERROR_PATCHWELCOME; + } -+ + +- av_freep(&ctx->buffers); + ff_mutex_init(&ctx->lock, NULL); + pthread_cond_init(&ctx->cond, NULL); + atomic_init(&ctx->q_count, 0); - -- av_freep(&ctx->buffers); ++ + if (s->output_drm) { + AVHWFramesContext *hwframes; + @@ -52488,7 +52566,7 @@ index ff1ea8e57b..eaaec44666 100644 return ret; } diff --git a/libavcodec/v4l2_context.h b/libavcodec/v4l2_context.h -index 22a9532444..311b6f10a4 100644 +index 22a9532444..108fc05a6f 100644 --- a/libavcodec/v4l2_context.h +++ b/libavcodec/v4l2_context.h @@ -31,6 +31,7 @@ @@ -52559,6 +52637,15 @@ index 22a9532444..311b6f10a4 100644 } V4L2Context; /** +@@ -147,7 +177,7 @@ int ff_v4l2_context_set_status(V4L2Context* ctx, uint32_t cmd); + * @param[inout] pkt The AVPacket to dequeue to. + * @return 0 in case of success, AVERROR(EAGAIN) if no buffer was ready, another negative error in case of error. + */ +-int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt); ++int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt, int timeout); + + /** + * Dequeues a buffer from a V4L2Context to an AVFrame. @@ -156,7 +186,10 @@ int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt); * @param[in] ctx The V4L2Context to dequeue from. * @param[inout] f The AVFrame to dequeue to. @@ -52579,21 +52666,45 @@ index 22a9532444..311b6f10a4 100644 /** * Enqueues a buffer to a V4L2Context from an AVFrame -@@ -183,4 +216,6 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt); +@@ -183,4 +216,28 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt); */ int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* f); -+void ff_v4l2_dq_all(V4L2Context *const ctx); ++/** ++ * Dequeue all buffers on this queue ++ * ++ * Used to recycle output buffers ++ * ++ * @param[in] ctx The V4L2Context to dequeue from. ++ * @param[in] timeout1 A timeout on dequeuing the 1st buffer, ++ * all others have a timeout of zero ++ * @return AVERROR(EAGAIN) if timeout1 non-zero then the return ++ * of the first dequeue operation, 0 otherwise. ++ */ ++int ff_v4l2_dq_all(V4L2Context *const ctx, int timeout1); ++ ++/** ++ * Returns the number of buffers currently queued ++ * ++ * @param[in] ctx The V4L2Context to evaluate ++ */ ++static inline int ++ff_v4l2_context_q_count(const V4L2Context* const ctx) ++{ ++ return atomic_load(&ctx->q_count); ++} + #endif // AVCODEC_V4L2_CONTEXT_H diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c -index cdfd579810..77fe5fc4e3 100644 +index cdfd579810..a919bdc030 100644 --- a/libavcodec/v4l2_m2m.c +++ b/libavcodec/v4l2_m2m.c -@@ -36,6 +36,14 @@ +@@ -35,6 +35,15 @@ + #include "v4l2_context.h" #include "v4l2_fmt.h" #include "v4l2_m2m.h" - ++#include "v4l2_req_dmabufs.h" ++ +static void +xlat_init(xlat_track_t * const x) +{ @@ -52601,21 +52712,35 @@ index cdfd579810..77fe5fc4e3 100644 + x->last_pts = AV_NOPTS_VALUE; +} + -+ + static inline int v4l2_splane_video(struct v4l2_capability *cap) { - if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) && -@@ -68,7 +76,9 @@ static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe) +@@ -68,7 +77,9 @@ static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe) s->capture.done = s->output.done = 0; s->capture.name = "capture"; -+ s->capture.buf_mem = V4L2_MEMORY_MMAP; ++ s->capture.buf_mem = s->db_ctl != NULL ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP; s->output.name = "output"; + s->output.buf_mem = s->input_drm ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP; atomic_init(&s->refcount, 0); sem_init(&s->refsync, 0, 0); -@@ -215,13 +225,7 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) +@@ -85,12 +96,14 @@ static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe) + if (v4l2_mplane_video(&cap)) { + s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; ++ s->output.format.type = s->output.type; + return 0; + } + + if (v4l2_splane_video(&cap)) { + s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ s->output.format.type = s->output.type; + return 0; + } + +@@ -215,13 +228,7 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n"); /* 2. unmap the capture buffers (v4l2 and ffmpeg): @@ -52629,7 +52754,7 @@ index cdfd579810..77fe5fc4e3 100644 ff_v4l2_context_release(&s->capture); /* 3. get the new capture format */ -@@ -240,7 +244,6 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) +@@ -240,7 +247,6 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) /* 5. complete reinit */ s->draining = 0; @@ -52637,7 +52762,7 @@ index cdfd579810..77fe5fc4e3 100644 return 0; } -@@ -274,7 +277,6 @@ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s) +@@ -274,7 +280,6 @@ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s) /* start again now that we know the stream dimensions */ s->draining = 0; @@ -52645,7 +52770,7 @@ index cdfd579810..77fe5fc4e3 100644 ret = ff_v4l2_context_get_format(&s->output, 0); if (ret) { -@@ -328,10 +330,14 @@ static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context) +@@ -328,10 +333,14 @@ static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context) ff_v4l2_context_release(&s->capture); sem_destroy(&s->refsync); @@ -52661,22 +52786,23 @@ index cdfd579810..77fe5fc4e3 100644 av_free(s); } -@@ -344,6 +350,11 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) +@@ -344,6 +353,11 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) if (!s) return 0; + av_log(s->avctx, AV_LOG_DEBUG, "V4L2 Codec end\n"); + -+ if (av_codec_is_decoder(s->avctx->codec)) ++ if (s->avctx && av_codec_is_decoder(s->avctx->codec)) + av_packet_unref(&s->buf_pkt); + if (s->fd >= 0) { ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF); if (ret) -@@ -356,7 +367,14 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) +@@ -356,7 +370,15 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) ff_v4l2_context_release(&s->output); ++ dmabufs_ctl_unref(&s->db_ctl); + close(s->fd); + s->fd = -1; + @@ -52688,7 +52814,7 @@ index cdfd579810..77fe5fc4e3 100644 av_buffer_unref(&priv->context_ref); return 0; -@@ -400,35 +418,38 @@ int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv) +@@ -400,35 +422,38 @@ int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv) return v4l2_configure_contexts(s); } @@ -52740,7 +52866,7 @@ index cdfd579810..77fe5fc4e3 100644 return 0; } diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h -index b67b216331..babf101d65 100644 +index b67b216331..ded1478a49 100644 --- a/libavcodec/v4l2_m2m.h +++ b/libavcodec/v4l2_m2m.h @@ -30,6 +30,7 @@ @@ -52751,7 +52877,7 @@ index b67b216331..babf101d65 100644 #include "v4l2_context.h" #define container_of(ptr, type, member) ({ \ -@@ -38,7 +39,37 @@ +@@ -38,7 +39,39 @@ #define V4L_M2M_DEFAULT_OPTS \ { "num_output_buffers", "Number of buffers in the output context",\ @@ -52783,14 +52909,16 @@ index b67b216331..babf101d65 100644 + +typedef struct xlat_track_s { + unsigned int track_no; -+ int64_t last_pts; ++ int64_t last_pts; // Last valid PTS decoded + int64_t last_opaque; + V4L2m2mTrackEl track_els[FF_V4L2_M2M_TRACK_SIZE]; +} xlat_track_t; ++ ++struct dmabufs_ctl; typedef struct V4L2m2mContext { char devname[PATH_MAX]; -@@ -52,7 +83,6 @@ typedef struct V4L2m2mContext { +@@ -52,10 +85,10 @@ typedef struct V4L2m2mContext { AVCodecContext *avctx; sem_t refsync; atomic_uint refcount; @@ -52798,7 +52926,11 @@ index b67b216331..babf101d65 100644 /* null frame/packet received */ int draining; -@@ -66,6 +96,36 @@ typedef struct V4L2m2mContext { ++ int running; + AVPacket buf_pkt; + + /* Reference to a frame. Only used during encoding */ +@@ -66,6 +99,35 @@ typedef struct V4L2m2mContext { /* reference back to V4L2m2mPriv */ void *priv; @@ -52813,8 +52945,6 @@ index b67b216331..babf101d65 100644 + + /* Frame tracking */ + xlat_track_t xlat; -+ int pending_hw; -+ int pending_n; + + pts_stats_t pts_stat; + @@ -52832,18 +52962,20 @@ index b67b216331..babf101d65 100644 + /* Quirks */ + unsigned int quirks; + ++ struct dmabufs_ctl * db_ctl; } V4L2m2mContext; typedef struct V4L2m2mPriv { -@@ -76,6 +136,7 @@ typedef struct V4L2m2mPriv { +@@ -76,6 +138,8 @@ typedef struct V4L2m2mPriv { int num_output_buffers; int num_capture_buffers; ++ const char * dmabuf_alloc; + enum AVPixelFormat pix_fmt; } V4L2m2mPriv; /** -@@ -129,4 +190,26 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *ctx); +@@ -129,4 +193,26 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *ctx); */ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *ctx); @@ -52871,7 +53003,7 @@ index b67b216331..babf101d65 100644 + #endif /* AVCODEC_V4L2_M2M_H */ diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c -index ab07c0a24a..18f3bc7ff2 100644 +index ab07c0a24a..2bd113facb 100644 --- a/libavcodec/v4l2_m2m_dec.c +++ b/libavcodec/v4l2_m2m_dec.c @@ -23,6 +23,10 @@ @@ -52885,7 +53017,7 @@ index ab07c0a24a..18f3bc7ff2 100644 #include "libavutil/pixfmt.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" -@@ -30,75 +34,264 @@ +@@ -30,75 +34,267 @@ #include "libavcodec/decode.h" #include "libavcodec/internal.h" @@ -52896,6 +53028,7 @@ index ab07c0a24a..18f3bc7ff2 100644 #include "v4l2_context.h" #include "v4l2_m2m.h" #include "v4l2_fmt.h" ++#include "v4l2_req_dmabufs.h" -static int v4l2_try_start(AVCodecContext *avctx) +// Pick 64 for max last count - that is >1sec at 60fps @@ -52957,13 +53090,13 @@ index ab07c0a24a..18f3bc7ff2 100644 + for (i = 0; i != 8; ++i) { + *s++ = ' '; + s = len > i + offset ? hex2(s, *m++) : dash2(s); -+ } + } + *s++ = ' '; + *s++ = ':'; + for (; i != 16; ++i) { + *s++ = ' '; + s = len > i + offset ? hex2(s, *m++) : dash2(s); - } ++ } + *s++ = 0; +} @@ -52989,6 +53122,8 @@ index ab07c0a24a..18f3bc7ff2 100644 - return ret; +static int64_t pts_stats_guess(const pts_stats_t * const stats) +{ ++ if (stats->last_count <= 1) ++ return stats->last_pts; + if (stats->last_pts == AV_NOPTS_VALUE || + stats->last_interval == 0 || + stats->last_count >= STATS_LAST_COUNT_MAX) @@ -53140,7 +53275,7 @@ index ab07c0a24a..18f3bc7ff2 100644 + else + len = src_len < 0 ? AVERROR(EINVAL) : src_len; + -+ // Zero length is OK but we swant to stop - -ve is error val ++ // Zero length is OK but we want to stop - -ve is error val + if (len <= 0) + return len; + @@ -53198,7 +53333,7 @@ index ab07c0a24a..18f3bc7ff2 100644 return 0; } -@@ -133,58 +326,548 @@ static int v4l2_prepare_decoder(V4L2m2mContext *s) +@@ -133,58 +329,548 @@ static int v4l2_prepare_decoder(V4L2m2mContext *s) return 0; } @@ -53231,41 +53366,54 @@ index ab07c0a24a..18f3bc7ff2 100644 +xlat_flush(xlat_track_t * const x) +{ + unsigned int i; ++ // Do not reset track_no - this ensures that any frames left in the decoder ++ // that turn up later get discarded. ++ ++ x->last_pts = AV_NOPTS_VALUE; ++ x->last_opaque = 0; + for (i = 0; i != FF_V4L2_M2M_TRACK_SIZE; ++i) { + x->track_els[i].pending = 0; + x->track_els[i].discard = 1; + } -+ x->last_pts = AV_NOPTS_VALUE; ++} ++ ++static void ++xlat_init(xlat_track_t * const x) ++{ ++ memset(x, 0, sizeof(*x)); ++ xlat_flush(x); +} + +static int +xlat_pending(const xlat_track_t * const x) +{ + unsigned int n = x->track_no % FF_V4L2_M2M_TRACK_SIZE; -+ unsigned int i; -+ int r = 0; -+ int64_t now = AV_NOPTS_VALUE; ++ int i; ++ const int64_t now = x->last_pts; + -+ for (i = 0; i < 32; ++i, n = (n - 1) % FF_V4L2_M2M_TRACK_SIZE) { ++ for (i = 0; i < FF_V4L2_M2M_TRACK_SIZE; ++i, n = (n - 1) & (FF_V4L2_M2M_TRACK_SIZE - 1)) { + const V4L2m2mTrackEl * const t = x->track_els + n; + ++ // Discard only set on never-set or flushed entries ++ // So if we get here we've never successfully decoded a frame so allow ++ // more frames into the buffer before stalling ++ if (t->discard) ++ return i - 16; ++ ++ // If we've got this frame out then everything before this point ++ // must have entered the decoder + if (!t->pending) ++ break; ++ ++ // If we've never seen a pts all we can do is count frames ++ if (now == AV_NOPTS_VALUE) + continue; + -+ if (now == AV_NOPTS_VALUE) -+ now = t->dts; -+ -+ if (t->pts == AV_NOPTS_VALUE || -+ ((now == AV_NOPTS_VALUE || t->pts <= now) && -+ (x->last_pts == AV_NOPTS_VALUE || t->pts > x->last_pts))) -+ ++r; ++ if (t->dts != AV_NOPTS_VALUE && now >= t->dts) ++ break; + } + -+ // If we never get any ideas about PTS vs DTS allow a lot more buffer -+ if (now == AV_NOPTS_VALUE) -+ r -= 16; -+ -+ return r; ++ return i; +} + +static inline int stream_started(const V4L2m2mContext * const s) { @@ -53382,34 +53530,34 @@ index ab07c0a24a..18f3bc7ff2 100644 + av_log(avctx, AV_LOG_ERROR, "Failed to get coded packet: err=%d\n", ret); return ret; + } -+ } -+ + } + +- if (s->draining) +- goto dequeue; + if (s->draining) { + if (s->buf_pkt.size) { + av_log(avctx, AV_LOG_WARNING, "Unexpected input whilst draining\n"); + av_packet_unref(&s->buf_pkt); + } + return NQ_DRAINING; - } - -- if (s->draining) -- goto dequeue; -+ if (!s->buf_pkt.size) -+ return NQ_NONE; ++ } - ret = ff_v4l2_context_enqueue_packet(output, &s->buf_pkt); - if (ret < 0 && ret != AVERROR(EAGAIN)) - goto fail; ++ if (!s->buf_pkt.size) ++ return NQ_NONE; + +- /* if EAGAIN don't unref packet and try to enqueue in the next iteration */ +- if (ret != AVERROR(EAGAIN)) + if ((ret = check_output_streamon(avctx, s)) != 0) + return ret; + + if (s->extdata_sent) + ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, NULL, 0); -+ else if (s->extdata_data) ++ else + ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, s->extdata_data, s->extdata_size); - -- /* if EAGAIN don't unref packet and try to enqueue in the next iteration */ -- if (ret != AVERROR(EAGAIN)) ++ + if (ret == AVERROR(EAGAIN)) { + // Out of input buffers - keep packet + ret = NQ_Q_FULL; @@ -53465,18 +53613,6 @@ index ab07c0a24a..18f3bc7ff2 100644 + return rv; +} + -+// Number of frames over what xlat_pending returns that we keep *16 -+// This is a min value - if it appears to be too small the threshold should -+// adjust dynamically. -+#define PENDING_HW_MIN (3 * 16) -+// Offset to use when setting dynamically -+// Set to %16 == 15 to avoid the threshold changing immediately as we relax -+#define PENDING_HW_OFFSET (PENDING_HW_MIN - 1) -+// Number of consecutive times we've failed to get a frame when we prefer it -+// before we increase the prefer threshold (5ms * N = max expected decode -+// time) -+#define PENDING_N_THRESHOLD 6 -+ +static int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame) +{ + V4L2m2mContext *const s = ((V4L2m2mPriv*)avctx->priv_data)->context; @@ -53486,9 +53622,11 @@ index ab07c0a24a..18f3bc7ff2 100644 + + do { + const int pending = xlat_pending(&s->xlat); -+ const int prefer_dq = (pending > s->pending_hw / 16); ++ const int prefer_dq = (pending > 4); + const int last_src_rv = src_rv; + ++ av_log(avctx, AV_LOG_TRACE, "Pending=%d, src_rv=%d, req_pkt=%d\n", pending, src_rv, s->req_pkt); ++ + // Enqueue another pkt for decode if + // (a) We don't have a lot of stuff in the buffer already OR + // (b) ... we (think we) do but we've failed to get a frame already OR @@ -53513,10 +53651,14 @@ index ab07c0a24a..18f3bc7ff2 100644 + // (b) enqueue returned a status indicating that decode should be attempted + if (dst_rv != 0 && TRY_DQ(src_rv)) { + // Pick a timeout depending on state ++ // The pending count isn't completely reliable so it is good enough ++ // hint that we want a frame but not good enough to require it in ++ // all cases; however if it has got > 31 that exceeds its margin of ++ // error so require a frame to prevent ridiculous levels of latency + const int t = + src_rv == NQ_Q_FULL ? -1 : + src_rv == NQ_DRAINING ? 300 : -+ prefer_dq ? 5 : 0; ++ prefer_dq ? (s->running && pending > 31 ? 100 : 5) : 0; + + // Dequeue frame will unref any previous contents of frame + // if it returns success so we don't need an explicit unref @@ -53533,18 +53675,11 @@ index ab07c0a24a..18f3bc7ff2 100644 + } + } + -+ // Adjust dynamic pending threshold + if (dst_rv == 0) { -+ if (--s->pending_hw < PENDING_HW_MIN) -+ s->pending_hw = PENDING_HW_MIN; -+ s->pending_n = 0; -+ + set_best_effort_pts(avctx, &s->pts_stat, frame); -+ } -+ else if (dst_rv == AVERROR(EAGAIN)) { -+ if (prefer_dq && ++s->pending_n > PENDING_N_THRESHOLD) { -+ s->pending_hw = pending * 16 + PENDING_HW_OFFSET; -+ s->pending_n = 0; ++ if (!s->running) { ++ s->running = 1; ++ av_log(avctx, AV_LOG_VERBOSE, "Decode running\n"); + } + } + @@ -53765,13 +53900,13 @@ index ab07c0a24a..18f3bc7ff2 100644 if (ret < 0) return ret; ++ xlat_init(&s->xlat); + pts_stats_init(&s->pts_stat, avctx, "decoder"); -+ s->pending_hw = PENDING_HW_MIN; + capture = &s->capture; output = &s->output; -@@ -192,14 +875,51 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) +@@ -192,14 +878,65 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) * by the v4l2 driver; this event will trigger a full pipeline reconfig and * the proper values will be retrieved from the kernel driver. */ @@ -53813,6 +53948,20 @@ index ab07c0a24a..18f3bc7ff2 100644 + s->output_drm = 0; + } + ++ s->db_ctl = NULL; ++ if (priv->dmabuf_alloc != NULL && strcmp(priv->dmabuf_alloc, "v4l2") != 0) { ++ if (strcmp(priv->dmabuf_alloc, "cma") == 0) ++ s->db_ctl = dmabufs_ctl_new(); ++ else { ++ av_log(avctx, AV_LOG_ERROR, "Unknown dmabuf alloc method: '%s'\n", priv->dmabuf_alloc); ++ return AVERROR(EINVAL); ++ } ++ if (!s->db_ctl) { ++ av_log(avctx, AV_LOG_ERROR, "Can't open dmabuf provider '%s'\n", priv->dmabuf_alloc); ++ return AVERROR(ENOMEM); ++ } ++ } ++ + s->device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); + if (!s->device_ref) { + ret = AVERROR(ENOMEM); @@ -53825,7 +53974,7 @@ index ab07c0a24a..18f3bc7ff2 100644 s->avctx = avctx; ret = ff_v4l2_m2m_codec_init(priv); -@@ -208,12 +928,83 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) +@@ -208,12 +945,84 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) return ret; } @@ -53901,7 +54050,8 @@ index ab07c0a24a..18f3bc7ff2 100644 + + // resend extradata + s->extdata_sent = 0; -+ // clear EOS status vars ++ // clear status vars ++ s->running = 0; + s->draining = 0; + output->done = 0; + capture->done = 0; @@ -53911,13 +54061,14 @@ index ab07c0a24a..18f3bc7ff2 100644 } #define OFFSET(x) offsetof(V4L2m2mPriv, x) -@@ -222,10 +1013,16 @@ static av_cold int v4l2_decode_close(AVCodecContext *avctx) +@@ -222,10 +1031,17 @@ static av_cold int v4l2_decode_close(AVCodecContext *avctx) 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 = 20}, 20, INT_MAX, FLAGS }, + OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 20}, 2, INT_MAX, FLAGS }, + { "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 }, ++ { "dmabuf_alloc", "Dmabuf alloc method", OFFSET(dmabuf_alloc), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, { NULL}, }; @@ -53929,7 +54080,7 @@ index ab07c0a24a..18f3bc7ff2 100644 #define M2MDEC_CLASS(NAME) \ static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \ .class_name = #NAME "_v4l2m2m_decoder", \ -@@ -246,9 +1043,15 @@ static const AVOption options[] = { +@@ -246,9 +1062,15 @@ static const AVOption options[] = { .init = v4l2_decode_init, \ .receive_frame = v4l2_receive_frame, \ .close = v4l2_decode_close, \ @@ -53946,7 +54097,7 @@ index ab07c0a24a..18f3bc7ff2 100644 } diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c -index f644b50133..7e8c896b66 100644 +index f644b50133..6472b56030 100644 --- a/libavcodec/v4l2_m2m_enc.c +++ b/libavcodec/v4l2_m2m_enc.c @@ -24,6 +24,8 @@ @@ -54011,7 +54162,7 @@ index f644b50133..7e8c896b66 100644 return AVERROR_PATCHWELCOME; } -@@ -271,13 +300,186 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) +@@ -271,17 +300,208 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) return 0; } @@ -54130,21 +54281,34 @@ index f644b50133..7e8c896b66 100644 + return 1; +} + ++static inline int q_full(const V4L2Context *const output) ++{ ++ return ff_v4l2_context_q_count(output) == output->num_buffers; ++} + static int v4l2_send_frame(AVCodecContext *avctx, const AVFrame *frame) { V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; V4L2Context *const output = &s->output; - -+ ff_v4l2_dq_all(output); ++ int rv; ++ const int needs_slot = q_full(output); + -+ // Signal EOF if needed ++ av_log(avctx, AV_LOG_TRACE, "<<< %s; needs_slot=%d\n", __func__, needs_slot); ++ ++ // Signal EOF if needed (doesn't need q slot) + if (!frame) { ++ av_log(avctx, AV_LOG_TRACE, "--- %s: EOS\n", __func__); + return ff_v4l2_context_enqueue_frame(output, frame); + } + ++ if ((rv = ff_v4l2_dq_all(output, needs_slot? 500 : 0)) != 0) { ++ // We should be able to return AVERROR(EAGAIN) to indicate buffer ++ // exhaustion, but ffmpeg currently treats that as fatal. ++ av_log(avctx, AV_LOG_WARNING, "Failed to get buffer for src frame: %s\n", av_err2str(rv)); ++ return rv; ++ } ++ + if (s->input_drm && !output->streamon) { -+ int rv; + struct v4l2_format req_format = {.type = output->format.type}; + + // Set format when we first get a buffer @@ -54192,31 +54356,56 @@ index f644b50133..7e8c896b66 100644 + selection.r.width, selection.r.height, selection.r.left, selection.r.top); + } + } -+ + #ifdef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME - if (frame && frame->pict_type == AV_PICTURE_TYPE_I) + if (frame->pict_type == AV_PICTURE_TYPE_I) v4l2_set_ext_ctrl(s, MPEG_CID(FORCE_KEY_FRAME), 0, "force key frame", 1); #endif -@@ -292,6 +494,8 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) +- return ff_v4l2_context_enqueue_frame(output, frame); ++ rv = ff_v4l2_context_enqueue_frame(output, frame); ++ if (rv) { ++ av_log(avctx, AV_LOG_ERROR, "Enqueue frame failed: %s\n", av_err2str(rv)); ++ } ++ ++ return rv; + } + + static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) +@@ -292,6 +512,11 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) AVFrame *frame = s->frame; int ret; -+ ff_v4l2_dq_all(output); ++ av_log(avctx, AV_LOG_TRACE, "<<< %s: qlen out %d cap %d\n", __func__, ++ ff_v4l2_context_q_count(output), ff_v4l2_context_q_count(capture)); ++ ++ ff_v4l2_dq_all(output, 0); + if (s->draining) goto dequeue; -@@ -328,7 +532,87 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) +@@ -328,7 +553,115 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) } dequeue: - return ff_v4l2_context_dequeue_packet(capture, avpkt); -+ ret = ff_v4l2_context_dequeue_packet(capture, avpkt); -+ ff_v4l2_dq_all(output); ++ // Dequeue a frame ++ for (;;) { ++ int t = q_full(output) ? -1 : s->draining ? 300 : 0; ++ int rv2; ++ ++ // If output is full wait for either a packet or output to become not full ++ ret = ff_v4l2_context_dequeue_packet(capture, avpkt, t); ++ ++ // If output was full retry packet dequeue ++ t = (ret != AVERROR(EAGAIN) || t != -1) ? 0 : 300; ++ rv2 = ff_v4l2_dq_all(output, t); ++ if (t == 0 || rv2 != 0) ++ break; ++ } + if (ret) -+ return ret; ++ return (s->draining && ret == AVERROR(EAGAIN)) ? AVERROR_EOF : ret; + + if (capture->first_buf == 1) { + uint8_t * data; @@ -54247,8 +54436,8 @@ index f644b50133..7e8c896b66 100644 + s->extdata_size = len; + } + -+ ret = ff_v4l2_context_dequeue_packet(capture, avpkt); -+ ff_v4l2_dq_all(output); ++ ret = ff_v4l2_context_dequeue_packet(capture, avpkt, 0); ++ ff_v4l2_dq_all(output, 0); + if (ret) + return ret; + } @@ -54285,19 +54474,35 @@ index f644b50133..7e8c896b66 100644 + avpkt->data = buf->data; + avpkt->size = newlen; + } ++ else if (ff_v4l2_context_q_count(capture) < 2) { ++ // Avoid running out of capture buffers ++ // In most cases the buffers will be returned quickly in which case ++ // we don't copy and can use the v4l2 buffers directly but sometimes ++ // ffmpeg seems to hold onto all of them for a long time (.mkv ++ // creation?) so avoid deadlock in those cases. ++ AVBufferRef * const buf = av_buffer_alloc(avpkt->size + AV_INPUT_BUFFER_PADDING_SIZE); ++ if (buf == NULL) ++ goto fail_no_mem; ++ ++ memcpy(buf->data, avpkt->data, avpkt->size); ++ av_buffer_unref(&avpkt->buf); // Will recycle the V4L2 buffer ++ ++ avpkt->buf = buf; ++ avpkt->data = buf->data; ++ } + -+// av_log(avctx, AV_LOG_INFO, "%s: PTS out=%"PRId64", size=%d, ret=%d\n", __func__, avpkt->pts, avpkt->size, ret); + capture->first_buf = 0; + return 0; + +fail_no_mem: ++ av_log(avctx, AV_LOG_ERROR, "Rx pkt failed: No memory\n"); + ret = AVERROR(ENOMEM); + av_packet_unref(avpkt); + return ret; } static av_cold int v4l2_encode_init(AVCodecContext *avctx) -@@ -340,6 +624,8 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) +@@ -340,6 +673,8 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) uint32_t v4l2_fmt_output; int ret; @@ -54306,7 +54511,7 @@ index f644b50133..7e8c896b66 100644 ret = ff_v4l2_m2m_create_context(priv, &s); if (ret < 0) return ret; -@@ -347,13 +633,17 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) +@@ -347,13 +682,17 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) capture = &s->capture; output = &s->output; @@ -54325,7 +54530,7 @@ index f644b50133..7e8c896b66 100644 /* capture context */ capture->av_codec_id = avctx->codec_id; -@@ -372,7 +662,7 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) +@@ -372,7 +711,7 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) v4l2_fmt_output = output->format.fmt.pix.pixelformat; pix_fmt_output = ff_v4l2_format_v4l2_to_avfmt(v4l2_fmt_output, AV_CODEC_ID_RAWVIDEO); @@ -54334,7 +54539,7 @@ index f644b50133..7e8c896b66 100644 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); -@@ -390,9 +680,10 @@ static av_cold int v4l2_encode_close(AVCodecContext *avctx) +@@ -390,9 +729,10 @@ static av_cold int v4l2_encode_close(AVCodecContext *avctx) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM #define V4L_M2M_CAPTURE_OPTS \ @@ -54954,10 +55159,11 @@ index 0000000000..956d9234f1 +#endif diff --git a/libavcodec/v4l2_req_dmabufs.c b/libavcodec/v4l2_req_dmabufs.c new file mode 100644 -index 0000000000..c4bbed18c6 +index 0000000000..acc0366e76 --- /dev/null +++ b/libavcodec/v4l2_req_dmabufs.c -@@ -0,0 +1,288 @@ +@@ -0,0 +1,369 @@ ++#include +#include +#include +#include @@ -54979,9 +55185,22 @@ index 0000000000..c4bbed18c6 + +#define TRACE_ALLOC 0 + ++struct dmabufs_ctl; ++struct dmabuf_h; ++ ++struct dmabuf_fns { ++ int (*buf_alloc)(struct dmabufs_ctl * dbsc, struct dmabuf_h * dh, size_t size); ++ void (*buf_free)(struct dmabuf_h * dh); ++ int (*ctl_new)(struct dmabufs_ctl * dbsc); ++ void (*ctl_free)(struct dmabufs_ctl * dbsc); ++}; ++ +struct dmabufs_ctl { ++ atomic_int ref_count; + int fd; + size_t page_size; ++ void * v; ++ const struct dmabuf_fns * fns; +}; + +struct dmabuf_h { @@ -54989,6 +55208,8 @@ index 0000000000..c4bbed18c6 + size_t size; + size_t len; + void * mapptr; ++ void * v; ++ const struct dmabuf_fns * fns; +}; + +#if TRACE_ALLOC @@ -55048,15 +55269,8 @@ index 0000000000..c4bbed18c6 +struct dmabuf_h * dmabuf_realloc(struct dmabufs_ctl * dbsc, struct dmabuf_h * old, size_t size) +{ + struct dmabuf_h * dh; -+ struct dma_heap_allocation_data data = { -+ .len = (size + dbsc->page_size - 1) & ~(dbsc->page_size - 1), -+ .fd = 0, -+ .fd_flags = O_RDWR, -+ .heap_flags = 0 -+ }; -+ + if (old != NULL) { -+ if (old->size == data.len) { ++ if (old->size >= size) { + return old; + } + dmabuf_free(old); @@ -55066,24 +55280,16 @@ index 0000000000..c4bbed18c6 + (dh = malloc(sizeof(*dh))) == NULL) + return NULL; + -+ while (ioctl(dbsc->fd, DMA_HEAP_IOCTL_ALLOC, &data)) { -+ int err = errno; -+ request_log("Failed to alloc %" PRIu64 " from dma-heap(fd=%d): %d (%s)\n", -+ (uint64_t)data.len, -+ dbsc->fd, -+ err, -+ strerror(err)); -+ if (err == EINTR) -+ continue; -+ goto fail; -+ } -+ + *dh = (struct dmabuf_h){ -+ .fd = data.fd, -+ .size = (size_t)data.len, -+ .mapptr = MAP_FAILED ++ .fd = -1, ++ .mapptr = MAP_FAILED, ++ .fns = dbsc->fns + }; + ++ if (dh->fns->buf_alloc(dbsc, dh, size) != 0) ++ goto fail; ++ ++ +#if TRACE_ALLOC + ++total_bufs; + total_size += dh->size; @@ -55180,8 +55386,6 @@ index 0000000000..c4bbed18c6 + dh->len = len; +} + -+ -+ +void dmabuf_free(struct dmabuf_h * dh) +{ + if (!dh) @@ -55193,20 +55397,72 @@ index 0000000000..c4bbed18c6 + request_log("%s: Free: %zd, total=%zd, bufs=%d\n", __func__, dh->size, total_size, total_bufs); +#endif + -+ if (dh->mapptr != MAP_FAILED) ++ dh->fns->buf_free(dh); ++ ++ if (dh->mapptr != MAP_FAILED && dh->mapptr != NULL) + munmap(dh->mapptr, dh->size); -+ while (close(dh->fd) == -1 && errno == EINTR) -+ /* loop */; ++ if (dh->fd != -1) ++ while (close(dh->fd) == -1 && errno == EINTR) ++ /* loop */; + free(dh); +} + -+struct dmabufs_ctl * dmabufs_ctl_new(void) ++static struct dmabufs_ctl * dmabufs_ctl_new2(const struct dmabuf_fns * const fns) +{ -+ struct dmabufs_ctl * dbsc = malloc(sizeof(*dbsc)); ++ struct dmabufs_ctl * dbsc = calloc(1, sizeof(*dbsc)); + + if (!dbsc) + return NULL; + ++ dbsc->fd = -1; ++ dbsc->fns = fns; ++ dbsc->page_size = (size_t)sysconf(_SC_PAGE_SIZE); ++ ++ if (fns->ctl_new(dbsc) != 0) ++ goto fail; ++ ++ return dbsc; ++ ++fail: ++ free(dbsc); ++ return NULL; ++} ++ ++static void dmabufs_ctl_free(struct dmabufs_ctl * const dbsc) ++{ ++ request_debug(NULL, "Free dmabuf ctl\n"); ++ ++ dbsc->fns->ctl_free(dbsc); ++ ++ free(dbsc); ++} ++ ++void dmabufs_ctl_unref(struct dmabufs_ctl ** const pDbsc) ++{ ++ struct dmabufs_ctl * const dbsc = *pDbsc; ++ ++ if (!dbsc) ++ return; ++ *pDbsc = NULL; ++ ++ if (atomic_fetch_sub(&dbsc->ref_count, 1) != 0) ++ return; ++ ++ dmabufs_ctl_free(dbsc); ++} ++ ++struct dmabufs_ctl * dmabufs_ctl_ref(struct dmabufs_ctl * const dbsc) ++{ ++ atomic_fetch_add(&dbsc->ref_count, 1); ++ return dbsc; ++} ++ ++//----------------------------------------------------------------------------- ++// ++// Alloc dmabuf via CMA ++ ++static int ctl_cma_new(struct dmabufs_ctl * dbsc) ++{ + while ((dbsc->fd = open(DMABUF_NAME1, O_RDWR)) == -1 && + errno == EINTR) + /* Loop */; @@ -55218,40 +55474,70 @@ index 0000000000..c4bbed18c6 + if (dbsc->fd == -1) { + request_log("Unable to open either %s or %s\n", + DMABUF_NAME1, DMABUF_NAME2); -+ goto fail; ++ return -1; + } + } -+ -+ dbsc->page_size = (size_t)sysconf(_SC_PAGE_SIZE); -+ -+ return dbsc; -+ -+fail: -+ free(dbsc); -+ return NULL; ++ return 0; +} + -+void dmabufs_ctl_delete(struct dmabufs_ctl ** const pDbsc) ++static void ctl_cma_free(struct dmabufs_ctl * dbsc) +{ -+ struct dmabufs_ctl * const dbsc = *pDbsc; ++ if (dbsc->fd != -1) ++ while (close(dbsc->fd) == -1 && errno == EINTR) ++ /* loop */; + -+ if (!dbsc) -+ return; -+ *pDbsc = NULL; -+ -+ while (close(dbsc->fd) == -1 && errno == EINTR) -+ /* loop */; -+ -+ free(dbsc); +} + ++static int buf_cma_alloc(struct dmabufs_ctl * const dbsc, struct dmabuf_h * dh, size_t size) ++{ ++ struct dma_heap_allocation_data data = { ++ .len = (size + dbsc->page_size - 1) & ~(dbsc->page_size - 1), ++ .fd = 0, ++ .fd_flags = O_RDWR, ++ .heap_flags = 0 ++ }; ++ ++ while (ioctl(dbsc->fd, DMA_HEAP_IOCTL_ALLOC, &data)) { ++ int err = errno; ++ request_log("Failed to alloc %" PRIu64 " from dma-heap(fd=%d): %d (%s)\n", ++ (uint64_t)data.len, ++ dbsc->fd, ++ err, ++ strerror(err)); ++ if (err == EINTR) ++ continue; ++ return -err; ++ } ++ ++ dh->fd = data.fd; ++ dh->size = (size_t)data.len; ++ return 0; ++} ++ ++static void buf_cma_free(struct dmabuf_h * dh) ++{ ++ // Nothing needed ++} ++ ++static const struct dmabuf_fns dmabuf_cma_fns = { ++ .buf_alloc = buf_cma_alloc, ++ .buf_free = buf_cma_free, ++ .ctl_new = ctl_cma_new, ++ .ctl_free = ctl_cma_free, ++}; ++ ++struct dmabufs_ctl * dmabufs_ctl_new(void) ++{ ++ request_debug(NULL, "Dmabufs using CMA\n");; ++ return dmabufs_ctl_new2(&dmabuf_cma_fns); ++} + diff --git a/libavcodec/v4l2_req_dmabufs.h b/libavcodec/v4l2_req_dmabufs.h new file mode 100644 -index 0000000000..c1d3d8c8d7 +index 0000000000..381ba2708d --- /dev/null +++ b/libavcodec/v4l2_req_dmabufs.h -@@ -0,0 +1,43 @@ +@@ -0,0 +1,44 @@ +#ifndef DMABUFS_H +#define DMABUFS_H + @@ -55261,7 +55547,8 @@ index 0000000000..c1d3d8c8d7 +struct dmabuf_h; + +struct dmabufs_ctl * dmabufs_ctl_new(void); -+void dmabufs_ctl_delete(struct dmabufs_ctl ** const pdbsc); ++void dmabufs_ctl_unref(struct dmabufs_ctl ** const pdbsc); ++struct dmabufs_ctl * dmabufs_ctl_ref(struct dmabufs_ctl * const dbsc); + +// Need not preserve old contents +// On NULL return old buffer is freed @@ -59108,10 +59395,10 @@ index 0000000000..a31cc1f4ec +#endif diff --git a/libavcodec/v4l2_request_hevc.c b/libavcodec/v4l2_request_hevc.c new file mode 100644 -index 0000000000..ebeb7bc6f6 +index 0000000000..fbec16a93e --- /dev/null +++ b/libavcodec/v4l2_request_hevc.c -@@ -0,0 +1,345 @@ +@@ -0,0 +1,347 @@ +/* + * This file is part of FFmpeg. + * @@ -59139,6 +59426,7 @@ index 0000000000..ebeb7bc6f6 +#include "v4l2_request_hevc.h" + +#include "libavutil/hwcontext_drm.h" ++#include "libavutil/pixdesc.h" + +#include "v4l2_req_devscan.h" +#include "v4l2_req_dmabufs.h" @@ -59217,7 +59505,7 @@ index 0000000000..ebeb7bc6f6 + mediabufs_ctl_unref(&ctx->mbufs); + media_pool_delete(&ctx->mpool); + pollqueue_unref(&ctx->pq); -+ dmabufs_ctl_delete(&ctx->dbufs); ++ dmabufs_ctl_unref(&ctx->dbufs); + devscan_delete(&ctx->devscan); + + decode_q_uninit(&ctx->decode_q); @@ -59419,10 +59707,11 @@ index 0000000000..ebeb7bc6f6 + // Set our s/w format + avctx->sw_pix_fmt = ((AVHWFramesContext *)avctx->hw_frames_ctx->data)->sw_format; + -+ av_log(avctx, AV_LOG_INFO, "Hwaccel %s; devices: %s,%s; buffers: src %s, dst %s\n", ++ av_log(avctx, AV_LOG_INFO, "Hwaccel %s; devices: %s,%s; buffers: src %s, dst %s; swfmt=%s\n", + ctx->fns->name, + decdev_media_path(decdev), decdev_video_path(decdev), -+ mediabufs_memory_name(src_memtype), mediabufs_memory_name(dst_memtype)); ++ mediabufs_memory_name(src_memtype), mediabufs_memory_name(dst_memtype), ++ av_get_pix_fmt_name(avctx->sw_pix_fmt)); + + return 0; + @@ -59435,7 +59724,7 @@ index 0000000000..ebeb7bc6f6 +fail2: + pollqueue_unref(&ctx->pq); +fail1: -+ dmabufs_ctl_delete(&ctx->dbufs); ++ dmabufs_ctl_unref(&ctx->dbufs); +fail0: + devscan_delete(&ctx->devscan); + return ret; @@ -59840,10 +60129,10 @@ index 92b27a1d14..19d2a9de55 100644 diff --git a/libavdevice/drm_vout.c b/libavdevice/drm_vout.c new file mode 100644 -index 0000000000..4b25ec4344 +index 0000000000..c7b90e6dd8 --- /dev/null +++ b/libavdevice/drm_vout.c -@@ -0,0 +1,643 @@ +@@ -0,0 +1,680 @@ +/* + * Copyright (c) 2020 John Cox for Raspberry Pi Trading + * @@ -59880,6 +60169,7 @@ index 0000000000..4b25ec4344 + +#include +#include ++#include + +#define TRACE_ALL 0 + @@ -59915,7 +60205,9 @@ index 0000000000..4b25ec4344 + uint32_t con_id; + struct drm_setup setup; + enum AVPixelFormat avfmt; ++ + int show_all; ++ const char * drm_module; + + unsigned int ano; + drm_aux_t aux[AUX_SIZE]; @@ -59961,9 +60253,11 @@ index 0000000000..4b25ec4344 +{ + drmModePlaneResPtr planes; + drmModePlanePtr plane; ++ drmModeObjectPropertiesPtr props = NULL; ++ drmModePropertyPtr prop = NULL; + unsigned int i; + unsigned int j; -+ int ret = 0; ++ int ret = -1; + + planes = drmModeGetPlaneResources(drmfd); + if (!planes) @@ -60000,11 +60294,37 @@ index 0000000000..4b25ec4344 + break; + } + -+ if (i == planes->count_planes) -+ ret = -1; ++ if (i == planes->count_planes) { ++ ret = -1; ++ goto fail; ++ } + -+ drmModeFreePlaneResources(planes); -+ return ret; ++ props = drmModeObjectGetProperties(drmfd, *pplane_id, DRM_MODE_OBJECT_PLANE); ++ if (!props) ++ goto fail; ++ for (i = 0; i != props->count_props; ++i) { ++ if (prop) ++ drmModeFreeProperty(prop); ++ prop = drmModeGetProperty(drmfd, props->props[i]); ++ if (!prop) ++ goto fail; ++ if (strcmp("zpos", prop->name) == 0) { ++ if (drmModeObjectSetProperty(drmfd, *pplane_id, DRM_MODE_OBJECT_PLANE, props->props[i], prop->values[1]) == 0) ++ av_log(avctx, AV_LOG_DEBUG, "ZPOS set to %d\n", (int)prop->values[1]); ++ else ++ av_log(avctx, AV_LOG_WARNING, "Failed to set ZPOS on DRM plane\n"); ++ break; ++ } ++ } ++ ++ ret = 0; ++fail: ++ if (props) ++ drmModeFreeObjectProperties(props); ++ if (prop) ++ drmModeFreeProperty(prop); ++ drmModeFreePlaneResources(planes); ++ return ret; +} + +static void da_uninit(drm_display_env_t * const de, drm_aux_t * da) @@ -60067,6 +60387,7 @@ index 0000000000..4b25ec4344 + uint32_t offsets[4] = {0}; + uint64_t modifiers[4] = {0}; + uint32_t bo_handles[4] = {0}; ++ int has_mods = 0; + int i, j, n; + + da->frame = frame; @@ -60076,6 +60397,9 @@ index 0000000000..4b25ec4344 + av_log(s, AV_LOG_WARNING, "drmPrimeFDToHandle[%d](%d) failed: %s\n", i, desc->objects[i].fd, ERRSTR); + return -1; + } ++ if (desc->objects[i].format_modifier != DRM_FORMAT_MOD_LINEAR && ++ desc->objects[i].format_modifier != DRM_FORMAT_MOD_INVALID) ++ has_mods = 1; + } + + n = 0; @@ -60117,11 +60441,13 @@ index 0000000000..4b25ec4344 +#endif + + if (drmModeAddFB2WithModifiers(de->drm_fd, -+ av_frame_cropped_width(frame), -+ av_frame_cropped_height(frame), -+ desc->layers[0].format, bo_handles, -+ pitches, offsets, modifiers, -+ &da->fb_handle, DRM_MODE_FB_MODIFIERS /** 0 if no mods */) != 0) { ++ av_frame_cropped_width(frame), ++ av_frame_cropped_height(frame), ++ desc->layers[0].format, bo_handles, ++ pitches, offsets, ++ has_mods ? modifiers : NULL, ++ &da->fb_handle, ++ has_mods ? DRM_MODE_FB_MODIFIERS : 0) != 0) { + av_log(s, AV_LOG_WARNING, "drmModeAddFB2WithModifiers failed: %s\n", ERRSTR); + return -1; + } @@ -60387,7 +60713,6 @@ index 0000000000..4b25ec4344 +{ + drm_display_env_t * const de = s->priv_data; + int rv; -+ const char * drm_module = DRM_MODULE; + + av_log(s, AV_LOG_DEBUG, "<<< %s\n", __func__); + @@ -60396,10 +60721,10 @@ index 0000000000..4b25ec4344 + de->setup = (struct drm_setup){0}; + de->q_terminate = 0; + -+ if ((de->drm_fd = drmOpen(drm_module, NULL)) < 0) ++ if ((de->drm_fd = drmOpen(de->drm_module, NULL)) < 0) + { + rv = AVERROR(errno); -+ av_log(s, AV_LOG_ERROR, "Failed to drmOpen %s: %s\n", drm_module, av_err2str(rv)); ++ av_log(s, AV_LOG_ERROR, "Failed to drmOpen %s: %s\n", de->drm_module, av_err2str(rv)); + return rv; + } + @@ -60414,7 +60739,7 @@ index 0000000000..4b25ec4344 + sem_init(&de->q_sem_out, 0, 0); + if (pthread_create(&de->q_thread, NULL, display_thread, s)) { + rv = AVERROR(errno); -+ av_log(s, AV_LOG_ERROR, "Failed to creatye display thread: %s\n", av_err2str(rv)); ++ av_log(s, AV_LOG_ERROR, "Failed to create display thread: %s\n", av_err2str(rv)); + goto fail_close; + } + @@ -60459,6 +60784,7 @@ index 0000000000..4b25ec4344 +#define OFFSET(x) offsetof(drm_display_env_t, x) +static const AVOption options[] = { + { "show_all", "show all frames", OFFSET(show_all), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "drm_module", "drm_module name to use, default=" DRM_MODULE, OFFSET(drm_module), AV_OPT_TYPE_STRING, { .str = DRM_MODULE }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } +}; + @@ -60489,10 +60815,10 @@ index 0000000000..4b25ec4344 + diff --git a/libavdevice/egl_vout.c b/libavdevice/egl_vout.c new file mode 100644 -index 0000000000..f666adc8e9 +index 0000000000..cc6e310551 --- /dev/null +++ b/libavdevice/egl_vout.c -@@ -0,0 +1,816 @@ +@@ -0,0 +1,788 @@ +/* + * Copyright (c) 2020 John Cox for Raspberry Pi Trading + * @@ -60543,20 +60869,20 @@ index 0000000000..f666adc8e9 +#define TRACE_ALL 0 + +struct egl_setup { -+ int conId; ++ int conId; + -+ Display *dpy; -+ EGLDisplay egl_dpy; -+ EGLContext ctx; -+ EGLSurface surf; -+ Window win; ++ Display *dpy; ++ EGLDisplay egl_dpy; ++ EGLContext ctx; ++ EGLSurface surf; ++ Window win; + -+ uint32_t crtcId; -+ int crtcIdx; -+ uint32_t planeId; -+ struct { -+ int x, y, width, height; -+ } compose; ++ uint32_t crtcId; ++ int crtcIdx; ++ uint32_t planeId; ++ struct { ++ int x, y, width, height; ++ } compose; +}; + +typedef struct egl_aux_s { @@ -60565,8 +60891,7 @@ index 0000000000..f666adc8e9 + +} egl_aux_t; + -+typedef struct egl_display_env_s -+{ ++typedef struct egl_display_env_s { + AVClass *class; + + struct egl_setup setup; @@ -60584,8 +60909,8 @@ index 0000000000..f666adc8e9 + sem_t display_start_sem; + sem_t q_sem; + int q_terminate; -+ AVFrame * q_this; -+ AVFrame * q_next; ++ AVFrame *q_this; ++ AVFrame *q_next; + +} egl_display_env_t; + @@ -60594,45 +60919,44 @@ index 0000000000..f666adc8e9 + * Remove window border/decorations. + */ +static void -+no_border( Display *dpy, Window w) ++no_border(Display *dpy, Window w) +{ -+ static const unsigned MWM_HINTS_DECORATIONS = (1 << 1); -+ static const int PROP_MOTIF_WM_HINTS_ELEMENTS = 5; ++ static const unsigned MWM_HINTS_DECORATIONS = (1 << 1); ++ static const int PROP_MOTIF_WM_HINTS_ELEMENTS = 5; + -+ typedef struct -+ { -+ unsigned long flags; -+ unsigned long functions; -+ unsigned long decorations; -+ long inputMode; -+ unsigned long status; -+ } PropMotifWmHints; ++ typedef struct { ++ unsigned long flags; ++ unsigned long functions; ++ unsigned long decorations; ++ long inputMode; ++ unsigned long status; ++ } PropMotifWmHints; + -+ PropMotifWmHints motif_hints; -+ Atom prop, proptype; -+ unsigned long flags = 0; ++ PropMotifWmHints motif_hints; ++ Atom prop, proptype; ++ unsigned long flags = 0; + -+ /* setup the property */ -+ motif_hints.flags = MWM_HINTS_DECORATIONS; -+ motif_hints.decorations = flags; ++ /* setup the property */ ++ motif_hints.flags = MWM_HINTS_DECORATIONS; ++ motif_hints.decorations = flags; + -+ /* get the atom for the property */ -+ prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True ); -+ if (!prop) { -+ /* something went wrong! */ -+ return; -+ } ++ /* get the atom for the property */ ++ prop = XInternAtom(dpy, "_MOTIF_WM_HINTS", True); ++ if (!prop) { ++ /* something went wrong! */ ++ return; ++ } + -+ /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */ -+ proptype = prop; ++ /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */ ++ proptype = prop; + -+ XChangeProperty( dpy, w, /* display, window */ ++ XChangeProperty(dpy, w, /* display, window */ + prop, proptype, /* property, type */ + 32, /* format: 32-bit datums */ + PropModeReplace, /* mode */ -+ (unsigned char *) &motif_hints, /* data */ ++ (unsigned char *)&motif_hints, /* data */ + PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */ -+ ); ++ ); +} + + @@ -60641,247 +60965,247 @@ index 0000000000..f666adc8e9 + * Return the window and context handles. + */ +static int -+make_window(struct AVFormatContext * const s, -+ egl_display_env_t * const de, ++make_window(struct AVFormatContext *const s, ++ egl_display_env_t *const de, + Display *dpy, EGLDisplay egl_dpy, const char *name, + Window *winRet, EGLContext *ctxRet, EGLSurface *surfRet) +{ -+ int scrnum = DefaultScreen( dpy ); -+ XSetWindowAttributes attr; -+ unsigned long mask; -+ Window root = RootWindow( dpy, scrnum ); -+ Window win; -+ EGLContext ctx; -+ const int fullscreen = de->fullscreen; -+ EGLConfig config; -+ int x = de->window_x; -+ int y = de->window_y; -+ int width = de->window_width ? de->window_width : 1280; -+ int height = de->window_height ? de->window_height : 720; ++ int scrnum = DefaultScreen(dpy); ++ XSetWindowAttributes attr; ++ unsigned long mask; ++ Window root = RootWindow(dpy, scrnum); ++ Window win; ++ EGLContext ctx; ++ const int fullscreen = de->fullscreen; ++ EGLConfig config; ++ int x = de->window_x; ++ int y = de->window_y; ++ int width = de->window_width ? de->window_width : 1280; ++ int height = de->window_height ? de->window_height : 720; + + -+ if (fullscreen) { -+ int scrnum = DefaultScreen(dpy); ++ if (fullscreen) { ++ int scrnum = DefaultScreen(dpy); + -+ x = 0; y = 0; -+ width = DisplayWidth(dpy, scrnum); -+ height = DisplayHeight(dpy, scrnum); -+ } ++ x = 0; y = 0; ++ width = DisplayWidth(dpy, scrnum); ++ height = DisplayHeight(dpy, scrnum); ++ } + -+ { -+ EGLint num_configs; -+ static const EGLint attribs[] = { -+ EGL_RED_SIZE, 1, -+ EGL_GREEN_SIZE, 1, -+ EGL_BLUE_SIZE, 1, -+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, -+ EGL_NONE -+ }; ++ { ++ EGLint num_configs; ++ static const EGLint attribs[] = { ++ EGL_RED_SIZE, 1, ++ EGL_GREEN_SIZE, 1, ++ EGL_BLUE_SIZE, 1, ++ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, ++ EGL_NONE ++ }; + -+ if (!eglChooseConfig(egl_dpy, attribs, &config, 1, &num_configs)) { -+ av_log(s, AV_LOG_ERROR, "Error: couldn't get an EGL visual config\n"); -+ return -1; -+ } -+ } ++ if (!eglChooseConfig(egl_dpy, attribs, &config, 1, &num_configs)) { ++ av_log(s, AV_LOG_ERROR, "Error: couldn't get an EGL visual config\n"); ++ return -1; ++ } ++ } + -+ { -+ EGLint vid; -+ if (!eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { -+ av_log(s, AV_LOG_ERROR, "Error: eglGetConfigAttrib() failed\n"); -+ return -1; -+ } ++ { ++ EGLint vid; ++ if (!eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { ++ av_log(s, AV_LOG_ERROR, "Error: eglGetConfigAttrib() failed\n"); ++ return -1; ++ } + -+ { -+ XVisualInfo visTemplate = { -+ .visualid = vid, -+ }; -+ int num_visuals; -+ XVisualInfo *visinfo = XGetVisualInfo(dpy, VisualIDMask, -+ &visTemplate, &num_visuals); ++ { ++ XVisualInfo visTemplate = { ++ .visualid = vid, ++ }; ++ int num_visuals; ++ XVisualInfo *visinfo = XGetVisualInfo(dpy, VisualIDMask, ++ &visTemplate, &num_visuals); + -+ /* window attributes */ -+ attr.background_pixel = 0; -+ attr.border_pixel = 0; -+ attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); -+ attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; -+ /* XXX this is a bad way to get a borderless window! */ -+ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; ++ /* window attributes */ ++ attr.background_pixel = 0; ++ attr.border_pixel = 0; ++ attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); ++ attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; ++ /* XXX this is a bad way to get a borderless window! */ ++ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + -+ win = XCreateWindow( dpy, root, x, y, width, height, -+ 0, visinfo->depth, InputOutput, -+ visinfo->visual, mask, &attr ); -+ XFree(visinfo); -+ } -+ } ++ win = XCreateWindow(dpy, root, x, y, width, height, ++ 0, visinfo->depth, InputOutput, ++ visinfo->visual, mask, &attr); ++ XFree(visinfo); ++ } ++ } + -+ if (fullscreen) -+ no_border(dpy, win); ++ if (fullscreen) ++ no_border(dpy, win); + -+ /* set hints and properties */ -+ { -+ XSizeHints sizehints; -+ sizehints.x = x; -+ sizehints.y = y; -+ sizehints.width = width; -+ sizehints.height = height; -+ sizehints.flags = USSize | USPosition; -+ XSetNormalHints(dpy, win, &sizehints); -+ XSetStandardProperties(dpy, win, name, name, -+ None, (char **)NULL, 0, &sizehints); -+ } ++ /* set hints and properties */ ++ { ++ XSizeHints sizehints; ++ sizehints.x = x; ++ sizehints.y = y; ++ sizehints.width = width; ++ sizehints.height = height; ++ sizehints.flags = USSize | USPosition; ++ XSetNormalHints(dpy, win, &sizehints); ++ XSetStandardProperties(dpy, win, name, name, ++ None, (char **)NULL, 0, &sizehints); ++ } + -+ eglBindAPI(EGL_OPENGL_ES_API); ++ eglBindAPI(EGL_OPENGL_ES_API); + -+ { -+ static const EGLint ctx_attribs[] = { -+ EGL_CONTEXT_CLIENT_VERSION, 2, -+ EGL_NONE -+ }; -+ ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); -+ if (!ctx) { -+ av_log(s, AV_LOG_ERROR, "Error: eglCreateContext failed\n"); -+ return -1; -+ } -+ } ++ { ++ static const EGLint ctx_attribs[] = { ++ EGL_CONTEXT_CLIENT_VERSION, 2, ++ EGL_NONE ++ }; ++ ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs); ++ if (!ctx) { ++ av_log(s, AV_LOG_ERROR, "Error: eglCreateContext failed\n"); ++ return -1; ++ } ++ } + + -+ XMapWindow(dpy, win); ++ XMapWindow(dpy, win); + -+ { -+ EGLSurface surf = eglCreateWindowSurface(egl_dpy, config, (EGLNativeWindowType)win, NULL); -+ if (!surf) { -+ av_log(s, AV_LOG_ERROR, "Error: eglCreateWindowSurface failed\n"); -+ return -1; -+ } ++ { ++ EGLSurface surf = eglCreateWindowSurface(egl_dpy, config, (EGLNativeWindowType)win, NULL); ++ if (!surf) { ++ av_log(s, AV_LOG_ERROR, "Error: eglCreateWindowSurface failed\n"); ++ return -1; ++ } + -+ if (!eglMakeCurrent(egl_dpy, surf, surf, ctx)) { -+ av_log(s, AV_LOG_ERROR, "Error: eglCreateContext failed\n"); -+ return -1; -+ } ++ if (!eglMakeCurrent(egl_dpy, surf, surf, ctx)) { ++ av_log(s, AV_LOG_ERROR, "Error: eglCreateContext failed\n"); ++ return -1; ++ } + -+ *winRet = win; -+ *ctxRet = ctx; -+ *surfRet = surf; -+ } ++ *winRet = win; ++ *ctxRet = ctx; ++ *surfRet = surf; ++ } + -+ return 0; ++ return 0; +} + +static GLint -+compile_shader(struct AVFormatContext * const avctx, GLenum target, const char *source) ++compile_shader(struct AVFormatContext *const avctx, GLenum target, const char *source) +{ -+ GLuint s = glCreateShader(target); ++ GLuint s = glCreateShader(target); + -+ if (s == 0) { -+ av_log(avctx, AV_LOG_ERROR, "Failed to create shader\n"); -+ return 0; -+ } ++ if (s == 0) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to create shader\n"); ++ return 0; ++ } + -+ glShaderSource(s, 1, (const GLchar **) &source, NULL); -+ glCompileShader(s); ++ glShaderSource(s, 1, (const GLchar **)&source, NULL); ++ glCompileShader(s); + -+ { -+ GLint ok; -+ glGetShaderiv(s, GL_COMPILE_STATUS, &ok); ++ { ++ GLint ok; ++ glGetShaderiv(s, GL_COMPILE_STATUS, &ok); + -+ if (!ok) { -+ GLchar *info; -+ GLint size; ++ if (!ok) { ++ GLchar *info; ++ GLint size; + -+ glGetShaderiv(s, GL_INFO_LOG_LENGTH, &size); -+ info = malloc(size); ++ glGetShaderiv(s, GL_INFO_LOG_LENGTH, &size); ++ info = malloc(size); + -+ glGetShaderInfoLog(s, size, NULL, info); -+ av_log(avctx, AV_LOG_ERROR, "Failed to compile shader: %ssource:\n%s\n", info, source); ++ glGetShaderInfoLog(s, size, NULL, info); ++ av_log(avctx, AV_LOG_ERROR, "Failed to compile shader: %ssource:\n%s\n", info, source); + -+ return 0; -+ } -+ } ++ return 0; ++ } ++ } + -+ return s; ++ return s; +} + -+static GLuint link_program(struct AVFormatContext * const s, GLint vs, GLint fs) ++static GLuint link_program(struct AVFormatContext *const s, GLint vs, GLint fs) +{ -+ GLuint prog = glCreateProgram(); ++ GLuint prog = glCreateProgram(); + -+ if (prog == 0) { -+ av_log(s, AV_LOG_ERROR, "Failed to create program\n"); -+ return 0; -+ } ++ if (prog == 0) { ++ av_log(s, AV_LOG_ERROR, "Failed to create program\n"); ++ return 0; ++ } + -+ glAttachShader(prog, vs); -+ glAttachShader(prog, fs); -+ glLinkProgram(prog); ++ glAttachShader(prog, vs); ++ glAttachShader(prog, fs); ++ glLinkProgram(prog); + -+ { -+ GLint ok; -+ glGetProgramiv(prog, GL_LINK_STATUS, &ok); -+ if (!ok) { -+ /* Some drivers return a size of 1 for an empty log. This is the size -+ * of a log that contains only a terminating NUL character. -+ */ -+ GLint size; -+ GLchar *info = NULL; -+ glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size); -+ if (size > 1) { -+ info = malloc(size); -+ glGetProgramInfoLog(prog, size, NULL, info); -+ } ++ { ++ GLint ok; ++ glGetProgramiv(prog, GL_LINK_STATUS, &ok); ++ if (!ok) { ++ /* Some drivers return a size of 1 for an empty log. This is the size ++ * of a log that contains only a terminating NUL character. ++ */ ++ GLint size; ++ GLchar *info = NULL; ++ glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size); ++ if (size > 1) { ++ info = malloc(size); ++ glGetProgramInfoLog(prog, size, NULL, info); ++ } + -+ av_log(s, AV_LOG_ERROR, "Failed to link: %s\n", -+ (info != NULL) ? info : ""); -+ return 0; -+ } -+ } ++ av_log(s, AV_LOG_ERROR, "Failed to link: %s\n", ++ (info != NULL) ? info : ""); ++ return 0; ++ } ++ } + -+ return prog; ++ return prog; +} + +static int -+gl_setup(struct AVFormatContext * const s) ++gl_setup(struct AVFormatContext *const s) +{ -+ const char *vs = -+ "attribute vec4 pos;\n" -+ "varying vec2 texcoord;\n" -+ "\n" -+ "void main() {\n" -+ " gl_Position = pos;\n" -+ " texcoord.x = (pos.x + 1.0) / 2.0;\n" -+ " texcoord.y = (-pos.y + 1.0) / 2.0;\n" -+ "}\n"; -+ const char *fs = -+ "#extension GL_OES_EGL_image_external : enable\n" -+ "precision mediump float;\n" -+ "uniform samplerExternalOES s;\n" -+ "varying vec2 texcoord;\n" -+ "void main() {\n" -+ " gl_FragColor = texture2D(s, texcoord);\n" -+ "}\n"; ++ const char *vs = ++ "attribute vec4 pos;\n" ++ "varying vec2 texcoord;\n" ++ "\n" ++ "void main() {\n" ++ " gl_Position = pos;\n" ++ " texcoord.x = (pos.x + 1.0) / 2.0;\n" ++ " texcoord.y = (-pos.y + 1.0) / 2.0;\n" ++ "}\n"; ++ const char *fs = ++ "#extension GL_OES_EGL_image_external : enable\n" ++ "precision mediump float;\n" ++ "uniform samplerExternalOES s;\n" ++ "varying vec2 texcoord;\n" ++ "void main() {\n" ++ " gl_FragColor = texture2D(s, texcoord);\n" ++ "}\n"; + -+ GLuint vs_s; -+ GLuint fs_s; -+ GLuint prog; ++ GLuint vs_s; ++ GLuint fs_s; ++ GLuint prog; + -+ if (!(vs_s = compile_shader(s, GL_VERTEX_SHADER, vs)) || -+ !(fs_s = compile_shader(s, GL_FRAGMENT_SHADER, fs)) || -+ !(prog = link_program(s, vs_s, fs_s))) -+ return -1; ++ if (!(vs_s = compile_shader(s, GL_VERTEX_SHADER, vs)) || ++ !(fs_s = compile_shader(s, GL_FRAGMENT_SHADER, fs)) || ++ !(prog = link_program(s, vs_s, fs_s))) ++ return -1; + -+ glUseProgram(prog); ++ glUseProgram(prog); + -+ { -+ static const float verts[] = { -+ -1, -1, -+ 1, -1, -+ 1, 1, -+ -1, 1, -+ }; -+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); -+ } ++ { ++ static const float verts[] = { ++ -1, -1, ++ 1, -1, ++ 1, 1, ++ -1, 1, ++ }; ++ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); ++ } + -+ glEnableVertexAttribArray(0); -+ return 0; ++ glEnableVertexAttribArray(0); ++ return 0; +} + +static int egl_vout_write_trailer(AVFormatContext *s) @@ -60895,12 +61219,12 @@ index 0000000000..f666adc8e9 + +static int egl_vout_write_header(AVFormatContext *s) +{ -+ const AVCodecParameters * const par = s->streams[0]->codecpar; ++ const AVCodecParameters *const par = s->streams[0]->codecpar; + +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "%s\n", __func__); +#endif -+ if ( s->nb_streams > 1 ++ if (s->nb_streams > 1 + || par->codec_type != AVMEDIA_TYPE_VIDEO + || par->codec_id != AV_CODEC_ID_WRAPPED_AVFRAME) { + av_log(s, AV_LOG_ERROR, "Only supports one wrapped avframe stream\n"); @@ -60911,10 +61235,10 @@ index 0000000000..f666adc8e9 +} + + -+static int do_display(AVFormatContext * const s, egl_display_env_t * const de, AVFrame * const frame) ++static int do_display(AVFormatContext *const s, egl_display_env_t *const de, AVFrame *const frame) +{ -+ const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)frame->data[0]; -+ egl_aux_t * da = NULL; ++ const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)frame->data[0]; ++ egl_aux_t *da = NULL; + unsigned int i; + +#if TRACE_ALL @@ -60935,26 +61259,26 @@ index 0000000000..f666adc8e9 + + if (da->texture == 0) { + EGLint attribs[50]; -+ EGLint * a = attribs; ++ EGLint *a = attribs; + int i, j; + static const EGLint anames[] = { -+ EGL_DMA_BUF_PLANE0_FD_EXT, -+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, -+ EGL_DMA_BUF_PLANE0_PITCH_EXT, -+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, -+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, -+ EGL_DMA_BUF_PLANE1_FD_EXT, -+ EGL_DMA_BUF_PLANE1_OFFSET_EXT, -+ EGL_DMA_BUF_PLANE1_PITCH_EXT, -+ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, -+ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, -+ EGL_DMA_BUF_PLANE2_FD_EXT, -+ EGL_DMA_BUF_PLANE2_OFFSET_EXT, -+ EGL_DMA_BUF_PLANE2_PITCH_EXT, -+ EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, -+ EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, ++ EGL_DMA_BUF_PLANE0_FD_EXT, ++ EGL_DMA_BUF_PLANE0_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE0_PITCH_EXT, ++ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, ++ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, ++ EGL_DMA_BUF_PLANE1_FD_EXT, ++ EGL_DMA_BUF_PLANE1_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE1_PITCH_EXT, ++ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, ++ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, ++ EGL_DMA_BUF_PLANE2_FD_EXT, ++ EGL_DMA_BUF_PLANE2_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE2_PITCH_EXT, ++ EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, ++ EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, + }; -+ const EGLint * b = anames; ++ const EGLint *b = anames; + + *a++ = EGL_WIDTH; + *a++ = av_frame_cropped_width(frame); @@ -60965,8 +61289,8 @@ index 0000000000..f666adc8e9 + + for (i = 0; i < desc->nb_layers; ++i) { + for (j = 0; j < desc->layers[i].nb_planes; ++j) { -+ const AVDRMPlaneDescriptor * const p = desc->layers[i].planes + j; -+ const AVDRMObjectDescriptor * const obj = desc->objects + p->object_index; ++ const AVDRMPlaneDescriptor *const p = desc->layers[i].planes + j; ++ const AVDRMObjectDescriptor *const obj = desc->objects + p->object_index; + *a++ = *b++; + *a++ = obj->fd; + *a++ = *b++; @@ -60974,13 +61298,13 @@ index 0000000000..f666adc8e9 + *a++ = *b++; + *a++ = p->pitch; + if (obj->format_modifier == 0) { -+ b += 2; ++ b += 2; + } + else { -+ *a++ = *b++; -+ *a++ = (EGLint)(obj->format_modifier & 0xFFFFFFFF); -+ *a++ = *b++; -+ *a++ = (EGLint)(obj->format_modifier >> 32); ++ *a++ = *b++; ++ *a++ = (EGLint)(obj->format_modifier & 0xFFFFFFFF); ++ *a++ = *b++; ++ *a++ = (EGLint)(obj->format_modifier >> 32); + } + } + } @@ -60989,54 +61313,29 @@ index 0000000000..f666adc8e9 + +#if TRACE_ALL + for (a = attribs, i = 0; *a != EGL_NONE; a += 2, ++i) { -+ av_log(s, AV_LOG_INFO, "[%2d] %4x: %d\n", i, a[0], a[1]); ++ av_log(s, AV_LOG_INFO, "[%2d] %4x: %d\n", i, a[0], a[1]); + } +#endif + { -+ const EGLImage image = eglCreateImageKHR(de->setup.egl_dpy, -+ EGL_NO_CONTEXT, -+ EGL_LINUX_DMA_BUF_EXT, -+ NULL, attribs); -+ if (!image) { -+ av_log(s, AV_LOG_ERROR, "Failed to import fd %d\n", desc->objects[0].fd); -+ return -1; -+ } ++ const EGLImage image = eglCreateImageKHR(de->setup.egl_dpy, ++ EGL_NO_CONTEXT, ++ EGL_LINUX_DMA_BUF_EXT, ++ NULL, attribs); ++ if (!image) { ++ av_log(s, AV_LOG_ERROR, "Failed to import fd %d\n", desc->objects[0].fd); ++ return -1; ++ } + -+ glGenTextures(1, &da->texture); -+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, da->texture); -+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); ++ glGenTextures(1, &da->texture); ++ glBindTexture(GL_TEXTURE_EXTERNAL_OES, da->texture); ++ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ++ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); + -+ eglDestroyImageKHR(de->setup.egl_dpy, image); ++ eglDestroyImageKHR(de->setup.egl_dpy, image); + } + + da->fd = desc->objects[0].fd; -+ -+#if 0 -+ av_log(s, AV_LOG_INFO, "%dx%d, fmt: %x, boh=%d,%d,%d,%d, pitch=%d,%d,%d,%d," -+ " offset=%d,%d,%d,%d, mod=%llx,%llx,%llx,%llx\n", -+ av_frame_cropped_width(frame), -+ av_frame_cropped_height(frame), -+ desc->layers[0].format, -+ bo_plane_handles[0], -+ bo_plane_handles[1], -+ bo_plane_handles[2], -+ bo_plane_handles[3], -+ pitches[0], -+ pitches[1], -+ pitches[2], -+ pitches[3], -+ offsets[0], -+ offsets[1], -+ offsets[2], -+ offsets[3], -+ (long long)modifiers[0], -+ (long long)modifiers[1], -+ (long long)modifiers[2], -+ (long long)modifiers[3] -+ ); -+#endif + } + + glClearColor(0.5, 0.5, 0.5, 0.5); @@ -61053,55 +61352,55 @@ index 0000000000..f666adc8e9 + return 0; +} + -+static void * display_thread(void * v) ++static void* display_thread(void *v) +{ -+ AVFormatContext * const s = v; -+ egl_display_env_t * const de = s->priv_data; ++ AVFormatContext *const s = v; ++ egl_display_env_t *const de = s->priv_data; + +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "<<< %s\n", __func__); +#endif + { -+ EGLint egl_major, egl_minor; ++ EGLint egl_major, egl_minor; + -+ de->setup.dpy = XOpenDisplay(NULL); -+ if (!de->setup.dpy) { -+ av_log(s, AV_LOG_ERROR, "Couldn't open X display\n"); -+ goto fail; -+ } ++ de->setup.dpy = XOpenDisplay(NULL); ++ if (!de->setup.dpy) { ++ av_log(s, AV_LOG_ERROR, "Couldn't open X display\n"); ++ goto fail; ++ } + -+ de->setup.egl_dpy = eglGetDisplay(de->setup.dpy); -+ if (!de->setup.egl_dpy) { -+ av_log(s, AV_LOG_ERROR, "eglGetDisplay() failed\n"); -+ goto fail; -+ } ++ de->setup.egl_dpy = eglGetDisplay(de->setup.dpy); ++ if (!de->setup.egl_dpy) { ++ av_log(s, AV_LOG_ERROR, "eglGetDisplay() failed\n"); ++ goto fail; ++ } + -+ if (!eglInitialize(de->setup.egl_dpy, &egl_major, &egl_minor)) { -+ av_log(s, AV_LOG_ERROR, "Error: eglInitialize() failed\n"); -+ goto fail; -+ } ++ if (!eglInitialize(de->setup.egl_dpy, &egl_major, &egl_minor)) { ++ av_log(s, AV_LOG_ERROR, "Error: eglInitialize() failed\n"); ++ goto fail; ++ } + -+ av_log(s, AV_LOG_INFO, "EGL version %d.%d\n", egl_major, egl_minor); ++ av_log(s, AV_LOG_INFO, "EGL version %d.%d\n", egl_major, egl_minor); + -+ if (!epoxy_has_egl_extension(de->setup.egl_dpy, "EGL_KHR_image_base")) { -+ av_log(s, AV_LOG_ERROR, "Missing EGL KHR image extension\n"); -+ goto fail; -+ } ++ if (!epoxy_has_egl_extension(de->setup.egl_dpy, "EGL_KHR_image_base")) { ++ av_log(s, AV_LOG_ERROR, "Missing EGL KHR image extension\n"); ++ goto fail; ++ } + } + + if (!de->window_width || !de->window_height) { -+ de->window_width = 1280; -+ de->window_height = 720; ++ de->window_width = 1280; ++ de->window_height = 720; + } + if (make_window(s, de, de->setup.dpy, de->setup.egl_dpy, "ffmpeg-vout", + &de->setup.win, &de->setup.ctx, &de->setup.surf)) { -+ av_log(s, AV_LOG_ERROR, "%s: make_window failed\n", __func__); -+ goto fail; ++ av_log(s, AV_LOG_ERROR, "%s: make_window failed\n", __func__); ++ goto fail; + } + + if (gl_setup(s)) { -+ av_log(s, AV_LOG_ERROR, "%s: gl_setup failed\n", __func__); -+ goto fail; ++ av_log(s, AV_LOG_ERROR, "%s: gl_setup failed\n", __func__); ++ goto fail; + } + +#if TRACE_ALL @@ -61110,7 +61409,7 @@ index 0000000000..f666adc8e9 + sem_post(&de->display_start_sem); + + for (;;) { -+ AVFrame * frame; ++ AVFrame *frame; + + while (sem_wait(&de->q_sem) != 0) { + av_assert0(errno == EINTR); @@ -61148,9 +61447,9 @@ index 0000000000..f666adc8e9 + +static int egl_vout_write_packet(AVFormatContext *s, AVPacket *pkt) +{ -+ const AVFrame * const src_frame = (AVFrame *)pkt->data; -+ AVFrame * frame; -+ egl_display_env_t * const de = s->priv_data; ++ const AVFrame *const src_frame = (AVFrame *)pkt->data; ++ AVFrame *frame; ++ egl_display_env_t *const de = s->priv_data; + +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "%s\n", __func__); @@ -61163,8 +61462,7 @@ index 0000000000..f666adc8e9 + else if (src_frame->format == AV_PIX_FMT_VAAPI) { + frame = av_frame_alloc(); + frame->format = AV_PIX_FMT_DRM_PRIME; -+ if (av_hwframe_map(frame, src_frame, 0) != 0) -+ { ++ if (av_hwframe_map(frame, src_frame, 0) != 0) { + av_log(s, AV_LOG_WARNING, "Failed to map frame (format=%d) to DRM_PRiME\n", src_frame->format); + av_frame_free(&frame); + return AVERROR(EINVAL); @@ -61177,12 +61475,12 @@ index 0000000000..f666adc8e9 + + // Really hacky sync + while (de->show_all && de->q_next) { -+ usleep(3000); ++ usleep(3000); + } + + pthread_mutex_lock(&de->q_lock); + { -+ AVFrame * const t = de->q_next; ++ AVFrame *const t = de->q_next; + de->q_next = frame; + frame = t; + } @@ -61197,7 +61495,7 @@ index 0000000000..f666adc8e9 +} + +static int egl_vout_write_frame(AVFormatContext *s, int stream_index, AVFrame **ppframe, -+ unsigned flags) ++ unsigned flags) +{ +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "%s: idx=%d, flags=%#x\n", __func__, stream_index, flags); @@ -61215,7 +61513,7 @@ index 0000000000..f666adc8e9 +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "%s: %d\n", __func__, type); +#endif -+ switch(type) { ++ switch (type) { + case AV_APP_TO_DEV_WINDOW_REPAINT: + return 0; + default: @@ -61225,14 +61523,14 @@ index 0000000000..f666adc8e9 +} + +// deinit is called if init fails so no need to clean up explicity here -+static int egl_vout_init(struct AVFormatContext * s) ++static int egl_vout_init(struct AVFormatContext *s) +{ -+ egl_display_env_t * const de = s->priv_data; ++ egl_display_env_t *const de = s->priv_data; + unsigned int i; + + av_log(s, AV_LOG_DEBUG, "<<< %s\n", __func__); + -+ de->setup = (struct egl_setup){0}; ++ de->setup = (struct egl_setup) { 0 }; + + for (i = 0; i != 32; ++i) { + de->aux[i].fd = -1; @@ -61246,8 +61544,8 @@ index 0000000000..f666adc8e9 + + sem_wait(&de->display_start_sem); + if (de->q_terminate) { -+ av_log(s, AV_LOG_ERROR, "%s: Display startup failure\n", __func__); -+ return -1; ++ av_log(s, AV_LOG_ERROR, "%s: Display startup failure\n", __func__); ++ return -1; + } + + av_log(s, AV_LOG_DEBUG, ">>> %s\n", __func__); @@ -61255,9 +61553,9 @@ index 0000000000..f666adc8e9 + return 0; +} + -+static void egl_vout_deinit(struct AVFormatContext * s) ++static void egl_vout_deinit(struct AVFormatContext *s) +{ -+ egl_display_env_t * const de = s->priv_data; ++ egl_display_env_t *const de = s->priv_data; + + av_log(s, AV_LOG_DEBUG, "<<< %s\n", __func__); + @@ -61275,11 +61573,11 @@ index 0000000000..f666adc8e9 + +#define OFFSET(x) offsetof(egl_display_env_t, x) +static const AVOption options[] = { -+ { "show_all", "show all frames", OFFSET(show_all), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "window_size", "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "window_x", "set window x offset", OFFSET(window_x), AV_OPT_TYPE_INT, {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "window_y", "set window y offset", OFFSET(window_y), AV_OPT_TYPE_INT, {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "fullscreen", "set fullscreen display", OFFSET(fullscreen), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "show_all", "show all frames", OFFSET(show_all), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "window_size", "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "window_x", "set window x offset", OFFSET(window_x), AV_OPT_TYPE_INT, { .i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "window_y", "set window y offset", OFFSET(window_y), AV_OPT_TYPE_INT, { .i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "fullscreen", "set fullscreen display", OFFSET(fullscreen), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } + +}; @@ -62042,10 +62340,10 @@ index da1cf9941e..c588ed23cb 100644 case AVMEDIA_TYPE_AUDIO: diff --git a/libavfilter/vf_deinterlace_v4l2m2m.c b/libavfilter/vf_deinterlace_v4l2m2m.c new file mode 100644 -index 0000000000..986fde5955 +index 0000000000..d4c11cfc51 --- /dev/null +++ b/libavfilter/vf_deinterlace_v4l2m2m.c -@@ -0,0 +1,1971 @@ +@@ -0,0 +1,2115 @@ +/* + * This file is part of FFmpeg. + * @@ -62083,6 +62381,8 @@ index 0000000000..986fde5955 +#include +#include + ++#include "config.h" ++ +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/common.h" @@ -62107,6 +62407,16 @@ index 0000000000..986fde5955 +#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel packed */ +#endif + ++// V4L2_PIX_FMT_NV12_10_COL128 and V4L2_PIX_FMT_NV12_COL128 should be defined ++// in drm_fourcc.h hopefully will be sometime in the future but until then... ++#ifndef V4L2_PIX_FMT_NV12_10_COL128 ++#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0') ++#endif ++ ++#ifndef V4L2_PIX_FMT_NV12_COL128 ++#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2') /* 12 Y/CbCr 4:2:0 128 pixel wide column */ ++#endif ++ +typedef struct V4L2Queue V4L2Queue; +typedef struct DeintV4L2M2MContextShared DeintV4L2M2MContextShared; + @@ -62130,6 +62440,7 @@ index 0000000000..986fde5955 +typedef struct V4L2Queue { + struct v4l2_format format; + struct v4l2_selection sel; ++ int eos; + int num_buffers; + V4L2Buffer *buffers; + const char * name; @@ -62163,20 +62474,41 @@ index 0000000000..986fde5955 + pts_track_el_t a[PTS_TRACK_SIZE]; +} pts_track_t; + ++typedef enum drain_state_e ++{ ++ DRAIN_NONE = 0, // Not draining ++ DRAIN_TIMEOUT, // Drain until normal timeout setup yields no frame ++ DRAIN_LAST, // Drain with long timeout last_frame in received on output expected ++ DRAIN_EOS, // Drain with long timeout EOS expected ++ DRAIN_DONE // Drained ++} drain_state_t; ++ +typedef struct DeintV4L2M2MContextShared { + void * logctx; // For logging - will be NULL when done + filter_type_v4l2_t filter_type; + + int fd; -+ int done; ++ int done; // fd closed - awating all refs dropped + int width; + int height; + ++ int drain; // EOS received (inlink status) ++ drain_state_t drain_state; ++ int64_t drain_pts; // PTS associated with inline status ++ ++ unsigned int frames_rx; ++ unsigned int frames_tx; ++ + // from options + int output_width; + int output_height; + enum AVPixelFormat output_format; + ++ int has_enc_stop; ++ // We expect to get exactly the same number of frames out as we put in ++ // We can drain by matching input to output ++ int one_to_one; ++ + int orig_width; + int orig_height; + atomic_uint refcount; @@ -62215,6 +62547,12 @@ index 0000000000..986fde5955 + enum AVChromaLocation chroma_location; +} DeintV4L2M2MContext; + ++ ++static inline int drain_frame_expected(const drain_state_t d) ++{ ++ return d == DRAIN_EOS || d == DRAIN_LAST; ++} ++ +// These just list the ones we know we can cope with +static uint32_t +fmt_av_to_v4l2(const enum AVPixelFormat avfmt) @@ -62224,9 +62562,11 @@ index 0000000000..986fde5955 + return V4L2_PIX_FMT_YUV420; + case AV_PIX_FMT_NV12: + return V4L2_PIX_FMT_NV12; ++#if CONFIG_SAND + case AV_PIX_FMT_RPI4_8: + case AV_PIX_FMT_SAND128: + return V4L2_PIX_FMT_NV12_COL128; ++#endif + default: + break; + } @@ -62241,8 +62581,10 @@ index 0000000000..986fde5955 + return AV_PIX_FMT_YUV420P; + case V4L2_PIX_FMT_NV12: + return AV_PIX_FMT_NV12; ++#if CONFIG_SAND + case V4L2_PIX_FMT_NV12_COL128: + return AV_PIX_FMT_RPI4_8; ++#endif + default: + break; + } @@ -62366,6 +62708,13 @@ index 0000000000..986fde5955 + return 0; +} + ++// We are only ever expecting in-order frames so nothing more clever is required ++static unsigned int ++pts_track_count(const pts_track_t * const trk) ++{ ++ return (trk->n - trk->last_n) & (PTS_TRACK_SIZE - 1); ++} ++ +static struct timeval pts_track_add_frame(pts_track_t * const trk, const AVFrame * const src) +{ + const uint32_t n = pts_track_next_n(trk); @@ -62438,6 +62787,12 @@ index 0000000000..986fde5955 + return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.pixelformat : fmt->fmt.pix.pixelformat; +} + ++static inline uint32_t ++buf_bytesused0(const struct v4l2_buffer * const buf) ++{ ++ return V4L2_TYPE_IS_MULTIPLANAR(buf->type) ? buf->m.planes[0].bytesused : buf->bytesused; ++} ++ +static void +init_format(V4L2Queue * const q, const uint32_t format_type) +{ @@ -62871,6 +63226,7 @@ index 0000000000..986fde5955 + h = src->layers[0].planes[1].offset / bpl; + w = bpl; + } ++#if CONFIG_SAND + else if (fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128) { + if (src->layers[0].nb_planes != 2) + break; @@ -62879,9 +63235,11 @@ index 0000000000..986fde5955 + h = src->layers[0].planes[1].offset / 128; + bpl = fourcc_mod_broadcom_param(mod); + } ++#endif + break; + + case DRM_FORMAT_P030: ++#if CONFIG_SAND + if (fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128) { + if (src->layers[0].nb_planes != 2) + break; @@ -62890,6 +63248,7 @@ index 0000000000..986fde5955 + h = src->layers[0].planes[1].offset / 128; + bpl = fourcc_mod_broadcom_param(mod); + } ++#endif + break; + + default: @@ -63096,7 +63455,6 @@ index 0000000000..986fde5955 + AVDRMLayerDescriptor * const layer = &drm_desc->layers[0]; + const struct v4l2_format *const fmt = &q->format; + const uint32_t height = fmt_height(fmt); -+ const uint32_t width = fmt_width(fmt); + ptrdiff_t bpl0; + + /* fill the DRM frame descriptor */ @@ -63111,7 +63469,7 @@ index 0000000000..986fde5955 + bpl0 = layer->planes[0].pitch; + + switch (fmt_pixelformat(fmt)) { -+ ++#if CONFIG_SAND + case V4L2_PIX_FMT_NV12_COL128: + mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(bpl0); + layer->format = V4L2_PIX_FMT_NV12; @@ -63122,9 +63480,10 @@ index 0000000000..986fde5955 + layer->nb_planes = 2; + layer->planes[1].object_index = 0; + layer->planes[1].offset = height * 128; -+ layer->planes[0].pitch = width; -+ layer->planes[1].pitch = width; ++ layer->planes[0].pitch = fmt_width(fmt); ++ layer->planes[1].pitch = layer->planes[0].pitch; + break; ++#endif + + case DRM_FORMAT_NV12: + layer->format = V4L2_PIX_FMT_NV12; @@ -63497,12 +63856,24 @@ index 0000000000..986fde5955 + + av_log(ctx->logctx, AV_LOG_TRACE, "<<< %s\n", __func__); + ++ if (queue->eos) { ++ av_log(ctx->logctx, AV_LOG_TRACE, ">>> %s: EOS\n", __func__); ++ return AVERROR_EOF; ++ } ++ + avbuf = deint_v4l2m2m_dequeue_buffer(queue, timeout); + if (!avbuf) { + av_log(ctx->logctx, AV_LOG_DEBUG, "%s: No buffer to dequeue (timeout=%d)\n", __func__, timeout); + return AVERROR(EAGAIN); + } + ++ if (V4L2_TYPE_IS_CAPTURE(avbuf->buffer.type)) { ++ if ((avbuf->buffer.flags & V4L2_BUF_FLAG_LAST) != 0) ++ queue->eos = 1; ++ if (buf_bytesused0(&avbuf->buffer) == 0) ++ return queue->eos ? AVERROR_EOF : AVERROR(EINVAL); ++ } ++ + // Fill in PTS and anciliary info from src frame + pts_track_get_frame(&ctx->track, avbuf->buffer.timestamp, frame); + @@ -63635,7 +64006,10 @@ index 0000000000..986fde5955 + return is_linear ? V4L2_PIX_FMT_YUV420 : 0; + case DRM_FORMAT_NV12: + return is_linear ? V4L2_PIX_FMT_NV12 : -+ fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128 ? V4L2_PIX_FMT_NV12_COL128 : 0; ++#if CONFIG_SAND ++ fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128 ? V4L2_PIX_FMT_NV12_COL128 : ++#endif ++ 0; + default: + break; + } @@ -63651,8 +64025,8 @@ index 0000000000..986fde5955 + V4L2Queue *output = &ctx->output; + int ret; + -+ av_log(priv, AV_LOG_DEBUG, "<<< %s: input pts: %"PRId64" (%"PRId64") field :%d interlaced: %d aspect:%d/%d\n", -+ __func__, in->pts, AV_NOPTS_VALUE, in->top_field_first, in->interlaced_frame, in->sample_aspect_ratio.num, in->sample_aspect_ratio.den); ++ av_log(priv, AV_LOG_DEBUG, "<<< %s: input pts: %"PRId64" dts: %"PRId64" field :%d interlaced: %d aspect:%d/%d\n", ++ __func__, in->pts, in->pkt_dts, in->top_field_first, in->interlaced_frame, in->sample_aspect_ratio.num, in->sample_aspect_ratio.den); + av_log(priv, AV_LOG_DEBUG, "--- %s: in status in %d/ot %d; out status in %d/out %d\n", __func__, + avctx->inputs[0]->status_in, avctx->inputs[0]->status_out, avctx->outputs[0]->status_in, avctx->outputs[0]->status_out); + @@ -63722,6 +64096,20 @@ index 0000000000..986fde5955 + else + ctx->field_order = V4L2_FIELD_INTERLACED_BT; + ++ { ++ struct v4l2_encoder_cmd ecmd = { ++ .cmd = V4L2_ENC_CMD_STOP ++ }; ++ ctx->has_enc_stop = 0; ++ if (ioctl(ctx->fd, VIDIOC_TRY_ENCODER_CMD, &ecmd) == 0) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Test encode stop succeeded\n"); ++ ctx->has_enc_stop = 1; ++ } ++ else { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Test encode stop fail: %s\n", av_err2str(AVERROR(errno))); ++ } ++ ++ } + } + + ret = deint_v4l2m2m_enqueue_frame(output, in); @@ -63730,6 +64118,41 @@ index 0000000000..986fde5955 + return ret; +} + ++static int ++ack_inlink(AVFilterContext * const avctx, DeintV4L2M2MContextShared *const s, ++ AVFilterLink * const inlink) ++{ ++ int instatus; ++ int64_t inpts; ++ ++ if (ff_inlink_acknowledge_status(inlink, &instatus, &inpts) <= 0) ++ return 0; ++ ++ s->drain = instatus; ++ s->drain_pts = inpts; ++ s->drain_state = DRAIN_TIMEOUT; ++ ++ if (s->field_order == V4L2_FIELD_ANY) { // Not yet started ++ s->drain_state = DRAIN_DONE; ++ } ++ else if (s->one_to_one) { ++ s->drain_state = DRAIN_LAST; ++ } ++ else if (s->has_enc_stop) { ++ struct v4l2_encoder_cmd ecmd = { ++ .cmd = V4L2_ENC_CMD_STOP ++ }; ++ if (ioctl(s->fd, VIDIOC_ENCODER_CMD, &ecmd) == 0) { ++ av_log(avctx->priv, AV_LOG_DEBUG, "Do Encode stop\n"); ++ s->drain_state = DRAIN_EOS; ++ } ++ else { ++ av_log(avctx->priv, AV_LOG_WARNING, "Encode stop fail: %s\n", av_err2str(AVERROR(errno))); ++ } ++ } ++ return 1; ++} ++ +static int deint_v4l2m2m_activate(AVFilterContext *avctx) +{ + DeintV4L2M2MContext * const priv = avctx->priv; @@ -63738,25 +64161,19 @@ index 0000000000..986fde5955 + AVFilterLink * const inlink = avctx->inputs[0]; + int n = 0; + int cn = 99; -+ int instatus = 0; -+ int64_t inpts = 0; + int did_something = 0; + + av_log(priv, AV_LOG_TRACE, "<<< %s\n", __func__); + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx); + -+ ff_inlink_acknowledge_status(inlink, &instatus, &inpts); ++ ack_inlink(avctx, s, inlink); + -+ if (!ff_outlink_frame_wanted(outlink)) { -+ av_log(priv, AV_LOG_TRACE, "%s: Not wanted out\n", __func__); -+ } -+ else if (s->field_order != V4L2_FIELD_ANY) // Can't DQ if no setup! ++ if (s->field_order != V4L2_FIELD_ANY) // Can't DQ if no setup! + { + AVFrame * frame = av_frame_alloc(); + int rv; + -+again: + recycle_q(&s->output); + n = count_enqueued(&s->output); + @@ -63765,10 +64182,21 @@ index 0000000000..986fde5955 + return AVERROR(ENOMEM); + } + -+ rv = deint_v4l2m2m_dequeue_frame(&s->capture, frame, n > 4 ? 300 : 0); ++ rv = deint_v4l2m2m_dequeue_frame(&s->capture, frame, ++ drain_frame_expected(s->drain_state) || n > 4 ? 300 : 0); + if (rv != 0) { + av_frame_free(&frame); -+ if (rv != AVERROR(EAGAIN)) { ++ if (rv == AVERROR_EOF) { ++ av_log(priv, AV_LOG_DEBUG, "%s: --- DQ EOF\n", __func__); ++ s->drain_state = DRAIN_DONE; ++ } ++ else if (rv == AVERROR(EAGAIN)) { ++ if (s->drain_state != DRAIN_NONE) { ++ av_log(priv, AV_LOG_DEBUG, "%s: --- DQ empty - drain done\n", __func__); ++ s->drain_state = DRAIN_DONE; ++ } ++ } ++ else { + av_log(priv, AV_LOG_ERROR, ">>> %s: DQ fail: %s\n", __func__, av_err2str(rv)); + return rv; + } @@ -63778,29 +64206,30 @@ index 0000000000..986fde5955 + // frame is always consumed by filter_frame - even on error despite + // a somewhat confusing comment in the header + rv = ff_filter_frame(outlink, frame); -+ -+ if (instatus != 0) { -+ av_log(priv, AV_LOG_TRACE, "%s: eof loop\n", __func__); -+ goto again; -+ } ++ ++s->frames_tx; + + av_log(priv, AV_LOG_TRACE, "%s: Filtered: %s\n", __func__, av_err2str(rv)); + did_something = 1; ++ ++ if (s->drain_state != DRAIN_NONE && pts_track_count(&s->track) == 0) { ++ av_log(priv, AV_LOG_DEBUG, "%s: --- DQ last - drain done\n", __func__); ++ s->drain_state = DRAIN_DONE; ++ } + } + + cn = count_enqueued(&s->capture); + } + -+ if (instatus != 0) { -+ ff_outlink_set_status(outlink, instatus, inpts); -+ av_log(priv, AV_LOG_TRACE, ">>> %s: Status done: %s\n", __func__, av_err2str(instatus)); ++ if (s->drain_state == DRAIN_DONE) { ++ ff_outlink_set_status(outlink, s->drain, s->drain_pts); ++ av_log(priv, AV_LOG_TRACE, ">>> %s: Status done: %s\n", __func__, av_err2str(s->drain)); + return 0; + } + + recycle_q(&s->output); + n = count_enqueued(&s->output); + -+ while (n < 6) { ++ while (n < 6 && !s->drain) { + AVFrame * frame; + int rv; + @@ -63811,8 +64240,13 @@ index 0000000000..986fde5955 + + if (frame == NULL) { + av_log(priv, AV_LOG_TRACE, "%s: No frame\n", __func__); ++ if (!ack_inlink(avctx, s, inlink)) { ++ ff_inlink_request_frame(inlink); ++ av_log(priv, AV_LOG_TRACE, "%s: req frame\n", __func__); ++ } + break; + } ++ ++s->frames_rx; + + rv = deint_v4l2m2m_filter_frame(inlink, frame); + av_frame_free(&frame); @@ -63821,16 +64255,11 @@ index 0000000000..986fde5955 + return rv; + + av_log(priv, AV_LOG_TRACE, "%s: Q frame\n", __func__); ++ did_something = 1; + ++n; + } + -+ if (n < 6) { -+ ff_inlink_request_frame(inlink); -+ did_something = 1; -+ av_log(priv, AV_LOG_TRACE, "%s: req frame\n", __func__); -+ } -+ -+ if (n > 4 && ff_outlink_frame_wanted(outlink)) { ++ if ((n > 4 || s->drain) && ff_outlink_frame_wanted(outlink)) { + ff_filter_set_ready(avctx, 1); + did_something = 1; + av_log(priv, AV_LOG_TRACE, "%s: ready\n", __func__); @@ -63909,7 +64338,18 @@ index 0000000000..986fde5955 + +static av_cold int scale_v4l2m2m_init(AVFilterContext *avctx) +{ -+ return common_v4l2m2m_init(avctx, FILTER_V4L2_SCALE); ++ int rv; ++ DeintV4L2M2MContext * priv; ++ DeintV4L2M2MContextShared * ctx; ++ ++ if ((rv = common_v4l2m2m_init(avctx, FILTER_V4L2_SCALE)) != 0) ++ return rv; ++ ++ priv = avctx->priv; ++ ctx = priv->shared; ++ ++ ctx->one_to_one = 1; ++ return 0; +} + +static void deint_v4l2m2m_uninit(AVFilterContext *avctx) @@ -63917,6 +64357,8 @@ index 0000000000..986fde5955 + DeintV4L2M2MContext *priv = avctx->priv; + DeintV4L2M2MContextShared *ctx = priv->shared; + ++ av_log(priv, AV_LOG_VERBOSE, "Frames Rx: %u, Frames Tx: %u\n", ++ ctx->frames_rx, ctx->frames_tx); + ctx->done = 1; + ctx->logctx = NULL; // Log to NULL works, log to missing crashes + pts_track_uninit(&ctx->track); @@ -66692,14 +67134,16 @@ index 18c7a0efc8..bab13a4d50 100644 #if FF_API_PLUS1_MINUS1 FF_ENABLE_DEPRECATION_WARNINGS diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h -index 46ef211add..84b7c9dd88 100644 +index 46ef211add..9195ead15f 100644 --- a/libavutil/pixfmt.h +++ b/libavutil/pixfmt.h -@@ -357,6 +357,12 @@ enum AVPixelFormat { +@@ -357,6 +357,14 @@ enum AVPixelFormat { AV_PIX_FMT_Y210BE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, big-endian AV_PIX_FMT_Y210LE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, little-endian +// RPI - not on ifdef so can be got at by calling progs ++// #define so code that uses this can know it is there ++#define AVUTIL_HAVE_PIX_FMT_SAND 1 + AV_PIX_FMT_SAND128, ///< 4:2:0 8-bit 128x*Y stripe, 64x*UV stripe, then next x stripe, mysterious padding + AV_PIX_FMT_SAND64_10, ///< 4:2:0 10-bit 64x*Y stripe, 32x*UV stripe, then next x stripe, mysterious padding + AV_PIX_FMT_SAND64_16, ///< 4:2:0 16-bit 64x*Y stripe, 32x*UV stripe, then next x stripe, mysterious padding @@ -68403,10 +68847,10 @@ index 0000000000..fc14f2a3c2 +1,WPP_F_ericsson_MAIN_2,WPP_F_ericsson_MAIN_2.bit,WPP_F_ericsson_MAIN_2_yuv.md5 diff --git a/pi-util/conf_native.sh b/pi-util/conf_native.sh new file mode 100755 -index 0000000000..37cea71756 +index 0000000000..a9e053801c --- /dev/null +++ b/pi-util/conf_native.sh -@@ -0,0 +1,106 @@ +@@ -0,0 +1,107 @@ +echo "Configure for native build" + +FFSRC=`pwd` @@ -68502,6 +68946,7 @@ index 0000000000..37cea71756 + --enable-libdrm\ + --enable-vout-egl\ + --enable-vout-drm\ ++ --enable-gpl\ + $SHARED_LIBS\ + $RPIOPTS\ + --extra-cflags="-ggdb $RPI_KEEPS $RPI_DEFINES $RPI_INCLUDES"\ @@ -69086,6 +69531,95 @@ index 0000000000..a4dbb6eacd +$QASM -mc_c:$DST_BASE,$DST_BASE,$DST_BASE $SRC_FILE > $TARGET_DIR/$DST_BASE.c +$QASM -mc_h:$DST_BASE,$DST_BASE,$DST_BASE $SRC_FILE > $TARGET_DIR/$DST_BASE.h + +diff --git a/pi-util/testfilt.py b/pi-util/testfilt.py +new file mode 100755 +index 0000000000..b322dac0c2 +--- /dev/null ++++ b/pi-util/testfilt.py +@@ -0,0 +1,83 @@ ++#!/usr/bin/env python3 ++ ++import string ++import os ++import subprocess ++import re ++import argparse ++import sys ++import csv ++from stat import * ++ ++class validator: ++ def __init__(self): ++ self.ok = False ++ ++ def isok(self): ++ return self.ok ++ ++ def setok(self): ++ self.ok = True ++ ++class valid_regex(validator): ++ def __init__(self, regex): ++ super().__init__() ++ self.regex = re.compile(regex) ++ ++ def scanline(self, line): ++ if self.isok() or self.regex.search(line): ++ self.setok() ++ ++ ++def validate(validators, flog): ++ for line in flog: ++ for v in validators: ++ v.scanline(line) ++ ++ ok = True ++ for v in validators: ++ if not v.isok(): ++ ok = False ++ # complain ++ print("Test failed") ++ ++ if ok: ++ print("OK") ++ return ok ++ ++def runtest(name, ffmpeg, args, suffix, validators): ++ log_root = os.path.join("/tmp", "testfilt", name) ++ ofilename = os.path.join(log_root, name + suffix) ++ ++ if not os.path.exists(log_root): ++ os.makedirs(log_root) ++ ++ try: ++ os.remove(ofilename) ++ except: ++ pass ++ ++ flog = open(os.path.join(log_root, name + ".log"), "wb") ++ ffargs = [ffmpeg] + args + [ofilename] ++ ++ subprocess.call(ffargs, stdout=flog, stderr=subprocess.STDOUT, text=False) ++ flog.close ++ ++ flog = open(os.path.join(log_root, name + ".log"), "rt") ++ return validate(validators, flog) ++ ++def sayok(log_root, flog): ++ print("Woohoo") ++ return True ++ ++if __name__ == '__main__': ++ ++ argp = argparse.ArgumentParser(description="FFmpeg filter tester") ++ argp.add_argument("--ffmpeg", default="./ffmpeg", help="ffmpeg exec name") ++ args = argp.parse_args() ++ ++ runtest("ATest", args.ffmpeg, ["-v", "verbose", "-no_cvt_hw", "-an", "-c:v", "h264_v4l2m2m", "-i", ++ "/home/johncox/server/TestMedia/Sony/jellyfish-10-mbps-hd-h264.mkv", ++# "/home/jc/rpi/streams/jellyfish-3-mbps-hd-h264.mkv", ++ "-c:v", "h264_v4l2m2m", "-b:v", "2M"], ".mkv", ++ [valid_regex(r'Output stream #0:0 \(video\): 900 frames encoded; 900 packets muxed')]) diff --git a/pi-util/v3dusage.py b/pi-util/v3dusage.py new file mode 100755 index 0000000000..5935a11ca5