Repository: pion/interceptor
Pull request #301 (2025-01-16 15:43:15)

Move NTP function to ntp package and add ToTime32
Moves all NTP-related functions to the NTP package and adds a function to recover a time.Time from a uint32 encoded NTP timestamp. This allows reusing code for CCFB handling (and testing).

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/pion/interceptor/pull/301?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 71.09%. Comparing base [(`e187410`)](https://app.codecov.io/gh/pion/interceptor/commit/e1874104865b23ba465ecc505f959daf156cc2a5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) to head [(`057d141`)](https://app.codecov.io/gh/pion/interceptor/commit/057d141d72957979ded1788c3ff4e81d1158dd53?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #301      +/-   ##
==========================================
- Coverage   71.31%   71.09%   -0.23%     
==========================================
  Files          80       80              
  Lines        4483     4480       -3     
==========================================
- Hits         3197     3185      -12     
- Misses       1153     1159       +6     
- Partials      133      136       +3     
```

| [Flag](https://app.codecov.io/gh/pion/interceptor/pull/301/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) | Coverage Δ | |
|---|---|---|
| [go](https://app.codecov.io/gh/pion/interceptor/pull/301/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) | `71.09% <100.00%> (-0.09%)` | :arrow_down: |
| [wasm](https://app.codecov.io/gh/pion/interceptor/pull/301/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion) | `69.24% <100.00%> (-0.16%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/pion/interceptor/pull/301?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=pion).


Patch:
diff --git a/internal/ntp/ntp.go b/internal/ntp/ntp.go
index 69f98d6..6d32bb5 100644
--- a/internal/ntp/ntp.go
+++ b/internal/ntp/ntp.go
@@ -20,6 +20,11 @@ func ToNTP(t time.Time) uint64 {
 	return uint64(integerPart)<<32 | uint64(fractionalPart)
 }
 
+// ToNTP32 converts a time.Time object to a uint32 NTP timestamp
+func ToNTP32(t time.Time) uint32 {
+	return uint32(ToNTP(t) >> 16)
+}
+
 // ToTime converts a uint64 NTP timestamps to a time.Time object
 func ToTime(t uint64) time.Time {
 	seconds := (t & 0xFFFFFFFF00000000) >> 32
@@ -28,3 +33,12 @@ func ToTime(t uint64) time.Time {
 
 	return time.Unix(0, 0).Add(-2208988800 * time.Second).Add(d)
 }
+
+// ToTime32 converts a uint32 NTP timestamp to a time.Time object, using the
+// highest 16 bit of the reference to recover the lost bits. The low 16 bits are
+// not recovered.
+func ToTime32(t uint32, reference time.Time) time.Time {
+	referenceNTP := ToNTP(reference) & 0xFFFF000000000000
+	tu64 := ((uint64(t) << 16) & 0x0000FFFFFFFF0000) | referenceNTP
+	return ToTime(tu64)
+}
diff --git a/pkg/rfc8888/recorder.go b/pkg/rfc8888/recorder.go
index 4423df1..e1dba0d 100644
--- a/pkg/rfc8888/recorder.go
+++ b/pkg/rfc8888/recorder.go
@@ -6,6 +6,7 @@ package rfc8888
 import (
 	"time"
 
+	"github.com/pion/interceptor/internal/ntp"
 	"github.com/pion/rtcp"
 )
 
@@ -44,7 +45,7 @@ func (r *Recorder) BuildReport(now time.Time, maxSize int) *rtcp.CCFeedbackRepor
 	report := &rtcp.CCFeedbackReport{
 		SenderSSRC:      r.ssrc,
 		ReportBlocks:    []rtcp.CCFeedbackReportBlock{},
-		ReportTimestamp: ntpTime32(now),
+		ReportTimestamp: ntp.ToNTP32(now),
 	}
 
 	maxReportBlocks := (maxSize - 12 - (8 * len(r.streams))) / 2
@@ -65,14 +66,3 @@ func (r *Recorder) BuildReport(now time.Time, maxSize int) *rtcp.CCFeedbackRepor
 
 	return report
 }
-
-func ntpTime32(t time.Time) uint32 {
-	// seconds since 1st January 1900
-	s := (float64(t.UnixNano()) / 1000000000.0) + 2208988800
-
-	integerPart := uint32(s)
-	fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF)
-
-	// higher 32 bits are the integer part, lower 32 bits are the fractional part
-	return uint32(((uint64(integerPart)<<32 | uint64(fractionalPart)) >> 16) & 0xFFFFFFFF)
-}


