首页 > 解决方案 > 如何使用 Pion/Webrtc 将 VP8 帧间转换为图像?

问题描述

我在Pion/Webrtc 示例中尝试了将 VP8 视频流转换为 jpeg 的快照示例

它运行良好,但不包括帧间。我修复它时显示一条错误消息。

vp8:Golden / AltRef 框架未实现。

这个错误来自golang 的 vp8 包

修改后的代码如下图:

if !videoKeyframe {
    decoder.Init(bytes.NewReader(sample.Data), len(sample.Data))

    _, err := decoder.DecodeFrameHeader()
    if err != nil {
        fmt.Printf("Header Error: %s\n", err)
        continue
    }

    _, err2 := decoder.DecodeFrame()
    if err2 != nil {
        fmt.Printf("DecodeFrame Error: %s\n", err2)
        continue
    }
} 

我还尝试修改 gstreamer-receive 示例。但是我很难用C做。

无论如何可以轻松地将 interframe 转换为 jpeg 吗?

标签: gowebrtc

解决方案


最后,我用libvpx-go解决了

我修改了代码,如下所示。

package main

import (
    "bytes"
    "fmt"
    "image/jpeg"

    //  "log"
    "os"

    "github.com/pion/rtp"
    "github.com/pion/rtp/codecs"
    "github.com/pion/webrtc/v3/pkg/media/samplebuilder"
    "github.com/xlab/libvpx-go/vpx"
)

// type Frame struct {
//  *image.RGBA
//  Timecode   time.Duration
//  IsKeyframe bool
// }

type VDecoder struct {
    enabled bool

    src   <-chan *rtp.Packet
    ctx   *vpx.CodecCtx
    iface *vpx.CodecIface
}

type VCodec string

const (
    CodecVP8  VCodec = "V_VP8"
    CodecVP9  VCodec = "V_VP9"
    CodecVP10 VCodec = "V_VP10"
)

func NewVDecoder(codec VCodec, src <-chan *rtp.Packet) *VDecoder {
    dec := &VDecoder{
        src: src,
        ctx: vpx.NewCodecCtx(),
    }
    switch codec {
    case CodecVP8:
        dec.iface = vpx.DecoderIfaceVP8()
    case CodecVP9:
        dec.iface = vpx.DecoderIfaceVP9()
    default: // others are currently disabled
        log.Println("[WARN] unsupported VPX codec:", codec)
        return dec
    }
    err := vpx.Error(vpx.CodecDecInitVer(dec.ctx, dec.iface, nil, 0, vpx.DecoderABIVersion))
    if err != nil {
        log.Println("[WARN]", err)
        return dec
    }
    dec.enabled = true
    return dec
}

func (v *VDecoder) Save(savePath string) { //, out chan<- Frame
    //  defer close(out)
    i := 0

    sampleBuilder := samplebuilder.New(20000, &codecs.VP8Packet{}, 90000)

    for pkt := range v.src {

        sampleBuilder.Push(pkt)

        // Use SampleBuilder to generate full picture from many RTP Packets
        sample := sampleBuilder.Pop()
        if sample == nil {
            continue
        }

        if !v.enabled {
            continue
        }

        dataSize := uint32(len(sample.Data))

        err := vpx.Error(vpx.CodecDecode(v.ctx, string(sample.Data), dataSize, nil, 0))
        if err != nil {
            log.Println("[WARN]", err)
            continue
        }

        var iter vpx.CodecIter
        img := vpx.CodecGetFrame(v.ctx, &iter)
        if img != nil {
            img.Deref()

            // out <- Frame{
            //  RGBA:     img.ImageRGBA(),
            //  Timecode: time.Duration(pkt.Timestamp),
            // }

            i++

            buffer := new(bytes.Buffer)
            if err = jpeg.Encode(buffer, img.ImageYCbCr(), nil); err != nil {
                //  panic(err)
                fmt.Printf("jpeg Encode Error: %s\r\n", err)
            }

            fo, err := os.Create(fmt.Sprintf("%s%d%s", savePath, i, ".jpg"))

            if err != nil {
                fmt.Printf("image create Error: %s\r\n", err)
                //panic(err)
            }
            // close fo on exit and check for its returned error
            defer func() {
                if err := fo.Close(); err != nil {
                    panic(err)
                }
            }()

            if _, err := fo.Write(buffer.Bytes()); err != nil {
                fmt.Printf("image write Error: %s\r\n", err)
                //panic(err)
            }

            fo.Close()
        }
    }
}

只需添加 2 行代码即可保存连续图像

vd := NewVDecoder(CodecVP8, rtpChan)

go vd.Save("/ramdisk/image_seq/xxx")

享受!

PS运行前别忘了安装 libvpx-go 的依赖go get,并确保 libvpx 的版本是1.8


推荐阅读