当前位置:   article > 正文

golang 结合ffmpeg 实现视频转码h265转码h264格式 并上传阿里云oss_h265转h264

h265转h264

根据业务需求 实现golang 结合ffmpeg 实现h265转码h264格式并上传阿里云oss

开发环境 gin+gorm+ubuntu20.04 go版本1.20 ffmpeg为6.1

ffmpeg 安装可参考Ubuntu20.04安装ffmpeg(解决h265转码h264而安装)-CSDN博客

  1. type VideoResponse struct {
  2. Size int64 `json:"size"`
  3. Path string `json:"path"`
  4. FullPath string `json:"full_path"`
  5. Name string `json:"name"`
  6. Type string `json:"type"`
  7. }
  8. const paths = "static/uploadfile/"
  9. type Video struct {
  10. api.Api
  11. }
  12. setOnlinePath 设置上传路径
  13. //func setOnlinePath(c *gin.Context) string {
  14. // shopUuid := c.GetHeader(mzl_shop.ShopUuidKey)
  15. // onlinePath := config.ApplicationConfig.Mode + "/" + "shopUuid_" + shopUuid + "/"
  16. // return onlinePath
  17. //}
  18. // UploadVideo 上传视频
  19. // @Summary 上传视频
  20. // @Description 获取JSON
  21. // @Tags 公共接口
  22. // @Accept multipart/form-data
  23. // @Param file formData file true "file"
  24. // @Param fileName formData file true "fileName"
  25. // @Success 200 {string} string "{"code": 200, "message": "添加成功"}"
  26. // @Success 200 {string} string "{"code": -1, "message": "添加失败"}"
  27. // @Router /api/v1/public/UploadVideo [post]
  28. // @Security Bearer
  29. func (e Video) UploadVideo(c *gin.Context) {
  30. e.MakeContext(c)
  31. if e.GetShopUuid() == "" {
  32. e.Error(200, errors.New(""), "商户号不能为空")
  33. return
  34. }
  35. // 获取文件
  36. files, err := c.FormFile("file")
  37. if err != nil {
  38. e.Error(200, errors.New(""), "视频文件不能为空")
  39. return
  40. }
  41. fileName := c.PostForm("fileName")
  42. if fileName == "" {
  43. guid := uuid.New().String()
  44. fileName = guid + utils.GetExt(files.Filename)
  45. }
  46. err = utils.IsNotExistMkDir(path)
  47. if err != nil {
  48. e.Error(500, errors.New(""), "初始化文件路径失败")
  49. }
  50. singleFile := paths + fileName
  51. //保存文件
  52. _ = c.SaveUploadedFile(files, singleFile)
  53. fileType, _ := utils.GetType(singleFile)
  54. fileResponse := VideoResponse{
  55. Size: pkg.GetFileSize(singleFile),
  56. Path: singleFile,
  57. //FullPath: urlPerfix + singleFile,
  58. FullPath: singleFile,
  59. Name: fileName,
  60. Type: fileType,
  61. }
  62. resp, err := e.GetOssoOperate(c, fileResponse)
  63. if err != nil {
  64. e.Error(200, errors.New(""), "视频上传失败")
  65. return
  66. }
  67. e.OK(resp, "视频上传成功")
  68. }
  69. // video 操作
  70. func (e Video) GetOssoOperate(c *gin.Context, fileResponse VideoResponse) (resp dto.UploadCallbackResp, err error) {
  71. rq := dto.UploadCallbackReq{}
  72. _ = c.ShouldBind(&rq)
  73. err = e.MakeContext(c).Errors
  74. if err != nil {
  75. // 处理错误
  76. e.Logger.Error(err)
  77. e.Error(500, err, err.Error())
  78. return
  79. }
  80. //一定要绝对路径 不然找不到文件
  81. filePrefix := "/www/wwwroot/xxxxx/uploadfile" //输入
  82. fileOutPrefix := "/www/wwwroot/xxxxx/uploadfile/out_file" //输出
  83. filePath := "/" + fileResponse.Name
  84. fileName := filePrefix + filePath
  85. codec := checkVideoCodec(fileName)
  86. switch codec {
  87. case "h265":
  88. tempFilePath := fileOutPrefix + filePath
  89. err = convertVideoCodec(fileName, tempFilePath, "libx264")
  90. if err != nil {
  91. log.Println("输出失败:%s", err)
  92. return
  93. }
  94. // 替换原文件(并删除输出文件)
  95. err = os.Rename(tempFilePath, fileName)
  96. if err != nil {
  97. log.Println("替换原文件失败:%s", err)
  98. return
  99. }
  100. }
  101. // 上传到对象存储
  102. oxsStore := file_store.OXS{
  103. Endpoint: config.OssConfig.Endpoint,
  104. AccessKeyID: config.OssConfig.AccessKeyId,
  105. AccessKeySecret: config.OssConfig.AccessKeySecret,
  106. BucketName: config.OssConfig.BucketName,
  107. }
  108. var oxs = oxsStore.Setup(file_store.AliYunOSS)
  109. ossFileName := config.ApplicationConfig.Mode + "/shopUuid_" + e.GetShopUuid() + filePath
  110. err = oxs.UpLoad(ossFileName, fileName)
  111. if err != nil {
  112. fmt.Println("err::", err)
  113. }
  114. // 删除本地文件
  115. err = os.Remove(fileResponse.Path)
  116. if err != nil {
  117. log.Println("删除文件 Error:", err)
  118. return
  119. }
  120. // 敏感过滤
  121. req := dto2.SafetyReq{}
  122. rq.MimeType = fileResponse.Type
  123. //oss文件地址
  124. rq.Filename = "xxxxxx/" + ossFileName
  125. resp.Data = rq
  126. greenSer := green.GreenService{
  127. Endpoint: config.GreenConfig.Endpoint,
  128. Region: config.GreenConfig.Region,
  129. AccessKeyId: config.GreenConfig.AccessKeyId,
  130. AccessKeySecret: config.GreenConfig.AccessKeySecret,
  131. }
  132. if strings.Contains(fileResponse.Type, "video/mp4") {
  133. req.Type = dto2.GreenTypeVideo
  134. req.Content = rq.Filename
  135. req.Level = 70
  136. req.Num = 5
  137. req.Offset = 2
  138. } else {
  139. e.Logger.Error("MimeType:" + rq.MimeType)
  140. e.OK(resp, "回调成功")
  141. return
  142. }
  143. res, err := greenSer.InitHandle(&req)
  144. if err != nil {
  145. e.Logger.Error(err)
  146. return
  147. }
  148. if res == nil {
  149. e.Logger.Error("res is nil")
  150. e.OK(resp, "回调成功")
  151. return
  152. }
  153. // 存在敏感
  154. if res.Confidence == 1 {
  155. // 删除资源
  156. err = ossObj().DeleteObject(ossFileName)
  157. if err != nil {
  158. // 处理错误
  159. e.Logger.Error(err)
  160. }
  161. // 设置返回
  162. resp.Msg = res.Msg
  163. resp.Code = res.Confidence
  164. e.OK(resp, resp.Msg)
  165. return
  166. }
  167. return
  168. }

调用用ffmpeg命令判断视频格式&转码为h264封装得操作方法

  1. // 检查视频编码格式
  2. func checkVideoCodec(filePath string) string {
  3. _, stderr, err := executeFFmpegCommand("-i", filePath, "-hide_banner", "-f", "null", "-")
  4. if err != nil {
  5. fmt.Printf("Error executing command: %v\n", err)
  6. fmt.Println("ffmpeg output:", stderr)
  7. return ""
  8. }
  9. if strings.Contains(stderr, "hevc") || strings.Contains(stderr, "h265") || strings.Contains(stderr, "mpeg") || strings.Contains(stderr, "av1") || strings.Contains(stderr, "Copy") {
  10. return "h265"
  11. } else if strings.Contains(stderr, "avc1") || strings.Contains(stderr, "h264") {
  12. return "h264"
  13. }
  14. return "unknown"
  15. }
  16. // 转换视频编码格式
  17. func convertVideoCodec(filePath, outputPath, codec string) error {
  18. // 调整转码参数,例如使用多线程和更快的预设
  19. args := []string{"-i", filePath, "-vcodec", codec, "-preset", "ultrafast", "-threads", "0", outputPath} //快 但是视频会变大
  20. //args := []string{"-i", filePath, "-vcodec", codec, outputPath} //慢 但是时间久
  21. //args := []string{"-i", filePath, "-vcodec", codec, "-preset", "veryfast", "-threads", "0", outputPath}
  22. _, stderr, err := executeFFmpegCommand(args...)
  23. if err != nil {
  24. fmt.Printf("Command finished with error: %v\n", err)
  25. fmt.Printf("ffmpeg error output: %s\n", stderr)
  26. return err
  27. }
  28. return nil
  29. }
  30. // 执行ffmpeg命令并返回输出
  31. func executeFFmpegCommand(args ...string) (string, string, error) {
  32. cmd := exec.Command("ffmpeg", args...)
  33. var out bytes.Buffer
  34. var stderr bytes.Buffer
  35. cmd.Stdout = &out
  36. cmd.Stderr = &stderr
  37. err := cmd.Run()
  38. return out.String(), stderr.String(), err
  39. }

apifox 传入file和filename

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/950054
推荐阅读
相关标签
  

闽ICP备14008679号