2023-03-03:请用go语言调用ffmpeg,摄像头捕获并编码为h264文件,不管音频。
Posted 福大大架构师每日一题
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2023-03-03:请用go语言调用ffmpeg,摄像头捕获并编码为h264文件,不管音频。相关的知识,希望对你有一定的参考价值。
2023-03-03:请用go语言调用ffmpeg,摄像头捕获并编码为h264文件,不管音频。
答案2023-03-03:
使用 github.com/moonfdd/ffmpeg-go 库。
先用如下命令,获取摄像头的名称。此摄像头名称是"Full HD webcam",你们需要修改代码,把名称给改了。
./lib/ffmpeg -list_devices true -f dshow -i dummy
代码命令如下:
go run ./examples/a14.video_encode_camera2h264/main.go
参考了14:摄像头捕获并编码为h264,代码用golang编写。代码如下:
package main
import (
"fmt"
"os"
"os/exec"
"time"
"unsafe"
"github.com/moonfdd/ffmpeg-go/ffcommon"
"github.com/moonfdd/ffmpeg-go/libavcodec"
"github.com/moonfdd/ffmpeg-go/libavdevice"
"github.com/moonfdd/ffmpeg-go/libavformat"
"github.com/moonfdd/ffmpeg-go/libavutil"
"github.com/moonfdd/ffmpeg-go/libswscale"
)
func main()
// ./lib/ffmpeg -list_devices true -f dshow -i dummy
// ./lib/ffplay -f dshow -i video="Full HD webcam"
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
// frame_index := 0 //统计帧数
// inVStreamIndex := -1
// outVStreamIndex := -1 //输入输出视频流在文件中的索引位置
// inVFileName := "./out/result.h264"
// outFileName := "./out/result.mp4"
//是否存在h264文件
// _, err = os.Stat(inVFileName)
// if err != nil
// if os.IsNotExist(err)
// fmt.Println("create h264 file")
// exec.Command("./lib/ffmpeg", "-i", "./resources/big_buck_bunny.mp4", "-vcodec", "copy", "-an", inVFileName, "-y").CombinedOutput()
//
//
ret := int32(0)
libavdevice.AvdeviceRegisterAll()
inFmtCtx := libavformat.AvformatAllocContext()
var inCodecCtx *libavcodec.AVCodecContext
var inCodec *libavcodec.AVCodec
inPkt := libavcodec.AvPacketAlloc()
srcFrame := libavutil.AvFrameAlloc()
yuvFrame := libavutil.AvFrameAlloc()
//打开输出文件,并填充fmtCtx数据
outFmtCtx := libavformat.AvformatAllocContext()
var outFmt *libavformat.AVOutputFormat
var outCodecCtx *libavcodec.AVCodecContext
var outCodec *libavcodec.AVCodec
var outVStream *libavformat.AVStream
outPkt := libavcodec.AvPacketAlloc()
var img_ctx *libswscale.SwsContext
inVideoStreamIndex := -1
for
/解码器部分//
//打开摄像头
inFmt := libavformat.AvFindInputFormat("dshow")
if libavformat.AvformatOpenInput(&inFmtCtx, "video=Full HD webcam", inFmt, nil) < 0
fmt.Printf("Cannot open camera.\\n")
return
if inFmtCtx.AvformatFindStreamInfo(nil) < 0
fmt.Printf("Cannot find any stream in file.\\n")
return
for i := 0; i < int(inFmtCtx.NbStreams); i++
if inFmtCtx.GetStream(uint32(i)).Codecpar.CodecType == libavutil.AVMEDIA_TYPE_VIDEO
inVideoStreamIndex = i
break
if inVideoStreamIndex == -1
fmt.Printf("Cannot find video stream in file.\\n")
return
inVideoCodecPara := inFmtCtx.GetStream(uint32(inVideoStreamIndex)).Codecpar
inCodec = libavcodec.AvcodecFindDecoder(inVideoCodecPara.CodecId)
if inCodec == nil
fmt.Printf("Cannot find valid video decoder.\\n")
return
inCodecCtx = inCodec.AvcodecAllocContext3()
if inCodecCtx == nil
fmt.Printf("Cannot alloc valid decode codec context.\\n")
return
if inCodecCtx.AvcodecParametersToContext(inVideoCodecPara) < 0
fmt.Printf("Cannot initialize parameters.\\n")
return
if inCodecCtx.AvcodecOpen2(inCodec, nil) < 0
fmt.Printf("Cannot open codec.\\n")
return
img_ctx = libswscale.SwsGetContext(inCodecCtx.Width,
inCodecCtx.Height,
inCodecCtx.PixFmt,
inCodecCtx.Width,
inCodecCtx.Height,
libavutil.AV_PIX_FMT_YUV420P,
libswscale.SWS_BICUBIC,
nil, nil, nil)
numBytes := libavutil.AvImageGetBufferSize(libavutil.AV_PIX_FMT_YUV420P,
inCodecCtx.Width,
inCodecCtx.Height, 1)
out_buffer := libavutil.AvMalloc(uint64(numBytes))
ret = libavutil.AvImageFillArrays((*[4]*ffcommon.FUint8T)(unsafe.Pointer(&yuvFrame.Data)),
(*[4]ffcommon.FInt)(unsafe.Pointer(&yuvFrame.Linesize)),
(*ffcommon.FUint8T)(unsafe.Pointer(out_buffer)),
libavutil.AV_PIX_FMT_YUV420P,
inCodecCtx.Width,
inCodecCtx.Height,
1)
if ret < 0
fmt.Printf("Fill arrays failed.\\n")
return
//解码器部分结束/
//编码器部分开始/
outFile := "./out/result14.h264"
if libavformat.AvformatAllocOutputContext2(&outFmtCtx, nil, "", outFile) < 0
fmt.Printf("Cannot alloc output file context.\\n")
return
outFmt = outFmtCtx.Oformat
//打开输出文件
if libavformat.AvioOpen(&outFmtCtx.Pb, outFile, libavformat.AVIO_FLAG_READ_WRITE) < 0
fmt.Printf("output file open failed.\\n")
return
//创建h264视频流,并设置参数
outVStream = outFmtCtx.AvformatNewStream(outCodec)
if outVStream == nil
fmt.Printf("create new video stream fialed.\\n")
return
outVStream.TimeBase.Den = 30
outVStream.TimeBase.Num = 1
//编码参数相关
outCodecPara := outFmtCtx.GetStream(uint32(outVStream.Index)).Codecpar
outCodecPara.CodecType = libavutil.AVMEDIA_TYPE_VIDEO
outCodecPara.CodecId = outFmt.VideoCodec
outCodecPara.Width = 480
outCodecPara.Height = 360
outCodecPara.BitRate = 110000
//查找编码器
outCodec = libavcodec.AvcodecFindEncoder(outFmt.VideoCodec)
if outCodec == nil
fmt.Printf("Cannot find any encoder.\\n")
return
//设置编码器内容
outCodecCtx = outCodec.AvcodecAllocContext3()
outCodecCtx.AvcodecParametersToContext(outCodecPara)
if outCodecCtx == nil
fmt.Printf("Cannot alloc output codec content.\\n")
return
outCodecCtx.CodecId = outFmt.VideoCodec
outCodecCtx.CodecType = libavutil.AVMEDIA_TYPE_VIDEO
outCodecCtx.PixFmt = libavutil.AV_PIX_FMT_YUV420P
outCodecCtx.Width = inCodecCtx.Width
outCodecCtx.Height = inCodecCtx.Height
outCodecCtx.TimeBase.Num = 1
outCodecCtx.TimeBase.Den = 30
outCodecCtx.BitRate = 110000
outCodecCtx.GopSize = 10
if outCodecCtx.CodecId == libavcodec.AV_CODEC_ID_H264
outCodecCtx.Qmin = 10
outCodecCtx.Qmax = 51
outCodecCtx.Qcompress = 0.6
else if outCodecCtx.CodecId == libavcodec.AV_CODEC_ID_MPEG2VIDEO
outCodecCtx.MaxBFrames = 2
else if outCodecCtx.CodecId == libavcodec.AV_CODEC_ID_MPEG1VIDEO
outCodecCtx.MbDecision = 2
//打开编码器
if outCodecCtx.AvcodecOpen2(outCodec, nil) < 0
fmt.Printf("Open encoder failed.\\n")
return
///编码器部分结束
///编解码部分//
yuvFrame.Format = outCodecCtx.PixFmt
yuvFrame.Width = outCodecCtx.Width
yuvFrame.Height = outCodecCtx.Height
ret = outFmtCtx.AvformatWriteHeader(nil)
count := 0
for inFmtCtx.AvReadFrame(inPkt) >= 0 && count < 50
if inPkt.StreamIndex == uint32(inVideoStreamIndex)
if inCodecCtx.AvcodecSendPacket(inPkt) >= 0
ret = inCodecCtx.AvcodecReceiveFrame(srcFrame)
for ret >= 0
if ret == -libavutil.EAGAIN || ret == libavutil.AVERROR_EOF
break
else if ret < 0
fmt.Printf("Error during decoding\\n")
return
img_ctx.SwsScale((**byte)(unsafe.Pointer(&srcFrame.Data)),
(*int32)(unsafe.Pointer(&srcFrame.Linesize)),
0, uint32(inCodecCtx.Height),
(**byte)(unsafe.Pointer(&yuvFrame.Data)), (*int32)(unsafe.Pointer(&yuvFrame.Linesize)))
yuvFrame.Pts = srcFrame.Pts
//encode
if outCodecCtx.AvcodecSendFrame(yuvFrame) >= 0
if outCodecCtx.AvcodecReceivePacket(outPkt) >= 0
fmt.Printf("encode one frame.\\n")
count++
outPkt.StreamIndex = uint32(outVStream.Index)
outPkt.AvPacketRescaleTs(outCodecCtx.TimeBase,
outVStream.TimeBase)
outPkt.Pos = -1
outFmtCtx.AvInterleavedWriteFrame(outPkt)
outPkt.AvPacketUnref()
// usleep(1000*24);
time.Sleep(time.Millisecond * 24)
ret = inCodecCtx.AvcodecReceiveFrame(srcFrame)
inPkt.AvPacketUnref()
ret = flush_encoder(outFmtCtx, outCodecCtx, int(outVStream.Index))
if ret < 0
fmt.Printf("flushing encoder failed.\\n")
return
outFmtCtx.AvWriteTrailer()
编解码部分结束
break
///内存释放部分/
libavcodec.AvPacketFree(&inPkt)
libavcodec.AvcodecFreeContext(&inCodecCtx)
inCodecCtx.AvcodecClose()
libavformat.AvformatCloseInput(&inFmtCtx)
libavutil.AvFrameFree(&srcFrame)
libavutil.AvFrameFree(&yuvFrame)
libavcodec.AvPacketFree(&outPkt)
libavcodec.AvcodecFreeContext(&outCodecCtx)
outCodecCtx.AvcodecClose()
libavformat.AvformatCloseInput(&outFmtCtx)
fmt.Println("-----------------------------------------")
_, err = exec.Command("./lib/ffplay.exe", "./out/result14.h264").Output()
if err != nil
fmt.Println("play err = ", err)
func flush_encoder(fmtCtx *libavformat.AVFormatContext, codecCtx *libavcodec.AVCodecContext, vStreamIndex int) int32
ret := int32(0)
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)
if codecCtx.AvcodecSendFrame(nil) >= 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
enc_pkt.AvPacketUnref()
return ret
以上是关于2023-03-03:请用go语言调用ffmpeg,摄像头捕获并编码为h264文件,不管音频。的主要内容,如果未能解决你的问题,请参考以下文章
2023-02-23:请用go语言调用ffmpeg,解码mp4文件并保存为YUV420P格式文件。
2023-02-22:请用go语言调用ffmpeg,保存mp4文件的视频帧,每帧用ppm图片保存。
2023-02-19:请用go语言调用ffmepg,输出视频文件信息。
2023-02-25:请用go语言调用ffmpeg,解码mp4文件并保存为YUV420SP格式文件,YUV420P不要转换成YUV420SP。
2023-02-24:请用go语言调用ffmpeg,解码mp4文件并保存为YUV420SP格式文件,采用YUV420P转YUV420SP的方式。
2023-03-08:x265的视频编码器,不用ffmpeg,用libx265.dll也行。请用go语言调用libx265.dll,将yuv文件编码成h265文件。