Test patch:
diff --git a/internal/ntp/ntp_test.go b/internal/ntp/ntp_test.go
index 8bbe79e..7365809 100644
--- a/internal/ntp/ntp_test.go
+++ b/internal/ntp/ntp_test.go
@@ -11,7 +11,7 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestNTPTimeConverstion(t *testing.T) {
+func TestNTPToTimeConverstion(t *testing.T) {
 	for i, cc := range []struct {
 		ts time.Time
 	}{
@@ -24,6 +24,7 @@ func TestNTPTimeConverstion(t *testing.T) {
 	} {
 		t.Run(fmt.Sprintf("TimeToNTP/%v", i), func(t *testing.T) {
 			assert.InDelta(t, cc.ts.UnixNano(), ToTime(ToNTP(cc.ts)).UnixNano(), float64(time.Millisecond.Nanoseconds()))
+			assert.InDelta(t, cc.ts.UnixNano(), ToTime32(ToNTP32(cc.ts), cc.ts).UnixNano(), float64(time.Millisecond.Nanoseconds()))
 		})
 	}
 }
@@ -50,3 +51,38 @@ func TestTimeToNTPConverstion(t *testing.T) {
 		})
 	}
 }
+
+func TestNTPTime32(t *testing.T) {
+	zero := time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)
+	notSoLongAgo := time.Date(2022, time.May, 5, 14, 48, 20, 0, time.UTC)
+	for i, cc := range []struct {
+		input    time.Time
+		expected uint32
+	}{
+		{
+			input:    zero,
+			expected: 0,
+		},
+		{
+			input:    zero.Add(time.Second),
+			expected: 1 << 16,
+		},
+		{
+			input:    notSoLongAgo,
+			expected: uint32(uint(notSoLongAgo.Sub(zero).Seconds())&0xffff) << 16,
+		},
+		{
+			input:    zero.Add(400 * time.Millisecond),
+			expected: 26214,
+		},
+		{
+			input:    zero.Add(1400 * time.Millisecond),
+			expected: 1<<16 + 26214,
+		},
+	} {
+		t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
+			res := ToNTP32(cc.input)
+			assert.Equalf(t, cc.expected, res, "%b != %b", cc.expected, res)
+		})
+	}
+}
diff --git a/pkg/rfc8888/recorder_test.go b/pkg/rfc8888/recorder_test.go
index d5a86db..7e38b9a 100644
--- a/pkg/rfc8888/recorder_test.go
+++ b/pkg/rfc8888/recorder_test.go
@@ -4,7 +4,6 @@
 package rfc8888
 
 import (
-	"fmt"
 	"testing"
 	"time"
 
@@ -161,38 +160,3 @@ func TestRecorder(t *testing.T) {
 		}
 	})
 }
-
-func TestNTPTime32(t *testing.T) {
-	zero := time.Date(1900, time.January, 1, 0, 0, 0, 0, time.UTC)
-	notSoLongAgo := time.Date(2022, time.May, 5, 14, 48, 20, 0, time.UTC)
-	for i, cc := range []struct {
-		input    time.Time
-		expected uint32
-	}{
-		{
-			input:    zero,
-			expected: 0,
-		},
-		{
-			input:    zero.Add(time.Second),
-			expected: 1 << 16,
-		},
-		{
-			input:    notSoLongAgo,
-			expected: uint32(uint(notSoLongAgo.Sub(zero).Seconds())&0xffff) << 16,
-		},
-		{
-			input:    zero.Add(400 * time.Millisecond),
-			expected: 26214,
-		},
-		{
-			input:    zero.Add(1400 * time.Millisecond),
-			expected: 1<<16 + 26214,
-		},
-	} {
-		t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
-			res := ntpTime32(cc.input)
-			assert.Equalf(t, cc.expected, res, "%b != %b", cc.expected, res)
-		})
-	}
-}