ffmpeg 解码drm显示的例子

由于记不清 代码参考的出处,如有侵权,请联系删除。

/*
 * FFMPEG DRM/KMS example application
 * Jorge Ramirez-Ortiz 
 *
 * Main file of the application
 *      Based on code from:
 *      	2001 Fabrice Bellard (FFMPEG/doc/examples/decode_video.c
 *      	2018 Stanimir Varbanov (v4l2-decode/src/drm.c)
 *
 * This code has been tested on Linaro's Dragonboard 820c
 *      kernel v4.14.15, venus decoder
 *      ffmpeg 4.0 + lrusacks ffmpeg/DRM support + review
 *      	https://github.com/ldts/ffmpeg  branch lrusak/v4l2-drmprime
 *
 * Copyright (c) 2018 Baylibre
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#define ALIGN(x, a)		((x) + (a - 1)) & (~(a - 1))
#define DRM_ALIGN(val, align)	((val + (align - 1)) & ~(align - 1))

#define INBUF_SIZE 4096

struct drm_buffer {
	unsigned int fourcc;
	unsigned int bo_handle;
	unsigned int fb_handle;
	int dbuf_fd;
	void *mmap_buf;
	uint32_t pitches[4];
	uint32_t offsets[4];
	uint32_t bo_handles[4];
};

struct drm_dev {
	int fd;
	uint32_t conn_id, enc_id, crtc_id, fb_id, plane_id, crtc_idx;
	uint32_t width, height;
	uint32_t pitch, size, handle;
	drmModeModeInfo mode;
	drmModeCrtc *saved_crtc;
	struct drm_dev *next;
};

static struct drm_dev *pdev;
static unsigned int drm_format;

#define DBG_TAG "  ffmpeg-drm"

#define print(msg, ...)							\
	do {								\
			struct timeval tv;				\
			gettimeofday(&tv, NULL);			\
			fprintf(stderr, "%08u:%08u :" msg,		\
				(uint32_t)tv.tv_sec,			\
				(uint32_t)tv.tv_usec, ##__VA_ARGS__);	\
	} while (0)

#define err(msg, ...)  print("error: " msg "\n", ##__VA_ARGS__)
#define info(msg, ...) print(msg "\n", ##__VA_ARGS__)
#define dbg(msg, ...)  print(DBG_TAG ": " msg "\n", ##__VA_ARGS__)

int drm_dmabuf_set_plane(struct drm_buffer *buf, uint32_t width,
			 uint32_t height, int fullscreen)
{
	uint32_t crtc_w, crtc_h;

	crtc_w = width;
	crtc_h = height;

	if (fullscreen) {
		crtc_w = pdev->width;
		crtc_h = pdev->height;
	}
	//dbg("\n 111111 plane_id id:%d crtc id:%d ;pdev->fd <%d>\n", pdev->plane_id, pdev->crtc_id, pdev->fd);

	return drmModeSetPlane(pdev->fd, pdev->plane_id, pdev->crtc_id,
		      buf->fb_handle, 0,
		      0, 0, crtc_w, crtc_h,
		      0, 0, width << 16, height << 16);
}

static int drm_dmabuf_import(struct drm_buffer *buf, unsigned int width,
		      unsigned int height)
{
	return drmPrimeFDToHandle(pdev->fd, buf->dbuf_fd, &buf->bo_handle);
}

static int drm_dmabuf_addfb(struct drm_buffer *buf, uint32_t width, uint32_t height)
{
	int ret;

	if (width > pdev->width)
		width = pdev->width;
	if (height > pdev->height)
		height = pdev->height;

	width = ALIGN(width, 8);

	uint32_t stride = DRM_ALIGN(width, 128);
	uint32_t y_scanlines = DRM_ALIGN(height, 32);

	ret = drmModeAddFB2(pdev->fd, width, height, buf->fourcc, buf->bo_handles,
			    buf->pitches, buf->offsets, &buf->fb_handle, 0);
	if (ret) {
		err("drmModeAddFB2 failed: %d (%s)\n", ret, strerror(errno));
		return ret;
	}

	return 0;
}

static int find_plane(int fd, unsigned int fourcc, uint32_t *plane_id,
			uint32_t crtc_id, uint32_t crtc_idx)
{
	drmModePlaneResPtr planes;
	drmModePlanePtr plane;
	unsigned int i;
	unsigned int j;
	int ret = 0;
	unsigned int format = fourcc;

	planes = drmModeGetPlaneResources(fd);
	if (!planes) {
		err("drmModeGetPlaneResources failed\n");
		return -1;
	}

	info("drm: found planes %u", planes->count_planes);

	for (i = 0; i < planes->count_planes; ++i) {
		plane = drmModeGetPlane(fd, planes->planes[i]);
		if (!plane) {
			err("drmModeGetPlane failed: %s\n", strerror(errno));
			break;
		}

		if (!(plane->possible_crtcs & (1 << crtc_idx))) {
			drmModeFreePlane(plane);
			continue;
		}

		for (j = 0; j < plane->count_formats; ++j) {
			if (plane->formats[j] == format)
				break;
		}

		if (j == plane->count_formats) {
			drmModeFreePlane(plane);
			continue;
		}

		*plane_id = plane->plane_id;
		
		dbg("00000--plane_id <%d>\n", *plane_id);
		drmModeFreePlane(plane);
		break;
	}

	if (i == planes->count_planes)
		ret = -1;

	drmModeFreePlaneResources(planes);

	return ret;
}

static struct drm_dev *drm_find_dev(int fd)
{
	int i;
	struct drm_dev *dev = NULL, *dev_head = NULL;
	drmModeRes *res;
	drmModeConnector *conn;
	drmModeEncoder *enc;
	drmModeCrtc *crtc = NULL;

	if ((res = drmModeGetResources(fd)) == NULL) {
		err("drmModeGetResources() failed");
		return NULL;
	}

	if (res->count_crtcs <= 0) {
		err("no Crtcs");
		goto free_res;
	}

	/* find all available connectors */
	for (i = 0; i < res->count_connectors; i++) {
		conn = drmModeGetConnector(fd, res->connectors[i]);

		if (conn) {
			if (conn->connection == DRM_MODE_CONNECTED) {
				dbg("drm: connector: connected");
			} else if (conn->connection == DRM_MODE_DISCONNECTED) {
				dbg("drm: connector: disconnected");
			} else if (conn->connection == DRM_MODE_UNKNOWNCONNECTION) {
				dbg("drm: connector: unknownconnection");
			} else {
				dbg("drm: connector: unknown");
			}
		}

		if (conn != NULL && conn->connection == DRM_MODE_CONNECTED
		    && conn->count_modes > 0) {
			dev = (struct drm_dev *) malloc(sizeof(struct drm_dev));
			memset(dev, 0, sizeof(struct drm_dev));

			dev->conn_id = conn->connector_id;
			dev->enc_id = conn->encoder_id;
			dev->next = NULL;

			memcpy(&dev->mode, &conn->modes[0], sizeof(drmModeModeInfo));
			dev->width = conn->modes[0].hdisplay;
			dev->height = conn->modes[0].vdisplay;

			if (conn->encoder_id) {
				enc = drmModeGetEncoder(fd, conn->encoder_id);
				if (!enc) {
					err("drmModeGetEncoder() faild");
					goto free_res;
				}
				if (enc->crtc_id) {
					crtc = drmModeGetCrtc(fd, enc->crtc_id);
					if (crtc)
						dev->crtc_id = enc->crtc_id;
				}
			}

			drmModeFreeEncoder(enc);

			dev->saved_crtc = NULL;

			/* create dev list */
			dev->next = dev_head;
			dev_head = dev;
		}
		drmModeFreeConnector(conn);
	}

	dev->crtc_idx = -1;

	for (i = 0; i < res->count_crtcs; ++i) {
		if (dev->crtc_id == res->crtcs[i]) {
			dev->crtc_idx = i;
			break;
		}
	}

	if (dev->crtc_idx == -1)
		err( "drm: CRTC %u not found\n");

