abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
    včera 23:22 | Nová verze

    Hudební přehrávač Amarok byl vydán v nové major verzi 3.0 postavené na Qt5/KDE Frameworks 5. Předchozí verze 2.9.0 vyšla před 6 lety a byla postavená na Qt4. Portace Amaroku na Qt6/KDE Frameworks 6 by měla začít v následujících měsících.

    Ladislav Hagara | Komentářů: 4
    včera 21:44 | Komunita

    Ubuntu 24.10 bude Oracular Oriole (věštecká žluva).

    Ladislav Hagara | Komentářů: 9
    včera 20:22 | Nová verze

    Byla vydána nová verze 2.45.0 distribuovaného systému správy verzí Git. Přispělo 96 vývojářů, z toho 38 nových. Přehled novinek v příspěvku na blogu GitHubu a v poznámkách k vydání. Vypíchnout lze počáteční podporu repozitářů, ve kterých lze používat SHA-1 i SHA-256.

    Ladislav Hagara | Komentářů: 0
    včera 13:33 | IT novinky

    Před 25 lety, ve čtvrtek 29. dubna 1999, byla spuštěna služba "Úschovna".

    Ladislav Hagara | Komentářů: 0
    včera 01:00 | Nová verze

    Byla vydána nová verze 24.04.28 s kódovým názvem Time After Time svobodného multiplatformního video editoru Shotcut (Wikipedie) a nová verze 7.24.0 souvisejícího frameworku MLT Multimedia Framework. Nejnovější Shotcut je vedle zdrojových kódů k dispozici také ve formátech AppImage, Flatpak a Snap.

    Ladislav Hagara | Komentářů: 0
    28.4. 16:33 | Nová verze Ladislav Hagara | Komentářů: 0
    28.4. 03:22 | Zajímavý článek

    V aktuálním příspěvku na blogu počítačové hry Factorio (Wikipedie) se vývojář s přezývkou raiguard rozepsal o podpoře Linuxu. Rozebírá problémy a výzvy jako přechod linuxových distribucí z X11 na Wayland, dekorace oken na straně klienta a GNOME, změna velikosti okna ve správci oken Sway, …

    Ladislav Hagara | Komentářů: 0
    28.4. 00:11 | Nová verze

    Rakudo (Wikipedie), tj. překladač programovacího jazyka Raku (Wikipedie), byl vydán ve verzi #171 (2024.04). Programovací jazyk Raku byl dříve znám pod názvem Perl 6.

    Ladislav Hagara | Komentářů: 7
    27.4. 17:44 | Nová verze

    Společnost Epic Games vydala verzi 5.4 svého proprietárního multiplatformního herního enginu Unreal Engine (Wikipedie). Podrobný přehled novinek v poznámkách k vydání.

    Ladislav Hagara | Komentářů: 0
    26.4. 17:11 | Nová verze

    Byl vydán Nextcloud Hub 8. Představení novinek tohoto open source cloudového řešení také na YouTube. Vypíchnout lze Nextcloud AI Assistant 2.0.

    Ladislav Hagara | Komentářů: 12
    KDE Plasma 6
     (75%)
     (8%)
     (2%)
     (15%)
    Celkem 884 hlasů
     Komentářů: 4, poslední 6.4. 15:51
    Rozcestník

    TapoVoko 🧿 📢

    20.2.2023 13:29 | Přečteno: 1975× | Výběrový blog | poslední úprava: 22.2.2023 11:48

    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 kamera

    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 😁

    Dekompilace offiko 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);
       }
    }
    
    
    třída s telemetrií

    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()));
                }
             }
    
          }
       }
    }
    
    double talk třída

    Některý konstantní proměný jsou nahrazený odpovidajicí číselnou hodnotou.


    Rekonstrukce

    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;
       }
    }
    
    
    Nula měla bejt jednička, nicmeně dekompilátor počítal vod nuly 😁 😕 😁 😕

    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

    slepý cesty

    watermark

    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

    AEC

    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 😮 😮

    Ghidra

    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


    Zdroják

    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!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
    
    
           

    Hodnocení: 100 %

            špatnédobré        

    Obrázky

    TapoVoko 🧿 📢, obrázek 1 TapoVoko 🧿 📢, obrázek 2 TapoVoko 🧿 📢, obrázek 3 TapoVoko 🧿 📢, obrázek 4

    Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

    Komentáře

    Vložit další komentář

    Max avatar 20.2.2023 13:49 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    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? :)
    Zdar Max
    PS: Já se podobně, na mnohem menší úrovni, bavím analýzou propri věcí, co se kupují do práce, protože ten sw bývá bez dokumentace, takže nevím, co kde povolit, co to potřebuje k životu atd. Většinou tedy s novým sw dělám sniff a analýzu komunikace, jak serverové části (pokud jí to má), tak klientské. Člověk se někdy nestačí divit :).
    Měl jsem sen ... :(
    Gréta avatar 21.2.2023 01:26 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    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

    Max avatar 21.2.2023 08:12 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Ponocování do půl druhý rána, na to není nic špatného. Pilovat si skill v CounterStrike 1.6 je na celý život, resp. na všechny noci do konce života :).
    Zdar Max
    Měl jsem sen ... :(
    21.2.2023 17:56 kralyk z abclinuxu | skóre: 29 | blog:
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    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 :)
    Gréta avatar 22.2.2023 11:52 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    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

    21.2.2023 10:10 rad
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    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...
    Max avatar 21.2.2023 10:56 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Nejdříve musíš nahodit požadavky, co to má splňovat.
    Zdar Max
    Měl jsem sen ... :(
    21.2.2023 11:38 rad
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    No víceméně to co tenhle bazmek, tj. vnitřní polohovatelná kamera s rozumnou kvalitou videa. Noční vidění/přísvit bych asi nepotřeboval ale neurazí, detekce pohybu by se asi hodila (ale když nebude, nevadí). Reproduktor určitě nepotřebuju, vlastně možná ani ten mikrofon. Přednost bych dal ethernetu, ale ani WiFi by mi nevadila. Prostě taková 'surveillance camera' pro takový to domácí survejlování.

    Hlavní požadavek byl, aby to bylo nějak rozumně bezpečný co se přístupu týče, tzn. žádný cloudy, aplikace třetích stran, podivný nezabezpečený web servery apod. Ale tehle požadavek se u podobnejch hoby zařízení splňuje asi špatně.
    Max avatar 21.2.2023 12:42 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Takže vlastně jakákoli ONVIF kamera, co má nesprzněný web ksicht. Mezi ně patří dnes skoro všechno normální, tj. Reolink, Hikvision, Dahua.
    Onvif je standard, přes který si systém vyzjistí rtsp streamy a další věci a je možné přes to i kameru nastavovat (pokud to CCTV systém umožňuje). A jako CCTV máš několik možností, bývá oblíbený i Synology, protože to má v sobě CCTV zdarma pro 2 kamery, další jsou za příplatek. Přístup na Synology z venku buď na přímo, nebo pokud není možnost, tak přes Synology QuickConnect (cloudová proxy, co dělá vpn).
    Je to jednoduchý, rychlý, funkční.
    Pokud máš doma servřík, tak můžeš na servřík naladit Shinoby, Motion + motionEye, ZoneMinder, nebo pokud chceš něco lepšího a nevadí ti komerce, tak Xeoma (placený, ale podpora Linuxu, RPi, aplikace do telefonu atd.).
    Jinak zabezpečení kamer se řeší tak, že se dávají do izolované sítě, protože každá kamera, se kterou jsem se setkal, měla dřív, nebo později nějakou zranitelnost a po pár letech na ně přestávají vycházet aktualizace.
    Zdar Max
    Měl jsem sen ... :(
    Max avatar 21.2.2023 12:43 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Jinak ještě dodám, že nějaký náhled na snapy, nebo live stream, umí i HomeAssistant. Takže pokud máš automatizace a nepotřebuješ ukládat záznam, tak si asi vystačíš.
    Zdar Max
    Měl jsem sen ... :(
    24.2.2023 10:35 Vantomas | skóre: 32 | Praha
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Shinoby vypadá hezky. Znáš i Frigate? A co z toho myslíš, že má budoucnost?
    Max avatar 24.2.2023 13:38 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Frigate neznám. Koukám, že je to novinka a zatím asi spíše minimum funkcí. Trochu bych se bál, jak to s kamerama bude komunikovat. Mám zkušenosti, že některé sw mívají problémy a nepřenesou se přes timeouty a další problémy, které kamery mají, takže pak se stává, že kamera třeba co 5 min neukazuje obraz apod.
    Podobné problémy jsem měl třeba u toho Shinobi před x lety. Tam, kde Shinobi vypadávala komunikace s kamerama, tam Xeoma neměla problém.
    Před x lety jsem měl nasazeno Shinobi a pak jsem koupil Xeomu, protože:
    1) Výpadky komunikace u méně kvalitních kamer.
    2) Absence kvalitní aplikace do androidu apod. (byla jediná možnost přes web rozhraní a kvůli snížení pásma si hrát s mjpeg přenosem).
    3) Borec to staví nad Nodejs a vešekeré analýzy a další věci dělá v js. Člověk pro provoz tedy musí trochu ovládat Nodejs a technologie okolo.
    4) Ksicht zlobil a občas tam byl problém. Prostě neodladěná beta.

    Takže u čehokoli nového mám trochu obavy a zvýšenou opatrnost. Nedávno jsem na Shinobi znovu koukal, znovu ho budu chtít zkoušet, ale už teď mají tendence se uzavírat. Mají tam omezení ohledně mobilní app a ohledně vzdáleného přístupu apod. A ty ceny v jejich eshopu jsou jak z jiného vesmíru. Pokud mi nebude vyhovovat ta OSS verze, tak prostě koupím osvědčenou Xeomu od Rusa.
    Zdar Max
    PS: dost problémů řeší prostě nekupovat šméčkový kamery
    Měl jsem sen ... :(
    Max avatar 24.2.2023 13:44 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Ještě doplním, že některá řešení neumí restreaming, jen server zprostředkovává komunikaci s kamerou. Tím se zatěžuje síť, zatěžuje se kamera, která pak začne vypadávat a další a další problémy navazují. Ten Frigate má podporu pro restreaming, takže do startu začali dobře.
    Zdar Max
    Měl jsem sen ... :(
    24.2.2023 15:27 Vantomas | skóre: 32 | Praha
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Já jedu všechno Hikvision, protože už od prvních dnů má perfektní podporu RTSP, pro dump-usery jim dávám zástupce s VLC na plochu nebo mám webpage kde z kamery stahuji JPEG snapy a přes javascript obnovuji - dá to sice jenom 0,5 FPS, ale pro 99 % usecasů živého náhledu naprosto dostačující a narozdíl od videí nebo aplikací se to načítá a zobrazuje okamžitě. Jenže takhle mi to fungovalo super, dokud byl na NVR 24/7 záznam a nepoužíval se motion detect. Teď jsem na pár kamerách nastavil motion detect a začaly problémy - když se aktivuje alarm, tak nefunguje generování jpeg snapshotů a tak se začínám poohlížet po něčem co vyřeší motion detect lépe, i kvůli slunci a větvím atd.

    Na Frigate jsem právě narazil v nějaké podobné diskusi na rootu a minimálně podle letáčku to vypadá jako věc co chci :) . Teďka jsem zkoušel na redditu hledat Shinobi vs Frigate a hodně lidí tam psalo, že jako dobrý a že tam přechází ze Shinobi kvůli jejich změně licenční politiky. Vyzkoušet bych si měl asi vše, ale nechce se mi do toho dávat moc času.
    Max avatar 21.2.2023 12:48 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Dodám info k detekci pohybu. Všude děláme detekci pohybu na úrovni CCTV, nikoli na úrovni kamery. Má to několik důvodů. CCTV umožňuje lépe analyzovat obraz, dále pokud máš pohyb, tak většinou chceš vidět, co tomu předcházelo. Tj. chceš záznam třeba 2s před pohybem a třeba ještě 2-5s po ukončení pohybu. To jen s kamerou většinou nedáš.
    IR přísvit dobrý, ale pokud to budeš mít venku, tak je dost pravděpodobné, že ti v noci před objektivem budou lítat mouchy a budeš mít spoustu falešných pohybů. To se řeší externím přísvitem, který je třeba metr od kamery.
    Zdar Max
    Měl jsem sen ... :(
    21.2.2023 14:26 rad
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Dík, určitě se na to podívám.

    Servřík doma právě mám, takže mě napadlo dělat spoustu věcí přes něj (právě třeba jako detekce pohybu), ale zas se znám a vím, že by to trvalo léta, takovejch projektů mám rozdělanejch tucet, chtěl jsem něco alespoň trochu 'out-of-box'.
    21.2.2023 14:32 rad
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Tak jsem se na Alze podíval na ty značky co jsi doporučil, a překryv s ONVIF je nulový, tak nevím... :-)
    Max avatar 21.2.2023 16:12 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Všechny zmíněné podporují ONVIF (v dnešní době i snad každá Čína, ale někdy to ta noname má zprzněno). To, že to nemá třeba Alza uvedeno, to nic neznamená, ale jde to pak najít u konkrétního modelu ve spec.
    Schválně nahoď nějaký model.
    Zdar Max
    Měl jsem sen ... :(
    21.2.2023 16:51 rad
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    No koukal jsem třeba na Reolink E1 (ale vybíral jsem víceméně náhodně). Ve specifikacích mají uvedeno: Protocols & Standards - SSL, TCP/IP, UDP, IPv4, UPnP, SMTP, NTP, DHCP, DNS, DDNS, P2P.
    Max avatar 21.2.2023 21:55 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Všechny Reolink kamery by měly podporovat ONVIF (ONVIF Compliant IP Security Cameras ) a E1 serie jej podporuje také, viz např.: Reolink E1 Zoom and/or Pro in HA.
    Problém je, že tato E1 vypadá, že to je cloudový typ. Tj. mobil, aplikace, žádný web ksicht. Ale dle toho infa by i tak měla mít web služby a ONVIF implementaci.
    Třeba se ale pletu a má i web ksicht. Já jedu kamery v cenové 3k+.
    Zdar Max
    Měl jsem sen ... :(
    20.2.2023 16:08 Sklamaney
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    A ja se tesil na video s Gretou a vono samej zdrojak :-(
    Gréta avatar 21.2.2023 01:35 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    20.2.2023 16:16
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    skrytý komentář Náš administrátor shledal tento komentář závadným. Zobrazit komentář
    Gréta avatar 21.2.2023 01:39 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    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

    21.2.2023 08:47 _
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    neexistuji hodni rusaci a cinani
    Max avatar 21.2.2023 10:01 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Chápu, že dnešní doba nabízí radikalizaci mnohem více, než před pár lety, ale ne, prostě nelze strkat všechny do jednoho pytle.
    Zdar Max
    Měl jsem sen ... :(
    22.2.2023 13:45 _
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Ze se tu rusaku zastava zrovna mistni nacista...

    Max avatar 24.2.2023 18:48 Max | skóre: 72 | blog: Max_Devaine
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Pokus se aspoň trošku pochopit, že existují lidé, kteří mají jiné, i né tak silně radikální názory. A pokud se umíš aspoň trochu chovat, odpusť si pro příště ty urážky.
    Díky
    Zdar Max
    Měl jsem sen ... :(
    Jendа avatar 24.2.2023 18:58 Jendа | skóre: 78 | blog: Jenda | JO70FB
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Poslední dobou se termín nacista/nácek používá dost zmatečně („ukrajinští nácci“, „ruští nácci“ atd.). Možná by pro lepší vysvětlení bylo vhodné, kdybys místo toho napsal konkrétní jednání, kterého se Max dopouští. Minimálně já tady nevím co přesně si pod tím vybavit. Je to butthurt ještě z covidu? Nebo že skryl nějaký komentář?
    20.2.2023 22:49 z_sk | skóre: 34 | blog: analyzy
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    Jo a takle ffmpeg nepiše hlaviču souboru, takže to zapisuje fakt jenom byty datovýho streamu
    Posiela sa surový stream (t.j. bez meta dat).


    Ináč, zverejnenie dekompilovanej verzie nie je porušením autorského zákonu?

    debian.plus@protonmail.com
    Gréta avatar 21.2.2023 01:34 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    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

    21.2.2023 09:21 $
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Autorstvi vzdyt nikdo nikomu nebere. Asi si to pletes s copyrightem.
    21.2.2023 16:47 J
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Za komunistu svazacky docela dobre pichali. Nevite jak je to dnes s ekosvazackami typu Greta?
    22.2.2023 19:27 _
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    K té se člověk nedostane, ale mohli by podle ni delat alespon silikonovou pannu
    22.2.2023 19:35 Cinak
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    To by myseli odlevat c cine a kdyz spocitaji kila silikonu tak jim vyjde ze z toho uhnetou 3 standardni cinanky.
    22.2.2023 23:46 _
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    to je mozny, ale cinanku nekoupim, nejsem uchyl
    23.2.2023 16:19 Tapu Koko
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    tak ji das pasku pres voci, od nosu dolu jsou vsechny zeny stejne
    23.2.2023 23:17 J
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Spise noviny pres jeji oblicej… cinanky jsou hezke pokud si nenajdete nejakou 50+. Obecne asiatky nad 30 ztraceji na pritazlivosti.
    24.2.2023 06:32 Roman.ce
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    To budes libat noviny?
    24.2.2023 18:31 J
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Tady je pan labuznik… proc bych mel libat cinanku?
    26.2.2023 06:34 Reskātor
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Spise romantik ne? Kdyz uz kopulace tak se vsim vsudy, i s libanim kremiku :-)
    24.2.2023 11:27 _
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Cinanky a obecne asiatky jsou sice hezke, ale v tom smyslu jak hezke muze byt dite nebo zviratko, naprosto bez sexualni pritazlivosti. Alespon pro me.
    23.2.2023 23:19 J
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Tak me napada, existuje neco jako nornik s linxuackama?
    24.2.2023 06:30 wokena
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Linuxacky vymreli spolu s ustupem linux desktopu na smetiste digitalnich dejin. Mame jiz jen zopar transek jako Alyssa a AsahiLina ktere stejne pouzivaji Macbooky i kdyz na oko patlaji Asahi linux...
    24.2.2023 08:55 xxxxx_______
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    :-) dobré vlákno... pobavili ste pri rannej káve
    24.2.2023 11:29 _
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    nevymreli, sukal jsem tri linuxacky (anicku nepocitam, ta dala kazdemu), posledni loni
    24.2.2023 18:34 J
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Muzete poradit kde jste se seznamili? Na mem dlouhem seznamu sexualnich zkusenosti mam sex s linuxackou jeste ve stavu TODO
    25.2.2023 21:20 Nic nebude
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Pozde. Nejsou. Vymreli spolu s linuxovym desktopem. Na konferencich jsou jen najate herecky nebo trans. Jeste tu nekdy zavita Kate ale ta kope jinou ligu takze ti neda.
    25.2.2023 22:12 J
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Tak me linux trans nevadi pokud je kam zasunout usb… Kate? Takze bych ji na ukazku kompilace sveho gentoo neukecal? A sakra…
    Gréta avatar 24.2.2023 13:36 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    nvm jestli to je jakože to co hledáš ale vyráběj se takový strašidelný specialní panenky pro děti trpicí downovým syndromem a vypadaj uplně stejně hele a hele třeba :O :D :D ;D

    23.2.2023 00:46 X
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Wow, pekny vanocni stromecek ten kod ;). Jinak respekt. Smekam.
    Gréta avatar 24.2.2023 13:29 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    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

    Jendа avatar 24.2.2023 04:29 Jendа | skóre: 78 | blog: Jenda | JO70FB
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Tak máme roční výročí speciální operace. Osobně by mě od Putina při příležitosti narozenin nejvíc potěšil speciální dárek, třeba kdyby se zastřelil. Spíš se ale bojím aby mu neruplo v kouli a nerozhodl se to řešit třeba atomovkama. I když to jsem se bál už loni 9.5. (Den Vítězství) a nic se nestalo, tak snad dobrý.
    Jendа avatar 24.2.2023 04:30 Jendа | skóre: 78 | blog: Jenda | JO70FB
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    Omlouvám se, to fakt mělo jít do vedlejší diskuze, i když mi to asi neuvěříte :(
    24.2.2023 08:57 xxxxx_______
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    :-) chcel si byť vtipný ako Gréta a nevyšlo to, čo už:)
    Gréta avatar 24.2.2023 13:28 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    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

    xxxs avatar 24.2.2023 18:05 xxxs | skóre: 25 | blog: vetvicky
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    ak by bolo realnych 500k+, tak UA by mala straty blizke 100k, co je pre nich koniec. ak by nevyslo, v dalsej sezone by ich uz RU doklepli aj s mobikmi. dufam, ze iba kecaju, maju nieco pod 200k a pridu s mosinmi, t34, hromadne sa vzdaju, poprosia o obed a potom pojdu zavesit svojich panov na nejake vyvysene miesto.
    Alkibiadés avatar 24.2.2023 21:34 Alkibiadés | Já to dost střídám :)
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢
    rusáci schopný zaplnit nebe 'konvenčníma zbraněma' a přetížit protizdušnou vobranu

    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 :-).

    Gréta avatar 24.2.2023 13:31 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧿 📢

    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

    24.2.2023 14:56 $
    Rozbalit Rozbalit vše Re: TapoVoko 🧾 📡
    Musime byt radikalni - smazat ruske kody z linuxu a do GPL pridat klauzuli ze se lnux v rusku nesmi pouzivat.
    Gréta avatar 27.2.2023 11:21 Gréta | skóre: 36 | blog: Grétin blogísek | 🇮🇱==❤️ , 🇵🇸==💩 , 🇪🇺==☭
    Rozbalit Rozbalit vše Re: TapoVoko 🧾 📡

    to je rasistický

    27.2.2023 18:29 ( . )( . )
    Rozbalit Rozbalit vše Re: TapoVoko 🧾 📡
    rusove a rasa? vzdyt se propaguji jako spasitelsky narod

    Založit nové vláknoNahoru

    ISSN 1214-1267   www.czech-server.cz
    © 1999-2015 Nitemedia s. r. o. Všechna práva vyhrazena.