chrisguttandin / timingsrc

A library to synchronize a MediaElement with a TimingObject.
MIT License
32 stars 4 forks source link

Infinite seeking on Tizen TV #23

Open aleksandrsakov opened 2 months ago

aleksandrsakov commented 2 months ago

Hello, I am using your lib for synchronisation. Everything works fine on Web (last chrome). But when I am running app on Samsung Smart TV, I am getting infinite seek events.

import { type ITimingObject, TimingObject } from "timing-object";
import { setTimingsrc } from "timingsrc";

// TODO: we should probably keep the async signature for all updates, to avoid any sort of spamming
// TODO: the timingsrc approach needs to be evaluated on low performance devices, lg in particular

/** @description Keep a html element in sync with a timing object */
export class PlayerSyncer {
  /** @description connectedSources hold the destroy function for each timingsrc connection */
  connectedSources = new Map<HTMLMediaElement, () => void>();

  timingObject: ITimingObject;

  constructor() {
    console.log('PlayerSyncer constructor');
    this.timingObject = new TimingObject();
  }

  start() {
    console.log('PlayerSyncer start');
    void this.timingObject.update({ velocity: 1 });
  }

  stop() {
    console.log('PlayerSyncer stop');
    void this.timingObject.update({ velocity: 0 });
  }

  setVelocity(velocity: number) {
    console.log('setVelocity');
    void this.timingObject.update({ velocity });
  }

  updatePosition(position: number) {
    console.log('updatePosition');
    void this.timingObject.update({ position });
  }

  connectMediaElement(element: HTMLMediaElement, offset = 0) {
    const connectedSrc = this.connectedSources.get(element);
    console.log('connectMediaElement1', {connectedSrc});
    if (connectedSrc) {
      connectedSrc();
    }
    console.log('connectMediaElement2');
    const destroy = setTimingsrc(
      element,
      this.timingObject,
      ({ position, ...vector }) => ({
        ...vector,
        position: position - offset,
      }),
    );
    console.log('connectMediaElement3');
    this.connectedSources.set(element, destroy);
    console.log('connectMediaElement4');
    return destroy;
  }

  destroy() {
    console.log('PlayerSyncer destroy');

    this.connectedSources.forEach((destroyFunction) => {
      destroyFunction();
    });
  }
}

Any recommendations or ideas why it is happening? Or how can I listen every sync operation?

chrisguttandin commented 2 months ago

Hi @aleksandrsakov, unfortunately I don't have a Samsung device to test this myself. Do you know what browser at which version the device is using? Or do you know of a way to reproduce the problem without a Samsung device?

aleksandrsakov commented 2 months ago

Hello @chrisguttandin. Here you can find a spec https://developer.samsung.com/smarttv/develop/specifications/web-engine-specifications.html#Web-Engine-Version. We saw the issue even on Tizen 6 (Chromium 76)

aleksandrsakov commented 2 months ago

UserAgent is next: 'Mozilla/5.0 (SMART-TV; LINUX; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) 69.0.3497.106/5.5 TV Safari/537.36' Seems like it goes by Safary mechanism createUpdateStepwise. When I tried to use createUpdateGradually (removed check of userAgent and remain only the else value https://github.com/chrisguttandin/timingsrc/blob/master/src/factories/default-set-timingsrc.ts#L26) the app is crashed (trying to play something for few seconds and crushes)

chrisguttandin commented 1 month ago

Thanks @aleksandrsakov, the userAgent string will indeed pick the stepwise algorithm. It's necessary for Safari because it can't handle gradual playbackRate changes. This algorithm will always perform a seek operation whenever the time difference is above the tolerance. It's 25ms by default.

https://github.com/chrisguttandin/timingsrc/blob/86b0bed629d503fcdb7f85ffc240f57d850d903e/src/factories/default-set-timingsrc.ts#L12

Could you try increasing the tolerance in your build? The following should create a custom version of setTimingsrc() that always uses the stepwise algorithm with a tolerance of 500ms.

import { createSetTimingsrc, createUpdateStepwise, setTimingsrcWithCustomUpdateFunction } from 'timingsrc';

const setTimingsrc = createSetTimingsrc(setTimingsrcWithCustomUpdateFunction, createUpdateStepwise(0.5));