diff --git a/src/manifests/handlers/dash/segment.test.ts b/src/manifests/handlers/dash/segment.test.ts new file mode 100644 index 0000000..16e4256 --- /dev/null +++ b/src/manifests/handlers/dash/segment.test.ts @@ -0,0 +1,22 @@ +import dashSegmentHandler from './segment'; + +describe('dashSegmentHandler', () => { + it('handles when a representationId contains underscore', async () => { + const result = await dashSegmentHandler({ + queryStringParameters: { + url: 'https://stream.with_underscore.com/live-$RepresentationID$-$Time$.dash' + }, + path: '/segment_82008145102133_123_audio_track_0_0_nor=128000_128000', + requestContext: { + elb: { targetGroupArn: '' } + }, + isBase64Encoded: false, + httpMethod: 'GET', + body: '', + headers: {} + }); + expect(result.headers.Location).toBe( + 'https://stream.with_underscore.com/live-audio_track_0_0_nor=128000_128000-82008145102133.dash' + ); + }); +}); diff --git a/src/manifests/handlers/dash/segment.ts b/src/manifests/handlers/dash/segment.ts index 7b672a8..59b60d1 100644 --- a/src/manifests/handlers/dash/segment.ts +++ b/src/manifests/handlers/dash/segment.ts @@ -37,16 +37,25 @@ export default async function dashSegmentHandler( const urlSearchParams = new URLSearchParams(event.queryStringParameters); const pathStem = path.basename(event.path).replace('.mp4', ''); // Get the number part after "segment_" - const [, reqSegmentIndexStr, representationIdStr, bitrateStr, timeStr] = + const [, reqSegmentIndexOrTimeStr, bitrateStr, ...representationIdStrList] = pathStem.split('_'); + const representationIdStr = representationIdStrList.join('_'); // Build correct Source Segment url // segment templates may contain a width parameter "$Number%0[width]d$", and then we need to zero-pad them to that length - let segmentUrl = url - .replace(/\$Number%0(\d+)d\$/, (_, width) => - reqSegmentIndexStr.padStart(Number(width), '0') - ) - .replace('$Number$', reqSegmentIndexStr); - const reqSegmentIndexInt = parseInt(reqSegmentIndexStr); + + let segmentUrl = url; + + if (segmentUrl.includes('$Time$')) { + segmentUrl = segmentUrl.replace('$Time$', reqSegmentIndexOrTimeStr); + } else { + segmentUrl = segmentUrl + .replace(/\$Number%0(\d+)d\$/, (_, width) => + reqSegmentIndexOrTimeStr.padStart(Number(width), '0') + ) + .replace('$Number$', reqSegmentIndexOrTimeStr); + } + const reqSegmentIndexInt = parseInt(reqSegmentIndexOrTimeStr); + // Replace RepresentationID in url if present if (representationIdStr) { segmentUrl = segmentUrl.replace( @@ -54,9 +63,7 @@ export default async function dashSegmentHandler( representationIdStr ); } - if (timeStr) { - segmentUrl = segmentUrl.replace('$Time$', timeStr); - } + if (bitrateStr) { urlSearchParams.set('bitrate', bitrateStr); } diff --git a/src/manifests/utils/dashManifestUtils.ts b/src/manifests/utils/dashManifestUtils.ts index e8a2d27..8dee572 100644 --- a/src/manifests/utils/dashManifestUtils.ts +++ b/src/manifests/utils/dashManifestUtils.ts @@ -88,7 +88,7 @@ export default function (): DASHManifestTools { // Media attr const mediaUrl = segmentTemplate.$.media; - + const hasTime = mediaUrl.toString().includes('$Time$'); // Convert relative segment offsets to absolute ones // Also clones params to avoid mutating input argument const [urlQuery, changed] = convertRelativeToAbsoluteSegmentOffsets( @@ -99,12 +99,14 @@ export default function (): DASHManifestTools { ); if (changed) staticQueryUrl = new URLSearchParams(urlQuery); - - segmentTemplate.$.media = proxyPathBuilder( + const proxy = proxyPathBuilder( mediaUrl.match(/^http/) ? mediaUrl : baseUrl + mediaUrl, urlQuery, - 'proxy-segment/segment_$Number$_$RepresentationID$_$Bandwidth$_$Time$' + hasTime + ? 'proxy-segment/segment_$Time$_$Bandwidth$_$RepresentationID$' + : 'proxy-segment/segment_$Number$_$Bandwidth$_$RepresentationID$' ); + segmentTemplate.$.media = proxy; // Initialization attr. const initUrl = segmentTemplate.$.initialization; if (!initUrl.match(/^http/)) { @@ -128,7 +130,7 @@ export default function (): DASHManifestTools { representation.SegmentTemplate.map((segmentTemplate) => { // Media attr. const mediaUrl = segmentTemplate.$.media; - + const hasTime = mediaUrl.toString().includes('$Time$'); // Convert relative segment offsets to absolute ones // Also clones params to avoid mutating input argument const [urlQuery, changed] = @@ -145,11 +147,14 @@ export default function (): DASHManifestTools { urlQuery.set('bitrate', representation.$.bandwidth); } - segmentTemplate.$.media = proxyPathBuilder( + const proxy = proxyPathBuilder( mediaUrl, urlQuery, - 'proxy-segment/segment_$Number$.mp4' + hasTime + ? 'proxy-segment/segment_$Time$.mp4' + : 'proxy-segment/segment_$Number$.mp4' ); + segmentTemplate.$.media = proxy; // Initialization attr. const masterDashUrl = originalUrlQuery.get('url'); const initUrl = segmentTemplate.$.initialization; diff --git a/src/manifests/utils/hlsManifestUtils.ts b/src/manifests/utils/hlsManifestUtils.ts index ff7e0de..d46083f 100644 --- a/src/manifests/utils/hlsManifestUtils.ts +++ b/src/manifests/utils/hlsManifestUtils.ts @@ -100,10 +100,12 @@ export default function (): HLSManifestTools { if (stateKey) { urlQuery.set('state', stateKey); } - streamItem.set( - 'uri', - proxyPathBuilder(currentUri, urlQuery, 'proxy-media.m3u8') + const proxy = proxyPathBuilder( + currentUri, + urlQuery, + 'proxy-media.m3u8' ); + streamItem.set('uri', proxy); return streamItem; }); @@ -119,10 +121,12 @@ export default function (): HLSManifestTools { if (stateKey) { urlQuery.set('state', stateKey); } - mediaItem.set( - 'uri', - proxyPathBuilder(currentUri, originalUrlQuery, 'proxy-media.m3u8') + const proxy = proxyPathBuilder( + currentUri, + urlQuery, + 'proxy-media.m3u8' ); + mediaItem.set('uri', proxy); return mediaItem; }); @@ -159,14 +163,12 @@ export default function (): HLSManifestTools { } const params = segmentUrlParamString(sourceSegURL, corruption); - item.set( - 'uri', - proxyPathBuilder( - item.get('uri'), - new URLSearchParams(params), - '../../segments/proxy-segment' - ) + const proxy = proxyPathBuilder( + item.get('uri'), + new URLSearchParams(params), + '../../segments/proxy-segment' ); + item.set('uri', proxy); } return m3u.toString(); } diff --git a/src/shared/utils.ts b/src/shared/utils.ts index 7b12dba..923d8ef 100644 --- a/src/shared/utils.ts +++ b/src/shared/utils.ts @@ -187,8 +187,11 @@ type ProxyBasenames = | 'proxy-media.m3u8' | '../../segments/proxy-segment' | 'proxy-segment/segment_$Number$.mp4' - | 'proxy-segment/segment_$Number$_$RepresentationID$_$Bandwidth$' - | 'proxy-segment/segment_$Number$_$RepresentationID$_$Bandwidth$_$Time$'; + | 'proxy-segment/segment_$Time$.mp4' + | 'proxy-segment/segment_$Number$_$RepresentationID$' + | 'proxy-segment/segment_$Time$_$RepresentationID$' + | 'proxy-segment/segment_$Number$_$Bandwidth$_$RepresentationID$' + | 'proxy-segment/segment_$Time$_$Bandwidth$_$RepresentationID$'; /** * Adjust paths based on directory navigation diff --git a/src/testvectors/dash/dash1_compressed/proxy-manifest.xml b/src/testvectors/dash/dash1_compressed/proxy-manifest.xml index 0ff7c44..632f172 100644 --- a/src/testvectors/dash/dash1_compressed/proxy-manifest.xml +++ b/src/testvectors/dash/dash1_compressed/proxy-manifest.xml @@ -34,7 +34,7 @@ + media="proxy-segment/segment_$Time$_$Bandwidth$_$RepresentationID$?url=https%3A%2F%2Fmock.mock.com%2Fstream%2Frelative_base%2Faudiotrack%2F%24RepresentationID%24%2F%24Time%24.m4s&statusCode=%5B%7Bi%3A0%2Ccode%3A404%7D%2C%7Bi%3A2%2Ccode%3A401%7D%5D&timeout=%5B%7Bi%3A3%7D%5D&delay=%5B%7Bi%3A2%2Cms%3A2000%7D%5D"> @@ -62,7 +62,7 @@ + media="proxy-segment/segment_$Time$_$Bandwidth$_$RepresentationID$?url=https%3A%2F%2Fmock.mock.com%2Fstream%2Frelative_base%2Fvideotrack%2F%24RepresentationID%24%2F%24Time%24.m4s&statusCode=%5B%7Bi%3A0%2Ccode%3A404%7D%2C%7Bi%3A2%2Ccode%3A401%7D%5D&timeout=%5B%7Bi%3A3%7D%5D&delay=%5B%7Bi%3A2%2Cms%3A2000%7D%5D"> diff --git a/src/testvectors/dash/dash_period_baseurl/proxy-manifest.xml b/src/testvectors/dash/dash_period_baseurl/proxy-manifest.xml index 23f08e6..c8081de 100644 --- a/src/testvectors/dash/dash_period_baseurl/proxy-manifest.xml +++ b/src/testvectors/dash/dash_period_baseurl/proxy-manifest.xml @@ -1,7 +1,7 @@ - + @@ -17,7 +17,7 @@ - +