Question

I am a hobby programmer and trying to make screen-recorder in Ubuntu using python. Using this code able to take screenshot.

import wx
app = wx.App(False)
s = wx.ScreenDC()
w, h = s.Size.Get()
b = wx.EmptyBitmap(w, h)
m = wx.MemoryDCFromDC(s)
m.SelectObject(b)
m.Blit(0, 0, w, h, s, 0, 0)
m.SelectObject(wx.NullBitmap)
b.SaveFile("screenshot.png", wx.BITMAP_TYPE_PNG)

And using loop I take more pictures and create video using these screen shots.My code is given bellow,

import wx,os
app=wx.App(False)
s = wx.ScreenDC()
w, h = s.Size.Get()
b = wx.EmptyBitmap(w, h)
m = wx.MemoryDCFromDC(s)
i=0
while i<50:
   m.SelectObject(b)
   m.Blit(0, 0, w, h, s, 0, 0)
   m.SelectObject(wx.NullBitmap)
   b.SaveFile('{0:05d}.png'.format(i), wx.BITMAP_TYPE_PNG)
   i+=1
os.system('ffmpeg -f image2 -r 8 -i %05d.png -vcodec mpeg4 -y movie1.mp4')
i=0  
while i<50:
   os.remove('{0:05d}.png'.format(i))
   i += 1   `

In above code I take 50 pictures and store as 00000.png to 00049.png and make video using ffmpeg. After creating video I delete all pictures.

Current Problems:

  • Very small delay between screen shoots. If try to record videos using this code out put is not perfect.
  • For recording long time it is not efficient. It take lot of hard drive memory to store screen shoots. And use more CPU .

What I do for get code more efficient? Using pure python how to create video from pictures? Is there any alternative methods to record screen? I like to improve my code.

Was it helpful?

Solution

The way you've written the code, you have to wait for each file to save before you grab the next screenshot. That's where your "very small delay" comes from.

You could grab all of the snapshots in memory and then write them at the end:

snapshots = []
for i in range(20):
   b = wx.EmptyBitmap(w, h)
   m.SelectObject(b)
   m.Blit(0, 0, w, h, s, 0, 0)
   m.SelectObject(wx.NullBitmap)
   snapshots.append(b)
for snapshot in snapshots:
   snapshot.SaveFile('{0:05d}.png'.format(i), wx.BITMAP_TYPE_PNG)

But this will use up a lot of memory—maybe enough for the malloc calls (or, worse, swap thrash) to slow you down.

Another alternative is to push the write off to a background thread. (Since the work is probably I/O bound, normal Python threads should be fine here.) For example:

with concurrent.futures.ThreadPoolExecutor() as executor:
    for i in range(20):
       b = wx.EmptyBitmap(w, h)
       m.SelectObject(b)
       m.Blit(0, 0, w, h, s, 0, 0)
       m.SelectObject(wx.NullBitmap)
       executor.submit(b.SaveFile, '{0:05d}.png'.format(i), wx.BITMAP_TYPE_PNG)

But one way or another, if you fix this… you don't want to snapshots to come as fast as possible. For example, if your screen refreshes, say, 60 times/second, and your 20 loops all finish in the first 1/60th of a second, you'll just get 20 copies of the same frame. That's no good. So, you really need to write some kind of simple scheduler for the snapshots. (Commonly, screenshots actually go much slower than the screen refresh rate—1/20th of a second, or even 1/6 or 1/2.)

Fortunately, wx has a few nice ways to do this. For example, use wx.Timer to run your code every 1000/60 ms.

This has an added advantage that you're not blocking the entire event loop while taking the snapshots, only briefly blocking it once every 1/60th (or 1/20th or whatever) of a second.


Of course you can, and may have to, combine these two solutions: Use a timer or other scheduler to set a maximum frame rate, and offload some of the work to get as close to that maximum as possible.


As for handling much longer screen grabs: If you try to save tens of thousands of frames as PNG files, that's going to take a lot of space. (Why? Well, a PNG uses lossless compression—it may get your 4MB screen grab down to 400K. But an H264 video uses lossy compression and, more importantly, only needs to track changes from one frame to the next, so it may only take 100K per key frame and 4K per difference frame.)

One thing you could do is every so often (e.g., every 120 frames) kick off a job to compress them into a video and delete them, them concatenate all of the videos at the end. To keep things simple, you probably want the first frame of each batch to be a new keyframe. MP4/H264 isn't the most fun format to concatenate, but it's still probably easier than switching to a streaming compressor.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top