十分钟上线 - FC&OSS构建serverless视频截取雪碧图服务

本文涉及的产品
简介: 在一些视频处理的场景求中,有对视频制作雪碧图需求,简单来说,就是对视频的按照一定的规律就行截帧,然后将截帧的图片组合成一张大的雪碧图。 本文基于对象存储服务 OSS 和函数计算 FC, 实现弹性高可用的海量视频的存储和雪碧图制作处理服务。

前言

CSS Sprite,多个图片被整合到一个精灵图中,用户不需要下载多个文件,而是只需要下载单个文件,当需要特定的图像时,CSS引用这张雪碧图,通过偏移和定义尺寸来达到目的。CSS Sprite 具有如下优点:

  • 更流畅的用户体验,因为一旦雪碧图被下载,所有使用雪碧图上面的图片的地方都会得到渲染,而不是一个文件一个文件的加载。
  • 减少HTTP请求,将原本需要的多个请求合并为一个,较少服务器压力,从而较少网络堵塞。
  • 减少图片的字节。多次比较,三张图片合并成一张图片之后的字节总是小于这三长图片的总和。
  • 更换风格方便,只需要在一张或少张图片上修改图片的颜色或样式,维护起来更加方便。

在一些视频处理的场景求中,有对视频制作雪碧图需求,简单来说,就是对视频的按照一定的规律就行截帧,然后将截帧的图片组合成一张大的雪碧图。 本文基于对象存储服务 OSS 和函数计算 FC, 实现弹性高可用的海量视频的存储和雪碧图制作处理服务。

体验入口地址

  • 10 * 10 的雪碧图

http://fcdemo.mofangdegisn.cn/vedio-process?srcdir=video&video=test_hd.mp4&w=144&h=128&offset=100&interval=20&sprite=10*10&saveas=picture&capture_num=100

  • 4 * 4 竖屏视频雪碧图(不变形处理)

http://fcdemo.mofangdegisn.cn/vedio-process?srcdir=video&video=shupin.mp4&w=144&h=128&offset=100&interval=20&sprite=4*4&saveas=picture&capture_num=16&autofill=1&oringin_ratio=0.5625

该体验地址采用的函数计算的http trigger, 其中:

返回值

截图的zip包和雪碧图的url

参数意义:

  • srcdir: oss bucket 中上传视频的目录
  • video :视频的名字
  • w: 视频截帧的宽度
  • h: 视频截帧的高度
  • offset: 视频截帧从起始处,单位是秒
  • interval: 每多少秒截一次
  • sprite:雪碧图的组成,列数*行数
  • capture_num: 需要截图的总数
  • saveas: 截图zip包和雪碧图在oss 上保存的目录
  • autofill: 和oringin_ratio(原始视频的 宽度/高度 比)一起使用,当值为1时, 竖屏处理

架构设计图

image

利用oss的存储、单帧截图能力与函数计算的自定义处理能力,可以实现各种自定义的视频截图与图片组合操作,再也不用担心各家厂商提供的视频截图功能无法满足自定义功能的情况,同时,OSS 的海量高可靠 和 FC 的弹性高可用相结合,整体服务也就具有了按需付费、弹性扩容、数据高可靠,计算高可用,免运维等一系列优点。

代码

# -*- coding: utf-8 -*-
from wand.image import Image
from wand.color import Color
from threading import Thread
import oss2
from urllib import parse
import math
import zipfile
import io

bucket_name = 'xbt-video'
oss_region = "cn-hangzhou"
images = []

# 获取oss 视频截帧的返回图片
def video_capture(bucket, srcdir, vedio_name, t, process):
  cap_pic = vedio_name + '_' + str(t) + '.png'
  r = bucket.get_object(srcdir + "/" + vedio_name, process=process)
  content = r.read()
  images.append(content)

