2013-08-02 1 views
1

저는 취미 프로그래머이며, 파이썬을 사용하여 우분투에서 스크린 레코더를 만들려고합니다. 이 코드를 사용하여 스크린 샷을 찍을 수 있습니다.우분투의 스크린 레코더, 파이썬을 사용합니다.

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) 

내가 더 많은 사진을 가지고 우는 소리 주어진다이 화면 shots.My 코드를 사용하여 비디오를 만들 루프를 사용하여, 위의 코드에서

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 ` 

나는 00049에 00000.png으로 50 개 사진 저장을 .png 및 ffmpeg를 사용하여 비디오를 만듭니다. 비디오를 만든 후 모든 사진을 삭제합니다.

현재 문제 : 스크린 촬영 사이

  • 아주 약간의 지연. 이 코드를 사용하여 비디오를 녹화하려고하면 put이 완벽하지 않습니다.
  • 장시간 녹음 할 경우 효율적이지 않습니다. 스크린 샷을 저장하기 위해 많은 하드 드라이브 메모리가 필요합니다. 그리고 더 많은 CPU를 사용하십시오.

코드를보다 효율적으로 만들려면 어떻게해야합니까? 순수한 파이썬을 사용하여 사진에서 비디오를 만드는 방법? 화면을 기록하는 다른 방법이 있습니까? 코드를 개선하고 싶습니다.

+1

+1 잘 생각한 질문입니다. 지금은 테스트 할 수 없지만 이미 ffmpeg를 사용하고 있다면 이미지없이 화면을 녹화 할 수 있습니다. 이 예제를 약간 변경하여 매뉴얼 페이지에서 5 초 동안 스크린을 기록한 다음 멈추게했다. ffmpeg -f x11grab -s cif -r 25 -i : 0.0 /tmp/out.mpg -t 5' –

+0

side note : while 루프를 작성하고 수동으로 카운터를 증가 시키면 혼란스럽고 장황하며 fencepost 오류가 발생할 가능성이 높습니다. 'for this in range (20) :'라고 쓰십시오. 'm'과 'b'와 같이 의미없는 한 글자 이름을 사용하면서 일관성이없고 비표준 간격을 사용하면 코드를 읽기가 훨씬 어려워집니다. – abarnert

+0

또한 'ffmpeg'(끝에있는 변환을 위해서나 실제 스크린 샷을하기 위해)를 사용한다면, 도구로 포격하는 대신 ffmpeg/libav * 주위에 파이썬 래퍼를 사용하는 것이 좋습니다. 또는 툴을 사용하고자한다면'os.system' 대신'subprocess'를 사용하는 것을 고려해야합니다 (예 : 에러 처리 가능). – abarnert

답변

2

코드를 작성한 방법에 따라 다음 스크린 샷을 캡처하기 전에 각 파일이 저장 될 때까지 기다려야합니다. 그것이 바로 당신의 "아주 작은 지연"이 나오는 곳입니다.

당신은 메모리의 스냅 샷을 모두 잡아 다음 말을 작성할 수

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) 

하지만이 malloc 전화 (또는 악화, 스왑 - 아마도 메모리 부족을 많이 사용합니다 스 래시).

또 다른 대안은 백그라운드 스레드로 쓰기를 푸시하는 것입니다. (일이기 때문에 아마 내가/O 바운드, 일반 파이썬 스레드가 여기에 괜찮을한다.) 예를 들면 :

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) 

그러나 어떤 식 으로든, 당신은이 문제를 해결할 경우 ... 당신이 원하지 않는 스냅 샷은 최대한 빨리 올 수 있습니다. 예를 들어, 화면이 60 회/초로 새로 고침되고 20 개의 루프가 모두 처음 1/60 초에 끝나면 동일한 프레임을 20 회 복사하면됩니다. 그건 좋지 않아. 따라서 스냅 샷을위한 간단한 스케줄러를 작성해야합니다. 일반적으로 스크린 샷은 화면 새로 고침 빈도 (1/20 초, 심지어 1/6 또는 1/2)보다 훨씬 느립니다.

다행히도 wx에는 몇 가지 방법이 있습니다. 예를 들어, 코드를 1000/60 ms마다 실행하려면 wx.Timer을 사용하십시오.

스냅 샷을 찍는 동안 전체 이벤트 루프를 차단하지 않고 1/60 초 (또는 1/20 초)에 한 번만 잠시 차단하면된다는 이점이 있습니다.


당신이 할 수 물론

하고,이 두 솔루션을 결합 할 수 있습니다하십시오 최대 프레임 속도를 설정하는 타이머 또는 다른 스케줄러를 사용하고, 그 최대에 가까운 얻을 수있는 작업의 일부를 오프로드 가능한 한. 더 이상 화면 잡고 처리에 관해서는


: 당신이 수십 PNG 파일로 프레임의 수천을 저장하려고하면, 그 공간을 많이 걸릴 거예요. (왜, PNG는 무손실 압축을 사용합니다. 4MB 화면 캡처를 400K로 낮출 수는 있지만, H264 비디오는 손실 압축을 사용하며 더 중요한 것은 한 프레임에서 다음 프레임으로의 변경 사항 만 추적하면되므로 키 프레임 당 100K 및 차이 프레임 당 4K를 가져옵니다.)

할 수있는 한 가지 작업은 동영상을 압축하여 삭제하는 모든 작업 (예 : 120 프레임마다)을 자주 실행하기 때문에 모든 결국 비디오. 일을 단순하게 유지하려면 각 배치의 첫 번째 프레임을 새 키 프레임으로 지정해야합니다. MP4/H264는 연결하기에 가장 재미있는 형식은 아니지만 스트리밍 압축기로 전환하는 것보다 쉽습니다.