<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[MeshLive]]></title><description><![CDATA[MeshLive]]></description><link>https://meshlive.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 30 Oct 2026 09:44:08 GMT</lastBuildDate><atom:link href="https://meshlive.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[MeshLive 0.4.0: Bluetooth Calls Across Phones and PCs]]></title><description><![CDATA[How MeshLive 0.4.0 brings offline Bluetooth calls to Android, Windows and Mac, with multi-hop relays, audio latency fixes and privacy improvements.]]></description><link>https://meshlive.hashnode.dev/meshlive-0-4-0-bluetooth-calls-across-phones-and-pcs</link><guid isPermaLink="true">https://meshlive.hashnode.dev/meshlive-0-4-0-bluetooth-calls-across-phones-and-pcs</guid><category><![CDATA[Android]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[iOS]]></category><category><![CDATA[macOS]]></category><category><![CDATA[Windows]]></category><category><![CDATA[communication]]></category><dc:creator><![CDATA[Gautam Gupta]]></dc:creator><pubDate>Tue, 27 Oct 2026 22:28:46 GMT</pubDate><content:encoded><![CDATA[<p>MeshLive is an open-source project for voice/video calls and live broadcasts over Bluetooth, without internet. With Android 0.4.0-beta and new Windows and macOS implementations, the project is moving beyond phone-to-phone communication. Here's how its transport choices, audio scheduling, relay architecture and privacy design evolved through testing.</p>
<p><a href="https://www.youtube.com/watch?v=47-NwaFXhkQ">Demo Video</a></p>
<h2>Why build video calls over Bluetooth?</h2>
<p>The idea behind MeshLive was to let nearby devices communicate using the radios they already have, even when normal internet connectivity isn't available.</p>
<p>Offline text messaging was already possible. I wanted to experiment with extending that to live audio, video calling and broadcasting.</p>
<p>The first challenge wasn't creating a calling interface. It was getting continuous, time-sensitive media through a connection with very limited and inconsistent throughput.</p>
<p>A video file can take longer to transfer without causing much trouble. A live conversation cannot.</p>
<p>If the network falls behind, audio delay becomes noticeable quickly.</p>
<p>That difference shaped many of the later architectural decisions.</p>
<h2>How does MeshLive send video without internet?</h2>
<p>MeshLive uses Bluetooth Low Energy for nearby device discovery and Bluetooth connections for control and media transfer.</p>
<p>On Android, the sender pipeline is roughly:</p>
<ol>
<li>Capture video through Camera2.</li>
<li>Encode it using VP9 or AV1.</li>
<li>Encode microphone audio using Opus.</li>
<li>Packetize audio and video using shared presentation timestamps.</li>
<li>Encrypt and authenticate the session.</li>
<li>Send the packets across the selected Bluetooth transport.</li>
<li>Decode and play them on the receiving device.</li>
</ol>
<p>For livestreams, a receiving device can also forward the stream to another device.</p>
<p>The app doesn't need an internet server to handle discovery or media transfer. Participating devices establish their own local connections.</p>
<h2>Why support three Bluetooth transports?</h2>
<p>No single Android Bluetooth transport worked equally well across the phones I tested.</p>
<p>MeshLive therefore supports BLE GATT, LE L2CAP CoC and Bluetooth Classic RFCOMM.</p>
<p><strong>GATT</strong> became the default because it has broad compatibility and doesn't require pairing.</p>
<p>But its behaviour also exposed one of the biggest audio latency problems: a large media write could keep smaller audio packets waiting.</p>
<p><strong>L2CAP CoC</strong> offers a byte-stream interface with credit-based flow control, but device compatibility has been a problem. Certain Android versions and vendor stacks have channel lifecycle issues or unreliable setup.</p>
<p><strong>Bluetooth Classic RFCOMM</strong> can deliver more throughput on suitable phone combinations, but normally requires pairing and has different privacy characteristics.</p>
<p>Rather than silently switching every session to whichever transport appears fastest, the application keeps transport choices explicit.</p>
<p>That makes debugging and comparing phone combinations much easier.</p>
<h2>How did video frames end up delaying audio?</h2>
<p>Initially, audio and video could compete for the same send path.</p>
<p>When a large video frame occupied that path, audio accumulated behind it.</p>
<p>Adding queue capacity addressed overflow but didn't fix the time audio spent waiting.</p>
<p>I tested two related changes.</p>
<p><strong>Fragment interleaving:</strong> Large video frames are split into smaller authenticated pieces. Waiting audio can be transmitted between those pieces rather than waiting for the complete video frame.</p>
<p><strong>Speech-led playback:</strong> Late video is prevented from increasing the shared call playback delay indefinitely. The tradeoff is that video can lag speech rather than forcing speech to wait for video.</p>
<p>On one recorded Samsung M31/ASUS GATT comparison, fixed-size fragments reduced p95 audio queue age from 457 ms to 140 ms, with roughly 4% framing overhead.</p>
<p>Adding speech-led playback in a separate test also reduced the median shared playback delay.</p>
<p>Those improvements are specific to the measured development builds and configurations. The test suite still identified other problems, including weak-device video performance.</p>
<p>That's an important distinction: improving one bottleneck doesn't mean the complete video call has passed every quality target.</p>
<p>The full A/B record is <a href="https://github.com/MeshLive/meshlive-android/blob/main/docs/dev/decisions/0040-call-head-of-line-ab-2026-10-11-build-958c8a53.md">public on GitHub</a>.</p>
<h2>How do calls and livestreams travel through other devices?</h2>
<p>MeshLive uses local relay relationships instead of requiring one device to maintain a global view of the entire network.</p>
<p>For a livestream, a device can receive the stream from its parent and forward the media to a downstream device.</p>
<p>Each relay deals with its immediate connections. It doesn't need to know every device in the network.</p>
<p>The stream is encoded once at the source. A forwarding device can pass along the encrypted media rather than encoding another video stream from scratch.</p>
<p>That saves processing work, but it doesn't eliminate the Bluetooth airtime cost.</p>
<p>Each relay still needs to receive and transmit.</p>
<p>For a real-time call, the problem becomes bidirectional. Both endpoints produce media, and the intermediate devices have to forward traffic in both directions.</p>
<p>The Android 0.4.0-beta release introduces experimental private-call relaying, enabled manually under Settings &gt; Advanced.</p>
<p>The intermediate devices forward encrypted media without holding the endpoints' content keys.</p>
<p>There are also hop-local admission checks and overload controls, because a device shouldn't continue accepting downstream traffic when it cannot deliver it.</p>
<p>It's a useful step toward longer-range communication, but it isn't unlimited range. Each additional hop introduces more opportunities for delay, congestion and disconnection.</p>
<h2>What made desktop support difficult?</h2>
<p>Extending MeshLive beyond Android wasn't simply a matter of reproducing the interface.</p>
<p>Windows, macOS and iOS expose different Bluetooth APIs, advertising capabilities and connection roles.</p>
<p>For example, Android can use custom manufacturer data in BLE advertisements.</p>
<p>The Apple discovery implementation has to work within CoreBluetooth's advertising restrictions, so it represents compact discovery information through a service UUID instead.</p>
<p>Android then needs to recognize and decode that format.</p>
<p>The advertisement is only a discovery hint. It isn't treated as proof of identity; authentication still happens after connection.</p>
<p>Background advertising on iOS also behaves differently from Android, making discovery less straightforward when the app isn't in the foreground.</p>
<p>Desktop implementations introduce their own connection-role and compatibility constraints.</p>
<p>The goal is to keep the same authenticated communication protocol while adapting to what each operating system actually allows.</p>
<p>The current platform status is:</p>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Availability</th>
</tr>
</thead>
<tbody><tr>
<td>Android</td>
<td>Downloadable 0.4.0-beta APKs</td>
</tr>
<tr>
<td>Windows</td>
<td>Experimental beta ZIP</td>
</tr>
<tr>
<td>macOS</td>
<td>Native implementation, build from source</td>
</tr>
<tr>
<td>iOS</td>
<td>Early implementation, build from source; more hardware testing needed</td>
</tr>
</tbody></table>
<p>The Windows-to-phone demo is one of the clearest examples of the progress so far.</p>
<h2>How is privacy handled without accounts?</h2>
<p>MeshLive doesn't depend on an online account or identity server.</p>
<p>The Android implementation generates a persistent cryptographic identity and uses authenticated handshakes with fresh ephemeral keys.</p>
<p>Communication has two layers of protection: authentication and encryption between neighbouring devices, plus separate end-to-end protection for the original endpoints.</p>
<p>That distinction matters for relaying.</p>
<p>A relay can forward traffic and observe metadata such as timing and packet sizes, but it shouldn't be able to decrypt the contents of a private call.</p>
<p>Android also uses rotating discovery identifiers, changing every 15 minutes by default.</p>
<p>When an unknown caller connects, the initial identity is temporary rather than immediately exposing the callee's long-term identity.</p>
<p>The latest release tightens the caller verification process before audio/video is transmitted and applies blocking rules to livestreams and relays.</p>
<p>There are still limitations.</p>
<p>Bluetooth advertisements remain observable. Some operating systems expose device names or addresses. Livestreams reveal information that private calls do not, and traffic metadata isn't hidden.</p>
<p>The protocol hasn't received an independent security audit.</p>
<p>These limitations are documented in <a href="https://github.com/MeshLive/meshlive-android/blob/main/docs/PRIVACY.md">MeshLive's privacy reference</a>.</p>
<h2>What did testing across different phones reveal?</h2>
<p>Possibly the biggest lesson has been that Bluetooth compatibility cannot be assumed from a device supporting Bluetooth on paper.</p>
<p>I've tested with phones from Samsung, Xiaomi, ASUS and OPPO running different Android versions.</p>
<p>The same application can encounter very different connection behaviour, throughput and codec performance across these devices.</p>
<p>A change that improves one phone pair might have no effect on another.</p>
<p>That led to a more measurement-driven development process:</p>
<ul>
<li>Reproduce the failure on actual devices.</li>
<li>Identify the affected stage: capture, encode, queue, transport, receive, decode or playback.</li>
<li>Compare changes under the same requested media settings.</li>
<li>Keep experimental alternatives switchable.</li>
<li>Record measurements, unsuccessful approaches and known limitations.</li>
</ul>
<p>Most of the code has been developed with Claude Code's assistance, but the physical measurements and engineering decisions remain essential. AI-generated code passing unit tests cannot establish how a particular Bluetooth controller will behave.</p>
<p>The public <a href="https://github.com/MeshLive/meshlive-android/blob/main/docs/dev/decisions/README.md">decision log</a> and <a href="https://github.com/MeshLive/meshlive-android/blob/main/docs/devices.md">device compatibility table</a> are intended to make that work reproducible.</p>
<h2>What's next for MeshLive?</h2>
<p>The immediate priorities are improving compatibility, connection reliability and latency, particularly for relayed calls and cross-platform communication.</p>
<p>There's also work planned around additional communication features and using Wi-Fi Direct/Aware as higher-bandwidth options where supported, while retaining Bluetooth for nearby discovery and control.</p>
<p>Those are future directions, not completed features.</p>
<p>For now, MeshLive remains an experimental beta. The aim is to keep testing, documenting problems and improving the implementation across more devices.</p>
<h2>Frequently asked questions</h2>
<h3>Can MeshLive make calls without Wi-Fi or mobile data?</h3>
<p>Yes. Compatible nearby devices running MeshLive can establish voice/video calls over Bluetooth without an internet connection. Bluetooth must remain enabled.</p>
<h3>Can a call reach someone outside direct Bluetooth range?</h3>
<p>Android 0.4.0-beta has experimental call relaying through intermediate MeshLive phones. It must be enabled manually and is not guaranteed to work on every device combination.</p>
<h3>Is MeshLive available for Windows, macOS and iPhone?</h3>
<p>Android APKs and an experimental Windows beta are available. The native Mac and early iPhone implementations currently require building from source. There is no public iOS App Store or TestFlight binary yet.</p>
<hr />
<h2>Try MeshLive</h2>
<ul>
<li><a href="https://github.com/MeshLive/meshlive-android/releases/latest"><strong>Android 0.4.0-beta</strong></a></li>
<li><a href="https://github.com/MeshLive/meshlive-windows"><strong>Windows beta</strong></a></li>
<li><a href="https://github.com/MeshLive/meshlive-apple"><strong>macOS and iPhone source</strong></a></li>
<li><a href="https://github.com/MeshLive"><strong>Project repositories</strong></a></li>
</ul>
<p>MeshLive is free, open source and licensed under GPL-3.0-or-later.</p>
<p>If you're interested in Bluetooth networking, Android development, media codecs or cross-platform communication, I'd appreciate feedback, device test results and contributions.</p>
]]></content:encoded></item><item><title><![CDATA[Building MeshLive: Video calls and live broadcasts without Internet]]></title><description><![CDATA[How I built MeshLive, an open-source Android app for offline encrypted voice/video calls and livestreams between phones over a Bluetooth mesh.]]></description><link>https://meshlive.hashnode.dev/building-meshlive-video-calls-and-live-broadcasts-without-internet</link><guid isPermaLink="true">https://meshlive.hashnode.dev/building-meshlive-video-calls-and-live-broadcasts-without-internet</guid><category><![CDATA[Open Source]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[bluetooth]]></category><category><![CDATA[privacy]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Gautam Gupta]]></dc:creator><pubDate>Thu, 22 Oct 2026 21:55:51 GMT</pubDate><content:encoded><![CDATA[<p>MeshLive is a free, open-source Android app for nearby voice calls, video calls, and live broadcasts over Bluetooth, without internet, a SIM card, or an account. This article explains the transport tradeoffs, audio queue fixes, iPhone discovery workaround, and relay limits I encountered while building it.</p>
<p>Video: <a href="https://youtu.be/I9_oEuNFcQE?si=iBAZGOC00_8EC9VW">https://youtu.be/I9_oEuNFcQE?si=iBAZGOC00_8EC9VW</a></p>
<p>I started working on it because I wanted nearby phones to keep communicating when internet access wasn’t available. Voice and live video felt worth exploring alongside offline messaging.</p>
<p>Getting a picture onto another phone was only part of the work. Making a conversation usable meant dealing with slow links, video blocking audio, different Bluetooth stacks, and an iPhone that couldn’t announce itself the same way an Android phone could.</p>
<p>Here are a few of the problems behind the app.</p>
<h2>Which Bluetooth transports does MeshLive use?</h2>
<p>MeshLive supports BLE GATT, LE L2CAP CoC, and Bluetooth Classic RFCOMM. GATT is the default, while CoC remains experimental. Each path involves different tradeoffs in phone compatibility, pairing, throughput, and how long media writes occupy the connection.</p>
<table>
<thead>
<tr>
<th>Transport</th>
<th>Why it is useful</th>
<th>Main tradeoff</th>
</tr>
</thead>
<tbody><tr>
<td><strong>BLE GATT</strong></td>
<td>Works across many phones without pairing</td>
<td>Large writes can keep other traffic waiting</td>
</tr>
<tr>
<td><strong>LE L2CAP CoC</strong></td>
<td>Provides a byte stream with credit-based flow control</td>
<td>Some phone stacks have serious implementation bugs</td>
</tr>
<tr>
<td><strong>Bluetooth Classic RFCOMM</strong></td>
<td>Offers higher throughput on suitable phone pairs</td>
<td>Generally needs an initial pairing step</td>
</tr>
</tbody></table>
<p>A connection working once also doesn’t tell you much about what happens after repeated calls, a Bluetooth restart, or a change in which phone initiates the connection. That has made testing actual phone pairs a large part of the work.</p>
<h2>Why did video frames make the audio wait?</h2>
<p>A large video write could occupy the connection while smaller audio packets waited behind it. I had to address two problems: audio filling the shared queue, and the writer having no opportunity to send that audio until the video write finished.</p>
<p>While a large video frame was being written, audio accumulated in the shared queue. The queue filled, video was dropped, and recovery requested a keyframe. That larger recovery frame could then block the writer again.</p>
<p>The first change was to give audio its own queue headroom, separate from the video budget. This addressed the overflow problem.</p>
<p>But audio could still wait behind a video write already in progress. In some GATT tests, that wait was roughly <strong>0.6–1.2 seconds behind a single frame</strong>. Having room in the queue didn’t make the connection available any sooner.</p>
<p>This is head-of-line blocking: a large item at the front prevents smaller, more urgent items from moving.</p>
<p>The next change was to split larger video frames into roughly <strong>2 KB authenticated fragments</strong>, with opportunities to send waiting audio between them. That gives speech a chance to move before the entire video frame has finished.</p>
<p>In the recorded Samsung M31/ASUS GATT comparison, the p95 windowed audio queue-age metric fell from about <strong>457 ms to 140 ms</strong> with fixed-size fragments, at roughly <strong>4% framing overhead</strong>.</p>
<p>That measures time waiting in the sender’s queue. It doesn’t establish the complete delay between speaking into one phone and hearing the other. Those test runs also missed other quality targets, so there is still work to do.</p>
<h2>Why did the receiver also delay speech?</h2>
<p>Audio and video shared a playback clock. When video arrived late, the receiver could increase their shared buffering delay and keep speech waiting, even after the audio had moved through the sender’s queue more quickly.</p>
<p>I added a speech-led playback policy that limits how much extra delay late video can introduce. The tradeoff is that the picture may lag speech more during a bad video burst.</p>
<p>The useful approach involved both ends of the connection: create opportunities to send audio, then limit how much late video can delay its playback.</p>
<p>Fixed-size fragments and speech-led playback are now enabled for normal direct GATT calls when the peers support them.</p>
<p>There is also an experimental alternative that sizes fragments around a target amount of transmission time. A fixed number of bytes takes very different amounts of time on fast and slow links. Smaller fragments create more scheduling opportunities, but add framing overhead. That alternative remains off by default.</p>
<h2>How do Android and iPhone discover each other?</h2>
<p>MeshLive uses Bluetooth Low Energy for discovery. Android advertises manufacturer data, while the iPhone implementation packs its announcement into one 128-bit service UUID. Android recognizes that compact announcement with a masked UUID scan filter, allowing the phones to find each other before connecting.</p>
<p>After discovery, the connection can proceed through the existing transport and authenticated handshake. The announcement is a hint about a nearby peer; authentication still happens when connecting.</p>
<p>There are limits: this iPhone discovery path currently requires the app to be in the foreground, and it advertises one role at a time.</p>
<p>The iOS implementation is still early, with no published consumer binary yet.</p>
<h2>Why does relaying consume more Bluetooth capacity?</h2>
<p>A nearby phone can extend a broadcast’s reach by forwarding it. That phone has to receive and transmit using the same radio, so each extra link consumes airtime.</p>
<p>Forwarders pass along the existing encrypted media without re-encoding it for the next viewer. That saves processing, but the transmission cost remains. Short chains and modest media rates are the practical focus.</p>
<p>Broadcast forwarding and private-call relaying also have different maturity levels. Call relaying remains experimental and off by default.</p>
<h2><strong>What is the current status of MeshLive?</strong></h2>
<p>Recent beta work has added audio-only live broadcasts, improved the calling experience, enabled initial Android–iPhone interoperability, and addressed slow-link and Bluetooth reconnect problems.</p>
<p>After the latest beta update, I’ve also <strong>finally submitted the closed alpha to Google Play for review</strong>.</p>
<p>Now I wait for approval so I can move into the next stage of testing and work toward a public Play Store release.</p>
<p>The Android APK is already available for anyone who wants to try it. It is still beta software, and the project hasn’t had an independent security audit yet.</p>
<p>If you try it with two phones, a useful report is the phone models, Android versions, what you tried, and where it worked or failed. Different phone combinations are especially valuable.</p>
<p>And if you work with Android Bluetooth, real-time audio/video, or iOS networking, I’d appreciate a look at the implementation and the decisions behind it.</p>
<ul>
<li><a href="https://github.com/MeshLive/meshlive-android"><strong>Source code and documentation</strong></a></li>
<li><a href="https://github.com/MeshLive/meshlive-android/releases/latest"><strong>Download the Android beta</strong></a></li>
<li><a href="https://github.com/MeshLive/meshlive-ios"><strong>Early iOS implementation</strong></a></li>
</ul>
]]></content:encoded></item></channel></rss>