awscdk-construct-live-channel-from-mp4-file
CDK Construct for deploying a live video channel using AWS Elemental MediaLive and MediaPackage
- The input is MP4 files
- The output is MediaPackage V1 and V2 endpoints
- You can specify encoding/packaging settings or leave them default
Install
Usage
Sample code
Here's an example for setting up a SINGLE_PIPELINE MediaLive channel using a local MP4 file (./upload/test.mp4
) as an input to get MediaPackage v1 and v2 endpoints
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { FilePublisher } from 'awscdk-construct-file-publisher';
import { LiveChannelFromMp4 } from 'awscdk-construct-live-channel-from-mp4-file';
export class ExampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const publicFolder = new FilePublisher(this, 'FilePublisher', {
path: './upload',
});
const { eml, empv1, empv2 } = new LiveChannelFromMp4(this, 'LiveChannelFromMp4', {
source: `${publicFolder.url}/test.mp4`,
channelClass: 'STANDARD',
encoderSpec: {
gopLengthInSeconds: 2,
timecodeBurninPrefix: 'Ch1',
},
mediaPackageVersionSpec: 'V1_AND_V2',
packagerSpec: {
segmentDurationSeconds: 4,
manifestWindowSeconds: 20,
},
autoStart: true,
});
new cdk.CfnOutput(this, "MediaLiveChannelId", {
value: eml.channel.ref,
exportName: cdk.Aws.STACK_NAME + "MediaLiveChannelId",
description: "MediaLive channel ID",
});
if (empv1?.endpoints) {
new cdk.CfnOutput(this, "MediaPackageV1HlsEndpoint", {
value: empv1.endpoints.hls.attrUrl,
exportName: cdk.Aws.STACK_NAME + "MediaPackageV1HlsEndpoint",
description: "MediaPackage V1 HLS endpoint URL",
});
new cdk.CfnOutput(this, "MediaPackageV1DashEndpoint", {
value: empv1.endpoints.dash.attrUrl,
exportName: cdk.Aws.STACK_NAME + "MediaPackageV1DashEndpoint",
description: "MediaPackage V1 DASH endpoint URL",
});
new cdk.CfnOutput(this, "MediaPackageV1CmafEndpoint", {
value: empv1.endpoints.cmaf.attrUrl,
exportName: cdk.Aws.STACK_NAME + "MediaPackageV1CmafEndpoint",
description: "MediaPackage V1 CMAF (HLS with fMP4) endpoint URL",
});
new cdk.CfnOutput(this, "MediaPackageV1MssEndpoint", {
value: empv1.endpoints.mss.attrUrl,
exportName: cdk.Aws.STACK_NAME + "MediaPackageV1MssEndpoint",
description: "MediaPackage V1 Microsoft Smooth Streaming endpoint URL",
});
}
if (empv2?.endpointUrls) {
new cdk.CfnOutput(this, "MediaPackageV2HlsEndpoint", {
value: empv2.endpointUrls.hls,
exportName: cdk.Aws.STACK_NAME + "MediaPackageV2HlsEndpoint",
description: "MediaPackage V2 HLS endpoint URL",
});
new cdk.CfnOutput(this, "MediaPackageV2LlHlsEndpoint", {
value: empv2.endpointUrls.llHls,
exportName: cdk.Aws.STACK_NAME + "MediaPackageV2LlHlsEndpoint",
description: "MediaPackage V2 Low-Latency HLS endpoint URL",
});
new cdk.CfnOutput(this, "MediaPackageV2DashEndpoint", {
value: empv2.endpointUrls.dash,
exportName: cdk.Aws.STACK_NAME + "MediaPackageV2DashEndpoint",
description: "MediaPackage V2 DASH endpoint URL",
});
}
}
}
Sample code (Harvest Job)
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { LiveChannelFromMp4 } from 'awscdk-construct-live-channel-from-mp4-file';
import { ScteScheduler } from 'awscdk-construct-scte-scheduler';
export class ExampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const ch = new LiveChannelFromMp4(this, 'LiveChannelFromMp4', {
source: `s3://my-bucket/test.mp4`,
}), {eml, empv1: emp} = ch;
if (emp?.endpoints.hls) {
const harvestJob = ch.createHarvestJob({
endpoint: emp.endpoints.hls,
});
new ScteScheduler(this, 'ScteScheduler', {
channelId: eml.channel.ref,
scteDurationInSeconds: 30,
intervalInMinutes: 1,
repeatCount: 50,
callback: harvestJob.func,
});
new cdk.CfnOutput(this, "HarvestedVODURL", {
value: harvestJob.s3Url,
exportName: cdk.Aws.STACK_NAME + "HarvestedVODURL",
description: "Harvested VOD URL",
});
}
}
}
Possible configurations
Input
The input needs to be a single or multiple MP4 file(s). The following formats are supported:
- Local filesystem path (using
FilePublisher
like the above code) - URL (
http://
and https://
) - S3 URL (
s3://
and s3ssl://
)
Also, you can convert File inputs into Push inputs before they're ingested by MediaPackage
The following code creates 3x inputs with different routes:
- MP4 ---> MediaLive ---> MediaPackage (defalut)
- MP4 ---> MediaLive ---(RTP_PUSH)---> MediaLive ---> MediaPackage
- MP4 ---> MediaLive ---(RTMP_PUSH)---> MediaLive ---> MediaPackage
const {eml, empv1, empv2} = new LiveChannelFromMp4(this, 'LiveChannelFromMp4', {
source: [
{
url: 's3://aems-input/test-1.mp4',
conversionType: 'NONE',
},
{
url: 's3://aems-input/test-2.mp4',
conversionType: 'RTP_PUSH',
},
{
url: 's3://aems-input/test-3.mp4',
conversionType: 'RTMP_PUSH',
},
],
autoStart: true,
});
Output
By default, all possible endpoints will be created for each version of MediaPackage:
- (MediaLive's MediaPackage output)---> MediaPackage V1 channel with 4x endpoints (HLS/DASH/CMAF/MSS)
- (MediaLive's HLS output)---> MediaPackage V2 channel with 3x endpoints (HLS/LL-HLS/DASH)
You can specify only necessary MediaPackage version:
const {eml, empv2} = new LiveChannelFromMp4(this, 'LiveChannelFromMp4', {
source: 's3://aems-input/test-1.mp4',
mediaPackageVersionSpec: 'V2_ONLY',
mediaPackageV2Settings: {
omitLlHls: true,
}
});
Encoding/Packaging
By default, the following parameters will be used for encoding and packaging:
- Encoding
- SINGLE_PIPELINE --> configurable
- Frame rate 29.97fps
- ABR with 3x bitrates (720p/540p/360p)
- GOP length: 3 seconds --> configurable
- Time-code burn-in: none --> configurable
- Packaging
- Segment length: 6 seconds --> configurable
- Manifest length: 60 seconds --> configurable
- DVR window: none --> configurable
- Separate audio/video renditions: no separation (muxed) --> configurable
- HLS ad marker: DATERANGE --> configurable
You can further configure the encoding/packaging behavior using low-level settings:
const {eml, empv1, empv2} = new LiveChannelFromMp4(this, 'LiveChannelFromMp4', {
source: 's3://aems-input/test-1.mp4',
encoderSpec: {
outputGroups,
videDescriptions,
audioDescriptions,
timecodeConfig,
availBlanking,
},
packagerSpec: {
startoverWindowSeconds,
endpointSpec,
} or {
channelGroupName,
startoverWindowSeconds,
endpointSpec: {
containerType,
manifests,
segment,
}
}
});