# IP camera status - how to catch "Stream ended." status

**URL:** <https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030>\
**Category:** Ignition\
**Created:** [May 15, 2018, 1:03am UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030 "2018-05-15T01:03:03Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![jsorlie](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jsorlie/32/3645_2.png) [@jsorlie](https://forum.inductiveautomation.com/u/jsorlie)\
**Post date:** [May 15, 2018, 1:03am UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/1 "2018-05-15T01:03:03Z")

</div>

Is there any way to programmatically monitor the status (quality) of the IP Camera component’s video stream? Seems like there would be some response code attribute available to check if/when things go wrong.

There are two situations I need to catch if possible:

1. When the number of connect retries has been reached
2. When (for whatever reason) the stream ends inadvertently.

Is there any hidden properties that can be used in scripting (eg. property change scripts) to catch these events?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [May 15, 2018, 2:18pm UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/2 "2018-05-15T14:18:31Z")

</div>

The IP camera component has an internally nested FrameProducer class on which you can call `getState()`. Getting to that is an exercise for the reader, but I’ll give two hints: 1. It’s not going to show up with a `dir()` call, and 2. it’s internally named `parser`.

---

<div class="post-metadata">

**Author:** ![jsorlie](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jsorlie/32/3645_2.png) [@jsorlie](https://forum.inductiveautomation.com/u/jsorlie)\
**Post date:** [May 15, 2018, 4:15pm UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/3 "2018-05-15T16:15:59Z")

</div>

Hmm… I find the **parser** attribute in the [serialized fields documentation](http://files.inductiveautomation.com/sdk/javadoc/ignition79/794-beta1/index.html?serialized-form.html).

I cannot find anything regarding the parser object:

- com.inductiveautomation.factorypmi.application.components.FrameProducer

Also there is no mention of the parser attribute in the [PMIIPCamViewer class](http://files.inductiveautomation.com/sdk/javadoc/ignition79/794-beta1/com/inductiveautomation/factorypmi/application/components/PMIIPCamViewer.htm) itself.

Programmatically the IP Camera Viewer component’s only exposed interface would be the property-change event script. Any hint on what property to monitor? The component does not allow creation of custom properties. Normally property-change scripts only fire for changes of bindable properties.

---

<div class="post-metadata">

**Author:** ![jsorlie](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jsorlie/32/3645_2.png) [@jsorlie](https://forum.inductiveautomation.com/u/jsorlie)\
**Post date:** [May 15, 2018, 4:27pm UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/4 "2018-05-15T16:27:04Z")

</div>

I tried getting around this issue by switching from MPEG stream to JPEG stills. Big bonus of doing this is the lower load it creates on the IP-camera app-server.

Unfortunately even using JPG Stills results in occasional exceptions that I would like to trap:  
 ![](https://us1.discourse-cdn.com/inductiveautomation/original/2X/7/7a2c35f57d8f58163a3b38422cd2613adc4459b6.png)

Really all I need is some way to catch any/whatever exception occurs in the component. Usually reloading the screen will rectify whatever the problem is.

---

<div class="post-metadata">

**Author:** ![jsorlie](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jsorlie/32/3645_2.png) [@jsorlie](https://forum.inductiveautomation.com/u/jsorlie)\
**Post date:** [May 16, 2018, 5:20pm UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/5 "2018-05-16T17:20:21Z")

</div>

> [@paul-griffith](#):
>
> The IP camera component has an internally nested FrameProducer class on which you can call `getState()`. Getting to that is an exercise for the reader, but I'll give two hints: 1. It's not going to show up with a `dir()` call, and 2. it's internally named `parser`.

Using script console I explored the PMIIPCamViewer object:

```auto
from com.inductiveautomation.factorypmi.application.components import PMIIPCamViewer 
ip = PMIIPCamViewer() 
dir(ip)

```

The hint about the **parser** attribute in the [serialized fields documentation](http://files.inductiveautomation.com/sdk/javadoc/ignition79/794-beta1/index.html?serialized-form.html) is misleading.

```auto
from com.inductiveautomation.factorypmi.application.components import FrameProducer

```

throws an exception:

```auto
ImportError: cannot import name FrameProducer

```

As Paul indicated there is no FrameProducer / parser attribute in the object.

```auto
>>> dir(ip)
['ABORT', 'ALLBITS', 'AccessibleJComponent', 'BOTTOM_ALIGNMENT', 'BaselineResizeBehavior', 'CENTER_ALIGNMENT', 'COMPONENT_RUNNING', 'ERROR', 'FRAMEBITS', 'HEIGHT', 'LEFT_ALIGNMENT', 'MODE_JPEG', 'MODE_MJPEG', 'PROPERTIES', 'RIGHT_ALIGNMENT', 'SOMEBITS', 'TOOL_TIP_TEXT_KEY', 'TOP_ALIGNMENT', 'UIClassID', 'UNDEFINED_CONDITION', 'WHEN_ANCESTOR_OF_FOCUSED_COMPONENT', 'WHEN_FOCUSED', 'WHEN_IN_FOCUSED_WINDOW', 'WIDTH', ' __class__', ' __copy__', ' __deepcopy__', ' __delattr__', ' __doc__', ' __eq__', ' __getattribute__', ' __hash__', ' __init__', ' __ne__', ' __new__', ' __reduce__', ' __reduce_ex__', ' __repr__', ' __setattr__', ' __str__', ' __unicode__', 'accessibleContext', 'action', 'actionMap', 'add', 'addAncestorListener', 'addComponentListener', 'addContainerListener', 'addFocusListener', 'addHierarchyBoundsListener', 'addHierarchyListener', 'addInputMethodListener', 'addKeyListener', 'addMouseListener', 'addMouseMotionListener', 'addMouseWheelListener', 'addNotify', 'addPropertyChangeListener', 'addVetoableChangeListener', 'alignmentX', 'alignmentY', 'ancestorAdded', 'ancestorListener', 'ancestorListeners', 'ancestorMoved', 'ancestorRemoved', 'ancestorResized', 'appContext', 'applyComponentOrientation', 'areFocusTraversalKeysSet', 'autoscrolls', 'background', 'backgroundSet', 'baselineResizeBehavior', 'border', 'bounds', 'cameraBufferSize', 'caretPositionChanged', 'checkImage', 'class', 'colorModel', 'componentAdded', 'componentCount', 'componentHidden', 'componentListener', 'componentListeners', 'componentMoved', 'componentOrientation', 'componentPopupMenu', 'componentRemoved', 'componentResized', 'componentShown', 'components', 'computeVisibleRect', 'connectRetries', 'containerListener', 'containerListeners', 'contains', 'countComponents', 'createImage', 'createToolTip', 'createVolatileImage', 'cursor', 'cursorCode', 'cursorSet', 'debugGraphicsOptions', 'deliverEvent', 'disable', 'dispatchEvent', 'displayable', 'doLayout', 'doubleBuffered', 'dropTarget', 'enable', 'enableInputMethods', 'enabled', 'equals', 'findComponentAt', 'firePropertyChange', 'focusCycleRoot', 'focusCycleRootAncestor', 'focusGained', 'focusListener', 'focusListeners', 'focusLost', 'focusOwner', 'focusTraversable', 'focusTraversalKeysEnabled', 'focusTraversalPolicy', 'focusTraversalPolicyProvider', 'focusTraversalPolicySet', 'focusable', 'font', 'fontSet', 'foreground', 'foregroundSet', 'getAccessibleContext', 'getActionForKeyStroke', 'getActionMap', 'getAlignmentX', 'getAlignmentY', 'getAncestorListeners', 'getAppContext', 'getAutoscrolls', 'getBackground', 'getBaseline', 'getBaselineResizeBehavior', 'getBorder', 'getBounds', 'getCameraBufferSize', 'getClass', 'getClientProperty', 'getColorModel', 'getComponent', 'getComponentAt', 'getComponentCount', 'getComponentListeners', 'getComponentOrientation', 'getComponentPopupMenu', 'getComponentZOrder', 'getComponents', 'getConditionForKeyStroke', 'getConnectRetries', 'getContainerListeners', 'getCursor', 'getCursorCode', 'getDebugGraphicsOptions', 'getDefaultLocale', 'getDropTarget', 'getFocusCycleRootAncestor', 'getFocusListeners', 'getFocusTraversalKeys', 'getFocusTraversalKeysEnabled', 'getFocusTraversalPolicy', 'getFont', 'getFontMetrics', 'getForeground', 'getGraphics', 'getGraphicsConfiguration', 'getHeight', 'getHierarchyBoundsListeners', 'getHierarchyListeners', 'getIgnoreRepaint', 'getInheritsPopupMenu', 'getInputContext', 'getInputMap', 'getInputMethodListeners', 'getInputMethodRequests', 'getInputVerifier', 'getInsets', 'getKeyListeners', 'getLayout', 'getListeners', 'getLocale', 'getLocation', 'getLocationOnScreen', 'getMaximumSize', 'getMinimumSize', 'getMode', 'getMouseListeners', 'getMouseMotionListeners', 'getMousePosition', 'getMouseWheelListeners', 'getName', 'getNextFocusableComponent', 'getParent', 'getPassword', 'getPeer', 'getPopupLocation', 'getPreferredSize', 'getPropertyChangeListeners', 'getRefreshRate', 'getRegisteredKeyStrokes', 'getRetryDelay', 'getRootPane', 'getScaleMode', 'getSize', 'getToolTipLocation', 'getToolTipText', 'getToolkit', 'getTopLevelAncestor', 'getTransferHandler', 'getTreeLock', 'getUIClassID', 'getUrl', 'getUserAgent', 'getUsername', 'getVerifyInputWhenFocusTarget', 'getVetoableChangeListeners', 'getVisibleRect', 'getWidth', 'getX', 'getY', 'gotFocus', 'grabFocus', 'graphics', 'graphicsConfiguration', 'handleEvent', 'hasFocus', 'hashCode', 'height', 'hide', 'hierarchyBoundsListener', 'hierarchyBoundsListeners', 'hierarchyChanged', 'hierarchyListener', 'hierarchyListeners', 'ignoreRepaint', 'imageUpdate', 'inheritsPopupMenu', 'inputContext', 'inputMap', 'inputMethodListener', 'inputMethodListeners', 'inputMethodRequests', 'inputMethodTextChanged', 'inputVerifier', 'insets', 'inside', 'invalidate', 'isAncestorOf', 'isBackgroundSet', 'isCursorSet', 'isDisplayable', 'isDoubleBuffered', 'isEnabled', 'isFocusCycleRoot', 'isFocusOwner', 'isFocusTraversable', 'isFocusTraversalPolicyProvider', 'isFocusTraversalPolicySet', 'isFocusable', 'isFontSet', 'isForegroundSet', 'isLightweight', 'isLightweightComponent', 'isManagingFocus', 'isMaximumSizeSet', 'isMinimumSizeSet', 'isOpaque', 'isOptimizedDrawingEnabled', 'isPaintingForPrint', 'isPaintingTile', 'isPreferredSizeSet', 'isRequestFocusEnabled', 'isScaleVideo', 'isShowStats', 'isShowing', 'isUseAuthentication', 'isValid', 'isValidateRoot', 'isVisible', 'keyDown', 'keyListener', 'keyListeners', 'keyPressed', 'keyReleased', 'keyTyped', 'keyUp', 'layout', 'lightweight', 'list', 'locale', 'locate', 'location', 'locationOnScreen', 'lostFocus', 'managingFocus', 'maximumSize', 'maximumSizeSet', 'minimumSize', 'minimumSizeSet', 'mode', 'mouseClicked', 'mouseDown', 'mouseDrag', 'mouseDragged', 'mouseEnter', 'mouseEntered', 'mouseExit', 'mouseExited', 'mouseListener', 'mouseListeners', 'mouseMotionListener', 'mouseMotionListeners', 'mouseMove', 'mouseMoved', 'mousePosition', 'mousePressed', 'mouseReleased', 'mouseUp', 'mouseWheelListener', 'mouseWheelListeners', 'mouseWheelMoved', 'move', 'name', 'nextFocus', 'nextFocusableComponent', 'nextFrame', 'notify', 'notifyAll', 'notifyStateChanged', 'opaque', 'optimizedDrawingEnabled', 'paint', 'paintAll', 'paintComponent', 'paintComponents', 'paintImmediately', 'paintingForPrint', 'paintingTile', 'parent', 'password', 'peer', 'postEvent', 'preferredSize', 'preferredSizeSet', 'prepareImage', 'print', 'printAll', 'printComponents', 'propertyChange', 'propertyChangeListener', 'propertyChangeListeners', 'putClientProperty', 'reconnect', 'refreshRate', 'registerKeyboardAction', 'registeredKeyStrokes', 'remove', 'removeAll', 'removeAncestorListener', 'removeComponentListener', 'removeContainerListener', 'removeFocusListener', 'removeHierarchyBoundsListener', 'removeHierarchyListener', 'removeInputMethodListener', 'removeKeyListener', 'removeMouseListener', 'removeMouseMotionListener', 'removeMouseWheelListener', 'removeNotify', 'removePropertyChangeListener', 'removeVetoableChangeListener', 'repaint', 'requestDefaultFocus', 'requestFocus', 'requestFocusEnabled', 'requestFocusInWindow', 'resetKeyboardActions', 'reshape', 'resize', 'retryDelay', 'revalidate', 'rootPane', 'scaleMode', 'scaleVideo', 'scrollRectToVisible', 'setActionMap', 'setAlignmentX', 'setAlignmentY', 'setAutoscrolls', 'setBackground', 'setBorder', 'setBounds', 'setCameraBufferSize', 'setComponentOrientation', 'setComponentPopupMenu', 'setComponentZOrder', 'setConnectRetries', 'setCursor', 'setCursorCode', 'setDebugGraphicsOptions', 'setDefaultLocale', 'setDoubleBuffered', 'setDropTarget', 'setEnabled', 'setFocusCycleRoot', 'setFocusTraversalKeys', 'setFocusTraversalKeysEnabled', 'setFocusTraversalPolicy', 'setFocusTraversalPolicyProvider', 'setFocusable', 'setFont', 'setForeground', 'setIgnoreRepaint', 'setInheritsPopupMenu', 'setInputMap', 'setInputVerifier', 'setLayout', 'setLocale', 'setLocation', 'setMaximumSize', 'setMinimumSize', 'setMode', 'setName', 'setNextFocusableComponent', 'setOpaque', 'setPassword', 'setPreferredSize', 'setRefreshRate', 'setRequestFocusEnabled', 'setRetryDelay', 'setScaleMode', 'setScaleVideo', 'setShowStats', 'setSize', 'setToolTipText', 'setTransferHandler', 'setUrl', 'setUseAuthentication', 'setUserAgent', 'setUsername', 'setVerifyInputWhenFocusTarget', 'setVisible', 'show', 'showStats', 'showing', 'shutdownComponent', 'size', 'startupComponent', 'toString', 'toolTipText', 'toolkit', 'topLevelAncestor', 'transferFocus', 'transferFocusBackward', 'transferFocusDownCycle', 'transferFocusUpCycle', 'transferHandler', 'treeLock', 'unregisterKeyboardAction', 'update', 'updateUI', 'url', 'useAuthentication', 'userAgent', 'username', 'valid', 'validate', 'validateRoot', 'verifyInputWhenFocusTarget', 'vetoableChange', 'vetoableChangeListener', 'vetoableChangeListeners', 'visible', 'visibleRect', 'wait', 'width', 'x', 'y']
>>> 

```

* * *

I am testing a bandage polled idea using `runScript()` on a bindable property (eg. `visible`) with a custom method:

```auto
def setTrue(self)
	try:
		self.nextFrame()
# system.gui.messageBox("success","nextFrame")
	except:
		self.reconnect()
		system.gui.messageBox("failure","reconnect")
	return True

```

Rationale is to periodically issue the `PMIIPCamViewer` object's `nextFrame()` method and -- if it fails -- try the `reconnect()` method.

For what it is worth, this is not a new issue, just unsolved:

1. 2014: [IP Camera Viewer - IOException: Premature EOF](http://forum.inductiveautomation.com/t/ip-camera-viewer-ioexception-premature-eof/8055)
2. 2014: [IP Camera not automatically reconnecting](http://forum.inductiveautomation.com/t/ip-camera-not-automatically-reconnecting/16987)
3. 2015: [Refresh IP Camera Module on Error](http://forum.inductiveautomation.com/t/refresh-ip-camera-module-on-error/9822)
4. 2015: [IP Camera Viewer Error & Recovery Question](http://forum.inductiveautomation.com/t/ip-camera-viewer-error-recovery-question/9709)

---

<div class="post-metadata">

**Author:** ![jsorlie](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jsorlie/32/3645_2.png) [@jsorlie](https://forum.inductiveautomation.com/u/jsorlie)\
**Post date:** [May 16, 2018, 9:12pm UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/6 "2018-05-16T21:12:39Z")

</div>

This band-aid solution seems work pretty well. For what it is worth, here is a template containing the IP camera with the script added.

[template\_IP Camera w reconnect baindaid.proj](https://us1.discourse-cdn.com/inductiveautomation/original/2X/c/c7562a7b09405e63154d6ddccb2d4a088b9d1b7e.proj) (6.1 KB)

Note: Rather than drinking MPEG, I am using a sequence of JPEG stills to reduce bandwidth / load on our already-over-taxed video servers. I have not tested the band-aid to see if it works with the full blown MPEG streaming.

---

<div class="post-metadata">

**Author:** ![jsorlie](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jsorlie/32/3645_2.png) [@jsorlie](https://forum.inductiveautomation.com/u/jsorlie)\
**Post date:** [May 17, 2018, 10:28pm UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/7 "2018-05-17T22:28:07Z")

</div>

Nope even this band-aid is not full proof.

Still getting occasional `HTTP 400: Bad Request` exceptions that leave the stream stalled.

Interim work around is to put another slower polled band-aid (eg. every 5 minutes) that simply hammers the cam-viewer, issuing the `reconnect()` whether or not it is necessary.

---

<div class="post-metadata">

**Author:** ![lewis.storey](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lewis.storey/32/15026_2.png) [@lewis.storey](https://forum.inductiveautomation.com/u/lewis.storey)\
**Post date:** [May 12, 2020, 11:36am UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/8 "2020-05-12T11:36:55Z")

</div>

After a little digging around I got this sorted out. Through the use of reflection I got hold of the parser object and then the getState method inside. The only oddity I found was when displaying the string in a label used for debugging purposes sometimes it was all upper case and other times it was first letter upper, rest lower case (RUNNING or Running).

The script below was attached to a mouseClicked event on the IP Cam viewer for testing but in prod we use a timer to run the check and issue the reconnect() call if need be

```auto
from com.inductiveautomation.factorypmi.application.components import PMIIPCamViewer

parserRef = PMIIPCamViewer().getClass().getDeclaredField('parser')
parserRef.setAccessible(True)
cam = event.source
parser = parserRef.get(cam)

# If the url property is blank, parser is None
if parser is not None:
    stateRef = parser.getClass().getSuperclass().getDeclaredMethod('getState')
    stateRef.setAccessible(True)
    # Enum contents: CONNECTING, RUNNING, CANCELED, ERROR
    state = stateRef.invoke(parser)
    stateRef.setAccessible(False)

parserRef.setAccessible(False)

```

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 12, 2020, 12:22pm UTC](https://forum.inductiveautomation.com/t/ip-camera-status-how-to-catch-stream-ended-status/18030/9 "2020-05-12T12:22:31Z")

</div>

Looks familiar.... If you look at the Usage Notes for my Image Streamer module, you'll see a formal version of this as a script module, suited to use in a binding. Allows you to copy camera view instances around without needing a separate timer component.

[https://www.automation-pros.com/streamer/doc/](https://www.automation-pros.com/streamer/doc/)

Also:

> [@Automation Professionals' Image Streamer Driver Module](http://forum.inductiveautomation.com/t/automation-professionals-image-streamer-driver-module/29371):
>
> Automation Professionals is pleased to announce a Release Candidate for its new driver module, supporting the live Network Image transmission format of [Keyence CV-5000](https://www.keyence.com/products/vision/vision-sys/cv-5000/index.jsp) machine vision controllers, plus support for common Network Video streams via [OpenCV](https://opencv.org/). Stream metadata is exposed as OPC items, and the output streams are Motion JPEG. The IP Camera Viewer Component in Vision can consume these streams, making it possible to integrate IP Cameras that don't have MJPEG support or have poor support f…
