1 /*
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>
5 *
6 * This file is part of FFmpeg.
7 *
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.
12 *
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.
17 *
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
21 */
22
23 /**
24 * @file
25 * Linux framebuffer input device,
26 * inspired by code from fbgrab.c by Gunnar Monell.
27 * @see http://linux-fbdev.sourceforge.net/
28 */
29
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <sys/ioctl.h>
33 #include <sys/mman.h>
34 #include <time.h>
35 #include <linux/fb.h>
36
37 #include "libavutil/internal.h"
38 #include "libavutil/log.h"
39 #include "libavutil/opt.h"
40 #include "libavutil/time.h"
41 #include "libavutil/parseutils.h"
42 #include "libavutil/pixdesc.h"
43 #include "libavformat/internal.h"
44 #include "avdevice.h"
45 #include "fbdev_common.h"
46
47 typedef struct FBDevContext {
48 AVClass *class; ///< class for private options
49 int frame_size; ///< size in bytes of a grabbed frame
50 AVRational framerate_q; ///< framerate
51 int64_t time_frame; ///< time for the next frame to output (in 1/1000000 units)
52
53 int fd; ///< framebuffer device file descriptor
54 int width, height; ///< assumed frame resolution
55 int frame_linesize; ///< linesize of the output frame, it is assumed to be constant
56 int bytes_per_pixel;
57
58 struct fb_var_screeninfo varinfo; ///< variable info;
59 struct fb_fix_screeninfo fixinfo; ///< fixed info;
60
61 uint8_t *data; ///< framebuffer data
62 } FBDevContext;
63
fbdev_read_header(AVFormatContext * avctx)64 static av_cold int fbdev_read_header(AVFormatContext *avctx)
65 {
66 FBDevContext *fbdev = avctx->priv_data;
67 AVStream *st = NULL;
68 enum AVPixelFormat pix_fmt;
69 int ret, flags = O_RDONLY;
70 const char* device;
71
72 if (!(st = avformat_new_stream(avctx, NULL)))
73 return AVERROR(ENOMEM);
74 avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in microseconds */
75
76 /* NONBLOCK is ignored by the fbdev driver, only set for consistency */
77 if (avctx->flags & AVFMT_FLAG_NONBLOCK)
78 flags |= O_NONBLOCK;
79
80 if (avctx->url[0])
81 device = avctx->url;
82 else
83 device = ff_fbdev_default_device();
84
85 if ((fbdev->fd = avpriv_open(device, flags)) == -1) {
86 ret = AVERROR(errno);
87 av_log(avctx, AV_LOG_ERROR,
88 "Could not open framebuffer device '%s': %s\n",
89 device, av_err2str(ret));
90 return ret;
91 }
92
93 if (ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->varinfo) < 0) {
94 ret = AVERROR(errno);
95 av_log(avctx, AV_LOG_ERROR,
96 "FBIOGET_VSCREENINFO: %s\n", av_err2str(ret));
97 goto fail;
98 }
99
100 if (ioctl(fbdev->fd, FBIOGET_FSCREENINFO, &fbdev->fixinfo) < 0) {
101 ret = AVERROR(errno);
102 av_log(avctx, AV_LOG_ERROR,
103 "FBIOGET_FSCREENINFO: %s\n", av_err2str(ret));
104 goto fail;
105 }
106
107 pix_fmt = ff_get_pixfmt_from_fb_varinfo(&fbdev->varinfo);
108 if (pix_fmt == AV_PIX_FMT_NONE) {
109 ret = AVERROR(EINVAL);
110 av_log(avctx, AV_LOG_ERROR,
111 "Framebuffer pixel format not supported.\n");
112 goto fail;
113 }
114
115 fbdev->width = fbdev->varinfo.xres;
116 fbdev->height = fbdev->varinfo.yres;
117 fbdev->bytes_per_pixel = (fbdev->varinfo.bits_per_pixel + 7) >> 3;
118 fbdev->frame_linesize = fbdev->width * fbdev->bytes_per_pixel;
119 fbdev->frame_size = fbdev->frame_linesize * fbdev->height;
120 fbdev->time_frame = AV_NOPTS_VALUE;
121 fbdev->data = mmap(NULL, fbdev->fixinfo.smem_len, PROT_READ, MAP_SHARED, fbdev->fd, 0);
122 if (fbdev->data == MAP_FAILED) {
123 ret = AVERROR(errno);
124 av_log(avctx, AV_LOG_ERROR, "Error in mmap(): %s\n", av_err2str(ret));
125 goto fail;
126 }
127
128 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
129 st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
130 st->codecpar->width = fbdev->width;
131 st->codecpar->height = fbdev->height;
132 st->codecpar->format = pix_fmt;
133 st->avg_frame_rate = fbdev->framerate_q;
134 st->codecpar->bit_rate =
135 fbdev->width * fbdev->height * fbdev->bytes_per_pixel * av_q2d(fbdev->framerate_q) * 8;
136
137 av_log(avctx, AV_LOG_INFO,
138 "w:%d h:%d bpp:%d pixfmt:%s fps:%d/%d bit_rate:%"PRId64"\n",
139 fbdev->width, fbdev->height, fbdev->varinfo.bits_per_pixel,
140 av_get_pix_fmt_name(pix_fmt),
141 fbdev->framerate_q.num, fbdev->framerate_q.den,
142 st->codecpar->bit_rate);
143 return 0;
144
145 fail:
146 close(fbdev->fd);
147 return ret;
148 }
149
fbdev_read_packet(AVFormatContext * avctx,AVPacket * pkt)150 static int fbdev_read_packet(AVFormatContext *avctx, AVPacket *pkt)
151 {
152 FBDevContext *fbdev = avctx->priv_data;
153 int64_t curtime, delay;
154 struct timespec ts;
155 int i, ret;
156 uint8_t *pin, *pout;
157
158 if (fbdev->time_frame == AV_NOPTS_VALUE)
159 fbdev->time_frame = av_gettime_relative();
160
161 /* wait based on the frame rate */
162 while (1) {
163 curtime = av_gettime_relative();
164 delay = fbdev->time_frame - curtime;
165 av_log(avctx, AV_LOG_TRACE,
166 "time_frame:%"PRId64" curtime:%"PRId64" delay:%"PRId64"\n",
167 fbdev->time_frame, curtime, delay);
168 if (delay <= 0) {
169 fbdev->time_frame += INT64_C(1000000) / av_q2d(fbdev->framerate_q);
170 break;
171 }
172 if (avctx->flags & AVFMT_FLAG_NONBLOCK)
173 return AVERROR(EAGAIN);
174 ts.tv_sec = delay / 1000000;
175 ts.tv_nsec = (delay % 1000000) * 1000;
176 while (nanosleep(&ts, &ts) < 0 && errno == EINTR);
177 }
178
179 if ((ret = av_new_packet(pkt, fbdev->frame_size)) < 0)
180 return ret;
181
182 /* refresh fbdev->varinfo, visible data position may change at each call */
183 if (ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->varinfo) < 0) {
184 av_log(avctx, AV_LOG_WARNING,
185 "Error refreshing variable info: %s\n", av_err2str(AVERROR(errno)));
186 }
187
188 pkt->pts = av_gettime();
189
190 /* compute visible data offset */
191 pin = fbdev->data + fbdev->bytes_per_pixel * fbdev->varinfo.xoffset +
192 fbdev->varinfo.yoffset * fbdev->fixinfo.line_length;
193 pout = pkt->data;
194
195 for (i = 0; i < fbdev->height; i++) {
196 memcpy(pout, pin, fbdev->frame_linesize);
197 pin += fbdev->fixinfo.line_length;
198 pout += fbdev->frame_linesize;
199 }
200
201 return fbdev->frame_size;
202 }
203
fbdev_read_close(AVFormatContext * avctx)204 static av_cold int fbdev_read_close(AVFormatContext *avctx)
205 {
206 FBDevContext *fbdev = avctx->priv_data;
207
208 munmap(fbdev->data, fbdev->fixinfo.smem_len);
209 close(fbdev->fd);
210
211 return 0;
212 }
213
fbdev_get_device_list(AVFormatContext * s,AVDeviceInfoList * device_list)214 static int fbdev_get_device_list(AVFormatContext *s, AVDeviceInfoList *device_list)
215 {
216 return ff_fbdev_get_device_list(device_list);
217 }
218
219 #define OFFSET(x) offsetof(FBDevContext, x)
220 #define DEC AV_OPT_FLAG_DECODING_PARAM
221 static const AVOption options[] = {
222 { "framerate","", OFFSET(framerate_q), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC },
223 { NULL },
224 };
225
226 static const AVClass fbdev_class = {
227 .class_name = "fbdev indev",
228 .item_name = av_default_item_name,
229 .option = options,
230 .version = LIBAVUTIL_VERSION_INT,
231 .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
232 };
233
234 const AVInputFormat ff_fbdev_demuxer = {
235 .name = "fbdev",
236 .long_name = NULL_IF_CONFIG_SMALL("Linux framebuffer"),
237 .priv_data_size = sizeof(FBDevContext),
238 .read_header = fbdev_read_header,
239 .read_packet = fbdev_read_packet,
240 .read_close = fbdev_read_close,
241 .get_device_list = fbdev_get_device_list,
242 .flags = AVFMT_NOFILE,
243 .priv_class = &fbdev_class,
244 };
245