11import picamera2 .picamera2
22import pykms
3+ import mmap
4+ import numpy as np
35from picamera2 .previews .null_preview import *
46
7+ dd = None
58
69class DrmPreview (NullPreview ):
710 FMT_MAP = {
@@ -36,6 +39,20 @@ def init_drm(self, x, y, width, height):
3639 self .drmfbs = {}
3740 self .current = None
3841 self .window = (x , y , width , height )
42+ self .overlay_plane = None
43+ self .overlay_fb = None
44+ self .overlay_new_fb = None
45+
46+ def set_overlay (self , overlay ):
47+ if overlay is None :
48+ self .overlay_new_fb = None
49+ else :
50+ h , w , channels = overlay .shape
51+ # Should I be recycling these instead of making new ones all the time?
52+ new_fb = pykms .DumbFramebuffer (self .card , w , h , "AB24" )
53+ with mmap .mmap (new_fb .fd (0 ), w * h * 4 , mmap .MAP_SHARED , mmap .PROT_WRITE ) as mm :
54+ mm .write (np .ascontiguousarray (overlay ).data )
55+ self .overlay_new_fb = new_fb
3956
4057 def render_drm (self , picam2 , completed_request ):
4158 if picam2 .display_stream_name is None :
@@ -55,9 +72,14 @@ def render_drm(self, picam2, completed_request):
5572 fmt = self .FMT_MAP [cfg .pixelFormat ]
5673 if self .plane is None :
5774 self .plane = self .resman .reserve_overlay_plane (self .crtc , fmt )
58- if picam2 .verbose_console :
59- print ("Got plane" , self .plane , "for format" , fmt )
60- assert (self .plane )
75+ if self .plane is None :
76+ raise RuntimeError ("Failed to reserve DRM plane" )
77+ # The second plane we ask for will go on top of the first.
78+ self .overlay_plane = self .resman .reserve_overlay_plane (self .crtc , pykms .PixelFormat .ABGR8888 )
79+ if self .overlay_plane is None :
80+ raise RuntimeError ("Failed to reserve DRM overlay plane" )
81+ # Want "coverage" mode, not pre-multiplied alpha.
82+ self .overlay_plane .set_prop ("pixel blend mode" , 1 )
6183 fd = fb .fd (0 )
6284 stride = cfg .stride
6385 if cfg .pixelFormat in ("YUV420" , "YVU420" ):
@@ -77,6 +99,21 @@ def render_drm(self, picam2, completed_request):
7799 drmfb = self .drmfbs [fb ]
78100 x , y , w , h = self .window
79101 self .crtc .set_plane (self .plane , drmfb , x , y , w , h , 0 , 0 , width , height )
102+ # An "atomic commit" would probably be better, but I can't get this to work...
103+ # ctx = pykms.AtomicReq(self.card)
104+ # ctx.add(self.plane, {"FB_ID": drmfb.id, "CRTC_ID": self.crtc.id,
105+ # "SRC_W": width << 16, "SRC_H": height << 16,
106+ # "CRTC_X": x, "CRTC_Y": y, "CRTC_W": w, "CRTC_H": h})
107+ # ctx.commit()
108+
109+ overlay_new_fb = self .overlay_new_fb
110+ if overlay_new_fb != self .overlay_fb :
111+ overlay_old_fb = self .overlay_fb # Must hang on to this momentarily to avoid a "wink"
112+ self .overlay_fb = overlay_new_fb
113+ if self .overlay_fb is not None :
114+ width , height = self .overlay_fb .width , self .overlay_fb .height
115+ self .crtc .set_plane (self .overlay_plane , self .overlay_fb , x , y , w , h , 0 , 0 , width , height )
116+ overlay_old_fb = None # The new one has been sent so it's safe to let this go now
80117
81118 if self .current :
82119 self .current .release ()
@@ -86,6 +123,8 @@ def stop(self):
86123 super ().stop ()
87124 # Seem to need some of this in order to be able to create another DrmPreview.
88125 self .drmfbs = {}
126+ self .overlay_new_fb = None
127+ self .overlay_fb = None
89128 self .crtc = None
90129 self .resman = None
91130 self .card = None
0 commit comments