{"id":23444082,"url":"https://github.com/sevagh/jitters","last_synced_at":"2025-10-25T11:02:16.882Z","repository":{"id":82225468,"uuid":"201631404","full_name":"sevagh/jitters","owner":"sevagh","description":"RTP jitter buffer implementation written in Rust with example sender and receiver programs","archived":false,"fork":false,"pushed_at":"2019-09-09T19:10:50.000Z","size":62,"stargazers_count":12,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-13T17:55:52.150Z","etag":null,"topics":["audio-streaming","jitter-correction","packet-loss","rtp-streaming"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sevagh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-08-10T13:06:09.000Z","updated_at":"2025-05-29T22:15:43.000Z","dependencies_parsed_at":"2023-04-27T21:06:12.379Z","dependency_job_id":null,"html_url":"https://github.com/sevagh/jitters","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sevagh/jitters","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fjitters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fjitters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fjitters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fjitters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sevagh","download_url":"https://codeload.github.com/sevagh/jitters/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sevagh%2Fjitters/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280943396,"owners_count":26417747,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-25T02:00:06.499Z","response_time":81,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio-streaming","jitter-correction","packet-loss","rtp-streaming"],"created_at":"2024-12-23T18:26:32.517Z","updated_at":"2025-10-25T11:02:16.877Z","avatar_url":"https://github.com/sevagh.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jitters\n\njitters is an incomplete RTP jitter buffer demo, written in Rust. What follows is a description of the various subcomponents of jitters based on the directories.\n\n### src\n\nrtp.rs contains some structs for working with a very lean subset of RTP:\n\n* RTP without extensions, custom payload types, or csrcs\n* The only supported payload types are 44100Hz L16 uncompressed audio mono and stereo. This means resampling to 44100 is required.\n* The marker bit of the header is used to mark the end of a stream (more as a personal convenience, so I know I can start playing the audio stream)\n* The initial sequence and timestamp are selected randomly, as recommended by the RFC\n    * The sequence is incremented by 1, the timestamp is incremented by the number of samples sent in the packet (i.e. `JITTERS_MAX_PACKET_SIZE - size_of::\u003cRtpHeader\u003e() = 1400 - 12 = 1388`). The timestamp can be converted to `ms` by the receiver using the sample rate\n\nI wrote the bulk of the code from [the original RFC](https://tools.ietf.org/html/rfc3550). I've also implemented waveform substitution for [packet loss concealment](https://en.wikipedia.org/wiki/Packet_loss_concealment#PLC_techniques).\n\n### examples\n\n#### wav_sender\n\n`wav_sender.rs` uses the [hound](https://github.com/ruuda/hound/) crate to convert a WAV file to an outgoing RTP stream. The stream is recognized by wireshark and ffplay.\n\nIn one command prompt, run `ffplay`:\n\n```\nsevagh:jitters $ ffplay -hide_banner rtp://@127.0.0.1:1337\n[rtp @ 0x7f011c000b80] Guessing on RTP content - if not received properly you need an SDP file describing it\nInput #0, rtp, from 'rtp://@127.0.0.1:1337':\n  Duration: N/A, start: 0.000000, bitrate: 1411 kb/s\n    Stream #0:0: Audio: pcm_s16be, 44100 Hz, 2 channels, s16, 1411 kb/s\n[rtp @ 0x7f011c000b80] jitter buffer full=    0KB sq=    0B f=0/0\n[rtp @ 0x7f011c000b80] RTP: missed 443 packets\n[rtp @ 0x7f011c000b80] jitter buffer full=    0KB sq=    0B f=0/0\n[rtp @ 0x7f011c000b80] RTP: missed 20 packets\n[rtp @ 0x7f011c000b80] jitter buffer full=    0KB sq=    0B f=0/0\n[rtp @ 0x7f011c000b80] RTP: missed 1 packets\n  63.77 M-A:  0.000 fd=   0 aq=  584KB vq=    0KB sq=    0B f=0/0\n```\n\nIn another, run the wav_sender example:\n\n```\nsevagh:jitters $ cargo run --example wav_sender '127.0.0.1:13337' '127.0.0.1:1337' 188692__arseniiv__pianoa-100bpm.wav\n    Finished dev [unoptimized + debuginfo] target(s) in 0.03s\n     Running `target/debug/examples/wav_sender '127.0.0.1:13337' '127.0.0.1:1337' 188692__arseniiv__pianoa-100bpm.wav`\nSent samples at timestamp 0.0ms with RTP over UDP to 127.0.0.1:1337\nSent samples at timestamp 7.8684807256235825ms with RTP over UDP to 127.0.0.1:1337\nSent samples at timestamp 15.736961451247165ms with RTP over UDP to 127.0.0.1:1337\nSent samples at timestamp 23.605442176870746ms with RTP over UDP to 127.0.0.1:1337\n```\n\n![ffplay](.github/ffplay.png)\n\n### wav_receiver, wav_jitter_receiver\n\nSimilar to the above send/receive test with `ffplay`, run the receiver examples to listen to the WAV file:\n\n\n```\nsevagh:jitters $ cargo run --example wav_jitter_receiver '127.0.0.1:1337'\n...\nReceived 1400 bytes from 127.0.0.1:13337\nStream ended - playing audio...\nCorrected 0 out-of-order packets\nYielding audio slice for sequence 0, timestamp 0.0ms\nYielding audio slice for sequence 1, timestamp 7.8684807256235825ms\nYielding audio slice for sequence 2, timestamp 15.736961451247165ms\nYielding audio slice for sequence 3, timestamp 23.60544217687075ms\n...\nYielding audio slice for sequence 2437, timestamp 19175.48752834467ms\nYielding audio slice for sequence 2438, timestamp 19183.356009070296ms\nYielding audio slice for sequence 2439, timestamp 19191.224489795917ms\naudio done, exiting program\n```\n\nIn another, run the wav_sender example:\n\n```\nsevagh:jitters $ cargo run --example wav_sender '127.0.0.1:13337' '127.0.0.1:1337' 188692__arseniiv__pianoa-100bpm.wav\n    Finished dev [unoptimized + debuginfo] target(s) in 0.03s\n     Running `target/debug/examples/wav_sender '127.0.0.1:13337' '127.0.0.1:1337' 188692__arseniiv__pianoa-100bpm.wav`\nSent samples at timestamp 0.0ms with RTP over UDP to 127.0.0.1:1337\nSent samples at timestamp 7.8684807256235825ms with RTP over UDP to 127.0.0.1:1337\nSent samples at timestamp 15.736961451247165ms with RTP over UDP to 127.0.0.1:1337\nSent samples at timestamp 23.605442176870746ms with RTP over UDP to 127.0.0.1:1337\n...\nSent samples at timestamp 19175.487528344514ms with RTP over UDP to 127.0.0.1:1337\nSent samples at timestamp 19183.35600907014ms with RTP over UDP to 127.0.0.1:1337\nEnd... set the marker bit\nSent samples at timestamp 19191.224489795764ms with RTP over UDP to 127.0.0.1:1337\n```\n\n### testing packet loss concealment\n\nI my [XDP tool](https://github.com/sevagh/ape) to intercept and randomly drop ~10% of UDP packets, and ran a sender + jitter receiver to test the PLC. The audio sounds choppy, given that waveform correction is not perfect, but plays in its entirety:\n\n```\nYielding audio slice for sequence 2436, timestamp 19167.619047619046ms\nYielding audio slice for sequence 2437, timestamp 19167.619047619046ms\nYielding audio slice for sequence 2438, timestamp 19167.619047619046ms\nYielding audio slice for sequence 2439, timestamp 19191.224489795917ms\naudio done, exiting program\nJitter stream stats: \"corrected 0 out-of-order packets, concealed 244 lost packets\"\n```\n\nWe can see from the timestamps above that seqs 2438 and 2437 are copies of the original/correct seq 2436.\n\n### testing jitter correction\n\nSimilar to the PLC testing, I used my XDP tool ape to scramble 2%\\* of incoming UDP packets on the jitters example port, which tests both the jitter correction and PLC (since the last packet might be received out of order):\n\n```\nYielding audio slice for sequence 2439, timestamp 19191.224489795917ms\naudio done, exiting program\nJitter stream stats: \"corrected 11 out-of-order packets, concealed 61 lost packets\"\n```\n\nThe audio sounds coherent, but again with some glitchiness from the imperfect PLC.\n\n\\*: In reality, more than 2%, since there's a feedback scrambling effect and packets can get randomly delayed multiple times\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsevagh%2Fjitters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsevagh%2Fjitters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsevagh%2Fjitters/lists"}