summaryrefslogtreecommitdiffstats
path: root/video/out/vo_gpu_next.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/vo_gpu_next.c')
-rw-r--r--video/out/vo_gpu_next.c122
1 files changed, 120 insertions, 2 deletions
diff --git a/video/out/vo_gpu_next.c b/video/out/vo_gpu_next.c
index 1bb2c53418..0d505f7913 100644
--- a/video/out/vo_gpu_next.c
+++ b/video/out/vo_gpu_next.c
@@ -421,6 +421,7 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
.len = mpi->icc_profile ? mpi->icc_profile->size : 0,
},
.rotation = par->rotate / 90,
+ .user_data = mpi,
};
// mp_image, like AVFrame, likes communicating RGB/XYZ/YCbCr status
@@ -717,10 +718,10 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
bool should_draw = sw->fns->start_frame(sw, NULL); // for wayland logic
if (!should_draw || !pl_swapchain_start_frame(p->sw, &swframe)) {
// Advance the queue state to the current PTS to discard unused frames
- pl_queue_update(p->queue, NULL, &(struct pl_queue_params) {
+ pl_queue_update(p->queue, NULL, pl_queue_params(
.pts = frame->current->pts + vsync_offset,
.radius = pl_frame_mix_radius(&p->params),
- });
+ ));
return;
}
@@ -881,6 +882,119 @@ static bool update_auto_profile(struct priv *p, int *events)
return false;
}
+static void video_screenshot(struct vo *vo, struct voctrl_screenshot *args)
+{
+ struct priv *p = vo->priv;
+ pl_gpu gpu = p->gpu;
+ pl_tex fbo = NULL;
+ args->res = NULL;
+
+ update_options(p);
+ p->params.info_callback = NULL;
+ p->params.skip_caching_single_frame = true;
+ p->params.preserve_mixing_cache = false;
+ p->params.allow_delayed_peak_detect = false;
+ p->params.frame_mixer = NULL;
+
+ // Retrieve the current frame from the frame queue
+ struct pl_frame_mix mix;
+ enum pl_queue_status status;
+ status = pl_queue_update(p->queue, &mix, pl_queue_params(.pts = p->last_pts));
+ assert(status != PL_QUEUE_EOF);
+ if (status == PL_QUEUE_ERR) {
+ MP_ERR(vo, "Unknown error occured while trying to take screenshot!\n");
+ return;
+ }
+ if (status == PL_QUEUE_MORE || !mix.num_frames) {
+ MP_ERR(vo, "No frames available to take screenshot of? Open issue\n");
+ return;
+ }
+
+ // Passing an interpolation radius of 0 guarantees that the first frame in
+ // the resulting mix is the correct frame for this PTS
+ struct pl_frame *image = (struct pl_frame *) mix.frames[0];
+ struct mp_image *mpi = image->user_data;
+ int orig_overlays = image->num_overlays;
+ if (!args->subs)
+ image->num_overlays = 0;
+
+ struct mp_rect src = p->src, dst = p->dst;
+ struct mp_osd_res osd = p->osd_res;
+ if (!args->scaled) {
+ src = dst = (struct mp_rect) {0, 0, mpi->params.w, mpi->params.h};
+ osd = (struct mp_osd_res) {
+ .w = mpi->params.w,
+ .h = mpi->params.h,
+ .display_par = 1.0,
+ };
+ }
+
+ // Create target FBO, try high bit depth first
+ int mpfmt;
+ for (int depth = args->high_bit_depth ? 16 : 8; depth; depth -= 8) {
+ mpfmt = depth == 16 ? IMGFMT_RGBA64 : IMGFMT_RGBA;
+ pl_fmt fmt = pl_find_fmt(gpu, PL_FMT_UNORM, 4, depth, depth,
+ PL_FMT_CAP_RENDERABLE | PL_FMT_CAP_HOST_READABLE);
+ if (!fmt)
+ continue;
+
+ fbo = pl_tex_create(gpu, pl_tex_params(
+ .w = osd.w,
+ .h = osd.h,
+ .format = fmt,
+ .blit_dst = true,
+ .renderable = true,
+ .host_readable = true,
+ .storable = fmt->caps & PL_FMT_CAP_STORABLE,
+ ));
+ if (fbo)
+ break;
+ }
+
+ if (!fbo) {
+ MP_ERR(vo, "Failed creating target FBO for screenshot!\n");
+ goto done;
+ }
+
+ struct pl_frame target = {
+ .num_planes = 1,
+ .planes[0] = {
+ .texture = fbo,
+ .components = 4,
+ .component_mapping = {0, 1, 2, 3},
+ },
+ };
+
+ apply_target_options(p, &target);
+ apply_crop(image, src, mpi->params.w, mpi->params.h);
+ apply_crop(&target, dst, fbo->params.w, fbo->params.h);
+ if (args->osd)
+ write_overlays(vo, osd, 0, OSD_DRAW_OSD_ONLY, &p->osd_state, &target, false);
+
+ if (!pl_render_image_mix(p->rr, &mix, &target, &p->params)) {
+ MP_ERR(vo, "Failed rendering frame!\n");
+ goto done;
+ }
+
+ args->res = mp_image_alloc(mpfmt, fbo->params.w, fbo->params.h);
+ if (!args->res)
+ goto done;
+
+ bool ok = pl_tex_download(gpu, pl_tex_transfer_params(
+ .tex = fbo,
+ .ptr = args->res->planes[0],
+ .row_pitch = args->res->stride[0],
+ ));
+
+ if (!ok)
+ TA_FREEP(&args->res);
+
+ // fall through
+done:
+ pl_tex_destroy(gpu, &fbo);
+ image->num_overlays = orig_overlays;
+}
+
static int control(struct vo *vo, uint32_t request, void *data)
{
struct priv *p = vo->priv;
@@ -922,6 +1036,10 @@ static int control(struct vo *vo, uint32_t request, void *data)
case VOCTRL_PERFORMANCE_DATA:
*(struct voctrl_performance_data *) data = p->perf;
return true;
+
+ case VOCTRL_SCREENSHOT:
+ video_screenshot(vo, data);
+ return true;
}
int events = 0;