2

I am working on a small scale app that displays videos in multiple ways using a video-player component. Currently I am implementing a stack-list, which is a container that holds video-stack components, and each stack contains one or more video-player components.

While the correct videos are loaded from the DOM, there is a noticeable multi-second lag (in terms of keyboard response) which seems to be related to the ending of the currently played video and the fetching of the next video in the stack.

How can I get rid of this lag? Videos are able to be toggled/selected via mouse hovers or WASD keyboard commands (A: previous, D: next), and the lag can cause a delay in keyboard inputs being registered.

video-stack.hbs

{{video-player highlightedStyle=(string-append stackStyle borderStyle)
looping=(is-single-video videos) videoPos=selectedVidPos 
isMuted=(if (video-selected key selectedStackIndex) isMuted true) 
url=(if curVideo.teaser.isUrl curVideo.teaser.fileIdentifier 
(make-local-url modelIdentifier curVideo.teaser.fileIdentifier)) 
onClickCallback=(action 'stackClicked') 
onHoverCallback=(action 'stackHovered')
onEndedCallback=(action 'getNextVid')}}

video-stack.js

import Ember from 'ember';

export default Ember.Component.extend({
selectedVidPos: 0,
selectedStackIndex: 0,
stackStyle: '',
playerSize: '',
isMuted: true,

init() {
    this._super(...arguments);

    switch(this.get('videos').length){
        case 1:
            break;
        case 2:
            this.set('stackStyle', 'vid-shadows--2');
            break;
        case 3:
            this.set('stackStyle', 'vid-shadows--3');
            break;
        case 4:
            this.set('stackStyle', 'vid-shadows--4');
            break;
        default:
            this.set('stackStyle', 'vid-shadows--4');
            break;
    }
},
curVideo: Ember.computed('videos', 'selectedVidPos', function () {
    return this.get('videos')[this.get('selectedVidPos')];
}),
actions: {
    stackClicked() {
        this.get('onClickCallback') (this.get('videos'),      this.get('selectedVidPos'));

    },
    getNextVid() {
        let arrayLength = this.get('videos').length;
//check if there is only 1 video in the stack
        if (arrayLength === 1) {
            return;
        }
        let curArrayPos = parseInt(this.get('selectedVidPos'));
        this.set('selectedVidPos', (curArrayPos + 1) % arrayLength);
    },
    stackHovered() {
        this.get('onHoverCallback') (this.get('videos'), this.get('selectedStackIndex'));
    }
}
});

video-player.hbs

<video oncanplay={{action 'play'}} looping=true 
onended={{action 'ended'}} src={{url}} 
class="video-player__video {{highlightedStyle}} {{if playing '' 'video-
player__darken'}}" muted={{muted}} />

video-player.js

import Ember from 'ember';

export default Ember.Component.extend({
url: null,
looping: false,
playing: true,
muted: true,
highlightedStyle: '',

click(event) {
    this.get('onClickCallback') (this.get('videoPos'));
    event.stopPropagation();
},
mouseEnter() {
    this.get('onHoverCallback') (this.get('videoPos'));
},
willClearRender() {
this.set('playingObserver', null);
this.set('urlObserver', null);
},
playingObserver: Ember.observer('playing', function() {
if (this) {
  var p = this.get("playing");
  var videoElement = this.$().find("video").get(0);
  if (videoElement) {
    if (p) {
      videoElement.play();
    }
    else {
      videoElement.pause();
    }
  }
  else {
    console.log("No video element found!");
  }
}
}),
urlObserver: Ember.observer('url', function() {
if (this) {
  var videoElement = this.$().find("video").get(0);
  if (videoElement) {
    videoElement.load();
  }
  else {
    console.log("No video element found");
  }
}
}),
actions: {
  ended() {
    if (this.get('looping')) {
        this.$().find("video").get(0).play();
        console.log("video-player ended");
    }
    else {
        console.log(this.get('videoPos'));
        this.get('onEndedCallback') (this.get('videoPos'));
    }
},
play() {
  if (this.get('playing')) {
    this.$().find("video").get(0).play();
   }
  }
 }
});

I can post more code if it would help shed light on the culprit, thanks!

