+ err = 0;
+fail:
+ drmModeFreeFB(fb);
+ return err;
+}
+
+#if HAVE_LIBDRM_GETFB2
+static int kmsgrab_get_fb2(AVFormatContext *avctx,
+ drmModePlane *plane,
+ AVDRMFrameDescriptor *desc)
+{
+ KMSGrabContext *ctx = avctx->priv_data;
+ drmModeFB2 *fb;
+ int err, i, nb_objects;
+ uint64_t modifier = ctx->drm_format_modifier;
+
+ fb = drmModeGetFB2(ctx->hwctx->fd, plane->fb_id);
+ if (!fb) {
+ err = errno;
+ av_log(avctx, AV_LOG_ERROR, "Failed to get framebuffer "
+ "%"PRIu32": %s.\n", plane->fb_id, strerror(err));
+ return AVERROR(err);
+ }
+ if (fb->pixel_format != ctx->drm_format) {
+ av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" framebuffer "
+ "format changed: now %"PRIx32".\n",
+ ctx->plane_id, fb->pixel_format);
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ if (fb->width != ctx->width || fb->height != ctx->height) {
+ av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" framebuffer "
+ "dimensions changed: now %"PRIu32"x%"PRIu32".\n",
+ ctx->plane_id, fb->width, fb->height);
+ err = AVERROR(EIO);
+ goto fail;
+ }
+ if (!fb->handles[0]) {
+ av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer.\n");
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ if (fb->flags & DRM_MODE_FB_MODIFIERS)
+ modifier = fb->modifier;
+
+ *desc = (AVDRMFrameDescriptor) {
+ .nb_layers = 1,
+ .layers[0] = {
+ .format = ctx->drm_format,
+ },
+ };
+
+ nb_objects = 0;
+ for (i = 0; i < 4 && fb->handles[i]; i++) {
+ size_t size;
+ int dup = 0, j, obj;
+
+ size = fb->offsets[i] + fb->height * fb->pitches[i];
+
+ for (j = 0; j < i; j++) {
+ if (fb->handles[i] == fb->handles[j]) {
+ dup = 1;
+ break;
+ }
+ }
+ if (dup) {
+ obj = desc->layers[0].planes[j].object_index;
+
+ if (desc->objects[j].size < size)
+ desc->objects[j].size = size;
+
+ desc->layers[0].planes[i] = (AVDRMPlaneDescriptor) {
+ .object_index = obj,
+ .offset = fb->offsets[i],
+ .pitch = fb->pitches[i],
+ };
+
+ } else {
+ int fd;
+ err = drmPrimeHandleToFD(ctx->hwctx->fd, fb->handles[i],
+ O_RDONLY, &fd);
+ if (err < 0) {
+ err = errno;
+ av_log(avctx, AV_LOG_ERROR, "Failed to get PRIME fd from "
+ "framebuffer handle: %s.\n", strerror(err));
+ err = AVERROR(err);
+ goto fail;
+ }
+
+ obj = nb_objects++;
+ desc->objects[obj] = (AVDRMObjectDescriptor) {
+ .fd = fd,
+ .size = size,
+ .format_modifier = modifier,
+ };
+ desc->layers[0].planes[i] = (AVDRMPlaneDescriptor) {
+ .object_index = obj,
+ .offset = fb->offsets[i],
+ .pitch = fb->pitches[i],
+ };
+ }
+ }
+ desc->nb_objects = nb_objects;
+ desc->layers[0].nb_planes = i;
+
+ err = 0;
+fail:
+ drmModeFreeFB2(fb);
+ return err;
+}
+#endif
+
+static int kmsgrab_read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+ KMSGrabContext *ctx = avctx->priv_data;
+ drmModePlane *plane = NULL;
+ AVDRMFrameDescriptor *desc = NULL;
+ AVFrame *frame = NULL;
+ int64_t now;
+ int err;
+
+ now = av_gettime_relative();
+ if (ctx->frame_last) {
+ int64_t delay;
+ while (1) {
+ delay = ctx->frame_last + ctx->frame_delay - now;
+ if (delay <= 0)
+ break;
+ av_usleep(delay);
+ now = av_gettime_relative();
+ }
+ }
+ ctx->frame_last = now;
+ now = av_gettime();
+
+ plane = drmModeGetPlane(ctx->hwctx->fd, ctx->plane_id);
+ if (!plane) {
+ err = errno;
+ av_log(avctx, AV_LOG_ERROR, "Failed to get plane "
+ "%"PRIu32": %s.\n", ctx->plane_id, strerror(err));
+ err = AVERROR(err);
+ goto fail;
+ }
+ if (!plane->fb_id) {
+ av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" no longer has "
+ "an associated framebuffer.\n", ctx->plane_id);
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ desc = av_mallocz(sizeof(*desc));
+ if (!desc) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+#if HAVE_LIBDRM_GETFB2
+ if (ctx->fb2_available)
+ err = kmsgrab_get_fb2(avctx, plane, desc);
+ else
+#endif
+ err = kmsgrab_get_fb(avctx, plane, desc);
+ if (err < 0)
+ goto fail;
+