-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ivs): support recording configuration for channel #31899
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
577179f
add recording configuraiton
mazyu36 1a0c5f9
add validation
mazyu36 1ffa50b
add README
mazyu36 2859000
Update README.md
mazyu36 0c4d481
Merge branch 'main' into ivs-recording-configuration-31780
mazyu36 38ce28e
docs update
mazyu36 4af5a13
add import method test
mazyu36 4273000
Update README.md
mazyu36 7e41a7f
incorporate review comments
mazyu36 3b05eb5
Merge branch 'main' into ivs-recording-configuration-31780
mazyu36 8fad7d5
add description about default value
mazyu36 b323f4d
Merge branch 'main' into ivs-recording-configuration-31780
paulhcsun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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 |
---|---|---|
@@ -1,5 +1,9 @@ | ||
export * from './channel'; | ||
export * from './playback-key-pair'; | ||
export * from './recording-configuration'; | ||
export * from './rendition-configuration'; | ||
export * from './stream-key'; | ||
export * from './thumbnail-configuration'; | ||
export * from './util'; | ||
|
||
// AWS::IVS CloudFormation Resources: |
210 changes: 210 additions & 0 deletions
210
packages/@aws-cdk/aws-ivs-alpha/lib/recording-configuration.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,210 @@ | ||
import { CfnRecordingConfiguration } from 'aws-cdk-lib/aws-ivs'; | ||
import { IBucket } from 'aws-cdk-lib/aws-s3'; | ||
import { Duration, Fn, IResource, Resource, Stack, Token } from 'aws-cdk-lib/core'; | ||
import { Construct } from 'constructs'; | ||
import { RenditionConfiguration } from './rendition-configuration'; | ||
import { ThumbnailConfiguration } from './thumbnail-configuration'; | ||
|
||
/** | ||
* Properties of the IVS Recording configuration | ||
*/ | ||
export interface RecordingConfigurationProps { | ||
/** | ||
* S3 bucket where recorded videos will be stored. | ||
*/ | ||
readonly bucket: IBucket; | ||
|
||
/** | ||
* The name of the Recording configuration. | ||
* The value does not need to be unique. | ||
* | ||
* @default - auto generate | ||
*/ | ||
readonly recordingConfigurationName?: string; | ||
|
||
/** | ||
* If a broadcast disconnects and then reconnects within the specified interval, | ||
* the multiple streams will be considered a single broadcast and merged together. | ||
* | ||
* `recordingReconnectWindow` must be between 0 and 300 seconds | ||
* | ||
* @default - 0 seconds (means disabled) | ||
*/ | ||
readonly recordingReconnectWindow?: Duration; | ||
|
||
/** | ||
* A rendition configuration describes which renditions should be recorded for a stream. | ||
* | ||
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-renditionconfiguration.html | ||
* | ||
* @default - no rendition configuration | ||
*/ | ||
readonly renditionConfiguration?: RenditionConfiguration; | ||
|
||
/** | ||
* A thumbnail configuration enables/disables the recording of thumbnails for a live session and controls the interval at which thumbnails are generated for the live session. | ||
* | ||
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ivs-recordingconfiguration-thumbnailconfiguration.html | ||
* | ||
* @default - no thumbnail configuration | ||
*/ | ||
readonly thumbnailConfiguration?:ThumbnailConfiguration; | ||
} | ||
|
||
/** | ||
* Represents the IVS Recording configuration. | ||
*/ | ||
export interface IRecordingConfiguration extends IResource { | ||
/** | ||
* The ID of the Recording configuration. | ||
* @attribute | ||
*/ | ||
readonly recordingConfigurationId: string; | ||
|
||
/** | ||
* The ARN of the Recording configuration. | ||
* @attribute | ||
*/ | ||
readonly recordingConfigurationArn: string; | ||
} | ||
|
||
/** | ||
* The IVS Recording configuration | ||
* | ||
* @resource AWS::IVS::RecordingConfiguration | ||
*/ | ||
export class RecordingConfiguration extends Resource implements IRecordingConfiguration { | ||
/** | ||
* Imports an IVS Recording Configuration from attributes. | ||
*/ | ||
public static fromRecordingConfigurationId(scope: Construct, id: string, | ||
recordingConfigurationId: string): IRecordingConfiguration { | ||
|
||
class Import extends Resource implements IRecordingConfiguration { | ||
public readonly recordingConfigurationId = recordingConfigurationId; | ||
public readonly recordingConfigurationArn = Stack.of(this).formatArn({ | ||
resource: 'recording-configuration', | ||
service: 'ivs', | ||
resourceName: recordingConfigurationId, | ||
}); | ||
} | ||
|
||
return new Import(scope, id); | ||
} | ||
|
||
/** | ||
* Imports an IVS Recording Configuration from its ARN | ||
*/ | ||
public static fromArn(scope: Construct, id: string, recordingConfigurationArn: string): IRecordingConfiguration { | ||
const resourceParts = Fn.split('/', recordingConfigurationArn); | ||
|
||
if (!resourceParts || resourceParts.length < 2) { | ||
throw new Error(`Unexpected ARN format: ${recordingConfigurationArn}`); | ||
} | ||
|
||
const recordingConfigurationId = Fn.select(1, resourceParts); | ||
|
||
class Import extends Resource implements IRecordingConfiguration { | ||
public readonly recordingConfigurationId = recordingConfigurationId; | ||
public readonly recordingConfigurationArn = recordingConfigurationArn; | ||
} | ||
|
||
return new Import(scope, id); | ||
} | ||
|
||
/** | ||
* The ID of the Recording configuration. | ||
* @attribute | ||
*/ | ||
readonly recordingConfigurationId: string; | ||
|
||
/** | ||
* The ARN of the Recording configuration. | ||
* @attribute | ||
*/ | ||
readonly recordingConfigurationArn: string; | ||
|
||
private readonly props: RecordingConfigurationProps; | ||
|
||
public constructor(scope: Construct, id: string, props: RecordingConfigurationProps) { | ||
super(scope, id, { | ||
physicalName: props.recordingConfigurationName, | ||
}); | ||
|
||
this.props = props; | ||
|
||
this.validateRecordingConfigurationName(); | ||
this.validateRecordingReconnectWindowSeconds(); | ||
|
||
const resource = new CfnRecordingConfiguration(this, 'Resource', { | ||
destinationConfiguration: { | ||
s3: { | ||
bucketName: this.props.bucket.bucketName, | ||
}, | ||
}, | ||
name: this.props.recordingConfigurationName, | ||
recordingReconnectWindowSeconds: this.props.recordingReconnectWindow?.toSeconds(), | ||
renditionConfiguration: this._renderRenditionConfiguration(), | ||
thumbnailConfiguration: this._renderThumbnailConfiguration(), | ||
}); | ||
|
||
this.recordingConfigurationId = resource.ref; | ||
this.recordingConfigurationArn = resource.attrArn; | ||
} | ||
|
||
private _renderRenditionConfiguration(): CfnRecordingConfiguration.RenditionConfigurationProperty | undefined { | ||
if (!this.props.renditionConfiguration) { | ||
return; | ||
} | ||
|
||
return { | ||
renditions: this.props.renditionConfiguration.renditions, | ||
renditionSelection: this.props.renditionConfiguration.renditionSelection, | ||
}; | ||
}; | ||
|
||
private _renderThumbnailConfiguration(): CfnRecordingConfiguration.ThumbnailConfigurationProperty | undefined { | ||
if (!this.props.thumbnailConfiguration) { | ||
return; | ||
} | ||
|
||
return { | ||
recordingMode: this.props.thumbnailConfiguration.recordingMode, | ||
resolution: this.props.thumbnailConfiguration.resolution, | ||
storage: this.props.thumbnailConfiguration.storage, | ||
targetIntervalSeconds: this.props.thumbnailConfiguration.targetInterval?.toSeconds(), | ||
}; | ||
}; | ||
|
||
private validateRecordingConfigurationName(): undefined { | ||
const recordingConfigurationName = this.props.recordingConfigurationName; | ||
|
||
if (recordingConfigurationName == undefined || Token.isUnresolved(recordingConfigurationName)) { | ||
return; | ||
} | ||
|
||
if (!/^[a-zA-Z0-9-_]*$/.test(recordingConfigurationName)) { | ||
throw new Error(`\`recordingConfigurationName\` must consist only of alphanumeric characters, hyphens or underbars, got: ${recordingConfigurationName}.`); | ||
} | ||
|
||
if (recordingConfigurationName.length > 128) { | ||
throw new Error(`\`recordingConfigurationName\` must be less than or equal to 128 characters, got: ${recordingConfigurationName.length} characters.`); | ||
} | ||
}; | ||
|
||
private validateRecordingReconnectWindowSeconds(): undefined { | ||
const recordingReconnectWindow = this.props.recordingReconnectWindow; | ||
|
||
if (recordingReconnectWindow === undefined || Token.isUnresolved(recordingReconnectWindow)) { | ||
return; | ||
} | ||
|
||
if (0 < recordingReconnectWindow.toMilliseconds() && recordingReconnectWindow.toMilliseconds() < Duration.seconds(1).toMilliseconds()) { | ||
throw new Error(`\`recordingReconnectWindow\` must be between 0 and 300 seconds, got ${recordingReconnectWindow.toMilliseconds()} milliseconds.`); | ||
} | ||
|
||
if (recordingReconnectWindow.toSeconds() > 300) { | ||
throw new Error(`\`recordingReconnectWindow\` must be between 0 and 300 seconds, got ${recordingReconnectWindow.toSeconds()} seconds.`); | ||
} | ||
}; | ||
} |
55 changes: 55 additions & 0 deletions
55
packages/@aws-cdk/aws-ivs-alpha/lib/rendition-configuration.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,55 @@ | ||
import { Resolution } from './util'; | ||
|
||
/** | ||
* Rendition selection mode. | ||
*/ | ||
export enum RenditionSelection { | ||
/** | ||
* Record all available renditions. | ||
*/ | ||
ALL = 'ALL', | ||
|
||
/** | ||
* Does not record any video. This option is useful if you just want to record thumbnails. | ||
*/ | ||
NONE = 'NONE', | ||
|
||
/** | ||
* Select a subset of video renditions to record. | ||
*/ | ||
CUSTOM = 'CUSTOM', | ||
} | ||
|
||
/** | ||
* Rendition configuration for IVS Recording configuration | ||
*/ | ||
export class RenditionConfiguration { | ||
/** | ||
* Record all available renditions. | ||
*/ | ||
public static all(): RenditionConfiguration { | ||
return new RenditionConfiguration(RenditionSelection.ALL); | ||
} | ||
|
||
/** | ||
* Does not record any video. | ||
*/ | ||
public static none(): RenditionConfiguration { | ||
return new RenditionConfiguration(RenditionSelection.NONE); | ||
} | ||
|
||
/** | ||
* Record a subset of video renditions. | ||
* | ||
* @param renditions A list of which renditions are recorded for a stream. | ||
*/ | ||
public static custom(renditions: Resolution[]): RenditionConfiguration { | ||
return new RenditionConfiguration(RenditionSelection.CUSTOM, renditions); | ||
} | ||
|
||
/** | ||
* @param renditionSelection The set of renditions are recorded for a stream. | ||
* @param renditions A list of which renditions are recorded for a stream. If you do not specify this property, no resolution is selected. | ||
*/ | ||
private constructor(public readonly renditionSelection: RenditionSelection, public readonly renditions?: Resolution[]) { } | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making the requested changes @mazyu36! I notice now though that the properties that are contained within the
renditionConfiguration
andthumbnailConfiguration
no longer have defaults because the new default is that the configuration is not provided. Is this intentional or should we still set a default for these properties somewhere?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@paulhcsun
Thanks.
The lack of default values is intentional. Is it correct to understand that you're suggesting we should document somewhere what the default values are when
@param
is omitted? If so, I plan to add this information to the@param
description.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added an explanation regarding the default value. Please let me know if it doesn’t match your intention.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes the documentation is appreciated! But also I wanted to check if this default is something that gets set by the service/cloudformation fills it in, since we're not explicitly setting it as the default anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
Those default values are provided by CFn.
So I've not explicitly set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, I'm good to approve then. Thank you for working on this @mazyu36!!