[FFmpeg-devel] Fwd: framebuffer device demuxer

Stefano Sabatini stefano.sabatini-lala
Fri Jan 28 15:36:03 CET 2011


On date Friday 2011-01-28 01:10:46 +0000, M?ns Rullg?rd encoded:
> Sorry to chip in so late, but here are some comments on this, mostly
> minor.
> 
> Stefano Sabatini <stefano.sabatini-lala at poste.it> writes:
> 
> > From 9059a39f16131efc768d59784bd0179a2dfce69d Mon Sep 17 00:00:00 2001
> > From: Stefano Sabatini <stefano.sabatini-lala at poste.it>
> > Date: Tue, 25 Jan 2011 19:40:29 +0100
> > Subject: [PATCH] Add Linux framebuffer device.
> >
> > Based on a patch by Giliard B. de Freitas /com/gmail/giliarde.
> >
> > See thread:
> > Subject: [FFmpeg-devel] Fwd: framebuffer device demuxer
> > Date: Sat, 23 May 2009 09:32:13 -0300
> > ---
> >  Changelog                 |    1 +
> >  configure                 |    2 +
> >  doc/indevs.texi           |   18 ++++
> >  libavdevice/Makefile      |    1 +
> >  libavdevice/alldevices.c  |    1 +
> >  libavdevice/framebuffer.c |  216 +++++++++++++++++++++++++++++++++++++++++++++
> >  6 files changed, 239 insertions(+), 0 deletions(-)
> >  create mode 100644 libavdevice/framebuffer.c
[...]
> > + at section framebuffer
> > +
> > +Linux framebuffer input device.
> > +
> > +The name of the framebuffer device to read is a file device node,
> > +usually @file{/dev/fb0}.
> > +
> > +For example, to record from the framebuffer device /dev/fb0 with
> > + at file{ffmpeg}:
> > + at example
> > +ffmpeg -f framebuffer -r 10 -i /dev/fb0 out.avi
> > + at end example
> > +
> > +You can take a single screenshot image with the command:
> > + at example
> > +ffmpeg -f framebuffer -vframes 1 -r 1 -i /dev/fb0 screenshot.jpeg
> > + at end example
> > +
> >  @section jack
> >  
> >  JACK input device.
> 
> I think "framebuffer" is a bit too generic a name.  After all,
> anything with a display of any kind has a framebuffer.  The Linux
> framebuffer device generally goes by the name "fbdev", so perhaps that
> would be a better name.

Replaced with linuxfb, this should be more explicative of the fact
that it is a linux thing, this should prevent having users ask "I'm on
windows, why can't I find the fbdev device?".

