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

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
函数计算FC,每月15万CU 3个月
简介: 在一些视频处理的场景求中,有对视频制作雪碧图需求,简单来说,就是对视频的按照一定的规律就行截帧,然后将截帧的图片组合成一张大的雪碧图。 本文基于对象存储服务 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']
相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
3月前
|
存储 Serverless 数据库
科普文:云计算服务类型IaaS, PaaS, SaaS, BaaS, Faas说明
本文介绍了云计算服务的几种主要类型,包括IaaS(基础设施即服务)、PaaS(平台即服务)、SaaS(软件即服务)、BaaS(后端即服务)和FaaS(函数即服务)。每种服务模式提供了不同的服务层次和功能,从基础设施的提供到应用的开发和运行,再到软件的交付使用,满足了企业和个人用户在不同场景下的需求。文章详细阐述了每种服务模式的特点、优势和缺点,并列举了相应的示例。云计算服务的发展始于21世纪初,随着互联网技术的普及,这些服务模式不断演进,为企业和个人带来了高效、灵活的解决方案。然而,使用这些服务时也需要注意服务的稳定性、数据安全性和成本等问题。
2135 4
|
21天前
|
人工智能 Serverless API
尽享红利,Serverless构建企业AI应用方案与实践
本次课程由阿里云云原生架构师计缘分享,主题为“尽享红利,Serverless构建企业AI应用方案与实践”。课程分为四个部分:1) Serverless技术价值,介绍其发展趋势及优势;2) Serverless函数计算与AI的结合,探讨两者融合的应用场景;3) Serverless函数计算AIGC应用方案,展示具体的技术实现和客户案例;4) 业务初期如何降低使用门槛,提供新用户权益和免费资源。通过这些内容,帮助企业和开发者快速构建高效、低成本的AI应用。
66 12
|
2月前
|
弹性计算 数据管理 应用服务中间件
活动实践 | 借助OSS搭建在线教育视频课程分享网站
本教程指导用户在阿里云ECS实例上搭建在线教育网站,包括重置ECS密码、配置安全组、安装Nginx、创建网站页面、上传数据至OSS、开通OSS传输加速、配置生命周期策略及清理资源等步骤,实现高效、低成本的数据管理和网站运营。
活动实践 | 借助OSS搭建在线教育视频课程分享网站
|
23天前
|
存储 对象存储 索引
对象存储OSS-m3u8视频私有权限
当上传至私有存储桶的M3U8视频缺少签名信息时,会导致播放失败(403错误)。解决方案是使用OSS的动态签名机制,在首次访问M3U8文件时,通过在URL中添加`x-oss-process=hls/sign`参数,OSS将自动对所有TS切片地址进行签名,确保视频正常播放。
59 2
|
1月前
|
运维 Serverless 测试技术
通义灵码 x 函数计算:构建高效开发流程,加速项目交付
本方案基于通义大模型的通义灵码,提供代码生成、补全、优化及单元测试生成等能力,提升编码效率和质量。结合云效和函数计算 FC 进行代码管理、持续集成、部署发布,加速项目交付,为开发者提供智能编码、CI/CD、部署上线体验,加快产品迭代速度。
|
2月前
|
弹性计算 人工智能 自然语言处理
魔搭社区与函数计算:高效部署开源大模型的文本生成服务体验
在数字化时代,人工智能技术迅速发展,开源大模型成为重要成果。魔搭社区(ModelScope)作为开源大模型的聚集地,结合阿里云函数计算,提供了一种高效、便捷的部署方式。通过按需付费和弹性伸缩,开发者可以快速部署和使用大模型,享受云计算的便利。本文介绍了魔搭社区与函数计算的结合使用体验,包括环境准备、部署应用、体验使用和资源清理等步骤,并提出了改进建议。
|
3月前
|
机器学习/深度学习 监控 物联网
函数即服务(FaaS)
函数即服务(FaaS)
125 6
|
4月前
|
Cloud Native 关系型数据库 Serverless
基于阿里云函数计算(FC)x 云原生 API 网关构建生产级别 LLM Chat 应用方案最佳实践
本文带大家了解一下如何使用阿里云Serverless计算产品函数计算构建生产级别的LLM Chat应用。该最佳实践会指导大家基于开源WebChat组件LobeChat和阿里云函数计算(FC)构建企业生产级别LLM Chat应用。实现同一个WebChat中既可以支持自定义的Agent,也支持基于Ollama部署的开源模型场景。
805 29
|
5月前
|
存储 运维 安全
函数计算产品使用问题之如何获取到访问其他阿里云服务所需的AccessKey、SecretKey或STS Token
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
4月前
|
人工智能 自然语言处理 Serverless
阿里云函数计算 x NVIDIA 加速企业 AI 应用落地
阿里云函数计算与 NVIDIA TensorRT/TensorRT-LLM 展开合作,通过结合阿里云的无缝计算体验和 NVIDIA 的高性能推理库,开发者能够以更低的成本、更高的效率完成复杂的 AI 任务,加速技术落地和应用创新。
191 13

相关产品

  • 函数计算