mirror of
https://github.com/pion/mediadevices.git
synced 2025-09-27 12:52:20 +08:00
add vpx decoder
This commit is contained in:
107
pkg/codec/vpx/vpx_decoder.go
Normal file
107
pkg/codec/vpx/vpx_decoder.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package vpx
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo pkg-config: vpx
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vpx/vpx_decoder.h>
|
||||||
|
#include <vpx/vpx_codec.h>
|
||||||
|
#include <vpx/vpx_image.h>
|
||||||
|
#include <vpx/vp8dx.h>
|
||||||
|
|
||||||
|
vpx_codec_iface_t *ifaceVP8Decoder() {
|
||||||
|
return vpx_codec_vp8_dx();
|
||||||
|
}
|
||||||
|
vpx_codec_iface_t *ifaceVP9Decoder() {
|
||||||
|
return vpx_codec_vp9_dx();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocates a new decoder context
|
||||||
|
vpx_codec_ctx_t* newDecoderCtx() {
|
||||||
|
return (vpx_codec_ctx_t*)malloc(sizeof(vpx_codec_ctx_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializes the decoder
|
||||||
|
vpx_codec_err_t decoderInit(vpx_codec_ctx_t* ctx, vpx_codec_iface_t* iface) {
|
||||||
|
return vpx_codec_dec_init_ver(ctx, iface, NULL, 0, VPX_DECODER_ABI_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decodes an encoded frame
|
||||||
|
vpx_codec_err_t decodeFrame(vpx_codec_ctx_t* ctx, const uint8_t* data, unsigned int data_sz) {
|
||||||
|
return vpx_codec_decode(ctx, data, data_sz, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an iterator
|
||||||
|
vpx_codec_iter_t* newIter() {
|
||||||
|
return (vpx_codec_iter_t*)malloc(sizeof(vpx_codec_iter_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the next decoded frame
|
||||||
|
vpx_image_t* getFrame(vpx_codec_ctx_t* ctx, vpx_codec_iter_t* iter) {
|
||||||
|
return vpx_codec_get_frame(ctx, iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees a decoder context
|
||||||
|
void freeDecoderCtx(vpx_codec_ctx_t* ctx) {
|
||||||
|
vpx_codec_destroy(ctx);
|
||||||
|
free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pion/mediadevices/pkg/prop"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Decoder struct {
|
||||||
|
codec *C.vpx_codec_ctx_t
|
||||||
|
raw *C.vpx_image_t
|
||||||
|
cfg *C.vpx_codec_dec_cfg_t
|
||||||
|
frameIndex int
|
||||||
|
tStart time.Time
|
||||||
|
tLastFrame time.Time
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDecoder(p prop.Media) (Decoder, error) {
|
||||||
|
cfg := &C.vpx_codec_dec_cfg_t{}
|
||||||
|
cfg.threads = 1
|
||||||
|
cfg.w = C.uint(p.Width)
|
||||||
|
cfg.h = C.uint(p.Height)
|
||||||
|
|
||||||
|
codec := C.newDecoderCtx()
|
||||||
|
if C.decoderInit(codec, C.ifaceVP8Decoder()) != C.VPX_CODEC_OK {
|
||||||
|
return Decoder{}, fmt.Errorf("vpx_codec_dec_init failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Decoder{
|
||||||
|
codec: codec,
|
||||||
|
cfg: cfg,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) Decode(data []byte) {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status := C.decodeFrame(d.codec, (*C.uint8_t)(&data[0]), C.uint(len(data)))
|
||||||
|
if status != C.VPX_CODEC_OK {
|
||||||
|
fmt.Println("Decode failed", status)
|
||||||
|
panic("Decode failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) GetFrame() *C.vpx_image_t {
|
||||||
|
iter := C.newIter()
|
||||||
|
return C.getFrame(d.codec, iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) FreeDecoderCtx() {
|
||||||
|
C.freeDecoderCtx(d.codec)
|
||||||
|
}
|
@@ -425,3 +425,78 @@ func TestVP8DynamicQPControl(t *testing.T) {
|
|||||||
assert.Less(t, math.Abs(float64(targetBitrate-currentBitrate)), math.Abs(float64(initialBitrate-currentBitrate)))
|
assert.Less(t, math.Abs(float64(targetBitrate-currentBitrate)), math.Abs(float64(initialBitrate-currentBitrate)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVP8EncodeDecode(t *testing.T) {
|
||||||
|
t.Run("VP8", func(t *testing.T) {
|
||||||
|
initialWidth, initialHeight := 800, 600
|
||||||
|
decoder, err := NewDecoder(prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: initialWidth,
|
||||||
|
Height: initialHeight,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating VP8 decoder: %v", err)
|
||||||
|
}
|
||||||
|
defer decoder.FreeDecoderCtx()
|
||||||
|
|
||||||
|
// [... encoder setup code ...]
|
||||||
|
p, err := NewVP8Params()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p.LagInFrames = 0 // Disable frame lag buffering for real-time encoding
|
||||||
|
p.RateControlEndUsage = RateControlCBR
|
||||||
|
totalFrames := 100
|
||||||
|
var cnt uint32
|
||||||
|
r, err := p.BuildVideoEncoder(
|
||||||
|
video.ReaderFunc(func() (image.Image, func(), error) {
|
||||||
|
i := atomic.AddUint32(&cnt, 1)
|
||||||
|
if i == uint32(totalFrames+1) {
|
||||||
|
return nil, nil, io.EOF
|
||||||
|
}
|
||||||
|
img := image.NewYCbCr(image.Rect(0, 0, initialWidth, initialHeight), image.YCbCrSubsampleRatio420)
|
||||||
|
return img, func() {}, nil
|
||||||
|
}),
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: initialWidth,
|
||||||
|
Height: initialHeight,
|
||||||
|
FrameRate: 30,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, rel, err := r.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer rel()
|
||||||
|
|
||||||
|
// Decode the frame
|
||||||
|
decoder.Decode(data)
|
||||||
|
|
||||||
|
// Poll for frame with timeout
|
||||||
|
timeout := time.After(2 * time.Second)
|
||||||
|
ticker := time.NewTicker(10 * time.Millisecond)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
t.Fatal("Timeout: No frame received within 2 seconds")
|
||||||
|
case <-ticker.C:
|
||||||
|
frame := decoder.GetFrame()
|
||||||
|
if frame != nil {
|
||||||
|
t.Log("Successfully received and decoded frame")
|
||||||
|
return // Test passes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user