"""Own modulo RTP-clock mapping with one fictional sender-report anchor.
Assumes offset within half a 32-bit cycle and constant declared clock.
Not a full RTCP parser, NTP synchronizer or delay estimator.
"""
from fractions import Fraction as F
import json
MOD=2**32
def signed_delta(timestamp,reference):
    d=(timestamp-reference+MOD//2)%MOD-MOD//2
    if d==-MOD//2:raise ValueError('half-cycle direction ambiguous')
    return d
def map_time(timestamp,ssrc,anchor,rate):
    if anchor is None:return None
    if ssrc!=anchor['ssrc']:raise ValueError('anchor source differs')
    if rate<=0:raise ValueError('positive declared RTP clock required')
    return F(anchor['reference_seconds'])+F(signed_delta(timestamp,anchor['rtp']),rate)
def demo():
    first={'ssrc':'owned-demo','rtp':MOD-160,'reference_seconds':'1000'}
    packet=(first['rtp']+320)%MOD;mapped=map_time(packet,'owned-demo',first,8000)
    second={'ssrc':'owned-demo','rtp':(first['rtp']+8000)%MOD,'reference_seconds':'1001'}
    estimated=F(signed_delta(second['rtp'],first['rtp']),F(second['reference_seconds'])-F(first['reference_seconds']))
    assert packet==160 and mapped==F(25001,25) and estimated==8000
    assert map_time(packet,'owned-demo',None,8000) is None
    negatives=[]
    for name,call in [('different_source',lambda:map_time(packet,'other',first,8000)),('zero_clock',lambda:map_time(packet,'owned-demo',first,0)),('half_cycle',lambda:signed_delta((first['rtp']+MOD//2)%MOD,first['rtp']))]:
        try:call()
        except ValueError:negatives.append(name)
        else:raise AssertionError(name)
    return {'anchor':first,'packet_timestamp':packet,'naive_unsigned_subtraction':packet-first['rtp'],'nearest_signed_modulo_delta_ticks':signed_delta(packet,first['rtp']),'declared_clock_hz':8000,'mapped_reference_seconds_fraction':str(mapped),'mapped_reference_seconds':float(mapped),'second_authored_anchor':second,'clock_from_two_ideal_anchors_hz':str(estimated),'missing_anchor_time':None,'negative_cases_rejected':negatives,'less_than_half_cycle_offset_assumed':True,'real_UTC_accuracy_or_delay_or_NTP_sync_checked':False}
if __name__=='__main__':print(json.dumps(demo(),ensure_ascii=False,indent=2))
