[视频]

获取视频封面图:

1) 如果是使用oss的话, 可以添加指定的后缀生成指定图片

视频截帧: https://help.aliyun.com/zh/oss/user-guide/video-snapshots?spm=a2c4g.11186623.0.0.8ea266d4kR5bST

2) 借助第三方包实现: https://github.com/u2takey/ffmpeg-go

注: 他是需要依赖ffmpeg的

package main

import (
"bytes"
"fmt"
"github.com/disintegration/imaging"
ffmpeg "github.com/u2takey/ffmpeg-go"
"log"
"os"
"strings"
) func main() {
var videoPath = "./test.mp4"
videoPath = "https://hk-cloud-screen-test.oss-cn-hangzhou.aliyuncs.com/ai_moment/108f17a6-2f94-4b2d-8667-d369058a5455.mp4"
snapshotName, err := GetSnapshot(videoPath, "test", 1)
if err != nil {
fmt.Println(snapshotName, err)
}
} func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string, err error) {
buf := bytes.NewBuffer(nil)
err = ffmpeg.Input(videoPath).
Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", frameNum)}).
Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}).
WithOutput(buf, os.Stdout).
Run() if err != nil {
log.Fatal("生成缩略图失败:", err)
return "", err
} img, err := imaging.Decode(buf)
if err != nil {
log.Fatal("生成缩略图失败:", err)
return "", err
} err = imaging.Save(img, snapshotPath+".png")
if err != nil {
log.Fatal("生成缩略图失败:", err)
return "", err
} names := strings.Split(snapshotPath, "\\")
snapshotName = names[len(names)-1] + ".png"
return
}

还支持更多操作 转GIF,视频格式转换等操作

获取视频长度:

1)若是oss上的视频,则可以使用阿里云的IMM中的提取视频信息的服务

注意这里获取需要使用到签名之后获取对应的数据

获取视频信息: https://help.aliyun.com/zh/oss/user-guide/video-information-extraction?spm=a2c4g.11186623.0.0.93817902yhOJ2r

这里使用基于阿里云oss包:

github.com/aliyun/aliyun-oss-go-sdk/oss
 示例:
client, err := oss.New("oss-cn-hangzhou.aliyuncs.com", "accessKeyID", "accessKeySecret")
if err != nil {
panic(err)
}
bucketName := "buekct名称"
bucket, err := client.Bucket(bucketName)
if err != nil {
panic(err)
} objectKey := "pp/cbe1655d-9f67-490c-901a-20932f79443c.mp4" // Get object
options := []oss.Option{
oss.AddParam("x-oss-process", "video/info"),
} signedURL, err := bucket.SignURL(objectKey, oss.HTTPGet, 10*60, options...)
if err != nil {
panic(err)
}
fmt.Println(signedURL) // 获取请求信息
resp, err := http.Get(signedURL)
if err != nil {
panic(err)
} defer resp.Body.Close() body, err := io.ReadAll(resp.Body)
fmt.Println("data:", string(body))
 

2)对于在线视频

package main

