Google Cloud Platform:上传MP4文件转换为HLS文件的技术方案咨询
GCP Transcoder 转码任务(Firebase Cloud Functions Node.js 版)实现方案
前置依赖配置
- 先在GCP控制台开启
Cloud Video Transcoder API - 给Firebase Cloud Functions默认服务账号(格式为
项目id@appspot.gserviceaccount.com)授予以下权限:- Cloud Transcoder > 转码作业创建者
- Cloud Storage > 存储对象管理员(用于后续删除原文件)
- 安装Transcoder SDK:在functions目录下执行
npm install @google-cloud/video-transcoder
核心代码实现
第一步:编写storage onFinalize 触发转码的云函数
const functions = require("firebase-functions"); const { TranscoderServiceClient } = require('@google-cloud/video-transcoder'); const transcoderClient = new TranscoderServiceClient(); const { Storage } = require('@google-cloud/storage'); const storage = new Storage(); // 配置项,自行替换为实际值 const PROJECT_ID = '你的GCP项目ID'; const LOCATION = '转码任务运行区域,比如us-central1'; const HLS_OUTPUT_BUCKET = '存储HLS流的bucket名称'; // 输出bucket建议和上传bucket分开,避免误触发循环 exports.triggerTranscode = functions.storage.object().onFinalize(async (object) => { // 过滤非MP4文件,跳过转码输出目录的文件,避免循环触发 const fileName = object.name; const sourceBucket = object.bucket; if (!fileName.endsWith('.mp4') || sourceBucket === HLS_OUTPUT_BUCKET) { functions.logger.log('跳过非目标文件:', fileName); return null; } // 构造输入输出路径 const inputUri = `gs://${sourceBucket}/${fileName}`; // 输出路径用原文件名做独立目录,避免文件冲突 const outputUri = `gs://${HLS_OUTPUT_BUCKET}/hls/${fileName.replace('.mp4', '')}/`; // 构造转码配置 const jobConfig = { parent: transcoderClient.locationPath(PROJECT_ID, LOCATION), job: { inputUri: inputUri, outputUri: outputUri, config: { elementaryStreams: [ { key: 'video-stream0', videoStream: { h264: { heightPixels: 720, widthPixels: 1280, bitrateBps: 3000000, frameRate: 30, }, }, }, { key: 'audio-stream0', audioStream: { codec: 'aac', bitrateBps: 128000, }, }, ], muxStreams: [ { key: 'hls-ts', container: 'ts', elementaryStreams: ['video-stream0', 'audio-stream0'], segmentSettings: { segmentDuration: { seconds: 3, // 切片固定3秒,符合2-4秒的需求 }, }, }, ], manifests: [ { fileName: 'master.m3u8', type: 'HLS', muxStreams: ['hls-ts'], }, ], }, // 配置转码完成后发通知到Pub/Sub,用于后续删除原文件 pubsubDestination: { topic: `projects/${PROJECT_ID}/topics/transcode-completed` } }, }; // 提交转码任务 const [response] = await transcoderClient.createJob(jobConfig); functions.logger.log('转码任务提交成功,任务ID:', response.name); return null; });
第二步:监听转码完成事件删除原MP4
- 先在GCP控制台创建名为
transcode-completed的Pub/Sub主题 - 编写监听该主题的云函数:
exports.deleteOriginalMp4 = functions.pubsub.topic('transcode-completed').onPublish(async (message) => { const job = message.json; // 校验转码任务状态,仅转码成功才删除原文件 if (job.state !== 'SUCCEEDED') { functions.logger.error('转码任务失败,不删除原文件,任务ID:', job.name); return null; } // 从输入地址解析原文件的bucket和文件名 const inputUri = job.inputUri; const [, bucketName, fileName] = inputUri.match(/^gs:\/\/([^\/]+)\/(.+)$/); // 删除原文件 await storage.bucket(bucketName).file(fileName).delete(); functions.logger.log('原MP4文件删除成功:', inputUri); return null; });
注意事项
- 云函数部署时建议将内存配置为256M以上,超时时间设置为60秒
- 如果上传bucket和HLS输出bucket必须复用,一定要在触发函数里加严格的路径/文件后缀过滤逻辑,避免转码生成的文件再次触发转码任务造成循环
- 转码区域尽量和存储bucket的区域保持一致,避免产生跨区域流量费用和额外延迟
内容的提问来源于stack exchange,提问作者Pete Streem
相关产品推荐
相关产品推荐

