video-dev / hls.js

HLS.js is a JavaScript library that plays HLS in browsers with support for MSE.
https://hlsjs.video-dev.org/demo
Other
14.42k stars 2.55k forks source link

fragParsingError Demux #6379

Open gabinfinity opened 2 months ago

gabinfinity commented 2 months ago

What version of Hls.js are you using?

1.5.8

What browser (including version) are you using?

Chrome lastest version

What OS (including version) are you using?

Windows 10

Test stream

No response

Configuration

import { useVideoSettingsStore } from "@/features/player/store/videoSettings.slice";
import { ISource, ISubtitle } from "@/features/player/types";
import { removeDuplicateObjects, sortVideoQualities } from "@/utils/helper";
import Hls, { Level } from "hls.js";
import { useRouter } from "next/router";
import React, { useEffect } from "react";
import { useContextSelector } from "use-context-selector";
import { GlobalStateContext } from "../../contexts/GlobalStateContext";
import useVideoSourcesStore from "../../store/sources.slice";
import { useVideoStatusStore } from "../../store/videoStatus.slice";
import { shouldPlayHls, sortDefaultLanguage } from "../../utils/helper";

const convertLevelToQuality = (level: Level): ISource => {
  return { quality: `${level.height}p`.replace("2160p", "4K"), url: level.uri, type: "m3u8" };
};

const canPlayHlsNatively = (video: HTMLVideoElement): boolean => {
  if (Hls.isSupported()) return false; // no need to play natively
  return !!video.canPlayType("application/vnd.apple.mpegurl");
}

