From b9b397ef8094b221bc1042aedf0dbbbb5d9a5f1e Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Sun, 27 May 2018 04:03:29 +0100 Subject: Add a demo client for dmabuf export --- examples/dmabuf-capture.c | 767 ++++++++++++++++++++++++++++++++++++++++++++++ examples/meson.build | 10 + 2 files changed, 777 insertions(+) create mode 100644 examples/dmabuf-capture.c (limited to 'examples') diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c new file mode 100644 index 00000000..f249d437 --- /dev/null +++ b/examples/dmabuf-capture.c @@ -0,0 +1,767 @@ +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 199309L +#include +#include +#include +#include +#include +#include + +#include "wlr-export-dmabuf-unstable-v1-client-protocol.h" + +#include +#include +#include +#include + +struct wayland_output { + struct wl_list link; + uint32_t id; + struct wl_output *output; + char *make; + char *model; + int width; + int height; + AVRational framerate; +}; + +struct capture_context { + AVClass *class; /* For pretty logging */ + struct wl_display *display; + struct wl_registry *registry; + struct zwlr_export_dmabuf_manager_v1 *export_manager; + + struct wl_list output_list; + + /* Target */ + struct wl_output *target_output; + uint32_t target_client; + + /* Main frame callback */ + struct zwlr_export_dmabuf_frame_v1 *frame_callback; + + /* If something happens during capture */ + int err; + int quit; + + /* FFmpeg specific parts */ + AVFrame *current_frame; + AVBufferRef *drm_device_ref; + AVBufferRef *drm_frames_ref; + + AVBufferRef *mapped_device_ref; + AVBufferRef *mapped_frames_ref; + + AVFormatContext *avf; + AVCodecContext *avctx; + + int64_t start_pts; + + /* Config */ + enum AVPixelFormat software_format; + enum AVHWDeviceType hw_device_type; + AVDictionary *encoder_opts; + int is_software_encoder; + char *hardware_device; + char *out_filename; + char *encoder_name; + float out_bitrate; +}; + +static void output_handle_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { + struct wayland_output *output = data; + output->make = av_strdup(make); + output->model = av_strdup(model); +} + +static void output_handle_mode(void *data, struct wl_output *wl_output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + if (flags & WL_OUTPUT_MODE_CURRENT) { + struct wayland_output *output = data; + output->width = width; + output->height = height; + output->framerate = (AVRational){ refresh, 1000 }; + } +} + +static void output_handle_done(void* data, struct wl_output *wl_output) { + /* Nothing to do */ +} + +static void output_handle_scale(void* data, struct wl_output *wl_output, + int32_t factor) { + /* Nothing to do */ +} + +static const struct wl_output_listener output_listener = { + output_handle_geometry, + output_handle_mode, + output_handle_done, + output_handle_scale, +}; + +static void registry_handle_add(void *data, struct wl_registry *reg, + uint32_t id, const char *interface, uint32_t ver) { + struct capture_context *ctx = data; + + if (!strcmp(interface, wl_output_interface.name)) { + struct wayland_output *output = av_mallocz(sizeof(*output)); + + output->id = id; + output->output = wl_registry_bind(reg, id, &wl_output_interface, 1); + + wl_output_add_listener(output->output, &output_listener, output); + wl_list_insert(&ctx->output_list, &output->link); + } + + if (!strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { + ctx->export_manager = wl_registry_bind(reg, id, + &zwlr_export_dmabuf_manager_v1_interface, 1); + } +} + +static void remove_output(struct wayland_output *out) { + wl_list_remove(&out->link); + av_free(out->make); + av_free(out->model); + av_free(out); + return; +} + +static struct wayland_output *find_output(struct capture_context *ctx, + struct wl_output *out, uint32_t id) { + struct wayland_output *output, *tmp; + wl_list_for_each_safe(output, tmp, &ctx->output_list, link) + if ((output->output == out) || (output->id == id)) + return output; + return NULL; +} + +static void registry_handle_remove(void *data, struct wl_registry *reg, + uint32_t id) { + remove_output(find_output((struct capture_context *)data, NULL, id)); +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_add, + registry_handle_remove, +}; + +static void frame_free(void *opaque, uint8_t *data) { + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)data; + + for (int i = 0; i < desc->nb_objects; ++i) { + close(desc->objects[i].fd); + } + + zwlr_export_dmabuf_frame_v1_destroy(opaque); + + av_free(data); +} + +static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, + uint32_t buffer_flags, uint32_t flags, uint32_t format, + uint32_t mod_high, uint32_t mod_low, uint32_t num_objects, + uint32_t num_planes) { + struct capture_context *ctx = data; + int err = 0; + + /* Allocate DRM specific struct */ + AVDRMFrameDescriptor *desc = av_mallocz(sizeof(*desc)); + if (!desc) { + err = AVERROR(ENOMEM); + goto fail; + } + + desc->nb_objects = num_objects; + desc->objects[0].format_modifier = ((uint64_t)mod_high << 32) | mod_low; + + desc->nb_layers = 1; + desc->layers[0].format = format; + desc->layers[0].nb_planes = num_planes; + + /* Allocate a frame */ + AVFrame *f = av_frame_alloc(); + if (!f) { + err = AVERROR(ENOMEM); + goto fail; + } + + /* Set base frame properties */ + ctx->current_frame = f; + f->width = width; + f->height = height; + f->format = AV_PIX_FMT_DRM_PRIME; + + /* Set the frame data to the DRM specific struct */ + f->buf[0] = av_buffer_create((uint8_t*)desc, sizeof(*desc), + &frame_free, frame, 0); + if (!f->buf[0]) { + err = AVERROR(ENOMEM); + goto fail; + } + + f->data[0] = (uint8_t*)desc; + + return; + +fail: + ctx->err = err; + frame_free(frame, (uint8_t *)desc); +} + +static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t index, int32_t fd, uint32_t size) { + struct capture_context *ctx = data; + AVFrame *f = ctx->current_frame; + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; + + desc->objects[index].fd = fd; + desc->objects[index].size = size; +} + +static void frame_plane(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t index, uint32_t object_index, + uint32_t offset, uint32_t stride) { + struct capture_context *ctx = data; + AVFrame *f = ctx->current_frame; + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; + + desc->layers[0].planes[index].object_index = object_index; + desc->layers[0].planes[index].offset = offset; + desc->layers[0].planes[index].pitch = stride; +} + +static const uint32_t pixfmt_to_drm_map[] = { + [AV_PIX_FMT_NV12] = WL_SHM_FORMAT_NV12, + [AV_PIX_FMT_BGRA] = WL_SHM_FORMAT_ARGB8888, + [AV_PIX_FMT_BGR0] = WL_SHM_FORMAT_XRGB8888, + [AV_PIX_FMT_RGBA] = WL_SHM_FORMAT_ABGR8888, + [AV_PIX_FMT_RGB0] = WL_SHM_FORMAT_XBGR8888, + [AV_PIX_FMT_ABGR] = WL_SHM_FORMAT_RGBA8888, + [AV_PIX_FMT_0BGR] = WL_SHM_FORMAT_RGBX8888, + [AV_PIX_FMT_ARGB] = WL_SHM_FORMAT_BGRA8888, + [AV_PIX_FMT_0RGB] = WL_SHM_FORMAT_BGRX8888, +}; + +static enum AVPixelFormat drm_fmt_to_pixfmt(uint32_t fmt) { + for (enum AVPixelFormat i = 0; i < AV_PIX_FMT_NB; i++) { + if (pixfmt_to_drm_map[i] == fmt) { + return i; + } + } + return AV_PIX_FMT_NONE; +} + +static int attach_drm_frames_ref(struct capture_context *ctx, AVFrame *f, + enum AVPixelFormat sw_format) { + int err = 0; + AVHWFramesContext *hwfc; + + if (ctx->drm_frames_ref) { + hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data; + if (hwfc->width == f->width && hwfc->height == f->height && + hwfc->sw_format == sw_format) { + goto attach; + } + av_buffer_unref(&ctx->drm_frames_ref); + } + + ctx->drm_frames_ref = av_hwframe_ctx_alloc(ctx->drm_device_ref); + if (!ctx->drm_frames_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + + hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data; + + hwfc->format = f->format; + hwfc->sw_format = sw_format; + hwfc->width = f->width; + hwfc->height = f->height; + + err = av_hwframe_ctx_init(ctx->drm_frames_ref); + if (err) { + av_log(ctx, AV_LOG_ERROR, "AVHWFramesContext init failed: %s!\n", + av_err2str(err)); + goto fail; + } + +attach: + /* Set frame hardware context referencce */ + f->hw_frames_ctx = av_buffer_ref(ctx->drm_frames_ref); + if (!f->hw_frames_ctx) { + err = AVERROR(ENOMEM); + goto fail; + } + + return 0; + +fail: + av_buffer_unref(&ctx->drm_frames_ref); + return err; +} + +static void register_cb(struct capture_context *ctx); + +static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { + struct capture_context *ctx = data; + AVFrame *f = ctx->current_frame; + AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; + int err = 0; + + /* Attach the hardware frame context to the frame */ + err = attach_drm_frames_ref(ctx, f, drm_fmt_to_pixfmt(desc->layers[0].format)); + if (err) { + goto end; + } + + AVFrame *mapped_frame = av_frame_alloc(); + if (!mapped_frame) { + err = AVERROR(ENOMEM); + goto end; + } + + AVHWFramesContext *mapped_hwfc; + mapped_hwfc = (AVHWFramesContext *)ctx->mapped_frames_ref->data; + mapped_frame->format = mapped_hwfc->format; + + /* Set frame hardware context referencce */ + mapped_frame->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref); + if (!mapped_frame->hw_frames_ctx) { + err = AVERROR(ENOMEM); + goto end; + } + + err = av_hwframe_map(mapped_frame, f, 0); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Error mapping: %s!\n", av_err2str(err)); + goto end; + } + + AVFrame *enc_input = mapped_frame; + + if (ctx->is_software_encoder) { + AVFrame *soft_frame = av_frame_alloc(); + av_hwframe_transfer_data(soft_frame, mapped_frame, 0); + av_frame_free(&mapped_frame); + enc_input = soft_frame; + } + + /* Nanoseconds */ + enc_input->pts = (((uint64_t)tv_sec_hi) << 32) | tv_sec_lo; + enc_input->pts *= 1000000000; + enc_input->pts += tv_nsec; + + if (!ctx->start_pts) { + ctx->start_pts = enc_input->pts; + } + + enc_input->pts -= ctx->start_pts; + + enc_input->pts = av_rescale_q(enc_input->pts, (AVRational){ 1, 1000000000 }, + ctx->avctx->time_base); + + do { + err = avcodec_send_frame(ctx->avctx, enc_input); + + av_frame_free(&enc_input); + + if (err) { + av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", av_err2str(err)); + goto end; + } + + while (1) { + AVPacket pkt; + av_init_packet(&pkt); + + int ret = avcodec_receive_packet(ctx->avctx, &pkt); + if (ret == AVERROR(EAGAIN)) { + break; + } else if (ret == AVERROR_EOF) { + av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n"); + ctx->quit = 2; + goto end; + } else if (ret) { + av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", + av_err2str(ret)); + err = ret; + goto end; + } + + pkt.stream_index = 0; + err = av_interleaved_write_frame(ctx->avf, &pkt); + + av_packet_unref(&pkt); + + if (err) { + av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n", + av_err2str(err)); + goto end; + } + }; + } while (ctx->quit); + + av_log(NULL, AV_LOG_INFO, "Encoded frame %i!\n", ctx->avctx->frame_number); + + register_cb(ctx); + +end: + ctx->err = err; + av_frame_free(&ctx->current_frame); +} + +static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, + uint32_t reason) { + struct capture_context *ctx = data; + av_log(ctx, AV_LOG_WARNING, "Frame cancelled!\n"); + av_frame_free(&ctx->current_frame); + if (reason != ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT) + register_cb(ctx); +} + +static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { + frame_start, + frame_object, + frame_plane, + frame_ready, + frame_cancel, +}; + +static void register_cb(struct capture_context *ctx) +{ + ctx->frame_callback = + zwlr_export_dmabuf_manager_v1_capture_output(ctx->export_manager, 0, + ctx->target_output); + + zwlr_export_dmabuf_frame_v1_add_listener(ctx->frame_callback, + &frame_listener, ctx); +} + +static int init_lavu_hwcontext(struct capture_context *ctx) { + + /* DRM hwcontext */ + ctx->drm_device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); + if (!ctx->drm_device_ref) + return AVERROR(ENOMEM); + + AVHWDeviceContext *ref_data = (AVHWDeviceContext*)ctx->drm_device_ref->data; + AVDRMDeviceContext *hwctx = ref_data->hwctx; + + /* We don't need a device (we don't even know it and can't open it) */ + hwctx->fd = -1; + + av_hwdevice_ctx_init(ctx->drm_device_ref); + + /* Mapped hwcontext */ + int err = av_hwdevice_ctx_create(&ctx->mapped_device_ref, + ctx->hw_device_type, ctx->hardware_device, NULL, 0); + if (err < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to create a hardware device: %s\n", + av_err2str(err)); + return err; + } + + return 0; +} + +static int set_hwframe_ctx(struct capture_context *ctx, + AVBufferRef *hw_device_ctx) +{ + AVHWFramesContext *frames_ctx = NULL; + int err = 0; + + if (!(ctx->mapped_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { + return AVERROR(ENOMEM); + } + + AVHWFramesConstraints *cst = + av_hwdevice_get_hwframe_constraints(ctx->mapped_device_ref, NULL); + if (!cst) { + av_log(ctx, AV_LOG_ERROR, "Failed to get hw device constraints!\n"); + av_buffer_unref(&ctx->mapped_frames_ref); + return AVERROR(ENOMEM); + } + + frames_ctx = (AVHWFramesContext *)(ctx->mapped_frames_ref->data); + frames_ctx->format = cst->valid_hw_formats[0]; + frames_ctx->sw_format = ctx->avctx->pix_fmt; + frames_ctx->width = ctx->avctx->width; + frames_ctx->height = ctx->avctx->height; + frames_ctx->initial_pool_size = 16; + + av_hwframe_constraints_free(&cst); + + if ((err = av_hwframe_ctx_init(ctx->mapped_frames_ref)) < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to initialize hw frame context: %s!\n", + av_err2str(err)); + av_buffer_unref(&ctx->mapped_frames_ref); + return err; + } + + if (!ctx->is_software_encoder) { + ctx->avctx->pix_fmt = frames_ctx->format; + ctx->avctx->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref); + if (!ctx->avctx->hw_frames_ctx) { + av_buffer_unref(&ctx->mapped_frames_ref); + err = AVERROR(ENOMEM); + } + } + + return err; +} + +static int init_encoding(struct capture_context *ctx) { + int err; + + /* lavf init */ + err = avformat_alloc_output_context2(&ctx->avf, NULL, + NULL, ctx->out_filename); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Unable to init lavf context!\n"); + return err; + } + + AVStream *st = avformat_new_stream(ctx->avf, NULL); + if (!st) { + av_log(ctx, AV_LOG_ERROR, "Unable to alloc stream!\n"); + return 1; + } + + /* Find encoder */ + AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); + if (!out_codec) { + av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n"); + return AVERROR(EINVAL); + } + ctx->avf->oformat->video_codec = out_codec->id; + ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE); + + ctx->avctx = avcodec_alloc_context3(out_codec); + if (!ctx->avctx) + return 1; + + ctx->avctx->opaque = ctx; + ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; + ctx->avctx->pix_fmt = ctx->software_format; + ctx->avctx->time_base = (AVRational){ 1, 1000 }; + ctx->avctx->compression_level = 7; + ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; + ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; + + if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) + ctx->avctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + + st->id = 0; + st->time_base = ctx->avctx->time_base; + st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; + + /* Init hw frames context */ + err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); + if (err) + return err; + + err = avcodec_open2(ctx->avctx, out_codec, &ctx->encoder_opts); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Cannot open encoder: %s!\n", + av_err2str(err)); + return err; + } + + if (avcodec_parameters_from_context(st->codecpar, ctx->avctx) < 0) { + av_log(ctx, AV_LOG_ERROR, "Couldn't copy codec params: %s!\n", + av_err2str(err)); + return err; + } + + /* Debug print */ + av_dump_format(ctx->avf, 0, ctx->out_filename, 1); + + /* Open for writing */ + err = avio_open(&ctx->avf->pb, ctx->out_filename, AVIO_FLAG_WRITE); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Couldn't open %s: %s!\n", ctx->out_filename, + av_err2str(err)); + return err; + } + + err = avformat_write_header(ctx->avf, NULL); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Couldn't write header: %s!\n", av_err2str(err)); + return err; + } + + return err; +} + +struct capture_context *q_ctx = NULL; + +void on_quit_signal(int signo) { + printf("\r"); + q_ctx->quit = 1; +} + +static int main_loop(struct capture_context *ctx) { + int err; + + q_ctx = ctx; + + if (signal(SIGINT, on_quit_signal) == SIG_ERR) { + av_log(ctx, AV_LOG_ERROR, "Unable to install signal handler!\n"); + return AVERROR(EINVAL); + } + + err = init_lavu_hwcontext(ctx); + if (err) + return err; + + err = init_encoding(ctx); + if (err) + return err; + + /* Start the frame callback */ + register_cb(ctx); + + while (!ctx->err && ctx->quit < 2) { + while (wl_display_prepare_read(ctx->display) != 0) { + wl_display_dispatch_pending(ctx->display); + } + + wl_display_flush(ctx->display); + + struct pollfd fds[1] = { + { .fd = wl_display_get_fd(ctx->display), .events = POLLIN }, + }; + + poll(fds, 1, -1); + + if (!(fds[0].revents & POLLIN)) { + wl_display_cancel_read(ctx->display); + } + + if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + av_log(ctx, AV_LOG_ERROR, "Error occurred on the display fd!\n"); + break; + } + + if (fds[0].revents & POLLIN) { + if (wl_display_read_events(ctx->display) < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to read Wayland events!\n"); + break; + } + wl_display_dispatch_pending(ctx->display); + } + } + + err = av_write_trailer(ctx->avf); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Error writing trailer: %s!\n", + av_err2str(err)); + return err; + } + + av_log(ctx, AV_LOG_INFO, "Wrote trailer!\n"); + + return ctx->err; +} + +static int init(struct capture_context *ctx) { + ctx->display = wl_display_connect(NULL); + if (!ctx->display) { + av_log(ctx, AV_LOG_ERROR, "Failed to connect to display!\n"); + return AVERROR(EINVAL); + } + + wl_list_init(&ctx->output_list); + + ctx->registry = wl_display_get_registry(ctx->display); + wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); + + wl_display_roundtrip(ctx->display); + wl_display_dispatch(ctx->display); + + if (!ctx->export_manager) { + av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n", + zwlr_export_dmabuf_manager_v1_interface.name); + return -1; + } + + return 0; +} + +static void print_capturable_surfaces(struct capture_context *ctx) { + + struct wayland_output *o, *tmp_o; + wl_list_for_each_reverse_safe(o, tmp_o, &ctx->output_list, link) { + ctx->target_output = o->output; /* Default is first, whatever */ + av_log(ctx, AV_LOG_INFO, "Capturable output: %s Model: %s:\n", + o->make, o->model); + } + + av_log(ctx, AV_LOG_INFO, "Capturing from output: %s!\n", + find_output(ctx, ctx->target_output, 0)->model); +} + +static void uninit(struct capture_context *ctx); + +int main(int argc, char *argv[]) { + int err; + struct capture_context ctx = { 0 }; + ctx.class = &((AVClass) { + .class_name = "dmabuf-capture", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, + }); + + err = init(&ctx); + if (err) + goto end; + + print_capturable_surfaces(&ctx); + + ctx.hw_device_type = av_hwdevice_find_type_by_name("vaapi"); + ctx.hardware_device = "/dev/dri/renderD128"; + + ctx.encoder_name = "libx264"; + ctx.software_format = av_get_pix_fmt("nv12"); + av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0); + + ctx.out_filename = "dmabuf_recording_01.mkv"; + ctx.out_bitrate = 29.2f; /* Mbps */ + + err = main_loop(&ctx); + if (err) + goto end; + +end: + uninit(&ctx); + return err; +} + +static void uninit(struct capture_context *ctx) { + struct wayland_output *output, *tmp_o; + wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) + remove_output(output); + + if (ctx->export_manager) + zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); + + av_buffer_unref(&ctx->drm_frames_ref); + av_buffer_unref(&ctx->drm_device_ref); + av_buffer_unref(&ctx->mapped_frames_ref); + av_buffer_unref(&ctx->mapped_device_ref); + + av_dict_free(&ctx->encoder_opts); + + avcodec_close(ctx->avctx); + if (ctx->avf) { + avio_closep(&ctx->avf->pb); + } + avformat_free_context(ctx->avf); +} diff --git a/examples/meson.build b/examples/meson.build index 4725b989..6a0bc46c 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,6 +1,10 @@ threads = dependency('threads') wayland_cursor = dependency('wayland-cursor') +libavutil = dependency('libavutil') +libavcodec = dependency('libavcodec') +libavformat = dependency('libavformat') + executable('simple', 'simple.c', dependencies: wlroots) executable('pointer', 'pointer.c', dependencies: wlroots) executable('touch', 'touch.c', 'cat.c', dependencies: wlroots) @@ -38,3 +42,9 @@ executable( 'input-inhibitor.c', dependencies: [wayland_cursor, wayland_client, wlr_protos, wlroots] ) + +executable( + 'dmabuf-capture', + 'dmabuf-capture.c', + dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat] +) -- cgit v1.2.3 From f204a9127c69cd896d3667a38e3bfb34377715c9 Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Mon, 28 May 2018 00:55:13 +0100 Subject: Command line parsing --- examples/dmabuf-capture.c | 62 ++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 28 deletions(-) (limited to 'examples') diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index f249d437..a1d7d965 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -35,7 +35,6 @@ struct capture_context { /* Target */ struct wl_output *target_output; - uint32_t target_client; /* Main frame callback */ struct zwlr_export_dmabuf_frame_v1 *frame_callback; @@ -80,10 +79,10 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output, static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { if (flags & WL_OUTPUT_MODE_CURRENT) { - struct wayland_output *output = data; - output->width = width; - output->height = height; - output->framerate = (AVRational){ refresh, 1000 }; + struct wayland_output *output = data; + output->width = width; + output->height = height; + output->framerate = (AVRational){ refresh, 1000 }; } } @@ -494,11 +493,10 @@ static int set_hwframe_ctx(struct capture_context *ctx, frames_ctx->sw_format = ctx->avctx->pix_fmt; frames_ctx->width = ctx->avctx->width; frames_ctx->height = ctx->avctx->height; - frames_ctx->initial_pool_size = 16; av_hwframe_constraints_free(&cst); - if ((err = av_hwframe_ctx_init(ctx->mapped_frames_ref)) < 0) { + if ((err = av_hwframe_ctx_init(ctx->mapped_frames_ref))) { av_log(ctx, AV_LOG_ERROR, "Failed to initialize hw frame context: %s!\n", av_err2str(err)); av_buffer_unref(&ctx->mapped_frames_ref); @@ -695,19 +693,6 @@ static int init(struct capture_context *ctx) { return 0; } -static void print_capturable_surfaces(struct capture_context *ctx) { - - struct wayland_output *o, *tmp_o; - wl_list_for_each_reverse_safe(o, tmp_o, &ctx->output_list, link) { - ctx->target_output = o->output; /* Default is first, whatever */ - av_log(ctx, AV_LOG_INFO, "Capturable output: %s Model: %s:\n", - o->make, o->model); - } - - av_log(ctx, AV_LOG_INFO, "Capturing from output: %s!\n", - find_output(ctx, ctx->target_output, 0)->model); -} - static void uninit(struct capture_context *ctx); int main(int argc, char *argv[]) { @@ -723,17 +708,38 @@ int main(int argc, char *argv[]) { if (err) goto end; - print_capturable_surfaces(&ctx); + struct wayland_output *o, *tmp_o; + wl_list_for_each_reverse_safe(o, tmp_o, &ctx.output_list, link) { + printf("Capturable output: %s Model: %s: ID: %i\n", + o->make, o->model, o->id); + } + + if (argc != 8) { + printf("Invalid number of arguments! Usage and example:\n" + "./dmabuf-capture " + " \n" + "./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 " + "dmabuf_recording_01.mkv\n"); + return 1; + } - ctx.hw_device_type = av_hwdevice_find_type_by_name("vaapi"); - ctx.hardware_device = "/dev/dri/renderD128"; + const int o_id = strtol(argv[1], NULL, 10); + o = find_output(&ctx, NULL, o_id); + if (!o) { + printf("Unable to find output with ID %i!\n", o_id); + return 1; + } - ctx.encoder_name = "libx264"; - ctx.software_format = av_get_pix_fmt("nv12"); - av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0); + ctx.target_output = o->output; + ctx.hw_device_type = av_hwdevice_find_type_by_name(argv[2]); + ctx.hardware_device = argv[3]; + + ctx.encoder_name = argv[4]; + ctx.software_format = av_get_pix_fmt(argv[5]); + ctx.out_bitrate = strtof(argv[6], NULL); + ctx.out_filename = argv[7]; - ctx.out_filename = "dmabuf_recording_01.mkv"; - ctx.out_bitrate = 29.2f; /* Mbps */ + av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0); err = main_loop(&ctx); if (err) -- cgit v1.2.3 From 2198fd5eeda18fee7aecae5b3aaf8e6a207d372e Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 28 May 2018 08:20:22 +0100 Subject: examples/dmabuf-capture: fix indentation --- examples/dmabuf-capture.c | 176 +++++++++++++++++++++++----------------------- 1 file changed, 88 insertions(+), 88 deletions(-) (limited to 'examples') diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index a1d7d965..6f7f9d70 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -48,13 +48,13 @@ struct capture_context { AVBufferRef *drm_device_ref; AVBufferRef *drm_frames_ref; - AVBufferRef *mapped_device_ref; - AVBufferRef *mapped_frames_ref; + AVBufferRef *mapped_device_ref; + AVBufferRef *mapped_frames_ref; - AVFormatContext *avf; - AVCodecContext *avctx; + AVFormatContext *avf; + AVCodecContext *avctx; - int64_t start_pts; + int64_t start_pts; /* Config */ enum AVPixelFormat software_format; @@ -136,7 +136,7 @@ static struct wayland_output *find_output(struct capture_context *ctx, wl_list_for_each_safe(output, tmp, &ctx->output_list, link) if ((output->output == out) || (output->id == id)) return output; - return NULL; + return NULL; } static void registry_handle_remove(void *data, struct wl_registry *reg, @@ -328,7 +328,7 @@ static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, AVHWFramesContext *mapped_hwfc; mapped_hwfc = (AVHWFramesContext *)ctx->mapped_frames_ref->data; - mapped_frame->format = mapped_hwfc->format; + mapped_frame->format = mapped_hwfc->format; /* Set frame hardware context referencce */ mapped_frame->hw_frames_ctx = av_buffer_ref(ctx->mapped_frames_ref); @@ -339,8 +339,8 @@ static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, err = av_hwframe_map(mapped_frame, f, 0); if (err) { - av_log(ctx, AV_LOG_ERROR, "Error mapping: %s!\n", av_err2str(err)); - goto end; + av_log(ctx, AV_LOG_ERROR, "Error mapping: %s!\n", av_err2str(err)); + goto end; } AVFrame *enc_input = mapped_frame; @@ -369,7 +369,7 @@ static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, do { err = avcodec_send_frame(ctx->avctx, enc_input); - av_frame_free(&enc_input); + av_frame_free(&enc_input); if (err) { av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n", av_err2str(err)); @@ -473,12 +473,12 @@ static int init_lavu_hwcontext(struct capture_context *ctx) { static int set_hwframe_ctx(struct capture_context *ctx, AVBufferRef *hw_device_ctx) { - AVHWFramesContext *frames_ctx = NULL; - int err = 0; + AVHWFramesContext *frames_ctx = NULL; + int err = 0; - if (!(ctx->mapped_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { - return AVERROR(ENOMEM); - } + if (!(ctx->mapped_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { + return AVERROR(ENOMEM); + } AVHWFramesConstraints *cst = av_hwdevice_get_hwframe_constraints(ctx->mapped_device_ref, NULL); @@ -512,7 +512,7 @@ static int set_hwframe_ctx(struct capture_context *ctx, } } - return err; + return err; } static int init_encoding(struct capture_context *ctx) { @@ -521,79 +521,79 @@ static int init_encoding(struct capture_context *ctx) { /* lavf init */ err = avformat_alloc_output_context2(&ctx->avf, NULL, NULL, ctx->out_filename); - if (err) { - av_log(ctx, AV_LOG_ERROR, "Unable to init lavf context!\n"); - return err; - } + if (err) { + av_log(ctx, AV_LOG_ERROR, "Unable to init lavf context!\n"); + return err; + } - AVStream *st = avformat_new_stream(ctx->avf, NULL); - if (!st) { - av_log(ctx, AV_LOG_ERROR, "Unable to alloc stream!\n"); - return 1; - } + AVStream *st = avformat_new_stream(ctx->avf, NULL); + if (!st) { + av_log(ctx, AV_LOG_ERROR, "Unable to alloc stream!\n"); + return 1; + } /* Find encoder */ - AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); - if (!out_codec) { - av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n"); - return AVERROR(EINVAL); - } - ctx->avf->oformat->video_codec = out_codec->id; - ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE); + AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); + if (!out_codec) { + av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n"); + return AVERROR(EINVAL); + } + ctx->avf->oformat->video_codec = out_codec->id; + ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE); ctx->avctx = avcodec_alloc_context3(out_codec); - if (!ctx->avctx) - return 1; + if (!ctx->avctx) + return 1; - ctx->avctx->opaque = ctx; - ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; - ctx->avctx->pix_fmt = ctx->software_format; - ctx->avctx->time_base = (AVRational){ 1, 1000 }; - ctx->avctx->compression_level = 7; - ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; - ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; + ctx->avctx->opaque = ctx; + ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; + ctx->avctx->pix_fmt = ctx->software_format; + ctx->avctx->time_base = (AVRational){ 1, 1000 }; + ctx->avctx->compression_level = 7; + ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; + ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) ctx->avctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; st->id = 0; - st->time_base = ctx->avctx->time_base; - st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; + st->time_base = ctx->avctx->time_base; + st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; - /* Init hw frames context */ - err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); - if (err) - return err; + /* Init hw frames context */ + err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); + if (err) + return err; err = avcodec_open2(ctx->avctx, out_codec, &ctx->encoder_opts); if (err) { - av_log(ctx, AV_LOG_ERROR, "Cannot open encoder: %s!\n", - av_err2str(err)); - return err; - } + av_log(ctx, AV_LOG_ERROR, "Cannot open encoder: %s!\n", + av_err2str(err)); + return err; + } if (avcodec_parameters_from_context(st->codecpar, ctx->avctx) < 0) { - av_log(ctx, AV_LOG_ERROR, "Couldn't copy codec params: %s!\n", - av_err2str(err)); - return err; - } - - /* Debug print */ - av_dump_format(ctx->avf, 0, ctx->out_filename, 1); - - /* Open for writing */ - err = avio_open(&ctx->avf->pb, ctx->out_filename, AVIO_FLAG_WRITE); - if (err) { - av_log(ctx, AV_LOG_ERROR, "Couldn't open %s: %s!\n", ctx->out_filename, - av_err2str(err)); - return err; - } + av_log(ctx, AV_LOG_ERROR, "Couldn't copy codec params: %s!\n", + av_err2str(err)); + return err; + } + + /* Debug print */ + av_dump_format(ctx->avf, 0, ctx->out_filename, 1); + + /* Open for writing */ + err = avio_open(&ctx->avf->pb, ctx->out_filename, AVIO_FLAG_WRITE); + if (err) { + av_log(ctx, AV_LOG_ERROR, "Couldn't open %s: %s!\n", ctx->out_filename, + av_err2str(err)); + return err; + } err = avformat_write_header(ctx->avf, NULL); - if (err) { - av_log(ctx, AV_LOG_ERROR, "Couldn't write header: %s!\n", av_err2str(err)); - return err; - } + if (err) { + av_log(ctx, AV_LOG_ERROR, "Couldn't write header: %s!\n", av_err2str(err)); + return err; + } return err; } @@ -608,12 +608,12 @@ void on_quit_signal(int signo) { static int main_loop(struct capture_context *ctx) { int err; - q_ctx = ctx; + q_ctx = ctx; - if (signal(SIGINT, on_quit_signal) == SIG_ERR) { + if (signal(SIGINT, on_quit_signal) == SIG_ERR) { av_log(ctx, AV_LOG_ERROR, "Unable to install signal handler!\n"); - return AVERROR(EINVAL); - } + return AVERROR(EINVAL); + } err = init_lavu_hwcontext(ctx); if (err) @@ -627,11 +627,11 @@ static int main_loop(struct capture_context *ctx) { register_cb(ctx); while (!ctx->err && ctx->quit < 2) { - while (wl_display_prepare_read(ctx->display) != 0) { + while (wl_display_prepare_read(ctx->display) != 0) { wl_display_dispatch_pending(ctx->display); } - wl_display_flush(ctx->display); + wl_display_flush(ctx->display); struct pollfd fds[1] = { { .fd = wl_display_get_fd(ctx->display), .events = POLLIN }, @@ -649,7 +649,7 @@ static int main_loop(struct capture_context *ctx) { } if (fds[0].revents & POLLIN) { - if (wl_display_read_events(ctx->display) < 0) { + if (wl_display_read_events(ctx->display) < 0) { av_log(ctx, AV_LOG_ERROR, "Failed to read Wayland events!\n"); break; } @@ -658,13 +658,13 @@ static int main_loop(struct capture_context *ctx) { } err = av_write_trailer(ctx->avf); - if (err) { - av_log(ctx, AV_LOG_ERROR, "Error writing trailer: %s!\n", - av_err2str(err)); - return err; - } + if (err) { + av_log(ctx, AV_LOG_ERROR, "Error writing trailer: %s!\n", + av_err2str(err)); + return err; + } - av_log(ctx, AV_LOG_INFO, "Wrote trailer!\n"); + av_log(ctx, AV_LOG_INFO, "Wrote trailer!\n"); return ctx->err; } @@ -679,7 +679,7 @@ static int init(struct capture_context *ctx) { wl_list_init(&ctx->output_list); ctx->registry = wl_display_get_registry(ctx->display); - wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); + wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); wl_display_roundtrip(ctx->display); wl_display_dispatch(ctx->display); @@ -721,7 +721,7 @@ int main(int argc, char *argv[]) { "./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 " "dmabuf_recording_01.mkv\n"); return 1; - } + } const int o_id = strtol(argv[1], NULL, 10); o = find_output(&ctx, NULL, o_id); @@ -759,9 +759,9 @@ static void uninit(struct capture_context *ctx) { zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); av_buffer_unref(&ctx->drm_frames_ref); - av_buffer_unref(&ctx->drm_device_ref); - av_buffer_unref(&ctx->mapped_frames_ref); - av_buffer_unref(&ctx->mapped_device_ref); + av_buffer_unref(&ctx->drm_device_ref); + av_buffer_unref(&ctx->mapped_frames_ref); + av_buffer_unref(&ctx->mapped_device_ref); av_dict_free(&ctx->encoder_opts); -- cgit v1.2.3 From 85b6b4b0c888deba26b2311a9a509d60160e6644 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 28 May 2018 08:24:25 +0100 Subject: examples/dmabuf-capture: make building this example optional --- examples/meson.build | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/meson.build b/examples/meson.build index 6a0bc46c..d82bd256 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,9 +1,9 @@ threads = dependency('threads') wayland_cursor = dependency('wayland-cursor') -libavutil = dependency('libavutil') -libavcodec = dependency('libavcodec') -libavformat = dependency('libavformat') +libavutil = dependency('libavutil', required: false) +libavcodec = dependency('libavcodec', required: false) +libavformat = dependency('libavformat', required: false) executable('simple', 'simple.c', dependencies: wlroots) executable('pointer', 'pointer.c', dependencies: wlroots) @@ -43,8 +43,10 @@ executable( dependencies: [wayland_cursor, wayland_client, wlr_protos, wlroots] ) -executable( - 'dmabuf-capture', - 'dmabuf-capture.c', - dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat] -) +if libavutil.found() and libavcodec.found() and libavformat.found() + executable( + 'dmabuf-capture', + 'dmabuf-capture.c', + dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat] + ) +endif -- cgit v1.2.3 From 70d324a0f9b94952b7e4fbbbcce76f8c570ced03 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 28 May 2018 08:37:18 +0100 Subject: examples/dmabuf-capture: simplify event loop and fix style --- examples/dmabuf-capture.c | 163 ++++++++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 91 deletions(-) (limited to 'examples') diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index 6f7f9d70..cd8a9267 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -1,19 +1,17 @@ #define _XOPEN_SOURCE 700 #define _POSIX_C_SOURCE 199309L +#include +#include +#include +#include #include +#include #include #include #include #include -#include - #include "wlr-export-dmabuf-unstable-v1-client-protocol.h" -#include -#include -#include -#include - struct wayland_output { struct wl_list link; uint32_t id; @@ -72,7 +70,7 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t subpixel, const char *make, const char *model, int32_t transform) { struct wayland_output *output = data; - output->make = av_strdup(make); + output->make = av_strdup(make); output->model = av_strdup(model); } @@ -80,8 +78,8 @@ static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { if (flags & WL_OUTPUT_MODE_CURRENT) { struct wayland_output *output = data; - output->width = width; - output->height = height; + output->width = width; + output->height = height; output->framerate = (AVRational){ refresh, 1000 }; } } @@ -96,10 +94,10 @@ static void output_handle_scale(void* data, struct wl_output *wl_output, } static const struct wl_output_listener output_listener = { - output_handle_geometry, - output_handle_mode, - output_handle_done, - output_handle_scale, + .geometry = output_handle_geometry, + .mode = output_handle_mode, + .done = output_handle_done, + .scale = output_handle_scale, }; static void registry_handle_add(void *data, struct wl_registry *reg, @@ -109,7 +107,7 @@ static void registry_handle_add(void *data, struct wl_registry *reg, if (!strcmp(interface, wl_output_interface.name)) { struct wayland_output *output = av_mallocz(sizeof(*output)); - output->id = id; + output->id = id; output->output = wl_registry_bind(reg, id, &wl_output_interface, 1); wl_output_add_listener(output->output, &output_listener, output); @@ -117,8 +115,8 @@ static void registry_handle_add(void *data, struct wl_registry *reg, } if (!strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { - ctx->export_manager = wl_registry_bind(reg, id, - &zwlr_export_dmabuf_manager_v1_interface, 1); + ctx->export_manager = wl_registry_bind(reg, id, + &zwlr_export_dmabuf_manager_v1_interface, 1); } } @@ -127,15 +125,16 @@ static void remove_output(struct wayland_output *out) { av_free(out->make); av_free(out->model); av_free(out); - return; } static struct wayland_output *find_output(struct capture_context *ctx, struct wl_output *out, uint32_t id) { struct wayland_output *output, *tmp; - wl_list_for_each_safe(output, tmp, &ctx->output_list, link) - if ((output->output == out) || (output->id == id)) + wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { + if ((output->output == out) || (output->id == id)) { return output; + } + } return NULL; } @@ -145,8 +144,8 @@ static void registry_handle_remove(void *data, struct wl_registry *reg, } static const struct wl_registry_listener registry_listener = { - registry_handle_add, - registry_handle_remove, + .global = registry_handle_add, + .global_remove = registry_handle_remove, }; static void frame_free(void *opaque, uint8_t *data) { @@ -192,7 +191,7 @@ static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, /* Set base frame properties */ ctx->current_frame = f; - f->width = width; + f->width = width; f->height = height; f->format = AV_PIX_FMT_DRM_PRIME; @@ -219,7 +218,7 @@ static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, AVFrame *f = ctx->current_frame; AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; - desc->objects[index].fd = fd; + desc->objects[index].fd = fd; desc->objects[index].size = size; } @@ -231,8 +230,8 @@ static void frame_plane(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; desc->layers[0].planes[index].object_index = object_index; - desc->layers[0].planes[index].offset = offset; - desc->layers[0].planes[index].pitch = stride; + desc->layers[0].planes[index].offset = offset; + desc->layers[0].planes[index].pitch = stride; } static const uint32_t pixfmt_to_drm_map[] = { @@ -278,10 +277,10 @@ static int attach_drm_frames_ref(struct capture_context *ctx, AVFrame *f, hwfc = (AVHWFramesContext*)ctx->drm_frames_ref->data; - hwfc->format = f->format; + hwfc->format = f->format; hwfc->sw_format = sw_format; - hwfc->width = f->width; - hwfc->height = f->height; + hwfc->width = f->width; + hwfc->height = f->height; err = av_hwframe_ctx_init(ctx->drm_frames_ref); if (err) { @@ -421,30 +420,31 @@ static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, struct capture_context *ctx = data; av_log(ctx, AV_LOG_WARNING, "Frame cancelled!\n"); av_frame_free(&ctx->current_frame); - if (reason != ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT) + if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT) { + av_log(ctx, AV_LOG_ERROR, "Permanent failure, exiting\n"); + ctx->err = 1; + } else { register_cb(ctx); + } } static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { - frame_start, - frame_object, - frame_plane, - frame_ready, - frame_cancel, + .frame = frame_start, + .object = frame_object, + .plane = frame_plane, + .ready = frame_ready, + .cancel = frame_cancel, }; -static void register_cb(struct capture_context *ctx) -{ - ctx->frame_callback = - zwlr_export_dmabuf_manager_v1_capture_output(ctx->export_manager, 0, - ctx->target_output); +static void register_cb(struct capture_context *ctx) { + ctx->frame_callback = zwlr_export_dmabuf_manager_v1_capture_output( + ctx->export_manager, 0, ctx->target_output); zwlr_export_dmabuf_frame_v1_add_listener(ctx->frame_callback, &frame_listener, ctx); } static int init_lavu_hwcontext(struct capture_context *ctx) { - /* DRM hwcontext */ ctx->drm_device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); if (!ctx->drm_device_ref) @@ -471,8 +471,7 @@ static int init_lavu_hwcontext(struct capture_context *ctx) { } static int set_hwframe_ctx(struct capture_context *ctx, - AVBufferRef *hw_device_ctx) -{ + AVBufferRef *hw_device_ctx) { AVHWFramesContext *frames_ctx = NULL; int err = 0; @@ -489,10 +488,10 @@ static int set_hwframe_ctx(struct capture_context *ctx, } frames_ctx = (AVHWFramesContext *)(ctx->mapped_frames_ref->data); - frames_ctx->format = cst->valid_hw_formats[0]; + frames_ctx->format = cst->valid_hw_formats[0]; frames_ctx->sw_format = ctx->avctx->pix_fmt; - frames_ctx->width = ctx->avctx->width; - frames_ctx->height = ctx->avctx->height; + frames_ctx->width = ctx->avctx->width; + frames_ctx->height = ctx->avctx->height; av_hwframe_constraints_free(&cst); @@ -545,25 +544,27 @@ static int init_encoding(struct capture_context *ctx) { if (!ctx->avctx) return 1; - ctx->avctx->opaque = ctx; - ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; - ctx->avctx->pix_fmt = ctx->software_format; - ctx->avctx->time_base = (AVRational){ 1, 1000 }; + ctx->avctx->opaque = ctx; + ctx->avctx->bit_rate = (int)ctx->out_bitrate*1000000.0f; + ctx->avctx->pix_fmt = ctx->software_format; + ctx->avctx->time_base = (AVRational){ 1, 1000 }; ctx->avctx->compression_level = 7; - ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; - ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; + ctx->avctx->width = find_output(ctx, ctx->target_output, 0)->width; + ctx->avctx->height = find_output(ctx, ctx->target_output, 0)->height; - if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) + if (ctx->avf->oformat->flags & AVFMT_GLOBALHEADER) { ctx->avctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + } - st->id = 0; - st->time_base = ctx->avctx->time_base; + st->id = 0; + st->time_base = ctx->avctx->time_base; st->avg_frame_rate = find_output(ctx, ctx->target_output, 0)->framerate; /* Init hw frames context */ err = set_hwframe_ctx(ctx, ctx->mapped_device_ref); - if (err) + if (err) { return err; + } err = avcodec_open2(ctx->avctx, out_codec, &ctx->encoder_opts); if (err) { @@ -616,45 +617,21 @@ static int main_loop(struct capture_context *ctx) { } err = init_lavu_hwcontext(ctx); - if (err) + if (err) { return err; + } err = init_encoding(ctx); - if (err) + if (err) { return err; + } /* Start the frame callback */ register_cb(ctx); - while (!ctx->err && ctx->quit < 2) { - while (wl_display_prepare_read(ctx->display) != 0) { - wl_display_dispatch_pending(ctx->display); - } - - wl_display_flush(ctx->display); - - struct pollfd fds[1] = { - { .fd = wl_display_get_fd(ctx->display), .events = POLLIN }, - }; - - poll(fds, 1, -1); - - if (!(fds[0].revents & POLLIN)) { - wl_display_cancel_read(ctx->display); - } - - if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { - av_log(ctx, AV_LOG_ERROR, "Error occurred on the display fd!\n"); - break; - } - - if (fds[0].revents & POLLIN) { - if (wl_display_read_events(ctx->display) < 0) { - av_log(ctx, AV_LOG_ERROR, "Failed to read Wayland events!\n"); - break; - } - wl_display_dispatch_pending(ctx->display); - } + while (wl_display_dispatch(ctx->display) != -1 && !ctx->err && + ctx->quit < 2) { + // This space intentionally left blank } err = av_write_trailer(ctx->avf); @@ -705,8 +682,9 @@ int main(int argc, char *argv[]) { }); err = init(&ctx); - if (err) + if (err) { goto end; + } struct wayland_output *o, *tmp_o; wl_list_for_each_reverse_safe(o, tmp_o, &ctx.output_list, link) { @@ -742,8 +720,9 @@ int main(int argc, char *argv[]) { av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0); err = main_loop(&ctx); - if (err) + if (err) { goto end; + } end: uninit(&ctx); @@ -752,11 +731,13 @@ end: static void uninit(struct capture_context *ctx) { struct wayland_output *output, *tmp_o; - wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) + wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) { remove_output(output); + } - if (ctx->export_manager) + if (ctx->export_manager) { zwlr_export_dmabuf_manager_v1_destroy(ctx->export_manager); + } av_buffer_unref(&ctx->drm_frames_ref); av_buffer_unref(&ctx->drm_device_ref); -- cgit v1.2.3 From 9eddcbc376ff92a3a03002b910c31bf96bdba2da Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Sun, 17 Jun 2018 14:06:52 +0100 Subject: Update example and protocol --- examples/dmabuf-capture.c | 28 ++--- protocol/wlr-export-dmabuf-unstable-v1.xml | 180 +++++++++++++++-------------- 2 files changed, 102 insertions(+), 106 deletions(-) (limited to 'examples') diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index cd8a9267..2f7db2f1 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -163,8 +163,7 @@ static void frame_free(void *opaque, uint8_t *data) { static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, uint32_t buffer_flags, uint32_t flags, uint32_t format, - uint32_t mod_high, uint32_t mod_low, uint32_t num_objects, - uint32_t num_planes) { + uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) { struct capture_context *ctx = data; int err = 0; @@ -180,7 +179,6 @@ static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, desc->nb_layers = 1; desc->layers[0].format = format; - desc->layers[0].nb_planes = num_planes; /* Allocate a frame */ AVFrame *f = av_frame_alloc(); @@ -213,25 +211,18 @@ fail: } static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, - uint32_t index, int32_t fd, uint32_t size) { + uint32_t index, int32_t fd, uint32_t size, uint32_t offset, + uint32_t stride, uint32_t plane_index) { struct capture_context *ctx = data; AVFrame *f = ctx->current_frame; AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; desc->objects[index].fd = fd; desc->objects[index].size = size; -} - -static void frame_plane(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, - uint32_t index, uint32_t object_index, - uint32_t offset, uint32_t stride) { - struct capture_context *ctx = data; - AVFrame *f = ctx->current_frame; - AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; - desc->layers[0].planes[index].object_index = object_index; - desc->layers[0].planes[index].offset = offset; - desc->layers[0].planes[index].pitch = stride; + desc->layers[0].planes[plane_index].object_index = index; + desc->layers[0].planes[plane_index].offset = offset; + desc->layers[0].planes[plane_index].pitch = stride; } static const uint32_t pixfmt_to_drm_map[] = { @@ -311,14 +302,18 @@ static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, struct capture_context *ctx = data; AVFrame *f = ctx->current_frame; AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)f->data[0]; + enum AVPixelFormat pix_fmt = drm_fmt_to_pixfmt(desc->layers[0].format); int err = 0; /* Attach the hardware frame context to the frame */ - err = attach_drm_frames_ref(ctx, f, drm_fmt_to_pixfmt(desc->layers[0].format)); + err = attach_drm_frames_ref(ctx, f, pix_fmt); if (err) { goto end; } + /* TODO: support multiplane stuff */ + desc->layers[0].nb_planes = av_pix_fmt_count_planes(pix_fmt); + AVFrame *mapped_frame = av_frame_alloc(); if (!mapped_frame) { err = AVERROR(ENOMEM); @@ -431,7 +426,6 @@ static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { .frame = frame_start, .object = frame_object, - .plane = frame_plane, .ready = frame_ready, .cancel = frame_cancel, }; diff --git a/protocol/wlr-export-dmabuf-unstable-v1.xml b/protocol/wlr-export-dmabuf-unstable-v1.xml index 760345a7..751f7efb 100644 --- a/protocol/wlr-export-dmabuf-unstable-v1.xml +++ b/protocol/wlr-export-dmabuf-unstable-v1.xml @@ -1,6 +1,5 @@ - Copyright © 2018 Rostislav Pehlivanov @@ -25,29 +24,56 @@ - An interface to capture surfaces in an efficient way. - Overall usage: - - 1.) client registers with zwlr_screencontent_manager_v1 - 2.) server sends client info about surfaces via "receive_surface_info" - 3.) client subscribes to capture a surface via the "capture" requests - 4.) server sends client events via the "zwlr_screencontent_frame" interface - 5.) client finishes and informs server via the "frame_destroy" event - 6.) client optionally resubscribes via repeating steps 3.) through 5.) + An interface to capture surfaces in an efficient way by exporting DMA-BUFs. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + This object is a manager with which to start capturing from sources. + + + + + Capture the next frame of a an entire output. + + + + + + + + + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + + + + - - This object represents a frame which is ready to have its resources - fetched and used. - - The receive callback shall be called first, followed by the "object" - callback once per dmabuf object or the "plane" callback, once per dmabuf - plane. The "ready" event is called last to indicate that all the data has - been made available for readout, as well as the time at which presentation - happened at. The ownership of the frame is passed to the client, who's - responsible for destroying it via the "destroy" event once finished and - by calling close() on the file descriptors received. + + This object represents a single DMA-BUF frame. + + If the capture is successful, the compositor will first send a "frame" + event, followed by one or several "object". When the frame is available + for readout, the "ready" event is sent. + + If the capture failed, the "cancel" event is sent. This can happen anytime + before the "ready" event. + + Once either a "ready" or a "cancel" event is received, the client should + destroy the frame. Once an "object" event is received, the client is + responsible for closing the associated file descriptor. + All frames are read-only and may not be written into or altered. @@ -55,25 +81,23 @@ Special flags that should be respected by the client. - - - Main callback supplying the client with information about the frame, - as well as an object to serve as context for destruction. Always called - first before any other events. - - The "transform" argument describes the orientation needed to be applied - to correctly orient the buffer. For example, a buffer rotated by 90 - degrees will have a value of "3" here, corresponding to the need to - apply a 270 degree transpose to correctly present the buffer. + + Main event supplying the client with information about the frame. If the + capture didn't fail, this event is always emitted first before any other + events. + + This event is followed by a number of "object" as specified by the + "num_objects" argument. + summary="frame width in pixels"/> + summary="frame height in pixels"/> - + - - Callback which serves to supply the client with the file descriptors + + Event which serves to supply the client with the file descriptors containing the data for each object. + + After receiving this event, the client must always close the file + descriptor as soon as they're done with it and even if the frame fails. @@ -105,31 +131,28 @@ summary="fd of the current object"/> - - - - Callback which supplies the client with plane information for each - plane. - - - + + - Called as soon as the frame is presented, indicating it is available - for reading. + This event is sent as soon as the frame is presented, indicating it is + available for reading. This event includes the time at which + presentation happened at. + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, each component being an unsigned 32-bit value. Whole seconds are in tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, and the additional fractional part in tv_nsec as nanoseconds. Hence, - for valid timestamps tv_nsec must be in [0, 999999999]. - The seconds part may have an arbitrary offset at start. + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. + + After receiving this event, the client should destroy this object. @@ -143,22 +166,25 @@ Indicates reason for cancelling the frame. - - - + - If the frame is no longer valid after the "frame" event has been called, - this callback will be used to inform the client to scrap the frame. - Source is still valid for as long as the subscription function does not - return NULL. - This may get called if for instance the surface is in the process of - resizing. + If the capture failed or if the frame is no longer valid after the + "frame" event has been emitted, this event will be used to inform the + client to scrap the frame. + + If the failure is temporary, the client may capture again the same + source. If the failure is permanent, any further attempts to capture the + same source will fail again. + + After receiving this event, the client should destroy this object. @@ -166,35 +192,11 @@ - Unreferences the frame, allowing it to be reused. Must be called as soon - as its no longer used. - Can be called at any time by the client after the "frame" event, after - which the compositor will not call any other events unless the client - resubscribes to capture more. The client will still have to close any - FDs it has been given. - - - + Unreferences the frame. This request must be called as soon as its no + longer used. - - - This object is a manager with which to start capturing from sources. - - - - - Request to start capturing from an entire wl_output. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. + It can be called at any time by the client. The client will still have + to close any FDs it has been given. -- cgit v1.2.3 From bd0c1b794992bf94560bd429f4057c9d09989f06 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 17 Jun 2018 14:19:45 +0100 Subject: export-dmabuf: update protocol --- examples/dmabuf-capture.c | 2 +- types/wlr_export_dmabuf_v1.c | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'examples') diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index 2f7db2f1..1aeaf9c5 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -415,7 +415,7 @@ static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, struct capture_context *ctx = data; av_log(ctx, AV_LOG_WARNING, "Frame cancelled!\n"); av_frame_free(&ctx->current_frame); - if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT) { + if (reason == ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT) { av_log(ctx, AV_LOG_ERROR, "Permanent failure, exiting\n"); ctx->err = 1; } else { diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 40a0e289..68adda02 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -93,7 +93,7 @@ static void manager_handle_capture_output(struct wl_client *client, if (!output->impl->export_dmabuf) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, - ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERNAMENT); + ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_PERMANENT); return; } @@ -110,16 +110,13 @@ static void manager_handle_capture_output(struct wl_client *client, zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, output->width, output->height, 0, 0, attribs->flags, frame_flags, - attribs->format, mod_high, mod_low, attribs->n_planes, - attribs->n_planes); + attribs->format, mod_high, mod_low, attribs->n_planes); for (int i = 0; i < attribs->n_planes; ++i) { off_t size = lseek(attribs->fd[i], 0, SEEK_END); zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i, - attribs->fd[i], size); - zwlr_export_dmabuf_frame_v1_send_plane(frame->resource, i, i, - attribs->offset[i], attribs->stride[i]); + attribs->fd[i], size, attribs->offset[i], attribs->stride[i], i); } wl_list_remove(&frame->output_swap_buffers.link); -- cgit v1.2.3 From ed7d5b0f53167b6191d408e5c7f20a3672fac3c2 Mon Sep 17 00:00:00 2001 From: Rostislav Pehlivanov Date: Sun, 17 Jun 2018 15:19:17 +0100 Subject: Fix example --- examples/dmabuf-capture.c | 31 +++++++++++++------------------ examples/meson.build | 2 +- 2 files changed, 14 insertions(+), 19 deletions(-) (limited to 'examples') diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index 1aeaf9c5..0d4aa3ee 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "wlr-export-dmabuf-unstable-v1-client-protocol.h" struct wayland_output { @@ -225,25 +226,19 @@ static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, desc->layers[0].planes[plane_index].pitch = stride; } -static const uint32_t pixfmt_to_drm_map[] = { - [AV_PIX_FMT_NV12] = WL_SHM_FORMAT_NV12, - [AV_PIX_FMT_BGRA] = WL_SHM_FORMAT_ARGB8888, - [AV_PIX_FMT_BGR0] = WL_SHM_FORMAT_XRGB8888, - [AV_PIX_FMT_RGBA] = WL_SHM_FORMAT_ABGR8888, - [AV_PIX_FMT_RGB0] = WL_SHM_FORMAT_XBGR8888, - [AV_PIX_FMT_ABGR] = WL_SHM_FORMAT_RGBA8888, - [AV_PIX_FMT_0BGR] = WL_SHM_FORMAT_RGBX8888, - [AV_PIX_FMT_ARGB] = WL_SHM_FORMAT_BGRA8888, - [AV_PIX_FMT_0RGB] = WL_SHM_FORMAT_BGRX8888, -}; - static enum AVPixelFormat drm_fmt_to_pixfmt(uint32_t fmt) { - for (enum AVPixelFormat i = 0; i < AV_PIX_FMT_NB; i++) { - if (pixfmt_to_drm_map[i] == fmt) { - return i; - } - } - return AV_PIX_FMT_NONE; + switch (fmt) { + case DRM_FORMAT_NV12: return AV_PIX_FMT_NV12; + case DRM_FORMAT_ARGB8888: return AV_PIX_FMT_BGRA; + case DRM_FORMAT_XRGB8888: return AV_PIX_FMT_BGR0; + case DRM_FORMAT_ABGR8888: return AV_PIX_FMT_RGBA; + case DRM_FORMAT_XBGR8888: return AV_PIX_FMT_RGB0; + case DRM_FORMAT_RGBA8888: return AV_PIX_FMT_ABGR; + case DRM_FORMAT_RGBX8888: return AV_PIX_FMT_0BGR; + case DRM_FORMAT_BGRA8888: return AV_PIX_FMT_ARGB; + case DRM_FORMAT_BGRX8888: return AV_PIX_FMT_0RGB; + default: return AV_PIX_FMT_NONE; + }; } static int attach_drm_frames_ref(struct capture_context *ctx, AVFrame *f, diff --git a/examples/meson.build b/examples/meson.build index d82bd256..939a4890 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -47,6 +47,6 @@ if libavutil.found() and libavcodec.found() and libavformat.found() executable( 'dmabuf-capture', 'dmabuf-capture.c', - dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat] + dependencies: [wayland_client, wlr_protos, libavutil, libavcodec, libavformat, wlroots] ) endif -- cgit v1.2.3