def handler(environ, start_response):
  global images
  images = []

  context = environ['fc.context']
  creds = context.credentials
  # Required by OSS sdk
  auth=oss2.StsAuth(
    creds.access_key_id,
    creds.access_key_secret,
    creds.security_token)
    
  endpoint = 'oss-{}-internal.aliyuncs.com'.format(oss_region)
  bucket = oss2.Bucket(auth, endpoint, bucket_name)
  
  # 解析出对应的请求参数
  try:
    query_string = environ.get('QUERY_STRING', '')
    params = parse.parse_qs(query_string)
    params = {k: v[0] for k, v in params.items()}
  except Exception as e:
    print(str(e))

  srcdir = params['srcdir']
  video = params['video']
  saveas = params['saveas']

  width, height, offset, interval = int(params['w']), int(params['h']), int(params.get('offset',0)), int(params.get('interval', 1))
  sprite = params.get('sprite', "1*1")
  rows, cols = sprite.split("*")
  rows, cols = int(rows), int(cols)
  
  capture_num = params.get('capture_num')
  if not capture_num:
    capture_num = rows*cols
  
  capture_num = int(capture_num)
  cap_width , cap_height = width , height
  # autofill 可选参数的处理
  autofill = params.get('autofill')
  if autofill and int(autofill) == 1:
    oringin_ratio = float(params['oringin_ratio'])
    cap_width = int(height * oringin_ratio)

  print("cap_info = ",  cap_width , cap_height)
  # 多线程调用oss视频截帧服务
  ts = []
  for i in range(capture_num):
    t = (offset + i* interval) * 1000
    process = "video/snapshot,t_{0},f_png,w_{1},h_{2},m_fast".format(t, cap_width, cap_height)
    t = Thread(target=video_capture, args=(bucket, srcdir, video, t, process,))
    t.start()
    ts.append(t)
    
  for t in ts:
    t.join()
    
  image_len = len(images)
  print("image length = {}".format(image_len))
  ret = []
  zip_buffer = io.BytesIO()
  with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zf:
    for i, content in enumerate(images):
      zf.writestr(video + "_{}.png".format(i),  content)
      
  zip_file = "{}/{}.zip".format(saveas, video)
  bucket.put_object(zip_file, zip_buffer.getvalue()) 
  
  # 生成截图的zip放入返回结果
  zip_url = "https://{}.oss-{}.aliyuncs.com/".format(bucket_name, oss_region) + zip_file + "\n"
  ret.append(bytes(zip_url, encoding = "utf8"))

  # 雪碧图之间的间隙
  cell_gap = 2
  # BATCH_NUM 数量的图片生成一张 rows * cols 雪碧图
  # 可以生成多张,如果capture_num > rows * cols
  BATCH_NUM = rows*cols
  xbt_num = int(math.ceil(image_len/float(BATCH_NUM)))
  for x in range(xbt_num):
    img = Image()
    img.blank((width+cell_gap)*rows, (height+cell_gap)*cols, Color('black'))
    begin = x * BATCH_NUM
    end = begin + BATCH_NUM if (begin + BATCH_NUM) < image_len else image_len
    sub_images = images[begin:end]
    for i, content in enumerate(sub_images):
      with Image(blob=content)  as sub_img:
        r = i % rows
        j = int(i / rows)
        if cap_width == width:
          img.composite(image=sub_img,left=r*(width + cell_gap), top=j*(height + cell_gap))
        else: # autofill为1时的特殊处理
          jz_img = Image()
          jz_img.blank(width, height, Color('blue'))
          jz_img.composite(image=sub_img,left=int((width - cap_width)/2), top=0)
          img.composite(image=jz_img,left=r*(width + cell_gap), top=j*(height + cell_gap))
    
    pic_file = "{}/xbt_{}_{}.png".format(saveas, video, x)
    bucket.put_object(pic_file, img.make_blob(format='png')) 
    pic_url = "https://{}.oss-{}.aliyuncs.com/".format(bucket_name, oss_region) + pic_file + "\n"
    # 生成的雪碧图的url地址放入返回值
    ret.append(bytes(pic_url, encoding = "utf8"))
  
  status = '200 OK'
  response_headers = [('Content-type', 'text/plain')]
  start_response(status, response_headers)
  
  return ret

fun 部署的template.yml

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  xbt-demo-pro:
    Type: 'Aliyun::Serverless::Log'
    Properties:
      Description: 'image process log pro'
    fc-log:
      Type: 'Aliyun::Serverless::Log::Logstore'
      Properties:
        TTL: 362
        ShardCount: 1

  xbt-service:
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Description: 'image process demo'
      Policies:
        - AliyunOSSFullAccess
      LogConfig:
        Project: 'xbt-demo-pro'
        Logstore: 'fc-log'
    xbt-func:
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: index.handler
        CodeUri: './'
        Description: 'xbt-process http function'
        Runtime: python3
        Timeout: 60
        MemorySize: 512
      Events:
        http-trigger:
          Type: HTTP
          Properties:
            AuthType: ANONYMOUS
            Methods: ['GET', 'POST']
相关实践学习
基于函数计算一键部署掌上游戏机
本场景介绍如何使用阿里云计算服务命令快速搭建一个掌上游戏机。
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
23天前
|
监控 Serverless 测试技术
Serverless 应用引擎常见问题之做的web服务计费如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
169 3
|
23天前
|
监控 关系型数据库 Serverless
Serverless 应用引擎常见问题之函数计算3.0的项目提示未知错误如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
31 5
|
23天前
|
运维 Serverless 调度
Serverless 应用引擎常见问题之资源使用完影响服务使用如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
19 0
|
23天前
|
监控 Serverless 测试技术
Serverless 应用引擎常见问题之生成的图片的oss地址配成自定义的域名如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
22 0
|
21天前
|
监控 Serverless
函数计算(FC)作为一种无服务器的计算服务,在使用过程中可能会遇到各种问题
函数计算(FC)作为一种无服务器的计算服务,在使用过程中可能会遇到各种问题
20 4
|
2月前
|
人工智能 NoSQL Serverless
基于函数计算3.0 Stable Diffusion Serverless API 的AI艺术字头像生成应用搭建与实践的报告
本文主要分享了自己基于函数计算3.0 Stable Diffusion Serverless API 的AI艺术字头像生成应用搭建与实践的报告
466 6
基于函数计算3.0 Stable Diffusion Serverless API 的AI艺术字头像生成应用搭建与实践的报告
|
24天前
|
人工智能 关系型数据库 Serverless
Serverless 应用引擎常见问题之API生成的函数镜像改为自定义的镜像如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
37 3
|
15天前
|
存储 Cloud Native Serverless
云原生最佳实践系列 7:基于 OSS Object FC 实现非结构化文件实时处理
阿里云OSS对象存储方案利用函数计算FC,在不同终端请求时实时处理OSS中的原图,减少衍生图存储,降低成本。
|
23天前
|
人工智能 JavaScript 小程序
Serverless 应用引擎常见问题之函数计算安装serverless devs成功了但是配置密钥这步一直不成功如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
20 1
|
23天前
|
关系型数据库 MySQL Serverless
Serverless 应用引擎常见问题之新发布的服务 arms 没了如何解决
Serverless 应用引擎(Serverless Application Engine, SAE)是一种完全托管的应用平台,它允许开发者无需管理服务器即可构建和部署应用。以下是Serverless 应用引擎使用过程中的一些常见问题及其答案的汇总:
24 3

热门文章

最新文章

相关产品

  • 函数计算