[WIP] Multiple Webcam streams in Control (Multicam)

My idea is to be able to put a second webcam stream into the octoprint control tab, with an overlay button (or jog-panel) to switch between the two cameras feeds. I attempted to implement this myself thinking it would be pretty simple, however having never created octoprint plugins before i ran into some issues getting some things to work as i intended.

Here is an example of what i'm talking about.

Under settings for the plugin there would be a single dropdown with the first option being what ever stream (if provided) is under the Webcams and Timelapse tab. The second options being "Add WebCam". When you select one of the options, two input boxes would show up below, where you could provide a name and URL to a second or a third webcam stream,

The screenshot above does not show the progress of my current work as getting those button to appear where i need them is where im having the most trouble. If anyone else wants to take a swing at it I would be very grateful! The name was going to be Multicam, since that seems to be available in the repository.

2 Likes

Personally, I think I'd like to (also) see both webcams at once (left/right on the available width).

Figured out how to inject the jinja2 into the control tab correctly finally. As well as making the buttons change the feed that is displayed! I still need to add a little more functionality to them, but it is working now at least!

Another question I have now is how do i dynamically create a new value in the settings for the cam data, as well as retrieval of these dynamic values (and counting them). This will be an important part to updating the ui for each cam.

@OutsourcedGuru I thought about doing this originally. Possibly laying them out in a grid, but have ran into so much trouble just getting thing to work that i decided to just update the img that is already available, and have some buttons that you can toggle between them all. Maybe if i can get this working though i can revisit this with an option in the settings to allow you to display all your cams at the same time.

Anyway, here is a gif of my progress on my dev environment. Ill keep this updated as i continue with this project, any help will be greatly appreciated! Thank you!

2 Likes

Looks amazing, dude. Guess I'll have to consider buying another camera for the inside of the printer. Was earlier thinking of putting a camera on the filament spool (had months of problems with cross-snagging) but I've recently squashed that problem.

Another question I have now is how do i dynamically create a new value in the settings for the cam data, as well as retrieval of these dynamic values (and counting them). This will be an important part to updating the ui for each cam.

Not sure I'm following or if I'm the best person to answer that one.

Ok, So im making some progress on storing the information about the webcam the way i was hoping by looking at some of the other built-in plugins, and think i understand that im suppose to be using knockouts instead of storing this in the config.yaml. That gives me the ability to do something like this:

        self.multicam_profiles = ko.observableArray();

        self.addMultiCamProfile = function() {
            self.multicam_profiles.push({name: "Webcam "+self.multicam_profiles().length, URL: "http://"});
        };

The above code creates a knockout array, and then gives a function to add to that array, however I seem to be confused on how saving the knockout data works. I found some code that seems to accomplish that, but it is way more complicated that i'm looking for and i don't even know where to begin with it.

I also put together a small settings dialog page to manage these using the Temperature settings profile as a template, but this breaks the bindings, which i assume is because im missing some parts in my js to handle loading and saving of the data into something that can be bound. Here is the setting page snippet too, just incase someone notices something else i may be missing:

<form class="form-horizontal">
    <h3>{{ _('Multicam Profiles') }}</h3>
    <div class="row-fluid">
        <div class="span5"><h4>{{ _('Name') }}</h4></div>
        <div class="span5"><h4>{{ _('URL') }}</h4></div>
    </div>
    <div data-bind="foreach: multicam_profiles">
        <div class="row-fluid" style="margin-bottom: 5px">
            <div class="span5">
                <input type="text" class="span12 text-right" data-bind="value: name">
            </div>
            <div class="input-append span5">
                <input type="text" class="span12 text-right" data-bind="value: URL">
            </div>
            <div class="span2">
                <a title="Remove Webcam" class="btn btn-danger" data-bind="click: $parent.removeMultiCamProfile"><i class="fa fa-trash-o"></i></a>
            </div>
        </div>
    </div>
    <div class="row-fluid">
        <div class="offset10 span2">
            <a title="Add Webcam" class="btn btn-primary" data-bind="click: addMultiCamProfile"><i class="fa fa-plus"></i></a>
        </div>
    </div>
