国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

golang基于FFmpeg實(shí)現(xiàn)視頻H264編解碼

這篇具有很好參考價(jià)值的文章主要介紹了golang基于FFmpeg實(shí)現(xiàn)視頻H264編解碼。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一、基本知識(shí)

1.1 FFmpeg相關(guān)

FFmpeg 是領(lǐng)先的多媒體框架,能夠解碼、編碼、轉(zhuǎn)碼、混合、解密、流媒體、過(guò)濾和播放人類(lèi)和機(jī)器創(chuàng)造的幾乎所有東西。它支持最晦澀的古老格式,直到最尖端的格式。無(wú)論它們是由某個(gè)標(biāo)準(zhǔn)委員會(huì)、社區(qū)還是公司設(shè)計(jì)的。它還具有高度的便攜性。

FFmpeg 可以在 Linux、Mac OS X、Microsoft Windows、BSDs、Solaris 等各種構(gòu)建環(huán)境、機(jī)器架構(gòu)和配置下編譯、運(yùn)行,并通過(guò)測(cè)試基礎(chǔ)設(shè)施 FATE

它包含了 libavcodec、libavutil、libavformat、libavfilter、libavdevice、libswscale 和 libswresample,可以被應(yīng)用程序使用。還有 ffmpeg、ffplay 和 ffprobe,可以被終端用戶(hù)用于轉(zhuǎn)碼和播放。

FFmpeg源碼下載地址:FFmpeg官網(wǎng)
(可以選擇下載源碼自己編譯并加入如x264, fdk-acc等,也可以直接下載動(dòng)/靜態(tài)庫(kù))
具體FFmpeg在Centos環(huán)境下編譯可以參考:FFmpeg在Centos環(huán)境下編譯

1.2 H.264相關(guān)

H.264是一種視頻編碼格式

視頻編碼是指視頻中存在很多冗余信息,比如圖像相鄰像素之間有較強(qiáng)的相關(guān)性,視頻序列的相鄰圖像之間內(nèi)容相似,人的視覺(jué)系統(tǒng)對(duì)某些細(xì)節(jié)不敏感等,對(duì)這部分冗余信息進(jìn)行處理的過(guò)程

常見(jiàn)的視頻編碼格式有:
golang基于FFmpeg實(shí)現(xiàn)視頻H264編解碼H.264是新一代的編碼標(biāo)準(zhǔn),以高壓縮高質(zhì)量和支持多種網(wǎng)絡(luò)的流媒體傳輸著稱(chēng)

1.3 YUV相關(guān)

在轉(zhuǎn)碼過(guò)程中需要將視頻解碼成yuv再重新編碼以便更改一些參數(shù), 也需要在yuv上做一些處理比如添加水印, 提升亮度,等等

YUV是一種視頻格式, YUV與RGB一樣,都是像素?cái)?shù)據(jù)的編碼格式,一組YUV渲染屏幕上的一個(gè)像素,控制屏幕用色彩的形式將事物表現(xiàn)出來(lái),其中Y表示像素中的亮度,英文是Luminance,U表示色度,英文是Chrominance,V表示濃度或飽和度,英文是Chroma。這是一種壓縮后的顏色表示方法,占用更少的物理空間,且對(duì)顏色的表現(xiàn)失真不明顯

YUV存儲(chǔ)方式有兩大分類(lèi):

  1. Packed
    從字面意思來(lái)看,packed是打包的意思,打包就不一定是平整的了,對(duì)應(yīng)到存儲(chǔ)方式上就是把YUV三種分量交叉存儲(chǔ),以YUY2為例,存儲(chǔ)方式為:Y0U0Y1V0 Y2U1Y3V1,這種方式在解析時(shí)就會(huì)比較麻煩
  2. Planar
    從字面意思上來(lái)看,planar是平面的意思,平面比較平整,對(duì)應(yīng)到存儲(chǔ)方式上就是把YUV三種分量分別存儲(chǔ),以I420為例,存儲(chǔ)方式為:YYYYYYYYUUVV,簡(jiǎn)單明了,先把Y存完,再存U,再存V,這種在解析時(shí)很方便