free_res:
	drmModeFreeResources(res);

	return dev_head;
}

static int drm_open(const char *path)
{
	int fd, flags;
	uint64_t has_dumb;
	int ret;

	fd = open(path, O_RDWR);
	if (fd < 0) {
		err("cannot open \"%s\"\n", path);
		return -1;
	}

	/* set FD_CLOEXEC flag */
	if ((flags = fcntl(fd, F_GETFD)) < 0 ||
	     fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
		err("fcntl FD_CLOEXEC failed\n");
		goto err;
	}

	/* check capability */
	ret = drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb);
	if (ret < 0 || has_dumb == 0) {
		err("drmGetCap DRM_CAP_DUMB_BUFFER failed or doesn't have dumb "
		    "buffer\n");
		goto err;
	}

	return fd;
err:
	close(fd);
	return -1;
}

static int drm_init(unsigned int fourcc, const char *device)
{
	struct drm_dev *dev_head, *dev;
	int fd;
	int ret;

	fd = drm_open(device);
	if (fd < 0)
		return -1;

	dev_head = drm_find_dev(fd);
	if (dev_head == NULL) {
		err("available drm devices not found\n");
		goto err;
	}

	dbg("available connector(s)");

	for (dev = dev_head; dev != NULL; dev = dev->next) {
		dbg("connector id:%d", dev->conn_id);
		dbg("\tencoder id:%d crtc id:%d fb id:%d", dev->enc_id,
		    dev->crtc_id, dev->fb_id);
		dbg("\twidth:%d height:%d", dev->width, dev->height);
	}

	/* FIXME: use first drm_dev */
	for (dev = dev_head; dev != NULL; dev = dev->next) {
		if(dev->conn_id == 78) {
			
			dbg("\n use connect_id <%d>", dev->conn_id);
			break;
		}
	}
	dev = dev_head;
	dev->fd = fd;
	pdev = dev;

	dbg("\n encoder id<%d>; crtc id<%d>; \n plane_id id<%d>; dev->fd <%d>; ", 
							dev->enc_id, dev->crtc_id, dev->plane_id, dev->fd);

	ret = find_plane(fd, fourcc, &dev->plane_id, dev->crtc_id, dev->crtc_idx);
	if (ret) {
		err("Cannot find plane\n");
		goto err;
	}
//	dbg("\n encoder id<%d>; crtc id<%d>; \n plane_id id<%d>; fd <%d>; \n fourcc <%d>\n", 
	//						dev->enc_id, dev->crtc_id, dev->plane_id, fd, fourcc);

	//dev->crtc_id = 68;
	//dev->plane_id = 66;
	info("\n drm: Found %c%c%c%c plane_id: %x \n",
		(fourcc>>0)&0xff, (fourcc>>8)&0xff, (fourcc>>16)&0xff, (fourcc>>24)&0xff,
		dev->plane_id);
	return 0;

err:
	close(fd);
	pdev = NULL;
	return -1;
}

