1 ffmpeg工具是什么
FFmpeg即是一款音视频编解码工具,同时也是一组音视频编码开发套件,作为编码开发套件,它为开发者提供了丰富的音视频处理的调用接口。
FFmpeg提供了多种媒体格式的封装和解封装,包括多种音视频编码、多种协议的流媒体、多种多彩格式转换、多种采样率转换、多种码率转换等;FFmpeg框架提供了多种丰富的插件模块,包含封装与解封装的插件、编码与解码的插件等。ffmpeg官网下载地址:https://johnvansickle.com/ffmpeg/
2 ffmpeg工具类
项目pakage:
2.1 获取编码格式NeedTranscode
2.2 获取时长getDuration
2.3 获取码率getCodeRate
2.4 获取分辨率getResRate
2.5 转码transcode
指定转码格式加参数 -c:a -c:v type a是Audio(音频) v是Video(视频)
ps:
ffmpeg -i input_file.ext -c:a aac output_file.aac
ffmpeg -i input_file.ext -c:v aac output_file.mp4
2.6 源文件ffmpeg_util.go
package ffmpeg_util
import (
"fmt"
"os/exec"
"regexp"
"strconv"
"strings"
"support/logger"
)
type IFfmpegTranscode interface {
NeedTranscode(srcPath string) (bool, error)
Transcode(srcPath, dstPath string) error
GetDuration(srcPath string) (int, error)
GetCodeRate(srcPath string) (int, error)
GetResRate(srcPath string) (string, error)
}
// 默认转码格式为文件名后缀
func transcode(log logger.ILog, srcPath, dstPath string) error {
cmd := exec.Command(getTranscodeTool(), "-i", srcPath, dstPath)
res, err := cmd.CombinedOutput()
log.Debug("ffmpeg exec srcPath: %s, result: %s", srcPath, res)
return err
}
// 获取音视频时长
func getDuration(log logger.ILog, srcPath string) (int, error) {
cmd := exec.Command(getBashTool(), "-c")
arg := fmt.Sprintf(`%s -i %s 2>&1 | grep -m 1 'Duration:' | cut -d ' ' -f 4 | sed s/,// | awk -F ":" '{ print (($1*3600) + ($2*60) + $3) * 1000 }'`,
getTranscodeTool(), srcPath)
cmd.Args = append(cmd.Args, arg)
res, err := cmd.CombinedOutput()
log.Debug("ffmpeg exec srcPath: %s, cmd:%s, result: %s", srcPath, arg, res)
if err != nil {
return 0, err
}
if len(string(res)) == 0 {
return 0, nil
}
// Chrome只支持标准的H.264编码
durStr := strings.Trim(string(res), "\n")
return strconv.Atoi(durStr)
}
// 获取码率
func getCodeRate(log logger.ILog, srcPath string) (int, error) {
cmd := exec.Command(getBashTool(), "-c")
arg := fmt.Sprintf(`%s -i %s 2>&1 | grep 'Audio:' | grep -m 1 'Hz'| awk -F "," '{print $2}'| awk -F " " '{print $1}'`,
getTranscodeTool(), srcPath)
cmd.Args = append(cmd.Args, arg)
res, err := cmd.CombinedOutput()
log.Debug("ffmpeg exec srcPath: %s, cmd:%s, result: %s", srcPath, arg, res)
if err != nil {
return 0, err
}
// Chrome只支持标准的H.264编码
resStr := strings.Trim(string(res), "\n")
if len(resStr) == 0 {
return 0, nil
}
return strconv.Atoi(resStr)
}
// 获取分辨率
func getResRate(log logger.ILog, srcPath string) (string, error) {
cmd := exec.Command(getBashTool(), "-c")
arg := fmt.Sprintf(`%s -i %s 2>&1 | grep 'Video:' | grep -m 1 'fps' `,
getTranscodeTool(), srcPath)
cmd.Args = append(cmd.Args, arg)
res, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
log.Debug("ffmpeg exec srcPath: %s, cmd:%s, result: %s", srcPath, arg, res)
resStr := extractResolution(string(res))
return resStr, nil
}
func extractResolution(input string) string {
re := regexp.MustCompile(`\b(\d{3,4}x\d{3,4})\b`)
matches := re.FindStringSubmatch(input)
if len(matches) < 2 {
return ""
}
return matches[1]
}
func needTranscode(log logger.ILog, srcPath, streamType, coderType string) (bool, error) {
cmd := exec.Command(getBashTool(), "-c")
arg := fmt.Sprintf(`%s -i %s 2>&1 | grep 'Stream' | grep -m 1 '%s:' | sed s/,// | awk -F " " '{ print $4 }'`,
getTranscodeTool(), srcPath, streamType)
cmd.Args = append(cmd.Args, arg)
res, err := cmd.CombinedOutput()
log.Debug("needTranscode exec srcPath: %s, cmd:%s, result: %s", srcPath, arg, res)
if err != nil {
return false, err
}
// Chrome支持标准的H.264编码以及MP3编码
coder := strings.Trim(string(res), "\n")
if coder == coderType {
return false, nil
}
return true, nil
}
3 系统差异
3.1 program_linux.go
package ffmpeg_util
func getTranscodeTool() string {
return "ffmpeg"
}
func getBashTool() string {
return "bash"
}
3.2 program_windows.go
package ffmpeg_util
func getTranscodeTool() string {
return "./resource/ffmpeg.exe"
}
func getBashTool() string {
return "D:\\install\\Git\\bin\\bash.exe"
}
4 客户端
4.1 audio_client.go
package ffmpeg_util
import (
"support/logger"
)
const coderMp3 = "mp3"
const Audio = "Audio"
type audioTranscode struct {
log logger.ILog
}
func NewAudioTranscode(log logger.ILog) *audioTranscode {
return &audioTranscode{
log: log,
}
}
func (at *audioTranscode) NeedTranscode(srcPath string) (bool, error) {
return needTranscode(at.log, srcPath, Audio, coderMp3)
}
func (at *audioTranscode) Transcode(srcPath, dstPath string) error {
return transcode(at.log, srcPath, dstPath)
}
func (at *audioTranscode) GetDuration(srcPath string) (int, error) {
return getDuration(at.log, srcPath)
}
func (at *audioTranscode) GetCodeRate(srcPath string) (int, error) {
return getCodeRate(at.log, srcPath)
}
func (at *audioTranscode) GetResRate(srcPath string) (string, error) {
return "", nil
}
4.2 video_client.go
4.2.1 获取视频封面
package ffmpeg_util
import (
"os/exec"
"support/logger"
)
const coderH264 = "h264"
const Video = "Video"
type videoTranscode struct {
log logger.ILog
transcodeTool string
bashTool string
}
func NewVideoTranscode(log logger.ILog) *videoTranscode {
return &videoTranscode{
log: log,
transcodeTool: getTranscodeTool(),
bashTool: getBashTool(),
}
}
func (vt *videoTranscode) NeedTranscode(srcPath string) (bool, error) {
return needTranscode(vt.log, srcPath, Video, coderH264)
}
func (vt *videoTranscode) Transcode(srcPath, dstPath string) error {
return transcode(vt.log, srcPath, dstPath)
}
func (vt *videoTranscode) GetDuration(srcPath string) (int, error) {
return getDuration(vt.log, srcPath)
}
func (vt *videoTranscode) GetCodeRate(srcPath string) (int, error) {
return getCodeRate(vt.log, srcPath)
}
func (vt *videoTranscode) GetResRate(srcPath string) (string, error) {
return getResRate(vt.log, srcPath)
}
// CaptureCover 视频截取第一帧当封面
func CaptureCover(log logger.ILog, srcPath string, coverPath string) error {
cmd := exec.Command(getTranscodeTool(), "-i", srcPath)
cmd.Args = append(cmd.Args, "-y", "-f", "image2")
cmd.Args = append(cmd.Args, "-frames", "1", coverPath)
res, err := cmd.CombinedOutput()
log.Debug("ffmpeg exec srcPath: %s, result: %s", srcPath, res)
return err
}
5.调用示例
var ft ffmpeg_util.IFfmpegTranscode
// 前面做过fileType校验
switch record.FileType {
case audioFileType:
ft = ffmpeg_util.NewAudioTranscode(log)
case videoFileType:
ft = ffmpeg_util.NewVideoTranscode(log)
}
// 本地转码
dstPath := path.Join(videoDir, util.GenObjectId()+filepath.Ext(localPath))
if err = ft.Transcode(localPath, dstPath); err != nil {
log.Error("fileId:%s trans failed as:%s", record.Id, err)
return
}