Clarified reciprocal framerate / timebase usage

This commit is contained in:
DataHoarder 2022-11-15 13:58:21 +01:00
parent 3c10219a47
commit f37c92e58d
Signed by: DataHoarder
SSH Key Fingerprint: SHA256:OLTRf6Fl87G52SiR7sWLGNzlJt4WOX+tfI2yxo0z7xk
4 changed files with 36 additions and 16 deletions

View File

@ -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,
}

View File

@ -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
}

View File

@ -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

View File

@ -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}
}