ホームページ  >  記事  >  バックエンド開発  >  Golang と FFmpeg: ビデオ フレームのインターセプトとスケーリングを実装する方法

Golang と FFmpeg: ビデオ フレームのインターセプトとスケーリングを実装する方法

WBOY
WBOYオリジナル
2023-09-27 09:34:431461ブラウズ

Golang与FFmpeg: 如何实现视频帧截取和缩放

Golang と FFmpeg: ビデオ フレームのインターセプトとスケーリングを実装する方法、具体的なコード例が必要です


概要:

ビデオ処理の需要が高まるにつれて、人々はビデオ処理用のプログラミング言語として Golang を使用する人が増えています。 FFmpeg は、業界で最も人気のあるオープンソース マルチメディア処理フレームワークとして、オーディオ データとビデオ データを処理するための豊富な機能を提供します。この記事では、Golang を使用して FFmpeg を呼び出し、ビデオ フレームのインターセプトおよびスケーリング機能を実装する方法と、対応するコード例を紹介します。

前提条件:

開始する前に、FFmpeg がマシンにインストールされ、正しい環境変数が構成されていることを確認する必要があります。

ビデオ フレーム インターセプト:

まず、ビデオ フレーム インターセプトの実装方法を見てみましょう。 FFmpeg では、「avformat」モジュールを使用してビデオ ファイルを読み取り、「avcodec」モジュールを使用してビデオ フレームをデコードできます。以下は簡単なサンプル コードです。 <pre class='brush:php;toolbar:false;'>package main import ( &quot;fmt&quot; &quot;log&quot; &quot;github.com/giorgisio/goav/avcodec&quot; &quot;github.com/giorgisio/goav/avformat&quot; ) func main() { // 打开视频文件 formatContext := avformat.AvformatAllocContext() if err := avformat.AvformatOpenInput(&amp;formatContext, &quot;/path/to/video.mp4&quot;, nil, nil); err != nil { log.Fatal(&quot;无法打开视频文件:&quot;, err) } defer avformat.AvformatFreeContext(formatContext) // 查找视频流 if err := formatContext.AvformatFindStreamInfo(nil); err != nil { log.Fatal(&quot;无法查找视频流:&quot;, 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(&quot;找不到视频流&quot;) } // 找到视频解码器 videoDecoder := avcodec.AvcodecFindDecoder(avcodec.CodecId(formatContext.Streams()[videoStreamIndex].CodecParameters().CodecId())) if videoDecoder == nil { log.Fatal(&quot;无法找到视频解码器&quot;) } // 打开解码器上下文 videoCodecContext := avcodec.AvcodecAllocContext3(videoDecoder) if err := avcodec.AvcodecParametersToContext(videoCodecContext, formatContext.Streams()[videoStreamIndex].CodecParameters()); err != nil { log.Fatal(&quot;无法打开解码器上下文:&quot;, err) } if err := videoCodecContext.AvcodecOpen2(videoDecoder, nil); err != nil { log.Fatal(&quot;无法打开解码器:&quot;, err) } defer avcodec.AvcodecFreeContext(videoCodecContext) // 读取视频帧 packet := avcodec.AvPacketAlloc() defer avcodec.AvPacketFree(packet) for formatContext.AvReadFrame(packet) &gt;= 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(&quot;视频帧:%d &quot;, frame.Pts()) } } } } }</pre> 上記のコードでは、まず avformat.AvformatAllocContext() を使用してフォーマット コンテキスト オブジェクトを割り当て、次に avformat.AvformatOpenInput( ) ビデオ ファイルが開かれました。次に、avformat.AvformatFindStreamInfo() を使用してビデオ ストリームを検索し、

avformat.AVMEDIA_TYPE_VIDEO

を使用してそれがビデオ ストリームであるかどうかを判断します。 次に、avcodec.AvcodecFindDecoder() を使用して適切なデコーダを見つけ、avcodec.AvcodecParametersToContext()

avcodec.AvcodecOpen2( )# を使用します。 ##デコーダ コンテキストを開きました。

最後に、formatContext.AvReadFrame() を使用してビデオ フレームを読み取り、videoCodecContext.AvcodecReceiveFrame()

で各フレームを処理します。この例では、各フレームの PTS 値を単純に出力します。


ビデオ スケーリング:

次に、ビデオ フレーム スケーリングを実現する方法を見てみましょう。 FFmpeg では、「swscale」モジュールを使用してビデオ フレームをスケーリングできます。以下は簡単なサンプル コードです。

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()

関数を使用して指定されたサイズにスケーリングし、スケーリングされたビデオ フレームをエンコーダー Encode に送信します。次に、エンコードされたフレームを出力ビデオ ファイルに書き込みます。


概要:

Golang と FFmpeg を組み合わせることで、開発者に強力なビデオ処理ツールが提供されます。この記事では、Golang を使用して FFmpeg を呼び出し、ビデオ フレームのインターセプトおよびスケーリング機能を実装する方法を紹介し、対応するコード例を示します。これらの例が、Golang と FFmpeg を使用してビデオ データを処理する方法をより深く理解するのに役立つことを願っています。 ###

以上がGolang と FFmpeg: ビデオ フレームのインターセプトとスケーリングを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。