demux_lavf: Adding av_dup_packet() missing from recent 6e8d420a41
[mplayer.git] / libvo / vo_fbdev2.c
blobbec9fea8ff856178c18f6c7f66d4d1ec176ec297
1 /*
2 * video driver for framebuffer device
3 * copyright (C) 2003 Joey Parrish <joey@nicewarrior.org>
5 * This file is part of MPlayer.
7 * MPlayer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * MPlayer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <errno.h>
29 #include <sys/mman.h>
30 #include <sys/ioctl.h>
31 #include <linux/fb.h>
33 #include "config.h"
34 #include "video_out.h"
35 #include "video_out_internal.h"
36 #include "fastmemcpy.h"
37 #include "sub/sub.h"
38 #include "mp_msg.h"
39 #include "aspect.h"
40 #include "libavutil/common.h"
42 static const vo_info_t info = {
43 "Framebuffer Device",
44 "fbdev2",
45 "Joey Parrish <joey@nicewarrior.org>",
49 const LIBVO_EXTERN(fbdev2)
51 static void set_bpp(struct fb_var_screeninfo *p, int bpp)
53 p->bits_per_pixel = (bpp + 1) & ~1;
54 p->red.msb_right = p->green.msb_right = p->blue.msb_right = p->transp.msb_right = 0;
55 p->transp.offset = p->transp.length = 0;
56 p->blue.offset = 0;
57 switch (bpp) {
58 case 32:
59 p->transp.offset = 24;
60 p->transp.length = 8;
61 case 24:
62 p->red.offset = 16;
63 p->red.length = 8;
64 p->green.offset = 8;
65 p->green.length = 8;
66 p->blue.length = 8;
67 break;
68 case 16:
69 p->red.offset = 11;
70 p->green.length = 6;
71 p->red.length = 5;
72 p->green.offset = 5;
73 p->blue.length = 5;
74 break;
75 case 15:
76 p->red.offset = 10;
77 p->green.length = 5;
78 p->red.length = 5;
79 p->green.offset = 5;
80 p->blue.length = 5;
81 break;
82 case 12:
83 p->red.offset = 8;
84 p->green.length = 4;
85 p->red.length = 4;
86 p->green.offset = 4;
87 p->blue.length = 4;
88 break;
92 static char *fb_dev_name = NULL; // such as /dev/fb0
93 static int fb_dev_fd; // handle for fb_dev_name
94 static uint8_t *frame_buffer = NULL; // mmap'd access to fbdev
95 static uint8_t *center = NULL; // where to begin writing our image (centered?)
96 static struct fb_fix_screeninfo fb_finfo; // fixed info
97 static struct fb_var_screeninfo fb_vinfo; // variable info
98 static struct fb_var_screeninfo fb_orig_vinfo; // variable info to restore later
99 static unsigned short fb_ored[256], fb_ogreen[256], fb_oblue[256];
100 static struct fb_cmap fb_oldcmap = { 0, 256, fb_ored, fb_ogreen, fb_oblue };
101 static int fb_cmap_changed = 0; // to restore map
102 static int fb_pixel_size; // 32: 4 24: 3 16: 2 15: 2
103 static int fb_bpp; // 32: 32 24: 24 16: 16 15: 15
104 static size_t fb_size; // size of frame_buffer
105 static int fb_line_len; // length of one line in bytes
106 static void (*draw_alpha_p)(int w, int h, unsigned char *src,
107 unsigned char *srca, int stride, unsigned char *dst,
108 int dstride);
110 static uint8_t *next_frame = NULL; // for double buffering
111 static int in_width;
112 static int in_height;
114 static struct fb_cmap *make_directcolor_cmap(struct fb_var_screeninfo *var)
116 int i, cols, rcols, gcols, bcols;
117 uint16_t *red, *green, *blue;
118 struct fb_cmap *cmap;
120 rcols = 1 << var->red.length;
121 gcols = 1 << var->green.length;
122 bcols = 1 << var->blue.length;
124 /* Make our palette the length of the deepest color */
125 cols = FFMAX3(rcols, gcols, bcols);
127 red = malloc(3 * cols * sizeof(red[0]));
128 if(!red) {
129 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't allocate red palette with %d entries.\n", cols);
130 return NULL;
132 green = red + cols;
133 blue = green + cols;
134 for (i = 0; i < cols; i++) {
135 red[i] = (65535/(rcols-1)) * i;
136 green[i] = (65535/(gcols-1)) * i;
137 blue[i] = (65535/(bcols-1)) * i;
140 cmap = malloc(sizeof(struct fb_cmap));
141 if(!cmap) {
142 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't allocate color map\n");
143 free(red);
144 return NULL;
146 cmap->start = 0;
147 cmap->transp = 0;
148 cmap->len = cols;
149 cmap->red = red;
150 cmap->blue = blue;
151 cmap->green = green;
152 cmap->transp = NULL;
154 return cmap;
157 static int fb_preinit(int reset)
159 static int fb_preinit_done = 0;
160 static int fb_err = -1;
162 if (reset) {
163 fb_preinit_done = 0;
164 return 0;
167 if (fb_preinit_done)
168 return fb_err;
169 fb_preinit_done = 1;
171 if (!fb_dev_name && !(fb_dev_name = getenv("FRAMEBUFFER")))
172 fb_dev_name = strdup("/dev/fb0");
174 mp_msg(MSGT_VO, MSGL_V, "[fbdev2] Using device %s\n", fb_dev_name);
176 if ((fb_dev_fd = open(fb_dev_name, O_RDWR)) == -1) {
177 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't open %s: %s\n", fb_dev_name, strerror(errno));
178 goto err_out;
180 if (ioctl(fb_dev_fd, FBIOGET_VSCREENINFO, &fb_vinfo)) {
181 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't get VSCREENINFO: %s\n", strerror(errno));
182 goto err_out;
184 fb_orig_vinfo = fb_vinfo;
186 fb_bpp = fb_vinfo.bits_per_pixel;
188 /* 16 and 15 bpp is reported as 16 bpp */
189 if (fb_bpp == 16)
190 fb_bpp = fb_vinfo.red.length + fb_vinfo.green.length +
191 fb_vinfo.blue.length;
193 fb_err = 0;
194 return 0;
195 err_out:
196 if (fb_dev_fd >= 0) close(fb_dev_fd);
197 fb_dev_fd = -1;
198 fb_err = -1;
199 return -1;
202 static int preinit(const char *subdevice)
204 if (subdevice)
206 free(fb_dev_name);
207 fb_dev_name = strdup(subdevice);
209 return fb_preinit(0);
212 static int config(uint32_t width, uint32_t height, uint32_t d_width,
213 uint32_t d_height, uint32_t flags, char *title,
214 uint32_t format)
216 struct fb_cmap *cmap;
217 int fs = flags & VOFLAG_FULLSCREEN;
218 int x_offset = vo_dx + (d_width - width ) / 2;
219 int y_offset = vo_dy + (d_height - height) / 2;
220 x_offset = av_clip(x_offset, 0, fb_vinfo.xres - width);
221 y_offset = av_clip(y_offset, 0, fb_vinfo.yres - height);
223 in_width = width;
224 in_height = height;
226 if (fb_vinfo.xres < in_width || fb_vinfo.yres < in_height) {
227 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Screensize is smaller than video size (%dx%d < %dx%d)\n",
228 fb_vinfo.xres, fb_vinfo.yres, in_width, in_height);
229 return 1;
232 switch (fb_bpp) {
233 case 32: draw_alpha_p = vo_draw_alpha_rgb32; break;
234 case 24: draw_alpha_p = vo_draw_alpha_rgb24; break;
235 case 16: draw_alpha_p = vo_draw_alpha_rgb16; break;
236 case 15: draw_alpha_p = vo_draw_alpha_rgb15; break;
237 case 12: draw_alpha_p = vo_draw_alpha_rgb12; break;
238 default: return 1;
241 if (vo_config_count == 0) {
242 if (ioctl(fb_dev_fd, FBIOGET_FSCREENINFO, &fb_finfo)) {
243 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't get FSCREENINFO: %s\n", strerror(errno));
244 return 1;
247 if (fb_finfo.type != FB_TYPE_PACKED_PIXELS) {
248 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] type %d not supported\n", fb_finfo.type);
249 return 1;
252 switch (fb_finfo.visual) {
253 case FB_VISUAL_TRUECOLOR:
254 break;
255 case FB_VISUAL_DIRECTCOLOR:
256 mp_msg(MSGT_VO, MSGL_V, "[fbdev2] creating cmap for directcolor\n");
257 if (ioctl(fb_dev_fd, FBIOGETCMAP, &fb_oldcmap)) {
258 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] can't get cmap: %s\n", strerror(errno));
259 return 1;
261 if (!(cmap = make_directcolor_cmap(&fb_vinfo)))
262 return 1;
263 if (ioctl(fb_dev_fd, FBIOPUTCMAP, cmap)) {
264 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] can't put cmap: %s\n", strerror(errno));
265 free(cmap->red);
266 free(cmap);
267 return 1;
269 fb_cmap_changed = 1;
270 free(cmap->red);
271 free(cmap);
272 break;
273 default:
274 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] visual: %d not yet supported\n", fb_finfo.visual);
275 return 1;
278 fb_size = fb_finfo.smem_len;
279 fb_line_len = fb_finfo.line_length;
280 if ((frame_buffer = (uint8_t *) mmap(0, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_dev_fd, 0)) == (uint8_t *) -1) {
281 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't mmap %s: %s\n", fb_dev_name, strerror(errno));
282 return 1;
286 center = frame_buffer +
287 x_offset * fb_pixel_size +
288 y_offset * fb_line_len;
290 #ifndef USE_CONVERT2FB
291 if (!(next_frame = realloc(next_frame, in_width * in_height * fb_pixel_size))) {
292 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't malloc next_frame: %s\n", strerror(errno));
293 return 1;
295 #endif
296 if (fs) memset(frame_buffer, '\0', fb_line_len * fb_vinfo.yres);
298 return 0;
301 static int query_format(uint32_t format)
303 // open the device, etc.
304 if (fb_preinit(0)) return 0;
305 if ((format & IMGFMT_BGR_MASK) == IMGFMT_BGR) {
306 int fb_target_bpp = format & 0xff;
307 set_bpp(&fb_vinfo, fb_target_bpp);
308 fb_vinfo.xres_virtual = fb_vinfo.xres;
309 fb_vinfo.yres_virtual = fb_vinfo.yres;
310 if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_vinfo))
311 // Needed for Intel framebuffer with 32 bpp
312 fb_vinfo.transp.length = fb_vinfo.transp.offset = 0;
313 if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_vinfo)) {
314 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't put VSCREENINFO: %s\n", strerror(errno));
315 return 0;
317 fb_pixel_size = fb_vinfo.bits_per_pixel / 8;
318 fb_bpp = fb_vinfo.bits_per_pixel;
319 if (fb_bpp == 16)
320 fb_bpp = fb_vinfo.red.length + fb_vinfo.green.length + fb_vinfo.blue.length;
321 if (fb_bpp == fb_target_bpp)
322 return VFCAP_CSP_SUPPORTED|VFCAP_CSP_SUPPORTED_BY_HW|VFCAP_ACCEPT_STRIDE;
324 return 0;
327 static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src,
328 unsigned char *srca, int stride)
330 unsigned char *dst;
331 int dstride;
333 #ifdef USE_CONVERT2FB
334 dst = center + (fb_line_len * y0) + (x0 * fb_pixel_size);
335 dstride = fb_line_len;
336 #else
337 dst = next_frame + (in_width * y0 + x0) * fb_pixel_size;
338 dstride = in_width * fb_pixel_size;
339 #endif
340 (*draw_alpha_p)(w, h, src, srca, stride, dst, dstride);
343 static void draw_osd(void)
345 vo_draw_text(in_width, in_height, draw_alpha);
348 // all csp support stride
349 static int draw_frame(uint8_t *src[]) { return 1; }
351 static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y)
353 uint8_t *in = src[0];
354 #ifdef USE_CONVERT2FB
355 uint8_t *dest = center + (fb_line_len * y) + (x * fb_pixel_size);
356 int next = fb_line_len;
357 #else
358 uint8_t *dest = next_frame + (in_width * y + x) * fb_pixel_size;
359 int next = in_width * fb_pixel_size;
360 #endif
362 memcpy_pic(dest, in, w * fb_pixel_size, h, next, stride[0]);
363 return 0;
366 static void check_events(void)
370 static void flip_page(void)
372 #ifndef USE_CONVERT2FB
373 int out_offset = 0, in_offset = 0;
375 memcpy_pic(center + out_offset, next_frame + in_offset,
376 in_width * fb_pixel_size, in_height,
377 fb_line_len, in_width * fb_pixel_size);
378 #endif
381 static void uninit(void)
383 if (fb_cmap_changed) {
384 if (ioctl(fb_dev_fd, FBIOPUTCMAP, &fb_oldcmap))
385 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't restore original cmap\n");
386 fb_cmap_changed = 0;
388 free(next_frame);
389 if (fb_dev_fd >= 0) {
390 if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_orig_vinfo))
391 mp_msg(MSGT_VO, MSGL_ERR, "[fbdev2] Can't reset original fb_var_screeninfo: %s\n", strerror(errno));
392 close(fb_dev_fd);
393 fb_dev_fd = -1;
395 if(frame_buffer) munmap(frame_buffer, fb_size);
396 next_frame = frame_buffer = NULL;
397 fb_preinit(1); // so that later calls to preinit don't fail
400 static int control(uint32_t request, void *data)
402 switch (request) {
403 case VOCTRL_QUERY_FORMAT:
404 return query_format(*((uint32_t*)data));
405 case VOCTRL_UPDATE_SCREENINFO:
406 vo_screenwidth = fb_vinfo.xres;
407 vo_screenheight = fb_vinfo.yres;
408 aspect_save_screenres(vo_screenwidth, vo_screenheight);
409 return VO_TRUE;
411 return VO_NOTIMPL;