diff --git a/packages/multimedia/ffmpeg/patches/rpi/ffmpeg-001-rpi.patch b/packages/multimedia/ffmpeg/patches/rpi/ffmpeg-001-rpi.patch index d9dc15a210..82b59fea06 100644 --- a/packages/multimedia/ffmpeg/patches/rpi/ffmpeg-001-rpi.patch +++ b/packages/multimedia/ffmpeg/patches/rpi/ffmpeg-001-rpi.patch @@ -1908,7 +1908,7 @@ index b73b80e5fdb1..7ca36ac84975 100644 }; diff --git a/libavcodec/rawenc.c b/libavcodec/rawenc.c -index 8c577006d922..8ca0379e1219 100644 +index 8c577006d922..b215577b7548 100644 --- a/libavcodec/rawenc.c +++ b/libavcodec/rawenc.c @@ -24,6 +24,7 @@ @@ -1951,10 +1951,10 @@ index 8c577006d922..8ca0379e1219 100644 + + dst = pkt->data; + -+ av_rpi_sand_to_planar_y8(dst, width, frame->data[0], frame->linesize[0], frame->linesize[3], x0, y0, width, height); ++ av_rpi_sand_to_planar_y8(dst, width, frame->data[0], frame->linesize[0], av_rpi_sand_frame_stride2_y(frame), x0, y0, width, height); + dst += width * height; + av_rpi_sand_to_planar_c8(dst, width / 2, dst + width * height / 4, width / 2, -+ frame->data[1], frame->linesize[1], av_rpi_sand_frame_stride2(frame), x0 / 2, y0 / 2, width / 2, height / 2); ++ frame->data[1], frame->linesize[1], av_rpi_sand_frame_stride2_c(frame), x0 / 2, y0 / 2, width / 2, height / 2); + return 0; +} + @@ -1974,10 +1974,10 @@ index 8c577006d922..8ca0379e1219 100644 + + dst = pkt->data; + -+ av_rpi_sand_to_planar_y16(dst, width * 2, frame->data[0], frame->linesize[0], frame->linesize[3], x0 * 2, y0, width * 2, height); ++ av_rpi_sand_to_planar_y16(dst, width * 2, frame->data[0], frame->linesize[0], av_rpi_sand_frame_stride2_y(frame), x0 * 2, y0, width * 2, height); + dst += width * height * 2; + av_rpi_sand_to_planar_c16(dst, width, dst + width * height / 2, width, -+ frame->data[1], frame->linesize[1], av_rpi_sand_frame_stride2(frame), x0, y0 / 2, width, height / 2); ++ frame->data[1], frame->linesize[1], av_rpi_sand_frame_stride2_c(frame), x0, y0 / 2, width, height / 2); + return 0; +} + @@ -1997,10 +1997,10 @@ index 8c577006d922..8ca0379e1219 100644 + + dst = pkt->data; + -+ av_rpi_sand30_to_planar_y16(dst, width * 2, frame->data[0], frame->linesize[0], frame->linesize[3], x0, y0, width, height); ++ av_rpi_sand30_to_planar_y16(dst, width * 2, frame->data[0], frame->linesize[0], av_rpi_sand_frame_stride2_y(frame), x0, y0, width, height); + dst += width * height * 2; + av_rpi_sand30_to_planar_c16(dst, width, dst + width * height / 2, width, -+ frame->data[1], frame->linesize[1], av_rpi_sand_frame_stride2(frame), x0/2, y0 / 2, width/2, height / 2); ++ frame->data[1], frame->linesize[1], av_rpi_sand_frame_stride2_c(frame), x0/2, y0 / 2, width/2, height / 2); + return 0; +} +#endif @@ -7369,10 +7369,10 @@ index 000000000000..0ff8bbe88207 + diff --git a/libavcodec/v4l2_req_devscan.c b/libavcodec/v4l2_req_devscan.c new file mode 100644 -index 000000000000..99a8c19710bc +index 000000000000..0416f3d4d104 --- /dev/null +++ b/libavcodec/v4l2_req_devscan.c -@@ -0,0 +1,475 @@ +@@ -0,0 +1,474 @@ +/* + Copyright (C) 2024 John Cox john.cox@raspberrypi.com + @@ -7535,9 +7535,8 @@ index 000000000000..99a8c19710bc +} + +#define REQ_BUF_CAPS (\ -+ V4L2_BUF_CAP_SUPPORTS_DMABUF |\ + V4L2_BUF_CAP_SUPPORTS_REQUESTS |\ -+ V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF) ++ 0) + +static void probe_formats(void * const dc, + struct devscan *const scan, @@ -8549,10 +8548,10 @@ index 000000000000..472df7cb0e39 + diff --git a/libavcodec/v4l2_req_hevc_vx.c b/libavcodec/v4l2_req_hevc_vx.c new file mode 100644 -index 000000000000..bb7535a49201 +index 000000000000..04b95dbbee17 --- /dev/null +++ b/libavcodec/v4l2_req_hevc_vx.c -@@ -0,0 +1,1454 @@ +@@ -0,0 +1,1452 @@ +/* + Copyright (C) 2024 John Cox john.cox@raspberrypi.com + @@ -8584,6 +8583,7 @@ index 000000000000..bb7535a49201 +#include "hwconfig.h" +#include "internal.h" +#include "thread.h" ++#include "v4l2_fmt.h" + +#include "libavutil/mem.h" + @@ -8688,22 +8688,21 @@ index 000000000000..bb7535a49201 +// Get an FFmpeg format from the v4l2 format +static enum AVPixelFormat pixel_format_from_format(const struct v4l2_format *const format) +{ -+ switch (V4L2_TYPE_IS_MULTIPLANAR(format->type) ? -+ format->fmt.pix_mp.pixelformat : format->fmt.pix.pixelformat) { -+ case V4L2_PIX_FMT_YUV420: -+ return AV_PIX_FMT_YUV420P; -+ case V4L2_PIX_FMT_NV12: -+ return AV_PIX_FMT_NV12; ++ const uint32_t vfmt = V4L2_TYPE_IS_MULTIPLANAR(format->type) ? ++ format->fmt.pix_mp.pixelformat : format->fmt.pix.pixelformat; ++ switch (vfmt) { +#if CONFIG_SAND + case V4L2_PIX_FMT_NV12_COL128: ++ case V4L2_PIX_FMT_NV12_COL128M: + return AV_PIX_FMT_RPI4_8; + case V4L2_PIX_FMT_NV12_10_COL128: ++ case V4L2_PIX_FMT_NV12_10_COL128M: + return AV_PIX_FMT_RPI4_10; +#endif + default: + break; + } -+ return AV_PIX_FMT_NONE; ++ return ff_v4l2_format_v4l2_to_avfmt(vfmt, AV_CODEC_ID_RAWVIDEO); +} + +static inline uint64_t frame_capture_dpb(const AVFrame * const frame) @@ -9247,8 +9246,6 @@ index 000000000000..bb7535a49201 +{ + V4L2MediaReqDescriptor *rd = (V4L2MediaReqDescriptor*)frame->data[0]; + -+ fprintf(stderr, "<<< %s\n", __func__); -+ +// av_log(NULL, AV_LOG_INFO, "%s\n", __func__); + frame->flags &= ~AV_FRAME_FLAG_CORRUPT; + if (frame_finish(rd) != 0) { @@ -9279,9 +9276,7 @@ index 000000000000..bb7535a49201 +{ + const HEVCContext *h = avctx->priv_data; + V4L2MediaReqDescriptor *const rd = (V4L2MediaReqDescriptor *)h->cur_frame->f->data[0]; -+ static int z = 0; + -+ fprintf(stderr, "<<< %s: %d\n", __func__, ++z); +// av_log(NULL, AV_LOG_INFO, "%s\n", __func__); + decode_q_add(&ctx->decode_q, &rd->decode_ent); + @@ -9315,7 +9310,10 @@ index 000000000000..bb7535a49201 + unsigned int width; + unsigned int height; + unsigned int bpl; ++ unsigned int bpl2; + uint32_t pixelformat; ++ uint64_t mod = DRM_FORMAT_MOD_LINEAR; ++ unsigned int object_count = 1; + + if (V4L2_TYPE_IS_MULTIPLANAR(format->type)) { + width = format->fmt.pix_mp.width; @@ -9329,51 +9327,68 @@ index 000000000000..bb7535a49201 + pixelformat = format->fmt.pix.pixelformat; + bpl = format->fmt.pix.bytesperline; + } ++ bpl2 = bpl; + + switch (pixelformat) { + case V4L2_PIX_FMT_NV12: + layer->format = DRM_FORMAT_NV12; -+ desc->objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR; ++ break; ++ case V4L2_PIX_FMT_P010: ++ layer->format = DRM_FORMAT_P010; + break; +#if CONFIG_SAND + case V4L2_PIX_FMT_NV12_COL128: + layer->format = DRM_FORMAT_NV12; -+ desc->objects[0].format_modifier = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(bpl); ++ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(bpl); + break; + case V4L2_PIX_FMT_NV12_10_COL128: + layer->format = DRM_FORMAT_P030; -+ desc->objects[0].format_modifier = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(bpl); ++ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(bpl); ++ break; ++ case V4L2_PIX_FMT_NV12_COL128M: ++ layer->format = DRM_FORMAT_NV12; ++ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0); ++ bpl = height; ++ bpl2 = height / 2; ++ object_count = 2; ++ break; ++ case V4L2_PIX_FMT_NV12_10_COL128M: ++ layer->format = DRM_FORMAT_P030; ++ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0); ++ bpl = height; ++ bpl2 = height / 2; ++ object_count = 2; + break; +#endif +#ifdef DRM_FORMAT_MOD_ALLWINNER_TILED + case V4L2_PIX_FMT_SUNXI_TILED_NV12: + layer->format = DRM_FORMAT_NV12; -+ desc->objects[0].format_modifier = DRM_FORMAT_MOD_ALLWINNER_TILED; ++ mod = DRM_FORMAT_MOD_ALLWINNER_TILED; + break; +#endif +#if defined(V4L2_PIX_FMT_NV15) && defined(DRM_FORMAT_NV15) + case V4L2_PIX_FMT_NV15: + layer->format = DRM_FORMAT_NV15; -+ desc->objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR; + break; +#endif + case V4L2_PIX_FMT_NV16: + layer->format = DRM_FORMAT_NV16; -+ desc->objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR; + break; +#if defined(V4L2_PIX_FMT_NV20) && defined(DRM_FORMAT_NV20) + case V4L2_PIX_FMT_NV20: + layer->format = DRM_FORMAT_NV20; -+ desc->objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR; + break; +#endif + default: + return -1; + } + -+ desc->nb_objects = 1; -+ desc->objects[0].fd = -1; -+ desc->objects[0].size = 0; ++ desc->nb_objects = object_count; ++ for (unsigned int i = 0; i != AV_DRM_MAX_PLANES; ++i) { ++ desc->objects[i].fd = -1; ++ desc->objects[i].size = 0; ++ desc->objects[i].format_modifier = (i >= object_count) ? DRM_FORMAT_MOD_INVALID : mod; ++ } + + desc->nb_layers = 1; + layer->nb_planes = 2; @@ -9397,9 +9412,9 @@ index 000000000000..bb7535a49201 + else +#endif + { -+ layer->planes[1].object_index = 0; -+ layer->planes[1].offset = layer->planes[0].pitch * height; -+ layer->planes[1].pitch = layer->planes[0].pitch; ++ layer->planes[1].object_index = (object_count > 1) ? 1 : 0; ++ layer->planes[1].offset = (object_count > 1) ? 0 : layer->planes[0].pitch * height; ++ layer->planes[1].pitch = bpl2; + } + + return 0; @@ -9498,7 +9513,6 @@ index 000000000000..bb7535a49201 + int rv; + struct slice_info * si; + -+ fprintf(stderr, "<<< %s: boff=%u\n", __func__, boff); + // This looks dodgy but we know that FFmpeg has parsed this from a buffer + // that contains the entire frame including the start code + if (ctx->start_code == V4L2_STATELESS_HEVC_START_CODE_ANNEX_B) { @@ -9511,17 +9525,6 @@ index 000000000000..bb7535a49201 + } + } + -+ if (ctx->decode_mode == V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED) { -+ if (rd->slices == NULL) { -+ if ((rd->slices = av_mallocz(sizeof(*rd->slices))) == NULL) -+ return AVERROR(ENOMEM); -+ rd->slices->ptr = buffer; -+ rd->num_slices = 1; -+ } -+ rd->slices->len = buffer - rd->slices->ptr + size; -+ return 0; -+ } -+ + if ((rv = slice_add(rd)) != 0) + return rv; + @@ -9573,7 +9576,6 @@ index 000000000000..bb7535a49201 +static void v4l2_request_hevc_abort_frame(AVCodecContext * const avctx, V4L2RequestContextHEVC *const ctx) +{ + const HEVCContext * const h = avctx->priv_data; -+ fprintf(stderr, "<<< %s\n", __func__); + if (h->cur_frame != NULL) { + V4L2MediaReqDescriptor *const rd = (V4L2MediaReqDescriptor *)h->cur_frame->f->data[0]; + @@ -9653,9 +9655,6 @@ index 000000000000..bb7535a49201 + struct req_controls rc; + unsigned int i; + int rv; -+ static int z = 0; -+ -+ fprintf(stderr, "<<< %s: %d\n", __func__, ++z); + + // It is possible, though maybe a bug, to get an end_frame without + // a previous start_frame. If we do then give up. @@ -9708,8 +9707,10 @@ index 000000000000..bb7535a49201 + + // Set the drm_prime desriptor + drm_from_format(&rd->drm, mediabufs_dst_fmt(ctx->mbufs)); -+ rd->drm.objects[0].fd = dmabuf_fd(qent_dst_dmabuf(rd->qe_dst, 0)); -+ rd->drm.objects[0].size = dmabuf_size(qent_dst_dmabuf(rd->qe_dst, 0)); ++ for (i = 0; i != rd->drm.nb_objects; ++i) { ++ rd->drm.objects[i].fd = dmabuf_fd(qent_dst_dmabuf(rd->qe_dst, i)); ++ rd->drm.objects[i].size = dmabuf_size(qent_dst_dmabuf(rd->qe_dst, i)); ++ } + + decode_q_remove(&ctx->decode_q, &rd->decode_ent); + return 0; @@ -9768,11 +9769,7 @@ index 000000000000..bb7535a49201 + + mediabufs_ctl_query_ext_ctrls(ctx->mbufs, qc, noof_ctrls); + i = 0; -+#if HEVC_CTRLS_VERSION >= 4 -+ // Skip slice check if no slice mode -+ if (qc[1].type != 0 && !ctrl_valid(qc + 1, V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED)) -+ i = 1; -+#else ++#if HEVC_CTRLS_VERSION < 4 + // Fail frame mode silently for anything prior to V4 + if (qc[1].type == 0 || !ctrl_valid(qc + 1, V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED)) + return AVERROR(EINVAL); @@ -9934,8 +9931,6 @@ index 000000000000..bb7535a49201 + AVHWFramesContext *hwfc = (AVHWFramesContext*)hw_frames_ctx->data; + const struct v4l2_format *vfmt = mediabufs_dst_fmt(ctx->mbufs); + -+ fprintf(stderr, "<<< %s\n", __func__); -+ + hwfc->format = AV_PIX_FMT_DRM_PRIME; + hwfc->sw_format = pixel_format_from_format(vfmt); + if (V4L2_TYPE_IS_MULTIPLANAR(vfmt->type)) { @@ -9974,8 +9969,6 @@ index 000000000000..bb7535a49201 +{ + int rv; + -+ fprintf(stderr, "<<< %s\n", __func__); -+ + frame->buf[0] = v4l2_req_frame_alloc(avctx, sizeof(V4L2MediaReqDescriptor)); + if (!frame->buf[0]) + return AVERROR(ENOMEM); @@ -9983,6 +9976,10 @@ index 000000000000..bb7535a49201 + frame->data[0] = frame->buf[0]->data; + + frame->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx); ++ // Cropping will be applied by hevc_refs.c:ff_hevc_set_new_ref ++ // Mirrors hwaccel path in avcodec_default_get_buffer2 ++ frame->width = avctx->coded_width; ++ frame->height = avctx->coded_height; + + if ((rv = ff_attach_decode_data(frame)) != 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to attach decode data to frame\n"); @@ -10009,10 +10006,10 @@ index 000000000000..bb7535a49201 + diff --git a/libavcodec/v4l2_req_media.c b/libavcodec/v4l2_req_media.c new file mode 100644 -index 000000000000..c94cc5b0f684 +index 000000000000..79dffa2ff97d --- /dev/null +++ b/libavcodec/v4l2_req_media.c -@@ -0,0 +1,1808 @@ +@@ -0,0 +1,1806 @@ +/* + * Copyright (C) 2018 Paul Kocialkowski + * @@ -10838,7 +10835,7 @@ index 000000000000..c94cc5b0f684 +{ + if (!be->dh[0] || len > dmabuf_size(be->dh[0])) { + size_t newsize = round_up_size(len); -+ request_log("%s: Overrun %zd > %zd; trying %zd\n", __func__, len, dmabuf_size(be->dh[0]), newsize); ++ request_debug(NULL, "%s: Overrun %zd > %zd; trying %zd\n", __func__, len, dmabuf_size(be->dh[0]), newsize); + if (!dbsc) { + request_log("%s: No dmbabuf_ctrl for realloc\n", __func__); + return -ENOMEM; @@ -11300,7 +11297,9 @@ index 000000000000..c94cc5b0f684 + + if (create_dst_bufs(mbc, 1, &be_dst) != 1) { + qe_dst_free(be_dst); -+ return NULL; ++ // We can't extend any more - try again but wait ++ mbc->dst_fixed = true; ++ return mediabufs_dst_qent_alloc(mbc, dbsc); + } + } + } @@ -11582,7 +11581,7 @@ index 000000000000..c94cc5b0f684 + return status; +} + -+int mediabufs_ctl_set_ext_ctrls(struct mediabufs_ctl * mbc, struct media_request * const mreq, struct v4l2_ext_control control_array[], unsigned int n) ++int mediabufs_ctl_set_ext_ctrls(struct mediabufs_ctl * mbc, struct media_request * const mreq, struct v4l2_ext_control * const control_array, unsigned int n) +{ + struct v4l2_ext_controls controls = { + .controls = control_array, @@ -11634,7 +11633,7 @@ index 000000000000..c94cc5b0f684 + return rv; +} + -+int mediabufs_ctl_query_ext_ctrls(struct mediabufs_ctl * mbc, struct v4l2_query_ext_ctrl ctrls[], unsigned int n) ++int mediabufs_ctl_query_ext_ctrls(struct mediabufs_ctl * mbc, struct v4l2_query_ext_ctrl * ctrls, unsigned int n) +{ + int rv = 0; + while (n--) { @@ -11655,13 +11654,9 @@ index 000000000000..c94cc5b0f684 + +int mediabufs_src_resizable(const struct mediabufs_ctl *const mbc) +{ -+#if 1 -+ return 0; -+#else + // Single planar OUTPUT can only take exact size buffers + // Multiplanar will take larger than negotiated + return V4L2_TYPE_IS_MULTIPLANAR(mbc->src_fmt.type); -+#endif +} + +static void mediabufs_ctl_delete(struct mediabufs_ctl *const mbc) @@ -11823,10 +11818,10 @@ index 000000000000..c94cc5b0f684 + diff --git a/libavcodec/v4l2_req_media.h b/libavcodec/v4l2_req_media.h new file mode 100644 -index 000000000000..0f1c79fb4ee1 +index 000000000000..ccb291cbf94c --- /dev/null +++ b/libavcodec/v4l2_req_media.h -@@ -0,0 +1,171 @@ +@@ -0,0 +1,176 @@ +/* +e.h +* @@ -11900,6 +11895,11 @@ index 000000000000..0f1c79fb4ee1 +struct dmabuf_h; +struct dmabufs_ctl; + ++struct timeval; ++enum v4l2_buf_type; ++struct v4l2_ext_control; ++struct v4l2_query_ext_ctrl; ++ +// 1-1 mammping to V4L2 type - just defined separetely to avoid some include versioning difficulties +enum mediabufs_memory { + MEDIABUFS_MEMORY_UNSET = 0, @@ -11964,12 +11964,12 @@ index 000000000000..0f1c79fb4ee1 +void mediabufs_src_qent_abort(struct mediabufs_ctl *const mbc, struct qent_src **const pqe_src); + +int mediabufs_ctl_set_ext_ctrls(struct mediabufs_ctl * mbc, struct media_request * const mreq, -+ struct v4l2_ext_control control_array[], unsigned int n); ++ struct v4l2_ext_control * const control_array, unsigned int n); +MediaBufsStatus mediabufs_set_ext_ctrl(struct mediabufs_ctl *const mbc, + struct media_request * const mreq, + unsigned int id, void *data, + unsigned int size); -+int mediabufs_ctl_query_ext_ctrls(struct mediabufs_ctl * mbc, struct v4l2_query_ext_ctrl ctrls[], unsigned int n); ++int mediabufs_ctl_query_ext_ctrls(struct mediabufs_ctl * mbc, struct v4l2_query_ext_ctrl * ctrls, unsigned int n); + +int mediabufs_src_resizable(const struct mediabufs_ctl *const mbc); + @@ -12496,10 +12496,10 @@ index 000000000000..a6160c5e1c3b +#endif diff --git a/libavcodec/v4l2_request_hevc.c b/libavcodec/v4l2_request_hevc.c new file mode 100644 -index 000000000000..94c647380364 +index 000000000000..e1a52f321d99 --- /dev/null +++ b/libavcodec/v4l2_request_hevc.c -@@ -0,0 +1,410 @@ +@@ -0,0 +1,430 @@ +/* + * This file is part of FFmpeg. + * @@ -12526,6 +12526,7 @@ index 000000000000..94c647380364 +#include "hwconfig.h" +#include "internal.h" + ++#include "v4l2_fmt.h" +#include "v4l2_request_hevc.h" + +#include "libavutil/hwcontext_drm.h" @@ -12559,7 +12560,7 @@ index 000000000000..94c647380364 + return bits_alloc; +} + -+static int v4l2_req_hevc_start_frame(AVCodecContext *avctx, ++int ff_v4l2_request_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ @@ -12568,35 +12569,35 @@ index 000000000000..94c647380364 + return ctx->fns->start_frame(avctx, ctx, buffer, size); +} + -+static int v4l2_req_hevc_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) ++int ff_v4l2_request_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) +{ + V4L2RequestPrivHEVC * const priv = avctx->internal->hwaccel_priv_data; + V4L2RequestContextHEVC *const ctx = priv->cctx; + return ctx->fns->decode_slice(avctx, ctx, buffer, size); +} + -+static int v4l2_req_hevc_end_frame(AVCodecContext *avctx) ++int ff_v4l2_request_end_frame(AVCodecContext *avctx) +{ + V4L2RequestPrivHEVC * const priv = avctx->internal->hwaccel_priv_data; + V4L2RequestContextHEVC *const ctx = priv->cctx; + return ctx->fns->end_frame(avctx, ctx); +} + -+static void v4l2_req_hevc_abort_frame(AVCodecContext * const avctx) ++void ff_v4l2_request_abort_frame(AVCodecContext * const avctx) +{ + V4L2RequestPrivHEVC * const priv = avctx->internal->hwaccel_priv_data; + V4L2RequestContextHEVC *const ctx = priv->cctx; + ctx->fns->abort_frame(avctx, ctx); +} + -+static int v4l2_req_hevc_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx) ++int ff_v4l2_request_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx) +{ + V4L2RequestPrivHEVC * const priv = avctx->internal->hwaccel_priv_data; + V4L2RequestContextHEVC *const ctx = priv->cctx; + return ctx->fns->frame_params(avctx, ctx, hw_frames_ctx); +} + -+static int v4l2_req_hevc_alloc_frame(AVCodecContext * avctx, AVFrame *frame) ++int ff_v4l2_request_alloc_frame(AVCodecContext * avctx, AVFrame *frame) +{ + V4L2RequestPrivHEVC * const priv = avctx->internal->hwaccel_priv_data; + V4L2RequestContextHEVC *const ctx = priv->cctx; @@ -12609,6 +12610,9 @@ index 000000000000..94c647380364 +{ + V4L2RequestContextHEVC *const ctx = (V4L2RequestContextHEVC *)data; + ++ if (ctx->fns && ctx->fns->uninit) ++ ctx->fns->uninit(ctx); ++ + mediabufs_ctl_unref(&ctx->mbufs); + media_pool_delete(&ctx->mpool); + pollqueue_unref(&ctx->pq); @@ -12620,16 +12624,18 @@ index 000000000000..94c647380364 + av_free(ctx); +} + -+static int v4l2_request_hevc_uninit(AVCodecContext *avctx) ++int ff_v4l2_request_uninit(AVCodecContext *avctx) +{ + V4L2RequestPrivHEVC * const priv = avctx->internal->hwaccel_priv_data; + + av_log(avctx, AV_LOG_DEBUG, "<<< %s\n", __func__); + -+// decode_q_wait(&ctx->decode_q, NULL); // Wait for all other threads to be out of decode ++ if (priv->cctx != NULL) { ++ decode_q_wait(&priv->cctx->decode_q, NULL); // Wait for all other threads to be out of decode + -+ priv->cctx = NULL; -+ av_buffer_unref(&priv->cctx_buf); ++ priv->cctx = NULL; ++ av_buffer_unref(&priv->cctx_buf); ++ } + +// if (avctx->hw_frames_ctx) { +// AVHWFramesContext *hwfc = (AVHWFramesContext*)avctx->hw_frames_ctx->data; @@ -12640,52 +12646,45 @@ index 000000000000..94c647380364 + +static int dst_fmt_accept_cb(void * v, const struct v4l2_fmtdesc *fmtdesc) +{ -+ AVCodecContext *const avctx = v; -+ const HEVCContext *const h = avctx->priv_data; -+ const HEVCPPS * const pps = h->pps; -+ const HEVCSPS * const sps = pps->sps; ++ const int bit_depth = *(int *)v; + -+ if (sps->bit_depth == 8) { -+ if (fmtdesc->pixelformat == V4L2_PIX_FMT_NV12_COL128 || -+ fmtdesc->pixelformat == V4L2_PIX_FMT_NV12) { -+ return 1; -+ } ++ // SAND is currently confised as to whether it is s/w or hardware ++ // *** Current usage is probably wrong - it shouldn't be a h/w fmt ++ if (fmtdesc->pixelformat == V4L2_PIX_FMT_NV12_COL128 || ++ fmtdesc->pixelformat == V4L2_PIX_FMT_NV12_COL128M) { ++ return bit_depth == 8; + } -+ else if (sps->bit_depth == 10) { -+ if (fmtdesc->pixelformat == V4L2_PIX_FMT_NV12_10_COL128) { -+ return 1; -+ } ++ if (fmtdesc->pixelformat == V4L2_PIX_FMT_NV12_10_COL128 || ++ fmtdesc->pixelformat == V4L2_PIX_FMT_NV12_10_COL128M) { ++ return bit_depth == 10; ++ } ++ else { ++ const enum AVPixelFormat fmt = ff_v4l2_format_v4l2_to_avfmt(fmtdesc->pixelformat, AV_CODEC_ID_RAWVIDEO); ++ const AVPixFmtDescriptor * const desc = av_pix_fmt_desc_get(fmt); ++ ++ if (fmt == AV_PIX_FMT_NONE || desc == NULL) ++ return 0; ++ ++ return bit_depth == desc->comp[0].depth; + } -+ return 0; +} + -+static int v4l2_request_hevc_init(AVCodecContext *avctx) ++int ff_v4l2_request_init(AVCodecContext *avctx, ++ const struct v4l2_req_decode_fns * const * const try_fns, ++ const int width, const int height, const int bit_depth, ++ const int dst_buffers) +{ -+ const HEVCContext *h = avctx->priv_data; + V4L2RequestPrivHEVC * const priv = avctx->internal->hwaccel_priv_data; + V4L2RequestContextHEVC * ctx; -+ const HEVCPPS * const pps = h->pps; -+ const HEVCSPS * const sps = pps->sps; + int ret; + const struct decdev * decdev; -+ const uint32_t src_pix_fmt = V2(ff_v4l2_req_hevc, 4).src_pix_fmt_v4l2; // Assuming constant for all APIs but avoiding V4L2 includes ++ const uint32_t src_pix_fmt = try_fns[0]->src_pix_fmt_v4l2; // Assuming constant for all APIs but avoiding V4L2 includes + size_t src_size; + enum mediabufs_memory src_memtype; + enum mediabufs_memory dst_memtype; + + av_log(avctx, AV_LOG_DEBUG, "<<< %s\n", __func__); + -+ // Give up immediately if this is something that we have no code to deal with -+ if (sps->chroma_format_idc != 1) { -+ av_log(avctx, AV_LOG_WARNING, "chroma_format_idc(%d) != 1: Not implemented\n", sps->chroma_format_idc); -+ return AVERROR_PATCHWELCOME; -+ } -+ if (!(sps->bit_depth == 10 || sps->bit_depth == 8) || -+ sps->bit_depth != sps->bit_depth_chroma) { -+ av_log(avctx, AV_LOG_WARNING, "Bit depth Y:%d C:%d: Not implemented\n", sps->bit_depth, sps->bit_depth_chroma); -+ return AVERROR_PATCHWELCOME; -+ } -+ + if ((ctx = av_mallocz(sizeof(*ctx))) == NULL) { + av_log(avctx, AV_LOG_ERROR, "Unable to allocate context"); + return AVERROR(ENOMEM); @@ -12697,6 +12696,8 @@ index 000000000000..94c647380364 + } + priv->cctx = ctx; + ++ decode_q_init(&ctx->decode_q); ++ + if ((ret = devscan_build(avctx, &ctx->devscan)) != 0) { + av_log(avctx, AV_LOG_WARNING, "Failed to find any V4L2 devices\n"); + ret = AVERROR(-ret); @@ -12706,7 +12707,7 @@ index 000000000000..94c647380364 + + if ((decdev = devscan_find(ctx->devscan, src_pix_fmt)) == NULL) + { -+ av_log(avctx, AV_LOG_WARNING, "Failed to find a V4L2 device for H265\n"); ++ av_log(avctx, AV_LOG_WARNING, "Failed to find a device for %s\n", try_fns[0]->name); + ret = AVERROR(ENODEV); + goto fail0; + } @@ -12751,7 +12752,7 @@ index 000000000000..94c647380364 + // We will realloc if we need more + // Must use sps->h/w as avctx contains cropped size +retry_src_memtype: -+ src_size = bit_buf_size(sps->width, sps->height, sps->bit_depth - 8); ++ src_size = bit_buf_size(width, height, bit_depth - 8); + if (src_memtype == MEDIABUFS_MEMORY_DMABUF && mediabufs_src_resizable(ctx->mbufs)) + src_size /= 4; + // Kludge for conformance tests which break Annex A limits @@ -12759,9 +12760,9 @@ index 000000000000..94c647380364 + src_size = 0x40000; + + if (mediabufs_src_fmt_set(ctx->mbufs, decdev_src_type(decdev), src_pix_fmt, -+ sps->width, sps->height, src_size)) { ++ width, height, src_size)) { + char tbuf1[5]; -+ av_log(avctx, AV_LOG_ERROR, "Failed to set source format: %s %dx%d\n", strfourcc(tbuf1, src_pix_fmt), sps->width, sps->height); ++ av_log(avctx, AV_LOG_ERROR, "Failed to set source format: %s %dx%d\n", strfourcc(tbuf1, src_pix_fmt), width, height); + goto fail4; + } + @@ -12774,28 +12775,19 @@ index 000000000000..94c647380364 + goto fail4; + } + -+ if (V2(ff_v4l2_req_hevc, 4).probe(avctx, ctx) == 0) -+ ctx->fns = &V2(ff_v4l2_req_hevc, 4); -+#if CONFIG_V4L2_REQ_HEVC_VX -+ else if (V2(ff_v4l2_req_hevc, 3).probe(avctx, ctx) == 0) -+ ctx->fns = &V2(ff_v4l2_req_hevc, 3); -+ else if (V2(ff_v4l2_req_hevc, 2).probe(avctx, ctx) == 0) -+ ctx->fns = &V2(ff_v4l2_req_hevc, 2); -+ else if (V2(ff_v4l2_req_hevc, 1).probe(avctx, ctx) == 0) -+ ctx->fns = &V2(ff_v4l2_req_hevc, 1); -+#endif -+ else { -+ av_log(avctx, AV_LOG_ERROR, "No HEVC version probed successfully\n"); ++ for (const struct v4l2_req_decode_fns * const * tf = try_fns; (ctx->fns = *tf) != NULL; ++tf) ++ if (ctx->fns->probe(avctx, ctx) == 0) ++ break; ++ if (ctx->fns == NULL) { ++ av_log(avctx, AV_LOG_ERROR, "No %s version probed successfully\n", try_fns[0]->name); + ret = AVERROR(EINVAL); + goto fail4; + } -+ + av_log(avctx, AV_LOG_DEBUG, "%s probed successfully: driver v %#x\n", + ctx->fns->name, mediabufs_ctl_driver_version(ctx->mbufs)); + -+ if (mediabufs_dst_fmt_set(ctx->mbufs, sps->width, sps->height, dst_fmt_accept_cb, avctx)) { -+ char tbuf1[5]; -+ av_log(avctx, AV_LOG_ERROR, "Failed to set destination format: %s %dx%d\n", strfourcc(tbuf1, src_pix_fmt), sps->width, sps->height); ++ if (mediabufs_dst_fmt_set(ctx->mbufs, width, height, dst_fmt_accept_cb, (void*)&bit_depth)) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to set destination format: %dx%d %dbit\n", width, height, bit_depth); + goto fail4; + } + @@ -12805,10 +12797,10 @@ index 000000000000..94c647380364 + } + + { -+ unsigned int dst_slots = sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering + ++ unsigned int dst_slots = dst_buffers + + avctx->thread_count + (avctx->extra_hw_frames > 0 ? avctx->extra_hw_frames : 6); + av_log(avctx, AV_LOG_DEBUG, "Slots=%d: Reordering=%d, threads=%d, hw+=%d\n", dst_slots, -+ sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering, ++ dst_buffers, + avctx->thread_count, avctx->extra_hw_frames); + + if (mediabufs_dst_chk_memtype(ctx->mbufs, dst_memtype)) { @@ -12842,8 +12834,6 @@ index 000000000000..94c647380364 + goto fail5; + } + -+ decode_q_init(&ctx->decode_q); -+ + // Set our s/w format + avctx->sw_pix_fmt = ((AVHWFramesContext *)avctx->hw_frames_ctx->data)->sw_format; + @@ -12867,8 +12857,8 @@ index 000000000000..94c647380364 + return ret; +} + -+static int -+v4l2_request_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) ++int ++ff_v4l2_request_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) +{ + V4L2RequestPrivHEVC * const spriv = src->internal->hwaccel_priv_data; + V4L2RequestPrivHEVC * const dpriv = dst->internal->hwaccel_priv_data; @@ -12883,10 +12873,40 @@ index 000000000000..94c647380364 + return 0; +} + -+static void -+v4l2_request_free_frame_priv(FFRefStructOpaque hwctx, void *data) ++void ++ff_v4l2_request_free_frame_priv(FFRefStructOpaque hwctx, void *data) +{ -+ fprintf(stderr, "%s\n", __func__); ++} ++ ++static int v4l2_request_hevc_init(AVCodecContext *avctx) ++{ ++ const HEVCContext *h = avctx->priv_data; ++ const HEVCPPS * const pps = h->pps; ++ const HEVCSPS * const sps = pps->sps; ++ ++ const struct v4l2_req_decode_fns * const try_fns[] = { ++ &V2(ff_v4l2_req_hevc, 4), ++#if CONFIG_V4L2_REQ_HEVC_VX ++ &V2(ff_v4l2_req_hevc, 3), ++ &V2(ff_v4l2_req_hevc, 2), ++ &V2(ff_v4l2_req_hevc, 1), ++#endif ++ NULL ++ }; ++ ++ // Give up immediately if this is something that we have no code to deal with ++ if (sps->chroma_format_idc != 1) { ++ av_log(avctx, AV_LOG_WARNING, "chroma_format_idc(%d) != 1: Not implemented\n", sps->chroma_format_idc); ++ return AVERROR_PATCHWELCOME; ++ } ++ if (!(sps->bit_depth == 10 || sps->bit_depth == 8) || ++ sps->bit_depth != sps->bit_depth_chroma) { ++ av_log(avctx, AV_LOG_WARNING, "Bit depth Y:%d C:%d: Not implemented\n", sps->bit_depth, sps->bit_depth_chroma); ++ return AVERROR_PATCHWELCOME; ++ } ++ ++ return ff_v4l2_request_init(avctx, try_fns, sps->width, sps->height, sps->bit_depth, ++ sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering); +} + +const FFHWAccel ff_hevc_v4l2request_hwaccel = { @@ -12896,26 +12916,26 @@ index 000000000000..94c647380364 + .id = AV_CODEC_ID_HEVC, + .pix_fmt = AV_PIX_FMT_DRM_PRIME, + }, -+ .alloc_frame = v4l2_req_hevc_alloc_frame, -+ .start_frame = v4l2_req_hevc_start_frame, -+ .decode_slice = v4l2_req_hevc_decode_slice, -+ .end_frame = v4l2_req_hevc_end_frame, -+ .abort_frame = v4l2_req_hevc_abort_frame, ++ .alloc_frame = ff_v4l2_request_alloc_frame, ++ .start_frame = ff_v4l2_request_start_frame, ++ .decode_slice = ff_v4l2_request_decode_slice, ++ .end_frame = ff_v4l2_request_end_frame, ++ .abort_frame = ff_v4l2_request_abort_frame, + .init = v4l2_request_hevc_init, -+ .uninit = v4l2_request_hevc_uninit, -+ .free_frame_priv = v4l2_request_free_frame_priv, ++ .uninit = ff_v4l2_request_uninit, ++ .free_frame_priv = ff_v4l2_request_free_frame_priv, + .frame_priv_data_size = 128, -+ .update_thread_context = v4l2_request_update_thread_context, ++ .update_thread_context = ff_v4l2_request_update_thread_context, + .priv_data_size = sizeof(V4L2RequestPrivHEVC), -+ .frame_params = v4l2_req_hevc_frame_params, ++ .frame_params = ff_v4l2_request_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE | HWACCEL_CAP_THREAD_SAFE, +}; diff --git a/libavcodec/v4l2_request_hevc.h b/libavcodec/v4l2_request_hevc.h new file mode 100644 -index 000000000000..9b41cbe9ceb3 +index 000000000000..4c145feb37cc --- /dev/null +++ b/libavcodec/v4l2_request_hevc.h -@@ -0,0 +1,131 @@ +@@ -0,0 +1,160 @@ +/* + Copyright (C) 2024 John Cox john.cox@raspberrypi.com + @@ -12945,6 +12965,8 @@ index 000000000000..9b41cbe9ceb3 + +#include +#include ++ ++#include "refstruct.h" +#include "v4l2_req_decode_q.h" + +#ifndef DRM_FORMAT_NV15 @@ -12969,6 +12991,13 @@ index 000000000000..9b41cbe9ceb3 +#define DRM_FORMAT_NV20 fourcc_code('N', 'V', '2', '0') +#endif + ++#ifndef V4L2_PIX_FMT_NV12_COL128M ++#define V4L2_PIX_FMT_NV12_COL128M v4l2_fourcc('N', 'c', '1', '2') /* 12 Y/CbCr 4:2:0 128 pixel wide column */ ++#define V4L2_PIX_FMT_NV12_10_COL128M v4l2_fourcc('N', 'c', '3', '0') ++ /* Y/CbCr 4:2:0 10bpc, 3x10 packed as 4 bytes in ++ * a 128 bytes / 96 pixel wide column */ ++#endif ++ +#include +#ifndef V4L2_CID_CODEC_BASE +#define V4L2_CID_CODEC_BASE V4L2_CID_MPEG_BASE @@ -13016,11 +13045,14 @@ index 000000000000..9b41cbe9ceb3 + struct pollqueue *pq; + struct media_pool * mpool; + struct mediabufs_ctl *mbufs; ++ ++ void * decode_ctx; +} V4L2RequestContextHEVC; + +typedef struct V4L2RequestPrivHEVC { + V4L2RequestContextHEVC * cctx; // Common context + AVBufferRef * cctx_buf; // Buf for cctx ++ int bit_depth; +} V4L2RequestPrivHEVC; + +typedef struct v4l2_req_decode_fns { @@ -13029,8 +13061,12 @@ index 000000000000..9b41cbe9ceb3 + + // Init setup + int (*probe)(AVCodecContext * const avctx, V4L2RequestContextHEVC * const ctx); ++ // Set controls & any other init (e.g. decode_ctx) + int (*set_controls)(AVCodecContext * const avctx, V4L2RequestContextHEVC * const ctx); + ++ // Called on shutdown - avctx may not exist ++ void (*uninit)(V4L2RequestContextHEVC * const ctx); ++ + // Passthrough of hwaccel fns + int (*start_frame)(AVCodecContext *avctx, V4L2RequestContextHEVC *const ctx, const uint8_t *buf, uint32_t buf_size); + int (*decode_slice)(AVCodecContext *avctx, V4L2RequestContextHEVC *const ctx, const uint8_t *buf, uint32_t buf_size); @@ -13040,6 +13076,19 @@ index 000000000000..9b41cbe9ceb3 + int (*alloc_frame)(AVCodecContext * avctx, V4L2RequestContextHEVC *const ctx, AVFrame *frame); +} v4l2_req_decode_fns; + ++int ff_v4l2_request_start_frame(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size); ++int ff_v4l2_request_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size); ++int ff_v4l2_request_end_frame(AVCodecContext *avctx); ++void ff_v4l2_request_abort_frame(AVCodecContext * const avctx); ++int ff_v4l2_request_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); ++int ff_v4l2_request_alloc_frame(AVCodecContext * avctx, AVFrame *frame); ++int ff_v4l2_request_init(AVCodecContext *avctx, ++ const struct v4l2_req_decode_fns * const * const try_fns, ++ const int width, const int height, const int bit_depth, ++ const int dst_buffers); ++int ff_v4l2_request_uninit(AVCodecContext *avctx); ++int ff_v4l2_request_update_thread_context(AVCodecContext *dst, const AVCodecContext *src); ++void ff_v4l2_request_free_frame_priv(FFRefStructOpaque hwctx, void *data); + +extern const v4l2_req_decode_fns V2(ff_v4l2_req_hevc, 1); +extern const v4l2_req_decode_fns V2(ff_v4l2_req_hevc, 2); @@ -17279,6 +17328,309 @@ index 000000000000..67750e4f12b8 + FILTER_OUTPUTS(avfilter_vf_unsand_outputs), +}; + +diff --git a/libavformat/Makefile b/libavformat/Makefile +index 7ca68a703694..d267813a7ecd 100644 +--- a/libavformat/Makefile ++++ b/libavformat/Makefile +@@ -171,6 +171,7 @@ OBJS-$(CONFIG_CODEC2_MUXER) += codec2.o rawenc.o + OBJS-$(CONFIG_CODEC2RAW_DEMUXER) += codec2.o pcm.o + OBJS-$(CONFIG_CODEC2RAW_MUXER) += rawenc.o + OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o ++OBJS-$(CONFIG_CONFORM_MUXER) += conformenc.o + OBJS-$(CONFIG_CRC_MUXER) += crcenc.o + OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o + OBJS-$(CONFIG_DATA_MUXER) += rawenc.o +diff --git a/libavformat/allformats.c b/libavformat/allformats.c +index 305fa4653232..34e993280d14 100644 +--- a/libavformat/allformats.c ++++ b/libavformat/allformats.c +@@ -125,6 +125,7 @@ extern const FFOutputFormat ff_codec2_muxer; + extern const FFInputFormat ff_codec2raw_demuxer; + extern const FFOutputFormat ff_codec2raw_muxer; + extern const FFInputFormat ff_concat_demuxer; ++extern const FFOutputFormat ff_conform_muxer; + extern const FFOutputFormat ff_crc_muxer; + extern const FFInputFormat ff_dash_demuxer; + extern const FFOutputFormat ff_dash_muxer; +diff --git a/libavformat/conformenc.c b/libavformat/conformenc.c +new file mode 100644 +index 000000000000..1ea7925eaa63 +--- /dev/null ++++ b/libavformat/conformenc.c +@@ -0,0 +1,273 @@ ++/* ++ * Copyright (c) 2020 John Cox for Raspberry Pi Trading ++ * ++ * This file is part of FFmpeg. ++ * ++ * FFmpeg is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * FFmpeg is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with FFmpeg; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++ ++// *** This module is a work in progress and its utility is strictly ++// limited to testing. ++ ++#include "libavutil/opt.h" ++#include "libavutil/frame.h" ++#include "libavutil/md5.h" ++#include "libavutil/mem.h" ++#include "libavutil/pixdesc.h" ++#include "libavutil/hwcontext.h" ++#include "libavutil/hwcontext_drm.h" ++#include "mux.h" ++ ++#include "pthread.h" ++#include ++#include ++ ++#define TRACE_ALL 1 ++ ++#define DRM_MODULE "vc4" ++ ++// Aux size should only need to be 2, but on a few streams (Hobbit) under FKMS ++// we get initial flicker probably due to dodgy drm timing ++#define AUX_SIZE 3 ++typedef struct conform_display_env_s ++{ ++ AVClass *class; ++ ++ void * line_buf; ++ size_t line_size; ++ ++ struct AVMD5 * frame_md5; ++ struct AVMD5 * md5; ++ ++ int conform_file; ++ int frame_md5_flag; ++ ++ unsigned long long foffset; ++ unsigned int fno; ++} conform_display_env_t; ++ ++ ++static int conform_vout_write_trailer(AVFormatContext *s) ++{ ++ conform_display_env_t * const de = s->priv_data; ++ ++#if TRACE_ALL ++ av_log(s, AV_LOG_DEBUG, "%s\n", __func__); ++#endif ++ ++ if (de->md5) { ++ uint8_t m[16]; ++ av_md5_final(de->md5, m); ++ avio_printf(s->pb, "MD5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", ++ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]); ++ } ++ ++ return 0; ++} ++ ++static int conform_vout_write_header(AVFormatContext *s) ++{ ++ conform_display_env_t * const de = s->priv_data; ++ ++#if TRACE_ALL ++ av_log(s, AV_LOG_DEBUG, "%s\n", __func__); ++#endif ++ ++ if (de->md5) ++ av_md5_init(de->md5); ++ de->fno = 1; ++ de->foffset = 0; ++ ++ return 0; ++} ++ ++static int conform_vout_write_packet(AVFormatContext *s, AVPacket *pkt) ++{ ++ const AVFrame * const sf = (AVFrame *)pkt->data; ++ AVFrame * cf = NULL; ++ const AVFrame * f = sf; ++ ++ conform_display_env_t * const de = s->priv_data; ++ const AVPixFmtDescriptor * pix_desc = av_pix_fmt_desc_get(sf->format); ++ int is_hw = (pix_desc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0; ++ enum AVPixelFormat fmt = is_hw ? AV_PIX_FMT_NONE : sf->format; ++ unsigned int i; ++ ++ if (is_hw) { ++ enum AVPixelFormat *xfmts = NULL; ++ av_hwframe_transfer_get_formats(sf->hw_frames_ctx, AV_HWFRAME_TRANSFER_DIRECTION_FROM, &xfmts, 0); ++ fmt = *xfmts; ++ av_free(xfmts); ++ } ++ ++ av_log(s, AV_LOG_DEBUG, "%s: Frame %3d: %#08llx %dx%d crop(ltrb) %zd,%zd,%zd,%zd fmt %s -> %s\n", __func__, ++ de->fno, de->foffset, ++ sf->width, sf->height, sf->crop_left, sf->crop_top, sf->crop_right, sf->crop_bottom, ++ av_get_pix_fmt_name(sf->format), av_get_pix_fmt_name(fmt)); ++ ++ if ((sf->flags & AV_FRAME_FLAG_CORRUPT) != 0) { ++ av_log(s, AV_LOG_WARNING, "Discard corrupt frame: fmt=%d, ts=%" PRId64 "\n", sf->format, sf->pts); ++ return 0; ++ } ++ ++ if (!is_hw) ++ f = sf; ++ else ++ { ++ cf = av_frame_alloc(); ++ cf->format = fmt; ++ av_hwframe_transfer_data(cf, sf, AV_HWFRAME_TRANSFER_DIRECTION_FROM); ++ pix_desc = av_pix_fmt_desc_get(cf->format); ++ f = cf; ++ } ++ ++ if (de->frame_md5) ++ av_md5_init(de->frame_md5); ++ ++ // This is fully generic - much optimisation possible ++ for (i = 0; i != pix_desc->nb_components; ++i) { ++ const AVComponentDescriptor * const cd = pix_desc->comp + i; ++ const unsigned int srw = ((i == 1 || i == 2) ? pix_desc->log2_chroma_w : 0); ++ const unsigned int rndw = (1 << srw) - 1; ++ const unsigned int srh = ((i == 1 || i == 2) ? pix_desc->log2_chroma_h : 0); ++ const unsigned int rndh = (1 << srh) - 1; ++ const unsigned int srp = cd->shift; ++ const unsigned int bpp = cd->depth > 8 ? 2 : 1; ++ const unsigned int h = (f->height - (f->crop_top + f->crop_bottom) + rndh) >> srh; ++ unsigned int y; ++ for (y = 0; y < h; ++y) { ++ const void *const lstart = f->data[cd->plane] + (y + (f->crop_top >> srh)) * f->linesize[cd->plane] + cd->offset + (f->crop_left >> srw) * cd->step; ++ const unsigned int w = (f->width - (f->crop_left + f->crop_right) + rndw) >> srw; ++ unsigned int x; ++ if (bpp == 1) { ++ uint8_t *d = de->line_buf; ++ const uint8_t *s = lstart; ++ for (x = 0; x != w; ++x) { ++ *d++ = *s >> srp; ++ s += cd->step; ++ } ++ } ++ else { ++ uint16_t *d = de->line_buf; ++ const uint8_t *s = lstart; ++ for (x = 0; x != w; ++x) { ++ *d++ = *(uint16_t*)s >> srp; ++ s += cd->step; ++ } ++ } ++ ++ // We have one line ++ ++ if (de->frame_md5) ++ av_md5_update(de->frame_md5, de->line_buf, w * bpp); ++ if (de->md5) ++ av_md5_update(de->md5, de->line_buf, w * bpp); ++ else { ++ avio_write(s->pb, de->line_buf, w * bpp); ++ de->foffset += w * bpp; ++ } ++ } ++ } ++ ++ if (de->frame_md5) { ++ uint8_t m[16]; ++ av_md5_final(de->frame_md5, m); ++ avio_printf(s->pb, "MD5-Frame-%d=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", ++ de->fno, ++ m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9], m[10], m[11], m[12], m[13], m[14], m[15]); ++ } ++ ++de->fno; ++ ++ av_frame_free(&cf); ++ ++ return 0; ++} ++ ++static int conform_vout_write_frame(AVFormatContext *s, int stream_index, AVFrame **ppframe, ++ unsigned flags) ++{ ++ av_log(s, AV_LOG_ERROR, "%s: NIF: idx=%d, flags=%#x\n", __func__, stream_index, flags); ++ return AVERROR_PATCHWELCOME; ++} ++ ++// deinit is called if init fails so no need to clean up explicity here ++static int conform_vout_init(struct AVFormatContext * s) ++{ ++ conform_display_env_t * const de = s->priv_data; ++ ++ av_log(s, AV_LOG_DEBUG, "<<< %s\n", __func__); ++ ++ de->line_size = (8192 * 4); // 4bpp * 8k seems plenty ++ de->line_buf = av_malloc(de->line_size); ++ ++ if (!de->conform_file) { ++ de->md5 = av_md5_alloc(); ++ if (de->frame_md5_flag) ++ de->frame_md5 = av_md5_alloc(); ++ } ++ ++ av_log(s, AV_LOG_DEBUG, ">>> %s\n", __func__); ++ ++ return 0; ++} ++ ++static void conform_vout_deinit(struct AVFormatContext * s) ++{ ++ conform_display_env_t * const de = s->priv_data; ++ ++ av_log(s, AV_LOG_DEBUG, "<<< %s\n", __func__); ++ ++ av_freep(&de->line_buf); ++ av_freep(&de->md5); ++ av_freep(&de->frame_md5); ++ ++ av_log(s, AV_LOG_DEBUG, ">>> %s\n", __func__); ++} ++ ++ ++#define OFFSET(x) offsetof(conform_display_env_t, x) ++static const AVOption options[] = { ++ { "conform_frame_md5", "Produce per-frame MD5s as well as final", OFFSET(frame_md5_flag), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "conform_yuv", "Output yuv file rather than md5", OFFSET(conform_file), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { NULL } ++}; ++ ++static const AVClass conform_vid_class = { ++ .class_name = "conform vid muxer", ++ .item_name = av_default_item_name, ++ .option = options, ++ .version = LIBAVUTIL_VERSION_INT, ++ .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT, ++}; ++ ++const FFOutputFormat ff_conform_muxer = { ++ .p = { ++ .name = "conform", ++ .long_name = NULL_IF_CONFIG_SMALL("Video out conformance test helper"), ++ .audio_codec = AV_CODEC_ID_NONE, ++ .video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, ++ .flags = AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS, ++ .priv_class = &conform_vid_class, ++ }, ++ .priv_data_size = sizeof(conform_display_env_t), ++ .write_header = conform_vout_write_header, ++ .write_packet = conform_vout_write_packet, ++ .write_uncoded_frame = conform_vout_write_frame, ++ .write_trailer = conform_vout_write_trailer, ++ .init = conform_vout_init, ++ .deinit = conform_vout_deinit, ++}; ++ diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 60c896e964eb..3967be997e86 100644 --- a/libavformat/matroskaenc.c @@ -19245,7 +19597,7 @@ index 000000000000..d8126676ee0c +#endif // AVUTIL_ARM_SAND_NEON_H + diff --git a/libavutil/frame.c b/libavutil/frame.c -index f0a0dba018a6..38458696c6ae 100644 +index f0a0dba018a6..cc14af82d600 100644 --- a/libavutil/frame.c +++ b/libavutil/frame.c @@ -16,6 +16,8 @@ @@ -19267,13 +19619,14 @@ index f0a0dba018a6..38458696c6ae 100644 static const AVSideDataDescriptor sd_props[] = { [AV_FRAME_DATA_PANSCAN] = { "AVPanScan" }, -@@ -1077,6 +1082,12 @@ int av_frame_apply_cropping(AVFrame *frame, int flags) +@@ -1077,6 +1082,13 @@ int av_frame_apply_cropping(AVFrame *frame, int flags) (frame->crop_top + frame->crop_bottom) >= frame->height) return AVERROR(ERANGE); +#if CONFIG_SAND + // Sand cannot be cropped - do not try -+ if (av_rpi_is_sand_format(frame->format)) ++ // If it might be encapsulated in DRM_PRIME don't crop that either ++ if (av_rpi_is_sand_format(frame->format) || frame->format == AV_PIX_FMT_DRM_PRIME) + return 0; +#endif + @@ -19302,7 +19655,7 @@ index f7806566d54c..00c5c925e31d 100644 * @return side data descriptor corresponding to a given side data type, NULL * when not available. diff --git a/libavutil/hwcontext_drm.c b/libavutil/hwcontext_drm.c -index 0847db09a08b..6365b7cf211b 100644 +index 0847db09a08b..73481d544e69 100644 --- a/libavutil/hwcontext_drm.c +++ b/libavutil/hwcontext_drm.c @@ -21,6 +21,7 @@ @@ -19352,14 +19705,11 @@ index 0847db09a08b..6365b7cf211b 100644 #if HAVE_LINUX_DMA_BUF_H if (flags & AV_HWFRAME_MAP_READ) map->sync_flags |= DMA_BUF_SYNC_READ; -@@ -186,6 +198,23 @@ static int drm_map_frame(AVHWFramesContext *hwfc, +@@ -186,12 +198,34 @@ static int drm_map_frame(AVHWFramesContext *hwfc, dst->width = src->width; dst->height = src->height; -+ dst->crop_top = src->crop_top; -+ dst->crop_bottom = src->crop_bottom; -+ dst->crop_left = src->crop_left; -+ dst->crop_right = src->crop_right; ++ // Crop copied with props + +#if CONFIG_SAND + // Rework for sand frames @@ -19367,16 +19717,31 @@ index 0847db09a08b..6365b7cf211b 100644 + // As it stands the sand formats hold stride2 in linesize[3] + // linesize[0] & [1] contain stride1 which is always 128 for everything we do + // * Arguably this should be reworked s.t. stride2 is in linesize[0] & [1] -+ dst->linesize[3] = fourcc_mod_broadcom_param(desc->objects[0].format_modifier); ++ int mod_stride = fourcc_mod_broadcom_param(desc->objects[0].format_modifier); ++ if (mod_stride == 0) { ++ dst->linesize[3] = dst->linesize[0]; ++ dst->linesize[4] = dst->linesize[1]; ++ } ++ else { ++ dst->linesize[3] = mod_stride; ++ dst->linesize[4] = mod_stride; ++ } + dst->linesize[0] = 128; + dst->linesize[1] = 128; -+ // *** Are we sure src->height is actually what we want ??? + } +#endif err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, - &drm_unmap_frame, map); -@@ -207,16 +236,29 @@ static int drm_transfer_get_formats(AVHWFramesContext *ctx, +- &drm_unmap_frame, map); ++ drm_unmap_frame, map); + if (err < 0) + goto fail; + ++ av_frame_copy_props(dst, src); + return 0; + + fail: +@@ -207,16 +241,29 @@ static int drm_transfer_get_formats(AVHWFramesContext *ctx, enum AVHWFrameTransferDirection dir, enum AVPixelFormat **formats) { @@ -19412,7 +19777,7 @@ index 0847db09a08b..6365b7cf211b 100644 return 0; } -@@ -232,18 +274,62 @@ static int drm_transfer_data_from(AVHWFramesContext *hwfc, +@@ -232,18 +279,62 @@ static int drm_transfer_data_from(AVHWFramesContext *hwfc, map = av_frame_alloc(); if (!map) return AVERROR(ENOMEM); @@ -19441,8 +19806,6 @@ index 0847db09a08b..6365b7cf211b 100644 +#endif +#if CONFIG_SAND + if (av_rpi_is_sand_frame(map)) { -+ // Preserve crop - later ffmpeg code assumes that we have in that it -+ // overwrites any crop that we create with the old values + const unsigned int w = FFMIN(dst->width, map->width); + const unsigned int h = FFMIN(dst->height, map->height); + @@ -19460,15 +19823,17 @@ index 0847db09a08b..6365b7cf211b 100644 + + dst->width = w; + dst->height = h; ++ // Cropping restored as part of props + } + else +#endif + { -+ // Kludge mapped h/w s.t. frame_copy works -+ map->width = dst->width; -+ map->height = dst->height; ++ dst->width = map->width; ++ dst->height = map->height; + err = av_frame_copy(dst, map); + } ++ ++ av_frame_copy_props(dst, src); - err = av_frame_copy(dst, map); if (err) @@ -19479,7 +19844,7 @@ index 0847db09a08b..6365b7cf211b 100644 err = 0; fail: -@@ -258,7 +344,10 @@ static int drm_transfer_data_to(AVHWFramesContext *hwfc, +@@ -258,7 +349,10 @@ static int drm_transfer_data_to(AVHWFramesContext *hwfc, int err; if (src->width > hwfc->width || src->height > hwfc->height) @@ -19818,10 +20183,10 @@ index 000000000000..0d5d203dc3cd + diff --git a/libavutil/rpi_sand_fns.c b/libavutil/rpi_sand_fns.c new file mode 100644 -index 000000000000..2e19dd3a7b84 +index 000000000000..5d3ea4db1e33 --- /dev/null +++ b/libavutil/rpi_sand_fns.c -@@ -0,0 +1,447 @@ +@@ -0,0 +1,449 @@ +/* +Copyright (c) 2018 Raspberry Pi (Trading) Ltd. +All rights reserved. @@ -20183,6 +20548,8 @@ index 000000000000..2e19dd3a7b84 + const int h = av_frame_cropped_height(src); + const int x = src->crop_left; + const int y = src->crop_top; ++ const unsigned int stride2_y = av_rpi_sand_frame_stride2_y(src); ++ const unsigned int stride2_c = av_rpi_sand_frame_stride2_c(src); + + // We will crop as part of the conversion + dst->crop_top = 0; @@ -20197,22 +20564,22 @@ index 000000000000..2e19dd3a7b84 + case AV_PIX_FMT_YUV420P: + av_rpi_sand_to_planar_y8(dst->data[0], dst->linesize[0], + src->data[0], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_y, + x, y, w, h); + av_rpi_sand_to_planar_c8(dst->data[1], dst->linesize[1], + dst->data[2], dst->linesize[2], + src->data[1], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_c, + x/2, y/2, w/2, h/2); + break; + case AV_PIX_FMT_NV12: + av_rpi_sand_to_planar_y8(dst->data[0], dst->linesize[0], + src->data[0], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_y, + x, y, w, h); + av_rpi_sand_to_planar_y8(dst->data[1], dst->linesize[1], + src->data[1], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_c, + x/2, y/2, w, h/2); + break; + default: @@ -20224,12 +20591,12 @@ index 000000000000..2e19dd3a7b84 + case AV_PIX_FMT_YUV420P10: + av_rpi_sand_to_planar_y16(dst->data[0], dst->linesize[0], + src->data[0], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_y, + x*2, y, w*2, h); + av_rpi_sand_to_planar_c16(dst->data[1], dst->linesize[1], + dst->data[2], dst->linesize[2], + src->data[1], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_c, + x, y/2, w, h/2); + break; + default: @@ -20241,22 +20608,22 @@ index 000000000000..2e19dd3a7b84 + case AV_PIX_FMT_YUV420P10: + av_rpi_sand30_to_planar_y16(dst->data[0], dst->linesize[0], + src->data[0], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_y, + x, y, w, h); + av_rpi_sand30_to_planar_c16(dst->data[1], dst->linesize[1], + dst->data[2], dst->linesize[2], + src->data[1], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_c, + x/2, y/2, w/2, h/2); + break; + case AV_PIX_FMT_NV12: + av_rpi_sand30_to_planar_y8(dst->data[0], dst->linesize[0], + src->data[0], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_y, + x, y, w, h); + av_rpi_sand30_to_planar_y8(dst->data[1], dst->linesize[1], + src->data[1], -+ av_rpi_sand_frame_stride1(src), av_rpi_sand_frame_stride2(src), ++ av_rpi_sand_frame_stride1(src), stride2_c, + x/2, y/2, w, h/2); + break; + default: @@ -20271,10 +20638,10 @@ index 000000000000..2e19dd3a7b84 +} diff --git a/libavutil/rpi_sand_fns.h b/libavutil/rpi_sand_fns.h new file mode 100644 -index 000000000000..f7ba62ff7380 +index 000000000000..c4031fcb690d --- /dev/null +++ b/libavutil/rpi_sand_fns.h -@@ -0,0 +1,188 @@ +@@ -0,0 +1,157 @@ +/* +Copyright (c) 2018 Raspberry Pi (Trading) Ltd. +All rights reserved. @@ -20390,11 +20757,15 @@ index 000000000000..f7ba62ff7380 +#endif +} + -+static inline unsigned int av_rpi_sand_frame_stride2(const AVFrame * const frame) ++static inline unsigned int av_rpi_sand_frame_stride2_y(const AVFrame * const frame) +{ + return frame->linesize[3]; +} + ++static inline unsigned int av_rpi_sand_frame_stride2_c(const AVFrame * const frame) ++{ ++ return frame->linesize[4]; ++} + +static inline int av_rpi_is_sand_format(const int format) +{ @@ -20426,41 +20797,6 @@ index 000000000000..f7ba62ff7380 + return av_rpi_is_sand8_frame(frame) ? 0 : 1; +} + -+// If x is measured in bytes (not pixels) then this works for sand64_16 as -+// well as sand128 - but in the general case we work that out -+ -+static inline unsigned int av_rpi_sand_frame_off_y(const AVFrame * const frame, const unsigned int x_y, const unsigned int y) -+{ -+ const unsigned int stride1 = av_rpi_sand_frame_stride1(frame); -+ const unsigned int stride2 = av_rpi_sand_frame_stride2(frame); -+ const unsigned int x = x_y << av_rpi_sand_frame_xshl(frame); -+ const unsigned int x1 = x & (stride1 - 1); -+ const unsigned int x2 = x ^ x1; -+ -+ return x1 + stride1 * y + stride2 * x2; -+} -+ -+static inline unsigned int av_rpi_sand_frame_off_c(const AVFrame * const frame, const unsigned int x_c, const unsigned int y_c) -+{ -+ const unsigned int stride1 = av_rpi_sand_frame_stride1(frame); -+ const unsigned int stride2 = av_rpi_sand_frame_stride2(frame); -+ const unsigned int x = x_c << (av_rpi_sand_frame_xshl(frame) + 1); -+ const unsigned int x1 = x & (stride1 - 1); -+ const unsigned int x2 = x ^ x1; -+ -+ return x1 + stride1 * y_c + stride2 * x2; -+} -+ -+static inline uint8_t * av_rpi_sand_frame_pos_y(const AVFrame * const frame, const unsigned int x, const unsigned int y) -+{ -+ return frame->data[0] + av_rpi_sand_frame_off_y(frame, x, y); -+} -+ -+static inline uint8_t * av_rpi_sand_frame_pos_c(const AVFrame * const frame, const unsigned int x, const unsigned int y) -+{ -+ return frame->data[1] + av_rpi_sand_frame_off_c(frame, x, y); -+} -+ +#endif + diff --git a/libswscale/aarch64/rgb2rgb.c b/libswscale/aarch64/rgb2rgb.c @@ -22556,10 +22892,10 @@ index 000000000000..0dbaa53e97e0 +# -Wa,-ahls diff --git a/pi-util/ffconf.py b/pi-util/ffconf.py new file mode 100755 -index 000000000000..573f1e03c0c0 +index 000000000000..02af4583aeea --- /dev/null +++ b/pi-util/ffconf.py -@@ -0,0 +1,276 @@ +@@ -0,0 +1,268 @@ +#!/usr/bin/env python3 + +import string @@ -22587,14 +22923,6 @@ index 000000000000..573f1e03c0c0 + valgrind = args.valgrind + rv = 0 + -+ pix_fmt = [] -+ if pix == "8": -+ pix_fmt = ["-pix_fmt", "yuv420p"] -+ elif pix == "10": -+ pix_fmt = ["-pix_fmt", "yuv420p10le"] -+ elif pix == "12": -+ pix_fmt = ["-pix_fmt", "yuv420p12le"] -+ + tmp_root = "/tmp" + + names = srcname.split('/') @@ -22621,10 +22949,10 @@ index 000000000000..573f1e03c0c0 + flog = open(os.path.join(tmp_root, name + ".log"), "w+t") + + ffargs = [ffmpeg_exec, "-flags", "unaligned"] +\ ++ ["-no_cvt_hw"] +\ + (["-hwaccel", dectype.hwaccel] if dectype.hwaccel else []) +\ + ["-vcodec", "hevc", "-i", os.path.join(fileroot, es_file)] +\ -+ pix_fmt +\ -+ ([yuv_file] if gen_yuv else ["-f", "md5", dec_file]) ++ (["-conform_yuv", "1", "-f", "conform", yuv_file] if gen_yuv else ["-f", "conform", dec_file]) + + if valgrind: + ffargs = ['valgrind', '--leak-check=full'] + ffargs @@ -22805,7 +23133,7 @@ index 000000000000..573f1e03c0c0 + dectype = None + if os.path.exists("/dev/rpivid-hevcmem"): + dectype = hwaccel_rpi -+ if os.path.exists("/sys/module/rpivid_hevc"): ++ if args.drm or os.path.exists("/sys/module/rpivid_hevc") or os.path.exists("/sys/module/rpi_hevc_dec"): + dectype = hwaccel_drm + + if args.pi4: