Source: lib/device/tizen.js

/*! @license
 * Shaka Player
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.device.Tizen');

goog.require('shaka.config.CrossBoundaryStrategy');
goog.require('shaka.device.AbstractDevice');
goog.require('shaka.device.DeviceFactory');
goog.require('shaka.device.IDevice');
goog.require('shaka.log');


/**
 * @final
 */
shaka.device.Tizen = class extends shaka.device.AbstractDevice {
  constructor() {
    super();

    const match = navigator.userAgent.match(/Tizen (\d+).(\d+)/);

    /** @private {?number} */
    this.osMajorVersion_ = match ? parseInt(match[1], 10) : null;

    /** @private {?number} */
    this.osMinorVersion_ = match ? parseInt(match[2], 10) : null;
  }

  /**
   * @override
   */
  getVersion() {
    return this.osMajorVersion_;
  }

  /**
   * @override
   */
  getDeviceName() {
    return 'Tizen';
  }

  /**
   * @override
   */
  getBrowserEngine() {
    return shaka.device.IDevice.BrowserEngine.CHROMIUM;
  }

  /**
   * @override
   */
  getDeviceType() {
    return shaka.device.IDevice.DeviceType.TV;
  }

  /**
   * @override
   */
  requiresEncryptionInfoInAllInitSegments(keySystem) {
    return true;
  }

  /**
   * @override
   */
  requiresEC3InitSegments() {
    return this.getVersion() === 3;
  }

  /**
   * @override
   */
  supportsMediaCapabilities() {
    return false;
  }

  /**
   * @override
   */
  supportsSequenceMode() {
    const version = this.getVersion();
    return version !== null ? version >= 4 : super.supportsSequenceMode();
  }

  /**
   * @override
   */
  supportsSmoothCodecSwitching() {
    return false;
  }

  /**
   * @override
   */
  supportsServerCertificate() {
    // Tizen 5.0 and earlier do not support server certificates.
    if (!this.osMajorVersion_ || !this.osMinorVersion_) {
      return super.supportsServerCertificate();
    }
    if (this.osMajorVersion_ === 5) {
      return this.osMinorVersion_ >= 5;
    }
    return this.osMajorVersion_ > 5;
  }

  /**
   * @override
   */
  detectMaxHardwareResolution() {
    const maxResolution = {width: 1920, height: 1080};
    try {
      if (webapis.systeminfo && webapis.systeminfo.getMaxVideoResolution) {
        const maxVideoResolution =
            webapis.systeminfo.getMaxVideoResolution();
        maxResolution.width = maxVideoResolution.width;
        maxResolution.height = maxVideoResolution.height;
      } else {
        if (webapis.productinfo.is8KPanelSupported &&
            webapis.productinfo.is8KPanelSupported()) {
          maxResolution.width = 7680;
          maxResolution.height = 4320;
        } else if (webapis.productinfo.isUdPanelSupported &&
            webapis.productinfo.isUdPanelSupported()) {
          maxResolution.width = 3840;
          maxResolution.height = 2160;
        }
      }
    } catch (e) {
      shaka.log.alwaysWarn('Tizen: Error detecting screen size, default ' +
          'screen size 1920x1080.');
    }

    return Promise.resolve(maxResolution);
  }

  /**
   * @override
   */
  adjustConfig(config) {
    super.adjustConfig(config);

    config.drm.ignoreDuplicateInitData = this.getVersion() !== 2;

    if (this.getVersion() === 3) {
      config.streaming.crossBoundaryStrategy =
          shaka.config.CrossBoundaryStrategy.RESET;
    }
    config.streaming.shouldFixTimestampOffset = true;
    // Tizen has long hardware pipeline that respond slowly to seeking.
    // Therefore we should not seek when we detect a stall on this platform.
    // Instead, default stallSkip to 0 to force the stall detector to pause
    // and play instead.
    config.streaming.stallSkip = 0;
    config.streaming.gapPadding = 2;
    return config;
  }

  /**
   * @override
   */
  rejectCodecs() {
    // Tizen's implementation of MSE does not work well with opus. To prevent
    // the player from trying to play opus on Tizen, we will override media
    // source to always reject opus content.
    const codecs = [];
    if (this.osMajorVersion_ !== null && this.osMajorVersion_ < 5) {
      codecs.push('opus');
    }
    return codecs;
  }

  /**
   * @override
   */
  misreportAC3UsingDrm() {
    return true;
  }

  /**
   * @override
   */
  misreportsSupportForPersistentLicenses() {
    return this.getVersion() === 3;
  }

  /**
   * @return {boolean}
   * @private
   */
  static isTizen_() {
    return navigator.userAgent.includes('Tizen');
  }
};

if (shaka.device.Tizen.isTizen_()) {
  shaka.device.DeviceFactory.registerDeviceFactory(
      () => new shaka.device.Tizen());
}