{"componentChunkName":"component---src-templates-best-practice-detail-tsx","path":"/best-practice/2020-05-03-video-compression","result":{"data":{"currentBlog":{"id":"71466a42-3d49-5877-a603-88867ab2d9e2","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020511/1589207417703-ZalNtxgQAC_small.jpg","authors":["Anycodes"],"categories":["best-practice"],"date":"2020-05-03T00:00:00.000Z","title":"Serverless 实现视频压缩与格式转换","description":"在 Serverless 架构的应用案例中，有这样一个非常实在的应用：视频处理，我们应该如何实现它呢？","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["Serverless","视频处理"],"keywords":"Serverless 多环境配置,Serverless 管理环境,Serverless配置方案","outdated":true},"wordCount":{"words":404,"sentences":48,"paragraphs":48},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2020-05-03-video-compression.md","fields":{"slug":"/best-practice/2020-05-03-video-compression/","keywords":["go","python","serverless","函数计算","云函数","ffmpeg","视频","copy","output","Serverless"]},"html":"<p>在 Serverless 架构的应用案例中，有这样一个非常实在的应用：视频的处理。</p>\n<p>腾讯云的函数计算平台对这个领域的描述：</p>\n<blockquote>\n<p>视频应用、社交应用等场景下，用户上传的图片、音视频的总量大、频率高，对处理系统的实时性和并发能力都有较高的要求。例如：对于用户上传的视频短片，我们可以使用多个云函数对其分别处理，对应不同的清晰度（1080p、720p 等），以满足不同场景下用户的需求，适应移动网络带宽较小且不稳定的特性。</p>\n</blockquote>\n<p><img src=\"https://img.serverlesscloud.cn/202058/2-6-1.png\"></p>\n<p>在阿里云的函数计算也有相关的描述：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/2-6-2.png\"></p>\n<p>所以可以看到视频的压缩/转码等操作，在 Serverless 架构下确实是一个很好的典型应用。那么有了这样的一个\"典型\"应用，我们应该如何实现它呢？</p>\n<h2 id=\"准备开始-ffmpeg\"><a href=\"#%E5%87%86%E5%A4%87%E5%BC%80%E5%A7%8B-ffmpeg\" aria-label=\"准备开始 ffmpeg permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>准备开始 FFmpeg</h2>\n<p>在百科上可以看到这样的描述：</p>\n<blockquote>\n<p>FFmpeg 是一套可以用来记录、转换数字音频、视频，并能将其转化为流的开源计算机程序。采用 LGPL 或 GPL 许可证。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库 libavcodec，为了保证高可移植性和编解码质量，libavcodec 里很多 code 都是从头开发的。\nFFmpeg 在 Linux 平台下开发，但它同样也可以在其它操作系统环境中编译运行，包括 Windows、macOS 等。这个项目最早由Fabrice Bellard 发起，2004 年至 2015 年间由 Michael Niedermayer 主要负责维护。许多 FFmpeg 的开发人员都来自 MPlayer 项目，而且当前 FFmpeg 也是放在 MPlayer 项目组的服务器上。项目的名称来自 MPEG 视频编码标准，前面的「FF」代表「Fast Forward」。</p>\n</blockquote>\n<p>而在实际生产生活中，ffmpeg 确实也是一个非常好的工具，我们可以通过这个工具来进行图像的压缩/转码等操作。</p>\n<p>通过 ffmpeg 的官网，我们可以看到不同的操作系统，有着不同的文件供我们选择：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/2-6-3.png\"></p>\n<p>也就是说，我们如果要在云函数中使用这个模块，那么我们就要有这样一个模块是在云函数所在的环境下可以运行起来的，根据云函数的文档可以看到：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/2-6-4.png\"></p>\n<p>也就是说，我们要有一个在 CentOS 操作系统下可以使用的 ffmpeg，接下来，我们就准备这个文件：</p>\n<ol>\n<li>在 CentOS 操作系统上，下载源码包：<code class=\"language-text\">wget http://www.ffmpeg.org/releases/ffmpeg-3.1.tar.gz</code></li>\n<li>解压并进入目录：<code class=\"language-text\">tar -zxvf ffmpeg-3.1.tar.gz &amp;&amp; cd ffmpeg-3.1</code></li>\n<li>编译安装： <code class=\"language-text\">./configure &amp;&amp; make &amp;&amp; make install</code></li>\n</ol>\n<p>在进行 <code class=\"language-text\">./configure</code> 操作的时候，可能出现 <code class=\"language-text\">yasm/nasm not found or too old. Use --disable-yasm for a crippledbuild</code> 错误。</p>\n<p>yasm 是汇编编译器，ffmpeg 为了提高效率使用了汇编指令，如 MMX 和 SSE 等。所以系统中未安装 yasm 时，就会报错误，此时可以安装 yasm 编译器来解决：</p>\n<ol>\n<li>下载 <code class=\"language-text\">wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz</code></li>\n<li>解压并进入目录：<code class=\"language-text\">tar zxvf yasm-1.3.0.tar.gz &amp;&amp; cd yasm-1.3.0</code></li>\n<li>编译安装：<code class=\"language-text\">./configure &amp;&amp; make &amp;&amp; make install</code></li>\n</ol>\n<p>完成 ffmpeg 的编译安装，可以在当前目录下看到生成了文件：ffmpeg</p>\n<p>此时我们保存这个文件即可在腾讯云的云函数中使用。</p>\n<h2 id=\"serverless-助力视频压缩\"><a href=\"#serverless-%E5%8A%A9%E5%8A%9B%E8%A7%86%E9%A2%91%E5%8E%8B%E7%BC%A9\" aria-label=\"serverless 助力视频压缩 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Serverless 助力视频压缩</h2>\n<p>按照腾讯云提供的时间架构图，我们可以看到其推荐的是对象存储触发器触发函数，也就是说我们将视频存储到对象存储中，然后通过对象存储的相关触发器触发函数，进行视频的处理，处理之后再回传对象存储的操作。</p>\n<p>代码实现：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"5112509200194348000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import os\nimport subprocess\nfrom qcloud_cos_v5 import CosConfig\nfrom qcloud_cos_v5 import CosS3Client\n\nsecret_id = os.environ.get('secret_id')\nsecret_key = os.environ.get('secret_key')\nregion = os.environ.get('region')\ncosClient = CosS3Client(CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key))\n\n# 移动 ffmpeg 到 tmp 目录，并且赋予权限\nwith open(&quot;./ffmpeg&quot;, &quot;rb&quot;) as rf:\n    with open(&quot;/tmp/ffmpeg&quot;, &quot;wb&quot;) as wf:\n        wf.write(rf.read())\nsubprocess.run('chmod 755 /tmp/ffmpeg', shell=True)\n\ndef main_handler(event, context):\n\n    for record in event['Records']:\n        bucket = record['cos']['cosBucket']['name'] + '-' + record['cos']['cosBucket']['appid']\n        key = &quot;/&quot;.join(record['cos']['cosObject']['key'].split(&quot;/&quot;)[3:])\n        download_path = '/tmp/{}'.format(key.split('/')[-1])\n        upload_path = '/tmp/new_mp4-{}'.format(key.split('/')[-1])\n\n        # 下载图片\n        print(&quot;key&quot;, key)\n        response = cosClient.get_object(Bucket=bucket, Key=key)\n        response['Body'].get_stream_to_file(download_path)\n\n        # 执行 ffmpeg 指令压缩视频\n        child = subprocess.run('/tmp/ffmpeg  -i %s -r 10 -b:a 32k %s'%(download_path, upload_path), stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True)\n\n        # 上传图片\n        cosClient.put_object_from_local_file(\n            Bucket=bucket,\n            LocalFilePath=upload_path,\n            Key=&quot;/new_mp4/&quot; + key.split('/')[-1]\n        )`, `5112509200194348000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> os\n<span class=\"token keyword\">import</span> subprocess\n<span class=\"token keyword\">from</span> qcloud_cos_v5 <span class=\"token keyword\">import</span> CosConfig\n<span class=\"token keyword\">from</span> qcloud_cos_v5 <span class=\"token keyword\">import</span> CosS3Client\n\nsecret_id <span class=\"token operator\">=</span> os<span class=\"token punctuation\">.</span>environ<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">'secret_id'</span><span class=\"token punctuation\">)</span>\nsecret_key <span class=\"token operator\">=</span> os<span class=\"token punctuation\">.</span>environ<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">'secret_key'</span><span class=\"token punctuation\">)</span>\nregion <span class=\"token operator\">=</span> os<span class=\"token punctuation\">.</span>environ<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">'region'</span><span class=\"token punctuation\">)</span>\ncosClient <span class=\"token operator\">=</span> CosS3Client<span class=\"token punctuation\">(</span>CosConfig<span class=\"token punctuation\">(</span>Region<span class=\"token operator\">=</span>region<span class=\"token punctuation\">,</span> SecretId<span class=\"token operator\">=</span>secret_id<span class=\"token punctuation\">,</span> SecretKey<span class=\"token operator\">=</span>secret_key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\"># 移动 ffmpeg 到 tmp 目录，并且赋予权限</span>\n<span class=\"token keyword\">with</span> <span class=\"token builtin\">open</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"./ffmpeg\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"rb\"</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> rf<span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">with</span> <span class=\"token builtin\">open</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/tmp/ffmpeg\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"wb\"</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> wf<span class=\"token punctuation\">:</span>\n        wf<span class=\"token punctuation\">.</span>write<span class=\"token punctuation\">(</span>rf<span class=\"token punctuation\">.</span>read<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nsubprocess<span class=\"token punctuation\">.</span>run<span class=\"token punctuation\">(</span><span class=\"token string\">'chmod 755 /tmp/ffmpeg'</span><span class=\"token punctuation\">,</span> shell<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">main_handler</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n\n    <span class=\"token keyword\">for</span> record <span class=\"token keyword\">in</span> event<span class=\"token punctuation\">[</span><span class=\"token string\">'Records'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span>\n        bucket <span class=\"token operator\">=</span> record<span class=\"token punctuation\">[</span><span class=\"token string\">'cos'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">'cosBucket'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">'name'</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">+</span> <span class=\"token string\">'-'</span> <span class=\"token operator\">+</span> record<span class=\"token punctuation\">[</span><span class=\"token string\">'cos'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">'cosBucket'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">'appid'</span><span class=\"token punctuation\">]</span>\n        key <span class=\"token operator\">=</span> <span class=\"token string\">\"/\"</span><span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span>record<span class=\"token punctuation\">[</span><span class=\"token string\">'cos'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">'cosObject'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">'key'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>split<span class=\"token punctuation\">(</span><span class=\"token string\">\"/\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">3</span><span class=\"token punctuation\">:</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        download_path <span class=\"token operator\">=</span> <span class=\"token string\">'/tmp/{}'</span><span class=\"token punctuation\">.</span><span class=\"token builtin\">format</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">.</span>split<span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        upload_path <span class=\"token operator\">=</span> <span class=\"token string\">'/tmp/new_mp4-{}'</span><span class=\"token punctuation\">.</span><span class=\"token builtin\">format</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">.</span>split<span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n        <span class=\"token comment\"># 下载图片</span>\n        <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"key\"</span><span class=\"token punctuation\">,</span> key<span class=\"token punctuation\">)</span>\n        response <span class=\"token operator\">=</span> cosClient<span class=\"token punctuation\">.</span>get_object<span class=\"token punctuation\">(</span>Bucket<span class=\"token operator\">=</span>bucket<span class=\"token punctuation\">,</span> Key<span class=\"token operator\">=</span>key<span class=\"token punctuation\">)</span>\n        response<span class=\"token punctuation\">[</span><span class=\"token string\">'Body'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>get_stream_to_file<span class=\"token punctuation\">(</span>download_path<span class=\"token punctuation\">)</span>\n\n        <span class=\"token comment\"># 执行 ffmpeg 指令压缩视频</span>\n        child <span class=\"token operator\">=</span> subprocess<span class=\"token punctuation\">.</span>run<span class=\"token punctuation\">(</span><span class=\"token string\">'/tmp/ffmpeg  -i %s -r 10 -b:a 32k %s'</span><span class=\"token operator\">%</span><span class=\"token punctuation\">(</span>download_path<span class=\"token punctuation\">,</span> upload_path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> stdout<span class=\"token operator\">=</span>subprocess<span class=\"token punctuation\">.</span>PIPE<span class=\"token punctuation\">,</span> stderr<span class=\"token operator\">=</span>subprocess<span class=\"token punctuation\">.</span>PIPE<span class=\"token punctuation\">,</span> close_fds<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">,</span> shell<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\n\n        <span class=\"token comment\"># 上传图片</span>\n        cosClient<span class=\"token punctuation\">.</span>put_object_from_local_file<span class=\"token punctuation\">(</span>\n            Bucket<span class=\"token operator\">=</span>bucket<span class=\"token punctuation\">,</span>\n            LocalFilePath<span class=\"token operator\">=</span>upload_path<span class=\"token punctuation\">,</span>\n            Key<span class=\"token operator\">=</span><span class=\"token string\">\"/new_mp4/\"</span> <span class=\"token operator\">+</span> key<span class=\"token punctuation\">.</span>split<span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span>\n        <span class=\"token punctuation\">)</span></code></pre></div>\n<p>这里的主要操作就是在容器建立的时候，或者说是函数冷启动的时候，将 ffmpeg 复制到可执行目录，并且设置其权限为 <code class=\"language-text\">755</code>。</p>\n<p>完成之后可以进行<code class=\"language-text\">serverless.yaml</code>的编写：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"86307008725368540000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`MyVideo:\n  component: &quot;@serverless/tencent-scf&quot;\n  inputs:\n    name: MyVideo\n    codeUri: ./\n    handler: index.main_handler\n    runtime: Python3.6\n    region: ap-guangzhou\n    memorySize: 128\n    timeout: 200\n    environment:\n      variables:\n        secret_id: 用户密钥id\n        secret_key: 用户密钥key\n        region: ap-guangzhou\n    events:\n      - cos:\n          name: video-1256773370.cos.ap-guangzhou.myqcloud.com\n          parameters:\n            bucket: video-1256773370.cos.ap-guangzhou.myqcloud.com\n            filter:\n              prefix: source/\n            events: cos:ObjectCreated:*\n            enable: true`, `86307008725368540000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">MyVideo</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"@serverless/tencent-scf\"</span>\n  <span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> MyVideo\n    <span class=\"token key atrule\">codeUri</span><span class=\"token punctuation\">:</span> ./\n    <span class=\"token key atrule\">handler</span><span class=\"token punctuation\">:</span> index.main_handler\n    <span class=\"token key atrule\">runtime</span><span class=\"token punctuation\">:</span> Python3.6\n    <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>guangzhou\n    <span class=\"token key atrule\">memorySize</span><span class=\"token punctuation\">:</span> <span class=\"token number\">128</span>\n    <span class=\"token key atrule\">timeout</span><span class=\"token punctuation\">:</span> <span class=\"token number\">200</span>\n    <span class=\"token key atrule\">environment</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">variables</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">secret_id</span><span class=\"token punctuation\">:</span> 用户密钥id\n        <span class=\"token key atrule\">secret_key</span><span class=\"token punctuation\">:</span> 用户密钥key\n        <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>guangzhou\n    <span class=\"token key atrule\">events</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">cos</span><span class=\"token punctuation\">:</span>\n          <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> video<span class=\"token punctuation\">-</span>1256773370.cos.ap<span class=\"token punctuation\">-</span>guangzhou.myqcloud.com\n          <span class=\"token key atrule\">parameters</span><span class=\"token punctuation\">:</span>\n            <span class=\"token key atrule\">bucket</span><span class=\"token punctuation\">:</span> video<span class=\"token punctuation\">-</span>1256773370.cos.ap<span class=\"token punctuation\">-</span>guangzhou.myqcloud.com\n            <span class=\"token key atrule\">filter</span><span class=\"token punctuation\">:</span>\n              <span class=\"token key atrule\">prefix</span><span class=\"token punctuation\">:</span> source/\n            <span class=\"token key atrule\">events</span><span class=\"token punctuation\">:</span> cos<span class=\"token punctuation\">:</span>ObjectCreated<span class=\"token punctuation\">:</span>*\n            <span class=\"token key atrule\">enable</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">true</span></code></pre></div>\n<p>部署完成之后，我们将一个测试的MP4文件上传到对应的存储的<code class=\"language-text\">source/</code>文件夹中：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/2-6-5.png\"></p>\n<p>稍等片刻，我们可以看到目标文件夹出现了对应的视频：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/2-6-6.png\"></p>\n<p>可以看到两个视频文件的差距。</p>\n<p>当然，这里仅仅是通过 <code class=\"language-text\">/tmp/ffmpeg  -i 原视频 -r 10 -b:a 32k 生成视频</code> 来进行视频压缩，除此之外，我们还可以使用 ffmpeg 进行额外的操作（以下内容来源于 canmeng 的博客）：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"75421419548830800000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`ffmpeg -ss 00:00:00 -t 00:00:30 -i test.mp4 -vcodec copy -acodec copy output.mp4`, `75421419548830800000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">ffmpeg -ss 00:00:00 -t 00:00:30 -i test.mp4 -vcodec copy -acodec copy output.mp4</code></pre></div>\n<blockquote>\n<p>-ss 指定从什么时间开始\n-t 指定需要截取多长时间\n-i 指定输入文件</p>\n</blockquote>\n<p>这个命令就是从 00 秒开始裁剪到 00+30=30 秒结束，总共 30 秒的视频。这个命令执行很快，因为只是原始数据的拷贝，中间没有什么编码和解码的过程。执行这个命令后你能得到 <code class=\"language-text\">output.mp4</code> 这个输出文件。你可以用视频播放软件播放这个视频看看。</p>\n<p>可能有些视频裁剪后的效果，如期望一致，00 秒开始，30 秒结束，总共 30 秒的视频，但是有些视频裁剪后你会发现可能开始和结束都不是很准确，有可能是从 00 秒开始，33 秒结束。这是为什么呢？</p>\n<p>因为这些视频里 30 秒处地方刚好不是关键帧，而 ffmpeg 会在你输入的时间点附近圆整到最接近的关键帧处，然后做接下来的事情。如果你不懂什么是关键帧，没关系，这也不影响你使用这个命令。</p>\n<p>合并视频</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"43384954571033354000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`//截取从头开始的30s\nffmpeg -ss 00:00:00 -t 00:00:30 -i keyoutput.mp4 -vcodec copy -acodec copy split.mp4\n//截取从30s开始的30s\nffmpeg -ss 00:00:30 -t 00:00:30 -i keyoutput.mp4 -vcodec copy -acodec copy split1.mp4\n//进行视频的合并\nffmpeg -f concat -i list.txt -c copy concat.mp4`, `43384954571033354000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">//截取从头开始的30s\nffmpeg -ss 00:00:00 -t 00:00:30 -i keyoutput.mp4 -vcodec copy -acodec copy split.mp4\n//截取从30s开始的30s\nffmpeg -ss 00:00:30 -t 00:00:30 -i keyoutput.mp4 -vcodec copy -acodec copy split1.mp4\n//进行视频的合并\nffmpeg -f concat -i list.txt -c copy concat.mp4</code></pre></div>\n<p>在list.txt文件中，对要合并的视频片段进行了描述。\n内容如下</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"15790717831712332000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`file ./split.mp4\nfile ./split1.mp4`, `15790717831712332000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">file ./split.mp4\nfile ./split1.mp4</code></pre></div>\n<p>更多常用命令：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"68921100883494765000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`ffmpeg -i in.mp4 -filter:v &quot;crop=in_w:in_h-40&quot; -c:a copy out.mp4\n// 去掉视频中的音频\nffmpeg -i input.mp4 -vcodec copy -an output.mp4\n// -an: 去掉音频；-vcodec:视频选项，一般后面加copy表示拷贝\n\n// 提取视频中的音频\nffmpeg -i input.mp4 -acodec copy -vn output.mp3\n// -vn: 去掉视频；-acodec: 音频选项， 一般后面加copy表示拷贝\n\n// 音视频合成\nffmpeg -y –i input.mp4 –i input.mp3 –vcodec copy –acodec copy output.mp4\n// -y 覆盖输出文件\n\n//剪切视频\nffmpeg -ss 0:1:30 -t 0:0:20 -i input.mp4 -vcodec copy -acodec copy output.mp4\n// -ss 开始时间; -t 持续时间\n\n// 视频截图\nffmpeg –i test.mp4 –f image2 -t 0.001 -s 320x240 image-%3d.jpg\n// -s 设置分辨率; -f 强迫采用格式fmt;\n\n// 视频分解为图片\nffmpeg –i test.mp4 –r 1 –f image2 image-%3d.jpg\n// -r 指定截屏频率\n\n// 将图片合成视频\nffmpeg -f image2 -i image%d.jpg output.mp4\n\n//视频拼接\nffmpeg -f concat -i filelist.txt -c copy output.mp4\n\n// 将视频转为gif\nffmpeg -i input.mp4 -ss 0:0:30 -t 10 -s 320x240 -pix_fmt rgb24 output.gif\n// -pix_fmt 指定编码\n\n// 将视频前30帧转为gif\nffmpeg -i input.mp4 -vframes 30 -f gif output.gif\n\n// 旋转视频\nffmpeg -i input.mp4 -vf rotate=PI/2 output.mp4\n\n// 缩放视频\nffmpeg -i input.mp4 -vf scale=iw/2:-1 output.mp4\n// iw 是输入的宽度， iw/2就是一半;-1 为保持宽高比\n\n//视频变速\nffmpeg -i input.mp4 -filter:v setpts=0.5*PTS output.mp4\n\n//音频变速\nffmpeg -i input.mp3 -filter:a atempo=2.0 output.mp3\n\n//音视频同时变速，但是音视频为互倒关系\nffmpeg -i input.mp4 -filter_complex &quot;[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]&quot; -map &quot;[v]&quot; -map &quot;[a]&quot; output.mp4\n\n// 视频添加水印\nffmpeg -i input.mp4 -i logo.jpg -filter_complex [0:v][1:v]overlay=main_w-overlay_w-10:main_h-overlay_h-10[out] -map [out] -map 0:a -codec:a copy output.mp4\n// main_w-overlay_w-10 视频的宽度-水印的宽度-水印边距；\n\n// 截取视频局部\nffmpeg -i in.mp4 -filter:v &quot;crop=out_w:out_h:x:y&quot; out.mp4\n// 截取部分视频，从[80,60]的位置开始，截取宽200，高100的视频\nffmpeg -i in.mp4 -filter:v &quot;crop=80:60:200:100&quot; -c:a copy out.mp4\n// 截取右下角的四分之一\nffmpeg -i in.mp4 -filter:v &quot;crop=in_w/2:in_h/2:in_w/2:in_h/2&quot; -c:a copy out.mp4\n// 截去底部40像素高度\nffmpeg -i in.mp4 -filter:v &quot;crop=in_w:in_h-40&quot; -c:a copy out.mp4`, `68921100883494765000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">ffmpeg -i in.mp4 -filter:v &quot;crop=in_w:in_h-40&quot; -c:a copy out.mp4\n// 去掉视频中的音频\nffmpeg -i input.mp4 -vcodec copy -an output.mp4\n// -an: 去掉音频；-vcodec:视频选项，一般后面加copy表示拷贝\n\n// 提取视频中的音频\nffmpeg -i input.mp4 -acodec copy -vn output.mp3\n// -vn: 去掉视频；-acodec: 音频选项， 一般后面加copy表示拷贝\n\n// 音视频合成\nffmpeg -y –i input.mp4 –i input.mp3 –vcodec copy –acodec copy output.mp4\n// -y 覆盖输出文件\n\n//剪切视频\nffmpeg -ss 0:1:30 -t 0:0:20 -i input.mp4 -vcodec copy -acodec copy output.mp4\n// -ss 开始时间; -t 持续时间\n\n// 视频截图\nffmpeg –i test.mp4 –f image2 -t 0.001 -s 320x240 image-%3d.jpg\n// -s 设置分辨率; -f 强迫采用格式fmt;\n\n// 视频分解为图片\nffmpeg –i test.mp4 –r 1 –f image2 image-%3d.jpg\n// -r 指定截屏频率\n\n// 将图片合成视频\nffmpeg -f image2 -i image%d.jpg output.mp4\n\n//视频拼接\nffmpeg -f concat -i filelist.txt -c copy output.mp4\n\n// 将视频转为gif\nffmpeg -i input.mp4 -ss 0:0:30 -t 10 -s 320x240 -pix_fmt rgb24 output.gif\n// -pix_fmt 指定编码\n\n// 将视频前30帧转为gif\nffmpeg -i input.mp4 -vframes 30 -f gif output.gif\n\n// 旋转视频\nffmpeg -i input.mp4 -vf rotate=PI/2 output.mp4\n\n// 缩放视频\nffmpeg -i input.mp4 -vf scale=iw/2:-1 output.mp4\n// iw 是输入的宽度， iw/2就是一半;-1 为保持宽高比\n\n//视频变速\nffmpeg -i input.mp4 -filter:v setpts=0.5*PTS output.mp4\n\n//音频变速\nffmpeg -i input.mp3 -filter:a atempo=2.0 output.mp3\n\n//音视频同时变速，但是音视频为互倒关系\nffmpeg -i input.mp4 -filter_complex &quot;[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]&quot; -map &quot;[v]&quot; -map &quot;[a]&quot; output.mp4\n\n// 视频添加水印\nffmpeg -i input.mp4 -i logo.jpg -filter_complex [0:v][1:v]overlay=main_w-overlay_w-10:main_h-overlay_h-10[out] -map [out] -map 0:a -codec:a copy output.mp4\n// main_w-overlay_w-10 视频的宽度-水印的宽度-水印边距；\n\n// 截取视频局部\nffmpeg -i in.mp4 -filter:v &quot;crop=out_w:out_h:x:y&quot; out.mp4\n// 截取部分视频，从[80,60]的位置开始，截取宽200，高100的视频\nffmpeg -i in.mp4 -filter:v &quot;crop=80:60:200:100&quot; -c:a copy out.mp4\n// 截取右下角的四分之一\nffmpeg -i in.mp4 -filter:v &quot;crop=in_w/2:in_h/2:in_w/2:in_h/2&quot; -c:a copy out.mp4\n// 截去底部40像素高度\nffmpeg -i in.mp4 -filter:v &quot;crop=in_w:in_h-40&quot; -c:a copy out.mp4</code></pre></div>\n<p>参数说明：</p>\n<blockquote>\n<p>-vcodec xvid 使用xvid压缩\n-s 320×240 指定分辨率\n-r fps 设置帧频 缺省25\n-b &#x3C;比特率> 指定压缩比特\n-acodec aac 设定声音编码\n-ac &#x3C;数值> 设定声道数，1就是单声道，2就是立体声\n-ar &#x3C;采样率> 设定声音采样率，PSP只认24000\n-ab &#x3C;比特率> 设定声音比特率\n-vol &#x3C;百分比> 设定音量\n-y 覆盖输出文件\n-t duration 设置纪录时间 hh:mm:ss[.xxx]格式的记录时间也支持\n-ss position 搜索到指定的时间 [-]hh:mm:ss[.xxx]的格式也支持\n-title string 设置标题\n-author string 设置作者\n-copyright string 设置版权\n-hq 激活高质量设置\n-aspect aspect 设置横纵比 4:3 16:9 或 1.3333 1.7777\n-croptop size 设置顶部切除带大小 像素单位\n-cropbottom size -cropleft size -cropright size\n-padtop size 设置顶部补齐的大小 像素单位\n-padbottom size -padleft size -padright size -padcolor color 设置补齐条颜色(hex,6个16进制的数，红:绿:兰排列，比如 000000代表黑色)\n-bt tolerance 设置视频码率容忍度kbit/s\n-maxrate bitrate设置最大视频码率容忍度\n-minrate bitreate 设置最小视频码率容忍度\n-bufsize size 设置码率控制缓冲区大小\n-vcodec codec 强制使用codec编解码方式。 如果用copy表示原始编解码数据必须被拷贝\n-sameq 使用同样视频质量作为源（VBR）\n-pass n 选择处理遍数（1或者2）。两遍编码非常有用。第一遍生成统计信息，第二遍生成精确的请求的码率\n-passlogfile file 选择两遍的纪录文件名为file\n-map file:stream 设置输入流映射\n-debug 打印特定调试信息</p>\n</blockquote>\n<h2 id=\"总结\"><a href=\"#%E6%80%BB%E7%BB%93\" aria-label=\"总结 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>总结</h2>\n<p>Serverless 架构在做一些同步的业务是有很不错效果的，同时 Serverless 架构在异步的一些流程上，也有很棒的表现，无论是通过 Serverless 架构做大数据的分析实现 MapReduce，还是做图像的压缩、水印和格式转换，抑或本文分享的视频相关的处理。</p>\n<p>通过 Serverless 架构，我们还可以挖掘更多领域的应用，例如通过 Serverless 架构做一个 Word/PPT 转 PDF 的工具等。Serverless 架构的行业应用，领域应用，需要更多人提供更多的实践。</p>\n<hr>\n<div id='scf-deploy-iframe-or-md'></div>\n<hr>\n<blockquote>\n<p><strong>传送门：</strong></p>\n<ul>\n<li>GitHub: <a href=\"https://github.com/serverless/serverless/blob/master/README_CN.md\">github.com/serverless</a></li>\n<li>官网：<a href=\"https://serverless.com/\">serverless.com</a></li>\n</ul>\n</blockquote>\n<p>欢迎访问：<a href=\"https://serverlesscloud.cn/\">Serverless 中文网</a>，您可以在 <a href=\"https://serverlesscloud.cn/best-practice\">最佳实践</a> 里体验更多关于 Serverless 应用的开发！</p>","tableOfContents":"<ul>\n<li><a href=\"/best-practice/2020-05-03-video-compression/#%E5%87%86%E5%A4%87%E5%BC%80%E5%A7%8B-ffmpeg\">准备开始 FFmpeg</a></li>\n<li><a href=\"/best-practice/2020-05-03-video-compression/#serverless-%E5%8A%A9%E5%8A%9B%E8%A7%86%E9%A2%91%E5%8E%8B%E7%BC%A9\">Serverless 助力视频压缩</a></li>\n<li><a href=\"/best-practice/2020-05-03-video-compression/#%E6%80%BB%E7%BB%93\">总结</a></li>\n</ul>"},"previousBlog":{"id":"60cdbe64-7a7b-5d2a-a46c-135944060867","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020511/1589207417692-ZalNtxgQAC_small.jpg","authors":["Anycodes"],"categories":["best-practice"],"date":"2020-05-04T00:00:00.000Z","title":"Serverless 实现图片压缩与水印","description":"图片和 Web 服务非常紧密，在 Serverless 架构下，是否有一种方法，可以对图像的压缩与水印实现「一条龙」服务，而且不会因为用户量比较多，而影响用户整体体验呢？","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["Serverless","图片压缩"],"keywords":"Serverless 多环境配置,Serverless 管理环境,Serverless配置方案","outdated":true},"wordCount":{"words":179,"sentences":36,"paragraphs":36},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2020-05-04-image-compression.md","fields":{"slug":"/best-practice/2020-05-04-image-compression/","keywords":["go","python","serverless","函数计算","云函数","image","width","水印","cos","height","图片","size","txtImage"]}},"nextBlog":{"id":"b5efeaba-7d1a-5fec-8de9-ffb2112d5bfc","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020428/1588065568171-scf6000s.jpg","authors":["Vinson"],"categories":["guides-and-tutorials"],"date":"2020-04-28T00:00:00.000Z","title":"云函数如何做到 1 分钟创建 6000 台云服务器？","description":"解密背后承担云服务器创建任务的 CBS 云硬盘快照服务","authorslink":["https://serverlesscloud.cn"],"translators":null,"translatorslink":null,"tags":["Serverless","云函数"],"keywords":"Serverless;云函数 SCF;","outdated":null},"wordCount":{"words":244,"sentences":48,"paragraphs":48},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-04-28-scf-bs.md","fields":{"slug":"/blog/2020-04-28-scf-bs/","keywords":["serverless","无服务器","云函数","云原生","硬盘","快照","服务器","创建","拷贝","回滚","系统"]}}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"71466a42-3d49-5877-a603-88867ab2d9e2","previousBlogId":"60cdbe64-7a7b-5d2a-a46c-135944060867","nextBlogId":"b5efeaba-7d1a-5fec-8de9-ffb2112d5bfc"}}}