@kaeawc/auto-mobile 0.0.93 → 0.0.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/dist/build-info.json +12 -12
  2. package/dist/src/ble-peripheral.py +153 -0
  3. package/dist/src/{chunk-r6z1eztj.js → chunk-026y62r9.js} +11 -11
  4. package/dist/src/chunk-0jyfg947.js +4 -0
  5. package/dist/src/{chunk-8g1922rp.js → chunk-0zfpp1wh.js} +4 -4
  6. package/dist/src/{chunk-qf6nynk6.js → chunk-13qxf7ym.js} +5 -5
  7. package/dist/src/chunk-1scenrk7.js +4 -0
  8. package/dist/src/chunk-1vb5ph1k.js +4 -0
  9. package/dist/src/{chunk-710rxz9y.js → chunk-20g565q7.js} +3 -3
  10. package/dist/src/chunk-21h6dhdm.js +5 -0
  11. package/dist/src/chunk-25mfpthk.js +78 -0
  12. package/dist/src/{chunk-rbpdk7m0.js → chunk-2b39zyxc.js} +3 -3
  13. package/dist/src/chunk-2gzd6wbd.js +4 -0
  14. package/dist/src/{chunk-xgan0qqp.js → chunk-3dntffy9.js} +1 -1
  15. package/dist/src/chunk-3gfczcsn.js +4 -0
  16. package/dist/src/{chunk-dgrbdpb7.js → chunk-3h5jz1vb.js} +6 -6
  17. package/dist/src/{chunk-t6c8akcm.js → chunk-41zcy6b3.js} +3 -3
  18. package/dist/src/chunk-47n9m2de.js +4 -0
  19. package/dist/src/chunk-4vxeq0vd.js +58 -0
  20. package/dist/src/chunk-59dx6qaf.js +4 -0
  21. package/dist/src/chunk-5sdf9y92.js +4 -0
  22. package/dist/src/{chunk-58wzr3mt.js → chunk-5wk1t46v.js} +3 -3
  23. package/dist/src/{chunk-vy3bwm1d.js → chunk-661c50hh.js} +5 -5
  24. package/dist/src/chunk-68n6bjjd.js +21 -0
  25. package/dist/src/{chunk-v6pya0dm.js → chunk-69pmjdf2.js} +3 -3
  26. package/dist/src/{chunk-e98dqnc9.js → chunk-6hrj3jt6.js} +20 -20
  27. package/dist/src/{chunk-wbh8spm5.js → chunk-6myr26wx.js} +8 -8
  28. package/dist/src/chunk-6n96k09n.js +4 -0
  29. package/dist/src/chunk-6vwrbkre.js +4 -0
  30. package/dist/src/chunk-6wk0yrab.js +4 -0
  31. package/dist/src/chunk-6yhm9een.js +4 -0
  32. package/dist/src/{chunk-mjrps0ac.js → chunk-7k7gjg2t.js} +5 -5
  33. package/dist/src/chunk-7n5fz68h.js +4 -0
  34. package/dist/src/chunk-8cvnpnbp.js +4 -0
  35. package/dist/src/chunk-8kzv7rk7.js +4 -0
  36. package/dist/src/chunk-8nj68t1v.js +167 -0
  37. package/dist/src/{chunk-g7tmjbkt.js → chunk-96r3bpg7.js} +3 -3
  38. package/dist/src/chunk-97x887te.js +4 -0
  39. package/dist/src/{chunk-8ph29g99.js → chunk-99a6tbvd.js} +1 -1
  40. package/dist/src/chunk-9p5d2994.js +5 -0
  41. package/dist/src/{chunk-aabchzxk.js → chunk-9q265er0.js} +4 -4
  42. package/dist/src/{chunk-jh6vhqzx.js → chunk-9xgnrv5w.js} +3 -3
  43. package/dist/src/chunk-9xgsmshb.js +5 -0
  44. package/dist/src/chunk-a02d1wgv.js +4 -0
  45. package/dist/src/{chunk-ndt1ccxm.js → chunk-a9ps00a1.js} +10 -10
  46. package/dist/src/{chunk-5d2hft6c.js → chunk-aewdveet.js} +3 -3
  47. package/dist/src/{chunk-ycaj2sgf.js → chunk-b3tdtq7r.js} +6 -6
  48. package/dist/src/chunk-bn7jx7a2.js +4 -0
  49. package/dist/src/{chunk-3y6eykz9.js → chunk-bnyw27ja.js} +2 -2
  50. package/dist/src/{chunk-rqc9ap3x.js → chunk-bpwsg76p.js} +2 -2
  51. package/dist/src/chunk-bvranw3g.js +4 -0
  52. package/dist/src/chunk-c25a84zb.js +4 -0
  53. package/dist/src/chunk-c27yv1dx.js +5 -0
  54. package/dist/src/chunk-c3ewxm4x.js +5 -0
  55. package/dist/src/{chunk-rvvy1bte.js → chunk-cb1ga9rd.js} +5 -5
  56. package/dist/src/chunk-cn7fwy7k.js +4 -0
  57. package/dist/src/chunk-cnvy4a64.js +4 -0
  58. package/dist/src/{chunk-7rz9898k.js → chunk-cqhw4yr6.js} +3 -3
  59. package/dist/src/{chunk-g04bwyj3.js → chunk-ctmfshjc.js} +2 -2
  60. package/dist/src/chunk-ctwmhz13.js +4 -0
  61. package/dist/src/{chunk-waqwcjyn.js → chunk-d3jx9bz5.js} +2 -2
  62. package/dist/src/{chunk-ayp07wbb.js → chunk-d6rb08ga.js} +1 -1
  63. package/dist/src/chunk-ddqhj4eg.js +5 -0
  64. package/dist/src/{chunk-r7bpkgff.js → chunk-ddrhs1c3.js} +2 -2
  65. package/dist/src/chunk-dnjnh9p6.js +4 -0
  66. package/dist/src/{chunk-wyrkw6hw.js → chunk-dw5rwgya.js} +3 -3
  67. package/dist/src/{chunk-3xkpk44w.js → chunk-e0870khr.js} +3 -3
  68. package/dist/src/chunk-ea3k10kj.js +4 -0
  69. package/dist/src/chunk-emv82m1w.js +7 -0
  70. package/dist/src/chunk-f3m6pdnx.js +4 -0
  71. package/dist/src/chunk-f84z32cb.js +4 -0
  72. package/dist/src/chunk-fpczgf0z.js +4 -0
  73. package/dist/src/{chunk-2tqzq5qh.js → chunk-g2js4c2v.js} +18 -18
  74. package/dist/src/chunk-g3af2qbk.js +5 -0
  75. package/dist/src/{chunk-3qwrzxh0.js → chunk-gadtna7s.js} +6 -6
  76. package/dist/src/{chunk-ybbtagbt.js → chunk-gcyx407v.js} +3 -3
  77. package/dist/src/{chunk-bfw6125s.js → chunk-gfpntwc1.js} +3 -3
  78. package/dist/src/chunk-gmnm5e3e.js +4 -0
  79. package/dist/src/chunk-gp6vb5gf.js +4 -0
  80. package/dist/src/chunk-gppgy7pa.js +4 -0
  81. package/dist/src/chunk-hegfz0ae.js +5 -0
  82. package/dist/src/chunk-hpzxdp1k.js +17 -0
  83. package/dist/src/{chunk-qcxzr2my.js → chunk-j69g8b50.js} +3 -3
  84. package/dist/src/chunk-jvstjnqm.js +19 -0
  85. package/dist/src/chunk-jzqrv78s.js +4 -0
  86. package/dist/src/chunk-k2948b8d.js +4 -0
  87. package/dist/src/chunk-k4ftd40d.js +4 -0
  88. package/dist/src/{chunk-bxmsax22.js → chunk-kd9vfaxh.js} +3 -3
  89. package/dist/src/{chunk-s3jc6cwp.js → chunk-kdczmgwk.js} +3 -3
  90. package/dist/src/{chunk-t6bfvfkg.js → chunk-ke3wn14v.js} +1 -1
  91. package/dist/src/chunk-kh5n7m6w.js +4 -0
  92. package/dist/src/{chunk-6wt9rgjf.js → chunk-khejfph4.js} +3 -3
  93. package/dist/src/chunk-kny2xemh.js +4 -0
  94. package/dist/src/chunk-knyzw9md.js +4 -0
  95. package/dist/src/chunk-mbzpt2hp.js +4 -0
  96. package/dist/src/chunk-mm8rcxad.js +4 -0
  97. package/dist/src/chunk-nc4516tx.js +4 -0
  98. package/dist/src/{chunk-6bznss4t.js → chunk-ntfrqrqn.js} +5 -5
  99. package/dist/src/chunk-nz5h2j52.js +4 -0
  100. package/dist/src/chunk-p22s97qy.js +4 -0
  101. package/dist/src/chunk-p9kt1sgr.js +6 -0
  102. package/dist/src/{chunk-yb97j25b.js → chunk-pf282r5g.js} +4 -4
  103. package/dist/src/chunk-pnd4mrxy.js +4 -0
  104. package/dist/src/chunk-pqqteyj6.js +4 -0
  105. package/dist/src/{chunk-6xmss8er.js → chunk-qj1tbnv2.js} +6 -6
  106. package/dist/src/chunk-qnh9egwr.js +4 -0
  107. package/dist/src/chunk-qy3khhf8.js +8 -0
  108. package/dist/src/{chunk-z637nqg8.js → chunk-r0vp315h.js} +6 -6
  109. package/dist/src/chunk-r4j43b9s.js +237 -0
  110. package/dist/src/{chunk-1p7xf5kb.js → chunk-r54cxxze.js} +3 -3
  111. package/dist/src/{chunk-yf7ympyz.js → chunk-r6pvhj33.js} +92 -92
  112. package/dist/src/chunk-rb7aa5gr.js +54 -0
  113. package/dist/src/chunk-rdj50kb4.js +4 -0
  114. package/dist/src/chunk-rnd4vcas.js +4 -0
  115. package/dist/src/{chunk-w5b2jwk3.js → chunk-s3y3zfcx.js} +8 -8
  116. package/dist/src/chunk-s4g43k75.js +5 -0
  117. package/dist/src/chunk-sfqe223f.js +311 -0
  118. package/dist/src/{chunk-284ptqa4.js → chunk-sqdrxjw5.js} +5 -5
  119. package/dist/src/{chunk-zgja5f3p.js → chunk-t54k47em.js} +4 -4
  120. package/dist/src/{chunk-3sy6ahhz.js → chunk-tek4pnvv.js} +10 -10
  121. package/dist/src/chunk-tn74y7af.js +4 -0
  122. package/dist/src/{chunk-thbh3jew.js → chunk-tvq0nzam.js} +3 -3
  123. package/dist/src/{chunk-0jrrsnc9.js → chunk-tx4f0h9c.js} +4 -4
  124. package/dist/src/chunk-vdqag3fj.js +5 -0
  125. package/dist/src/chunk-vz5zzs57.js +4 -0
  126. package/dist/src/chunk-w4s2vfpj.js +4 -0
  127. package/dist/src/{chunk-4hw3665t.js → chunk-w5yypt53.js} +11 -11
  128. package/dist/src/{chunk-8037tzzj.js → chunk-w68v64vh.js} +3 -3
  129. package/dist/src/chunk-whwj0pdk.js +5 -0
  130. package/dist/src/chunk-wnmqt1dc.js +4 -0
  131. package/dist/src/{chunk-cyy71ggq.js → chunk-wpe46vsa.js} +3 -3
  132. package/dist/src/{chunk-w3vs79ex.js → chunk-wxdjeb4k.js} +19 -19
  133. package/dist/src/chunk-xcqzwf9j.js +247 -0
  134. package/dist/src/chunk-xezekwwn.js +5 -0
  135. package/dist/src/chunk-xf19m15s.js +4 -0
  136. package/dist/src/chunk-xghbxha4.js +4 -0
  137. package/dist/src/{chunk-00167wan.js → chunk-xn9m3xc2.js} +5 -5
  138. package/dist/src/{chunk-26k317v5.js → chunk-yqfz4vts.js} +3 -3
  139. package/dist/src/chunk-z8tvs8nk.js +5 -0
  140. package/dist/src/chunk-zm2rehqb.js +5 -0
  141. package/dist/src/chunk-zs6dqm35.js +4 -0
  142. package/dist/src/daemon/processEntry.js +2 -2
  143. package/dist/src/index.js +4 -4
  144. package/dist/src/runtime/bunRuntimeGuard.js +2 -2
  145. package/dist/src/utils/workers/iosBundleExtractWorker.js +2 -2
  146. package/package.json +2 -2
  147. package/dist/src/chunk-0n6swkwg.js +0 -5
  148. package/dist/src/chunk-0qsy1qsx.js +0 -57
  149. package/dist/src/chunk-11m8wj8j.js +0 -237
  150. package/dist/src/chunk-23p5f095.js +0 -4
  151. package/dist/src/chunk-28re4xrg.js +0 -4
  152. package/dist/src/chunk-2f9rkmhx.js +0 -5
  153. package/dist/src/chunk-2fxdh48t.js +0 -4
  154. package/dist/src/chunk-2tbdrbt6.js +0 -4
  155. package/dist/src/chunk-32gvv9b4.js +0 -21
  156. package/dist/src/chunk-3f3t1x97.js +0 -8
  157. package/dist/src/chunk-3fspkbc7.js +0 -4
  158. package/dist/src/chunk-3vg455e6.js +0 -4
  159. package/dist/src/chunk-49bsqdx3.js +0 -4
  160. package/dist/src/chunk-5snsn9cf.js +0 -4
  161. package/dist/src/chunk-5tm9mn47.js +0 -54
  162. package/dist/src/chunk-5zc77a5t.js +0 -4
  163. package/dist/src/chunk-64j0majr.js +0 -19
  164. package/dist/src/chunk-65y8v4td.js +0 -17
  165. package/dist/src/chunk-6awz7vhq.js +0 -5
  166. package/dist/src/chunk-6ep7f5ge.js +0 -4
  167. package/dist/src/chunk-7cs2da62.js +0 -5
  168. package/dist/src/chunk-7g103b54.js +0 -4
  169. package/dist/src/chunk-7j5vj4pa.js +0 -4
  170. package/dist/src/chunk-7tw8x05v.js +0 -4
  171. package/dist/src/chunk-7zy586cb.js +0 -4
  172. package/dist/src/chunk-84nanf8s.js +0 -5
  173. package/dist/src/chunk-8gvwcv2p.js +0 -4
  174. package/dist/src/chunk-8thrmfat.js +0 -4
  175. package/dist/src/chunk-8zgzzs7k.js +0 -78
  176. package/dist/src/chunk-97b1zmaq.js +0 -4
  177. package/dist/src/chunk-9fth9ayh.js +0 -4
  178. package/dist/src/chunk-9j6anhjr.js +0 -4
  179. package/dist/src/chunk-bfjwqk8q.js +0 -4
  180. package/dist/src/chunk-bnhscm2n.js +0 -4
  181. package/dist/src/chunk-c1vs4g1n.js +0 -4
  182. package/dist/src/chunk-ct3e7yng.js +0 -4
  183. package/dist/src/chunk-ddzkz92z.js +0 -4
  184. package/dist/src/chunk-drdz8hee.js +0 -5
  185. package/dist/src/chunk-eersndy7.js +0 -5
  186. package/dist/src/chunk-eggq53ks.js +0 -4
  187. package/dist/src/chunk-f2dya7cs.js +0 -4
  188. package/dist/src/chunk-fbakd6cw.js +0 -4
  189. package/dist/src/chunk-fgb98zjj.js +0 -5
  190. package/dist/src/chunk-fmpad823.js +0 -5
  191. package/dist/src/chunk-g0xr8zw0.js +0 -4
  192. package/dist/src/chunk-g3rjs80x.js +0 -4
  193. package/dist/src/chunk-g78rtrfv.js +0 -4
  194. package/dist/src/chunk-gabgv3y5.js +0 -4
  195. package/dist/src/chunk-gmr45m8f.js +0 -4
  196. package/dist/src/chunk-gsyfebqj.js +0 -4
  197. package/dist/src/chunk-h5rpn9wr.js +0 -247
  198. package/dist/src/chunk-hb1x7v55.js +0 -4
  199. package/dist/src/chunk-hrfytkxh.js +0 -168
  200. package/dist/src/chunk-j4yjzbak.js +0 -4
  201. package/dist/src/chunk-jd6a5v6p.js +0 -5
  202. package/dist/src/chunk-kn5spq15.js +0 -4
  203. package/dist/src/chunk-m94qs0f0.js +0 -4
  204. package/dist/src/chunk-n03dmn5q.js +0 -5
  205. package/dist/src/chunk-n72qhwd9.js +0 -4
  206. package/dist/src/chunk-nr6y0n4y.js +0 -5
  207. package/dist/src/chunk-pcsen2vs.js +0 -4
  208. package/dist/src/chunk-ppfr0b8h.js +0 -4
  209. package/dist/src/chunk-q10bdnat.js +0 -4
  210. package/dist/src/chunk-q9xmq595.js +0 -4
  211. package/dist/src/chunk-qyqypn28.js +0 -5
  212. package/dist/src/chunk-r2epb56s.js +0 -4
  213. package/dist/src/chunk-r5bxavdx.js +0 -311
  214. package/dist/src/chunk-r8jf3d92.js +0 -4
  215. package/dist/src/chunk-rc0dbypm.js +0 -4
  216. package/dist/src/chunk-rhcty4ms.js +0 -4
  217. package/dist/src/chunk-rpvckv3p.js +0 -4
  218. package/dist/src/chunk-ry0mz41y.js +0 -4
  219. package/dist/src/chunk-sfv61xjb.js +0 -6
  220. package/dist/src/chunk-t7w8rctc.js +0 -4
  221. package/dist/src/chunk-v2wd4n3e.js +0 -5
  222. package/dist/src/chunk-w4zga19f.js +0 -4
  223. package/dist/src/chunk-w7hkv422.js +0 -5
  224. package/dist/src/chunk-xa73x2cx.js +0 -5
  225. package/dist/src/chunk-y2vs6jwg.js +0 -4
  226. package/dist/src/chunk-yattm52s.js +0 -4
  227. package/dist/src/chunk-yjawdzk8.js +0 -4
  228. package/dist/src/chunk-zs1sghs9.js +0 -4
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{i,Q,No,l,e}from"./chunk-z637nqg8.js";import{f}from"./chunk-58wzr3mt.js";import{re,fz,v,z,b}from"./chunk-r5bxavdx.js";import{B,Yn}from"./chunk-yf7ympyz.js";import{b0,S0,s_,oQ,qq,w0,Yy,qc,I0,jq,B4,Jd}from"./chunk-hrfytkxh.js";import{dA,gS,yS}from"./chunk-v2wd4n3e.js";import{Ci}from"./chunk-284ptqa4.js";import{r,t,u}from"./chunk-rbpdk7m0.js";var Md=7,Nd=8;function Is(o){return o.length>0?o[0]&31:0}function F(o){let s=Is(o);return s>=1&&s<=5}function mt(o){return o.length>1&&(o[1]&128)!==0}class _f{current=[];hasVcl=!1;push(o){let s=[],n=Is(o),a=n===7||n===8||n===6;if(F(o)&&this.hasVcl&&mt(o)||n===9&&this.current.length>0||a&&this.hasVcl)s.push(this.current),this.current=[],this.hasVcl=!1;if(this.current.push(o),F(o))this.hasVcl=!0;return s}flushPicture(){return this.hasVcl?this.flush():[]}flush(){if(this.current.length===0)return[];let o=this.current;return this.current=[],this.hasVcl=!1,[o]}}var iy=5,ht=28,Ee=2,bE=1200,JO=4194304;function VK(o){return Is(o)===iy}class tc{maxBufferedBytes;buffered=Buffer.alloc(0);constructor(o=JO){this.maxBufferedBytes=o;if(!Number.isSafeInteger(o)||o<1)throw Error("H.264 Annex-B parser buffer limit must be a positive integer.")}push(o){if(this.buffered=this.buffered.length===0?o:Buffer.concat([this.buffered,o]),this.buffered.length>this.maxBufferedBytes)throw this.buffered=Buffer.alloc(0),Error(`H.264 Annex-B parser buffer exceeded ${this.maxBufferedBytes} bytes without a complete NAL.`);return this.drain(!1)}flush(){return this.drain(!0)}hasBufferedNalType(o){let n=this.findStartCodes(this.buffered).at(-1);if(!n)return!1;let a=n.offset+n.length;return a<this.buffered.length&&Is(this.buffered.subarray(a))===o}drain(o){let s=[],n=this.findStartCodes(this.buffered);if(n.length===0){if(o)this.buffered=Buffer.alloc(0);return s}for(let c=0;c<n.length-1;c++){let d=n[c].offset+n[c].length,p=n[c+1].offset,m=this.buffered.subarray(d,p);if(m.length>0)s.push(Buffer.from(m))}let a=n[n.length-1];if(o){let c=this.buffered.subarray(a.offset+a.length);if(c.length>0)s.push(Buffer.from(c));this.buffered=Buffer.alloc(0)}else this.buffered=Buffer.from(this.buffered.subarray(a.offset));return s}findStartCodes(o){let s=[];for(let n=0;n+2<o.length;n++)if(o[n]===0&&o[n+1]===0&&o[n+2]===1){let a=n>0&&o[n-1]===0;s.push({offset:a?n-1:n,length:a?4:3})}return s}}function ft(o,s=bE){if(o.length===0)return[];if(o.length<=s)return[Buffer.from(o)];if(!Number.isFinite(s)||s<=Ee)throw new t(`RTP MTU ${s} is too small to fragment a ${o.length}-byte NAL unit; it must exceed the ${Ee}-byte FU-A header.`);let n=o[0],a=n&224,c=n&31,d=a|ht,p=o.subarray(1),m=s-2,S=[],g=0;while(g<p.length){let y=Math.min(m,p.length-g),h=g===0,w=g+y>=p.length,E=c;if(h)E|=128;if(w)E|=64;S.push(Buffer.concat([Buffer.from([d,E]),p.subarray(g,g+y)])),g+=y}return S}function AJ(o,s=bE){let n=[];for(let a of o)for(let c of ft(a,s))n.push(c);return n.map((a,c)=>({payload:a,marker:c===n.length-1}))}class I{entry;options;active=!0;hasConsumers;joined=!1;pendingAlignment=!1;released=!1;startWaiter=null;constructor(o,s,n){this.entry=o;this.options=s;this.hasConsumers=n}start(){if(!this.active||this.entry.retired)return this.released?Promise.resolve():this.entry.startPromise??Promise.resolve();if(!this.joined){if(this.joined=!0,this.pendingAlignment=this.entry.emittedData,this.entry.replayMetrics(this),this.pendingAlignment)this.entry.source?.requestKeyFrame?.("viewer")}if(this.entry.running)return this.entry.start();if(!this.startWaiter)this.startWaiter=Promise.withResolvers(),this.entry.start().then(this.startWaiter.resolve,this.startWaiter.reject);return this.startWaiter.promise}releaseStart(){this.released=!0,this.startWaiter?.resolve()}stop(){return this.entry.release(this)}get stopStale(){return this.entry.source?.stopStale?(o)=>this.entry.release(this,{producerStale:o}):void 0}get setHasConsumers(){let o=this.entry.source??this.entry.lastSource;if(!o||!("setHasConsumers"in o)||typeof o.setHasConsumers!=="function")return;return(s)=>{if(!this.active)return;this.hasConsumers=s,this.entry.updateConsumers()}}get requestKeyFrame(){let o=this.entry.source;return o?.requestKeyFrame?o.requestKeyFrame.bind(o):void 0}get getTelemetry(){let o=this.entry.source;return o?.getTelemetry?o.getTelemetry.bind(o):void 0}}import{isDeepStrictEqual as vt}from"util";var St=["bitrateBps","size","quality","fps"];function be(o,s,n=[]){let a=St.filter((c)=>!n.includes(c)&&s[c]!==void 0&&!vt(o[c],s[c]));if(s.audioEnabled&&!o.audioEnabled)a.push("audioEnabled");return a}class Y{request;onRetire;handles=new Set;parser=new tc;source=null;creation=null;startPromise=null;startCompletion=null;retryOwner=null;retryAvailable=!1;finalStop=!1;stopSettled=!1;stopPromise=null;retired=!1;running=!1;sps=null;pps=null;emittedData=!1;frameMetrics=null;loggedConflicts=new Set;lastSource=null;fps;sourceFps;generation=0;restarting=null;constructor(o,s){this.request=o;this.onRetire=s;this.fps=o.options.fps}sharesFrameRate(){return this.request.device.platform==="ios"&&this.fps!==void 0}conflicts(o,s=[],n){let a=be({...this.request.options,fps:this.fps},o,s),c=o.fps;return this.sharesFrameRate()&&c!==void 0&&(n||c<=this.fps)?a.filter((p)=>p!=="fps"):a}canShare(o){let s=this.conflicts(o.options,o.flexibleHints,!0);if(o.options.audioEnabled&&o.options.onAudioData&&!this.request.options.onAudioData)s.push("onAudioData");if(s.length===0)return!0;let n=s.join(", ");if(!this.loggedConflicts.has(n))this.loggedConflicts.add(n),e.info(`[DeviceCapture] ${o.device.deviceId}: private capture required by incompatible settings (${n})`);return!1}add(o){let s=new I(this,o.options,o.hasConsumers??!0);this.handles.add(s),s.pendingAlignment=this.emittedData,this.updateConsumers();let n=o.options.fps;if(!o.flexibleHints?.includes("fps")&&n!==void 0&&this.sharesFrameRate()&&n>this.fps)this.raiseFrameRate(n);return s}initialize(){let o=this.request.create(this.sourceOptions());if(!(o instanceof Promise))this.adopt(o),this.creation=Promise.resolve(o);else this.creation=o.then((s)=>(this.adopt(s),s)),this.creation.then(void 0,()=>this.creationFailed())}adopt(o){this.source=o,this.lastSource=o,this.updateConsumers()}raiseFrameRate(o){e.info(`[DeviceCapture] ${this.request.device.deviceId}: restarting shared capture at ${o} fps (was ${this.fps})`),this.fps=o,this.restarting??=this.restartAtTargetRate()}async restartAtTargetRate(){if(this.running=!1,this.startPromise)this.startCompletion=Promise.withResolvers(),this.startPromise=this.startCompletion.promise,this.startPromise.catch((o)=>{e.debug(`[DeviceCapture] restart start rejected: ${r(o)}`)});try{while(!this.retired&&this.sourceFps!==this.fps)if(await this.replaceSource(),!this.retired&&this.startPromise&&this.source&&this.sourceFps===this.fps)await this.source.start();this.running=!this.retired&&this.startPromise!==null,this.startCompletion?.resolve()}catch(o){this.restartFailed(o)}finally{this.restarting=null}}restartFailed(o){let s=this.startCompletion;if(this.retired){s?.resolve();return}e.warn(`[DeviceCapture] ${this.request.device.deviceId}: frame-rate restart failed`,o),this.fail(o instanceof Error?o:Error(String(o)));let n=()=>s?.reject(o);this.stopPromise?.then(n,n)}async replaceSource(){if(!this.source)await this.creation?.catch(()=>null);let o=this.source;if(this.retired||!o||this.sourceFps===this.fps)return;this.generation++,this.source=null,this.parser=new tc,this.sps=null,this.pps=null;let s=Promise.withResolvers();this.creation=s.promise,s.promise.catch((a)=>{e.debug(`[DeviceCapture] restart replacement not adopted: ${r(a)}`)});try{await o.stop()}catch(a){e.warn(`[DeviceCapture] ${this.request.device.deviceId}: stopping capture for restart failed`,a)}if(this.retired){s.reject(Error("Capture released during frame-rate restart"));return}let n;try{n=await this.request.create(this.sourceOptions())}catch(a){throw s.reject(a),a}this.adopt(n),s.resolve(n)}start(){if(this.startPromise)return this.startPromise;if(this.startCompletion=Promise.withResolvers(),this.startPromise=this.startCompletion.promise,this.restarting)return this.startPromise;if(this.source)this.startSource(this.source);else{if(!this.creation)this.initialize();this.creation.then((o)=>this.startSource(o),(o)=>this.startFailed(o))}return this.startPromise}startSource(o){if(this.retired){this.startCompletion?.resolve();return}if(this.restarting)return;let s=this.generation,n=()=>s!==this.generation||this.restarting!==null;try{o.start().then(()=>{if(n())return;this.running=!this.retired,this.startCompletion?.resolve()},(a)=>{if(n()){e.debug(`[DeviceCapture] ${this.request.device.deviceId}: replaced source start aborted: ${r(a)}`);return}this.startFailed(a)})}catch(a){e.warn(`[DeviceCapture] ${this.request.device.deviceId}: source start failed`,a),this.startFailed(a)}}creationFailed(){if(!this.startPromise)this.retire(void 0,!0)}startFailed(o){if(this.retired){this.startCompletion?.resolve();return}this.retire(void 0,!0).then(()=>this.startCompletion?.reject(o))}release(o,s){if(!o.active){if(o.releaseStart(),o===this.retryOwner&&this.retryAvailable)return this.retryAvailable=!1,this.retryOwner=null,this.finalStop=!0,this.beginStop(s,!1);return this.finalStop&&this.stopSettled?Promise.resolve():this.stopPromise??Promise.resolve()}if(o.active=!1,o.releaseStart(),this.handles.delete(o),s&&this.handles.size>0){this.retryOwner=o;let n=[...this.handles],a=Error(`Shared capture for ${this.request.device.deviceId} retired: producer declared stale`);return this.retire(s,!1,()=>{for(let c of n)this.deliver(c,(d)=>d.onError?.(a))})}if(this.handles.size===0)return this.retryOwner=o,this.retire(s);return this.updateConsumers(),Promise.resolve()}retire(o,s=!1,n){if(this.stopPromise)return this.stopPromise;this.retired=!0,this.running=!1;for(let a of this.handles)if(a.active=!1,o)a.releaseStart();return this.handles.clear(),this.beginStop(o,s,n)}beginStop(o,s,n){let a=Promise.withResolvers();this.stopSettled=!1,this.stopPromise=a.promise,this.onRetire(this),n?.();let c=()=>this.stopSource(o,s,a);if(!this.source&&this.creation)this.creation.then(c,c);else c();return a.promise}stopSource(o,s,n){try{((o&&this.source?.stopStale?this.source.stopStale(o.producerStale):this.source?.stop())??Promise.resolve()).then(()=>{this.stopSettled=!0,n.resolve()},(c)=>this.stopFailed(c,s,n))}catch(a){this.stopFailed(a,s,n)}}stopFailed(o,s,n){if(this.stopSettled=!0,e.warn(`[DeviceCapture] ${this.request.device.deviceId}: source stop failed`,o),s)n.resolve();else this.retryAvailable=this.retryOwner!==null,n.reject(o)}updateConsumers(){let o=this.source;if(o&&"setHasConsumers"in o&&typeof o.setHasConsumers==="function")o.setHasConsumers([...this.handles].some((s)=>s.hasConsumers))}deliver(o,s){try{s(o.options)}catch(n){e.warn(`[DeviceCapture] ${this.request.device.deviceId}: capture sink failed`,n)}}fanout(o){for(let s of[...this.handles])if(s.active)this.deliver(s,o)}cache(o){for(let s of o){let n=Is(s);if(n===Md)this.sps=Buffer.concat([Buffer.from([0,0,0,1]),s]);else if(n===Nd)this.pps=Buffer.concat([Buffer.from([0,0,0,1]),s])}}replay(o){for(let s of[this.sps,this.pps])if(s&&o.active)this.deliver(o,(n)=>(n.onReplayData??n.onData)(s))}replayMetrics(o){if(this.frameMetrics)this.deliver(o,(s)=>s.onFrameMetrics?.(this.frameMetrics))}deliverData(o){for(let s of[...this.handles]){if(!s.active)continue;let n=o;if(s.pendingAlignment){let a=o.indexOf(Buffer.from([0,0,1]));if(a<0)continue;let c=a>0&&o[a-1]===0?a-1:a;s.pendingAlignment=!1,this.replay(s),n=o.subarray(c)}if(s.active)this.deliver(s,(a)=>a.onData(n))}}fail(o){if(this.retired)return;let s=[...this.handles];this.retire(void 0,!0,()=>{for(let n of s)this.deliver(n,(a)=>a.onError?.(o))})}sourceOptions(){let o=this.generation;this.sourceFps=this.fps;let s=()=>this.retired||o!==this.generation,n=(a)=>(...c)=>{if(!s())a(...c)};return{...this.request.options,fps:this.fps,onData:(a)=>{if(s())return;try{this.cache(this.parser.push(a))}catch(c){e.warn(`[DeviceCapture] ${this.request.device.deviceId}: invalid H.264 stream`,c)}this.emittedData||=a.length>0,this.deliverData(a)},onEncodedAccessUnit:n(()=>{this.cache(this.parser.flush());for(let a of[...this.handles])if(a.active&&!a.pendingAlignment)this.deliver(a,(c)=>c.onEncodedAccessUnit?.())}),onSourceFrame:n(()=>this.fanout((a)=>a.onSourceFrame?.())),onSourceIdle:n(()=>this.fanout((a)=>a.onSourceIdle?.())),onIdleAttestationSupport:n((a)=>this.fanout((c)=>c.onIdleAttestationSupport?.(a))),onRotation:n((a)=>this.fanout((c)=>c.onRotation?.(a))),onDroppedFrames:n((a)=>this.fanout((c)=>c.onDroppedFrames?.(a))),onFrameMetrics:n((a)=>{this.frameMetrics=a,this.fanout((c)=>c.onFrameMetrics?.(a))}),onAudioData:this.request.options.onAudioData?n((a)=>this.fanout((c)=>c.onAudioData?.(a))):void 0,onError:n((a)=>this.fail(a))}}}function TJ(o,s,n=[]){return o instanceof I&&o.entry.handles.size>1&&o.entry.conflicts(s,n,!1).length===0}function kJ(o,s){return o instanceof I?o.entry.release(o,{producerStale:s}):o.stopStale?o.stopStale(s):o.stop()}class Ce{entries=new Map;stopping=new Map;acquire(o){let s=o.device.deviceId,n=this.entries.get(s),a=n?!n.canShare(o):!1;if(n&&!a)return n.add(o);let c=new Y(o,(m)=>{if(this.entries.get(s)===m)this.entries.delete(s);let S=m.stopPromise,g=this.stopping.get(s)??new Set;g.add(m),this.stopping.set(s,g);let y=()=>{if(m.stopPromise===S){if(g.delete(m),g.size===0)this.stopping.delete(s)}};S.then(y,y)}),d=c.add(o);if(!a)this.entries.set(s,c);let p=this.stopping.get(s);if(p?.size){let m=()=>{if(c.retired)throw Error("Capture released before creation");return c.initialize(),c.creation},S=(g)=>{let y=g.stopPromise,h=()=>g.stopPromise!==y?S(g):void 0;return y.then(h,h)};c.creation=Promise.all([...p].map(S)).then(m),c.creation.then(void 0,()=>c.creationFailed())}else try{c.initialize()}catch(m){if(e.warn(`[DeviceCapture] ${s}: source creation failed`,m),this.entries.get(s)===c)this.entries.delete(s);throw c.retired=!0,d.active=!1,c.handles.clear(),m}return d}}function WK(){return new Ce}var gt;function QO(){return gt??=WK()}var yt={low:2000000,medium:4000000,high:8000000},j={low:540,medium:720,high:1080};function D(o,s){if(!s)return o;if(!Number.isFinite(o.width)||!Number.isFinite(o.height)||o.width<=0||o.height<=0)return o;let n=j[s],a=Math.max(o.width,o.height);if(a<=n)return{width:Math.max(2,o.width&-2),height:Math.max(2,o.height&-2)};let c=n/a;if(o.height>=o.width)return{width:Math.max(2,Math.trunc(o.width*c)&-2),height:n};return{width:n,height:Math.max(2,Math.trunc(o.height*c)&-2)}}function N(o){return o?yt[o]:void 0}var wt=(Ci-5)*1000,Et={width:1280,height:720},bt=3000,Ct=2000,Pt=30000,Tt=2,Rt=Q({initialDelayMs:500,multiplier:2,maxDelayMs:2000});class X{options;adbFactory;timer;segmentTimeLimitSeconds;segmentRotateMs;healthySegmentAgeMs;runningRetryMaxAttempts;runningRetryBackoff;current=null;segmentStartedAtMs=null;rotateHandle=null;cancelRetryDelay=null;running=!1;runId=0;runningRetryAttempts=0;segmentCount=0;resolvedSize=null;lastForcedKeyFrameMs=Number.NEGATIVE_INFINITY;constructor(o){this.options=o,this.adbFactory=o.adbFactory??v,this.timer=o.timer??i,this.segmentTimeLimitSeconds=Math.min(o.segmentTimeLimitSeconds??Ci,Ci),this.segmentRotateMs=o.segmentRotateMs??wt,this.healthySegmentAgeMs=Math.min(Pt,this.segmentRotateMs/2),this.runningRetryMaxAttempts=o.runningRetryMaxAttempts??Tt,this.runningRetryBackoff=No(o.runningRetryBackoff??Rt)}get segmentsStarted(){return this.segmentCount}get isRunning(){return this.running}async start(){if(this.running)throw new t("Android H.264 source already started.");this.runId++,this.runningRetryAttempts=0,this.running=!0,await this.startSegment()}async stop(){if(this.runId++,this.cancelRetryDelay?.(),!this.running)return;this.running=!1,this.clearRotateTimer(),this.segmentStartedAtMs=null;let o=this.current;if(this.current=null,!o)return;o.kill("SIGINT")}requestKeyFrame(){let o=this.current;if(!this.running||!o)return!1;let s=this.timer.now();if(s-this.lastForcedKeyFrameMs<bt)return!1;return this.lastForcedKeyFrameMs=s,e.info(`[AndroidH264Source] keyframe requested; rotating segment ${this.segmentCount} to emit a fresh IDR`),o.kill("SIGINT"),!0}async startSegment(){let o=this.runId,s=this.adbFactory.create(this.options.device);if(!this.running||o!==this.runId)return;let n=await this.captureSize(s);if(!this.running||o!==this.runId)return;let a=["exec-out","screenrecord","--output-format=h264","--time-limit",String(this.segmentTimeLimitSeconds)],c=this.options.bitrateBps&&this.options.bitrateBps>0?this.options.bitrateBps:N(this.options.quality);if(c!==void 0)a.push("--bit-rate",String(Math.round(c)));a.push("--size",`${n.width}x${n.height}`),a.push("-"),e.info(`[AndroidH264Source] starting screenrecord segment ${this.segmentCount+1}: ${a.join(" ")}`);let d=await s.spawn(a);if(!this.running||o!==this.runId){d.kill("SIGINT");return}this.current=d,this.segmentStartedAtMs=this.timer.now(),this.segmentCount++;let p=new tc;d.stdout.on("data",(m)=>{if(this.current===d){if(m.length>0){let S=!1;try{S=p.push(m).some(F)}catch(g){this.options.onError?.(g instanceof Error?g:Error(String(g)));return}if(S)this.options.onSourceFrame?.()}this.options.onData(m)}}),d.stderr.on("data",(m)=>{let S=m.toString().trim();if(S)e.debug(`[AndroidH264Source] screenrecord stderr: ${S}`)}),d.once("error",(m)=>{if(e.warn(`[AndroidH264Source] screenrecord process error: ${m.message}`),this.current===d&&this.running)this.resetRetryAfterHealthySegment(),this.current=null,this.segmentStartedAtMs=null,this.clearRotateTimer(),d.kill("SIGINT"),this.retryAfterFailure(m)}),d.once("exit",(m,S)=>{this.handleSegmentExit(d,m,S)}),this.armRotateTimer()}handleSegmentExit(o,s,n){if(this.current!==o)return;let a=this.segmentStartedAtMs,c=a===null?0:this.timer.now()-a;if(this.resetRetryAfterHealthySegment(),this.current=null,this.segmentStartedAtMs=null,this.clearRotateTimer(),!this.running)return;if(!(n==="SIGINT"||s===0&&c>=Ct)){e.warn(`[AndroidH264Source] screenrecord failed (code=${s}, signal=${n}, age=${c}ms); retrying`),this.retryAfterFailure(Error(`screenrecord exited with code ${s}`));return}e.info(`[AndroidH264Source] segment ${this.segmentCount} ended (code=${s}, signal=${n}); rotating`);let p=this.runId;this.startSegment().catch((m)=>{e.warn(`[AndroidH264Source] failed to start next segment: ${m}`),this.retryAfterFailure(m instanceof Error?m:Error(String(m)),p)})}resetRetryAfterHealthySegment(){if(this.segmentStartedAtMs!==null&&this.timer.now()-this.segmentStartedAtMs>=this.healthySegmentAgeMs)this.runningRetryAttempts=0}async retryAfterFailure(o,s=this.runId){if(!this.running||s!==this.runId)return;if(this.runningRetryAttempts>=this.runningRetryMaxAttempts){this.running=!1,this.clearRotateTimer(),this.options.onError?.(o);return}let n=++this.runningRetryAttempts;if(await this.waitRetryBackoff(this.runningRetryBackoff.delayForAttempt(n))||!this.running||s!==this.runId)return;this.startSegment().catch((c)=>{e.warn(`[AndroidH264Source] failed to start retry segment: ${c}`),this.retryAfterFailure(c instanceof Error?c:Error(String(c)),s)})}waitRetryBackoff(o){return new Promise((s)=>{let n=this.timer.setTimeout(()=>{this.cancelRetryDelay=null,s(!1)},o);this.cancelRetryDelay=()=>{this.timer.clearTimeout(n),this.cancelRetryDelay=null,s(!0)}})}armRotateTimer(){this.clearRotateTimer(),this.rotateHandle=this.timer.setTimeout(()=>{let o=this.current;if(!this.running||!o)return;e.info(`[AndroidH264Source] rotating segment ${this.segmentCount} before time limit`),o.kill("SIGINT")},this.segmentRotateMs)}clearRotateTimer(){if(this.rotateHandle)this.timer.clearTimeout(this.rotateHandle),this.rotateHandle=null}async captureSize(o){if(this.options.size)return this.options.size;if(this.resolvedSize)return this.resolvedSize;try{let{stdout:s}=await o.executeCommand("shell wm size"),n=/Physical size:\s*(\d+)x(\d+)/.exec(s);if(n)return this.resolvedSize=At(D({width:Number(n[1]),height:Number(n[2])},this.options.quality)),this.resolvedSize}catch(s){e.debug(`[AndroidH264Source] could not query display size: ${s}`)}return this.resolvedSize=D(Et,this.options.quality),this.resolvedSize}}function At(o){let s=Math.min(1,Math.sqrt(I0*256/(o.width*o.height))),n=Math.max(2,Math.floor(o.width*s/2)*2),a=Math.max(2,Math.floor(o.height*s/2)*2);while(jq(n,a)>I0)if(n>=a)n-=2;else a-=2;return{width:n,height:a}}import{connect as $t}from"net";var It=new Set;class L{byDevice=new Map;add(o,s){let n=this.byDevice.get(o);if(n){n.add(s);return}this.byDevice.set(o,new Set([s]))}remove(o,s){let n=this.byDevice.get(o);if(!n)return;if(n.delete(s),n.size===0)this.byDevice.delete(o)}active(o){return this.byDevice.get(o)??It}}import{createHash as Ht}from"crypto";var V="automobile_video",te="dev.jasonpearson.automobile.video.VideoServer",T="/data/local/tmp/automobile-video-sessions",_t=30000,Ft=300000,Mt=300000;function kt(o){return Ht("sha256").update(o,"ascii").digest("hex")}function xt(o){let s=o.trim().split(/\s+/),n=s.find((d)=>d.startsWith(`localabstract:${V}_`)),a=s.find((d)=>d.startsWith("tcp:"));if(n===void 0||a===void 0)return null;let c=Number.parseInt(a.slice(4),10);if(!Number.isInteger(c)||c<=0)return null;return{port:c,socketName:n.slice(14)}}function Ot(o){return typeof o==="string"&&/^[a-f0-9]{64}$/.test(o)}var ie=/^[A-Za-z0-9_.-]{1,100}$/;function Z(o){return typeof o==="number"&&Number.isInteger(o)&&o>0}function ee(o){return typeof o==="number"&&Number.isFinite(o)}function Dt(o){return o===void 0||ee(o)&&o>=0}function Nt(o){if(typeof o!=="object"||o===null)return!1;if(!Bt(o))return!1;let s=o;return[Ot(s.sessionTokenHash),typeof s.socketName==="string",typeof s.socketName==="string"&&ie.test(s.socketName),typeof s.deviceSerial==="string",Z(s.ownerPid),Z(s.pid),Z(s.forwardPort),ee(s.startedAtMs),ee(s.heartbeatAtMs),Dt("heartbeatElapsedRealtimeMs"in s?s.heartbeatElapsedRealtimeMs:void 0)].every(Boolean)}function Pe(o){return o.split(/\r?\n/).flatMap((s)=>{try{let n=JSON.parse(s);if(!Nt(n))return[];return[n]}catch(n){return e.debug(`[PersistentEncoderH264Source] ignoring malformed video session lease: ${n}`),[]}})}var Lt=/^[A-Za-z0-9_.-]+\.json(?:\.tmp)?$/;function Vt(o){let s=null,n=[];for(let a of o.split(/\r?\n/)){let c=a.trim();if(c.length===0)continue;if(c.startsWith("NOW ")){let g=Number.parseInt(c.slice(4).trim(),10);if(Number.isFinite(g)&&g>=0)s=g*1000;continue}let d=c.indexOf(" ");if(d<=0)continue;let p=Number.parseInt(c.slice(0,d),10),m=c.slice(d+1).trim(),S=m.slice(m.lastIndexOf("/")+1);if(!Number.isFinite(p)||p<0||!Lt.test(S))continue;n.push({name:S,mtimeMs:p*1000})}return{nowEpochMs:s,files:n}}class oe{options;adbFactory;timer;teardownTimeoutMs;withTimeout;activeVideoSessionRegistry;constructor(o,s,n,{activeVideoSessionRegistry:a,teardown:c}){this.options=o;this.adbFactory=s;this.timer=n;this.activeVideoSessionRegistry=a,this.teardownTimeoutMs=c.timeoutMs,this.withTimeout=c.withTimeout}async reconcileExpiredLeases(o){let s;try{let a=`for f in ${T}/*.json; do [ -f "$f" ] || continue; cat "$f"; printf "\\n"; done`,c=await this.withTimeout(o.executeCommand(`shell sh -c ${b(a)}`),this.teardownTimeoutMs,"adb read video session leases");s=Pe(c.stdout)}catch(a){e.debug(`[PersistentEncoderH264Source] unable to read video session leases: ${a}`);return}if(await this.sweepOrphanLeaseFiles(o,new Set(s.map((a)=>`${a.socketName}.json`))),await this.sweepOrphanForwards(o,new Set(s.map((a)=>a.socketName))),s.length===0)return;let n=await this.readDeviceElapsedRealtimeMs(o);if(n===null){e.warn("[PersistentEncoderH264Source] unable to read device uptime; skipping stale-session cleanup");return}for(let a of s){let c=this.classifyLeaseForReclaim(a,n);if(c===null)continue;e.info(`[PersistentEncoderH264Source] stale-session-cleanup socket=${a.socketName} reason=${c}`),await this.removeForwardIfMatching(o,a.forwardPort,a.socketName),await this.terminateProcessIfMatching(o,a),await this.removeLeaseIfMatching(o,a)}}classifyLeaseForReclaim(o,s){let n=o.heartbeatElapsedRealtimeMs;if(n!==void 0&&s-n<0)return"previous-boot";if(o.deviceSerial!==this.options.device.deviceId)return null;if(n===void 0)return this.timer.now()-o.heartbeatAtMs>=Ft?"wall-clock-stale":null;return s-n>=_t?"elapsed-stale":null}async sweepOrphanLeaseFiles(o,s){let n;try{let a=`printf 'NOW %s\\n' "$(date +%s)"; for f in ${T}/*.json ${T}/*.json.tmp; do [ -f "$f" ] || continue; stat -c '%Y %n' "$f"; done`,c=await this.withTimeout(o.executeCommand(`shell sh -c ${b(a)}`),this.teardownTimeoutMs,"adb list stale video session lease files");n=Vt(c.stdout)}catch(a){e.debug(`[PersistentEncoderH264Source] unable to list video session lease files: ${a}`);return}if(n.nowEpochMs===null){e.debug("[PersistentEncoderH264Source] lease-file sweep skipped: no device clock in listing");return}for(let a of n.files){if(!a.name.endsWith(".json.tmp")&&s.has(a.name))continue;let d=n.nowEpochMs-a.mtimeMs;if(d<Mt)continue;e.info(`[PersistentEncoderH264Source] stale-lease-file-sweep name=${a.name} ageMs=${d}`);try{await this.withTimeout(o.executeCommand(`shell rm -f ${T}/${a.name}`),this.teardownTimeoutMs,"adb remove stale video session lease file")}catch(p){e.debug(`[PersistentEncoderH264Source] stale-lease-file sweep failed name=${a.name}: ${p}`)}}}async sweepOrphanForwards(o,s){let n;try{n=(await this.withTimeout(o.executeCommand("forward --list"),this.teardownTimeoutMs,"adb forward --list for orphan sweep")).stdout}catch(d){e.debug(`[PersistentEncoderH264Source] unable to list forwards for orphan sweep: ${d}`);return}let a=this.activeVideoSessionRegistry.active(this.options.device.deviceId),c=[];for(let d of n.split(/\r?\n/)){let p=xt(d);if(p===null)continue;if(s.has(p.socketName)||a.has(p.socketName))continue;c.push(p)}for(let d of c)e.info(`[PersistentEncoderH264Source] orphan-forward-sweep port=${d.port} socket=${d.socketName}`),await this.removeForwardIfMatching(o,d.port,d.socketName)}async cleanupOwnedSession(o,s,n){let a=this.adbFactory.create(this.options.device),c=await this.readLease(a,o.socketName);if(c&&this.isOwnedSessionLease(c,o,n)){await this.removeForwardIfMatching(a,c.forwardPort,c.socketName),await this.removeOwnedForward(a,s,o.socketName,c.forwardPort),await this.terminateProcessIfMatching(a,c),await this.removeLeaseIfMatching(a,c);return}if(c){e.warn(`[PersistentEncoderH264Source] refusing owned-session process cleanup socket=${o.socketName}: lease identity or PID mismatch`),await this.removeOwnedForward(a,s,o.socketName);return}await this.removeOwnedForward(a,s,o.socketName)}isOwnedSessionLease(o,s,n){return o.sessionTokenHash===kt(s.token)&&o.socketName===s.socketName&&o.ownerPid===s.ownerPid&&o.deviceSerial===s.deviceSerial&&(n===null||o.pid===n)}async removeOwnedForward(o,s,n,a){if(s!==null&&s!==a)await this.removeForwardIfMatching(o,s,n)}async readLease(o,s){try{let n=await this.withTimeout(o.executeCommand(`shell cat ${T}/${s}.json`),this.teardownTimeoutMs,"adb read owned video session lease");return Pe(n.stdout).find((a)=>a.socketName===s)??null}catch(n){return e.debug(`[PersistentEncoderH264Source] unable to read owned video session lease: ${n}`),null}}async terminateProcessIfMatching(o,s){try{let n=await this.withTimeout(o.executeCommand(`shell cat /proc/${s.pid}/cmdline`),this.teardownTimeoutMs,"adb read video-server process cmdline"),a=n.stdout.split("\x00");if(n.stdout.length===0||a.every((d)=>d.length===0)){e.debug(`[PersistentEncoderH264Source] stale-session process cleanup skipped socket=${s.socketName}: process already exited`);return}let c=a.some((d,p)=>d==="--socket-name"&&a[p+1]===s.socketName);if(!a.includes(te)||!c){e.warn(`[PersistentEncoderH264Source] refusing stale-session process cleanup socket=${s.socketName}: PID/socket mismatch`);return}await this.withTimeout(o.executeCommand(`shell kill -2 ${s.pid}`),this.teardownTimeoutMs,"adb terminate video-server process")}catch(n){e.debug(`[PersistentEncoderH264Source] stale-session process cleanup skipped socket=${s.socketName}: ${n}`)}}async removeForwardIfMatching(o,s,n){try{let a=await this.withTimeout(o.executeCommand("forward --list"),this.teardownTimeoutMs,"adb forward --list"),c=`tcp:${s}`,d=`localabstract:${n}`;if(!a.stdout.split(/\r?\n/).some((m)=>{let S=m.trim().split(/\s+/);return S.includes(c)&&S.includes(d)})){e.warn(`[PersistentEncoderH264Source] refusing stale-session forward cleanup port=${s}: destination mismatch`);return}await this.withTimeout(o.executeCommand(`forward --remove tcp:${s}`),this.teardownTimeoutMs,"adb forward --remove")}catch(a){e.debug(`[PersistentEncoderH264Source] forward --remove failed: ${a}`)}}async removeLeaseIfMatching(o,s){let n=await this.readLease(o,s.socketName);if(n?.socketName!==s.socketName||n.sessionTokenHash!==s.sessionTokenHash||n.pid!==s.pid)return;try{await this.withTimeout(o.executeCommand(`shell rm -f ${T}/${s.socketName}.json`),this.teardownTimeoutMs,"adb remove video session lease")}catch(a){e.debug(`[PersistentEncoderH264Source] stale-session lease cleanup failed: ${a}`)}}async readDeviceElapsedRealtimeMs(o){try{let s=await this.withTimeout(o.executeCommand("shell cat /proc/uptime"),this.teardownTimeoutMs,"adb read device uptime"),n=Number.parseFloat(s.stdout.trim().split(/\s+/,1)[0]??"");if(Number.isFinite(n)&&n>=0)return Math.floor(n*1000)}catch(s){e.debug(`[PersistentEncoderH264Source] unable to read device uptime: ${s}`)}return null}}function Bt(o){if(!(("sessionTokenHash"in o)&&("socketName"in o)&&("deviceSerial"in o)&&("ownerPid"in o)&&("pid"in o)&&("forwardPort"in o)&&("startedAtMs"in o)&&("heartbeatAtMs"in o)))return!1;return!0}var Re=1,se=1,Te=Buffer.from([65,86,77,72]),Ae=5000,Ie=100,He=20000,_e=5000,Fe=3,Me=Q({initialDelayMs:1000,multiplier:2,maxDelayMs:8000}),ke=60000,xe=new L,Oe=(o,s)=>new Promise((n,a)=>{let c=$t(o,"127.0.0.1"),d=!1,p=(y)=>{if(d)return;d=!0,c.off("connect",m),c.off("error",S),s?.removeEventListener("abort",g),y()},m=()=>p(()=>n(c)),S=(y)=>p(()=>a(y)),g=()=>p(()=>{c.destroy(),a(Error("video-server socket connection aborted"))});if(c.once("connect",m),c.once("error",S),s?.aborted){g();return}s?.addEventListener("abort",g,{once:!0})}),De=1e4;function Ne(o){return`${V}_${o.next().replaceAll("-","")}`}function Le(o,s){let n=Buffer.from(o,"ascii"),a=Buffer.alloc(Te.length+2+n.length),c=Te.copy(a,0);return c=a.writeUInt8(s,c),c=a.writeUInt8(n.length,c),n.copy(a,c),a}function Ve(o){return/^[A-Za-z0-9-]{8,80}$/.test(o)}var R="/data/local/tmp/automobile-video.jar";function Ut(o){let s=null,n=null;for(let a of o.split(/\r?\n/)){let c=a.trim();if(c.length===0)continue;if(/^[0-9]+$/.test(c)){n=Number.parseInt(c,10);continue}let d=/^([a-f0-9]{64})(?:\s|$)/.exec(c);if(d)s=d[1]}if(s===null||n===null||!Number.isInteger(n)||n<0)return null;return{sha256:s,size:n}}class ne{options;commandTimeoutMs;withTimeout;expectedJarIntegrity=null;remoteJarVerified=!1;jarIntegrityProvider;constructor(o,s,n){this.options=o;this.commandTimeoutMs=s;this.withTimeout=n;this.jarIntegrityProvider=o.jarIntegrityProvider}async ensureRemoteJar(o){let s=await this.resolveExpectedJarIntegrity(),n=await this.statAndHashRemoteJar(o);if(n!==null&&n.size===s.size&&n.sha256===s.sha256){this.remoteJarVerified=!0,e.info(`[PersistentEncoderH264Source] on-device video-server jar already matches (sha256=${s.sha256}, size=${s.size}); skipping push`);return}if(n!==null)e.info(`[PersistentEncoderH264Source] on-device video-server jar differs (expected sha256=${s.sha256} size=${s.size}, remote sha256=${n.sha256} size=${n.size}); pushing`);await this.withTimeout(o.executeCommand(`push "${this.options.jarPath}" ${R}`,{timeoutMs:this.commandTimeoutMs}),this.commandTimeoutMs,"adb push video-server jar")}async verifyRemoteJarBeforeLaunch(o){if(this.remoteJarVerified)return;let s=await this.resolveExpectedJarIntegrity(),n=await this.statAndHashRemoteJar(o);if(n===null){e.warn(`[PersistentEncoderH264Source] could not hash the on-device jar at ${R} before launch; proceeding on the just-pushed trusted bytes (integrity verification degraded)`);return}if(n.size!==s.size||n.sha256!==s.sha256)throw new t(`Refusing to launch video-server: the on-device jar at ${R} failed integrity verification (expected sha256=${s.sha256} size=${s.size}, got sha256=${n.sha256} size=${n.size}). The bytes on the device do not match the verified host copy; app_process will not be launched.`);this.remoteJarVerified=!0}async resolveExpectedJarIntegrity(){if(this.expectedJarIntegrity===null){let s=await(this.jarIntegrityProvider??Jd.getInstance()).computeLocalJarIntegrity(this.options.jarPath);this.expectedJarIntegrity={sha256:s.sha256.toLowerCase(),size:s.size}}return this.expectedJarIntegrity}async statAndHashRemoteJar(o){let s=`sha256sum ${R} 2>/dev/null; wc -c < ${R} 2>/dev/null`;try{let n=await this.withTimeout(o.executeCommand(`shell sh -c ${b(s)}`,{timeoutMs:this.commandTimeoutMs}),this.commandTimeoutMs,"adb hash video-server jar");return Ut(n.stdout)}catch(n){return e.debug(`[PersistentEncoderH264Source] unable to hash on-device video-server jar: ${n}`),null}}}function Be(o){let s=o.session;if(!s||o.forwardedPort===null)throw Error("Video server session was not initialized.");let n=["shell",`CLASSPATH=${R}`,"app_process","/",te,"--quality",o.options.quality??"medium","--socket-name",s.socketName,"--session-token",s.token,"--owner-pid",String(s.ownerPid),"--device-serial",s.deviceSerial,"--forward-port",String(o.forwardedPort)];if(o.options.bitrateBps&&o.options.bitrateBps>0)n.push("--bit-rate",String(Math.round(o.options.bitrateBps)));if(Number.isInteger(o.options.fps)&&o.options.fps>0)n.push("--fps",String(o.options.fps));if(o.options.size)n.push("--size",`${o.options.size.width}x${o.options.size.height}`);if(o.options.audioEnabled)n.push("--audio");return n}function $e(o){let s=o.sessionTokenFactory();if(!Ve(s))throw Error("Video server session token factory returned an unsafe token.");let n=o.socketNameFactory();if(!ie.test(n))throw Error("Video server socket-name factory returned an unsafe socket name.");return{token:s,socketName:n,ownerPid:process.pid,deviceSerial:o.options.device.deviceId}}function Ue(o){if(o===null)return e.warn("[PersistentEncoderH264Source] device advertised no handshake protocol; streaming without a token handshake (defense-in-depth degraded, UID gating still applies)"),null;return Math.min(o,se)}function We(o,s){let{session:n,negotiatedHandshakeVersion:a}=o;if(!n||a===null)return;try{s.write(Le(n.token,a))}catch(c){e.debug(`[PersistentEncoderH264Source] handshake write failed: ${c}`)}}function qe(o,s,n,a){return new Promise((c,d)=>{let p=!1,m=o.timer.setTimeout(()=>{if(p)return;p=!0,e.warn(`[PersistentEncoderH264Source] ${a} timed out after ${n}ms`),d(new t(`${a} did not complete within ${n}ms`))},n);s.then((S)=>{if(p)return;p=!0,o.timer.clearTimeout(m),c(S)},(S)=>{if(p)return;p=!0,o.timer.clearTimeout(m),d(S instanceof Error?S:Error(String(S)))})})}var q=1748121140,Wt=1634563448,x=1932605036,qt=1,Kt=2,M=12,U=12,Ke=16,W=16,ze=16777216,zt=16,ae=1n<<63n,Je=1n<<62n,Ge=1n<<61n,ue=59n,Jt=0b11n<<ue,Ye=(1n<<ue)-1n;class k{callbacks;buffered=new b0;header=null;muxTracks=null;constructor(o){this.callbacks=o}static rotationFromFlags(o){if((o&ae)===0n)return;return Number((o&Jt)>>ue)}assertWithinCap(o,s,n){if(o>s)throw new t(`video-server stream declared ${n}=${o}, exceeding the ${s} cap; the stream is likely corrupt or out of sync.`)}push(o){if(this.buffered.append(o),!this.header&&!this.muxTracks&&!this.readStreamHeader())return;if(this.muxTracks){this.drainMuxPackets();return}while(this.buffered.length>=U){let s=this.buffered.peek(U),n=s.readBigUInt64BE(0),a=s.readUInt32BE(8);if(this.assertWithinCap(a,ze,"packet size"),this.buffered.length<U+a)break;this.buffered.discard(U);let c=this.buffered.takeDetached(a);this.callbacks.onPacket({trackId:qt,codecId:this.header?.codecId??q,data:c,config:(n&ae)!==0n,keyFrame:(n&Je)!==0n,replayed:(n&Ge)!==0n,rotation:k.rotationFromFlags(n),ptsUs:Number(n&Ye)})}}readStreamHeader(){if(this.buffered.length<M)return!1;let o=this.buffered.peek(M),s=o.readUInt32BE(0);if(s===Wt){let n=o.readUInt32BE(8);this.assertWithinCap(n,zt,"trackCount");let a=M+n*Ke;if(this.buffered.length<a)return!1;let c=this.buffered.peek(a);this.muxTracks=new Map;let d=null,p=!1;for(let m=0;m<n;m++){let S=M+m*Ke,g=c.readUInt32BE(S),y=c.readUInt32BE(S+4),h=c.readUInt32BE(S+8),w=c.readUInt32BE(S+12);if(this.muxTracks.set(g,{codecId:y,param1:h,param2:w}),y===q)d={codecId:y,width:h,height:w};else if(g===Kt&&y===x)p=!0}if(d)this.header={...d,muxed:!0,audio:p,muxVersion:c.readUInt32BE(4)},this.callbacks.onHeader?.(this.header);this.buffered.discard(a)}else this.header={codecId:s,width:o.readUInt32BE(4),height:o.readUInt32BE(8)},this.buffered.discard(M),this.callbacks.onHeader?.(this.header);return!0}drainMuxPackets(){while(this.buffered.length>=W){let o=this.buffered.peek(W),s=o.readUInt32BE(0),n=o.readBigUInt64BE(4),a=o.readUInt32BE(12);if(this.assertWithinCap(a,ze,"packet size"),this.buffered.length<W+a)break;this.buffered.discard(W);let c=this.buffered.takeDetached(a),d=this.muxTracks?.get(s);if(!d)continue;this.callbacks.onPacket({trackId:s,codecId:d.codecId,data:c,config:(n&ae)!==0n,keyFrame:(n&Je)!==0n,replayed:(n&Ge)!==0n,rotation:k.rotationFromFlags(n),ptsUs:Number(n&Ye)})}}}var Gt=/^VIDEO_SESSION_READY token=([^ ]+) pid=(\d+) socket=([^ ]+?)(?: proto=(\d+))?$/,je="Streaming started",Yt=/(?:^|\s)dropped=(\d+)(?:\s|$)/;class ce{options;timer;readyTimeoutMs;session;resolveStartupAudioHeader;resolveStartupAudioPacket;serverOutputObserver=null;serverOutputBuffer="";sawStreamingStarted=!1;constructor(o,s,n,a){this.options=o;this.timer=s;this.readyTimeoutMs=n;this.session=a}waitForReady(o){let s=this.session()?.token;if(!s)throw Error("Video server session token was unavailable.");return new Promise((n,a)=>{let c=!1,d="",p=(h)=>{if(c)return;c=!0,this.timer.clearTimeout(m),o.stdout.off("data",S),o.removeListener("exit",g),o.removeListener("error",y),h()},m=this.timer.setTimeout(()=>{p(()=>a(new t(`video-server did not become session-ready within ${this.readyTimeoutMs}ms. Set AUTOMOBILE_VIDEO_SERVER_JAR to a current automobile-video.jar.`)))},this.readyTimeoutMs),S=(h)=>{if(c)return;d+=h.toString();let w=d.split(/\r?\n/);d=w.pop()??"";for(let E of w){let C=Gt.exec(E);if(!C)continue;if(C[1]!==s||C[3]!==this.session()?.socketName){p(()=>a(new t(`video-server session readiness token mismatch (expected=${s}, got=${C[1]})`)));return}let _=Number.parseInt(C[2],10);if(!Number.isInteger(_)||_<=0){p(()=>a(new t("video-server returned an invalid session PID")));return}let P=C[4]!==void 0?Number.parseInt(C[4],10):null;p(()=>n({pid:_,protocolVersion:P}));return}},g=(h,w)=>{p(()=>a(Error(`video-server exited before ready (code=${h}, signal=${w})`)))},y=(h)=>p(()=>a(h));o.stdout.on("data",S),o.once("exit",g),o.once("error",y)})}waitForStreamingStarted(o){if(this.sawStreamingStarted)return Promise.resolve();return this.waitForServerLine(o,je,`video-server did not start streaming within ${this.readyTimeoutMs}ms`,"video-server exited before streaming started")}waitForStartupAudioReady(){return new Promise((o,s)=>{let n=!1,a=!1,c=this.timer.setTimeout(()=>{d(()=>s(new t(`video-server did not produce PCM audio within ${this.readyTimeoutMs}ms`)))},this.readyTimeoutMs),d=(p)=>{if(n)return;n=!0,this.timer.clearTimeout(c),this.resolveStartupAudioHeader=void 0,this.resolveStartupAudioPacket=void 0,p()};this.resolveStartupAudioHeader=(p)=>{if(p.muxed===!0&&p.audio===!0){a=!0;return}d(()=>{s(new t("Audio was requested, but video-server did not advertise muxed PCM audio. Rebuild or provide a current automobile-video.jar."))})},this.resolveStartupAudioPacket=(p)=>{if(a&&p.codecId===x&&p.data.length>0)d(o)}})}waitForServerLine(o,s,n,a){return new Promise((c,d)=>{let p=!1,m="",S=(E)=>{if(p)return;p=!0,this.timer.clearTimeout(g),o.stdout.off("data",y),o.removeListener("exit",h),o.removeListener("error",w),E()},g=this.timer.setTimeout(()=>{S(()=>d(new t(n)))},this.readyTimeoutMs),y=(E)=>{if(p)return;if(m+=E.toString(),m.includes(s))S(c)},h=(E,C)=>{S(()=>d(Error(`${a} (code=${E}, signal=${C})`)))},w=(E)=>S(()=>d(E));o.stdout.on("data",y),o.once("exit",h),o.once("error",w)})}attachServerOutputObserver(o){this.serverOutputBuffer="",this.sawStreamingStarted=!1;let s=(n)=>{this.serverOutputBuffer+=n.toString();let a=this.serverOutputBuffer.split(/\r?\n/);if(this.serverOutputBuffer=a.pop()??"",a.includes(je))this.sawStreamingStarted=!0;for(let c of a){if(!c.startsWith("VIDEO_STATS "))continue;let d=Yt.exec(c),p=d?Number(d[1]):NaN;if(Number.isSafeInteger(p)&&p>=0)this.options.onDroppedFrames?.(p)}};this.serverOutputObserver=s,o.stdout.on("data",s)}detachServerOutputObserver(o){if(this.serverOutputObserver)o.stdout.off("data",this.serverOutputObserver);this.serverOutputObserver=null,this.serverOutputBuffer="",this.sawStreamingStarted=!1}}class K{leases;jar;startup;options;adbFactory;connector;timer;readyTimeoutMs;commandTimeoutMs;teardownTimeoutMs;localSocketReconnectWindowMs;localSocketReconnectRetryMs;sessionTokenFactory;socketNameFactory;activeVideoSessionRegistry;maxServerRelaunchAttempts;serverRelaunchBackoff;serverRelaunchHealthyWindowMs;server=null;socket=null;forwardedPort=null;session=null;deviceProcessId=null;negotiatedHandshakeVersion=null;running=!1;teardownPromise=null;socketReconnectPromise=null;socketReconnectAbortController=null;telemetryInitialized=!1;lastEncodedFrameTimestampUs=null;lastIdrTimestampUs=null;idrRequestCount=0;idrCompletionCount=0;pendingIdrRequests=0;encodedAccessUnitCount=0;serverStartupInProgress=null;serverStartupFailure=null;serverRelaunchAttempts=0;serverHealthySinceMs=null;relaunching=!1;relaunchAbortController=null;constructor(o){this.options=o,this.adbFactory=o.adbFactory??v,this.connector=o.connector??Oe,this.timer=o.timer??i,this.readyTimeoutMs=o.readyTimeoutMs??De,this.commandTimeoutMs=o.commandTimeoutMs??He,this.teardownTimeoutMs=o.teardownTimeoutMs??_e,this.localSocketReconnectWindowMs=o.localSocketReconnectWindowMs??Ae,this.localSocketReconnectRetryMs=o.localSocketReconnectRetryMs??Ie,this.sessionTokenFactory=o.sessionTokenFactory??(()=>B.next()),this.socketNameFactory=o.socketNameFactory??(()=>Ne(B)),this.activeVideoSessionRegistry=o.activeVideoSessionRegistry??xe,this.jar=new ne(o,this.commandTimeoutMs,(n,a,c)=>this.withTimeout(n,a,c));let s=K.resolveRelaunchPolicy(o);this.maxServerRelaunchAttempts=s.maxAttempts,this.serverRelaunchBackoff=s.backoff,this.serverRelaunchHealthyWindowMs=s.healthyWindowMs,this.leases=new oe(o,this.adbFactory,this.timer,{activeVideoSessionRegistry:this.activeVideoSessionRegistry,teardown:{timeoutMs:this.teardownTimeoutMs,withTimeout:(n,a,c)=>this.withTimeout(n,a,c)}}),this.startup=new ce(o,this.timer,this.readyTimeoutMs,()=>this.session),this.validateTimings()}static resolveRelaunchPolicy(o){return{maxAttempts:o.maxServerRelaunchAttempts??Fe,backoff:No(o.serverRelaunchBackoff??Me),healthyWindowMs:o.serverRelaunchHealthyWindowMs??ke}}validateTimings(){if(this.commandTimeoutMs<=0||this.teardownTimeoutMs<=0)throw new t("Video server command timeouts must be positive milliseconds.");if(this.localSocketReconnectWindowMs<=0||this.localSocketReconnectRetryMs<=0)throw new t("Local socket reconnect timings must be positive milliseconds.");if(!Number.isInteger(this.maxServerRelaunchAttempts)||this.maxServerRelaunchAttempts<0)throw new t("Server relaunch attempts must be a non-negative integer.");if(this.serverRelaunchHealthyWindowMs<=0)throw new t("Server relaunch healthy window must be positive milliseconds.")}get isRunning(){return this.running}getTelemetry(){if(!this.telemetryInitialized)return{lastEncodedFrameTimestampUs:null,lastIdrTimestampUs:null,idrRequestCount:null,idrCompletionCount:null,encodedAccessUnitCount:null};return{lastEncodedFrameTimestampUs:this.lastEncodedFrameTimestampUs,lastIdrTimestampUs:this.lastIdrTimestampUs,idrRequestCount:this.idrRequestCount,idrCompletionCount:this.idrCompletionCount,encodedAccessUnitCount:this.encodedAccessUnitCount}}async start(){if(this.running)throw new t("Persistent encoder H.264 source already started.");this.running=!0,this.serverStartupInProgress=null,this.serverStartupFailure=null;try{await this.launch(),this.serverHealthySinceMs=this.timer.now()}catch(o){throw this.running=!1,await this.beginTeardown(),o}}async stop(){if(!this.running){await this.teardownPromise;return}this.running=!1,this.relaunchAbortController?.abort(),await this.beginTeardown()}requestKeyFrame(){let o=this.socket;if(!this.running||!o)return!1;this.telemetryInitialized=!0,this.idrRequestCount++,this.pendingIdrRequests++;try{return o.write(Buffer.from([Re])),!0}catch(s){return this.pendingIdrRequests--,e.debug(`[PersistentEncoderH264Source] keyframe request write failed: ${s}`),!1}}withTimeout(o,s,n){return qe({timer:this.timer},o,s,n)}async launch(){let o=this.adbFactory.create(this.options.device),s=await this.prepareSession(o);if(s===null)return;let n=await this.spawnAndWaitForServer(o);if(!n)return;await this.connectToServer(n,s)}async prepareSession(o){if(!this.running)return null;let s=this.createSession();if(this.session=s,this.deviceProcessId=null,this.jar.remoteJarVerified=!1,this.activeVideoSessionRegistry.add(this.options.device.deviceId,s.socketName),await this.leases.reconcileExpiredLeases(o),!this.running)return null;if(await this.jar.ensureRemoteJar(o),!this.running)return null;let n=await this.withTimeout(o.executeCommand(`forward tcp:0 localabstract:${s.socketName}`,{timeoutMs:this.commandTimeoutMs}),this.commandTimeoutMs,"adb forward video-server socket"),a=Number.parseInt(n.stdout.trim(),10);if(!Number.isInteger(a)||a<=0)throw new t(`adb forward returned an invalid port: "${n.stdout.trim()}"`);if(!this.running)return await this.leases.removeForwardIfMatching(o,a,s.socketName),null;return this.forwardedPort=a,a}async spawnAndWaitForServer(o){if(await this.jar.verifyRemoteJarBeforeLaunch(o),!this.running)return null;let s=await this.withTimeout(o.spawn(this.buildServerArgs()),this.commandTimeoutMs,"adb spawn video-server");if(!this.running)return s.kill("SIGINT"),null;this.server=s,this.startup.attachServerOutputObserver(s),s.stderr.on("data",(a)=>{let c=a.toString().trim();if(c)e.debug(`[PersistentEncoderH264Source] server stderr: ${c}`)});let n=await this.startup.waitForReady(s);return this.deviceProcessId=n.pid,this.negotiatedHandshakeVersion=Ue(n.protocolVersion),this.serverStartupInProgress=s,this.watchServer(s),this.running?s:null}async connectToServer(o,s){this.throwIfServerStartupFailed(o);let n=this.options.audioEnabled?this.startup.waitForStreamingStarted(o):null;n?.catch((h)=>{e.debug(`[PersistentEncoderH264Source] Audio startup waiter rejected: ${r(h)}`)});let a=new AbortController,c=this.timer.now()+this.commandTimeoutMs,d=this.connectBeforeDeadline(s,c,a.signal),p=!1,m,S=new Promise((h,w)=>{m=w}),g=(h,w)=>{m(Error(`video-server exited (code=${h}, signal=${w})`))},y=(h)=>{m(h)};o.once("exit",g),o.once("error",y);try{let h=await l([d,S],{timer:this.timer,label:"video-server connection"});if(p=!0,!this.running){h.destroy();return}this.socket=h,this.sendHandshake(h);let w=this.options.audioEnabled?this.startup.waitForStartupAudioReady():null,E=this.options.audioEnabled?"startup":"post-start",C,_=this.options.audioEnabled?new Promise((P,pt)=>{C=pt}):null;if(this.wireSocket(h,(P)=>{if(E==="startup"){C?.(P);return}this.reconnectSocket(h,P)},(P)=>this.startup.resolveStartupAudioHeader?.(P)),this.options.audioEnabled){if(await l([Promise.all([n,w]),Promise.resolve(_)],{timer:this.timer,label:"video-server audio startup"}),!this.running)return}E="post-start"}finally{if(o.removeListener("exit",g),o.removeListener("error",y),!p)a.abort(),d.catch((h)=>{e.debug(`[PersistentEncoderH264Source] Losing connection rejected: ${r(h)}`)});if(this.serverStartupInProgress===o)this.serverStartupInProgress=null}}throwIfServerStartupFailed(o){let s=this.serverStartupFailure;if(s?.server===o)throw s.error}watchServer(o){o.once("exit",(s,n)=>{if(this.server!==o)return;if(this.startup.detachServerOutputObserver(o),this.server=null,this.serverStartupInProgress===o){this.serverStartupFailure={server:o,error:Error(`video-server exited (code=${s}, signal=${n})`)};return}this.handlePostStartServerLoss(Error(`video-server exited (code=${s}, signal=${n})`))}),o.once("error",(s)=>{if(this.serverStartupInProgress===o){this.serverStartupFailure={server:o,error:s};return}this.handlePostStartServerLoss(s)})}buildServerArgs(){return Be({session:this.session,forwardedPort:this.forwardedPort,options:this.options})}createSession(){return $e({options:this.options,sessionTokenFactory:this.sessionTokenFactory.bind(this),socketNameFactory:this.socketNameFactory.bind(this)})}sendHandshake(o){return We({session:this.session,negotiatedHandshakeVersion:this.negotiatedHandshakeVersion},o)}wireSocket(o,s,n){let a=new k({onHeader:(p)=>{n?.(p),this.requestKeyFrame(),e.info(`[PersistentEncoderH264Source] stream ${p.width}x${p.height} codec=0x${p.codecId.toString(16)}`)},onPacket:(p)=>{if(this.socket===o){if(p.codecId===q)this.deliverVideoPacket(p);else if(p.codecId===x)this.options.onAudioData?.(p.data),this.startup.resolveStartupAudioPacket?.(p)}}}),c=!1,d=(p)=>{if(c||this.socket!==o)return;c=!0,s(p)};o.on("data",(p)=>{try{a.push(p)}catch(m){d(m instanceof Error?m:Error(String(m)))}}),o.on("error",d),o.on("close",()=>d(Error("video-server socket closed")))}deliverVideoPacket(o){if(this.observeVideoPacket(o),o.config&&o.rotation!==void 0)this.options.onRotation?.(o.rotation);if(!o.config&&!o.replayed)this.options.onSourceFrame?.();this.options.onData(o.data),this.options.onEncodedAccessUnit?.()}observeVideoPacket(o){if(o.config)return;if(this.telemetryInitialized=!0,o.replayed){if(this.lastEncodedFrameTimestampUs??=o.ptsUs,o.keyFrame)this.lastIdrTimestampUs??=o.ptsUs;return}if(this.lastEncodedFrameTimestampUs=o.ptsUs,this.encodedAccessUnitCount++,!o.keyFrame)return;if(this.lastIdrTimestampUs=o.ptsUs,this.pendingIdrRequests>0)this.pendingIdrRequests--,this.idrCompletionCount++}reconnectSocket(o,s){if(!this.running||this.socket!==o||this.socketReconnectPromise!==null)return;this.socket=null,o.destroy();let n=new AbortController;this.socketReconnectAbortController=n,this.socketReconnectPromise=this.tryReconnectSocket(s,n.signal).finally(()=>{if(this.socketReconnectAbortController===n)this.socketReconnectAbortController=null;this.socketReconnectPromise=null})}async tryReconnectSocket(o,s){let n=this.forwardedPort;if(n===null){this.failIfRunning(o);return}let a=this.timer.now()+this.localSocketReconnectWindowMs,c=o;while(this.running&&this.server!==null&&!s.aborted){try{let p=await this.connectBeforeDeadline(n,a,s);if(!this.running||this.server===null||s.aborted){p.destroy();return}this.socket=p,this.sendHandshake(p),this.wireSocket(p,(m)=>this.reconnectSocket(p,m)),e.info("[PersistentEncoderH264Source] reconnected to retained video-server socket");return}catch(p){c=p instanceof Error?p:Error(String(p))}let d=a-this.timer.now();if(d<=0)break;await this.waitForReconnectRetry(Math.min(this.localSocketReconnectRetryMs,d),s)}this.failReconnectIfServerRetained(s,c)}failReconnectIfServerRetained(o,s){if(o.aborted||this.server===null)return;this.failIfRunning(Error(`video-server socket did not reconnect within ${this.localSocketReconnectWindowMs}ms: ${s.message}`))}waitForReconnectRetry(o,s){if(s.aborted)return Promise.resolve();return new Promise((n)=>{let a=!1,c=()=>{if(a)return;a=!0,this.timer.clearTimeout(d),s.removeEventListener("abort",c),n()},d=this.timer.setTimeout(c,o);s.addEventListener("abort",c,{once:!0})})}async connectBeforeDeadline(o,s,n){let a=s-this.timer.now();if(a<=0)throw Error("video-server socket reconnect deadline elapsed");let c=new AbortController,d=new AbortController,p=!1,m=()=>c.abort();if(n.aborted)m();else n.addEventListener("abort",m,{once:!0});let S=this.connector(o,c.signal),g=this.timer.setTimeout(()=>{p=!0,c.abort()},a),y=()=>d.abort(Error(p?"video-server socket reconnect attempt timed out":"video-server socket reconnect attempt aborted"));if(c.signal.aborted)y();else c.signal.addEventListener("abort",y,{once:!0});try{return await l(S,{timer:this.timer,signal:d.signal,label:"video-server socket reconnect attempt"})}finally{if(this.timer.clearTimeout(g),n.removeEventListener("abort",m),c.signal.removeEventListener("abort",y),c.signal.aborted)S.then((h)=>h.destroy(),()=>{})}}handlePostStartServerLoss(o){if(!this.running||this.relaunching)return;this.resetRelaunchBudgetAfterHealthyRun(),this.relaunching=!0,this.relaunchAbortController=new AbortController,this.recoverFromServerLoss(o).finally(()=>{this.relaunching=!1})}async recoverFromServerLoss(o){let s=this.relaunchAbortController?.signal??new AbortController().signal,n=o;while(this.canAttemptRelaunch(s)){this.serverRelaunchAttempts++;let a=await this.attemptRelaunch(this.serverRelaunchAttempts,n,s);if(a.status!=="retry")return;n=a.error}if(this.running&&!s.aborted)await this.fallBackAfterRelaunchExhausted(n)}resetRelaunchBudgetAfterHealthyRun(){if(this.serverHealthySinceMs!==null&&this.timer.now()-this.serverHealthySinceMs>=this.serverRelaunchHealthyWindowMs)this.serverRelaunchAttempts=0}canAttemptRelaunch(o){return this.running&&!o.aborted&&this.serverRelaunchAttempts<this.maxServerRelaunchAttempts}async attemptRelaunch(o,s,n){let a=this.serverRelaunchBackoff.delayForAttempt(o);if(e.warn(`[PersistentEncoderH264Source] on-device server lost post-start (${s.message}); relaunch attempt ${o}/${this.maxServerRelaunchAttempts} after ${a}ms`),await this.delayBeforeRelaunch(a,n),!this.running||n.aborted)return{status:"aborted",error:s};if(await this.beginTeardown(),!this.running||n.aborted)return{status:"aborted",error:s};try{if(await this.launch(),!this.running)return{status:"aborted",error:s};return this.serverHealthySinceMs=this.timer.now(),e.info(`[PersistentEncoderH264Source] on-device server relaunched after ${o} attempt(s)`),{status:"recovered",error:s}}catch(c){let d=c instanceof Error?c:Error(String(c));return e.warn(`[PersistentEncoderH264Source] relaunch attempt ${o} failed: ${d.message}`,c),{status:"retry",error:d}}}delayBeforeRelaunch(o,s){if(o<=0||s.aborted)return Promise.resolve();return new Promise((n)=>{let a=()=>{this.timer.clearTimeout(c),s.removeEventListener("abort",a),n()},c=this.timer.setTimeout(a,o);s.addEventListener("abort",a,{once:!0})})}async fallBackAfterRelaunchExhausted(o){if(!this.running)return;let s=this.options.onScreenrecordFallback;if(!s){this.failIfRunning(o);return}e.warn(`[PersistentEncoderH264Source] relaunch budget of ${this.maxServerRelaunchAttempts} spent; falling back to screenrecord: ${o.message}`),this.running=!1,await this.beginTeardown();try{await s(o)}catch(n){this.options.onError?.(n instanceof Error?n:Error(String(n)))}}failIfRunning(o){if(!this.running)return;this.running=!1,this.beginTeardown(),this.options.onError?.(o)}beginTeardown(){return this.teardownPromise??=this.teardown().finally(()=>{this.teardownPromise=null}),this.teardownPromise}async teardown(){let o=this.socketReconnectAbortController;this.socketReconnectAbortController=null,o?.abort();let s=this.socket;this.socket=null,s?.destroy();let n=this.server;if(this.server=null,n)this.startup.detachServerOutputObserver(n);n?.kill("SIGINT");let a=this.session,c=this.forwardedPort,d=this.deviceProcessId;if(this.forwardedPort=null,this.deviceProcessId=null,this.session=null,a)await this.leases.cleanupOwnedSession(a,c,d),this.activeVideoSessionRegistry.remove(this.options.device.deviceId,a.socketName);else if(c!==null)e.warn(`[PersistentEncoderH264Source] refusing forward cleanup port=${c}: session identity is unavailable`)}}var jt={createPersistent:(o)=>new K(o),createScreenrecord:(o)=>new X(o)};class Xe{buildScreenrecord;active=null;stopped=!1;persistent;constructor(o,s){this.buildScreenrecord=s;this.persistent=o((n)=>this.switchToScreenrecord(n))}async start(){this.stopped=!1,this.active=this.persistent;try{await this.persistent.start()}catch(o){await this.switchToScreenrecord(o instanceof Error?o:Error(String(o)))}}async switchToScreenrecord(o){if(this.stopped||this.active!==this.persistent)return;e.warn(`[webrtc] persistent encoder unavailable, falling back to screenrecord: ${o.message}`);let s=this.buildScreenrecord();this.active=s,await s.start()}async stop(){this.stopped=!0;let o=this.active;this.active=null,await o?.stop()}requestKeyFrame(){return this.active?.requestKeyFrame?.()??!1}get getTelemetry(){let o=this.active;return o?.getTelemetry?.bind(o)}}function Qe(o,s,n=jt){if(!s){if(o.audioEnabled)throw Error("WebRTC audio requires the persistent Android video-server jar.");return n.createScreenrecord(o)}let a=(c)=>({device:o.device,onData:o.onData,onSourceFrame:o.onSourceFrame,onEncodedAccessUnit:o.onEncodedAccessUnit,onAudioData:o.onAudioData,onRotation:o.onRotation,onDroppedFrames:o.onDroppedFrames,onError:o.onError,bitrateBps:o.bitrateBps,size:o.size,quality:o.quality,fps:o.fps,audioEnabled:o.audioEnabled,adbFactory:o.adbFactory,timer:o.timer,jarPath:s,onScreenrecordFallback:c});if(o.audioEnabled)return n.createPersistent(a());return new Xe((c)=>n.createPersistent(a(c)),()=>n.createScreenrecord(o))}class A extends t{finalStartupError(o){return this}}class Wp extends t{approvalTarget;constructor(o="AutoMobile"){super("Screen Recording permission is required to discover and observe iOS Simulator windows.");this.approvalTarget=o}}class le extends A{finalStartupError(o){return new Wp(o??"AutoMobile")}}var Xt="warn: no frames received";function Ze(o,s){let n=s===null?"":` (last stage: ${s})`,a=Zt(o,s);return new A(`iOS screen capture did not produce a first frame${n}.${a}`)}function Qt(o,s){return new le(Ze(o,s).message)}function Zt(o,s){switch(s){case null:case"helper-executable-found":case"helper-process-spawned":return o.kind==="simulator"?" Screen Recording permission may be required to observe the iOS Simulator window.":"";case"permission-ready":return" The Simulator window could not be resolved; ensure the Simulator is booted and visible.";case"target-resolved":return" Capture never started after the window resolved (hung start); retry or restart the Simulator.";case"capture-started":return o.kind==="simulator"?" Screen Recording permission may be required to observe the iOS Simulator window.":"";case"first-frame":return""}}function er(o){return o.toLowerCase().includes(Xt)}function de(o){return o.trimStart().toLowerCase().startsWith("error:")}class pe{timer;firstFrameTimeoutMs;context;cancelFirstFrameWait=null;cancelFirstAudioWait=null;rejectFirstAudioWait=null;constructor(o,s,n){this.timer=o;this.firstFrameTimeoutMs=s;this.context=n}waitForFirstAudio(o){let s=this.context.generation();return new Promise((n,a)=>{let c=!1,d=(g)=>{if(c)return;if(c=!0,this.timer.clearTimeout(S),this.cancelFirstAudioWait===p)this.cancelFirstAudioWait=null;if(this.rejectFirstAudioWait===m)this.rejectFirstAudioWait=null;g()},p=()=>d(n),m=(g)=>d(()=>a(g)),S=this.timer.setTimeout(()=>{d(()=>a(new t("iOS Simulator audio capture did not produce PCM audio before startup timed out.")))},this.firstFrameTimeoutMs);this.cancelFirstAudioWait=p,this.rejectFirstAudioWait=m,o.on("audio",()=>{if(this.context.isCurrentHelper(o,s)&&this.context.isActive())d(n)}),o.on("error",(g)=>{if(this.context.isCurrentHelper(o,s)&&this.context.isActive())d(()=>a(g))}),o.on("exit",(g)=>{if(this.context.isCurrentHelper(o,s)&&this.context.isActive())d(()=>a(Error(this.context.describeHelperExit(g,"exited before audio"))))})})}waitForFirstEncodedRecord(o,s){let n=this.context.generation();return new Promise((a,c)=>{let d=!1,p=!1,m=(h)=>{if(d)return;if(d=!0,this.timer.clearTimeout(y),this.cancelFirstFrameWait===S)this.cancelFirstFrameWait=null;h()},S=()=>{m(a)},g=(h)=>{m(()=>c(this.context.encodedStartupError(h)))},y=this.timer.setTimeout(()=>{g(Ze(s,this.context.lastReadinessPhase()))},this.firstFrameTimeoutMs);this.cancelFirstFrameWait=S,o.on("encodedVideo",()=>{if(!this.context.isCurrentHelper(o,n)||!this.context.isActive())return;p=!0,this.context.markRunning(),m(a)}),o.on("stderr",(h)=>{if(!this.context.isCurrentHelper(o,n)||!this.context.isActive())return;if(er(h))m(()=>c(Qt(s,this.context.lastReadinessPhase())));else if(de(h))g(this.context.helperFailureFor(h))}),o.on("error",(h)=>{if(!this.context.isCurrentHelper(o,n)||!this.context.isActive())return;if(p){this.context.failIfCurrentHelper(o,n,h);return}g(h)}),o.on("exit",(h)=>{if(!this.context.isCurrentHelper(o,n)||!this.context.isActive())return;let w=Error(this.context.describeHelperExit(h,"exited"));if(p){this.context.failIfCurrentHelper(o,n,w);return}g(w)})})}}var tr=5;function et(o,s){o.on("capability",(n)=>{if(s.isCurrentHelper()&&n===oQ)s.confirmIdleSupport(),s.options.onIdleAttestationSupport?.(!0)}),o.on("idle",()=>{if(s.isCurrentHelper()&&s.isActive()&&s.nativeIdleCapabilityConfirmed())s.options.onSourceIdle?.()}),o.on("captureMetrics",(n)=>{if(s.isCurrentHelper()&&s.isActive())s.options.onDroppedFrames?.(n.droppedFrames),s.setNativeFrameMetrics(n)}),o.on("audio",(n)=>{if(s.isCurrentHelper()&&s.isActive()&&s.options.audioEnabled)s.options.onAudioData?.(n.pcm16le)}),o.on("permission",(n)=>{if(s.isCurrentHelper())s.setRequiredPermission(n)}),o.on("permissionTarget",(n)=>{if(s.isCurrentHelper())s.setRequiredPermissionTarget(n)}),o.on("stderr",(n)=>{if(!s.isCurrentHelper())return;if(n.length>0)if(n.startsWith(S0))rr(n);else{if(s.helperStderrTail().push(n.slice(-2048)),s.helperStderrTail().length>tr)s.helperStderrTail().shift();e.warn(`[IosH264Source] screen-capture-helper stderr: ${n}`)}if(de(n))s.failForHelperLine(n)}),o.on("readiness",(n)=>{if(s.isCurrentHelper()&&s.isActive())s.setLastReadinessPhase(n.phase);e.debug(`[IosH264Source] capture readiness phase=${n.phase} atMs=${n.atMs}${n.detail?` detail=${n.detail}`:""}`)})}function rr(o){try{let s=JSON.parse(o.slice(S0.length));if(s===null||typeof s!=="object"||Array.isArray(s)){e.warn(`[IosH264Source] unrecognised frame-metrics line: ${o}`);return}let n=s;if(typeof n.droppedFrames==="number"&&n.droppedFrames>0||typeof n.frameQueueDepth==="number"&&n.frameQueueDepth!==0){e.warn(`[IosH264Source] screen-capture-helper frame metrics: ${o}`);return}e.debug(`[IosH264Source] screen-capture-helper frame metrics: ${o}`)}catch(s){e.warn(`[IosH264Source] unrecognised frame-metrics line: ${o}; ${r(s)}`)}}import{EventEmitter as ir}from"events";class me extends ir{pool;options;started=!1;attachment=null;attachmentGeneration=0;entryKey=null;constructor(o,s){super();this.pool=o;this.options=s}async start(){if(this.started)throw Error("IOSSimulatorCaptureHelperLease already started");this.started=!0;let o=this.pool.attach(this),s=++this.attachmentGeneration;this.attachment=o;try{await o}catch(n){throw this.started=!1,n}finally{if(this.attachmentGeneration===s)this.attachment=null}}async stop(){if(!this.started)return null;return this.started=!1,this.removeAllListeners(),await this.attachment?.catch(()=>{return}),await this.pool.detach(this),null}async invalidate(){if(!this.started)return;this.started=!1,this.removeAllListeners(),await this.attachment?.catch(()=>{return}),await this.pool.invalidate(this)}requestKeyFrame(){return this.started&&this.pool.requestKeyFrame(this)}get isStarted(){return this.started}forward(o,s){if(this.started)this.emit(o,s)}}var or=45000,tt=5000;class rt{idleTtlMs;timer;createHelper;entries=new Map;transitions=new Map;shutdownTransition=Promise.resolve();constructor(o={}){this.idleTtlMs=o.idleTtlMs??or,this.timer=o.timer??i,this.createHelper=o.createHelper??((s)=>new Yy(s))}acquire(o){if(o.target.kind!=="simulator")throw Error("IOSSimulatorCaptureHelperPool only accepts simulator targets.");if(!o.target.encode)throw Error("IOSSimulatorCaptureHelperPool requires H.264 encode settings.");return new me(this,o)}async shutdown(){let o=[...this.transitions.values()],s=this.shutdownTransition.then(async()=>{await Promise.all(o);let n=[...this.entries.values()];this.entries.clear();for(let a of n)this.clearEntryIdleTimer(a),await this.stopHelperWithinDeadline(a).catch((c)=>{e.debug(`[IOSSimulatorCaptureHelperPool] helper shutdown failed: ${c}`)})});this.shutdownTransition=s.catch(()=>{return}),await s}async attach(o){await this.enqueue(J(o.options.target),async()=>{if(!o.isStarted)return;let s=sr(o.options.target,o.options.binaryPath),n=await this.resolveOrCreateEntry(s,o);if(n!==null)this.finalizeAttach(n,o,s)})}async resolveOrCreateEntry(o,s){let n=this.entries.get(o);if(n?.failedStopFailed)this.clearEntryIdleTimer(n),this.entries.delete(o),n=void 0;if(n)return n;let a=J(s.options.target);for(let d of this.entries.values())if(d.key!==o&&d.leases.size===0)if(d.windowID===a)await this.stopIdleEntry(d);else this.enqueueBestEffort(d.windowID,()=>this.stopIdleEntry(d),"idle helper eviction failed");if(!s.isStarted)return null;let c={key:o,windowID:J(s.options.target),helper:this.createHelper(s.options),capabilities:new Set,leases:new Set,idleTimer:null,failed:!1,failedStopFailed:!1,failedStopError:void 0};return this.entries.set(o,c),this.wireHelper(c),c}finalizeAttach(o,s,n){this.clearEntryIdleTimer(o),o.leases.add(s),s.entryKey=n;for(let a of o.capabilities)s.forward("capability",a);if(!o.helper.isRunning)try{o.helper.start()}catch(a){throw o.leases.delete(s),s.entryKey=null,this.entries.delete(n),a}o.helper.requestKeyFrame()}requestKeyFrame(o){let s=o.entryKey;if(!s)return!1;let n=this.entries.get(s);if(!n||!n.leases.has(o))return!1;return n.helper.requestKeyFrame()}async detach(o){let s=o.entryKey;if(o.entryKey=null,!s)return;let n=this.entries.get(s);if(!n)return;if(n.leases.delete(o),n.leases.size!==0)return;this.clearEntryIdleTimer(n),n.idleTimer=this.timer.setTimeout(()=>{n.idleTimer=null,this.enqueueBestEffort(n.windowID,()=>this.stopIdleEntry(n),"idle helper stop failed")},this.idleTtlMs)}async invalidate(o){let s=o.entryKey;if(o.entryKey=null,!s)return;await this.enqueue(J(o.options.target),async()=>{let n=this.entries.get(s);if(!n||!n.leases.has(o))return;if(n.failed){await this.stopFailedEntry(n);return}await this.stopEntry(n)})}wireHelper(o){o.helper.on("idle",(s)=>this.broadcast(o,"idle",s)),o.helper.on("encodedVideo",(s)=>this.broadcast(o,"encodedVideo",s)),o.helper.on("capability",(s)=>{o.capabilities.add(s),this.broadcast(o,"capability",s)}),o.helper.on("permission",(s)=>this.broadcast(o,"permission",s)),o.helper.on("permissionTarget",(s)=>this.broadcast(o,"permissionTarget",s)),o.helper.on("captureMetrics",(s)=>this.broadcast(o,"captureMetrics",s)),o.helper.on("audio",(s)=>this.broadcast(o,"audio",s)),o.helper.on("malformed",(s)=>this.broadcast(o,"malformed",s)),o.helper.on("stderr",(s)=>{if(nr(s)&&this.entries.get(o.key)===o)o.failed=!0,this.enqueueBestEffort(o.windowID,()=>this.stopFailedEntry(o),"failed helper stop failed");this.broadcast(o,"stderr",s)}),o.helper.on("readiness",(s)=>this.broadcast(o,"readiness",s)),o.helper.on("error",(s)=>{if(this.entries.get(o.key)===o)o.failed=!0,this.enqueueBestEffort(o.windowID,()=>this.stopFailedEntry(o),"failed helper stop failed");this.broadcast(o,"error",s)}),o.helper.on("exit",(s)=>{if(this.broadcast(o,"exit",s),this.entries.get(o.key)===o)this.clearEntryIdleTimer(o),this.entries.delete(o.key)})}broadcast(o,s,n){for(let a of o.leases)a.forward(s,n)}async stopIdleEntry(o){if(o.leases.size!==0||this.entries.get(o.key)!==o)return;await this.stopEntry(o)}async stopFailedEntry(o){if(this.entries.get(o.key)!==o)return;if(o.failedStopFailed)throw o.failedStopError;this.clearEntryIdleTimer(o);try{await this.stopHelperWithinDeadline(o)}catch(s){throw o.failedStopFailed=!0,o.failedStopError=s,s}if(this.entries.get(o.key)===o)this.entries.delete(o.key)}async stopEntry(o){if(this.entries.get(o.key)!==o)return;this.clearEntryIdleTimer(o),this.entries.delete(o.key),await this.stopHelperWithinDeadline(o)}async stopHelperWithinDeadline(o){let s=o.helper.stop(),n=!1;s.catch((c)=>{if(n)e.debug(`[IOSSimulatorCaptureHelperPool] helper stop failed after timeout: ${c}`)});let a=Error("Helper stop deadline expired");try{await l(s,{timer:this.timer,timeoutMs:tt,label:"Helper stop",timeoutError:()=>a,onTimeout:()=>{n=!0,e.warn(`[IOSSimulatorCaptureHelperPool] helper stop exceeded ${tt}ms`)}})}catch(c){if(c!==a)throw c}}enqueue(o,s){let n=this.transitions.get(o)??Promise.resolve(),a=Promise.all([n,this.shutdownTransition]).then(s),c=a.catch(()=>{return});return this.transitions.set(o,c),c.then(()=>{if(this.transitions.get(o)===c)this.transitions.delete(o)}),a}enqueueBestEffort(o,s,n){this.enqueue(o,s).catch((a)=>{e.warn(`[IOSSimulatorCaptureHelperPool] ${n}: ${a}`)})}clearEntryIdleTimer(o){if(o.idleTimer)this.timer.clearTimeout(o.idleTimer),o.idleTimer=null}}function sr(o,s){if(o.kind!=="simulator")throw Error("Simulator helper pool received a non-simulator target.");return JSON.stringify({binaryPath:s,windowID:o.windowID,fps:o.fps,audio:o.audio===!0,encode:o.encode})}function J(o){if(o.kind!=="simulator")throw Error("Simulator helper pool received a non-simulator target.");return o.windowID}function nr(o){let s=o.trimStart().toLowerCase();return s.startsWith("error:")||s.includes("warn: no frames received")}var XO=new rt;class H extends t{}import{basename as mr}from"path";var ar=new re,ur=(o=ar)=>(s,n)=>fz(o.spawn(s,n,{stdio:["ignore","pipe","pipe"]}),s),it=ur();var ot=0.1,G=B4,st=15000,cr=f.IOS_SIMULATOR_WINDOW_TIMEOUT_MS.name;function lr(o=process.env){return Yn(cr,void 0,{fallback:2000,env:o})}var O=lr(),he=500,nt=500,dr=2,fe=6000,at=35000,pr=Q({initialDelayMs:500,multiplier:2,maxDelayMs:2000});function ut(o){return{maxAttempts:o.runningReconnectMaxAttempts??dr,backoff:No(o.runningReconnectBackoff??pr)}}var ct=(o,s,n)=>new Promise((a,c)=>{if(n?.aborted){c(Error(`Command aborted: ${o}`));return}let d=it(o,s),p="",m="",S=!1,g=(h)=>{if(S)return;S=!0,n?.removeEventListener("abort",y),h()},y=()=>{d.kill("SIGTERM"),g(()=>c(Error(`Command aborted: ${o}`)))};d.stdout.on("data",(h)=>{p+=Buffer.isBuffer(h)?h.toString("utf8"):String(h)}),d.stderr.on("data",(h)=>{m+=Buffer.isBuffer(h)?h.toString("utf8"):String(h)}),d.once("error",(h)=>g(()=>c(h))),d.once("exit",(h,w)=>g(()=>a({stdout:p,stderr:m,exitCode:h,signal:w}))),n?.addEventListener("abort",y,{once:!0})});class ve{dependencies;constructor(o){this.dependencies=o}async resolveCaptureTarget(o){if(z({deviceId:this.dependencies.options.device.deviceId})==="simulator")return{kind:"simulator",windowID:await this.resolveSimulatorWindowIdWithDeadline(o),fps:this.dependencies.fps,...this.dependencies.options.audioEnabled===!0?{audio:!0}:{}};return{kind:"device",deviceId:this.dependencies.options.device.deviceId}}async resolveHelperPath(){let o=this.dependencies.helperPath??gS(process.env);if(o)return yS(o,{exists:this.dependencies.helperPathExists,env:{}});let s=await this.dependencies.withPreCaptureDeadline(this.dependencies.screenCaptureHelperProvider.ensure(),at,"Resolving iOS screen-capture-helper");if(s)return s;throw new t(`iOS WebRTC streaming requires a verified screen-capture-helper from the matching GitHub Release. For local development, run swift build in ios/screen-capture and set ${dA} to the resulting absolute path.`)}async resolveSimulatorWindowIdWithDeadline(o){return new Promise((s,n)=>{let a=new AbortController,c=!1,d=(m)=>{if(c)return;c=!0,this.dependencies.timer.clearTimeout(p),m()},p=this.dependencies.timer.setTimeout(()=>{a.abort(),d(()=>n(new t(`Timed out resolving iOS Simulator window for ${this.dependencies.options.device.name} after ${O}ms. Open the Simulator window and verify Screen Recording permission.`)))},O);this.dependencies.simulatorWindowResolver(o,this.dependencies.options.device,this.dependencies.options.audioEnabled===!0,a.signal).then((m)=>d(()=>s(m)),(m)=>d(()=>n(m)))})}async reresolveSimulatorTarget(o,s){if(s.kind!=="simulator")return s;let n;try{n=await this.resolveSimulatorWindowIdWithDeadline(o)}catch(a){throw u(a,"Failed to re-resolve iOS Simulator window for capture retry")}if(n===s.windowID)return s;return e.debug(`[IosH264Source] Simulator windowID changed from ${s.windowID} to ${n} before retry; targeting the freshly-resolved window`),{...s,windowID:n}}}function Se(o){return o.kind==="simulator"?`iOS Simulator window ${o.windowID} at ${o.fps??G} fps`:`iOS device ${o.deviceId??"default"}`}async function lt(o,s,n,{audioEnabled:a,poll:c}){let{signal:d}=c,p=c.timer.now(),m=0;for(;;){m+=1;let S=await hr(o,s,n,{audioEnabled:a,signal:d});if(S.kind==="found")return S.windowID;if(d.aborted||c.timer.now()-p+he>=c.pollBudgetMs)throw new t(`No visible iOS Simulator window matched ${s.name} after ${m} lookup(s) over ${c.timer.now()-p}ms; windows seen: ${S.seen}. Open the simulator window and grant Screen Recording permission if prompted.`);await c.timer.sleep(he)}}async function hr(o,s,n,{audioEnabled:a,signal:c}){let d=await n(o,["--list-simulators"],c);if(d.exitCode!==0){if(ge(d.stderr))throw new Wp(fr(d.stderr)??ye(o));throw new t(`Unable to list iOS Simulator windows: ${d.stderr.trim()||`exited with code ${d.exitCode}`}`)}let p;try{p=JSON.parse(d.stdout).windows??[]}catch(h){throw u(h,"Unable to parse iOS Simulator window list")}if(a&&p.length>1)throw new t("iOS Simulator audio capture requires exactly one visible Simulator window because ScreenCaptureKit cannot isolate audio to a selected Simulator window. Close other Simulator windows and try again.");let m=s.name.trim().toLowerCase(),S=p.filter((h)=>typeof h.title==="string"&&h.title.trim().length>0),g=S.filter((h)=>vr(h.title,m)),y=g.length>0?g:S.filter((h)=>h.title.toLowerCase().includes(m));if(y.length===1)return{kind:"found",windowID:y[0].windowID};if(y.length===0)return{kind:"missing",seen:`[${p.map((w)=>JSON.stringify(w.title??null)).join(", ")}]`};throw new t(`Multiple iOS Simulator windows matched ${s.name}; close extras or use a more specific device.`)}function ge(o){return o.split(/\r?\n/).some((s)=>{let n=s.trim().toLowerCase();return n===`${qq} screen-recording`||n.startsWith("error: screen recording permission is required.")||n.includes("the user declined tccs for application, window, display capture")})}function fr(o){for(let s of o.split(/\r?\n/)){let n=s.trim();if(!n.startsWith(w0))continue;let a=n.slice(w0.length).trim();if(a.length>0)return a}return null}function ye(o){let s=mr(o).trim();return s==="screen-capture-helper"||s.length===0?"AutoMobile":s}function vr(o,s){let n=o.trim().toLowerCase();if(n===s)return!0;let[a]=n.split(/\s+[\u2014\u2013-]\s+/,1);return a.trim()===s}function dt(o,s){let n=o.bitrateBps&&o.bitrateBps>0?o.bitrateBps:N(o.quality),a=n!==void 0?{kind:"explicitBps",bps:n}:s==="simulator"?{kind:"bitsPerPixel",bpp:ot}:{kind:"videoToolboxDefault"},c=o.quality?j[o.quality]:void 0;return{codec:"h264",bitrate:a,...c===void 0?{}:{maxLongSide:c}}}class we{options;resolver;nativeFrameMetrics=null;captureKind=null;lastForcedKeyFrameMs=Number.NEGATIVE_INFINITY;startup;getFrameMetrics(){return{native:this.nativeFrameMetrics}}helperPath;fps;createHelper;simulatorWindowResolver;commandRunner;helperPathExists;screenCaptureHelperProvider;timer;firstFrameTimeoutMs;simulatorHelperPool;runningReconnectMaxAttempts;runningReconnectBackoff;helper=null;helperGeneration=0;cancelPreCaptureDeadline=null;encodedCapabilityConfirmed=!1;nativeIdleCapabilityConfirmed=!1;idleSupportReported=!1;teardownPromise=null;helperStderrTail=[];lastReadinessPhase=null;phase="idle";requiredPermission=null;requiredPermissionTarget=null;startupComplete=!1;hasConsumers=!0;deferredHelperFailure=null;invalidateHelperOnStop=!1;reconnectPromise=null;cancelReconnectDelay=null;constructor(o){this.options=o;this.helperPath=o.helperPath,this.fps=o.fps??G,this.createHelper=o.createHelper??((n)=>new Yy(n)),this.commandRunner=o.commandRunner??ct,this.helperPathExists=o.helperPathExists,this.screenCaptureHelperProvider=o.screenCaptureHelperProvider??qc.getInstance(),this.timer=o.timer??i,this.firstFrameTimeoutMs=o.firstFrameTimeoutMs??st,this.simulatorHelperPool=o.simulatorHelperPool??XO;let s=ut(o);this.runningReconnectMaxAttempts=s.maxAttempts,this.runningReconnectBackoff=s.backoff,this.simulatorWindowResolver=o.simulatorWindowResolver??((n,a,c,d)=>lt(n,a,this.commandRunner,{audioEnabled:c,poll:{signal:d,timer:this.timer,pollBudgetMs:Math.max(0,O)}})),this.startup=new pe(this.timer,this.firstFrameTimeoutMs,{generation:()=>this.helperGeneration,lastReadinessPhase:()=>this.lastReadinessPhase,markRunning:()=>{this.phase="running"},isCurrentHelper:(n,a)=>this.isCurrentHelper(n,a),isActive:()=>this.isActive(),helperFailureFor:(n)=>this.helperFailureFor(n),failIfCurrentHelper:(n,a,c)=>this.failIfCurrentHelper(n,a,c),describeHelperExit:(n,a)=>this.describeHelperExit(n,a),encodedStartupError:(n)=>this.encodedStartupError(n)}),this.resolver=new ve({options:this.options,helperPath:this.helperPath,helperPathExists:this.helperPathExists,screenCaptureHelperProvider:this.screenCaptureHelperProvider,fps:this.fps,timer:this.timer,simulatorWindowResolver:this.simulatorWindowResolver,withPreCaptureDeadline:(n,a,c)=>this.withPreCaptureDeadline(n,a,c)})}async start(){if(this.isActive())throw new t("iOS H.264 source already started.");await this.teardownPromise;let o=++this.helperGeneration;if(this.phase="starting",this.startupComplete=!1,this.deferredHelperFailure=null,this.helperStderrTail=[],this.lastReadinessPhase=null,this.nativeFrameMetrics=null,this.lastForcedKeyFrameMs=Number.NEGATIVE_INFINITY,this.encodedCapabilityConfirmed=!1,this.options.size!==void 0)e.warn("[IosH264Source] iOS ignores explicit encoder size; capture encodes at native size capped by the quality preset.");try{if(await this.establishCapture(o),o===this.helperGeneration)this.startupComplete=this.phaseNow()==="running"}catch(s){if(o!==this.helperGeneration)return;throw this.phase="stopping",await this.beginTeardown(),s}}async establishCapture(o){let s=await this.resolver.resolveHelperPath();if(o!==this.helperGeneration)return;let n=await this.resolver.resolveCaptureTarget(s);if(this.captureKind=n.kind,!this.isActive()||o!==this.helperGeneration)return;await this.startCaptureWithSimulatorRetry(s,{...n,encode:dt(this.options,n.kind)})}async stop(){if(this.deferredHelperFailure=null,!this.isActive()){await this.teardownPromise;return}this.phase="stopping",this.helperGeneration++,this.startupComplete=!1,this.cancelPreCaptureDeadline?.(),this.cancelReconnectDelay?.(),this.startup.cancelFirstFrameWait?.(),this.startup.cancelFirstAudioWait?.(),await this.beginTeardown()}async stopStale(o=!0){this.invalidateHelperOnStop=o,await this.stop()}setHasConsumers(o){if(this.hasConsumers=o,o){if(this.deferredHelperFailure)this.queueReconnect(this.deferredHelperFailure)}else this.cancelReconnectDelay?.()}requestKeyFrame(){if(this.phase!=="running"||!this.helper)return!1;let o=this.timer.now();if(o-this.lastForcedKeyFrameMs<nt)return!1;let s=this.helper.requestKeyFrame?.()??!1;if(s)this.lastForcedKeyFrameMs=o,e.debug("[IosH264Source] forced keyframe requested on helper control channel");return s}async withPreCaptureDeadline(o,s,n){let a=new AbortController,c=()=>a.abort(new t(`${n} cancelled by stop.`));this.cancelPreCaptureDeadline=c;let d=this.timer.setTimeout(()=>a.abort(new t(`${n} timed out after ${s}ms.`)),s);try{return await l(o,{timer:this.timer,signal:a.signal,label:n})}finally{if(this.cancelPreCaptureDeadline===c)this.cancelPreCaptureDeadline=null;if(d)this.timer.clearTimeout(d)}}createCaptureHelper(o){if(o.target.kind==="simulator"&&!this.options.createHelper)return this.simulatorHelperPool.acquire(o);return this.createHelper(o)}async startCaptureWithSimulatorRetry(o,s){let n=s.kind==="simulator"&&!this.options.createHelper,a=s;for(let c=0;;c++)try{await this.startCaptureAttempt(o,a);return}catch(d){if(d instanceof H)throw d;if(!n||!(d instanceof A))throw u(d,"Failed to start iOS screen capture");try{await this.helper?.invalidate?.()}catch(p){throw u(p,"Failed to invalidate silent iOS Simulator capture")}if(await this.stopCurrentHelper(),!this.isActive())return;if(c!==0)throw d.finalStartupError(this.requiredPermissionTarget);e.debug(`[IosH264Source] no first frame from pooled Simulator helper for ${Se(a)}; retrying once`),a=await this.reresolveSimulatorTarget(o,a)}}reresolveSimulatorTarget(o,s){return this.resolver.reresolveSimulatorTarget(o,s)}async startCaptureAttempt(o,s){e.info(`[IosH264Source] starting screen-capture-helper for ${Se(s)}`),this.lastReadinessPhase=null,this.requiredPermission=null,this.requiredPermissionTarget=ye(o),this.nativeIdleCapabilityConfirmed=!1,this.idleSupportReported=!1;let n=this.createCaptureHelper({binaryPath:o,target:s});this.helper=n,this.encodedCapabilityConfirmed=!1,this.wireEncodedHelper(n);let a=this.options.audioEnabled?this.startup.waitForFirstAudio(n):null,c=this.startup.waitForFirstEncodedRecord(n,s);try{await Promise.all([c,a,Promise.resolve().then(()=>n.start())])}catch(d){throw this.encodedStartupError(d instanceof Error?d:Error(r(d)))}}encodedStartupError(o){if(this.encodedCapabilityConfirmed||o instanceof Wp||o instanceof H)return o;return new H(`The screen-capture-helper did not advertise the '${s_}' capability before failing to start encoded capture (${o.message}). Update to the screen-capture-helper from the matching AutoMobile release, or rebuild the local helper and update AUTOMOBILE_IOS_SCREEN_CAPTURE_HELPER.`)}wireEncodedHelper(o){let s=this.helperGeneration;o.on("encodedVideo",(n)=>{if(this.isCurrentHelper(o,s))this.handleEncodedVideo(n)}),o.on("capability",(n)=>{if(this.isCurrentHelper(o,s)&&n===s_)this.encodedCapabilityConfirmed=!0}),o.on("malformed",(n)=>{if(!this.isCurrentHelper(o,s))return;e.warn(`[IosH264Source] malformed encoded record from helper: ${n.reason}; requesting keyframe to recover`),this.requestKeyFrame()}),this.wireHelperDiagnostics(o)}wireHelperDiagnostics(o){let s=this.helperGeneration;et(o,{isCurrentHelper:()=>this.isCurrentHelper(o,s),isActive:()=>this.isActive(),confirmIdleSupport:()=>{this.nativeIdleCapabilityConfirmed=!0,this.idleSupportReported=!0},nativeIdleCapabilityConfirmed:()=>this.nativeIdleCapabilityConfirmed,setRequiredPermission:(n)=>{this.requiredPermission=n},setRequiredPermissionTarget:(n)=>{this.requiredPermissionTarget=n},setLastReadinessPhase:(n)=>{this.lastReadinessPhase=n},helperStderrTail:()=>this.helperStderrTail,failForHelperLine:(n)=>this.failIfCurrentHelper(o,s,this.helperFailureFor(n)),setNativeFrameMetrics:(n)=>{this.nativeFrameMetrics=n,this.options.onFrameMetrics?.(this.getFrameMetrics())},options:this.options})}helperFailureFor(o){return this.requiredPermission==="screen-recording"||ge(o)?new Wp(this.requiredPermissionTarget??"AutoMobile"):Error(`screen-capture-helper reported an error: ${o}`)}handleEncodedVideo(o){if(!this.isActive())return;this.encodedCapabilityConfirmed=!0,this.reportLegacyIdleSupport(),this.options.onSourceFrame?.(),this.options.onData(o.payload),this.options.onEncodedAccessUnit?.()}reportLegacyIdleSupport(){if(this.captureKind==="simulator"&&!this.idleSupportReported)this.idleSupportReported=!0,this.options.onIdleAttestationSupport?.(!1)}failIfRunning(o){if(this.phase!=="running")return;if(!this.startupComplete||this.runningReconnectMaxAttempts<=0){this.failNow(o);return}if(!this.hasConsumers){this.phase="reconnecting",this.startupComplete=!1,this.beginTeardown(),this.queueReconnect(o);return}this.phase="reconnecting",this.startupComplete=!1,this.queueReconnect(o)}failNow(o){this.startup.rejectFirstAudioWait?.(o),this.phase="stopping",this.startupComplete=!1,this.beginTeardown(),this.options.onError?.(o)}async runReconnect(o){if(e.warn(`[IosH264Source] running-phase capture failure; attempting bounded reconnect (up to ${this.runningReconnectMaxAttempts}): ${o.message}`),await this.beginTeardown(),!this.hasConsumers){this.deferredHelperFailure=o;return}for(let s=1;this.phase==="reconnecting"&&s<=this.runningReconnectMaxAttempts;s++){let n=this.runningReconnectBackoff.delayForAttempt(s);if(!await this.waitForActiveReconnect(n,o))return;this.phase="starting";let a=++this.helperGeneration;try{if(await this.establishCapture(a),a!==this.helperGeneration)return;if(this.phaseNow()==="running"){this.startupComplete=!0,e.info(`[IosH264Source] running-phase reconnect succeeded on attempt ${s}`);return}return}catch(c){if(a!==this.helperGeneration)return;e.warn(`[IosH264Source] reconnect attempt ${s}/${this.runningReconnectMaxAttempts} failed: ${r(c)}`),await this.beginTeardown();let d=this.phaseNow();if(d==="stopping"||d==="idle")return;if(c instanceof H){this.failNow(c);return}this.phase="reconnecting"}}this.finishReconnectExhausted(o)}finishReconnectExhausted(o){if(this.phase!=="reconnecting")return;if(!this.hasConsumers){this.deferredHelperFailure=o;return}this.failNow(o)}async waitForActiveReconnect(o,s){let n=await this.waitReconnectBackoff(o);if(this.phase==="reconnecting"&&(!this.hasConsumers||n))this.deferredHelperFailure=s;return!n&&this.hasConsumers&&this.phase==="reconnecting"}queueReconnect(o){if(this.deferredHelperFailure=o,!this.hasConsumers||this.phase!=="reconnecting"||this.reconnectPromise)return;this.deferredHelperFailure=null,this.reconnectPromise=this.runReconnect(o).finally(()=>{if(this.reconnectPromise=null,this.deferredHelperFailure&&this.hasConsumers)this.queueReconnect(this.deferredHelperFailure)})}waitReconnectBackoff(o){return new Promise((s)=>{let n=this.timer.setTimeout(()=>{this.cancelReconnectDelay=null,s(!1)},o);this.cancelReconnectDelay=()=>{this.timer.clearTimeout(n),this.cancelReconnectDelay=null,s(!0)}})}describeHelperExit(o,s){let n=this.helperStderrTail.length===0?"":`; last stderr: ${this.helperStderrTail.join(" | ")}`;return`screen-capture-helper ${s} (code=${o.code}, signal=${o.signal})${n}`}failIfCurrentHelper(o,s,n){if(!this.isCurrentHelper(o,s))return;this.failIfRunning(n)}isCurrentHelper(o,s){return this.helper===o&&this.helperGeneration===s}isActive(){return this.phase==="starting"||this.phase==="running"||this.phase==="reconnecting"}phaseNow(){return this.phase}beginTeardown(){return this.teardownPromise??=this.teardown().finally(()=>{if(this.teardownPromise=null,this.phase==="stopping")this.phase="idle"}),this.teardownPromise}async teardown(){await this.stopCurrentHelper()}async stopCurrentHelper(){this.startup.cancelFirstFrameWait?.(),this.startup.cancelFirstFrameWait=null,this.startup.cancelFirstAudioWait?.(),this.startup.cancelFirstAudioWait=null,this.startup.rejectFirstAudioWait=null;let o=this.helper;if(this.helper=null,!o)return;let s=(this.invalidateHelperOnStop&&o.invalidate?o.invalidate():o.stop()).then(()=>!0,(a)=>(e.debug(`[IosH264Source] helper stop failed: ${a}`),!0)),n=Symbol("helper stop timeout");try{await l(s,{timer:this.timer,timeoutMs:fe,label:"iOS capture helper stop",timeoutError:()=>n})}catch(a){if(a!==n)throw a;e.warn(`[IosH264Source] helper stop exceeded ${fe}ms; continuing teardown`)}}}var ZO=(o,s)=>{if(o.device.platform==="android")return Qe(o,s);if(o.device.platform==="ios"){if(o.audioEnabled&&z({deviceId:o.device.deviceId})!=="simulator")throw new t("WebRTC playback audio capture is available only for iOS Simulator targets; public iOS APIs cannot capture playback audio from a physical device.");return new we(o)}throw new t(`WebRTC streaming does not support ${o.device.platform} devices.`)};
3
- export{Md,Nd,Is,_f,iy,bE,JO,VK,tc,AJ,Wp,XO,ZO,TJ,kJ,WK,QO};
2
+ import{i,Q,Lo,l,e}from"./chunk-r0vp315h.js";import{h}from"./chunk-5wk1t46v.js";import{ee,Fz,v,z,b}from"./chunk-sfqe223f.js";import{B,ei}from"./chunk-r6pvhj33.js";import{O0,M0,g_,SQ,Qq,N0,uv,Gc,q0,nV,Q4,il}from"./chunk-8nj68t1v.js";import{EA,DS,OS}from"./chunk-g3af2qbk.js";import{Oi}from"./chunk-sqdrxjw5.js";import{r,t,u}from"./chunk-2b39zyxc.js";var qd=7,Vd=8;function _s(o){return o.length>0?o[0]&31:0}function F(o){let s=_s(o);return s>=1&&s<=5}function mt(o){return o.length>1&&(o[1]&128)!==0}class Wf{current=[];hasVcl=!1;push(o){let s=[],n=_s(o),a=n===7||n===8||n===6;if(F(o)&&this.hasVcl&&mt(o)||n===9&&this.current.length>0||a&&this.hasVcl)s.push(this.current),this.current=[],this.hasVcl=!1;if(this.current.push(o),F(o))this.hasVcl=!0;return s}flushPicture(){return this.hasVcl?this.flush():[]}flush(){if(this.current.length===0)return[];let o=this.current;return this.current=[],this.hasVcl=!1,[o]}}var Ry=5,ht=28,Ee=2,NE=1200,gM=4194304;function lG(o){return _s(o)===Ry}class ic{maxBufferedBytes;buffered=Buffer.alloc(0);constructor(o=gM){this.maxBufferedBytes=o;if(!Number.isSafeInteger(o)||o<1)throw Error("H.264 Annex-B parser buffer limit must be a positive integer.")}push(o){if(this.buffered=this.buffered.length===0?o:Buffer.concat([this.buffered,o]),this.buffered.length>this.maxBufferedBytes)throw this.buffered=Buffer.alloc(0),Error(`H.264 Annex-B parser buffer exceeded ${this.maxBufferedBytes} bytes without a complete NAL.`);return this.drain(!1)}flush(){return this.drain(!0)}hasBufferedNalType(o){let n=this.findStartCodes(this.buffered).at(-1);if(!n)return!1;let a=n.offset+n.length;return a<this.buffered.length&&_s(this.buffered.subarray(a))===o}drain(o){let s=[],n=this.findStartCodes(this.buffered);if(n.length===0){if(o)this.buffered=Buffer.alloc(0);return s}for(let c=0;c<n.length-1;c++){let d=n[c].offset+n[c].length,p=n[c+1].offset,m=this.buffered.subarray(d,p);if(m.length>0)s.push(Buffer.from(m))}let a=n[n.length-1];if(o){let c=this.buffered.subarray(a.offset+a.length);if(c.length>0)s.push(Buffer.from(c));this.buffered=Buffer.alloc(0)}else this.buffered=Buffer.from(this.buffered.subarray(a.offset));return s}findStartCodes(o){let s=[];for(let n=0;n+2<o.length;n++)if(o[n]===0&&o[n+1]===0&&o[n+2]===1){let a=n>0&&o[n-1]===0;s.push({offset:a?n-1:n,length:a?4:3})}return s}}function ft(o,s=NE){if(o.length===0)return[];if(o.length<=s)return[Buffer.from(o)];if(!Number.isFinite(s)||s<=Ee)throw new t(`RTP MTU ${s} is too small to fragment a ${o.length}-byte NAL unit; it must exceed the ${Ee}-byte FU-A header.`);let n=o[0],a=n&224,c=n&31,d=a|ht,p=o.subarray(1),m=s-2,S=[],g=0;while(g<p.length){let y=Math.min(m,p.length-g),f=g===0,w=g+y>=p.length,E=c;if(f)E|=128;if(w)E|=64;S.push(Buffer.concat([Buffer.from([d,E]),p.subarray(g,g+y)])),g+=y}return S}function j8(o,s=NE){let n=[];for(let a of o)for(let c of ft(a,s))n.push(c);return n.map((a,c)=>({payload:a,marker:c===n.length-1}))}class I{entry;options;active=!0;hasConsumers;joined=!1;pendingAlignment=!1;released=!1;startWaiter=null;constructor(o,s,n){this.entry=o;this.options=s;this.hasConsumers=n}start(){if(!this.active||this.entry.retired)return this.released?Promise.resolve():this.entry.startPromise??Promise.resolve();if(!this.joined){if(this.joined=!0,this.pendingAlignment=this.entry.emittedData,this.entry.replayMetrics(this),this.pendingAlignment)this.entry.source?.requestKeyFrame?.("viewer")}if(this.entry.running)return this.entry.start();if(!this.startWaiter)this.startWaiter=Promise.withResolvers(),this.entry.start().then(this.startWaiter.resolve,this.startWaiter.reject);return this.startWaiter.promise}releaseStart(){this.released=!0,this.startWaiter?.resolve()}stop(){return this.entry.release(this)}get stopStale(){return this.entry.source?.stopStale?(o)=>this.entry.release(this,{producerStale:o}):void 0}get setHasConsumers(){let o=this.entry.source??this.entry.lastSource;if(!o||!("setHasConsumers"in o)||typeof o.setHasConsumers!=="function")return;return(s)=>{if(!this.active)return;this.hasConsumers=s,this.entry.updateConsumers()}}get requestKeyFrame(){let o=this.entry.source;return o?.requestKeyFrame?o.requestKeyFrame.bind(o):void 0}get getTelemetry(){let o=this.entry.source;return o?.getTelemetry?o.getTelemetry.bind(o):void 0}}import{isDeepStrictEqual as vt}from"util";var St=["bitrateBps","size","quality","fps"];function be(o,s,n=[]){let a=St.filter((c)=>!n.includes(c)&&s[c]!==void 0&&!vt(o[c],s[c]));if(s.audioEnabled&&!o.audioEnabled)a.push("audioEnabled");return a}class Y{request;onRetire;handles=new Set;parser=new ic;source=null;creation=null;startPromise=null;startCompletion=null;retryOwner=null;retryAvailable=!1;finalStop=!1;stopSettled=!1;stopPromise=null;retired=!1;running=!1;sps=null;pps=null;emittedData=!1;frameMetrics=null;loggedConflicts=new Set;lastSource=null;fps;sourceFps;generation=0;restarting=null;constructor(o,s){this.request=o;this.onRetire=s;this.fps=o.options.fps}sharesFrameRate(){return this.request.device.platform==="ios"&&this.fps!==void 0}conflicts(o,s=[],n){let a=be({...this.request.options,fps:this.fps},o,s),c=o.fps;return this.sharesFrameRate()&&c!==void 0&&(n||c<=this.fps)?a.filter((p)=>p!=="fps"):a}canShare(o){let s=this.conflicts(o.options,o.flexibleHints,!0);if(o.options.audioEnabled&&o.options.onAudioData&&!this.request.options.onAudioData)s.push("onAudioData");if(s.length===0)return!0;let n=s.join(", ");if(!this.loggedConflicts.has(n))this.loggedConflicts.add(n),e.info(`[DeviceCapture] ${o.device.deviceId}: private capture required by incompatible settings (${n})`);return!1}add(o){let s=new I(this,o.options,o.hasConsumers??!0);this.handles.add(s),s.pendingAlignment=this.emittedData,this.updateConsumers();let n=o.options.fps;if(!o.flexibleHints?.includes("fps")&&n!==void 0&&this.sharesFrameRate()&&n>this.fps)this.raiseFrameRate(n);return s}initialize(){let o=this.request.create(this.sourceOptions());if(!(o instanceof Promise))this.adopt(o),this.creation=Promise.resolve(o);else this.creation=o.then((s)=>(this.adopt(s),s)),this.creation.then(void 0,()=>this.creationFailed())}adopt(o){this.source=o,this.lastSource=o,this.updateConsumers()}raiseFrameRate(o){e.info(`[DeviceCapture] ${this.request.device.deviceId}: restarting shared capture at ${o} fps (was ${this.fps})`),this.fps=o,this.restarting??=this.restartAtTargetRate()}async restartAtTargetRate(){if(this.running=!1,this.startPromise)this.startCompletion=Promise.withResolvers(),this.startPromise=this.startCompletion.promise,this.startPromise.catch((o)=>{e.debug(`[DeviceCapture] restart start rejected: ${r(o)}`)});try{while(!this.retired&&this.sourceFps!==this.fps)if(await this.replaceSource(),!this.retired&&this.startPromise&&this.source&&this.sourceFps===this.fps)await this.source.start();this.running=!this.retired&&this.startPromise!==null,this.startCompletion?.resolve()}catch(o){this.restartFailed(o)}finally{this.restarting=null}}restartFailed(o){let s=this.startCompletion;if(this.retired){s?.resolve();return}e.warn(`[DeviceCapture] ${this.request.device.deviceId}: frame-rate restart failed`,o),this.fail(o instanceof Error?o:Error(String(o)));let n=()=>s?.reject(o);this.stopPromise?.then(n,n)}async replaceSource(){if(!this.source)await this.creation?.catch(()=>null);let o=this.source;if(this.retired||!o||this.sourceFps===this.fps)return;this.generation++,this.source=null,this.parser=new ic,this.sps=null,this.pps=null;let s=Promise.withResolvers();this.creation=s.promise,s.promise.catch((a)=>{e.debug(`[DeviceCapture] restart replacement not adopted: ${r(a)}`)});try{await o.stop()}catch(a){e.warn(`[DeviceCapture] ${this.request.device.deviceId}: stopping capture for restart failed`,a)}if(this.retired){s.reject(Error("Capture released during frame-rate restart"));return}let n;try{n=await this.request.create(this.sourceOptions())}catch(a){throw s.reject(a),a}this.adopt(n),s.resolve(n)}start(){if(this.startPromise)return this.startPromise;if(this.startCompletion=Promise.withResolvers(),this.startPromise=this.startCompletion.promise,this.restarting)return this.startPromise;if(this.source)this.startSource(this.source);else{if(!this.creation)this.initialize();this.creation.then((o)=>this.startSource(o),(o)=>this.startFailed(o))}return this.startPromise}startSource(o){if(this.retired){this.startCompletion?.resolve();return}if(this.restarting)return;let s=this.generation,n=()=>s!==this.generation||this.restarting!==null;try{o.start().then(()=>{if(n())return;this.running=!this.retired,this.startCompletion?.resolve()},(a)=>{if(n()){e.debug(`[DeviceCapture] ${this.request.device.deviceId}: replaced source start aborted: ${r(a)}`);return}this.startFailed(a)})}catch(a){e.warn(`[DeviceCapture] ${this.request.device.deviceId}: source start failed`,a),this.startFailed(a)}}creationFailed(){if(!this.startPromise)this.retire(void 0,!0)}startFailed(o){if(this.retired){this.startCompletion?.resolve();return}this.retire(void 0,!0).then(()=>this.startCompletion?.reject(o))}release(o,s){if(!o.active){if(o.releaseStart(),o===this.retryOwner&&this.retryAvailable)return this.retryAvailable=!1,this.retryOwner=null,this.finalStop=!0,this.beginStop(s,!1);return this.finalStop&&this.stopSettled?Promise.resolve():this.stopPromise??Promise.resolve()}if(o.active=!1,o.releaseStart(),this.handles.delete(o),s&&this.handles.size>0){this.retryOwner=o;let n=[...this.handles],a=Error(`Shared capture for ${this.request.device.deviceId} retired: producer declared stale`);return this.retire(s,!1,()=>{for(let c of n)this.deliver(c,(d)=>d.onError?.(a))})}if(this.handles.size===0)return this.retryOwner=o,this.retire(s);return this.updateConsumers(),Promise.resolve()}retire(o,s=!1,n){if(this.stopPromise)return this.stopPromise;this.retired=!0,this.running=!1;for(let a of this.handles)if(a.active=!1,o)a.releaseStart();return this.handles.clear(),this.beginStop(o,s,n)}beginStop(o,s,n){let a=Promise.withResolvers();this.stopSettled=!1,this.stopPromise=a.promise,this.onRetire(this),n?.();let c=()=>this.stopSource(o,s,a);if(!this.source&&this.creation)this.creation.then(c,c);else c();return a.promise}stopSource(o,s,n){try{((o&&this.source?.stopStale?this.source.stopStale(o.producerStale):this.source?.stop())??Promise.resolve()).then(()=>{this.stopSettled=!0,n.resolve()},(c)=>this.stopFailed(c,s,n))}catch(a){this.stopFailed(a,s,n)}}stopFailed(o,s,n){if(this.stopSettled=!0,e.warn(`[DeviceCapture] ${this.request.device.deviceId}: source stop failed`,o),s)n.resolve();else this.retryAvailable=this.retryOwner!==null,n.reject(o)}updateConsumers(){let o=this.source;if(o&&"setHasConsumers"in o&&typeof o.setHasConsumers==="function")o.setHasConsumers([...this.handles].some((s)=>s.hasConsumers))}deliver(o,s){try{s(o.options)}catch(n){e.warn(`[DeviceCapture] ${this.request.device.deviceId}: capture sink failed`,n)}}fanout(o){for(let s of[...this.handles])if(s.active)this.deliver(s,o)}cache(o){for(let s of o){let n=_s(s);if(n===qd)this.sps=Buffer.concat([Buffer.from([0,0,0,1]),s]);else if(n===Vd)this.pps=Buffer.concat([Buffer.from([0,0,0,1]),s])}}replay(o){for(let s of[this.sps,this.pps])if(s&&o.active)this.deliver(o,(n)=>(n.onReplayData??n.onData)(s))}replayMetrics(o){if(this.frameMetrics)this.deliver(o,(s)=>s.onFrameMetrics?.(this.frameMetrics))}deliverData(o){for(let s of[...this.handles]){if(!s.active)continue;let n=o;if(s.pendingAlignment){let a=o.indexOf(Buffer.from([0,0,1]));if(a<0)continue;let c=a>0&&o[a-1]===0?a-1:a;s.pendingAlignment=!1,this.replay(s),n=o.subarray(c)}if(s.active)this.deliver(s,(a)=>a.onData(n))}}fail(o){if(this.retired)return;let s=[...this.handles];this.retire(void 0,!0,()=>{for(let n of s)this.deliver(n,(a)=>a.onError?.(o))})}sourceOptions(){let o=this.generation;this.sourceFps=this.fps;let s=()=>this.retired||o!==this.generation,n=(a)=>(...c)=>{if(!s())a(...c)};return{...this.request.options,fps:this.fps,onData:(a)=>{if(s())return;try{this.cache(this.parser.push(a))}catch(c){e.warn(`[DeviceCapture] ${this.request.device.deviceId}: invalid H.264 stream`,c)}this.emittedData||=a.length>0,this.deliverData(a)},onEncodedAccessUnit:n(()=>{this.cache(this.parser.flush());for(let a of[...this.handles])if(a.active&&!a.pendingAlignment)this.deliver(a,(c)=>c.onEncodedAccessUnit?.())}),onSourceFrame:n(()=>this.fanout((a)=>a.onSourceFrame?.())),onSourceIdle:n(()=>this.fanout((a)=>a.onSourceIdle?.())),onIdleAttestationSupport:n((a)=>this.fanout((c)=>c.onIdleAttestationSupport?.(a))),onRotation:n((a)=>this.fanout((c)=>c.onRotation?.(a))),onDroppedFrames:n((a)=>this.fanout((c)=>c.onDroppedFrames?.(a))),onFrameMetrics:n((a)=>{this.frameMetrics=a,this.fanout((c)=>c.onFrameMetrics?.(a))}),onAudioData:this.request.options.onAudioData?n((a)=>this.fanout((c)=>c.onAudioData?.(a))):void 0,onError:n((a)=>this.fail(a))}}}function K8(o,s,n=[]){return o instanceof I&&o.entry.handles.size>1&&o.entry.conflicts(s,n,!1).length===0}function G8(o,s){return o instanceof I?o.entry.release(o,{producerStale:s}):o.stopStale?o.stopStale(s):o.stop()}class Ce{entries=new Map;stopping=new Map;acquire(o){let s=o.device.deviceId,n=this.entries.get(s),a=n?!n.canShare(o):!1;if(n&&!a)return n.add(o);let c=new Y(o,(m)=>{if(this.entries.get(s)===m)this.entries.delete(s);let S=m.stopPromise,g=this.stopping.get(s)??new Set;g.add(m),this.stopping.set(s,g);let y=()=>{if(m.stopPromise===S){if(g.delete(m),g.size===0)this.stopping.delete(s)}};S.then(y,y)}),d=c.add(o);if(!a)this.entries.set(s,c);let p=this.stopping.get(s);if(p?.size){let m=()=>{if(c.retired)throw Error("Capture released before creation");return c.initialize(),c.creation},S=(g)=>{let y=g.stopPromise,f=()=>g.stopPromise!==y?S(g):void 0;return y.then(f,f)};c.creation=Promise.all([...p].map(S)).then(m),c.creation.then(void 0,()=>c.creationFailed())}else try{c.initialize()}catch(m){if(e.warn(`[DeviceCapture] ${s}: source creation failed`,m),this.entries.get(s)===c)this.entries.delete(s);throw c.retired=!0,d.active=!1,c.handles.clear(),m}return d}}function uG(){return new Ce}var gt;function bM(){return gt??=uG()}var yt={low:2000000,medium:4000000,high:8000000},j={low:540,medium:720,high:1080};function D(o,s){if(!s)return o;if(!Number.isFinite(o.width)||!Number.isFinite(o.height)||o.width<=0||o.height<=0)return o;let n=j[s],a=Math.max(o.width,o.height);if(a<=n)return{width:Math.max(2,o.width&-2),height:Math.max(2,o.height&-2)};let c=n/a;if(o.height>=o.width)return{width:Math.max(2,Math.trunc(o.width*c)&-2),height:n};return{width:n,height:Math.max(2,Math.trunc(o.height*c)&-2)}}function N(o){return o?yt[o]:void 0}var wt=(Oi-5)*1000,Et={width:1280,height:720},bt=3000,Ct=2000,Pt=30000,Tt=2,Rt=Q({initialDelayMs:500,multiplier:2,maxDelayMs:2000});class X{options;adbFactory;timer;segmentTimeLimitSeconds;segmentRotateMs;healthySegmentAgeMs;runningRetryMaxAttempts;runningRetryBackoff;current=null;segmentStartedAtMs=null;rotateHandle=null;cancelRetryDelay=null;running=!1;runId=0;runningRetryAttempts=0;segmentCount=0;resolvedSize=null;lastForcedKeyFrameMs=Number.NEGATIVE_INFINITY;constructor(o){this.options=o,this.adbFactory=o.adbFactory??v,this.timer=o.timer??i,this.segmentTimeLimitSeconds=Math.min(o.segmentTimeLimitSeconds??Oi,Oi),this.segmentRotateMs=o.segmentRotateMs??wt,this.healthySegmentAgeMs=Math.min(Pt,this.segmentRotateMs/2),this.runningRetryMaxAttempts=o.runningRetryMaxAttempts??Tt,this.runningRetryBackoff=Lo(o.runningRetryBackoff??Rt)}get segmentsStarted(){return this.segmentCount}get isRunning(){return this.running}async start(){if(this.running)throw new t("Android H.264 source already started.");this.runId++,this.runningRetryAttempts=0,this.running=!0,await this.startSegment()}async stop(){if(this.runId++,this.cancelRetryDelay?.(),!this.running)return;this.running=!1,this.clearRotateTimer(),this.segmentStartedAtMs=null;let o=this.current;if(this.current=null,!o)return;o.kill("SIGINT")}requestKeyFrame(){let o=this.current;if(!this.running||!o)return!1;let s=this.timer.now();if(s-this.lastForcedKeyFrameMs<bt)return!1;return this.lastForcedKeyFrameMs=s,e.info(`[AndroidH264Source] keyframe requested; rotating segment ${this.segmentCount} to emit a fresh IDR`),o.kill("SIGINT"),!0}async startSegment(){let o=this.runId,s=this.adbFactory.create(this.options.device);if(!this.running||o!==this.runId)return;let n=await this.captureSize(s);if(!this.running||o!==this.runId)return;let a=["exec-out","screenrecord","--output-format=h264","--time-limit",String(this.segmentTimeLimitSeconds)],c=this.options.bitrateBps&&this.options.bitrateBps>0?this.options.bitrateBps:N(this.options.quality);if(c!==void 0)a.push("--bit-rate",String(Math.round(c)));a.push("--size",`${n.width}x${n.height}`),a.push("-"),e.info(`[AndroidH264Source] starting screenrecord segment ${this.segmentCount+1}: ${a.join(" ")}`);let d=await s.spawn(a);if(!this.running||o!==this.runId){d.kill("SIGINT");return}this.current=d,this.segmentStartedAtMs=this.timer.now(),this.segmentCount++;let p=new ic;d.stdout.on("data",(m)=>{if(this.current===d){if(m.length>0){let S=!1;try{S=p.push(m).some(F)}catch(g){this.options.onError?.(g instanceof Error?g:Error(String(g)));return}if(S)this.options.onSourceFrame?.()}this.options.onData(m)}}),d.stderr.on("data",(m)=>{let S=m.toString().trim();if(S)e.debug(`[AndroidH264Source] screenrecord stderr: ${S}`)}),d.once("error",(m)=>{if(e.warn(`[AndroidH264Source] screenrecord process error: ${m.message}`),this.current===d&&this.running)this.resetRetryAfterHealthySegment(),this.current=null,this.segmentStartedAtMs=null,this.clearRotateTimer(),d.kill("SIGINT"),this.retryAfterFailure(m)}),d.once("exit",(m,S)=>{this.handleSegmentExit(d,m,S)}),this.armRotateTimer()}handleSegmentExit(o,s,n){if(this.current!==o)return;let a=this.segmentStartedAtMs,c=a===null?0:this.timer.now()-a;if(this.resetRetryAfterHealthySegment(),this.current=null,this.segmentStartedAtMs=null,this.clearRotateTimer(),!this.running)return;if(!(n==="SIGINT"||s===0&&c>=Ct)){e.warn(`[AndroidH264Source] screenrecord failed (code=${s}, signal=${n}, age=${c}ms); retrying`),this.retryAfterFailure(Error(`screenrecord exited with code ${s}`));return}e.info(`[AndroidH264Source] segment ${this.segmentCount} ended (code=${s}, signal=${n}); rotating`);let p=this.runId;this.startSegment().catch((m)=>{e.warn(`[AndroidH264Source] failed to start next segment: ${m}`),this.retryAfterFailure(m instanceof Error?m:Error(String(m)),p)})}resetRetryAfterHealthySegment(){if(this.segmentStartedAtMs!==null&&this.timer.now()-this.segmentStartedAtMs>=this.healthySegmentAgeMs)this.runningRetryAttempts=0}async retryAfterFailure(o,s=this.runId){if(!this.running||s!==this.runId)return;if(this.runningRetryAttempts>=this.runningRetryMaxAttempts){this.running=!1,this.clearRotateTimer(),this.options.onError?.(o);return}let n=++this.runningRetryAttempts;if(await this.waitRetryBackoff(this.runningRetryBackoff.delayForAttempt(n))||!this.running||s!==this.runId)return;this.startSegment().catch((c)=>{e.warn(`[AndroidH264Source] failed to start retry segment: ${c}`),this.retryAfterFailure(c instanceof Error?c:Error(String(c)),s)})}waitRetryBackoff(o){return new Promise((s)=>{let n=this.timer.setTimeout(()=>{this.cancelRetryDelay=null,s(!1)},o);this.cancelRetryDelay=()=>{this.timer.clearTimeout(n),this.cancelRetryDelay=null,s(!0)}})}armRotateTimer(){this.clearRotateTimer(),this.rotateHandle=this.timer.setTimeout(()=>{let o=this.current;if(!this.running||!o)return;e.info(`[AndroidH264Source] rotating segment ${this.segmentCount} before time limit`),o.kill("SIGINT")},this.segmentRotateMs)}clearRotateTimer(){if(this.rotateHandle)this.timer.clearTimeout(this.rotateHandle),this.rotateHandle=null}async captureSize(o){if(this.options.size)return this.options.size;if(this.resolvedSize)return this.resolvedSize;try{let{stdout:s}=await o.executeCommand("shell wm size"),n=/Physical size:\s*(\d+)x(\d+)/.exec(s);if(n)return this.resolvedSize=At(D({width:Number(n[1]),height:Number(n[2])},this.options.quality)),this.resolvedSize}catch(s){e.debug(`[AndroidH264Source] could not query display size: ${s}`)}return this.resolvedSize=D(Et,this.options.quality),this.resolvedSize}}function At(o){let s=Math.min(1,Math.sqrt(q0*256/(o.width*o.height))),n=Math.max(2,Math.floor(o.width*s/2)*2),a=Math.max(2,Math.floor(o.height*s/2)*2);while(nV(n,a)>q0)if(n>=a)n-=2;else a-=2;return{width:n,height:a}}import{connect as $t}from"net";var It=new Set;class L{byDevice=new Map;add(o,s){let n=this.byDevice.get(o);if(n){n.add(s);return}this.byDevice.set(o,new Set([s]))}remove(o,s){let n=this.byDevice.get(o);if(!n)return;if(n.delete(s),n.size===0)this.byDevice.delete(o)}active(o){return this.byDevice.get(o)??It}}import{createHash as Ht}from"crypto";var V="automobile_video",re="dev.jasonpearson.automobile.video.VideoServer",T="/data/local/tmp/automobile-video-sessions",_t=30000,Ft=300000,Mt=300000;function kt(o){return Ht("sha256").update(o,"ascii").digest("hex")}function xt(o){let s=o.trim().split(/\s+/),n=s.find((d)=>d.startsWith(`localabstract:${V}_`)),a=s.find((d)=>d.startsWith("tcp:"));if(n===void 0||a===void 0)return null;let c=Number.parseInt(a.slice(4),10);if(!Number.isInteger(c)||c<=0)return null;return{port:c,socketName:n.slice(14)}}function Ot(o){return typeof o==="string"&&/^[a-f0-9]{64}$/.test(o)}var ie=/^[A-Za-z0-9_.-]{1,100}$/;function Z(o){return typeof o==="number"&&Number.isInteger(o)&&o>0}function te(o){return typeof o==="number"&&Number.isFinite(o)}function Dt(o){return o===void 0||te(o)&&o>=0}function Nt(o){if(typeof o!=="object"||o===null)return!1;if(!Bt(o))return!1;let s=o;return[Ot(s.sessionTokenHash),typeof s.socketName==="string",typeof s.socketName==="string"&&ie.test(s.socketName),typeof s.deviceSerial==="string",Z(s.ownerPid),Z(s.pid),Z(s.forwardPort),te(s.startedAtMs),te(s.heartbeatAtMs),Dt("heartbeatElapsedRealtimeMs"in s?s.heartbeatElapsedRealtimeMs:void 0)].every(Boolean)}function Pe(o){return o.split(/\r?\n/).flatMap((s)=>{try{let n=JSON.parse(s);if(!Nt(n))return[];return[n]}catch(n){return e.debug(`[PersistentEncoderH264Source] ignoring malformed video session lease: ${n}`),[]}})}var Lt=/^[A-Za-z0-9_.-]+\.json(?:\.tmp)?$/;function Vt(o){let s=null,n=[];for(let a of o.split(/\r?\n/)){let c=a.trim();if(c.length===0)continue;if(c.startsWith("NOW ")){let g=Number.parseInt(c.slice(4).trim(),10);if(Number.isFinite(g)&&g>=0)s=g*1000;continue}let d=c.indexOf(" ");if(d<=0)continue;let p=Number.parseInt(c.slice(0,d),10),m=c.slice(d+1).trim(),S=m.slice(m.lastIndexOf("/")+1);if(!Number.isFinite(p)||p<0||!Lt.test(S))continue;n.push({name:S,mtimeMs:p*1000})}return{nowEpochMs:s,files:n}}class oe{options;adbFactory;timer;teardownTimeoutMs;withTimeout;activeVideoSessionRegistry;constructor(o,s,n,{activeVideoSessionRegistry:a,teardown:c}){this.options=o;this.adbFactory=s;this.timer=n;this.activeVideoSessionRegistry=a,this.teardownTimeoutMs=c.timeoutMs,this.withTimeout=c.withTimeout}async reconcileExpiredLeases(o){let s;try{let a=`for f in ${T}/*.json; do [ -f "$f" ] || continue; cat "$f"; printf "\\n"; done`,c=await this.withTimeout(o.executeCommand(`shell sh -c ${b(a)}`),this.teardownTimeoutMs,"adb read video session leases");s=Pe(c.stdout)}catch(a){e.debug(`[PersistentEncoderH264Source] unable to read video session leases: ${a}`);return}if(await this.sweepOrphanLeaseFiles(o,new Set(s.map((a)=>`${a.socketName}.json`))),await this.sweepOrphanForwards(o,new Set(s.map((a)=>a.socketName))),s.length===0)return;let n=await this.readDeviceElapsedRealtimeMs(o);if(n===null){e.warn("[PersistentEncoderH264Source] unable to read device uptime; skipping stale-session cleanup");return}for(let a of s){let c=this.classifyLeaseForReclaim(a,n);if(c===null)continue;e.info(`[PersistentEncoderH264Source] stale-session-cleanup socket=${a.socketName} reason=${c}`),await this.removeForwardIfMatching(o,a.forwardPort,a.socketName),await this.terminateProcessIfMatching(o,a),await this.removeLeaseIfMatching(o,a)}}classifyLeaseForReclaim(o,s){let n=o.heartbeatElapsedRealtimeMs;if(n!==void 0&&s-n<0)return"previous-boot";if(o.deviceSerial!==this.options.device.deviceId)return null;if(n===void 0)return this.timer.now()-o.heartbeatAtMs>=Ft?"wall-clock-stale":null;return s-n>=_t?"elapsed-stale":null}async sweepOrphanLeaseFiles(o,s){let n;try{let a=`printf 'NOW %s\\n' "$(date +%s)"; for f in ${T}/*.json ${T}/*.json.tmp; do [ -f "$f" ] || continue; stat -c '%Y %n' "$f"; done`,c=await this.withTimeout(o.executeCommand(`shell sh -c ${b(a)}`),this.teardownTimeoutMs,"adb list stale video session lease files");n=Vt(c.stdout)}catch(a){e.debug(`[PersistentEncoderH264Source] unable to list video session lease files: ${a}`);return}if(n.nowEpochMs===null){e.debug("[PersistentEncoderH264Source] lease-file sweep skipped: no device clock in listing");return}for(let a of n.files){if(!a.name.endsWith(".json.tmp")&&s.has(a.name))continue;let d=n.nowEpochMs-a.mtimeMs;if(d<Mt)continue;e.info(`[PersistentEncoderH264Source] stale-lease-file-sweep name=${a.name} ageMs=${d}`);try{await this.withTimeout(o.executeCommand(`shell rm -f ${T}/${a.name}`),this.teardownTimeoutMs,"adb remove stale video session lease file")}catch(p){e.debug(`[PersistentEncoderH264Source] stale-lease-file sweep failed name=${a.name}: ${p}`)}}}async sweepOrphanForwards(o,s){let n;try{n=(await this.withTimeout(o.executeCommand("forward --list"),this.teardownTimeoutMs,"adb forward --list for orphan sweep")).stdout}catch(d){e.debug(`[PersistentEncoderH264Source] unable to list forwards for orphan sweep: ${d}`);return}let a=this.activeVideoSessionRegistry.active(this.options.device.deviceId),c=[];for(let d of n.split(/\r?\n/)){let p=xt(d);if(p===null)continue;if(s.has(p.socketName)||a.has(p.socketName))continue;c.push(p)}for(let d of c)e.info(`[PersistentEncoderH264Source] orphan-forward-sweep port=${d.port} socket=${d.socketName}`),await this.removeForwardIfMatching(o,d.port,d.socketName)}async cleanupOwnedSession(o,s,n){let a=this.adbFactory.create(this.options.device),c=await this.readLease(a,o.socketName);if(c&&this.isOwnedSessionLease(c,o,n)){await this.removeForwardIfMatching(a,c.forwardPort,c.socketName),await this.removeOwnedForward(a,s,o.socketName,c.forwardPort),await this.terminateProcessIfMatching(a,c),await this.removeLeaseIfMatching(a,c);return}if(c){e.warn(`[PersistentEncoderH264Source] refusing owned-session process cleanup socket=${o.socketName}: lease identity or PID mismatch`),await this.removeOwnedForward(a,s,o.socketName);return}await this.removeOwnedForward(a,s,o.socketName)}isOwnedSessionLease(o,s,n){return o.sessionTokenHash===kt(s.token)&&o.socketName===s.socketName&&o.ownerPid===s.ownerPid&&o.deviceSerial===s.deviceSerial&&(n===null||o.pid===n)}async removeOwnedForward(o,s,n,a){if(s!==null&&s!==a)await this.removeForwardIfMatching(o,s,n)}async readLease(o,s){try{let n=await this.withTimeout(o.executeCommand(`shell cat ${T}/${s}.json`),this.teardownTimeoutMs,"adb read owned video session lease");return Pe(n.stdout).find((a)=>a.socketName===s)??null}catch(n){return e.debug(`[PersistentEncoderH264Source] unable to read owned video session lease: ${n}`),null}}async terminateProcessIfMatching(o,s){try{let n=await this.withTimeout(o.executeCommand(`shell cat /proc/${s.pid}/cmdline`),this.teardownTimeoutMs,"adb read video-server process cmdline"),a=n.stdout.split("\x00");if(n.stdout.length===0||a.every((d)=>d.length===0)){e.debug(`[PersistentEncoderH264Source] stale-session process cleanup skipped socket=${s.socketName}: process already exited`);return}let c=a.some((d,p)=>d==="--socket-name"&&a[p+1]===s.socketName);if(!a.includes(re)||!c){e.warn(`[PersistentEncoderH264Source] refusing stale-session process cleanup socket=${s.socketName}: PID/socket mismatch`);return}await this.withTimeout(o.executeCommand(`shell kill -2 ${s.pid}`),this.teardownTimeoutMs,"adb terminate video-server process")}catch(n){e.debug(`[PersistentEncoderH264Source] stale-session process cleanup skipped socket=${s.socketName}: ${n}`)}}async removeForwardIfMatching(o,s,n){try{let a=await this.withTimeout(o.executeCommand("forward --list"),this.teardownTimeoutMs,"adb forward --list"),c=`tcp:${s}`,d=`localabstract:${n}`;if(!a.stdout.split(/\r?\n/).some((m)=>{let S=m.trim().split(/\s+/);return S.includes(c)&&S.includes(d)})){e.warn(`[PersistentEncoderH264Source] refusing stale-session forward cleanup port=${s}: destination mismatch`);return}await this.withTimeout(o.executeCommand(`forward --remove tcp:${s}`),this.teardownTimeoutMs,"adb forward --remove")}catch(a){e.debug(`[PersistentEncoderH264Source] forward --remove failed: ${a}`)}}async removeLeaseIfMatching(o,s){let n=await this.readLease(o,s.socketName);if(n?.socketName!==s.socketName||n.sessionTokenHash!==s.sessionTokenHash||n.pid!==s.pid)return;try{await this.withTimeout(o.executeCommand(`shell rm -f ${T}/${s.socketName}.json`),this.teardownTimeoutMs,"adb remove video session lease")}catch(a){e.debug(`[PersistentEncoderH264Source] stale-session lease cleanup failed: ${a}`)}}async readDeviceElapsedRealtimeMs(o){try{let s=await this.withTimeout(o.executeCommand("shell cat /proc/uptime"),this.teardownTimeoutMs,"adb read device uptime"),n=Number.parseFloat(s.stdout.trim().split(/\s+/,1)[0]??"");if(Number.isFinite(n)&&n>=0)return Math.floor(n*1000)}catch(s){e.debug(`[PersistentEncoderH264Source] unable to read device uptime: ${s}`)}return null}}function Bt(o){if(!(("sessionTokenHash"in o)&&("socketName"in o)&&("deviceSerial"in o)&&("ownerPid"in o)&&("pid"in o)&&("forwardPort"in o)&&("startedAtMs"in o)&&("heartbeatAtMs"in o)))return!1;return!0}var Re=1,se=1,Te=Buffer.from([65,86,77,72]),Ae=5000,Ie=100,He=20000,_e=5000,Fe=3,Me=Q({initialDelayMs:1000,multiplier:2,maxDelayMs:8000}),ke=60000,xe=new L,Oe=(o,s)=>new Promise((n,a)=>{let c=$t(o,"127.0.0.1"),d=!1,p=(y)=>{if(d)return;d=!0,c.off("connect",m),c.off("error",S),s?.removeEventListener("abort",g),y()},m=()=>p(()=>n(c)),S=(y)=>p(()=>a(y)),g=()=>p(()=>{c.destroy(),a(Error("video-server socket connection aborted"))});if(c.once("connect",m),c.once("error",S),s?.aborted){g();return}s?.addEventListener("abort",g,{once:!0})}),De=1e4;function Ne(o){return`${V}_${o.next().replaceAll("-","")}`}function Le(o,s){let n=Buffer.from(o,"ascii"),a=Buffer.alloc(Te.length+2+n.length),c=Te.copy(a,0);return c=a.writeUInt8(s,c),c=a.writeUInt8(n.length,c),n.copy(a,c),a}function Ve(o){return/^[A-Za-z0-9-]{8,80}$/.test(o)}var R="/data/local/tmp/automobile-video.jar";function Ut(o){let s=null,n=null;for(let a of o.split(/\r?\n/)){let c=a.trim();if(c.length===0)continue;if(/^[0-9]+$/.test(c)){n=Number.parseInt(c,10);continue}let d=/^([a-f0-9]{64})(?:\s|$)/.exec(c);if(d)s=d[1]}if(s===null||n===null||!Number.isInteger(n)||n<0)return null;return{sha256:s,size:n}}class ne{options;commandTimeoutMs;withTimeout;expectedJarIntegrity=null;remoteJarVerified=!1;jarIntegrityProvider;constructor(o,s,n){this.options=o;this.commandTimeoutMs=s;this.withTimeout=n;this.jarIntegrityProvider=o.jarIntegrityProvider}async ensureRemoteJar(o){let s=await this.resolveExpectedJarIntegrity(),n=await this.statAndHashRemoteJar(o);if(n!==null&&n.size===s.size&&n.sha256===s.sha256){this.remoteJarVerified=!0,e.info(`[PersistentEncoderH264Source] on-device video-server jar already matches (sha256=${s.sha256}, size=${s.size}); skipping push`);return}if(n!==null)e.info(`[PersistentEncoderH264Source] on-device video-server jar differs (expected sha256=${s.sha256} size=${s.size}, remote sha256=${n.sha256} size=${n.size}); pushing`);await this.withTimeout(o.executeCommand(`push "${this.options.jarPath}" ${R}`,{timeoutMs:this.commandTimeoutMs}),this.commandTimeoutMs,"adb push video-server jar")}async verifyRemoteJarBeforeLaunch(o){if(this.remoteJarVerified)return;let s=await this.resolveExpectedJarIntegrity(),n=await this.statAndHashRemoteJar(o);if(n===null){e.warn(`[PersistentEncoderH264Source] could not hash the on-device jar at ${R} before launch; proceeding on the just-pushed trusted bytes (integrity verification degraded)`);return}if(n.size!==s.size||n.sha256!==s.sha256)throw new t(`Refusing to launch video-server: the on-device jar at ${R} failed integrity verification (expected sha256=${s.sha256} size=${s.size}, got sha256=${n.sha256} size=${n.size}). The bytes on the device do not match the verified host copy; app_process will not be launched.`);this.remoteJarVerified=!0}async resolveExpectedJarIntegrity(){if(this.expectedJarIntegrity===null){let s=await(this.jarIntegrityProvider??il.getInstance()).computeLocalJarIntegrity(this.options.jarPath);this.expectedJarIntegrity={sha256:s.sha256.toLowerCase(),size:s.size}}return this.expectedJarIntegrity}async statAndHashRemoteJar(o){let s=`sha256sum ${R} 2>/dev/null; wc -c < ${R} 2>/dev/null`;try{let n=await this.withTimeout(o.executeCommand(`shell sh -c ${b(s)}`,{timeoutMs:this.commandTimeoutMs}),this.commandTimeoutMs,"adb hash video-server jar");return Ut(n.stdout)}catch(n){return e.debug(`[PersistentEncoderH264Source] unable to hash on-device video-server jar: ${n}`),null}}}function Be(o){let s=o.session;if(!s||o.forwardedPort===null)throw Error("Video server session was not initialized.");let n=["shell",`CLASSPATH=${R}`,"app_process","/",re,"--quality",o.options.quality??"medium","--socket-name",s.socketName,"--session-token",s.token,"--owner-pid",String(s.ownerPid),"--device-serial",s.deviceSerial,"--forward-port",String(o.forwardedPort)];if(o.options.bitrateBps&&o.options.bitrateBps>0)n.push("--bit-rate",String(Math.round(o.options.bitrateBps)));if(Number.isInteger(o.options.fps)&&o.options.fps>0)n.push("--fps",String(o.options.fps));if(o.options.size)n.push("--size",`${o.options.size.width}x${o.options.size.height}`);if(o.options.audioEnabled)n.push("--audio");return n}function $e(o){let s=o.sessionTokenFactory();if(!Ve(s))throw Error("Video server session token factory returned an unsafe token.");let n=o.socketNameFactory();if(!ie.test(n))throw Error("Video server socket-name factory returned an unsafe socket name.");return{token:s,socketName:n,ownerPid:process.pid,deviceSerial:o.options.device.deviceId}}function Ue(o){if(o===null)return e.warn("[PersistentEncoderH264Source] device advertised no handshake protocol; streaming without a token handshake (defense-in-depth degraded, UID gating still applies)"),null;return Math.min(o,se)}function We(o,s){let{session:n,negotiatedHandshakeVersion:a}=o;if(!n||a===null)return;try{s.write(Le(n.token,a))}catch(c){e.debug(`[PersistentEncoderH264Source] handshake write failed: ${c}`)}}function qe(o,s,n,a){return new Promise((c,d)=>{let p=!1,m=o.timer.setTimeout(()=>{if(p)return;p=!0,e.warn(`[PersistentEncoderH264Source] ${a} timed out after ${n}ms`),d(new t(`${a} did not complete within ${n}ms`))},n);s.then((S)=>{if(p)return;p=!0,o.timer.clearTimeout(m),c(S)},(S)=>{if(p)return;p=!0,o.timer.clearTimeout(m),d(S instanceof Error?S:Error(String(S)))})})}var q=1748121140,Wt=1634563448,x=1932605036,qt=1,Kt=2,M=12,U=12,Ke=16,W=16,ze=16777216,zt=16,ae=1n<<63n,Je=1n<<62n,Ge=1n<<61n,ue=59n,Jt=0b11n<<ue,Ye=(1n<<ue)-1n;class k{callbacks;buffered=new O0;header=null;muxTracks=null;constructor(o){this.callbacks=o}static rotationFromFlags(o){if((o&ae)===0n)return;return Number((o&Jt)>>ue)}assertWithinCap(o,s,n){if(o>s)throw new t(`video-server stream declared ${n}=${o}, exceeding the ${s} cap; the stream is likely corrupt or out of sync.`)}push(o){if(this.buffered.append(o),!this.header&&!this.muxTracks&&!this.readStreamHeader())return;if(this.muxTracks){this.drainMuxPackets();return}while(this.buffered.length>=U){let s=this.buffered.peek(U),n=s.readBigUInt64BE(0),a=s.readUInt32BE(8);if(this.assertWithinCap(a,ze,"packet size"),this.buffered.length<U+a)break;this.buffered.discard(U);let c=this.buffered.takeDetached(a);this.callbacks.onPacket({trackId:qt,codecId:this.header?.codecId??q,data:c,config:(n&ae)!==0n,keyFrame:(n&Je)!==0n,replayed:(n&Ge)!==0n,rotation:k.rotationFromFlags(n),ptsUs:Number(n&Ye)})}}readStreamHeader(){if(this.buffered.length<M)return!1;let o=this.buffered.peek(M),s=o.readUInt32BE(0);if(s===Wt){let n=o.readUInt32BE(8);this.assertWithinCap(n,zt,"trackCount");let a=M+n*Ke;if(this.buffered.length<a)return!1;let c=this.buffered.peek(a);this.muxTracks=new Map;let d=null,p=!1;for(let m=0;m<n;m++){let S=M+m*Ke,g=c.readUInt32BE(S),y=c.readUInt32BE(S+4),f=c.readUInt32BE(S+8),w=c.readUInt32BE(S+12);if(this.muxTracks.set(g,{codecId:y,param1:f,param2:w}),y===q)d={codecId:y,width:f,height:w};else if(g===Kt&&y===x)p=!0}if(d)this.header={...d,muxed:!0,audio:p,muxVersion:c.readUInt32BE(4)},this.callbacks.onHeader?.(this.header);this.buffered.discard(a)}else this.header={codecId:s,width:o.readUInt32BE(4),height:o.readUInt32BE(8)},this.buffered.discard(M),this.callbacks.onHeader?.(this.header);return!0}drainMuxPackets(){while(this.buffered.length>=W){let o=this.buffered.peek(W),s=o.readUInt32BE(0),n=o.readBigUInt64BE(4),a=o.readUInt32BE(12);if(this.assertWithinCap(a,ze,"packet size"),this.buffered.length<W+a)break;this.buffered.discard(W);let c=this.buffered.takeDetached(a),d=this.muxTracks?.get(s);if(!d)continue;this.callbacks.onPacket({trackId:s,codecId:d.codecId,data:c,config:(n&ae)!==0n,keyFrame:(n&Je)!==0n,replayed:(n&Ge)!==0n,rotation:k.rotationFromFlags(n),ptsUs:Number(n&Ye)})}}}var Gt=/^VIDEO_SESSION_READY token=([^ ]+) pid=(\d+) socket=([^ ]+?)(?: proto=(\d+))?$/,je="Streaming started",Yt=/(?:^|\s)dropped=(\d+)(?:\s|$)/;class ce{options;timer;readyTimeoutMs;session;resolveStartupAudioHeader;resolveStartupAudioPacket;serverOutputObserver=null;serverOutputBuffer="";sawStreamingStarted=!1;constructor(o,s,n,a){this.options=o;this.timer=s;this.readyTimeoutMs=n;this.session=a}waitForReady(o){let s=this.session()?.token;if(!s)throw Error("Video server session token was unavailable.");return new Promise((n,a)=>{let c=!1,d="",p=(f)=>{if(c)return;c=!0,this.timer.clearTimeout(m),o.stdout.off("data",S),o.removeListener("exit",g),o.removeListener("error",y),f()},m=this.timer.setTimeout(()=>{p(()=>a(new t(`video-server did not become session-ready within ${this.readyTimeoutMs}ms. Set AUTOMOBILE_VIDEO_SERVER_JAR to a current automobile-video.jar.`)))},this.readyTimeoutMs),S=(f)=>{if(c)return;d+=f.toString();let w=d.split(/\r?\n/);d=w.pop()??"";for(let E of w){let C=Gt.exec(E);if(!C)continue;if(C[1]!==s||C[3]!==this.session()?.socketName){p(()=>a(new t(`video-server session readiness token mismatch (expected=${s}, got=${C[1]})`)));return}let _=Number.parseInt(C[2],10);if(!Number.isInteger(_)||_<=0){p(()=>a(new t("video-server returned an invalid session PID")));return}let P=C[4]!==void 0?Number.parseInt(C[4],10):null;p(()=>n({pid:_,protocolVersion:P}));return}},g=(f,w)=>{p(()=>a(Error(`video-server exited before ready (code=${f}, signal=${w})`)))},y=(f)=>p(()=>a(f));o.stdout.on("data",S),o.once("exit",g),o.once("error",y)})}waitForStreamingStarted(o){if(this.sawStreamingStarted)return Promise.resolve();return this.waitForServerLine(o,je,`video-server did not start streaming within ${this.readyTimeoutMs}ms`,"video-server exited before streaming started")}waitForStartupAudioReady(){return new Promise((o,s)=>{let n=!1,a=!1,c=this.timer.setTimeout(()=>{d(()=>s(new t(`video-server did not produce PCM audio within ${this.readyTimeoutMs}ms`)))},this.readyTimeoutMs),d=(p)=>{if(n)return;n=!0,this.timer.clearTimeout(c),this.resolveStartupAudioHeader=void 0,this.resolveStartupAudioPacket=void 0,p()};this.resolveStartupAudioHeader=(p)=>{if(p.muxed===!0&&p.audio===!0){a=!0;return}d(()=>{s(new t("Audio was requested, but video-server did not advertise muxed PCM audio. Rebuild or provide a current automobile-video.jar."))})},this.resolveStartupAudioPacket=(p)=>{if(a&&p.codecId===x&&p.data.length>0)d(o)}})}waitForServerLine(o,s,n,a){return new Promise((c,d)=>{let p=!1,m="",S=(E)=>{if(p)return;p=!0,this.timer.clearTimeout(g),o.stdout.off("data",y),o.removeListener("exit",f),o.removeListener("error",w),E()},g=this.timer.setTimeout(()=>{S(()=>d(new t(n)))},this.readyTimeoutMs),y=(E)=>{if(p)return;if(m+=E.toString(),m.includes(s))S(c)},f=(E,C)=>{S(()=>d(Error(`${a} (code=${E}, signal=${C})`)))},w=(E)=>S(()=>d(E));o.stdout.on("data",y),o.once("exit",f),o.once("error",w)})}attachServerOutputObserver(o){this.serverOutputBuffer="",this.sawStreamingStarted=!1;let s=(n)=>{this.serverOutputBuffer+=n.toString();let a=this.serverOutputBuffer.split(/\r?\n/);if(this.serverOutputBuffer=a.pop()??"",a.includes(je))this.sawStreamingStarted=!0;for(let c of a){if(!c.startsWith("VIDEO_STATS "))continue;let d=Yt.exec(c),p=d?Number(d[1]):NaN;if(Number.isSafeInteger(p)&&p>=0)this.options.onDroppedFrames?.(p)}};this.serverOutputObserver=s,o.stdout.on("data",s)}detachServerOutputObserver(o){if(this.serverOutputObserver)o.stdout.off("data",this.serverOutputObserver);this.serverOutputObserver=null,this.serverOutputBuffer="",this.sawStreamingStarted=!1}}class K{leases;jar;startup;options;adbFactory;connector;timer;readyTimeoutMs;commandTimeoutMs;teardownTimeoutMs;localSocketReconnectWindowMs;localSocketReconnectRetryMs;sessionTokenFactory;socketNameFactory;activeVideoSessionRegistry;maxServerRelaunchAttempts;serverRelaunchBackoff;serverRelaunchHealthyWindowMs;server=null;socket=null;forwardedPort=null;session=null;deviceProcessId=null;negotiatedHandshakeVersion=null;running=!1;teardownPromise=null;socketReconnectPromise=null;socketReconnectAbortController=null;telemetryInitialized=!1;lastEncodedFrameTimestampUs=null;lastIdrTimestampUs=null;idrRequestCount=0;idrCompletionCount=0;pendingIdrRequests=0;encodedAccessUnitCount=0;serverStartupInProgress=null;serverStartupFailure=null;serverRelaunchAttempts=0;serverHealthySinceMs=null;relaunching=!1;relaunchAbortController=null;constructor(o){this.options=o,this.adbFactory=o.adbFactory??v,this.connector=o.connector??Oe,this.timer=o.timer??i,this.readyTimeoutMs=o.readyTimeoutMs??De,this.commandTimeoutMs=o.commandTimeoutMs??He,this.teardownTimeoutMs=o.teardownTimeoutMs??_e,this.localSocketReconnectWindowMs=o.localSocketReconnectWindowMs??Ae,this.localSocketReconnectRetryMs=o.localSocketReconnectRetryMs??Ie,this.sessionTokenFactory=o.sessionTokenFactory??(()=>B.next()),this.socketNameFactory=o.socketNameFactory??(()=>Ne(B)),this.activeVideoSessionRegistry=o.activeVideoSessionRegistry??xe,this.jar=new ne(o,this.commandTimeoutMs,(n,a,c)=>this.withTimeout(n,a,c));let s=K.resolveRelaunchPolicy(o);this.maxServerRelaunchAttempts=s.maxAttempts,this.serverRelaunchBackoff=s.backoff,this.serverRelaunchHealthyWindowMs=s.healthyWindowMs,this.leases=new oe(o,this.adbFactory,this.timer,{activeVideoSessionRegistry:this.activeVideoSessionRegistry,teardown:{timeoutMs:this.teardownTimeoutMs,withTimeout:(n,a,c)=>this.withTimeout(n,a,c)}}),this.startup=new ce(o,this.timer,this.readyTimeoutMs,()=>this.session),this.validateTimings()}static resolveRelaunchPolicy(o){return{maxAttempts:o.maxServerRelaunchAttempts??Fe,backoff:Lo(o.serverRelaunchBackoff??Me),healthyWindowMs:o.serverRelaunchHealthyWindowMs??ke}}validateTimings(){if(this.commandTimeoutMs<=0||this.teardownTimeoutMs<=0)throw new t("Video server command timeouts must be positive milliseconds.");if(this.localSocketReconnectWindowMs<=0||this.localSocketReconnectRetryMs<=0)throw new t("Local socket reconnect timings must be positive milliseconds.");if(!Number.isInteger(this.maxServerRelaunchAttempts)||this.maxServerRelaunchAttempts<0)throw new t("Server relaunch attempts must be a non-negative integer.");if(this.serverRelaunchHealthyWindowMs<=0)throw new t("Server relaunch healthy window must be positive milliseconds.")}get isRunning(){return this.running}getTelemetry(){if(!this.telemetryInitialized)return{lastEncodedFrameTimestampUs:null,lastIdrTimestampUs:null,idrRequestCount:null,idrCompletionCount:null,encodedAccessUnitCount:null};return{lastEncodedFrameTimestampUs:this.lastEncodedFrameTimestampUs,lastIdrTimestampUs:this.lastIdrTimestampUs,idrRequestCount:this.idrRequestCount,idrCompletionCount:this.idrCompletionCount,encodedAccessUnitCount:this.encodedAccessUnitCount}}async start(){if(this.running)throw new t("Persistent encoder H.264 source already started.");this.running=!0,this.serverStartupInProgress=null,this.serverStartupFailure=null;try{await this.launch(),this.serverHealthySinceMs=this.timer.now()}catch(o){throw this.running=!1,await this.beginTeardown(),o}}async stop(){if(!this.running){await this.teardownPromise;return}this.running=!1,this.relaunchAbortController?.abort(),await this.beginTeardown()}requestKeyFrame(){let o=this.socket;if(!this.running||!o)return!1;this.telemetryInitialized=!0,this.idrRequestCount++,this.pendingIdrRequests++;try{return o.write(Buffer.from([Re])),!0}catch(s){return this.pendingIdrRequests--,e.debug(`[PersistentEncoderH264Source] keyframe request write failed: ${s}`),!1}}withTimeout(o,s,n){return qe({timer:this.timer},o,s,n)}async launch(){let o=this.adbFactory.create(this.options.device),s=await this.prepareSession(o);if(s===null)return;let n=await this.spawnAndWaitForServer(o);if(!n)return;await this.connectToServer(n,s)}async prepareSession(o){if(!this.running)return null;let s=this.createSession();if(this.session=s,this.deviceProcessId=null,this.jar.remoteJarVerified=!1,this.activeVideoSessionRegistry.add(this.options.device.deviceId,s.socketName),await this.leases.reconcileExpiredLeases(o),!this.running)return null;if(await this.jar.ensureRemoteJar(o),!this.running)return null;let n=await this.withTimeout(o.executeCommand(`forward tcp:0 localabstract:${s.socketName}`,{timeoutMs:this.commandTimeoutMs}),this.commandTimeoutMs,"adb forward video-server socket"),a=Number.parseInt(n.stdout.trim(),10);if(!Number.isInteger(a)||a<=0)throw new t(`adb forward returned an invalid port: "${n.stdout.trim()}"`);if(!this.running)return await this.leases.removeForwardIfMatching(o,a,s.socketName),null;return this.forwardedPort=a,a}async spawnAndWaitForServer(o){if(await this.jar.verifyRemoteJarBeforeLaunch(o),!this.running)return null;let s=await this.withTimeout(o.spawn(this.buildServerArgs()),this.commandTimeoutMs,"adb spawn video-server");if(!this.running)return s.kill("SIGINT"),null;this.server=s,this.startup.attachServerOutputObserver(s),s.stderr.on("data",(a)=>{let c=a.toString().trim();if(c)e.debug(`[PersistentEncoderH264Source] server stderr: ${c}`)});let n=await this.startup.waitForReady(s);return this.deviceProcessId=n.pid,this.negotiatedHandshakeVersion=Ue(n.protocolVersion),this.serverStartupInProgress=s,this.watchServer(s),this.running?s:null}async connectToServer(o,s){this.throwIfServerStartupFailed(o);let n=this.options.audioEnabled?this.startup.waitForStreamingStarted(o):null;n?.catch((f)=>{e.debug(`[PersistentEncoderH264Source] Audio startup waiter rejected: ${r(f)}`)});let a=new AbortController,c=this.timer.now()+this.commandTimeoutMs,d=this.connectBeforeDeadline(s,c,a.signal),p=!1,m,S=new Promise((f,w)=>{m=w}),g=(f,w)=>{m(Error(`video-server exited (code=${f}, signal=${w})`))},y=(f)=>{m(f)};o.once("exit",g),o.once("error",y);try{let f=await l([d,S],{timer:this.timer,label:"video-server connection"});if(p=!0,!this.running){f.destroy();return}this.socket=f,this.sendHandshake(f);let w=this.options.audioEnabled?this.startup.waitForStartupAudioReady():null,E=this.options.audioEnabled?"startup":"post-start",C,_=this.options.audioEnabled?new Promise((P,pt)=>{C=pt}):null;if(this.wireSocket(f,(P)=>{if(E==="startup"){C?.(P);return}this.reconnectSocket(f,P)},(P)=>this.startup.resolveStartupAudioHeader?.(P)),this.options.audioEnabled){if(await l([Promise.all([n,w]),Promise.resolve(_)],{timer:this.timer,label:"video-server audio startup"}),!this.running)return}E="post-start"}finally{if(o.removeListener("exit",g),o.removeListener("error",y),!p)a.abort(),d.catch((f)=>{e.debug(`[PersistentEncoderH264Source] Losing connection rejected: ${r(f)}`)});if(this.serverStartupInProgress===o)this.serverStartupInProgress=null}}throwIfServerStartupFailed(o){let s=this.serverStartupFailure;if(s?.server===o)throw s.error}watchServer(o){o.once("exit",(s,n)=>{if(this.server!==o)return;if(this.startup.detachServerOutputObserver(o),this.server=null,this.serverStartupInProgress===o){this.serverStartupFailure={server:o,error:Error(`video-server exited (code=${s}, signal=${n})`)};return}this.handlePostStartServerLoss(Error(`video-server exited (code=${s}, signal=${n})`))}),o.once("error",(s)=>{if(this.serverStartupInProgress===o){this.serverStartupFailure={server:o,error:s};return}this.handlePostStartServerLoss(s)})}buildServerArgs(){return Be({session:this.session,forwardedPort:this.forwardedPort,options:this.options})}createSession(){return $e({options:this.options,sessionTokenFactory:this.sessionTokenFactory.bind(this),socketNameFactory:this.socketNameFactory.bind(this)})}sendHandshake(o){return We({session:this.session,negotiatedHandshakeVersion:this.negotiatedHandshakeVersion},o)}wireSocket(o,s,n){let a=new k({onHeader:(p)=>{n?.(p),this.requestKeyFrame(),e.info(`[PersistentEncoderH264Source] stream ${p.width}x${p.height} codec=0x${p.codecId.toString(16)}`)},onPacket:(p)=>{if(this.socket===o){if(p.codecId===q)this.deliverVideoPacket(p);else if(p.codecId===x)this.options.onAudioData?.(p.data),this.startup.resolveStartupAudioPacket?.(p)}}}),c=!1,d=(p)=>{if(c||this.socket!==o)return;c=!0,s(p)};o.on("data",(p)=>{try{a.push(p)}catch(m){d(m instanceof Error?m:Error(String(m)))}}),o.on("error",d),o.on("close",()=>d(Error("video-server socket closed")))}deliverVideoPacket(o){if(this.observeVideoPacket(o),o.config&&o.rotation!==void 0)this.options.onRotation?.(o.rotation);if(!o.config&&!o.replayed)this.options.onSourceFrame?.();this.options.onData(o.data),this.options.onEncodedAccessUnit?.()}observeVideoPacket(o){if(o.config)return;if(this.telemetryInitialized=!0,o.replayed){if(this.lastEncodedFrameTimestampUs??=o.ptsUs,o.keyFrame)this.lastIdrTimestampUs??=o.ptsUs;return}if(this.lastEncodedFrameTimestampUs=o.ptsUs,this.encodedAccessUnitCount++,!o.keyFrame)return;if(this.lastIdrTimestampUs=o.ptsUs,this.pendingIdrRequests>0)this.pendingIdrRequests--,this.idrCompletionCount++}reconnectSocket(o,s){if(!this.running||this.socket!==o||this.socketReconnectPromise!==null)return;this.socket=null,o.destroy();let n=new AbortController;this.socketReconnectAbortController=n,this.socketReconnectPromise=this.tryReconnectSocket(s,n.signal).finally(()=>{if(this.socketReconnectAbortController===n)this.socketReconnectAbortController=null;this.socketReconnectPromise=null})}async tryReconnectSocket(o,s){let n=this.forwardedPort;if(n===null){this.failIfRunning(o);return}let a=this.timer.now()+this.localSocketReconnectWindowMs,c=o;while(this.running&&this.server!==null&&!s.aborted){try{let p=await this.connectBeforeDeadline(n,a,s);if(!this.running||this.server===null||s.aborted){p.destroy();return}this.socket=p,this.sendHandshake(p),this.wireSocket(p,(m)=>this.reconnectSocket(p,m)),e.info("[PersistentEncoderH264Source] reconnected to retained video-server socket");return}catch(p){c=p instanceof Error?p:Error(String(p))}let d=a-this.timer.now();if(d<=0)break;await this.waitForReconnectRetry(Math.min(this.localSocketReconnectRetryMs,d),s)}this.failReconnectIfServerRetained(s,c)}failReconnectIfServerRetained(o,s){if(o.aborted||this.server===null)return;this.failIfRunning(Error(`video-server socket did not reconnect within ${this.localSocketReconnectWindowMs}ms: ${s.message}`))}waitForReconnectRetry(o,s){if(s.aborted)return Promise.resolve();return new Promise((n)=>{let a=!1,c=()=>{if(a)return;a=!0,this.timer.clearTimeout(d),s.removeEventListener("abort",c),n()},d=this.timer.setTimeout(c,o);s.addEventListener("abort",c,{once:!0})})}async connectBeforeDeadline(o,s,n){let a=s-this.timer.now();if(a<=0)throw Error("video-server socket reconnect deadline elapsed");let c=new AbortController,d=new AbortController,p=!1,m=()=>c.abort();if(n.aborted)m();else n.addEventListener("abort",m,{once:!0});let S=this.connector(o,c.signal),g=this.timer.setTimeout(()=>{p=!0,c.abort()},a),y=()=>d.abort(Error(p?"video-server socket reconnect attempt timed out":"video-server socket reconnect attempt aborted"));if(c.signal.aborted)y();else c.signal.addEventListener("abort",y,{once:!0});try{return await l(S,{timer:this.timer,signal:d.signal,label:"video-server socket reconnect attempt"})}finally{if(this.timer.clearTimeout(g),n.removeEventListener("abort",m),c.signal.removeEventListener("abort",y),c.signal.aborted)S.then((f)=>f.destroy(),()=>{})}}handlePostStartServerLoss(o){if(!this.running||this.relaunching)return;this.resetRelaunchBudgetAfterHealthyRun(),this.relaunching=!0,this.relaunchAbortController=new AbortController,this.recoverFromServerLoss(o).finally(()=>{this.relaunching=!1})}async recoverFromServerLoss(o){let s=this.relaunchAbortController?.signal??new AbortController().signal,n=o;while(this.canAttemptRelaunch(s)){this.serverRelaunchAttempts++;let a=await this.attemptRelaunch(this.serverRelaunchAttempts,n,s);if(a.status!=="retry")return;n=a.error}if(this.running&&!s.aborted)await this.fallBackAfterRelaunchExhausted(n)}resetRelaunchBudgetAfterHealthyRun(){if(this.serverHealthySinceMs!==null&&this.timer.now()-this.serverHealthySinceMs>=this.serverRelaunchHealthyWindowMs)this.serverRelaunchAttempts=0}canAttemptRelaunch(o){return this.running&&!o.aborted&&this.serverRelaunchAttempts<this.maxServerRelaunchAttempts}async attemptRelaunch(o,s,n){let a=this.serverRelaunchBackoff.delayForAttempt(o);if(e.warn(`[PersistentEncoderH264Source] on-device server lost post-start (${s.message}); relaunch attempt ${o}/${this.maxServerRelaunchAttempts} after ${a}ms`),await this.delayBeforeRelaunch(a,n),!this.running||n.aborted)return{status:"aborted",error:s};if(await this.beginTeardown(),!this.running||n.aborted)return{status:"aborted",error:s};try{if(await this.launch(),!this.running)return{status:"aborted",error:s};return this.serverHealthySinceMs=this.timer.now(),e.info(`[PersistentEncoderH264Source] on-device server relaunched after ${o} attempt(s)`),{status:"recovered",error:s}}catch(c){let d=c instanceof Error?c:Error(String(c));return e.warn(`[PersistentEncoderH264Source] relaunch attempt ${o} failed: ${d.message}`,c),{status:"retry",error:d}}}delayBeforeRelaunch(o,s){if(o<=0||s.aborted)return Promise.resolve();return new Promise((n)=>{let a=()=>{this.timer.clearTimeout(c),s.removeEventListener("abort",a),n()},c=this.timer.setTimeout(a,o);s.addEventListener("abort",a,{once:!0})})}async fallBackAfterRelaunchExhausted(o){if(!this.running)return;let s=this.options.onScreenrecordFallback;if(!s){this.failIfRunning(o);return}e.warn(`[PersistentEncoderH264Source] relaunch budget of ${this.maxServerRelaunchAttempts} spent; falling back to screenrecord: ${o.message}`),this.running=!1,await this.beginTeardown();try{await s(o)}catch(n){this.options.onError?.(n instanceof Error?n:Error(String(n)))}}failIfRunning(o){if(!this.running)return;this.running=!1,this.beginTeardown(),this.options.onError?.(o)}beginTeardown(){return this.teardownPromise??=this.teardown().finally(()=>{this.teardownPromise=null}),this.teardownPromise}async teardown(){let o=this.socketReconnectAbortController;this.socketReconnectAbortController=null,o?.abort();let s=this.socket;this.socket=null,s?.destroy();let n=this.server;if(this.server=null,n)this.startup.detachServerOutputObserver(n);n?.kill("SIGINT");let a=this.session,c=this.forwardedPort,d=this.deviceProcessId;if(this.forwardedPort=null,this.deviceProcessId=null,this.session=null,a)await this.leases.cleanupOwnedSession(a,c,d),this.activeVideoSessionRegistry.remove(this.options.device.deviceId,a.socketName);else if(c!==null)e.warn(`[PersistentEncoderH264Source] refusing forward cleanup port=${c}: session identity is unavailable`)}}var jt={createPersistent:(o)=>new K(o),createScreenrecord:(o)=>new X(o)};class Xe{buildScreenrecord;active=null;stopped=!1;persistent;constructor(o,s){this.buildScreenrecord=s;this.persistent=o((n)=>this.switchToScreenrecord(n))}async start(){this.stopped=!1,this.active=this.persistent;try{await this.persistent.start()}catch(o){await this.switchToScreenrecord(o instanceof Error?o:Error(String(o)))}}async switchToScreenrecord(o){if(this.stopped||this.active!==this.persistent)return;e.warn(`[webrtc] persistent encoder unavailable, falling back to screenrecord: ${o.message}`);let s=this.buildScreenrecord();this.active=s,await s.start()}async stop(){this.stopped=!0;let o=this.active;this.active=null,await o?.stop()}requestKeyFrame(){return this.active?.requestKeyFrame?.()??!1}get getTelemetry(){let o=this.active;return o?.getTelemetry?.bind(o)}}function Qe(o,s,n=jt){if(!s){if(o.audioEnabled)throw Error("WebRTC audio requires the persistent Android video-server jar.");return n.createScreenrecord(o)}let a=(c)=>({device:o.device,onData:o.onData,onSourceFrame:o.onSourceFrame,onEncodedAccessUnit:o.onEncodedAccessUnit,onAudioData:o.onAudioData,onRotation:o.onRotation,onDroppedFrames:o.onDroppedFrames,onError:o.onError,bitrateBps:o.bitrateBps,size:o.size,quality:o.quality,fps:o.fps,audioEnabled:o.audioEnabled,adbFactory:o.adbFactory,timer:o.timer,jarPath:s,onScreenrecordFallback:c});if(o.audioEnabled)return n.createPersistent(a());return new Xe((c)=>n.createPersistent(a(c)),()=>n.createScreenrecord(o))}class A extends t{finalStartupError(o){return this}}class om extends t{approvalTarget;constructor(o="AutoMobile"){super("Screen Recording permission is required to discover and observe iOS Simulator windows.");this.approvalTarget=o}}class le extends A{finalStartupError(o){return new om(o??"AutoMobile")}}var Xt="warn: no frames received";function Ze(o,s){let n=s===null?"":` (last stage: ${s})`,a=Zt(o,s);return new A(`iOS screen capture did not produce a first frame${n}.${a}`)}function Qt(o,s){return new le(Ze(o,s).message)}function Zt(o,s){switch(s){case null:case"helper-executable-found":case"helper-process-spawned":return o.kind==="simulator"?" Screen Recording permission may be required to observe the iOS Simulator window.":"";case"permission-ready":return" The Simulator window could not be resolved; ensure the Simulator is booted and visible.";case"target-resolved":return" Capture never started after the window resolved (hung start); retry or restart the Simulator.";case"capture-started":return o.kind==="simulator"?" Screen Recording permission may be required to observe the iOS Simulator window.":"";case"first-frame":return""}}function er(o){return o.toLowerCase().includes(Xt)}function de(o){return o.trimStart().toLowerCase().startsWith("error:")}class pe{timer;firstFrameTimeoutMs;context;cancelFirstFrameWait=null;cancelFirstAudioWait=null;rejectFirstAudioWait=null;constructor(o,s,n){this.timer=o;this.firstFrameTimeoutMs=s;this.context=n}waitForFirstAudio(o){let s=this.context.generation();return new Promise((n,a)=>{let c=!1,d=(g)=>{if(c)return;if(c=!0,this.timer.clearTimeout(S),this.cancelFirstAudioWait===p)this.cancelFirstAudioWait=null;if(this.rejectFirstAudioWait===m)this.rejectFirstAudioWait=null;g()},p=()=>d(n),m=(g)=>d(()=>a(g)),S=this.timer.setTimeout(()=>{d(()=>a(new t("iOS Simulator audio capture did not produce PCM audio before startup timed out.")))},this.firstFrameTimeoutMs);this.cancelFirstAudioWait=p,this.rejectFirstAudioWait=m,o.on("audio",()=>{if(this.context.isCurrentHelper(o,s)&&this.context.isActive())d(n)}),o.on("error",(g)=>{if(this.context.isCurrentHelper(o,s)&&this.context.isActive())d(()=>a(g))}),o.on("exit",(g)=>{if(this.context.isCurrentHelper(o,s)&&this.context.isActive())d(()=>a(Error(this.context.describeHelperExit(g,"exited before audio"))))})})}waitForFirstEncodedRecord(o,s){let n=this.context.generation();return new Promise((a,c)=>{let d=!1,p=!1,m=(f)=>{if(d)return;if(d=!0,this.timer.clearTimeout(y),this.cancelFirstFrameWait===S)this.cancelFirstFrameWait=null;f()},S=()=>{m(a)},g=(f)=>{m(()=>c(this.context.encodedStartupError(f)))},y=this.timer.setTimeout(()=>{g(Ze(s,this.context.lastReadinessPhase()))},this.firstFrameTimeoutMs);this.cancelFirstFrameWait=S,o.on("encodedVideo",()=>{if(!this.context.isCurrentHelper(o,n)||!this.context.isActive())return;p=!0,this.context.markRunning(),m(a)}),o.on("stderr",(f)=>{if(!this.context.isCurrentHelper(o,n)||!this.context.isActive())return;if(er(f))m(()=>c(Qt(s,this.context.lastReadinessPhase())));else if(de(f))g(this.context.helperFailureFor(f))}),o.on("error",(f)=>{if(!this.context.isCurrentHelper(o,n)||!this.context.isActive())return;if(p){this.context.failIfCurrentHelper(o,n,f);return}g(f)}),o.on("exit",(f)=>{if(!this.context.isCurrentHelper(o,n)||!this.context.isActive())return;let w=Error(this.context.describeHelperExit(f,"exited"));if(p){this.context.failIfCurrentHelper(o,n,w);return}g(w)})})}}var tr=5;function et(o,s){o.on("capability",(n)=>{if(s.isCurrentHelper()&&n===SQ)s.confirmIdleSupport(),s.options.onIdleAttestationSupport?.(!0)}),o.on("idle",()=>{if(s.isCurrentHelper()&&s.isActive()&&s.nativeIdleCapabilityConfirmed())s.options.onSourceIdle?.()}),o.on("captureMetrics",(n)=>{if(s.isCurrentHelper()&&s.isActive())s.options.onDroppedFrames?.(n.droppedFrames),s.setNativeFrameMetrics(n)}),o.on("audio",(n)=>{if(s.isCurrentHelper()&&s.isActive()&&s.options.audioEnabled)s.options.onAudioData?.(n.pcm16le)}),o.on("permission",(n)=>{if(s.isCurrentHelper())s.setRequiredPermission(n)}),o.on("permissionTarget",(n)=>{if(s.isCurrentHelper())s.setRequiredPermissionTarget(n)}),o.on("stderr",(n)=>{if(!s.isCurrentHelper())return;if(n.length>0)if(n.startsWith(M0))rr(n);else{if(s.helperStderrTail().push(n.slice(-2048)),s.helperStderrTail().length>tr)s.helperStderrTail().shift();e.warn(`[IosH264Source] screen-capture-helper stderr: ${n}`)}if(de(n))s.failForHelperLine(n)}),o.on("readiness",(n)=>{if(s.isCurrentHelper()&&s.isActive())s.setLastReadinessPhase(n.phase);e.debug(`[IosH264Source] capture readiness phase=${n.phase} atMs=${n.atMs}${n.detail?` detail=${n.detail}`:""}`)})}function rr(o){try{let s=JSON.parse(o.slice(M0.length));if(s===null||typeof s!=="object"||Array.isArray(s)){e.warn(`[IosH264Source] unrecognised frame-metrics line: ${o}`);return}let n=s;if(typeof n.droppedFrames==="number"&&n.droppedFrames>0||typeof n.frameQueueDepth==="number"&&n.frameQueueDepth!==0){e.warn(`[IosH264Source] screen-capture-helper frame metrics: ${o}`);return}e.debug(`[IosH264Source] screen-capture-helper frame metrics: ${o}`)}catch(s){e.warn(`[IosH264Source] unrecognised frame-metrics line: ${o}; ${r(s)}`)}}import{EventEmitter as ir}from"events";class me extends ir{pool;options;started=!1;attachment=null;attachmentGeneration=0;entryKey=null;constructor(o,s){super();this.pool=o;this.options=s}async start(){if(this.started)throw Error("IOSSimulatorCaptureHelperLease already started");this.started=!0;let o=this.pool.attach(this),s=++this.attachmentGeneration;this.attachment=o;try{await o}catch(n){throw this.started=!1,n}finally{if(this.attachmentGeneration===s)this.attachment=null}}async stop(){if(!this.started)return null;return this.started=!1,this.removeAllListeners(),await this.attachment?.catch(()=>{return}),await this.pool.detach(this),null}async invalidate(){if(!this.started)return;this.started=!1,this.removeAllListeners(),await this.attachment?.catch(()=>{return}),await this.pool.invalidate(this)}requestKeyFrame(){return this.started&&this.pool.requestKeyFrame(this)}get isStarted(){return this.started}forward(o,s){if(this.started)this.emit(o,s)}}var or=45000,tt=5000;class rt{idleTtlMs;timer;createHelper;entries=new Map;transitions=new Map;shutdownTransition=Promise.resolve();constructor(o={}){this.idleTtlMs=o.idleTtlMs??or,this.timer=o.timer??i,this.createHelper=o.createHelper??((s)=>new uv(s))}acquire(o){if(o.target.kind!=="simulator")throw Error("IOSSimulatorCaptureHelperPool only accepts simulator targets.");if(!o.target.encode)throw Error("IOSSimulatorCaptureHelperPool requires H.264 encode settings.");return new me(this,o)}async shutdown(){let o=[...this.transitions.values()],s=this.shutdownTransition.then(async()=>{await Promise.all(o);let n=[...this.entries.values()];this.entries.clear();for(let a of n)this.clearEntryIdleTimer(a),await this.stopHelperWithinDeadline(a).catch((c)=>{e.debug(`[IOSSimulatorCaptureHelperPool] helper shutdown failed: ${c}`)})});this.shutdownTransition=s.catch(()=>{return}),await s}async attach(o){await this.enqueue(J(o.options.target),async()=>{if(!o.isStarted)return;let s=sr(o.options.target,o.options.binaryPath),n=await this.resolveOrCreateEntry(s,o);if(n!==null)this.finalizeAttach(n,o,s)})}async resolveOrCreateEntry(o,s){let n=this.entries.get(o);if(n?.failedStopFailed)this.clearEntryIdleTimer(n),this.entries.delete(o),n=void 0;if(n)return n;let a=J(s.options.target);for(let d of this.entries.values())if(d.key!==o&&d.leases.size===0)if(d.windowID===a)await this.stopIdleEntry(d);else this.enqueueBestEffort(d.windowID,()=>this.stopIdleEntry(d),"idle helper eviction failed");if(!s.isStarted)return null;let c={key:o,windowID:J(s.options.target),helper:this.createHelper(s.options),capabilities:new Set,leases:new Set,idleTimer:null,failed:!1,failedStopFailed:!1,failedStopError:void 0};return this.entries.set(o,c),this.wireHelper(c),c}finalizeAttach(o,s,n){this.clearEntryIdleTimer(o),o.leases.add(s),s.entryKey=n;for(let a of o.capabilities)s.forward("capability",a);if(!o.helper.isRunning)try{o.helper.start()}catch(a){throw o.leases.delete(s),s.entryKey=null,this.entries.delete(n),a}o.helper.requestKeyFrame()}requestKeyFrame(o){let s=o.entryKey;if(!s)return!1;let n=this.entries.get(s);if(!n||!n.leases.has(o))return!1;return n.helper.requestKeyFrame()}async detach(o){let s=o.entryKey;if(o.entryKey=null,!s)return;let n=this.entries.get(s);if(!n)return;if(n.leases.delete(o),n.leases.size!==0)return;this.clearEntryIdleTimer(n),n.idleTimer=this.timer.setTimeout(()=>{n.idleTimer=null,this.enqueueBestEffort(n.windowID,()=>this.stopIdleEntry(n),"idle helper stop failed")},this.idleTtlMs)}async invalidate(o){let s=o.entryKey;if(o.entryKey=null,!s)return;await this.enqueue(J(o.options.target),async()=>{let n=this.entries.get(s);if(!n||!n.leases.has(o))return;if(n.failed){await this.stopFailedEntry(n);return}await this.stopEntry(n)})}wireHelper(o){o.helper.on("idle",(s)=>this.broadcast(o,"idle",s)),o.helper.on("encodedVideo",(s)=>this.broadcast(o,"encodedVideo",s)),o.helper.on("capability",(s)=>{o.capabilities.add(s),this.broadcast(o,"capability",s)}),o.helper.on("permission",(s)=>this.broadcast(o,"permission",s)),o.helper.on("permissionTarget",(s)=>this.broadcast(o,"permissionTarget",s)),o.helper.on("captureMetrics",(s)=>this.broadcast(o,"captureMetrics",s)),o.helper.on("audio",(s)=>this.broadcast(o,"audio",s)),o.helper.on("malformed",(s)=>this.broadcast(o,"malformed",s)),o.helper.on("stderr",(s)=>{if(nr(s)&&this.entries.get(o.key)===o)o.failed=!0,this.enqueueBestEffort(o.windowID,()=>this.stopFailedEntry(o),"failed helper stop failed");this.broadcast(o,"stderr",s)}),o.helper.on("readiness",(s)=>this.broadcast(o,"readiness",s)),o.helper.on("error",(s)=>{if(this.entries.get(o.key)===o)o.failed=!0,this.enqueueBestEffort(o.windowID,()=>this.stopFailedEntry(o),"failed helper stop failed");this.broadcast(o,"error",s)}),o.helper.on("exit",(s)=>{if(this.broadcast(o,"exit",s),this.entries.get(o.key)===o)this.clearEntryIdleTimer(o),this.entries.delete(o.key)})}broadcast(o,s,n){for(let a of o.leases)a.forward(s,n)}async stopIdleEntry(o){if(o.leases.size!==0||this.entries.get(o.key)!==o)return;await this.stopEntry(o)}async stopFailedEntry(o){if(this.entries.get(o.key)!==o)return;if(o.failedStopFailed)throw o.failedStopError;this.clearEntryIdleTimer(o);try{await this.stopHelperWithinDeadline(o)}catch(s){throw o.failedStopFailed=!0,o.failedStopError=s,s}if(this.entries.get(o.key)===o)this.entries.delete(o.key)}async stopEntry(o){if(this.entries.get(o.key)!==o)return;this.clearEntryIdleTimer(o),this.entries.delete(o.key),await this.stopHelperWithinDeadline(o)}async stopHelperWithinDeadline(o){let s=o.helper.stop(),n=!1;s.catch((c)=>{if(n)e.debug(`[IOSSimulatorCaptureHelperPool] helper stop failed after timeout: ${c}`)});let a=Error("Helper stop deadline expired");try{await l(s,{timer:this.timer,timeoutMs:tt,label:"Helper stop",timeoutError:()=>a,onTimeout:()=>{n=!0,e.warn(`[IOSSimulatorCaptureHelperPool] helper stop exceeded ${tt}ms`)}})}catch(c){if(c!==a)throw c}}enqueue(o,s){let n=this.transitions.get(o)??Promise.resolve(),a=Promise.all([n,this.shutdownTransition]).then(s),c=a.catch(()=>{return});return this.transitions.set(o,c),c.then(()=>{if(this.transitions.get(o)===c)this.transitions.delete(o)}),a}enqueueBestEffort(o,s,n){this.enqueue(o,s).catch((a)=>{e.warn(`[IOSSimulatorCaptureHelperPool] ${n}: ${a}`)})}clearEntryIdleTimer(o){if(o.idleTimer)this.timer.clearTimeout(o.idleTimer),o.idleTimer=null}}function sr(o,s){if(o.kind!=="simulator")throw Error("Simulator helper pool received a non-simulator target.");return JSON.stringify({binaryPath:s,windowID:o.windowID,fps:o.fps,audio:o.audio===!0,encode:o.encode})}function J(o){if(o.kind!=="simulator")throw Error("Simulator helper pool received a non-simulator target.");return o.windowID}function nr(o){let s=o.trimStart().toLowerCase();return s.startsWith("error:")||s.includes("warn: no frames received")}var yM=new rt;class H extends t{}import{basename as mr}from"path";var ar=new ee,ur=(o=ar)=>(s,n)=>Fz(o.spawn(s,n,{stdio:["ignore","pipe","pipe"]}),s),it=ur();var ot=0.1,G=Q4,st=15000,cr=h.IOS_SIMULATOR_WINDOW_TIMEOUT_MS.name;function lr(o=process.env){return ei(cr,void 0,{fallback:2000,env:o})}var O=lr(),he=500,nt=500,dr=2,fe=6000,at=35000,pr=Q({initialDelayMs:500,multiplier:2,maxDelayMs:2000});function ut(o){return{maxAttempts:o.runningReconnectMaxAttempts??dr,backoff:Lo(o.runningReconnectBackoff??pr)}}var ct=(o,s,n)=>new Promise((a,c)=>{if(n?.aborted){c(Error(`Command aborted: ${o}`));return}let d=it(o,s),p="",m="",S=!1,g=(f)=>{if(S)return;S=!0,n?.removeEventListener("abort",y),f()},y=()=>{d.kill("SIGTERM"),g(()=>c(Error(`Command aborted: ${o}`)))};d.stdout.on("data",(f)=>{p+=Buffer.isBuffer(f)?f.toString("utf8"):String(f)}),d.stderr.on("data",(f)=>{m+=Buffer.isBuffer(f)?f.toString("utf8"):String(f)}),d.once("error",(f)=>g(()=>c(f))),d.once("exit",(f,w)=>g(()=>a({stdout:p,stderr:m,exitCode:f,signal:w}))),n?.addEventListener("abort",y,{once:!0})});class ve{dependencies;constructor(o){this.dependencies=o}async resolveCaptureTarget(o){if(z({deviceId:this.dependencies.options.device.deviceId})==="simulator")return{kind:"simulator",windowID:await this.resolveSimulatorWindowIdWithDeadline(o),fps:this.dependencies.fps,...this.dependencies.options.audioEnabled===!0?{audio:!0}:{}};return{kind:"device",deviceId:this.dependencies.options.device.deviceId}}async resolveHelperPath(){let o=this.dependencies.helperPath??DS(process.env);if(o)return OS(o,{exists:this.dependencies.helperPathExists,env:{}});let s=await this.dependencies.withPreCaptureDeadline(this.dependencies.screenCaptureHelperProvider.ensure(),at,"Resolving iOS screen-capture-helper");if(s)return s;throw new t(`iOS WebRTC streaming requires a verified screen-capture-helper from the matching GitHub Release. For local development, run swift build in ios/screen-capture and set ${EA} to the resulting absolute path.`)}async resolveSimulatorWindowIdWithDeadline(o){return new Promise((s,n)=>{let a=new AbortController,c=!1,d=(m)=>{if(c)return;c=!0,this.dependencies.timer.clearTimeout(p),m()},p=this.dependencies.timer.setTimeout(()=>{a.abort(),d(()=>n(new t(`Timed out resolving iOS Simulator window for ${this.dependencies.options.device.name} after ${O}ms. Open the Simulator window and verify Screen Recording permission.`)))},O);this.dependencies.simulatorWindowResolver(o,this.dependencies.options.device,this.dependencies.options.audioEnabled===!0,a.signal).then((m)=>d(()=>s(m)),(m)=>d(()=>n(m)))})}async reresolveSimulatorTarget(o,s){if(s.kind!=="simulator")return s;let n;try{n=await this.resolveSimulatorWindowIdWithDeadline(o)}catch(a){throw u(a,"Failed to re-resolve iOS Simulator window for capture retry")}if(n===s.windowID)return s;return e.debug(`[IosH264Source] Simulator windowID changed from ${s.windowID} to ${n} before retry; targeting the freshly-resolved window`),{...s,windowID:n}}}function Se(o){return o.kind==="simulator"?`iOS Simulator window ${o.windowID} at ${o.fps??G} fps`:`iOS device ${o.deviceId??"default"}`}async function lt(o,s,n,{audioEnabled:a,poll:c}){let{signal:d}=c,p=c.timer.now(),m=0;for(;;){m+=1;let S=await hr(o,s,n,{audioEnabled:a,signal:d});if(S.kind==="found")return S.windowID;if(d.aborted||c.timer.now()-p+he>=c.pollBudgetMs)throw new t(`No visible iOS Simulator window matched ${s.name} after ${m} lookup(s) over ${c.timer.now()-p}ms; windows seen: ${S.seen}. Open the simulator window and grant Screen Recording permission if prompted.`);await c.timer.sleep(he)}}async function hr(o,s,n,{audioEnabled:a,signal:c}){let d=await n(o,["--list-simulators"],c);if(d.exitCode!==0){if(ge(d.stderr))throw new om(fr(d.stderr)??ye(o));throw new t(`Unable to list iOS Simulator windows: ${d.stderr.trim()||`exited with code ${d.exitCode}`}`)}let p;try{p=JSON.parse(d.stdout).windows??[]}catch(f){throw u(f,"Unable to parse iOS Simulator window list")}if(a&&p.length>1)throw new t("iOS Simulator audio capture requires exactly one visible Simulator window because ScreenCaptureKit cannot isolate audio to a selected Simulator window. Close other Simulator windows and try again.");let m=s.name.trim().toLowerCase(),S=p.filter((f)=>typeof f.title==="string"&&f.title.trim().length>0),g=S.filter((f)=>vr(f.title,m)),y=g.length>0?g:S.filter((f)=>f.title.toLowerCase().includes(m));if(y.length===1)return{kind:"found",windowID:y[0].windowID};if(y.length===0)return{kind:"missing",seen:`[${p.map((w)=>JSON.stringify(w.title??null)).join(", ")}]`};throw new t(`Multiple iOS Simulator windows matched ${s.name}; close extras or use a more specific device.`)}function ge(o){return o.split(/\r?\n/).some((s)=>{let n=s.trim().toLowerCase();return n===`${Qq} screen-recording`||n.startsWith("error: screen recording permission is required.")||n.includes("the user declined tccs for application, window, display capture")})}function fr(o){for(let s of o.split(/\r?\n/)){let n=s.trim();if(!n.startsWith(N0))continue;let a=n.slice(N0.length).trim();if(a.length>0)return a}return null}function ye(o){let s=mr(o).trim();return s==="screen-capture-helper"||s.length===0?"AutoMobile":s}function vr(o,s){let n=o.trim().toLowerCase();if(n===s)return!0;let[a]=n.split(/\s+[\u2014\u2013-]\s+/,1);return a.trim()===s}function dt(o,s){let n=o.bitrateBps&&o.bitrateBps>0?o.bitrateBps:N(o.quality),a=n!==void 0?{kind:"explicitBps",bps:n}:s==="simulator"?{kind:"bitsPerPixel",bpp:ot}:{kind:"videoToolboxDefault"},c=o.quality?j[o.quality]:void 0;return{codec:"h264",bitrate:a,...c===void 0?{}:{maxLongSide:c}}}class we{options;resolver;nativeFrameMetrics=null;captureKind=null;lastForcedKeyFrameMs=Number.NEGATIVE_INFINITY;startup;getFrameMetrics(){return{native:this.nativeFrameMetrics}}helperPath;fps;createHelper;simulatorWindowResolver;commandRunner;helperPathExists;screenCaptureHelperProvider;timer;firstFrameTimeoutMs;simulatorHelperPool;runningReconnectMaxAttempts;runningReconnectBackoff;helper=null;helperGeneration=0;cancelPreCaptureDeadline=null;encodedCapabilityConfirmed=!1;nativeIdleCapabilityConfirmed=!1;idleSupportReported=!1;teardownPromise=null;helperStderrTail=[];lastReadinessPhase=null;phase="idle";requiredPermission=null;requiredPermissionTarget=null;startupComplete=!1;hasConsumers=!0;deferredHelperFailure=null;invalidateHelperOnStop=!1;reconnectPromise=null;cancelReconnectDelay=null;constructor(o){this.options=o;this.helperPath=o.helperPath,this.fps=o.fps??G,this.createHelper=o.createHelper??((n)=>new uv(n)),this.commandRunner=o.commandRunner??ct,this.helperPathExists=o.helperPathExists,this.screenCaptureHelperProvider=o.screenCaptureHelperProvider??Gc.getInstance(),this.timer=o.timer??i,this.firstFrameTimeoutMs=o.firstFrameTimeoutMs??st,this.simulatorHelperPool=o.simulatorHelperPool??yM;let s=ut(o);this.runningReconnectMaxAttempts=s.maxAttempts,this.runningReconnectBackoff=s.backoff,this.simulatorWindowResolver=o.simulatorWindowResolver??((n,a,c,d)=>lt(n,a,this.commandRunner,{audioEnabled:c,poll:{signal:d,timer:this.timer,pollBudgetMs:Math.max(0,O)}})),this.startup=new pe(this.timer,this.firstFrameTimeoutMs,{generation:()=>this.helperGeneration,lastReadinessPhase:()=>this.lastReadinessPhase,markRunning:()=>{this.phase="running"},isCurrentHelper:(n,a)=>this.isCurrentHelper(n,a),isActive:()=>this.isActive(),helperFailureFor:(n)=>this.helperFailureFor(n),failIfCurrentHelper:(n,a,c)=>this.failIfCurrentHelper(n,a,c),describeHelperExit:(n,a)=>this.describeHelperExit(n,a),encodedStartupError:(n)=>this.encodedStartupError(n)}),this.resolver=new ve({options:this.options,helperPath:this.helperPath,helperPathExists:this.helperPathExists,screenCaptureHelperProvider:this.screenCaptureHelperProvider,fps:this.fps,timer:this.timer,simulatorWindowResolver:this.simulatorWindowResolver,withPreCaptureDeadline:(n,a,c)=>this.withPreCaptureDeadline(n,a,c)})}async start(){if(this.isActive())throw new t("iOS H.264 source already started.");await this.teardownPromise;let o=++this.helperGeneration;if(this.phase="starting",this.startupComplete=!1,this.deferredHelperFailure=null,this.helperStderrTail=[],this.lastReadinessPhase=null,this.nativeFrameMetrics=null,this.lastForcedKeyFrameMs=Number.NEGATIVE_INFINITY,this.encodedCapabilityConfirmed=!1,this.options.size!==void 0)e.warn("[IosH264Source] iOS ignores explicit encoder size; capture encodes at native size capped by the quality preset.");try{if(await this.establishCapture(o),o===this.helperGeneration)this.startupComplete=this.phaseNow()==="running"}catch(s){if(o!==this.helperGeneration)return;throw this.phase="stopping",await this.beginTeardown(),s}}async establishCapture(o){let s=await this.resolver.resolveHelperPath();if(o!==this.helperGeneration)return;let n=await this.resolver.resolveCaptureTarget(s);if(this.captureKind=n.kind,!this.isActive()||o!==this.helperGeneration)return;await this.startCaptureWithSimulatorRetry(s,{...n,encode:dt(this.options,n.kind)})}async stop(){if(this.deferredHelperFailure=null,!this.isActive()){await this.teardownPromise;return}this.phase="stopping",this.helperGeneration++,this.startupComplete=!1,this.cancelPreCaptureDeadline?.(),this.cancelReconnectDelay?.(),this.startup.cancelFirstFrameWait?.(),this.startup.cancelFirstAudioWait?.(),await this.beginTeardown()}async stopStale(o=!0){this.invalidateHelperOnStop=o,await this.stop()}setHasConsumers(o){if(this.hasConsumers=o,o){if(this.deferredHelperFailure)this.queueReconnect(this.deferredHelperFailure)}else this.cancelReconnectDelay?.()}requestKeyFrame(){if(this.phase!=="running"||!this.helper)return!1;let o=this.timer.now();if(o-this.lastForcedKeyFrameMs<nt)return!1;let s=this.helper.requestKeyFrame?.()??!1;if(s)this.lastForcedKeyFrameMs=o,e.debug("[IosH264Source] forced keyframe requested on helper control channel");return s}async withPreCaptureDeadline(o,s,n){let a=new AbortController,c=()=>a.abort(new t(`${n} cancelled by stop.`));this.cancelPreCaptureDeadline=c;let d=this.timer.setTimeout(()=>a.abort(new t(`${n} timed out after ${s}ms.`)),s);try{return await l(o,{timer:this.timer,signal:a.signal,label:n})}finally{if(this.cancelPreCaptureDeadline===c)this.cancelPreCaptureDeadline=null;if(d)this.timer.clearTimeout(d)}}createCaptureHelper(o){if(o.target.kind==="simulator"&&!this.options.createHelper)return this.simulatorHelperPool.acquire(o);return this.createHelper(o)}async startCaptureWithSimulatorRetry(o,s){let n=s.kind==="simulator"&&!this.options.createHelper,a=s;for(let c=0;;c++)try{await this.startCaptureAttempt(o,a);return}catch(d){if(d instanceof H)throw d;if(!n||!(d instanceof A))throw u(d,"Failed to start iOS screen capture");try{await this.helper?.invalidate?.()}catch(p){throw u(p,"Failed to invalidate silent iOS Simulator capture")}if(await this.stopCurrentHelper(),!this.isActive())return;if(c!==0)throw d.finalStartupError(this.requiredPermissionTarget);e.debug(`[IosH264Source] no first frame from pooled Simulator helper for ${Se(a)}; retrying once`),a=await this.reresolveSimulatorTarget(o,a)}}reresolveSimulatorTarget(o,s){return this.resolver.reresolveSimulatorTarget(o,s)}async startCaptureAttempt(o,s){e.info(`[IosH264Source] starting screen-capture-helper for ${Se(s)}`),this.lastReadinessPhase=null,this.requiredPermission=null,this.requiredPermissionTarget=ye(o),this.nativeIdleCapabilityConfirmed=!1,this.idleSupportReported=!1;let n=this.createCaptureHelper({binaryPath:o,target:s});this.helper=n,this.encodedCapabilityConfirmed=!1,this.wireEncodedHelper(n);let a=this.options.audioEnabled?this.startup.waitForFirstAudio(n):null,c=this.startup.waitForFirstEncodedRecord(n,s);try{await Promise.all([c,a,Promise.resolve().then(()=>n.start())])}catch(d){throw this.encodedStartupError(d instanceof Error?d:Error(r(d)))}}encodedStartupError(o){if(this.encodedCapabilityConfirmed||o instanceof om||o instanceof H)return o;return new H(`The screen-capture-helper did not advertise the '${g_}' capability before failing to start encoded capture (${o.message}). Update to the screen-capture-helper from the matching AutoMobile release, or rebuild the local helper and update AUTOMOBILE_IOS_SCREEN_CAPTURE_HELPER.`)}wireEncodedHelper(o){let s=this.helperGeneration;o.on("encodedVideo",(n)=>{if(this.isCurrentHelper(o,s))this.handleEncodedVideo(n)}),o.on("capability",(n)=>{if(this.isCurrentHelper(o,s)&&n===g_)this.encodedCapabilityConfirmed=!0}),o.on("malformed",(n)=>{if(!this.isCurrentHelper(o,s))return;e.warn(`[IosH264Source] malformed encoded record from helper: ${n.reason}; requesting keyframe to recover`),this.requestKeyFrame()}),this.wireHelperDiagnostics(o)}wireHelperDiagnostics(o){let s=this.helperGeneration;et(o,{isCurrentHelper:()=>this.isCurrentHelper(o,s),isActive:()=>this.isActive(),confirmIdleSupport:()=>{this.nativeIdleCapabilityConfirmed=!0,this.idleSupportReported=!0},nativeIdleCapabilityConfirmed:()=>this.nativeIdleCapabilityConfirmed,setRequiredPermission:(n)=>{this.requiredPermission=n},setRequiredPermissionTarget:(n)=>{this.requiredPermissionTarget=n},setLastReadinessPhase:(n)=>{this.lastReadinessPhase=n},helperStderrTail:()=>this.helperStderrTail,failForHelperLine:(n)=>this.failIfCurrentHelper(o,s,this.helperFailureFor(n)),setNativeFrameMetrics:(n)=>{this.nativeFrameMetrics=n,this.options.onFrameMetrics?.(this.getFrameMetrics())},options:this.options})}helperFailureFor(o){return this.requiredPermission==="screen-recording"||ge(o)?new om(this.requiredPermissionTarget??"AutoMobile"):Error(`screen-capture-helper reported an error: ${o}`)}handleEncodedVideo(o){if(!this.isActive())return;this.encodedCapabilityConfirmed=!0,this.reportLegacyIdleSupport(),this.options.onSourceFrame?.(),this.options.onData(o.payload),this.options.onEncodedAccessUnit?.()}reportLegacyIdleSupport(){if(this.captureKind==="simulator"&&!this.idleSupportReported)this.idleSupportReported=!0,this.options.onIdleAttestationSupport?.(!1)}failIfRunning(o){if(this.phase!=="running")return;if(!this.startupComplete||this.runningReconnectMaxAttempts<=0){this.failNow(o);return}if(!this.hasConsumers){this.phase="reconnecting",this.startupComplete=!1,this.beginTeardown(),this.queueReconnect(o);return}this.phase="reconnecting",this.startupComplete=!1,this.queueReconnect(o)}failNow(o){this.startup.rejectFirstAudioWait?.(o),this.phase="stopping",this.startupComplete=!1,this.beginTeardown(),this.options.onError?.(o)}async runReconnect(o){if(e.warn(`[IosH264Source] running-phase capture failure; attempting bounded reconnect (up to ${this.runningReconnectMaxAttempts}): ${o.message}`),await this.beginTeardown(),!this.hasConsumers){this.deferredHelperFailure=o;return}for(let s=1;this.phase==="reconnecting"&&s<=this.runningReconnectMaxAttempts;s++){let n=this.runningReconnectBackoff.delayForAttempt(s);if(!await this.waitForActiveReconnect(n,o))return;this.phase="starting";let a=++this.helperGeneration;try{if(await this.establishCapture(a),a!==this.helperGeneration)return;if(this.phaseNow()==="running"){this.startupComplete=!0,e.info(`[IosH264Source] running-phase reconnect succeeded on attempt ${s}`);return}return}catch(c){if(a!==this.helperGeneration)return;e.warn(`[IosH264Source] reconnect attempt ${s}/${this.runningReconnectMaxAttempts} failed: ${r(c)}`),await this.beginTeardown();let d=this.phaseNow();if(d==="stopping"||d==="idle")return;if(c instanceof H){this.failNow(c);return}this.phase="reconnecting"}}this.finishReconnectExhausted(o)}finishReconnectExhausted(o){if(this.phase!=="reconnecting")return;if(!this.hasConsumers){this.deferredHelperFailure=o;return}this.failNow(o)}async waitForActiveReconnect(o,s){let n=await this.waitReconnectBackoff(o);if(this.phase==="reconnecting"&&(!this.hasConsumers||n))this.deferredHelperFailure=s;return!n&&this.hasConsumers&&this.phase==="reconnecting"}queueReconnect(o){if(this.deferredHelperFailure=o,!this.hasConsumers||this.phase!=="reconnecting"||this.reconnectPromise)return;this.deferredHelperFailure=null,this.reconnectPromise=this.runReconnect(o).finally(()=>{if(this.reconnectPromise=null,this.deferredHelperFailure&&this.hasConsumers)this.queueReconnect(this.deferredHelperFailure)})}waitReconnectBackoff(o){return new Promise((s)=>{let n=this.timer.setTimeout(()=>{this.cancelReconnectDelay=null,s(!1)},o);this.cancelReconnectDelay=()=>{this.timer.clearTimeout(n),this.cancelReconnectDelay=null,s(!0)}})}describeHelperExit(o,s){let n=this.helperStderrTail.length===0?"":`; last stderr: ${this.helperStderrTail.join(" | ")}`;return`screen-capture-helper ${s} (code=${o.code}, signal=${o.signal})${n}`}failIfCurrentHelper(o,s,n){if(!this.isCurrentHelper(o,s))return;this.failIfRunning(n)}isCurrentHelper(o,s){return this.helper===o&&this.helperGeneration===s}isActive(){return this.phase==="starting"||this.phase==="running"||this.phase==="reconnecting"}phaseNow(){return this.phase}beginTeardown(){return this.teardownPromise??=this.teardown().finally(()=>{if(this.teardownPromise=null,this.phase==="stopping")this.phase="idle"}),this.teardownPromise}async teardown(){await this.stopCurrentHelper()}async stopCurrentHelper(){this.startup.cancelFirstFrameWait?.(),this.startup.cancelFirstFrameWait=null,this.startup.cancelFirstAudioWait?.(),this.startup.cancelFirstAudioWait=null,this.startup.rejectFirstAudioWait=null;let o=this.helper;if(this.helper=null,!o)return;let s=(this.invalidateHelperOnStop&&o.invalidate?o.invalidate():o.stop()).then(()=>!0,(a)=>(e.debug(`[IosH264Source] helper stop failed: ${a}`),!0)),n=Symbol("helper stop timeout");try{await l(s,{timer:this.timer,timeoutMs:fe,label:"iOS capture helper stop",timeoutError:()=>n})}catch(a){if(a!==n)throw a;e.warn(`[IosH264Source] helper stop exceeded ${fe}ms; continuing teardown`)}}}var vM=(o,s)=>{if(o.device.platform==="android")return Qe(o,s);if(o.device.platform==="ios"){if(o.audioEnabled&&z({deviceId:o.device.deviceId})!=="simulator")throw new t("WebRTC playback audio capture is available only for iOS Simulator targets; public iOS APIs cannot capture playback audio from a physical device.");return new we(o)}throw new t(`WebRTC streaming does not support ${o.device.platform} devices.`)};
3
+ export{qd,Vd,_s,Wf,Ry,NE,gM,lG,ic,j8,om,yM,vM,K8,G8,uG,bM};
4
4
 
