[视频]

获取视频封面图:

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. CSS – Icon

    前言 Icon 并不容易搞. 市场有许多平台支持 Icon, 有些收费有些免费. 有些 icon 很丰富, 有些很缺失. 尤其是在做网站的时候寻找 icon 是一个挺累的事情. 这篇就来聊聊 icon ...

  2. MyBatis——案例——查询-单条件查询-动态条件查询

    单条件查询-动态条件查询(choose(when,otherwise))      从多个条件中选择一个   choose(when,otherwise) 选择,类似于java中的Switch语句(w ...

  3. ConcurrentLinkedQueue详解(图文并茂)

    前言 ConcurrentLinkedQueue是基于链接节点的无界线程安全队列.此队列按照FIFO(先进先出)原则对元素进行排序.队列的头部是队列中存在时间最长的元素,而队列的尾部则是最近添加的元素 ...

  4. oneforall配置环境,报错cannot import name 'sre_parse' from 're' 解决方法

    高版本python中re模块没有了sre_parse模块, 可以修改python中的exrex.py 代码,直接导入sre_parse模块

  5. SXYZ-6.27专题比赛

    好的,现在正式定义今天的比赛为一场伤心的比赛. ↑这张图片首先能说明一些问题,但这并不是关键. ↓这才是伤心的关键 ↑第一题文件输入输入爆 ↑第二题文件名直接爆 评语,一个比一个离谱! 然后只是很简单 ...

  6. IDEA更改远程git仓库地址

    前言 我们在使用IDEA开发时,一般会配置好对应的git仓库,这样就比较容易对代码进行控制以及协同开发.   但有时候,我们远程的仓库地址由于这样那样的原因,需要迁移(这在爱折腾的企业是常有的事情). ...

  7. Ant-Design-Vue 图片上传

    功能演示 我们要实现的功能如下,有两个按钮,点击第一个按钮选择文件,选择文件后点击第二个按钮上传到服务器. 功能需求: 只允许上传 png.jpg/jpeg 格式的图片 没有上传图片时显示占位图 选择 ...

  8. initrd&init进程

    initrd的全名是 init ramdisk,是一个启动时存在于内存的文件系统. kernal 到 initrd的流程 在GRUB加载kernel时,kernel会先在内存中制造一个rootfs当做 ...

  9. C++第六节课 引用变量 指针的升级版

    #include <iostream> using namespace std; // C++的引用 是C指针的升级 可以提高代码的稳定性和健壮性 // const 修饰的引用 是 常引用 ...

  10. for 和双重 for 的区别?

    1. 内层的循环可以看作外层循环的语句 2. 外层循环执行 1 次 ,内层循环执行全部