import (
"bytes"
"encoding/binary"
"fmt"
"io"
"os"
) // 获取本地文件视频秒数 // BoxHeader 信息头
type BoxHeader struct {
Size uint32
FourccType [4]byte
Size64 uint64
} func main() {
url := "./video.mp4"
file, err := os.Open(url)
if err != nil {
panic(err)
}
duration, err := GetMP4Duration(file)
if err != nil {
panic(err)
}
fmt.Println(duration)
} // GetMP4Duration 获取视频时长,以秒计
func GetMP4Duration(reader io.ReaderAt) (lengthOfTime uint32, err error) {
var info = make([]byte, 0x10)
var boxHeader BoxHeader
var offset int64 = 0
// 获取moov结构偏移
for {
_, err = reader.ReadAt(info, offset)
if err != nil {
return
}
boxHeader = getHeaderBoxInfo(info)
fourccType := getFourccType(boxHeader)
if fourccType == "moov" {
break
}
// 有一部分mp4 mdat尺寸过大需要特殊处理
if fourccType == "mdat" {
if boxHeader.Size == 1 {
offset += int64(boxHeader.Size64)
continue
}
}
offset += int64(boxHeader.Size)
}
// 获取moov结构开头一部分
moovStartBytes := make([]byte, 0x100)
_, err = reader.ReadAt(moovStartBytes, offset)
if err != nil {
return
}
// 定义timeScale与Duration偏移
timeScaleOffset := 0x1C
durationOffest := 0x20
timeScale := binary.BigEndian.Uint32(moovStartBytes[timeScaleOffset : timeScaleOffset+4])
Duration := binary.BigEndian.Uint32(moovStartBytes[durationOffest : durationOffest+4])
lengthOfTime = Duration / timeScale
return
} // getHeaderBoxInfo 获取头信息
func getHeaderBoxInfo(data []byte) (boxHeader BoxHeader) {
buf := bytes.NewBuffer(data)
binary.Read(buf, binary.BigEndian, &boxHeader)
return
} // getFourccType 获取信息头类型
func getFourccType(boxHeader BoxHeader) (fourccType string) {
fourccType = string(boxHeader.FourccType[:])
return
}

另外还有其他第三方库可以使用:

go get -u github.com/Stitch-Zhang/gmp4

 可以处理非下载模式的在线资源

 

可以借助ffmpeg, 添加到环境变量中

https://github.com/BtbN/FFmpeg-Builds

package main

import (
"context"
"fmt"
"gopkg.in/vansante/go-ffprobe.v2"
"log"
"time"
) func main() { api := "https://www.runoob.com/try/demo_source/movie.mp4"
//api := "https://hk-ai-test.oss-cn-hangzhou.aliyuncs.com/volcVideo/ac6f7816-d0de-4ddd-b57d-590cc5e2e94f_0.mp4"
ctx, cancelFunc := context.WithTimeout(context.Background(), 20*time.Second)
defer cancelFunc() data, err := ffprobe.ProbeURL(ctx, api)
if err != nil {
panic(err)
} if data == nil {
log.Fatal("获取数据为空")
} fmt.Println(data.Format.Duration().Seconds())
}

视频上传oss:

    videoUrl := "https://muse.console.volcengine.com/api/storage/objects/media/7307071523758653478_origin.mp4?x-muse-token=ChUIARABGIzen6sGIIyBpasGKgM2MTMSmQEKDgiggI3e06eehGQQl5UBEgAaAhgCIggKAmxmEgJjbioGCNo0EIYDMikKJ3siYml6X2lkIjoiMTAwMDAwMDAxIiwiZGVyaXZlZF91aWQiOiIifUIUChJpY2FyY2guaWFtLmF1dGhycGNKLi9hcGkvc3RvcmFnZS9vYmplY3RzL21lZGlhLzczMDcwNzE1MjM3NTg2NTM0Nzga2AJpSExFbFAzM0RjWFZLSzJDenk5UXVlU3QxZXRidlkwSDZnaFBDdFB2VkZ4YjU3YjFnVlpmdlBkaEFtK3pZeEx1U3N2aWEydmZ1RVBaRStFNmd0aFdJSk5sUmFVM3RvNDkvWTZ1ME1IWUlOeVY5bjdicWxCZFI3ZGhZT2VRd3dZS2dXSEdhTzl5VllxSVRQcTgvdk14M3lXd1hvQ21LWjYwY0tUN2dBMlFjQStIN0cxWnJwRzc0bzU2UEtqd3ZTbVVPYzBLdlhtN1pJVTROOStTVFVoaExZdGpMUzFWVEo2Z3FqalVZeGMxV0FsTkN0WXJ6YitabitJandHMTl1WGMveHJZb2xOeFhZQk9sZ3JwNXBQTmV5QmRBYVdQSHpPdDlTbHpITytpeXJITzR3SmRQc0FWQ1E0cXJPbnpPaGJlWEdTVENZME5KelJISnJzT0ZuZE4xTFE9PQ==&infer_mime=ext"
