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):
for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices():
try:
return output_device.getCameraImage(), QSize(15, 15)
return output_device.activePrinter.camera.getImage(), QSize(15, 15)
except AttributeError:
pass
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()
keyChanged = pyqtSignal()
typeChanged = pyqtSignal()
cameraChanged = pyqtSignal()
def __init__(self, output_controller: "PrinterOutputController", number_of_extruders: int = 1, parent=None):
super().__init__(parent)
@ -38,6 +39,17 @@ class PrinterOutputModel(QObject):
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)
def type(self):
return self._type

View file

@ -12,6 +12,7 @@ from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutp
from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel
from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel
from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel
from cura.PrinterOutput.NetworkCamera import NetworkCamera
from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController
@ -290,6 +291,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
if printer is None:
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)
printer_list_changed = True

View file

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