user1526912
user1526912

Reputation: 17220

How to set a video current time on page load in HTML5 then add a seek listener synchronously

I have the following React component that grabs a video frame and loads the frame image into another image component upon specific events(pause, seek).

I wish to be able to set the current video frame position on rendering of the video element then add a seek event afterwards .

The problem I am experiencing is setting the frame position in componentDidMount with: video.currentTime = this.props.currentVideoFramePosition actually calls the seek event even though I added the event afterwards.

How can I modify the below code to set the current time of the video without triggering the seek event.

I am trying to avoid this on page load because the draw function is returning a value to another component which I do not want…….

import React from 'react';

export default class MyComponent extends React.Component {

  constructor() {
    super();
    const self = this;
    this.frameCaptureListener =  function(e){
      self.draw();
    };
  }

  componentDidMount() {
    var video = document.getElementById('video');
    document.getElementById('video-canvas').style.display='none';
    video.currentTime  = this.props.currentVideoFramePosition;
    video.addEventListener('pause', this.frameCaptureListener, false);
    video.addEventListener('seeked', this.frameCaptureListener, false);
}

componentWillUnmount(){
    var video = document.getElementById('video');
    video.removeEventListener('pause', this.frameCaptureListener, false );
    video.removeEventListener('seeked', this.frameCaptureListener, false);
}

   draw(){
     var video = document.getElementById('video');
     var canvas = document.getElementById('video-canvas');
     var context = canvas.getContext('2d');
     context.clearRect(0, 0, canvas.width, canvas.height);
     canvas.width = 1280;
     canvas.height = 720;
     context.drawImage( video, 0, 0, canvas.width, canvas.height);
     var dataURL = canvas.toDataURL();
     video.setAttribute('crossOrigin', 'anonymous');
     var frameData = {imgSrc:dataURL, lastFramePosition:video.currentTime};
     return this.props.onPause(frameData);
   }

   onArrowNavClick(navNum){
     const self = this;
     var video = document.getElementById('video');
     video.currentTime = video.currentTime + navNum;
     video.pause();
   }

  render(){
    const videoUrl = this.props.videoUrl;
    return(
      <div>
            <div className="framegrab-videocontainer">
              <img src="/images/arrow_left_grab.svg" className="arrow-left" onClick={() => this.onArrowNavClick(-1)}></img>
              <video id="video" src={videoUrl} className="framegrab-video"  controls crossOrigin="anonymous" />
              <img src="/images/arrow_right_grab.svg" className="arrow-right"  onClick={() => this.onArrowNavClick(1)}></img>
            </div>
          <canvas id="video-canvas" ></canvas>
        </div>
);
  }
}

Upvotes: 0

Views: 2407

Answers (1)

J. Mark Stevens
J. Mark Stevens

Reputation: 4945

Here is how I am doing it with audio. Your problem with triggering seeking might be fixed by using a if (video.captureTime == props.captureTime) return; in frameCaptureListener.

import React from 'react';
import kshuffle, {knuthShuffle} from 'knuth-shuffle';

import JProgressBar from '../common/jProgressBar';
import PlayerActions from './player.actions';

let PlayerCtrlSty = {
  backgroundColor: '#072202',
  color: '#eaab5f',
  margin: 'auto',
  padding: '3px',
  width: '320px'
}

let timerLeftSty = {minWidth: '40px'}
let timerRightSty = {textAlign: 'right', minWidth: '40px'}

let getTime = (time) => {
  var minutes = time < 60 ? 0 : Math.floor(time / 60);
  var seconds = Math.floor(time - (minutes * 60)) * .01;
  return (minutes + seconds).toFixed(2);
}

class PlayerCtrlRender extends React.Component {
   render() {
    let index = this.state.playIndex;
    let currentTrack = this.state.randomOn ? this.state.shuffledQueue[index] : this.props.queue[index];
    let source = 'http://192.168.0.101:3950/play/' + currentTrack.location;
    let title = currentTrack.title;

    let progressData = {count: this.state.progressCount * 100, index: this.state.progressIndex * 100};
    return (
      <div id='PlayerCtrlSty' style={PlayerCtrlSty}>
        <div className='FlexBox'>
          <div style={timerLeftSty}>{this.state.progressIndex}</div>
          <PlayerActions playing={this.state.isPlaying} clickHandler={this.clickHandler}/>
          <div style={timerRightSty}>{this.state.progressCount}</div>
        </div>
        <JProgressBar data={progressData} position='none' />
        <div id="title" style={{textAlign: 'center'}}>{title}</div>
        <audio
          ref="audioDiv"
          src={source}
          onDurationChange={this.onDurationChange}
          onTimeUpdate={this.onTimeUpdate}
          onEnded={this.nextSong}
        />
      </div>
    );
  }
}

export default class PlayerCtrl extends PlayerCtrlRender {
  constructor() {
    super();
    this.state = {
      playIndex: 0,
      queueLength: 1,
      isPlaying: false,
      progressCount: 0,
      progressIndex: 0,
      shuffledQueue: [{title: '', location: ''}],
      randomOn: false
    };
  }

  componentDidMount = () => {
    let queue = knuthShuffle(this.props.queue.slice(0));
    this.setState({queueLength: queue.length, shuffledQueue: queue});
    this.refs.audioDiv.volume = .1;
  };
  clickHandler = (buttonid) => {
    this.refs.audioDiv.autoplay = false;
    switch (buttonid) {
      case 'play': this.refs.audioDiv.play(); this.setState({isPlaying: true}); break;
      case 'pause': this.refs.audioDiv.pause(); this.setState({isPlaying: false}); break;
      case 'back': this.refs.audioDiv.currentTime = 0; break;
      case 'next': this.nextSong(); break;
      case 'random': this.refs.audioDiv.autoplay = this.state.isPlaying;
          this.setState({randomOn: !this.state.randomOn}); break;
    }
  };
  nextSong = () => {
    this.refs.audioDiv.autoplay = this.state.isPlaying;
    this.refs.audioDiv.currentTime = 0;
    let newIndex = this.state.playIndex + 1;
    if (newIndex == this.state.queueLength) newIndex = 0;
    this.setState({playIndex: newIndex});
  };
  onDurationChange = () => {
    let duration = this.refs.audioDiv.duration;
    duration = getTime(Math.floor(duration));
    this.setState({progressCount: duration})
    this.setState({progressIndex: 0})
  };
  onTimeUpdate = () => {
    let currentTime = this.refs.audioDiv.currentTime;
    currentTime = getTime(Math.floor(currentTime));
    this.setState({progressIndex: currentTime})
  };
}

Upvotes: 1

Related Questions