前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2023-02-28:moonfdd/ffmpeg-go是用go语言绑定ffmpeg的库,目前是github上最好用的库。请用go语言将yuv文件编码为h264

2023-02-28:moonfdd/ffmpeg-go是用go语言绑定ffmpeg的库,目前是github上最好用的库。请用go语言将yuv文件编码为h264

原创
作者头像
福大大架构师每日一题
发布2023-02-28 21:30:31
6700
发布2023-02-28 21:30:31
举报

2023-02-28:moonfdd/ffmpeg-go是用go语言绑定ffmpeg的库,目前是github上最好用的库。请用go语言将yuv文件编码为h264文件。

答案2023-02-28:

使用 github.com/moonfdd/ffmpeg-go 库。

命令如下:

代码语言:shell
复制
go run ./examples/a12.video_encode_yuv2h264/main.go

参考了12:yuv编码为h264,代码用golang编写。代码如下:

代码语言:go
复制
package main

import (
	"fmt"
	"os"
	"os/exec"
	"unsafe"

	"github.com/moonfdd/ffmpeg-go/ffcommon"
	"github.com/moonfdd/ffmpeg-go/libavcodec"
	"github.com/moonfdd/ffmpeg-go/libavformat"
	"github.com/moonfdd/ffmpeg-go/libavutil"
)

