Added videostream to cluster again

CL-541
This commit is contained in:
Jaime van Kessel 2017-12-06 14:59:20 +01:00
parent aba8bd89c3
commit 77e3965fc7
5 changed files with 139 additions and 6 deletions

View file

@ -12,7 +12,7 @@ class CameraImageProvider(QQuickImageProvider):
def requestImage(self, id, size): def requestImage(self, id, size):
for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices(): for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
try: try:
return output_device.getCameraImage(), QSize(15, 15) return output_device.activePrinter.camera.getImage(), QSize(15, 15)
except AttributeError: except AttributeError:
pass pass
return QImage(), QSize(15, 15) return QImage(), QSize(15, 15)

View file

@ -0,0 +1,113 @@
from UM.Logger import Logger
from PyQt5.QtCore import QUrl, pyqtProperty, pyqtSignal, QObject, pyqtSlot
from PyQt5.QtGui import QImage
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager
class NetworkCamera(QObject):
newImage = pyqtSignal()
def __init__(self, target = None, parent = None):
super().__init__(parent)
self._stream_buffer = b""
self._stream_buffer_start_index = -1
self._manager = None
self._image_request = None
self._image_reply = None
self._image = QImage()
self._image_id = 0
self._target = target
self._started = False
@pyqtSlot(str)
def setTarget(self, target):
restart_required = False
if self._started:
self.stop()
restart_required = True
self._target = target
if restart_required:
self.start()
@pyqtProperty(QUrl, notify=newImage)
def latestImage(self):
self._image_id += 1
# There is an image provider that is called "camera". In order to ensure that the image qml object, that
# requires a QUrl to function, updates correctly we add an increasing number. This causes to see the QUrl
# as new (instead of relying on cached version and thus forces an update.
temp = "image://camera/" + str(self._image_id)
return QUrl(temp, QUrl.TolerantMode)
@pyqtSlot()
def start(self):
if self._target is None:
Logger.log("w", "Unable to start camera stream without target!")
return
self._started = True
url = QUrl(self._target)
self._image_request = QNetworkRequest(url)
if self._manager is None:
self._manager = QNetworkAccessManager()
self._image_reply = self._manager.get(self._image_request)
self._image_reply.downloadProgress.connect(self._onStreamDownloadProgress)
@pyqtSlot()
def stop(self):
self._manager = None
self._stream_buffer = b""
self._stream_buffer_start_index = -1
if self._image_reply:
try:
# disconnect the signal
try:
self._image_reply.downloadProgress.disconnect(self._onStreamDownloadProgress)
except Exception:
pass
# abort the request if it's not finished
if not self._image_reply.isFinished():
self._image_reply.close()
except Exception as e: # RuntimeError
pass # It can happen that the wrapped c++ object is already deleted.
self._image_reply = None
self._image_request = None
self._started = False
def getImage(self):
return self._image
def _onStreamDownloadProgress(self, bytes_received, bytes_total):
# An MJPG stream is (for our purpose) a stream of concatenated JPG images.
# JPG images start with the marker 0xFFD8, and end with 0xFFD9
if self._image_reply is None:
return
self._stream_buffer += self._image_reply.readAll()
if len(self._stream_buffer) > 2000000: # No single camera frame should be 2 Mb or larger
Logger.log("w", "MJPEG buffer exceeds reasonable size. Restarting stream...")
self.stop() # resets stream buffer and start index
self.start()
return
if self._stream_buffer_start_index == -1:
self._stream_buffer_start_index = self._stream_buffer.indexOf(b'\xff\xd8')
stream_buffer_end_index = self._stream_buffer.lastIndexOf(b'\xff\xd9')
# If this happens to be more than a single frame, then so be it; the JPG decoder will
# ignore the extra data. We do it like this in order not to get a buildup of frames
if self._stream_buffer_start_index != -1 and stream_buffer_end_index != -1:
jpg_data = self._stream_buffer[self._stream_buffer_start_index:stream_buffer_end_index + 2]
self._stream_buffer = self._stream_buffer[stream_buffer_end_index + 2:]
self._stream_buffer_start_index = -1
self._image.loadFromData(jpg_data)
self.newImage.emit()

View file

@ -22,6 +22,7 @@ class PrinterOutputModel(QObject):
headPositionChanged = pyqtSignal() headPositionChanged = pyqtSignal()
keyChanged = pyqtSignal() keyChanged = pyqtSignal()
typeChanged = pyqtSignal() typeChanged = pyqtSignal()
cameraChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None): def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None):
super().__init__(parent) super().__init__(parent)
@ -38,6 +39,17 @@ class PrinterOutputModel(QObject):
self._type = "" self._type = ""
self._camera = None
def setCamera(self, camera):
if self._camera is not camera:
self._camera = camera
self.cameraChanged.emit()
@pyqtProperty(QObject, notify=cameraChanged)
def camera(self):
return self._camera
@pyqtProperty(str, notify = typeChanged) @pyqtProperty(str, notify = typeChanged)
def type(self): def type(self):
return self._type return self._type

View file

@ -12,6 +12,7 @@ from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutp
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutput.NetworkCamera import NetworkCamera
from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController
@ -290,6 +291,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if printer is None: if printer is None:
printer = PrinterOutputModel(output_controller=ClusterUM3PrinterOutputController(self), number_of_extruders=self._number_of_extruders) printer = PrinterOutputModel(output_controller=ClusterUM3PrinterOutputController(self), number_of_extruders=self._number_of_extruders)
printer.setCamera(NetworkCamera("http://" + printer_data["ip_address"] + ":8080/?action=stream"))
self._printers.append(printer) self._printers.append(printer)
printer_list_changed = True printer_list_changed = True

View file

@ -32,7 +32,7 @@ Item
width: 20 * screenScaleFactor width: 20 * screenScaleFactor
height: 20 * screenScaleFactor height: 20 * screenScaleFactor
onClicked: OutputDevice.selectAutomaticPrinter() onClicked: OutputDevice.setActivePrinter(null)
style: ButtonStyle style: ButtonStyle
{ {
@ -65,17 +65,23 @@ Item
{ {
if(visible) if(visible)
{ {
OutputDevice.startCamera() if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
{
OutputDevice.activePrinter.camera.start()
}
} else } else
{ {
OutputDevice.stopCamera() if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null)
{
OutputDevice.activePrinter.camera.stop()
}
} }
} }
source: source:
{ {
if(OutputDevice.cameraImage) if(OutputDevice.activePrinter != null && OutputDevice.activePrinter.camera != null && OutputDevice.activePrinter.camera.latestImage)
{ {
return OutputDevice.cameraImage; return OutputDevice.activePrinter.camera.latestImage;
} }
return ""; return "";
} }