-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(dash): Enable Stateful Mode and rsq Corruptions for DASH
- Imported STATEFUL and newState utilities for DASH (segment.ts, dashManifestUtils.ts and dashManifestUtils.test.ts) to support stateful mode. - Ensured rsq corruptions are possible for DASH when the proxy is running in stateful mode, similar to how rsq corruptions are enabled for HLS. This update allows for the use of relative sequence numbers in DASH manifests, aligning the functionality with HLS. The DASH proxy now properly handles stateful operations and rsq parameters, enabling more flexible stream corruptions.
- Loading branch information
Showing
93 changed files
with
15,168 additions
and
7 deletions.
There are no files selected for viewing
112 changes: 112 additions & 0 deletions
112
.history/src/manifests/handlers/dash/segment_20240613120409.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { ALBEvent, ALBResult } from 'aws-lambda'; | ||
import { ServiceError } from '../../../shared/types'; | ||
import { | ||
composeALBEvent, | ||
generateErrorResponse, | ||
isValidUrl, | ||
segmentUrlParamString | ||
} from '../../../shared/utils'; | ||
import delaySCC from '../../utils/corruptions/delay'; | ||
import statusCodeSCC from '../../utils/corruptions/statusCode'; | ||
import timeoutSCC from '../../utils/corruptions/timeout'; | ||
import throttleSCC from '../../utils/corruptions/throttle'; | ||
import path from 'path'; | ||
import dashManifestUtils from '../../utils/dashManifestUtils'; | ||
import { corruptorConfigUtils } from '../../utils/configs'; | ||
import segmentHandler from '../../../segments/handlers/segment'; | ||
|
||
export default async function dashSegmentHandler( | ||
event: ALBEvent | ||
): Promise<ALBResult> { | ||
/** | ||
* #1 - const originalUrl = req.body.query("url"); | ||
* #2 - const originalManifest = await fetch(originalUrl); | ||
* #3 - build proxy version and response with the correct header | ||
*/ | ||
const { url } = event.queryStringParameters; | ||
|
||
if (!url || !isValidUrl(url)) { | ||
const errorRes: ServiceError = { | ||
status: 400, | ||
message: "Missing a valid 'url' query parameter" | ||
}; | ||
return generateErrorResponse(errorRes); | ||
} | ||
|
||
try { | ||
const urlSearchParams = new URLSearchParams(event.queryStringParameters); | ||
const pathStem = path.basename(event.path).replace('.mp4', ''); | ||
// Get the number part after "segment_" | ||
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; | ||
|
||
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( | ||
'$RepresentationID$', | ||
representationIdStr | ||
); | ||
} | ||
|
||
if (bitrateStr) { | ||
urlSearchParams.set('bitrate', bitrateStr); | ||
} | ||
// Break down Corruption Objects | ||
// Send source URL with a corruption json (if it is appropriate) to segmentHandler... | ||
const configUtils = corruptorConfigUtils(urlSearchParams); | ||
configUtils | ||
.register(delaySCC) | ||
.register(statusCodeSCC) | ||
.register(timeoutSCC) | ||
.register(throttleSCC); | ||
const [error, allMutations] = configUtils.getAllManifestConfigs( | ||
reqSegmentIndexInt, | ||
true | ||
); | ||
if (error) { | ||
return generateErrorResponse(error); | ||
} | ||
const dashUtils = dashManifestUtils(); | ||
const mergedMaps = dashUtils.utils.mergeMap( | ||
reqSegmentIndexInt, | ||
allMutations | ||
); | ||
const segUrl = new URL(segmentUrl); | ||
const cleanSegUrl = segUrl.origin + segUrl.pathname; | ||
let eventParamsString: string; | ||
if (mergedMaps.size < 1) { | ||
eventParamsString = `url=${cleanSegUrl}`; | ||
} else { | ||
eventParamsString = segmentUrlParamString(cleanSegUrl, mergedMaps); | ||
} | ||
const albEvent = await composeALBEvent( | ||
event.httpMethod, | ||
`${event.path}?${eventParamsString}`, | ||
event.headers | ||
); | ||
return await segmentHandler(albEvent); | ||
} catch (err) { | ||
const errorRes: ServiceError = { | ||
status: 500, | ||
message: err.message ? err.message : err | ||
}; | ||
//for unexpected errors | ||
return generateErrorResponse(errorRes); | ||
} | ||
} |
113 changes: 113 additions & 0 deletions
113
.history/src/manifests/handlers/dash/segment_20240626093448.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { ALBEvent, ALBResult } from 'aws-lambda'; | ||
import { ServiceError } from '../../../shared/types'; | ||
import { | ||
composeALBEvent, | ||
generateErrorResponse, | ||
isValidUrl, | ||
segmentUrlParamStringSTATEFUL, | ||
newState | ||
} from '../../../shared/utils'; | ||
import delaySCC from '../../utils/corruptions/delay'; | ||
import statusCodeSCC from '../../utils/corruptions/statusCode'; | ||
import timeoutSCC from '../../utils/corruptions/timeout'; | ||
import throttleSCC from '../../utils/corruptions/throttle'; | ||
import path from 'path'; | ||
import dashManifestUtils from '../../utils/dashManifestUtils'; | ||
import { corruptorConfigUtils } from '../../utils/configs'; | ||
import segmentHandler from '../../../segments/handlers/segment'; | ||
|
||
export default async function dashSegmentHandler( | ||
event: ALBEvent | ||
): Promise<ALBResult> { | ||
/** | ||
* #1 - const originalUrl = req.body.query("url"); | ||
* #2 - const originalManifest = await fetch(originalUrl); | ||
* #3 - build proxy version and response with the correct header | ||
*/ | ||
const { url } = event.queryStringParameters; | ||
|
||
if (!url || !isValidUrl(url)) { | ||
const errorRes: ServiceError = { | ||
status: 400, | ||
message: "Missing a valid 'url' query parameter" | ||
}; | ||
return generateErrorResponse(errorRes); | ||
} | ||
|
||
try { | ||
const urlSearchParams = new URLSearchParams(event.queryStringParameters); | ||
const pathStem = path.basename(event.path).replace('.mp4', ''); | ||
// Get the number part after "segment_" | ||
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; | ||
|
||
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( | ||
'$RepresentationID$', | ||
representationIdStr | ||
); | ||
} | ||
|
||
if (bitrateStr) { | ||
urlSearchParams.set('bitrate', bitrateStr); | ||
} | ||
// Break down Corruption Objects | ||
// Send source URL with a corruption json (if it is appropriate) to segmentHandler... | ||
const configUtils = corruptorConfigUtils(urlSearchParams); | ||
configUtils | ||
.register(delaySCC) | ||
.register(statusCodeSCC) | ||
.register(timeoutSCC) | ||
.register(throttleSCC); | ||
const [error, allMutations] = configUtils.getAllManifestConfigs( | ||
reqSegmentIndexInt, | ||
true | ||
); | ||
if (error) { | ||
return generateErrorResponse(error); | ||
} | ||
const dashUtils = dashManifestUtils(); | ||
const mergedMaps = dashUtils.utils.mergeMap( | ||
reqSegmentIndexInt, | ||
allMutations | ||
); | ||
const segUrl = new URL(segmentUrl); | ||
const cleanSegUrl = segUrl.origin + segUrl.pathname; | ||
let eventParamsString: string; | ||
if (mergedMaps.size < 1) { | ||
eventParamsString = `url=${cleanSegUrl}`; | ||
} else { | ||
eventParamsString = segmentUrlParamString(cleanSegUrl, mergedMaps); | ||
} | ||
const albEvent = await composeALBEvent( | ||
event.httpMethod, | ||
`${event.path}?${eventParamsString}`, | ||
event.headers | ||
); | ||
return await segmentHandler(albEvent); | ||
} catch (err) { | ||
const errorRes: ServiceError = { | ||
status: 500, | ||
message: err.message ? err.message : err | ||
}; | ||
//for unexpected errors | ||
return generateErrorResponse(errorRes); | ||
} | ||
} |
114 changes: 114 additions & 0 deletions
114
.history/src/manifests/handlers/dash/segment_20240626093451.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { ALBEvent, ALBResult } from 'aws-lambda'; | ||
import { ServiceError } from '../../../shared/types'; | ||
import { | ||
composeALBEvent, | ||
generateErrorResponse, | ||
isValidUrl, | ||
segmentUrlParamString, | ||
STATEFUL, | ||
newState | ||
} from '../../../shared/utils'; | ||
import delaySCC from '../../utils/corruptions/delay'; | ||
import statusCodeSCC from '../../utils/corruptions/statusCode'; | ||
import timeoutSCC from '../../utils/corruptions/timeout'; | ||
import throttleSCC from '../../utils/corruptions/throttle'; | ||
import path from 'path'; | ||
import dashManifestUtils from '../../utils/dashManifestUtils'; | ||
import { corruptorConfigUtils } from '../../utils/configs'; | ||
import segmentHandler from '../../../segments/handlers/segment'; | ||
|
||
export default async function dashSegmentHandler( | ||
event: ALBEvent | ||
): Promise<ALBResult> { | ||
/** | ||
* #1 - const originalUrl = req.body.query("url"); | ||
* #2 - const originalManifest = await fetch(originalUrl); | ||
* #3 - build proxy version and response with the correct header | ||
*/ | ||
const { url } = event.queryStringParameters; | ||
|
||
if (!url || !isValidUrl(url)) { | ||
const errorRes: ServiceError = { | ||
status: 400, | ||
message: "Missing a valid 'url' query parameter" | ||
}; | ||
return generateErrorResponse(errorRes); | ||
} | ||
|
||
try { | ||
const urlSearchParams = new URLSearchParams(event.queryStringParameters); | ||
const pathStem = path.basename(event.path).replace('.mp4', ''); | ||
// Get the number part after "segment_" | ||
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; | ||
|
||
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( | ||
'$RepresentationID$', | ||
representationIdStr | ||
); | ||
} | ||
|
||
if (bitrateStr) { | ||
urlSearchParams.set('bitrate', bitrateStr); | ||
} | ||
// Break down Corruption Objects | ||
// Send source URL with a corruption json (if it is appropriate) to segmentHandler... | ||
const configUtils = corruptorConfigUtils(urlSearchParams); | ||
configUtils | ||
.register(delaySCC) | ||
.register(statusCodeSCC) | ||
.register(timeoutSCC) | ||
.register(throttleSCC); | ||
const [error, allMutations] = configUtils.getAllManifestConfigs( | ||
reqSegmentIndexInt, | ||
true | ||
); | ||
if (error) { | ||
return generateErrorResponse(error); | ||
} | ||
const dashUtils = dashManifestUtils(); | ||
const mergedMaps = dashUtils.utils.mergeMap( | ||
reqSegmentIndexInt, | ||
allMutations | ||
); | ||
const segUrl = new URL(segmentUrl); | ||
const cleanSegUrl = segUrl.origin + segUrl.pathname; | ||
let eventParamsString: string; | ||
if (mergedMaps.size < 1) { | ||
eventParamsString = `url=${cleanSegUrl}`; | ||
} else { | ||
eventParamsString = segmentUrlParamString(cleanSegUrl, mergedMaps); | ||
} | ||
const albEvent = await composeALBEvent( | ||
event.httpMethod, | ||
`${event.path}?${eventParamsString}`, | ||
event.headers | ||
); | ||
return await segmentHandler(albEvent); | ||
} catch (err) { | ||
const errorRes: ServiceError = { | ||
status: 500, | ||
message: err.message ? err.message : err | ||
}; | ||
//for unexpected errors | ||
return generateErrorResponse(errorRes); | ||
} | ||
} |
Oops, something went wrong.