Alex Reid
  • 21
  • 1
  • See [HTML5 audio streaming: precisely measure latency?](https://stackoverflow.com/questions/38768375/html5-audio-streaming-precisely-measure-latency/), [Preload mp3 file in queue to avoid any delay in playing the next file in queue](https://stackoverflow.com/questions/42049639/preload-mp3-file-in-queue-to-avoid-any-delay-in-playing-the-next-file-in-queue/) – guest271314 Jun 09 '17 at 19:19

1 Answers1

0

I found the culprit of the lag. The issue was in the parent container, content-area.js, which had a resetTimeout action that was being called incorrectly, which caused the focus to cycle needlessly, resulting in the lag.

Also implemented a switch off in terms of rendering videos to ensure smooth loading from one video to the next in video-stack.js, there are now 2 video objects, A & B, which are fetched and preloaded from the blob object, showing one while the other is hidden. Once the displayed video ends, they swap out, and the next video in the stack is loaded.

video-stack.js

export default Ember.Component.extend({
selectedVidAPos: 0,
selectedVidBPos: 0,
selectedStackIndex: 0,
stackStyle: '',
playerSize: '',
isMuted: true,
showVidA: true,

init() {
    ...
    }
},
videoA: Ember.computed('videos', 'selectedVidAPos', function () {
    return this.get('videos')[this.get('selectedVidAPos')];
}),
videoB: Ember.computed('videos', 'selectedVidBPos', function () {
    return this.get('videos')[this.get('selectedVidBPos')];
}),
actions: {
    stackClicked() {
        this.get('onClickCallback') (this.get('videos'), (this.get('showVidA') ? this.get('selectedVidAPos') : this.get('selectedVidBPos')));
    },
    getNextVideoA() {
        let arrayLength = this.get('videos').length;
        if (arrayLength === 1) {
            return;
        }
        let curArrayPos = parseInt(this.get('selectedVidAPos'));
        this.set('selectedVidAPos', (curArrayPos + 2) % arrayLength);
        this.set('showVidA', false);        
    },
    getNextVideoB(){
        let arrayLength = this.get('videos').length;
        if (arrayLength === 1) {
            return;
        }
        let curArrayPos = parseInt(this.get('selectedVidBPos'));
        this.set('selectedVidBPos', (curArrayPos + 2) % arrayLength);
        this.set('showVidA', true);
    },

    stackHovered() {
        this.get('onHoverCallback') (this.get('videos'), this.get('selectedStackIndex'));
    }
}
});

content-area.js

import Ember from 'ember';
import KeyboardControls from '../mixins/keyboard-controls';

export default Ember.Component.extend(KeyboardControls, {
displayVideoSelect: false,
displayVideoSelectTimeout: null,
displayVideo: false,
video: null,
videoPlaying: false,
keyboard: null,
backgroundVideoPos: 0,
backgroundVideoUrl: null,
backgroundVideoKeys: null,
selectionVideos: [],
stackListData: null,

showVideoSelect: function() {
  this.set('displayVideoSelect', true);
  this.send('resetTimeout');
},
hideVideoSelect: function() {
  this.set('displayVideoSelect', false);
  clearTimeout(this.get('displayVideoSelectTimeout'));
},
pauseVideo: function() {
  this.set('videoPlaying', !this.get('videoPlaying'));
  this.set('displayVideoSelect', !this.get('videoPlaying'));
  this.set('focus', this.get('videoPlaying'));
},
select: function() {
  this.set('videoPlaying', false);
  this.set('focus', false);
  this.showVideoSelect();
  this.send('resetTimeout');
},
cancel: function() {
  this.pauseVideo();
  this.send('resetTimeout');
},
goNext: function() {
  this.pauseVideo();
  this.send('resetTimeout');
},
goPrevious: function() {
  this.pauseVideo();
  this.send('resetTimeout');
},
updateFocus: function(param) {
if (param) {
  this.$().attr('tabindex', 2);
  this.$().focus();
}//if
else {
  this.$().attr('tabindex', -2);
  this.$().blur();
}//else
},

init() {
...
},

click() {
  this.set('focus', false);
  this.showVideoSelect();
},
actions: {
  videoSelected(sender, videoData) {
    ...
  },
  videoEnded() {
    this.set('focus', false);
    this.showVideoSelect();
    this.set('displayVideo', false);
  },
  cycleBackground() {
    ...
  },
  cancelPressed() {
    this.cancel();
  },
  resetTimeout() {
    let component = this;
    clearTimeout(this.get('displayVideoSelectTimeout'));

    let timeout = setTimeout(() => {
                  component.hideVideoSelect();
                  //This set command was responsible for the lag
                  component.set('focus', true);
                  }, this.get('data.config.ui.idle') * 1000);
    this.set('displayVideoSelectTimeout', timeout);
    }
  }
});
Alex Reid
  • 21
  • 1