function VideoHls() {
  const router = useRouter();
  const locale = router?.locale || "en";
  const hls = React.useRef<Hls | null>(null);
  const setPaused = useVideoStatusStore((s) => s.setPaused);
  const { playerRef, setGlobalState } = useContextSelector(GlobalStateContext, (s) => ({
    playerRef: s.playerRef,
    setGlobalState: s.setGlobalState
  }));
  const { setHandleChangeQuality, currentSource, setCurrentSource, setCurrentQuality } =
    useVideoSourcesStore((s) => ({
      setHandleChangeQuality: s.setHandleChangeQuality,
      setCurrentSource: s.setCurrentSource,
      currentSource: s.currentSource,
      setCurrentQuality: s.setCurrentQuality
    }));
  const { currentAudio, audios, setState } = useVideoSettingsStore((s) => ({
    currentAudio: s.currentAudio,
    audios: s.audios,
    setState: s.setState
  }));
  useEffect(() => {
    const _handleChangeQuality = (choosenQuality: ISource) => {
      try {
        if (shouldPlayHls(choosenQuality)) {
          if (
            !playerRef.current ||
            canPlayHlsNatively(playerRef.current) ||
            !hls.current
          ) {
            setCurrentQuality(choosenQuality);
            setCurrentSource(choosenQuality);
            return; // nothing to change
          }
          if (hls.current.levels.length > 1) {
            const levelIndex = hls.current.levels.findIndex((v) => v.uri === choosenQuality.url);
            if (levelIndex !== -1) {
              hls.current.currentLevel = levelIndex;
              hls.current.loadLevel = levelIndex;
              const level = hls.current.levels[levelIndex];
              if (!level?.height) return;
              setCurrentQuality(convertLevelToQuality(level));
            }
          } else {
            hls.current.currentLevel = -1;
            hls.current.loadLevel = -1;
            setCurrentQuality(choosenQuality);
            setCurrentSource(choosenQuality);
          }
        } else {
          if (hls.current !== null) {
            hls.current.destroy();
            hls.current = null;
          }
          setCurrentQuality(choosenQuality);
          setCurrentSource(choosenQuality);
        }
      } catch (error: any) {
        console.log("error: ", error?.message);
      }
    };
    setHandleChangeQuality(_handleChangeQuality);
  }, [playerRef, setHandleChangeQuality, setCurrentQuality, setCurrentSource]);
  useEffect(() => {
    if (!playerRef.current || !currentSource) return;
    if (canPlayHlsNatively(playerRef.current)) {
      // HLS supported natively by browser
      playerRef.current.src = currentSource?.url;
    } else {
      if (Hls.isSupported()) {
        const initPlayer = async () => {
          if (hls.current !== null) hls.current.destroy();
          let _hls: Hls;
          _hls = new Hls({ enableWorker: false });
          if (playerRef.current !== null) _hls.attachMedia(playerRef.current);
          _hls.loadSource(currentSource?.url);
          _hls.on(Hls.Events.MANIFEST_PARSED, async () => {
            playerRef.current?.play().catch(() => {
              console.warn("User must interact before playing the video.");
              setPaused(true);
            });
            if (!_hls) return;
            if (_hls.levels?.length) {
              const levels: ISource[] = _hls.levels
                .filter((level) => level.height)
                .sort((a, b) => b.height - a.height)
                .map((level) => convertLevelToQuality(level));
              setGlobalState((prev) => {
                return {
                  ...prev,
                  sources: sortVideoQualities(
                    removeDuplicateObjects([...prev.sources, ...levels], "url") as ISource[]
                  )
                };
              });
            }
          });
          _hls.on(Hls.Events.LEVEL_SWITCHED, () => {
            if (!_hls) return;
            const level = _hls.levels[_hls.currentLevel];
            if (!level.height) return;
            const quality: ISource = convertLevelToQuality(level);
            setCurrentQuality(quality);
          });
          _hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, async (_, event) => {
            const { localeToLanguage } = await import("@/constants");
            const modifiedAudios = sortDefaultLanguage(
              event.audioTracks.map((track, index) => ({
                lang: `${track.lang}${index}`,
                language: track.name
              })) as ISubtitle[],
              locale,
              localeToLanguage[locale as keyof typeof localeToLanguage]
            );
            setState({
              audios: modifiedAudios,
              // @ts-ignore
              currentAudio: modifiedAudios[_hls.audioTrack >= 0 ? _hls.audioTrack : 0]?.lang
            });
          });
          _hls.on(Hls.Events.ERROR, function (_event, data) {
            console.error("Hls error: ", _event, data);
            if (data.fatal) {
              switch (data.type) {
                case Hls.ErrorTypes.NETWORK_ERROR:
                  _hls.startLoad();
                  break;
                case Hls.ErrorTypes.MEDIA_ERROR:
                  _hls.recoverMediaError();
                  break;
                default:
                  break;
              }
            }
          });
          hls.current = _hls;
        };
        initPlayer();
      } else {
        console.log("HLS is not supported on this device/browser");
      }
    }
    return () => {
      if (hls.current !== null) {
        hls.current.destroy();
        hls.current = null;
      }
    };
  }, [playerRef, currentSource, setPaused, locale, setState, setCurrentQuality, setGlobalState]);
  useEffect(() => {
    if (!currentAudio || !Boolean(audios?.length)) return;
    let currentAudioTrack = audios?.findIndex((audio) => audio.lang === currentAudio);
    if (currentAudioTrack === undefined || currentAudioTrack === -1) return;
    if (!hls?.current) return;
    hls.current.audioTrack = currentAudioTrack;
  }, [audios, currentAudio]);
  return null;
}

export default VideoHls;

Additional player setup steps

No response

Checklist

Steps to reproduce

  1. Go to https://www.braflix.video/movie/438631?admin=1z4z1p1p1
  2. Open the player and click in CLOUD icon
  3. Select Golf option

Expected behaviour

HLS should play fine

What actually happened?

HLS doesnt play and i got console error spam

Console output

861.31312f4eb2a7d4c4.js:1 Hls error:  hlsError 
{type: 'mediaError', details: 'fragParsingError', fatal: false, error: Error: Found 571 TS packet/s that do not start with 0x47
    at i2.demux (https://www.braflix.video…, reason: 'Found 571 TS packet/s that do not start with 0x47', …}

Chrome media internals output

Hi, im having this issue on my website

861.31312f4eb2a7d4c4.js:1 Hls error:  hlsError 
{type: 'mediaError', details: 'fragParsingError', fatal: false, error: Error: Found 571 TS packet/s that do not start with 0x47
    at i2.demux (https://www.braflix.video…, reason: 'Found 571 TS packet/s that do not start with 0x47', …}
robwalch commented 2 months ago

This is behaving as expected. The logs are clear about what the media error is - the segments are invalid and the error is fatal. The spamming is a result of calling recoverMediaError which only results in the error sequence repeating itself:

 case Hls.ErrorTypes.MEDIA_ERROR:
    _hls.recoverMediaError();