前言

最近团队在做短视频平台的技术调研,其中有一个环节便是音视频开发,即对用户上传的视频进行自适应转码。自适应的原理其实就是预先将视频转换为几个常用的分辨率,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.pswp.cn/bicheng/94037.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94037.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/94037.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT之QWaitCondition降低cpu占用率,从忙等待到高效同步

在多线程编程中&#xff0c;线程间的同步是一个核心问题。在处理线程等待时&#xff0c;经常会写出高CPU占用率的代码&#xff0c;其中最典型的就是使用忙等待&#xff08;busy waiting&#xff09;。本文将详细介绍如何使用Qt框架中的QWaitCondition类来优雅地解决这一问题&am…

pcl求平面点云的边界凸包点

基本流程1&#xff0c;读入点云&#xff0c;并去除无效点2&#xff0c;拟合平面3&#xff0c;去除离平面距离较远的点4&#xff0c;对点云进行平面投影5&#xff0c;进行convex_hull运算初学者&#xff0c;暂时不知道能用来干嘛。练手还是非常不错的&#xff01;#define _CRT_S…

Windows系统上使用GIT

首先破除一下畏惧心理&#xff1a;在Windows上使用git和在linux系统中的使用方法是一样的&#xff0c;只是安装方式没那么便捷&#xff0c;毕竟linux中安装git只需要一行命令 GIT下载地址 如果你的电脑的CPU是64位的&#xff0c;就点击&#xff1a; Git-2.50.1-64-bit.exe 如果…

《设计模式之禅》笔记摘录 - 17.模板方法模式

模板方法模式的定义模板方法模式(Template Method Pattern)是如此简单&#xff0c;以致让你感觉你已经能够掌握其精髓了。其定义如下&#xff1a;Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.Template Method lets subclasses r…

SpreadJS 协同服务器 MongoDB 数据库适配支持

为了支持 SpreadJS 协同编辑场景&#xff0c;协同服务器需要持久化存储文档、操作、快照及里程碑数据。本文介绍了 MongoDB 数据库适配器的实现方法&#xff0c;包括集合初始化、适配器接口实现以及里程碑存储支持。 一、MongoDB 集合初始化 协同编辑服务需要以下集合&#x…

Ubuntu 主机名:精通配置与管理

主机名&#xff08;hostname&#xff09;是Linux系统中用于标识网络上特定设备的名称&#xff0c;它在网络通信、服务配置&#xff08;如 Kubernetes 集群、数据库&#xff09;以及日志记录中扮演着至关重要的角色。对于初学者来说&#xff0c;配置主机名似乎很简单&#xff0c…

C/C++ 协程:Stackful 手动控制的工程必然性

&#x1f680; C/C 协程&#xff1a;Stackful 手动控制的工程必然性 引用&#xff1a; C/C 如何正确的切换协同程序&#xff1f;&#xff08;基于协程的并行架构&#xff09; #mermaid-svg-SXgplRf3WRYc8A7l {font-family:"trebuchet ms",verdana,arial,sans-serif;…

新手向:使用STM32通过RS485通信接口控制步进电机

新手向&#xff1a;使用STM32通过RS485通信接口控制步进电机 准备工作 本文使用的STM32芯片是STM32F407ZGTx&#xff0c;使用的电机是57步进电机&#xff0c;驱动器是用的是时代超群的RS485总线一体化步进电机驱动器&#xff08;42 型&#xff1a;ZD-M42P-485&#xff09;。使…

设计模式笔记_行为型_命令模式

1.命令模式介绍命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求或操作封装为对象&#xff0c;使得可以用不同的请求对客户端进行参数化。命令模式的核心思想是将方法调用、请求或操作封装到一个独立的命令对象中&#xff0c;从而使得客…

详解MySQL中的多表查询:多表查询分类讲解、七种JOIN操作的实现

精选专栏链接 &#x1f517; MySQL技术笔记专栏Redis技术笔记专栏大模型搭建专栏Python学习笔记专栏深度学习算法专栏 欢迎订阅&#xff0c;点赞&#xff0b;关注&#xff0c;每日精进1%&#xff0c;与百万开发者共攀技术珠峰 更多内容持续更新中&#xff01;希望能给大家带来…

