Maison  >  Article  >  développement back-end  >  Golang et FFmpeg : Comment implémenter l'interception et la mise à l'échelle d'images vidéo

Golang et FFmpeg : Comment implémenter l'interception et la mise à l'échelle d'images vidéo

WBOY
WBOYoriginal
2023-09-27 09:34:431464parcourir

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

Golang et FFmpeg : Comment implémenter l'interception et la mise à l'échelle d'images vidéo, des exemples de code spécifiques sont nécessaires

Aperçu :
Avec l'augmentation des besoins en traitement vidéo, les gens sont de plus en plus enclins à utiliser Golang comme langage de programmation pour le traitement vidéo. En tant que framework de traitement multimédia open source le plus populaire du secteur, FFmpeg fournit des fonctions riches pour traiter les données audio et vidéo. Cet article expliquera comment utiliser Golang pour appeler FFmpeg afin d'implémenter les fonctions d'interception et de mise à l'échelle d'images vidéo, et fournira des exemples de code correspondants.

Prérequis :
Avant de commencer, vous devez vous assurer que FFmpeg est installé sur votre machine et que les variables d'environnement correctes sont configurées.

Interception d'images vidéo :
Tout d'abord, voyons comment mettre en œuvre l'interception d'images vidéo. Dans FFmpeg, vous pouvez utiliser le module « avformat » pour lire les fichiers vidéo et le module « avcodec » pour décoder les images vidéo. Voici un exemple de code simple :

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

Dans le code ci-dessus, nous utilisons d'abord avformat.AvformatAllocContext() pour allouer un objet de contexte de format, et utilisons avformat.AvformatOpenInput() code >Un fichier vidéo est ouvert. Ensuite, nous utilisons <code>avformat.AvformatFindStreamInfo() pour trouver le flux vidéo, puis utilisons avformat.AVMEDIA_TYPE_VIDEO pour déterminer s'il s'agit d'un flux vidéo. 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()

Ensuite, nous utilisons avcodec.AvcodecFindDecoder() pour trouver un décodeur approprié, et utilisons avcodec.AvcodecParametersToContext() et avcodec.AvcodecOpen2() ouvre le contexte du décodeur. <p><br>Enfin, nous utilisons <code>formatContext.AvReadFrame() pour lire les images vidéo et traiter chaque image dans videoCodecContext.AvcodecReceiveFrame(). Dans cet exemple, nous imprimons simplement la valeur PTS pour chaque image.

🎜Mise à l'échelle vidéo : 🎜Ensuite, voyons comment réaliser la mise à l'échelle de l'image vidéo. Dans FFmpeg, vous pouvez utiliser le module « swscale » pour mettre à l'échelle les images vidéo. Voici un exemple de code simple : 🎜rrreee🎜Dans le code ci-dessus, nous créons un contexte de mise à l'échelle vidéo swscaleContext, dont l'entrée est la taille de l'image vidéo d'origine et la sortie est la taille de la image vidéo mise à l'échelle. Nous créons également un nouveau contexte d'encodeur videoCodecCtx qui fait la moitié de la taille de l'image vidéo d'origine et le définissons au format de pixel YUV420P. 🎜🎜Après avoir lu chaque image vidéo, nous utilisons la fonction swscale.SwsScale() pour la redimensionner à la taille spécifiée et envoyer l'image vidéo mise à l'échelle à l'encodeur pour l'encodage. Nous écrivons ensuite les images codées dans le fichier vidéo de sortie. 🎜🎜Résumé : 🎜La combinaison de Golang et FFmpeg offre aux développeurs un puissant outil de traitement vidéo. Dans cet article, nous expliquons comment utiliser Golang pour appeler FFmpeg afin d'implémenter les fonctions d'interception et de mise à l'échelle d'images vidéo, et fournissons des exemples de code correspondants. Espérons que ces exemples vous aideront à mieux comprendre comment utiliser Golang et FFmpeg pour traiter les données vidéo. 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn