|
@@ -1,6 +1,7 @@
|
|
|
package controllers
|
|
|
|
|
|
import (
|
|
|
+ "bytes"
|
|
|
"encoding/binary"
|
|
|
"encoding/json"
|
|
|
"eta/eta_mini_crm_ht/models"
|
|
@@ -15,13 +16,13 @@ import (
|
|
|
"os"
|
|
|
"path"
|
|
|
"strconv"
|
|
|
+ "strings"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
videoType = map[string]int{
|
|
|
"mp4": 1,
|
|
|
- "m4a": 1,
|
|
|
}
|
|
|
)
|
|
|
|
|
@@ -57,7 +58,8 @@ func (this *VideoController) UploadVideo() {
|
|
|
return
|
|
|
}
|
|
|
ext := path.Ext(h.Filename)
|
|
|
- if _, ok := videoType[ext]; !ok {
|
|
|
+ lowCaseExt := strings.ToLower(ext)
|
|
|
+ if _, ok := videoType[lowCaseExt]; !ok {
|
|
|
br.Msg = "视频格式不正确"
|
|
|
br.ErrMsg = "视频上传失败,Err:" + err.Error()
|
|
|
return
|
|
@@ -102,7 +104,7 @@ func (this *VideoController) UploadVideo() {
|
|
|
br.ErrMsg = "初始化OSS服务失败"
|
|
|
return
|
|
|
}
|
|
|
- videoUrl, err := ossClient.UploadFile("", fpath, savePdfToOssPath)
|
|
|
+ mp3Url, err := ossClient.UploadFile("", fpath, savePdfToOssPath)
|
|
|
if err != nil {
|
|
|
br.Msg = "视频上传失败"
|
|
|
br.ErrMsg = "视频上传失败,Err:" + err.Error()
|
|
@@ -110,7 +112,7 @@ func (this *VideoController) UploadVideo() {
|
|
|
}
|
|
|
base := path.Base(h.Filename)
|
|
|
resp := new(response.MediaUploadResp)
|
|
|
- resp.Url = videoUrl
|
|
|
+ resp.Url = mp3Url
|
|
|
resp.FileName = base
|
|
|
resp.DurationMillisecond = duration * 1000
|
|
|
br.Data = resp
|
|
@@ -402,6 +404,77 @@ func (this *VideoController) VideoList() {
|
|
|
br.Msg = "获取成功"
|
|
|
}
|
|
|
|
|
|
+type BoxHeader struct {
|
|
|
+ Size uint32
|
|
|
+ FourccType [4]byte
|
|
|
+ Size64 uint64
|
|
|
+}
|
|
|
+
|
|
|
+// GetMP4Duration 获取视频时长,以秒计
|
|
|
+func GetMP4Duration(reader io.ReaderAt) (lengthOfTime int, err error) {
|
|
|
+ var info = make([]byte, 0x10)
|
|
|
+ var boxHeader BoxHeader
|
|
|
+ var offset int64 = 0
|
|
|
+ // 获取moov结构偏移
|
|
|
+ for {
|
|
|
+ _, err = reader.ReadAt(info, offset)
|
|
|
+ if err == io.EOF {
|
|
|
+ fmt.Println("Reached EOF without finding moov box.")
|
|
|
+ return 0, fmt.Errorf("moov box not found")
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return 0, fmt.Errorf("error reading at offset %d: %w", offset, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ boxHeader = getHeaderBoxInfo(info)
|
|
|
+ fourccType := getFourccType(boxHeader)
|
|
|
+
|
|
|
+ if fourccType == "moov" {
|
|
|
+ fmt.Println("Found moov box at offset", offset)
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ nextOffset := int64(boxHeader.Size)
|
|
|
+ if fourccType == "mdat" && boxHeader.Size == 1 {
|
|
|
+ nextOffset = int64(boxHeader.Size64)
|
|
|
+ }
|
|
|
+ if nextOffset == 0 {
|
|
|
+ return 0, fmt.Errorf("box size is zero, which is invalid and likely means a parsing error")
|
|
|
+ }
|
|
|
+ offset += nextOffset
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取moov结构开头一部分
|
|
|
+ moovStartBytes := make([]byte, 0x100)
|
|
|
+ _, err = reader.ReadAt(moovStartBytes, offset)
|
|
|
+ if err != nil {
|
|
|
+ return 0, fmt.Errorf("error reading moov box at offset %d: %w", offset, err)
|
|
|
+ }
|
|
|
+ // 定义timeScale与Duration偏移
|
|
|
+ timeScaleOffset := 0x1C
|
|
|
+ durationOffset := 0x20
|
|
|
+ timeScale := binary.BigEndian.Uint32(moovStartBytes[timeScaleOffset : timeScaleOffset+4])
|
|
|
+ duration := binary.BigEndian.Uint32(moovStartBytes[durationOffset : durationOffset+4])
|
|
|
+
|
|
|
+ fmt.Printf("timeScale: %d, duration: %d\n", timeScale, duration)
|
|
|
+ if timeScale == 0 {
|
|
|
+ return 0, fmt.Errorf("timeScale is zero, division by zero is not possible")
|
|
|
+ }
|
|
|
+
|
|
|
+ lengthOfTime = int(duration / timeScale)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// getHeaderBoxInfo 获取头信息
|
|
|
+func getHeaderBoxInfo(data []byte) (boxHeader BoxHeader) {
|
|
|
+ buf := bytes.NewBuffer(data)
|
|
|
+ binary.Read(buf, binary.BigEndian, &boxHeader)
|
|
|
+ if boxHeader.Size == 1 { // Large Size
|
|
|
+ binary.Read(buf, binary.BigEndian, &boxHeader.Size64)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
// UploadFile @Title 上传图片
|
|
|
// @Description 上传视频
|
|
|
// @Param File query file true "文件"
|
|
@@ -463,7 +536,7 @@ func (this *VideoController) UploadFile() {
|
|
|
br.ErrMsg = "初始化OSS服务失败"
|
|
|
return
|
|
|
}
|
|
|
- imageUrl, err := ossClient.UploadFile("", fpath, savePdfToOssPath)
|
|
|
+ mp3Url, err := ossClient.UploadFile("", fpath, savePdfToOssPath)
|
|
|
if err != nil {
|
|
|
br.Msg = "图片上传失败"
|
|
|
br.ErrMsg = "图片上传失败,Err:" + err.Error()
|
|
@@ -471,7 +544,7 @@ func (this *VideoController) UploadFile() {
|
|
|
}
|
|
|
base := path.Base(h.Filename)
|
|
|
resp := new(response.MediaUploadResp)
|
|
|
- resp.Url = imageUrl
|
|
|
+ resp.Url = mp3Url
|
|
|
resp.FileName = base
|
|
|
br.Data = resp
|
|
|
br.Msg = "上传成功"
|
|
@@ -479,127 +552,17 @@ func (this *VideoController) UploadFile() {
|
|
|
br.Success = true
|
|
|
}
|
|
|
|
|
|
-// BoxHeader represents the header of an ISO BMFF box.
|
|
|
-type BoxHeader struct {
|
|
|
- Size uint32
|
|
|
- Type [4]byte
|
|
|
- Size64 uint64
|
|
|
-}
|
|
|
-
|
|
|
-// getHeaderBoxInfo parses the box header from the provided byte slice.
|
|
|
-func getHeaderBoxInfo(data []byte) BoxHeader {
|
|
|
- var header BoxHeader
|
|
|
- header.Size = binary.BigEndian.Uint32(data[0:4])
|
|
|
- copy(header.Type[:], data[4:8])
|
|
|
- if header.Size == 1 {
|
|
|
- header.Size64 = binary.BigEndian.Uint64(data[8:16])
|
|
|
- }
|
|
|
- return header
|
|
|
-}
|
|
|
-
|
|
|
-// getFourccType returns the FourCC type as a string.
|
|
|
-func getFourccType(header BoxHeader) string {
|
|
|
- return string(header.Type[:])
|
|
|
-}
|
|
|
-
|
|
|
-// readBox reads the entire box data from the reader at the given offset.
|
|
|
-func readBox(reader io.ReaderAt, offset int64, size uint64) ([]byte, error) {
|
|
|
- boxData := make([]byte, size)
|
|
|
- _, err := reader.ReadAt(boxData, offset)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- return boxData, nil
|
|
|
-}
|
|
|
-
|
|
|
-// findBox recursively finds a box of the specified type within the given box data.
|
|
|
-func findBox(boxData []byte, boxType string) ([]byte, error) {
|
|
|
- var offset uint32 = 0
|
|
|
- for offset < uint32(len(boxData)) {
|
|
|
- header := getHeaderBoxInfo(boxData[offset:])
|
|
|
- size := uint64(header.Size)
|
|
|
- if header.Size == 1 {
|
|
|
- size = header.Size64
|
|
|
- }
|
|
|
- if getFourccType(header) == boxType {
|
|
|
- return boxData[offset : offset+uint32(size)], nil
|
|
|
- }
|
|
|
- offset += uint32(size)
|
|
|
- }
|
|
|
- return nil, fmt.Errorf("box type %s not found", boxType)
|
|
|
+type Chunk struct {
|
|
|
+ Filename string
|
|
|
+ UniqueCode string
|
|
|
+ ChunkNumber int
|
|
|
+ TotalChunks int
|
|
|
+ Data []byte
|
|
|
}
|
|
|
|
|
|
-// GetMP4Duration reads the duration of an MP4 or M4A file from the provided reader.
|
|
|
-func GetMP4Duration(reader io.ReaderAt) (lengthOfTime int, err error) {
|
|
|
- var info = make([]byte, 0x10)
|
|
|
- var boxHeader BoxHeader
|
|
|
- var offset int64 = 0
|
|
|
-
|
|
|
- // 获取moov结构偏移
|
|
|
- for {
|
|
|
- _, err = reader.ReadAt(info, offset)
|
|
|
- if err == io.EOF {
|
|
|
- fmt.Println("Reached EOF without finding moov box.")
|
|
|
- return 0, fmt.Errorf("moov box not found")
|
|
|
- }
|
|
|
- if err != nil {
|
|
|
- return 0, fmt.Errorf("error reading at offset %d: %w", offset, err)
|
|
|
- }
|
|
|
-
|
|
|
- boxHeader = getHeaderBoxInfo(info)
|
|
|
- fourccType := getFourccType(boxHeader)
|
|
|
-
|
|
|
- if fourccType == "moov" {
|
|
|
- fmt.Println("Found moov box at offset", offset)
|
|
|
- break
|
|
|
- }
|
|
|
-
|
|
|
- nextOffset := int64(boxHeader.Size)
|
|
|
- if fourccType == "mdat" && boxHeader.Size == 1 {
|
|
|
- nextOffset = int64(boxHeader.Size64)
|
|
|
- }
|
|
|
- if nextOffset == 0 {
|
|
|
- return 0, fmt.Errorf("box size is zero, which is invalid and likely means a parsing error")
|
|
|
- }
|
|
|
- offset += nextOffset
|
|
|
- }
|
|
|
-
|
|
|
- // 读取moov盒子数据
|
|
|
- moovBoxData, err := readBox(reader, offset, uint64(boxHeader.Size))
|
|
|
- if err != nil {
|
|
|
- return 0, fmt.Errorf("error reading moov box at offset %d: %w", offset, err)
|
|
|
- }
|
|
|
-
|
|
|
- // 在moov盒子中查找mvhd或tkhd盒子
|
|
|
- mvhdBox, err := findBox(moovBoxData, "mvhd")
|
|
|
- if err != nil {
|
|
|
- tkhdBox, err := findBox(moovBoxData, "tkhd")
|
|
|
- if err != nil {
|
|
|
- return 0, fmt.Errorf("neither mvhd nor tkhd box found in moov box")
|
|
|
- }
|
|
|
- mvhdBox = tkhdBox
|
|
|
- }
|
|
|
-
|
|
|
- // 解析mvhd或tkhd盒子中的timeScale和duration
|
|
|
- var timeScaleOffset, durationOffset uint32
|
|
|
- if getFourccType(getHeaderBoxInfo(mvhdBox)) == "mvhd" {
|
|
|
- timeScaleOffset = 0x1C
|
|
|
- durationOffset = 0x20
|
|
|
- } else if getFourccType(getHeaderBoxInfo(mvhdBox)) == "tkhd" {
|
|
|
- timeScaleOffset = 0x14
|
|
|
- durationOffset = 0x18
|
|
|
- } else {
|
|
|
- return 0, fmt.Errorf("unsupported box type in moov box")
|
|
|
- }
|
|
|
-
|
|
|
- timeScale := binary.BigEndian.Uint32(mvhdBox[timeScaleOffset : timeScaleOffset+4])
|
|
|
- duration := binary.BigEndian.Uint32(mvhdBox[durationOffset : durationOffset+4])
|
|
|
+var chunksMap = make(map[string][]Chunk)
|
|
|
|
|
|
- fmt.Printf("timeScale: %d, duration: %d\n", timeScale, duration)
|
|
|
- if timeScale == 0 {
|
|
|
- return 0, fmt.Errorf("timeScale is zero, division by zero is not possible")
|
|
|
- }
|
|
|
-
|
|
|
- lengthOfTime = int(duration / timeScale)
|
|
|
- return
|
|
|
+// getFourccType 获取信息头类型
|
|
|
+func getFourccType(boxHeader BoxHeader) (fourccType string) {
|
|
|
+ return string(boxHeader.FourccType[:])
|
|
|
}
|