mirror of
https://github.com/pion/mediadevices.git
synced 2025-10-04 08:16:33 +08:00
Fix the VPX encoder is not properly handling the frame timestamp (#606)
* Fix the VPX encoder is not properly handling the frame timestamp * Apply suggestions from code review Co-authored-by: Atsushi Watanabe <atsushi.w@ieee.org> --------- Co-authored-by: Atsushi Watanabe <atsushi.w@ieee.org>
This commit is contained in:
@@ -69,8 +69,8 @@ type encoder struct {
|
|||||||
cfg *C.vpx_codec_enc_cfg_t
|
cfg *C.vpx_codec_enc_cfg_t
|
||||||
r video.Reader
|
r video.Reader
|
||||||
frameIndex int
|
frameIndex int
|
||||||
tStart int
|
tStart time.Time
|
||||||
tLastFrame int
|
tLastFrame time.Time
|
||||||
frame []byte
|
frame []byte
|
||||||
deadline int
|
deadline int
|
||||||
requireKeyFrame bool
|
requireKeyFrame bool
|
||||||
@@ -198,7 +198,7 @@ func newEncoder(r video.Reader, p prop.Media, params Params, codecIface *C.vpx_c
|
|||||||
); ec != 0 {
|
); ec != 0 {
|
||||||
return nil, fmt.Errorf("vpx_codec_enc_init failed (%d)", ec)
|
return nil, fmt.Errorf("vpx_codec_enc_init failed (%d)", ec)
|
||||||
}
|
}
|
||||||
t0 := time.Now().Nanosecond() / 1000000
|
t0 := time.Now()
|
||||||
return &encoder{
|
return &encoder{
|
||||||
r: video.ToI420(r),
|
r: video.ToI420(r),
|
||||||
codec: codec,
|
codec: codec,
|
||||||
@@ -233,7 +233,7 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
e.raw.stride[1] = C.int(yuvImg.CStride)
|
e.raw.stride[1] = C.int(yuvImg.CStride)
|
||||||
e.raw.stride[2] = C.int(yuvImg.CStride)
|
e.raw.stride[2] = C.int(yuvImg.CStride)
|
||||||
|
|
||||||
t := time.Now().Nanosecond() / 1000000
|
t := time.Now()
|
||||||
|
|
||||||
if e.cfg.g_w != C.uint(width) || e.cfg.g_h != C.uint(height) {
|
if e.cfg.g_w != C.uint(width) || e.cfg.g_h != C.uint(height) {
|
||||||
e.cfg.g_w, e.cfg.g_h = C.uint(width), C.uint(height)
|
e.cfg.g_w, e.cfg.g_h = C.uint(width), C.uint(height)
|
||||||
@@ -252,7 +252,7 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
e.raw.d_w, e.raw.d_h = C.uint(width), C.uint(height)
|
e.raw.d_w, e.raw.d_h = C.uint(width), C.uint(height)
|
||||||
}
|
}
|
||||||
|
|
||||||
duration := t - e.tLastFrame
|
duration := t.Sub(e.tLastFrame).Microseconds()
|
||||||
// VPX doesn't allow 0 duration. If 0 is given, vpx_codec_encode will fail with VPX_CODEC_INVALID_PARAM.
|
// VPX doesn't allow 0 duration. If 0 is given, vpx_codec_encode will fail with VPX_CODEC_INVALID_PARAM.
|
||||||
// 0 duration is possible because mediadevices first gets the frame meta data by reading from the source,
|
// 0 duration is possible because mediadevices first gets the frame meta data by reading from the source,
|
||||||
// and consequently the codec will read the first frame from the buffer. This means the first frame won't
|
// and consequently the codec will read the first frame from the buffer. This means the first frame won't
|
||||||
@@ -267,7 +267,7 @@ func (e *encoder) Read() ([]byte, func(), error) {
|
|||||||
}
|
}
|
||||||
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.Sub(e.tStart).Microseconds()), C.ulong(duration), C.long(flags), C.ulong(e.deadline),
|
||||||
(*C.uchar)(&yuvImg.Y[0]), (*C.uchar)(&yuvImg.Cb[0]), (*C.uchar)(&yuvImg.Cr[0]),
|
(*C.uchar)(&yuvImg.Y[0]), (*C.uchar)(&yuvImg.Cb[0]), (*C.uchar)(&yuvImg.Cr[0]),
|
||||||
); ec != C.VPX_CODEC_OK {
|
); ec != C.VPX_CODEC_OK {
|
||||||
return nil, func() {}, fmt.Errorf("vpx_codec_encode failed (%d)", ec)
|
return nil, func() {}, fmt.Errorf("vpx_codec_encode failed (%d)", ec)
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
package vpx
|
package vpx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"image"
|
"image"
|
||||||
"io"
|
"io"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pion/mediadevices/pkg/codec"
|
"github.com/pion/mediadevices/pkg/codec"
|
||||||
"github.com/pion/mediadevices/pkg/codec/internal/codectest"
|
"github.com/pion/mediadevices/pkg/codec/internal/codectest"
|
||||||
@@ -245,3 +247,49 @@ func TestShouldImplementKeyFrameControl(t *testing.T) {
|
|||||||
t.Error()
|
t.Error()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEncoderFrameMonotonic(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
params, err := NewVP8Params()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder, err := params.BuildVideoEncoder(
|
||||||
|
video.ReaderFunc(func() (image.Image, func(), error) {
|
||||||
|
return image.NewYCbCr(
|
||||||
|
image.Rect(0, 0, 320, 240),
|
||||||
|
image.YCbCrSubsampleRatio420,
|
||||||
|
), func() {}, nil
|
||||||
|
},
|
||||||
|
), prop.Media{
|
||||||
|
Video: prop.Video{
|
||||||
|
Width: 320,
|
||||||
|
Height: 240,
|
||||||
|
FrameRate: 30,
|
||||||
|
FrameFormat: frame.FormatI420,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(33 * time.Millisecond)
|
||||||
|
defer ticker.Stop()
|
||||||
|
ctxx, cancell := context.WithCancel(ctx)
|
||||||
|
defer cancell()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctxx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
_, rel, err := encoder.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user