You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

  1. 先在GCP控制台创建名为transcode-completed的Pub/Sub主题
  2. 编写监听该主题的云函数:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 16:06:05