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/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" 45#include "avdevice.h" 46#include "fbdev_common.h" 47 48typedef struct { 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) 53 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 57 int bytes_per_pixel; 58 59 struct fb_var_screeninfo varinfo; ///< variable info; 60 struct fb_fix_screeninfo fixinfo; ///< fixed info; 61 62 uint8_t *data; ///< framebuffer data 63} FBDevContext; 64 65static av_cold int fbdev_read_header(AVFormatContext *avctx) 66{ 67 FBDevContext *fbdev = avctx->priv_data; 68 AVStream *st = NULL; 69 enum AVPixelFormat pix_fmt; 70 int ret, flags = O_RDONLY; 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 ((fbdev->fd = avpriv_open(avctx->filename, flags)) == -1) { 81 ret = AVERROR(errno); 82 av_log(avctx, AV_LOG_ERROR, 83 "Could not open framebuffer device '%s': %s\n", 84 avctx->filename, av_err2str(ret)); 85 return ret; 86 } 87 88 if (ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->varinfo) < 0) { 89 ret = AVERROR(errno); 90 av_log(avctx, AV_LOG_ERROR, 91 "FBIOGET_VSCREENINFO: %s\n", av_err2str(ret)); 92 goto fail; 93 } 94 95 if (ioctl(fbdev->fd, FBIOGET_FSCREENINFO, &fbdev->fixinfo) < 0) { 96 ret = AVERROR(errno); 97 av_log(avctx, AV_LOG_ERROR, 98 "FBIOGET_FSCREENINFO: %s\n", av_err2str(ret)); 99 goto fail; 100 } 101 102 pix_fmt = ff_get_pixfmt_from_fb_varinfo(&fbdev->varinfo); 103 if (pix_fmt == AV_PIX_FMT_NONE) { 104 ret = AVERROR(EINVAL); 105 av_log(avctx, AV_LOG_ERROR, 106 "Framebuffer pixel format not supported.\n"); 107 goto fail; 108 } 109 110 fbdev->width = fbdev->varinfo.xres; 111 fbdev->height = fbdev->varinfo.yres; 112 fbdev->bytes_per_pixel = (fbdev->varinfo.bits_per_pixel + 7) >> 3; 113 fbdev->frame_linesize = fbdev->width * fbdev->bytes_per_pixel; 114 fbdev->frame_size = fbdev->frame_linesize * fbdev->height; 115 fbdev->time_frame = AV_NOPTS_VALUE; 116 fbdev->data = mmap(NULL, fbdev->fixinfo.smem_len, PROT_READ, MAP_SHARED, fbdev->fd, 0); 117 if (fbdev->data == MAP_FAILED) { 118 ret = AVERROR(errno); 119 av_log(avctx, AV_LOG_ERROR, "Error in mmap(): %s\n", av_err2str(ret)); 120 goto fail; 121 } 122 123 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 124 st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; 125 st->codec->width = fbdev->width; 126 st->codec->height = fbdev->height; 127 st->codec->pix_fmt = pix_fmt; 128 st->codec->time_base = av_inv_q(fbdev->framerate_q); 129 st->codec->bit_rate = 130 fbdev->width * fbdev->height * fbdev->bytes_per_pixel * av_q2d(fbdev->framerate_q) * 8; 131 132 av_log(avctx, AV_LOG_INFO, 133 "w:%d h:%d bpp:%d pixfmt:%s fps:%d/%d bit_rate:%d\n", 134 fbdev->width, fbdev->height, fbdev->varinfo.bits_per_pixel, 135 av_get_pix_fmt_name(pix_fmt), 136 fbdev->framerate_q.num, fbdev->framerate_q.den, 137 st->codec->bit_rate); 138 return 0; 139 140fail: 141 close(fbdev->fd); 142 return ret; 143} 144 145static int fbdev_read_packet(AVFormatContext *avctx, AVPacket *pkt) 146{ 147 FBDevContext *fbdev = avctx->priv_data; 148 int64_t curtime, delay; 149 struct timespec ts; 150 int i, ret; 151 uint8_t *pin, *pout; 152 153 if (fbdev->time_frame == AV_NOPTS_VALUE) 154 fbdev->time_frame = av_gettime(); 155 156 /* wait based on the frame rate */ 157 while (1) { 158 curtime = av_gettime(); 159 delay = fbdev->time_frame - curtime; 160 av_dlog(avctx, 161 "time_frame:%"PRId64" curtime:%"PRId64" delay:%"PRId64"\n", 162 fbdev->time_frame, curtime, delay); 163 if (delay <= 0) { 164 fbdev->time_frame += INT64_C(1000000) / av_q2d(fbdev->framerate_q); 165 break; 166 } 167 if (avctx->flags & AVFMT_FLAG_NONBLOCK) 168 return AVERROR(EAGAIN); 169 ts.tv_sec = delay / 1000000; 170 ts.tv_nsec = (delay % 1000000) * 1000; 171 while (nanosleep(&ts, &ts) < 0 && errno == EINTR); 172 } 173 174 if ((ret = av_new_packet(pkt, fbdev->frame_size)) < 0) 175 return ret; 176 177 /* refresh fbdev->varinfo, visible data position may change at each call */ 178 if (ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->varinfo) < 0) 179 av_log(avctx, AV_LOG_WARNING, 180 "Error refreshing variable info: %s\n", av_err2str(ret)); 181 182 pkt->pts = curtime; 183 184 /* compute visible data offset */ 185 pin = fbdev->data + fbdev->bytes_per_pixel * fbdev->varinfo.xoffset + 186 fbdev->varinfo.yoffset * fbdev->fixinfo.line_length; 187 pout = pkt->data; 188 189 for (i = 0; i < fbdev->height; i++) { 190 memcpy(pout, pin, fbdev->frame_linesize); 191 pin += fbdev->fixinfo.line_length; 192 pout += fbdev->frame_linesize; 193 } 194 195 return fbdev->frame_size; 196} 197 198static av_cold int fbdev_read_close(AVFormatContext *avctx) 199{ 200 FBDevContext *fbdev = avctx->priv_data; 201 202 munmap(fbdev->data, fbdev->fixinfo.smem_len); 203 close(fbdev->fd); 204 205 return 0; 206} 207 208static int fbdev_get_device_list(AVFormatContext *s, AVDeviceInfoList *device_list) 209{ 210 return ff_fbdev_get_device_list(device_list); 211} 212 213#define OFFSET(x) offsetof(FBDevContext, x) 214#define DEC AV_OPT_FLAG_DECODING_PARAM 215static const AVOption options[] = { 216 { "framerate","", OFFSET(framerate_q), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC }, 217 { NULL }, 218}; 219 220static const AVClass fbdev_class = { 221 .class_name = "fbdev indev", 222 .item_name = av_default_item_name, 223 .option = options, 224 .version = LIBAVUTIL_VERSION_INT, 225 .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, 226}; 227 228AVInputFormat ff_fbdev_demuxer = { 229 .name = "fbdev", 230 .long_name = NULL_IF_CONFIG_SMALL("Linux framebuffer"), 231 .priv_data_size = sizeof(FBDevContext), 232 .read_header = fbdev_read_header, 233 .read_packet = fbdev_read_packet, 234 .read_close = fbdev_read_close, 235 .get_device_list = fbdev_get_device_list, 236 .flags = AVFMT_NOFILE, 237 .priv_class = &fbdev_class, 238}; 239