Repository: pion/rtp

Issue:
Allow the ability to define the initial timestamp for the packetizer. 
## Summary

Currently the timestamp is defined using a random number

https://github.com/pion/rtp/blob/319e780b743768216fcd1facedd8b26ca208ff01/packetizer.go#L40

This doesn't allow for syncing tracks from multiple sources where we can then swap the tracks from the server without repair.

## Motivation

I have 4 video sources with different quality settings but they cannot be used with ABR since the initial timestamps do not correspond with each other.



Hints from issue comments:
@Sean-Der: Hey @TroyDota 

I am in support of this! I would add a `...Option` to the end and have it function like [SampleBuilder.Option](https://github.com/pion/webrtc/blob/master/pkg/media/samplebuilder/samplebuilder.go#L329-L330)@sylwek78: Mmm@sylwek78: 👍

Solution patch:
diff --git a/packetizer.go b/packetizer.go
index 9ad07e0..7119e09 100644
--- a/packetizer.go
+++ b/packetizer.go
@@ -59,6 +59,51 @@ func NewPacketizer(
 	}
 }
 
+// WithSSRC sets the SSRC for the Packetizer.
+func WithSSRC(ssrc uint32) func(*packetizer) {
+	return func(p *packetizer) {
+		p.SSRC = ssrc
+	}
+}
+
+// WithPayloadType sets the PayloadType for the Packetizer.
+func WithPayloadType(pt uint8) func(*packetizer) {
+	return func(p *packetizer) {
+		p.PayloadType = pt
+	}
+}
+
+// WithTimestamp sets the initial Timestamp for the Packetizer.
+func WithTimestamp(timestamp uint32) func(*packetizer) {
+	return func(p *packetizer) {
+		p.Timestamp = timestamp
+	}
+}
+
+// NewPacketizerWithOptions returns a new instance of a Packetizer with the given options.
+func NewPacketizerWithOptions(
+	mtu uint16,
+	payloader Payloader,
+	sequencer Sequencer,
+	clockRate uint32,
+	options ...func(*packetizer),
+) Packetizer {
+	packetizerInstance := &packetizer{
+		MTU:       mtu,
+		Payloader: payloader,
+		Sequencer: sequencer,
+		Timestamp: globalMathRandomGenerator.Uint32(),
+		ClockRate: clockRate,
+		timegen:   time.Now,
+	}
+
+	for _, option := range options {
+		option(packetizerInstance)
+	}
+
+	return packetizerInstance
+}
+
 func (p *packetizer) EnableAbsSendTime(value int) {
 	p.extensionNumbers.AbsSendTime = value
 }



Test patch:
diff --git a/packetizer_test.go b/packetizer_test.go
index d0a33b6..7a35998 100644
--- a/packetizer_test.go
+++ b/packetizer_test.go
@@ -110,3 +110,61 @@ func TestPacketizer_Roundtrip(t *testing.T) {
 		assert.Equal(t, expectedPkt, pkt)
 	}
 }
+
+func TestNewPacketizerWithOptions_DefaultValues(t *testing.T) {
+	pack := NewPacketizerWithOptions(100, &codecs.G722Payloader{}, NewRandomSequencer(), 90000)
+	p, ok := pack.(*packetizer)
+	assert.True(t, ok, "Failed to cast to *packetizer")
+
+	assert.Equal(t, uint16(100), p.MTU)
+	assert.Equal(t, uint8(0), p.PayloadType)
+	assert.Equal(t, uint32(0), p.SSRC)
+	assert.NotZero(t, p.Timestamp)
+	assert.Equal(t, uint32(90000), p.ClockRate)
+}
+
+func TestNewPacketizerWithOptions_WithOptions(t *testing.T) {
+	pack := NewPacketizerWithOptions(
+		100,
+		&codecs.G722Payloader{},
+		NewRandomSequencer(),
+		90000,
+		WithSSRC(0x1234ABCD),
+		WithPayloadType(98),
+		WithTimestamp(45678),
+	)
+	p, ok := pack.(*packetizer)
+	assert.True(t, ok, "Failed to cast to *packetizer")
+
+	assert.Equal(t, uint16(100), p.MTU)
+	assert.Equal(t, uint8(98), p.PayloadType)
+	assert.Equal(t, uint32(0x1234ABCD), p.SSRC)
+	assert.Equal(t, uint32(45678), p.Timestamp)
+	assert.Equal(t, uint32(90000), p.ClockRate)
+
+	payload := []byte{0x11, 0x12, 0x13, 0x14}
+	packets := pack.Packetize(payload, 2000)
+
+	assert.Len(t, packets, 1, "Should generate exactly one packet")
+	assert.Equal(t, uint8(98), packets[0].PayloadType)
+	assert.Equal(t, uint32(0x1234ABCD), packets[0].SSRC)
+	assert.Equal(t, uint32(45678), packets[0].Timestamp)
+}
+
+func TestNewPacketizerWithOptions_PartialOptions(t *testing.T) {
+	pack := NewPacketizerWithOptions(
+		100,
+		&codecs.G722Payloader{},
+		NewRandomSequencer(),
+		90000,
+		WithPayloadType(98),
+	)
+	p, ok := pack.(*packetizer)
+	assert.True(t, ok, "Failed to cast to *packetizer")
+
+	assert.Equal(t, uint16(100), p.MTU)
+	assert.Equal(t, uint8(98), p.PayloadType)
+	assert.Equal(t, uint32(0), p.SSRC)
+	assert.NotZero(t, p.Timestamp)
+	assert.Equal(t, uint32(90000), p.ClockRate)
+}