2 * Copyright (c) 2011 Stefano Sabatini
3 * Copyright (c) 2009 Giliard B. de Freitas <giliarde@gmail.com>
4 * Copyright (C) 2002 Gunnar Monell <gmo@linux.nu>
6 * This file is part of Libav.
8 * Libav is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * Libav is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with Libav; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 * Linux framebuffer input device,
26 * inspired by code from fbgrab.c by Gunnar Monell.
27 * @see http://linux-fbdev.sourceforge.net/
34 #include <sys/ioctl.h>
39 #include "libavutil/log.h"
40 #include "libavutil/mem.h"
41 #include "libavutil/opt.h"
42 #include "libavutil/time.h"
43 #include "libavutil/parseutils.h"
44 #include "libavutil/pixdesc.h"
45 #include "libavformat/avformat.h"
46 #include "libavformat/internal.h"
48 struct rgb_pixfmt_map_entry
{
50 int red_offset
, green_offset
, blue_offset
, alpha_offset
;
51 enum AVPixelFormat pixfmt
;
54 static struct rgb_pixfmt_map_entry rgb_pixfmt_map
[] = {
55 // bpp, red_offset, green_offset, blue_offset, alpha_offset, pixfmt
56 { 32, 0, 8, 16, 24, AV_PIX_FMT_RGBA
},
57 { 32, 16, 8, 0, 24, AV_PIX_FMT_BGRA
},
58 { 32, 8, 16, 24, 0, AV_PIX_FMT_ARGB
},
59 { 32, 3, 2, 8, 0, AV_PIX_FMT_ABGR
},
60 { 24, 0, 8, 16, 0, AV_PIX_FMT_RGB24
},
61 { 24, 16, 8, 0, 0, AV_PIX_FMT_BGR24
},
64 static enum AVPixelFormat
get_pixfmt_from_fb_varinfo(struct fb_var_screeninfo
*varinfo
)
68 for (i
= 0; i
< FF_ARRAY_ELEMS(rgb_pixfmt_map
); i
++) {
69 struct rgb_pixfmt_map_entry
*entry
= &rgb_pixfmt_map
[i
];
70 if (entry
->bits_per_pixel
== varinfo
->bits_per_pixel
&&
71 entry
->red_offset
== varinfo
->red
.offset
&&
72 entry
->green_offset
== varinfo
->green
.offset
&&
73 entry
->blue_offset
== varinfo
->blue
.offset
)
77 return AV_PIX_FMT_NONE
;
81 AVClass
*class; ///< class for private options
82 int frame_size
; ///< size in bytes of a grabbed frame
83 AVRational framerate_q
; ///< framerate
84 char *framerate
; ///< framerate string set by a private option
85 int64_t time_frame
; ///< time for the next frame to output (in 1/1000000 units)
87 int fd
; ///< framebuffer device file descriptor
88 int width
, height
; ///< assumed frame resolution
89 int frame_linesize
; ///< linesize of the output frame, it is assumed to be constant
92 struct fb_var_screeninfo varinfo
; ///< variable info;
93 struct fb_fix_screeninfo fixinfo
; ///< fixed info;
95 uint8_t *data
; ///< framebuffer data
98 static av_cold
int fbdev_read_header(AVFormatContext
*avctx
)
100 FBDevContext
*fbdev
= avctx
->priv_data
;
102 enum AVPixelFormat pix_fmt
;
103 int ret
, flags
= O_RDONLY
;
105 ret
= av_parse_video_rate(&fbdev
->framerate_q
, fbdev
->framerate
);
107 av_log(avctx
, AV_LOG_ERROR
, "Could not parse framerate '%s'.\n", fbdev
->framerate
);
111 if (!(st
= avformat_new_stream(avctx
, NULL
)))
112 return AVERROR(ENOMEM
);
113 avpriv_set_pts_info(st
, 64, 1, 1000000); /* 64 bits pts in microseconds */
115 /* NONBLOCK is ignored by the fbdev driver, only set for consistency */
116 if (avctx
->flags
& AVFMT_FLAG_NONBLOCK
)
119 if ((fbdev
->fd
= open(avctx
->filename
, flags
)) == -1) {
120 ret
= AVERROR(errno
);
121 av_log(avctx
, AV_LOG_ERROR
,
122 "Could not open framebuffer device '%s': %s\n",
123 avctx
->filename
, strerror(ret
));
127 if (ioctl(fbdev
->fd
, FBIOGET_VSCREENINFO
, &fbdev
->varinfo
) < 0) {
128 ret
= AVERROR(errno
);
129 av_log(avctx
, AV_LOG_ERROR
,
130 "FBIOGET_VSCREENINFO: %s\n", strerror(errno
));
134 if (ioctl(fbdev
->fd
, FBIOGET_FSCREENINFO
, &fbdev
->fixinfo
) < 0) {
135 ret
= AVERROR(errno
);
136 av_log(avctx
, AV_LOG_ERROR
,
137 "FBIOGET_FSCREENINFO: %s\n", strerror(errno
));
141 pix_fmt
= get_pixfmt_from_fb_varinfo(&fbdev
->varinfo
);
142 if (pix_fmt
== AV_PIX_FMT_NONE
) {
143 ret
= AVERROR(EINVAL
);
144 av_log(avctx
, AV_LOG_ERROR
,
145 "Framebuffer pixel format not supported.\n");
149 fbdev
->width
= fbdev
->varinfo
.xres
;
150 fbdev
->height
= fbdev
->varinfo
.yres
;
151 fbdev
->bytes_per_pixel
= (fbdev
->varinfo
.bits_per_pixel
+ 7) >> 3;
152 fbdev
->frame_linesize
= fbdev
->width
* fbdev
->bytes_per_pixel
;
153 fbdev
->frame_size
= fbdev
->frame_linesize
* fbdev
->height
;
154 fbdev
->time_frame
= AV_NOPTS_VALUE
;
155 fbdev
->data
= mmap(NULL
, fbdev
->fixinfo
.smem_len
, PROT_READ
, MAP_SHARED
, fbdev
->fd
, 0);
156 if (fbdev
->data
== MAP_FAILED
) {
157 ret
= AVERROR(errno
);
158 av_log(avctx
, AV_LOG_ERROR
, "Error in mmap(): %s\n", strerror(errno
));
162 st
->codec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
163 st
->codec
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
164 st
->codec
->width
= fbdev
->width
;
165 st
->codec
->height
= fbdev
->height
;
166 st
->codec
->pix_fmt
= pix_fmt
;
167 st
->codec
->time_base
= (AVRational
){fbdev
->framerate_q
.den
, fbdev
->framerate_q
.num
};
168 st
->codec
->bit_rate
=
169 fbdev
->width
* fbdev
->height
* fbdev
->bytes_per_pixel
* av_q2d(fbdev
->framerate_q
) * 8;
171 av_log(avctx
, AV_LOG_INFO
,
172 "w:%d h:%d bpp:%d pixfmt:%s fps:%d/%d bit_rate:%d\n",
173 fbdev
->width
, fbdev
->height
, fbdev
->varinfo
.bits_per_pixel
,
174 av_get_pix_fmt_name(pix_fmt
),
175 fbdev
->framerate_q
.num
, fbdev
->framerate_q
.den
,
176 st
->codec
->bit_rate
);
184 static int fbdev_read_packet(AVFormatContext
*avctx
, AVPacket
*pkt
)
186 FBDevContext
*fbdev
= avctx
->priv_data
;
187 int64_t curtime
, delay
;
192 if (fbdev
->time_frame
== AV_NOPTS_VALUE
)
193 fbdev
->time_frame
= av_gettime();
195 /* wait based on the frame rate */
196 curtime
= av_gettime();
197 delay
= fbdev
->time_frame
- curtime
;
199 "time_frame:%"PRId64
" curtime:%"PRId64
" delay:%"PRId64
"\n",
200 fbdev
->time_frame
, curtime
, delay
);
202 if (avctx
->flags
& AVFMT_FLAG_NONBLOCK
)
203 return AVERROR(EAGAIN
);
204 ts
.tv_sec
= delay
/ 1000000;
205 ts
.tv_nsec
= (delay
% 1000000) * 1000;
206 while (nanosleep(&ts
, &ts
) < 0 && errno
== EINTR
);
208 /* compute the time of the next frame */
209 fbdev
->time_frame
+= INT64_C(1000000) / av_q2d(fbdev
->framerate_q
);
211 if ((ret
= av_new_packet(pkt
, fbdev
->frame_size
)) < 0)
214 /* refresh fbdev->varinfo, visible data position may change at each call */
215 if (ioctl(fbdev
->fd
, FBIOGET_VSCREENINFO
, &fbdev
->varinfo
) < 0)
216 av_log(avctx
, AV_LOG_WARNING
,
217 "Error refreshing variable info: %s\n", strerror(errno
));
221 /* compute visible data offset */
222 pin
= fbdev
->data
+ fbdev
->bytes_per_pixel
* fbdev
->varinfo
.xoffset
+
223 fbdev
->varinfo
.yoffset
* fbdev
->fixinfo
.line_length
;
226 // TODO it'd be nice if the lines were aligned
227 for (i
= 0; i
< fbdev
->height
; i
++) {
228 memcpy(pout
, pin
, fbdev
->frame_linesize
);
229 pin
+= fbdev
->fixinfo
.line_length
;
230 pout
+= fbdev
->frame_linesize
;
233 return fbdev
->frame_size
;
236 static av_cold
int fbdev_read_close(AVFormatContext
*avctx
)
238 FBDevContext
*fbdev
= avctx
->priv_data
;
240 munmap(fbdev
->data
, fbdev
->frame_size
);
246 #define OFFSET(x) offsetof(FBDevContext, x)
247 #define DEC AV_OPT_FLAG_DECODING_PARAM
248 static const AVOption options
[] = {
249 { "framerate","", OFFSET(framerate
), AV_OPT_TYPE_STRING
, {.str
= "25"}, 0, 0, DEC
},
253 static const AVClass fbdev_class
= {
254 .class_name
= "fbdev indev",
255 .item_name
= av_default_item_name
,
257 .version
= LIBAVUTIL_VERSION_INT
,
260 AVInputFormat ff_fbdev_demuxer
= {
262 .long_name
= NULL_IF_CONFIG_SMALL("Linux framebuffer"),
263 .priv_data_size
= sizeof(FBDevContext
),
264 .read_header
= fbdev_read_header
,
265 .read_packet
= fbdev_read_packet
,
266 .read_close
= fbdev_read_close
,
267 .flags
= AVFMT_NOFILE
,
268 .priv_class
= &fbdev_class
,