Golang implements video H264 codec based on FFmpeg

1. Basic knowledge

1.1 FFmpeg related

FFmpeg is the leading multimedia framework capable of decoding, encoding, transcoding, muxing, decrypting, streaming, filtering and playing almost anything created by humans and machines. It supports the most obscure archaic formats right up to the most cutting edge. Whether they were designed by some standards committee, community, or company. It is also highly portable.

FFmpeg can be compiled and run under various build environments, machine architectures and configurations such as Linux, Mac OS X, Microsoft Windows, BSDs, Solaris, and passed the test infrastructure FATE

It contains libavcodec, libavutil, libavformat, libavfilter, libavdevice, libswscale and libswresample, which can be used by applications. There are also ffmpeg, ffplay and ffprobe, which can be used by end users for transcoding and playback.

FFmpeg source code download address: FFmpeg official website
(you can choose to download the source code and compile it yourself and add it, such as x264, fdk-acc, etc., or you can directly download the dynamic/static library) For
specific FFmpeg compilation in Centos environment, please refer to: FFmpeg Compilation in Centos Environment

1.2 H.264 related

H.264 is a video encoding format

Video coding refers to the existence of a lot of redundant information in the video, such as strong correlation between adjacent pixels of the image, similar content between adjacent images of the video sequence, and the human visual system is not sensitive to certain details. The process of processing this part of redundant information

Common video encoding formats are:
insert image description hereH.264 is a new generation of encoding standards, known for its high compression quality and support for streaming media transmission on multiple networks

1.3 YUV related

During the transcoding process, the video needs to be decoded into yuv and then re-encoded to change some parameters, and some processing needs to be done on yuv such as adding watermark, improving brightness, etc.

YUV is a video format . Like RGB, YUV is the encoding format of pixel data. A group of YUV renders a pixel on the screen, and controls the screen to display things in the form of color, where Y represents the brightness in the pixel, English It is Luminance, U means chroma, English is Chrominance, V means concentration or saturation, English is Chroma. This is a compressed color representation method, which takes up less physical space, and the color distortion is not obvious

There are two categories of YUV storage methods:

  1. From the literal meaning of Packed
    , packed means packing, and packing is not necessarily flat. Corresponding to the storage method, the three components of YUV are interleaved. Taking YUY2 as an example, the storage method is: Y0U0Y1V0 Y2U1Y3V1. The method will be more troublesome when parsing
  2. From the literal meaning of Planar
    , planar means plane, and the plane is relatively flat. Corresponding to the storage method, the three components of YUV are stored separately. Taking I420 as an example, the storage method is: YYYYYYYYUUVV, which is simple and clear, and Y is stored first. , then save U, and then save V, which is very convenient in parsing

Mainstream YUV sampling methods :

  1. 4:4:4 , if you want to store it completely, you need to store three YUV components for each pixel, this form is 4:4:4
  2. 4:2:2 , because human eyes are not particularly sensitive to chroma and saturation, so losing part of UV to a certain extent does not affect our ability to distinguish colors. When storing, we deliberately lose part of the UV component, and use two Y components to share one Group UV components, this form is 4:2:2
  3. 4:2:0 or share a set of UV with four Y components, this form is 4:2:0

In the figure below, black dots represent the Y component of the sampled pixel, and hollow circles represent the UV component of the pixel that is used for
insert image description herestorage. YUV each occupies a Byte. If it is 4:4:4, the resolution is 256X256 The picture in the picture will occupy 256×256×3=196608Byte, the 4:2:2 method will occupy 256×256×2=131072Byte, and the 4:2:0 method will occupy 256×256×2/3=43690.7Byte, you can see Using the 4:2:0 method reduces the storage space by half

Two, H264 encoding principle

The H264 encoding process is mainly divided into five modules:
insert image description here

2.1 Frame type analysis

Determine a type for each frame of the input YUV data, namely I frame, P frame and B frame , I frame is an intra-coding frame, P frame is a forward prediction frame, and B frame is a bidirectional interpolation frame . I-frames do not depend on information from other frames, that is, self-referencing frames. Both P and B frames are frames that rely on other frame information to complete their own predictions. The difference is that in the display sequence, the P frame is a forward reference, and the B frame is a front-to-back two-way reference.

The I frame can be understood as a complete picture in the movie, which contains all the image information, while the P frame and B frame record the changes relative to the I frame

It can be imagined that there is a video where a person walks from the left side of the screen to the right side. When the video is first opened, the first frame image displayed must be self-reconstructed, because there is no image to refer to, and such a frame is an I frame. When the second image is displayed later, it is found that except for a slight position change in the frame except for the movement of the person, the rest of the still place is the same as the previous frame. In this case, the static data of the previous frame can be copied directly, and the current frame only needs to save the difference from the previous frame (that is, the motion displacement vector). Such a frame is a P/B frame, and a B frame Because there is also a backward reference, that is, it has a wider range of search than the P frame reference, so the compression rate of the B frame is relatively higher. At this time, the camera suddenly turned and gave a close-up of this person's face. Then a new picture needs to be reconstructed at this time, which is a new I frame

2.2 Intra/Inter Prediction

Usually, the encoder divides the image into blocks by algorithm, and then performs subsequent compression processing block by block

Assuming that the current block is not at the edge of the image, we can use the adjacent value of the upper adjacent block boundary as the basic value, that is, each value in the above row, and copy it vertically downward to construct a prediction block of the same size as the source YUV block , this way of constructing prediction blocks is called vertical prediction mode, which is a kind of intra prediction mode. Similar to it, there are horizontal prediction mode, mean prediction mode (that is, the mean value of 4x4 fills the entire 4x4), etc.

Then, use the source YUV data and the predicted YUV data to do the difference to get the residual block, so that we can directly transmit the residual data and the flag bit of the prediction mode of the current 4x4 block in the code stream, which is extremely Great savings in code stream

insert image description here

2.3 Transformation + Quantization

The residual after prediction is transformed by DCT space-frequency, DC and low frequency (relatively flat, most of the image or block) energy is concentrated on the upper left, high frequency (details, a small portion of the image or block) energy is concentrated on the right Under the circumstances, although DCT itself has no compression effect, it has laid an indispensable foundation for the choice of future compression.

Since the human eye is not sensitive to high-frequency signals, we can define such a variable QP=5, and divide all the values ​​in the transform block by QP, which further saves the bit width of the transmission code stream, and at the same time mainly removes the high-frequency components. value, at the decoding end, it is only necessary to multiply all the values ​​in the transform block by QP to basically restore the low-frequency components

We call the process of QP calculation quantization . It can be seen that the larger the quantization value, the more high-frequency information is lost. In addition, the encoder uses plastic variables to represent pixel values, so the low-frequency information restored by the maximum quantization value will also be The less accurate, the greater the distortion and the greater the block effect. The quality loss of video coding mainly comes from this

2.4 Filtering

When the quantization value fluctuates greatly, it may cause obvious block effects in the area of ​​the real boundary of the picture. Filtering is an operation to reduce block effects and improve picture quality.
There are three main operations: 1. Preliminary estimation of block effect boundary strength; 2. Distinguish between true and false boundaries; 3. Calculate difference

2.5 Entropy Coding

In the process of real network transmission, it must be binary code, so the current pixel value needs to be further compressed into a binary stream. There are two entropy coding methods in coding:

  1. Simpler Cavlc
  2. Cabac with higher compression efficiency but more complex calculation

Specific reference paper " Comparison and Improvement of CABAC Algorithm and CAVLC Algorithm in H.264 "

Three, H264 decoding to YUV

The source code and library of FFmpeg are both C code and library. The interface of FFmpeg used by golang language needs to use cgo to encapsulate the interface of C library so that the Go code can call it. The third-party library of Go is used here.

import (
	"github.com/giorgisio/goav/avcodec"
	"github.com/giorgisio/goav/avformat"
	"github.com/giorgisio/goav/avutil"
	 ...  ...  ...  ...  ...
)

3.1 Code logic and use API

H264 is decoded as YUV, the whole code logic is as follows:

  1. Create the AvformatContext structure:
 func avformat.AvformatAllocContext() *avformat.Context
  1. Open the input file:
func avformat.AvformatOpenInput(ps **avformat.Context, fi string, fmt *avformat.InputFormat, d **avutil.Dictionary) int
  1. Get the video stream information of the input file:
func (*avformat.Context).AvformatFindStreamInfo(d **avutil.Dictionary) int
  1. Loop through the stream information contained in the video until a stream of video type is found:
func (*avformat.Context).Streams() []*avformat.Stream
func (*avformat.Stream).CodecParameters() *avcodec.AvCodecParameters
func (*avcodec.AvCodecParameters).AvCodecGetType() avcodec.MediaType 
  1. Find codecs:
func avcodec.AvcodecFindDecoder(id avcodec.CodecId) *avcodec.Codec   or
func avcodec.AvcodecFindDecoderByName(name string) *avcodec.Codec 
  1. Configure the decoder:
func (*avcodec.Codec).AvcodecAllocContext3() *avcodec.Context 
func (*avcodec.Context).AvcodecCopyContext(ctxt2 *avcodec.Context) int
  1. Open the decoder:
func (*avcodec.Context).AvcodecOpen2(c *avcodec.Codec, d **avcodec.Dictionary) int
  1. Allocate frame and packet structure
func avutil.AvFrameAlloc() *avutil.Frame
func avcodec.AvPacketAlloc() *avcodec.Packet
  1. Provide packet data as the input of the decoder, and the frame receives the output of the decoder
func (*avformat.Context).AvReadFrame(pkt *avcodec.Packet)
func (*avcodec.Context).AvcodecSendPacket(packet *avcodec.Packet) int
func (*avcodec.Context).AvcodecReceiveFrame(frame *avcodec.Frame) int

3.2 Specific code implementation

package main

import (
	"errors"
	"fmt"
	"os"
	"unsafe"

	"github.com/giorgisio/goav/avcodec"
	"github.com/giorgisio/goav/avformat"
	"github.com/giorgisio/goav/avutil"
)

var width int
var height int

func FFmpeg_H264DecodeToYUV(input_filename string, output_filename string) error {
    
    
	file, _ := os.Create(output_filename)

	//创建AvformatContext结构体
	Inputformatctx := avformat.AvformatAllocContext()
	//打开文件
	if avformat.AvformatOpenInput(&Inputformatctx, input_filename, nil, nil) != 0 {
    
    
		return errors.New("Unable to open input file " + input_filename)
	}
	//获取视频流信息
	if Inputformatctx.AvformatFindStreamInfo(nil) < 0 {
    
    
		Inputformatctx.AvformatCloseInput()
		return errors.New("Error: Couldn't find stream information.")
	}

	Inputformatctx.AvDumpFormat(0, input_filename, 0)

	nCount := 0
	//循环查找视频中包含的流信息,直到找到视频类型的流
	//记录下来,保存到videoStreamIndex变量中
	var i int
	for i = 0; i < int(Inputformatctx.NbStreams()); i++ {
    
    
		switch Inputformatctx.Streams()[i].CodecParameters().AvCodecGetType() {
    
    
		case avformat.AVMEDIA_TYPE_VIDEO:

			// Get a pointer to the codec context for the video stream
			pCodecCtxOrig := Inputformatctx.Streams()[i].Codec()
			// 查找解码器
			pCodec := avcodec.AvcodecFindDecoder(avcodec.CodecId(pCodecCtxOrig.GetCodecId()))
			//pCodec := avcodec.AvcodecFindDecoderByName("libx264")
			if pCodec == nil {
    
    
				return errors.New("Unsupported codec!----------")
			}
			// 配置解码器
			pCodecCtx := pCodec.AvcodecAllocContext3()
			if pCodecCtx.AvcodecCopyContext((*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig))) != 0 {
    
    
				return errors.New("Couldn't copy codec context--------------")
			}

			// 打开解码器
			if pCodecCtx.AvcodecOpen2(pCodec, nil) < 0 {
    
    
				return errors.New("Could not open codec-------------")
			}

			width := pCodecCtx.Width()

			height := pCodecCtx.Height()
			pFrameYUV := avutil.AvFrameAlloc()
			packet := avcodec.AvPacketAlloc()       //分配一个packet
			packet.AvNewPacket(int(width * height)) //调整packet的数据
			fmt.Println("width:", width)
			fmt.Println("height:", height)

			for Inputformatctx.AvReadFrame(packet) >= 0 {
    
    
				// Is this a packet from the video stream?
				if packet.StreamIndex() == i {
    
    
					// 提供原始数据包数据作为解码器的输入
					if pCodecCtx.AvcodecSendPacket(packet) >= 0 {
    
    
						//从解码器返回解码的输出数据
						for pCodecCtx.AvcodecReceiveFrame((*avcodec.Frame)(unsafe.Pointer(pFrameYUV))) == 0 {
    
    
							nCount++

							bytes := []byte{
    
    }
							//y
							ptr := uintptr(unsafe.Pointer(avutil.Data(pFrameYUV)[0]))
							for j := 0; j < width*height; j++ {
    
    
								bytes = append(bytes, *(*byte)(unsafe.Pointer(ptr)))
								ptr++
							}
							//u
							ptr = uintptr(unsafe.Pointer(avutil.Data(pFrameYUV)[1]))
							for j := 0; j < width*height/4; j++ {
    
    
								bytes = append(bytes, *(*byte)(unsafe.Pointer(ptr)))
								ptr++
							}
							//v
							ptr = uintptr(unsafe.Pointer(avutil.Data(pFrameYUV)[2]))
							for j := 0; j < width*height/4; j++ {
    
    
								bytes = append(bytes, *(*byte)(unsafe.Pointer(ptr)))
								ptr++
							}
							//写文件
							file.Write(bytes)
						}
						if nCount == 100 {
    
    
							break
						}

					}

				}
				packet.AvPacketUnref()
			}
			fmt.Printf("There are %d frames int total.\n", nCount)
			// Free the YUV frame
			avutil.AvFrameFree(pFrameYUV)
			packet.AvFreePacket()
			file.Close()
			// Close the codecs
			pCodecCtx.AvcodecClose()
			(*avcodec.Context)(unsafe.Pointer(pCodecCtxOrig)).AvcodecClose()

			// 关闭视频文件
			Inputformatctx.AvformatCloseInput()
			// Stop after saving frames of first video straem
			break

		default:
			return errors.New("Didn't find a video stream")
		}

	}
	return nil
}

func main() {
    
    

	input_filename := "song.mp4"
	output_filename := "1280x720_yuv420p.yuv"
	avformat.AvRegisterAll()
	//解码视频流数据
	FFmpeg_H264DecodeToYUV(input_filename, output_filename)

}

3.3 YUV file playback

You can use YUVplayer to play YUV files, the download address is YUVplayer player

When playing, set the size and color of the player
insert image description here

4. YUV encoding is H264

4.1 Code logic and use API

YUV data is encoded in H264 format, and its code logic is as follows:

  1. open output file
func avformat.AvGuessFormat(sn string, f string, mt string) *avformat.OutputFormat
func avformat.AvformatAllocContext() *avformat.Context
func avformat.AvformatAllocOutputContext2(ctx **avformat.Context, o *avformat.OutputFormat, fo string, fi string) int
func avformat.AvIOOpen(url string, flags int) (res *avformat.AvIOContext, err error)
func (*avformat.Context).SetPb(pb *avformat.AvIOContext)
  1. Create H264 video stream and set parameters
func (*avformat.Context).AvformatNewStream(c *avformat.AvCodec) *avformat.Stream
func (*avformat.Stream).AvStreamSetRFrameRate(r avcodec.Rational)
  1. find encoder
func avcodec.AvcodecFindEncoderByName(c string) *avcodec.Codec  or
func avcodec.AvcodecFindEncoder(id avcodec.CodecId) *avcodec.Codec
  1. configure encoder
func (*avcodec.Codec).AvcodecAllocContext3() *avcodec.Context
func (*avcodec.Context).SetEncodeParams2(width int, height int, pxlFmt avcodec.PixelFormat, hasBframes bool, gopSize int, profile int)
  1. open encoder
func (*avcodec.Context).AvcodecOpen2(c *avcodec.Codec, d **avcodec.Dictionary) int
  1. Create frame and configure
func avutil.AvFrameAlloc() *avutil.Frame
func avcodec.AvpictureGetSize(pf avcodec.PixelFormat, w int, h int) int
func (*avcodec.Picture).AvpictureFill(pt *uint8, pf avcodec.PixelFormat, w int, h int) int
  1. Write the file header and create the packet structure
func (*avformat.Context).AvformatWriteHeader(o **avutil.Dictionary) int
func avcodec.AvPacketAlloc() *avcodec.Packet
  1. Read the data of the YUV file into the frame, and send the frame to the decoder, and the packet receives the encoded data
func (*avcodec.Context).AvcodecSendFrame(frame *avcodec.Frame) int
func (*avcodec.Context).AvcodecReceivePacket(packet *avcodec.Packet) int
  1. Convert the Pts and dts of the packet
func (*avcodec.Context).AvCodecGetPktTimebase() avcodec.Rational
func (*avformat.Stream).TimeBase() avcodec.Rational
func avutil.AVRescaleQRnd(a int64, bq avutil.Rational, cq avutil.Rational, rnd uint32) int64
func avutil.AVRescaleQRnd(a int64, bq avutil.Rational, cq avutil.Rational, rnd uint32) int64
func (*avcodec.Packet).SetPts(pts int64)
func (*avcodec.Packet).SetDts(dts int64)
  1. Write the data in the packet to the output file
