当前位置:   article > 正文

2023-04-01:当Go语言遇见FFmpeg视频解码器,使用Go语言改写decode_video.c文件,提升视频解码效率与开发体验。

2023-04-01

2023-04-01:当Go语言遇见FFmpeg视频解码器,使用Go语言改写decode_video.c文件,提升视频解码效率与开发体验。

答案2023-04-01:

步骤如下:

1.导入必要的依赖库,包括 fmt、os、unsafe 和其它 FFmpeg 库相关的 Go 库。

2.定义一个名为 main0() 的函数,该函数负责视频解码操作。在函数中定义了许多变量,例如文件名、编解码器、解析器、编解码器上下文、文件句柄、AVFrame 等等。

3.通过命令行参数获取输入文件名和输出文件名,并进行一些基本的参数检查。

4.通过调用 AvPacketAlloc() 函数创建一个 AVPacket 对象,用于存储解码后的帧数据。如果创建失败,则退出程序。

5.初始化输入缓冲区 inbuf 并设置结尾填充字节为 0。

6.调用 AvcodecFindDecoder() 函数查找 MPEG-1 视频解码器。如果找不到,则退出程序。

7.调用 AvParserInit() 函数初始化解析器。如果初始化失败,则退出程序。

8.调用 AvCodecAllocContext3() 函数分配一个新的编解码器上下文对象。如果分配失败,则退出程序。

9.调用 AvcodecOpen2() 函数打开编解码器。如果打开失败,则退出程序。

10.打开输入文件,并创建一个 AVFrame 对象。

11.进入循环,读取输入文件并将其分解成视频帧。如果读取失败或读取完毕,则跳出循环。

12.调用 AvParserParse2() 函数将输入缓冲区中的数据解析为视频帧,并存储在 AVPacket 对象中。如果解析失败,则退出程序。

13.如果成功解析到一个视频帧,则调用 decode() 函数对其进行解码并保存到输出文件中。

14.在循环结束后,调用 decode() 函数对剩余的数据进行解码并保存到输出文件中。

15.关闭输入文件句柄、解析器、编解码器上下文和 AVFrame 对象等资源,以避免内存泄漏。

16.定义一个名为 pgm_save() 的函数,该函数用于将视频帧写入 PGM 格式文件。

17.定义一个名为 decode() 的函数,该函数用于对视频帧进行解码并调用 pgm_save() 函数将其写入 PGM 格式文件。

18.定义 main() 函数,该函数将 FFmpeg 库的路径设置为当前目录下的 lib 子目录,并调用 main0() 函数进行视频解码操作。

注意:在 Windows 操作系统中,您可能需要将 FFmpeg 库的可执行文件添加到 PATH 环境变量中,或者使用 SetXXXPath() 函数设置它们的路径,才能够正常运行此代码。

代码见github/moonfdd/ffmpeg-go。

执行命令:

./lib/ffmpeg -i ./resources/big_buck_bunny.mp4 -c:v mpeg1video ./out/big_buck_bunny.mpg

go run ./examples/internalexamples/decode_video/main.go ./out/big_buck_bunny.mpg ./out/ppm/big_buck_bunny.yuv

./lib/ffplay  ./out/ppm/big_buck_bunny.yuv-113.ppm
  • 1
  • 2
  • 3
  • 4
  • 5

golang代码如下:

package main

