Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HLS Video stream does not play with multiple audio streams with audio group #1556

Open
lastpeony opened this issue Dec 3, 2024 · 2 comments

Comments

@lastpeony
Copy link

lastpeony commented Dec 3, 2024

I am trying to play video stream with audio track selection options in videojs HLS

#EXTM3U
#EXT-X-STREAM-INF:BANDWIDTH=1005136,RESOLUTION=1280x720,CODECS="avc1.4d001f,mp4a.40.2",AUDIO="audio-group"
video_stream.m3u8
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-group",NAME="audio1",URI="audio_stream1.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-group",NAME="audio2",URI="audio_stream2.m3u8"

When the page loads, the player defaults to playing audio1 while displaying a black screen. It successfully requests .ts files from video_stream.m3u8, but the video either doesn’t play at all or plays for a few seconds before the screen turns black. The audio always works without issues, and I can switch between audio1 and audio2 smoothly.

If I remove the audio tracks from the .m3u8 file, the video plays correctly without any interruptions. This suggests there might be a conflict between the audio tracks and the video playback.

My expectation is for the video stream to play consistently while allowing me to switch between audio tracks when needed. What could be causing this issue, and how can I ensure both video and audio function properly?

videojs 8.20.0. behaviour is same in chrome/firefox

I did few tests with HLS.js comparing it to Video.JS

In HLS.js it behaves correctly. Plays video fine and switching audio is fine. In Video.JS it plays the video for few seconds but then randomly falls to black screen.

Image

Adding react components if someone wants to make a quick test to reproduce.

HLS.js:

import React, { useEffect, useRef, useState } from 'react';
import Hls from 'hls.js';

const HLSPlayer = ({ src, width = "640px", height = "360px" }) => {
  const videoRef = useRef(null);
  const hlsRef = useRef(null);
  const [audioTracks, setAudioTracks] = useState([]);
  const [selectedTrack, setSelectedTrack] = useState(null);

  useEffect(() => {
    const video = videoRef.current;

    if (!video) return;

    if (Hls.isSupported()) {
      const hls = new Hls();
      hlsRef.current = hls;

      hls.attachMedia(video);

      hls.on(Hls.Events.MEDIA_ATTACHED, () => {
        hls.loadSource(src);
      });

      hls.on(Hls.Events.MANIFEST_PARSED, () => {
        console.log('Manifest loaded, video is ready.');
      });

      hls.on(Hls.Events.AUDIO_TRACKS_UPDATED, (_, data) => {
        setAudioTracks(data.audioTracks);
        if (data.audioTracks.length > 0) {
          setSelectedTrack(data.audioTracks[0].id); // Default to the first track
        }
      });

      hls.on(Hls.Events.ERROR, (event, data) => {
        console.error('HLS.js error:', data);
      });

      return () => {
        hls.destroy();
      };
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = src;
      console.warn(
        'Audio track switching is not supported for native HLS playback.'
      );
    } else {
      console.error('HLS is not supported in this browser.');
    }
  }, [src]);

  const handleAudioTrackChange = (trackId) => {
    if (hlsRef.current) {
      hlsRef.current.audioTrack = trackId;
      setSelectedTrack(trackId);
    }
  };

  return (
    <div>
      <video
        ref={videoRef}
        controls
        width={width}
        height={height}
        style={{ backgroundColor: 'black' }}
      />
      <div style={{ marginTop: '10px' }}>
        <label htmlFor="audioTracks">Select Audio Track: </label>
        <select
          id="audioTracks"
          value={selectedTrack || ''}
          onChange={(e) => handleAudioTrackChange(parseInt(e.target.value))}
          disabled={audioTracks.length === 0}
        >
          {audioTracks.map((track) => (
            <option key={track.id} value={track.id}>
              {track.name || `Track ${track.id + 1}`} ({track.lang || 'Unknown'})
            </option>
          ))}
        </select>
      </div>
    </div>
  );
};

export default HLSPlayer;

Video.JS. I know videojs handles audio selection but anyway

import React, { useEffect, useRef, useState } from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';

const VideoJSPlayer = ({ src, width = "640px", height = "360px" }) => {
  const videoRef = useRef(null);
  const playerRef = useRef(null);
  const [audioTracks, setAudioTracks] = useState([]);
  const [selectedTrack, setSelectedTrack] = useState(null);

  useEffect(() => {
    if (!videoRef.current) return;

    // Initialize the Video.js player
    const player = videojs(videoRef.current, {
      controls: true,
      autoplay: false,
      preload: 'auto',
      width,
      height,
    });
    playerRef.current = player;

    // Load the source
    player.src({ src, type: 'application/x-mpegURL' });

    // Handle audio track updates
    const updateAudioTracks = () => {
      const vjsAudioTracks = player.audioTracks();
      const tracks = [];
      for (let i = 0; i < vjsAudioTracks.length; i++) {
        const track = vjsAudioTracks[i];
        tracks.push({
          id: i,
          label: track.label || `Track ${i + 1}`,
          language: track.language || 'Unknown',
          enabled: track.enabled,
        });
      }
      setAudioTracks(tracks);

      // Select the first enabled track by default
      const activeTrack = tracks.find((track) => track.enabled);
      if (activeTrack) setSelectedTrack(activeTrack.id);
    };

    player.on('loadedmetadata', updateAudioTracks);
    player.on('audioTrackChanged', updateAudioTracks);

    return () => {
      if (player) {
        player.dispose();
      }
    };
  }, [src, width, height]);

  const handleAudioTrackChange = (trackId) => {
    if (!playerRef.current) return;

    const vjsAudioTracks = playerRef.current.audioTracks();
    for (let i = 0; i < vjsAudioTracks.length; i++) {
      vjsAudioTracks[i].enabled = i === trackId;
    }
    setSelectedTrack(trackId);
  };

  return (
    <div>
      <div data-vjs-player>
        <video
          ref={videoRef}
          className="video-js vjs-default-skin"
          style={{ backgroundColor: 'black' }}
        />
      </div>
      <div style={{ marginTop: '10px' }}>
        <label htmlFor="audioTracks">Select Audio Track: </label>
        <select
          id="audioTracks"
          value={selectedTrack || ''}
          onChange={(e) => handleAudioTrackChange(parseInt(e.target.value))}
          disabled={audioTracks.length === 0}
        >
          {audioTracks.map((track) => (
            <option key={track.id} value={track.id}>
              {track.label} ({track.language})
            </option>
          ))}
        </select>
      </div>
    </div>
  );
};

export default VideoJSPlayer;
@lastpeony
Copy link
Author

issue updated

@lastpeony lastpeony changed the title Video stream does not play with multiple audio tracks HLS Video stream does not play with multiple audio tracks Dec 6, 2024
@lastpeony lastpeony changed the title HLS Video stream does not play with multiple audio tracks HLS Video stream does not play with multiple audio streams with audio group Dec 6, 2024
@mister-ben
Copy link
Contributor

I couldn't replicate with a known good stream after manipulating the main manifest to match yours. Please provide a test stream.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants