golang使用ffmpeg的那些坑(整理后)

因业务需求需要用到golang调用ffmpeg,期间遇到各种曲折离奇的事情,例如服务运行时崩溃,流解析出错等等,还好最终拨开云雾见日出

1、静态编译问题

(1)常规使用

  • go1.11.2版本之前
   go build -ldflags -extldflags=-static
  • go1.11.2

可以直接在声明C或C++代码区域添加-static

//#cgo CFLAGS: -D_REENTRANT
//#cgo LDFLAGS:-static 
//#cgo pkg-config: libavutil
import "C"

(2)go调用ffmpeg

正常情况以上办法就可以编译生成静态可执行文件,但是go调用ffmpeg时就不行了,因为要用到glibc只能进行动态链接,解决办法是修改ffmpeg编译后生成的pkgconfig中的相关*.pc文件。

-Wl,-Bstatic:ffmpeg接口强制静态链接, -Wl,-Bdynamic -lc :glibc强制动态链接 ,将libav-开头的.pc文件改过就好。

Conflicts:
Libs: -L${libdir} -Wl,-Bstatic -lavutil -lm -Wl,-Bdynamic -lc 

注:本人使用过程中发现要先进行动态编译后再进行以上操作才可,原因未明。

2、找到合适的ffmpeg包

本人在原先go-libav包的基础上做了很多改动,不知这个包为什么有那么多star,结果确连example都跑不起来,最后无奈进行了些许改动,例如超时函数的实现,音频数据的处理等。修改后的包看glibav,暂无时间写example,因项目紧任务重,未来得及进行重新架构,本人的项目主要功能是处理各种视频stream生成jpg和wav数据然后发送到kafka,已经上线使用。

3、项目性能调优

在实际应用中发现服务性能与完全用C编写性能差距还是很大的,最后使用多个goroutine勉强达到业务需求。以下列举一些性能调优的常用命令:

1、goTool之pprof的使用
//生成cpu profile
go test -run=^$ -bench=. -cpuprofile=profile.out
//生成memery profile
go test -run=^$ -bench=. -memprofile=profile.out
//使用原生pprof启动Web UI
pprof -http=:8080 profile.out
2、代码优化--goreporter
goreporter -p [projectRelativePath] -r [reportPath] -e [exceptPackagesName] -f [json / html / text] {-t templatePathIfHtml}

问题汇总

获取输入流帧率

videoFPS 即输入流的帧率,下面是在原代码例子上改的,可以参考原示例对照,https://github.com/imkira/go-libav

func (si *SIConf) openInputVideoStream(ctx *avContext) error {
	var err error

	// find first video stream
	if ctx.decStream = videoStream(ctx.decFmt); ctx.decStream == nil {
		logger.Error("Could not find a video stream. Aborting...")
		return fmt.Errorf("Could not find a video stream")
	}

	codecCtx := ctx.decStream.CodecContext()
	codecPar := ctx.decStream.CodecParameters()
	codec := avcodec.FindDecoderByID(codecCtx.CodecID())

	rat := ctx.decFmt.GuessFrameRate(ctx.decStream, nil)

	si.videoFPS = float32(rat.Numerator()) / float32(rat.Denominator())

	if codec == nil {
		logger.Error("Could not find decoder:", codecCtx.CodecID())
	}
	if ctx.decCodec, err = avcodec.NewContextWithCodec(codec); err != nil {
		logger.Error("Failed to create codec context:", err)
	}

	if err := codecCtx.CopyTo(ctx.decCodec, codecPar); err != nil {
		logger.Error("Failed to copy codec context:", err)
	}
	if err := ctx.decCodec.SetInt64Option("refcounted_frames", 1); err != nil {
		logger.Error("Failed to copy codec context:", err)
	}
	//设置多线程
	options := avutil.NewDictionary()
	defer options.Free()
	if err := options.Set("threads", "auto"); err != nil {
		logger.Error("Failed to set input options:", err)
	}
	if err := ctx.decCodec.OpenWithCodec(codec, options); err != nil {
		logger.Error("Failed to open codec:", err)
	}

	//set time base
	ctx.decCodec.SetTimeBase(ctx.decStream.TimeBase())

	// we need a v	log.Println(ctx.decCodec.Width(), ctx.decCodec.Height(), ctx.decCodec.PixelFormat(), ctx.decCodec.TimeBase())ideo filter to push the decoded frames to
	ctx.srcFilter = addFilter(ctx, "buffer", "in")
	if err = ctx.srcFilter.SetImageSizeOption("video_size", ctx.decCodec.Width(), ctx.decCodec.Height()); err != nil {
		logger.Error("Failed to set filter option:", err)
	}
	if err = ctx.srcFilter.SetPixelFormatOption("pix_fmt", ctx.decCodec.PixelFormat()); err != nil {
		logger.Error("Failed to set filter option:", err)
	}
	if err = ctx.srcFilter.SetRationalOption("time_base", ctx.decStream.TimeBase()); err != nil {
		logger.Error("Failed to set filter option:", err)
	}
	//log.Println(ctx.decCodec.Width(), ctx.decCodec.Height(), ctx.decCodec.PixelFormat(), ctx.decStream.TimeBase())

	if err = ctx.srcFilter.Init(); err != nil {
		logger.Error("Failed to initialize buffer filter:", err)
	}
	return nil
}

因厌烦csdn的各种垃圾广告,建了个个人博客站点,欢迎访问:https://robinsea.github.io/post/golang使用ffmpeg的那些坑/

你可能感兴趣的:(golang使用ffmpeg的那些坑(整理后))