<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Exchange Server">
<!-- converted from text --><style><!-- .EmailQuote { margin-left: 1pt; padding-left: 4pt; border-left: #800000 2px solid; } --></style>
</head>
<body>
<meta content="text/html; charset=UTF-8">
<style type="text/css" style="">
<!--
p
        {margin-top:0;
        margin-bottom:0}
-->
</style>
<div dir="ltr">
<div id="x_divtagdefaultwrapper" dir="ltr" style="font-size:12pt; color:#000000; font-family:Calibri,Helvetica,sans-serif">
<p>Hi Eliot,</p>
<p><br>
</p>
<p>do you plan to add support for cameras to Windows? Or should this already work and something is broken here? At the moment, <span>primOpenCamera always fails for me without an error code ...</span> :-)</p>
<p><br>
</p>
<p>Best,</p>
<p>Christoph</p>
<div id="x_Signature">
<div id="x_divtagdefaultwrapper" dir="ltr" style="font-size:12pt; color:rgb(0,0,0); font-family:Calibri,Helvetica,sans-serif,EmojiFont,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Segoe UI Symbol","Android Emoji",EmojiSymbols">
<div name="x_divtagdefaultwrapper" style="font-family:Calibri,Arial,Helvetica,sans-serif; font-size:; margin:0">
<div>
<div class="x__rp_T4" id="x_Item.MessagePartBody">
<div class="x__rp_U4 x_ms-font-weight-regular x_ms-font-color-neutralDark x_rpHighlightAllClass x_rpHighlightBodyClass" id="x_Item.MessageUniqueBody" style="font-family:wf_segoe-ui_normal,"Segoe UI","Segoe WP",Tahoma,Arial,sans-serif,serif,EmojiFont">
<div dir="ltr">
<div id="x_divtagdefaultwrapper"><font face="Calibri,Helvetica,sans-serif,EmojiFont,Apple Color Emoji,Segoe UI Emoji,NotoColorEmoji,Segoe UI Symbol,Android Emoji,EmojiSymbols">
<div id="x_Signature">
<div style="margin:0px"><font style="font-family:Calibri,Arial,Helvetica,sans-serif,serif,EmojiFont">
<div><font size="3" color="black"><span style="font-size:12pt"><a href="http://www.hpi.de/" target="_blank" rel="noopener noreferrer" id="LPNoLP"><font size="2"><span id="LPlnk909538"><font color="#757B80"></font></span></font></a></span></font></div>
</font></div>
</div>
</font></div>
</div>
</div>
</div>
</div>
<div><font size="2" color="#808080"></font></div>
</div>
</div>
</div>
</div>
<hr tabindex="-1" style="display:inline-block; width:98%">
<div id="x_divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" color="#000000" style="font-size:11pt"><b>Von:</b> Squeak-dev <squeak-dev-bounces@lists.squeakfoundation.org> im Auftrag von commits@source.squeak.org <commits@source.squeak.org><br>
<b>Gesendet:</b> Samstag, 17. April 2021 04:14:18<br>
<b>An:</b> squeak-dev@lists.squeakfoundation.org; packages@lists.squeakfoundation.org<br>
<b>Betreff:</b> [squeak-dev] The Trunk: MorphicExtras-eem.293.mcz</font>
<div> </div>
</div>
</div>
<font size="2"><span style="font-size:10pt;">
<div class="PlainText">Eliot Miranda uploaded a new version of MorphicExtras to project The Trunk:<br>
<a href="http://source.squeak.org/trunk/MorphicExtras-eem.293.mcz">http://source.squeak.org/trunk/MorphicExtras-eem.293.mcz</a><br>
<br>
==================== Summary ====================<br>
<br>
Name: MorphicExtras-eem.293<br>
Author: eem<br>
Time: 16 April 2021, 7:14:15.621938 pm<br>
UUID: 27489c5b-4cc5-4581-905d-881e877c32d6<br>
Ancestors: MorphicExtras-mt.292<br>
<br>
Add frame buffering to the CameraInterface (i.e. if possible the CameraPlugin copies data directly into a pinned bitmap instead of into an internal buffer which is then copied into the bitmap in getFrameForCamera:into:.  Update the camera:framesDo:while: utility
 to use buffering and/or interrupt notification if possible.<br>
<br>
=============== Diff against MorphicExtras-mt.292 ===============<br>
<br>
Item was added:<br>
+ ----- Method: CameraInterface class>>bufferedInterruptDrivenVideoTest: (in category 'test') -----<br>
+ bufferedInterruptDrivenVideoTest: camNum<br>
+        "A quick test of video input. Displays video on the screen until the mouse is pressed.<br>
+         Answer nil if the interrupt-driven interface is unavailable."<br>
+        "self bufferedInterruptDrivenVideoTest: 1"<br>
+        "self bufferedInterruptDrivenVideoTest: 2"<br>
+        "[self bufferedInterruptDrivenVideoTest: 2] fork.<br>
+          self bufferedInterruptDrivenVideoTest: 1"<br>
+ <br>
+        | semaphore height frameExtent frameBuffer |<br>
+        height := 16.<br>
+        1 to: camNum - 1 do:<br>
+                [:camIndex| "N.B. the extent of an unopened camera is 0@0"<br>
+                height := height + (self frameExtent: camIndex) y + 16].<br>
+        (self cameraIsOpen: camNum) ifFalse:<br>
+                [(self openCamera: camNum width: 352 height: 288) ifNil:<br>
+                        [self inform: 'no camera'.<br>
+                         ^nil]].<br>
+        frameExtent := self frameExtent: camNum.<br>
+        frameBuffer := Form extent: frameExtent depth: 32.<br>
+        frameBuffer bits pin.<br>
+        self camera: camNum setFrameBuffer: frameBuffer bits.<br>
+                 <br>
+        semaphore := Semaphore new.<br>
+        [self camera: camNum setSemaphore: (Smalltalk registerExternalObject: semaphore)]<br>
+                on: Error<br>
+                do: [:err|<br>
+                        Smalltalk unregisterExternalObject: semaphore.<br>
+                        self inform: 'interrupt-driven camera interface unavailable: ', err messageText.<br>
+                        ^nil].<br>
+        [| n startTime frameCount msecs fps |<br>
+         [semaphore wait.<br>
+          startTime ifNil:<br>
+                [frameCount := 0.<br>
+                 frameExtent := self frameExtent: camNum.<br>
+                "N.B. the actual frame size may not be determined until delivery of the first frame.<br>
+                 So resize the form if necessary."<br>
+                 frameExtent ~= frameBuffer extent ifTrue:<br>
+                        [frameBuffer := Form extent: frameExtent depth: 32 bits: frameBuffer bits].<br>
+                 startTime := Time millisecondClockValue].<br>
+          Sensor anyButtonPressed] whileFalse:<br>
+                [n := self getFrameForCamera: camNum into: frameBuffer bits.<br>
+                n > 0 ifTrue:<br>
+                        [frameCount := frameCount + 1.<br>
+                         frameBuffer displayAt: 16 @ height]].<br>
+         msecs := Time millisecondClockValue - startTime.<br>
+         fps := (frameCount * 1000) // msecs.<br>
+         ^(self cameraName: camNum), ': ', frameExtent printString, ' ', frameCount printString, ' frames at ', fps printString, ' frames/sec']<br>
+                ensure:<br>
+                        [self closeCamera: camNum.<br>
+                         Smalltalk unregisterExternalObject: semaphore.<br>
+                         Sensor waitNoButton]!<br>
<br>
Item was changed:<br>
  ----- Method: CameraInterface class>>camera:framesDo:while: (in category 'utilities') -----<br>
  camera: cameraNum framesDo: aBlock while: whileBlock<br>
+        "Evaluate aBlock every time a frame becomes available.  Answer a tuple of frames per second<br>
+         and the number of 16ms delays per second if polling is used, plus indications of which schemes<br>
+         were used. Be destructive; use only one bitmap, overwriting its contents with each successive frame.<br>
+         Use the buffered interface if possible. It is the sender's responsibility to open and close the camera."<br>
+        | form bitmap schemes delay start duration frameCount delayCount semaphore  |<br>
-        "Evaluate aBlock every time a frame becomes available.  Answer a tuple of frames per second and number of 16ms delays per second.<br>
-         Be destructive; use only one bitmap, overwriting its contents with each successive frame.<br>
-         It is the sender's responsibility to open and close the camera."<br>
-        | form bitmap delay start duration frameCount delayCount |<br>
         form := Form<br>
                                 extent: (self frameExtent: cameraNum)<br>
                                 depth: 32.<br>
         bitmap := form bits.<br>
+        bitmap pin.<br>
+        schemes := Array new writeStream.<br>
+        [self camera: cameraNum setFrameBuffer: bitmap.<br>
+         schemes nextPut: 'buffered']<br>
+                on: Error<br>
+                do: [:err|<br>
+                        bitmap unpin.<br>
+                        schemes nextPut: 'copied'].<br>
+        semaphore := Semaphore new.<br>
+        [self camera: cameraNum setSemaphore: (Smalltalk registerExternalObject: semaphore).<br>
+         schemes nextPut: 'interrupt driven']<br>
+                on: Error<br>
+                do: [:err|<br>
+                        Smalltalk unregisterExternalObject: semaphore.<br>
+                        semaphore := nil.<br>
+                        schemes nextPut: 'polling'].<br>
         delay := Delay forMilliseconds: (1000 / 60) asInteger. "60 fps is fast"<br>
+ <br>
         start := Time utcMicrosecondClock.<br>
         frameCount := delayCount := 0.<br>
+        [semaphore ifNotNil:<br>
+                [semaphore wait].<br>
+        [(self getFrameForCamera: cameraNum into: bitmap) <= 0] whileTrue:<br>
-        [[(self camera: cameraNum getParam: 1) <= 0] whileTrue:<br>
                 [delay wait. delayCount := delayCount + 1].<br>
-         self getFrameForCamera: cameraNum into: bitmap.<br>
          frameCount := frameCount + 1.<br>
          aBlock value: form.<br>
          whileBlock value] whileTrue.<br>
         ^{ frameCount * 1.0e6 / (duration := Time utcMicrosecondClock - start).<br>
+                delayCount * 1.0e6 / duration },<br>
+         schemes contents<br>
-                delayCount * 1.0e6 / duration }<br>
  <br>
         "| cameraNum |<br>
          self openCamera: (cameraNum := 1) width: 640 height: 480.<br>
          self waitForCameraStart: cameraNum.<br>
          [self camera: cameraNum framesDo: [:bitmap| bitmap display] while: [Sensor noButtonPressed]] ensure:<br>
                 [self closeCamera: cameraNum]"!<br>
<br>
Item was added:<br>
+ ----- Method: CameraInterface class>>camera:setFrameBuffer: (in category 'camera ops') -----<br>
+ camera: cameraNum setFrameBuffer: frameBuffer<br>
+        "Set a pinned non-pointer object as the frame buffer for the camera.<br>
+         Fail if cameraNum does not reference an open camera, or if the buffer is not large enough."<br>
+        <primitive: 'primSetCameraBuffers' module: 'CameraPlugin' error: ec><br>
+        ^self primitiveFailed!<br>
<br>
Item was added:<br>
+ ----- Method: CameraInterface class>>camera:setFrameBufferA:B: (in category 'camera ops') -----<br>
+ camera: cameraNum setFrameBufferA: frameBufferA B: frameBufferBOrNil<br>
+        "Set a pair of pinned non-pointer objects as the frame buffers for the camera.<br>
+         If both are non-nil the plugin will fill them alternating between first frameBufferA and second frameBufferBOrNil.<br>
+         Fail if frameBufferBOrNil is not nil and a different size from frameBufferA.<br>
+         Fail if cameraNum does not reference an open camera, or if the buffers are not large enough."<br>
+        <primitive: 'primSetCameraBuffers' module: 'CameraPlugin' error: ec><br>
+        ^self primitiveFailed!<br>
<br>
Item was changed:<br>
  ----- Method: CameraInterface class>>interruptDrivenVideoTest: (in category 'test') -----<br>
  interruptDrivenVideoTest: camNum<br>
         "A quick test of video input. Displays video on the screen until the mouse is pressed.<br>
          Answer nil if the interrupt-driven interface is unavailable."<br>
         "self interruptDrivenVideoTest: 1"<br>
         "self interruptDrivenVideoTest: 2"<br>
         "[self interruptDrivenVideoTest: 2] fork.<br>
           self interruptDrivenVideoTest: 1"<br>
  <br>
         | semaphore height frameExtent |<br>
         height := 16.<br>
         1 to: camNum - 1 do:<br>
+                [:camIndex| "N.B. the extent of an unopened camera is 0@0"<br>
-                [:camIndex| "N.B. the of an unopened camera is 0@0"<br>
                 height := height + (self frameExtent: camIndex) y + 16].<br>
         (self cameraIsOpen: camNum) ifFalse:<br>
                 [(self openCamera: camNum width: 352 height: 288) ifNil:<br>
                         [self inform: 'no camera'.<br>
                          ^nil]].<br>
         semaphore := Semaphore new.<br>
         [self camera: camNum setSemaphore: (Smalltalk registerExternalObject: semaphore)]<br>
                 on: Error<br>
                 do: [:err|<br>
                         Smalltalk unregisterExternalObject: semaphore.<br>
                         self inform: 'interrupt-driven camera interface unavailable: ', err messageText.<br>
                         ^nil].<br>
         [| f n startTime frameCount msecs fps |<br>
          [semaphore wait.<br>
          "N.B. the frame extent may not be known until the delivery of the first frame.<br>
           So we have to delay initialization."<br>
           startTime ifNil:<br>
                 [(frameExtent := self frameExtent: camNum) x = 0 ifTrue: [self inform: 'no camera'. ^nil].<br>
                  f := Form extent: (self frameExtent: camNum) depth: 32.<br>
                  frameCount := 0.<br>
                  startTime := Time millisecondClockValue].<br>
           Sensor anyButtonPressed] whileFalse:<br>
                 [n := self getFrameForCamera: camNum into: f bits.<br>
                 n > 0 ifTrue:<br>
                         [frameCount := frameCount + 1.<br>
                          f displayAt: 16 @ height]].<br>
          msecs := Time millisecondClockValue - startTime.<br>
          fps := (frameCount * 1000) // msecs.<br>
          ^(self cameraName: camNum), ': ', frameExtent printString, ' ', frameCount printString, ' frames at ', fps printString, ' frames/sec']<br>
                 ensure:<br>
                         [self closeCamera: camNum.<br>
                          Smalltalk unregisterExternalObject: semaphore.<br>
                          Sensor waitNoButton]!<br>
<br>
Item was changed:<br>
  ----- Method: CameraInterface class>>videoTest: (in category 'test') -----<br>
  videoTest: camNum<br>
         "A quick test of video input. Displays video on the screen until the mouse is pressed."<br>
         "self videoTest: 1"<br>
         "self videoTest: 2"<br>
  <br>
         | frameExtent f n startTime frameCount msecs fps |<br>
+        (self cameraIsOpen: camNum) ifFalse:<br>
+                [(self openCamera: camNum width: 320 height: 240) ifNil:<br>
+                        [self inform: 'no camera'.<br>
+                         ^nil]].<br>
-        (self openCamera: camNum width: 320 height: 240) ifNil: [^ self inform: 'no camera'].<br>
         self waitForCameraStart: camNum.<br>
         (frameExtent := self frameExtent: camNum) x = 0 ifTrue: [^ self inform: 'no camera'].<br>
         f := Form extent: (self frameExtent: camNum) depth: 32.<br>
         frameCount := 0.<br>
         startTime := nil.<br>
         [Sensor anyButtonPressed] whileFalse:<br>
                 [n := self getFrameForCamera: camNum into: f bits.<br>
                 n > 0 ifTrue:<br>
                         [startTime ifNil: [startTime := Time millisecondClockValue].<br>
                         frameCount := frameCount + 1.<br>
                         f display]].<br>
         Sensor waitNoButton.<br>
         msecs := Time millisecondClockValue - startTime.<br>
         self closeCamera: camNum.<br>
         fps := frameCount * 1000 // msecs.<br>
+        ^(self cameraName: camNum), ': ', frameExtent printString, ' ', frameCount printString, ' frames at ', fps printString, ' frames/sec'!<br>
-        ^frameExtent printString, ' ', frameCount printString, ' frames at ', fps printString, ' frames/sec'!<br>
<br>
<br>
</div>
</span></font>
</body>
</html>