Taking Screen Snapshots on the Raspberry Pi

Last time around, I was doing some display wrangling, trying to put some amount of ‘framework’ goodness around this part of the Raspberry Pi operating system. With the addition of a couple more classes, I an finally do something useful.

Here is how to take a screen snapshot:

local ffi = require "ffi"
local DMX = require "DisplayManX"

local Display = DMXDisplay();

local width = 640;
local height = 480;
local layer = 0;	-- keep the snapshot view on top

-- Create a resource to copy image into
local pixmap = DMX.DMXResource(width,height);

-- create a view with the snapshot as
-- the backing store
local mainView = DMX.DMXView.new(Display, 200, 200, width, height, layer, pformat, pixmap);


-- Hide the view so it's not in the picture
mainView:Hide();	

-- Do the snapshot
Display:Snapshot(pixmap);

-- Show it on the screen
mainView:Show();

ffi.C.sleep(5);

This piece of code is so short, it’s almost self explanatory. But, I’ll explain it anyway.

The first few lines are just setup.

local ffi = require "ffi"
local DMX = require "DisplayManX"

local Display = DMXDisplay();

local width = 640;
local height = 480;
local layer = 0;	-- keep the snapshot view on top

The only two that are strictly needed are these:

local DMX = require "DisplayManX"
local Display = DMXDisplay();

The first line pulls in the Lua DisplayManager module that I wrote. This is the entry point into the Raspberry Pi’s low level VideoCore routines. Besides containing the simple wrappers around the core routines, it also contains some convenience classes which make doing certain tasks very simple.

Creating a DMXDisplay object is most certainly the first thing you want to do in any application involving the display. This gives you a handle on the entire display. From here, you can ask what size things are, and it’s necessary for things like creating a view on the display.

The DMXDisplay interface has a function to take a snapshot. That function looks like this:

Snapshot = function(self, resource, transform)
  transform = transform or ffi.C.VC_IMAGE_ROT0;

  return DisplayManX.snapshot(self.Handle, resource.Handle, transform);
end,

The important part here is to note that a ‘resource’ is required to take a snapshot. This might look like 3 parameters are required, but through the magic of Lua, it atually turns into only 1. We’ll come back to this.

So, a resource is needed. What’s a resource? Basically a bitmap that the VideoCore system controls. You can create one easily like this:

local pixmap = DMX.DMXResource(width,height);

There are a few other parameters that you could use while creating your bitmap, but width and height are the essentials.

One thing of note, when you eventually call: Display:Snapshot(pixmap), you can not control which part of the screen is taken as the snapshot. It will take a snapshot of the entire screen. But, your bitmap does not have to be the same size! It can be any size you like. The VideoCore library will automatically squeeze your snapshot down to the size you specified when you created your resource.

So, we have a bitmap within which our snapshot will be stored. The last thing to do is to actually take the snapshot:

Display:Snapshot(pixmap);

In this particular example, I also want to display the snapshot on the screen. So, I created a ‘view’. This view is simply a way to display something on the screen.

local mainView = DMX.DMXView.new(Display, 200, 200, width, height, layer, pformat, pixmap);

In this case, I do a couple of special things. I create the view to be the same size as the pixel buffer, and in fact, I use the pixel buffer as the backing store of the view. That means that whenever the pixel buffer changes, for example, when a snapshot is taken, it will automatically show up in the view, because the system draws the view from the pixel buffer. I know it’s a mouth full, but that’s how the system works.

So the following sequence:

-- Hide the view so it's not in the picture
mainView:Hide();	

-- Do the snapshot
Display:Snapshot(pixmap);

-- Show it on the screen
mainView:Show();

ffi.C.sleep(5);

…will hide the view
take a snapshot
show the view

That’s so the view itself is not a part of the snapshot. You could achieve the same by moving the view ‘offscreen’ and then back again, but I haven’t implemented that part yet.

Well, there you have it. A whole bunch of words to describe a fairly simple process. I think this is an interesting thing though. Thus far, when I’ve seen Raspberry Pi ‘demo’ videos, it’s typically someone with a camera in one hand, bad lighting, trying to type on their keyboard and use their mouse while taking video. With the ability to take screen snapshots in code, making screencasts can’t be that far off.

Now, if only I could mate this capability with that x264 video compression library, I’d be all set!


8 Comments on “Taking Screen Snapshots on the Raspberry Pi”

  1. […] Taking Screen Snapshots on the Raspberry Pi → […]

    • MiCk says:

      hello, nice script.

      But it seems to fail when the display is rotate ! ( i test on raspbian with last firmware update ). If you have a fix … thx

      • I’ve never tried with rotated display. I will have to investigate and figure out what to do about it.

      • MiCk says:

        Any news ?

        i still cannot get it working ;o

        thx

      • I haven’t touched the Pi in quite some time. How are you rotating the display? It might be as simple as changing the ‘width’ and ‘height’. If it’s crashing, it’s probably going beyond boundaries. Or it might be a different color depth?

      • MiCk says:

        Hello,

        I was hoping that new firmware update will correct the bug but not …

        The depth is the same … i am going to test again with specific size …

        thx

      • MiCk says:

        Hey !

        Now i get a black image 😀 but no crash anymore .. just did

        Display:Snapshot(displayView.Resource, ffi.C.VC_IMAGE_MIRROR_ROT270);

  2. MiCk says:

    Hi, i am not a pro, but for what i get, when display is rotated the script completely freeze my raspberry ( all bcm function just “loop for ever” ).

    It seems to block with :

    print(“HIDE”);
    displayView:Hide();
    print(“SNAPSHOT”);
    Display:Snapshot(displayView.Resource);
    print(“SHOW”);
    displayView:Show();
    print(“THIS IS NOT PRINT WITH ROTATED DISPLAY”);

    thx!


Leave a comment