主流的YUV采樣方式

  1. 4:4:4,如果要完全存儲(chǔ),那一個(gè)一個(gè)像素點(diǎn)就要存儲(chǔ)YUV三個(gè)分量,這種形式就是4:4:4
  2. 4:2:2,因?yàn)槿说难劬?duì)色度和飽和度不是特別敏感,所以一定程度上丟失一部分UV并不影響我們分辨顏色在存儲(chǔ)時(shí)就故意丟掉部分UV分量,用兩個(gè)Y分量共用一組UV分量,這種形式就是4:2:2
  3. 4:2:0或用四個(gè)Y分量共用一組UV,這種形式就是4:2:0

下圖中以黑點(diǎn)表示采樣該像素點(diǎn)的Y分量,以空心圓圈表示采用該像素點(diǎn)的UV分量
golang基于FFmpeg實(shí)現(xiàn)視頻H264編解碼在存儲(chǔ)時(shí)YUV各占一個(gè)字節(jié)Byte,如果4:4:4方式,那一個(gè)256X256分辨率的圖片要占用256×256×3=196608Byte,4:2:2方式要占用256×256×2=131072Byte,4:2:0方式要占用256×256×2/3=43690.7Byte,可以看到采用4:2:0方式存儲(chǔ)空間整整減少了一半

二、H264編碼原理

H264編碼過(guò)程主要分為五個(gè)模塊:
golang基于FFmpeg實(shí)現(xiàn)視頻H264編解碼

2.1 幀類(lèi)型分析

對(duì)輸入進(jìn)來(lái)的YUV數(shù)據(jù)的每一幀確定一個(gè)類(lèi)型,即I幀,P幀和B幀, I幀是內(nèi)部編碼幀,P幀是向前預(yù)測(cè)幀,B幀是雙向內(nèi)插幀。I幀不會(huì)依賴(lài)其他幀的信息,也就是自我進(jìn)行參考的幀。P和B幀的話(huà),都是會(huì)依賴(lài)其他幀信息來(lái)完成自身預(yù)測(cè)的幀,區(qū)別在于顯示序列中P幀是前向參考,B幀是前后雙向參考。

I 幀可以理解為電影中的一個(gè)完整畫(huà)面,里面包含了所有的圖像信息,而P幀和B幀記錄的是相對(duì)于I幀的變化

可以想象現(xiàn)在有一段視頻,一個(gè)人從畫(huà)面左邊走到右邊,剛打開(kāi)這個(gè)視頻的時(shí)候,顯示的第一幀圖像肯定是要自我重建的,因?yàn)闆](méi)有圖像可以參考,這樣的幀就是I幀。后面的再顯示第二張圖像發(fā)現(xiàn)除了畫(huà)面中除了人運(yùn)動(dòng)的一點(diǎn)點(diǎn)位置發(fā)生了變化,剩下靜止不動(dòng)的地方都和前一幀一樣。這樣的話(huà),就可以把前一幀靜止的數(shù)據(jù)直接復(fù)制過(guò)來(lái),當(dāng)前幀只需要把和前一幀的不同點(diǎn)(也就是運(yùn)動(dòng)位移矢量)保存下來(lái)就行,這樣的幀就是P/B幀,B幀因?yàn)檫€有后向參考,也就是說(shuō),它比P幀參考搜索的范圍更大,所以B幀的壓縮率相對(duì)更高。這時(shí)候鏡頭突然一轉(zhuǎn),給了這個(gè)人的臉一張?zhí)貙?xiě)。那么這時(shí)候就會(huì)需要重建一個(gè)新的畫(huà)面,就是一個(gè)新的 I 幀

2.2 幀內(nèi)/幀間預(yù)測(cè)

通常,編碼器會(huì)通過(guò)算法將圖像劃分為一塊一塊的,然后逐塊進(jìn)行后續(xù)的壓縮處理

假設(shè)當(dāng)前的塊不在圖像邊緣,我們可以用上方相鄰塊邊界鄰近值作為基礎(chǔ)值,也就是上面一行中的每一個(gè)值,都垂直向下做拷貝,構(gòu)建出和源 YUV 塊一樣大小的預(yù)測(cè)塊,這種構(gòu)建預(yù)測(cè)塊的方式,叫做垂直預(yù)測(cè)模式,屬于幀內(nèi)預(yù)測(cè)模式的一種。與它相似的,還有水平預(yù)測(cè)模式、均值預(yù)測(cè)模式(也就是4x4的均值填充整個(gè) 4x4)等

