Add screen recorder tool

This commit adds a new, built-in screen recorder tool.  The tool UI is
still very simple, start with mod-r and stop it again with mod-r.
The recording is written to capture.wcap, in a simple run-length encoded
adhoc format.  The wcap-decode tool can be used to extract a single frame
from the capture, for now, but the plan is to hook this up to libvpx and
generate webm output.
dev
Kristian Høgsberg 13 years ago
parent e68fd10f87
commit 5fb70bf301
  1. 9
      src/Makefile.am
  2. 3
      src/compositor.c
  3. 1
      src/compositor.h
  4. 183
      src/screenshooter.c
  5. 207
      src/wcap-decode.c

@ -1,5 +1,12 @@
bin_PROGRAMS = weston \ bin_PROGRAMS = weston \
$(weston_launch) $(weston_launch) \
wcap-decode
wcap_decode_SOURCES = \
wcap-decode.c
wcap_decode_CFLAGS = $(CAIRO_EGL_CFLAGS)
wcap_decode_LDADD = $(CAIRO_EGL_LIBS)
AM_CPPFLAGS = \ AM_CPPFLAGS = \
-DDATADIR='"$(datadir)"' \ -DDATADIR='"$(datadir)"' \

@ -1082,6 +1082,8 @@ weston_output_finish_frame(struct weston_output *output, int msecs)
wl_display_get_event_loop(compositor->wl_display); wl_display_get_event_loop(compositor->wl_display);
int fd; int fd;
wl_signal_emit(&output->frame_signal, &msecs);
if (output->repaint_needed) { if (output->repaint_needed) {
weston_output_repaint(output, msecs); weston_output_repaint(output, msecs);
return; return;
@ -2530,6 +2532,7 @@ weston_output_init(struct weston_output *output, struct weston_compositor *c,
weston_output_move(output, x, y); weston_output_move(output, x, y);
weston_output_damage(output); weston_output_damage(output);
wl_signal_init(&output->frame_signal);
wl_list_init(&output->frame_callback_list); wl_list_init(&output->frame_callback_list);
wl_list_init(&output->resource_list); wl_list_init(&output->resource_list);

@ -124,6 +124,7 @@ struct weston_output {
struct weston_output_zoom zoom; struct weston_output_zoom zoom;
int dirty; int dirty;
struct wl_list read_pixels_list; struct wl_list read_pixels_list;
struct wl_signal frame_signal;
char *make, *model; char *make, *model;
uint32_t subpixel; uint32_t subpixel;

@ -21,8 +21,12 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <string.h> #include <string.h>
#include <linux/input.h> #include <linux/input.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include "compositor.h" #include "compositor.h"
#include "screenshooter-server-protocol.h" #include "screenshooter-server-protocol.h"
@ -206,6 +210,183 @@ screenshooter_binding(struct wl_seat *seat, uint32_t time,
screenshooter_exe, screenshooter_sigchld); screenshooter_exe, screenshooter_sigchld);
} }
struct weston_recorder {
struct weston_output *output;
uint32_t *frame, *rect;
uint32_t total;
int fd;
struct wl_listener frame_listener;
};
static uint32_t *
output_run(uint32_t *p, uint32_t delta, int run)
{
int i;
while (run > 0) {
if (run <= 0xe0) {
*p++ = delta | ((run - 1) << 24);
break;
}
i = 24 - __builtin_clz(run);
*p++ = delta | ((i + 0xe0) << 24);
run -= 1 << (7 + i);
}
return p;
}
static void
weston_recorder_frame_notify(struct wl_listener *listener, void *data)
{
struct weston_recorder *recorder =
container_of(listener, struct weston_recorder, frame_listener);
struct weston_output *output = recorder->output;
uint32_t msecs = * (uint32_t *) data;
pixman_box32_t *r;
pixman_region32_t damage;
int i, j, k, n, width, height, run, stride;
uint32_t delta, prev, *d, *s, *p, next;
struct {
uint32_t msecs;
uint32_t nrects;
} header;
struct iovec v[2];
pixman_region32_init(&damage);
pixman_region32_intersect(&damage, &recorder->output->region,
&recorder->output->previous_damage);
r = pixman_region32_rectangles(&damage, &n);
if (n == 0)
return;
header.msecs = msecs;
header.nrects = n;
v[0].iov_base = &header;
v[0].iov_len = sizeof header;
v[1].iov_base = r;
v[1].iov_len = n * sizeof *r;
recorder->total += writev(recorder->fd, v, 2);
stride = output->current->width;
for (i = 0; i < n; i++) {
width = r[i].x2 - r[i].x1;
height = r[i].y2 - r[i].y1;
glReadPixels(r[i].x1, output->current->height - r[i].y2,
width, height,
output->compositor->read_format,
GL_UNSIGNED_BYTE, recorder->rect);
s = recorder->rect;
p = recorder->rect;
run = prev = 0; /* quiet gcc */
for (j = 0; j < height; j++) {
d = recorder->frame +
stride * (r[i].y2 - j - 1) + r[i].x1;
for (k = 0; k < width; k++) {
next = *s++;
delta = (next - *d) & 0x00ffffff;
*d++ = next;
if (run == 0 || delta == prev) {
run++;
} else {
p = output_run(p, prev, run);
run = 1;
prev = delta;
}
}
}
p = output_run(p, prev, run);
recorder->total += write(recorder->fd,
recorder->rect,
(p - recorder->rect) * 4);
#if 0
fprintf(stderr,
"%dx%d at %d,%d rle from %d to %d bytes (%f) total %dM\n",
width, height, r[i].x1, r[i].y1,
width * height * 4, (int) (p - rect) * 4,
(float) (p - rect) / (width * height),
total / 1024 / 1024);
#endif
}
pixman_region32_fini(&damage);
}
static void
weston_recorder_create(struct weston_output *output, const char *filename)
{
struct weston_recorder *recorder;
int stride, size;
struct { uint32_t width, height; } header;
recorder = malloc(sizeof *recorder);
recorder->output = output;
stride = output->current->width;
size = stride * 4 * output->current->height;
recorder->frame = malloc(size);
recorder->rect = malloc(size);
recorder->total = 0;
memset(recorder->frame, 0, size);
recorder->fd = open(filename,
O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
header.width = output->current->width;
header.height = output->current->height;
recorder->total += write(recorder->fd, &header, sizeof header);
recorder->frame_listener.notify = weston_recorder_frame_notify;
wl_signal_add(&output->frame_signal, &recorder->frame_listener);
weston_output_damage(output);
}
static void
weston_recorder_destroy(struct weston_recorder *recorder)
{
wl_list_remove(&recorder->frame_listener.link);
close(recorder->fd);
free(recorder->frame);
free(recorder->rect);
free(recorder);
}
static void
recorder_binding(struct wl_seat *seat, uint32_t time,
uint32_t key, uint32_t button, uint32_t axis,
int32_t state, void *data)
{
struct weston_seat *ws = (struct weston_seat *) seat;
struct weston_compositor *ec = ws->compositor;
struct weston_output *output =
container_of(ec->output_list.next,
struct weston_output, link);
struct wl_listener *listener;
struct weston_recorder *recorder;
static const char filename[] = "capture.wcap";
listener = wl_signal_get(&output->frame_signal,
weston_recorder_frame_notify);
if (listener) {
recorder = container_of(listener, struct weston_recorder,
frame_listener);
fprintf(stderr, "stopping recorder, total file size %dM\n",
recorder->total / (1024 * 1024));
weston_recorder_destroy(recorder);
} else {
fprintf(stderr, "starting recorder, file %s\n", filename);
weston_recorder_create(output, filename);
}
}
static void static void
screenshooter_destroy(struct wl_listener *listener, void *data) screenshooter_destroy(struct wl_listener *listener, void *data)
{ {
@ -236,6 +417,8 @@ screenshooter_create(struct weston_compositor *ec)
shooter, bind_shooter); shooter, bind_shooter);
weston_compositor_add_binding(ec, KEY_S, 0, 0, MODIFIER_SUPER, weston_compositor_add_binding(ec, KEY_S, 0, 0, MODIFIER_SUPER,
screenshooter_binding, shooter); screenshooter_binding, shooter);
weston_compositor_add_binding(ec, KEY_R, 0, 0, MODIFIER_SUPER,
recorder_binding, shooter);
shooter->destroy_listener.notify = screenshooter_destroy; shooter->destroy_listener.notify = screenshooter_destroy;
wl_signal_add(&ec->destroy_signal, &shooter->destroy_listener); wl_signal_add(&ec->destroy_signal, &shooter->destroy_listener);

@ -0,0 +1,207 @@
/*
* Copyright © 2012 Intel Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <cairo.h>
struct wcap_header {
uint32_t width, height;
};
struct wcap_frame_header {
uint32_t msecs;
uint32_t nrects;
};
struct wcap_rectangle {
int32_t x1, y1, x2, y2;
};
struct wcap_decoder {
int fd;
size_t size;
void *map, *p, *end;
uint32_t *frame;
int width, height;
};
static void
wcap_decoder_decode_rectangle(struct wcap_decoder *decoder,
struct wcap_rectangle *rect)
{
uint32_t v, *p = decoder->p, *d;
int width = rect->x2 - rect->x1, height = rect->y2 - rect->y1;
int x, i, j, k, l, count = width * height;
d = decoder->frame + (rect->y2 - 1) * decoder->width;
x = rect->x1;
i = 0;
while (i < count) {
v = *p++;
l = v >> 24;
if (l < 0xe0) {
j = l + 1;
} else {
j = 1 << (l - 0xe0 + 7);
}
for (k = 0; k < j; k++) {
d[x] = (d[x] + v) | 0xff000000;
x++;
if (x == rect->x2) {
x = rect->x1;
d -= decoder->width;
}
}
i += j;
}
if (i != count)
printf("rle encoding longer than expected (%d expected %d)\n",
i, count);
decoder->p = p;
}
static int
wcap_decoder_get_frame(struct wcap_decoder *decoder)
{
struct wcap_rectangle *rects;
struct wcap_frame_header *header;
uint32_t *s;
uint32_t i;
int width, height;
if (decoder->p == decoder->end)
return 0;
header = decoder->p;
rects = (void *) (header + 1);
decoder->p = (uint32_t *) (rects + header->nrects);
for (i = 0; i < header->nrects; i++) {
width = rects[i].x2 - rects[i].x1;
height = rects[i].y2 - rects[i].y1;
wcap_decoder_decode_rectangle(decoder, &rects[i]);
}
return 1;
}
struct wcap_decoder *
wcap_decoder_create(const char *filename)
{
struct wcap_decoder *decoder;
struct wcap_header *header;
int frame_size;
struct stat buf;
decoder = malloc(sizeof *decoder);
if (decoder == NULL)
return NULL;
decoder->fd = open(filename, O_RDONLY);
if (decoder->fd == -1)
return NULL;
fstat(decoder->fd, &buf);
decoder->size = buf.st_size;
decoder->map = mmap(NULL, decoder->size,
PROT_READ, MAP_PRIVATE, decoder->fd, 0);
header = decoder->map;
decoder->width = header->width;
decoder->height = header->height;
decoder->p = header + 1;
decoder->end = decoder->map + decoder->size;
frame_size = header->width * header->height * 4;
decoder->frame = malloc(frame_size);
memset(decoder->frame, 0, frame_size);
return decoder;
}
void
wcap_decoder_destroy(struct wcap_decoder *decoder)
{
munmap(decoder->map, decoder->size);
free(decoder->frame);
free(decoder);
}
static void
write_png(struct wcap_decoder *decoder, const char *filename)
{
cairo_surface_t *surface;
surface = cairo_image_surface_create_for_data((unsigned char *) decoder->frame,
CAIRO_FORMAT_ARGB32,
decoder->width,
decoder->height,
decoder->width * 4);
cairo_surface_write_to_png(surface, filename);
cairo_surface_destroy(surface);
}
int main(int argc, char *argv[])
{
struct wcap_decoder *decoder;
int i, output_frame;
char filename[200];
if (argc != 2 && argc != 3) {
fprintf(stderr, "usage: wcap-decode WCAP_FILE [FRAME]\n");
return 1;
}
decoder = wcap_decoder_create(argv[1]);
output_frame = -1;
if (argc == 3)
output_frame = strtol(argv[2], NULL, 0);
i = 0;
while (wcap_decoder_get_frame(decoder)) {
if (i == output_frame) {
snprintf(filename, sizeof filename,
"wcap-frame-%d.png", i);
write_png(decoder, filename);
printf("wrote %s\n", filename);
}
i++;
}
printf("wcap file: size %dx%d, %d frames\n",
decoder->width, decoder->height, i);
wcap_decoder_destroy(decoder);
}
Loading…
Cancel
Save