vue3+elemeent-plus, el-tooltip的样式修改不生效

修改后的样式&#xff0c;直接贴图&#xff0c;经过删除出现悬浮1、在书写代码的时候切记effect“light”&#xff0c;如果你需要的是深色的样式:disabled"!multiple" 是否禁用<el-tooltip effect"light" placement"top" content"请先选…

网页作品惊艳亮相!这个浪浪山小妖怪网站太治愈了!

大家好呀&#xff01;今天要给大家分享一个超级治愈的网页作品——浪浪山小妖怪主题网站&#xff01;这个纯原生开发的项目不仅颜值在线&#xff0c;功能也很能打哦&#xff5e;至于灵感来源的话&#xff0c;要从一部动画说起。最近迷上了治愈系动画&#xff0c;就想做一个温暖…

搭建最新--若依分布式spring cloudv3.6.6 前后端分离项目--步骤与记录常见的坑

首先 什么拉取代码&#xff0c;安装数据库&#xff0c;安装redis&#xff0c;安装jdk这些我就不说了 导入数据库 &#xff1a;数据库是分库表的 &#xff0c;不要建错了 【一定要注意&#xff0c;不然nacos读取不到配置文件】这个是给nacos用的这个是给项目配置或项目用的2. 服…

分布式唯一 ID 生成方案

在复杂分布式系统中&#xff0c;往往需要对大量的数据和消息进行唯一标识。如在美团点评的金融、支付、餐饮、酒店、猫眼电影等产品的系统中&#xff0c;数据日渐增长&#xff0c;对数据分库分表后需要有一个唯一 ID 来标识一条数据或消息&#xff0c;数据库的自增 ID 显然不能…

飞算JavaAI赋能高吞吐服务器模拟:从0到百万级QPS的“流量洪峰”征服之旅

引言&#xff1a;当“流量洪峰”来袭&#xff0c;如何用低代码驯服高并发&#xff1f; 在数字化时代&#xff0c;从电商平台的“双11”大促到社交网络的突发热点事件&#xff0c;再到金融系统的实时交易高峰&#xff0c;服务器时刻面临着**高吞吐量&#xff08;High Throughput…

C#数据访问帮助类

一.中文注释using System; using System.Data; using System.Xml; using System.Data.SqlClient; using System.Collections;namespace Microsoft.ApplicationBlocks.Data.Ch {/// <summary>/// SqlServer数据访问帮助类/// </summary>public sealed class SqlHelp…

B站 韩顺平 笔记 (Day 21)

目录 1&#xff08;面向对象高级部分练习题&#xff09; 1.1&#xff08;题1&#xff09; 1.2&#xff08;题2&#xff09; 1.3&#xff08;题3&#xff09; Vehicles接口类&#xff1a; Horse类&#xff1a; Boat类&#xff1a; Plane类&#xff1a; VehiclesFactory…

Linux(十四)——进程管理和计划任务管理

文章目录前言一、程序与进程的关系1.1 程序与进程的定义1.2 父进程与子进程二、查看进程信息2.1 ps 命令&#xff08;重点&#xff09;2.2 动态查看进程信息top命令&#xff08;重点&#xff09;2.3 pgrep命令查询进程信息2.4 pstree命令以树形结构列出进程信息三、进程的启动方…

太阳光模拟器在无人机老化测试中的应用

在无人机技术飞速发展的当下&#xff0c;其户外作业环境复杂多变&#xff0c;长期暴露在阳光照射下&#xff0c;部件老化问题日益凸显&#xff0c;严重影响无人机的性能与寿命。紫创测控Luminbox专注于太阳光模拟器技术创新与精密光学测试系统开发&#xff0c;其涵盖的 LED、卤…

网络原理-TCP_IP

1.UDP&#xff08;即用户数据报协议&#xff09;UDP是一种无连接的传输层协议&#xff0c;提供简单的、不可靠的数据传输服务。它不保证数据包的顺序、可靠性或重复性&#xff0c;但具有低延迟和高效率的特点。UDP协议段格式16位UDP⻓度,表⽰整个数据报(UDP⾸部UDP数据)的最⼤⻓…