From f37c92e58d1f60eee36229512fc14fc7f446505d Mon Sep 17 00:00:00 2001 From: WeebDataHoarder <57538841+WeebDataHoarder@users.noreply.github.com> Date: Tue, 15 Nov 2022 13:58:21 +0100 Subject: [PATCH] Clarified reciprocal framerate / timebase usage --- decoder/libdav1d/libdav1d.go | 6 +++--- encoder/libaom/libaom.go | 26 ++++++++++++++++++++++---- utilities/obuwriter/writer.go | 15 ++++++--------- utilities/ratio.go | 5 +++++ 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/decoder/libdav1d/libdav1d.go b/decoder/libdav1d/libdav1d.go index 9575790..3c41b6b 100644 --- a/decoder/libdav1d/libdav1d.go +++ b/decoder/libdav1d/libdav1d.go @@ -71,9 +71,9 @@ func NewDecoder(r io.Reader, settings map[string]any) (d *Decoder, err error) { PixelAspectRatio: fP.PixelAspectRatio, ColorSpace: fP.ColorSpace, FrameRate: utilities.Ratio{ - Numerator: int(d.h.TimebaseDenominator), - Denominator: int(d.h.TimebaseNumerator), - }, + Numerator: int(d.h.TimebaseNumerator), + Denominator: int(d.h.TimebaseDenominator), + }.Reciprocal(), FullColorRange: fP.FullColorRange, } diff --git a/encoder/libaom/libaom.go b/encoder/libaom/libaom.go index b6f5e91..5f8ca24 100644 --- a/encoder/libaom/libaom.go +++ b/encoder/libaom/libaom.go @@ -25,6 +25,7 @@ type Encoder struct { cfg C.aom_codec_enc_cfg_t codec C.aom_codec_ctx_t raw *C.aom_image_t + frames uint32 } var libaomVersion = "libaom-av1 " + C.GoString(C.aom_codec_version_str()) @@ -116,8 +117,23 @@ func NewEncoder(w io.Writer, properties frame.StreamProperties, settings map[str e.cfg.g_w = C.uint(properties.Width) e.cfg.g_h = C.uint(properties.Height) - e.cfg.g_timebase.num = C.int(properties.FrameRate.Denominator) - e.cfg.g_timebase.den = C.int(properties.FrameRate.Numerator) + + /*!\brief Stream timebase units + * + * Indicates the smallest interval of time, in seconds, used by the stream. + * For fixed frame rate material, or variable frame rate material where + * frames are timed at a multiple of a given clock (ex: video capture), + * the \ref RECOMMENDED method is to set the timebase to the reciprocal + * of the frame rate (ex: 1001/30000 for 29.970 Hz NTSC). This allows the + * pts to correspond to the frame number, which can be handy. For + * re-encoding video from containers with absolute time timestamps, the + * \ref RECOMMENDED method is to set the timebase to that of the parent + * container or multimedia framework (ex: 1/1000 for ms, as in FLV). + */ + reciprocalFrameRate := properties.FrameRate.Reciprocal() + + e.cfg.g_timebase.num = C.int(reciprocalFrameRate.Numerator) + e.cfg.g_timebase.den = C.int(reciprocalFrameRate.Denominator) e.cfg.g_threads = C.uint(getSettingInt(settings, "threads", int(e.cfg.g_threads))) e.cfg.g_lag_in_frames = C.uint(getSettingInt(settings, "lag-in-frames", int(e.cfg.g_lag_in_frames))) @@ -193,7 +209,7 @@ func NewEncoder(w io.Writer, properties frame.StreamProperties, settings map[str } var err error - if e.w, err = obuwriter.NewWriter(w, properties.Width, properties.Height, 0x31305641, properties.FrameRate); err != nil { + if e.w, err = obuwriter.NewWriter(w, properties.Width, properties.Height, 0x31305641, reciprocalFrameRate); err != nil { return nil, err } @@ -233,6 +249,8 @@ func (e *Encoder) Encode(f frame.Frame) error { return err } + e.frames++ + return nil } @@ -284,7 +302,7 @@ func (e *Encoder) Flush() error { } } - _ = e.w.WriteLength() + _ = e.w.WriteLength(e.frames) return nil } diff --git a/utilities/obuwriter/writer.go b/utilities/obuwriter/writer.go index a291d29..b84e8e6 100644 --- a/utilities/obuwriter/writer.go +++ b/utilities/obuwriter/writer.go @@ -8,11 +8,10 @@ import ( ) type Writer struct { - w io.Writer - frames uint32 + w io.Writer } -func NewWriter(w io.Writer, width, height int, fourCC uint32, frameRate utilities.Ratio) (*Writer, error) { +func NewWriter(w io.Writer, width, height int, fourCC uint32, timeBase utilities.Ratio) (*Writer, error) { if err := binary.Write(w, binary.LittleEndian, struct { Magic [4]byte Version uint16 @@ -31,8 +30,8 @@ func NewWriter(w io.Writer, width, height int, fourCC uint32, frameRate utilitie FourCC: fourCC, Width: uint16(width), Height: uint16(height), - Denominator: uint32(frameRate.Numerator), - Numerator: uint32(frameRate.Denominator), + Denominator: uint32(timeBase.Denominator), + Numerator: uint32(timeBase.Numerator), Length: 0, }); err != nil { return nil, err @@ -52,20 +51,18 @@ func (w *Writer) WriteFrameBytes(pts uint64, data []byte) error { return err } - w.frames++ - return nil } // WriteLength writes the Length field for number of frames, if writer was set to io.WriteSeeker -func (w *Writer) WriteLength() error { +func (w *Writer) WriteLength(frames uint32) error { if seeker, ok := w.w.(io.WriteSeeker); ok { if currentIndex, err := seeker.Seek(0, io.SeekCurrent); err != nil { return err } else { if _, err = seeker.Seek(4+2+2+4+2+2+4+4, io.SeekStart); err != nil { return err - } else if err = binary.Write(w.w, binary.LittleEndian, w.frames); err != nil { + } else if err = binary.Write(w.w, binary.LittleEndian, frames); err != nil { return err } else if _, err = seeker.Seek(currentIndex, io.SeekStart); err != nil { return err diff --git a/utilities/ratio.go b/utilities/ratio.go index 9fcb07e..b74c2fe 100644 --- a/utilities/ratio.go +++ b/utilities/ratio.go @@ -14,3 +14,8 @@ func (r Ratio) Float64() float64 { func (r Ratio) String() string { return fmt.Sprintf("%d:%d", r.Numerator, r.Denominator) } + +// Reciprocal get the reciprocal, for example, to convert frame rate into time base +func (r Ratio) Reciprocal() Ratio { + return Ratio{Numerator: r.Denominator, Denominator: r.Numerator} +}