00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00030
00031
00032 #include <unistd.h>
00033 #include <fcntl.h>
00034 #include <sys/ioctl.h>
00035 #include <sys/time.h>
00036 #include <sys/mman.h>
00037 #include <time.h>
00038 #include <linux/fb.h>
00039
00040 #include "libavutil/mem.h"
00041 #include "libavutil/pixdesc.h"
00042 #include "libavformat/avformat.h"
00043
00044 struct rgb_pixfmt_map_entry {
00045 int bits_per_pixel;
00046 int red_offset, green_offset, blue_offset, alpha_offset;
00047 enum PixelFormat pixfmt;
00048 };
00049
00050 static struct rgb_pixfmt_map_entry rgb_pixfmt_map[] = {
00051
00052 { 32, 0, 8, 16, 24, PIX_FMT_RGBA },
00053 { 32, 16, 8, 0, 24, PIX_FMT_BGRA },
00054 { 32, 8, 16, 24, 0, PIX_FMT_ARGB },
00055 { 32, 3, 2, 8, 0, PIX_FMT_ABGR },
00056 { 24, 0, 8, 16, 0, PIX_FMT_RGB24 },
00057 { 24, 16, 8, 0, 0, PIX_FMT_BGR24 },
00058 };
00059
00060 static enum PixelFormat
00061 get_pixfmt_from_fb_varinfo(struct fb_var_screeninfo *varinfo)
00062 {
00063 int i;
00064
00065 for (i = 0; i < FF_ARRAY_ELEMS(rgb_pixfmt_map); i++) {
00066 struct rgb_pixfmt_map_entry *entry = &rgb_pixfmt_map[i];
00067 if (entry->bits_per_pixel == varinfo->bits_per_pixel &&
00068 entry->red_offset == varinfo->red.offset &&
00069 entry->green_offset == varinfo->green.offset &&
00070 entry->blue_offset == varinfo->blue.offset)
00071 return entry->pixfmt;
00072 }
00073
00074 return PIX_FMT_NONE;
00075 }
00076
00077 typedef struct {
00078 int frame_size;
00079 AVRational time_base;
00080 int64_t time_frame;
00081
00082 int fd;
00083 int width, heigth;
00084 int frame_linesize;
00085 int bytes_per_pixel;
00086
00087 struct fb_var_screeninfo varinfo;
00088 struct fb_fix_screeninfo fixinfo;
00089
00090 uint8_t *data;
00091 } FBDevContext;
00092
00093 av_cold static int fbdev_read_header(AVFormatContext *avctx,
00094 AVFormatParameters *ap)
00095 {
00096 FBDevContext *fbdev = avctx->priv_data;
00097 AVStream *st = NULL;
00098 enum PixelFormat pix_fmt;
00099 int ret, flags = O_RDONLY;
00100
00101 if (!(st = av_new_stream(avctx, 0)))
00102 return AVERROR(ENOMEM);
00103 av_set_pts_info(st, 64, 1, 1000000);
00104
00105 if (ap->time_base.den <= 0) {
00106 av_log(avctx, AV_LOG_ERROR, "Invalid time base %d/%d\n",
00107 ap->time_base.num, ap->time_base.den);
00108 return AVERROR(EINVAL);
00109 }
00110
00111
00112 if (avctx->flags & AVFMT_FLAG_NONBLOCK)
00113 flags |= O_NONBLOCK;
00114
00115 if ((fbdev->fd = open(avctx->filename, flags)) == -1) {
00116 ret = AVERROR(errno);
00117 av_log(avctx, AV_LOG_ERROR,
00118 "Could not open framebuffer device '%s': %s\n",
00119 avctx->filename, strerror(ret));
00120 return ret;
00121 }
00122
00123 if (ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->varinfo) < 0) {
00124 ret = AVERROR(errno);
00125 av_log(avctx, AV_LOG_ERROR,
00126 "FBIOGET_VSCREENINFO: %s\n", strerror(errno));
00127 goto fail;
00128 }
00129
00130 if (ioctl(fbdev->fd, FBIOGET_FSCREENINFO, &fbdev->fixinfo) < 0) {
00131 ret = AVERROR(errno);
00132 av_log(avctx, AV_LOG_ERROR,
00133 "FBIOGET_FSCREENINFO: %s\n", strerror(errno));
00134 goto fail;
00135 }
00136
00137 pix_fmt = get_pixfmt_from_fb_varinfo(&fbdev->varinfo);
00138 if (pix_fmt == PIX_FMT_NONE) {
00139 ret = AVERROR(EINVAL);
00140 av_log(avctx, AV_LOG_ERROR,
00141 "Framebuffer pixel format not supported.\n");
00142 goto fail;
00143 }
00144
00145 fbdev->width = fbdev->varinfo.xres;
00146 fbdev->heigth = fbdev->varinfo.yres;
00147 fbdev->bytes_per_pixel = (fbdev->varinfo.bits_per_pixel + 7) >> 3;
00148 fbdev->frame_linesize = fbdev->width * fbdev->bytes_per_pixel;
00149 fbdev->frame_size = fbdev->frame_linesize * fbdev->heigth;
00150 fbdev->time_base = ap->time_base;
00151 fbdev->time_frame = AV_NOPTS_VALUE;
00152 fbdev->data = mmap(NULL, fbdev->fixinfo.smem_len, PROT_READ, MAP_SHARED, fbdev->fd, 0);
00153 if (fbdev->data == MAP_FAILED) {
00154 ret = AVERROR(errno);
00155 av_log(avctx, AV_LOG_ERROR, "Error in mmap(): %s\n", strerror(errno));
00156 goto fail;
00157 }
00158
00159 st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00160 st->codec->codec_id = CODEC_ID_RAWVIDEO;
00161 st->codec->width = fbdev->width;
00162 st->codec->height = fbdev->heigth;
00163 st->codec->pix_fmt = pix_fmt;
00164 st->codec->time_base = ap->time_base;
00165 st->codec->bit_rate =
00166 fbdev->width * fbdev->heigth * fbdev->bytes_per_pixel / av_q2d(ap->time_base) * 8;
00167
00168 av_log(avctx, AV_LOG_INFO,
00169 "w:%d h:%d bpp:%d pixfmt:%s tb:%d/%d bit_rate:%d\n",
00170 fbdev->width, fbdev->heigth, fbdev->varinfo.bits_per_pixel,
00171 av_pix_fmt_descriptors[pix_fmt].name,
00172 ap->time_base.num, ap->time_base.den,
00173 st->codec->bit_rate);
00174 return 0;
00175
00176 fail:
00177 close(fbdev->fd);
00178 return ret;
00179 }
00180
00181 static int fbdev_read_packet(AVFormatContext *avctx, AVPacket *pkt)
00182 {
00183 FBDevContext *fbdev = avctx->priv_data;
00184 int64_t curtime, delay;
00185 struct timespec ts;
00186 int i, ret;
00187 uint8_t *pin, *pout;
00188
00189 if (fbdev->time_frame == AV_NOPTS_VALUE)
00190 fbdev->time_frame = av_gettime();
00191
00192
00193 while (1) {
00194 curtime = av_gettime();
00195 delay = fbdev->time_frame - curtime;
00196 av_dlog(avctx,
00197 "time_frame:%"PRId64" curtime:%"PRId64" delay:%"PRId64"\n",
00198 fbdev->time_frame, curtime, delay);
00199 if (delay <= 0) {
00200 fbdev->time_frame += INT64_C(1000000) * av_q2d(fbdev->time_base);
00201 break;
00202 }
00203 if (avctx->flags & AVFMT_FLAG_NONBLOCK)
00204 return AVERROR(EAGAIN);
00205 ts.tv_sec = delay / 1000000;
00206 ts.tv_nsec = (delay % 1000000) * 1000;
00207 while (nanosleep(&ts, &ts) < 0 && errno == EINTR);
00208 }
00209
00210 if ((ret = av_new_packet(pkt, fbdev->frame_size)) < 0)
00211 return ret;
00212
00213
00214 if (ioctl(fbdev->fd, FBIOGET_VSCREENINFO, &fbdev->varinfo) < 0)
00215 av_log(avctx, AV_LOG_WARNING,
00216 "Error refreshing variable info: %s\n", strerror(errno));
00217
00218 pkt->pts = curtime;
00219
00220
00221 pin = fbdev->data + fbdev->bytes_per_pixel * fbdev->varinfo.xoffset +
00222 fbdev->varinfo.yoffset * fbdev->fixinfo.line_length;
00223 pout = pkt->data;
00224
00225 for (i = 0; i < fbdev->heigth; i++) {
00226 memcpy(pout, pin, fbdev->frame_linesize);
00227 pin += fbdev->fixinfo.line_length;
00228 pout += fbdev->frame_linesize;
00229 }
00230
00231 return fbdev->frame_size;
00232 }
00233
00234 av_cold static int fbdev_read_close(AVFormatContext *avctx)
00235 {
00236 FBDevContext *fbdev = avctx->priv_data;
00237
00238 munmap(fbdev->data, fbdev->frame_size);
00239 close(fbdev->fd);
00240
00241 return 0;
00242 }
00243
00244 AVInputFormat ff_fbdev_demuxer = {
00245 .name = "fbdev",
00246 .long_name = NULL_IF_CONFIG_SMALL("Linux framebuffer"),
00247 .priv_data_size = sizeof(FBDevContext),
00248 .read_header = fbdev_read_header,
00249 .read_packet = fbdev_read_packet,
00250 .read_close = fbdev_read_close,
00251 .flags = AVFMT_NOFILE,
00252 };