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 FFmpeg.
8 * FFmpeg 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 * FFmpeg 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 FFmpeg; 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/
32 #include <sys/ioctl.h>
37 #include "libavutil/internal.h"
38 #include "libavutil/log.h"
39 #include "libavutil/mem.h"
40 #include "libavutil/opt.h"
41 #include "libavutil/time.h"
42 #include "libavutil/parseutils.h"
43 #include "libavutil/pixdesc.h"
44 #include "libavformat/internal.h"
46 #include "fbdev_common.h"
48 typedef struct FBDevContext
{
49 AVClass
*class; ///< class for private options
50 int frame_size
; ///< size in bytes of a grabbed frame
51 AVRational framerate_q
; ///< framerate
52 int64_t time_frame
; ///< time for the next frame to output (in 1/1000000 units)
54 int fd
; ///< framebuffer device file descriptor
55 int width
, height
; ///< assumed frame resolution
56 int frame_linesize
; ///< linesize of the output frame, it is assumed to be constant
59 struct fb_var_screeninfo varinfo
; ///< variable info;
60 struct fb_fix_screeninfo fixinfo
; ///< fixed info;
62 uint8_t *data
; ///< framebuffer data
65 static av_cold
int fbdev_read_header(AVFormatContext
*avctx
)
67 FBDevContext
*fbdev
= avctx
->priv_data
;
69 enum AVPixelFormat pix_fmt
;
70 int ret
, flags
= O_RDONLY
;
73 if (!(st
= avformat_new_stream(avctx
, NULL
)))
74 return AVERROR(ENOMEM
);
75 avpriv_set_pts_info(st
, 64, 1, 1000000); /* 64 bits pts in microseconds */
77 /* NONBLOCK is ignored by the fbdev driver, only set for consistency */
78 if (avctx
->flags
& AVFMT_FLAG_NONBLOCK
)
81 if (avctx
->filename
[0])
82 device
= avctx
->filename
;
84 device
= ff_fbdev_default_device();
86 if ((fbdev
->fd
= avpriv_open(device
, flags
)) == -1) {
88 av_log(avctx
, AV_LOG_ERROR
,
89 "Could not open framebuffer device '%s': %s\n",
90 device
, av_err2str(ret
));
94 if (ioctl(fbdev
->fd
, FBIOGET_VSCREENINFO
, &fbdev
->varinfo
) < 0) {
96 av_log(avctx
, AV_LOG_ERROR
,
97 "FBIOGET_VSCREENINFO: %s\n", av_err2str(ret
));
101 if (ioctl(fbdev
->fd
, FBIOGET_FSCREENINFO
, &fbdev
->fixinfo
) < 0) {
102 ret
= AVERROR(errno
);
103 av_log(avctx
, AV_LOG_ERROR
,
104 "FBIOGET_FSCREENINFO: %s\n", av_err2str(ret
));
108 pix_fmt
= ff_get_pixfmt_from_fb_varinfo(&fbdev
->varinfo
);
109 if (pix_fmt
== AV_PIX_FMT_NONE
) {
110 ret
= AVERROR(EINVAL
);
111 av_log(avctx
, AV_LOG_ERROR
,
112 "Framebuffer pixel format not supported.\n");
116 fbdev
->width
= fbdev
->varinfo
.xres
;
117 fbdev
->height
= fbdev
->varinfo
.yres
;
118 fbdev
->bytes_per_pixel
= (fbdev
->varinfo
.bits_per_pixel
+ 7) >> 3;
119 fbdev
->frame_linesize
= fbdev
->width
* fbdev
->bytes_per_pixel
;
120 fbdev
->frame_size
= fbdev
->frame_linesize
* fbdev
->height
;
121 fbdev
->time_frame
= AV_NOPTS_VALUE
;
122 fbdev
->data
= mmap(NULL
, fbdev
->fixinfo
.smem_len
, PROT_READ
, MAP_SHARED
, fbdev
->fd
, 0);
123 if (fbdev
->data
== MAP_FAILED
) {
124 ret
= AVERROR(errno
);
125 av_log(avctx
, AV_LOG_ERROR
, "Error in mmap(): %s\n", av_err2str(ret
));
129 st
->codec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
130 st
->codec
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
131 st
->codec
->width
= fbdev
->width
;
132 st
->codec
->height
= fbdev
->height
;
133 st
->codec
->pix_fmt
= pix_fmt
;
134 st
->codec
->time_base
= av_inv_q(fbdev
->framerate_q
);
135 st
->codec
->bit_rate
=
136 fbdev
->width
* fbdev
->height
* fbdev
->bytes_per_pixel
* av_q2d(fbdev
->framerate_q
) * 8;
138 av_log(avctx
, AV_LOG_INFO
,
139 "w:%d h:%d bpp:%d pixfmt:%s fps:%d/%d bit_rate:%d\n",
140 fbdev
->width
, fbdev
->height
, fbdev
->varinfo
.bits_per_pixel
,
141 av_get_pix_fmt_name(pix_fmt
),
142 fbdev
->framerate_q
.num
, fbdev
->framerate_q
.den
,
143 st
->codec
->bit_rate
);
151 static int fbdev_read_packet(AVFormatContext
*avctx
, AVPacket
*pkt
)
153 FBDevContext
*fbdev
= avctx
->priv_data
;
154 int64_t curtime
, delay
;
159 if (fbdev
->time_frame
== AV_NOPTS_VALUE
)
160 fbdev
->time_frame
= av_gettime();
162 /* wait based on the frame rate */
164 curtime
= av_gettime();
165 delay
= fbdev
->time_frame
- curtime
;
167 "time_frame:%"PRId64
" curtime:%"PRId64
" delay:%"PRId64
"\n",
168 fbdev
->time_frame
, curtime
, delay
);
170 fbdev
->time_frame
+= INT64_C(1000000) / av_q2d(fbdev
->framerate_q
);
173 if (avctx
->flags
& AVFMT_FLAG_NONBLOCK
)
174 return AVERROR(EAGAIN
);
175 ts
.tv_sec
= delay
/ 1000000;
176 ts
.tv_nsec
= (delay
% 1000000) * 1000;
177 while (nanosleep(&ts
, &ts
) < 0 && errno
== EINTR
);
180 if ((ret
= av_new_packet(pkt
, fbdev
->frame_size
)) < 0)
183 /* refresh fbdev->varinfo, visible data position may change at each call */
184 if (ioctl(fbdev
->fd
, FBIOGET_VSCREENINFO
, &fbdev
->varinfo
) < 0) {
185 av_log(avctx
, AV_LOG_WARNING
,
186 "Error refreshing variable info: %s\n", av_err2str(AVERROR(errno
)));
191 /* compute visible data offset */
192 pin
= fbdev
->data
+ fbdev
->bytes_per_pixel
* fbdev
->varinfo
.xoffset
+
193 fbdev
->varinfo
.yoffset
* fbdev
->fixinfo
.line_length
;
196 for (i
= 0; i
< fbdev
->height
; i
++) {
197 memcpy(pout
, pin
, fbdev
->frame_linesize
);
198 pin
+= fbdev
->fixinfo
.line_length
;
199 pout
+= fbdev
->frame_linesize
;
202 return fbdev
->frame_size
;
205 static av_cold
int fbdev_read_close(AVFormatContext
*avctx
)
207 FBDevContext
*fbdev
= avctx
->priv_data
;
209 munmap(fbdev
->data
, fbdev
->fixinfo
.smem_len
);
215 static int fbdev_get_device_list(AVFormatContext
*s
, AVDeviceInfoList
*device_list
)
217 return ff_fbdev_get_device_list(device_list
);
220 #define OFFSET(x) offsetof(FBDevContext, x)
221 #define DEC AV_OPT_FLAG_DECODING_PARAM
222 static const AVOption options
[] = {
223 { "framerate","", OFFSET(framerate_q
), AV_OPT_TYPE_VIDEO_RATE
, {.str
= "25"}, 0, 0, DEC
},
227 static const AVClass fbdev_class
= {
228 .class_name
= "fbdev indev",
229 .item_name
= av_default_item_name
,
231 .version
= LIBAVUTIL_VERSION_INT
,
232 .category
= AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT
,
235 AVInputFormat ff_fbdev_demuxer
= {
237 .long_name
= NULL_IF_CONFIG_SMALL("Linux framebuffer"),
238 .priv_data_size
= sizeof(FBDevContext
),
239 .read_header
= fbdev_read_header
,
240 .read_packet
= fbdev_read_packet
,
241 .read_close
= fbdev_read_close
,
242 .get_device_list
= fbdev_get_device_list
,
243 .flags
= AVFMT_NOFILE
,
244 .priv_class
= &fbdev_class
,