I’ve been doing a bit of development with FFmpeg to try and add a device for decoding the Looking Glass framebuffer. Long story short I now have working code that appears to be presentable. I’m making this post to attempt to gauge interest to see if it would be worth the trouble of trying to get it added into the main branch of FFmpeg.
diff --git a/libavdevice/KVMFR.h b/libavdevice/KVMFR.h
new file mode 100644
index 0000000000..c4c3d19495
--- /dev/null
+++ b/libavdevice/KVMFR.h
"no new code just copy KVMFR.h from looking glass project"
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index 6ea62b914e..cffd1b8e79 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -31,6 +31,7 @@ OBJS-$(CONFIG_GDIGRAB_INDEV) += gdigrab.o
OBJS-$(CONFIG_IEC61883_INDEV) += iec61883.o
OBJS-$(CONFIG_JACK_INDEV) += jack.o timefilter.o
OBJS-$(CONFIG_KMSGRAB_INDEV) += kmsgrab.o
+OBJS-$(CONFIG_FBDEV_INDEV) += kvmfr_dec.o
OBJS-$(CONFIG_LAVFI_INDEV) += lavfi.o
OBJS-$(CONFIG_OPENAL_INDEV) += openal-dec.o
OBJS-$(CONFIG_OPENGL_OUTDEV) += opengl_enc.o
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index 8633433254..61fe350997 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -39,6 +39,7 @@ extern AVInputFormat ff_gdigrab_demuxer;
extern AVInputFormat ff_iec61883_demuxer;
extern AVInputFormat ff_jack_demuxer;
extern AVInputFormat ff_kmsgrab_demuxer;
+extern AVInputFormat ff_kvmfr_demuxer;
extern AVInputFormat ff_lavfi_demuxer;
extern AVInputFormat ff_openal_demuxer;
extern AVOutputFormat ff_opengl_muxer;
diff --git a/libavdevice/kvmfr_dec.c b/libavdevice/kvmfr_dec.c
new file mode 100644
index 0000000000..aa0e5097cb
--- /dev/null
+++ b/libavdevice/kvmfr_dec.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2011 Stefano Sabatini
+ * Copyright (c) 2009 Giliard B. de Freitas <[email protected]>
+ * Copyright (C) 2002 Gunnar Monell <[email protected]>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * Looking-Glass framebuffer input device,
+ * Mirrors some code from fbdev_dec.c.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "libavutil/internal.h"
+#include "libavutil/log.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/time.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/pixdesc.h"
+#include "libavformat/internal.h"
+#include "avdevice.h"
+#include "KVMFR.h"
+
+typedef struct KVMFRContext {
+ AVClass *class; ///< class for private options
+ int frame_size; ///< size in bytes of a grabbed frame
+ AVRational framerate_q; ///< framerate
+ int64_t time_frame; ///< time for the next frame to output (in 1/1000000 units)
+
+ int shmFD; // KVMFR framebuffer device file
+ struct KVMFRHeader * shm;
+ int shmFD_size; ///< framebuffer device file descriptor
+ int width, height; ///< assumed frame resolution
+ int frame_linesize; ///< linesize of the output frame, it is assumed to be constant
+ int bytes_per_pixel;
+
+ uint8_t *data; ///< framebuffer data
+
+} KVMFRContext;
+
+static av_cold int kvmfr_read_header(AVFormatContext *avctx)
+{
+ KVMFRContext *kvmfr = avctx->priv_data;
+ AVStream *st = NULL;
+ enum AVPixelFormat pix_fmt;
+ int ret, bpp, flags = O_RDWR;
+ const char* shared_mem;
+ struct stat file_stat;
+ int bpp_map[6] = {0, 32, 32, 32, 12, 0};
+ enum AVPixelFormat pix_map[6] = {
+ AV_PIX_FMT_NONE, AV_PIX_FMT_BGRA, AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE, AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE};
+
+
+ if (!(st = avformat_new_stream(avctx, NULL)))
+ return AVERROR(ENOMEM);
+ avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in microseconds */
+
+ if (avctx->url[0])
+ shared_mem = avctx->url;
+ else
+ shared_mem = "/dev/shm/looking-glass";
+
+ if ((kvmfr->shmFD = avpriv_open(shared_mem, flags)) == -1) {
+ ret = AVERROR(errno);
+ av_log(avctx, AV_LOG_ERROR,
+ "Could not open looking-glass file '%s': %s\n",
+ shared_mem, av_err2str(ret));
+ return ret;
+ }
+
+ stat(shared_mem, &file_stat);
+ kvmfr->shmFD_size = file_stat.st_size;
+ kvmfr->shm = (struct KVMFRHeader *)mmap(0, kvmfr->shmFD_size, PROT_READ | PROT_WRITE, MAP_SHARED, kvmfr->shmFD, 0);
+ if (kvmfr->shm == MAP_FAILED) {
+ ret = AVERROR(errno);
+ av_log(avctx, AV_LOG_ERROR, "Error in mmap(): %s\n", av_err2str(ret));
+ goto fail;
+ }
+
+ __sync_or_and_fetch(&kvmfr->shm->flags, KVMFR_HEADER_FLAG_RESTART);
+
+ pix_fmt = pix_map[kvmfr->shm->frame.type];
+ if (pix_fmt == AV_PIX_FMT_NONE) {
+ ret = AVERROR(EINVAL);
+ av_log(avctx, AV_LOG_ERROR,
+ "Framebuffer pixel format not supported.\n");
+ munmap(kvmfr->shm, kvmfr->shmFD_size);
+ goto fail;
+ }
+ bpp = bpp_map[kvmfr->shm->frame.type];
+
+ kvmfr->width = kvmfr->shm->frame.width;
+ kvmfr->height = kvmfr->shm->frame.height;
+ kvmfr->bytes_per_pixel = (bpp + 7) >> 3;
+ kvmfr->frame_linesize = kvmfr->width * kvmfr->bytes_per_pixel;
+ kvmfr->frame_size = kvmfr->frame_linesize * kvmfr->height;
+ kvmfr->time_frame = AV_NOPTS_VALUE;
+ kvmfr->data = (uint8_t *)kvmfr->shm + kvmfr->shm->frame.dataPos;
+
+ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
+ st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
+ st->codecpar->width = kvmfr->width;
+ st->codecpar->height = kvmfr->height;
+ st->codecpar->format = pix_fmt;
+ st->avg_frame_rate = kvmfr->framerate_q;
+ st->codecpar->bit_rate = kvmfr->frame_size * av_q2d(kvmfr->framerate_q) * 8;
+
+ av_log(avctx, AV_LOG_INFO,
+ "w:%d h:%d bpp:%d pixfmt:%s fps:%d/%d bit_rate:%"PRId64"\n",
+ kvmfr->width, kvmfr->height, bpp,
+ av_get_pix_fmt_name(pix_fmt),
+ kvmfr->framerate_q.num, kvmfr->framerate_q.den,
+ st->codecpar->bit_rate);
+ return 0;
+
+fail:
+ close(kvmfr->shmFD);
+ return ret;
+}
+
+static int kvmfr_read_packet(AVFormatContext *avctx, AVPacket *pkt)
+{
+ KVMFRContext *kvmfr = avctx->priv_data;
+ int64_t curtime, delay;
+ struct timespec ts;
+ int ret;
+
+ if (kvmfr->time_frame == AV_NOPTS_VALUE)
+ kvmfr->time_frame = av_gettime();
+
+ /* wait based on the frame rate */
+ while (1) {
+ curtime = av_gettime();
+ delay = kvmfr->time_frame - curtime;
+ av_log(avctx, AV_LOG_TRACE,
+ "time_frame:%"PRId64" curtime:%"PRId64" delay:%"PRId64"\n",
+ kvmfr->time_frame, curtime, delay);
+ if (delay <= 0) {
+ kvmfr->time_frame += INT64_C(1000000) / av_q2d(kvmfr->framerate_q);
+ break;
+ }
+ if (avctx->flags & AVFMT_FLAG_NONBLOCK)
+ return AVERROR(EAGAIN);
+ ts.tv_sec = delay / 1000000;
+ ts.tv_nsec = (delay % 1000000) * 1000;
+ while (nanosleep(&ts, &ts) < 0 && errno == EINTR);
+ }
+
+ if ((ret = av_new_packet(pkt, kvmfr->frame_size)) < 0)
+ return ret;
+
+ // refresh where KVMFR framebuffer pointer due to double buffer
+ kvmfr->data = (uint8_t *)kvmfr->shm + kvmfr->shm->frame.dataPos;
+
+ __sync_and_and_fetch(&kvmfr->shm->frame.flags, ~KVMFR_FRAME_FLAG_UPDATE);
+
+ pkt->pts = curtime;
+
+ memcpy(pkt->data, kvmfr->data, kvmfr->frame_size);
+
+ return kvmfr->frame_size;
+}
+
+static av_cold int kvmfr_read_close(AVFormatContext *avctx)
+{
+ KVMFRContext *kvmfr = avctx->priv_data;
+
+ munmap(kvmfr->shm, kvmfr->shmFD_size);
+ close(kvmfr->shmFD);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(KVMFRContext, x)
+#define DEC AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+ { "framerate","", OFFSET(framerate_q), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC },
+ { NULL },
+};
+
+static const AVClass kvmfr_class = {
+ .class_name = "kvmfr indev",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
+};
+
+AVInputFormat ff_kvmfr_demuxer = {
+ .name = "kvmfr",
+ .long_name = NULL_IF_CONFIG_SMALL("KVM frame Relay"),
+ .priv_data_size = sizeof(KVMFRContext),
+ .read_header = kvmfr_read_header,
+ .read_packet = kvmfr_read_packet,
+ .read_close = kvmfr_read_close,
+ .flags = AVFMT_NOFILE,
+ .priv_class = &kvmfr_class,
+};
kvmfr.diff.txt (8.9 KB)
Edits: Embedded git diff into post. Same content as file.