func main() {
	os.Setenv("Path", os.Getenv("Path")+";./lib")
	ffcommon.SetAvutilPath("./lib/avutil-56.dll")
	ffcommon.SetAvcodecPath("./lib/avcodec-58.dll")
	ffcommon.SetAvdevicePath("./lib/avdevice-56.dll")
	ffcommon.SetAvfilterPath("./lib/avfilter-56.dll")
	ffcommon.SetAvformatPath("./lib/avformat-58.dll")
	ffcommon.SetAvpostprocPath("./lib/postproc-55.dll")
	ffcommon.SetAvswresamplePath("./lib/swresample-3.dll")
	ffcommon.SetAvswscalePath("./lib/swscale-5.dll")

	genDir := "./out"
	_, err := os.Stat(genDir)
	if err != nil {
		if os.IsNotExist(err) {
			os.Mkdir(genDir, 0777) //  Everyone can read write and execute
		}
	}

	//是否存在yuv文件
	_, err = os.Stat("./out/result.yuv")
	if err != nil {
		if os.IsNotExist(err) {
			fmt.Println("create yuv file")
			exec.Command("./lib/ffmpeg", "-i", "./resources/big_buck_bunny.mp4", "-pix_fmt", "yuv420p", "./out/result.yuv", "-y").CombinedOutput()
		}
	}

	ret := int32(0) //默认返回值
	//需要的变量名并初始化
	var fmtCtx *libavformat.AVFormatContext
	var outFmt *libavformat.AVOutputFormat
	var vStream *libavformat.AVStream
	pkt := libavcodec.AvPacketAlloc()
	var codecCtx *libavcodec.AVCodecContext
	var codec *libavcodec.AVCodec
	var picture_buf uintptr
	var picFrame *libavutil.AVFrame
	var size ffcommon.FSizeT

	//[1]!打开视频文件
	in_file, err := os.Open("./out/result.yuv")
	if err != nil {
		fmt.Printf("can not open file!\n")
		return
	}
	defer in_file.Close()

	for {
		//[2]!打开输出文件,并填充fmtCtx数据
		in_w := int32(640)
		in_h := int32(360)
		frameCnt := 1440
		outFile := "./out/result.h264"
		os.Remove(outFile)
		if libavformat.AvformatAllocOutputContext2(&fmtCtx, nil, "", outFile) < 0 {
			fmt.Printf("Cannot alloc output file context.\n")
			break
		}
		outFmt = fmtCtx.Oformat
		//[2]!

		//[3]!打开输出文件
		if libavformat.AvioOpen(&fmtCtx.Pb, outFile, libavformat.AVIO_FLAG_READ_WRITE) < 0 {
			fmt.Printf("output file open failed.\n")
			break
		}

		//[3]!

		//[4]!创建h264视频流,并设置参数
		vStream = fmtCtx.AvformatNewStream(codec)
		if vStream == nil {
			fmt.Printf("failed create new video stream.\n")
			break
		}
		vStream.TimeBase.Den = 25
		vStream.TimeBase.Num = 1
		//[4]!

		//[5]!编码参数相关
		codecPara := fmtCtx.GetStream(uint32(vStream.Index)).Codecpar
		codecPara.CodecType = libavutil.AVMEDIA_TYPE_VIDEO
		codecPara.Width = in_w
		codecPara.Height = in_h
		//[5]!

		//[6]!查找编码器
		codec = libavcodec.AvcodecFindEncoder(outFmt.VideoCodec)
		if codec == nil {
			fmt.Printf("Cannot find any endcoder.\n")
			break
		}
		//[6]!

		//[7]!设置编码器内容
		codecCtx = codec.AvcodecAllocContext3()
		codecCtx.AvcodecParametersToContext(codecPara)
		if codecCtx == nil {
			fmt.Printf("Cannot alloc context.")
			break
		}
		codecCtx.CodecId = outFmt.VideoCodec
		codecCtx.CodecType = libavutil.AVMEDIA_TYPE_VIDEO
		codecCtx.PixFmt = libavutil.AV_PIX_FMT_YUV420P
		codecCtx.Width = in_w
		codecCtx.Height = in_h
		codecCtx.TimeBase.Num = 1
		codecCtx.TimeBase.Den = 25
		codecCtx.BitRate = 400000
		codecCtx.GopSize = 12

		if codecCtx.CodecId == libavcodec.AV_CODEC_ID_H264 {
			codecCtx.Qmin = 10
			codecCtx.Qmax = 51
			codecCtx.Qcompress = 0.6
		}
		if codecCtx.CodecId == libavcodec.AV_CODEC_ID_MPEG2VIDEO {
			codecCtx.MaxBFrames = 2
		}
		if codecCtx.CodecId == libavcodec.AV_CODEC_ID_MPEG1VIDEO {
			codecCtx.MbDecision = 2
		}
		//[7]!

		//[8]!打开编码器
		if codecCtx.AvcodecOpen2(codec, nil) < 0 {
			fmt.Printf("Open encoder failed.\n")
			break
		}
		//[8]!

		fmtCtx.AvDumpFormat(0, outFile, 1) //输出 输出文件流信息

		//初始化帧
		picFrame = libavutil.AvFrameAlloc()
		picFrame.Width = codecCtx.Width
		picFrame.Height = codecCtx.Height
		picFrame.Format = codecCtx.PixFmt
		size = uint64(libavutil.AvImageGetBufferSize(codecCtx.PixFmt, codecCtx.Width, codecCtx.Height, 1))
		picture_buf = libavutil.AvMalloc(size)
		libavutil.AvImageFillArrays((*[4]*byte)(unsafe.Pointer(&picFrame.Data)), (*[4]int32)(unsafe.Pointer(&picFrame.Linesize)), (*byte)(unsafe.Pointer(picture_buf)), codecCtx.PixFmt,
			codecCtx.Width, codecCtx.Height, 1)

		//[9] --写头文件
		ret = fmtCtx.AvformatWriteHeader(nil)
		//[9]

		y_size := codecCtx.Width * codecCtx.Height
		pkt.AvNewPacket(int32(size * 3))
		buf := make([]byte, size)
		//picture_buf = uintptr(unsafe.Pointer(&buf[0]))

		//[10] --循环编码每一帧
		for i := 0; i < frameCnt; i++ {
			//读入YUV
			n, err := in_file.Read(buf)
			if err != nil {
				fmt.Println("read end")
				break
			}
			if n <= 0 {
				break
			}

			for i := 0; i < n; i++ {
				*(*byte)(unsafe.Pointer(picture_buf + uintptr(i))) = buf[i]
			}

			picFrame.Data[0] = (*byte)(unsafe.Pointer(picture_buf))                       //亮度Y
			picFrame.Data[1] = (*byte)(unsafe.Pointer(picture_buf + uintptr(y_size)))     // U
			picFrame.Data[2] = (*byte)(unsafe.Pointer(picture_buf + uintptr(y_size*5/4))) // V
			// AVFrame PTS
			picFrame.Pts = int64(i)

			//编码
			if codecCtx.AvcodecSendFrame(picFrame) >= 0 {
				for codecCtx.AvcodecReceivePacket(pkt) >= 0 {
					fmt.Printf("encoder success!\n")
					// parpare packet for muxing
					pkt.StreamIndex = uint32(vStream.Index)
					pkt.AvPacketRescaleTs(codecCtx.TimeBase, vStream.TimeBase)
					pkt.Pos = -1
					ret = fmtCtx.AvInterleavedWriteFrame(pkt)
					if ret < 0 {
						fmt.Printf("error is: %s.\n", libavutil.AvErr2str(ret))
					}
					pkt.AvPacketUnref() //刷新缓存
				}
			}
		}
		//[10]

		//[11] --Flush encoder
		ret = flush_encoder(fmtCtx, codecCtx, vStream.Index)
		if ret < 0 {
			fmt.Printf("flushing encoder failed!\n")
			break
		}
		//[11]

		//[12] --写文件尾
		fmtCtx.AvWriteTrailer()
		//[12]

		break
	}

	// //===========================释放所有指针===============================//
	libavcodec.AvPacketFree(&pkt)
	codecCtx.AvcodecClose()
	libavutil.AvFree(uintptr(unsafe.Pointer(picFrame)))
	libavutil.AvFree(picture_buf)

	if fmtCtx != nil {
		fmtCtx.Pb.AvioClose()
		fmtCtx.AvformatFreeContext()
	}

	_, err = exec.Command("./lib/ffplay.exe", "./out/result.h264").Output()
	if err != nil {
		fmt.Println("play err = ", err)
	}
}

//刷新编码器
func flush_encoder(fmtCtx *libavformat.AVFormatContext, codecCtx *libavcodec.AVCodecContext, vStreamIndex int32) int32 {
	var ret int32
	enc_pkt := libavcodec.AvPacketAlloc()
	enc_pkt.Data = nil
	enc_pkt.Size = 0

	if codecCtx.Codec.Capabilities&libavcodec.AV_CODEC_CAP_DELAY == 0 {
		return 0
	}

	fmt.Printf("Flushing stream #%d encoder\n", vStreamIndex)
	ret = codecCtx.AvcodecSendFrame(nil)
	if ret >= 0 {
		for codecCtx.AvcodecReceivePacket(enc_pkt) >= 0 {
			fmt.Printf("success encoder 1 frame.\n")

			// parpare packet for muxing
			enc_pkt.StreamIndex = uint32(vStreamIndex)
			enc_pkt.AvPacketRescaleTs(codecCtx.TimeBase, fmtCtx.GetStream(uint32(vStreamIndex)).TimeBase)
			ret = fmtCtx.AvInterleavedWriteFrame(enc_pkt)
			if ret < 0 {
				break
			}
		}
	}

	return ret
}
在这里插入图片描述
在这里插入图片描述

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com