Skip to content

Commit

Permalink
fix(DASH): Use labels to stitch streams across periods (#6121)
Browse files Browse the repository at this point in the history
If available streams differ only by label, PeriodCombiner does not take it into account when looking for the best candidate. Due to that streams from newly arrived periods will create new audio tracks, as existing streams match always with firstly found stream from new period.

Issue has been mitigated by recent PeriodCombiner improvements, as label has been used for hash generation. But if hash don't match i.e. due to bandwidth change, issue would still appear.
  • Loading branch information
tykus160 committed Feb 20, 2024
1 parent 58d946e commit 0de7af9
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 1 deletion.
24 changes: 23 additions & 1 deletion lib/util/periods.js
Expand Up @@ -1234,7 +1234,18 @@ shaka.util.PeriodCombiner = class {
return false;
}

// If language-based differences haven't decided this, look at roles. If
// If language-based differences haven't decided this, look at labels.
// If available options differ, look does any matches with output stream.
if (best.label !== candidate.label) {
if (outputStream.label === best.label) {
return false;
}
if (outputStream.label === candidate.label) {
return true;
}
}

// If label-based differences haven't decided this, look at roles. If
// the candidate has more roles in common with the output, upgrade to the
// candidate.
if (outputStream.roles.length) {
Expand Down Expand Up @@ -1429,6 +1440,17 @@ shaka.util.PeriodCombiner = class {
return false;
}

// If language-based differences haven't decided this, look at labels.
// If available options differ, look does any matches with output stream.
if (best.label !== candidate.label) {
if (outputStream.label === best.label) {
return false;
}
if (outputStream.label === candidate.label) {
return true;
}
}

// If the candidate has more roles in common with the output, upgrade to the
// candidate.
if (outputStream.roles.length) {
Expand Down
64 changes: 64 additions & 0 deletions test/util/periods_unit.js
Expand Up @@ -1060,6 +1060,70 @@ describe('PeriodCombiner', () => {
expect(audio2.originalId).toBe('2,4');
});

it('Matches streams with labels', async () => {
const stream1 = makeAudioStream('en', /* channels= */ 2);
stream1.originalId = '1';
stream1.bandwidth = 129597;
stream1.codecs = 'mp4a.40.2';

const stream2 = makeAudioStream('en', /* channels= */ 2);
stream2.originalId = '2';
stream2.bandwidth = 129637;
stream2.codecs = 'mp4a.40.2';
stream2.label = 'description';

const stream3 = makeAudioStream('en', /* channels= */ 2);
stream3.originalId = '3';
stream3.bandwidth = 131037;
stream3.codecs = 'mp4a.40.2';

const stream4 = makeAudioStream('en', /* channels= */ 2);
stream4.originalId = '4';
stream4.bandwidth = 131034;
stream4.codecs = 'mp4a.40.2';
stream4.label = 'description';

/** @type {!Array.<shaka.extern.Period>} */
const periods = [
{
id: '0',
videoStreams: [
makeVideoStream(1080),
],
audioStreams: [
stream1,
stream2,
],
textStreams: [],
imageStreams: [],
},
{
id: '1',
videoStreams: [
makeVideoStream(1080),
],
audioStreams: [
stream3,
stream4,
],
textStreams: [],
imageStreams: [],
},
];

await combiner.combinePeriods(periods, /* isDynamic= */ true);
const variants = combiner.getVariants();
expect(variants.length).toBe(2);
// We can use the originalId field to see what each track is composed of.
const audio1 = variants[0].audio;
expect(audio1.label).toBe(null);
expect(audio1.originalId).toBe('1,3');

const audio2 = variants[1].audio;
expect(audio2.label).toBe('description');
expect(audio2.originalId).toBe('2,4');
});

it('Matches streams with related codecs', async () => {
const stream1 = makeVideoStream(1080);
stream1.originalId = '1';
Expand Down

0 comments on commit 0de7af9

Please sign in to comment.