[...]
> > +/**
> > + * Initialize the framebuffer input device.
> > + */
> > +av_cold static int framebuffer_read_header(AVFormatContext *avctx, AVFormatParameters *ap)
> > +{
> > +    FrameBufferContext *fb = avctx->priv_data;
> > +    struct fb_var_screeninfo fb_varinfo;
> > +    struct fb_fix_screeninfo fb_fixinfo;
> > +    AVStream *st = NULL;
> > +    enum PixelFormat pix_fmt;
> > +    int ret, bits_per_pixel, flags = O_RDONLY;
> > +
> > +    if (!(st = av_new_stream(avctx, 0)))
> > +        return AVERROR(ENOMEM);
> > +
> > +    if (avctx->flags & AVFMT_FLAG_NONBLOCK)
> > +        flags |= O_NONBLOCK;
> > +
> > +    if ((fb->fd = open(avctx->filename, flags)) == -1) {
> > +        ret = AVERROR(errno);
> > +        av_log(avctx, AV_LOG_ERROR, "Could not open framebuffer device '%s'.\n", avctx->filename);
> > +        goto fail;
> 
> You could return directly here and drop the check before close().

Done.

[...]
> > +    fb->width  = fb_varinfo.xres;
> > +    fb->height = fb_varinfo.yres;
> > +    bits_per_pixel = fb_varinfo.bits_per_pixel;
> > +
> > +    switch (bits_per_pixel) {
> > +    case  8: pix_fmt = PIX_FMT_PAL8  ; break;
> > +    case 15: pix_fmt = PIX_FMT_BGR555; break;
> > +    case 16: pix_fmt = PIX_FMT_RGB565; break;
> > +    case 24: pix_fmt = PIX_FMT_RGB24 ; break;
> > +    case 32: pix_fmt = PIX_FMT_RGB32 ; break;
> > +    default:
> > +        ret = AVERROR(EINVAL);
> > +        av_log(avctx, AV_LOG_ERROR, "Image with bits per pixel depth %d not supported.\n",
> > +               bits_per_pixel);
> > +        goto fail;
> > +    }
> 
> These are not the only formats possible.  You cannot use bits per
> pixel exclusively to determine the format.

Yes indeed. I'm using a far more robust solution now, yet incomplete,
also I have to see how to handle pal formats.

[...]
> > +static int framebuffer_read_packet(AVFormatContext *avctx, AVPacket *pkt)
> > +{
> > +    FrameBufferContext *fb = avctx->priv_data;
> > +    int64_t curtime, delay;
> > +    struct timespec ts;
> > +    int i, ret;
> > +    uint8_t *pin, *pout;
> > +
> > +    /* wait based on the frame rate */
> > +    while (1) {
> > +        curtime = av_gettime();
> > +        delay = curtime - fb->time_frame * av_q2d(fb->time_base);
> > +        if (delay >= 0) {
> > +            fb->time_frame += INT64_C(1000000) * av_q2d(fb->time_base);
> > +            break;
> > +        }
> > +        if (avctx->flags & AVFMT_FLAG_NONBLOCK)
> > +            return AVERROR(EAGAIN);
> > +        ts.tv_sec  =  delay / 1000000;
> > +        ts.tv_nsec = (delay % 1000000) * 1000;
> > +        nanosleep(&ts, NULL);
> > +    }
> 
> Using e.g. sem_timedwait(), which takes an absolute timeout, might
> give better accuracy here, at least on the off-chance that the process
> is descheduled between the av_gettime() and nanosleep() calls.

I confess that I have not much experience with this, attaching this
change as a second patch for easier review.

> > +    if ((ret = av_new_packet(pkt, fb->frame_size)) < 0)
> > +        return ret;
> > +
> > +    pkt->pts = curtime;
> > +    pin  = fb->data;
> > +    pout = pkt->data;
> > +    for (i = 0; i < fb->height; i++) {
> > +        memcpy(pout, pin, fb->frame_linesize);
> > +        pin  += fb->linesize;
> > +        pout += fb->frame_linesize;
> > +    }
> > +
> > +    return fb->frame_size;
> > +}
> > +
> > +/**
> > + * Close framebuffer input device.
> > + *
> > + * @return a negative value in case of errors, 0 otherwise
> 
> This comment is a lie.  The function always returns 0.  Not that much
> could go wrong...

I removed all the internal documentation, since it looked redundant
(and internal documentation tends to get outdated very easily).

[...]
> > +AVInputFormat framebuffer_demuxer = {
> > +    .name           = "framebuffer",
> > +    .long_name      = NULL_IF_CONFIG_SMALL("Linux framebuffer"),
> > +    .priv_data_size = sizeof(FrameBufferContext),
> > +    .read_header    = framebuffer_read_header,
> > +    .read_packet    = framebuffer_read_packet,
> > +    .read_close     = framebuffer_read_close,
> > +    .flags          = AVFMT_NOFILE,
> > +};
> 
> Is AVFMT_NOFILE really correct here?  I'm not sure what its semantics
> are supposed to be.

It's not explained in the docs, but AVFMT_NOFILE is used for all the
input and output device (possibly is what distinguishes a device from
a muxer/demuxer).

And I'm still observing the 100% CPU problem noted by Luca A. in this
thread, a 0-memcpy solution is possible but it requires some change to
the framework (as the written packet contains aligned data, which is
not supported ATM).
-- 
FFmpeg = Frenzy Fancy Minimal Powerful Extensive Generator



More information about the ffmpeg-devel mailing list