static int display(struct drm_buffer *drm_buf, int width, int height)
{
        struct drm_gem_close gem_close;
        int ret;

	ret = drm_dmabuf_import(drm_buf, width, height);
	if (ret) {
		err("cannot import dmabuf %d, fd=%d\n", ret, drm_buf->dbuf_fd);
		return -EFAULT;
	}
	drm_buf->bo_handles[0] = drm_buf->bo_handle;
	drm_buf->bo_handles[1] = drm_buf->bo_handle;
	drm_buf->bo_handles[2] = drm_buf->bo_handle;
	drm_buf->bo_handles[3] = drm_buf->bo_handle;

	ret = drm_dmabuf_addfb(drm_buf, width, height);
	if (ret) {
		err("cannot add framebuffer %d\n", ret);
		return -EFAULT;
	}

	drm_dmabuf_set_plane(drm_buf, width, height, 1);

        /* WARNING: this will _obviously_ cause the screen to flicker!!
         *
         *   Instead of using some simple stuff to postpone the release actions
         *   (a list or a ping/ping buffer or whatever) we will just keep it
         *   this way for clarity.
         *
         *   1. the client MUST remove the fb_handle
         *   2. the client MUST close the bo_handle (GEM object)
         *
         *   Not doing so will cause FFMPEG to _fail_ when releasing the capture
         *   mmap'ed buffers since the dmabufs are exported to DRM and therefore
         *   DRM keeps a reference to those buffers.
         *
         *   REQBUFS --> MMAP --> EXPBUF --> fb_handle / bo_handle
         *
         *   ==> releasing the buffers requires the handles to be released
         */

		  // SM2W :  此处不注释掉,3288无法显示
        if (drmModeRmFB(pdev->fd, drm_buf->fb_handle))
      //      err("cant remove fb %d\n", drm_buf->fb_handle);

        memset(&gem_close, 0, sizeof gem_close);
        gem_close.handle = drm_buf->bo_handle;
        if (drmIoctl(pdev->fd, DRM_IOCTL_GEM_CLOSE, &gem_close) < 0)
            err("cant close gem: %s\n", strerror(errno));

	return 0;
}