5
- //# debugId=606B6BD478E3D1A164756E2164756E21
5
+ //# debugId=7EA27DD73354005764756E2164756E21
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ import{R4,P4}from"./chunk-hpzxdp1k.js";import"./chunk-tx4f0h9c.js";import"./chunk-8nj68t1v.js";import"./chunk-yqfz4vts.js";import"./chunk-jvstjnqm.js";import"./chunk-xezekwwn.js";import"./chunk-z8tvs8nk.js";import"./chunk-xn9m3xc2.js";import"./chunk-sqdrxjw5.js";import"./chunk-6hrj3jt6.js";import"./chunk-c27yv1dx.js";import"./chunk-cqhw4yr6.js";import"./chunk-vdqag3fj.js";import"./chunk-e0870khr.js";import"./chunk-4vxeq0vd.js";import"./chunk-9xgnrv5w.js";import"./chunk-cb1ga9rd.js";import"./chunk-sfqe223f.js";import"./chunk-r6pvhj33.js";import"./chunk-w5yypt53.js";import"./chunk-aewdveet.js";import"./chunk-r0vp315h.js";import"./chunk-5wk1t46v.js";export{R4 as printUnknownDaemonCommand,P4 as runDaemonCommand};
3
+
4
+ //# debugId=2908C1C41B55746864756E2164756E21