|
|
|
@@ -27,6 +27,11 @@ |
|
|
|
#include <xf86drm.h> |
|
|
|
#include <xf86drmMode.h> |
|
|
|
|
|
|
|
// Required for compatibility when building against libdrm < 2.4.83. |
|
|
|
#ifndef DRM_FORMAT_MOD_INVALID |
|
|
|
#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1) |
|
|
|
#endif |
|
|
|
|
|
|
|
#include "libavutil/hwcontext.h" |
|
|
|
#include "libavutil/hwcontext_drm.h" |
|
|
|
#include "libavutil/internal.h" |
|
|
|
@@ -45,6 +50,7 @@ typedef struct KMSGrabContext { |
|
|
|
AVBufferRef *device_ref; |
|
|
|
AVHWDeviceContext *device; |
|
|
|
AVDRMDeviceContext *hwctx; |
|
|
|
int fb2_available; |
|
|
|
|
|
|
|
AVBufferRef *frames_ref; |
|
|
|
AVHWFramesContext *frames; |
|
|
|
@@ -68,8 +74,10 @@ typedef struct KMSGrabContext { |
|
|
|
static void kmsgrab_free_desc(void *opaque, uint8_t *data) |
|
|
|
{ |
|
|
|
AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)data; |
|
|
|
int i; |
|
|
|
|
|
|
|
close(desc->objects[0].fd); |
|
|
|
for (i = 0; i < desc->nb_objects; i++) |
|
|
|
close(desc->objects[i].fd); |
|
|
|
|
|
|
|
av_free(desc); |
|
|
|
} |
|
|
|
@@ -144,6 +152,116 @@ fail: |
|
|
|
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; |
|
|
|
|
|
|
|
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->modifier != ctx->drm_format_modifier) { |
|
|
|
av_log(avctx, AV_LOG_ERROR, "Plane %"PRIu32" framebuffer " |
|
|
|
"format modifier changed: now %"PRIx64".\n", |
|
|
|
ctx->plane_id, fb->modifier); |
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
*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 = fb->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; |
|
|
|
@@ -187,7 +305,12 @@ static int kmsgrab_read_packet(AVFormatContext *avctx, AVPacket *pkt) |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
err = kmsgrab_get_fb(avctx, plane, desc); |
|
|
|
#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; |
|
|
|
|
|
|
|
@@ -281,6 +404,9 @@ static av_cold int kmsgrab_read_header(AVFormatContext *avctx) |
|
|
|
drmModePlaneRes *plane_res = NULL; |
|
|
|
drmModePlane *plane = NULL; |
|
|
|
drmModeFB *fb = NULL; |
|
|
|
#if HAVE_LIBDRM_GETFB2 |
|
|
|
drmModeFB2 *fb2 = NULL; |
|
|
|
#endif |
|
|
|
AVStream *stream; |
|
|
|
int err, i; |
|
|
|
|
|
|
|
@@ -383,28 +509,79 @@ static av_cold int kmsgrab_read_header(AVFormatContext *avctx) |
|
|
|
|
|
|
|
ctx->plane_id = plane->plane_id; |
|
|
|
|
|
|
|
fb = drmModeGetFB(ctx->hwctx->fd, plane->fb_id); |
|
|
|
if (!fb) { |
|
|
|
#if HAVE_LIBDRM_GETFB2 |
|
|
|
fb2 = drmModeGetFB2(ctx->hwctx->fd, plane->fb_id); |
|
|
|
if (!fb2 && errno == ENOSYS) { |
|
|
|
av_log(avctx, AV_LOG_INFO, "GETFB2 not supported, " |
|
|
|
"will try to use GETFB instead.\n"); |
|
|
|
} else if (!fb2) { |
|
|
|
err = errno; |
|
|
|
av_log(avctx, AV_LOG_ERROR, "Failed to get " |
|
|
|
"framebuffer %"PRIu32": %s.\n", |
|
|
|
plane->fb_id, strerror(err)); |
|
|
|
err = AVERROR(err); |
|
|
|
goto fail; |
|
|
|
} else { |
|
|
|
av_log(avctx, AV_LOG_INFO, "Template framebuffer is " |
|
|
|
"%"PRIu32": %"PRIu32"x%"PRIu32" " |
|
|
|
"format %"PRIx32" modifier %"PRIx64" flags %"PRIx32".\n", |
|
|
|
fb2->fb_id, fb2->width, fb2->height, |
|
|
|
fb2->pixel_format, fb2->modifier, fb2->flags); |
|
|
|
|
|
|
|
ctx->width = fb2->width; |
|
|
|
ctx->height = fb2->height; |
|
|
|
|
|
|
|
if (!fb2->handles[0]) { |
|
|
|
av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer: " |
|
|
|
"maybe you need some additional capabilities?\n"); |
|
|
|
err = AVERROR(EINVAL); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
if (ctx->drm_format != fb2->pixel_format) { |
|
|
|
av_log(avctx, AV_LOG_ERROR, "Framebuffer pixel format " |
|
|
|
"%"PRIx32" does not match expected format.\n", |
|
|
|
fb2->pixel_format); |
|
|
|
err = AVERROR(EINVAL); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
if (ctx->drm_format_modifier != DRM_FORMAT_MOD_INVALID && |
|
|
|
ctx->drm_format_modifier != fb2->modifier) { |
|
|
|
av_log(avctx, AV_LOG_ERROR, "Framebuffer format modifier " |
|
|
|
"%"PRIx64" does not match expected modifier.\n", |
|
|
|
fb2->modifier); |
|
|
|
err = AVERROR(EINVAL); |
|
|
|
goto fail; |
|
|
|
} else { |
|
|
|
ctx->drm_format_modifier = fb2->modifier; |
|
|
|
} |
|
|
|
ctx->fb2_available = 1; |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
av_log(avctx, AV_LOG_INFO, "Template framebuffer is %"PRIu32": " |
|
|
|
"%"PRIu32"x%"PRIu32" %"PRIu32"bpp %"PRIu32"b depth.\n", |
|
|
|
fb->fb_id, fb->width, fb->height, fb->bpp, fb->depth); |
|
|
|
if (!ctx->fb2_available) { |
|
|
|
fb = drmModeGetFB(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)); |
|
|
|
err = AVERROR(err); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
|
|
|
|
ctx->width = fb->width; |
|
|
|
ctx->height = fb->height; |
|
|
|
av_log(avctx, AV_LOG_INFO, "Template framebuffer is %"PRIu32": " |
|
|
|
"%"PRIu32"x%"PRIu32" %"PRIu32"bpp %"PRIu32"b depth.\n", |
|
|
|
fb->fb_id, fb->width, fb->height, fb->bpp, fb->depth); |
|
|
|
|
|
|
|
if (!fb->handle) { |
|
|
|
av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer: " |
|
|
|
"maybe you need some additional capabilities?\n"); |
|
|
|
err = AVERROR(EINVAL); |
|
|
|
goto fail; |
|
|
|
ctx->width = fb->width; |
|
|
|
ctx->height = fb->height; |
|
|
|
|
|
|
|
if (!fb->handle) { |
|
|
|
av_log(avctx, AV_LOG_ERROR, "No handle set on framebuffer: " |
|
|
|
"maybe you need some additional capabilities?\n"); |
|
|
|
err = AVERROR(EINVAL); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
stream = avformat_new_stream(avctx, NULL); |
|
|
|
@@ -415,8 +592,8 @@ static av_cold int kmsgrab_read_header(AVFormatContext *avctx) |
|
|
|
|
|
|
|
stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
|
|
|
stream->codecpar->codec_id = AV_CODEC_ID_WRAPPED_AVFRAME; |
|
|
|
stream->codecpar->width = fb->width; |
|
|
|
stream->codecpar->height = fb->height; |
|
|
|
stream->codecpar->width = ctx->width; |
|
|
|
stream->codecpar->height = ctx->height; |
|
|
|
stream->codecpar->format = AV_PIX_FMT_DRM_PRIME; |
|
|
|
|
|
|
|
avpriv_set_pts_info(stream, 64, 1, 1000000); |
|
|
|
@@ -430,8 +607,8 @@ static av_cold int kmsgrab_read_header(AVFormatContext *avctx) |
|
|
|
|
|
|
|
ctx->frames->format = AV_PIX_FMT_DRM_PRIME; |
|
|
|
ctx->frames->sw_format = ctx->format, |
|
|
|
ctx->frames->width = fb->width; |
|
|
|
ctx->frames->height = fb->height; |
|
|
|
ctx->frames->width = ctx->width; |
|
|
|
ctx->frames->height = ctx->height; |
|
|
|
|
|
|
|
err = av_hwframe_ctx_init(ctx->frames_ref); |
|
|
|
if (err < 0) { |
|
|
|
@@ -448,6 +625,9 @@ fail: |
|
|
|
drmModeFreePlaneResources(plane_res); |
|
|
|
drmModeFreePlane(plane); |
|
|
|
drmModeFreeFB(fb); |
|
|
|
#if HAVE_LIBDRM_GETFB2 |
|
|
|
drmModeFreeFB2(fb2); |
|
|
|
#endif |
|
|
|
return err; |
|
|
|
} |
|
|
|
|
|
|
|
@@ -472,7 +652,7 @@ static const AVOption options[] = { |
|
|
|
{ .i64 = AV_PIX_FMT_BGR0 }, 0, UINT32_MAX, FLAGS }, |
|
|
|
{ "format_modifier", "DRM format modifier for framebuffer", |
|
|
|
OFFSET(drm_format_modifier), AV_OPT_TYPE_INT64, |
|
|
|
{ .i64 = DRM_FORMAT_MOD_NONE }, 0, INT64_MAX, FLAGS }, |
|
|
|
{ .i64 = DRM_FORMAT_MOD_INVALID }, 0, INT64_MAX, FLAGS }, |
|
|
|
{ "crtc_id", "CRTC ID to define capture source", |
|
|
|
OFFSET(source_crtc), AV_OPT_TYPE_INT64, |
|
|
|
{ .i64 = 0 }, 0, UINT32_MAX, FLAGS }, |
|
|
|
|