From dd02369c3223fda5bcb2658b15404d43232bb38f Mon Sep 17 00:00:00 2001 From: rr- Date: Thu, 13 Oct 2016 01:30:45 +0200 Subject: vo_tct: introduce modern caca alternative --- DOCS/man/vo.rst | 15 ++++ video/out/vo.c | 2 + video/out/vo_tct.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++ wscript_build.py | 1 + 4 files changed, 252 insertions(+) create mode 100644 video/out/vo_tct.c diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index 34302f9a3f..fde62b4999 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -387,6 +387,21 @@ Available video output drivers are: .. note:: This driver is a joke. +``tct`` + Color Unicode art video output driver that works on a text console. + Depends on support of true color by modern terminals to display the images + at full color range. + + ``--vo-tct-algo=`` + Select how to write the pixels to the terminal. + + half-blocks + Uses unicode LOWER HALF BLOCK character to achieve higher vertical + resolution. (Default.) + plain + Uses spaces. Causes vertical resolution to drop twofolds, but in + theory works in more places. + ``image`` Output each frame into an image file in the current directory. Each file takes the frame number padded with leading zeros as name. diff --git a/video/out/vo.c b/video/out/vo.c index 4b28aadaa1..46908d2d59 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -59,6 +59,7 @@ extern const struct vo_driver video_out_sdl; extern const struct vo_driver video_out_vaapi; extern const struct vo_driver video_out_wayland; extern const struct vo_driver video_out_rpi; +extern const struct vo_driver video_out_tct; const struct vo_driver *const video_out_drivers[] = { @@ -89,6 +90,7 @@ const struct vo_driver *const video_out_drivers[] = &video_out_null, // should not be auto-selected &video_out_image, + &video_out_tct, #if HAVE_CACA &video_out_caca, #endif diff --git a/video/out/vo_tct.c b/video/out/vo_tct.c new file mode 100644 index 0000000000..22354c5283 --- /dev/null +++ b/video/out/vo_tct.c @@ -0,0 +1,234 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see . + */ + +#include +#include +#include +#include + +#include "options/m_config.h" +#include "config.h" +#include "vo.h" +#include "sub/osd.h" +#include "video/sws_utils.h" +#include "video/mp_image.h" + +#define IMGFMT IMGFMT_BGR24 + +#define ALGO_PLAIN 1 +#define ALGO_HALF_BLOCKS 2 +#define ESC_HIDE_CURSOR "\e[?25l" +#define ESC_RESTORE_CURSOR "\e[?25h" +#define ESC_CLEAR_SCREEN "\e[2J" +#define ESC_CLEAR_COLORS "\e[0m" +#define ESC_GOTOXY "\e[%d;%df" +#define ESC_COLOR_BACKGROUND "\e[48;2;%d;%d;%dm" +#define ESC_COLOR_FOREGROUND "\e[38;2;%d;%d;%dm" + +struct vo_tct_opts { + int algo; +}; + +#define OPT_BASE_STRUCT struct vo_tct_opts +static const struct m_sub_options vo_tct_conf = { + .opts = (const m_option_t[]) { + OPT_CHOICE("vo-tct-algo", algo, 0, + ({"plain", ALGO_PLAIN}, + {"half-blocks", ALGO_HALF_BLOCKS})), + {0} + }, + .defaults = &(const struct vo_tct_opts) { + .algo = ALGO_HALF_BLOCKS, + }, + .size = sizeof(struct vo_tct_opts), +}; + +struct priv { + struct vo_tct_opts *opts; + size_t buffer_size; + char *buffer; + int swidth; + int sheight; + struct mp_image *frame; + struct mp_rect src; + struct mp_rect dst; + struct mp_sws_context *sws; +}; + +static void write_plain( + const int dwidth, const int dheight, + const int swidth, const int sheight, + const unsigned char *source, const int source_stride) +{ + assert(source); + const int tx = (dwidth - swidth) / 2; + const int ty = (dheight - sheight) / 2; + for (int y = 0; y < sheight; y++) { + const unsigned char *row = source + y * source_stride; + printf(ESC_GOTOXY, ty + y, tx); + for (int x = 0; x < swidth; x++) { + unsigned char b = *row++; + unsigned char g = *row++; + unsigned char r = *row++; + printf(ESC_COLOR_BACKGROUND, r, g, b); + printf(" "); + } + printf(ESC_CLEAR_COLORS); + } + printf("\n"); +} + +static void write_half_blocks( + const int dwidth, const int dheight, + const int swidth, const int sheight, + unsigned char *source, int source_stride) +{ + assert(source); + const int tx = (dwidth - swidth) / 2; + const int ty = (dheight - sheight) / 2; + for (int y = 0; y < sheight * 2; y += 2) { + const unsigned char *row_up = source + y * source_stride; + const unsigned char *row_down = source + (y + 1) * source_stride; + printf(ESC_GOTOXY, ty + y / 2, tx); + for (int x = 0; x < swidth; x++) { + unsigned char b_up = *row_up++; + unsigned char g_up = *row_up++; + unsigned char r_up = *row_up++; + unsigned char b_down = *row_down++; + unsigned char g_down = *row_down++; + unsigned char r_down = *row_down++; + printf(ESC_COLOR_BACKGROUND, r_up, g_up, b_up); + printf(ESC_COLOR_FOREGROUND, r_down, g_down, b_down); + printf("▄"); + } + printf(ESC_CLEAR_COLORS); + } + printf("\n"); +} + +static int reconfig(struct vo *vo, struct mp_image_params *params) +{ + struct priv *p = vo->priv; + + struct winsize winsize; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize); + vo->dwidth = winsize.ws_col; + vo->dheight = winsize.ws_row; + + struct mp_osd_res osd; + vo_get_src_dst_rects(vo, &p->src, &p->dst, &osd); + p->swidth = p->dst.x1 - p->dst.x0; + p->sheight = p->dst.y1 - p->dst.y0; + + if (p->buffer) + free(p->buffer); + + mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts); + p->sws->src = *params; + p->sws->dst = (struct mp_image_params) { + .imgfmt = IMGFMT, + .w = p->swidth, + .h = p->sheight, + .p_w = 1, + .p_h = 1, + }; + + const int mul = (p->opts->algo == ALGO_PLAIN ? 1 : 2); + p->frame = mp_image_alloc(IMGFMT, p->swidth, p->sheight * mul); + if (!p->frame) + return -1; + + if (mp_sws_reinit(p->sws) < 0) + return -1; + + printf(ESC_HIDE_CURSOR); + printf(ESC_CLEAR_SCREEN); + vo->want_redraw = true; + return 0; +} + +static void draw_image(struct vo *vo, mp_image_t *mpi) +{ + struct priv *p = vo->priv; + struct mp_image src = *mpi; + // XXX: pan, crop etc. + mp_sws_scale(p->sws, p->frame, &src); + talloc_free(mpi); +} + +static void flip_page(struct vo *vo) +{ + struct priv *p = vo->priv; + if (p->opts->algo == ALGO_PLAIN) { + write_plain( + vo->dwidth, vo->dheight, p->swidth, p->sheight, + p->frame->planes[0], p->frame->stride[0]); + } else { + write_half_blocks( + vo->dwidth, vo->dheight, p->swidth, p->sheight, + p->frame->planes[0], p->frame->stride[0]); + } + fflush(stdout); +} + +static void uninit(struct vo *vo) +{ + printf(ESC_RESTORE_CURSOR); + printf(ESC_CLEAR_SCREEN); + printf(ESC_GOTOXY, 0, 0); + struct priv *p = vo->priv; + if (p->buffer) + talloc_free(p->buffer); + if (p->sws) + talloc_free(p->sws); +} + +static int preinit(struct vo *vo) +{ + // most terminal characters aren't 1:1, so we default to 2:1. + // if user passes their own value of choice, it'll be scaled accordingly. + vo->monitor_par = vo->opts->monitor_pixel_aspect * 2; + + struct priv *p = vo->priv; + p->opts = mp_get_config_group(vo, vo->global, &vo_tct_conf); + p->sws = mp_sws_alloc(vo); + return 0; +} + +static int query_format(struct vo *vo, int format) +{ + return format == IMGFMT; +} + +static int control(struct vo *vo, uint32_t request, void *data) +{ + return VO_NOTIMPL; +} + +const struct vo_driver video_out_tct = { + .name = "tct", + .description = "true-color terminals", + .preinit = preinit, + .query_format = query_format, + .reconfig = reconfig, + .control = control, + .draw_image = draw_image, + .flip_page = flip_page, + .uninit = uninit, + .priv_size = sizeof(struct priv), + .global_opts = &vo_tct_conf, +}; diff --git a/wscript_build.py b/wscript_build.py index cfab785ec0..e05bbe8d11 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -375,6 +375,7 @@ def build(ctx): ( "video/out/vo_opengl.c", "gl" ), ( "video/out/vo_opengl_cb.c", "gl" ), ( "video/out/vo_sdl.c", "sdl2" ), + ( "video/out/vo_tct.c", "posix" ), ( "video/out/vo_vaapi.c", "vaapi-x11" ), ( "video/out/vo_vdpau.c", "vdpau" ), ( "video/out/vo_wayland.c", "wayland" ), -- cgit v1.2.3