Golang调用FFmpeg转换视频流

问题背景

        问题背景是在,由于视频采集端使用的是H264编码采集的裸流,而网络流媒体大多是以FLV为主的直播方式进行的,为了实现实时直播,当前是打算直接使用FFmpeg将H264裸流实时转成FLV视频流。

        为什么是使用FLV视频流呢,因为相对简单,加上FLV Header后将每个NALU打包成Tag并进去大致就行了。但是这块怕有疏忽,最终还是想使用成熟的工具FFmpeg。

方法实现

1. 使用FFmpeg-go封装好的方法

FFmpeg-go

上面有具体的Demo,但是只有流转文件,也只有文件转流的方法,对于流转流还是需要自己动手处理一下。

import ffmpeg "github.com/u2takey/ffmpeg-go"

err := ffmpeg.Input("pipe:", ffmpeg.KwArgs{
		"format":     "rawvideo",
		"video_size": fmt.Sprintf("%dx%d", 480, 1064)}).WithInput(filein).
		Output("pipe:",
			ffmpeg.KwArgs{"c:v": "libx264", "f": "flv", "crf": "24"}).
		WithOutput(buf, errorbuf).
		Run()
if err != nil {
		panic(err)
}
bufs := make([]byte, 1024)
out, _ := os.OpenFile("res2.flv", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
for {
	n, err := buf.Read(bufs)
	if n == 0 || err == io.EOF {
		out.Close()
		break
	} else {
		//此处处理输出流,这边简单地写到文件里
		out.Write(bufs)
	}
}

其中

filein   是输入的H264视频裸流   buf是输出的视频流

虽说代码是这么写,但是实际上是run不起来的,不清楚具体是什么原因,因此后续还是打算直接调用ffmpeg可执行程序。

2. 直接调用FFmpeg

首先需要在电脑上下载好ffmpeg并且添加到环境变量,保证执行Powshell或者cmd指令的ffmpeg时能够正常运行,接下来还是直接上代码。

cmd := exec.Command("ffmpeg", "-re", "-r", "30", "-i", "pipe:0", "-vcodec", "libx264", "-f", "flv", "pipe:1", "-y", "another.flv")

//获取输入流
stdin, err := cmd.StdinPipe()
if err != nil {
	fmt.Println("Error getting stdout pipe:", err)
	return
}

//要写东西进去的时候只需要
stdin.Write(bytes) 

//获取输出流
stdout, err := cmd.StdoutPipe()
if err != nil {
	fmt.Println("Error getting stdout pipe:", err)
	return
}

// Start the command
err = cmd.Start()
if err != nil {
	fmt.Println("Error starting command:", err)
	return
}

        这边需要注意到的点是,如果不加上"-re"的话,需要等stdin.close()之后,整个ffmpeg才会运行,才能见到有输出流。

        其次,这边加了 “-y” "another.flv",是用来进行对比的,这里ffmpeg将会把输出同时放在两个地方,一个写入到文件another.flv里(-y 是覆盖原有文件),另一个通过stdout传出来,我将stdout的数据流保存成out.flv后发现同another.flv对比发现并不相同。或许视频流和文件不应该这么处理。

总结

建议直接调用FFmpeg的可执行文件进行操作最好。

但是这样实时的流转流的方式实际上还是有挺大延迟的,如果要实时视频流P2P模式建议要么在采集端处理好要么在播放端处理好,中间层就不要过多操作影响实时性,如果是推流拉流的模式还是建议在中间层处理好。

存在问题

这边还是发现一些问题的,尤其是直接使用ffmpeg的时候

1. 直接读取文件和读取文件通过stdin传入byte获取到的结果是不一致的。

2. 直接output成文件和通过stdout获取byte再保存成文件得到的结果也是不一致的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要改变视频的分辨率,可以使用golang中的FFmpeg库(例如go-ffmpeg),通过调用FFmpeg的API进行处理。 以下是一个修改分辨率的示例代码: ```go package main import ( "github.com/gabriel-vasile/mimetype" "github.com/golang/glog" "github.com/wailovet/go-ffmp/ffmp" ) func main() { inputFilename := "input.mp4" outputFilename := "output.mp4" width := 640 height := 360 // 检查文件类型 mime, err := mimetype.DetectFile(inputFilename) if err != nil || mime.Extension() != "mp4" { glog.Errorf("Invalid input file") return } // 初始化FFmpeg ff := ffmp.NewFFmpeg() defer ff.Close() // 打开输入文件 if err := ff.AddInputFile(inputFilename); err != nil { glog.Errorf("Failed to add input file: %v", err) return } // 设置输出格式 if err := ff.SetOutputFormat("mp4"); err != nil { glog.Errorf("Failed to set output format: %v", err) return } // 添加过滤器 filter := "scale=%d:%d" if err := ff.AddVideoFilter(filter, width, height); err != nil { glog.Errorf("Failed to add video filter: %v", err) return } // 输出文件 if err := ff.SetOutputFile(outputFilename); err != nil { glog.Errorf("Failed to set output file: %v", err) return } // 开始转码 if err := ff.Run(); err != nil { glog.Errorf("Failed to run FFmpeg: %v", err) return } } ``` 该代码使用go-ffmpeg库对视频文件进行处理。首先检查输入文件是否为mp4格式,然后使用AddInputFile方法打开输入文件。接着,使用SetOutputFormat方法设置输出格式为mp4,并使用AddVideoFilter方法添加过滤器(即修改分辨率),最后使用SetOutputFile方法设置输出文件名。最后,调用Run方法启动FFmpeg进行转码。 你可以根据自己的需求修改代码中的参数来实现不同的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值