Skip to content

Instantly share code, notes, and snippets.

@philhartung
Last active May 14, 2024 09:31
Show Gist options
  • Save philhartung/72a2296a8c83a3468ffba509cdc9d104 to your computer and use it in GitHub Desktop.
Save philhartung/72a2296a8c83a3468ffba509cdc9d104 to your computer and use it in GitHub Desktop.
Sync to PTPv2 clock and send RTP according to AES67. SAP/SDP not implemented here.
import sys
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstNet', '1.0')
from gi.repository import Gst, GstNet, GObject, GLib
Gst.init([])
mainloop = GLib.MainLoop()
# audiotestsrc to aes67
pipelineString = """
audiotestsrc freq=480 volume=0.1 !
audioconvert !
audio/x-raw, format=S24BE, channels=2, rate=48000 !
rtpL24pay name=rtppay min-ptime=1000000 max-ptime=1000000 !
application/x-rtp, clock-rate=48000, channels=2, payload=98 !
udpsink host=239.69.0.121 port=5004 qos=true qos-dscp=34 multicast-iface=eth0
"""
if GstNet.ptp_init(GstNet.PTP_CLOCK_ID_NONE, ['eth0']):
print('PTP initialized')
else:
print('PTP failed to initialize')
sys.exit()
ptpClock = GstNet.PtpClock.new('PTP-Master', 0)
if ptpClock != None:
print('PTP clock obtained')
else:
print('PTP failed to obtain clock')
sys.exit()
def ptpstats(domain, stats, userdata):
t = stats.get_name()
if t == GstNet.PTP_STATISTICS_NEW_DOMAIN_FOUND:
print('PTP new domain found')
elif t == GstNet.PTP_STATISTICS_BEST_MASTER_CLOCK_SELECTED:
print("PTP master clock selected")
elif t == GstNet.PTP_STATISTICS_PATH_DELAY_MEASURED:
print('PTP path delay measured')
elif t == GstNet.PTP_STATISTICS_TIME_UPDATED:
print('PTP time updated')
if ptpClock.is_synced():
calcOffset = (round((ptpClock.get_time()) * (48 / 1000000)) & 0xffffffff)
currentOffset = pipeline.get_by_name('rtppay').get_property("timestamp")
diff = (currentOffset - calcOffset)
print("Difference of", diff, "samples")
if diff < 0:
pipeline.set_state(Gst.State.PAUSED)
pipeline.set_state(Gst.State.READY)
calcOffset = (round((ptpClock.get_time()) * (48 / 1000000)) & 0xffffffff) - diff + 96
pipeline.get_by_name('rtppay').set_property("timestamp-offset", calcOffset)
pipeline.set_state(Gst.State.PLAYING)
return True
GstNet.ptp_statistics_callback_add(ptpstats, None)
print('PTP Syncing to Master')
ptpClock.wait_for_sync(Gst.CLOCK_TIME_NONE)
print('PTP successfully synced to Master')
pipeline = Gst.parse_launch(pipelineString)
pipeline.use_clock(ptpClock)
# calculate intial offset (gstreamer uses random offset as default)
ptpOffset = (round((ptpClock.get_time()) * (48000 / 1000000000)) & 0xffffffff) # convert ptp nanosecond timestamp to mediaclock add 72 (1.5ms) as offset for time alignment
# ptpOffset = (round((ptpClock.get_time()) * (48000 / 1000000000)) & 0xffffffff) + round(20*48) # convert ptp nanosecond timestamp to mediaclock add 72 (1.5ms) as offset for time alignment
pipeline.get_by_name('rtppay').set_property("timestamp-offset", ptpOffset)
pipeline.set_state(Gst.State.PLAYING)
print('starting mainloop')
mainloop.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment