Golang と FFmpeg: ビデオ フレームのインターセプトとスケーリングを実装する方法、具体的なコード例が必要です
ビデオ処理の需要が高まるにつれて、人々はビデオ処理用のプログラミング言語として Golang を使用する人が増えています。 FFmpeg は、業界で最も人気のあるオープンソース マルチメディア処理フレームワークとして、オーディオ データとビデオ データを処理するための豊富な機能を提供します。この記事では、Golang を使用して FFmpeg を呼び出し、ビデオ フレームのインターセプトおよびスケーリング機能を実装する方法と、対応するコード例を紹介します。
開始する前に、FFmpeg がマシンにインストールされ、正しい環境変数が構成されていることを確認する必要があります。
まず、ビデオ フレーム インターセプトの実装方法を見てみましょう。 FFmpeg では、「avformat」モジュールを使用してビデオ ファイルを読み取り、「avcodec」モジュールを使用してビデオ フレームをデコードできます。以下は簡単なサンプル コードです。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>package main
import (
"fmt"
"log"
"github.com/giorgisio/goav/avcodec"
"github.com/giorgisio/goav/avformat"
)
func main() {
// 打开视频文件
formatContext := avformat.AvformatAllocContext()
if err := avformat.AvformatOpenInput(&formatContext, "/path/to/video.mp4", nil, nil); err != nil {
log.Fatal("无法打开视频文件:", err)
}
defer avformat.AvformatFreeContext(formatContext)
// 查找视频流
if err := formatContext.AvformatFindStreamInfo(nil); err != nil {
log.Fatal("无法查找视频流:", err)
}
var videoStreamIndex int32 = -1
for i, stream := range formatContext.Streams() {
if stream.CodecParameters().CodecType() == avformat.AVMEDIA_TYPE_VIDEO {
videoStreamIndex = int32(i)
break
}
}
if videoStreamIndex == -1 {
log.Fatal("找不到视频流")
}
// 找到视频解码器
videoDecoder := avcodec.AvcodecFindDecoder(avcodec.CodecId(formatContext.Streams()[videoStreamIndex].CodecParameters().CodecId()))
if videoDecoder == nil {
log.Fatal("无法找到视频解码器")
}
// 打开解码器上下文
videoCodecContext := avcodec.AvcodecAllocContext3(videoDecoder)
if err := avcodec.AvcodecParametersToContext(videoCodecContext, formatContext.Streams()[videoStreamIndex].CodecParameters()); err != nil {
log.Fatal("无法打开解码器上下文:", err)
}
if err := videoCodecContext.AvcodecOpen2(videoDecoder, nil); err != nil {
log.Fatal("无法打开解码器:", err)
}
defer avcodec.AvcodecFreeContext(videoCodecContext)
// 读取视频帧
packet := avcodec.AvPacketAlloc()
defer avcodec.AvPacketFree(packet)
for formatContext.AvReadFrame(packet) >= 0 {
if packet.StreamIndex() == videoStreamIndex {
frame := avutil.AvFrameAlloc()
defer avutil.AvFrameFree(frame)
if err := videoCodecContext.AvcodecSendPacket(packet); err == nil {
for videoCodecContext.AvcodecReceiveFrame(frame) == nil {
// 处理视频帧
fmt.Printf("视频帧:%d
", frame.Pts())
}
}
}
}
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
上記のコードでは、まず avformat.AvformatAllocContext()
を使用してフォーマット コンテキスト オブジェクトを割り当て、次に avformat.AvformatOpenInput( )
ビデオ ファイルが開かれました。次に、avformat.AvformatFindStreamInfo()
を使用してビデオ ストリームを検索し、
を使用してそれがビデオ ストリームであるかどうかを判断します。 次に、
avcodec.AvcodecFindDecoder()
を使用して適切なデコーダを見つけ、avcodec.AvcodecParametersToContext()
と
最後に、
formatContext.AvReadFrame() を使用してビデオ フレームを読み取り、
videoCodecContext.AvcodecReceiveFrame()
ビデオ スケーリング:
package main import ( "fmt" "image" "log" "os" "github.com/giorgisio/goav/avcodec" "github.com/giorgisio/goav/avformat" "github.com/giorgisio/goav/swscale" "github.com/nfnt/resize" ) func main() { // 打开视频文件 formatContext := avformat.AvformatAllocContext() if err := avformat.AvformatOpenInput(&formatContext, "/path/to/video.mp4", nil, nil); err != nil { log.Fatal("无法打开视频文件:", err) } defer avformat.AvformatFreeContext(formatContext) // 查找视频流 if err := formatContext.AvformatFindStreamInfo(nil); err != nil { log.Fatal("无法查找视频流:", err) } var videoStreamIndex int32 = -1 for i, stream := range formatContext.Streams() { if stream.CodecParameters().CodecType() == avformat.AVMEDIA_TYPE_VIDEO { videoStreamIndex = int32(i) break } } if videoStreamIndex == -1 { log.Fatal("找不到视频流") } // 找到视频解码器 videoDecoder := avcodec.AvcodecFindDecoder(avcodec.CodecId(formatContext.Streams()[videoStreamIndex].CodecParameters().CodecId())) if videoDecoder == nil { log.Fatal("无法找到视频解码器") } // 打开解码器上下文 videoCodecContext := avcodec.AvcodecAllocContext3(videoDecoder) if err := avcodec.AvcodecParametersToContext(videoCodecContext, formatContext.Streams()[videoStreamIndex].CodecParameters()); err != nil { log.Fatal("无法打开解码器上下文:", err) } if err := videoCodecContext.AvcodecOpen2(videoDecoder, nil); err != nil { log.Fatal("无法打开解码器:", err) } defer avcodec.AvcodecFreeContext(videoCodecContext) // 创建视频缩放上下文 swscaleContext := swscale.SwsGetContext( videoCodecContext.Width(), videoCodecContext.Height(), videoCodecContext.PixFmt(), videoCodecContext.Width()/2, videoCodecContext.Height()/2, avcodec.AV_PIX_FMT_RGB24, 0, nil, nil, nil, ) defer swscale.SwsFreeContext(swscaleContext) // 创建输出视频文件 outfile, err := os.Create("/path/to/output.mp4") if err != nil { log.Fatal("无法创建输出视频文件:", err) } defer outfile.Close() // 创建视频编码器 videoEncoder := avcodec.AvcodecFindEncoder(avcodec.AV_CODEC_ID_MPEG4) if videoEncoder == nil { log.Fatal("无法找到视频编码器") } // 创建编码器上下文 videoCodecCtx := avcodec.AvcodecAllocContext3(videoEncoder) videoCodecCtx.SetBitRate(400000) videoCodecCtx.SetWidth(videoCodecContext.Width() / 2) videoCodecCtx.SetHeight(videoCodecContext.Height() / 2) videoCodecCtx.SetTimeBase(avformat.AVR{Num: 1, Den: 25}) videoCodecCtx.SetPixFmt(avcodec.AV_PIX_FMT_YUV420P) // 打开编码器上下文 if err := videoCodecCtx.AvcodecOpen2(videoEncoder, nil); err != nil { log.Fatal("无法打开编码器上下文:", err) } defer avcodec.AvcodecFreeContext(videoCodecCtx) // 写入视频文件头 formatContext.SetOutput(outfile) if err := formatContext.AvformatWriteHeader(nil); err != nil { log.Fatal("无法写入视频文件头:", err) } defer formatContext.AvformatFreeOutputContext() // 准备编码帧和缩放帧 encodeFrame := avutil.AvFrameAlloc() defer avutil.AvFrameFree(encodeFrame) encodeFrame.SetWidth(videoCodecCtx.Width()) encodeFrame.SetHeight(videoCodecCtx.Height()) encodeFrame.SetFormat(int32(videoCodecCtx.PixFmt())) frameSize := avcodec.AvpixelAvImageGetBufferSize(avcodec.AV_PIX_FMT_RGB24, videoCodecCtx.Width()/2, videoCodecCtx.Height()/2, 1) encodeFrameBuffer := avutil.AvMalloc(frameSize) defer avutil.AvFree(encodeFrameBuffer) encodeFrame.AvpixelAvImageFillArrays(encodeFrameBuffer, 1) for formatContext.AvReadFrame(packet) >= 0 { if packet.StreamIndex() == videoStreamIndex { frame := avutil.AvFrameAlloc() defer avutil.AvFrameFree(frame) if err := videoCodecContext.AvcodecSendPacket(packet); err != nil { log.Fatal("无法发送视频包:", err) } for videoCodecContext.AvcodecReceiveFrame(frame) == nil { // 缩放视频帧 swscale.SwsScale( swscaleContext, frame.Data(), frame.Linesize(), 0, frame.Height(), encodeFrame.Data(), encodeFrame.Linesize(), ) // 编码视频帧 encodeFrame.SetPts(frame.Pts()) packet := avcodec.AvPacketAlloc() if err := avcodec.AvcodecSendFrame(videoCodecCtx, encodeFrame); err != nil { log.Fatal("无法发送编码帧:", err) } if err := avcodec.AvcodecReceivePacket(videoCodecCtx, packet); err != nil { log.Fatal("无法接收编码包:", err) } defer avcodec.AvPacketFree(packet) // 写入编码后的帧到文件 if err := formatContext.AvWriteFrame(packet); err != nil { log.Fatal("无法写入帧到文件:", err) } } } } // 写入视频文件尾 if err := formatContext.AvWriteTrailer(); err != nil { log.Fatal("无法写入视频文件尾:", err) } }
上記のコードでは、入力が元のビデオ フレームのサイズで、出力がスケーリングされたビデオであるビデオ スケーリング コンテキスト
swscaleContext を作成します。フレームのサイズ。また、元のビデオ フレームの半分のサイズの新しいエンコーダ コンテキスト
videoCodecCtx を作成し、それを YUV420P ピクセル形式に設定します。 ビデオの各フレームを読み取った後、
swscale.SwsScale()
概要:
以上がGolang と FFmpeg: ビデオ フレームのインターセプトとスケーリングを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。