緊接著,用源YUV的數(shù)據(jù)和預(yù)測(cè)YUV的數(shù)據(jù)做差值,得到殘差塊,這樣我們?cè)诖a流中,就直接傳輸殘差的數(shù)據(jù)和當(dāng)前4x4塊的預(yù)測(cè)模式的標(biāo)志位就行,這極大地節(jié)省了碼流

golang基于FFmpeg實(shí)現(xiàn)視頻H264編解碼

2.3 變換+量化

預(yù)測(cè)之后的殘差經(jīng)過(guò)DCT空頻變換,直流和低頻(相對(duì)平坦,圖像或塊中大部分占比)能量集中在左上,高頻(細(xì)節(jié),圖像或塊中少部分占比)能量集中在右下,DCT本身雖然沒(méi)有壓縮作用,卻為以后壓縮時(shí)的取舍,奠定了必不可少的基礎(chǔ)

于人眼對(duì)高頻信號(hào)不敏感,我們可以定義這樣一個(gè)變量QP=5,將變換塊中所有的值都除以QP,這樣做進(jìn)一步節(jié)省傳輸碼流位寬,同時(shí)主要去掉了高頻分量的值,在解碼端只需要將變換塊中所有的值在乘QP就可以基本還原低頻分量

我們將QP運(yùn)算的過(guò)程稱(chēng)為量化,可見(jiàn)量化值越大,丟掉的高頻信息就越多,再加上編碼器中都是用整形變量代表像素值,所以量化值最大還原的低頻信息也會(huì)越不準(zhǔn)確,即造成的失真就越大,塊效應(yīng)也會(huì)越大,視頻編碼的質(zhì)量損失主要來(lái)源于此

2.4 濾波

當(dāng)量化值波動(dòng)特別大的時(shí)候,可能會(huì)造成畫(huà)面真實(shí)邊界的區(qū)域內(nèi)有明顯的塊效應(yīng),濾波是一個(gè)減少塊效應(yīng)提升畫(huà)面質(zhì)量的操作。
主要有三部分操作:1、初步估算塊效應(yīng)邊界強(qiáng)度;2、區(qū)分真假邊界 ;3、計(jì)算差值

2.5 熵編碼

在真實(shí)網(wǎng)絡(luò)傳輸?shù)倪^(guò)程中肯定都是二進(jìn)制碼,所以需要將當(dāng)前的像素值進(jìn)一步壓縮成二進(jìn)制流。在編碼中一共有兩種熵編碼方式:

  1. 較為簡(jiǎn)單的Cavlc
  2. 壓縮效率更高但運(yùn)算更復(fù)雜的Cabac

具體參考論文《H.264中CABAC算法與CAVLC算法比較和改進(jìn)》

三、H264解碼為YUV

FFmpeg的源代碼和庫(kù)都是C的代碼和庫(kù),golang語(yǔ)言使用FFmpeg的接口需要使用cgo對(duì)C庫(kù)的接口進(jìn)行封裝,以便go代碼調(diào)用,這里使用的是go的第三方庫(kù)

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

3.1 代碼邏輯及使用API

H264解碼為YUV,整個(gè)代碼邏輯如下:

  1. 創(chuàng)建AvformatContext結(jié)構(gòu)體:
 func avformat.AvformatAllocContext() *avformat.Context
  1. 打開(kāi)輸入文件:
func avformat.AvformatOpenInput(ps **avformat.Context, fi string, fmt *avformat.InputFormat, d **avutil.Dictionary) int
  1. 獲取輸入文件的視頻流信息:
func (*avformat.Context).AvformatFindStreamInfo(d **avutil.Dictionary) int
  1. 循環(huán)查找視頻中包含的流信息,直到找到視頻類(lèi)型的流:
func (*avformat.Context).Streams() []*avformat.Stream
func (*avformat.Stream).CodecParameters() *avcodec.AvCodecParameters
func (*avcodec.AvCodecParameters).AvCodecGetType() avcodec.MediaType 
  1. 查找解碼器:
func avcodec.AvcodecFindDecoder(id avcodec.CodecId) *avcodec.Codec   or
func avcodec.AvcodecFindDecoderByName(name string) *avcodec.Codec 
  1. 配置解碼器:
func (*avcodec.Codec).AvcodecAllocContext3() *avcodec.Context 
func (*avcodec.Context).AvcodecCopyContext(ctxt2 *avcodec.Context) int
  1. 打開(kāi)解碼器:
