Skip to content

Instantly share code, notes, and snippets.

@uobikiemukot
Last active November 26, 2020 18:20
Show Gist options
  • Save uobikiemukot/ccb5e1fd71e43f3adec6 to your computer and use it in GitHub Desktop.
Save uobikiemukot/ccb5e1fd71e43f3adec6 to your computer and use it in GitHub Desktop.
add fbdev video output to mpv
commit de7e2edc14a5418abd5298e0cc76caa3d3bb5faa
Author: uobikiemukot <uobikiemukot@gmail.com>
Date: Wed Aug 26 20:14:34 2015 +0900
add framebuffer video output
diff --git a/video/out/vo.c b/video/out/vo.c
index de79fb0..d7d909c 100644
--- a/video/out/vo.c
+++ b/video/out/vo.c
@@ -63,6 +63,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_fbdev;
const struct vo_driver *const video_out_drivers[] =
{
@@ -107,6 +108,9 @@ const struct vo_driver *const video_out_drivers[] =
&video_out_opengl_hq,
&video_out_opengl_cb,
#endif
+#if HAVE_FBDEV
+ &video_out_fbdev,
+#endif
NULL
};
diff --git a/video/out/vo_fbdev.c b/video/out/vo_fbdev.c
new file mode 100644
index 0000000..673eebf
--- /dev/null
+++ b/video/out/vo_fbdev.c
@@ -0,0 +1,292 @@
+/*
+ * based on vo_drm.c
+ *
+ * Copyright (C) Aaron Holtzman - June 2000
+ *
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fb.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <libswscale/swscale.h>
+
+#include "common/msg.h"
+#include "options/m_option.h"
+#include "sub/osd.h"
+#include "video/fmt-conversion.h"
+#include "video/mp_image.h"
+#include "video/sws_utils.h"
+#include "vo.h"
+
+enum {
+ BITS_PER_BYTE = 8,
+ BYTES_PER_PIXEL = 4,
+ IMGFMT = IMGFMT_BGR0
+};
+
+struct priv {
+ int fd; /* file descriptor of framebuffer */
+ uint8_t *fp; /* pointer of framebuffer */
+ int width, height; /* display resolution */
+ long screen_size; /* screen data size (byte) */
+ int line_length; /* line length (byte) */
+ int bytes_per_pixel;
+ int bits_per_pixel;
+
+ /* option params */
+ char *fb_path; /* framebuffer device (check FRAMEBUFFER env too) */
+ int shift_x, shift_y;
+ int video_width, video_height;
+
+ /* mp image */
+ struct mp_image *cur_frame;
+ struct mp_image *last_input;
+ struct mp_rect src;
+ struct mp_rect dst;
+ struct mp_osd_res osd;
+ struct mp_sws_context *sws;
+};
+
+static void draw_image(struct vo *vo, mp_image_t *mpi)
+{
+ struct priv *fb = vo->priv;
+ struct mp_image src = *mpi;
+
+ if (mpi) {
+ mp_sws_scale(fb->sws, fb->cur_frame, &src);
+ osd_draw_on_image(vo->osd, fb->osd, src.pts, 0, fb->cur_frame);
+
+ int w = fb->src.x1 - fb->src.y0;
+ int h = fb->src.y1 - fb->src.y0;
+
+ memcpy_pic(fb->fp + fb->shift_y * fb->line_length + fb->shift_x * fb->bytes_per_pixel,
+ fb->cur_frame->planes[0],
+ w * BYTES_PER_PIXEL,
+ h,
+ fb->line_length,
+ fb->cur_frame->stride[0]);
+
+ /*
+ for (int i = 0; i < h; i++) {
+ memcpy(fb->fp + (i + fb->shift_y) * fb->line_length + fb->shift_x * fb->bytes_per_pixel,
+ fb->cur_frame->planes[0] + i * fb->cur_frame->stride[0],
+ w * BYTES_PER_PIXEL);
+ }
+ */
+ }
+
+ if (mpi != fb->last_input) {
+ talloc_free(fb->last_input);
+ fb->last_input = mpi;
+ }
+}
+
+static void flip_page(struct vo *vo)
+{
+ return;
+}
+
+static int query_format(struct vo *vo, int format)
+{
+ return sws_isSupportedInput(imgfmt2pixfmt(format));
+}
+
+static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
+{
+ struct priv *fb = vo->priv;
+ int dst_w, dst_h, src_w, src_h;
+
+ vo_get_src_dst_rects(vo, &fb->src, &fb->dst, &fb->osd);
+
+ dst_w = fb->dst.x1 - fb->dst.x0;
+ dst_h = fb->dst.y1 - fb->dst.y0;
+
+ if (fb->video_width != -1) {
+ src_w = fb->video_width;
+ src_h = (fb->video_height != -1) ?
+ fb->video_height:
+ (double) src_w * vo->dheight / vo->dwidth;
+
+ fb->src.x0 = fb->src.y0 = 0;
+ fb->src.x1 = src_w;
+ fb->src.y1 = src_h;
+
+ } else {
+ src_w = fb->src.x1 - fb->src.x0;
+ src_h = fb->src.y1 - fb->src.y0;
+ }
+
+ // fb->osd contains the parameters assuming OSD rendering in window
+ // coordinates, but OSD can only be rendered in the intersection
+ // between window and video rectangle (i.e. not into panscan borders).
+ fb->osd.w = src_w;
+ fb->osd.h = src_h;
+ fb->osd.mt = MPMIN(0, fb->osd.mt);
+ fb->osd.mb = MPMIN(0, fb->osd.mb);
+ fb->osd.mr = MPMIN(0, fb->osd.mr);
+ fb->osd.ml = MPMIN(0, fb->osd.ml);
+
+ mp_sws_set_from_cmdline(fb->sws, vo->opts->sws_opts);
+ fb->sws->src = *params;
+ fb->sws->dst = (struct mp_image_params) {
+ .imgfmt = IMGFMT,
+ .w = src_w,
+ .h = src_h,
+ .d_w = dst_w,
+ .d_h = dst_h,
+ };
+
+ talloc_free(fb->cur_frame);
+ fb->cur_frame = mp_image_alloc(IMGFMT, src_w, src_h);
+
+ mp_image_params_guess_csp(&fb->sws->dst);
+ mp_image_set_params(fb->cur_frame, &fb->sws->dst);
+
+ if (mp_sws_reinit(fb->sws) < 0)
+ return -1;
+
+ vo->want_redraw = true;
+
+ return 0;
+}
+
+static void uninit(struct vo *vo)
+{
+ struct priv *fb = vo->priv;
+
+ munmap(fb->fp, fb->screen_size);
+
+ talloc_free(fb->last_input);
+ talloc_free(fb->cur_frame);
+ talloc_free(fb->sws);
+
+ close(fb->fd);
+}
+
+static int preinit(struct vo *vo)
+{
+ char *env, *path;
+ struct priv *fb = vo->priv;
+ struct fb_fix_screeninfo finfo;
+ struct fb_var_screeninfo vinfo;
+
+ path = ((env = getenv("FRAMEBUFFER")) != NULL) ? env: fb->fb_path;
+ if ((fb->fd = open(path, O_RDWR)) < 0) {
+ MP_ERR(vo, "cannot open %s: %s\n", path, mp_strerror(errno));
+ goto err;
+ }
+
+ if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &finfo)) {
+ MP_ERR(vo, "ioctl: FBIOGET_FSCREENINFO failed\n");
+ goto err;
+ }
+
+ if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &vinfo)) {
+ MP_ERR(vo, "ioctl: FBIOGET_FSCREENINFO failed\n");
+ goto err;
+ }
+
+ fb->width = vinfo.xres;
+ fb->height = vinfo.yres;
+ fb->screen_size = finfo.smem_len;
+ fb->line_length = finfo.line_length;
+
+ fb->bits_per_pixel = vinfo.bits_per_pixel;
+ fb->bytes_per_pixel = (fb->bits_per_pixel + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
+
+ if (fb->bits_per_pixel != 32
+ || finfo.type != FB_TYPE_PACKED_PIXELS
+ || finfo.visual != FB_VISUAL_TRUECOLOR) {
+ MP_ERR(vo, "bits per pixel: %d not supported\n", fb->bits_per_pixel);
+ goto err;
+ }
+
+ /* check screen [xy]offset and initialize because linux console changes these values */
+ if (vinfo.xoffset != 0 || vinfo.yoffset != 0) {
+ vinfo.xoffset = vinfo.yoffset = 0;
+ if (ioctl(fb->fd, FBIOPUT_VSCREENINFO, &vinfo))
+ MP_WARN(vo, "couldn't reset offset (x:%d y:%d)\n", vinfo.xoffset, vinfo.yoffset);
+ }
+
+ fb->fp = mmap(0, fb->screen_size, PROT_WRITE | PROT_READ, MAP_SHARED, fb->fd, 0);
+ fb->sws = mp_sws_alloc(vo);
+
+ if (fb->fp == MAP_FAILED)
+ goto err;
+
+ return 0;
+
+err:
+ return -1;
+}
+
+static int control(struct vo *vo, uint32_t request, void *data)
+{
+ struct priv *fb = vo->priv;
+
+ switch (request) {
+ case VOCTRL_SCREENSHOT_WIN:
+ *(struct mp_image **) data = mp_image_new_copy(fb->cur_frame);
+ return VO_TRUE;
+ case VOCTRL_REDRAW_FRAME:
+ draw_image(vo, fb->last_input);
+ return VO_TRUE;
+ case VOCTRL_GET_PANSCAN:
+ return VO_TRUE;
+ case VOCTRL_SET_PANSCAN:
+ if (vo->config_ok)
+ reconfig(vo, vo->params, 0);
+ return VO_TRUE;
+ }
+ return VO_NOTIMPL;
+}
+
+#define OPT_BASE_STRUCT struct priv
+
+const struct vo_driver video_out_fbdev = {
+ .description = "Linux framebuffer output",
+ .name = "fbdev",
+ .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),
+ .options = (const struct m_option[]) {
+ OPT_STRING("fb_path", fb_path, 0),
+ OPT_INT("shift_x", shift_x, 0),
+ OPT_INT("shift_y", shift_y, 0),
+ OPT_INT("video_width", video_width, 0),
+ OPT_INT("video_height", video_height, 0),
+ {0},
+ },
+ .priv_defaults = &(const struct priv) {
+ .fb_path = "/dev/fb0",
+ .shift_x = 0,
+ .shift_y = 0,
+ .video_width = -1,
+ .video_height = -1,
+ },
+};
diff --git a/wscript b/wscript
index 0a5bf61..c5229b1 100644
--- a/wscript
+++ b/wscript
@@ -655,6 +655,10 @@ video_output_features = [
'deps': [ 'vt.h' ],
'func': check_pkg_config('libdrm'),
}, {
+ 'name': '--fbdev',
+ 'desc': 'FBDEV',
+ 'func': check_headers('linux/fb.h'),
+ }, {
'name': '--jpeg',
'desc': 'JPEG support',
'func': check_cc(header_name=['stdio.h', 'jpeglib.h'],
diff --git a/wscript_build.py b/wscript_build.py
index a96fb87..37a9218 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -348,6 +348,7 @@ def build(ctx):
( "video/out/vo.c" ),
( "video/out/vo_caca.c", "caca" ),
( "video/out/vo_drm.c", "drm" ),
+ ( "video/out/vo_fbdev.c", "fbdev" ),
( "video/out/vo_direct3d.c", "direct3d" ),
( "video/out/vo_image.c" ),
( "video/out/vo_lavc.c", "encoding" ),
@bodqhrohro
Copy link

Why is this still not in upstream?

@mbiegert
Copy link

mbiegert commented Nov 9, 2017

I can't find a pull request or open issue on mpv repository. Did you consider upstreaming this or is there a particular reason for not doing so?

@uobikiemukot
Copy link
Author

uobikiemukot commented May 1, 2018

mplayer and mplayer2 have fbdev driver, but mpv dropped framebuffer support.
(maybe there are a few users who need fbdev driver...)
So I don't intend to create pull request for this patch.

@xlla
Copy link

xlla commented Nov 26, 2020

I am try to build on this patch, but a lot of errors occur.

| ../../../../../../workspace/sources/mpv/video/out/vo_fbdev.c:149:4: error: implicit declaration of function 'mp_sws_set_from_cmdline' [-Werror=implicit-function-declaration]
|   149 |    mp_sws_set_from_cmdline(fb->sws, vo->opts->sws_opts);
|       |    ^~~~~~~~~~~~~~~~~~~~~~~
| ../../../../../../workspace/sources/mpv/video/out/vo_fbdev.c:149:45: error: 'struct mp_vo_opts' has no member named 'sws_opts'
|   149 |    mp_sws_set_from_cmdline(fb->sws, vo->opts->sws_opts);
|       |                                             ^~
| ../../../../../../workspace/sources/mpv/video/out/vo_fbdev.c:155:9: error: 'struct mp_image_params' has no member named 'd_w'; did you mean 'p_w'?
|   155 |        .d_w = dst_w,
|       |         ^~~
|       |         p_w
| ../../../../../../workspace/sources/mpv/video/out/vo_fbdev.c:156:9: error: 'struct mp_image_params' has no member named 'd_h'; did you mean 'p_h'?
|   156 |        .d_h = dst_h,
|       |         ^~~
|       |         p_h
| ../../../../../../workspace/sources/mpv/video/out/vo_fbdev.c: In function 'control':
| ../../../../../../workspace/sources/mpv/video/out/vo_fbdev.c:254:9: error: 'VOCTRL_GET_PANSCAN' undeclared (first use in this function); did you mean 'VOCTRL_SET_PANSCAN'?
|   254 |    case VOCTRL_GET_PANSCAN:
|       |         ^~~~~~~~~~~~~~~~~~
|       |         VOCTRL_SET_PANSCAN
| ../../../../../../workspace/sources/mpv/video/out/vo_fbdev.c:254:9: note: each undeclared identifier is reported only once for each function it appears in

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment