mirror of
https://github.com/pion/mediadevices.git
synced 2025-10-04 00:06:32 +08:00
Implements RequireKeyFrame for VPX codec (#334)
This commit is contained in:
@@ -16,6 +16,7 @@ type Params struct {
|
|||||||
RateControlOvershootPercent uint
|
RateControlOvershootPercent uint
|
||||||
RateControlMinQuantizer uint
|
RateControlMinQuantizer uint
|
||||||
RateControlMaxQuantizer uint
|
RateControlMaxQuantizer uint
|
||||||
|
LagInFrames uint
|
||||||
ErrorResilient ErrorResilientMode
|
ErrorResilient ErrorResilientMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,6 +23,9 @@ package vpx
|
|||||||
// int pktSz(vpx_codec_cx_pkt_t *pkt) {
|
// int pktSz(vpx_codec_cx_pkt_t *pkt) {
|
||||||
// return pkt->data.frame.sz;
|
// return pkt->data.frame.sz;
|
||||||
// }
|
// }
|
||||||
|
// vpx_codec_frame_flags_t pktFrameFlags(vpx_codec_cx_pkt_t *pkt) {
|
||||||
|
// return pkt->data.frame.flags;
|
||||||
|
// }
|
||||||
//
|
//
|
||||||
// // Alloc helpers
|
// // Alloc helpers
|
||||||
// vpx_codec_ctx_t *newCtx() {
|
// vpx_codec_ctx_t *newCtx() {
|
||||||
@@ -70,6 +73,8 @@ type encoder struct {
|
|||||||
tLastFrame int
|
tLastFrame int
|
||||||
frame []byte
|
frame []byte
|
||||||
deadline int
|
deadline int
|
||||||
|
requireKeyFrame bool
|
||||||
|
isKeyFrame bool
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
closed bool
|
closed bool
|
||||||
@@ -141,6 +146,7 @@ func newParams(codecIface *C.vpx_codec_iface_t) (Params, error) {
|
|||||||
RateControlOvershootPercent: uint(cfg.rc_overshoot_pct),
|
RateControlOvershootPercent: uint(cfg.rc_overshoot_pct),
|
||||||
RateControlMinQuantizer: uint(cfg.rc_min_quantizer),
|
RateControlMinQuantizer: uint(cfg.rc_min_quantizer),
|
||||||
RateControlMaxQuantizer: uint(cfg.rc_max_quantizer),
|
RateControlMaxQuantizer: uint(cfg.rc_max_quantizer),
|
||||||
|
LagInFrames: uint(cfg.g_lag_in_frames),
|
||||||
ErrorResilient: ErrorResilientMode(cfg.g_error_resilient),
|
ErrorResilient: ErrorResilientMode(cfg.g_error_resilient),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -171,6 +177,7 @@ func newEncoder(r video.Reader, p prop.Media, params Params, codecIface *C.vpx_c
|
|||||||
cfg.g_h = C.uint(p.Height)
|
cfg.g_h = C.uint(p.Height)
|
||||||
cfg.g_timebase.num = 1
|
cfg.g_timebase.num = 1
|
||||||
cfg.g_timebase.den = 1000
|
cfg.g_timebase.den = 1000
|
||||||
|
cfg.g_lag_in_frames = C.uint(params.LagInFrames)
|
||||||
cfg.rc_target_bitrate = C.uint(params.BitRate) / 1000
|
cfg.rc_target_bitrate = C.uint(params.BitRate) / 1000
|
||||||
cfg.kf_max_dist = C.uint(params.KeyFrameInterval)
|
cfg.kf_max_dist = C.uint(params.KeyFrameInterval)
|
||||||
|
|
||||||
@@ -254,6 +261,9 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
duration = 1
|
duration = 1
|
||||||
}
|
}
|
||||||
var flags int
|
var flags int
|
||||||
|
if e.requireKeyFrame {
|
||||||
|
flags = flags | C.VPX_EFLAG_FORCE_KF
|
||||||
|
}
|
||||||
if ec := C.encode_wrapper(
|
if ec := C.encode_wrapper(
|
||||||
e.codec, e.raw,
|
e.codec, e.raw,
|
||||||
C.long(t-e.tStart), C.ulong(duration), C.long(flags), C.ulong(e.deadline),
|
C.long(t-e.tStart), C.ulong(duration), C.long(flags), C.ulong(e.deadline),
|
||||||
@@ -262,6 +272,7 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
return nil, func() {}, fmt.Errorf("vpx_codec_encode failed (%d)", ec)
|
return nil, func() {}, fmt.Errorf("vpx_codec_encode failed (%d)", ec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.requireKeyFrame = false
|
||||||
e.frameIndex++
|
e.frameIndex++
|
||||||
e.tLastFrame = t
|
e.tLastFrame = t
|
||||||
|
|
||||||
@@ -273,6 +284,7 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if pkt.kind == C.VPX_CODEC_CX_FRAME_PKT {
|
if pkt.kind == C.VPX_CODEC_CX_FRAME_PKT {
|
||||||
|
e.isKeyFrame = C.pktFrameFlags(pkt)&C.VPX_FRAME_IS_KEY == C.VPX_FRAME_IS_KEY
|
||||||
encoded := C.GoBytes(unsafe.Pointer(C.pktBuf(pkt)), C.pktSz(pkt))
|
encoded := C.GoBytes(unsafe.Pointer(C.pktBuf(pkt)), C.pktSz(pkt))
|
||||||
e.frame = append(e.frame, encoded...)
|
e.frame = append(e.frame, encoded...)
|
||||||
}
|
}
|
||||||
@@ -288,7 +300,10 @@ func (e *encoder) SetBitRate(b int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) ForceKeyFrame() error {
|
func (e *encoder) ForceKeyFrame() error {
|
||||||
panic("ForceKeyFrame is not implemented")
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
e.requireKeyFrame = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *encoder) Close() error {
|
func (e *encoder) Close() error {
|
||||||
|
@@ -20,6 +20,8 @@ func TestImageSizeChange(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"VP9": func() (codec.VideoEncoderBuilder, error) {
|
"VP9": func() (codec.VideoEncoderBuilder, error) {
|
||||||
p, err := NewVP9Params()
|
p, err := NewVP9Params()
|
||||||
|
// Disable latency to ease test and begin to receive packets for each input frame
|
||||||
|
p.LagInFrames = 0
|
||||||
return &p, err
|
return &p, err
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
@@ -87,3 +89,72 @@ func TestImageSizeChange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRequestKeyFrame(t *testing.T) {
|
||||||
|
for name, factory := range map[string]func() (codec.VideoEncoderBuilder, error){
|
||||||
|
"VP8": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP8Params()
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
"VP9": func() (codec.VideoEncoderBuilder, error) {
|
||||||
|
p, err := NewVP9Params()
|
||||||
|
// Disable latency to ease test and begin to receive packets for each input frame
|
||||||
|
p.LagInFrames = 0
|
||||||
|
return &p, err
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
factory := factory
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
param, err := factory()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var initialWidth, initialHeight, width, height int = 320, 240, 320, 240
|
||||||
|
|
||||||
|
var cnt uint32
|
||||||
|
r, err := param.BuildVideoEncoder(
|
||||||
|
video.ReaderFunc(func() (image.Image, func(), error) {
|
||||||
|
i := atomic.AddUint32(&cnt, 1)
|
||||||
|
if i == 3 {
|
||||||
|
return nil, nil, io.EOF
|
||||||
|
}
|
||||||
|
return image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, width, height),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
), func() {}, nil
|
||||||
|
}),
|
||||||
|
prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: initialWidth,
|
||||||
|
Height: initialHeight,
|
||||||
|
FrameRate: 1,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, rel, err := r.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rel()
|
||||||
|
r.ForceKeyFrame()
|
||||||
|
_, rel, err = r.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !r.(*encoder).isKeyFrame {
|
||||||
|
t.Fatal("Not a key frame")
|
||||||
|
}
|
||||||
|
rel()
|
||||||
|
_, _, err = r.Read()
|
||||||
|
if err != io.EOF {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user