十分钟上线 - 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']
相关实践学习
【AI破次元壁合照】少年白马醉春风,函数计算一键部署AI绘画平台
本次实验基于阿里云函数计算产品能力开发AI绘画平台,可让您实现“破次元壁”与角色合照,为角色换背景效果,用AI绘图技术绘出属于自己的少年江湖。
从 0 入门函数计算
在函数计算的架构中,开发者只需要编写业务代码,并监控业务运行情况就可以了。这将开发者从繁重的运维工作中解放出来,将精力投入到更有意义的开发任务上。
目录
相关文章
|
6月前
|
编解码 数据处理 API
如何用阿里云OSS对图片和视频进行数据处理?
本文介绍了如何利用阿里云对象存储OSS进行图片和视频处理。OSS提供了丰富的功能,如图片的缩放、裁剪、旋转和水印添加等,用户只需在图片URL后附加处理参数即可实现自动化处理。同时,OSS还支持自定义样式模板,便于批量操作。对于视频处理,OSS支持转码、截图、拼接等功能,满足多终端播放需求。通过OSS的API和SDK,开发者可以方便地集成这些功能,提升数据管理效率。
|
8月前
|
存储 编解码 Serverless
Serverless架构下的OSS应用:函数计算FC自动处理图片/视频转码(演示水印添加+缩略图生成流水线)
本文介绍基于阿里云函数计算(FC)和对象存储(OSS)构建Serverless媒体处理流水线,解决传统方案资源利用率低、运维复杂、成本高等问题。通过事件驱动机制实现图片水印添加、多规格缩略图生成及视频转码优化,支持毫秒级弹性伸缩与精确计费,提升处理效率并降低成本,适用于高并发媒体处理场景。
597 0
|
8月前
|
编解码 监控 算法
CDN+OSS边缘加速实践:动态压缩+智能路由降低30%视频流量成本(含带宽峰值监控与告警配置)
本方案通过动态压缩、智能路由及CDN与OSS集成优化,实现视频业务带宽成本下降31%,首帧时间缩短50%,错误率降低53%。结合实测数据分析与架构创新,有效解决冷启动延迟、跨区域传输及设备适配性问题,具备快速投入回收能力。
580 0
|
存储 人工智能 Cloud Native
函数计算×百炼新春活动正式上线!三步赢取蛇年精美好礼
本次场景利用函数计算 FC 构建 Web 服务,由其提供函数计算资源以及工作流能力,您无需管理服务器等基础设施,函数计算 FC 能够根据需求自动扩展,按需计算,结合百炼模型服务实现了从文案、声音、字幕、图像生成到视频合成的一站式自动化流程,大幅简化使用 AI 创作的流程,降低了技术要求,使创作者能够更高效地生产出高质量的内容,快速响应市场需求,同时保证了作品的专业水准和创意表达。
357 24
|
11月前
|
人工智能 运维 文字识别
新书上线 |《零门槛AIGC应用实战——Serverless+AI 轻松玩转高频AIGC场景》免费下载
新书上线 |《零门槛AIGC应用实战——Serverless+AI 轻松玩转高频AIGC场景》免费下载
232 1
|
12月前
|
人工智能 运维 文字识别
新书上线 |《零门槛AIGC应用实战——Serverless+AI 轻松玩转高频AIGC场景》免费下载
《零门槛AIGC应用实战——Serverless+AI 轻松玩转高频AIGC场景》电子书正式上线!多种精选 AI 部署方案带你深入了解 Serverless+AI 最新趋势、AI 应用的架构设计与详细的部署教程等。函数计算 AI 技术解决方案助您一键上云,高效部署。
|
11月前
|
存储 人工智能 Cloud Native
函数计算×百炼新春活动正式上线!三步赢取蛇年精美好礼
函数计算×百炼新春活动正式上线!三步赢取蛇年精美好礼
135 0
|
12月前
|
SQL 分布式计算 Serverless
基于阿里云 EMR Serverless Spark 版快速搭建OSS日志分析应用
基于阿里云 EMR Serverless Spark 版快速搭建OSS日志分析应用
307 0
|
12月前
|
人工智能 运维 文字识别
新书上线 |《零门槛AIGC应用实战——Serverless+AI 轻松玩转高频AIGC场景》免费下载
新书上线 |《零门槛AIGC应用实战——Serverless+AI 轻松玩转高频AIGC场景》免费下载

相关产品

  • 函数计算