golang之媒体处理
[视频]
获取视频封面图:
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中的提取视频信息的服务
注意这里获取需要使用到签名之后获取对应的数据
这里使用基于阿里云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之媒体处理的更多相关文章
- TODO:这是一个我的自媒体
TODO:这是一个我的自媒体 自媒体(外文名:We Media)又称"公民媒体"或"个人媒体",是指私人化.平民化.普泛化.自主化的传播者,以现代化.电子化的手 ...
- GoLang几种读文件方式的比较
GoLang提供了很多读文件的方式,一般来说常用的有三种.使用Read加上buffer,使用bufio库和ioutil 库. 那他们的效率如何呢?用一个简单的程序来评测一下: package main ...
- 经济学人使用Golang构建微服务历程回顾
关键点 经济学人内容分发系统需要更大的灵活性,将内容传递给日益多样化的数字渠道.为了实现这一灵活性目标并保持高水平的性能和可靠性,平台从一个单体结构过渡到微服务体系结构. 用Go编写的服务是新系统的一 ...
- Golang源码分析之目录详解
开源项目「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 导读 学习Go语言源码的第一步就是了解先了解它的目录结构,你对它的源码目录了解多少呢? 目 ...
- Golang 实现 RTP
在 Coding 之前我们先来简单介绍一下 RTP(Real-time Transport Protocol), 正如它的名字所说,用于互联网的实时传输协议,通过 IP 网络传输音频和视频的网络协议. ...
- Golang, 以17个简短代码片段,切底弄懂 channel 基础
(原创出处为本博客:http://www.cnblogs.com/linguanh/) 前序: 因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程.管道等高并发编程知识 ...
- 说说Golang的使用心得
13年上半年接触了Golang,对Golang十分喜爱.现在是2015年,离春节还有几天,从开始学习到现在的一年半时间里,前前后后也用Golang写了些代码,其中包括业余时间的,也有产品项目中的.一直 ...
- TODO:Golang指针使用注意事项
TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...
- JavaScript自定义媒体播放器
使用<audio>和<video>元素的play()和pause()方法,可以手工控制媒体文件的播放.组合使用属性.事件和这两个方法,很容易创建一个自定义的媒体播放器,如下面的 ...
- Golang 编写的图片压缩程序,质量、尺寸压缩,批量、单张压缩
目录: 前序 效果图 简介 全部代码 前序: 接触 golang 不久,一直是边学边做,边总结,深深感到这门语言的魅力,等下要跟大家分享是最近项目 服务端 用到的图片压缩程序,我单独分离了出来,做成了 ...
随机推荐
- C语言数据的存储
目录 类型的基本归类 整形在内存中的存储 原码.反码.补码 大小端介绍 练习 浮点型在内存中的存储 浮点数存储的例子 浮点数存储规则 类型的基本归类 整形家族: char unsigned char ...
- Angular Material 18+ 高级教程 – CDK Observers
前言 Observers 是 Angular Material 对游览器原生 MutationObserver 的上层封装.主要用于监听 add/remove Node. 不熟悉的朋友可以先看这篇 D ...
- JavaScript Library – Lit
前言 我写过一篇关于 Lit 的文章,Material Design, Angular Material, MDC, MWC, Lit 的关系. 如今 material-web MWC 已经发布 1. ...
- EF Core – Unit of Work, DbContext, Transaction 概念解释
前言 踩了一个坑, 下面是 2 个 scope 的调用, 第 1 和 3 是一个 Audit log filter action, 第 2 个是 controller. // open tran // ...
- 基于 Session 实现短信登录
短信验证 一.基于Session 1.登录流程 1)发送验证码 用户在提交手机号后,会校验手机号是否合法,如果不合法,则要求用户重新输入手机号 如果手机号合法,后台此时生成对应的验证码,同时将验证码进 ...
- 【Abyss】Android 平台应用级系统调用拦截框架
Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环 -- SVC系统调用拦截. ☞ Github ☜ 由于我们虚拟化产品的需求,需要支持在普通的Android手机运行.我们 ...
- Python — 循环语句
while循环语句 语法: 嵌套使用: for循环语句:(for循环外部不允许访问临时变量) 语法: 使用: 待处理的数据集: range语句: 嵌套for循环: continue: break: 随 ...
- 痞子衡嵌入式:MCUBootUtility v6.3发布,支持获取与解析启动日志
-- 痞子衡维护的 NXP-MCUBootUtility 工具距离上一个大版本(v5.3.0)发布过去一年了,期间痞子衡也做过三个版本更新,但不足以单独介绍.这一次痞子衡为大家带来了全新重要版本v6. ...
- ++i与i++在效率上的细微差别
在一些特定的使用中, i++ 可能将原值用中间量存起来以待使用,下面看相关程序的汇编代码(使用 gcc ). i++ 源程序: #include <stdio.h> int main(){ ...
- 数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪
数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪 随着全球对可持续发展和产品透明度的关注日益增加,企业需要一种可靠的方法来跟踪和管理产品生命周期中的关键数据.我们 ...