Modifying a DOM Property in html.Video - Dash Video Component

I’m working on building the same concept, syncing timestamped CSV Data with video! Would love to know how your going about this :slight_smile:

I’ve made a JavaScript video player component (I’ve actually made 4 - such is the weakness of my JavaScript skills) - but right now - in proof of concept mode - I have:

A JavaScript video playing component that takes a time cursor parameter and “cleverly” advances or slows that video speed - or jumps (depending on the lag) to the cursor time. This allows you vary playback speed, single frame step or jump to a new location. This component can run in your main window or as a separate window reading cursor time regularly from disk or a memcache.

A interval timer in my main window that updates and displays the cursor time and writes it to disk so all my data/video windows can have access to slightly lagged cursor time.

You need to manage the video start wall time and then it can be synced with data wall time.

Let me know if you would like to discuss. I plan to tidy it up in a week or two and would be happy to share it.

Ian

2 Likes

That’s awesome, If you want to talk via discord feel free to add me. ether#9458
I would love to try It out.
I’m working right now to add video-react.js component into dash I’ll let you know how that goes, it exposes current time and seeking time in the state which seem quite useful.

1 Like

I have a video-react component built with marginal results on the speed variation. I had better luck with react-player. I reached out on discord - chat soon.

1 Like

Great, I hit you back up on discord. I’m new to react so currently just binge watching and absorbing all information I can on the topic. I’ll check out react-player also :slight_smile:

1 Like

I’ve got the Data and Video to sync with this code.

import dash
import datetime
from dash.dependencies import Input, Output, State, Event
import dash_core_components as dcc
import dash_html_components as html
import plotly.plotly as py
from plotly import graph_objs as go

from plotly.graph_objs import *
from flask import Flask
import pandas as pd
from collections import deque

import video_engine as rpd

app = dash.Dash()

app.scripts.config.serve_locally = True 
X = deque(maxlen=1)
Y = deque(maxlen=1)
X.append(1)
Y.append(1)

newtime = deque(maxlen=0)

df = pd.read_csv('CleanData.csv') #Reads CSV Data


app.layout = html.Div(children=[
    html.H1(children='the best fucking data analyzer ever',
            style={
                'textAlign': 'center'
                    }),
    dcc.Graph(
        id='sens1',
        style={
            'width': '50%',
            'height': '50%',
            'lineHeight': '60px',
            'borderWidth': '10px',
            'borderStyle': 'solid',
            'borderRadius': '20px',
            'textAlign': 'center',
            'margin': 'left',
        }),
 #   html.H2(id = 'time_counter',
     #    children='''NO TIME YET''',
   #      style={
  #              'textAlign': 'center'
   #                 }),
    html.Div(children=rpd.my_Player(
        id = 'video_player',
        url = 'http://127.0.0.1:8080/testvideo.mp4',
        width = 900,
        height = 720,
        controls = True,
        playing = False,
        seekTo = 0,
        volume = 1 ),
        style={'textAlign': 'center',
                'margin': 'auto'}
    ),
dcc.Slider(id = 'time-slider', value=0, min=0, max=120, step=0.00001,
           marks={0: 'Start', 120: 'End'}),

    
    ])


@app.callback( ##Graph 1
    dash.dependencies.Output('sens1', 'figure'),
    [dash.dependencies.Input('video_player', 'currTime')])
def update_time(newtime):
    X.append(newtime)
    data = go.Scatter(
                x = df['timestamp'],
                y = df['sens1'],
                name = 'EEG 1',
                mode = 'lines'
        )
    data2 = go.Scatter(
                x = df['timestamp'],
                y = df['sens2'],
                name = 'EEG 2',
                mode = 'lines'
    )
    data3 = go.Scatter(
                x = df['timestamp'],
                y = df['sens3'],
                name = 'EEG 3',
                mode = 'lines'
    )
    data4 = go.Scatter(
                x = df['timestamp'],
                y = df['sens4'],
                name = 'EEG 4',
                mode = 'lines'
    )
    
    return {'data': [data, data2, data3], 'layout': go.Layout(xaxis = dict(range=[min(X), max(X)], fixedrange='true'),
                                                              yaxis=dict(range=[min(df['sens3']),max(df['sens3'])]),)}


# TESTING
#  Tested
#   Playing (True or False)
#   Volume (between 0 and 1)
#   seekTo
#   muted
#   playbackRate
#  To BE Tested
#   getCurrentTime
#   ![itworks|600x338]
#   controls (volume only)
#   styles
#   playsInline
#   config


app.css.append_css({
    'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'})

app.css.append_css({
    'external_url': 'https://unpkg.com/video-react@0.9.4/dist/video-react.css'})

if __name__ == '__main__':
    app.run_server(debug=True)

Now I want to update 4 different graphs, when I set this code up using 4 different callback functions, they seem to update at different times and it’s super super laggy, Also If i add 4 go.Scatter data sets to the one graph it won’t work at all, 3 seems to be the max for some reason.

Another problem is in the javascript when I change the interval at which data is sent out to dash anything lower that a half of a second and the application doesn’t work.

Is there a way to improve the functionality of this script and have it update quicker/faster?

Maybe create a slider component and have the value be the input for callback functions that update the video/data?