func (*avformat.Context).AvInterleavedWriteFrame(pkt *avcodec.Packet) int
  1. Write the end of the file and release the previous resources
func (*avformat.Context).AvWriteTrailer() int

4.2 Specific code implementation

package main

import (
	"errors"
	"fmt"
	"os"
	"unsafe"

	"github.com/giorgisio/goav/avcodec"
	"github.com/giorgisio/goav/avformat"
	"github.com/giorgisio/goav/avutil"
)

const (
	width   = 1280
	height  = 720
	fps     = 25
	bitrate = 400000
	fmtCnt  = 100
)

func encode(enc_ctx *avcodec.Context, frame *avutil.Frame, packet *avcodec.Packet, VStream *avformat.Stream, outFmtCtx *avformat.Context) int {
    
    
	var ret int

	if frame != nil {
    
    
		fmt.Println("frame Send..........")
	}

	ret = enc_ctx.AvcodecSendFrame((*avcodec.Frame)(unsafe.Pointer(frame)))
	if ret < 0 {
    
    
		fmt.Println("Avcodec Send Frame failed")
		return -1
	}
	for ret >= 0 {
    
    
		ret = enc_ctx.AvcodecReceivePacket(packet)

		fmt.Println("packet size is ", ret, " ", packet.Size())
		if ret == avutil.AvErrorEAGAIN || ret == avutil.AvErrorEOF {
    
    
			return 0
		} else if ret < 0 {
    
    
			continue
		}
		fmt.Println("finish encode and write data to out_file")
		packet.SetStreamIndex(VStream.Index())
		input_time_base := enc_ctx.AvCodecGetPktTimebase()
		output_time_base := VStream.TimeBase()
		input_tmp := (*avutil.Rational)(unsafe.Pointer(&input_time_base))
		output_tmp := (*avutil.Rational)(unsafe.Pointer(&output_time_base))
		dtscurrent := avutil.AVRescaleQRnd(packet.Dts(), *input_tmp, *output_tmp, uint32(avutil.AV_ROUND_NEAR_INF|avutil.AV_ROUND_PASS_MINMAX))
		ptscurrent := avutil.AVRescaleQRnd(packet.Pts(), *input_tmp, *output_tmp, uint32(avutil.AV_ROUND_NEAR_INF|avutil.AV_ROUND_PASS_MINMAX))
		packet.SetPts(ptscurrent)
		packet.SetDts(dtscurrent)

		outFmtCtx.AvInterleavedWriteFrame(packet)

		packet.AvPacketUnref()
	}
	return 0
}