resp, err := http.Get(videoUrl)
if err != nil {
fmt.Println("网络发生错误")
return
}
if resp.StatusCode == http.StatusNotFound {
fmt.Println("图片资源不存在")
return
}
defer resp.Body.Close() fmt.Println(resp.StatusCode)
// 读取文件内容
body, err := io.ReadAll(resp.Body) // 读取本地文件并上传
//videoPath := "./video.mp4"
//f, err := os.Open(videoPath)
//if err != nil {
// panic(err)
//}
//fmt.Println("-----读取视频文件")
//defer f.Close()
//data := make([]byte, 0)
//n, err := f.Read(data)
//fmt.Println(n, err)
//
err = bucket.PutObject("demo.mp4", bytes.NewReader(body))
fmt.Println("上传结果:", err)

[图片]

上传图片到oss:

//imgPath := "./oss.jpg"
// 本地读取文件后上传 //f, err := os.OpenFile(imgPath, os.O_RDONLY, 0644)
//if err != nil {
// panic(err)
//}
//defer f.Close()
//
//stat, err := f.Stat()
//if err != nil {
// panic(err)
//}
//
//// 创建一个byte切片
//data := make([]byte, stat.Size())
//count, err := f.Read(data)
//fmt.Println("文件长度:", count) // 读取一个文件base64
pictureUrl := "http://pic.netbian.com/uploads/allimg/210423/224716-1619189236e4d9.jpg"
//pictureUrl := "http://pic.netbian.com/uploads/allimg/210423/224716-16191892369.jpg" // 不存在的图片
resp, err := http.Get(pictureUrl)
if err != nil {
fmt.Println("网络发生错误")
return
}
if resp.StatusCode == http.StatusNotFound {
fmt.Println("图片资源不存在")
return
}
defer resp.Body.Close() fmt.Println(resp.StatusCode)
// 读取文件内容
body, err := io.ReadAll(resp.Body)
// 将[]byte数据转换成base64的字符串
imgBase64 := base64.StdEncoding.EncodeToString(body)
fmt.Println("img base64:", len(imgBase64))
// 解析base64字符串到[]byte
decode, err := base64.StdEncoding.DecodeString(imgBase64) err = bucket.PutObject("test222.jpg", bytes.NewReader(decode)) // 从本地文件上传
//err = bucket.PutObjectFromFile("test.jpg", imgPath)
if err != nil {
// HandleError(err)
panic(err)
}
fmt.Println("上传图片成功")

获取远程图片信息方法:

func GetImageInfo(imgUrl string) (*types.ImgInfo, error) {
resp, err := http.Get(imgUrl)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
logx.Infof("[获取图片信息]失败:%v 状态码", err, resp.StatusCode)
return nil, errors.New("获取远程图片失败")
}
defer resp.Body.Close() body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
} resp.Body = io.NopCloser(bytes.NewReader(body)) m, _, err := image.Decode(resp.Body)
if err != nil {
return nil, err
} imgBase64 := base64.StdEncoding.EncodeToString(body)
return &types.ImgInfo{
Width: m.Bounds().Dx(),
Heigh: m.Bounds().Dy(),
Base64Str: imgBase64,
}, nil
}

注:

1. 对于上传到oss的资源,如果使用未绑定域名方式的话, 他会默认附件下载的方式, 如果绑定了域名之后,可以在线查看资源

