Summary of the PulseAudio/Bluetooth discussions at the BlueZ meeting in Helsinki
A message that is not in the archive (sent off-list or not yet received): [email protected]
Luiz Augusto von Dentz
maintainer---------- Forwarded message ----------
From: Lennart Poettering <[email protected]>
Date: Wed, Jun 4, 2008 at 5:07 PM
Subject: Summary of the PulseAudio/Bluetooth discussions at the BlueZ meeting in Helsinki
To: [email protected], [email protected], [email protected], PulseAudio Discussion List <[email protected]>
Heya!
(Luiz, please forward this to the bluez ML!)
So, here's my summary of the grand plan to get PA and BT working well together. This is basically the result of the audio discussions during the BlueZ Meeting we had in Helsinki that ended today. Especially for you, Jo=E3o-Paulo, this might be very interesting!
There should be two new PA modules, module-bluetooth-discover and module-bluetooth-device. The former will use D-Bus to connect to the BlueZ system services and whenever a new BT audio devices appears load one m-bt-device instance for it. (as a side node: in contrast to linux kernel modules, PA modules can be loaded more than once at the same time).
The latter, m-bt-device, then connects to the BlueZ audio service via one BlueZ specific well known unix socket, configures a connection to the BT device, gets a BT socket fd passed in via the unix socket and then hands this over to its RT thread. The code for this would actually be very similar to module-esd-sink which we ship already. m-esd-s is a sink for PA that hands data to an existing EsounD server. It's basic structure is very similar: we first configure the ESD connection from the main thread via exchanging a few simple packets and then hand the actual communication socket to the RT thread for the actual work. The big difference in logic is mostly that the BT module needs to encode the audio data to SBC/SCO first, while the ESD module just spits raw PCM to the TCP stream. And then, the timing needs to be implemented differently.
Neither ESD nor A2DP/SCO provide any reasonable timing source. However, the TCP socket used by ESD provides flow control which we misuse for timing estimation. Unfortunately A2DP/SCO doesn't even provide that, so we need to roll our own timing all the time -- with the exception that for the SCO case we can probably deduce the remote clock from the time we get the recording packets flowing in. Since for A2DP we mostly lack recording support we don't have that clock and we have no other option than just using the raw local clock. The BT socket uses DGRAM/SEQPACKET as socket type, This should actually make things much easier than for the m-e-s case, since packet deserilization is much easier.
Since we only have a single socket for both recording and playback m-bt-device should register both a sink and a source at the same time and run it from the same RT thread. This is similar to what module-oss does, but different from e.g. the alsa modules which run the sink and the source from seperate threads.
The seperation of m-bt-discover and m-bt-device is very similar to the seperation between module-hal and module-alsa-{sink,source} or module-zeroconf-discover and module-tunnel.
To improve the timing estimation, the BlueZ team will add support for and SIOCOUTQ/SIOCINQ on the BT audio socket. This should tell us how many bytes are buffered locally. Of course, we still would have no idea how many bytes are buffered on the receivng side, but it would help us at least a bit to make our timing more reliable. Also, they want to add a new sockopt which would allow us to query the BT clock of the BT device. We can probably assume that the audio clock of the BT device is dependant or identical to the BT clock. This would allow us to make sure we stay in sync with the device as much as possible, even tough we still would have no idea about the actual latency.
The first step to implement is certainly m-bt-device without any fancy timing. The next step would be m-bt-discover and finally, the fancier timing estimation should be implemented. And finally the native volume support should be implemented.
m-bt-device would probably not use D-Bus itself at all. Only m-bt-discover would.
Eventually the btaudio unix socket should also be used to pass the key events from the bt head set. I'd need to add a simple subsystem to pass them on to the application then. Those keypresses would then be passed to the application inline via the audio API. On one hand I see that it would be good send the key events inline to make clear to which audio device they belong. OTOH I have a bad feeling about this because I don't want to add yet another key event subsystem, in addtion to XIE, the linux input stuff and even HAL. We need to think about this a bit more. (It is interesting to note, that having the sound server forward "pause" and "resume" request to applications is on the todo list anyway, to allow policy modules to request music playback stopping on imcoming phone calls and suchlike. So doing the full "audio key" set of keypresses would be a simple extension of that.)
Marcel, Johan, Luiz, Marc-Andr=E9, Claudio, did I miss anything?
Lennart
--
Lennart Poettering Red Hat, Inc.
lennart [at] poettering [dot] net ICQ# 11060553
http://0pointer.net/lennart/ GnuPG 0x1A015CC4
-- =
Luiz Augusto von Dentz
Engenheiro de Computa=E7=E3o
-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://sourceforge.net/services/buy/index.php
_______________________________________________
Bluez-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/bluez-devel
SGVsbG8gTGVubmFydCwgTHVpeiBhbmQgYWxsIG90aGVyIGRldnMhCgpGaXJzdCwgc2luY2UgSSB0 aGluayBzb21lIG1heSBub3Qga25vdyB3aGF0IGlzIHRoaXMgYWxsIGFib3V0Cihlc3BlY2lhbGx5 IFBBIHBlb3BsZSksIEkgd29ya2luZyBvbiBnZXR0aW5nIFB1bHNlQXVkaW8gdG8gc3VwcG9ydApi bHVldG9vdGggZGV2aWNlcywgYXMgYSBHU29DMjAwOCBwcm9qZWN0LiBUaGF0J3MgdGhlIHdheSBw ZW9wbGUgaW4KQmx1ZVogdGhpbmsgaXMgdGhlIGJlc3QgdG8gc3VwcG9ydCBBMkRQIChzdGVyZW8p IGF1ZGlvIGRldmljZXMgaW4KbGludXgsIHNpbmNlIEFMU0EgaXMgbm90IHdlbGwgc3VpdGVkIGZv ciBuZXR3b3JrIGRldmljZXMuCgpUaGFua3MgTGVubmFydCBmb3IgeW91ciBleHBsYW5hdGlvbnMs IGl0IGhlbHBlZCBtZSBhIGxvdCB0byB1bmRlcnN0YW5kCmhvdyBpdCBoYXZlIHRvIGJlIGRvbmUu CgpJJ3ZlIHN0YXJ0ZWQgd29ya2luZyBvbiBtb2R1bGUtYmx1ZXRvb3RoLWRpc2NvdmVyIGZpcnN0 LCBlc3BlY2lhbGx5CmJlY2F1c2UgSSBkb24ndCBoYXZlIGFueSBBMkRQIGRldmljZSBmb3IgdGVz dGluZyByaWdodCBub3cuIEknbGwgbWFrZQppdCBjYWxsIHNvbWUgc3R1YiB3aGlsZSB0aGUgZGV2 aWNlIG1vZHVsZSBpcyBub3Qgd29ya2luZy4gVGhlIGNvZGUKcmlnaHQgbm93IGRvZXMgdGhlIHNh bWUgYXMgYmx1ZXotdXRpbHMvaGNpZC9saXN0X2RldmljZXMsIG5vdyBJJ2xsCnN0YXJ0IHRvIGZp bHRlciB3aGF0IGFyZSB0aGUgZGV2aWNlcyBvZiBpbnRlcmVzdCBmb3IgUEEuIEkgaGF2ZW4ndApi ZWVuIGFibGUgdG8gdG91Y2ggdGhlIGNvZGUgc2luY2UgVGh1cnNkYXksIGJlY2F1c2Ugb2YgYSBz ZW1pbmFyIHRoYXQKSSBoYWQgdG8gcHJlc2VudCBhdCBzY2hvb2wsIGJ1dCB0aGlzIHdlZWsgSSds bCBiZSB3b3JraW5nIG9uIGl0LgoKLS0gCkpvw6NvIFBhdWxvIFJlY2hpIFZpdGEKTVNjIENvbXB1 dGVyIFNjaWVuY2UgU3R1ZGVudApDb21wdXRlciBFbmdpbmVlcgpJQyAvIFVuaWNhbXAKCmlyYy5m cmVlbm9kZS5uZXQ6IGpwcnZpdGEKCk9uIFdlZCwgSnVuIDQsIDIwMDggYXQgNTowNyBQTSwgTGVu bmFydCBQb2V0dGVyaW5nCjxsZW5uYXJ0QHBvZXR0ZXJpbmcubmV0PiB3cm90ZToKPiBIZXlhIQo+ Cj4gKEx1aXosIHBsZWFzZSBmb3J3YXJkIHRoaXMgdG8gdGhlIGJsdWV6IE1MISkKPgo+IFNvLCBo ZXJlJ3MgbXkgc3VtbWFyeSBvZiB0aGUgZ3JhbmQgcGxhbiB0byBnZXQgUEEgYW5kIEJUIHdvcmtp bmcgd2VsbAo+IHRvZ2V0aGVyLiBUaGlzIGlzIGJhc2ljYWxseSB0aGUgcmVzdWx0IG9mIHRoZSBh dWRpbyBkaXNjdXNzaW9ucyBkdXJpbmcKPiB0aGUgQmx1ZVogTWVldGluZyB3ZSBoYWQgaW4gSGVs c2lua2kgdGhhdCBlbmRlZCB0b2RheS4gRXNwZWNpYWxseSBmb3IKPiB5b3UsIEpvw6NvLVBhdWxv LCB0aGlzIG1pZ2h0IGJlIHZlcnkgaW50ZXJlc3RpbmchCj4KPiBUaGVyZSBzaG91bGQgYmUgdHdv IG5ldyBQQSBtb2R1bGVzLCBtb2R1bGUtYmx1ZXRvb3RoLWRpc2NvdmVyIGFuZAo+IG1vZHVsZS1i bHVldG9vdGgtZGV2aWNlLiBUaGUgZm9ybWVyIHdpbGwgdXNlIEQtQnVzIHRvIGNvbm5lY3QgdG8g dGhlCj4gQmx1ZVogc3lzdGVtIHNlcnZpY2VzIGFuZCB3aGVuZXZlciBhIG5ldyBCVCBhdWRpbyBk ZXZpY2VzIGFwcGVhcnMgbG9hZAo+IG9uZSBtLWJ0LWRldmljZSBpbnN0YW5jZSBmb3IgaXQuIChh cyBhIHNpZGUgbm9kZTogaW4gY29udHJhc3QgdG8gbGludXgKPiBrZXJuZWwgbW9kdWxlcywgUEEg bW9kdWxlcyBjYW4gYmUgbG9hZGVkIG1vcmUgdGhhbiBvbmNlIGF0IHRoZSBzYW1lCj4gdGltZSku Cj4KPiBUaGUgbGF0dGVyLCBtLWJ0LWRldmljZSwgdGhlbiBjb25uZWN0cyB0byB0aGUgQmx1ZVog YXVkaW8gc2VydmljZSB2aWEgb25lCj4gQmx1ZVogc3BlY2lmaWMgd2VsbCBrbm93biB1bml4IHNv Y2tldCwgY29uZmlndXJlcyBhIGNvbm5lY3Rpb24gdG8gdGhlCj4gQlQgZGV2aWNlLCBnZXRzIGEg QlQgc29ja2V0IGZkIHBhc3NlZCBpbiB2aWEgdGhlIHVuaXggc29ja2V0IGFuZCB0aGVuCj4gaGFu ZHMgdGhpcyBvdmVyIHRvIGl0cyBSVCB0aHJlYWQuIFRoZSBjb2RlIGZvciB0aGlzIHdvdWxkIGFj dHVhbGx5IGJlCj4gdmVyeSBzaW1pbGFyIHRvIG1vZHVsZS1lc2Qtc2luayB3aGljaCB3ZSBzaGlw IGFscmVhZHkuIG0tZXNkLXMgaXMgYQo+IHNpbmsgZm9yIFBBIHRoYXQgaGFuZHMgZGF0YSB0byBh biBleGlzdGluZyBFc291bkQgc2VydmVyLiBJdCdzIGJhc2ljCj4gc3RydWN0dXJlIGlzIHZlcnkg c2ltaWxhcjogd2UgZmlyc3QgY29uZmlndXJlIHRoZSBFU0QgY29ubmVjdGlvbiBmcm9tCj4gdGhl IG1haW4gdGhyZWFkIHZpYSBleGNoYW5naW5nIGEgZmV3IHNpbXBsZSBwYWNrZXRzIGFuZCB0aGVu IGhhbmQgdGhlCj4gYWN0dWFsIGNvbW11bmljYXRpb24gc29ja2V0IHRvIHRoZSBSVCB0aHJlYWQg Zm9yIHRoZSBhY3R1YWwgd29yay4gVGhlCj4gYmlnIGRpZmZlcmVuY2UgaW4gbG9naWMgaXMgbW9z dGx5IHRoYXQgdGhlIEJUIG1vZHVsZSBuZWVkcyB0byBlbmNvZGUKPiB0aGUgYXVkaW8gZGF0YSB0 byBTQkMvU0NPIGZpcnN0LCB3aGlsZSB0aGUgRVNEIG1vZHVsZSBqdXN0IHNwaXRzIHJhdwo+IFBD TSB0byB0aGUgVENQIHN0cmVhbS4gQW5kIHRoZW4sIHRoZSB0aW1pbmcgbmVlZHMgdG8gYmUgaW1w bGVtZW50ZWQKPiBkaWZmZXJlbnRseS4KPgo+IE5laXRoZXIgRVNEIG5vciBBMkRQL1NDTyBwcm92 aWRlIGFueSByZWFzb25hYmxlIHRpbWluZwo+IHNvdXJjZS4gSG93ZXZlciwgdGhlIFRDUCBzb2Nr ZXQgdXNlZCBieSBFU0QgcHJvdmlkZXMgZmxvdyBjb250cm9sCj4gd2hpY2ggd2UgbWlzdXNlIGZv ciB0aW1pbmcgZXN0aW1hdGlvbi4gVW5mb3J0dW5hdGVseSBBMkRQL1NDTyBkb2Vzbid0Cj4gZXZl biBwcm92aWRlIHRoYXQsIHNvIHdlIG5lZWQgdG8gcm9sbCBvdXIgb3duIHRpbWluZyBhbGwgdGhl IHRpbWUgLS0KPiB3aXRoIHRoZSBleGNlcHRpb24gdGhhdCBmb3IgdGhlIFNDTyBjYXNlIHdlIGNh biBwcm9iYWJseSBkZWR1Y2UgdGhlCj4gcmVtb3RlIGNsb2NrIGZyb20gdGhlIHRpbWUgd2UgZ2V0 IHRoZSByZWNvcmRpbmcgcGFja2V0cyBmbG93aW5nCj4gaW4uIFNpbmNlIGZvciBBMkRQIHdlIG1v c3RseSBsYWNrIHJlY29yZGluZyBzdXBwb3J0IHdlIGRvbid0IGhhdmUgdGhhdAo+IGNsb2NrIGFu ZCB3ZSBoYXZlIG5vIG90aGVyIG9wdGlvbiB0aGFuIGp1c3QgdXNpbmcgdGhlIHJhdyBsb2NhbCBj bG9jay4KPiBUaGUgQlQgc29ja2V0IHVzZXMgREdSQU0vU0VRUEFDS0VUIGFzIHNvY2tldCB0eXBl LCBUaGlzIHNob3VsZAo+IGFjdHVhbGx5IG1ha2UgdGhpbmdzIG11Y2ggZWFzaWVyIHRoYW4gZm9y IHRoZSBtLWUtcyBjYXNlLCBzaW5jZSBwYWNrZXQKPiBkZXNlcmlsaXphdGlvbiBpcyBtdWNoIGVh c2llci4KPgo+IFNpbmNlIHdlIG9ubHkgaGF2ZSBhIHNpbmdsZSBzb2NrZXQgZm9yIGJvdGggcmVj b3JkaW5nIGFuZCBwbGF5YmFjawo+IG0tYnQtZGV2aWNlIHNob3VsZCByZWdpc3RlciBib3RoIGEg c2luayBhbmQgYSBzb3VyY2UgYXQgdGhlIHNhbWUgdGltZQo+IGFuZCBydW4gaXQgZnJvbSB0aGUg c2FtZSBSVCB0aHJlYWQuIFRoaXMgaXMgc2ltaWxhciB0byB3aGF0IG1vZHVsZS1vc3MKPiBkb2Vz LCBidXQgZGlmZmVyZW50IGZyb20gZS5nLiB0aGUgYWxzYSBtb2R1bGVzIHdoaWNoIHJ1biB0aGUg c2luayBhbmQKPiB0aGUgc291cmNlIGZyb20gc2VwZXJhdGUgdGhyZWFkcy4KPgo+IFRoZSBzZXBl cmF0aW9uIG9mIG0tYnQtZGlzY292ZXIgYW5kIG0tYnQtZGV2aWNlIGlzIHZlcnkgc2ltaWxhciB0 byB0aGUKPiBzZXBlcmF0aW9uIGJldHdlZW4gbW9kdWxlLWhhbCBhbmQgbW9kdWxlLWFsc2Ete3Np bmssc291cmNlfSBvcgo+IG1vZHVsZS16ZXJvY29uZi1kaXNjb3ZlciBhbmQgbW9kdWxlLXR1bm5l bC4KPgo+IFRvIGltcHJvdmUgdGhlIHRpbWluZyBlc3RpbWF0aW9uLCB0aGUgQmx1ZVogdGVhbSB3 aWxsIGFkZCBzdXBwb3J0IGZvcgo+IGFuZCBTSU9DT1VUUS9TSU9DSU5RIG9uIHRoZSBCVCBhdWRp byBzb2NrZXQuIFRoaXMgc2hvdWxkIHRlbGwgdXMgaG93Cj4gbWFueSBieXRlcyBhcmUgYnVmZmVy ZWQgbG9jYWxseS4gT2YgY291cnNlLCB3ZSBzdGlsbCB3b3VsZCBoYXZlIG5vCj4gaWRlYSBob3cg bWFueSBieXRlcyBhcmUgYnVmZmVyZWQgb24gdGhlIHJlY2Vpdm5nIHNpZGUsIGJ1dCBpdCB3b3Vs ZAo+IGhlbHAgdXMgYXQgbGVhc3QgYSBiaXQgdG8gbWFrZSBvdXIgdGltaW5nIG1vcmUgcmVsaWFi bGUuIEFsc28sIHRoZXkKPiB3YW50IHRvIGFkZCBhIG5ldyBzb2Nrb3B0IHdoaWNoIHdvdWxkIGFs bG93IHVzIHRvIHF1ZXJ5IHRoZSBCVCBjbG9jawo+IG9mIHRoZSBCVCBkZXZpY2UuIFdlIGNhbiBw cm9iYWJseSBhc3N1bWUgdGhhdCB0aGUgYXVkaW8gY2xvY2sgb2YgdGhlCj4gQlQgZGV2aWNlIGlz IGRlcGVuZGFudCBvciBpZGVudGljYWwgdG8gdGhlIEJUIGNsb2NrLiBUaGlzIHdvdWxkIGFsbG93 Cj4gdXMgdG8gbWFrZSBzdXJlIHdlIHN0YXkgaW4gc3luYyB3aXRoIHRoZSBkZXZpY2UgYXMgbXVj aCBhcyBwb3NzaWJsZSwKPiBldmVuIHRvdWdoIHdlIHN0aWxsIHdvdWxkIGhhdmUgbm8gaWRlYSBh Ym91dCB0aGUgYWN0dWFsIGxhdGVuY3kuCj4KPiBUaGUgZmlyc3Qgc3RlcCB0byBpbXBsZW1lbnQg aXMgY2VydGFpbmx5IG0tYnQtZGV2aWNlIHdpdGhvdXQgYW55IGZhbmN5Cj4gdGltaW5nLiBUaGUg bmV4dCBzdGVwIHdvdWxkIGJlIG0tYnQtZGlzY292ZXIgYW5kIGZpbmFsbHksIHRoZSBmYW5jaWVy Cj4gdGltaW5nIGVzdGltYXRpb24gc2hvdWxkIGJlIGltcGxlbWVudGVkLiBBbmQgZmluYWxseSB0 aGUgbmF0aXZlIHZvbHVtZQo+IHN1cHBvcnQgc2hvdWxkIGJlIGltcGxlbWVudGVkLgo+Cj4gbS1i dC1kZXZpY2Ugd291bGQgcHJvYmFibHkgbm90IHVzZSBELUJ1cyBpdHNlbGYgYXQgYWxsLiBPbmx5 Cj4gbS1idC1kaXNjb3ZlciB3b3VsZC4KPgo+IEV2ZW50dWFsbHkgdGhlIGJ0YXVkaW8gdW5peCBz b2NrZXQgc2hvdWxkIGFsc28gYmUgdXNlZCB0byBwYXNzIHRoZSBrZXkKPiBldmVudHMgZnJvbSB0 aGUgYnQgaGVhZCBzZXQuIEknZCBuZWVkIHRvIGFkZCBhIHNpbXBsZSBzdWJzeXN0ZW0gdG8KPiBw YXNzIHRoZW0gb24gdG8gdGhlIGFwcGxpY2F0aW9uIHRoZW4uIFRob3NlIGtleXByZXNzZXMgd291 bGQgdGhlbiBiZQo+IHBhc3NlZCB0byB0aGUgYXBwbGljYXRpb24gaW5saW5lIHZpYSB0aGUgYXVk aW8gQVBJLiBPbiBvbmUgaGFuZCBJIHNlZQo+IHRoYXQgaXQgd291bGQgYmUgZ29vZCBzZW5kIHRo ZSBrZXkgZXZlbnRzIGlubGluZSB0byBtYWtlIGNsZWFyIHRvCj4gd2hpY2ggYXVkaW8gZGV2aWNl IHRoZXkgYmVsb25nLiBPVE9IIEkgaGF2ZSBhIGJhZCBmZWVsaW5nIGFib3V0IHRoaXMKPiBiZWNh dXNlIEkgZG9uJ3Qgd2FudCB0byBhZGQgeWV0IGFub3RoZXIga2V5IGV2ZW50IHN1YnN5c3RlbSwg aW4KPiBhZGR0aW9uIHRvIFhJRSwgdGhlIGxpbnV4IGlucHV0IHN0dWZmIGFuZCBldmVuIEhBTC4g V2UgbmVlZCB0byB0aGluawo+IGFib3V0IHRoaXMgYSBiaXQgbW9yZS4gKEl0IGlzIGludGVyZXN0 aW5nIHRvIG5vdGUsIHRoYXQgaGF2aW5nIHRoZQo+IHNvdW5kIHNlcnZlciBmb3J3YXJkICJwYXVz ZSIgYW5kICJyZXN1bWUiIHJlcXVlc3QgdG8gYXBwbGljYXRpb25zIGlzCj4gb24gdGhlIHRvZG8g bGlzdCBhbnl3YXksIHRvIGFsbG93IHBvbGljeSBtb2R1bGVzIHRvIHJlcXVlc3QgbXVzaWMKPiBw bGF5YmFjayBzdG9wcGluZyBvbiBpbWNvbWluZyBwaG9uZSBjYWxscyBhbmQgc3VjaGxpa2UuIFNv IGRvaW5nIHRoZQo+IGZ1bGwgImF1ZGlvIGtleSIgc2V0IG9mIGtleXByZXNzZXMgd291bGQgYmUg YSBzaW1wbGUgZXh0ZW5zaW9uIG9mCj4gdGhhdC4pCj4KPiBNYXJjZWwsIEpvaGFuLCBMdWl6LCBN YXJjLUFuZHLDqSwgQ2xhdWRpbywgZGlkIEkgbWlzcyBhbnl0aGluZz8KPgo+IExlbm5hcnQKPgo+ IC0tCj4gTGVubmFydCBQb2V0dGVyaW5nICAgICAgICAgICAgICAgICAgICAgICAgUmVkIEhhdCwg SW5jLgo+IGxlbm5hcnQgW2F0XSBwb2V0dGVyaW5nIFtkb3RdIG5ldCAgICAgICAgIElDUSMgMTEw NjA1NTMKPiBodHRwOi8vMHBvaW50ZXIubmV0L2xlbm5hcnQvICAgICAgICAgICBHbnVQRyAweDFB MDE1Q0M0Cj4KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpDaGVjayBvdXQgdGhlIG5ldyBTb3VyY2VGb3JnZS5u ZXQgTWFya2V0cGxhY2UuCkl0J3MgdGhlIGJlc3QgcGxhY2UgdG8gYnV5IG9yIHNlbGwgc2Vydmlj ZXMgZm9yCmp1c3QgYWJvdXQgYW55dGhpbmcgT3BlbiBTb3VyY2UuCmh0dHA6Ly9zb3VyY2Vmb3Jn ZS5uZXQvc2VydmljZXMvYnV5L2luZGV4LnBocApfX19fX19fX19fX19fX19fX19fX19fX19fX19f X19fX19fX19fX19fX19fX19fXwpCbHVlei1kZXZlbCBtYWlsaW5nIGxpc3QKQmx1ZXotZGV2ZWxA bGlzdHMuc291cmNlZm9yZ2UubmV0Cmh0dHBzOi8vbGlzdHMuc291cmNlZm9yZ2UubmV0L2xpc3Rz L2xpc3RpbmZvL2JsdWV6LWRldmVsCg==