使用drm api显示NV12数据的C语言代码
时间: 2023-12-14 21:36:56 浏览: 232
C++下纯代码NV12转换YUV420和YUV444内附实例,可以直接运行
以下是一个简单的使用drm api显示NV12数据的C语言代码示例:
```c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_mode.h>
#define WIDTH 640
#define HEIGHT 480
int main() {
int fd = open("/dev/dri/card0", O_RDWR);
if (fd < 0) {
perror("open /dev/dri/card0 failed");
return -1;
}
struct drm_mode_create_dumb create_dumb = {
.width = WIDTH,
.height = HEIGHT,
.bpp = 8,
};
if (ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) < 0) {
perror("DRM_IOCTL_MODE_CREATE_DUMB failed");
return -1;
}
uint32_t handle = create_dumb.handle;
struct drm_mode_map_dumb map_dumb = {
.handle = handle,
};
if (ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb) < 0) {
perror("DRM_IOCTL_MODE_MAP_DUMB failed");
return -1;
}
size_t size = create_dumb.size;
uint8_t *buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map_dumb.offset);
if (buffer == MAP_FAILED) {
perror("mmap failed");
return -1;
}
// fill the buffer with NV12 data
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
int uv_x = x / 2;
int uv_y = y / 2 + HEIGHT;
buffer[y * WIDTH + x] = (uint8_t)(0.2126 * x + 0.7152 * y + 0.0722 * (x + y) / 2);
buffer[uv_y * WIDTH + uv_x * 2] = (uint8_t)(128 + 0.5 * x - 0.4187 * y - 0.0813 * (x + y) / 2);
buffer[uv_y * WIDTH + uv_x * 2 + 1] = (uint8_t)(128 - 0.1687 * x - 0.3313 * y + 0.5 * (x + y) / 2);
}
}
struct drm_mode_create_dumb create_dumb2 = {
.width = WIDTH,
.height = HEIGHT * 3 / 2,
.bpp = 8,
};
if (ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb2) < 0) {
perror("DRM_IOCTL_MODE_CREATE_DUMB failed");
return -1;
}
uint32_t handle2 = create_dumb2.handle;
struct drm_mode_map_dumb map_dumb2 = {
.handle = handle2,
};
if (ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb2) < 0) {
perror("DRM_IOCTL_MODE_MAP_DUMB failed");
return -1;
}
size_t size2 = create_dumb2.size;
uint8_t *buffer2 = mmap(NULL, size2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map_dumb2.offset);
if (buffer2 == MAP_FAILED) {
perror("mmap failed");
return -1;
}
// convert NV12 to YUV420
for (int y = 0; y < HEIGHT; y++) {
memcpy(buffer2 + y * WIDTH, buffer + y * WIDTH, WIDTH);
}
for (int y = 0; y < HEIGHT / 2; y++) {
for (int x = 0; x < WIDTH / 2; x++) {
int uv_x = x * 2;
int uv_y = y * 2 + HEIGHT;
buffer2[uv_y * WIDTH + uv_x] = buffer[y * WIDTH + x * 2 + WIDTH * HEIGHT];
buffer2[uv_y * WIDTH + uv_x + 1] = buffer[y * WIDTH + x * 2 + WIDTH * HEIGHT + 1];
buffer2[(uv_y + 1) * WIDTH + uv_x] = buffer[y * WIDTH + x * 2 + WIDTH * HEIGHT];
buffer2[(uv_y + 1) * WIDTH + uv_x + 1] = buffer[y * WIDTH + x * 2 + WIDTH * HEIGHT + 1];
}
}
struct drm_mode_create_plane create_plane = {
.fb_id = 0,
.crtc_id = 0,
.format = DRM_FORMAT_YUV420,
.plane_id = 0,
.flags = 0,
};
if (ioctl(fd, DRM_IOCTL_MODE_CREATE_PLANES, &create_plane) < 0) {
perror("DRM_IOCTL_MODE_CREATE_PLANES failed");
return -1;
}
struct drm_mode_set_plane set_plane = {
.plane_id = create_plane.plane_id,
.crtc_id = 0,
.fb_id = create_plane.fb_id,
.src_x = 0,
.src_y = 0,
.src_w = WIDTH << 16,
.src_h = HEIGHT * 3 / 2 << 16,
.crtc_x = 0,
.crtc_y = 0,
.crtc_w = WIDTH,
.crtc_h = HEIGHT,
};
if (ioctl(fd, DRM_IOCTL_MODE_SETPLANE, &set_plane) < 0) {
perror("DRM_IOCTL_MODE_SETPLANE failed");
return -1;
}
getchar(); // wait for user input
if (ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &create_dumb2) < 0) {
perror("DRM_IOCTL_MODE_DESTROY_DUMB failed");
return -1;
}
if (ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &create_dumb) < 0) {
perror("DRM_IOCTL_MODE_DESTROY_DUMB failed");
return -1;
}
close(fd);
return 0;
}
```
该示例代码假设你有一个名为 `/dev/dri/card0` 的DRM设备,并且它支持 `DRM_FORMAT_YUV420` 格式。它使用两个dumb buffer,一个用于存储原始的NV12数据,另一个用于存储转换后的YUV420数据。它还使用一个DRM plane将YUV420数据显示在屏幕上。
阅读全文