import (
	"fmt"
	"os"
	"unsafe"

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

func main0() (ret ffcommon.FInt) {
	var filename, outfilename string
	var codec *libavcodec.AVCodec
	var parser *libavcodec.AVCodecParserContext
	var c *libavcodec.AVCodecContext
	var f *os.File
	var frame *libavutil.AVFrame
	var inbuf [INBUF_SIZE + libavcodec.AV_INPUT_BUFFER_PADDING_SIZE]ffcommon.FUint8T
	var data *ffcommon.FUint8T
	var data_size ffcommon.FSizeT
	var pkt *libavcodec.AVPacket

	if len(os.Args) <= 2 {
		fmt.Printf("Usage: %s <input file> <output file>\nAnd check your input file is encoded by mpeg1video please.\n", os.Args[0])
		os.Exit(0)
	}
	filename = os.Args[1]
	outfilename = os.Args[2]

	pkt = libavcodec.AvPacketAlloc()
	if pkt == nil {
		os.Exit(1)
	}

	/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
	//memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

	/* find the MPEG-1 video decoder */
	codec = libavcodec.AvcodecFindDecoder(libavcodec.AV_CODEC_ID_MPEG1VIDEO)
	if codec == nil {
		fmt.Printf("Codec not found\n")
		os.Exit(1)
	}

	parser = libavcodec.AvParserInit(int32(codec.Id))
	if parser == nil {
		fmt.Printf("parser not found\n")
		os.Exit(1)
	}

	c = codec.AvcodecAllocContext3()
	if c == nil {
		fmt.Printf("Could not allocate video codec context\n")
		os.Exit(1)
	}

	/* For some codecs, such as msmpeg4 and mpeg4, width and height
	   MUST be initialized there because this information is not
	   available in the bitstream. */

	/* open it */
	if c.AvcodecOpen2(codec, nil) < 0 {
		fmt.Printf("Could not open codec\n")
		os.Exit(1)
	}

	var err error
	f, err = os.Open(filename)
	if err != nil {
		fmt.Printf("Could not open %s,err = %s\n", filename, err)
		os.Exit(1)
	}

	frame = libavutil.AvFrameAlloc()
	if frame == nil {
		fmt.Printf("Could not allocate video frame\n")
		os.Exit(1)
	}

	for {
		/* read raw data from the input file */
		var n int
		n, err = f.Read(inbuf[:INBUF_SIZE])
		if err != nil {
			break
		}
		data_size = uint64(n)
		if data_size == 0 {
			break
		}

		/* use the parser to split the data into frames */
		data = (*byte)(unsafe.Pointer(&inbuf))
		for data_size > 0 {
			ret = parser.AvParserParse2(c, &pkt.Data, (*int32)(unsafe.Pointer(&pkt.Size)),
				data, int32(data_size), libavutil.AV_NOPTS_VALUE, libavutil.AV_NOPTS_VALUE, 0)
			if ret < 0 {
				fmt.Printf("Error while parsing\n")
				os.Exit(1)
			}
			data = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(data)) + uintptr(ret)))
			data_size -= uint64(ret)

			if pkt.Size != 0 {
				decode(c, frame, pkt, outfilename)
			}
		}
	}

	/* flush the decoder */
	decode(c, frame, nil, outfilename)

	f.Close()

	parser.AvParserClose()
	libavcodec.AvcodecFreeContext(&c)
	libavutil.AvFrameFree(&frame)
	libavcodec.AvPacketFree(&pkt)

	return 0
}

const INBUF_SIZE = 4096

func pgm_save(buf ffcommon.FBuf, wrap, xsize, ysize ffcommon.FInt, filename string) {
	var f *os.File
	var i ffcommon.FInt

	var err error
	f, err = os.Create(filename)
	if err != nil {
		return
	}
	f.WriteString(fmt.Sprintf("P5\n%d %d\n%d\n", xsize, ysize, 255))
	bytes := []byte{}
	for i = 0; i < ysize; i++ {
		for j := int32(0); j < xsize; j++ {
			bytes = append(bytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + uintptr(i*wrap+j))))
		}
	}
	f.Write(bytes)
	f.Close()
}

func decode(dec_ctx *libavcodec.AVCodecContext, frame *libavutil.AVFrame, pkt *libavcodec.AVPacket, filename string) {
	// var buf [1024]byte
	var ret ffcommon.FInt

	ret = dec_ctx.AvcodecSendPacket(pkt)
	if ret < 0 {
		fmt.Printf("Error sending a packet for decoding\n")
		os.Exit(1)
	}

	for ret >= 0 {
		ret = dec_ctx.AvcodecReceiveFrame(frame)
		if ret == -libavutil.EAGAIN || ret == libavutil.AVERROR_EOF {
			return
		} else if ret < 0 {
			fmt.Printf("Error during decoding %d\n", ret)
			os.Exit(1)
		}

		fmt.Printf("saving frame %3d\n", dec_ctx.FrameNumber)
		//fflush(stdout)

		/* the picture is allocated by the decoder. no need to
		   free it */
		pgm_save(frame.Data[0], frame.Linesize[0],
			frame.Width, frame.Height, fmt.Sprintf("%s-%d.ppm", filename, dec_ctx.FrameNumber))
	}
}

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-58.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
		}
	}

	main0()
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/950076
推荐阅读
相关标签
  

闽ICP备14008679号