tomexsans / videojs-quality-selector-hls

MIT License
4 stars 2 forks source link

Safari Compatibility on macOS? #3

Open chrisspiegl opened 1 year ago

chrisspiegl commented 1 year ago

Just tried this (as mentioned before). Found this potential bug when opening a site with the video player in Safari on macOS.

Xnapper-2023-09-10-17 21 16

Basically, in a Chromium based browser it works. But in Safari it does not show?

tomexsans commented 1 year ago

Hello thanks for submitting.

I'll try to look into it, i currently have no access to Any MAC.

I'm only relying on UI Tests which turn out did not catch.

Could you add here the Version of Safari and MacOS you are using?

chrisspiegl commented 1 year ago

Sure thing. I understand. I will be available to test any code commits if you have any.

Safari Version: Version 17.0 (19616.1.27.111.16) macOS: 14.0 Beta (23A5337a) (Sanoma)

cbarburescu commented 1 year ago

Hi,

For me, it works on Safari - MacOS Ventura 13.4 - if I set overrideNative to true from videojs' options: https://github.com/videojs/http-streaming#overridenative (without it, it looks the same as in your screenshot @chrisspiegl)

However, I have the same problem on multiple browsers (Chrome, Firefox, Safari) on iPhone (iOS 16.6.1) and the setting above does not fix it.

Does anyone have any ideas?

Thank you!

chrisspiegl commented 1 year ago

The overrideNative: true does appear to change a lot and is very useful in this scenario. I also have found this.

And I was able to get it to work on Desktop (Chrome, Safari), Android, Safari, and iPhone (when using the appropriate plugins and setting the overrideNative: true).

However, to be honest, at the end of the day, I actually chose to keep overrideNative but disable the quality selector for now and just rely on the system to give me the best quality that the internet connection can handle.

cbarburescu commented 1 year ago

Thanks for responding @chrisspiegl. Do you remember what you settings/ plugins did you use for iPhone in order to have a select quality button?

chrisspiegl commented 1 year ago

Hi @cbarburescu ,

I put together a bit of a gist about the code that I am using. It is a bit of an excerpt though.

Obviously, the css also needs to be included and the whole vue component wrapped around it all. That is not included in the code below and I will not have time to build a CodeSandbox anytime soon 🙈.

But I hope this helps to document what works and maybe find more settings to add / change.

Devices to be tested:

Code Excerpt to run Video.JS Player

import type Player from 'video.js/dist/types/player'
import type { QualityLevel } from 'videojs-contrib-quality-levels'
import qualitySelector from 'videojs-quality-selector-hls'
import videojs from 'video.js'
import consola from 'consola'

interface CustomPlayer extends Player {
  [key: string]: any
}

const logger = consola.withTag('VideoFileEmbedPlayer')

const player = ref<CustomPlayer | undefined>(undefined)
const videoElement = ref()

onMounted(async () => {
  if (!videojs.getPlugin('qualitySelector')) {
    logger.log('🚧 register qualitySelector')
    videojs.registerPlugin('qualitySelector', qualitySelector)
  }

  player.value = videojs(videoElement.value, {
    qualityLevels: true,
    aspectRatio: '16:9',
    autoplay: false,
    preload: false,
    fluid: true,
    controlBar: {
      playToggle: {
        replay: false,
      },
      volumePanel: {
        inline: true,
      },
      currentTimeDisplay: true,
      timeDivider: true,
      durationDisplay: true,
      progressControl: true,
      remainingTimeDisplay: false,
      playbackRateMenuButton: true,
      qualitySelector: true, // Quality selector should show up
      fullscreenToggle: true,

      liveDisplay: false,
      customControlSpacer: false,
      chaptersButton: false,
      descriptionsButton: false,
      subtitlesButton: false,
      captionsButton: false,
      subsCapsButton: false,
      audioTrackButton: false,
      pictureInPictureToggle: true,
    },
    controls: true,
    playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 2, 2.5, 3],
    loop: false,
    volume: 1,
    html5: {
      // VHS has to be present for Safari (macOS) to pic up the video stream.
      vhs: {
        useDevicePixelRatio: true,
        // NOTE: The NetworkInformationAPI appears to make things higher quality on Safari.
        useNetworkInformationApi: true,
        // NOTE: this overrides the native HLS support for Safari which in turn activates the ability to more finely control the resolutions available and logging to console.
        // This can be deactivated and it will use the quality level appropriate for the internet speed, screen resolution, pixel density.
        overrideNative: true, // this makes it possible for videojs to overtake the quality selection (could use overrideNative: !videojs.browser.IS_SAFARI)
      },
      hls: {
        useDevicePixelRatio: true,
        // NOTE: The NetworkInformationAPI appears to make things higher quality on Safari.
        useNetworkInformationApi: true,
        // NOTE: this overrides the native HLS support for Safari which in turn activates the ability to more finely control the resolutions available and logging to console.
        // This can be deactivated and it will use the quality level appropriate for the internet speed, screen resolution, pixel density.
        overrideNative: true, // this makes it possible for videojs to overtake the quality selection (could use overrideNative: !videojs.browser.IS_SAFARI)
      },
      // These two also did something which I don't remember, I think Android devices had problems when this was set to true.
      nativeAudioTracks: false,
      nativeVideoTracks: false,
      // nativeTextTracks: false,
    },
    sources: [{
      src: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8',
      type: 'application/x-mpegURL',
    }],
  }, async () => {
    if (!player.value) {
      logger.error('Player should have been ready but wasn\'t!')
      return
    }
    logger.log('Video Player Ready')

    // Activate Quality Selector
    player.value.qualitySelectorHls({
      displayCurrentQuality: true, // makes it display the quality level in text form vs. false => displays a icon.
      // placementIndex: 2,
      vjsIconClass: 'vjs-icon-hd',
    })

    // Add some logging to see if the video.js player even has information about quality levels
    const qualityLevels = player.value.qualityLevels()
    player.value.qualityLevels().on('addqualitylevel', (data: { qualityLevel: QualityLevel }) => {
      logger.log('📺 M3U8 playlist is loaded!', `${data.qualityLevel.height}p`, `${data.qualityLevel.bitrate / 1000} kB/s`)
    })
    player.value.qualityLevels().on('change', () => {
      logger.log('📺 Quality Level changed! New level:', qualityLevels[qualityLevels.selectedIndex])
    })
  })
})
minna-no-jihye commented 9 months ago

Hi Mr @chrisspiegl Have you conducted the test in the 'Fairplay DRM environment' as well? When overrideNative is set to true, this setting cannot be used because the webkitneedkey is not triggered.

chrisspiegl commented 9 months ago

@minna-no-jihye: I have not worked with any DRM type things. So, so I do not know anything about this.