golang之媒体处理的更多相关文章

  1. TODO:这是一个我的自媒体

    TODO:这是一个我的自媒体 自媒体(外文名:We Media)又称"公民媒体"或"个人媒体",是指私人化.平民化.普泛化.自主化的传播者,以现代化.电子化的手 ...

  2. GoLang几种读文件方式的比较

    GoLang提供了很多读文件的方式,一般来说常用的有三种.使用Read加上buffer,使用bufio库和ioutil 库. 那他们的效率如何呢?用一个简单的程序来评测一下: package main ...

  3. 经济学人使用Golang构建微服务历程回顾

    关键点 经济学人内容分发系统需要更大的灵活性,将内容传递给日益多样化的数字渠道.为了实现这一灵活性目标并保持高水平的性能和可靠性,平台从一个单体结构过渡到微服务体系结构. 用Go编写的服务是新系统的一 ...

  4. Golang源码分析之目录详解

    开源项目「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 导读 学习Go语言源码的第一步就是了解先了解它的目录结构,你对它的源码目录了解多少呢? 目 ...

  5. Golang 实现 RTP

    在 Coding 之前我们先来简单介绍一下 RTP(Real-time Transport Protocol), 正如它的名字所说,用于互联网的实时传输协议,通过 IP 网络传输音频和视频的网络协议. ...

  6. Golang, 以17个简短代码片段,切底弄懂 channel 基础

    (原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...

  7. 说说Golang的使用心得

    13年上半年接触了Golang,对Golang十分喜爱.现在是2015年,离春节还有几天,从开始学习到现在的一年半时间里,前前后后也用Golang写了些代码,其中包括业余时间的,也有产品项目中的.一直 ...

  8. TODO:Golang指针使用注意事项

    TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...

  9. JavaScript自定义媒体播放器

    使用<audio>和<video>元素的play()和pause()方法,可以手工控制媒体文件的播放.组合使用属性.事件和这两个方法,很容易创建一个自定义的媒体播放器,如下面的 ...

  10. Golang 编写的图片压缩程序,质量、尺寸压缩,批量、单张压缩

    目录: 前序 效果图 简介 全部代码 前序: 接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 ...

随机推荐

  1. kubernetes删除ns异常状态为:Terminating

    在部署kuboard控制平台的时候,不规范删除,导致ns状态为Terminating [root@master01 ~]# kubectl delete namespace kuboard ^C ro ...

  2. 钉钉H5微应用本地开发调试记录

    准备工具: 手机(ios,安卓) 安卓手机可以用网易Mumu模拟器,下载一个移动版钉钉 钉钉开放文档 本地开发的话,可以去钉钉后台管理系统,将你的本地ip地址配置成应用的首页,比如 10.xx.xx. ...

  3. C++ STL stack容器——栈

    stack容器 基本概念 stack是一种先进后出的数据结构,它只有一个出口,形式如下图所示.stack容器允许新增元素,移除元素,取得栈顶元素,但是除了最顶端外,没有任何地方可以存取stack的娶她 ...

  4. 使用 Flutter 加速应用开发

    作者 / Larry McKenzie 本文由 eBay 技术负责人 Larry Mckenzie 和 Corey Sprague 撰写.您可以收听他们在 Google Apps, Games &am ...

  5. 搭建本地nginx-rtmp服务,初体验rtmp推流、拉流

    实验环境说明: ubuntu 16.04 进行本实验的前提:需要在ubuntu上搭建好ffmpeg环境,参考我的另一篇博文 ffmpeg编译过程经历的99八十一难 下面开始本文内容 PART1 编译安 ...

  6. 关于 xfg 的班会

  7. 禁用QQ自动升级(实测可用)

    事件起因: 某客户电脑QQ从怀旧版(9.7)自动升级到NT版本(9.9)版本,新版本由于消息通知功能不能直接查看,老版本右下角消息闪烁,鼠标放上去便可以看到有几个联系人的消息,因此客户强烈要求版本回退 ...

  8. EAS(能量感知调度)绿色节能调度器

    能量感知调度(EAS)使调度程序能够预测其决策对 CPU 消耗的电量的影响. EAS 依赖于 CPU 的能量模型 (EM) 来为每个任务选择省电的 CPU,同时要求对执行任务的吞吐量的影响最小. EA ...

  9. ssr屏幕空间射线追踪

    本轮作业中,我们需要在一个光源为方向光,材质为漫反射 (Diffuse) 的场景 中,完成屏幕空间下的全局光照效果(两次反射). 为了在作业框架中实现上述效果,基于我们需要的信息不同我们会分三阶段 着 ...

  10. 手动检查 token 是否过期

    1.在 存储 token 的时候说明登录了 此时 把时间戳记录一下  js-cookies - auth.js // 导入 js-cookie 用于操作 cookies import Cookies ...