~emersion/vaapi-decoder

a5437e1ad12b443b9f173329145490d5c37b2eb8 — Simon Ser 11 months ago 7dd8e1d
Display frame via KMS
1 files changed, 109 insertions(+), 6 deletions(-)

M main.c
M main.c => main.c +109 -6
@@ 1,3 1,4 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>


@@ 8,6 9,8 @@
#include <libavutil/hwcontext.h>
#include <libavutil/hwcontext_drm.h>
#include <libavutil/pixdesc.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

static enum AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE;



@@ 64,6 67,44 @@ static enum AVPixelFormat get_hw_format(AVCodecContext *ctx,
	return AV_PIX_FMT_NONE;
}

static uint32_t pick_crtc(int drm_fd) {
	drmModeRes *res = drmModeGetResources(drm_fd);
	if (!res) {
		perror("drmModeGetResources");
		return 0;
	}

	drmModeConnector *conn = NULL;
	for (int i = 0; i < res->count_connectors; i++) {
		drmModeConnector *c = drmModeGetConnector(drm_fd, res->connectors[i]);
		if (c->connection == DRM_MODE_CONNECTED) {
			conn = c;
			break;
		}
		drmModeFreeConnector(c);
	}
	if (!conn) {
		fprintf(stderr, "Failed to find connected connector\n");
		return 0;
	}
	if (conn->encoder_id == 0) {
		fprintf(stderr, "Connector is not connected to an encoder\n");
		return 0;
	}

	drmModeFreeResources(res);

	drmModeEncoder *enc = drmModeGetEncoder(drm_fd, conn->encoder_id);
	if (enc->crtc_id == 0) {
		fprintf(stderr, "Encoder is not connected to a CRTC\n");
		return 0;
	}
	uint32_t crtc_id = enc->crtc_id;
	drmModeFreeEncoder(enc);

	return crtc_id;
}

static int decode_frame(AVFormatContext *input_ctx, int video_stream,
		AVCodecContext *decoder_ctx, AVFrame **frame_ptr) {
	int ret;


@@ 137,6 178,52 @@ static void print_drm_frame_desc(const AVDRMFrameDescriptor *drm_frame_desc) {
	}
}

static uint32_t import_drm_frame_desc(const AVDRMFrameDescriptor *drm_frame_desc,
		int drm_fd, int width, int height) {
	// VA-API drivers may use separate layers with one plane each, or a single
	// layer with multiple planes. We need to handle both.
	uint32_t drm_format = get_drm_frame_format(drm_frame_desc);
	if (drm_format == DRM_FORMAT_INVALID) {
		fprintf(stderr, "Failed to get DRM frame format\n");
		return 0;
	}
	fprintf(stderr, "DRM format: 0x%X\n", drm_format);

	uint32_t handles[4];
	uint32_t pitches[4];
	uint32_t offsets[4];
	int k = 0;
	for (int i = 0; i < drm_frame_desc->nb_layers; i++) {
		const AVDRMLayerDescriptor *drm_layer = &drm_frame_desc->layers[i];
		for (int j = 0; j < drm_layer->nb_planes; j++) {
			const AVDRMPlaneDescriptor *drm_plane = &drm_layer->planes[j];
			const AVDRMObjectDescriptor *drm_object = &drm_frame_desc->objects[drm_plane->object_index];

			uint32_t handle = 0;
			int ret = drmPrimeFDToHandle(drm_fd, drm_object->fd, &handle);
			if (ret < 0) {
				perror("drmPrimeFDToHandle");
				return 0;
			}

			handles[k] = handle;
			pitches[k] = drm_plane->pitch;
			offsets[k] = drm_plane->offset;
			k++;
		}
	}

	uint32_t fb_id = 0;
	int ret = drmModeAddFB2(drm_fd, width, height, drm_format,
		handles, pitches, offsets, &fb_id, 0);
	if (ret < 0) {
		perror("drmModeAddFB2");
		return 0;
	}

	return fb_id;
}

int main(int argc, char *argv[]) {
	if (argc < 2) {
		fprintf(stderr, "usage: %s <file>\n", argv[0]);


@@ 218,6 305,17 @@ int main(int argc, char *argv[]) {
		return 1;
	}

	int drm_fd = open("/dev/dri/card0", O_RDWR);
	if (drm_fd < 0) {
		fprintf(stderr, "Failed to open DRM primary node\n");
		return 1;
	}

	uint32_t crtc_id = pick_crtc(drm_fd);
	if (crtc_id == 0) {
		return 1;
	}

	AVFrame *frame = NULL;
	ret = decode_frame(input_ctx, video_stream, decoder_ctx, &frame);
	if (ret < 0) {


@@ 240,14 338,19 @@ int main(int argc, char *argv[]) {
	AVDRMFrameDescriptor *drm_frame_desc = (void *)drm_frame->data[0];
	print_drm_frame_desc(drm_frame_desc);

	// VA-API drivers may use separate layers with one plane each, or a single
	// layer with multiple planes. We need to handle both.
	uint32_t drm_format = get_drm_frame_format(drm_frame_desc);
	if (drm_format == DRM_FORMAT_INVALID) {
		fprintf(stderr, "Failed to get DRM frame format\n");
	uint32_t fb_id = import_drm_frame_desc(drm_frame_desc, drm_fd,
		drm_frame->width, drm_frame->height);
	if (!fb_id) {
		return 1;
	}
	fprintf(stderr, "DRM format: 0x%X\n", drm_format);

	ret = drmModePageFlip(drm_fd, crtc_id, fb_id, 0, NULL);
	if (ret < 0) {
		perror("drmModePageFlip");
		return 1;
	}

	sleep(5);

	av_frame_free(&drm_frame);
	av_frame_free(&frame);