static void decode_and_display(AVCodecContext *dec_ctx, AVFrame *frame,
			AVPacket *pkt, const char *device)
{
	AVDRMFrameDescriptor *desc = NULL;
	struct drm_buffer drm_buf;
	int ret;
	uint8_t *srcSlice[8] = { NULL };
	int srcStride[8] = { 0 };
	int av_format_tmp ;

	ret = avcodec_send_packet(dec_ctx, pkt);
	if (ret < 0) {
		err("Error sending a packet for decoding\n");
		exit(1);
	}

	while (ret >= 0) {
		ret = avcodec_receive_frame(dec_ctx, frame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			return;
		else if (ret < 0) {
			err("Error during decoding\n");
			exit(1);
		}

		desc = (AVDRMFrameDescriptor *) frame->data[0];
		
		drm_buf.dbuf_fd = desc->objects[0].fd;
		for (int i = 0; i < desc->layers->nb_planes && i < 4; i++ ) {
			drm_buf.pitches[i] = desc->layers->planes[i].pitch;
			drm_buf.offsets[i] = desc->layers->planes[i].offset;
		}

                if (!pdev) {
                    /* initialize DRM with the format returned in the frame */
                    ret = drm_init(desc->layers[0].format, device);
                    if (ret) {
                        err("Error initializing drm\n");
                        exit(1);
                    }

                    /* remember the format */
                    drm_format = desc->layers[0].format;
                }

                /* pass the format in the buffer */
                drm_buf.fourcc = drm_format;
		ret = display(&drm_buf, frame->width, frame->height);
		if (ret < 0){
			drmModeRmFB(pdev->fd, drm_buf.fb_handle);
			return;}
    }
}

static const struct option options[] = {
	{
#define help_opt	0
		.name = "help",
		.has_arg = 0,
		.flag = NULL,
	},
	{
#define video_opt	1
		.name = "video",
		.has_arg = 1,
		.flag = NULL,
	},
	{
#define codec_opt	2
		.name = "codec",
		.has_arg = 1,
		.flag = NULL,
	},
	{
#define height_opt	3
		.name = "height",
		.has_arg = 1,
		.flag = NULL,
	},
	{
#define width_opt	4
		.name = "width",
		.has_arg = 1,
		.flag = NULL,
	},
	{
#define device_opt	5
		.name = "device",
		.has_arg = 1,
		.flag = NULL,
	},
	{
		.name = NULL,
	},
};

static void usage(void)
{
	fprintf(stderr, "usage: ffmpeg-drm , with:\n");
	fprintf(stderr, "--help            display this menu\n");
	fprintf(stderr, "--video=    video to display\n");
	fprintf(stderr, "--codec=    ffmpeg codec: ie h264_v4l2m2m\n");
	fprintf(stderr, "--width=   frame width\n");
	fprintf(stderr, "--height=  frame height\n");
    fprintf(stderr, "--device=  dri device to use\n");
	fprintf(stderr, "\n");
}

int main(int argc, char *argv[])
{
	uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
	AVCodecParserContext *parser;
	AVCodecContext *c = NULL;
	const AVCodec *codec;
	AVFrame *frame;
	AVPacket *pkt;
	size_t data_size;
	uint8_t *data;
	FILE *f;
	int ret;
	int lindex, opt;
	unsigned int frame_width = 0, frame_height = 0;
	char *codec_name = NULL, *video_name = NULL;
    char *device_name = "/dev/dri/card0";

	for (;;) {
		lindex = -1;

		opt = getopt_long_only(argc, argv, "", options, &lindex);
		if (opt == EOF)
			break;

		switch (lindex) {
		case help_opt:
			usage();
			exit(0);
		case video_opt:
			video_name = optarg;
			break;
		case codec_opt:
			codec_name = optarg;
			break;
		case width_opt:
			frame_width = atoi(optarg);
			break;
		case height_opt:
			frame_height = atoi(optarg);
			break;
        case device_opt:
			device_name = optarg;
			break;
		default:
			usage();
			exit(1);
		}
	}

	if (!frame_width || !frame_height || !codec_name || !video_name) {
		usage();
		exit(0);
	}

	avcodec_register_all();

	pkt = av_packet_alloc();
	if (!pkt) {
		err("Error allocating packet\n");
		exit(1);
	}

	/* set end of buffer to 0 (this ensures that no overreading happens for
	   damaged MPEG streams) */
	memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

	/* find the video decoder: ie: h264_v4l2m2m */
	codec = avcodec_find_decoder_by_name(codec_name);
	if (!codec) {
		err("Codec not found\n");
		exit(1);
	}

	parser = av_parser_init(codec->id);
	if (!parser) {
		err("parser not found\n");
		exit(1);
	}

	c = avcodec_alloc_context3(codec);
	if (!c) {
		err("Could not allocate video codec context\n");
		exit(1);
	}

	/* For some codecs, such as msmpeg4 and mpeg4, width and height
	   MUST be initialized before opening the ffmpeg codec (ie, before
	   calling avcodec_open2) because this information is not available in
	   the bitstream). */
	c->pix_fmt = AV_PIX_FMT_DRM_PRIME;   /* request a DRM frame */
        c->coded_height = frame_height;
	c->coded_width = frame_width;

	/* open it */
	if (avcodec_open2(c, codec, NULL) < 0) {
		err("Could not open codec\n");
		exit(1);
	}

	f = fopen(video_name, "rb");
	if (!f) {
		err("Could not open %s\n", video_name);
		exit(1);
	}

	frame = av_frame_alloc();
	if (!frame) {
		err("Could not allocate video frame\n");
		exit(1);
	}

	while (!feof(f)) {
		/* read raw data from the input file */
		data_size = fread(inbuf, 1, INBUF_SIZE, f);
		if (!data_size)
			break;

		/* use the parser to split the data into frames */
		data = inbuf;
		while (data_size > 0) {
			ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
				data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
			if (ret < 0) {
				err("Error while parsing\n");
				exit(1);
			}

			data += ret;
			data_size -= ret;

			if (pkt->size)
				decode_and_display(c, frame, pkt, device_name);
		}
	}
	fclose(f);

        decode_and_display(c, frame, NULL, device_name);

        av_parser_close(parser);
	avcodec_free_context(&c);
	av_frame_free(&frame);
	av_packet_free(&pkt);

	return 0;
}

但是 该代码,没有调起drm的双缓冲机制,画面刷新有些异常;

找到了使用双缓冲的dmeo,Github: drm-howto/modeset-vsync.c,但是不知道二者怎么结合起来?

有高手给看下。

你可能感兴趣的:(#,drm显示)