diff --git a/round.go b/round.go new file mode 100644 index 00000000..2b40f72b --- /dev/null +++ b/round.go @@ -0,0 +1,56 @@ +package m3u8 + +import ( + "math" +) + +// some constants copied from https://github.com/golang/go/blob/master/src/math/bits.go +const ( + shift = 64 - 11 - 1 + bias = 1023 + mask = 0x7FF +) + +// round returns the nearest integer, rounding half away from zero. +// This function is available natively in Go 1.10 +// +// Special cases are: +// round(±0) = ±0 +// round(±Inf) = ±Inf +// round(NaN) = NaN +func round(x float64) float64 { + // Round is a faster implementation of: + // + // func Round(x float64) float64 { + // t := Trunc(x) + // if Abs(x-t) >= 0.5 { + // return t + Copysign(1, x) + // } + // return t + // } + const ( + signMask = 1 << 63 + fracMask = 1<>shift) & mask + if e < bias { + // Round abs(x) < 1 including denormals. + bits &= signMask // +-0 + if e == bias-1 { + bits |= one // +-1 + } + } else if e < bias+shift { + // Round any abs(x) >= 1 containing a fractional component [0,1). + // + // Numbers with larger exponents are returned unchanged since they + // must be either an integer, infinity, or NaN. + e -= bias + bits += half >> e + bits &^= fracMask >> e + } + return math.Float64frombits(bits) +} diff --git a/writer.go b/writer.go index 36f0c8ec..299fd4c4 100644 --- a/writer.go +++ b/writer.go @@ -305,7 +305,11 @@ func (p *MediaPlaylist) AppendSegment(seg *MediaSegment) error { p.tail = (p.tail + 1) % p.capacity p.count++ if p.TargetDuration < seg.Duration { - p.TargetDuration = math.Ceil(seg.Duration) + if p.durationAsInt { + p.TargetDuration = math.Ceil(seg.Duration) + } else { + p.TargetDuration = round(seg.Duration) + } } p.buf.Reset() return nil diff --git a/writer_test.go b/writer_test.go index c737e690..4d27759a 100644 --- a/writer_test.go +++ b/writer_test.go @@ -167,8 +167,8 @@ func TestTargetDurationForMediaPlaylist(t *testing.T) { if e != nil { t.Errorf("Add 2nd segment to a media playlist failed: %s", e) } - if p.TargetDuration < 10.0 { - t.Errorf("Target duration must = 10 (nearest greater integer to durations 9.0 and 9.1)") + if p.TargetDuration != 9.0 { + t.Errorf("Target duration must = 9 (nearest integer to durations 9.0 and 9.1)") } }