Octoprint in Docker Webcam Setup PS3 eye USB camera


#1

What is the problem?
I'm having trouble setting up a webcam with a docker install of Octoprint.

I'm guessing the mpeg streamer is not part of the default docker install and I'm guessing I need to add this manually. I was hoping someone could point me in the right direction (or even better update the offical docker repository to include webcam setup/) of the correct modifications to the dockerfile & docker-compose.yaml

Background
I previously had Octopi / Octoprint working nicely with a raspberryPi & a PS2 eye USB webcam.
I'm in the process of consolidating several devices within docker containers.

What did you already try to solve it?

  • I've check out the /wiki/Webcams-known-to-work for information but no docker specific guidance
  • I believe i have allowed (refer docker-compose.ymal below) the dev/video0 device through to the docker container (and I don't think i have a permissions problem)

Additional information about your setup
Octoprint looks to be working successfully
Docker: hub.docker.com/r/octoprint/octoprint/
Printer: Mendel Max 3

docker-compose.ymal
'''
version: '2'
services:
octoprint:
build: .
image: octoprint/octoprint
container_name: octoprint
ports:
- 5000:5000
devices:
- /dev/ttyACM0:/dev/ttyACM0
- /dev/video0:/dev/video0
volumes:
- ./config:/home/octoprint/.octoprint
restart: always
'''


#2

My current theory is that being a USB webcam, it needs to be allowed through from the host machine to the container in the docker-compose.yaml and that

devices: 
-/dev/video0:/dev/video0

doesn't sufficiently allow the USB port in use through to the container.

But I'm still having trouble identifying exactly what USB port it is connected to (?)

I can Identify the device and get its details with lsusb -vvv or I can get a list of tty ports ls /dev | grep tty (this shows a long list of tty ports but no clearly identification of the webcam)

tty
tty0
tty1
tty10
tty11
tty12
tty13
tty14
tty15
tty16
tty17
tty18
tty19
tty2
tty20
tty21
tty22
tty23
tty24
tty25
tty26
tty27
tty28
tty29
tty3
tty30
tty31
tty32
tty33
tty34
tty35
tty36
tty37
tty38
tty39
tty4
tty40
tty41
tty42
tty43
tty44
tty45
tty46
tty47
tty48
tty49
tty5
tty50
tty51
tty52
tty53
tty54
tty55
tty56
tty57
tty58
tty59
tty6
tty60
tty61
tty62
tty63
tty7
tty8
tty9
ttyACM0
ttyprintk
ttyS0
ttyS1
ttyS10
ttyS11
ttyS12
ttyS13
ttyS14
ttyS15
ttyS16
ttyS17
ttyS18
ttyS19
ttyS2
ttyS20
ttyS21
ttyS22
ttyS23
ttyS24
ttyS25
ttyS26
ttyS27
ttyS28
ttyS29
ttyS3
ttyS30
ttyS31
ttyS4
ttyS5
ttyS6
ttyS7
ttyS8
ttyS9

But it is still unclear (to me at least) exactly which tty USB port has the webcam connected.

Any help, hits and tips would be greatly appreciated.

lsusb -vvv
Bus 001 Device 003: ID 1415:2000 Nam Tai E&E Products Ltd. or OmniVision Technologies, Inc. Sony Playstation Eye
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x1415 Nam Tai E&E Products Ltd. or OmniVision Technologies, Inc.
  idProduct          0x2000 Sony Playstation Eye
  bcdDevice            1.00
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength          142
    bNumInterfaces          3
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval              10
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass         1 Audio
      bInterfaceSubClass      1 Control Device
      bInterfaceProtocol      0 
      iInterface              0 
      AudioControl Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      1 (HEADER)
        bcdADC               1.00
        wTotalLength           42
        bInCollection           1
        baInterfaceNr( 0)       2
      AudioControl Interface Descriptor:
        bLength                12
        bDescriptorType        36
        bDescriptorSubtype      2 (INPUT_TERMINAL)
        bTerminalID             1
        wTerminalType      0x0201 Microphone
        bAssocTerminal          2
        bNrChannels             4
        wChannelConfig     0x0000
        iChannelNames           0 
        iTerminal               0 
      AudioControl Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      3 (OUTPUT_TERMINAL)
        bTerminalID             2
        wTerminalType      0x0101 USB Streaming
        bAssocTerminal          1
        bSourceID               3
        iTerminal               0 
      AudioControl Interface Descriptor:
        bLength                12
        bDescriptorType        36
        bDescriptorSubtype      6 (FEATURE_UNIT)
        bUnitID                 3
        bSourceID               1
        bControlSize            1
        bmaControls( 0)      0x00
        bmaControls( 1)      0x02
          Volume Control
        bmaControls( 2)      0x02
          Volume Control
        bmaControls( 3)      0x02
          Volume Control
        bmaControls( 4)      0x02
          Volume Control
        iFeature                0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass         1 Audio
      bInterfaceSubClass      2 Streaming
      bInterfaceProtocol      0 
      iInterface              0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       1
      bNumEndpoints           1
      bInterfaceClass         1 Audio
      bInterfaceSubClass      2 Streaming
      bInterfaceProtocol      0 
      iInterface              0 
      AudioStreaming Interface Descriptor:
        bLength                 7
        bDescriptorType        36
        bDescriptorSubtype      1 (AS_GENERAL)
        bTerminalLink           2
        bDelay                  1 frames
        wFormatTag              1 PCM
      AudioStreaming Interface Descriptor:
        bLength                11
        bDescriptorType        36
        bDescriptorSubtype      2 (FORMAT_TYPE)
        bFormatType             1 (FORMAT_TYPE_I)
        bNrChannels             4
        bSubframeSize           2
        bBitResolution         16
        bSamFreqType            1 Discrete
        tSamFreq[ 0]        16000
      Endpoint Descriptor:
        bLength                 9
        bDescriptorType         5
        bEndpointAddress     0x84  EP 4 IN
        bmAttributes            5
          Transfer Type            Isochronous
          Synch Type               Asynchronous
          Usage Type               Data
        wMaxPacketSize     0x0300  1x 768 bytes
        bInterval               4
        bRefresh                0
        bSynchAddress           0
        AudioControl Endpoint Descriptor:
          bLength                 7
          bDescriptorType        37
          bDescriptorSubtype      1 (EP_GENERAL)
          bmAttributes         0x01
            Sampling Frequency
          bLockDelayUnits         0 Undefined
          wLockDelay              0 Undefined

#3

It's possible if not even likely that the device endpoint doesn't include "tty" in it. It could start with "usb" or have that somewhere in the name. Or maybe it doesn't.

ls -l /dev

I will say that there is often a lookup created dynamically in cases like this and it can be useful in finding your device once it's been plugged in.

cd /dev
find . -name by-id

Once you find a subdirectory like this (especially under the appropriate folder like "video" or similar device category), then cd into this folder and do an ls -l again. You should see the individual IDs as presented earlier in that lsusb command. I was doing some development with RFID readers and barcode scanners and this technique comes in handy. Once you get to the symlink in question, it should indicate the actual path to the device that was created.

Also, dmesg can sometimes reveal what this is.


#4

Thanks for the pointers.
I'm a little out of my depth with the whole tty & dev naming area but as far as I can tell...

My printer is coming through on /dev/ttyACM0 and the camera is /dev/video0 as such i added these to the docker-compose.yaml but I was suspicion that I may also need to add something more than /dev/video0.

I may need to allow another port through to the host machine, after a lot of googling, looks like the mpeg-streamer may use a different port (?). I'm guessing this would be part of the choice in the setup of the container. I've played with adding the following but still no success thus far.

port: 
      - 8089:8080
      - 8088:8088

I did find a nasty hack in the form of streaming the camera from the host machine (refer https://gist.github.com/endolith/2052778) but it really goes against the whole docker containerisation concept. This at least showed the camera was working and could be streamed to a port, but its no real long term solution.


#5

A standard command line without the webcam might look like:

docker run -d -v --device /dev/ttyACM0:/dev/ttyACM0 -p 5000:5000 --name octoprint octoprint/octoprint

So I'm guessing that you'd have to double up in those --device and -p sections for the video, right?

docker run -d -v --device /dev/ttyACM0:/dev/ttyACM0 --device /dev/video0:/dev/video0 -p 5000:5000 -p 8080:8080 --name octoprint octoprint/octoprint

#6

mmm... lets see if I can clarify. (Sorry if I'm stating the obvious here)

I'm running my docker instance using docker-compose rather than a docker run <image>
(If you haven't used it, at its most basic level this is a nice tool to spool up docker containers with the parameters stored in a text file docker-compose.ymal which can then quickly launch containers via a single command docker-compose up -d)

...but in essentially it should do the same thing as run.
So i think the answer to your question is yes, I'm trying to allow several things through from the Host to the container:

Devices:

  • /dev/ttyACM0 #USB connection to Printer
  • /dev/video0 #PS3 eye webcam

Ports:

  • 5000 #Octoprint container
  • 8080 #This was for testing I suspected this was the port the mpeg-streamer was using
  • 8088 #This was for testing I suspected this was the port the mpeg-streamer was using
version: '2'
services:
  octoprint:
    build: .
    image: octoprint/octoprint
    container_name: octoprint
    ports:
      - 5000:5000
      - 8080:8080
      - 8088:8088

    devices:
      - /dev/ttyACM0:/dev/ttyACM0
      - /dev/video0:/dev/video0
    volumes:
     - ./config:/home/octoprint/.octoprint
     - /dev/ttyACM0:/dev/ttyACM0
    restart: always

#7

So... more looking... no more answers yet.:face_with_raised_eyebrow:

I drilled into the dockerfile (see below) and it looks like there is only one port exposed port 5000
I also can't obviously see where the jpeg-streamer is added to the dockerfile not sure if I'm just over looking something obvious or its not included in the Image and needs adding / exposing separately.

The single exposed port makes me think that it is not possible to access the webcam stream on any other port via the http://<hostIP>:<port> (thus its pointless exposing other ports to the host in the docker-compose.ymal) and the only option for the stream is the internal loop back on 127.0.0.1 (assuming that the streamer is installed at all)

Any thoughts or ideas are welcome?

FROM python:2.7
EXPOSE 5000
LABEL maintainer "gaetancollaud@gmail.com"

ENV CURA_VERSION=15.04.6
ARG tag=master

WORKDIR /opt/octoprint

# In case of alpine
#RUN apk update &amp;&amp; apk upgrade \
# &amp;&amp; apk add --no-cache bash git openssh gcc\
# &amp;&amp; pip install virtualenv \
# &amp;&amp; rm -rf /var/cache/apk/*

#install ffmpeg
RUN cd /tmp \
&amp;&amp; wget -O ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-32bit-static.tar.xz \
&amp;&amp; mkdir -p /opt/ffmpeg \
&amp;&amp; tar xvf ffmpeg.tar.xz -C /opt/ffmpeg --strip-components=1 \
&amp;&amp; rm -Rf /tmp/*

#install Cura
RUN cd /tmp \
&amp;&amp; wget https://github.com/Ultimaker/CuraEngine/archive/${CURA_VERSION}.tar.gz \
&amp;&amp; tar -zxf ${CURA_VERSION}.tar.gz \
&amp;&amp; cd CuraEngine-${CURA_VERSION} \
&amp;&amp; mkdir build \
&amp;&amp; make \
&amp;&amp; mv -f ./build /opt/cura/ \
&amp;&amp; rm -Rf /tmp/*

#Create an octoprint user
RUN useradd -ms /bin/bash octoprint &amp;&amp; adduser octoprint dialout
RUN chown octoprint:octoprint /opt/octoprint
USER octoprint
#This fixes issues with the volume command setting wrong permissions
RUN mkdir /home/octoprint/.octoprint

#Install Octoprint
RUN git clone --branch $tag https://github.com/foosel/OctoPrint.git /opt/octoprint \
&amp;&amp; virtualenv venv \
&amp;&amp; ./venv/bin/python setup.py install

VOLUME /home/octoprint/.octoprint

CMD ["/opt/octoprint/venv/bin/octoprint", "serve"]

#8

This probably works as expected but I've also seen foosel specify the port here in this invocation. But you're not complaining about OctoPrint just the webcam so I guess 5000 is the default.

Do you need to activate the virtual environment? Not that I've taken the approach that you're using but it's typical to call the source path/bin/activate before doing the python setup.py install command.


#9

Not 100% clear what you are recommending trying here.

It my understanding that docker setups the image with a dockerfile ("offical" octoprint/octoprint dockerfile was included in the previous post) this builds the image & environment from which the active docker container is generated from.
From what I can tell the serve command is just launching the octoprint webserver/ webpage via ip http://0.0.0.0:5000 within the container this is then shared to the host environment via the EXPOSE 5000
command.
I'm not really have any problem with octoprint living on port 5000.

I have found another "unofficial" docker image https://hub.docker.com/r/atwoz/octoprint/ that looks like it maybe a promising guide whilst I haven't had the time to test anything yet looks to contain a number of added components in its dockerfile: (see below & compare to the previous posted dockerfile)
Notably installing mjpg-streamer and exposing port 8088. I suspect these maybe the parts "missing" (or deliberately left out) of the offical docker octoprint image.
Note: There maybe some very deliberate reasons for leaving these parts out of the image, I'm just not clear on what that would be. Possible the theory is the mjpg-streamer should live in an independent container and be "linked" to the octoprint container, given it was developed by a third party to octoprint

I would love to hear any reasoning on the exclusion / inclusion of these components and if possible the recommended strategy for including webcams with the offical docker version.
I suspect I'm not the first to have this problem and maybe not the last

FROM pypy:2-slim
MAINTAINER Ohad Galor Kimchi <ohadgk@gmail.com>

ENV OCTOPRINT_VERSION=1.2.15
ENV CURA_ENGINE_VERSION=15.04.6

RUN set -xe \
	&& echo "Setup Temporary packages for compilation" \
	&& export PKGS='build-essential subversion libjpeg-dev zlib1g-dev libv4l-dev wget unzip git' \
	&& echo "Installing Dependencies" \
	&& apt-get update \
	&& apt-get install -y libprotobuf9 libav-tools avrdude libjpeg62-turbo curl imagemagick psmisc --no-install-recommends \
	&& apt-get install -y ${PKGS} --no-install-recommends \
	&& echo "Download OctoPrint/CuraEngine/mjpg-streamer" \
	&& cd /tmp/ \
	&& wget https://github.com/foosel/OctoPrint/archive/${OCTOPRINT_VERSION}.tar.gz \
	&& wget https://github.com/Ultimaker/CuraEngine/archive/${CURA_ENGINE_VERSION}.tar.gz \
	&& wget http://sourceforge.net/code-snapshots/svn/m/mj/mjpg-streamer/code/mjpg-streamer-code-182.zip \
	&& echo "Installing mjpg-streamer" \
	&& unzip mjpg-streamer-code-182.zip \
	&& cd mjpg-streamer-code-182/mjpg-streamer \
	&& make \
	&& make install \
	&& cd ../.. \
	&& echo "Installing CuraEngine" \
	&& tar -zxf ${CURA_ENGINE_VERSION}.tar.gz \
	&& cd CuraEngine-${CURA_ENGINE_VERSION} \
	&& mkdir build \
	&& make \
	&& mv -f ./build /CuraEngine/ \
	&& cd .. \
	&& echo "Installing OctoPrint" \
	&& tar -zxf ${OCTOPRINT_VERSION}.tar.gz \
	&& mv -f OctoPrint-${OCTOPRINT_VERSION} /octoprint/ \
	&& cd /octoprint/ \
	&& echo "Install OctoPrint requirements" \
	&& pip install -r requirements.txt \
	&& pip install pillow \
	&& pypy setup.py install \
	&& echo "Cleaning Temporary Packages + Installation leftovers" \
	&& apt-get purge -y --auto-remove ${PKGS} \
	&& rm -rf /var/lib/apt/lists/* \
	&& rm -rf /tmp/* /var/tmp/*

VOLUME /data
WORKDIR /data

EXPOSE 5000
# TODO: Until i'll add HAProxy
EXPOSE 8088

ADD octoprint.sh /usr/bin/
ENV YUV_CAMERA "true"

ENTRYPOINT ["octoprint.sh"]
CMD []

#10

Regarding "why" the exclusion, I'd suggest that someone was pretty happy that they got this to work under Docker and called it a success. If/when they remembered the webcam, they probably got a headache and rationalized that it was good enough. There's some sort of "rest inertia" in these situations, as a would-be author: "should I add support for the webcam?", "if I add support for the webcam, don't I also have to do USB-based webcams and ribbon-based...?"

Guy was just indicating to me in another thread that he's just added Docker support to the official OctoPi image he maintains. He's asked me to test it on OSX so I may do this later this week. Actually, I think he indicated that he did this work with CustomPiOS, the underlying platform that he uses for OctoPi.


In this version you've provided, I don't see any of the virtualenv stuff but perhaps this is how Docker just does it.


#11

I can certainly empathise with Theory that the basic version works so that's a nice point to stop and take a break, maybe play with another issue. I have become a fan of containerisation but understandably its not for everyone, the octoPi image is a really great way to get up and running quickly also.

Not sure about virtualenv I really haven't played with it, but the beauty of docker & containerisation is the ability to hold an image with specific dependancies eg the specific python version required. I suspect this is removes the need for virtualenv. (I'm sure someone more knowledgeable can correct me if I'm wrong)

virtualenv is a tool to create isolated Python environments.

I would prefer to remain on the main offical image if possible. I may need to delve into the docker documentation on the best / nicest way to add the jpeg-streamer to an offical image. (I suspect that just hacking in / cloning for the other version the missing jpeg-streamer lines to the existing dockerfile is considered bad form, from an update & version control perspective)

I'll have a play when I get a clear afternoon and post back any lessons here.


#12

That's my take on it, too: the containerization of Docker is the same sort of compartmentalization that virtualenv hopes to achieve. So why the need for two layers of compartmentalization? It makes no sense but I still see some of these examples/attempts/tutorials going there.

So I'm agreeing with you, in other words. The atwoz link above (foregoing virtualenv) looks like the best approach, in my humble opinion.