func (*avcodec.Context).AvcodecOpen2(c *avcodec.Codec, d **avcodec.Dictionary) int
  1. 分配frame和packet結(jié)構(gòu)
func avutil.AvFrameAlloc() *avutil.Frame
func avcodec.AvPacketAlloc() *avcodec.Packet
  1. 提供packet數(shù)據(jù)作為解碼器的輸入,frame接收解碼器的輸出
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 具體代碼實(shí)現(xiàn)

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)

	//創(chuàng)建AvformatContext結(jié)構(gòu)體
	Inputformatctx := avformat.AvformatAllocContext()
	//打開(kāi)文件
	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
	//循環(huán)查找視頻中包含的流信息,直到找到視頻類(lèi)型的流
	//記錄下來(lái),保存到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--------------")
			}

			// 打開(kāi)解碼器
			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()       //分配一個(gè)packet
			packet.AvNewPacket(int(width * height)) //調(diào)整packet的數(shù)據(jù)
			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 {
					// 提供原始數(shù)據(jù)包數(shù)據(jù)作為解碼器的輸入
					if pCodecCtx.AvcodecSendPacket(packet) >= 0 {
						//從解碼器返回解碼的輸出數(shù)據(jù)
						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++
							}
							//寫(xiě)文件
							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()

			// 關(guān)閉視頻文件
			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()
	//解碼視頻流數(shù)據(jù)
	FFmpeg_H264DecodeToYUV(input_filename, output_filename)

}

3.3 YUV文件播放

可以使用YUVplayer播放YUV文件,下載地址為YUVplayer播放器

播放時(shí),設(shè)置好播放器的Size和Color
golang基于FFmpeg實(shí)現(xiàn)視頻H264編解碼

四、YUV編碼為H264

4.1 代碼邏輯及使用API

YUV數(shù)據(jù)編碼為H264格式,其代碼邏輯如下:

  1. 打開(kāi)輸出文件
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. 創(chuàng)建H264視頻流,并設(shè)置參數(shù)
func (*avformat.Context).AvformatNewStream(c *avformat.AvCodec) *avformat.Stream
func (*avformat.Stream).AvStreamSetRFrameRate(r avcodec.Rational)
  1. 查找編碼器
func avcodec.AvcodecFindEncoderByName(c string) *avcodec.Codec  or
func avcodec.AvcodecFindEncoder(id avcodec.CodecId) *avcodec.Codec
  1. 配置編碼器
func (*avcodec.Codec).AvcodecAllocContext3() *avcodec.Context
func (*avcodec.Context).SetEncodeParams2(width int, height int, pxlFmt avcodec.PixelFormat, hasBframes bool, gopSize int, profile int)
  1. 打開(kāi)編碼器
func (*avcodec.Context).AvcodecOpen2(c *avcodec.Codec, d **avcodec.Dictionary) int
  1. 創(chuàng)建frame并配置
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. 寫(xiě)文件頭并創(chuàng)建packet結(jié)構(gòu)
func (*avformat.Context).AvformatWriteHeader(o **avutil.Dictionary) int
func avcodec.AvPacketAlloc() *avcodec.Packet
  1. 將YUV文件的數(shù)據(jù)讀入frame中,并將frame發(fā)送給解碼器,packet接收編碼后的數(shù)據(jù)
func (*avcodec.Context).AvcodecSendFrame(frame *avcodec.Frame) int
func (*avcodec.Context).AvcodecReceivePacket(packet *avcodec.Packet) int
  1. 轉(zhuǎn)換packet的Pts、dts
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. 將packet里的數(shù)據(jù)寫(xiě)入輸出文件中
func (*avformat.Context).AvInterleavedWriteFrame(pkt *avcodec.Packet) int
  1. 寫(xiě)文件尾,并釋放之前的資源
func (*avformat.Context).AvWriteTrailer() int

4.2 具體代碼實(shí)現(xiàn)

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)
	// 創(chuàng)建h264流, 并設(shè)置參數(shù)
	var rational avcodec.Rational
	rational.Set(1, fps)
	VStream := outFmtCtx.AvformatNewStream(nil)
	if VStream == nil {
		return errors.New("VStream is nil")
	}
	VStream.AvStreamSetRFrameRate(rational) //設(shè)置25幀每秒,fps為25

	// //設(shè)置相關(guān)編碼參數(shù)
	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) //設(shè)置25幀每秒,fps為25

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

	//打開(kāi)編碼器
	if enc_ctx.AvcodecOpen2(pCodec, nil) < 0 {
		errors.New("Could not open codec-------------")
	}

	//創(chuàng)建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())

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

	//創(chuàng)建編碼后的數(shù)據(jù)包,用來(lái)存儲(chǔ)frame編碼后的數(shù)據(jù)
	packet = avcodec.AvPacketAlloc()
	packet.AvNewPacket(newSize)
	y_size := enc_ctx.Width() * enc_ctx.Height()
	//循環(huán)編碼每一幀
	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數(shù)據(jù)拷貝倒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("準(zhǔn)備編碼 ---------------------")

		avutil.FrameSetPts(frame, int64(j))
		j = i + 1
		//利用編碼器進(jìn)行編碼, 將frame的數(shù)據(jù)傳入packet
		if encode(enc_ctx, frame, packet, VStream, outFmtCtx) == -1 {
			break
		}

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

	//寫(xiě)文件尾
	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()

	//編碼碼視頻流數(shù)據(jù)
	FFmpeg_YuvEncodeToH264(output_filename, filename)
}

4.3 H264文件播放

H264文件可以用vlc播放器進(jìn)行播放,播放器下載地址為:vlc播放器

vlc播放器工具欄—編解碼器信息,可以查看視頻的編碼格式以及數(shù)據(jù)丟失率文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-513238.html

到了這里,關(guān)于golang基于FFmpeg實(shí)現(xiàn)視頻H264編解碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 音視頻處理 ffmpeg中級(jí)開(kāi)發(fā) H264編碼

    音視頻處理 ffmpeg中級(jí)開(kāi)發(fā) H264編碼

    libavcodec/avcodec.h 常用的數(shù)據(jù)結(jié)構(gòu) AVCodec 編碼器結(jié)構(gòu)體 AVCodecContext 編碼器上下文 AVFrame 解碼后的幀 結(jié)構(gòu)體內(nèi)存的分配和釋放 av_frame_alloc 申請(qǐng) av_frame_free() 釋放 avcodec_alloc_context3() 創(chuàng)建編碼器上下文 avcodec_free_context() 釋放編碼器上下文 解碼步驟 avcodec_find_decoder 查找解碼器 avcod

    2024年02月01日
    瀏覽(109)
  • ffmpeg學(xué)習(xí)日記604-指令-將視頻格式轉(zhuǎn)為H264格式

    ffmpeg學(xué)習(xí)日記604-指令-將視頻格式轉(zhuǎn)為H264格式 在第四篇中,想要解碼視頻,缺沒(méi)有弄清楚怎樣的一個(gè)數(shù)據(jù)流,現(xiàn)在又明晰了一點(diǎn),所謂的h264編解碼,并不是直接將視頻格式,通過(guò)h264編解碼為視頻原始數(shù)據(jù)流,這種說(shuō)法是錯(cuò)誤的,而是應(yīng)該將視頻格式轉(zhuǎn)換為h264的數(shù)據(jù)流,然后

    2024年02月11日
    瀏覽(20)
  • 【音視頻處理】轉(zhuǎn)編碼H264 to H265,F(xiàn)Fmpeg,代碼分享講解

    【音視頻處理】轉(zhuǎn)編碼H264 to H265,F(xiàn)Fmpeg,代碼分享講解

    大家好,歡迎來(lái)到停止重構(gòu)的頻道。 本期我們討論音視頻文件 轉(zhuǎn)編碼 ,如將視頻H264轉(zhuǎn)H265等。 內(nèi)容中所提及的 代碼都會(huì)放在GitHub ,感興趣的小伙伴可以到GitHub下載。 我們按這樣的順序展開(kāi)討論:? 1、??編碼的作用? 2、??轉(zhuǎn)編碼的工作原理 3、??編解碼器安裝? 4、??示

    2024年02月11日
    瀏覽(26)
  • Android MediaCodec將h264實(shí)時(shí)視頻流數(shù)據(jù)解碼為yuv,并轉(zhuǎn)換yuv的顏色格式為nv21

    初始化mediacodec 處理數(shù)據(jù),解碼h264數(shù)據(jù)為yuv格式 這里傳入的是h264格式的實(shí)時(shí)視頻流數(shù)據(jù)。 處理獲取到的nv21顏色格式的yuv數(shù)據(jù) ?yuv視頻數(shù)據(jù)顏色格式轉(zhuǎn)換 h264實(shí)時(shí)視頻流的數(shù)據(jù)來(lái)源 寫(xiě)入h264視頻流到sdcard中 rtsp獲取h264實(shí)時(shí)視頻流數(shù)據(jù) ?編寫(xiě)C代碼加載ffmpeg庫(kù) 源碼地址 https://gi

    2024年01月17日
    瀏覽(30)
  • FFmpeg 解碼 H.264 視頻出現(xiàn)花屏和馬賽克的解決辦法

    FFmpeg 解碼 H.264 視頻出現(xiàn)花屏和馬賽克的解決辦法

    發(fā)送數(shù)據(jù)包太大,超過(guò)了 FFmpeg 的默認(rèn)最大值。 網(wǎng)絡(luò)情況較差時(shí),因網(wǎng)絡(luò)狀況出現(xiàn)的丟包。 解碼出錯(cuò)。 包亂序。 一種方法是控制播放源的發(fā)送數(shù)據(jù)大小,但這極大浪費(fèi)了當(dāng)前的網(wǎng)絡(luò)帶寬,非優(yōu)選方案。 更好的做法是擴(kuò)大接收端的接收緩沖區(qū),其修改方法為: 在 FFmpeg 的源碼

    2024年04月26日
    瀏覽(29)
  • JAVA實(shí)現(xiàn)H264視頻流推送到RTSP、RTMP服務(wù)----JavaCV

    前提: 1.準(zhǔn)備好rtsp、rtmp服務(wù) 2.準(zhǔn)備好視頻流接收程序 基本思路是:?jiǎn)?dòng)兩個(gè)線(xiàn)程,線(xiàn)程1接收視頻流,線(xiàn)程2使用JavaCV將視頻流推送到RTSP、RTMP服務(wù),兩者之間使用管道流進(jìn)行通信。線(xiàn)程2接收到視頻流后的具體操作:?jiǎn)?dòng)grabber接收視頻流并捕獲視頻幀,然后啟動(dòng)recoder將捕獲的

    2024年02月11日
    瀏覽(30)
  • FPGA純verilog代碼實(shí)現(xiàn)H264視頻壓縮 提供工程源碼和技術(shù)支持

    FPGA純verilog代碼實(shí)現(xiàn)H264視頻壓縮 提供工程源碼和技術(shù)支持

    H264視頻壓縮與解碼在FPGA圖傳領(lǐng)域應(yīng)用廣泛,Xilinx高端器件已經(jīng)內(nèi)嵌了H264加速器,在Linux系統(tǒng)下調(diào)用API即可使用,但對(duì)于需要定制私有算法或者協(xié)議的H264視頻壓縮與解碼應(yīng)用或者學(xué)習(xí)研究者而言,純verilog代碼實(shí)現(xiàn)H264視頻壓縮依然具有實(shí)用價(jià)值,本設(shè)計(jì)采用純verilog代碼實(shí)現(xiàn)

    2024年02月06日
    瀏覽(21)
  • FFmpeg4入門(mén)13:h264編碼為mp4

    上一篇將yuv源視頻文件編碼為 *.h264 的由libx264實(shí)現(xiàn)壓縮的文件,將源文件從55M編碼為620KB,但是h264文件只有視頻數(shù)據(jù),而且使用范圍不太廣。那么就需要進(jìn)一步的封裝,在此選用最常用的mp4格式為例。 隨便選一個(gè)mp4格式文件,用FFmpeg4入門(mén)4:解析視頻并輸出視頻信息或者ffp

    2023年04月10日
    瀏覽(24)
  • 使用NVIDIA GPU FFmpeg轉(zhuǎn)碼 YUV to H264(成功)

    使用NVIDIA GPU FFmpeg轉(zhuǎn)碼 YUV to H264(成功)

    NVIDIA官方教程:鏈接,本篇內(nèi)容主要參考2.2 Software Setup。 確保nvidia-smi能夠正常使用: 注意要與顯卡驅(qū)動(dòng)版本對(duì)應(yīng),驗(yàn)證toolkit是否正確安裝: 下載地址 編譯方法:解壓進(jìn)入文件夾后 驗(yàn)證安裝 ?????????顯示版本號(hào)證明安裝成功: 下載地址 配置方法: 進(jìn)入ffmpeg-x.x文件夾

    2024年02月06日
    瀏覽(21)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包