Homebrew (Wikipedie), správce balíčků pro macOS a od verze 2.0.0 také pro Linux, byl vydán ve verzi 4.5.0. Na stránce Homebrew Formulae lze procházet seznamem balíčků. K dispozici jsou také různé statistiky.
Byl vydán Mozilla Firefox 138.0. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 138 je již k dispozici také na Flathubu a Snapcraftu.
Šestnáctý ročník ne-konference jOpenSpace se koná 3. – 5. října 2025 v Hotelu Antoň v Telči. Pro účast je potřeba vyplnit registrační formulář. Ne-konference neznamená, že se organizátorům nechce připravovat program, ale naopak dává prostor všem pozvaným, aby si program sami složili z toho nejzajímavějšího, čím se v poslední době zabývají nebo co je oslovilo. Obsah, který vytvářejí všichni účastníci, se skládá z desetiminutových
… více »Richard Stallman přednáší ve středu 7. května od 16:30 na Technické univerzitě v Liberci o vlivu technologií na svobodu. Přednáška je určená jak odborné tak laické veřejnosti.
Jean-Baptiste Mardelle se v příspěvku na blogu rozepsal o novinkám v nejnovější verzi 25.04.0 editoru videa Kdenlive (Wikipedie). Ke stažení také na Flathubu.
TmuxAI (GitHub) je AI asistent pro práci v terminálu. Vyžaduje účet na OpenRouter.
Byla vydána nová verze R14.1.4 desktopového prostředí Trinity Desktop Environment (TDE, fork KDE 3.5, Wikipedie). Přehled novinek i s náhledy v poznámkách k vydání. Podrobný přehled v Changelogu.
Bylo vydáno OpenBSD 7.7. Opět bez písničky.
V Tiraně proběhl letošní Linux App Summit (LAS) (Mastodon). Zatím nesestříhané videozáznamy přednášek jsou k dispozici na YouTube.
jak se mi jako podařilo mluvit do Tapo C200 kamerky
Celá tadlecta hovadina znikla dřív než sem voběvila že existuje něco jako projekt go2rtc hele kterej umí do Tapo kamerek streamovat 😁 klidně by sem přísahala že to tam u toho pytapo projektu eště někdy před tejdnem, kdy sem se rozhodla do tý kamery vlízt, nebylo napsaný 🤔 😏 😜 😜
A je to trošičku děsivý, že ruskej hacker dělá uplně to samý co já a v uplně stejnou dobu ale asi se to stát muže 😮 😮 ale kdyžuž sem to začala vyrábět tak se aspoň pochlubim jak 😁
Tapo C200 a podobný příbuzný kamerky vod TP-linku sou uplně supr relativně levný číncký kamerky s PTZ votačením, mikrofonem a reproduktorem, nočnim IR přisvětlením, sdcartou a vubec se všim (tak jo, ethernetovej port chybí). Jenom na nich bylo takový blbý, že je šlo vovládat jenom skrs offiko appku hele 😮 😕 ale to už díky hodnejm lidem a jednomu rusákoj neni pravda 😮 😁
tapo kamera
Protože tydlecty číncký IoT serepetičky naštěstí bejvaj děsně nezabezpečený 😁 😁 se lidem podařilo vodposlechnout a dumpnout firmware (hele a hele) nazakladě kterýho hele slovák s divným legračním ménem zpřístupnil rest/json API, takže s kamerkou mužete třeba hejbat i bez offiko appky. Kamerka pochopytelně umí RTSP stream i s audiem, blbý překvápko ale bylo, že kamerčin OnVif strandard neumožňuje audio backchannel, jakože kdyby sme do kamerky chtěli mluvit třeba z iSpy nebo podobnýho softwaru 3tích stran nóóóó tak máme smůlu 😮 😕 Audio backchannel je offiko možnej jenom pomocí mobilní appky vod TP-Linku.
Nó a protože mam voči z Kašparovy krávy a rychlejc dělám než přemejšlim tak sem si nevšimla že třeba existuje něco jako tamto go2rtc a že do pytapo knihovny hodnej pan Juraj mezitim přidal věci na dělání s audiostreamama, nóó tak sem se pustila do ziskání backchannelu 😁 dobře to dopadlo, ukázkovej zdrojáček s převedením wavu do mp2t streamu, přehrání streamu, streamování mikrofonu nebo bytů ze stdin vstupu sou dole pod blogískovým zapiskem. Neni to určený pro produkci, pytapo knihovnička je tam naroubovaná různejma divnejma hnusnejma 'hackama' který by se rozhodně měli přepsat, vobrovskou peči vyžadujou refaktorovaný kusy javovskýho kódu do pythonu (myslimže to už udělal rusák vod projektu go2rtc pro jazyk go) etc, nicmeně muj bastl funguje 😁 Kdyby ten pytapo projekt přes víkend neaktualizovali by sem v tom asi pokračovala, když se ale našlo tamto go2rtc tak to už asi nemá smysl už páč všecko duležitý je nejspíš už hotový 🤷♀️ 🤪
Aspoň napišu jak se jako dá zdrojáček na dělání audiobackchannelu vydolovat z android appky 😁
Pomocí jd-gui hele (a nějakejch jinejch random online dekompilátorů, já to pak různě smíchala dohromady a teďko se vtom nemužu vyznat 😁 důvod proč použít víc nástrojů je že různý dekompilátory třeba některej kus kódu nezvládnou zatimco jinej si s tim poradí. jd-gui třeba vubec nemumělo vybalit celej jeden androidí*.dex soubor ale webová appka si stim 'poradila') sem extrahovala z appky javovskej zdrojáček a začalo pátrání po pro nás zajimavejch tříd/kusů zdrojáčku.
Z jd-gui (nebo webovejch appek) exportovanej zdrojáček mužeme otevřít/importovat do intellijIDEy pro lepčí orientaci. Ten zdrojáček samozdřejmě nepude nějak jednoduše pustit, dekompilátor dělá různý chyby, část zdrojáčku se třeba nepovede přeložit ze smali/bytekódu, část muže dokonce uplně chybět a tak různě podobně. Při kompilaci do hotový aplikace se ztratí věčina původních názvů tříd a metod a dekompilátor musí všecko vodznova pojmenovat ale vůůůůbec neví jak, takže se všecko jmenuje různě pitomě jako třeba 'class a' 'function b' 'a.b.c.d(a.e(i(o,this.u)))' a IDE je dobrá berlička jak se vtom jako vyznat. Jednak případně umožní rychlej refaktoring na ňáký víc rozumější názvy (nóó sem si teda zvykla na ty generovaný tak sem je nechala😁), umožní rychlý přeskakování mezi třídama (nevim jestli to je default, u mě stačí voznačit kousek kódu a když se máčkne F3, tak to skočí na deklaraci), jednak zobrazením použití jednotlivejch metod v rozklikávacím seznamu napsaným nad funkcema třídy. Jo, i takovejdle zmrtvicovanej zdrojáček to umí parsovat a analyzovat 😁 😜
Když si prohlídneme packety který appka posílá (třeba si na androidu pustíme tcpdump hele, přetáhneme do počítače zachycenej dump a pak pomocí wiresharku koukneme na to) do kamerky, tak mužeme vidět něco jako
----client-stream-boundary-- X-Session-Id: 7 Content-Type: audio/mp2t X-If-Encrypt: 1 Content-Length: 1520 dlouhááááá nudle bytů..............
takže jeden směr pátrání ved po výskytu substringu 'audio/mp2t' v kusech dekompilovanýho zdrojáčku, druhej po substringu 'AudioRecord', páč takle se menuje androidí třída pro čtení streamu z mikrofonu. Jinak řečeno, hledáme místa kde zvuk vstupuje do zařízení, takže asi začátek celýho audio procesu, a pak místa, kde se zaručeně se streamem ňák něco dělá.
Hledáním řetězce 'audio/mp2t' sem se prakticky vokamžitě dostala ke třídě, která něčim prostrkává pole bytů a zpracovaný (se stringama, který mužem vidět u tý ----client-stream-boundary-- ukázky) strká někam dál. To 'někam dál' se ukázalo jako tcp socket, kterej ke vstupům přidával všecky ty různý naležitosti jako to dělá pytapo knihovnička, různý ty \r\n byty zakončovací a tak. To něco prostrkávací se ukazalo jako lepenina několika javovskejch tříd, kde do voka ve spojitosti s mp2t streamem píchne pole vo dýlce 188 bytů, páč přesně takle jsou dlouhý jednotlivý packety mp2t streamu. Takže to by jsme měli jednu duležitou součástku, řikejme tomu třeba packetovadlo.
package com.tplink.libmediakit.c.d.n; import android.text.TextUtils; import b.d.f.c.d4; import com.tplink.libmediakit.c.d.m.b.n; import com.tplink.libmediakit.media.statistics.ConnectionInfoVO; import com.tplink.libmediakit.media.statistics.OnceConnectionVO; import com.tplink.libmediakit.media.statistics.StatisticsManager; import com.tplink.libmediakit.media.statistics.StatisticsStreamType; import com.tplink.libmediakit.media.statistics.StopReason; import com.tplink.libmediakit.utils.GenKey; import com.tplink.libmediakit.utils.s; import com.tplink.libtpnetwork.IoTNetwork.TPIoTClientManager; import com.tplink.libtpnetwork.cameranetwork.util.j; import java.io.IOException; import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; public class d extends com.tplink.libmediakit.c.d.l.c { private c P0; private e Q0; private Future<Boolean> R0; private volatile boolean S0; private com.tplink.libmediakit.media.stream.httpconnection.a T0; private String U0; private com.tplink.libtpnetwork.cameranetwork.util.b V0; private com.tplink.libmediakit.utils.h0.e W0; private long X0 = 0L; private n Y0; private final String Z0; private String a1; private long b1; private d.a c1; public d(String var1) { this.Z0 = var1; this.U0 = null; } // $FF: synthetic method private void l() { StringBuilder var1 = new StringBuilder(); var1.append("设备:"); var1.append(super.f); var1.append("语音流被release()"); b.d.g.c.a.c("DoubleTalkStreamConnection", var1.toString()); this.r(); com.tplink.libmediakit.media.stream.httpconnection.a var2 = this.T0; if (var2 != null) { var2.e(); } com.tplink.libmediakit.utils.h0.e var3 = this.W0; if (var3 != null) { var3.m(); } } private void n() { boolean var1 = true; while(true) { while(true) { String var3; boolean var4; do { do { Map var2; do { do { if (!var1) { return; } this.T0.f(); var2 = this.T0.o(); } while(var2.size() == 0); } while(!var2.containsKey("Content-Type")); var3 = (String)var2.get("Content-Type"); if (var2.containsKey("X-If-Encrypt") && "1".equals(var2.get("X-If-Encrypt"))) { var4 = true; } else { var4 = false; } } while("video/mp2t".equals(var3)); } while(!"application/json".equals(var3)); byte[] var7 = this.T0.l(); byte[] var6 = var7; if (var4) { com.tplink.libtpnetwork.cameranetwork.util.b var5 = this.V0; var6 = var7; if (var5 != null) { var6 = var5.b(var7, var7.length); } } var3 = new String(var6); StringBuilder var8 = new StringBuilder(); var8.append("payload: "); var8.append(var3); b.d.g.c.a.c("DoubleTalkStreamConnection", var8.toString()); com.tplink.libmediakit.c.d.m.c.a var9 = (com.tplink.libmediakit.c.d.m.c.a)com.tplink.libmediakit.c.d.m.a.b(var3, com.tplink.libmediakit.c.d.m.c.a.class); if ("notification".equals(var9.c())) { com.tplink.libmediakit.c.d.m.c.b var13 = com.tplink.libmediakit.c.d.m.a.c(var9); if (var13 != null && this.Y0 != null) { var8 = new StringBuilder(); var8.append("event type "); var8.append(var13.a()); b.d.g.c.a.c("DoubleTalkStreamConnection", var8.toString()); this.Y0.c(super.f, var13); } } else if ("response".equals(var9.c())) { if (this.U0 == null) { com.tplink.libmediakit.c.d.m.f.e var10 = (com.tplink.libmediakit.c.d.m.f.e)com.tplink.libmediakit.c.d.m.a.e(var9, com.tplink.libmediakit.c.d.m.f.e.class).a(); if (var10.a() == 0 && var10.c() != null) { this.x(var10.c()); d.a var11 = this.c1; if (var11 != null) { var11.a(); } } else { d.a var12 = this.c1; if (var12 != null) { var12.b(var10.a()); } } } else { n var14 = this.Y0; if (var14 != null) { var14.a(var9); } } var1 = false; } } } } private void p() { StatisticsManager var1 = StatisticsManager.getInstance(); String var2 = super.f; StatisticsStreamType var3 = StatisticsStreamType.DOUBLE_TALK; var2 = var1.getOnceConnectionCacheKey(var2, var3, super.z); OnceConnectionVO var6 = StatisticsManager.getInstance().getAndRemoveOnceConnectionVO(var2); if (var6 != null) { var6.setStopReason(StopReason.USER_CLOSE.value()); StatisticsManager.getInstance().insertOnceConnectionVO(var2, var6); } else { var6 = new OnceConnectionVO(); } String var4 = StatisticsManager.getInstance().getConnectionInfoCacheKey(super.f, var3); ConnectionInfoVO var7 = StatisticsManager.getInstance().getConnectionInfoVO(var4); ConnectionInfoVO var8 = var7; if (var7 == null) { var8 = new ConnectionInfoVO(); StatisticsManager.getInstance().insertConnectionInfoVO(var4, var8); } int var5 = super.z; if (var5 == 16) { var8.getP2pConnectionVO().addData(var6); } else if (var5 == 0) { var8.getRelayConnectionVO().addData(var6); } else if (var5 == 256) { var8.getLocalConnectionVO().addData(var6); } } private void q(byte[] var1) { if (this.T0 != null && var1 != null && var1.length > 0) { Exception var10000; label65: { boolean var10001; try { if (this.X0 == 0L) { this.X0 = System.currentTimeMillis() / 1000L * 90000L; } } catch (Exception var8) { var10000 = var8; var10001 = false; break label65; } byte[] var2; try { var2 = this.W0.k(var1, var1.length, this.X0); this.X0 += (long)var1.length * 90000L / 8000L; } catch (Exception var7) { var10000 = var7; var10001 = false; break label65; } if (var2 == null) { try { b.d.g.c.a.c("DoubleTalkStreamConnection", "mux ts data failed"); return; } catch (Exception var3) { var10000 = var3; var10001 = false; } } else { label51: { com.tplink.libtpnetwork.cameranetwork.util.b var9; try { var9 = this.V0; } catch (Exception var6) { var10000 = var6; var10001 = false; break label51; } HashMap var10; if (var9 != null) { try { b.d.g.c.a.c("DoubleTalkStreamConnection", "send encrypt data"); var2 = this.V0.c(var2); this.T0.H(true); var10 = new HashMap(); var10.put("Content-Type", "audio/mp2t"); var10.put("X-If-Encrypt", "1"); var10.put("X-Session-Id", this.U0); this.T0.D(var10, var2); return; } catch (Exception var4) { var10000 = var4; var10001 = false; } } else { try { b.d.g.c.a.c("DoubleTalkStreamConnection", "send plain data"); this.T0.H(true); var10 = new HashMap(); var10.put("Content-Type", "audio/mp2t"); var10.put("X-Session-Id", this.U0); this.T0.D(var10, var2); return; } catch (Exception var5) { var10000 = var5; var10001 = false; } } } } } Exception var11 = var10000; var11.printStackTrace(); c var12 = this.P0; if (var12 != null) { var12.a(super.f, -1, var11); } StringBuilder var13 = new StringBuilder(); var13.append(""); var13.append(var11.toString()); b.d.g.c.a.c("DoubleTalkStreamConnection", var13.toString()); } } private void r() { if (this.T0 != null && !TextUtils.isEmpty(this.U0)) { try { int var1 = this.Y0.b(); com.tplink.libmediakit.c.d.m.e.d var2 = new com.tplink.libmediakit.c.d.m.e.d(); com.tplink.libmediakit.c.d.m.c.c var3 = new com.tplink.libmediakit.c.d.m.c.c(var2); var3.b(var1); String var6 = com.tplink.libmediakit.c.d.m.a.d(var3); HashMap var5 = new HashMap(); var5.put("Content-Type", "application/json"); var5.put("X-Session-Id", this.U0); this.T0.H(true); this.T0.C(var5, var6); } catch (Exception var4) { var4.printStackTrace(); } } } public void A() { this.b1 = System.currentTimeMillis(); } public void B() { int var1 = Math.round((float)(System.currentTimeMillis() - this.b1) / 1000.0F); String var2 = StatisticsManager.getInstance().getOnceConnectionCacheKey(super.f, StatisticsStreamType.DOUBLE_TALK, super.z); OnceConnectionVO var3 = StatisticsManager.getInstance().getOnceConnectionVO(var2); if (var3 != null) { var3.addUsageTime(var1); StatisticsManager.getInstance().insertOnceConnectionVO(var2, var3); } } public void f(String var1) { if (this.T0 != null && !TextUtils.isEmpty(this.Z0)) { HashMap var2 = new HashMap(); if (!TextUtils.isEmpty(this.U0)) { var2.put("X-Session-Id", this.U0); } this.T0.s(var1, var2); if (this.T0.r()) { this.T0.H(false); } try { this.n(); } catch (Exception var3) { this.P0.a(super.f, -1, var3); } } } public Boolean k() { d4 var1 = TPIoTClientManager.J1(super.f); c var16; if (var1 != null && var1.b() != null) { boolean var2 = true; this.S0 = true; this.W0 = new com.tplink.libmediakit.utils.h0.e(); String var17; if (s.c() != null) { var17 = s.c(); } else { var17 = ""; } String var3 = j.a(var17); var17 = var3; if (TextUtils.isEmpty(var3)) { var17 = GenKey.a(); } StringBuilder var18 = new StringBuilder(); var18.append("设备建立语音流:账号:"); var18.append("admin"); var18.append(" 密码:"); var18.append(var17); b.d.g.c.a.c("DoubleTalkStreamConnection", var18.toString()); String var4; if (this.e()) { HashMap var19 = new HashMap(); var19.put("Content-Type", "multipart/mixed; boundary=--client-stream-boundary--"); var4 = com.tplink.libmediakit.media.stream.httpconnection.a.n(super.x, super.y); var3 = com.tplink.libmediakit.c.d.p.a.a().c(var4, "admin", var17, "POST", var19); } else { var3 = ""; } if (TextUtils.isEmpty(var3) && this.e()) { var16 = this.P0; if (var16 != null) { var16.e(super.f, 2); } return Boolean.TRUE; } else { Exception var10000; label145: { boolean var10001; label130: { try { com.tplink.libmediakit.media.stream.httpconnection.a var21 = new com.tplink.libmediakit.media.stream.httpconnection.a(super.x, super.y); this.T0 = var21; var21.E(30000); this.T0.G(15000); this.T0.F("POST"); this.T0.d(); if (this.b() == 0) { break label130; } } catch (Exception var15) { var10000 = var15; var10001 = false; break label145; } var2 = false; } int var5; try { var5 = this.T0.p(var3, var2); } catch (Exception var14) { var10000 = var14; var10001 = false; break label145; } if (var5 == 200) { try { Map var23 = this.T0.o(); if (var23.containsKey("Key-Exchange")) { var4 = (String)var23.get("Key-Exchange"); var18 = new StringBuilder(); var18.append(""); var18.append(var4); b.d.g.c.a.c("DoubleTalkStreamConnection", var18.toString()); this.V0 = com.tplink.libmediakit.c.d.l.b.b(var4, var17); } } catch (Exception var13) { var10000 = var13; var10001 = false; break label145; } } StringBuilder var20; if (var5 != 200 && var5 != 204) { label107: { try { var20 = new StringBuilder(); var20.append("设备:"); var20.append(super.f); var20.append("LiveStream连接失败,errorCode:"); var20.append(var5); b.d.g.c.a.c("DoubleTalkStreamConnection", var20.toString()); } catch (Exception var10) { var10000 = var10; var10001 = false; break label107; } if (var5 == 401) { label144: { com.tplink.libmediakit.media.stream.httpconnection.a var24; try { this.P0.e(super.f, 2); var24 = this.T0; } catch (Exception var8) { var10000 = var8; var10001 = false; break label144; } if (var24 != null) { try { var24.e(); } catch (Exception var7) { var10000 = var7; var10001 = false; break label144; } } try { return Boolean.FALSE; } catch (Exception var6) { var10000 = var6; var10001 = false; } } } else { try { IOException var27 = new IOException(); throw var27; } catch (Exception var9) { var10000 = var9; var10001 = false; } } } } else { label146: { try { var20 = new StringBuilder(); var20.append("设备:"); var20.append(super.f); var20.append("ConnectType "); var20.append(this.b()); var20.append(" 双向语音连接成功"); b.d.g.c.a.c("DoubleTalkStreamConnection", var20.toString()); if (!TextUtils.isEmpty(this.Z0)) { var20 = new StringBuilder(); var20.append("requestTalk "); var20.append(this.Z0); b.d.g.c.a.c("DoubleTalkStreamConnection", var20.toString()); this.T0.s(this.Z0, (Map)null); } } catch (Exception var12) { var10000 = var12; var10001 = false; break label146; } try { this.n(); } catch (Exception var11) { var10000 = var11; var10001 = false; break label146; } e var22 = this.Q0; if (var22 != null) { var22.e(super.f, this.b()); } return Boolean.TRUE; } } } Exception var25 = var10000; var25.printStackTrace(); c var26 = this.P0; if (var26 != null) { var26.e(super.f, 2); } if (var25 instanceof SocketTimeoutException) { e var28 = this.Q0; if (var28 != null) { var28.f(super.f, this.b(), var25); } } return Boolean.FALSE; } } else { var16 = this.P0; if (var16 != null) { var16.e(super.f, 2); } return Boolean.FALSE; } } // $FF: synthetic method public void m() { this.l(); } public void o() { this.S0 = false; this.P0 = null; this.Q0 = null; this.c1 = null; (new Thread(new com.tplink.libmediakit.c.d.n.a(this))).start(); this.p(); if ("aec".equalsIgnoreCase(this.a1)) { this.B(); } } public void s(byte[] var1) { if (this.S0) { this.q(var1); } } public void t(d.a var1) { this.c1 = var1; } public void u(c var1) { this.P0 = var1; } public void v(e var1) { this.Q0 = var1; } public void w(Future<Boolean> var1) { this.R0 = var1; } public void x(String var1) { this.U0 = var1; } public void y(n var1) { this.Y0 = var1; } public void z(String var1) { this.a1 = var1; } public interface a { void a(); void b(int var1); } }
Pokud se podiváme kolik prostoru ve zdrojáčku dostala telemetrie ze tříd com.tplink.libmediakit.media.statistics.* tak asi jako mužeme tušit proč nás tplink znadlivě zbytečně nutí pro backchannel použivat jejich appku 😁
Další duležitý věci se podařilo najít vyhledáváním tamtoho AudioRecordéru.
Jednak třídu se statickejma metodama, kterejma se prostrákvalo pole s právě nahraným audiochunkem. Protože to vrací pole o poloviční dýlce, než se dotoho strčí, tak tomu řikám resamplovadlo, podezírám to ale že to dělá víc než že jenom tupě převzorkovává signál.
Noa pak hlavně nahrávací parametry androidí třídy AudioRecorder, ze kterejch šlo dosazením ze třídy 'm' poděděnejch konstant určit, že signál má bejt 8kHz jednokanálový audio s přesností 16bitů na zorek + minimální dýlka nahranýho audiochunku 320 vzorků (resamplovadlo nám z toho vyrobí 160 vzorků, to je polovička kdyby to někomu nedošlo třeba, jak to jako počitám je vidět na řádcích 57-66 😁 😜)
package com.tplink.libmediakit.c.a.h.z; import android.media.AudioRecord; import android.media.AudioRecord.OnRecordPositionUpdateListener; import androidx.annotation.NonNull; import c.b.r; import com.tplink.libmediakit.media.audioprocess.AudioProcessUtils; import java.util.Arrays; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; public class p extends m implements OnRecordPositionUpdateListener { private static final String v = p.class.getSimpleName(); private p.e A; private short[] B; private short[] C; private Timer D; private int E; private AudioRecord w = null; private com.tplink.libmediakit.media.audioprocess.b<com.tplink.libmediakit.media.audioprocess.a> x; private final LinkedBlockingQueue<com.tplink.libmediakit.media.audioprocess.a> y = new LinkedBlockingQueue(); private p.d z; public p(String var1, com.tplink.libmediakit.c.d.m.b.o var2, String var3) { super(var1, var2, var3); short[] var4 = new short[512]; this.B = var4; Arrays.fill(var4, (short)-1); var4 = this.B; this.C = AudioProcessUtils.a(var4, var4.length); this.D = new Timer(); this.E = 0; com.tplink.libmediakit.media.audioprocess.b var5 = new com.tplink.libmediakit.media.audioprocess.b(); this.x = var5; var5.j(8000); this.x.c(2); this.x.c(6); this.x.c(7); } // $FF: synthetic method private void B(String var1, int var2, Integer var3) { com.tplink.libmediakit.c.d.n.c var4 = super.l; if (var4 != null) { var4.a(var1, var2, new Exception("SendDataFailure")); } } private void D() { int var2 = 10 * 8000 / 1000; //80 super.g = var2; super.h = var2 * 2 * 1 * 16 / 8; // 80*2*1*16/8 = 320 var2 = AudioRecord.getMinBufferSize(8000,CHANNEL_IN_MONO, ENCODING_PCM_16BIT); if (320 < var2) { var2 = AudioRecord.getMinBufferSize(8000, CHANNEL_IN_MONO, ENCODING_PCM_16BIT); super.h = var2; super.g = var2 / (1 * 2 * 16 / 8); } AudioRecord var3 = new AudioRecord(1, 8000, CHANNEL_IN_MONO, ENCODING_PCM_16BIT, super.h); this.w = var3; if (var3.getState() == 1) { super.i = super.g * 1 * 16 / 8; this.A = new p.e(this); } else { StringBuilder var4 = new StringBuilder(); var4.append("AudioRecord initialization failed:"); var4.append(this.w.getState()); throw new Exception(var4.toString()); } } private int E() { AudioRecord var1 = this.w; if (var1 != null && var1.getState() != 0) { this.w.release(); this.w = null; } b.d.g.c.a.c(v, "Audio recorder stopped"); return 0; } private void F() { Timer var1 = this.D; if (var1 != null) { var1.cancel(); this.D = null; } this.E = -4; var1 = new Timer(); this.D = var1; var1.schedule(new TimerTask(this) { final p f; { this.f = var1; } public void run() { com.tplink.libmediakit.c.a.h.z.p.o(this.f); } }, 0L, 1000L); } private void G(com.tplink.libmediakit.media.audioprocess.a var1) { byte[] var2; if (super.t.get()) { var2 = var1.d; var2 = com.tplink.libmediakit.utils.g0.a.b(var2, var2.length); com.tplink.libmediakit.c.d.k.D().B(super.k, var2); } else { var2 = this.t(); com.tplink.libmediakit.c.d.k.D().B(super.k, var2); } } private void I() { com.tplink.libmediakit.c.d.k.D().r0(super.k); if (!super.m.get()) { super.m.set(true); try { this.D(); } catch (Exception var2) { var2.printStackTrace(); super.l.f(super.k); return; } this.D.schedule(new TimerTask(this) { final p f; { this.f = var1; } public void run() { com.tplink.libmediakit.c.a.h.z.p.o(this.f); } }, 0L, 1000L); this.H(); } } // $FF: synthetic method static int o(p var0) { int var1 = var0.E++; return var1; } // $FF: synthetic method public static void u(p var0, com.tplink.libmediakit.media.audioprocess.a var1) { var0.G(var1); } // $FF: synthetic method private void v(String var1) { com.tplink.libmediakit.c.d.n.c var2 = super.l; if (var2 != null) { var2.f(var1); } } // $FF: synthetic method private void x(String var1, int var2, Integer var3) { com.tplink.libmediakit.c.d.n.c var4 = super.l; if (var4 != null) { var4.e(var1, var2); } } // $FF: synthetic method private void z(String var1) { com.tplink.libmediakit.c.d.n.c var2 = super.l; if (var2 != null) { var2.c(var1); } } // $FF: synthetic method public void A(String var1) { this.z(var1); } // $FF: synthetic method public void C(String var1, int var2, Integer var3) { this.B(var1, var2, var3); } public int H() { AudioRecord var1 = this.w; if (var1 != null && var1.getState() != 0) { this.w.startRecording(); } p.e var2 = this.A; if (var2 != null) { var2.start(); b.d.g.c.a.c(v, "Audio recorder start"); } return 0; } public int J() { super.m.set(false); AudioRecord var1 = this.w; if (var1 != null && var1.getState() != 0) { this.w.stop(); } InterruptedException var10000; label32: { boolean var10001; p.e var6; try { var6 = this.A; } catch (InterruptedException var5) { var10000 = var5; var10001 = false; break label32; } if (var6 == null) { return 0; } try { var6.join(); return 0; } catch (InterruptedException var4) { var10000 = var4; var10001 = false; } } InterruptedException var2 = var10000; String var7 = v; StringBuilder var3 = new StringBuilder(); var3.append(""); var3.append(var2); b.d.g.c.a.c(var7, var3.toString()); return 0; } public void a(String var1, int var2, Exception var3) { c.b.r.i0(1).n0(c.b.e0.b.a.a()).I0(new i(this, var1, var2)); } void b() { super.p.set(false); this.D.cancel(); this.E = 1; this.J(); this.E(); this.x.i((com.tplink.libmediakit.media.audioprocess.b.a)null); this.x.b(); p.d var1 = this.z; if (var1 != null) { var1.c(); } super.o.clear(); super.l = null; com.tplink.libmediakit.c.d.k.D().d0(super.k); } public void c(String var1) { if (super.p.get()) { ExecutorService var2 = super.n; if (var2 != null && !var2.isShutdown()) { this.x.i(new j(this)); if (this.z == null) { p.d var3 = new p.d(this); this.z = var3; var3.b(super.n.submit(var3)); } } } c.b.r.i0(var1).n0(c.b.e0.b.a.a()).I0(new g(this)); this.I(); } public void e(String var1, int var2) { c.b.r.i0(1).n0(c.b.e0.b.a.a()).I0(new h(this, var1, var2)); } public void f(String var1) { c.b.r.i0(var1).n0(c.b.e0.b.a.a()).I0(new k(this)); } void g() { if (!super.p.get()) { super.p.set(true); super.n = Executors.newFixedThreadPool(1, new ThreadFactory(this) { private final AtomicInteger f; final p x; { this.x = var1; this.f = new AtomicInteger(0); } public Thread newThread(@NonNull Runnable var1) { Thread var3 = new Thread(var1); StringBuilder var2 = new StringBuilder(); var2.append("pool-AecDoubleTalkClient.speakerService-"); var2.append(this.f.incrementAndGet()); var3.setName(var2.toString()); return var3; } }); com.tplink.libmediakit.c.d.k.D().y(super.k, this, super.q, super.s); } } void h() { super.h(); this.F(); } public void j(com.tplink.libmediakit.c.d.n.c var1) { super.l = var1; } void n() { super.n(); } public void onMarkerReached(AudioRecord var1) { } public void onPeriodicNotification(AudioRecord var1) { } protected byte[] t() { short[] var1 = this.C; if (var1 != null) { int var2 = this.E; if (var2 < 0) { this.E = var2 + 1; return com.tplink.libmediakit.utils.g0.a.f(var1); } if (var2 % 6 == 0) { this.E = var2 + 1; return com.tplink.libmediakit.utils.g0.a.f(var1); } } return null; } // $FF: synthetic method public void w(String var1) { this.v(var1); } // $FF: synthetic method public void y(String var1, int var2, Integer var3) { this.x(var1, var2, var3); } private class d implements Callable<Boolean> { private boolean f; private Future<Boolean> x; final p y; public d(p var1) { this.y = var1; this.f = true; } public Boolean a() { while(this.f) { com.tplink.libmediakit.media.audioprocess.a var1 = (com.tplink.libmediakit.media.audioprocess.a)this.y.y.take(); if (var1.d != null && this.y.x != null) { this.y.x.h(var1); } } return Boolean.TRUE; } public void b(Future<Boolean> var1) { this.x = var1; } public void c() { this.f = false; this.y.y.clear(); this.y.y.offer(new com.tplink.libmediakit.media.audioprocess.a()); Future var1 = this.x; if (var1 != null) { var1.cancel(true); this.x = null; } } } private class e extends Thread { final p f; public e(p var1) { super("tp-recorder"); this.f = var1; } public void run() { byte[] var1 = new byte[this.f.i]; b.d.g.c.a.c(com.tplink.libmediakit.c.a.h.z.p.v, "Recording..."); while(this.f.m.get()) { if (this.f.w != null && this.f.w.getState() != 0 && this.f.w.read(var1, 0, this.f.i) > 0) { this.f.y.offer(new com.tplink.libmediakit.media.audioprocess.a(this.f.k, System.currentTimeMillis(), (byte[])var1.clone())); } } } } }
Některý konstantní proměný jsou nahrazený odpovidajicí číselnou hodnotou.
Takže ty nalezený javovský třídy sem transplantovala do novýho java projektu v intellijIDEe, trochu učesala, AudioRecorder nahradila *.wav souborem a že takle vyrobim *.ts soubor kterej pak pomocí pytapo pošlu do kamery. Takže napišu pustim převedu pošlu a ............nic. A tady začal velkej zásek protože sem začala zkoušet různý nesmyslný terorie vo kterejch pišu kousek dál. Nakonec to bylo v uplný hovadince a nejvíc nejvrrrražžžednější programátorský chybce a to přepočitání se o jedničku 😮 😁
Jedna z transplantovanejch tříd bylo nějaký divný Enum na výběr codecu. To sem přepsala normálním enumem. Problém byl v tom, že ty číslený hodnoty který u puvodního enumu byly napsaný v závorkách vůůůůůůůůůbec nevodpovídaly skutečnosti a neseděli pak s metodama v Packetovadlu (kde maj napevno zadrátovanej codec PCMA btw) a nejspíš se jedná o nějakej bug dekompilátoru, kterej asi narazil na něco divnýho nevědel co s tim tak to vyplivnul takle. Opravila sem to vodečtením jendičky na kritickejch místech, kde se pracovalo s enumem jako s čiselnou hodnotou. Pak už to fungovalo a tadá, máme pustitelnej *.mp2t stream.
package com.tplink.libmediakit.utils.h0; public enum f { f; private static final f[] p0; x, y, z; int M0; static { f var0 = new f("MUXTS_CODEC_HEVC", 0, 0); f = var0; f var1 = new f("MUXTS_CODEC_AVC", 1, 1); x = var1; f var2 = new f("MUXTS_CODEC_AAC", 2, 2); y = var2; f var3 = new f("MUXTS_CODEC_PCMA", 3, 3); z = var3; p0 = new f[]{var0, var1, var2, var3}; } private f(int var3) { this.M0 = var3; } }
Rozhodla sem se to zkřížit s tou pytapo knihovnou, takže ty javovský třídy bylo potřeba přepsat do pythonu. Sem si najivně myslela že to prostě všecko poloautomaticky nahradim regexpema/refaktoringem, strčim k tý knihovně a pustim. Akorátže sem děsně narazila na jeden detail, a to že ten původní javovskej zdrojáček děsně často pracuje s bitovejma operacema přímo na proměnejch a overflow/přetečení proměnejch byla asi součást optimalizačních triků různejch 😁 A tady v pythonu narazíme, protože zatimco v puvodním zdrojáčku to bylo samej byte,short,int,long.... v pythonu si musíme vystačit jenom s intem(děsně velkým že se do něj i ten long long long nacpe), takže bylo nutný začít hlídat případný překlopení proměnejch. Vicemeně stačilo nahradit všecky explicitní přetypování pythoní funkcí která to overflow hlídá, jakože třeba '(short)( blabla | 8)' na 'hlidaci_fce(blabla | 8)'.
Další nepříjemný bylo, že javovský byty jsou v rosahu -128 až 127, zatimco pythoní jsou 0 až 255, takže to potřebovalo další hlídání. Jo, nějakej výkon nás ty kejkle navíc stojej, nicmeně to je furt použitelný, ten audio přenos zase neni buchvíjak rychlej takže na packetovávání máme hodně času. Vostatně ani voni i když měli možnost použít poctivý nativní ccčkový *.so knihovny todlecto (k mýmu štěstí) naprasili v javě, takže se asi ani já nemusim bát prasit to v pythonu 😁
Jak to jako dopadlo mužete vidět pak dole
Jak sem jako už řekla, sem se zašprajcla na tom asi blbě dekompilovaným enumu. Akorátže to sem eště nevěděla, na čemže sem se to šprajcla a začala zkoušet různý hovadinky 😁 Tak třeba na tý třídě s AudioRecorderem jsem si všimla nějakýho děsně podezřelýho pole bytů vo dýlce [512] a zistila sem, že je inicializovaný z nativní funkce třídy 'com.tplink.libtpaudioprocess.AudioProcessUtils', pojmenovaný děěěěěsně podezřele 'nativeWatermark', která načítá cčkovou knihovnu 'libaudio_proc.so'. Pole je pak proháněný nějakou jinou statickou metodou našeho starýho známýho resamplovadla (jinou než normální audiostream z mikrofonu) a je to pak posílaný do packetovadla a dál.
To mě vedlo k mylnýmu předpokladu, že audiostream asi jako 100% musí bejt ňák poznamenanej timdlectim nějakým jejich watermarkem, by to jakože fungovalo. Z kouknutí na metodu 'onPeriodicNotification' šlo usuzovat, že to asi každejch pár ňákejch časovejch jednotek nebo možná iterací pošle ten samej kousek na začátku vygenerovanýho audia. Takže asi ten kousek bude potřeba ňák z tý appky vydolovat.
Naštěstí z androidího *.apk souboru je snadný získat *.so soubor knihovny a když byla knihovna zkompilovaná s java native interface hele (těžko to asi jako bude u java appky jinak 😁) tak mužeme ty metody volat i z nějaký svý uplně jiný třídy, jenom musíme pohlídat by se naše nová třída menovala uplně stejně + musí bejt skovaná v uplně stejný složšce/javovský package, by si samo bez ňákejch dalších zásahů JNI mohlo tu metodu načíst. V tomdle připadě je pochopytleně potřeba zařízení s architekturou na který by mohly knihovny normálně fungovat (cokoliv na čem de spustit tu jejich tapo aplikaci, momentálně tam vidim knihnovny pro aarch64 a armeabi-v7a) a nainstalovanou javou, takže třeba mobil/tablet s termuxem nebo třeba aarch64 odroid počitačovou destičku. Neznámá knihovna ripnutá z pochybný číncký apkky prolezlý šmírovací telemetrí se nemusí strkat do systému, java si nativní knihovny umí načíst ze složšky při spouštění binárky 'java -Djava.library.path=/cesta/ke/slozsce/s/knihovnou -jar NejakejNazev.jar', nicmeně bezpečný to je asi uplně stejně 😁 😜
Takže knihovna se z *.apk vyndala, strčila do novýho projektu a nechala vygenerovat watermarky. Nezávisle na čase nebo počtu volání tý funkce to pokaždý vrátilo to samý pole, který když proženeme Resamplovadlem tak dostaneme
[-1, -1, -2, -1, -1, -1, -1, -1, -2, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -2, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -1, -1, -2, -1, -2, -1, -2, -1, -1, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, .....a furt samý -1 až dokonce
zkoušela sem tim audiostream různě prokládat ale k ničemu to pochopytelně nevedlo
Taky mi bylo děsně podezřelý, že to číňanům piše do logu něco jako 'pool-AecDoubleTalkClient.speakerService-' a podobný věci, zlášť když jedna z nativních tříd ze složšky/package 'com.tplink.libmediakit.media.audioprocess' se taky menuje AEC. První, co sem si řekla, žeto je asi jako děsně blbý, žeto je nejspíš nějakej proprietární kodek děsně tajnej aže sem buďto skončila nebo že to budu muset ňák disassemblovat, nebo pouštět jenom v javě na nějakým aarch64 počitadle 😮 😮
Takže přišla na řadu ghidra, by se zistilo co ty tajemný nativní volání všecko dělaj. Ghidra je takový disassemblovadlo co vyvíjí NSA Litoměřice, komunitní verze je veřejně dostupná. Ghidrou se dá kouknout na různý zkompilovaný binárky nebo právě knihovny a když máme štěstí, tak se tim muže zistit něco užitečnýho. A štěstí bylo 😮 😜
všecky JNI metody nám to krásně naháže do jedný složšky
Tak tamten watermark je skutečně furt konstantní, jeho obsahem je string 'MUTE' a je generovanej tidmlectim kódem (konstantní hodnoty jsem přepsala do desitkový soustavy)
undefined8 Java_com_tplink_libtpaudioprocess_AudioProcessUtils_nativeWatermark (long *param_1,undefined8 param_2,undefined8 param_3,int param_4) { void *__ptr; undefined8 uVar1; __ptr = (void *)(**(code **)(*param_1 + 1488 ))(param_1,param_3,0); SetWatermark((long)__ptr,param_4,"MUTE"); uVar1 = (**(code **)(*param_1 + 1424 ))(param_1,param_4); (**(code **)(*param_1 + 1680))(param_1,uVar1,0,param_4,__ptr); free(__ptr); return uVar1; }
Votázka je, proč by někdo watermarkoval prakticky prázdný audio stringem 'MUTE' kdyby ho nechtěl použít na nic jinýho než jako 'ticho' 🤔 🤔
předpokládám, že se to používá v případech, když se třeba zasekne vstupní stream nebo když uživatel appky použije takovej ten režim videohovoru, ve kterým muže kamerou mluvit jenom když máčkne červenej čudlík, by se třeba nepřerušil stream 🤔 🤔 Nevim, hádám 😮 🤪
Každopádně, pro posílání audia do kamerky asi neni watermark nějak důležitej 🤔 🤷♀️
A ani tamto AEC žádnej kodek nebyl. Když se proklikáme ke konstruktoru třídy AEC, tak mužeme vidět užitečnou nápovědu........
void __thiscall AEC::init(AEC *this,int param_1) { void *pvVar1; pvVar1 = *(void **)(this + 0x10); *(int *)(this + 8) = param_1; *(undefined4 *)(this + 0x18) = 0x10000; *(undefined2 *)(this + 0x1c) = 1; *(undefined4 *)(this + 0x20) = 1; if (pvVar1 != (void *)0x0) { WebRtcAec_Free(pvVar1); } WebRtcAec_Create((void **)(this + 0x10)); WebRtcAec_Init(*(undefined4 **)(this + 0x10),param_1,param_1); WebRtcAec_set_config(*(long *)(this + 0x10),*(undefined8 *)(this + 0x18),*(uint *)(this + 0x20)); return; }
...............a to tamten prefix 'WebRtc'. To neni žádný real time clock nebo tak něco, ale tendlecten hele 'standard'/knihovna/api pro dělání webový real-time komunikace 😮 😁
A tamto AEC je věc, který se řiká echo cancellation a slouží to předevšim k potlačovávání zpětný vazby z reproduktorů zpátky do mikrofonu (myslimže se tomu řiká 'zpětnovazební smyčka' 😮) když třeba s někym mluvíte, a vubec tadlecta věc muže za to že lidi při videokonferencích mužou normálně mluvit a nemusej mit na uších ňáký vobrovský Otíkovský sluchátka, by tu zpětnou vazbu vyřídili takle ztlumením 😮 😜
tl;dr prostě to AEC je takovej bonus a ikdyž je jim prostrkávanej audiostream, nás to pro mluvení nebo posílání do kamerky, která je navíc na úúúúplně druhý straně baráku moc nemusí zajímat a mužeme to imo bezpečně vynechat 😁 😜
Z AEC vypadne stejnej počet vzorků jakej do něj spadne, takže resamplovadlo je nejspíš samostatná komponenta která dělá něco nezavisle na AEC. Když se to Resamplovadlo přeskočí, tak kamera jenom divně 'bzučí' a nedělá žádnej smysluplnej vystup.
jak to asi jako běhá v appce
AEC asi mužeme vynechat
Celý předělaný do pythonu tady hele, javovskej zdrojáček s naroubovaným kódem kterej sem si vypučila z Tapo appky je ve složšce tady hele
Resamplovadlo
from PseudoJava import jakoby_short_overflow class Resamplovadlo: _A = [255, 511, 1023, 2047, 4095, 8191, 16383, 32767] @staticmethod def a(var0: list, var1: int) -> list: var2 = [None] * (var1 << 1) for var3 in range(var1): var4 = Resamplovadlo.c(var0[var3]) var5 = var3 * 2 var2[var5] = var4 var2[var5 + 1] = var4 >> 8 return var2 @staticmethod def b(var0: list, var1: int) -> list: var2 = var1 // 2 var3 = [None] * var2 for var1 in range(var2): var4 = var1 * 2 var5 = var0[var4] # všechno identický s java verzí _x = (var0[var4 + 1] & 255) << 8 _y = var5 & 255 _z = _x | _y _z = jakoby_short_overflow(_z) var3[var1] = Resamplovadlo.d(_z) return var3 @staticmethod def c(var0: int) -> int: var1 = var0 ^ 85 var2 = (var1 & 15) << 4 var4 = (var1 & 112) >> 4 var5 = 0 if var4 != 0: if var4 != 1: var5 = (var2 + 264) << var4 - 1 else: var5 = var2 + 264 else: var5 = var2 + 8 var3 = var5 if (var1 & 128) == 0: var3 = -var3 return var3 @staticmethod def d(var0: int) -> int: var1 = 0 if var0 >= 0: var1 = 213 else: var2 = 85 var3 = -var0 - 1 var1 = var2 var0 = var3 if var3 < 0: var0 = 32767 var1 = var2 var5 = Resamplovadlo.e(var0, Resamplovadlo._A, 8) var6 = 0 if var5 >= 8: var6 = var1 ^ 127 else: var4 = var5 << 4 # tady byla konverze na char - pozor!!!!!!!! var7 = 0 if var5 < 2: var7 = var0 >> 4 else: var7 = var0 >> var5 + 3 var6 = (var7 & 15 | var4) ^ var1 # tady taky char return var6 @staticmethod def e(var0: int, var1: list, var2: int) -> int: for var3 in range(var2): if var0 <= var1[var3]: return var3 return var2 @staticmethod def f(var0: list) -> list: var1 = len(var0) var2 = [None] * (var1 << 1) for var3 in range(var1): var4 = var3 * 2 var2[var4] = var0[var3] var2[var4 + 1] = var0[var3] >> 8 return var2
Pomocný třídy z javovský package 'g', tak tomu název g zustal 😁 Dělá to v settrech/gettrech různý bitový operace, jinak tam neni nic zajimavýho
class Cecko: def __init__(self): # self._a : long = 0 self._a: int = 0 self._b: int = 0 self._c: int = 0 def a(self) -> int: return self._a & 8589934591 # L def b(self) -> int: return self._c & 511 def c(self) -> int: return self._b & 63 def d(self, var1: int) -> None: self._a = var1 & 8589934591 # L def e(self, var1: int) -> None: self._c = var1 & 511 def f(self, var1: int) -> None: self._b = var1 & 63 class Acko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: int = 0 self._f: int = 0 self._g: int = 0 self._h: int = 0 self._i: int = 0 self._j: Cecko = None def a(self) -> int: return self._i & 1 def b(self) -> int: return self._a & 255 def c(self) -> int: return self._b & 1 def d(self) -> int: return self._d & 1 def e(self) -> int: return self._f & 1 def f(self) -> Cecko: return self._j def g(self) -> int: return self._e & 1 def h(self) -> int: return self._c & 1 def i(self) -> int: return self._g & 1 def j(self) -> int: return self._h & 1 def k(self, var1: int) -> None: self._i = var1 & 1 def l(self, var1: int) -> None: self._a = var1 & 255 def m(self, var1: int) -> None: self._b = var1 & 1 def n(self, var1: int) -> None: self._d = var1 & 1 def o(self, var1: int) -> None: self._f = var1 & 1 def p(self, var1: Cecko) -> None: self._j = var1 def q(self, var1: int) -> None: self._e = var1 & 1 def r(self, var1: int) -> None: self._c = var1 & 1 def s(self, var1: int) -> None: self._g = var1 & 1 def t(self, var1: int) -> None: self._h = var1 & 1 class Becko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: int = 0 self._f: int = 0 self._g: int = 0 self._h: int = 0 def a(self) -> int: return self._g & 3 def b(self) -> int: return self._h & 15 def c(self) -> int: return self._e & 8191 def d(self) -> int: return self._c & 1 def e(self) -> int: return self._a & 255 def f(self) -> int: return self._b & 1 def g(self) -> int: return self._d & 1 def h(self) -> int: return self._f & 3 def i(self, var1: int) -> None: self._g = var1 & 3 def j(self, var1: int) -> None: self._h = var1 & 15 def k(self, var1: int) -> None: self._e = var1 & 8191 def l(self, var1: int) -> None: self._c = var1 & 1 def m(self, var1: int) -> None: self._a = var1 & 255 def n(self, var1: int) -> None: self._b = var1 & 1 def o(self, var1: int) -> None: self._d = var1 & 1 def p(self, var1: int) -> None: self._f = var1 & 3 class Decko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: int = 0 self._f: int = 0 self._g: int = 0 self._h: int = 0 self._i: int = 0 self._j: Cecko = None self._k: int = 0 self._l: list = [] # datovej typ třída 'e' self._m: int = 0 def a(self) -> int: return self._i & 1 def b(self) -> int: return self._k & 255 def c(self) -> int: return self._d & 3 def d(self) -> int: return self._g & 3 def e(self) -> int: return self._e & 4095 def f(self) -> int: return self._j & 255 def g(self) -> int: return self._b & 1 def h(self) -> int: return self._a & 255 def i(self) -> int: # return self._f & '\uffff' return self._f & 65535 def j(self) -> int: return self._h & 31 def k(self) -> int: return self._c & 1 def l(self, var1: int) -> None: self._m = var1 & -1 def m(self, var1: int) -> None: self._i = var1 & 1 def n(self, var1: int) -> None: self._k = var1 & 255 def o(self, var1: list) -> None: self._l = var1 def p(self, var1: int) -> None: self._d = var1 & 3 def q(self, var1: int) -> None: self._g = var1 & 3 def r(self, var1: int) -> None: self._e = var1 & 4095 def s(self, var1: int) -> None: self._j = var1 & 255 def t(self, var1: int) -> None: self._b = var1 & 1 def u(self, var1: int) -> None: self._a = var1 & 255 def v(self, var1: int) -> None: # self._f = var1 & '\uffff' self._f = var1 & 65535 def w(self, var1: int) -> None: self._h = var1 & 31 def x(self, var1: int) -> None: self._c = var1 & 1 class Ecko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 def a(self) -> int: return self._c & 8191 def b(self) -> int: # return self._a & '\uffff' return self._a & 65535 def c(self) -> int: return self._b & 7 def d(self, var1: int) -> None: self._c = var1 & 8191 def e(self, var1: int) -> None: # self._a = var1 & '\uffff' self._a = var1 & 65535 def f(self, var1: int) -> None: self._b = var1 & 7 class Efko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: int = 0 self._f: int = 0 self._g: int = 0 self._h: int = 0 self._i: int = 0 self._j: int = 0 self._k: int = 0 self._l: int = 0 self._m: int = 0 self._n: int = 0 self._o: int = 0 self._p: int = 0 self._q: int = 0 def A(self, var1: int) -> None: self._o = var1 & 1 def B(self, var1: int) -> None: self._p = var1 & 1 def C(self, var1: int) -> None: self._q = var1 & 255 def D(self, var1: int) -> None: # self._c = var1 & '\uffff' self._c = var1 & 65535 def E(self, var1: int) -> None: self._f = var1 & 1 def F(self, var1: int) -> None: self._e = var1 & 3 def G(self, var1: int) -> None: self._j = var1 & 3 def H(self, var1: int) -> None: self._b = var1 & 255 def a(self) -> int: return self._n & 1 def b(self) -> int: return self._h & 1 def c(self) -> int: return self._m & 1 def d(self) -> int: return self._g & 1 def e(self) -> int: return self._l & 1 def f(self) -> int: return self._k & 1 def g(self) -> int: return self._d & 3 def h(self) -> int: return self._i & 1 def i(self) -> int: return self._a & 16777215 def j(self) -> int: return self._o & 1 def k(self) -> int: return self._p & 1 def l(self) -> int: return self._q & 255 def m(self) -> int: # return self._c & '\uffff' return self._c & 65535 def n(self) -> int: return self._f & 1 def o(self) -> int: return self._e & 3 def p(self) -> int: return self._j & 3 def q(self) -> int: return self._b & 255 def r(self, var1: int) -> None: self._n = var1 & 1 def s(self, var1: int) -> None: self._h = var1 & 1 def t(self, var1: int) -> None: self._m = var1 & 1 def u(self, var1: int) -> None: self._g = var1 & 1 def v(self, var1: int) -> None: self._l = var1 & 1 def w(self, var1: int) -> None: self._k = var1 & 1 def x(self, var1: int) -> None: self._d = var1 & 3 def y(self, var1: int) -> None: self._i = var1 & 1 def z(self, var1: int) -> None: self._a = var1 & 16777215 class Gecko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: int = 0 self._f: int = 0 self._g: int = 0 self._h: int = 0 self._i: int = 0 self._j: int = 0 self._k: int = 0 self._l: int = 0 self._m: int = 0 self._n: int = 0 self._o: int = 0 self._p: list = [] # list třídy 'h' self._q: int = 0 def A(self, var1: int) -> None: self._e = var1 & 4095 def B(self, var1: int) -> None: self._j = var1 & 255 def C(self, var1: int) -> None: self._b = var1 & 1 def D(self, var1: int) -> None: self._a = var1 & 255 def E(self, var1: int) -> None: self._h = var1 & 31 def F(self, var1: int) -> None: self._c = var1 & 1 def a(self) -> int: return self._i & 1 def b(self) -> int: return self._k & 255 def c(self) -> int: return self._m & 8191 def d(self) -> int: return self._o & 4095 def e(self) -> int: # return self._f & '\uffff' return self._f & 65535 def f(self) -> int: return self._d & 3 def g(self) -> int: return self._g & 3 def h(self) -> int: return self._l & 7 def i(self) -> int: return self._n & 15 def j(self) -> int: return self._e & 4095 def k(self) -> int: return self._j & 255 def l(self) -> int: return self._b & 1 def m(self) -> int: return self._a & 255 def n(self) -> int: return self._h & 31 def o(self) -> int: return self._c & 1 def p(self, var1: int) -> None: self._q = var1 & -1 def q(self, var1: int) -> None: self._i = var1 & 1 def r(self, var1: int) -> None: self._k = var1 & 255 def s(self, var1: int) -> None: self._m = var1 & 8191 def t(self, var1: list) -> None: self._p = var1 def u(self, var1: int) -> None: self._o = var1 & 4095 def v(self, var1: int) -> None: # self._f = var1 & '\uffff' self._f = var1 & 65535 def w(self, var1: int) -> None: self._d = var1 & 3 def x(self, var1: int) -> None: self._g = var1 & 3 def y(self, var1: int) -> None: self._l = var1 & 7 def z(self, var1: int) -> None: self._n = var1 & 15 class Hacko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: int = 0 def a(self) -> int: return self._e & 4095 def b(self) -> int: return self._c & 8191 def c(self) -> int: return self._b & 7 def d(self) -> int: return self._d & 15 def e(self) -> int: return self._a & 255 def f(self, var1: int) -> None: self._e = var1 & 4095 def g(self, var1: int) -> None: self._c = var1 & 8191 def h(self, var1: int) -> None: self._b = var1 & 7 def i(self, var1: int) -> None: self._d = var1 & 15 def j(self, var1: int) -> None: self._a = var1 & 255 class Icko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: int = 0 self._f: int = 0 self._g: int = 0 def getB(self) -> int: return b def getD(self) -> int: return d def getF(self) -> int: return f def a(self) -> int: return self._a & 15 def b(self) -> int: return self._b & 7 def c(self) -> int: return self._d & 32767 def d(self) -> int: return self._f & 32767 def e(self) -> int: return self._c & 1 def f(self) -> int: return self._e & 1 def g(self) -> int: return self._g & 1 def h(self, var1: int) -> None: self._a = var1 & 15 def i(self, var1: int) -> None: self._b = var1 & 7 def j(self, var1: int) -> None: self._d = var1 & 32767 def k(self, var1: int) -> None: self._f = var1 & 32767 def l(self, var1: int) -> None: self._c = var1 & 1 def m(self, var1: int) -> None: self._e = var1 & 1 def n(self, var1: int) -> None: self._g = var1 & 1
mp2t packetovadlo
from PseudoJava import * import enum import g as g class Becko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: int = 0 def a(self) -> int: return self._b def b(self) -> int: return self._a def c(self) -> int: return self._d def d(self) -> int: return self._c def e(self, var1: int) -> None: self._e = var1 def f(self, var1: int) -> None: self._b = var1 def g(self, var1: int) -> None: self._a = var1 def h(self, var1: int) -> None: self._d = var1 def i(self, var1: int) -> None: self._c = var1 class Cecko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: int = 0 self._e: list = [] def a(self) -> int: return self._b def b(self) -> int: return self._a def c(self) -> list: return self._e def d(self) -> int: return self._c def e(self) -> int: return self._d def f(self, var1: int) -> None: self._b = var1 def g(self, var1: int) -> None: self._a = var1 def h(self, var1: int) -> None: self._c = var1 def i(self, var1: int) -> None: self._d = var1 class Acko: def __init__(self): self._a: int = 0 self._b: int = 0 self._c: int = 0 self._d: Cecko = None def a(self) -> int: return self._b def b(self) -> int: return self._a def c(self) -> Cecko: return self._d def d(self, var1: int) -> None: self._b = var1 def e(self, var1: int) -> None: self._a = var1 def f(self, var1: Cecko) -> None: self._d = var1 def g(self, var1: int) -> None: self._c = var1 class Decko: a = [0 for i in range(256)] # celá tadle divná hovadinka s nějakou tajemnou nudlí pseudo mp2t packetů nemá žádnej smysl jenom se vezme dýlka toho pole # v několika zřetězenejch funkcích ruzně se to podělí/pronásobí/etc a vysledná hodnota se použije jako dýlka pole skovanýho # v tý statický proměný 'a' a vyplní se to nulama # mužem to spočitat manualně a dojdem k použitý hodnotě '256' """ @staticvariable a = d.decode_I("00000000b71dc1046e3b8209d926430ddc7604136b6bc517b24d861a0550471eb8ed08260ff0c922d6d68a2f61cb4b2b649b0c35d386cd310aa08e3cbdbd4f3870db114cc7c6d0481ee09345a9fd5241acad155f1bb0d45bc2969756758b5652c836196a7f2bd86ea60d9b6311105a6714401d79a35ddc7d7a7b9f70cd665e74e0b6239857abe29c8e8da191399060953cc0278b8bdde68f52fba582e5e66486585b2bbeef46eaba3660a9b7817d68b3842d2fad3330eea9ea16ada45d0b6ca0906d32d42770f3d0fe56b0dd494b71d94c1b36c7fb06f7c32220b4ce953d75ca28803af29f9dfbf646bbb8fbf1a679fff4f63ee143ebffe59acdbce82dd07dec77708634c06d4730194b043dae56c539ab0682271c1b4323c53d002e7220c12acf9d8e1278804f16a1a60c1b16bbcd1f13eb8a01a4f64b057dd00808cacdc90c07ab9778b0b6567c69901571de8dd475dbdd936b6cc0526fb5e6116202fbd066bf469f5e085b5e5ad17d1d576660dc5363309b4dd42d5a490d0b1944ba16d84097c6a5ac20db64a8f9fd27a54ee0e6a14bb0a1bffcad60bb258b23b69296e2b22f2bad8a98366c8e41102f83f60dee87f35da9994440689d9d662b902a7bea94e71db4e0500075e4892636e93e3bf7ed3b6bb0f38c7671f7555032fae24df3fe5ff0bcc6e8ed7dc231cb3ecf86d6ffcb8386b8d5349b79d1edbd3adc5aa0fbd8eee00c6959fdcd6d80db8e6037c64f643296087a858bc97e5cad8a73ebb04b77560d044fe110c54b383686468f2b47428a7b005c3d66c158e4408255535d43519e3b1d252926dc21f0009f2c471d5e28424d1936f550d8322c769b3f9b6b5a3b26d6150391cbd40748ed970afff0560efaa011104dbdd014949b93192386521d0e562ff1b94beef5606dadf8d7706cfcd2202be2653deae6bc1ba9eb0b0668efb6bb27d701a6e6d3d880a5de6f9d64da6acd23c4ddd0e2c004f6a1cdb3eb60c97e8d3ebdc990ffb910b6bcb4a7ab7db0a2fb3aae15e6fbaaccc0b8a77bdd79a3c660369b717df79fa85bb4921f4675961a163288ad0bf38c742db081c330718599908a5d2e8d4b59f7ab085440b6c95045e68e4ef2fb4f4a2bdd0c479cc0cd43217d827b9660437f4f460072f85bc176fd0b86684a16476c93300461242dc565e94b9b115e565a1587701918306dd81c353d9f0282205e065b061d0bec1bdc0f51a69337e6bb52333f9d113e8880d03a8dd097243acd5620e3eb152d54f6d4297926a9c5ce3b68c1171d2bcca000eac8a550add6124d6cd2cb6b2fdf7c76eedbc1cba1e376d660e7aff023ea18ede2ee1dbda5f0aaa064f4738627f9c49be6fd09fdb889bee0798d67c63a80d0dbfb84d58bbc9a62967d9ebbb03e930cadff97b110b0af060d71abdf2b32a66836f3a26d66b4bcda7b75b8035d36b5b440f7b1"); @staticmethod def decode_I(src: str) -> list: #_d = d.decode_B(src) # víme že to dycky krmíme samejma nulama return [0 * (len(src)*4)] @staticmethod def decode_B(src: str) -> bytes: return bytes(len(src) // 2) """ class Codec(enum.Enum): # inkrementovaný vo jedničku voproti javě protože jinak to nefunguje # (nejspiš bug/nedostatek dekompilatoru) MUXTS_CODEC_HEVC = 1 MUXTS_CODEC_AVC = 2 MUXTS_CODEC_AAC = 3 MUXTS_CODEC_PCMA = 4 class Paketovadlo: """ private final byte[] a = new byte[188]; private int b = 0; private ByteBuffer c = ByteBuffer.allocateDirect(512000); private final h0.a d; private int e; private int f; private int g; private final List<h0.g.e> h; private final List<h> i; private boolean j; """ def __init__(self): var1 = Acko() var1.e(0) var1.d(0) var1.g(0) var1.f(Cecko()) self._a = [0 for i in range(188)] self._b = 0 self._c = [0 for i in range(512000)] # puvodně byteBufer self._d = var1 self._g = 0 self._h = [] self._i = [] self._j = False def c(self, var1: int, var2: int, var3: int) -> int: return jakoby_byte_overflow(var1 << var2 | var3) def d(self, var1: int, var2: int, var3: int) -> int: return jakoby_int_overflow(var1 << var2 | var3) def e(self, var1: int, var2: int, var3: int) -> int: return jakoby_long_overflow(var1 << var2 | var3) def f(self, var1: int) -> list: return [ jakoby_byte_overflow(var1 >> 24 & 255), jakoby_byte_overflow(var1 >> 16 & 255), jakoby_byte_overflow(var1 >> 8 & 255), jakoby_byte_overflow(var1 & 255), ] def g(self, var1: int) -> list: return [ jakoby_byte_overflow(jakoby_int_overflow(var1 >> 56 & 255)), jakoby_byte_overflow(jakoby_int_overflow(var1 >> 48 & 255)), jakoby_byte_overflow(jakoby_int_overflow(var1 >> 40 & 255)), jakoby_byte_overflow(jakoby_int_overflow(var1 >> 32 & 255)), jakoby_byte_overflow(jakoby_int_overflow(var1 >> 24 & 255)), jakoby_byte_overflow(jakoby_int_overflow(var1 >> 16 & 255)), jakoby_byte_overflow(jakoby_int_overflow(var1 >> 8 & 255)), jakoby_byte_overflow(jakoby_int_overflow(var1 & 255)), ] def h(self, var1: int) -> int: return var1 + 1 & 15 def i(self, var1: list, var2: int, var3: int) -> int: # var4 = 255 var4 = -1 for var5 in range(var2, var2 + var3): var6 = var1[var5] var4 = Decko.a[(var4 >> 24 ^ var6) & 255] ^ var4 << 8 return var4 def j(self, var1: int) -> int: if self._b + var1 > 188: return -4 else: for i in range(var1): # self._a[self._b + i] = 255 self._a[self._b + i] = -1 # pozor!! self._b += var1 return 0 def o(self, var1: int) -> None: _i = self.i(self._a, var1, self._b - var1) _f = self.f(_i) jakobyArrayCopy(_f, 0, self._a, self._b, 4) self._b += 4 def p(self) -> None: if self._b == 188: self._c += self._a[:188] # možná blbost # for _ in self self._b = 0 # var3 == g.Cecko def q(self, var1: int, var2: bool, var3: object) -> None: var4 = g.Acko() var4.l(var1) var4.m(0) var4.n(0) if var2: var4.q(255) var4.r(0) var4.p(var3) else: var4.q(0) var4.r(0) if 1 == var1: var4.r(0) var4.o(0) var4.s(0) var4.t(0) var4.k(0) var5 = self.c(0, 0, var4.b()) var8 = self._a var1 = self._b var8[var1] = var5 self._b = var1 + 1 if var4.b() != 0: var5 = self.c( self.c( self.c( self.c( self.c( self.c( self.c(self.c(0, 0, var4.c()), 1, var4.h()), 1, var4.d(), ), 1, var4.g(), ), 1, var4.e(), ), 1, var4.i(), ), 1, var4.j(), ), 1, var4.a(), ) var8 = self._a var1 = self._b var8[var1] = var5 self._b = var1 + 1 if var2: var6 = 0 var5 = jakoby_byte_overflow( jakoby_int_overflow(var4.f().a() >> 25 & 255 | var6) ) var8 = self._a var1 = self._b var8[var1] = var5 self._b = var1 + 1 jakobyArrayCopy( self.f( self.d((var6 + (var4.f().a() & 33554431)), 6, var4.f().c()) << 1 | var4.f().b() & 256 ), 0, self._a, self._b, 4, ) self._b += 4 var5 = var4.f().b() | 0 var8 = self._a var1 = self._b var8[var1] = var5 self._b = var1 + 1 else: self.j(var4.b() - 1) def s(self, var1: int, var2: int, var3: int, var4: int) -> None: var5 = g.Becko() var5.m(71) var5.n(0) var5.l(var2) var5.o(0) var5.k(var1) var5.p(0) var5.i(var3) var5.j(var4) jakobyArrayCopy( self.f( self.d( self.d( self.d( self.d( self.d( self.d( self.d(self.d(0, 0, var5.e()), 1, var5.f()), 1, var5.d(), ), 1, var5.g(), ), 13, var5.c(), ), 2, var5.h(), ), 2, var5.a(), ), 4, var5.b(), ) ), 0, self._a, self._b, 4, ) self._b = 4 if 1 == var2 and 1 == var3: self._a[4] = 0 self._b = 4 + 1 def t(self, var1: list, var2: bool) -> None: if var1 is not None and len(var1) > 0: var3 = g.Decko() var3.u(0) var3.t(255) var3.x(0) var3.p(255) var4 = 0 var5 = 0 if var2: var4 = 9 var5 = 5 else: var4 = 8 var5 = 4 var3.r(var4 + len(var1) * 4) var3.v(1) var3.q(255) var3.w(0) var3.m(255) var3.s(0) var3.n(0) var3.o(var1) var3.l(1) jakobyArrayCopy( self.g( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e(0, 0, var3.h()), 1, var3.g(), ), 1, var3.k(), ), 2, var3.c(), ), 12, var3.e(), ), 16, var3.i(), ), 2, var3.d(), ), 5, var3.j(), ), 1, var3.a(), ), 8, var3.f(), ), 8, var3.b(), ) ), 0, self._a, self._b, 8, ) self._b += 8 for cosi in var1: jakobyArrayCopy( self.f( self.d( self.d(self.d(0, 0, cosi.b()), 3, cosi.c()), 13, cosi.a() ) ), 0, self._a, self._b, 4, ) self._b += 4 self.o(var5) self.j(188 - self._b) self.p() # var5 == g.Icko def u(self, var1: int, var2: int, var3: int, var4: int, var5: object) -> None: var6 = g.Efko() var6.z(1) var6.H(var2) var6.D(var1) var6.x(2) var6.F(0) var6.E(0) var6.u(0) var6.s(0) var6.y(0) var6.G(var3) var6.w(0) var6.v(0) var6.t(0) var6.r(0) var6.A(0) var6.B(0) var6.C(var4) jakobyArrayCopy( self.g( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( 0, 0, var6.i(), ), 8, var6.q(), ), 16, var6.m(), ), 2, var6.g(), ), 2, var6.o(), ), 1, var6.n(), ), 1, var6.d(), ), 1, var6.b(), ), 1, var6.h(), ), 2, var6.p(), ), 1, var6.f(), ), 1, var6.e(), ), 1, var6.c(), ), 1, var6.a(), ), 1, var6.j(), ), 1, var6.k(), ) ), 0, self._a, self._b, 8, ) self._b += 8 var7 = jakoby_byte_overflow(var6.l() | 0) var8 = self._a var1 = self._b var8[var1] = var7 self._b = var1 + 1 if 2 == var6.p() and 5 == var6.l(): var7 = self.c(self.c(self.c(0, 0, var5.a()), 3, var5.b()), 1, var5.e()) var8 = self._a var1 = self._b var8[var1] = var7 self._b = var1 + 1 jakobyArrayCopy( self.f( self.d( self.d( self.d(self.d(0, 0, var5.c()), 1, var5.f()), 15, var5.d() ), 1, var5.g(), ) ), 0, self._a, self._b, 4, ) self._b += 4 if 3 == var6.p() and 10 == var6.l(): var5.h(3) var7 = self.c(self.c(self.c(0, 0, var5.a()), 3, var5.b()), 1, var5.e()) var10 = self._a var1 = self._b var10[var1] = var7 self._b = var1 + 1 jakobyArrayCopy( self.f( self.d( self.d( self.d(self.d(0, 0, var5.c()), 1, var5.f()), 15, var5.d() ), 1, var5.g(), ) ), 0, self._a, self._b, 4, ) self._b += 4 if 224 == var2 and 27 == self._d.c().c()[self._f].d(): var9 = self._a var9[self._b] = 0 self._b += 1 var9[self._b] = 0 self._b += 1 var9[self._b] = 0 self._b += 1 var9[self._b] = 1 self._b += 1 var9[self._b] = 9 self._b += 1 var9[self._b] = -16 self._b += 1 def v(self, var1: list, var2: int, var3: bool) -> None: if var1 is not None and len(var1) > 0: var4 = g.Gecko() var4.D(2) var4.C(1) var4.F(0) var4.w(255) var5 = 0 var6 = 0 if var3: var5 = 13 var6 = 5 else: var5 = 12 var6 = 4 var4.A(var5 + len(var1) * 5) var4.v(var2) var4.x(255) var4.E(0) var4.q(255) var4.B(0) var4.r(0) var4.y(255) var4.s(256) var4.z(255) var4.u(0) var4.t(var1) var4.p(1) jakobyArrayCopy( self.g( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e( self.e(0, 0, var4.m()), 1, var4.l(), ), 1, var4.o(), ), 2, var4.f(), ), 12, var4.j(), ), 16, var4.e(), ), 2, var4.g(), ), 5, var4.n(), ), 1, var4.a(), ), 8, var4.k(), ), 8, var4.b(), ) ), 0, self._a, self._b, 8, ) self._b += 8 jakobyArrayCopy( self.f( self.d( self.d( self.d(self.d(0, 0, var4.h()), 13, var4.c()), 4, var4.i() ), 12, var4.d(), ) ), 0, self._a, self._b, 4, ) self._b += 4 for cosi in var1: var8 = self.c(0, 0, cosi.e()) var10 = self._a var10[self._b] = var8 self._b += 1 jakobyArrayCopy( self.f( self.d( self.d( self.d(self.d(0, 0, cosi.c()), 13, cosi.b()), 4, cosi.d(), ), 12, cosi.a(), ) ), 0, self._a, self._b, 4, ) self._b += 4 self.o(var6) self.j(188 - self._b) self.p() def a(self, var1: int, var2: int) -> None: self._d.c().g(var1) self._d.c().f(0) self._d.c().h(0) self._d.c().i(var2) def b(self, var1: int, codec: Codec) -> None: var3 = Becko() var3.g(var1) var3.f(0) var1 = codec.value if var1 != 1: if var1 != 2: if var1 != 3: if var1 == 4: var3.h(192) var3.e(17) var3.i(144) self._e = len(self._d.c().c()) else: var3.h(192) var3.e(17) var3.i(15) self._e = len(self._d.c().c()) else: var3.h(224) var3.e(18) var3.i(27) self._f = len(self._d.c().c()) else: var3.h(224) var3.e(18) var3.i(36) self._f = len(self._d.c().c()) var1 = self._d.c().d() self._d.c().h(var1 + 1) self._d.c().c().append(var3) def k(self, var1: list, var2: int, var3: int) -> list: if var1 is None: return None else: var5 = var1 if len(var1) > var2: var5 = jakobyUtilArraysCopyOf(var1, var2) self.n() self.a(66, 1) var6 = Codec.MUXTS_CODEC_PCMA self.b(68, var6) self.r(var5, var3, var6) return self.l() def l(self) -> list: var1 = len(self._c) if var1 > 0: var2 = [x for x in self._c] return var2 else: return None def m(self) -> None: self._c = None def n(self) -> None: self._b = 0 # self._c = [0 for i in range(512000)] self._c = [] self._d.e(0) self._d.d(0) self._d.g(0) self._d.f(Cecko()) self._g = 0 self._h = [] self._i = [] def r(self, var1: list, var2: int, var4: Codec) -> None: if var1 is not None: var5 = 0 var6 = 0 var7 = 0 var8 = 0 var9 = 0 var14 = False outer_break = False while True: # label81 while True: # label92 var5 = len(var1) if Codec.MUXTS_CODEC_HEVC != var4 and Codec.MUXTS_CODEC_AVC != var4: if Codec.MUXTS_CODEC_AAC == var4: var6 = self._e var7 = var5 + 8 break if Codec.MUXTS_CODEC_PCMA != var4: var14 = False var7 = 0 var8 = 0 var9 = 0 outer_break = True break var7 = self._e else: var7 = self._f var6 = var7 var7 = var5 break if outer_break: break var9 = var6 var8 = var7 var7 = 0 var14 = False break while True: while var5 > 0: var13 = Becko() var15 = 0 if self._g == 0: var12 = g.Ecko() var12.e(self._d.c().e()) var12.d(self._d.c().b()) var12.f(255) self._h.append(var12) for var15 in range(self._d.c().d()): var16 = g.Hacko() var13 = self._d.c().c()[var15] var16.g(var13.b()) var16.j(var13.d()) var16.f(0) var16.h(255) var16.i(255) self._i.append(var16) if self._g % 64 == 0: self.s(self._d.b(), 1, 1, self._d.a()) self.t(self._h, True) var15 = self.h(self._d.a()) self._d.d(var15) self._g += 1 self.s(self._d.c().b(), 1, 1, self._d.c().a()) self.v(self._i, self._d.c().e(), True) var15 = self.h(self._d.c().a()) self._d.c().f(var15) self._g += 1 else: var17 = None if var14: var17 = self._d.c().c()[var9] var20 = [] if var5 >= 184: self.s(var17.b(), 0, 1, var17.a()) var20 = self._a var15 = self._b jakobyArrayCopy(var1, var7, var20, var15, 188 - var15) var15 = self._b var5 -= 188 - var15 self._b = var15 + (188 - var15) var7 += 188 - var15 else: self.s(var17.b(), 0, 3, var17.a()) self.q(188 - var5 - 5, False, g.Cecko()) var20 = self._a var15 = self._b jakobyArrayCopy(var1, var7, var20, var15, 188 - var15) var15 = self._b var7 += 188 - var15 var5 -= 188 - var15 self._b = var15 + (188 - var15) var17.f(self.h(var17.a())) self._g += 1 self.p() else: var17 = self._d.c().c()[var9] self.s(var17.b(), 1, 3, var17.a()) var18 = g.Cecko() var18.d(var2) var18.f(255) var18.e(0) if self._j: self.q(7, True, var18) elif ( Codec.MUXTS_CODEC_AAC != var4 and Codec.MUXTS_CODEC_PCMA != var4 ): self.q(7, True, var18) else: self.q(1, False, var18) var19 = g.Icko() var19.h(2) var19.l(255) var19.m(255) var19.n(255) var19.i(jakoby_int_overflow(var2 >> 30)) var19.j(jakoby_int_overflow(var2 >> 15)) var19.k(var2) var13 = self._d.c().c()[var9] self.u(var8, var13.c(), 2, 5, var19) var13.f(self.h(var13.a())) self._g += 1 var6 = self._b if var5 >= 188 - var6: jakobyArrayCopy(var1, var7, self._a, var6, 188 - var6) var6 = self._b var7 += 188 - var6 var5 -= 188 - var6 self._b = var6 + (188 - var6) self.p() var14 = True return
pseudojavovský metody + hlídání přetejkání proměnejch
# napodoba javovskejch fcí system.ArrayCopy() a ArraysCopyOf() def jakobyArrayCopy( src_arr: list, src_pos: int, dest_arr: list, dest_pos: int, length: int ) -> None: # nemužem nahradit trikem s vodkazama/referencí # dest_arr_ = dest_arr[:dest_pos] +src_arr[src_pos:src_pos + length] + dest_arr[dest_pos + length:] for i in range(length): dest_arr[dest_pos + i] = src_arr[src_pos + i] def jakobyUtilArraysCopyOf(original: list, new_len: int) -> list: # mužeme nahradit trikem s vodkazama/referencí if new_len <= len(original): return original[:new_len] else: return original[:] + ([0] * (new_len - len(original))) # manualní hlídání overflow/přetečení proměnejch který byly puvodně # v nějakým javovským datovým typu # hooooodně se s tim v puvodním javovským zdrojačku čarovalo pro # optimimizalilaci kodu, pro nás je to boužel teďko nevyhoda # a navopak nas to počitaci víkon stojí :O :/ def jakoby_byte_overflow(byte): if byte > 127: return byte - (2 * 128) elif byte < -128: return byte + (2 * 128) else: return byte def jakoby_short_overflow(short): if short > 32767: return short - (2 * 32768) elif short < -32768: return short + (2 * 32768) else: return short # zbejvajicí int a long nejspíš nejsou nutný ale kdo ví 😁 def jakoby_int_overflow(int_): if int_ > 2147483647: return int_ - (2 * 2147483648) elif int_ < -2147483648: return int_ + (2 * 2147483648) else: return int_ def jakoby_long_overflow(long_): if long_ > 9223372036854775807: return long_ - (2 * 9223372036854775808) elif long_ < -9223372036854775808: return long_ + (2 * 9223372036854775808) else: return long_
podědění z HttpMediaSession, se mi nechtělo zkoumat coto je ten generátor co to vrací a jak to jako funguje tak sem to ucvakla 😁
takle se to nemá dělat 😮 😮
import asyncio import hashlib import json import logging import random import warnings from asyncio import StreamReader, StreamWriter, Task, Queue from json import JSONDecodeError from typing import Optional, Mapping, Generator, MutableMapping from pytapo.media_stream.response import HttpMediaResponse from pytapo import HttpMediaSession logger = logging.getLogger(__name__) class HttpAudioSession(HttpMediaSession): def __init__( self, ip: str, cloud_password: str, username: str = "admin", super_secret_key: str = "dadada" ): super().__init__(ip=ip,cloud_password=cloud_password,super_secret_key=super_secret_key) async def transceive_audio( self, data: str, mimetype: str = "audio/mp2t", session: int = None, encrypt: bool = False, no_data_timeout=1.0, ) -> None: sequence = None queue = None if mimetype != "application/json" and session is None: raise ValueError("Non-JSON streams must always be bound to a session") if mimetype == "application/json": j = json.loads(data) if "type" in j and j["type"] == "request": # Use random high sequence number to avoid collisions # with sequence numbers from server in queue # dispatching sequence = random.randint(1000, 0x7FFF) j["seq"] = sequence data = json.dumps(j, separators=(",", ":")) if ( (sequence is None) and (session is None) or (session is not None and session not in self._sessions) ): raise ValueError( "Data is not a request and no existing session has been found" ) if session is not None: queue = self._sessions[session] if sequence is not None: queue = asyncio.Queue(128) self._sequence_numbers[sequence] = queue if type(data) == str: data = data.encode() headers = { b"Content-Type": mimetype.encode(), } if encrypt: data = self._aes.encrypt(data) headers[b"X-If-Encrypt"] = b"1" headers[b"Content-Length"] = str(len(data)).encode() if mimetype != "application/json": headers[b"X-If-Encrypt"] = str( int(encrypt) ).encode() # Always sent if data is not JSON if session is not None: headers[b"X-Session-Id"] = str( session ).encode() # If JSON, session is included in the payload if self.window_size is not None: headers[b"X-Data-Window-Size"] = str(self.window_size).encode() await self._send_http_request(b"--" + self.client_boundary, headers) chunk_size = 4096 # print("Sending:") for i in range(0, len(data), chunk_size): # print(data[i : i + chunk_size]) self._writer.write(data[i : i + chunk_size]) await self._writer.drain() self._writer.write(b"\r\n") await self._writer.drain() logger.debug( ( "{} request of type {} sent (sequence {}, session {})" ", expecting {} responses from queue {}" ).format( "Encrypted" if encrypt else "Plaintext", mimetype, sequence, session, self.window_size + 1, id(queue), ) ) async def transceive_keepSession( self, data: str, mimetype: str = "application/json", session: int = None, encrypt: bool = False, no_data_timeout=1.0, ) -> Generator[HttpMediaResponse, None, None]: sequence = None queue = None if mimetype != "application/json" and session is None: raise ValueError("Non-JSON streams must always be bound to a session") if mimetype == "application/json": j = json.loads(data) if "type" in j and j["type"] == "request": # Use random high sequence number to avoid collisions # with sequence numbers from server in queue # dispatching sequence = random.randint(1000, 0x7FFF) j["seq"] = sequence data = json.dumps(j, separators=(",", ":")) if ( (sequence is None) and (session is None) or (session is not None and session not in self._sessions) ): raise ValueError( "Data is not a request and no existing session has been found" ) if session is not None: queue = self._sessions[session] if sequence is not None: queue = asyncio.Queue(128) self._sequence_numbers[sequence] = queue if type(data) == str: data = data.encode() headers = { b"Content-Type": mimetype.encode(), } if encrypt: data = self._aes.encrypt(data) headers[b"X-If-Encrypt"] = b"1" headers[b"Content-Length"] = str(len(data)).encode() if mimetype != "application/json": headers[b"X-If-Encrypt"] = str( int(encrypt) ).encode() # Always sent if data is not JSON if session is not None: headers[b"X-Session-Id"] = str( session ).encode() # If JSON, session is included in the payload if self.window_size is not None: headers[b"X-Data-Window-Size"] = str(self.window_size).encode() await self._send_http_request(b"--" + self.client_boundary, headers) chunk_size = 4096 # print("Sending:") for i in range(0, len(data), chunk_size): # print(data[i : i + chunk_size]) self._writer.write(data[i : i + chunk_size]) await self._writer.drain() self._writer.write(b"\r\n") await self._writer.drain() logger.debug( ( "{} request of type {} sent (sequence {}, session {})" ", expecting {} responses from queue {}" ).format( "Encrypted" if encrypt else "Plaintext", mimetype, sequence, session, self.window_size + 1, id(queue), ) ) try: while True: coro = queue.get() if no_data_timeout is not None: try: resp: HttpMediaResponse = await asyncio.wait_for( coro, timeout=no_data_timeout ) except asyncio.exceptions.TimeoutError: print( "Server did not send a new chunk in {} sec (sequence {}" ", session {}), assuming the stream is over".format( no_data_timeout, sequence, session ) ) logger.debug( "Server did not send a new chunk in {} sec (sequence {}" ", session {}), assuming the stream is over".format( no_data_timeout, sequence, session ) ) break else: # No timeout, the user needs to cancel this externally resp: HttpMediaResponse = await coro logger.debug("Got one response from queue {}".format(id(queue))) if resp.session is not None: session = resp.session if resp.encrypted and isinstance(resp.plaintext, Exception): raise resp.plaintext yield resp break finally: pass # Ensure the queue is deleted even if the coroutine is canceled externally #if session in self._sessions: # del self._sessions[session]
a nakonec samotný dělání všech těch věcí. Když to pustíte tak uslyšíte nejvíc nejvěčího současnýho čechijskýho dezinformátora žijicího jak mluvil předtim než se někde v bruseli nakazil eurodemencí 😁 😁
from pytapo import Tapo import time import math import asyncio from HttpAudioSession import HttpAudioSession from ResamplovadloFix import Resamplovadlo from hfix import Paketovadlo class TapoTalk: # dýlka audiochunků # min. možná dýlka podle java zdrojačku je 320 zorků # předpokládám že idelál bude ňákej malej násobek čisla 80,160, nebo pravě 320 # hodnota by měla bejt v rosahu 1810 - 2177 (včetně) protože # v tomdlectom intervalu nám Packetovadlo generuje právě 8 chunků # který daj dohormady přesně těch 1504 bytů stejně jako v tamtom # jejich origo audiopacketu CHUNK_LEN = 320 * 6 # == 1920 # CHUNK_LEN = 1810 # minimální možnej počet zorků pro vyrobení osmi paketů # CHUNK_LEN = 2177 # maximální možnej počet zorků pro vyrobení osmi paketů # dylka puvodního audiopacketu TAPO_AUDIO_PCKT_LEN = 1504 def __init__(self, host, user, cloud_password, super_secret_key, encrypt=True): self._paketovadlo = Paketovadlo() # upravená HttpMediaSession z knihovy Tapo self._tapo_ses = HttpAudioSession( ip=host, cloud_password=cloud_password, username=user, super_secret_key=super_secret_key ) self._ses_id = -1 self._timestamp = 0 self._encrypt = encrypt # request kterej to dělá nazačátku zapnutí mluvení do kamery async def half_duplex_start(self) -> None: if not self._tapo_ses._started: await self._tapo_ses.start() # pole 'seq' nám přepiše tapo knihovna rt = self._tapo_ses.transceive_keepSession( data='{"type": "request","seq":1, "params": {"talk": {"mode": "half_duplex"}, "method": "get"}}', mimetype="application/json", encrypt=True, ) async for h in rt: self._ses_id = h.session # .......a navopak request kterej to dělá při skončení mluvení async def half_duplex_stop(self) -> None: # pole 'seq' nám zase přepiše knihovna rt = self._tapo_ses.transceive_keepSession( data='{"type":"request","seq":2,"params":{"stop":"null","method":"do"}}', mimetype="application/json", encrypt=True, session=self._ses_id, ) async for h in rt: pass # překodování *.wav souboru do *.mp2t/*.ts formátu kterýmu rozumí Tapokamerka # pomocí pučenýho zdrojáku z offiko android appky # wav by měl bejt: # monochannel PCMA encoded *.wav file, sample rate 8KHz, 16bit precision def process_audio_file(self, wav_filename) -> bytearray: wav = open(wav_filename, "rb") data = wav.read() print(data) # ucvaknem hlavičku wav souboru a načtem jenom zvukový data # (hlavička *.wav souboru je prej dycky 44 bytů dlouhá) # a převedem do javovskýho rosahu # funkce který jsme si vypučili z android appky vočekávaj hodnoty # bytů jako v javě, java dělá s bytama jinak než python, v pythonu # je byte v rosahu 0 až 255, v javě -128 až 127 data = [ int.from_bytes(data[44 + i : 45 + i], byteorder="big", signed=True) for i in range(len(data) - 44) ] stream = [] chunku = math.ceil(len(data) / TapoTalk.CHUNK_LEN) self._timestamp = round(time.time() * 1000) for i in range(chunku): chunk = data[i * TapoTalk.CHUNK_LEN : (i + 1) * (TapoTalk.CHUNK_LEN) + 0] # imo trošku lepčejšího zvuku s míň klapání de dosahnout jednoduše malým overlapem # sousednich packetů ale možná se mi to jenom zdá jak si to furt pouštim dokolečka 😮 😮 # chunk = data[ i * TapoTalk.CHUNK_LEN : (i+1) * (TapoTalk.CHUNK_LEN) + 80] chunk = Resamplovadlo.b(chunk, len(chunk)) # převedeme audiostream na mp2t pakety prostrčením tou javovskou třídou _len = len(chunk) chunk = self._paketovadlo.k(chunk, _len, self._timestamp) stream += chunk # zvedneme hodnotu _timestampu uplně stejně jako javovskej kód # skutečnej čas si to bere jenom nazačátku a ani tam možná neni skutečnej nutnej 😮 😜 # duležitej je už jenom relativní čas jednotlivejch paketů mp2t streamu self._timestamp += round(_len * 90000 / 8000) # převedeme z rosahu hodnot javovovejch bytů (-128 až 127) do rosahu # hodnot pythoních bytů (0 až 255) stream = [x if x > -1 else (256 + x) for x in stream] return bytearray(stream) # přehraje zakodovanej mp2t audiostream ze souboru # asi by to měl bejt *.ts soubor vyrobenej timdlectim algoritmusem # s *.ts souborama vyrobenejma třeba ffmpegem si to vubec nerozumělo async def _play_ts_file(self, ts_filename) -> None: await self.half_duplex_start() in_file = open(ts_filename, "rb") data = in_file.read() in_file.close() # packety posíláme po vosmy najednou stejně jako voni # když se to nedodrží tak se kamerka muže šprajcnout a uplně se zamkne pro # tcp komunikaci a musí se restartovat vyndáním šňůry zezdi (reset appkou neni možnej) packetu = math.ceil(len(data) / TapoTalk.TAPO_AUDIO_PCKT_LEN) for i in range(packetu): packet = data[ i * TapoTalk.TAPO_AUDIO_PCKT_LEN : (i + 1) * TapoTalk.TAPO_AUDIO_PCKT_LEN ] time.sleep( (TapoTalk.CHUNK_LEN) / (8000) / 2 ) # todlecto by mělo fungovat bez dělení dvojkou ne 😮 😮 await self._tapo_ses.transceive_audio( data=packet, encrypt=self._encrypt, session=self._ses_id ) await self.half_duplex_stop() # wrap na poslání jendoho audio packetu async def send_pcma_packet(self, data): rt = await self._tapo_ses.transceive_audio( data=data, encrypt=self._encrypt, session=self._ses_id ) # to samý jako process_audio_file ale pro jedinejch chunk def process_audio_chunk(self, chunk): chunku = math.ceil(len(chunk) / TapoTalk.CHUNK_LEN) if self._timestamp == 0: self._timestamp = round(time.time() * 1000) chunk = Resamplovadlo.b(chunk, len(chunk)) _len = len(chunk) chunk = self._paketovadlo.k(chunk, _len, self._timestamp) self._timestamp += round(_len * 90000 / 8000) chunk = [x if x > -1 else (256 + x) for x in chunk] return bytearray(chunk) # zpracovat + poslat skovaný v jediný metodě async def process_and_send_audio_chunk(self, data): data = self.process_audio_chunk(data) await self.send_pcma_packet(data) # zapsání wavu do *.ts def wav2ts(self, wav_filename, out_filename): stream = self.process_audio_file(wav_filename) out = open(out_filename, "wb") out.write(stream) out.close() # pokusná funkce na živý streamování ze souboru # jakože jestli to bude dost rychle současně číst ze souboru, převádět na mp2t a posilat do kamery # nóóóóóóóóóó jakštakš to de 😁 víc praktičtější použití je s multithreadingem async def stream_wav(self, wav_filename) -> None: await self.half_duplex_start() if not self._tapo_ses._started: await self._tapo_ses.start() wav = open(wav_filename, "rb") data = wav.read() data = [ int.from_bytes(data[44 + i : 45 + i], byteorder="big", signed=True) for i in range(len(data) - 44) ] chunku = math.ceil(len(data) / TapoTalk.CHUNK_LEN) self._timestamp = round(time.time() * 1000) stream = [] for i in range(chunku): chunk = data[i * TapoTalk.CHUNK_LEN : (i + 1) * TapoTalk.CHUNK_LEN] chunk = Resamplovadlo.b(chunk, len(chunk)) _len = len(chunk) chunk = self._paketovadlo.k(chunk, _len, self._timestamp) chunk = [x if x > -1 else (256 + x) for x in chunk] stream += chunk self._timestamp += round(_len * 90000 / 8000) if len(stream) >= 1504: chunk = bytes(stream[:1504]) stream = stream[1504:] rt = await self._tapo_ses.transceive_audio( data=chunk, encrypt=self._encrypt, session=self._ses_id ) time.sleep((TapoTalk.CHUNK_LEN) / (8000) / 2) await self.half_duplex_stop() # sync volání def ts2camera(self, ts_filename) -> None: asyncio.run(self._play_ts_file(ts_filename)) def streamovat(self, filename) -> None: asyncio.run(self.stream_wav(filename)) if __name__ == "__main__": IP = "192.168.0.123" USER = "greta" CLOUD_PASSWORD = "1234" SUPER_SECRET = "dadada" talk = TapoTalk(IP, USER, CLOUD_PASSWORD, SUPER_SECRET, encrypt=True) talk.wav2ts("fiala_8khz.wav", "fiala.ts") talk.ts2camera("fiala.ts")
stream z mikrofonu
vypne se normálně pomocí ctrl+C
import pyaudio import time from TapoTalk import TapoTalk import asyncio from multiprocessing import Queue IP = "192.168.0.123" USER = "greta" CLOUD_PASSWORD = "1234" SUPER_SECRET = "dadada" talk = TapoTalk(IP, USER, CLOUD_PASSWORD, SUPER_SECRET,encrypt=False) asyncio.run(talk.half_duplex_start()) audio = pyaudio.PyAudio() data = [] queue = Queue() def send_chunk(chunk_data): asyncio.run(talk.process_and_send_audio_chunk(chunk_data)) def callback(in_data, frame_count, time_info, flag): global data data += [ int.from_bytes(in_data[i : i + 1], byteorder="big", signed=True) for i in range(len(in_data)) ] while len(data) > TapoTalk.CHUNK_LEN: queue.put(data[: TapoTalk.CHUNK_LEN]) data = data[TapoTalk.CHUNK_LEN :] return in_data, pyaudio.paContinue stream = audio.open( format=pyaudio.paInt16, channels=1, rate=8000, output=False, input=True, stream_callback=callback, ) stream.start_stream() while stream.is_active(): try: if not queue.empty(): chunk = queue.get() send_chunk(chunk) time.sleep(0.01) except KeyboardInterrupt: break stream.stop_stream() print("Stream is stopped") stream.close() audio.terminate() asyncio.run(talk.half_duplex_stop()) print("hotovo!!!!!!!!!!!!!!!!!!!!!!!!!!!")
stream ze stdin
streamuje byty který tomu přicházej na stdin, třeba pomocí trubky
možný použití s ffmpegem třeba takle, ty argumenty ffmpegu řikaj aby do výstupu psal PCMA kódování s 16ti bitovou little-endianovou přesností do -ac1 monofoního -ar 8000 8kHz zvuku pokud možno v blocích velkejch 1504 bytů a a strkal to trubkou na stdin nějakýho dalšího procesu. Jo a takle ffmpeg nepiše hlavičku souboru, takže to zapisuje fakt jenom byty datovýho streamu 😮 😜
ffmpeg -y -i ~/aligator.wav -acodec pcm_s16le -f s16le -ac 1 -ar 8000 -blocksize 1504 pipe: | python3 trubka.py
import sys import threading import time import multiprocessing from multiprocessing import Queue import asyncio from TapoTalk import TapoTalk # ffmpeg -y -i ~/aligator.wav -acodec pcm_s16le -f s16le -ac 1 -ar 8000 -blocksize 1504 pipe: | python3 trubka.py IP = "192.168.0.123" USER = "greta" CLOUD_PASSWORD = "1234" SUPER_SECRET = "dadada" talk = TapoTalk(IP, USER, CLOUD_PASSWORD, SUPER_SECRET, encrypt=True) asyncio.run(talk.half_duplex_start()) queue = Queue() data = [] posilat = True def send_chunk(chunk_data): asyncio.run(talk.process_and_send_audio_chunk(chunk_data)) def process_buffer(): while True: try: if not queue.empty(): chunk = queue.get() send_chunk(chunk) time.sleep(TapoTalk.CHUNK_LEN / 8000 / 2) except KeyboardInterrupt: break if __name__ == "__main__": thread = multiprocessing.Process(target=process_buffer) thread.start() last = time.time() while True: try: if queue.qsize() > 32: time.sleep(0.01) continue in_data = sys.stdin.buffer.read(TapoTalk.CHUNK_LEN) if in_data is not b"": last = time.time() else: if time.time() - last > 5 and queue.empty(): print("dlouho nic neprislo a fronta je prazdna - koncim!!!!") thread.terminate() break data += [ int.from_bytes(in_data[i : i + 1], byteorder="big", signed=True) for i in range(len(in_data)) ] while len(data) > TapoTalk.CHUNK_LEN: queue.put(data[: TapoTalk.CHUNK_LEN]) data = data[TapoTalk.CHUNK_LEN :] time.sleep(0.01) except KeyboardInterrupt: break thread.join() asyncio.run(talk.half_duplex_stop()) print("hotovo!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
Tiskni
Sdílej:
Ještě si to jednou v klidu projdu večer u kávy, ale dotaz. Není jednodušší si trochu připlatit a koupit něco normálního? Nebo zábava je prostě zábava a přes to vlak nejede? :)
asi by to jako bylo víc jednoduší :D ale těch kamerek maj lidi doma už spousty a je škoda nemit všechny funkce když to je možný + todle tapo bylo eště vod ježiška sem se k tomu dostala až teďkonc a vo boží vůli se nesmí pochybovat takže ani vyměňovat ve vobchodě za něco jinýho nebo se rožčilovat coto je za čínckej bazmek kterej neumí tamto nebo todleto :D :D a samozdřejmě bylo zajimavý se v tom zdrojáčku hrabat :D ;D
joa kafe by se věčer pit nemělo páč to neni zdravý a člověk pak muže ponocovat až do půl druhý ráno třeba :P :D :D ;D
asi by to jako bylo víc jednoduší :D ale těch kamerek maj lidi doma už spousty a je škoda nemit všechny funkce když to je možnýJá myslim, že to je chválihodné, kupovat další krám by mělo uhlíkovou stopu :)
se teďko navopak zvyší poptávka po tapokamerkách + všude na servrech těďko navíc poběží go2rtc a repráčky který doteďka nic neďáli budou spotřebovávat děsně moc moc eletriky takže se uhliková stopa taky děsně moc moc zvedne :O ;D
Není jednodušší si trochu připlatit a koupit něco normálního?Můžeš dát tip? Onehdá jsem něco podobného hledal a v té hromadě podobnejch čínskejch backdorů do bytu je obtížný najít něco 'normálního', tak jsem to nakonec vzdal...
to máš víc lepčí :P ;D
třeba to sou zrovna ty hodný rusáci a čiňai :D ;D
...............ikdyž ten API port číslo 1984 u go2rtc je trošičku strašidelnej stejně jako pole dýlky 1488 v tom disassemblovaným zdrojáčku watermarku :O :D :D ;D
Jo a takle ffmpeg nepiše hlaviču souboru, takže to zapisuje fakt jenom byty datovýho streamuPosiela sa surový stream (t.j. bez meta dat).
Ináč, zverejnenie dekompilovanej verzie nie je porušením autorského zákonu?
jo piše to PCMA stream bez hlavičky *.wav souboru :D ;D
Ináč, zverejnenie dekompilovanej verzie nie je porušením autorského zákonu?
tady hele pišou že 'autorským právem není chráněn počítačový program, u něhož není naplněn požadavek jedinečnosti ani původnosti' takže například když nějakej číňan vyrobí nějakou svou vočesanou parodii mp2t streamu nebo když vykrade veřejnej zdrojáček WebRtc hele :D ;D
..............a taky sem si ten zdrojáček mohla celej vymyslet a ňákou nahodou neuvěřitelou se trefit a napsat uplně stejnej :D ;D
nj už to vidim :D :D to z toho vyrobil ten bystroušáákův nastroj 'black' na automatický formátování pythoního zdrojáčku :D ;D
rusák má to svý packetovadlo vo moc víc čitelnější hele ....jenom si lidi stěžujou že mu to vobčas restartuje tu tapokamerku takže tam možná ňáká vodlišnost bude se nato eště kouknu asi pak :O ;D
už přesně rok furt slyšíme jak jako ukčka vyhrávaj jak rusákum už každou chvilkou dojdou granáty/rakety/etc a přitom furt nic navopak postraníma kanálama se de dovědět že rusáci letos v lednu mobilizovali ňákejch novejch 700k lidí a k ukrajinckoruský hranici se blíží novejch 350k vojáků stejně jako loni......... pochybuju že má ukrajina z dlouhodobýho pohledu šanci vyhrát bez přímý učasti NATO ato si všichni fakt jako dóóóóóóóóbře rozmyslete jestli chcete by vám pávek&fiala&vystrčil&pekarka takovýdle vítězství ukrajiny zařidili :D :/ :D :/
rusákům to že tlučou xkrát menčí ukrajinu připadá ňák jakože 'čestný' a kdyby měli teďko válčit navopak s xkrát věčím NATO by se asi pokusili o první uder páč kdyby nastala stejná situace ale vobráceně by jim to už nepřipadalo fér + zadruhý by asi jako fakt neměli šanci jinak vyhrát proti silnějšímu nepříteli
pokud putin v koronavirovým lockdownu/karanténě :P :P skutečně ňák jakože zešílel a fakt se jako stal ňákým tim šileným diktátorem nejde u něj předpokládat symetrický cíle a uvažování se 'západníma' politikama třeba a zatimco my by sme vnimali statisice vojáků v pytlých jako šilenou ztrátu pro putina nemá třeba lickej život víc věčí cenu než hadr na podlahu třeba ato zlášť když má russko +- 3x víc lickejch zdrojů než ukrajina. a rozhodně si lickejch životů vážej míň než použitelnejch atomovek na skladech který by teďkonc měli šoupnout na kyjev + předpokládám že sou rusáci schopný zaplnit nebe 'konvenčníma zbraněma' a přetížit protizdušnou vobranu a udělat srovnatelný škody tzn. uplný zničení města i bez atomovek viz. jak se ty americký generálové po druhý světový dohadovali jestli je lepčí vyrábět další atomovky nebo jestli je lacinější dál dělat 'konvenční' nálety zápalnejma bombama páč škody udělaný japoncům byly +- stejný :D
jinak řečeno nevidim duvod proč by se putin chtěl zasebevraždit protože z jeho křivýho uhlu pohledu mu to fakt jako muže připadat že de všecko podle plánu a když nemá duvod se zasebevraždit tak imo nemá ani žádnej duvod rozmetat všecky figurky na šachovnici atomovkovovým dracarysem ňákým :D ;D taky težško by pak moch furt dál řikat žese snaží ua jakože ňák denacifikovat kdyby se tam dopustil atomovkový genocidy :D
.................a ikdyby se chtěl picnout by podle toho jak se mu jako tadlecta invaze daří postřilel všecky v mistonsti a jedinej koho by netrefil by byl von sám :D ;D
Ale to jsou přece dobré zprávy: zakryjí-li Skythové slunce svými šípy, budou s nimi moci Sarmati bojovat v chládku, a ne na slunci .
hele rusák tam má eště jeden uplně supr projekt ato na vovládání sonoff iot věciček bez nutnosti strčit do nich neoriginální firmware :O :O
to je rasistický