kms: get plane every frame, might fix not updating issue and issue where plane gets invalid

This commit is contained in:
dec05eba 2023-04-10 02:25:43 +02:00
parent 9bdbca864d
commit dddd426904
7 changed files with 297 additions and 271 deletions

1
TODO
View File

@ -39,7 +39,6 @@ Fix constant framerate not working properly on amd/intel because capture framera
JPEG color range on amd seems to produce too bright video with h264 but not hevc, why? JPEG color range on amd seems to produce too bright video with h264 but not hevc, why?
Support recording screen/monitor on amd/intel.
Better configure vaapi. The file size is too large. Better configure vaapi. The file size is too large.
Better colors for vaapi. It looks a bit off when recording vscode for example. Better colors for vaapi. It looks a bit off when recording vscode for example.

View File

@ -88,7 +88,6 @@ static int recv_msg_from_server(int server_fd, gsr_kms_response *response) {
int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) { int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
self->kms_server_pid = -1; self->kms_server_pid = -1;
self->card_path = NULL;
self->socket_fd = -1; self->socket_fd = -1;
self->client_fd = -1; self->client_fd = -1;
self->socket_path[0] = '\0'; self->socket_path[0] = '\0';
@ -144,12 +143,6 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
} }
} }
self->card_path = strdup(card_path);
if(!self->card_path) {
fprintf(stderr, "gsr error: gsr_kms_client_init: failed to duplicate card_path\n");
goto err;
}
self->socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); self->socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(self->socket_fd == -1) { if(self->socket_fd == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_init: socket failed, error: %s\n", strerror(errno)); fprintf(stderr, "gsr error: gsr_kms_client_init: socket failed, error: %s\n", strerror(errno));
@ -174,13 +167,13 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
goto err; goto err;
} else if(pid == 0) { /* child */ } else if(pid == 0) { /* child */
if(inside_flatpak) { if(inside_flatpak) {
const char *args[] = { "flatpak-spawn", "--host", "pkexec", "flatpak", "run", "--command=gsr-kms-server", "com.dec05eba.gpu_screen_recorder", self->socket_path, NULL }; const char *args[] = { "flatpak-spawn", "--host", "pkexec", "flatpak", "run", "--command=gsr-kms-server", "com.dec05eba.gpu_screen_recorder", self->socket_path, card_path, NULL };
execvp(args[0], (char *const*)args); execvp(args[0], (char *const*)args);
} else if(has_perm) { } else if(has_perm) {
const char *args[] = { server_filepath, self->socket_path, NULL }; const char *args[] = { server_filepath, self->socket_path, card_path, NULL };
execvp(args[0], (char *const*)args); execvp(args[0], (char *const*)args);
} else { } else {
const char *args[] = { "pkexec", server_filepath, self->socket_path, NULL }; const char *args[] = { "pkexec", server_filepath, self->socket_path, card_path, NULL };
execvp(args[0], (char *const*)args); execvp(args[0], (char *const*)args);
} }
fprintf(stderr, "gsr error: gsr_kms_client_init: execvp failed, error: %s\n", strerror(errno)); fprintf(stderr, "gsr error: gsr_kms_client_init: execvp failed, error: %s\n", strerror(errno));
@ -189,7 +182,7 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
self->kms_server_pid = pid; self->kms_server_pid = pid;
} }
fprintf(stderr, "gsr info: gsr_kms_client_init: waiting for client to connect\n"); fprintf(stderr, "gsr info: gsr_kms_client_init: waiting for server to connect\n");
for(;;) { for(;;) {
struct timeval tv; struct timeval tv;
fd_set rfds; fd_set rfds;
@ -217,7 +210,7 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
} }
} }
} }
fprintf(stderr, "gsr info: gsr_kms_client_init: client connected\n"); fprintf(stderr, "gsr info: gsr_kms_client_init: server connected\n");
return 0; return 0;
@ -227,11 +220,6 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
} }
void gsr_kms_client_deinit(gsr_kms_client *self) { void gsr_kms_client_deinit(gsr_kms_client *self) {
if(self->card_path) {
free(self->card_path);
self->card_path = NULL;
}
if(self->client_fd != -1) { if(self->client_fd != -1) {
close(self->client_fd); close(self->client_fd);
self->client_fd = -1; self->client_fd = -1;
@ -261,7 +249,6 @@ int gsr_kms_client_get_kms(gsr_kms_client *self, gsr_kms_response *response) {
gsr_kms_request request; gsr_kms_request request;
request.type = KMS_REQUEST_TYPE_GET_KMS; request.type = KMS_REQUEST_TYPE_GET_KMS;
strcpy(request.data.card_path, self->card_path);
if(send_msg_to_server(self->client_fd, &request) == -1) { if(send_msg_to_server(self->client_fd, &request) == -1) {
fprintf(stderr, "gsr error: gsr_kms_client_get_kms: failed to send request message to server\n"); fprintf(stderr, "gsr error: gsr_kms_client_get_kms: failed to send request message to server\n");
return -1; return -1;

View File

@ -9,7 +9,6 @@ typedef struct {
int socket_fd; int socket_fd;
int client_fd; int client_fd;
char socket_path[255]; char socket_path[255];
char *card_path;
} gsr_kms_client; } gsr_kms_client;
/* |card_path| should be a path to card, for example /dev/dri/card0 */ /* |card_path| should be a path to card, for example /dev/dri/card0 */

View File

@ -10,18 +10,12 @@ typedef enum {
typedef enum { typedef enum {
KMS_RESULT_OK, KMS_RESULT_OK,
KMS_RESULT_INVALID_REQUEST, KMS_RESULT_INVALID_REQUEST,
KMS_RESULT_FAILED_TO_OPEN_CARD, KMS_RESULT_FAILED_TO_GET_PLANE,
KMS_RESULT_INSUFFICIENT_PERMISSIONS,
KMS_RESULT_FAILED_TO_GET_KMS,
KMS_RESULT_NO_KMS_AVAILABLE,
KMS_RESULT_FAILED_TO_SEND KMS_RESULT_FAILED_TO_SEND
} gsr_kms_result; } gsr_kms_result;
typedef struct { typedef struct {
int type; /* gsr_kms_request_type */ int type; /* gsr_kms_request_type */
union {
char card_path[255];
} data;
} gsr_kms_request; } gsr_kms_request;
typedef struct { typedef struct {

View File

@ -16,6 +16,11 @@
#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 #define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2
typedef struct {
int drmfd;
uint32_t plane_id;
} gsr_drm;
static int max_int(int a, int b) { static int max_int(int a, int b) {
return a > b ? a : b; return a > b ? a : b;
} }
@ -57,63 +62,112 @@ static int send_msg_to_client(int client_fd, gsr_kms_response *response, int *fd
return sendmsg(client_fd, &response_message, 0); return sendmsg(client_fd, &response_message, 0);
} }
static int get_kms(const char *card_path, gsr_kms_response *response) { static int kms_get_plane_id(gsr_drm *drm) {
drmModePlaneResPtr planes = NULL;
drmModePlanePtr plane = NULL;
drmModeFB2Ptr drmfb = NULL;
int result = -1;
if(drmSetClientCap(drm->drmfd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1) != 0) {
fprintf(stderr, "kms server error: drmSetClientCap failed, error: %s\n", strerror(errno));
goto error;
}
planes = drmModeGetPlaneResources(drm->drmfd);
if(!planes) {
fprintf(stderr, "kms server error: failed to access planes, error: %s\n", strerror(errno));
goto error;
}
for(uint32_t i = 0; i < planes->count_planes; ++i) {
plane = drmModeGetPlane(drm->drmfd, planes->planes[i]);
if(!plane) {
fprintf(stderr, "kms server warning: failed to get drmModePlanePtr for plane %#x: %s (%d)\n", planes->planes[i], strerror(errno), errno);
continue;
}
if(!plane->fb_id) {
drmModeFreePlane(plane);
continue;
}
break;
}
if(!plane) {
fprintf(stderr, "kms server error: failed to find a usable plane\n");
goto error;
}
// TODO: Fallback to getfb(1)?
drmfb = drmModeGetFB2(drm->drmfd, plane->fb_id);
if(!drmfb) {
fprintf(stderr, "kms server error: drmModeGetFB2 failed on plane fb id %d, error: %s\n", plane->fb_id, strerror(errno));
goto error;
}
if(!drmfb->handles[0]) {
fprintf(stderr, "kms server error: drmfb handle is NULL\n");
goto error;
}
drm->plane_id = plane->plane_id;
result = 0;
error:
if(drmfb)
drmModeFreeFB2(drmfb);
if(plane)
drmModeFreePlane(plane);
if(planes)
drmModeFreePlaneResources(planes);
return result;
}
static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
drmModePlanePtr plane = NULL;
drmModeFB2 *drmfb = NULL;
int result = -1;
response->result = KMS_RESULT_OK; response->result = KMS_RESULT_OK;
response->data.fd.fd = 0; response->data.fd.fd = 0;
response->data.fd.width = 0; response->data.fd.width = 0;
response->data.fd.height = 0; response->data.fd.height = 0;
const int drmfd = open(card_path, O_RDONLY); plane = drmModeGetPlane(drm->drmfd, drm->plane_id);
if (drmfd < 0) {
response->result = KMS_RESULT_FAILED_TO_OPEN_CARD;
snprintf(response->data.err_msg, sizeof(response->data.err_msg), "failed to open %s, error: %s", card_path, strerror(errno));
return -1;
}
if (0 != drmSetClientCap(drmfd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
response->result = KMS_RESULT_INSUFFICIENT_PERMISSIONS;
snprintf(response->data.err_msg, sizeof(response->data.err_msg), "drmSetClientCap failed, error: %s", strerror(errno));
close(drmfd);
return -1;
}
drmModePlaneResPtr planes = drmModeGetPlaneResources(drmfd);
if (!planes) {
response->result = KMS_RESULT_FAILED_TO_GET_KMS;
snprintf(response->data.err_msg, sizeof(response->data.err_msg), "failed to access planes, error: %s", strerror(errno));
close(drmfd);
return -1;
}
fprintf(stderr, "DRM planes %d:\n", planes->count_planes);
for (uint32_t i = 0; i < planes->count_planes; ++i) {
drmModePlanePtr plane = drmModeGetPlane(drmfd, planes->planes[i]);
if(!plane) { if(!plane) {
fprintf(stderr, "Cannot get drmModePlanePtr for plane %#x: %s (%d)\n", planes->planes[i], strerror(errno), errno); response->result = KMS_RESULT_FAILED_TO_GET_PLANE;
continue; snprintf(response->data.err_msg, sizeof(response->data.err_msg), "failed to get drm plane with id %u, error: %s\n", drm->plane_id, strerror(errno));
fprintf(stderr, "kms server error: %s\n", response->data.err_msg);
goto error;
} }
fprintf(stderr, "\t%d: fb_id=%#x\n", i, plane->fb_id); drmfb = drmModeGetFB2(drm->drmfd, plane->fb_id);
if (!plane->fb_id)
goto plane_continue;
drmModeFB2Ptr drmfb = drmModeGetFB2(drmfd, plane->fb_id);
if(!drmfb) { if(!drmfb) {
fprintf(stderr, "Cannot get drmModeFBPtr for fb %#x: %s (%d)\n", plane->fb_id, strerror(errno), errno); response->result = KMS_RESULT_FAILED_TO_GET_PLANE;
} else { snprintf(response->data.err_msg, sizeof(response->data.err_msg), "drmModeGetFB2 failed, error: %s", strerror(errno));
fprintf(stderr, "kms server error: %s\n", response->data.err_msg);
goto error;
}
if(!drmfb->handles[0]) { if(!drmfb->handles[0]) {
fprintf(stderr, "\t\tFB handle for fb %#x is NULL\n", plane->fb_id); response->result = KMS_RESULT_FAILED_TO_GET_PLANE;
fprintf(stderr, "\t\tPossible reason: not permitted to get FB handles. Do `sudo setcap cap_sys_admin+ep`\n"); snprintf(response->data.err_msg, sizeof(response->data.err_msg), "drmfb handle is NULL");
} else { fprintf(stderr, "kms server error: %s\n", response->data.err_msg);
goto error;
}
// TODO: Check if dimensions have changed by comparing width and height to previous time this was called.
// TODO: Support other plane formats than rgb (with multiple planes, such as direct YUV420 on wayland).
int fb_fd = -1; int fb_fd = -1;
const int ret = drmPrimeHandleToFD(drmfd, drmfb->handles[0], 0, &fb_fd); const int ret = drmPrimeHandleToFD(drm->drmfd, drmfb->handles[0], O_RDONLY, &fb_fd);
if(ret != 0 || fb_fd == -1) { if(ret != 0 || fb_fd == -1) {
fprintf(stderr, "Cannot get fd for fb %#x handle %#x: %s (%d)\n", plane->fb_id, drmfb->handles[0], strerror(errno), errno); response->result = KMS_RESULT_FAILED_TO_GET_PLANE;
} else if(drmfb->width * drmfb->height > response->data.fd.width * response->data.fd.height) { snprintf(response->data.err_msg, sizeof(response->data.err_msg), "failed to get fd from drm handle, error: %s", strerror(errno));
if(response->data.fd.fd != 0) { fprintf(stderr, "kms server error: %s\n", response->data.err_msg);
close(response->data.fd.fd); goto error;
response->data.fd.fd = 0;
} }
response->data.fd.fd = fb_fd; response->data.fd.fd = fb_fd;
@ -123,33 +177,20 @@ static int get_kms(const char *card_path, gsr_kms_response *response) {
response->data.fd.offset = drmfb->offsets[0]; response->data.fd.offset = drmfb->offsets[0];
response->data.fd.pixel_format = drmfb->pixel_format; response->data.fd.pixel_format = drmfb->pixel_format;
response->data.fd.modifier = drmfb->modifier; response->data.fd.modifier = drmfb->modifier;
fprintf(stderr, "kms width: %u, height: %u, pixel format: %u, modifier: %lu\n", response->data.fd.width, response->data.fd.height, response->data.fd.pixel_format, response->data.fd.modifier); result = 0;
} else {
close(fb_fd); error:
} if(drmfb)
}
drmModeFreeFB2(drmfb); drmModeFreeFB2(drmfb);
} if(plane)
plane_continue:
drmModeFreePlane(plane); drmModeFreePlane(plane);
}
drmModeFreePlaneResources(planes); return result;
close(drmfd);
if(response->data.fd.fd == 0) {
response->result = KMS_RESULT_NO_KMS_AVAILABLE;
snprintf(response->data.err_msg, sizeof(response->data.err_msg), "no kms found");
return -1;
}
return 0;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
if(argc != 2) { if(argc != 3) {
fprintf(stderr, "usage: kms_server <domain_socket_path>\n"); fprintf(stderr, "usage: kms_server <domain_socket_path> <card_path>\n");
return 1; return 1;
} }
@ -160,7 +201,22 @@ int main(int argc, char **argv) {
return 2; return 2;
} }
fprintf(stderr, "kms server info: connecting to the server\n"); const char *card_path = argv[2];
gsr_drm drm;
drm.plane_id = 0;
drm.drmfd = open(card_path, O_RDONLY);
if(drm.drmfd < 0) {
fprintf(stderr, "kms server error: failed to open %s, error: %s", card_path, strerror(errno));
return 2;
}
if(kms_get_plane_id(&drm) != 0) {
close(drm.drmfd);
return 2;
}
fprintf(stderr, "kms server info: connecting to the client\n");
for(;;) { for(;;) {
struct sockaddr_un remote_addr = {0}; struct sockaddr_un remote_addr = {0};
remote_addr.sun_family = AF_UNIX; remote_addr.sun_family = AF_UNIX;
@ -171,11 +227,13 @@ int main(int argc, char **argv) {
continue; // Host not ready yet? TODO: sleep continue; // Host not ready yet? TODO: sleep
if(errno == EISCONN) // TODO? if(errno == EISCONN) // TODO?
break; break;
fprintf(stderr, "kms server error: connect failed, error: %s (%d)\n", strerror(errno), errno); fprintf(stderr, "kms server error: connect failed, error: %s (%d)\n", strerror(errno), errno);
close(drm.drmfd);
return 2; return 2;
} }
} }
fprintf(stderr, "kms server info: connected to the server\n"); fprintf(stderr, "kms server info: connected to the client\n");
int res = 0; int res = 0;
for(;;) { for(;;) {
@ -202,33 +260,27 @@ int main(int argc, char **argv) {
} }
continue; continue;
} }
request.data.card_path[254] = '\0';
switch(request.type) { switch(request.type) {
case KMS_REQUEST_TYPE_GET_KMS: { case KMS_REQUEST_TYPE_GET_KMS: {
gsr_kms_response response; gsr_kms_response response;
int kms_fd = 0;
if (get_kms(request.data.card_path, &response) == 0) {
kms_fd = response.data.fd.fd;
}
if(send_msg_to_client(socket_fd, &response, &kms_fd, kms_fd == 0 ? 0 : 1) == -1) { if(kms_get_fb(&drm, &response) == 0) {
if(send_msg_to_client(socket_fd, &response, &response.data.fd.fd, 1) == -1)
fprintf(stderr, "kms server error: failed to respond to client KMS_REQUEST_TYPE_GET_KMS request\n");
close(response.data.fd.fd);
} else {
if(send_msg_to_client(socket_fd, &response, NULL, 0) == -1)
fprintf(stderr, "kms server error: failed to respond to client KMS_REQUEST_TYPE_GET_KMS request\n"); fprintf(stderr, "kms server error: failed to respond to client KMS_REQUEST_TYPE_GET_KMS request\n");
if(kms_fd != 0)
close(kms_fd);
break;
} }
if(kms_fd != 0)
close(kms_fd);
break; break;
} }
default: { default: {
gsr_kms_response response; gsr_kms_response response;
response.result = KMS_RESULT_INVALID_REQUEST; response.result = KMS_RESULT_INVALID_REQUEST;
snprintf(response.data.err_msg, sizeof(response.data.err_msg), "invalid request type %d, expected %d (%s)", request.type, KMS_REQUEST_TYPE_GET_KMS, "KMS_REQUEST_TYPE_GET_KMS"); snprintf(response.data.err_msg, sizeof(response.data.err_msg), "invalid request type %d, expected %d (%s)", request.type, KMS_REQUEST_TYPE_GET_KMS, "KMS_REQUEST_TYPE_GET_KMS");
fprintf(stderr, "%s\n", response.data.err_msg); fprintf(stderr, "kms server error: %s\n", response.data.err_msg);
if(send_msg_to_client(socket_fd, &response, NULL, 0) == -1) { if(send_msg_to_client(socket_fd, &response, NULL, 0) == -1) {
fprintf(stderr, "kms server error: failed to respond to client request\n"); fprintf(stderr, "kms server error: failed to respond to client request\n");
break; break;
@ -239,6 +291,7 @@ int main(int argc, char **argv) {
} }
done: done:
close(drm.drmfd);
close(socket_fd); close(socket_fd);
return res; return res;
} }

View File

@ -5,5 +5,5 @@ window=$(xdotool selectwindow)
active_sink="$(pactl get-default-sink).monitor" active_sink="$(pactl get-default-sink).monitor"
mkdir -p "$HOME/Videos" mkdir -p "$HOME/Videos"
video="$HOME/Videos/$(date +"Video_%Y-%m-%d_%H-%M-%S.mp4")" video="$HOME/Videos/$(date +"Video_%Y-%m-%d_%H-%M-%S.mp4")"
notify-send -u low 'GPU Screen Recorder' "Started recording video to $video" notify-send -t 5000 -u low 'GPU Screen Recorder' "Started recording video to $video"
gpu-screen-recorder -w "$window" -c mp4 -f 60 -a "$active_sink" -o "$video" gpu-screen-recorder -w "$window" -c mp4 -f 60 -a "$active_sink" -o "$video"

View File

@ -102,19 +102,6 @@ static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_c
return -1; return -1;
} }
gsr_kms_response kms_response;
if(gsr_kms_client_get_kms(&cap_kms->kms_client, &kms_response) != 0) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to get kms, error: %d (%s)\n", kms_response.result, kms_response.data.err_msg);
gsr_capture_kms_vaapi_stop(cap, video_codec_context);
return -1;
}
cap_kms->dmabuf_fd = kms_response.data.fd.fd;
cap_kms->pitch = kms_response.data.fd.pitch;
cap_kms->offset = kms_response.data.fd.offset;
cap_kms->fourcc = kms_response.data.fd.pixel_format;
cap_kms->modifiers = kms_response.data.fd.modifier;
// TODO: Update on monitor reconfiguration, make sure to update on window focus change (maybe for kms update each second?), needs to work on focus change to the witcher 3 on full window, fullscreen firefox, etc // TODO: Update on monitor reconfiguration, make sure to update on window focus change (maybe for kms update each second?), needs to work on focus change to the witcher 3 on full window, fullscreen firefox, etc
gsr_monitor monitor; gsr_monitor monitor;
if(strcmp(cap_kms->params.display_to_capture, "screen") == 0) { if(strcmp(cap_kms->params.display_to_capture, "screen") == 0) {
@ -132,8 +119,6 @@ static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_c
cap_kms->capture_pos = monitor.pos; cap_kms->capture_pos = monitor.pos;
cap_kms->capture_size = monitor.size; cap_kms->capture_size = monitor.size;
cap_kms->kms_size.x = kms_response.data.fd.width;
cap_kms->kms_size.y = kms_response.data.fd.height;
if(!gsr_egl_load(&cap_kms->egl, cap_kms->params.dpy)) { if(!gsr_egl_load(&cap_kms->egl, cap_kms->params.dpy)) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to load opengl\n"); fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to load opengl\n");
@ -162,27 +147,6 @@ static void gsr_capture_kms_vaapi_tick(gsr_capture *cap, AVCodecContext *video_c
//cap_kms->egl.glClear(GL_COLOR_BUFFER_BIT); //cap_kms->egl.glClear(GL_COLOR_BUFFER_BIT);
//const double window_resize_timeout = 1.0; // 1 second //const double window_resize_timeout = 1.0; // 1 second
if(!cap_kms->created_hw_frame) {
if(cap_kms->buffer_id) {
vaDestroyBuffer(cap_kms->va_dpy, cap_kms->buffer_id);
cap_kms->buffer_id = 0;
}
if(cap_kms->context_id) {
vaDestroyContext(cap_kms->va_dpy, cap_kms->context_id);
cap_kms->context_id = 0;
}
if(cap_kms->config_id) {
vaDestroyConfig(cap_kms->va_dpy, cap_kms->config_id);
cap_kms->config_id = 0;
}
if(cap_kms->input_surface) {
vaDestroySurfaces(cap_kms->va_dpy, &cap_kms->input_surface, 1);
cap_kms->input_surface = 0;
}
if(!cap_kms->created_hw_frame) { if(!cap_kms->created_hw_frame) {
cap_kms->created_hw_frame = true; cap_kms->created_hw_frame = true;
av_frame_free(frame); av_frame_free(frame);
@ -209,8 +173,68 @@ static void gsr_capture_kms_vaapi_tick(gsr_capture *cap, AVCodecContext *video_c
cap_kms->stop_is_error = true; cap_kms->stop_is_error = true;
return; return;
} }
VAStatus va_status = vaCreateConfig(cap_kms->va_dpy, VAProfileNone, VAEntrypointVideoProc, NULL, 0, &cap_kms->config_id);
if(va_status != VA_STATUS_SUCCESS) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_tick: vaCreateConfig failed: %d\n", va_status);
cap_kms->should_stop = true;
cap_kms->stop_is_error = true;
return;
}
}
} }
static bool gsr_capture_kms_vaapi_should_stop(gsr_capture *cap, bool *err) {
gsr_capture_kms_vaapi *cap_kms = cap->priv;
if(cap_kms->should_stop) {
if(err)
*err = cap_kms->stop_is_error;
return true;
}
if(err)
*err = false;
return false;
}
static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) {
gsr_capture_kms_vaapi *cap_kms = cap->priv;
if(cap_kms->dmabuf_fd > 0) {
close(cap_kms->dmabuf_fd);
cap_kms->dmabuf_fd = 0;
}
gsr_kms_response kms_response;
if(gsr_kms_client_get_kms(&cap_kms->kms_client, &kms_response) != 0) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to get kms, error: %d (%s)\n", kms_response.result, kms_response.data.err_msg);
return -1;
}
cap_kms->dmabuf_fd = kms_response.data.fd.fd;
cap_kms->pitch = kms_response.data.fd.pitch;
cap_kms->offset = kms_response.data.fd.offset;
cap_kms->fourcc = kms_response.data.fd.pixel_format;
cap_kms->modifiers = kms_response.data.fd.modifier;
cap_kms->kms_size.x = kms_response.data.fd.width;
cap_kms->kms_size.y = kms_response.data.fd.height;
if(cap_kms->buffer_id) {
vaDestroyBuffer(cap_kms->va_dpy, cap_kms->buffer_id);
cap_kms->buffer_id = 0;
}
if(cap_kms->context_id) {
vaDestroyContext(cap_kms->va_dpy, cap_kms->context_id);
cap_kms->context_id = 0;
}
if(cap_kms->input_surface) {
vaDestroySurfaces(cap_kms->va_dpy, &cap_kms->input_surface, 1);
cap_kms->input_surface = 0;
}
VASurfaceID target_surface_id = (uintptr_t)frame->data[3];
uintptr_t dmabuf = cap_kms->dmabuf_fd; uintptr_t dmabuf = cap_kms->dmabuf_fd;
VASurfaceAttribExternalBuffers buf = {0}; VASurfaceAttribExternalBuffers buf = {0};
@ -251,31 +275,21 @@ static void gsr_capture_kms_vaapi_tick(gsr_capture *cap, AVCodecContext *video_c
++num_attribs; ++num_attribs;
} }
// TODO: Do we really need to create a new surface every frame?
VAStatus va_status = vaCreateSurfaces(cap_kms->va_dpy, VA_RT_FORMAT_RGB32, cap_kms->kms_size.x, cap_kms->kms_size.y, &cap_kms->input_surface, 1, attribs, num_attribs); VAStatus va_status = vaCreateSurfaces(cap_kms->va_dpy, VA_RT_FORMAT_RGB32, cap_kms->kms_size.x, cap_kms->kms_size.y, &cap_kms->input_surface, 1, attribs, num_attribs);
if(va_status != VA_STATUS_SUCCESS) { if(va_status != VA_STATUS_SUCCESS) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_tick: vaCreateSurfaces failed: %d\n", va_status); fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_tick: vaCreateSurfaces failed: %d\n", va_status);
cap_kms->should_stop = true; cap_kms->should_stop = true;
cap_kms->stop_is_error = true; cap_kms->stop_is_error = true;
return; return -1;
} }
//vaBeginPicture(cap_kms->va_dpy, )
va_status = vaCreateConfig(cap_kms->va_dpy, VAProfileNone, VAEntrypointVideoProc, NULL, 0, &cap_kms->config_id);
if(va_status != VA_STATUS_SUCCESS) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_tick: vaCreateConfig failed: %d\n", va_status);
cap_kms->should_stop = true;
cap_kms->stop_is_error = true;
return;
}
VASurfaceID target_surface_id = (uintptr_t)(*frame)->data[3];
va_status = vaCreateContext(cap_kms->va_dpy, cap_kms->config_id, cap_kms->kms_size.x, cap_kms->kms_size.y, VA_PROGRESSIVE, &target_surface_id, 1, &cap_kms->context_id); va_status = vaCreateContext(cap_kms->va_dpy, cap_kms->config_id, cap_kms->kms_size.x, cap_kms->kms_size.y, VA_PROGRESSIVE, &target_surface_id, 1, &cap_kms->context_id);
if(va_status != VA_STATUS_SUCCESS) { if(va_status != VA_STATUS_SUCCESS) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_tick: vaCreateContext failed: %d\n", va_status); fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_tick: vaCreateContext failed: %d\n", va_status);
cap_kms->should_stop = true; cap_kms->should_stop = true;
cap_kms->stop_is_error = true; cap_kms->stop_is_error = true;
return; return -1;
} }
cap_kms->input_region = (VARectangle) { cap_kms->input_region = (VARectangle) {
@ -296,43 +310,23 @@ static void gsr_capture_kms_vaapi_tick(gsr_capture *cap, AVCodecContext *video_c
params.output_background_color = 0; params.output_background_color = 0;
params.filter_flags = VA_FRAME_PICTURE; params.filter_flags = VA_FRAME_PICTURE;
// TODO: Colors // TODO: Colors
params.input_color_properties.color_range = (*frame)->color_range == AVCOL_RANGE_JPEG ? VA_SOURCE_RANGE_FULL : VA_SOURCE_RANGE_REDUCED; params.input_color_properties.color_range = frame->color_range == AVCOL_RANGE_JPEG ? VA_SOURCE_RANGE_FULL : VA_SOURCE_RANGE_REDUCED;
params.output_color_properties.color_range = (*frame)->color_range == AVCOL_RANGE_JPEG ? VA_SOURCE_RANGE_FULL : VA_SOURCE_RANGE_REDUCED; params.output_color_properties.color_range = frame->color_range == AVCOL_RANGE_JPEG ? VA_SOURCE_RANGE_FULL : VA_SOURCE_RANGE_REDUCED;
va_status = vaCreateBuffer(cap_kms->va_dpy, cap_kms->context_id, VAProcPipelineParameterBufferType, sizeof(params), 1, &params, &cap_kms->buffer_id); va_status = vaCreateBuffer(cap_kms->va_dpy, cap_kms->context_id, VAProcPipelineParameterBufferType, sizeof(params), 1, &params, &cap_kms->buffer_id);
if(va_status != VA_STATUS_SUCCESS) { if(va_status != VA_STATUS_SUCCESS) {
fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_tick: vaCreateBuffer failed: %d\n", va_status); fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_tick: vaCreateBuffer failed: %d\n", va_status);
cap_kms->should_stop = true; cap_kms->should_stop = true;
cap_kms->stop_is_error = true; cap_kms->stop_is_error = true;
return; return -1;
} }
// Clear texture with black background because the source texture (window_texture_get_opengl_texture_id(&cap_kms->window_texture)) // Clear texture with black background because the source texture (window_texture_get_opengl_texture_id(&cap_kms->window_texture))
// might be smaller than cap_kms->target_texture_id // might be smaller than cap_kms->target_texture_id
// TODO: // TODO:
//cap_kms->egl.glClearTexImage(cap_kms->target_texture_id, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); //cap_kms->egl.glClearTexImage(cap_kms->target_texture_id, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
}
static bool gsr_capture_kms_vaapi_should_stop(gsr_capture *cap, bool *err) { va_status = vaBeginPicture(cap_kms->va_dpy, cap_kms->context_id, target_surface_id);
gsr_capture_kms_vaapi *cap_kms = cap->priv;
if(cap_kms->should_stop) {
if(err)
*err = cap_kms->stop_is_error;
return true;
}
if(err)
*err = false;
return false;
}
static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) {
gsr_capture_kms_vaapi *cap_kms = cap->priv;
VASurfaceID target_surface_id = (uintptr_t)frame->data[3];
VAStatus va_status = vaBeginPicture(cap_kms->va_dpy, cap_kms->context_id, target_surface_id);
if(va_status != VA_STATUS_SUCCESS) { if(va_status != VA_STATUS_SUCCESS) {
static bool error_printed = false; static bool error_printed = false;
if(!error_printed) { if(!error_printed) {