[视频]

获取视频封面图:

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. C语言数据的存储

    目录 类型的基本归类 整形在内存中的存储 原码.反码.补码 大小端介绍 练习 浮点型在内存中的存储 浮点数存储的例子 浮点数存储规则 类型的基本归类 整形家族: char unsigned char ...

  2. Angular Material 18+ 高级教程 – CDK Observers

    前言 Observers 是 Angular Material 对游览器原生 MutationObserver 的上层封装.主要用于监听 add/remove Node. 不熟悉的朋友可以先看这篇 D ...

  3. JavaScript Library – Lit

    前言 我写过一篇关于 Lit 的文章,Material Design, Angular Material, MDC, MWC, Lit 的关系. 如今 material-web MWC 已经发布 1. ...

  4. EF Core – Unit of Work, DbContext, Transaction 概念解释

    前言 踩了一个坑, 下面是 2 个 scope 的调用, 第 1 和 3 是一个 Audit log filter action, 第 2 个是 controller. // open tran // ...

  5. 基于 Session 实现短信登录

    短信验证 一.基于Session 1.登录流程 1)发送验证码 用户在提交手机号后,会校验手机号是否合法,如果不合法,则要求用户重新输入手机号 如果手机号合法,后台此时生成对应的验证码,同时将验证码进 ...

  6. 【Abyss】Android 平台应用级系统调用拦截框架

    Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环 -- SVC系统调用拦截. ☞ Github ☜ 由于我们虚拟化产品的需求,需要支持在普通的Android手机运行.我们 ...

  7. Python — 循环语句

    while循环语句 语法: 嵌套使用: for循环语句:(for循环外部不允许访问临时变量) 语法: 使用: 待处理的数据集: range语句: 嵌套for循环: continue: break: 随 ...

  8. 痞子衡嵌入式:MCUBootUtility v6.3发布,支持获取与解析启动日志

    -- 痞子衡维护的 NXP-MCUBootUtility 工具距离上一个大版本(v5.3.0)发布过去一年了,期间痞子衡也做过三个版本更新,但不足以单独介绍.这一次痞子衡为大家带来了全新重要版本v6. ...

  9. ++i与i++在效率上的细微差别

    在一些特定的使用中, i++ 可能将原值用中间量存起来以待使用,下面看相关程序的汇编代码(使用 gcc ). i++ 源程序: #include <stdio.h> int main(){ ...

  10. 数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪

    数字产品护照 (DPP) 解决方案:利用 Blazor 和区块链实现产品全生命周期追踪 随着全球对可持续发展和产品透明度的关注日益增加,企业需要一种可靠的方法来跟踪和管理产品生命周期中的关键数据.我们 ...