func FFmpeg_YuvEncodeToH264(input_filename string, output_filename string) error {
    
    

	in_file, err := os.Open(input_filename)
	if err != nil {
    
    
		return errors.New("Open file failed")
	}
	defer in_file.Close()

	var packet *avcodec.Packet
	var enc_ctx *avcodec.Context
	var frame *avutil.Frame
	var outFmtCtx *avformat.Context

	ufmt := avformat.AvGuessFormat("H264", output_filename, "")
	outFmtCtx = avformat.AvformatAllocContext()
	if avformat.AvformatAllocOutputContext2(&outFmtCtx, ufmt, "mp4", output_filename) < 0 {
    
    
		return errors.New("Cannot alloc output file context.")
	}

	pb, err := avformat.AvIOOpen(output_filename, avformat.AVIO_FLAG_WRITE)
	if err != nil {
    
    
		return err
	}
	outFmtCtx.SetPb(pb)
	// 创建h264流, 并设置参数
	var rational avcodec.Rational
	rational.Set(1, fps)
	VStream := outFmtCtx.AvformatNewStream(nil)
	if VStream == nil {
    
    
		return errors.New("VStream is nil")
	}
	VStream.AvStreamSetRFrameRate(rational) //设置25帧每秒,fps为25

	// //设置相关编码参数
	codecPara := outFmtCtx.Streams()[VStream.Index()].Codec()
	codecPara.SetCodecType(avformat.AVMEDIA_TYPE_VIDEO)
	codecPara.SetWidth(width)
	codecPara.SetHeight(height)

	//查找编码器
	pCodec := avcodec.AvcodecFindEncoderByName("libx264")
	if pCodec == nil {
    
    
		return errors.New("avcodec_find_encoder_by_name fail")
	}

	//配置编码器的上下文
	enc_ctx = pCodec.AvcodecAllocContext3()

	enc_ctx.SetEncodeParams2(width, height, avcodec.AV_PIX_FMT_YUV420P, false, 10, avcodec.FF_PROFILE_H264_HIGH)
	enc_ctx.SetTimebase(1, fps) //设置25帧每秒,fps为25

	if int(pCodec.AvcodecAllocContext3().CodecId()) == avcodec.AV_CODEC_ID_H264 {
    
    
		fmt.Println("H264........")
	}

	//打开编码器
	if enc_ctx.AvcodecOpen2(pCodec, nil) < 0 {
    
    
		errors.New("Could not open codec-------------")
	}

	//创建frame并初始化
	frame = avutil.AvFrameAlloc()
	avutil.AvSetFrame(frame, enc_ctx.Width(), enc_ctx.Height(), int(enc_ctx.PixFmt()))

	newSize := avcodec.AvpictureGetSize(enc_ctx.PixFmt(), enc_ctx.Width(), enc_ctx.Height())
	picture_buf := avutil.AvMalloc(uintptr(newSize))

	avp := (*avcodec.Picture)(unsafe.Pointer(frame))
	avp.AvpictureFill((*uint8)(picture_buf), enc_ctx.PixFmt(), enc_ctx.Width(), enc_ctx.Height())

	fmt.Println("newSize = ", newSize)
	fmt.Println("width:", enc_ctx.Width())
	fmt.Println("height:", enc_ctx.Height())
	fmt.Println("PixFmt:", enc_ctx.PixFmt())
	fmt.Println("Profile:", enc_ctx.Profile())

	//写文件头
	if outFmtCtx.AvformatWriteHeader(nil) < 0 {
    
    
		return errors.New("write header error,outputfile name : " + output_filename)
	}

	//创建编码后的数据包,用来存储frame编码后的数据
	packet = avcodec.AvPacketAlloc()
	packet.AvNewPacket(newSize)
	y_size := enc_ctx.Width() * enc_ctx.Height()
	//循环编码每一帧
	var j int = 0
	for i := 0; i < fmtCnt; i++ {
    
    
		//读入YUV

		buf := make([]byte, newSize)
		n, err := in_file.Read(buf)
		if err != nil {
    
    
			return errors.New("read in_file failed....")
		}

		//将buf数据拷贝倒picture_buf
		copy((*[1 << 30]byte)(picture_buf)[:newSize:newSize], buf[:n])

		pic_ptr := uintptr(picture_buf)
		//y
		avutil.SetData(frame, 0, (*uint8)(unsafe.Pointer(pic_ptr)))
		fmt.Println("data[0]:", avutil.Data(frame)[0])
		fmt.Println("*data[0]:", *(avutil.Data(frame)[0]))

		//u
		avutil.SetData(frame, 1, (*uint8)(unsafe.Pointer(pic_ptr+uintptr(y_size))))
		fmt.Println("data[1]:", avutil.Data(frame)[1])
		fmt.Println("*data[1]:", *(avutil.Data(frame)[1]))

		// v
		avutil.SetData(frame, 2, (*uint8)(unsafe.Pointer(pic_ptr+uintptr(y_size*5/4))))
		fmt.Println("data[2]:", avutil.Data(frame)[2])
		fmt.Println("*data[2]:", *(avutil.Data(frame)[2]))

		fmt.Println("准备编码 ---------------------")

		avutil.FrameSetPts(frame, int64(j))
		j = i + 1
		//利用编码器进行编码, 将frame的数据传入packet
		if encode(enc_ctx, frame, packet, VStream, outFmtCtx) == -1 {
    
    
			break
		}

	}
	//flush encoder
	encode(enc_ctx, nil, packet, VStream, outFmtCtx)

	//写文件尾
	outFmtCtx.AvWriteTrailer()

	//释放所有指针资源
	enc_ctx.AvcodecClose()
	avutil.AvFree(unsafe.Pointer(frame))

	if outFmtCtx != nil {
    
    
		outFmtCtx.Pb().Close()
		outFmtCtx.AvformatFreeContext()
	}
	return nil
}

func main() {
    
    
	output_filename := "1280x720_yuv420p.yuv"
	filename := "result.H264"
	avformat.AvRegisterAll()

	//编码码视频流数据
	FFmpeg_YuvEncodeToH264(output_filename, filename)
}

4.3 H264 file playback

H264 files can be played with vlc player, the download address of the player is: vlc player

vlc player toolbar—codec information, you can view the video encoding format and data loss rate

Guess you like

Origin blog.csdn.net/weixin_54792212/article/details/130317737