当前位置: 首页 > news >正文

使用AWS S3 + Lambda + MediaConvert 实现上传视频文件并自动转码

前言

最近团队在做短视频平台的技术调研,其中有一个环节便是音视频开发,即对用户上传的视频进行自适应转码。自适应的原理其实就是预先将视频转换为几个常用的分辨率,app端根据用户手机分辨率拉取相应分辨率的视频。 目前尝试了两种方案,一种是自己搭建完整的转码服务,利用minio + 桶通知事件 + javacv实现(https://gitee.com/chengzhi2/javacv-demo.git)。 另一种就是使用亚马逊的MediaConvert转码服务(这东西是真的贵啊)

配置S3存储

1、配置源存储桶

创建桶的步骤很简单,参考文档或者提示操作即可。但是这里要着重注意需要配置“跨源资源共享(CORS)”,要不然转码时会读取不到。源目标存储桶可以不提供对外访问权限。

[{"AllowedHeaders": ["*"],"AllowedMethods": ["GET"],"AllowedOrigins": ["*"],"ExposeHeaders": []}
]

在这里插入图片描述

2、配置目标存储桶

目标存储桶需要提供对外访问,需要配置存储桶访问策略:

{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": "*","Action": "s3:GetObject","Resource": "arn:aws:s3:::demo-video-out/*"}]
}

配置目标存储桶的目的是为了存储转码后的文件,因此这里不再需要配置跨资源共享了。

配置Lambda

1、创建函数

在这里插入图片描述

2、添加Lambda触发器

在这里插入图片描述

3、编写代码:

我这里是利用cursor + 调试得出的一个可行的方案:

index.mjs:

import { S3Client, HeadObjectCommand } from "@aws-sdk/client-s3";
import TranscodeService from './TranscodeService.js';const client = new S3Client();
const transcodeService = new TranscodeService('ap-northeast-3');export const handler = async (event, context) => {console.log('上传文件触发了函数')const bucket = event.Records[0].s3.bucket.name;const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));try {// 获取文件对象详细信息const { ContentType } = await client.send(new HeadObjectCommand({Bucket: bucket,Key: key,}));console.log('ContentType:', ContentType);// todo 如果是视频文件并且是上传事件if (ContentType.startsWith('video/')) {console.log('监听到视频文件,自动触发转码任务!!!');// 手动初始化并等待完成console.log('Initializing MediaConvert...');await transcodeService.initializeMediaConvert();console.log('MediaConvert initialized');// 调用转码服务await startTranscoding(bucket, key, ContentType);return {success: true,message: 'Transcoding started',contentType: ContentType};}} catch (err) {console.error('Error:', err);throw err;}
};async function startTranscoding(bucket, s3Key, contentType) {const inputS3Path = `s3://${bucket}/${s3Key}`;const outputS3Path = `s3://demo-video-out`;const transcodeSettings = {hls: true,mp4: true,resolutions: ['1080p', '720p', '480p'],segmentLength: 10};const job = await transcodeService.createTranscodeJob(inputS3Path, outputS3Path, transcodeSettings);console.log('转码任务创建成功!!!')
}

TranscodeService.js

const AWS = require('aws-sdk');
const { v4: uuidv4 } = require('uuid');class TranscodeService {constructor(region = 'ap-northeast-3') {this.region = region;this.mediaConvert = null;this.initialized = false;this.initializing = false;this.initializeMediaConvert();}/*** 初始化MediaConvert客户端*/async initializeMediaConvert() {if (this.initialized) {return;}if (this.initializing) {while (this.initializing) {await new Promise(resolve => setTimeout(resolve, 100));}return;}this.initializing = true;try {console.log('Starting MediaConvert initialization...');const mediaConvert = new AWS.MediaConvert({ region: this.region });const response = await mediaConvert.describeEndpoints().promise();this.mediaConvert = new AWS.MediaConvert({region: this.region,endpoint: response.Endpoints[0].Url});this.initialized = true;console.log('MediaConvert initialized successfully');} catch (error) {console.error('Failed to initialize MediaConvert:', error);throw error;} finally {this.initializing = false;}}/*** 确保MediaConvert已初始化*/async ensureInitialized() {if (!this.initialized) {await this.initializeMediaConvert();}}/*** 创建转码任务*/async createTranscodeJob(inputS3Path, outputS3Path, settings = {}) {try {await this.ensureInitialized();if (!this.mediaConvert) {throw new Error('MediaConvert client is not initialized');}const jobId = uuidv4();const jobSettings = {TimecodeConfig: {Source: "ZEROBASED"},Inputs: [{FileInput: inputS3Path,AudioSelectors: {"Audio Selector 1": {DefaultSelection: "DEFAULT"}}}],OutputGroups: this.buildOutputGroups(outputS3Path, settings)};const params = {Role: 'xxxxxxx', // todo 这里需要替换为MediaConvert角色的arn码UserMetadata: {assetID: jobId,application: 'video-transcode-app',input: inputS3Path,settings: 'custom'},Settings: jobSettings};console.log('Creating MediaConvert job:', JSON.stringify(params, null, 2));const response = await this.mediaConvert.createJob(params).promise();return {jobId: response.Job.Id,status: response.Job.Status,settings: jobSettings};} catch (error) {console.error('Failed to create transcode job:', error);throw error;}}/*** 构建输出组配置*/buildOutputGroups(outputS3Path, settings) {const outputGroups = [];// HLS输出组if (settings.hls !== false) {outputGroups.push({Name: "HLS",OutputGroupSettings: {Type: "HLS_GROUP_SETTINGS",HlsGroupSettings: {SegmentLength: settings.segmentLength || 10,MinSegmentLength: 0, SegmentControl: "SEGMENTED_FILES",Destination: `${outputS3Path}/hls/`,ManifestDurationFormat: "INTEGER"}},Outputs: this.buildHLSOutputs(settings.resolutions || ['1080p', '720p', '480p'])});}// MP4输出组if (settings.mp4 !== false) {outputGroups.push({Name: "MP4",OutputGroupSettings: {Type: "FILE_GROUP_SETTINGS",FileGroupSettings: {Destination: `${outputS3Path}/mp4/`}},Outputs: this.buildMP4Outputs(settings.resolutions || ['1080p', '720p'])});}return outputGroups;}/*** 构建HLS输出配置*/buildHLSOutputs(resolutions) {const resolutionConfigs = {'1080p': { width: 1920, height: 1080, bitrate: 5000000 },'720p': { width: 1280, height: 720, bitrate: 3000000 },'480p': { width: 854, height: 480, bitrate: 1500000 },'360p': { width: 640, height: 360, bitrate: 800000 }};return resolutions.map(resolution => {const config = resolutionConfigs[resolution];if (!config) {throw new Error(`Unsupported resolution: ${resolution}`);}return {NameModifier: `_${resolution}`,VideoDescription: {Width: config.width,Height: config.height,ScalingBehavior: "DEFAULT",CodecSettings: {Codec: "H_264",H264Settings: {MaxBitrate: config.bitrate * 2,HrdBufferSize: config.bitrate * 4,  GopSize: 90,GopSizeUnits: "FRAMES",ParControl: "INITIALIZE_FROM_SOURCE",CodecProfile: "HIGH",  RateControlMode: "QVBR",QvbrSettings: {QvbrQualityLevel: 8}}}},AudioDescriptions: [{AudioSourceName: "Audio Selector 1",CodecSettings: {Codec: "AAC",AacSettings: {Bitrate: 128000,CodingMode: "CODING_MODE_2_0",SampleRate: 48000,CodecProfile: "LC"}}}],ContainerSettings: {Container: "M3U8",M3u8Settings: {AudioFramesPerPes: 4,PcrControl: "PCR_EVERY_PES_PACKET",PmtPid: 480,PrivateMetadataPid: 503,VideoPid: 481,AudioPids: [482, 483, 484, 485, 486, 487]}}};});}/*** 构建MP4输出配置*/buildMP4Outputs(resolutions) {const resolutionConfigs = {'1080p': { width: 1920, height: 1080, bitrate: 5000000 },'720p': { width: 1280, height: 720, bitrate: 3000000 },'480p': { width: 854, height: 480, bitrate: 1500000 }};return resolutions.map(resolution => {const config = resolutionConfigs[resolution];if (!config) {throw new Error(`Unsupported resolution: ${resolution}`);}return {NameModifier: `_${resolution}`,VideoDescription: {Width: config.width,Height: config.height,ScalingBehavior: "DEFAULT",CodecSettings: {Codec: "H_264",H264Settings: {MaxBitrate: config.bitrate * 2,HrdBufferSize: config.bitrate * 4, GopSize: 90,GopSizeUnits: "FRAMES",ParControl: "INITIALIZE_FROM_SOURCE",CodecProfile: "MAIN", RateControlMode: "QVBR",QvbrSettings: {QvbrQualityLevel: 8}}}},AudioDescriptions: [{AudioSourceName: "Audio Selector 1",CodecSettings: {Codec: "AAC",AacSettings: {Bitrate: 128000,CodingMode: "CODING_MODE_2_0",SampleRate: 48000,CodecProfile: "LC"}}}],ContainerSettings: {Container: "MP4",Mp4Settings: {MoovPlacement: "PROGRESSIVE_DOWNLOAD"}}};});}/*** 查询转码任务状态*/async getJobStatus(jobId) {try {await this.ensureInitialized();const params = { Id: jobId };const response = await this.mediaConvert.getJob(params).promise();return {jobId: response.Job.Id,status: response.Job.Status,progress: response.Job.Status === 'COMPLETE' ? 100 : response.Job.Status === 'ERROR' ? 0 : response.Job.JobPercentComplete || 0,errorMessage: response.Job.ErrorMessage,outputFiles: response.Job.Settings.OutputGroups?.map(group => ({name: group.Name,destination: group.OutputGroupSettings.FileGroupSettings?.Destination ||group.OutputGroupSettings.HlsGroupSettings?.Destination}))};} catch (error) {console.error('Failed to get job status:', error);throw error;}}/*** 取消转码任务*/async cancelJob(jobId) {try {await this.ensureInitialized();const params = { Id: jobId };await this.mediaConvert.cancelJob(params).promise();console.log(`Job ${jobId} cancelled successfully`);} catch (error) {console.error('Failed to cancel job:', error);throw error;}}/*** 批量创建转码任务*/async batchTranscode(inputFiles, outputPath, settings = {}) {const jobs = [];for (const inputFile of inputFiles) {try {const job = await this.createTranscodeJob(inputFile, outputPath, settings);jobs.push(job);} catch (error) {console.error(`Failed to create job for ${inputFile}:`, error);jobs.push({ error: error.message, inputFile });}}return jobs;}/*** 等待转码任务完成*/async waitForJobCompletion(jobId, maxWaitTime = 3600000) { // 默认等待1小时const startTime = Date.now();while (Date.now() - startTime < maxWaitTime) {const status = await this.getJobStatus(jobId);if (status.status === 'COMPLETE') {console.log(`Job ${jobId} completed successfully`);return status;} else if (status.status === 'ERROR') {throw new Error(`Job ${jobId} failed: ${status.errorMessage}`);} else if (status.status === 'CANCELED') {throw new Error(`Job ${jobId} was canceled`);}// 等待30秒后再次检查await new Promise(resolve => setTimeout(resolve, 30000));}throw new Error(`Job ${jobId} timed out after ${maxWaitTime / 1000} seconds`);}/*** 获取转码任务列表*/async listJobs(status = null, maxResults = 20) {try {await this.ensureInitialized();const params = {MaxResults: maxResults};if (status) {params.Status = status;}const response = await this.mediaConvert.listJobs(params).promise();return response.Jobs || [];} catch (error) {console.error('Failed to list jobs:', error);throw error;}}
}module.exports = TranscodeService;

package.json

{"name": "lambda-s3","version": "1.0.0","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","description": "","dependencies": {"aws-sdk": "^2.1450.0","sharp": "^0.32.6","uuid": "^8.3.2"}
}

代码中需要注意一点,就是角色的arn码,从这里获取:
在这里插入图片描述

调试

日志:

日志需要在cloudwatch中对应所属区域下看

上述代码生成的json配置

{"Role": "arn:aws:iam::548259613792:role/service-role/MediaConvert_Default_Role","UserMetadata": {"assetID": "e9cce2b7-c64d-443c-89e8-ac2be2c3bfa7","application": "video-transcode-app","input": "s3://demo-video-source/276985.mp4","settings": "custom"},"Settings": {"TimecodeConfig": {"Source": "ZEROBASED"},"Inputs": [{"FileInput": "s3://demo-video-source/276985.mp4","AudioSelectors": {"Audio Selector 1": {"DefaultSelection": "DEFAULT"}}}],"OutputGroups": [{"Name": "HLS","OutputGroupSettings": {"Type": "HLS_GROUP_SETTINGS","HlsGroupSettings": {"SegmentLength": 10,"MinSegmentLength": 0,"SegmentControl": "SEGMENTED_FILES","Destination": "s3://demo-video-out/hls/","ManifestDurationFormat": "INTEGER"}},"Outputs": [{"NameModifier": "_1080p","VideoDescription": {"Width": 1920,"Height": 1080,"ScalingBehavior": "DEFAULT","CodecSettings": {"Codec": "H_264","H264Settings": {"MaxBitrate": 10000000,"HrdBufferSize": 20000000,"GopSize": 90,"GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","CodecProfile": "HIGH","RateControlMode": "QVBR","QvbrSettings": {"QvbrQualityLevel": 8}}}},"AudioDescriptions": [{"AudioSourceName": "Audio Selector 1","CodecSettings": {"Codec": "AAC","AacSettings": {"Bitrate": 128000,"CodingMode": "CODING_MODE_2_0","SampleRate": 48000,"CodecProfile": "LC"}}}],"ContainerSettings": {"Container": "M3U8","M3u8Settings": {"AudioFramesPerPes": 4,"PcrControl": "PCR_EVERY_PES_PACKET","PmtPid": 480,"PrivateMetadataPid": 503,"VideoPid": 481,"AudioPids": [482,483,484,485,486,487]}}},{"NameModifier": "_720p","VideoDescription": {"Width": 1280,"Height": 720,"ScalingBehavior": "DEFAULT","CodecSettings": {"Codec": "H_264","H264Settings": {"MaxBitrate": 6000000,"HrdBufferSize": 12000000,"GopSize": 90,"GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","CodecProfile": "HIGH","RateControlMode": "QVBR","QvbrSettings": {"QvbrQualityLevel": 8}}}},"AudioDescriptions": [{"AudioSourceName": "Audio Selector 1","CodecSettings": {"Codec": "AAC","AacSettings": {"Bitrate": 128000,"CodingMode": "CODING_MODE_2_0","SampleRate": 48000,"CodecProfile": "LC"}}}],"ContainerSettings": {"Container": "M3U8","M3u8Settings": {"AudioFramesPerPes": 4,"PcrControl": "PCR_EVERY_PES_PACKET","PmtPid": 480,"PrivateMetadataPid": 503,"VideoPid": 481,"AudioPids": [482,483,484,485,486,487]}}},{"NameModifier": "_480p","VideoDescription": {"Width": 854,"Height": 480,"ScalingBehavior": "DEFAULT","CodecSettings": {"Codec": "H_264","H264Settings": {"MaxBitrate": 3000000,"HrdBufferSize": 6000000,"GopSize": 90,"GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","CodecProfile": "HIGH","RateControlMode": "QVBR","QvbrSettings": {"QvbrQualityLevel": 8}}}},"AudioDescriptions": [{"AudioSourceName": "Audio Selector 1","CodecSettings": {"Codec": "AAC","AacSettings": {"Bitrate": 128000,"CodingMode": "CODING_MODE_2_0","SampleRate": 48000,"CodecProfile": "LC"}}}],"ContainerSettings": {"Container": "M3U8","M3u8Settings": {"AudioFramesPerPes": 4,"PcrControl": "PCR_EVERY_PES_PACKET","PmtPid": 480,"PrivateMetadataPid": 503,"VideoPid": 481,"AudioPids": [482,483,484,485,486,487]}}}]},{"Name": "MP4","OutputGroupSettings": {"Type": "FILE_GROUP_SETTINGS","FileGroupSettings": {"Destination": "s3://demo-video-out/mp4/"}},"Outputs": [{"NameModifier": "_1080p","VideoDescription": {"Width": 1920,"Height": 1080,"ScalingBehavior": "DEFAULT","CodecSettings": {"Codec": "H_264","H264Settings": {"MaxBitrate": 10000000,"HrdBufferSize": 20000000,"GopSize": 90,"GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","CodecProfile": "MAIN","RateControlMode": "QVBR","QvbrSettings": {"QvbrQualityLevel": 8}}}},"AudioDescriptions": [{"AudioSourceName": "Audio Selector 1","CodecSettings": {"Codec": "AAC","AacSettings": {"Bitrate": 128000,"CodingMode": "CODING_MODE_2_0","SampleRate": 48000,"CodecProfile": "LC"}}}],"ContainerSettings": {"Container": "MP4","Mp4Settings": {"MoovPlacement": "PROGRESSIVE_DOWNLOAD"}}},{"NameModifier": "_720p","VideoDescription": {"Width": 1280,"Height": 720,"ScalingBehavior": "DEFAULT","CodecSettings": {"Codec": "H_264","H264Settings": {"MaxBitrate": 6000000,"HrdBufferSize": 12000000,"GopSize": 90,"GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","CodecProfile": "MAIN","RateControlMode": "QVBR","QvbrSettings": {"QvbrQualityLevel": 8}}}},"AudioDescriptions": [{"AudioSourceName": "Audio Selector 1","CodecSettings": {"Codec": "AAC","AacSettings": {"Bitrate": 128000,"CodingMode": "CODING_MODE_2_0","SampleRate": 48000,"CodecProfile": "LC"}}}],"ContainerSettings": {"Container": "MP4","Mp4Settings": {"MoovPlacement": "PROGRESSIVE_DOWNLOAD"}}},{"NameModifier": "_480p","VideoDescription": {"Width": 854,"Height": 480,"ScalingBehavior": "DEFAULT","CodecSettings": {"Codec": "H_264","H264Settings": {"MaxBitrate": 3000000,"HrdBufferSize": 6000000,"GopSize": 90,"GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","CodecProfile": "MAIN","RateControlMode": "QVBR","QvbrSettings": {"QvbrQualityLevel": 8}}}},"AudioDescriptions": [{"AudioSourceName": "Audio Selector 1","CodecSettings": {"Codec": "AAC","AacSettings": {"Bitrate": 128000,"CodingMode": "CODING_MODE_2_0","SampleRate": 48000,"CodecProfile": "LC"}}}],"ContainerSettings": {"Container": "MP4","Mp4Settings": {"MoovPlacement": "PROGRESSIVE_DOWNLOAD"}}}]}]}
}

参考转码配置:

{"Queue": "arn:aws:mediaconvert:us-east-1:533267335205:queues/Default","UserMetadata": {},"Role": "arn:aws:iam::533267335205:role/MediaConvertRole","Settings": {"TimecodeConfig": {"Source": "EMBEDDED"},"OutputGroups": [{"Name": "HLS","Outputs": [{"ContainerSettings": {"Container": "M3U8","M3u8Settings": {"AudioFramesPerPes": 4,"PcrControl": "PCR_EVERY_PES_PACKET","PmtPid": 480,"PrivateMetadataPid": 503,"ProgramNumber": 1,"PatInterval": 0,"PmtInterval": 0,"Scte35Source": "NONE","Scte35Pid": 500,"TimedMetadata": "NONE","TimedMetadataPid": 502,"TransportStreamId": 1,"VideoPid": 481}},"VideoDescription": {"Width": 1920,"ScalingBehavior": "DEFAULT","Height": 1080,"TimecodeInsertion": "DISABLED","AntiAlias": "ENABLED","Sharpness": 50,"CodecSettings": {"Codec": "H_264","H264Settings": {"InterlaceMode": "PROGRESSIVE","NumberReferenceFrames": 3,"Syntax": "DEFAULT","Softness": 0,"GopClosedCadence": 1,"GopSize": 90,"Slices": 1,"GopBReference": "DISABLED","SlowPal": "DISABLED","SpatialAdaptiveQuantization": "ENABLED","TemporalAdaptiveQuantization": "ENABLED","FlickerAdaptiveQuantization": "DISABLED","EntropyEncoding": "CABAC","Bitrate": 5000000,"FramerateControl": "INITIALIZE_FROM_SOURCE","RateControlMode": "CBR","CodecProfile": "MAIN","Telecine": "NONE","MinIInterval": 0,"AdaptiveQuantization": "HIGH","CodecLevel": "AUTO","FieldEncoding": "PAFF","SceneChangeDetect": "ENABLED","QualityTuningLevel": "SINGLE_PASS","FramerateConversionAlgorithm": "DUPLICATE_DROP","UnregisteredSeiTimecode": "DISABLED","GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","NumberBFramesBetweenReferenceFrames": 2,"RepeatPps": "DISABLED"}},"AfdSignaling": "NONE","DropFrameTimecode": "ENABLED","RespondToAfd": "NONE","ColorMetadata": "INSERT"},"AudioDescriptions": [{"AudioTypeControl": "FOLLOW_INPUT","CodecSettings": {"Codec": "AAC","AacSettings": {"AudioDescriptionBroadcasterMix": "NORMAL","Bitrate": 96000,"RateControlMode": "CBR","CodecProfile": "LC","CodingMode": "CODING_MODE_2_0","RawFormat": "NONE","SampleRate": 48000,"Specification": "MPEG4"}},"LanguageCodeControl": "FOLLOW_INPUT"}],"NameModifier": "_1080p"},{"ContainerSettings": {"Container": "M3U8","M3u8Settings": {"AudioFramesPerPes": 4,"PcrControl": "PCR_EVERY_PES_PACKET","PmtPid": 480,"PrivateMetadataPid": 503,"ProgramNumber": 1,"PatInterval": 0,"PmtInterval": 0,"Scte35Source": "NONE","Scte35Pid": 500,"TimedMetadata": "NONE","TimedMetadataPid": 502,"TransportStreamId": 1,"VideoPid": 481}},"VideoDescription": {"Width": 1280,"ScalingBehavior": "DEFAULT","Height": 720,"TimecodeInsertion": "DISABLED","AntiAlias": "ENABLED","Sharpness": 50,"CodecSettings": {"Codec": "H_264","H264Settings": {"InterlaceMode": "PROGRESSIVE","NumberReferenceFrames": 3,"Syntax": "DEFAULT","Softness": 0,"GopClosedCadence": 1,"GopSize": 90,"Slices": 1,"GopBReference": "DISABLED","SlowPal": "DISABLED","SpatialAdaptiveQuantization": "ENABLED","TemporalAdaptiveQuantization": "ENABLED","FlickerAdaptiveQuantization": "DISABLED","EntropyEncoding": "CABAC","Bitrate": 2500000,"FramerateControl": "INITIALIZE_FROM_SOURCE","RateControlMode": "CBR","CodecProfile": "MAIN","Telecine": "NONE","MinIInterval": 0,"AdaptiveQuantization": "HIGH","CodecLevel": "AUTO","FieldEncoding": "PAFF","SceneChangeDetect": "ENABLED","QualityTuningLevel": "SINGLE_PASS","FramerateConversionAlgorithm": "DUPLICATE_DROP","UnregisteredSeiTimecode": "DISABLED","GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","NumberBFramesBetweenReferenceFrames": 2,"RepeatPps": "DISABLED"}},"AfdSignaling": "NONE","DropFrameTimecode": "ENABLED","RespondToAfd": "NONE","ColorMetadata": "INSERT"},"AudioDescriptions": [{"AudioTypeControl": "FOLLOW_INPUT","CodecSettings": {"Codec": "AAC","AacSettings": {"AudioDescriptionBroadcasterMix": "NORMAL","Bitrate": 96000,"RateControlMode": "CBR","CodecProfile": "LC","CodingMode": "CODING_MODE_2_0","RawFormat": "NONE","SampleRate": 48000,"Specification": "MPEG4"}},"LanguageCodeControl": "FOLLOW_INPUT"}],"NameModifier": "_720p"},{"ContainerSettings": {"Container": "M3U8","M3u8Settings": {"AudioFramesPerPes": 4,"PcrControl": "PCR_EVERY_PES_PACKET","PmtPid": 480,"PrivateMetadataPid": 503,"ProgramNumber": 1,"PatInterval": 0,"PmtInterval": 0,"Scte35Source": "NONE","Scte35Pid": 500,"TimedMetadata": "NONE","TimedMetadataPid": 502,"TransportStreamId": 1,"VideoPid": 481}},"VideoDescription": {"Width": 640,"ScalingBehavior": "DEFAULT","Height": 360,"TimecodeInsertion": "DISABLED","AntiAlias": "ENABLED","Sharpness": 50,"CodecSettings": {"Codec": "H_264","H264Settings": {"InterlaceMode": "PROGRESSIVE","NumberReferenceFrames": 3,"Syntax": "DEFAULT","Softness": 0,"GopClosedCadence": 1,"GopSize": 90,"Slices": 1,"GopBReference": "DISABLED","SlowPal": "DISABLED","SpatialAdaptiveQuantization": "ENABLED","TemporalAdaptiveQuantization": "ENABLED","FlickerAdaptiveQuantization": "DISABLED","EntropyEncoding": "CABAC","Bitrate": 800000,"FramerateControl": "INITIALIZE_FROM_SOURCE","RateControlMode": "CBR","CodecProfile": "MAIN","Telecine": "NONE","MinIInterval": 0,"AdaptiveQuantization": "HIGH","CodecLevel": "AUTO","FieldEncoding": "PAFF","SceneChangeDetect": "ENABLED","QualityTuningLevel": "SINGLE_PASS","FramerateConversionAlgorithm": "DUPLICATE_DROP","UnregisteredSeiTimecode": "DISABLED","GopSizeUnits": "FRAMES","ParControl": "INITIALIZE_FROM_SOURCE","NumberBFramesBetweenReferenceFrames": 2,"RepeatPps": "DISABLED"}},"AfdSignaling": "NONE","DropFrameTimecode": "ENABLED","RespondToAfd": "NONE","ColorMetadata": "INSERT"},"AudioDescriptions": [{"AudioTypeControl": "FOLLOW_INPUT","CodecSettings": {"Codec": "AAC","AacSettings": {"AudioDescriptionBroadcasterMix": "NORMAL","Bitrate": 96000,"RateControlMode": "CBR","CodecProfile": "LC","CodingMode": "CODING_MODE_2_0","RawFormat": "NONE","SampleRate": 48000,"Specification": "MPEG4"}},"LanguageCodeControl": "FOLLOW_INPUT"}],"NameModifier": "_360p"}],"OutputGroupSettings": {"Type": "HLS_GROUP_SETTINGS","HlsGroupSettings": {"ManifestDurationFormat": "INTEGER","SegmentLength": 10,"TimedMetadataId3Period": 10,"CaptionLanguageSetting": "OMIT","Destination": "s3://wyz-mediaconvert-bucket-virginia/output/hls/","TimedMetadataId3Frame": "PRIV","CodecSpecification": "RFC_4281","OutputSelection": "MANIFESTS_AND_SEGMENTS","ProgramDateTimePeriod": 600,"MinSegmentLength": 0,"DirectoryStructure": "SINGLE_DIRECTORY","ProgramDateTime": "EXCLUDE","SegmentControl": "SEGMENTED_FILES","ManifestCompression": "NONE","ClientCache": "ENABLED","StreamInfResolution": "INCLUDE"}}}],"AdAvailOffset": 0,"Inputs": [{"AudioSelectors": {"Audio Selector 1": {"Offset": 0,"DefaultSelection": "DEFAULT","ProgramSelection": 1}},"VideoSelector": {"ColorSpace": "FOLLOW"},"FilterEnable": "AUTO","PsiControl": "USE_PSI","FilterStrength": 0,"DeblockFilter": "DISABLED","DenoiseFilter": "DISABLED","TimecodeSource": "EMBEDDED","FileInput": "s3://wyz-mediaconvert-bucket-virginia/input/4ktest.mp4"}]},"BillingTagsSource": "JOB","AccelerationSettings": {"Mode": "DISABLED"},"StatusUpdateInterval": "SECONDS_60","Priority": 0
}

参考文档:

https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/tutorial-s3-batchops-lambda-mediaconvert-video.html
https://docs.aws.amazon.com/zh_cn/mediaconvert/latest/ug/example-job-settings.html
https://docs.aws.amazon.com/zh_cn/lambda/latest/dg/with-s3-example.html

http://www.lryc.cn/news/624502.html

相关文章:

  • 一套GoldenGate → Kafka → Flink → MySQL 的端到端增量同步方案
  • 「Flink」业务搭建方法总结
  • 基于Flink CDC实现联系人与标签数据实时同步至ES的实践
  • Ansible文件部署与大项目多主机管理
  • 大数据开发面试题:美团秋招一面
  • 数据赋能(401)——大数据——持续学习与优化原则
  • 自建K8s集群无缝集成阿里云RAM完整指南
  • The Open Group 休斯敦峰会:进步之路——以开放标准定义未来
  • [openvela] Hello World :从零开始的完整实践与问题复盘
  • PDF转图片需要用到什么技术?苹果手机怎样将PDF转为jpg?
  • 在Excel启动时直接打开多个Excel文件
  • 2025上半年AI核心成果与趋势报告深度解析:技术突破、应用落地与未来展望
  • SQLsever基本操作
  • 网络间的通用语言TCP/IP-网络中的通用规则1
  • H264: SPS和PPS概念
  • thinkphp8:一、环境准备
  • Java-101 深入浅出 MySQL InnoDB 锁机制全景图:行锁原理、Next-Key Lock、Gap Lock 详解
  • 机器学习——XGBoost算法
  • python-----机器学习中常用的数据预处理
  • 机器学习之数据预处理(一)
  • 英特尔公司Darren Pulsipher 博士:以架构之力推动政府数字化转型
  • STM32使用WS2812灯环
  • 吴恩达 Machine Learning(Class 2)
  • Windows桌面自动化的革命性突破:深度解析Windows-MCP.Net Desktop模块的技术奥秘
  • 从零到一构建企业级GraphRAG系统:GraphRag.Net深度技术解析
  • OpenCV---特征检测算法(ORB,Oriented FAST and Rotated BRIEF)
  • SkyWalking + Elasticsearch8 容器化部署指南:国内镜像加速与生产级调优
  • 深度解析阿里巴巴国际站商品详情 API:从接口调用到数据结构化处理
  • Vision Master的C#脚本与opencv联合编程
  • 【GM3568JHF】FPGA+ARM异构开发板烧录指南