@chriddyp I know your the GOAT on this stuff, I finally succeeded in getting this thing to work after 100+ attempts working roughly 12 hrs a day on it, I’m not much of a programmer but I feel I’ve learned a lot. If you see any obvious improvements that I’m missing to increase the functionality of this please let me know, I think having data synced to video is awesome and could be very useful.
itworks

2 Likes

Awesome stuff @Beyond_EEG! I’m happy to take a look. Is your video_engine code on GitHub somewhere?

It’s @freshwuzhere 's original code for the react module, I couldn’t of done this without his help. I just built ontop of it to get it to work with dash in the way I needed to update the graph to make it like a timeline sort of view. There’s some issues going on getting access to all the variables however.

@freshwuzhere or @Beyond_EEG - Where is this code published? I’m happy to take a look if the code is published on GitHub

1 Like

Code is published here, let me know if you have any questions :slight_smile: I’ll be available any time of day for the next week as I work to develop this

Embarrassing - it is really just a dev/proof of concept code - I was going to tidy up next week and publish then.

I’ve published a component called react_player_dash on npm and pypi.

It will allow a component to be loaded and play a video in Dash

It gives sufficient control for videos from a number of sources (react-player supports a lot - files, youtube, Vimeo etc).

It is alpha version and my javascript skills are rudimentary but it appears to be working.

I will upload some dcoumentation and sufficient Dash examples to get any intrepid users going.

Updated react-player-dash to fix a number of bugs in the component - all functions tested OK in current version 1.0.9 - have included video_demo.py to give live examples of usage in Dash.

4 Likes

Hello all,
A bit of a late post, but perhaps it helps someone as well. In my app, I run an image processing model and produce a MP4 file. Then I try to show the resulting MP4 file in my app. The processed video is stored in my server folder under ./static/.

Previously, I was having trouble getting the page to update with the new video after changing if I kept the same file name all the time. After a few days of struggling with this, I figured out the solution is to change the file name in the URL definition and then the video will update. If the name stays the same, then the page won’t reload the new file (since it determines the need to reload based on the difference in the URL definition).

Here is a mockup app which produces the right behaviour. It simply switches the URL name which forces the app to reload the video. This is based on the ‘dash-player’ library example from Github. Here, the key is I switch between 2 videos (‘movie.mp4’ and ‘movie_orig.mp4’) in order to simulate such a change. It simply switches the name of the URL definition.

import dash_player
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State

app = dash.Dash(__name__ )
application = app.server

app.scripts.config.serve_locally = True


app.layout = html.Div([
    dash_player.DashPlayer(
        id='video-player',
        #url='http://media.w3.org/2010/05/bunny/movie.mp4',
        url='static/movie.mp4',
        controls=True
    ),

    html.Button('Set seekTo to 10', id='button-seek-to'),
    html.Button('Switch video', id='button-switch-video'),

    html.Div(id='div-current-time', style={'margin-bottom': '20px'}),

    html.Div(id='div-method-output')
])


@app.callback(Output('div-current-time', 'children'),
              [Input('video-player', 'currentTime')])
def update_time(currentTime):
    return 'Current Time: {}'.format(currentTime)


@app.callback(Output('div-method-output', 'children'),
              [Input('video-player', 'secondsLoaded')],
              [State('video-player', 'duration')])
def update_methods(secondsLoaded, duration):
    return 'Second Loaded: {}, Duration: {}'.format(secondsLoaded, duration)


@app.callback(Output('video-player', 'seekTo'),
              [Input('button-seek-to', 'n_clicks')])
def set_seekTo(n_clicks):
    return 10


@app.callback(Output('video-player','url'),
              [Input('button-switch-video','n_clicks')])
def switch_video(n_clicks):
    if (n_clicks is None) or (n_clicks % 2):
        return "static/movie.mp4"
    else:
        return "static/movie_orig.mp4"



if __name__ == '__main__':
    app.run_server(host='0.0.0.0', port=8050,debug=True)
1 Like

Hi @freshwuzhere. Bro, I am working on my university project where i need to control a local video in dashboard on basis of time. The requirement is that at specific times , the video should go slow and at specific times, the speed should be normal. Can you pl suggest or help. I am new to javascript and react

Hi Suvo,

I think you can find most of what you want here.

Excellent Video Player

All the speed and controls are shown in the demo app.

Note you will need to calibrate the video time with your time base (wall time?) but it is simple calculation. If you need frame position - it is usually done by multiplying the frame rate by elapsed time.

Thank you so much. I want the video to pause at certain times. I will check this and I will let you know if i face any issues.

Hi bro, I am trying to enter in an if loop and change the speed at a particular duration. But the duration is always changing. Can you please suggest

Hi Ian,

I want the video to pause at certain time but the time is always changing and it comes in 5 decimal places. For example, I want to stop the video at time 4. I am not able to do so as it’s not entering the loop because time is coming different everytime. Can you please suggest

Thanks,
Suvojit

I am following dash-player Dash Player Custom Component - Playing and controlling your videos with Dash . I want the video to pause at certain time but the time is always changing and it comes in 5 decimal places. For example, I want to stop the video at time 4. I am not able to do so as it’s not entering the loop because time is coming different everytime. Can anyone please suggest