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