<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>