</form>

I'm going to have to research more into knockouts i believe in order to get this working, but if anyone has any suggestions I would be very interested in hearing them.

Figured out why the binding was broken, I just had to set custom_bindings to true for my settings template in the _init_.py, that has fixed all the binding errors. I still need to work on how to save and load the data though, as well as getting the Add Webcam button to actually insert a new row, it seems i may be missing some other code that accomplished that from the other plugin. Its late here though so i'm going to call it a night and take another look at this tomorrow.

Finally someone stepped up and looked into this :heart_eyes: You are probably going to be the hero of many :smiley:

3 Likes

Made lots of progress on this today. I figured out how to save everything and update the view as new cams are added in the settings. Another question i have is in the _init_.py is there a way to pull a value from config.yaml such as webcam.stream? One of the last few things i want to setup is making the plugin grab the webcam stream already configured and set that as the first (Default) value. Hopefully i can have this all finished and ready to push by tomorrow night.

Pretty sure this tutorial goes over the how-to.

config.yaml

# [...]
plugins:
  helloworld:
    url: https://de.wikipedia.org/wiki/Hallo-Welt-Programm
# [...]

jinja2

data-bind="value: settings.plugins.helloworld.url"

init.py

def on_after_startup(self):
    self._logger.info("Hello World! (more: %s)" % self._settings.get(["url"]))

Yeah that i understood, but it seems it is only able to grab from my plugin values, whereas i'm looking to get data from another field in the config, such as the stream url field that is set by Webcam & Timelapse settings tab.

So... rather than self.something you'd be interested in something like this...?

init.py

import octoprint.settings
...
	def get_template_vars(self):
		return dict(webcamurl=octoprint.settings.settings().get(["webcam","stream"]))

Yep, thats exactly what im looking for.

Just sent the Pull Request to have version 0.1.0 merged back into the repository! Still have a few things id like to work on, but for now this should at least be functional.

if you want to test before the pull request is completed the install string is below:

pip install "https://github.com/mikedmor/OctoPrint_MultiCam/archive/master.zip"

For the record, the global_get* and global_set* family of methods of the injected PluginSettings instance are there for accessing things outside of the plugin's own settings, e.g.:

class MyPlugin(octoprint.plugin.TemplatePlugin, octoprint.plugin.SettingsPlugin):
    def get_template_vars(self):
        return dict(webcamurl=self._settings.global_get(["webcam", "server"])
2 Likes

Thank you foosel, that is a much cleaner solution. However i seem to be having problems getting that to work under the get_settings_defaults section. Below is a sniplet from that section that i am try to use and my plugin is using both octoprint.plugin.TemplatePlugin and octoprint.plugin.SettingsPlugin:

    def get_settings_defaults(self):
        return dict(multicam_profiles=[{'name':'Default','URL':self._settings.global_get(["webcam", "stream"])}])

I also made a small change that keeps the plugin hidden until after it binds, it seems on my printer (which has quite a few plugins) that during the loading of the page my plugin was being shown at the bottom of the page before it bound. That should all be fixed too in my next version.

Ah, yeah, it doesn't work in that section because the PluginSettings instance isn't yet injected when that gets called (it's actually used for constructing the instance). In that case you'll indeed need to use the long way around.

I just installed it. I'll add a second camera to one of my printers and let you know how it goes

Thanks !

1 Like

Awesome! Here in a few hours i should have a new version uploaded that adds a few more QOL features, i'm just trying to finish them up now. I will post again as soon as i push to let you know.

Update...

The main webcam I'm using on both printers is a Logitech C270. I've been using a Creative webcam on my bench connected to a RPi that isn't connected to a printer, and it's been working fine with no messing around to get it to work. It's plug and play

I connected the Creative cam (cuz I consider it extra) to the printer that I haven't updated the Octopi on (it's still running 14) I installed the plugin, and I see a new button at the bottom of the main screen

I've got a title that says Webcams and a button under it that says Default

The part that says Webcams isn't clickable, and the button doesn't seem to do anything other than click

The camera doesn't change views

What have I done wrong ?

camswap