hls.js icon indicating copy to clipboard operation
hls.js copied to clipboard

Question about playlists alignment based on discontinuity counter

Open hongfeih-es opened this issue 4 years ago • 4 comments

What do you want to do with Hls.js?

We noticed sometimes playback will be stalled when play ssai stream (with several discontinuities), and it normally happened when switching levels during ad breaks.

What have you tried so far?

Checked the code, findDiscontinuousReferenceFrag try to find first segment with same cc of of the first frag of the new level: https://github.com/video-dev/hls.js/blob/feab619b6973f7df602b49887c07c741bdbecbd0/src/utils/discontinuities.ts#L39-L60 But I think it's wrong. For example,

it('finds the first fragment in an array which matches the CC of the first fragment in another array', function () {
    const mockReferenceFrags = [
      {
        start: 20,
        startPTS: 20,
        endPTS: 24,
        duration: 4,
        cc: 0,
      },
      {
        start: 24,
        startPTS: 24,
        endPTS: 28,
        duration: 4,
        cc: 0,
      },
      {
        start: 28,
        startPTS: 28,
        endPTS: 32,
        duration: 4,
        cc: 1,
      }
    ];
    const mockFrags = [
      {
        start: 0,
        startPTS: 0,
        endPTS: 4,
        duration: 4,
        cc: 0,
      },
      {
        start: 4,
        startPTS: 4,
        endPTS: 8,
        duration: 4,
        cc: 1,
      },
      {
        start: 8,
        startPTS: 8,
        endPTS: 16,
        duration: 8,
        cc: 1,
      },
    ];
    const prevDetails = {
      fragments: mockReferenceFrags,
    };
    const curDetails = {
      fragments: mockFrags,
    };
    const actual = findDiscontinuousReferenceFrag(prevDetails, curDetails);
    expect(actual.start).to.equal(24);
  });

findDiscontinuousReferenceFrag will find first segment of mockReferenceFrags, which is start: 20,, and new playlist will be adjusted to :

{
        start: 20,
        startPTS: 20,
        endPTS: 24,
        duration: 4,
        cc: 0,
      },
      {
        start: 24,
        startPTS: 24,
        endPTS: 28,
        duration: 4,
        cc: 1,
      },
      {
        start: 28,
        startPTS: 28,
        endPTS: 36,
        duration: 8,
        cc: 1,
      },

which is wrong, it should be:

{
        start: 24,
        startPTS: 24,
        endPTS: 28,
        duration: 4,
        cc: 0,
      },
      {
        start: 28,
        startPTS: 28,
        endPTS: 32,
        duration: 4,
        cc: 1,
      },
      {
        start: 32,
        startPTS: 32,
        endPTS: 40,
        duration: 8,
        cc: 1,
      },

I think start segment of next break (cc+1) should be used to align playlists between levels, like:

export function findDiscontinuousReferenceFrag2(
  prevDetails: LevelDetails,
  curDetails: LevelDetails
) {
  const prevFrags = prevDetails.fragments;
  const curFrags = curDetails.fragments;

  if (!curFrags.length || !prevFrags.length) {
    logger.log('No fragments to align');
    return;
  }

  const prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc + 1);
  const curStartFrag = findFirstFragWithCC(curFrags, curFrags[0].cc + 1);

  if (!prevStartFrag || (prevStartFrag && !prevStartFrag.startPTS)) {
    logger.log('No frag in previous level to align on');
    return;
  }

  if (curStartFrag?.start) {
    return {
      start:  prevStartFrag.start - curStartFrag.start
    };
  }
}

Any comment? Thanks.

hongfeih-es avatar Nov 08 '21 07:11 hongfeih-es

I think the real problem is in alignDiscontinuities which uses findDiscontinuousReferenceFrag.

alignDiscontinuities needs a sliding time for the difference between two matching fragments. You are correct in that matching the first segment of a playlist to the first segment with the same discontinuity in another will not produce the correct offset if the first segment in the former playlist is not also the first in the discontinuity sequence.

This method should find any match between two fragments that start the same discontinuity sequence. It would also make sense for it to return a number when it finds a match, or null when unsuccessful.

robwalch avatar Jan 04 '23 18:01 robwalch

Changed to enhancement. Will escalate to Bug if given steps to repro.

robwalch avatar Jan 04 '23 18:01 robwalch

It looks like we made improvements to alignMediaPlaylistByPDT in #4984 that are relevant to this issue. When aligning on PDT we actually find the best DISCONTINUITY for the job:

https://github.com/video-dev/hls.js/blob/7c7c39fc031767ffcc2cee83775a67f753c4a3a1/src/utils/discontinuities.ts#L170-L179

But we don't do the same in alignDiscontinuities - we only use the first which may not be present in both. As long as we use a disco sequence present in both, we should get the same alignment. If the same discontinuity sequence has different durations across playlists that is an issue with playlist authoring.

robwalch avatar Sep 30 '23 03:09 robwalch

@hongfeih-es would you bring the targetCC logic from alignMediaPlaylistByPDT into alignDiscontinuities and resolve this issue?

robwalch avatar Sep 30 '23 03:09 robwalch