From 2478b4899726688eda1f0b82921e24d4413d108c Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Fri, 12 May 2023 11:37:11 +0000 Subject: [PATCH 001/516] Rollback of https://github.com/androidx/media/commit/925aa34e13e87deb03f68f790b46a0975f1045fb *** Original commit *** Rollback of https://github.com/androidx/media/commit/65d5132f76b02e8b2c5dbab4b62c867ffc4cea41 *** Original commit *** Create InAppMuxer in transformer To use the InAppMuxer, the client needs to pass InAppMuxer Factory. *** *** PiperOrigin-RevId: 531470081 (cherry picked from commit 867355fdc55722a5fde9f22cd207c5fb57ba067d) --- .../java/androidx/media3/muxer/Boxes.java | 7 +- .../java/androidx/media3/muxer/Mp4Muxer.java | 10 ++ .../java/androidx/media3/muxer/Mp4Writer.java | 20 ++- .../media/mp4/h265_with_metadata_track.mp4 | Bin 0 -> 682941 bytes libraries/transformer/build.gradle | 1 + .../media3/transformer/AndroidTestUtil.java | 10 ++ ...TransformerWithInAppMuxerEndToEndTest.java | 102 ++++++++++++ ...TransformerWithInAppMuxerEndToEndTest.java | 71 +++++++++ .../media3/transformer/FrameworkMuxer.java | 13 +- .../media3/transformer/InAppMuxer.java | 146 ++++++++++++++++++ .../media3/transformer/TransformerUtil.java | 13 ++ 11 files changed, 374 insertions(+), 19 deletions(-) create mode 100644 libraries/test_data/src/test/assets/media/mp4/h265_with_metadata_track.mp4 create mode 100644 libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerWithInAppMuxerEndToEndTest.java create mode 100644 libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerWithInAppMuxerEndToEndTest.java create mode 100644 libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java index 31e945a86e0..ca939c880cb 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java @@ -562,7 +562,7 @@ public static ByteBuffer videoSampleEntry(Format format) { contents.put(paspBox()); // Put in a "colr" box if any of the three color format parameters has a non-default (0) value. - // TODO(b/278101856): Only null check should be enough once we disallow invalid values. + // TODO: b/278101856 - Only null check should be enough once we disallow invalid values. if (format.colorInfo != null && (format.colorInfo.colorSpace != 0 || format.colorInfo.colorTransfer != 0 @@ -588,6 +588,7 @@ public static ByteBuffer videoSampleEntry(Format format) { * @param lastDurationBehavior The behaviour for the last sample duration. * @return A list of all the sample durations. */ + // TODO: b/280084657 - Add support for setting last sample duration. public static List durationsVuForStts( List writtenSamples, long minInputPresentationTimestampUs, @@ -671,7 +672,7 @@ public static ByteBuffer stsz(List writtenSamples) { contents.putInt(0x0); // version and flags. - // TODO(b/270583563): Consider optimizing for identically-sized samples. + // TODO: b/270583563 - Consider optimizing for identically-sized samples. // sample_size; specifying the default sample size. Set to zero to indicate that the samples // have different sizes and they are stored in the sample size table. contents.putInt(0); @@ -697,7 +698,7 @@ public static ByteBuffer stsc(List writtenChunkSampleCounts) { int currentChunk = 1; - // TODO(b/270583563): Consider optimizing for consecutive chunks having same number of samples. + // TODO: b/270583563 - Consider optimizing for consecutive chunks having same number of samples. for (int i = 0; i < writtenChunkSampleCounts.size(); i++) { int samplesInChunk = writtenChunkSampleCounts.get(i); contents.putInt(currentChunk); // first_chunk. diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java index f9dbb9e8ecf..ffe3cd6a2cc 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java @@ -23,7 +23,9 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; +import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.FileOutputStream; import java.io.IOException; @@ -140,6 +142,14 @@ public Mp4Muxer build() { } } + /** A list of supported video sample mime types. */ + public static final ImmutableList SUPPORTED_VIDEO_SAMPLE_MIME_TYPES = + ImmutableList.of(MimeTypes.VIDEO_H264, MimeTypes.VIDEO_H265, MimeTypes.VIDEO_AV1); + + /** A list of supported audio sample mime types. */ + public static final ImmutableList SUPPORTED_AUDIO_SAMPLE_MIME_TYPES = + ImmutableList.of(MimeTypes.AUDIO_AAC); + private final Mp4Writer mp4Writer; private final MetadataCollector metadataCollector; diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java index ec77063918d..dcfb85eeffd 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java @@ -319,7 +319,7 @@ private void updateMdatSize() throws IOException { private void doInterleave() throws IOException { for (int i = 0; i < tracks.size(); i++) { Track track = tracks.get(i); - // TODO(b/270583563): check if we need to consider the global timestamp instead. + // TODO: b/270583563 - check if we need to consider the global timestamp instead. if (track.pendingSamples.size() > 2) { BufferInfo firstSampleInfo = checkNotNull(track.pendingSamples.peekFirst()).first; BufferInfo lastSampleInfo = checkNotNull(track.pendingSamples.peekLast()).first; @@ -377,9 +377,21 @@ public void writeSampleData(ByteBuffer byteBuffer, BufferInfo bufferInfo) throws } // Skip empty samples. - // TODO(b/279931840): Confirm whether muxer should throw when writing empty samples. + // TODO: b/279931840 - Confirm whether muxer should throw when writing empty samples. if (byteBuffer.remaining() > 0) { - pendingSamples.addLast(Pair.create(bufferInfo, byteBuffer)); + // Copy sample data and release the original buffer. + ByteBuffer byteBufferCopy = ByteBuffer.allocateDirect(byteBuffer.remaining()); + byteBufferCopy.put(byteBuffer); + byteBufferCopy.rewind(); + + BufferInfo bufferInfoCopy = new BufferInfo(); + bufferInfoCopy.set( + /* newOffset= */ byteBufferCopy.position(), + /* newSize= */ byteBufferCopy.remaining(), + bufferInfo.presentationTimeUs, + bufferInfo.flags); + + pendingSamples.addLast(Pair.create(bufferInfoCopy, byteBufferCopy)); doInterleave(); } } @@ -387,7 +399,7 @@ public void writeSampleData(ByteBuffer byteBuffer, BufferInfo bufferInfo) throws @Override public int videoUnitTimebase() { return MimeTypes.isAudio(format.sampleMimeType) - ? 48_000 // TODO(b/270583563): Update these with actual values from mediaFormat. + ? 48_000 // TODO: b/270583563 - Update these with actual values from mediaFormat. : 90_000; } diff --git a/libraries/test_data/src/test/assets/media/mp4/h265_with_metadata_track.mp4 b/libraries/test_data/src/test/assets/media/mp4/h265_with_metadata_track.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..cc2ebc2964b9dd38ca017095f4ae135d5dc72b74 GIT binary patch literal 682941 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

tq|9u#vmf~7Z1XpEyAJAO=@RxA{A>TKbr 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

wvlUcdiuL{ zx3C1+`9BwU%66oHu?PtuLE?8LE%Wj?7XTv}wnTGW=qi)UC01tMca%} z5kFZUs$Y+o!rvDt0+DC&2fn7-7AJr4lpKCjc?Dyl#H58P$!=SG`hpX8B9Nd`@VvoA zMLIO>?XiXVz8tE11PIqh6_#TKA1&fV7bTgu3ay%8`Gt;b5zrbcYU!7aK*uNm}I%PP9AQfhnb+N%wNMk!094~R)UW6qAa zr5D4p7|xh*>mnZ_+?!KPtqB>1G}_TF@PO<~b>vJCV$n>y0d6_!+Z6G>{{cOAz--3bD;UD88TRG2Urb!5i&FP|uFJE_%7sB@X={ z1Lme#P6jY;A!2bEnZhbRmn<;VqavY?(k9hjkR4RuZZ_HbtdaM6eY2a?ewQ`7!xKcQ zEV7+mD^DiZa=aW)RemPpDf8R5SgRbV=5k)s>M?U-<%TQ@Ir{fM-@uGD*`|#+4KKL! zuExAY;xWEeLYsRhIuV5qht9@Wj%CWo1s2+W@as(5+=$|IVD_)0!v9OkfBIsu3W0!y z(8$c$lGzUbM%g?zSt9PhJMN8#jU~*M_$Ft1wAEKBwz)U!&Sc#yJ)yJ6Aam65)baNb zv?>zFulT>_x1bOjSTF&oGHOPG;eQb( z2=fb`!v)Atp|td*`lL+w8nAb+VQA2C7@b$$@M8^W_jUjcV(XI7X@sy2*5$pMojS#!Sygm z{r0f`njj*KPfhMHjt?%+0kFf~omWwUSJ4L0D*uyW<6kj?{dTjqL_H~add-hxgst@a z{AA-3$WThUY6E|fQz}(=&otk<*UXh-5T?Y=59D(}hl!PD^k0FT9nj#fd!K%=L7D{BZr`JLyKkMU@DgyjC=dI}BxIg&^kUVN0o-R{y(KR@8gCPh#L{ z!2_HL;@R6`V2EW7ttSIF^=6}vU~-SDOkQ0QUU(iGM0eV{#o@X~st-BIMY~9D8cjJV z>bLbO*TlW5zMvGhzMn74upmEk4Ze*dTe5>sC+ds3D+n6v4P25! zxhy_ECl9?)ZPF4gzs0{BkCX$kA93E5++6y|vbh(ff1Zp-x*XZ?)|dYVnsOwWI=?>z z)KDNp<-LFgESN?sM^-)P@RpZG30)yP`rU#8D}(n0-SzoE3Lr}xaVGA0&#oiSw0Y@` zWA~B~ZoOH#E0JV_Hb;^iEn;PtakQ$Lxx4INzAa@|8%dUowO}w$D!nrg08@18q2dSE zg)JqYGtLfTH6~os{bn5c+*9-TuK1aNFD8a8qc(b9MS7B}V{R=;0rXZ`{WhARu(0%} zKquToqG0NF!{(T!Y3`T&MXI=A8kmjUV!N8{_D=rMd#Wm=K~6-%AWc%`E? z&I)LKkTO0B!lll)|7^|CW*{bhy{lr)V2y6LGiC8*rs_kKwN-KNqwCuQ+h$l3-OWtZ z$!k+_4H4<>1oZbrG!`4oE``$2X$1BAlqq;YU0icNtKq<^MWvyIVL45>e~Loz+SC-8 zHiArmlh2sm2u797i7DZXp#Ty<#f|fZvXpC?-gzwh#>fO636&I`OgscsJJy5&lq=vV zj1=jt;0ZGrev@gKh2p9|%&rfanBwd&6vD5sNtYxdyO|zjfnu#VnkUDd-j`4zm}xev z#DBn}IJr4axcx#sFUATX=XFc+AD&~Hy9$$ZL$l6L?tA}QtgK+-GR!_5Lh&{jGe3L^ z*apQ|Fti};ldR51qujbKnZ^MnOAHbcsNLre`ucaX=l3{OwDM{edQR z69k3dMaZ(qFV5P4)ql{dm-O4;RzAS|PF}~&itypb3`7ORmsHmJyu+W2WE2o5_%m%{|kGu zx7r$;HCq>`1wSo~tzAT4O&vhmhtMt^d}dIZhe*3MJbX;9#e%2aAJMWi0R@!uFpUovKsPE&PqrnI8H6!z3j!x^m~CE6z}gsjct@dAroOgV4v_ZBrEa`>9mf`Z)F7nT+~ z1~qFj2{ax<4ZI6dsLp?@Zg9iN%*PoW#P75d#JC^1o!!1~aQ|7HdV5i`Xd0PY5n46k z5Yh;&F6}Z;zh}r&znqbp#;$^iSOgkGV&D7OSGd*|d6#2nR zJm%f-%VTxa*us$V*kFNUoPA7VOs2L83Sic6izkr?jR~HHsHXS@=kL^iC8pe1k}Ps7 znRwE^imv|uRj?JBXi`zrn;N;_ZDg(c^DR1`9VYT=X%(cTrV~Vh z(&SE{?zI=S8VCe=^hd`49A6y&?^uo16MeDFYi`f*hbT3Fi}ryw#W$FNeBq>?pQmd0 zh|17Xu=fTawGspg=u7B+#EQ_GOl6G*22~cLg91O@CGLt`->!;hV?F%_QOI+jkVTbS zfItr+Qs2EcVM$MhiXppel4aWQh5}^}dn!ln?R2Yay@%AS&5!svvXeWJpW`slM0l<< zSw|tAeakfwX%CdWbm2nfEbSM54`F87)zb9L+h^ppD2ZK^;-Y|83g3gYzqgRW#phI> z#e!?G2=OLH$k}I^*%s!B4Q)8rsw?>yjYsMiF9Sx34#&t{2ir?bOkEvXW>u$_0eO4- z(Q=_}RKWoBJ^jcE#kUYL@+|&}8hym!96PZam&~`)vH>s@3nBRls0<_SZELTXICoqS z(1c#kNzA#`c54B?R4Czz$d|cQf;1NmeyK}JWjidGwxj_8S#$?frrFG@Utu^wxA}}v zoSgMz#R3&GF97_XC|$3M4f6MwY!^Bd-IUX&I~w-1=)-e-A{dyjir6%-hzt@xr=H8z zrE`wX3E0heoLAvHrG=A(4q3qp=+vIolfsH+to=8;msMxlt55AnZX0G@hME5a&US)|2<)V)iTj)2fMTNav zIG+EVz+$FVAcF_gQ?%AkYu%ZG)m&vwAp^dD8s?~~|6bMhs@OYb?M@UjPd&2a-yEna z&31%tGZ_&B4<#*Yt|5;0c(NH6QYtelLN?D~bv5B-J5Gt^*_l}Z9bORh^GGkI(n$d2 z=e6j_uo`>lVLPl%N71mEJNPTRXQ~B(er{T>yp0yzc_y_NPhvQTw`?91qSTuGMdqEX zVQf*QL5sF#3fMU6u2ZU>sxuh^3q50|xWh9OC6(655T@#dJEH-CMnhaFH95O1s0)|PAalNyRIgA5dM z4H35D_Wch3^xUmNe~>W8LC>D82i4!jU!ZtaqMbmuTj6MBemq?BN{PQP5Ae8JnXO^v zJk(l~0UHL$8u{l>R~k%1gAs3-0N(a|geF=$kM87WQRyWLxh>6(Tsgl-e2X;tU%x+F zSLSaun3Y2;xu&Pa6>v2w0_gyvcB3*;l=v|~FV!-$D;Sqz=hwtyZSDp?y}YtGSOUw# zKZ&C1{0WP1dx>FFOay=-zbp(U*t9B(=o=Hedmuj%fx8=nHGCkNOSH-|aBC1e3W zct$5|s4PWq8G>8!#*X*zZSop@CEMS2_$`0TUD`Qd1-TYjq5Zg=B8af}3CRS=OpoLU z2W?}WNwDKCx#WB$24`N0Y4VU{Aa0jSOgI-XaUsEPoi3RGmj<=G)}Nlqf74jMM4SM1 zfl(<-O1Nmrl*S;~{5U}^qu#;OF&l>OX=}iJ9tU`2F8}y<*9t)3)BUCajx%h?x6NizES0V{) zm|-Ev-%oc!{%~gNI#%pECH2z5y+~V z@V!4Qsof^hL-C!e62k5mS5Y?q6&}7KKbr@w!;Nt;<`2vOw`w}6BTQA_{dL)W(mb_!Oce!{q~U`2_PZZ9$o;#@ZL8P zb9y}Vaa_mceV6@v8RB3^_@*W)Hdyt2_Egot4eI^n)R>+IJ)^-3n5X-51zy?0 z)BjGLdP=q1tbuvM8AGynh_)IPO#m&pq1LP%km83L3J?BJ5CR)-u`tZMRFCe>Y2 zkBKL=;;jl-z76PNZ=!f$Ts?{`F>6N$9Kcu{en~3qZ$l>0D$Buc75NY08UNB1@?f2P zr1dI5Udu5iaSTS7m|laB&BYRq8t<X*Lnj_}wVHMxSt3 zbS&CV`}&@Gb%{^#H6!@-8aK!RjqcOhs7N_4zAoq?24wUOXo>l(UjK1SK<8h%__#1uuh!K75!0b_yG+Xgsjt`d>Uwm=g(vrkKTmbPgX% zX1X-C^+R5oqZ<`5b%ktyZ@G4^T!4G=7nrVKTc$4p?F!S$+|s7&AF5TEznoEHRRdPSno^BWZ@wx7I-r{ovk>CoWz=O`Mg5)4kv*#x zbY21f?A`dQZM)i9QjlqWJjrR(&8o4`QqvK|GRwO9L+FbuEF|`hQT~bq8Sv0#2L_$D zsw{OX4tyGGz{kA1ke=TII!{HrsYK zAt0N~hgq0gmbsuM?B~1(O-Lt|rcW>yo2J!r235#`VV#uh1%l+Pf-?+i^?1^TW?XVN z5WZtgE{2MFdC*nFDI=X3-SiCuHYu-+i95)}1z@+e!1}`N7%70I3k+D?>~A{wVLSzY z7IahY&PbGL`y($&;xhhyjwX*xwS|tP{*{m@?41?on%$uDlGfePg24z_1SBicu3KCBowy*V`XDnxzUj0i|7- z3jv%*G@LwR@hlvSA7UZECJmlciKul`T`9Q->{p4^Ul1d$#fYoChC{#B?(!SW!yzZ+ zX(6i%w>|D%C(1A?7%v>*bJTqKcx#^e=boZ2S8T7h8p98U00czYP=Sx1 zUP3kWeC-coO3J}dAf+El{te?Wm)LOfI!SPeV{!v*A_JJUfaPU|JTswhFzHRyf$_yd z^ze{@z0mLEXx^|K^^dV3go4x%#4X+m(-g9*PsxhmR%& zRaKlqSqVR^iR8+IWqH?5r<(e3AhY`WC50(cSgePUSJ>~E%V#7y;12NiuISHMFNCE0p2aQ>ci3;-d=P*eDC z-mk%e<4IZ+ya{JXdJ@l%G6ECC8nct)KRP-f7}AgmAfLVWfG6YV?e9=&<8U5<3DcIy zYs_$t1v3qqr+$O*Y$o01Xy9j+(A5HjQN=1MgwM!zhXSPA{^fyBO9PcW_X#)M_!TDP zDBeg&zZca=OBc`|x(8UWi+%^D7x0e<|J=obCWWO^dlyg39FF?4>3fEgwGWXfqKG|m zo0U(~o?B`qJe}IHvOc6P^r`FfcyQo9wah-<^sQ5+H{h&Vt#XG}Lr8Jq0E`Y6&4Wll zPR37_qrhU;Ox7K&(H+t1nxgg%AI%dC93lIr5kEZYj-z4^q16)6bbMYTG(MnjBt<1* zqQP2Eb(i*q3?}`IR}83Ra5!%E+{b@i%+B?B;2ET$wi`4j-+xpwAF_$u#;89LL;1uU z;8IP&1$^KzqkLluu$y6M$~^J_xl16cAw5{CJ=F6qls0t%kU7eihXNqk+Rh8@|Lk)#wRkQc!#K!%S9F`Qok?C_Qa&m$#DT!43 z@`u$0A2teyNtp?2GHg*!!JFQQ9j+X^$E`IJgi=ac3z0;R=JW|p{l+CN@Sj82Tuw@y zXY^n)EjXb%9KNU|ouOAud_OZq$x?`yjuyEMIy3lwaXl$h=0 z2!}QTN^&gJXH6nbiCHJajeYwtq{APW5uv%WJ3})}s=?3Sny1UmtboKnT2YkA<$gJ2 zF-$>GXoe7D^^xSN@pmjZi{*InQH@h*lnv3);!X-kX2{69k&TRuo_(D`-;gd;HsNW z1t?yWQ`IOForW3M5-6CDsgp%M!eyB93*d)lfKf)J6dX$%!>nU*p+0FDTTMaa335Rd zThr=WkKR-R8^WnqqdQJ-%a#0{1wDOZz{tBh3ZrfC2tf?nc75JOc3k*O@Kn~IQeznn z?WAf2qAGQS(iPwl7&(3I9L3x!Oh#VRreL8)7&=0Dm~Lb&Q0W>N?8F38c}c-l`xU9K zLrJ&;ZyBJHq-!>&oh89o(z2y-YgUEv@a}bko6d~k0!{->k&3q#+T6CPTsc;1;(Tfm zPv;W2$s~On1amM2Qic236wI<9Gwr=QRL zWcLDI$UFRt3`!1UVZ)h577n@JVMOh{7Otx;T3m-ulg`+CGI>m7JZ5Y>s}-Wz&1c*3 zYy1WdA+9QTHtXs9*F}zPVjCmu(s2T}5^7Z1+@IiIPO&6cGy-#Z$O1lx?cMML0lH$0%rgq zvkK6%uOR(gFu}87K(B*_U8Hkg)2_eX$$6L&8B$x<11@LJ&d+>MPPx->l&rL?j;LkA zxN3{xLmeDG?~~JXL(7J)NwU$Qb`R z=I5R(VKBT!&^EPh1=C7{Apl=AW*BQY2WAKJ$H90HqKh32Uc0BlhvF|H%Q1JQyUmgPoz_o(otI*$2Dvm zR@O20hj1edae(o!T-6_FY;1>NB`{>=X#T%j82N(zepY!mvj@|#2I6auXffMyJeBF{ z?YmBK9EYBW-=AFkKCx|p7Et=xrYbKI%PYYI1o7P#GY3BsIw2+$HVE%YnLZ#y%NYI+ zL&gFdcUl3w5JQc&&wSMy$PFt`9xC7K^xq{vmVu`ZSE%Cy+sPb%4zp+VQ-|Tt|4v6` zl!fw?LCeUA-o5&Q@pSlE_ajKRoQYou7KVIU7ya5n%5%Uyy}xRVy;*gODT}HDzhHdB zj?6hk;|dEVOgD$%FO^)%-5%J)=FD}v_4@J=Izf*~iWW{r`N4@WGpHpwxWcWvto^X< z*G2K_KQqc%8@9lNn~)OWU(s6_{h%{2x6+p)MZYkkSqJRH148}ac0;AmtHB8ryjbg` z)G^0t)sa4Q{FkwK@SL73AkVq*MU%N&I-C(5413WyTV6I$MlX2khaRXk-_ZeRhS9j( z_M(9i;y78x6DZz+$c_#y#eKxX$lx9Qo9OXPVU(nyaN6%H3{zKK1(uylGUm_kf5 zI**qCM;Z7==Hb`^;&iE2yc+Jg`h(AAv3sxI>@XMJjC9dKypfaMwn~;t0h%+=GZ@GLQ(#Gb-64(N# zX*NVn7wvmO6@m>_!Xuph6GZsS?#dSbJ60&-8_V(HR+=2jCTuXUWGGGj4`L{2X=0A3 zHgmYa7H^+tSaXk>c3H=a0Q`1-eNBbMJTjgvjt|Q@WiF$n^WaYv@K&j$8{fJI?nw!U zFL929k@<~wbv(Q+ajJO+z@eeDX6d*6T>l7|BdkG-k?NJEH*xB?*MLD$r8d{~9w%oP zTHs^lK8!7j!N+d%3*&5`~nlF!Jf5;zRK1fDPR#Hz;{_3vgO1P5y{Un;j+KMqz%?r zNAI*P>D!@YsL0T$XOf)+C}~U!r{^_f7y`XW-Kj`zl}(iKS}5J_7$LpD0f&Z3(diyW zlH-p3-Z>3tD;l9sv3U~3W!^4JqkUGU$bI5a7UK<=Iv{KavHBT4#skT1BH*DCyNiAO zXct>Kc12jhLDwE`%@=!c8tc)=QU_ZI8$}zb)?f0S2KfPbXH(?*d5r z1_((EW~==D=`X#gU#f*qFkkw^*%hSW;*NPs-&~W-%M!4~ErAfagtm&TF##-~h2f2D zCmiZL==&G6qU(V;WIVV+qE9|Udd4;XMrMlYO3x&`&{FC+theI{B*}Sqga7RHNP&2u z!JLO^S4gSYWA(V|_)bQx$Ow_VxwFh^j?*z7eWm@O87*zEHUC+XzQYE*}Zm_nvdx(_vyS4lHdg>?(L=J*;bA6}N0{k{c%>2Vr~numEpZB)}o< zHX1kKQ4<)y=`AtHEC+1~M1#ii0Av$gj)9H;SlJ63YXd~{-57@QBo~s!dQv5jEzJevRLrW2>0R2e479{qP#((LuEG{lAh@@5|;kk{3 z426p#!Twl5{*&8^D?__8Ql&Q!y0JQ=ULpr%As9k3sl#L|k;RYeYXS8YZ~G|3zDNm8#XW~fuo&Bc0f#}6-cPp4`HPbceYeOt++CHYh;`TpK@-2rQKKMKFp zjhqxXw{Qqj}Os;?vRBtYZm~dRNj*x;jA%b*i{C$#tYrWnbct5ziD0w zmzKl1C-qeMiZ0PM)GR`yiqHWgkQF}d>>F8_K>^uI;0qH5=4oV7{-h(L&1Y})*5^KA z5UGG1K7~@}(Dr=f`NIr8{T`1au!W;Bop4Dy)Iq1N!Go%|E+7(Tb4n?!_Eu?r8F^r! zn`#E*!cHaPrR<^W#CemMZOyKFGm!yA!Ox=H z#Mcek`=OK#2O0Mx64`6=J9)|S zqHXSS3M?x_07S2u4x?meSQao4xu)pu}KrXOD>H9?I zW?EaY!?&G8<2}iQ!GsjgixxB6#j+RQGw2FZ!eR{xxU?BuSpW+O{GFW4Y_G=U!8c8B z)CqVjlQ~gFFe2H5>Y1S{LzS<9cfWogpT2Mj5$gde6q2ftI*jZJxA+5iAFZyzzXL>)M4l*`qMeU zPjNhBeN@nGCVhO9I$_*Y;lqp8h9nare$7%;vWVyjP_`61`8}8yw6$Gj;s3%g9h2nC zFZbPHyUf8Ts)kO2>fPlv!7da*!qo@w!-j#YUpX@b)VXw~`y~^L4$CQDa7`$z9K63j zw^+Qo5a5X?pn-^=U_X)OO0CiaQ?Dz;AMMlDlR}X35TK#4novB;cy0nkF6{i1kP9~G ze77fnxG2T>{Ei7>gBYD%)4_up`Zwa=<^$|9X#e&i-`ot(;E$rl1H2p@96S0^cr*Hh z`&8NMLtd&O*Yeu$XB&iLJ>HrR?tr`mB zrRsFln=fgQl_>Z^TiAV#4a_e9_W6Ru&0|qJ@2{E7KSw^qBu41d9RW2p+aKp)3U5x zI8!SEbjD{G{{{L>n~qfvsR*6(mU7^wxGkXXa<8x)pTddgi44esEH~m-(1?d#0>QRW z;f<(3xF1lEpuMEVnPh+J0lIojvk#jS+W_OaU{s5_c^+ehIYAAnRI3l&tIG5E-#vsFD?8uTon%Bg;*}9>xFc2lt4dO!Oz^+|rYq@a?uDn9ej3JK zHYc$_@|VX5IIK|r6oIPk+@Fjgd$dh8OmJ!M3(I-Xp7#a>u#jfMWiphw-_8hTld7Q3 zY~$tnK=E$#*R~&7#Wf zLc?ZnY+9mH543-!0qgrND~z3!@-v!{h5&wVcYe@VkaJXv#P!NrOPFu~!mL<&TsLy~ zWv%MjuGNp4mnL2Lg{7kqXgY!#C7cUx zqa`rTU!{x&gumM4%+n*ugzeFsyl6n-KiR_W5u4*fGNy|$B~jUnuxntwiv?&)XQMzg z%|rS+UEbLh&HfX={6qxldb8jqRc!YjfeHlcbfNR-8Hp_YNLhLH5*rM- z7+u|?HHdc@ZCNf?m3BjZhvoL-NhZZ79k5`dLe_V2Mn-ESRj9CNn6=&KK11+2v{RAM zWq*PG<7bJ%+skf_NB0>L2nLvb;(z#m7E#hgM0C^do9G)}0t-BP+dbAtzAbXR-HdXR zEx#POsaKxg{w^@_+KFgk=*OiW9Vw)IlBY+35m@{RF5>NjOi};%Uc<|8oTQ_WfYYK{ z`NS%mNEhY)iS2HhU3kmswX;4uziHo$tP1$swT%resmI1xd?e$m?c026&|WMLhGQkN z4>Vh>QT~0Xx1)SrPkCFlyn%rCJkhyz<+c;jfaEJD8Eil{NZqU3<0J*-xL#}1%^eu; z1qx}oHfico1bUJJ2Eh-5NR2VLtcfDwb6V4qS5jYjD83zXWjv%tW6~+&KG-`3Gm>Ro zo^FZ1Zs3m8t{LvrC*>!RdX1sDsl0vFl?N#URR-88lUNZq+E62g!J}g0*h08w#m^O{ zqDa{@35MO<^(B?=14QtgDWrB-=F;9gfv`43$w}3nW8*c)bDS*|2|?zMzTzf49K)Dh zuS)9@ZK7#a%k^#*9-<5dw0#NuS3rIY38n?N^e>v>EH07Pd`$8sx*NtemE`5Pn9!MJX zQY|r&xes!f9T7RTo3a>;Mtc`^NhqAOMd1{kGvcDw_ey|(MZdT=&7lUt)7JFoZ)cif z-aeU-WrVe*Y7+kwE*$moSf1i^#yO5owyb06eljd^h_g|e05w3$zfi${B6RCkzL(R{ zC)+^;6xi+NBFO=tS$@FPy_tR)Fpy&+*(}&g0?#v8XkG9LCjd{OJX@SxUKy=gPD4hC zLs8Xkb_~OJbfSr&w_hA%PeoU?Ph5r1UONCj3b^2f|7?BH|AIe-8A?Tvw>o8#k4l>pR%S zAHr2nge90ru9D(Mn|p7$Nxjfj2eto3|~=LJCioQyzWQ;g~wP&yCf48&CSmPTXF= zS2v5a`NxvMGdA!RTPMpcs<{bAX`cF)u`P6rR|14QAe?sTXa}cds!R^V4e*?oDo?~p zGkas-fgY@_3a!ij;IM~7jjXCEvw(b4(5eKV6{0lvJcu!ltW6=lSpQZGdC@Gs2GWw7EHLsr?NMG| z;$v{zvPoP~JK@WWq2CNZ|M5k%onTdbo-q)wbj=BPhezm2SHrWJtS-2KHX*lTgHNW> zTvx-JY^m!9{aeij&V|n_)IdtbEO$BsIJrIF$s8Uw+B(=uJ-54ZE{#^f(2M#Hu!ymaHQTJqm7pt_}VA#3;oGUXIV zN5UmSBKlaY%`QLSQS?XJ0n$2Zf^wwc5+!DUS)(ZBIVU&%PhOc2Z_wTLW z!uCt5VX%D3IbZT0hZA7fyor0AZXXgH8g;8c3$u!|Hw6+l)~0Y>%()zSm75b8e;IXF z%#d#Jv~fLMs6r)iH6FG)dS_v_J*Vo}8hkBUO4w4UxTXg4a#zbC;*}p-2d2D*rIfHQ z!0?g+X|btlr_y3xAJnRIG`6*nt8d#d!)v+cnY?+ z327w0ss{o^OYsyR-HOZ_JSDCLq@~5Lx7){)%lNgHkyZ(;9ydPlo3)g`~g-Mpavoe+JywM z>f&7?-c<{cDKI7b{}JxDV^G)Y^eGIM>SC&m1q{Z(DHBpUP^v=eKVV67@?%qRqxj+487SApT4M68l6^ zt26t%CXDyjwr7LVR!*4jefXyp2*F7d+9VI5YMd+QL<#GUN*7vhczZn)4(1hW?EB65 zq|hKhk7$e*{jA4*7=lx8>TZuTS3}4vuNU`IaTPG-KE2xs9TI@=H*mOc z3GUe4FOj2MS=RNOqzu;%>)axu!tS3!HcXi>N-nxCEQHAk=IPtVG)vP8;IB3MGN)@Y zQKJo7+2yani%PgYWUgI76DP=1M6y~>Msdl~*$w_*cHKyft!)vGtl$l-IwEeq8v&fV z^~Idw$G|lZ{isOP8^^RO3>tm7F2E%qxVF3X;V}zBw}Lmw6Lp@T-1>N!0A3#k^tGL{ zPp?VbqpNjSWYFl|jw|i`Q#_TriDctiNJ~F!gQmEIL=BNkr`I>|S#UQoE4Pavnx2X2 zW2dA{Lpfs#Pxh4LrFT0NLyiiD_F9qruMgqF>0ddS-Zm3ckZdc!Fb03QIZ*VF9dTRr z`3uux`zRP*JzpX6E6ZQ%k3J~pvSF0_DVWbzt<*VLsbR$Sm+@dwMgTCvg!h$k{=HTW zl&FCf;k=ux9YuJ_;zS{^_j4VELIAE=U{efF&k$J^nU*liV@ zfTDbpGa8L2xb`XPs@aha@A*=nnx&s$peUwUa$7_cEG@hk!ocYE_#)iV&hEtK-eNf8GrUy=sX52l ziG-d+y)3oR=f%{NF;1-9A%M6fKlo|yUQjDze&|TIkA?xj-7L+5{0!O}5jq7h=ej30 zl?K~4PSX8HMnBI4Xq(x`(B|$0$%X+w{0m(zY@Suo&^m5~-Do@d6tijf>J2_4XJXn7 z#^I$)`s``9ToVuQbEPF{eD1Fym_ES5c#xY)4Q_Sq4rV3JkUm|#q`!%BQ7Fd!;c-gl z$$=k*9Rr@}I02b19!fKvQ|wc1XVeK&2aq{dCyR=kE1F{?P$OnCa|-EL0$NR<|4QD3a!N(v!Xc?E!M~4ma{N-?S9mN-9EwM$6ASZz1?0r}O|`L%Q4mn8u;el2SW@ z9OD7u8uaUk;c1W{V9lg*Q;lHIeU8|US}r1oyw+MbclU7xhc)S-AuSzcDV8{0Kqqb#8JkVJz_2yK@lKSSiycsg8)R3fN$&J zEI|V54JYJ3SzuDd7sAd!?yeuW(X2WiHQ}sfP*tr1lRXgYLjB0hgou)> zU!lL1ofoj^7&&?N)21JMT@y(@9-Q!H0K@d4SHtLizg)k2<2njAF%yJX`nRpQ+43MS zWBr3fg-`(#gqtJ)dSJRF09h;@(+wEjpKPCdn9%TWAZ`Mhp|!48u8iOpo2Gc-NGk=O z!l-B)&`O8`sN_Vx>ds#n=VpZ>Ga)i!VBc_th1=?bzk@f7+qwe>4|;Lln9;iI+f#RE zgFUIV6~oE$fqQsyl?Am2AXrEyrrOFCaXT)|hUq*;cF5iR)IrQu)Lh7OHJyL4WLvFr zPGkAnj_?MI?j+u8eAShCoYEM52lPt0w-|V>rHhEgk*?f=v zK*eAvP2QOu1h7(dY5j-CzapdG_e3AJ8BM9krj&CUoCDCQNHL)teWf^uTS^jp2Jhr@ ziTYeT>TUF!hjSpaYRoFx7%|(-ITls(VkAZW9dgy^&z$K1j|8ahY=N;N?u(2;vaoCI zx4wrRud)>3x^UknZ6m}cor!zAI9nCmNw{Z=H0KBG^t~E&hna`t^%`jWA-{McCLFYV z{^VDh{5m*|WGej?Xr`#E6nklx6vR7|g0BNzn&f#)S=VIV`hQ&@QLn}7HnD|i{p`?s z1oi$S6tZS68U>gsjK^LRP~zkOVuaEAYdjJ9u|GO1i&Hp@xXUHLX+=U<%3g1dlR{+^ zq~^m16U=zij%H@x!cW#0xV{pF-M$~K@CWC(EDg=0$=%jk_-&17lXgmXq zC)P{lmfPUcgMqZu_N3@hnv?9etTpwgBu*cXzi0Z`m><*+!lRq>V0@E=g55}X%7(Vb z!eWpzU1>wF6E`QaPmS{}c}#z-9YXoRIrdaK|66mS?Ly3}r+J0%%XR~+6JkeVanF;h zA(IL++6X2jWx{A@G%Q7K_Nz~?NFi#u8EvNw_lnE5epcO(ON!q>1s!Py0%f%CasQbQ zVY*r+D5~G+>Kk;n*yT?kJ$>OV>)49G;VJTwgZ_7S0Y36FY!hk(#Sa}t*^uP5#&h7+ zhdx=|S_~TIAm9Dat@Vx6grp(!wDZ(24;}={V;Wr8tL1yr+gQqQ-#eVk*MQ1IfN}3A zg{{BnZC-$hp$&d?T&CGq0!6tl_j8}q6Qce7iG=kFk?#>kqMX${TRoAY@-_Qy+A0)t zwpwtV3j2NNF{%$g2k=~T&qP%b$>BDuYTwxCXG_>BkMsnALsP4he2OUV9h){>o3mX6 z?Lh;0YuN-s4JK3aTe@p7%_}oKX!94c0^H?CA?%?L56;IYVT9l}JW-`HahiR3uM+1f zfvRioF$5FhU-xwxcqjT(%cIVWLf%4lW9Om06fL$<9Q)8K?}=nT{JXxJa(2C+O`f?$FU zKc{T>$f7VLST_J^W?lAyy<}`*!2XVDlSrAs4?Bc#c{y`UFYrP;->lVx4G?AO=xu! z!7e$Sm@U7_&Q?f$2m_Xwa|@KyExzJBMZUZo^kEuky2+m64Jpd13iCa zw%=xX;+t-AP18{M~ z>#Q2)zxNBgeHw>&g(`9ts4WUO<~CFRBuw3Faxc@&N)X=)g216oLNKv89GXd{#tg5c zHrbn)1wQx4GCzA25PIvh9~rQa4vQ{{LOu|ybreW5FJ)*ztQD!9cC&b>bRsUYle%$Q zG|U4vxA_sb~y&y~OmtD@i}5^?e` zF@_rVbzsc>Fbz58X-Z*Tw5y}!c-`MoZ>%9EorRe58#J&9)$ieshepTKg_DBTk6MSW zodSeEJzb$X==ACbC@jdreXyQlN1ytOVC`YsNO|;4;OMTwDz%qFXW35O%p0x54zA!> zEsTbW4tHOZ*#1u0=8NO~F~&VE$xg@Hm^4!JK=%0wftX15{OP1otG+^`%M9ntQ{g_&nIei!^h? z##=74Qthkk)DPoG^4nC6dX!QDjgVVw*C;Bm8Zfj*Qn3`7ycjAg37ZL4HMqKQ{r6sR z1u2=E=XhxEyoM5J4C?S96&|fq1vyzE$=rE6J6$Yc62tq&+;+8i4c-uYqZLkX*IXs6 zxFaXxeI*hEwta+KEq?!E5%U8g`*8JCdQBL|Rd%{7d=EP8x{sX5D~hXHgN)#J z3y|wp8J_xHIvM5^EU`E8{gTzH)0s|kFJ=Dt zG}<7U*JV{?#>p)I^{s#7a)+Eq%|W$8qAfeXNz{}Zo)(LZ>DG!{T!zlTD2Xq{3~E1K zY>xBL7WwW@dha}wIgqMhI7;I&J!Ci=!EtwCC&h1P^cO+2cL*S82&- zsyFHrX=Uj)Dk!Hs^P`?zj*3*{7nui@2SBf&5KYKqtnjP9Q1GPX-BnZYil2lM-D0_zQ}dt%MjZD_MQ;kPvVUdU9ktF zXOE{QNZ_6vv{L_itJ$ESlM^x@(ao{`yIrtF%2VbxyQaXx8=o>91&w9mTf+p)oTJ)n7T&(S*#a$)8sfne`p-UJWYqH&m z{6z28Aino$0jil#hT&)I3(`&QVuQCT!en%M7jHXBz5#{sJ&JiZpFmA-3&c?)f-G1e z$)TSC1yA528{}YP1YYV+XK00UhPV&zt$?!?;LM^CrqO6?&)R+VEjOQ|v3Rm^;Qm}& zKCHq+1+g7OAvW2dhV=koU%YGF#CaVY&UmOQ76M(b@p992(wXG|en9y6PynJ85i#9( z&M=zO=t_9xZoro;D9ySYe$uVw62<*!#~|rZpj;Rc3{Gso-ORmIq<;5H4P&NdD?ejc zlyoufJ-iB8n}hOHy-07K^x_MK4v!aW3CFUQExF$zRmZP0s!p2206|nI>;|Kde8dGx zaLwCL1`d{hMR%mEkqeK;M(y-g^x{ht++XB`wkpS)d3fb04a{Q8#V&0~V{qvxiIQsE zQsQbEkowkDRPX#%P=a0yK&U0}N}ixlC%*aBa;~W|c)V4TuktK(9{@Ggy6A7?xlBk8bFG(i3bLcVJrBB|H|C0{i z0@B^$XSksiC_H^mtqu?!Xy(Km=971!b{S0@KhGrVCk**uBe*3MAfI%%yg$1uQP+{$2#{+DoA!_;(uJ95D6JwhXK<5)kL51wkCC3_1 zq;R8S`M65d$I3gvgH|HUmsaSk;4i-YfGpYvf;*32k0)G0_K%J!MH-?>jjjioF{|rD zyoF%e0wZeey7j~kxX!IM+-?<4zxAOC>_G$ypQKoWj}3kGO%QK2Ti6Yan-tPGQe+!) ztY=+?nGKe9X>&jJb|jH23ju$3TB1W%4y=(bW&fmZ5IA-uc>TlC!Av`I*yVvXg6}AX zQ<6}APxBV&sa8E$3+WR=WYl=^On;n<){PH#+MtBX79$7OP5e@k+mc&3OrDnedvP(l z4_+8GRTWwuZWT!o`%ododn`M}RhPCvA+jL|>N&8%IflCCh|`S)8_~q$CSN38ua|rU zi1=yFyJ`=o;`ll~%01$F{T||dV}*RhmIo`_!NT`2`738?& zvEw8$6u524VlC$%fl1_vkq|BTPcQ6T7%u(bkSZdaT@DAZvr)|emj0b|8<)pr$(#Yh zuS*|~58j@Y*KsfJR1UnK8rbz>feiM$8-bjP-TU;#KN(}7s%1rRHhl|)z+-E2dOe`6 zKZjmgN2c9YJ}0A}Iy>v;v$OiiVmx?JTzfK_uo~`1#$)=(!jyWsQ@L)jwBPeUk4Na-eWalkvkx+mShwXL2 zF4rTKa!MCg(Q&Ux3BVkg5#+`Yy}@$&KyNrP5- zRpsYRjqnCJ1C6>;F%o-DyzG~SHWsZw(E&n3k?xW0Xok+urFTC}QM?lgrWLa!VkOCs z8hkc=S*uZvj75&hYN<{DGrn!-X|SkD2vA^qthn=QI9ViCzv_bWH_Iow?9l|Sv(RU< z$VFG1RisiEcWT$l5~+Dcmo1TWTRqqRptXiC)EV4Q>G6xlfHlW z-p^fua5USj0_}<3IRD|ipK_jV=cG4P;s;v-0$))BoUGGd@FC*vo;^~*25DOZs{I$m zqWgg2wn)0$;F2P$%VGup?2c;htd!{P%G+~Fp+=(PIBJ3`ok&t*BNybgPBd&8<@$(= zYRhW*03%Jkn;Hf%&j3K)W(E#mn)dUA?@VzSxiJ=!GI3wvSbaJ3{zC+1!!MeqhP4@o z^v9!09|Q@LSa{wi)wD0UnMoA{qd=6|(HZ&Qfw|{@0-9C&bv0O7spMX=w;wwKAvUdm zxE4BmClKK_FQ(P`HG8}NQe(E*8gF5{rVH+xd1H-kFQ!X*H;4VG`1MY9VMyDs>#0gq z=bGO-ggIb6pMiUsEKdzO@*E#~2Juq&DzJ@m9uiobdp=yv!qUtW@d^>XA5VsO3sOO3 zI`dY7VVUNd$m2A;30^%V$_q{Ztk96^Efx~m2jvCbhKhw_A5~e95;kHP3p)^?2V)6h z#779$c!X2DQWYCY2{cQL7V2M62WU}+@36fKFO$nX%EIBuz0)>IEau;xByW4$F1Kgi zFwQe44jJdI$alV}AvlE)09e@h zGWnc>^e&cPwwi5nkScinKHB4{U+$XX3`b@w>!9?gmnp_StyeeksLwfx`wuVVZ*pp z%|wfgPzo<30eLEwPb61(2304furzuWz>*p1v5=oD;-5t{DZ3r?l%{e~Xbi9p7&a$o z`)g|lQdl8eGeo}*fhAx>Ay2ztO5`v8Y4#KdmisO~g$!J$?GHWk8_13C*wUAEMUJqgTHF5I|;rwrkP5+TO z#B!3T=e6EsS7q3Ti|T!4hvm}V1mQJE%TG|o%c~&watjKBv?kje{EG#ZDWB%GUJ}gBwR?lE> z38Kg9_P8fC2lP)&NU@r@o%r1D#KPetAZ+1+@i=rgVL^-6M|)uMb3XZA z;gT(Cr$(0GcGm8F>Y;nU!Tf}cx+K3oHAnat-o-zlM?!HE6ztpB?~h$8Px zBj*`hmJguX*a%9(NwD8pUFDt_zMsC4So#5@ve6e%TRxT`7i52c){Zl~rG!7ND2#rV zpXMXz@d24?nx^anKl?8e{$J&%ahnD^+$T|7&il_`v>O(fP~MEpc@;i(3x*K$2MmnA z8_5Hfv3&ftKMSmK8HJ^W*x6GS0zI?GOim=k8(_N9$%e0T+y z?Y_?cSrzX+_Jcx2i)wheU@ZE4LADh6Z8UW#E{yNoK>1O?dxdz@Hn%&h6CMUHH?SXk zLPZm~mY#-553BoNf96O^qoe7vtHI56K;!L&kuZ8;UTDvY$`3iF6O#y+D1x^JYhrXcVerZBag1-!Km;2%%54pS{7iTUQ>RZXhNjNuI9DcEMp6_1_Gp}rj9o4Fo1xNteECz^JuYpV@ax^WNE zYD_4Xn|W^Dsl^PXLYKrWCD-%8r~>V1$Mw4Rh8F-rZYTpDpe%iggsbb{lmMGs1Es7k z2#W-^0PYLsHwHQ-wro#1?R`t+0ZHEiQiw&(S{eH!l9+cGg6-4{8;q*6Nwqe#ba)pL zSU_i#gf;^S)N_tjT+iKA+5Pegw}zZ2Edj)uk!0aK^9sLmLW(N1?wo|m+s7Y2?Brc) zhdMTpLf_6(`{}fS-i6Rp1kU4l2pI^m62VVOGs4*W6*CM3#Xu&Ny2`}nQI8CWvEhW* z(!n8V!P`Y3msuG0$luVbl+ZTYpcrB0VZx@^&Xj+NB4>VnafZ}oV;V(YncS|m%+ZvQ z=Zl26)av=MK{lLz+O`8E8D>A|trzWQ*c_C-jc93rudGgzA5o$m8k3>_kYDVuG^9!b zBWiBCE-J(2lVW$3xZgLIMk*NTtNCIMjSK_l0~kh(3@l?##Yn2ozi)LTz*_`3Rici$+U6O3;fY8Q{p&PZx$uzh25Elb^Zo&b#wK3y&1 zXA5S0_>iSsT#BWbyRjh~PqXNrGEnP_n+{MI0B{3wTq-1K16V;aPhc`0MV8LU4;rPM z!y$EA44M>M!-xs6h1+^e1|^Rkw<`JbW!uPKhO$ED5x4V+<5**Q*?0-1HG@~w(pB;z z3Fj6|m?S7e8iE~$vX-O4QX|OJ#a$hq+{rB-xV%JCXk>Xni@@I1IjZ*aNfyBMO^JNh zFOho*V!cug;FN|Hbefy$4pWitQp4Yk5qDZr5w=RxFSJF}SA&EMU@?~$$wVnmWfkpN z+&l?uu5_A^P=L=RcuR%W0iWo^0|po2m_~zv=A>I2FHXT!NUwy*VDA5Te{q<;c(^9T z(z06AOXjuIDOHPnMnHstgk|mfA9oIRWVWpPh*0?cZO3xxeG-n1P!N)L=7#k zAdl&W-INP{5Y?>3GIN*qZ8eU@N zC>OC<#G~Ha60p<@#bBi7{W?PVuUkIgd0YTMa-ONe)F~<2ps}z_WT!@HJzz3gkalHm zkn(SHf?n!GX1@OtMhCHpmQIwd6{2recryBUydtYb)Xa%SsK~P$M>Ne^X~&g0%EjY| z!zk)_2r`5pvj5F-V|$lIftI$7Z4x<4KdYB)anaG(#}R7zOz4C_w?&J=o;7>nzknGX z2tJ%a`q>oIBTgC08b$THCH;vbU=%4CQvxBZtnzqdBobEmWfmZm{Abz#z%f-p%j9yC zTNO4ln0(=w#(6y9MWO0iYZ;)ap`ATP`rx024ZOD`E>TIRQc9_Bk!u|lAQt*R&9N1Y zEj%B9*uo!}xlMF0ae_Qs{-juayn%mxlA%HmMf|B}g`2Ew$OY(PC! zkTu4-kTovyz%|ciV6}a#2SMm?zh8rg-MxTG?6YyC#o41yvPbz&VuJjLwCRpusn5?(_O@6D!S#FilBwB$~zZGg<2!I$e zJFdU~jW&3uCY#%DL{!98(s6Z=;*Go~%8du-21fUsV7t4cb`pLKQX*k_+D$Q+w451p z^0Lh!*90HENi3VvTGB#Yo&_o7rYqm99Be-yK3DkA1wcsSGjxS6BEA-+l9}5m+pgFl zUD+U13c6UyPytzM(ZgS4Zc)_tI7{|Bn$mF+l*S@uZGZ!%enPwZ1zee=-jbaWCCQ{4 zP@cM*ScfZHGU;i5Z+Vbq+|~3|-h72mdSGKkSElPQwQVI5(9b&AsyFJS_uoB)k!FxX zzYNQ199b=yc0a=)ZCxC=Ps^_J-u&+?!v*Z3#OnWO=fe90rQ)x|ghVx+AdDTdOv+EcQ zn%}kFvHu#v4>VmI1)X>l6`%yW?m^@%Cl+@Q@y*O59=QuFI3*P~1+UvREcR}6K#Za`n8aATMwwTT_H_s<_`BITf5$30 z&NU^VOPAjS2kzX7qGS92KMl;~(kQ|GB1x0GXfGa3G^bFUN#|drk1`Y?&7QQE@JjWC zCh?h@$QjVyr8Cqn{+Zi%&7g3=mURfG?G&>gbj!KV^uHrg$T^E3fpvR@GOO-1vDz_L zfErR*!H86k9k&-yI(&gg@*P&sxf}_)fB1-n2z%pgRzzcLPM_Q6^cKG^SUV#{6;m0f z=B0f298W#II^x_K7f+_R9|b}9|7htj#VqdS-g6uyEKS-+RSWgjxYtx_yL#p)ricQI ze~(kat(-H^?#TD{CpH9(T?mu9hILUObHwxyMCB%i2RF4IMb z;iv&nv1^u#+S(L?NErr=IA4fLcN$zJ(#%|@7`g2s{q2xzbtq)q>A%fG+u3!i1ZK#XM_P+$dw$p+Prgp4uglS3d@CC z={L_v(&2Rw`tKmL0kR0MS)1@EnDMRG)i#*%c2<4yBXkI3cp#mp^6YH=mu&}|-U6EM zlfBNW%kG5wY_k|dsir5|Z*5$^J>-?6AVb47#F`e#fVEqx(Sj4l?SkE>N))o(;2+@$ z!{&kIe(_RDa`ku!XfnYD@6NSsMtwff0M&U%TF$JI$W62tV)i^r6(ud@qbg$H4QHU0 zqXNTsF=69KLIJ6OXZx5x&IUxaq;ie3CV{rVdm14eNoU=e{(k&CcM)JKf}q~sEvlGr z)6U`9;us6TKqo^AO^JYe@}oQ{b&8b$jzTiOTO*fSj7wg>}0R zJiQMc2K%UYTYLBYOED9yk_`kNT4dJs<=D(kpfKH%G#w{gh7N*RlD+pAcI42E5(+9d z_KJod7ZNIS@+s*Wrf4QMt#d8r;Nh!w466^Y+xJZLC;5e#c=+3S>~&~SP>T9(8ry0n zNolw`=Ud7XZjtixXIzEb`bPA**02F6!ZHla8vA~l{NJG>4(HGoa0NZ~1fcwNc;A-g zBABa*G!VWi)V~8dtI=tT#|c_dMf}s`9g2usK6|LxXrMZPwmUV8#3!0ZORsPEKvBab~kDsqiA4y34eY)A=dU1`3>IY2dfU8O*2 zYo{FJ=dM#{T1Wu87T0**td#}kDvx(5UK;#=%tBzyi1zp2#*ey{5Tg|VnRji*7pc`6 za=bVJN6cQj4X8f<NheP!R znpog$=s*dPv*fy~SKB+lZ8ccTyARY>R*b&ln|7NN$}* zcVWy4B$SxmbI4+_RzqRF>VVAhJoULM%M`v%q$&=hp|e$BSfBz=$`VJe_6}5Fz~i1| z2&Vyp$u=@mvY>}9h9LIxbS?HvWd4UStb0Zii=VV zGwqpPq-I_^F0inQd8RgJm3aS^9Mz%FQ;B)j0WKCMNR+cSx>Xq2%XbEU`PzH%8ou8JJpr|zAB%Xow+KyX} zu7r4yFuL~R33$MfFpdm>H_wbJVh1L3oO1BPY)Uk&SCpK( zf?Ff~7I@ssBH#cOIAz+NM{<~~Badt1IE`&8?$=z)9tXF_XrV36?By>Y!K)*ejIyJz zqirD2GlwhMh(}hF=%19MY8zgF8VzPw6BtEG&Hz1IgWUZ44cUSs*yZEaWAya8b(Tl5 zQMQ_EC~&|9lmsN`%@=hKHv7@sqOv~Yn1`pfWoPL;TP_Sk`HguFtjEXC2&t3t55xfJ z5Lk2vBQ5xyWO1mVZS|}!CK2u(3VJh7E-7hB;+j*k#EU>(n%xyxn!bg!$u5-wup&ZYM@v1l_OK8< zp%1O~T<0Cva*uq`te;7Eum5v&oX^eu-$qJTH$ZC-jG6?n?5dO^O18`4lRM=I?6fh3 z3HedzL)GyWR$D|S0%n8;%Lmf%mgZ8f|q*=wj`zxrT#7 zpqA@dsL^rXuWACSo+)p_BZOP~@j zs?cr&N&Jzkhb`!P6ox%W8k1xrky_$=05kb2|9`BC9o{6z`&kjdA`1NT$ zg}2L|7>UP3wPBM}!c-kXk7neITRZUd8c2zB>*Z1rV;r|V5waiaQv+?J$0hehP&j@8 zpjvme#?pd!lRo0JFz>ErOC6-R@&RYki9-v4VkeW~kLb&h3sKu5rOMY;TCPhmD~*y6 zR;Ry6Hvj`V5Z0(^y;_2dg9rY~=G79JYg11*J+zJONLG9WOk1cu?;@X5vKf8(7$GOU zW)NH3?etpUYmC09uHv_dsGB@$LG$Nkrj_U#XhdcU>NUb;HwVKnLFu1;fo(!0l&KJY z#~XyvV)rot6x$aFKnyzsF%T<{rjvKVH2&1X48>h?LsUr9SEh6Mk)wnxv?G+x!RLo=gF@77Xf9}hNF-*D%VVgPc;<5DzrSqP7>?az8)p6%4&0|KQ zUvNxV$>51K=X(_LFYEQRD$}k!S|EHs5JPI#XiwIy;{co~ON-L7F*>WG*FES?E$Szp zFkbqFX*`zK=g9`Swa=Qc$B>yZE1e02@Vtb@kGTP(j8TQ3=sC-X+U=z67E zw{J!@jMwCUe34P{MPZ*CelC$*Aa0kFRTL}C_TNt{ZY9@zgG#d&9@M1(Yu5u9aVpsg zi+Fx)KQny5zG(q0^I$Iv4$6|8!n=ejj!KVsbP6Z_oi?eeBC4_hh-c&pUDTH;0?^Gy5gc=cCS(^Y z;M@Oos+w0jv!+LbkLn^Ppn=B?&lWzRB_{3Jv`qq}R~X&ST`B%@Qw-v*fd&Z< zx9HPrxqJdZTsFDavC5vF-SJWWNpCWT?4cFJ4}!;Y|4})~11)zYZ&J4ka5-Zd`(YQ@ zwTrN*$*EKrMQuv-R7&jWhtObm9dN^B==vR7q~}nw{~* z_cT=IUbapglY{!tp*!P=9p$0jEXU(a-hb;n0?4~5d!mHh+4 zW*F4V@|mF0AO>Z6dI7{wgFv-qyj2W&OJfUp=4L;(TBL%M=?Is#|n~A%F2!oBZ_C zQ>c$`=LwAEddUj@;Y;V5X1M*i*>+nk=vg1R#kw58c3uPD z^qCP?TRXWnN#nH|b5P1@|6icjN+Er-ZNK&33Qow*tle=x9z4tO0MYEY}&Ml+*~hLCQ8Z{bFCBNo25EJbX0 zKdUdMTDnxo>ukY*)P7#1>x&pBLwcI}8p&N2*DmEhYGa+2+vuCh9`8w&euZi*3$KX} zKTengWHm3k-K{NA)CA$_9%umOQgu8~R{${A&7(3FEr76>7td|tEUBlqcls|Q>1#Ay zpMu-=UVV^0_iXhu@<_D(kj6$V1*!(zr+4x^o#IH~XBg4Q(K^Imjps-BR-YRA)D)<1 z6PQg4(kk?t7|Oag*@2D;@Y3@>!Cf=%IV?JMxWw1h$}uP41y{P?_6qi!`&{Cljq>0? z5)GI+RM;jN-hEGv|1I%1bl>yaWCaveA2TW1-&cYy{H)6Z)~sKDUr$=fV2{O43I_ye zH9iHbCIW((HFkpih0y%l=Ww9UPc_;UWxN^OhJ+UM&?ZBZ2X(c7g$=`O3EwjwlP6^1 z(P`WEElt~_D!bf2#eI>#=4?);eXo+gVy%i3*^(kH){hoLVf)fUPQpL zRLDTUOpxoya5&DL-P2`e4NApj6Q{FU6If;j08C-~9Qf?^$uOO#&i?G;Ym0b@^V5iZ zWhwF9;`L9*NY35xT{NNU>qM6=#Pt-Adv3-2dRFWF6dbzTd71d)oYFp}`oLmTu=%1_ zzn7Elf=^OYLGRFNoJq446TzPH-;TMz%LBM%=#OZZ&K+sLu_#U``&56-GOs_TN)yK+ zoDaHXUWzFdvz8Q<43kWiw$7Lu4#?%TL|>DcEPFkp;0BqOkJ$hK#aa;oeZ?vIr<*%j zupBw8j`IFmL@V-Dy#T51!&V-;I1Ss5$eu7YE`$BDQV^il7U0dXKv?*<>_cK?@UOa4 znFm$Hh9M;~#hL0mNJ;u~>pxyipFR;R5ncKuPd9#1+k^0{KdN5V-6T5t6YUj|)Qv#@ z9P$8gtA%%TpuP7cTqnJ~@pg(=xvntn;0a)G{{YchtQmZ3s~=^XC_PkOZ&}_mNPVc* zc!A5yYlN=rfE}gZek=`%aoCT+e=V!LMP+m#T$X zJzVM$s`hn(2DhwgK>RPUDS`%9w6MSQMGlZVT81#YJ_yAj$80WYcmF;oKGn+$mll3S&68Pukm+qSmLTC^6d4imyywRffA1}1Nf;|@e~vzTpi zW!k2Gg9~zr=Cg9L#$Wx1#@$H`cpkc5o^>eScnLT#6)&%Xz1B3q@N}5!8jd};%XWac zL!{yp&+`&dqu6M3v-$xDL}HUE6#ug;BvCVetoS^4+EP;zD&El^yH^!o$Sp74_3@l3 zMXZ)^s(QNn5E$v)dj8tDT<+BKRb>oNE-Dy{-NNr4TRBnh#7+69|N& zQ|?_J-pdM@_5zQ$o&6l+>Q)6?yBUjzS9u%fUzBUC(T*9#Pypk)NMEFLqc3tId;B(R z(ScQ&H=-1OlUovNI+xmSff4md? zhV>P9N+LdT&PpDQj>ClVs2>vLKD0`1Q87oi0DKN)_8mp|bSk;jMv9eNkjv40*J6)6#?>q*exy3iCH4LXXd4((7T(96C;y(a z&#ggY)jh^X>6ZcjA0Yau1H~Et+hFj#DWH|}C^HW;B&M2LVW}l@JJ?zMQj(W1rk-Kh zhC5lXY-#Q$0{cbw@Sz@nUgGPtsq1#20R4|YU? zs61pn_dMZ!SlbObD_n;sc4XN44NluFu0tJSJnRCmNDfw@;eR8&M~-ZO}c(HTt48i_LvWc)J(AF$H? zwqYQ}c0$~%QgEhEXr`B^1jM;zF<4i5Du<`KG$$_An7hrn-xm~yv$#iMZV*GxVS&+L z+06D?^Ba=_y1y`7Y`{VFg31=G( z84hEtULUDtabY)KdpwljmXUggqUMC%1Cac|0TDFpyu!_KuKHSJe~gu~PrD#<{$`7< zl#bL-*S|7^UPCkY1ff~rkU|tDNktW}(Rtz*5FEi;6yC09=eHMe5Ys#}o`RLExNeFbP|7ie1i`R6 zVa=m{keHq9j}iVg3y%OnuWW4^dWD;=`aSzHyO0oF<5pr*C^SqJe(iG1cv2YVKEqD} zL$;Z<6Mh7}qA}66XRz!MEKgQX1dJfuqgYv6$B1&F=*|dg2%l3PG}MC_&d0YgWsPXR zGPA4+1UpsZ@;-DB2xaaV|H@XHML0kZkaHlh{3a4lb!s zbXGIQxYx}Ms~kXY%x^vkoPML=*ifRO|7kbgg7#_S$lKLXap_hrXv346`lCp?15xz8 z25M;O2-}ol$+sL;=3KiJ+XC7TH)p>HjM#$GjNw;?RRr7kOu8dc5KGP3$bxdO8)tcVNbETK5 zz9uu z6ZWl9Lc$OOKhvYfY;sO(-=R)d6g?UC{d^>xw)kFuq>LfU#6M=VQ3_aC5898^j&V`k zW6-O}N8u+JX3jjz0G;rIyIf*Snd1#U<(slDrMC30c`PNiUb=@r*?_pZlY>1CMY zo5wTbRBJ(M)96HNZN*eyz7ns8_vlC^E#Au9elrFh=8?^>$B&{f7y39TghUQFP73I^ z0>_L*BwjLYLjXU>%ogN9NtByJw-aL!m{b<=BtZ|lNYv#g&MpPm1_mSBt+`QX7~rwp z{wX$s&n?LjoeiTc7tlIy3RJab0~DKv z$nq~*9e2*I&F9UYQs$|X8-ZIR!PdevT6X)vYTC*-rNW>oOLDBWB*eve@=PXFJ05QI z&{5as6t~VTH1hn3e7c*JvFn_T+rS4_VK$>=Tw7!53w~9q6L0KzVYIQ3)#XH zUCdT4wX805Ip{jqX9S~Qm4NFrF8E;vU5o1aP6fshRP(ipLq zL)raH=0g`+s7gmD#)~2r02r5v-T>$TfRItb(D@>2>8j%^(4AC!fV`8~k#$ z3f4msf=TR|UI9A9=OJf=stK+57^pE(iqAusXo<)9)}hwZ4-{vt%DKG|O}uhYJufq( zuq&y_mw#)sTwLN4*e*SPONu1wvmM0G}dIE$6K&ZKMxogJEwFps`fpulngQmS3!#1CO zfJ;M03&-EuR#^;DHLHr15Ss2=snw(I^4hZJ%G>Eo1&Vm%qzlB@O`}*m&r)&c zECUprmv^Ie`7^N%EX}+Jag{$Z2Qphvin1q4y@xT^&IoU)cQLfueI8{PBwx;alZ(B;Cl|o?8b1o;@0BKls#1xyyLj7c&i5o_-Oc))M>ZAS4aA@m zZH8MEV&o+wbx&aP^rYxgW(|j05%XnxDOeMZ@8ob|K;z%(EQxOEq!bq|KqF)4>R}cI z#nE`3m9lV(+NX5@%HohT^6kN^w<~;dP&+LBc~(!-A7NOYj?_zSD#B#_m2ev&bH0`* z;))J00^`(WSqW*EZjG~wH%f$Ug{XD*cIT(a?46K^9u=kTCrk*XkZvAuwyxvhUM*+b zs$LRdXj@@@x(H+Tj6rcRS&zSQtQiI{2_?(ZnmHl7Eycrkj%BL-Z|FuKS%}_B!{w_& z(FhXIeHn>0lp_0<&BwZec*tCYX8?C~olLSxb)z9|r%sUrXLDEuGpv>{iL^k1=DULW z$;S*|5(F9i;tPT4IlWD%9LCOCvtfa zth_;ShtsY}#q%?S{QR}Ab^ znA46utot9o;PqyFl+g^XhT6vjZO~8Cq2F|ym6vKPxh-{s_b=hsv_!)}`FK1rLOPk$aE@(3a)=K2 z9_Cr=;{szt#oN#D(V!78HU@|jPo#Z>$9i@B4b-(|A1$RLt?TWISuZitt-qtexnU^F zOLd19002q0e1iwgT=*X!+uT9~Y~W0h^RQ_;ffEY-+yQKs=@#FS_QdrsCIDtvd@~F$ zlq?)Z76a<<{8q=F@oVBn_zcmNP}+3Mek@zv2Rvm24Zg{Vo(%t2yGOtjCV zV~2fBOlwIXPMm;+{0GX#n3A3RXIUd@x^$}NblA|)TOItNkXocdv=k}tm(}DgaaIsW53m)QKjBS>O$~h6VD#Do7JI3WQp$_Ji0< z$Z$KTi0HpmZS4{rFm<{*`+JE-^&v%&DaAV^^5aX;zQCMrYeRTYuY|b0s0&N!IJ64( zTIZ_jfHY_o>eoTW1FuVhK_HO=Z%u~(1V@Dyx-sT)4gdw{;MGzJk0?5y>uds`dL>q4 zm?NnsCI8-Q;wXq}_ukq(58d435>w&r4~k9TqFlDsNx7C1w!~eWMFSbH)>@|dHguz< z7{C%EKeUHzC0q&~Slpx53Ph_vD40Yh|NpY+Zzjx^^1@D=h5D5fv#0%i$*gG+@2yQH zY$Sj}J!VZkj)Ny&KbKVNtX^i>%0_?-D8%wY@z93uSc<2CLuw=k8GAGkhRarM3RW*# z#+(sPW~vF5a2V{LZ=IyqsX+*$baD7t99c*eC`VA0SbNT*RS0M9}M#hGSt zQ>Y~P#+k=MRRdR8j>VI00f$*q$CM4H!xr}bZA-MzI>e!$SJ6iR%YI0uUH$1j zU5wsGjK#7jMAmHLzpg^LfM7U25b6*-`PZdOKNZy# zv*xz#tObUO&6B$p+aoHV*CmZ+?)NVBN-W_@^sEH#dT1D5&VIqAlnZ6js8l7qzp7gah>un~A9>8nigXrx(!a(ejqCq`jVhb(vO5^2 zbz;8rgiOb%uVa#me3svKX9H235ovgtF>s)jhs6k#Et9&JBvl{UFN62gT804SLG**e z^dP|+rLte(SLq-IWcsM|Dx&}fG<+1DP4DE|G+Rl2+CgaJ2r{v@93K4uyc1Fs zpM2iVJWZSKA-XDzCv3+cfqhY!QI0V?8hzLJ_<54tXhy*myOOe~*{2e6#sX&d#OlZa zh1JLC*$~fQ%5kM{HLbHWuxtifDgZlp9%W;?1y~CxS8ygaldG=xaQ@ z+S1P&e?%l-tMj_6K8&z395kt5wW;e%Ksng#Az-K;U^N-;Lt?C}Sl3nU=76@nZ^I%k zaLPFy)_7_}$xiMd_=oNoB=yLb|*1^#VwGPY^Q~_=T(vOYVCJ-}I9vDJh z`4~3|=X`<9&bH;EH1t{MHm2tqc~-J;0^DAuo?>rT`#HjKfNAD^5YT+-<@BoAR9*CP zHuNQs+r%_|9oM#EQia*n@?N*)v4lv^an9O8fs_>BkED1QF4I346_|qqcZo+Gfi4dk zwiykQKV?|2*3?27rCF*#3n)`#`Dt14rXHPenzxUqu&zYJcm=Y__Xo)*=}K@Kp{2#I zw*&-{{iV2~)=4J5KPBRmVB6goIIBEWZu6Td%@!2V@jLqqB+KStzDnP>xWD{PJLEwV zZ~q>V8^{6i?wwKez zBk?K|jg^YSaaif4^oKOsdbX0VC#{by|GmN15|~(pa0^icGcq2}A}B6;8XvU1%V(dx7cV}igSiROm}pmQNrI}V*e=VH6B(T~%Q zq4s#jh{&E7FIy!V*Z6ah5I8Y`cF0LM`k0Y?;p(>_AXX_3UYAqRNkh+l4Oopw;d4)5 zRPSjXuRLG_+;!jOb${LGrg%$;QljM>R-0R#_WWX#=wmwXu5;?^5Z6<(pR6vkqw~Sp z-Ccl9(Ti5=NmNMbx6wFXHT)mvK(Zy$ZLcd;1)+Onu2bq%{swup3(H=QC{EDk)mTRI zQvXL7LVF?XW>fzOL7MY)GsQegz}FR}ho4o?`QhL?8V%onxi@v+H=m~4Vnzks-Ly!m z;s^Paf%LiWEFqFm?(LoM6s+TN*)*>-)yLOA)*SA^T)WEBS~vF+dD+991VMU1eZnV> z-9S;?gq6J*E*G`FT8Sh$Vx?YZ0i|IlUSs+GEbn`tR;ZhaTj}TZlIlv)F?i*C{3N`2 z#h;Y|Hz6Oe;G9`37<&f{g%4EqFOq!x= zo`MGjDoEo`jbqtrLmY|qeJQWkpcGo80j>oIF687jQA@v;0y5HUF;d4wyPsdf=gCgc z+?2|T_Z8sEb4wLIT4^}qV}MSfDSjUkTm(SfcE?1`9aVKA8S|gwm7fqRMJxBV#)(2v zG`*KSoC}sB+pr-=14Uccc>Ke$A*dv?R`Rotm7Hz}({*@$_n8@TUW)}&yfupQaM>Z$ zs+yYNrE%lGw@u6Z<4#?X?l-})=%Wpo;awBJU_HV~mnm@yNJ2ElZg0(#MY}2nki}48 z9;pqY`?*Y{Mz?k@DmbBRa2oRg>5q>FX;>fhtG@Mf{_It=N}CN8@$|%{Ryisv3jB+ z6ae!L(5kWS>I407TKn(GrmUTBH?zQE(3@Po@VtHfalqKNoXl!Krl1>&^T1Q)qPAa6 z(%sbqMy*#jXzByu*%oPOxh5DfjRcF)%|k_WYB8hO$qul$t_)p3GOX?Tg%6N*>&*)Y zAFjoDsl{p_D`rc8IH9sK=Z*vv27$@c2RH+17|>~T4Qp=$*7AN`w_s@tvCAQ*zy96< zmUbfb*e^`(97MbRhzJ{UA$KUR65<21?p{}$moGhw2;^`dd3XqtDw4~Z=_n|+#M1)jxxxMWcS5rg!UewGIuC>DOl(rn3p(&~pQ^R%SQv~@&q0?!gzxpdDs>atT(hCyNoya^?5ut5G0Bh3`c*cg8r(Q`<7wX5w z_6?Fhvu$(mV<(!K9vQdu!|o)9s+oF;ekITy80Wm!KOAJx%DBJxDK!~Gja|3D?bBRt z%XqkqSf8fyKQ%(#4Wlf{ac1z%siD=7c@D3%zR8u-KF9lX=+B8W~4^d}h;c9_>S{w+YwS<=U_*;N)f zuw*UvF#QaQ=1yAY*wGCowfB*Gp0WwoCY!y86h-#aJgtJKcO>I z-@fP#$HT%k=QaF`ynRfZYgt@@tb!UGR~a<%9KOFuhP>=pGfsAVyTX*QVgI#?$|61( z7awrXm^VCP#8jmvvaShAP4kUZ=`lT-U^SaUYEbKE|I+>OG+^%KarNmJAKmPzx=_=g zA;8Bg1KUpI(xbam7X4#Z88W`*b}Zv|U|guI%;ovL=)Y<*?CTGN`SG^b9uAf1b2LLPcK0h*wKWGG;69@Qf2KDFTv&r)4jcKxN`t^ znQ%KWRAz{(+s6F%9ku`sa8;$(OR+FVs<~@iUi^0w8RnJWZ$O<*=E2Mwv!?_E8-|{AwSBrPj1VpU}vAk7z=eD z-q#g!3~M(QGn$C{AQR|QWAvause`Aj%OF%r6EM(qua)}LXnaDn9-GlYicDhWPYKnd z=lxmqO3_$dp~vWwvopn(nmv6HtV=uhUj)}$B~-vnlik8Gzbnl@uiiSCTzY1V zX>#eXC~#ec&)tQpaD#aTMVp3&U@>JK%}v25Iez5xVbmIHUS0a`WAg5)@Z0~kk9MlV z8q{5WXvA0z&#^x~N~+)%CXR318uC!Gprw6>FfNc3^MUI&(bmAdUY$o6M$FUtH32VY#sjaaWFa{)4KJc1Bjlc%rV><_M`839&zlW_sga@63-< zLihKCaXf7aFoCF3Go5dbz_h?A_e)h61WOms^OhPhW0rs-x=N!B(Ty)AU8;cjs2WOs$ftf!A(;8o1@IhMxGD53_~vm({&{t$`7{cX6Jlh zNJKj+Tdu*>0PYa){*tgKznexc{Ure)Ynikt*JJL@+ZVTk7q42`>K?JfoxmYeB~1+d zwpiOsP(1AaA>j!SX;v?|28xLjON!TO3cy^$`_GePupRL~?=+=Gg%(X`FkT7MG8%CC zwG9W+Udy^{k3Fe+wPT+w=#wP3R}qvGec{?vIG1o)wTmMKSa7QTFCmkBPGUn}>E^n4 zMQYyR6EE?_QAB%>)jD6EJgFYkiI^-(_gFAVDMX$8(6L91CHcMjo{puaeA34HVfe5` zb9mxZ$ZgCu+UVBjCoAMRPL6M_Oi)Ob;*J%>dIplbjzU9HSIS(|%d<#tvWYYy!xYBl zQfAhtRL8En?8+G+f`~w_rrc1J+nn`5SW#7xfd-|kU{X$;DT5U5N*xhWZgsL5`2o;Q1<3os6h&si^zqLcFaGjXvvsXB6 z1WhA=ly#a4a~5{aI#U93sBC$~Op*$)6N*jHc@vcc zTBnmL&(Zq`I=*;qTvXs~gId%!rKK|`1dzPyN|3(X1x7F6xoCJ~=@$FIT`A?i6w#xY z??zm4KsGS*xO*9~P3 z4#Cl^*`JW*ofP-3cK+EHBPbR;Ezb2fG^yFR6+)8QC0G-b?Ui_sFjyv-mA^C&`!j;i)<8=CSd|bh_^~Y zRQJpOHs!o18Y@Sw#^^Zry1TTm(!@BCTM6Z%9td>@mhJg>$+@F?=@l*A*18hM8Au7d zEi!ANa)SD+;X7bF5qnJ*ap+aQ35t1qX6o4n~Wv(X6sO}_;|oq&frY}>1_0R3mpB2 z>!Ii(JR^1xRdYBo-IXM)prJ@~BpCl1Ei)vsBtX=`x@eB;hV&*d^+a z&7B_|NRSa52+KJRL%m z99&?`MVGp|!$3Xx#&?e$WK7Cfc`@ck^{ z63@!uqjvtEwH+-toHvye8)EhWu2wZNiNftj7CyaRG8&lRE+{VlC-RBn>h5F)C@^M> z5|xdZSTqs&4n{eWS9+689CCM+bJ~hb+lk;tAUu`78u0fVS$~``OSwOi^F>Or+&=VSv>o)XHQ^PLqght8s)%o)LIyS2po3`<@12tJKjZ@mMOoh0O)pLU*3eTW8{D zv@hwO9A&l?RvOvxJnjdY0A!6goG)@nO29iH^1}Ot-w|e#gDUr#TTc!0Z+d-i0wG5R zcaiC;yx%j9j+KL(YP;O34Z>_i_INLu$!IKV;_a4?>PM zyl(^d+jh^&{&OXL7B(2*DM&t{YB3ui=wtf%NL|vh5fJRBA347LYyG~2YhZW8Rlc}@ zl^JC6*hyh6dO6quJN{VRXByv^Yxs!=122|@oR?kzL!7Nr8%*ASa21}{CFM_;Ho`-p z5;+aqkv;#Wo263gci$no!-J$SwBv-YmGoT_X^!0!B^5-{BSHHpE3SiJb zHeetMSdMhYp-(~+@BK4jcjh7j{>adhCET~dU3GrriZ0v7 zOq^AxO(bmD;f2Ua=9bZN!u9YUA{gzTZx&AU-3zT>tmgz80q{dA85z^`|V zTSBU?XOat*5G=z0*22=*%uM61pCMew{>*XVnrU*(QmOU<&z!?P#~1#qjw<33zQVL; zpuD;i7VQs#O+JHXP`SJkN72K;nEEQoB3S6vM_j?doTbkSG{f%kVNN)Y&%R%~X9M4T z9H#EA`uLv~mJKU;jb7gj{@N-XJGfLu>w1jh#)+;bgn_F;mN2^hRR50yB{Er0W0{BM z`0+r}8NLYvOD=4+`+EuJt9$kDsXn;S?!T99cE9US4!!a1@&-c-h-xnKCJw#5pT%-R);nf3-iZ$WAs10(M*q; z(Q)LkeRYT#YjT+K2;d;<{Z_BW;~D1hs^E{|-1`s+JDn08n9(jv@sGY`IYnwhPDKj) z?`iY<)C6HjMR6xEk!mvlD1lUdd$#+ZBPkzU#GvejQ3^6e(iCGw){|q7*v`*-;{eX% z{e_Q+WYfjY$imlbyNev}$7D7JJ-nuIQyl%qUxjx3iu7nA6lvA)tL<~ICrw9+8>}Jg zqKdARi>b62*OeF)zi-=7B;_TJ3%YVm{%fPE#)wA*nQ?L|icd~wC%_5lC(}TJ=~&oo zfIkSb6-kBf^|Rn#V|jo&3demp=zD-QuQdGQeI-hitvx0Ti_34UubD-KHrGVv1{(Z4 zg6!W=EiFKf&87w&-koa0^COkh2B4VNx-rW*+0C8J4CS=|VMTbLxpEpXJ#o-)+!u~C z;XYPzB}dQVQd=Ecp;a!kFt5aHt@TSZ%NFTJ*4fO^hfC%l(S|_E9liaA)-c@FlU-&C zGr25zsm)bowKeQvZL1y6@zo#nWH{*Dix*f~dH~uX`@oFX$q4r7VQne4s0{hNvqZW5 zOHXM)f-{!fz<3tn%mpa$tq{ebUK15vVroIQnlrV05f zVmed|nMmTP+{63#aKI!j97a%g1nOSuSjnaR#+g%$f>&h5u&kU0FG zE&~UirdpqANgKeQ8sT{+hWItH8mOWuZcN7oNgm($H+KMe4YzG@Zlf$Wu4Tk<$FIfYak_FBjN|#UC>Rugb@90fVY4NfUZj zcu`jX=?A>dH~u6ZgFGsj4Y?=gO9Jn&s`N&A6JYc^JY=XE0Ygm2U^w)M2yXs<{Vj%#|B7= z#aH+rVsMF*tpD6hP4E^FW6so}RBYhL?}z_~F1)Pdj&~Js8Lb>889O4l_=f4={+`W!mUj3{WKWB?T9ciTRHAT1Giie7zV=|VDdlW~;nw;H-%R9oq zm$!d0TN~YF_qA`XvLM~iK8>n%=!&f2fI3A{Xmn_wZv2pVM$dW-S@spIWI$^`pZd!5 zisQ=!ZlQv`M)1m7u`FOJD8X`MqL>$Vh80eaH?h-3I3 z*heMN?mVWKfHlMf!690do*{^B%a7qrAyAiHST^+<2O~-hZEh%qhCTP=`||5X+x_Kk zH~YB3?=z1vJ4hfL48-9(UFiSGfv9|sL~ro-Dd(V!WhOrTU66q?YLpY#8g4ZQuWd1F ziV)jRGu@YvVy;W;4)(iQck`a6iq^eQ3*-xj`QZw$=Wv?s=1Me0Lv>ENkq!1}4yRA% zHkuK*@QKVm`^YVKj_un%C1`H}{b8EkNT2{@Jx+Zj8KvOb0Jnnc(4>&`6&iKY@bkG1#6|6%nw-oT<;<=#%RK`L>Qc6xK#|xux1U@ znNw|cGxyEj8tLxg1e~C>xBv|JJWtRd0o#M}Kr*L(E-<%CHeM9mMnjnOJQ_L28_7+a{$>T>If$Vxz+&6Z+A^x^J zlfuUaT-|Dzy=iMG0G7G83#8pmHu?dXVTv=Nll^k<`^X?g7=9(y1C9#S(-541lh|mm zaQ2^`F_?N6P?Q;1iDAi1Q2Ph6jav5joozB9b93ClW!)k2($-C2(g1h?s5nO8`;zC` zOz{ZBIi~9sKRJX)>1R1!w5DwKMFmeYi;`qT?}6A1GN1K(DpULLO9qb0yGkJ;@st3h4$TP4szs95Nmo_|-cEwrK;VMnkx1@>$ z!0%PIiNEI>mHKJg1?X)^&EaqHx<))Q=ZNJB)41$*C~`9=aluEgo}kKnc=%{av^Y&a z6AvO<1D@Fo0G{qZ@%l)w0MIHtk2ex1c>xFOwHI~w1PH(N3@Tx$Nm0`VAvdbp%|{=AirmP?9s^3AK5RK_1$pA<#_^%~ zfEn4JE`i<|5JHy3f&eiohZ_+;#yoo((F6658P?k+YksE9@hbOBJ5^FjI+s}xGLw>h z>7Wf8f%S~`Ds6^D@MdRbiM}8NCCPezsVxinM7bByc_}-VE>3qa!FZB@j=fuo)^n_ojYyD(AY##|Y5w44Xz z-0JZ9Gw4}N0}O+=vM2CO|_Fq zN_VoW&oFZxhAI4h4Vg>k#WQg9y{G}#L2zy5u^g`?e4srsz>qF@gj#)fClClTxgttL z+$6Jm2g7=MAhq3XT!7g!RDd`%YOW_C(qA$=X0U;KH3OYliF1qspBT?bi4_~~Sc2cT zPhhxy1qoQ0r6zr@4PA+f7eJh%lN~O3RfBzYy&+sN_URSdx%;+W2B>Gbw}yy5)lNaf%D_7z4i5Va(iq146#W9bWmeEl_ZVhN0{DkIQKt zg3+_Js=rq)kqWzs{<8{Qc+@KNOBs~Mf3J$aA?KkO85ZUZB_Kg~69(gVo$z*P^8Zbh z*SkFL02U7y2C#g(CDp$-^8s3Ii5eq{l(DzQpsaUuc<)xPy203HO#|y`h6E$=oU6y# zjSK8l`%dXITfmJ+dQzT0G&cm~>{PCyZ1hpt8O4clS4`Bn)q-Lv2h7qe7fmXiOE!v1 za^p(=00ki+<)5O-0ngvIihJ;&rgkiRkUVR8?F_NXca3~VXTAqX9vuqGS1K-PjTxSD zp%SOqIfIa@xIvz=0)kITrCZeVc;j2piB)>xcK@4Dsc5zZ09DA`41O$itgS(BxRy`{ zF~S1PfA!$q-1Ru6K0AlZiUd>|0er1>QRVm!U(n?HP=3Nw}W2^iCkS{AMGPlp~pBQ;Jwb~h#u=O3XVWC}~( zg5YgosFX}_+A=QKs82n_4U{tQ+Ah?dK;h40K|jYFu=L&XCWZPp>z$HF-eV&=k9S;_ z1vDPL+Rz3DrNc2Rx*hzs&TGwKxfW{)8i};^K;fqzw;uH>X~CPm4vCRIEn(*y^qKc? z=xugKl#JhJ4Jr9ZtlhGCGPT`LKv!b{$S>fh#Ycg{U>HDWc2Qy?p0*!xN33D44(NoE z=F!a+n=dT5dKu zU#LT~BI%KopUg&Df`oBftw;4fIrUtFlDfm-6(md4RM5Xyj)I zGGvFsoetkGpM4g%Ar6B9m_K1G)kDEVhL2qdifs$9Y01|QO@iwbfh~Hce3lEO?2U*U zh!K9ifsM4kN4*Vd`I8+j`XZ<@1f=~H!Luiv0)eVxFhxS+6mWI$$(RvGSBDGZ^%ai$ zdR|U8xR?1c0eF`vkprEXfSd0eFJ@Nmsi=l zgylfa#B7jh(4Nz8tsYb~H{#E))_u6~9gGEsLPA{=l&y0R}l_2|?s2-JVxxa6< z)&!3w98V}xTH~W*7|uHL%$~yc`8h};ft|sp@Ze<#g>mO`#Kux_eLuh6aRuaymp9Ny z;f9rx*sy>x@}iw;(o^LZ*vv_Lq3~eZzs4y?-kDIoknKMj`cDGrc7uv7?^G3$}4>8YbH`0!dS1jYkaV1p^XDhstGfNghPhu#`9EOID2as6a{p z@JBNMviL<7+9(L7r@Z-n91g)B!LjrELe@&!U9bVxkmAIOGiBvmi=hf8rihsXztN1A zKZ8XT3Y0yr+L>3`1+q5Xl=H}<0!8*KNR@a?kpp@d#l+NJhBlx&mdE?WII` zb-nNKn1?TS%j%SpW0<0b;ZNOHJ16=2X<@ZTP=WY+D-!8Ws?EzWEKoZWG~1a9T44PQ zay8(wy5@FT=Xc!ndnihCV#3VptUTwV=GpS;b=y%HTJDV0MyD*nK>_Ntj|^t&2}ZQsAdxqgG;7?a5Uu-{IG) ztvw%c&0FtlfS4jeB|Za^e|6-nb2R!7!87IhmU0>)C!t{YC4y-}l}wXh7Z@L9c^IKv zycQGRkCjwLRMHGi5UTkdEqK`6o%FT2w+K6!NwdA_RGns1Agj1IRvY#ZEKosNq)~|m z_ce0A%%te_w|g@JGJN)omGYXi0q2zqySM>%n$7bWs2DsB;P+91U2xsz@B`I!HyrR_ z6Kk0}@md+71y^VbzG6u7IbPwIk?GqBMq`Y)G)f3gJch3FJOo!srWqdYT%pOOMuZ+}9O6I>2_V-Uc%20U@5_)LxK;y2gL2qIN@Ame0gXEhXCq}>PTc=?Lt#*D$M1vl?tyMs$Q z;)El$$z5k60Smr81>yO z2BL8QhFMB0jGakjbLV5raFd}$JlE=Je-w9$Fg7Btt69S#Luso`C=7ig<9*HH)pQ9` ztIU)j)2Z+dkwHKh42t%;ckaQ z>b~yyr>Xwn=J>0Jf%GT*a{QyHJ$UP-Ht|>Oa>PEBs!ptGPtAqkWqWBzNTTster4Kq z_s?OFoUIYLm4RB4_PGKvJqBi_CSvrV?{&?c0=W}f*C7LE*enh9%7PSdF&jstJA&Kz zp_M28GMDj0#9Yn|J`I@iWRZ@w68!B1XG|R7V zsfT?+nER*ler__1P}*(RkC0cMmiRyVrG;o2fPDo{`=q6|8@FJ79m;Hv;l4EZw(fTj znV?2vE9|HA4sy!i#2Xs=Bevn;AE+|N5+P2aq6&7uSl)^ZMhXfWMLU6VZ2N=*VGdST zv~`lmF<;@hmzd470<4FZ!1scw<3Ej~^^d|n)iT6;_)^$|OKTjD{HZ zBjLopMHe?(+&nU`D0{LfZ`+E*y|-t$JtJUVJ_ucYDOZj(rm|XBLLPB`50_Cte0``{ zv`t%+$zyH-x?J3+C~>O$wz809`KP_t-Zbt@6d^g8kSCaBDU`Ay+>lU>gIu8N@4j%@ z;%LJPceUW~E}6P2Lcx3w^9ol(!8%w8ftz>1Qw=bui;k6lta4PJjAd^-%&&V|x4O>p?7FgEpG$U9 zBFcKWs@}bFT4A5sYE1L`n>=#K+eC# z@Y^czuUK-Rm;ueSG^XDY$me9(NZutw$Ng6*cm0~I7Q-bf=e0_Z_V{>j5hU&s`BC7Q zA8rp+e~ao(E$m>9fM{~BN_O9Sr0(LgGMdIdq<{b0y477*9#|kl5{alA?i8~b5YAaL zZjAM9l7nAVo~E(va~k8$M}Fj8r8Blq}6HqE_rQ8}$D|b}{GS&XbyiQj^=KI`_a>6EQpZ64ZjV>3 zJKrUuV%eVN*s1r`*QE&7GfjYO2Tpdv4L(xCoXUlE{~N{oVHsP(@Oh@hYJ4Xl6F^Q} z=u~x5D#LaF2?QB0l|yBL^4!kPtM=-GMfrc*d}6#3M<#^6AWMRnp^Ms9pxD0D{3#Yk z1c8&l8(^5^4&kfF2>F;kRxx-$Z%Ai|%B2%m{a|YJ-V(|OZl?`FKe*a;2DF)-&K!_|IdCz+_{Mx2B<(2b4s zGs+lvec_QHj7y{ykL6K)0t>0Io`qceWhISh!0^hr|7{|`&RYXlT_nNhzs#*{i?Y=B zY0qxqSz3*;ad6=W-G{>~R6q|2fvQ0CWCTq3o@IW$U%Y~kJ?oW5tx)Hxm`|_movkw= z9m(BC+}TSxn8(IuWFuaL(AP=GADw(wCpW(VW$M7e7jk(6A(04Jf#62(SM}w%1-T95 z*`N5;EUSR^mB`;8fx#zjDK3wWj|8cSDE8r)Znu=E2f7(=%3tU87R zqB!rscgz|VqPMqC+$z?5fMpe_#Od_Q{bmlbY+1sH%Z&;bb9_o~#$@5vcb>DR+&+1L zmTTl_bm%JA+P81H$3}KA0Cmz(bY~aY&BV{jZkm<#P$H9xJmhNSPO=crmf%WI&^0O} z-uIWb!nOt%KtZ$%31!#9&kEZU;)eO4Sa#viy4moM!CU#D8pFM4izZq}(AF)kak= zRMQHsjQfWa9MX8|rJ)83F)h#mQGLUy=O5Tk{rxvxogd#XY+vrAQU({;3pkCJxk3mb?isYdDZ$J? z78wYQ!>kLrf8hw5_ic__VZfkS>%)muFjsv5r()&&7s*x$;^?Ic)|C2z`4|wdYdFIx zA&GoOmmHLT?v{LeHRde~ZE9RK=xt+b7B9RF?@@os0O?rc#CDqJ7h&Qx#!%7pqhhkQ z_t7DW6bKrOy*RFe?P(}q6&?fJCwfe_6zC+q+KReT{@hfb$zj-Ms=1SCZ zvOLE`YNyD(9~^3G80}lhG!U<)FaW`ONYy_V zNj)Yw{+^^W2AP^dUmyMni&+TPF6YSkJ0{^W!G?dIIpe6DYo%#OvwKN8WJOcd1)wW# z6?hvrfcz;}PtWm-JWyWZ8~BJV*KV`biuGyp30U5EcF<-$t7P3=o4_?&7AgD$bX$Lq+v z!cT)4x`lxCjZLygGwO3#V`7_08mv=KxZV6l+Eh`(o_4y{RE4dA=bp;DV$2gCY!eP@ zJqm19_uVV}{Zd8_MGI>int~Osda;CQa~({0m`-;Y6nUej=_Tn%Qvyg%O(M`iP3y!4 z3gD4-;dUlo+noB{#C@<28EM91jbY(GfbrOP0+%eC$GyBbUMBkZz2#HgKF0>(g!;kq zq{eCk1gj8oEqDe1oG>u- zvyn+lJ>_GQLO@l=HI8)7hG$FtiAMw=;n;AP+tcqJq)#dBi}adRy09NWO}0aDC?x;F z&cnIY)9g3!psp?$5)P)Lcln|T_*uH6ujsCLv?1bOG>ViQ(4}uwgSJQDD5nee=E861#Cp`$=(svNrFyEilPv((mw`m3`1Bk zhqP{ir7NDdx_)=`arM&b5{{2zw;!(FH{?l{q0}@=%r)vbqd9`@yU)clnxHTjJ*39o z-JFDT)*e?(+TRhu5oDq)+x_O$7pbfQY)?$%>#On@H;cyi1fFWU(vI8;%HN=$UJS^1 zB-@|y?pO)5&0M$Hoy6L07jiIY*&ClWHMT@iTGE`tkaRT4g-Ts(?1Wvbr$Qjq`7Oc^vD8(J zI(PXI400{5v1>KeipPRcUj`yuhv=ZPC_&u6fnz-KD(#}U>?c#@zd5D*6TBz;x?8sh zAQGDBC?L#P52$D@`FVkwCA6bI(2VyWNitpHFINrQwbx{mK|PQ4AjRW* zRy60mgcK&3o9R2x!W4>W)MPoBYz`_aLrDuelgaIRtqxq4L>7DyxS=4t328TG$MSiI;CIFwoZs#nUr@%rcj6iEA`AN-EhEIopVV8pfvNxoe1mCC792A7vj zs-xh{s}`fWH$;Fjb0_tZv@HYwm0CeiKa8uANpI;!NGlA|9 z*f_PDF%kDSFvreRgbOAvA?;?hAChC~5x=LaVaK)@sUb3trmppOSz(XVaI`b)~wuy7stLtpdCrg7LLl zC4Q|)M=WkBV2ovD>bZGc-MeV?*v`5VGQ~VROf3P+KB; zV8KlI0*TA$jQU+BSB=|wg~RKq&8yGS*lj@cPS#Gsmv$c|1ZQ*(Q#$)*x7dU>O$&$T zfUc=Pn8A8xghG9oQGUX?)Jb=fQp2e;X7;MJGqWZE4RfRDL}I{{dMTH6@-WwDTk;;`NS08L5cbd5!E`aQIITs?=b+Q`-=B= z>lgoI;ncST+j-kyDFd{n>&GJSXVi5-#4U(=p*d6ap0VO_wmI@5nx&xx-xhJqzXDez zqY|Aqx~RGON|VTHrSS#~ay=))*^t>&jkzW?FtJi$x^#^mUIs$iAmwoV0Zbvj+?A~h zorX1gugmOytF0ru<4AKIm)0MyztrH2+phF!&k|=ca8?* z*967TQ-VRdb2BVFma(kukYl-&(=l84BlzWK=*XGuwKyvB7G3dWE9muL8{7r&kF+A;KuDvrIK5Zi2l@+GlhMHxP(Y`Lh99Fri ze2c;aCL%2um!9saDbPIHNom`d6V5G7c<~QYvEQ$37hYgNsk-tP&j>OURzvBu&iOGBr4z?kX*@uqyv z#)rK7kL$#P(Mr=2O3sn}Swtvjmc8vOF=7)h`AKP4V(`39M)4l#F=0H=`($JOsUvog zk@@eCZihU&`FB6}WRedT5^%YtC~n_KMwBocOp6Q!yOPAF8S-6xEUuBggwu zlHnCvYKqz3@+H};-{ic_yHuwXsj(yCKq6TsPGv3N!BBT0T1z74h>p@(>+X{hv%5G- zrah$+7!6I)#HlbO!3a1wS<<@<87$>(a;-~_v0cgn=-@%!xrrgkZt39aT(<7|2R~TM zg?p*k-x*eCIv$`-kBpH~w{vHr7yOHTG05)EOr$Q&NU*1^C)!q(!i#wsdfN?_S69{A zO|F~`9Q-c7+p;eOM5O2jVI0A&ZAdmE&o*obn);3V}Xa!lCdR!umYXYUFalM z6i&?FM@mO}&bpFF3AO>#iv>iM1Ff3qt0z|K9N@u=?W;en#ml8>fY`gRLlhhiUI;%s zd|urx@-t%fMBriW8CpM8^eflA^8|2w$cUZ-2B2liw(VZz&&1n8PYbcX>@7G6R;E^v z#$_M<5`-{z{J41V;`noewVBmK?j(PwqBuB@IVWU_&=fUavaS}Kj`9{ zF+*`OG-Hsh%Q0Vo(AJ-#Q|&IzYl#fL(RLMzShT0nujxAx^E?9QQbp}|Jq+o(Yc`#A zM&{Txp9BmM7PU$s;$6JeL<)_KDASU5_WsJTwiT@Kz#LaQ8u)F=@JQ?>b!?rXF9G9+ zA2E;yI*TqLXeb_kZG9uh0l?hr+ORu!-Ice`pzE2lDT^TI9vK@&Gn~ntJ+5rq{(%=7 zxu1KJq*y#3zY)~}>FJ(*%mRQZ#sDYC*{!Qc8`Y6OHRQbX=f$I-L8?VjCU>>^&&9*% zW}SB^z+pYj0b%V5f9iK_Nin}39vP1T?*jyErpJaZy@`WI(uTIQ-PzeTSvWlIOog$; zG`)vZ|4JmSrAwxM4ljH?9_pnc>^vZ*=ESLram7o5T5j&$pq@^X_cg_ZD55XKF4BCk zgH0T%EI&f4vhJRJao&EAy_IC9swH->&g_`;{_?C=>+zuo>vjT--R&@cB)p|kIB~hm zSDvS%eSXTq;FfRY9NtNhb(2yppGq2jE!?dK zw*nwH!hYrzlMk3@UKeCJ3gv^imCKI`q$nLiQ}pH4>7-CuT~)F_fN8ojd+BWq6N`#< zAu17N;~JsC5;k@ZNk{#FH@cC8#l<8x$l6-Nk9e(!+$kvGTHy;0(6+8rq zdKmoDF6&eM?$Bc1+NrvxNLZtmpQxeTBE_{6t+zV|WK}RuvMAqJ!4-~BPZ^}a4F%q( zlyciUiOF26GnOvm`OAa**F~(h3#9FpQamQLlXJb^JjSmY<{#38APzF*H>BHt?xen* zNgHvdRvLOKK@0{&z~)#x)XeTzWWdN(&_EL7Jd;$qi`~%YYjSfs6atX;iI{mFaFvl2(^fZNv+Hp=YY{oElf&+2p*purz&iaOfs6T!^&KIYsd< zGS^yRP;*^zA^3aKW5Fn!Wd`0?<_g^gP=^3SCOP~zR|o~V_7OWc=P(wh1Qt@?khkaV ziII>TNZvrn(F9HXm`iNuBS(cYl9s*c-4~RcL!q@z7}anh4u*^u6J}K5NGM{(;1xvS zxA^`uOha9WKN#I3$c)&VFc9>FWYWnkjdJC;902w{aR<&tqw43gXU$4GE~ls)NK7iO zYllWPf$esmC^sDs9ancd@Y!dJl#hqJi)u@^V=ak?$v2l}z}j0mSaIBy9o!Mzcrfc$ zqql(@vO9e}H!;C#iybh6e6dWGBzw#jPKk*CZNeP={Jy}fYx9;d9)9~)H-foz0BSYV z&FF(HFew)nU^Yx8jqf_Q6?qIZKjGshQosDL@kMtl3Z@aM-si;knMT`84ZdD4lM4{= zMU3p~1X*%3$YhTwuTI4avyV{?dvo?s00(n^um~xlhELd98tEm>#~qV`1))=#|2vqq z)YR>CmWlUXtm_eWM7{ipyP!_E5@JzxfcOg!?l{X3rnI~eC*y(ReVnthWE&ypX&00C9g zCIKM9;E#Y2n#Q{w*Zec?XbRDsMOc75OG~aw$e&*oxa@&4S~-1^>8^ncJolW1Bm23z(|&-{U#{$HF-v)p?QRXF7-lLRj^wf*vy;(f zFZ-`tQXy{Gwkt+_VMjc$Y5Vtnx60q|M9-FI6z`CKC0PXI=GhLLbgzoy#zDm@NClwX zuD*tIDrh#?DG?$otQiE(FMwgULRf5d|9le$M=wtgYHyM|{+j3(GM|#llH(UV*%OV> zX^MFtr$>NJ_+dcRQerF~K=!oN(9Qy2I-Dn4y@wV9^N~rbLuamj`vhvG@^fT5@h9!u z^|a&Y=t;;?ucEYaP9AD+VJ>000s36A(`JC>{$gl`Q82nDGRS{4FV*I<^Q7J7uEcgl z$EX~laJ}`UX{~YTB!jqB&GaV{AtFJS?fdq`Ud>sej2*v@EvV2SX0pLJ`hkt`FNRb6 zra0OEWGL}M6OUrhik$p%tT7owH#vm7DCwsw*eb!YGXltCy@JkHwc4!Leb&q1hA5&yhd?|D|*zA!XnzxbJ&T=iMc5|o4im(iWwpf@0`%&Xlau^x`w~f(rov;Y`yRk zA>RXCwFtjwr~Y$}(UyX?^Kdkp=9Kxxck~VBPH_cS32xl=r9Om7D|5=!Fhvt%6_7Sj$;iQ>J(Qj66CK;G z;Z83z-jFO&N4b<-9BbeNZpwFn9w#Fn-J2Q7Zqxi~isho-wA?1&L#&9{mU*ON)g`w@ z=mcU6Mjzdy>}VMauX(HqfionMLX~cAF_WH3`J*K0=ROu7TtRnwr4DYR0||;kFp>@^ zz0sxRWo@bV?i2Z<`Q0#({tS^ywHvggwm(nUmK}o5dvq_T!`q0T3VPDwWz?78ufwcOom5tUQJpn%e^z|huRmgm}(9>?Qm_v8HA7qZC3B~u~qc< zdZ#1O^f!Jmgx(s2x=JAW3-l%eiGH&_F-CCNjbfuz`d*?E>T8l!Ux~R`UxjHkDS}bu zEL&C6Tlbj&olTG-T%=DvEFj`XHTRa0Mf%uDzja82oAVlns(0ft`ME0y6X@oBNZEW@ zLniT`w6IV&uSPsRk-(+Uw^v%$K<+Nn%=MM&NnaR4vqy$DfBD;urlx$K#-F_VQ$mJ zlc=~U1Bp0rkcuC25~SWn_7LexIZu{|qsyYTWZCaU!#>g{-X-LkW=`(lp5DU@&-k|{ zfCx*(fp}7I3}V=)2Wtu0Wpdoeuev{1fcfi(<|eLVdAqS8U% z_IrGNmlEIJH3g||1U^CoI)*F?KF{&DoU&Z&Vl(| z?4&?NP^tF4ZScm{@^=MUScz0sB8{o$Dq6AaH#50M>VhkH}%&h`a(oqAFm z(2n9MqLPhvDO`MGsBN^>{vNi^{jnRFMLBZ03|99?mp`{9V&YCaaQ#y859y?(pP6NI zJ64sQqKfj}4PBWPo4oX8xstYE0egjIL5okq-q5SnZ)9xHCmnOQ^Q#R3{fWCYv5|kZ z!iPp9Q$dBWg5)E^j&zD7bfCiekPH_0^EP&p%NPf#w@zWA=WR+)`1FjY9Yr?L|6g}- z_rEw^ki)7_L=`B+%eDoP0J%*R8Y?OA5kfEZJ$e%eg{ z@h*RNnya$6%*p>Q-v3N@!n9nrdee|+9wmH_j#7d*FYoQ|GvRdZ0rUff!lE2NLL|I= zGvri7v5!F^^($N=Q}0Q%^@abghDUU}fgK)&OaIq2D6!h?coT@PZz};=R|U=vfm=EL zqb$zOgJ^E}(>UD+Pj8m`ScPG*8SNjHA^&}ZMQrz9V+zbS;m4w%kv_*@U_A|^t<{}6 zQ^!tVwk9P( z8z`WLr!$(2PdZpk$3HK^v6y=~4s!#1E{S)WLqnqlYFI=$9kcf<1et%+q~pq!mTIL& zYQ(8b2m$xqCa!lg&k9HO6!dljEG2yY+~vlxvnH@+$pF}z6!WLwb($kWiUlL&e~0)N z`YBEP$M{_F*v!c=4;yz(o?o5%EO&MhsS7!6V4mL!P#}sTOau;vs>CNm*)oi>u~`$T2JSZ?B+Gx()Lz& z=q1F^r$}W{TL*a%BD$NO>;qy3C;4bh_zqM1yrSyNcHA1V2dGAfd2_N(7IjJaZiAoN zXy%9WVs9-9R~C&~x@rjO7+7mpZv1j;P?i$oj3F3Xd}~$b16qA5GHuu)D9g}KbMAdd z!=M`LB}xl@JRI_Or~Qu9$oEFkTk@bJvuh=Bq^ksv@z=;(C_F&$4$y^!vu2k@Lp~ei z6F|?DUWBHzwZjXvur>2oARaER!?~Q$O3EMvFp6v5OO#rY4H|@)nQO0m253k&4ggo& z0WfZJr7XN@PUN1-9o3Yg4Wn~@6WMIk;*7&l&97w< zBdO5d{0%*Bw2Tnz)$YP}Y7&%NpdR915c^BbvirlT^p!TkXR89tt%9?!n$ ztIH52{S|wYyBq|mA3*B~H@+u#8Tu7Q@V}+oa?sX|bcm7!*Drjql$!N6^yulA;Su=s^jiYmT-*dnuoY%SU1C74w4(NFh|HGY zGpB!71vH~viFl11{>^eBE5Hy|XKtd6#Wj6Tg9+GaPal5Q9|S8hwt)c`T{kHqM%sQ; zF%tsh_Wub#=c_QOFaypYt;$~n9JC!1)iW-LTVkf03v36;0p^Bf&1;D1m8nyIqKc6z zJ%50I$K}NsTKrt8VSKnt(LEzM+mBF0&6{YEByt;MZ%)$0WRMf>``H+f$Srbl<1pA9 zKNF$uG@>maYOrtFQ5ph!3Q!hAJ zi^>Xw8ihYOU3(OjYDL}ubPY*hrC{v0o9+kYG5-sq5P#b-pQFmh(W7L&u6hvy$}pxS zxX?UHhc~M%GKy+DyZvYVQrkCW&k2q+X9hzFsw$g!ND?=z;rU-80j;oP@dpGl%GI&^ z10K<+942LL(~Lfbq|Rm$BAfRemnG1BH(k?)2jKu zu~hR=h%tzi?ZZx4xwLvr+fzG9T9v@paZ}U!fb`!^QG(x7L-W|)&-g5uWz})f2pUo6 zy}wQ;y`-Yzk1f}8(xTei?Bq=T&5Z2)+5#qhA*i#kd_hZMRv0GItLp#ZfFR+Os+&nj zj|hxvX^1JrpjGIZ;_HIX#%>qAtntrEsUli*dcBY zav?3wQMbzvis1W^BiY%yn@t+>gR;Sg^GE-IZdnvjTTuXAy5meK`1%=?TrX}WghQj*oz+03i%?_qp`fJ^c?U{gOv+9_HIAIU z)}hpz53^}PF%i*>ypruQ<#Jy4T#+bVwlbH|E}E)7b_A3tC_3*3sq<;B0SZ_mUXeH$ za7>%V2twaW4b&S%NE&0CN5P!4K*7*0r`PlGR+Bgaj_|6bJe}wifE)#iW#`oRqcDmn zC?TTh02gf(_DF!TDXDdof;GuJi8G;Jog6SMakRyVX)FKshUOJOACuV)Z$O`Wh%yp- z*4#o|-Ps}Xs02deCrk1l)pvrot5X`FL5h%NPWCLsNX2e{)4Zk=*0LJs{C_s{3t)FU z=o?h4StxQ9QJ%3<3r|6>loSYn8xuL0fSlIY8^5lwiT5WW z<~{6UW?1`-989_bkpzNlukd9lgfv^`c<(;Yr?&AEfGpT^6#hOx2;Jsx?J@R{qV{Xd^Ys2Xo|C0pGDbG3d6t_TdaxMBF_OT@g9le zyM|_($a}6#QPlI0qUgx!A??y?-2!#?cKsLox%*X?!>AiX#w<f$1|qW0L#2H_JH#MDYl8=^}KhqyC?*{jOwJhfenWTP^ajG63!$7P@9s8_0% zyV8{rfeIGvu!~&WG~8qx5R9`VRc022>Y~mwC04aKiohR5d+hLPyb**^r}~$4Um|WB z(@6y4TuXG;58F)N&@LiUgI`dz6Sy)J5<2nPUo2~p>crM{>a9w5<&r?z#`wRhx#2hb zililF#Pjw4rq5dla$x$pxFFl|-0_+DzdnF3FS0ET zBEpdW!+oWwjU#-YS$;^>2&M@MrgO`Y^gdvcbe?B0f}5KdNT#GS>})?I%o!l&V;0?h zg;raHM8wNX(Nh=9>OB}OTMM21TflT!JeH5oL7{jso{oii06Hw^cYWSJtR;0+k>%_Z z9Hjtnpj>X)d&D}>el>yT72k*zxZdNlCh8y<9a2=T9Y#(QP|3z6Q|*@OMKOQ;du!j-g)5~Plnc}TrVr&8*=bL=fDwALd6kLdEduKwQE4^ zC2{iW>4(&?)&`O{&F}f}?#KG(Y=a%f z8s!mZsu__vix1yEvV1yA3UFyPv5@mY<(KWT4j9-AwHZKGq*-V8EvcM4k>=#=jrDAk zD#K9n3@xyqP{4QI_SF}c!G`36e{}ao^&+n7w}>9uCEsa?y`%iB@RK~*T$~ma3zXqe zMp3Hn4swj)^x`&C1vt<%8SM)%y^O$lxSFxPC&JyQD&Fv%s0K$ppPJ?cYS%>{UQW@~ z6wS==)Y)~;lo8j&EFXu)Li#ExD)%tLsBfqL-`>rR;;)tE{5*4VKAm5aNWBj2%c4~w z)03y0UtuLgEs&&E8TN>s<-vYiUs5~@0G4G|Qr83qFOHit)Tnm}LH1H}N4G`HBI7cyvIV-=8GE{raxp8N~*49h6iQZfsa}U%{ zm-StqJ2Nj(+z2(X$&ox@zDhxOUMZqls<6=CT@r*6A;?6DJa~Xn-vPiDK&x7HisAlQ z>Olp)akOHoI4Wxd+?w?Sd*Fnx%U?%OTlgqv4^^=HhB9Gfdo zP%j8Zf7T|mojTHU0pQ~ZbK=|QPJocs2z_pWp zyc9p}E{Wli{7DSP|E$yhOHeI3nTNW(^r~DwZjaeahR^Ez2E{se#;TR{U?BuU-zuIO z2t6-3QcU+M)$|9=H#$vYWyEfi7s7fHU8mu__ItVMHP(Abxrv_L_Q1!Q9{64Zm)70v zNQ|2YDJ2Xy>1S%G&w;oxjFIC>Q+b1!4!*QPlhNS->%U$`x@WONvr1B*^z#6VB$d6V zS-fgObZcmw`^cnvfBqp@5{My)X*im)a#Z;-u5u++h^+cOdn6=x8sUZw6CH}M&H6#u z=KwDJMcvkcXW&fgu~@vXYF1e&jzhgb{(i-c9I~%w5jO%5H4k?+i?n#{gW-vB6U^vb zew**hs}0>o>WW%K+r+K7QNBR*>#ycx*>LH&1Sgz?gG=&bnhXQ45e=*b#HS|?iG zilLik$m&z2YKA}fR`s8@@B+Eg(=o?Z#1|!o z5GFc?1*K7KGQ$Y8LF zYo~5cq%Jd|N>!Sgg#~7;84=Y+=`X6TNO_}(0XU{<69>(^&RMdd%B*(T0J@gVJB0e< zynq*$SO4}WeQmV9ow>L9JUL2`N;be+nq(y!mb6GTki?NxPlLbkur_jJ<1 z>{-?|lS#LSSy?L|D(EA%hT-w9Nb-Na z>_<~%hs*Dd$*f?wNU-C5kDE0@VgJ88M>7O#mYrOfz@oUgHd*`c!@yA~xdrSBYDnm2 z*GS?tMV0(%8`g@V>%^u3dd6*oL&b( z&}5{U+VxGO%(@j)xj{gsnD$FPrL9>;t z>j|*hU>Uqhb_eU!Ax~T}Dv{D;^=mLqkz2&NRKl<*@-Ye_m?eg>1Z$k~vSZ+upUsAt zNM`v2>B~VzGkSL9tgw&yCp;o6jlQvk^OpRN)0mGszaHo{f{WCcXSS!zrTiNv3%eI2 zB0V-C_zuA&l@0!v!T}l=`i0@eb;T>C;mT}h~o z&2oRXBNU`E9dBB9848_9l3G0b%;!UGY){R4NnEP zaJri7784|n12lCR$E2dN+$AS01R>R00Iv}NN=rzKR$%^EytdGuRl z_o9#V?H$rHW*=IgPk?)t;Neid?GVHC!2&JJ|ZzX1gI$Q;j-6iy?=-hr_xzxYSmQQm| zjm;sm6a~1^PKQI5gveTsb?;=50ca;zB~~`FV)CFkzk?qfF4L35;_p+`>(*@8?A2D9 zNLJV2YZNy)oIX0RV0$Fv5`)z7+FgFMKJ%;VYpw{DA*;oD5?5ww;O=p>y6lbo#+>e% z0OKR1VH^fO7uo<=5-n{7qwL-ZIS8PhQN?q@l~(y$-kSGr>A8^_!|W*g*mgN3tvWsn z%=j#;fBtguCCWU03S5QM^iO@HF=646nKaKopL*-n8p@Qa!A)l0`YttE}lN4@B zM_o4UodDYM>}t5?RCflQu*)o>ww`y;PsJ_)%a4QwM372AwgfaxS@rxzar*$UNyboS zQ}KiJ`S#9K`N5(9bp8pgKgkSJk9LhINach7oN1Ch=i5P$e~gY=-XR}IQx<~;C_N41&w&;@m>*gl_W)JJ~QehVIFU&oZW$gd6u{gRKS=;TqlM0MP+hZ z%*x-S+3|QgPvGohn5vKxEIm!jA(^%g3URcY3VxBYXV(+?e>Gs4y$fj;NChL}<$ecz z!9lSz<$y+x!M$;1nS;W`L&gH%BIs z@e=Dg(#mNv9nt`t0DzIon2ppp_5O~o$=~rji?DS9h|Rk~r;I+LBd+Z2DqaqLO^nGf z%qfG6f`Jg4K^zL!2eqEOt%k%7R#Yv*4QIIvXXSq&KfeHJVybG3O`R4&j6{JKHEwNF!} zKW!*xTN6Ibwqzqms#O&2xNqqGT(GtU4bo71IZIS7tb$crl3;-ua!G&Ht5qUV2M_X1 z!ke84XV51We8%~oee074`<|)0Lmoyoio{kk69a!&eKL4l&dk!3%tsU3CQMfC`p#@L zX9eT5L?a3$NPm}w%s_3$NhjrQZK$|OAq>A(8B>%UkY8^0t#$eV@5?~nd{#q)yQKjh z(e|@0*Wg#f`vJ-D*k5b3Lz@=OWmYlj3u2y9ZU%=Vv>(5tg1eX%D^dD_gD)sQRZbWp z&c*oVvl8M+Ht%yv;CS|grN;j0;|pPIg-a;D_0;H83C=c2noE>y*ffH+6jR4@3h_xg zI6;`V4p5c(`dF_+_D1Xc3X0ZPo;~s{5`NLz3-360#R<30o3u#K*ZF7D@^JZHeK>5= zRwe_PciN4Teij6a)XDjCt2OK5U7tdLj5@=Bl7EM5Iw@CB;`amvRf;L$-O$NZ;?hqk zU^&*gAeebhOgTcVecPn6RZSgnlzxtJ(21w5Pj5wNwWip%4Ey zsluy1&Q^127#FCaB?jxA*Q^tZ^Kgx&_;CC!wIhrzC;kVLlyF1_P5Mmx$MBBO!La zVi!nv)adP<9=ecrOCl}e>mn}Ev@#FzAbB+@3$mU0a`|z=$lIs!~ma8{uGqa*+!cYw^?ImI1^_V2Sd;3U^ zMn-d5K22I-sj&ac_sb;Z2$V{%bxT{P!Am9b;~_2vIZ2Mr#5>94)?kp*TL1!Pzs^pN zME*ud1rR2KJh1{Rklw90%RsJ|O9YIQasMVC`lUwwTXr*#=HAWp&nu<;Gvo>{_*pf{ z`Qg{8fXfSE9T!5@HVftcx2&6^mDS`r%aSJmR(wq;M*K#{lBPbBYb!Zqjw;q5&cCF= zr%P)5y6Qp9->yD8V%7%AaYn26ItYJkgaeTuuM)_SemFN`8B+@YH1P*%gD#KXyp82u@w;S+rGyh=V&fd8EFHxLZ3Iwgm%2Q|6q_G z`zwy-)@;)`r^XIOZtQ^=H1cd%-2^u*VKWn7_m6Y`u5O{)rwAN)WOd6o?Vz723E#r3{q4c>2OS-ei?<2P}^%` z=et4%9(H53IwMH3bSOcs0{ZeB(}?)*!jWT|KA==6F{N?< zkP>8Zh3B`SBEveP(}3|HXPKS8y`Kv@VHu0^u5ln&D6&G2$df=h8ok-B&2g779&El z1NM!H2KD#Hi!TyOlPwJoRMI4;_VvHfF)09B^-({#lfpD&Z~nSLYGXu{m+fdmt1t6e z!$IwFq)(TlLG!nA5|;z(eG|oD#j1O*X*3goxl;mk-|Kk-q?}RCaUlet%S=q?5$%NA zW?@4fis4Q2m|Om8wa);CMS0KeQzGwx2jHhPb z!_TeNt65@DY4QkC1S)FAr2g4O7H1i&Q+t|gVEB((!?jiF1WOuPvDJph{XepWEVfHb5pq+9%m`KX#e~@3TJVV=LyNMP4X5$u-*8#j0H&8%*0Q^V> zJ3GU@sO{X6)3KU^oLNl^u1fH#;_EKjHIk`s+x7}yz~6(F*C=NBnE=qY$J>$5Of(VH z1o5-U2}|;bN;JB2lyf(#|1+$}({|ZTqJwe!avx8>t$%f=kS%_xIkj42C*Na==Iv~& z03$%$zo(B=Vcd|4?9FApn-yU9(-JUj#Sr5-k3*xXLPq#;{cMhcB$99g%?Vv@`9_mz z=oy`@kM!jIjb~11d?4~F0*qj{`rr;pXubMo?uVt|{2G;hupr>5;}|FHEPQ5w@x*eA zao@MV##ON_dfpw7D56=v3DZFu?Ssegs9$2!_O=OfO8O{F(O-3j`3wSre9b)K^E zdDz;ziE@`neoZ~z)QHrs!NR*!jhZOk)gIL=!Oz99UHE%vG(AU zE?kB9E3Pu-ge>#%Ilj^4NC^2Tgg}=)!EKTG;Z)%wLClQzDZHVgqg1;$y0 zE{mAk5KMkK%v483ZkiG(tyz+l$B2I0-~qali_&qqpvY+PpNQ`Is-7aW;8FioeMa85 zdB<0Xi2c=CukCZgzVB-%Ak=qvQBGynes%_%{WkVdt6m8d?u}-VbxbkA;(_gTUA6`* ztqk#MDz$OTe-LlNw6wIQP%(l>o-kyNZV*>_?uJxh(rcDJI}e~&+3Wt8kv$@}ag&#Q z#>SVW<+&PvzCq(Yb|O|q!;z4!ZC3sRsUX(JHZ)V6M3Yi!TunjD#$uXJtdHXq{{;Tl zD~z0GGO^nf6CLZLA^1`xHkL{nbfOHczT7Tc%c*$5ZQ+o5K0`>anSz{f`{=o9nKUkT zhgHYk9XHC=*sN4*HICYd9EHZGn@*QF-0!q1OZ+x6tZ!v+9hX+m`Wc9!w5#Jq0xEzZ z9_iOwIgI2v5%wXM4*Lt8mA?$vl>r~Mc6Qc?keMpr!-{(o9A0>mCaIOPq9`FT z(iM2~^U{J%+4XpTMC)Lw#!);m$9T16HrBLX2FxSP(8^0RfE7Szu9* z9gB;e<~A;T;7yMxI)W|~e?#Vr*w5#3b693%!s&q|1>-_KMBhA%wJAFlY;hdiP8q(QsHWWe@T zr(_So$5`uhS5Cd}dW2}iFZ^yEP_#jHZa<>e7;Z%dYKTkYezhilmGmFoH-K_LBET&& z)`U(eP&wz!-TkMPh^_3|P1Y5M#WW!}5zPxOTg+qt!Rfx5f;2ArpV_$ekdi(|;p3a8 z_r+0j$0(Sn=}v5PSDrvPXb9x!HD@>Nr9KqohWVY%QNaJ!5&%8CHy|LMjT->sv{~0Y zDA}J%lZ4If3yhFlDg(f0EH~B{_TAcDupy*wNgYyf9u);TJW&o%e6+X9zRcx=QK>|R z5n9B>ViTH-!x8OHRTvHs)Sn6uj#ML6kAwNwNe8hv3aSD1xS{juA4M)ed4k3vdW~d7 zw-3Q$mv3l`YV&4Or0>p)ANC#6%uX%s6PM8HPZ6^slCv>pE=zdhBj@a?&UE2uUPZpNR{Og6Z{VA~`m{NF2{3udi< z)JyVPs7$vs(WQyQ!>Bu)CC;XOVECK+!qUJ**a~Y`s1yL`X%0YL`N_8+Fue`?v_Qbs z^%f!F0r_R|NMZb;^#gU}RY5!&4(kn~Oi?@4r2>35CT!@yA!P+PWtEWE=V?C!yzlOa zCV7UnrOf)_7b6zw`nU7h8{N9#;KKHjVM&`^qnn_1GkkR#d#6?)#MRQ*f;GmQe%WRROz^is!uP{dc_`?KcD(ZFZui0yR^{qWKnAT=73#3vsFJG3A6ykp z)!vc9;OomWcj+^EsB|h59lrqOa`j#{kAqNb&}z(RS@dUQw%&~>_*S{}QYapQD|o+O z>B#SakD}LJE$Z?K==&38K8MTJJ9rBm$byA>U8H#uA-t?AveIG}4XT_cc57GUj-8W; za>2-#C7(%na;ph>8~?#bzP?$< zWr&rLusktL;{pw)UhK@IFBTN;C+^Ks)XGxnR$w92jqIP&lm zH=YQ>67vj;LWP7#RiEH}E8#<^6$VT-Xe;&w$3~ycnJ*Y=Rjv{{ z5&$sEc}JrFRuM0AIi*E$Z_uW}XSo!hkFl$#$0)n~e-)O8NL25)({dvLTC18?j4UH* zE5Ff^V$C4(>1fm(a_R=V#$q+t(`Z?ZdxM&;L4P+wYxLTnZ=Ha}c|c%*DkQdZOCw)k zI&QJr(SWMGFYkx(y>i=8`woFQ`$8kfWqMVQ$3ITSkFs^NO-X-Y?3fr@B?MB~0TD*> zcLm+nyb#{-wVzd3r7zhz6|y?W@nxO})+|XCO)}T?zE6Um`o+aqR&N|5S79<@TmRD} zSta-7MHdy!nQj*W=yv8tT-1!g9|oaFx*gT-D|Qg za7sULiQXz=LJ;PxQ;&o!m6I~$f~kF}^Es92A{dLB=!*ECm1lzI5+(5k-QvE1rCepm zX9?S!qN4oyrMWaU_e(S231W1~g6Z3$gxX2mnxzcOIKFrjmYEZHa)FLYSAh;Fg)uJZ zoxbZWktfWcZ+Xj=+PQ-4MTr0&s|o_x{$=F(hk3t;emhCyzr~BcY)K(Sv%VI49G*{HS3IIC5Y8BcS!Wfcz<@pP;5OmkxunEOA zLRMLg3$oXe0fLaB=FuFAN;KVek-NPuVm$ZAZ3mssQ;z!%f$w?5)bpZGj$tS^&xTJq z(|q{M9>m3sE939u{sQZyEzSChm0B#_`}6i{R!_APXtzh@KtM@>%xVbJd^_&+A28)C zUh?(a??xc!|2E6u&jRVtv+jJ%ycMHP03%+2#Yic3b@fH)zYA;m;QE|8 z%OD2Fmmnf87(4?&e0rv)S`AN&=r~|h)fCa^-p3L|KFF|9}+p59z_G~w|$5gH897nXB9Z>Pj`~D(ftuGE^lFR$@GTk3C~-#KLfb+_)s zRoV-h^lP~(M-z?P#gkMjDHwk#64ST-ef%?y(q_(_*!(s4Hq#-^lDNKM&iORQ{)OR7 zyx0*7=wCO$PkI|l9A7@z1MMnjc^mnmKGuXQ6q{LJv~%MZ!eLYPN|}DpBl)Z5`y`n5 ziTB3~EM3ng(*m2HlqXN=Xq;PhN-@XLvn-~i*pmXKPzcD->CRjZHaA)2MJHiznn~Ex zkkEK1RpgAse(C?W8b<_9c%Thh+m51to%@AQIoAWq9-oN-3v%Sps}&ifMe^cZlD*sAMVHTmK18~+BO z+6xry35!M~i&Ey`Ld35%O<9DaXu5@mL6X6)bpl3K*P^DaWickUPUdgxGI<(eJWCcu zPua~gYXzP-R*}~gWnufZPkZxEO|FUmxeilQdaEx8J-%Itqk+U& z$Ibf31u&178V@Sz;gFAhH+>B{j9H&pBk-Uht{*04vX}P`k(JfA-Jthh$Ltm6=F4)} z1CMv#YQUW^djruatUZO)9WM#WW=079>F|Bz(bJuex@hleb-^nW{PL9plKzqOn9YHP zP+{Ly9imn-Cs!;tzsln1_iQlNx}aLdPcf%)Fj%l%Ej>Q;FsczTLK4N5 zm-gkx?q76a`q(RwUqnp6&j3v)M6Dl4dgX};etx}atc>hmS>hE>0(_XJ>1f^k>V6Hf z!gQ%VK=2)673UwLi@23Qjs9JFcx}5yqoR-Lc1hqo>uu6+F!PFQiKFab<&63hFTnfZ z+Kq{mBw7Lfvs}Z9D3$tH%>l55ojG-UVtVKDkB8RKz8oB~d*@&=cq6wZ!}Ki4=n*Il z!Hz?#w2_Df>5(K`Z=bQV&5p&myb-UP6f9&8k5QT6tGZhz)3akq){p{>4|oG^TryU^ z3KR^X!PaW39d#uY5iQCnYdt(-C&&sn0^dr!7!;#S?e|G30pRDY#O?kglBUDYmsM`l zdjIMtoFiI7*`%pg^twl= zjtDv&)`FH9X7HqwK&0{8NP3>tBQ#7Opg{q4%qLdSupF^%2VOt?Dig$KVIteIe8F!< z)sm*3yIJf4g*V%!%3RA^i4YV<=&MQ<~s6lTpAjMCH5-y{!v~v>5Wkq(|oP*iYC5O^LU_reU z<4o0Ab3rH&DlWc+iIDHp0C6zr-o7{VS%JNlG_&j>-^41@u!= zw_2{f^WG6pHOp}H^3VOI2XjI+F4i;`-IUcnRQM0fpK zupx;p!3zecH0;^8f86;myfZ&VSAS{x9rB#($d2?R53Lmxiw>E}MkQ{)V#C&H^W@3} zN#ZRH$Rfk1lNFb;X?(2LBSyRHQ4&vkR}Ms*UT+!BVbwGbOf}Mp7eM3R)xUBl)}HLw zhS(`@C97OFo1wffoeCI$D*=PjmCirklfBAGBWK_|PL|&mcaKfXXNGa;3>yLzEY-$y zD(ufO+HZ3~NC_#ZI^&G=HC^GFH6-e6cdBEf9ET0B)huzfR}r!-@O#KPm`u=5(q=ps z?0qJ$uscTS(+07mgqb@<00~%ut|@L{X36t74oHf~@FU4ej1Zaqg|FKdocrnccy%@l z7pVk)ssX~(NI11Ia3{2IY=P9PfZeU)Qt*Q?Jrh#oha!eiI2+E~ zQ=uB|CJhHi@V(-?PMNFxrFhnYCA4Jpr{#`oCU^4N^*lbbl8<>C5>N)3FB=j)W8h-uGpn6lq8*1cGEM)MbCK>7qC{|Mdm zZz=tS6;sfrvCM<2PcJAz&cd*%n~l))fa41UTe8_h=iTURUKO{Ms26{+XVTuWdpDm_ zKrK2F^=SU2CA2PMirhb)KClz-0}UJ$BFLfb+bfV|a&c4D3ORM8mLuT#guDKeZv-sq zypot#FnRg8h2$a;{K=9LCd?hE()JkdxVq(l&AsNN4vwmhR9zJ{6?I|iJ6hrX_c+Sn z)8GrKD*}$y4P$06FZ-aOiYUdR;QC-9%a|0+6huszeF2717H3Ib99Aa)?9y7-_sjwg zZ+)zXBHfO24|hD6S)f9o2kYUWTMBIjW?w11w@*K7H2F__aauJ_1anwMyk`>co6(<$ z+lhwj#LALJnhaBmR=NsK>a!V-`RWU060h^4kMn5Xt<75k{6HoPN9dO7V+MW-K8C%h z5%lGPCYseTV{&B4`I-Xl|7NkVy!8+r4w2D<>s#@w3AHG5+70CnF72z;+)7m0T*B0_ z+x;9>Ce%3$1hLsv8)%9YjmUANzBJ&I4=|bny+4v;xaWD04sx4ZB@7JvdY~Pu?az%T zc%}cxA%{`X2f@d#2TA}G9?j)!bg#60%F0_|csO4f77Dr)>B@nq#?G+ycN9;ZeZcrC zYDxbdEJ6ZP09>4^&nA5KeL_18v);B2& z^j!a!M9@4$2X8%dGVl@Y>JRly{4fQ59a?(qpzE4Fga7g_pu_Ni^6_z9x5~={aXNW zUC-(sdKIX5V;GD=@^P7*Qzh4!n8OVzQVc%QXH12BvChvjF_b-z;?}@LRu7z<&>~26 z*2PSpmOR~DQD(}~t1{SX_Hz_-T$pEbP_1RSfeFMagXL&szQ1ecqGYDOX4!yJ(0)2J z|MJO!>CH@rNlU+jhC@4Z@i}ks25M&2qhNew0k9(NX}|ZMe1QofJYX_im4NLb^S!&b?Ry2r z%`SVVpamWg6hd4KD{r0Ee=`3u`rpg!CT2kYThDO*EsHf^f`_jVTYAwV?Ob1wIrHXR z4YDxz4hGCqpY+0|6+@VI(O)l3YqlrdXC zoM<`1`53@Q*}{72jA~H)RC9S#P(7>x%1NpvFQ3^W%`Db{(0DAhKC737GhzR zC!^fX&Ioa7S?Q$=VAYd~qc1hK8`UKHvD~4RH>8$}mxu}y6fOA}L%OVlh3N=V;+v+L z5Dd<;0Vi5_&eZ5UtF$J@Jf2mMcli+cmdnAEKyz7(0}$W9YaE-# zIOWqIh>(b5|COtbcm3kdePg-lV2SMhLg{|2X`R^c2O;AC-dSf!#@p5M?A-fdx7GwH zg4Y0NTS{z;x$NarOPtp^QAYQWe9){?;PRHyW!gxH1JCE#9@W74)xrN`4*<_s(jw1k z4BKSkHEm#d+9L&(=rV;{HbSz`p0VvQ#d$q-p#c%rvMVB)pCx^bpkOlSybP_ z02Xa;4l({@2a+jbGp*>a-z)j#MzcRm>Pjy`2V?%`ystil)w%%3(ud5|0f=Y9Uy3Ne zb;vbO@ewdXr}UonLZ3(Fe{$cdm6#vj2UI^`mp2#Or33Jsx03oAiDk)TjbQk39y03G zx=RNNT~@xVpNL7x9Bd6vCaU?w=@WvV%CY$j@ATh5kkpogRnk~JvWFEJTgMZiAqd@G zt%_w2Z9C-#+j8I^ageb{^k#X0ar2nJpDQjKb7kwqmIeBoo$1}l|VWIY7Y~8 zl}bGKam~n-e3jNj^5@nLN7FT$jLKQvYspAxzp8H&wjGPx(Kb`CnM^J zevJBWC>AyF_5rZ(4)ikQ9zNf6ics}ihJ6s=)MTESHh86Y9AAkKP)otF5Wh-U>s)90 z;y;Qjv&;7*PP#n%HKTECC+~}o#1H?qPSXWy+Fo`e4GC{E9#{oKY+|L(o0vN(9rUClxu88Po*Y#N>uP*{uP9?`awL1v&59& zMa67pGkDNp_QqC%d27h9ZIS(bM+)68MBHyCd8id20%gj>OTy|eBoU~7JiC8~EM0Gh z{6uNR?QO-Vr<~5IIbrS6V_vao*MKeAm(Yz6a6PjF5HVX^n0M{H$snfzDs+YfT1PG3 z4tqAJg$%rfw2Z~yTtDTHs?BkI$I6o3PrVXzr*w+@*sOMPQK%7`^(uQwRFBUmjw>fi zB&^F+6vOtBFj4)7ds2ac9-f*?PZB-v&%R8mTsf>Zj&0dnx0#b1^}MczZz)S&5!<5h zlPWTmO*}%s`sks#@dT)HM*EgPUaH69=Dt)6E~ZD?+1Tw0n*^vc13iSuleGg@iegOf&t*XZABR=I3(KSE%`CI5#1Euuo-7Ixm{+=;|?@BGknbdhB+xD)VAu0{P)|(K+Mfr~rbdeRiyLyEV@Jn~+(! ztx|j(=A`@ZNaCe0kEcBg&Gf>0lWxJoY?l%v2AId7Yay3z~GH+ysyw1@FVm z1uop+w6W#7!_mUmCKeez?=~w%LhS6X>-?FRPfE;zIlu8QvVphydmTc*cO8SV9NPaV{`KMx@O-|t?c!~HRBvovAeZZqbb|xg z0}CPP7o+VIT57^dl_e**acnD<7!sFA9g>T+Dlwr~Zn1x-)~&BQtJ1*jEw*NTwt8gF z3D3^Y@#(2wVqxzRqG9cZ>V~?f#NC(0$h+znm6U~He2(nqAu&KL$g6apFRY&?e_r-! z+6rMklMZiN)K(~F(0wxVGtkNgRaM)7^k;_7vprJ`S5~gwZG6e&(%Y{FZ-uxnlwJL2 zQ8q^emKT8$K=aaY+tO}_yfG=O+~)%!QpIvI5n48#&83zIs`e1VCi-V?oigz19O#DU zBTrWr#>is5=oxsAsVaiBo`d?uMduH_6osB!f^}jSsJytub_*m~E_|QPFxZuPG9S78 zG|>Cn1Xk~UpDVfT3Bb?N#7XoonfaqiIX!KTpCqNF2WobWBd~owi3-{xHKQ1kMkuP8#9!p|jN@EwpU+1whWYbg+C zMcyZsL^M zBc-_rElwsoy;~S0Kb~Y?1=$k825@h47c;my*fXE6P+Yf#RUStdeACKi(2z93T=)32 zMNmjsHoDY(gO%cjTS^SjQ`4qZ;-Ip|zb!(6gt?5AL^k#GWbjuM${#$%?tuRzBq-F>i%Me%A|Xr@%7ld$B_7`->ka4 z6?4lhp1_`b%lGMl;u|oNeB#2?Z4MV3MHEY(H2{1=7JR_jdJypd<)RdjlF|QEq?k=(>0gZC;a}^hl?$a)$ z=}*Jf(emcd#h8WAwMF@%LZ6@8X+rt?B9xy}te13H1!b8R9S<_g^VvJKu_7P!YUQ@P zQ*@LDjT;&RQ{8k)t!Vr$7l^1PPY{vMP-Gt_Sw1sOZ>fLppbIxll^^DO#Q&e?M@00B z^;mgXOz9q{N`%WAMEnq5YtIPUK2 zd!je_@F4UgeRHTOCRPjV2NkoiDhz$73#0ZG_ThbDZJyc&54+h&@Llv%<*DP##?tVN zk{F-)4UM+TZ-hmTquknQ3NF@Y<#-GY6gk>8OFzb~!p?GzB%Gy14blSRTAnSwiRF=I zciQGqV>rPph1Y`?J${F}*VW67Pk_jH*3IT5XypblnryA|gy?zm1FYd1{E!)?)oN^1 zcZdy9GVG|kzSj0z)_c?%z9s-z*_1%w8_sY31$~h-F5<(~Yz8i+9{9>8p%?nEWmMXH zgf|3Ib*ztf(;SJG(LN7~5u1Wj1X+2f5Z-?yqTTb(UaJv$E()EHn*FnZm)~GIXKgj> z>Gvm2^DEP)q#q!1t`wA7>!sb;?7on15>|%03=q&rK{mKrB8=h*UYedofgzn_pj=?D z0I8s3EvDAT+2aPvk|d+k8(>^Gvyk1Iw~D0b5g!A4)*Ekk-cxO`$Qo3(%C7wJo=Rj* zU9GQu*4?j=PNPHm-}{q9$s@=sxR1iY` zJ{L}!j|Vad_fFFaa@%b4X}9Y*&ztfEM!=J_Ey8tDe5e2I@cfQrTOLs&Z{(1wz9JhU zo+ur*@j8$+wSQnBRnXHs`lPj3g)%AG`(c>2F$6a?QC;N;TdN5f8(Par1s@ zZoR6ipd{WW2)u`%T^4)b=nTR&`!j1mpyd)U-AWZ4e_xFoVDWp7p6Q*Q;td=Gpo*(d z@M##=mKN;IUSQk|a4CMsGDDnI#n5%)Iv()EHFzLkbk6RNl8-Rkm1^Xs<4hb_fh~Ex z=0fgffG(K{K9`{8mNCVbF>D{d@Ew3i$~r0o!>C@OOD7}6VH1PPs0@dyJIQqY%31q&2L@#C1`$r?6fG>8JP+2_YP7gn+ zW!GI2TG&G4#w~zi9|GCHFzw!r&kE_YYec_H$BdbD5rooM(g;xMVE*08XB%_0*MM*; zRCj;E$$%HWq-BZykJhX^;RrJ~s^0|;C|S+wghKT+ns(+D)BD3v$K4uF0Ws0ygRHvT zzke{Odt7!u8$DL9hKTp8 z+`OV6GLSi5Belb@kpyZoBa)iCp~zCDacs4RC3}Cb*e`4MTqmc*Nj0WglO4SW45S~C zU`e%{C%qHZeT#XjQTOUcKGsO>BqrZ+zp&3_a1HupQ2x~)z*U4;K~EddxSochQV}AL z1F^Rn+}pZ0{hp?9v0E3L6T20hR%8YU3Fn(m(@zU&`2=R_If6C|8?7ton>G&}W0(H{ zRCTzY(T$W$>}zESqs-#}|Ass^r$RCEXy(C7A$nIwdA@yWo^gclpR$O@45%$!+9<;a6mb@)!bA4i7-7P#)_DmdxE`U4mvW$^z-HVzXfGgpacF{>gR5?z$C{rGH@G zcw{PnO(9iUzT}GWPi3U0FqIMbcG-CP4{o*xKA&v8M6IHjDT1b~kxA8b0KiG0t0{+? zk|_v#xQSdQ9WkZ;G@et;_!}9Qms-Hn8l^be{6N^eQqeE40Z8<*!Bnuqqc~KB=^Fxr5`c$Sdf+!TIg}StH{*gtIXWDcXgA z0MB>j)iGM#W8KwOIO8M!=?p4y?jtckMgomzv=+utds570C_@6n9&DJ9c=ovRAfy%^ zr-E+mGBv%zxgFoVf*#sD0=ZckgJF-;_2KYgzvGOB<`Im<7u_0C%cY@7Qi}8N<@RSL zNL6(bq|n@QZ(P>uWVSGq)Z-1%fU{kR1e{FIu(Hy`C<1;$M*>gpb zc~}frtZVBceT^Yjq%KkjPbG&NS|hU^$V)~2Do8N0sZ31HU*=^m&D(?-!7UiTpNb$= zL{5G;IR;pECA#y;KK0s}^t7uw+t~yX^MB~-TAps+fj=UFp={0O_RpRW7u;j4b$)-X z&BTfr0X-3rZHuO&$P3?%92Xqq}{;TWa30`nALUsXqbNwxBE6%Sd`Z zLOROrvbCANq}GT~BZOsfUwo+Q`*@W4zr3R1T#K5Oy++H)GxP#IH4{i<@w)gzI6Ni- zgo_NK;6_1M_TzWPFCstpd}qqBI?RsPe9&UYKGFqnO)%0xw9}4gtctTYafLww ze}rIThp1Km9|R?PqGxsM?;kCQ@KR6V%X$wyW*}TV4lI;Y?~*<|=pqZ-zmhTMMg$bR zY>M^qp8+_vhlbj6Fn_f5NXD;+T-d{)y3XRDLJPaxKZC{>UhoFyc|@0TW%=Vph(>W^ zX71KT+5xgJt$P|cgX*@@i!Fbn6Xo!>iKI`i11|fo9SjV*tp&?Fk>#XDH+odHt}bJ> zEmA2&3lr5SWB0H)2M&5+V;gPz9{S3FSfUvQEOma;?WC2xD@rGyrx-Ml;mU&^NIHU% z)3SSlPo*3~XKywZVij7|zSWa~!Lzd)B$1aJ6irfygKyiXJ3zdD0bWWh`CMda2U$2s z)4Y*Qxs+=_vx+4&pxkybqF{c`MRAwk@R8>D9?`45Kb^;NHA$((YoTP$_JQ#cXwb5n zb07G&MKeK8kufb~-^u4}8jhD&nCmAPSrC_({)k|vrH`qCenx!P2WJ0Yi4`|}Qr9eG*yeid! z{Wt2RL5S=8yOzDhkTx5+8#i6*$t(eaI?n_Da;TeTIlAum!*A zW=^EU=<|dv)~-GEKg7MRh=8#5i#l0kfl5X_fP2<@b>$oce@yTjeZmCZFL6g!0D=Fp zK521iO?~J`0kf%Dr)HE|{5D5+Nu;_D(ZT2>5|~J|#`$945iE~}^ggF+Z~bKtYNb~> z!pQw=q&_dBW0E05T7*=vMNcz-NSlPcgt9WRv&%$X<}ElnGvh`#V`Kd}RxV*@V?1^= z?l#o(5B(H0k)adE>=~9NjOF(f4^(**qitjt`}nPVl2{xHf&$P-E<6#stP~BbE@@Lr z6z7?QL{n)Z52>c> zM)rIoP!e4_s#g1K;<0!R+bs_qZa#bbti}U#w;XZ z5gf;HGx_t<@5PV6*~T7aPBcc^q%Yq>Q2<7@?4e7`e*`Yru&P#q4LMD%h^r`qdp&7y zcC}dpS!|?jG5uAfdR%;z{~JIc+koAjQ-_XJI`|hY8H%Wc-3ZRw6c?Mj+im0TQDPTa z?q*P&vs}hj<={B&X1PvrNM!N%JoX?z5;gq=L!}Iw^(1?-J0(YSJ-ON3 z4f4)7&h7Cds6NBL1oq4O!_f07&7xp`>6*o5rOR*C7nLIl*$Vr$uG0%zA4dqbGF5YdRKHyzaS-KV8LI_m zfPp}z2mj#jYI1R`W;X4?P4U}wE(i*aOfOr3n_KdSReJ!Zqfh>*@FEv?Cu%_ zXLdRAEe{A9ydT^Sg_Vc`(g+wZAi4$37qWM)w%&s=Ae92K7tEy>(WW#i8LuEf3QiC4 zAr-}>@mU*ikzL=gX23xN{yGSR@mR<~FiBJ#G%w@55zg?`Ts3@MUj8nY6LXaxvhPTWbl&B8(b@MI%6_}8SVLYqdDZ$_2 zAsDP11n~P5oiB_XEuHc&`p^F*$8iOe)#4Bg7kBTsy3qqKtedj<$YSKQtP7}q__(HL zL=lr0Zu1;}{WR^4ss!kF>oZGo2r`NXpuY_rjJYPowTn zz(Ha&Gmy&7)c^X2e@%LfzrEaBbtL1_N(o!aMoFAmC=1gLQuMj)7$Er59p*PFrE-k~f5JNe)a$Z)#*Pya0~6C6d|SB2n9 z9rqF$adbG(isA!J!IZ-1k2;C4T{Xnb7!4A}2d|J=q&4n9o-Pfcu8;rm@}?^rScU8Gk-{VZkY2d8pS?^F)+}4T2q-41rjkJL$eWXy+N>4)d(k8L0AcLr!5L zwL|pr1VmnT>H~1%f;{N?v{p ze>`~s(Mn`ExyQDtJi;x8!=|92nsJ1Tu#Mr8RKeJ^~zk`I68bqp+vgJ!l1?|_!b(ly+1HcT#*ShZBw+eUe5Z=L`!9pBKe?mfZ2Fambp6?-#tZO z?cQj`BZ>?)hz9?*NwiQ{7^#XbHfybh&yoRBC<;}$NzDoGAfm3Q_%+FmeEx(ss5pmE zw%RBDbhwcK3rfG`0)}$-tI(S-A_<$sDaB?W^B}R-WyFiv;GIz161o0ymtsS@%29Tx zJZCO_!boxW`2S6YRBkLb11@C{nB4z4CUc(oDP=^)1Y!lt+DtayNqcZ&9!|G}O@0%Y z4<~10a2_}(z^0|TRGm?#13!OS{;6~l>6VIWWPv8qSpqQYQN zupXX+(mHmn1JHkMGQYvr{*2NNyR~I|+Q(iM zMlh!r&OYeXZmU2(@53AxSS6!%L(8E;J(pIqEGSoinIt-fI%RhPql6$xSmREs&I8Fe zyl``&nIVu$-dj&c@NE$}{O1D~0?-Ul0-IQ8*k8RKphmX#2K(;2>HX$zeuSE6Qto>x z;KpFRh2wKl=MzQZ>kpDrL}1W~xXr<#db)M59((tsk~V&Z^;k zly*_8ht(=ixI@dmi0MRicv49UDdCo;h1_!r6CXwV-LwO#GcRmQoVX8$ONl8@uf;qi z)#^61I7Uq;y%LG`P{_x9*XxPlTE zdNjxu47yC8RY{V&;M3Hwrn1FU@d(_Gn(HA7qOIxV`X)#GkS0q7Js97oGs&FK6k6;F zshxozEu=(bcR;y+~*!&f|vL)q`T9PVYAM>@c=HaiGVMR5HNz)tv}4*q@?%2!4plZQqiP4 zhl@M~K^SYeJ92|vM-s<%3mB&FB(=e;xt2|WaeZdJNGYXhh~V-AP96Adu1svFeLxu^ zM2^EWTu_A^@wkV2sU9qXoC*|E#n0q)%>lK%-O21k*pj=p10e#MpR%bEBE(xPj;)zm zSs0aSHT#1ZNN)!DX1wR|K6LQBKC&oTTWBYI+B=L3?meG?FZX-8q(i)nqSmxb08G;p zk6o!h!F~LGk^yr(Vs?bq5_SzR=q$>$-)v(<-bENg?&P7)jaKAjq<<0%7CNGrdVMj| zp1oMUh3L!OC8pedU)1P+O!y%r9zQ7Ir*ZGnZ-{61SD|eLQKv%@(ovQqOs_ND-%j5h zQ)^5-^su}5hp!Qbb-UYMPK-luz0s)KZu$Ej@en2LLCsT8m#heaEuOTrw3RKE-cE*z zyh8~P%5%>8o&KNt9S7yA$+NpyE}(#RVS+(wn8rW@1$xi~RLw(dX@8*rWqWl^{a5`( z@olG_lfw6@RJxWF0gI5m#E#CK3`)T@IqAaE6w%x6AIC`4_dB-$Z!LxwUTX(#kCtiv zW@DD+s8_H)%cWCC#e`927J{4}S3g^`MhgJd^ZX{%dwRUOZttbHCd}5mh&2{*{CBT z@0V8%*wv$-BW@bc0zJAG$#oiVhqS30@AryeY*@tIlhd1F*9=1`$T$4JX#l4_IOXYF za8o&k&F}36vsYkKRuGkHnMVNZDm*N2^AeE@^w@1pk90nXBn&nFAA*Y*f)w|H@qQfL zdt_c^ciL2Y<@#tFU3sQIDg;y?04<+j9AJ5{U&o(9Bw(waiHvS5QwH;l06{>$zb#cS zUH!j-d3Gd`)*4aAdBa81Rmiy=2m0!hvSk8^jMl6H=sOVwQ9pe2x0{uCDOO~9BJ!d6 zE+SUxJE8#d#QWC*s4r7L{WlUxuub;+9mWQ(i`)3T&d1sPwx^Qsq1>VNjyKfoVUU%@ zUo8lSmuhVf4OFZ-EZw|w&DWH}$<}1|Y>UvJ=hiCEln}X7*G#QU6-5GrMlO69vj!ap zOi&W(k6f8XEcGgp--Fe1o2$Wg_X7z5Ma+bzpB_Z)u50}K;g93}>y)QnSX(fsJln6%%tfR@N@MYDS@$7u0=(M>FFsHTDJFO_?l28e;y z+K(N4RW}9I40-_1(2f{Bf^1o}iNU5eG0kbxj-~|Pc5Sq*Yx78}pAg*JH+^NOJ`@io zG4MoT;$duLnAYp*uf^Q;#lGLwvhE*Qs)BCcJy5HH1SGUOua?qveH8wt zA5ee9*eiv;lU`zjLi%t(%#6`SL;IS++OthP#k}`CRJvKIas065?KUI}|ZHqp6%1f{&u3t7Zl8(AM_gkAPh_l;ac7B4!t>5rQQ?=~34H+0wE+aTA*5x3 zY!HJz#?3g>E0>ela{rCp{s5%{+6RneFoedt0byg_l2O?bssTXMmf~qMW*V;7Fep`P zW8lCtrYt58qF+lmOAx)2gB}Lb$VOcnzv@oi#8y-9D=atXm)8!6s6dVDW`9dZp{On+ z{Lli(Nrsh7Xaov6Lj+&$e6xn(s*6{-kfOZ6s{nniX7KY(WB(QFijo}D+1*QevO3a) zl`la>RhPHPVWr29aML6--7Sc4e^P0-H@>PAj zVnpw9DZnoRJ33op1;Y$Po{D*`m6FOT0*=s8pNFE5OCma-*^_5)6mW%So=qt%O_5XR za!TmMYAeLLgT>_D%-uRl!3y+PX`RMEU<}zQO^h^{1j+S@c-fPYg$hPh^OEs8;PTB# z+~+w4E&Z8f^n&Q`7|fRC$;y~A+AYgxVk-@c(ccyj4)lI*Vu(w864XqC0JbUCv3w2Z zfzYIs@%u2X=_$jZu&q3AVOwtQ@cd+-89Zg1gn`md%SFfhJ8r{LH}Scl6V}tG6xRKE zD%EIG+faW%diG`@Gd>&vMEhFI_Fm8H_xVg55`h4D>7y3tMFYtH1L^gC@r0Y!Lu)1eQF}U$2YQo>P2eeviILh!7 zJzI&bj+X{6A0_P7IdHmLw^n_h(#Tm^%1uM$hG7e3kPtA<^lW^5IyRnk1XB>crTujU zl7N$O9}})>gzgY1Z5@l2eR=RyXT(uUNv$B6^FWxhc?lY2?(pUP82EaHS}~nAS->>J zJDfRrVHHo8GYY{vYWYxyZ((@m18|!v*f{MSt61`z9>-)#2JMU&>}Jy->n;2WQF(UT zAbGQxoter&-U0POAcQ44v-)3Gn;KN47`7|Y{Vo#9BuMGKoqiTWj*RzWz?(>LNdW#w zgOfdI`xo(;g4bGav~#g+kJVVli$mL~|4_e+mACw08=K3Yyg&z{SMM#T$+k2BwhA*M z7xxtGbwQ&XD++57@MO2Cz}F0+39+LHC-{Dt{^=UQO9gzN_* z2y*W+chi3%G2vaXo5sJB!)ja>T<{9F;X0>M0oUEbKF)Uqu!W%)HCs3EB_Bj)e-2pI zHlKRS$UyPRddZtPw29SP7u09x0v?}QRb_JC%C(dN+sieZC5CQSDXCRhSsoq5kZA25 zI?xu^=!_uZjPlbJL^!g1LCrHwHQQ^Qa@_)?9^KNrCZT-9DXWf%-6%#B<8xJdt-~-) zC+X%&A$<~Q90&V#StfU~cjF8V?-4b;an;~n;alL;iub`AC(?2yq3)&jbWLw0w*LU& zWH-i4;fr`Uo`gJ@1TA>xY-y)dfDgNdPKu?!7B$a1OK_U&TD@MHsrJPrjh|aUInzoC zgBFvh=GF4q6~H@9-2z`U{e0ENCoH6U=DdZOwh4aFrLc$6eM)HFnBIJnWBxHu9aI(x zReeCy$#bveIr-5y>w}($`=#V@Fg^SKrxQ!!zkxuKj8!ZbX zz$+G<&Lbsx_B%zXz|>MS$=&^ub+Yo2I=odk;)8eesU;!1MVW5yenM2B&B~H=^iZlb z+M7w}H|{LdU1Y{%M$wQik0Q9vOZR1sR+(n-ZyV&F-5+HR3#3M^uzE_S29Be_#7#eO zIcgriJPCqdS!p=YoV6d?fxL-T)u_*$>y1tlz;8QHB1 zDHW#|L?W zl|R@^LF&V*MFCHoBM;y^F~b75>|`S$W%1ai|pL6%!2Wq?xIkATi@X0h)>X{EDjwp~<|2^WRS7d*p;6Vo`S-08qkq&q-pgBT_? zqqglsBUw4NpNcz#iwEk_dP1@CYt22wE1SxBOn?;L#JX7?OAaezC<2w{n{v?Rd}8T#bn@6!AIM!y#YCOJo(h zi&FXfY3_M30I3}~$fVOKmXIIRMy`oH?oyz{VzOGXdJ@_kH zBsTj#V)(92Vj9@l~mRZYI z=+rnllOi~3>}?gD@d&blF*b#i@gh#4BRDs0C_EA|Hp2Q&#?ui5LyD#-g6TG z!FUhHwc%9tTWnejN()y%c^Jt$#=L4#mAWfCy|j9K;|1=5d6iQZhW*w6pm0~f13t#W z8Bo4Ey@l3tar&_0pWRbi$X59d2Z)F-yXW!>UbNm}YF}lK$7f4MsNapC+Q0$QK8cOZ zP4!p|c2PBC+{5h7H|DP=-(OjOG(w`D4A6^Ao8+5qN=)kx+ug}{)X*V3k3pV3QRZ=S z#L;zrySJf&{Ga+Sr2_nFMR;rm?a}T>Idc18Xdk#Q{`P(E)%BRtnDcOK23HwQ!->Ak z#9O@uJ*8-gH71wPqJ~P7{Oj_8{L^cg@_jN6;l=0aQCrC?ii>xpbm5(W^7#L&t0bsq zeh|KP&V~nrv77l3dZ3)e1R2rr6knYO0-ARrem@)U!R7qpBh>a5@wzZ(T_Ylw=%8oP zrO*u<8}=m1>@Bj3Q%on|t)QH?%%u3|U3z6eS`NOj9|BhyIU`f%sn1oxho9 zOV{n9yz-Ps)Xy4gj?N~ejqcomCIveWUFBwGo_T(h67?T^Y+x5%Zf@P1*ZDshSFdj% z5Iap)Y>Qb@nTLKhe3*MShYJ*n(kTtIB#^upg|Ij}kv<~rMT0XYiDX|FgNCU`GiCZ& z2=DOCrJ%-YnjFT_wZ4!#)%J3z{%$q;SO+j=v%-qs;pb&W=$*XEdI+BevnVnwg6RX28kGZCJ%>eb_G_7YX$+8B`Ca}BBb zy`fjIuU-R8U*{%3yy16RHHGa?ahInF{hPWB_id!FQwT3P4tlW$guYc;`59A?^)OmI z1WW!-;?%3~1@g*FNTkQ(C^K`WyPzP(6O|NPSG1S50d11!=+e@BSaEh1-@c$OktdRjHHRd`8{HwgP0mvzCvZP3oIweWjAlT_@x5a z&L2EY@=PU(tjT=z8Jv1jm*{mD59Ai&SrQ6I6+57@c)*!oTh0R=K(n$TzsQaOY^rpv z`w{T5WDddoN?3=^h)m50TFByQPjQBrsvd(w;H!l}H;G}1@p}okEXT31A^n8C_zs(z zEEJlZDXfk!x5r5!v7HPcBaX?}tj5k7GNluA!eMT`&hyAHha0wlC~{6+LjLcQ&LO+r zP3iJ?^MY;b1$6tbbjxR`WwjIF7{UD+glKkFpzkz|t1XDoRd3GeO8U%qWf~LPOfBDN zV+%s9t)0EUs{!x*oOKfilu&D1Om?8jjT_Ezu3`{B6pO#4sViDOvD5ZzLRvziqTQJJ zZ72vAoSj&g16v?6A@jN`q2h96t|$4nnEc~yWgiE#olI3?(~^lt$;)fIF|Sr;{qN5n zxK)u~xkq!(h`UYVYDJe@Y|V&EdG~<7&-x_@ka(v0O=aPK75cl6F;qzxlT2Er#_kJk zMXPWIA;Tb3N5bZqLNMy$=#83}dy>x6TsjoH70acIx8;M8Rtb*AL$b=AsjU-BZb%*iuY=PWiS z-@^lT5<*`kX8+;=JcgHoGq*BPa}IK<`TSWUBQxvmo77=zhtUO%XCR)&HfYJVKTV@MBJVyc8OEl<<=2DJ$0_R)I zK!jX+H*!LEloXA~gcVdYc`n+cf*GK|&aYp=*Ov8UxN!7*n?HfAM`nV}|4G zQoJzZs_c#js;i2~!BAbM5!Js!z%`P<{E?j4XY%b=l=oDzUlZ$Oba`rk!xL~%{9wIq z1KV5$h~7|?$?2~05Cw9d`$1uM%rzS*6^uLGJ!~p=Js)%@s7`IWQ?-4w!nrHc;*d`1 zAKH}%Z62cV{gI(MdWY4yNmA%IUbMy3GQ50Yv&T@emhKJmFo61U5bQ_6G;|CZ&n!;n zGT6Uyyj**l$DzL6j;a)_;HthTTh^KL3!cU)k05JuNp^L0f+v{^EiJnXO|-@7l7%HL zxDz|==@irKBS$MF9S*+4=S3K|D z{4kxYVQ>P`0mn>Z(sH$$cHPy)qus?iTNqRWyF}CZaW7Pp&FbJBQ!V{AY4)V2bgfxZ z*g~bBIm)F6GqJa6(`Nu~UcZ>Z40~GHR#v$f^flvOJ-h159`Xp+n_T6q9K9`PEAx~- zwBc~UdjqUOZ^Hk`^(^S&Y0Z#WN-We~_sSb@-F z!QKailw#KrXfhx+@AvSs`Z|q-2h|T@oZw?lV);=7 zeBd?(IJy9tkeY9e%{OwDpwbm<<+^w`*&V{-xH+(YEgPI;a2Wg&I1rzC68PCcS4r^og*8* zdV7SN&NhzYx4E>fQC#d*8&(_$`)Ul=Iz5bb3a(qg-qI_V6)L%=;z z#OX3Hu&F=)!BQ(0+ozE;7p|6!C+GIcWz-WfYu$5(0svJ@zs*{bLE5wVLAJIxD+U%8 zbak?1ex{IzfhDjGTdUmdIxRsSuz)dx*u#9zPR}_l?Jhz&yRM8=A!(b1`6}>2!>t#Khu4a+TiL*5w&mW)nDc56N%ljzx$ntUesQ5YAH!{T*EuM@u{K}7_# zT=9^*7mNm8WAd);bjDL8AqHG&)-P3b3XxDziBMk*{h4Arj1A11raDm-?Rotkz_9we7d%~c%HpdSrcC%0pe!zayRM)}g(g+D)kLefC?S=pPN8fye9$Nv-{8j9!zTbF<7Q=af;v^E;q8MAGU zPi~=T3HbW-_&`omq(MKDNC)RPr=0IBwQ5#wAB{fdw4fRM8obpnGT15sC5aE5$w;b& z>`OiBc*?>M#7FGV}Iyj7cvGwLy@6w_Vpk<|JlwZlhm3{aYV|s4NIvR;?b3R|cyW zmU4jKr(r&=%3Fu3NC~s;l?;bedpnf4T}lOLcN-}U+uX>4TxnspAKpHGFt(>y#xNY{ zAFsKKQ^6Ey3!6CywKaSGGY5tx`(4@E>3(!+$^w`&wB@T?9G2o~Dg$8~`+{ma+tC=> zsodGu5wb5%HyCeP@fJjF2i%|E@0^1$h{gVWRvy}`5QE{~31k$&`G&9A@;(ZdIr}y` zy`+MIT}-@i^AT-_LsioGv&bu9`qAda3 zrU+bElkGAyMb7HafwuFr2IJTJS1&~s-Ww~NhHC>-Ln~G51J8{6&auzB=3R&eb!FFp zOD;&NK_Vc@uv))+?obDpX8{(XEXuEVubC&-!@-ztOmJ}BRlW?! z@;qLZavWqlenVbiWFXWk=<&=*;Gq#NQu9E~#;38h-wvZ&vS-8bAvGO&@z}9R^vY*? zwp7yVSAX)?Z$GHbB$<=9cd2R1>2@Opt)SOBfy(zpF8Cl}3R_luEK~E=29QhX!_91f zuq3NYlBZrm4)MjqO1##fJa=>+@+F;UPD{%=!|(mo^SO^S($PxVLoCSBbgAq;;CBPc znX531+@!zAFll@O&mZNHm>o?a!Ex*!!kjy!`*s^`*Hj(|7_tq-@R-p?DR47d%Vn%0 zOnN4US1ZA0-gIb#t=Vj+M9}6-DZt_aKfRVdf(0WV_vbPJn^t^Hfta9oDe_=)Hz67_8^0Id?>03l^JG zZ@H&D^JYg>R6*q>GWJ_dU+neekd?*fS;y{l3Hj_3(sA^)H6+(nYTm>`Oa%7@57rJ$ z<;AVWqijr)6>)#Xc|tq%L@CJCJ&>O7>Q!tGoY}%UhsD)mTz)ngWFq}K4=({;09}GNq#O~ zq1;Y~q&c=*bm5TUq7puF6Azopi%pT#qmhA}&nsu-7^Io!;7|;yg_f=I=XVU- z-w(Szal}D@s_%dKP|7!EK0p)nRimsStTtS_gLIBr&`w>7#nsVFv-O)uRuN=2&Vcp; z-5=ZET-)*sF;G6$F)a#4ne@sVz9r8AG>Sqih^8d(4YlHX&(ZVEhoS&eJ7Yc|nYj*( znj>?NnKlRUAj7s<+UEJ|zvppP(xTCeqvD*HL6ScYjpSp&+a{plrmCTKrCGt~YR6ao z3aw@nt0@K65uEcT*g?!KsFF7xXs+?#s_S6N9n+sK0xKBLX2ZodMFwR=P*UwvZwFuP zdqq_VLY8N05`;y9&5%7QrKMNDW!7UT)VzQ2?QrlSBZoX}!EF)Hh0{>#FQlq3uBwI* z)nJwmU|*0;`rG-AC9cMWuFn^CcR+p;W%Io$ER-g2qR*X9^hy`Yn<(lO5Xur}Rgy;p z>Kwu&cEDky!sL0WW0G=2?n*W6^)aW+nX-zs&=2!NKWV1`+?H_nE6HK@4rWga_-`E{ZIJxO^hS=FSBL` z5H4eb((;R(mA11LlIYm{nVoGIy7Z8-wB=zutWWAq-DL}-uli>Ss&@|B@|+#VJIJ@# z?pDD2|KzSPM^hSj2O%{?0Y&46iuBxG_>&V4K{o+*E`T5MXxd(zyW+HvKgJ0K`)y$^ zIZTv$CwL|*1>PR?5e=4hKVNO`#fM)%cgN1uN+FB9&roRZ`c=)`16N5^h)v&S9!h9p zsZ>z+T=zeWH$|*vrLtkrVhMwm++4Vns?Se3&cJaO* zTjGdnEv9ZWJobdy+Sgq+YhXk%PgM|9_lzzxOi{x+AFh&>?*iW05s40ir@QH{@g-lF+>R>|eT0e06*j zikMyCl(woPNOc*ulQ-m=(bl|8iR&Kxuyxlwn)#vxuTigu{mm!+$akChB;c2=8h7&k zJ`2AP_J&JKVlL`S5z0%{X3c)iDlshK{w@z})Dlg%!Q}%c%mF>QFKvpQTp&h|sjT^{ zPpTz<&ncY#KMF6+_+gExb_z&E2*?cvlujYo-+!5da`M%UfVUP)LDjawAurCRKKq}cp zUfIbB51VQUz)?%h+t80fk+Z#FS7gMurPGI6Ar4P;A6HxX(MA}?ss z#xBt&49_R_8jeM7|CBg=`YKW(bfbKD;Cn?WVePa?2#hZeWqnxmy}{z(TjbefaR?r_ zl9+!yFA~PMVTYJfZRh(>XP=YmT*O)tLOBNBiNWHza?GJFaMQpj`IG|W{$#Mc{qJC% zXfPPfQ4AiV#JVr3^3xlAF1#B>&cw%D*Ur!(DO~|g;UZbyLl3{>)+&n=5282+oHuc(! zS;&*RcVm9CeE84|?(7@gapZ*T4*THQ?+=6d&D5Z(8p4k9tj|{AILz0lW}l)L28?Wv zu47bWk8bplp;qn~PiLWNDxTMGkM_Js*b549e=1rvN+hXj#j= zA;OOCtHKC%BK$7;$JFk~czwxCY!|^E>SJu)tSCnN6qPyqh8Qbb!keD)M0<}b+c=4+MBrD-QNx|adJQkp4z!+ji-Fxi}voi6Vuc%2tMU0}5)CmG?n z`dS;R?JaCogtQTS`0rPV=@5NAakj9MVcri5A;+9e=foga>vj!W0rXi#V89_HLHjNSv3JYd5N1 z7IlU_B1lMxPIHD_?z*5Vs^DB3!X>b{jWTm`dlVEi z6Z1jwo%ZrX;AZ8Ce+UIAWZ^cpJa0-?z5HEAa2lSef&|nM0BCVd#BT1ChQfX%)Y&oi z!Rlh>=?2qi2+6a65SQpPRmT-_bsrfe|84X0Z8@nyHh1DU#Iq~)x~ADy^&eEgBkBILF{pHf+T}u z$KG7{UG?bGE(b8ScA>w@_~Hzr*_-00o4nGT9ALQf9f~3u`1LEIez-bBA9t*ijcdB+ zkM%xZb;!Pny4&uM4Oe<@x@e^+Ea0;PoHkG5p`(RLyeO8m(yocxZir@~ky z2LXgoa}hF>hN)(C;FZ&cR3no&{gMe;OV;$g3AOSuH<0o50Uj00W|@*jg#AZ>^Gb3i z^6~Oed|*+&*^Iv9`TdGnPpQIC4$1g&zO>&Xfuy@~saAl;fz8!M8i{hUAKWAcwEe{v z$H%Te6tTN=)4iR7ZV|QgISHh_*jrx|d~**_GgN4+?`&W}20~|$-U3x3G$pm61P;@< zaZ@r>mxk8pq_W02*#geoo&viB^Y7$A>MpQ6ks=Y-^wbt*4Zk_K$ajq%9H?HZqrIU6 z2yqofPnDpqDwuvhuFO0vuK*b&2EQ4b>t3|jvkJEg*RgSzjZy?lee1Lzr%ARqEpv__ zvqRfHLzZk5ixR|N71}r#l%3HQRC7^n#Tra+7V`728K0l2M3`1Ry8je{jnaoEfG}z& z+Gszys)qSbO0fUZYB@zY#9B(@A&JmKx&lHn{N0s=2aXX zp`2orPe?klC{<->V^`-JY0I40DaPtL&O2};OBzd{$Bi9P2h1WFuu)3OX^?DQM)5xD zfmECEtp*M{i;oGLC?LGe;b@mV2hS8KhwTjWZm^rUJ041Py7(Mh6`i+Q7XasCNUiNV z=tJ-3bdD9ZKwEyLgfe+HXpe19c(vb$dn5N@M*y<_lkG7}djPmg*}(gnBAuyHtp0*{ z`h59J%=~(-Dz8o(zOwZQh_m^Ra;|24;yuDD__GCfyAg-dD3r5ENvAK}4wnk~;HadL z@F5~6#kMLNak9-lf;J(rcw$I@L{#`KUrLT#&C%FA%at~dD|MWr@C_8TBU+~OY}`Ne zK(ToTXs8XdcnJdem%DXZUWB>RSL)hRb?xep8|i#NWhq;f`zhv|)N!3c(suJy=06M( z_;@a9+$IS;ziWBcx0Cu=`)Q4fWE-7n%s2D^e0TDh{*Drg5ZhtBu}QaQ^g{uNUjC^Q{V zQLjys14-C^5rPNPk!r!QHatFw?r84?@|P#3dYl>&4Bfkj;&#Hn>x8`sm5cy@8tX0I z&O6^5N&j#oibd{;YGs|`Z&}UW(n3Fsufv|+(Kdp}HEetrny7;E95X*72*q#`b>ov>3g zgw(-5nd+5TZ6617JsGu8XHwydtG*UKoJ6FCm`9U@v2=5J$Mx!pVo_QA+9nw!YlleI0hg&fVaN0$dq`F`a2ba)RVsIsi$iZ(e!PLf}_-$3HG zH5?Eo^n!7|6=;qZw5T2Dr%aoPnIAM>BaYk-Ql5-Rc?VQXbDmUqMYxIb3`t0!z}%&b zTYfD#n~FUl`}Z1&>uBQqyOStqlbgK;EnW`sG>JiGkNwr}elY}l-5ndD4`e1c`NAnp zB8`z?sIXU|B#YoAvYME#)N(w<9KmA?H^Ub7jA@glv~r6u3fgGbm0(|TJUs)t5W*sa z?BnnyKVHNc)U(^9pM|wA3BJu1w~f%87+YhIi@03ZiYu=e2`aJ)R-u*c(O#?zV?YW% z;{xxxr)>|jg6PSA717CcvKT9uth86meRU;6P#;fM#3|PPEa>B-8S1xa7tPgx=egU9 zRIo0#zaUmOdSIChBmxE*+b&#cg;&pK=2vPAs&2>@MprRf7V>;4xY@RjR zl3K)&btK;I~FJ{z|mDINv)VX zc;84>_o1JB5}lr|YRa+8Ux462=G%3Q<6I;EonNgJ=Q4>4Lo-IS%_a|U0y3b>(M9}N zRfZBHS`>EFBS;J`k+w^rnEv6AU>pOGBR^d$)9sV=gT&J~MAt=trZcyvwPXlzSu0I{ zUg%sQ2x)`J)PI%Jl+pPSgxJRO@r=b=s?Fp)m_S-Za*WQK+;j3VZq7A}mWZ-VX&|Lu z24t4T2m|r><`kZJwDXL2S9>OG%2QaaT`fmYTyqQ6I`~#L+2B7Ct%O|X3Ze@nTXV^T z6kS!V_UDZm4OU@B_t9P`Mhx>ntt&+Aa5;~sxeWy*7-KemoPLoqPjz>^PaJ;auY1)t zM2iTBu`_RIM(K(_ib6^RfSU=Asp zsOoU|G7QY`I9{^c;lr{~V8Sl5P=GHyvqpwiyRRCTG~Q4`?%(pA3{Tiq_6tMq3(LVu*36heVC7WXG6@K)(*?zu?%wLJRKgxOo3N zsGeGP6lceks6zA{-wd-zuEZol*3Qdll7q|hokR;|2nVlAlj0&3L3S;R!J9zeD zctcnCtcRyvOE+av5^^g*=H3}e_xWCZ0GclI5`719avW2f;E4YatJ(%61x-Y67z4ri zee>3)8O9Id2OwMy(Zg@W7VCpV(TKvljPX1RU!cf(4)&?i{6g>Z=H zoZV@66hX~FP_etiZ&w)9nCol3N8s>|a-fE}(YGw`N^P=QL)l|XinIvsQVlFyj%#}Q zK){oCYpVavo%{kZQ_)$um`N{C>7#o-BH3DdIVmb*0QUTE)ZBOJBtF$O!m$mwxeGL z@$Cog+Brj4$~dQc51PjtqQ?Rh+U3y+~zS0hMaoAF51WQI{h0WG8NR1G5l+KD=YLY$@#XD)$^QMv8 zGnkg~TsL~Ml^5zfNgFafKnjSE*HX|2|NsB=@Cw8fV*(M?GedV&2+>ndU|Z-1&A3eT zQU1pl%w+@7v&#rD?reU0l8#2C7ag`p=uD2P0~%YLEaiRd z>)O7~i8P^<71^iVpeeq>exVBgUP&iZkIr?}K0sWOXQ@W&K+JpzPB%??+y|?K*ILs& zp#gZQiHw$tI#8eFplZ$=^qIidr}TgKzH&;uhGyfaU&leh7ZGI8&N%7wg6Ow#-&iQx z^$MpxEHQH4OZ^5Iid^SqAftmzf}Jq9T!;0mvDDae)Y9+0$Sq|}Xu|l*%3j`Alun6A zS2{`YYK6a_pw_ct@nqz5l%TZ+Mf1|?IK3OaH$o_FMo^uYzRPxD4>mwJ8y#bwV6%{X zVSr}VgR#U|0|flI!+EO->YFqJK<1$D0ncswC%{nSfYBgWl*fJ<`F`V*qoa@v{33Ag zmrsw(O0uc@Xh|n}u&;oDi++HJFE>)g$d6+tUj3_~jxd*P5`-$0m&f9TtABN}3edEs zkxcdkm#?1~IkpJf3A3=rvd~YFD(KIqvXuEhU^VM($wq)2h-Z!)8s#T$JLQGQ_Lo1Q2K4CNLV< z*c+9HZq#?UEwcAoXrxKXT^5U(r3}IuY_~sZF9<$6{Ad1Ve0U@Il9}JGLa6znz^-TvB2=8gTOfLQ; z8)R`iIG*6$Hu3zEu$a3%Jzv7lM>&aCP%EB$LNtSq!muu~yaOnE4su!zB~fQZ_Zyh` zrXo@={UXuD7yLT~JT47F4DLq)Sp9@bLxT?DagjIL9DL~NO=klfvpnT!_*ynd;p*c$ z3n*TTO*3RmOfW+*%3p zlmiOc)S9ZY^NST3=qF_NsCgeo8kVIu|B2Syg|7OH$Yi zx;Ye9I23svI0twdLtF~ahhcF*6*kF|Pph`;g3>>!U6vPa6U>Qd?y8>Q_$c*RZDj1- zxJt=(Li@HHhi853C-Oi^7o+OPi=j{RL9rk&U4{+=8h54lg+5-NA7>{>-WOg*O~uiu zjJHp_&*A(1qgdssdYO<21)SP#{yhk*Y|0h^X~fs@2-q z#Ywh0Aidpgn|e9V=)_^-JQpyNeRnHAFuieQN1j{*C*7@#SFJkw&i(6kQjlF)VsD53QtL^C#) zlG;pdwZimF`5tt2$-wi{DP-I~MNO@v%u;H02E7?3%#{osvUV+Ikj^e>Pe7nMMPi|sxkHSz2sQ%@B@0ws| z?`|#sL&mzU)JEP)0||MojZ;T)$ExFNM`^>^Sh$M457&ws`y)-mA_NWA9`yA~ME|I*zG*u1fVeJld%epEU#6u9Yf;k06(2H%`IC%4O? zFxtGP8Q#TbRo;P1&tO=RY*JXsM(U|Mp8%Z2HBF*KU;u~SfFBuDQ{@vfiNzFro4@H{ z-cxMzgM%3!PyJ^N{KDj4$%=E2wB>Rf`#VT^66mg_s ziB=QrEqmS>h#go~3~a^l=4v_3VIZ_)uoViQ)aMp1$K-I?e$sefwhZ)St~1UNdhVQ+ zg5y_v@QBHFHkJ_q*4V?jKBQg+hee_l<8ovW9L_xxn%{%eVcJ1FQiN)%gse()KDvAy?p%H4J8Ps3X}JKhbCF1{(;C(m@M+7@cK~kaw`It*8J>t}YFfk)9^AV&3PhjE`l z9kOodZ`1sm`c#P4>|Z-U1803@2Bgx^?!RjjE-jjFG=Hk^-#8G9dcF1O^xhcfz>|>t z2rQ`G%u~Exq~9x`wSUWaBUMo!%F=Evi~TI?l@YM^xrGu_Y5KEtAL`CrFj5p$YPp|_ zi=5(>LCkV>7P75{aM?R zlnQbKMjq^VvqX_tBxaiptvauI2WUeMKbQALkF3{^@SVlzI=`3W%2{F*jWw*rCtiei zic2_NFe1$QwKBzRxzcp<+HX;?=i>FKaLXGN|aK*IffC$!B20d>uCk_ph%Q zrXU!hE`#l;*KaAGX`NdS>CP=38hS4(E+}WSgFPLEFZFue>aSa(D6WHHr1*7Pf<9gT zbLv~i)AG;9eYN^8kRCc9jt%jS7@(in0$Oh(WWo_JzPBcc9~_eM?NXS}jsg zoF}te(xs>j9GD5J-=R<%17UdpWoOrYuin28uyjAMX{@yLo9( zHm;c@>=mYD@5k=GM;%{eO6v`2k%rk=`|ZPExt^=s$wEJ`dgc{N%%FpS@-Ue|s@IbO zsA?O;dkEH?`3vvR4$O0a+ra*!5qvW_V*dGoxrYk!XrfjKtC?wQ8nnyp2gjoe%k_1r z%n=JTFzH0p=b+XVO_-wDmPw>CdKHr%Q~gw8gG7w5n$1UUPeYkx=Jh>78(wr3L-A!R zsh$McO#6v0*9nO){p1y%fzLI$>D|L|0ZkAvjaT%iXfhY22ek4l-T)PBB7FJ~JKY(* z->4m(_ez=7CjMguHju2bGPH3h>U^0@S0ou&Ax_=9n>wjJKEExEJgwzlt1nkU>_mc< zhucMmSh{)dFrErZ!9X;Jt=B+KMwRP&E=t*%&#MH~0|%r@8st3h;Lw$<#mEaY*w_;? zuWR94Uxftt8r&}e34&)8j6u{pnp_M>pnI2fW8u$5{vPvAk$#&8tild)9>G5mAF|4L zVkj#QdXuYy(kX+3_Fs#wHFq5Kl`!o1uNsa^F~Ljww0~(pzyIRQj8xkJTw%Zuc)-*u zMp(n$Ku;xK3p_~_NpR0>n4l9*5dx-h@H-nVPjvJN>s;ClJY;FjEF{vY17TgQdV#ET zH!2~PA$pR;J~$hkEb+v7Bymy6$7Sn#i#i{j*5w>|j}YS)MQ;S_zHNa+itq8*H2W&zAdG^Y7$Q=W2{+n0HekRBuvZ{Hw94exX01(9 ziDe4%6l)GIyV{IS z)CsewP!8p`0YX@C#xo2)cV_(#8|&qpriNxT#p4Z8nM%cD8`w=5qjSUBow%u$TL)<%-to2<<-;ZJ!ba zIjHQsPx@~@;#eUQLBzrYC>|5gQnXhLE;OQEJdPIz3UV7my z83YG~;p^sDw>~B*4a3gVgL2*<$5#yY%Iw-R>?c)9n%2b}UwiM%^;RGOv_`05M(_7T zze&l@bfCPrj}9n+SPX6wSqO;eZ)bE8mIJYA_Od2_I8OA+I$(uE`Jv;R5FD$b^7Piu z3$6#{%(-Sf7L_99VJ)U@M@-%y6;^!7a zz-V2*lXwQYqhJsc+#8ZGCA>;ACb**#1V;#1U(6+%p-;m)AdDm08JIYr_-a`M3G9_^Tew{udcH?B41$M9eGvF^{)x z_qi_kD2%Kzx^_z_J2_7NgnhNinD9gNGTFL`!j4QhR96esICnoD1Glc(8=X|*^i=^J zfW~P_$Wb|(ZtJN|Q#OCjlC`d0K`0ouO}QX)YM-ADxfdA``S;nCdH_CYoESynP1wpT z>wRPF@i3FV50xnN2SFC+Q@9?YLjSg^1?Q`ZnxK-g8s1?L{&FZUp-r}Eg{o8(&x`dy%2yed%-_MV^>bD)iycBy+(ov*yJ42iD z!Z_!s4l-xxne@4g#8G^^1X_$IVu51T6V^-nvAxh^lkxfGx8w)xb?Xvf6wH|_(PT!B zwO?5Tg^LaAU_VNZi1xB}_l+aRIxI_46LxnZiaPVqR1-<|qlxyXgXVvAsqaq(maFyZeP1Nt)xd$ag}$Ut(R)X0c(Vo5=!nvV4OTgjs}}(pvCsl} zPa$4**<9Gx3;_L*RqoQ|nu*Df%2eG`C#S%`Y=E z(J_v}p194o!dq8`(i_H1vtKoO5(eFk97=bREhSs36c3Jlas5|;YOHGM>`qrPi)oII z8y`aN5_=75P_`6~t_gR$F{lc_?$}R*&=Fq_CI8dKV2ex9^)mGtf}&yoT|{| z)om!_jqlVS0*g-F6(l1+E0{*bG5zwnr`iVLR;xPPAVtia<+8D^?juJ`eoDs+?U;8` zz6(+)-rG>;)9P&mkK~WPv9VKs(PV19^&df-CrcFF8snr~&;|0va8l6dBQpPj0hlgS zKQ_J(Z>N=|Y1|OnG%Q0~6E?Dwp-V1q4cjM`;d86+!oQK%h`k00E_olauoe3yeiO7l&y2q3dU1(ZAsPq2`67i~_$j@X=?Sk1;;B$bYja|n7+;+R8BxB!m^!4ZTDjOe$ zB&QdbuYHiLQN}pGNgcIgW>g*6f1w{By=2G8-#_S$*y&-gv zs0UEW8#kU4vK&-JzDfhtoxJ2^05-`&Sfzi!=-B-lirHZDjbzYekbA$?1@Q7};=)mF zlf3zpeykU~^3CI4Bbgefb_*Xo_+dU>?KG7B1BJm7tVdZ-7xsi4^2@dMDjqU!oAS^M zE+m8qenIIkEq`>XZ5d;6+VI+Nu}9 zS^AicYA6RA@FNHd5d(iZJbkaW4VCP+;tLz)Y#*7LnU2Q;U*zX$<{({0u07>%^5!=L(d-rcZYf=TZm5sNU9j%Xz!ObYLVeT63IFiyK3 ziS;M9e#O6q9RtI|n9;q$76(?9687SMKEycq^zU;ZA+)etM~Obp87fhNEe~Ws1Nk2yRbT zvTf?%y;OfO(NP4!zZ{bexfZMJuvi27A9F3S>tLdcFY?cwL3wb4N3LkNCrhhl#?pc# zGu~b+G)QmotohhT+c@oA;Nr-(m)tVl0KUR`f$ujABR{&0j$=YABJrY9uJ4CIpjsa0 zzQDZmXbJ4koyqHOd?aD=ClyZwUo4PS8Z_fLyobI7!aS^uR)4Fwrevkn7Y3WTF+9@Q z@!INyY@`7SHXy}p)!IfvbYB4quwB3g03*lpee5w1iZdC+M>6lDO%XUMfyXSs7qZup z8kFhJ$~#NYv&1-OMaK}?tUhr(9;kkmPQ6$8$vNN%YyGWgD(LD423xNfmbEYh47Qgq zYq0QlBEBz}_rN{Zf#2@OL7osSLa{=MNE}dT3Ggd&a}+Bd@<{mfaSmJs1C5&s&>7 zOoZuQ-!{+T(sTh*jJm~MBUoZYbaqw|Ga5B{1?iddNa{THB(#o%!yX5F?xqFktzspb zLB`Pz{%fB7X)VCPgS)(-YFm1MM|iX6hJuhbERX~?vFGOF5cR@)C`9h z3ReyyG8C9AaR4&wYM(N_4Kf^D7O%rJ-VoHuiziv$uP-;dKg6RTz>a4a>38CeZF*JM zlw0LP7t1j1y*ZTAZZ07J>OSG)<2k&O)coD(CO$#PCQ8cafP*7pAbD zt!%(#&Yhn25Rvn+}wskQlkZNlIW@$LO;w9nj!oPKri-6MyA zj5s_W3wOCMld>@9$z0Z5GyDS9Zp?4R?FXI@xi|n^E8mCl4lXWW%+Gh5Ddydu6%*OS0lyDg3dRJj}ntXYF;iPpSm^d z_PR%)lb}C5dO?}0RW=;|{_Dwm(L~h*05g%fv+djK!oAs8t^T5I@grPMUVwMgvCR&s zj5P$S{(jT~+XYwzHWwB+0=yu;5^t3C+5e4@ag$b;w+Gx1hrVWNx}qn~Mwtd35xiBd zZ1-9D_4HH*WPW!ySbrNOCGMCg9_Eu!qTprqd!_xQn(2S9o3aUv6`_veFBnw<+YEZ)8zOb3U8bk_~Jm8TOxqn zffG{M8Z~Ctwj>>ND_fi+ zcabu66jTjm*sckM03fTnY%xv;3n7QJ1KcyqrfpvkEf>JrEsaNOoY`_npl|=88Vcpi ziBK#xsJOkEL3fBmco^NgYSz<5p`7BJS7=z05%xq?{oK}lFPf>IGO7hO=Bcu*M9(5E zJ0Wy7`ewqK*Ty4I|M!onc9hosoJ!yy?IT^FHQu{11>H@11~odB1|u#cGSVshGVvU? zlpa`fyxgjcO+;;+0ZG3&FP&ja7X}X4DXGPgt(CXMJ!Yu{l1G66e|7D^e~>zk_x1`v zX}~*Pb1x@{=^CalG`3=!q7VWMpig=?__q2ZQ3iRk9t|Ju6o_uwbD;yDF$F3%r{J`z zA=k`udQZXr$KbRD3)8NbLVL}o-b}ChJUMDn1jT_xqu^E}r1U7W~vb4+4D1$7A0lH3ku_3g@=aNLva zAs|>L2!fqS{jRkFhD>`UA-`Y0$`WTt;z&6N_{r#T;C)@x6nkBLBpfz7>6SBFl?rzS z_POW`XPuKSfL6|Kq-jZbYLbj`iyA&Q!i6@d`%WD_`<{s+qgd>q4lr=wUh zYk>3cxI-lQun+y7I$0J8#F7DxB<2YR*!z$eJRV^Rz;ndmGGm~R}9JDlxa7?p>+IF2W zmNvu_pzwl< z)AdhqdXC8mRDO0b+-;>G<4z&FY(k|#)DcQ~ya##q^b_epII@$auui{I+Vp=^Ca-&X zI*3u5q?#Y?{<8HMwDF-I-D*81hc_v2INYIr!#e(>B8>MWo>? zz6;8Guuie%?j{e`EDBdWs?y|1OoSnj3eOVCyui^7w48AIZTbvD1Sv}+sq7^1 z%;;hdZ_9}IO|V}(*lq7xB2ZKs{4n(+HTMUdPQfd5#uHKw!DXrNht}ej1vj*M26X`@ zn){&?M&fPX7#ix@jNjkQSIvCBoslN6<;KUYWzZt;XyBSgQ1SsVFysCinj`g7(EyKZ zEubrPvH)4fR4p*pUSDh8EGVW)4p-w;_3I5P@l+2Gjd4lcGf*z=Iy=@N+|#sfRgG%f zty0Cxw#QWxh6qKB=04s8cD@3|5O0tN3uVf5{OJ}UVeC$Q<#Enw0BIx#cjvGu5EGLN zh9(f^LM2!#7V9URod$PuDAnikAg8;1n|83WV8wkH>f{lBJtmKlh5bjYwlcQPA?bRO z_~z8;+G)CZ3k#G+d~B|T)F*T<{)LSt&+Ei)jR>j4-StNSYSWyVff@UY0Y`3~e?}b= z{f#&ubBMsop~Gf|KG#Cc7RHL@zYz|UptS$o}qnd}B=u`80~>vRFX0{f~@^UJ=d{iCyXw$Szwt>_F61>RzTagz0+-6Pgz zYmn#5!S^7b6VPqf##Xvpd?k1r>jRG{LFqF6g$pS)Rr(G55k>bUJ0JwsBdlA~v*6Ld zmN@MwAhUs;M8v$-A)AI!SY}@lFu$24PhPWf5IxLctU^4_CW^NMKH;l!yTsTQlYYB7 z|6V>hl36w#<2545i1*QIQEEnF-UOEgYPF#h(r&J+$bZ+1kRS4;u^BAbfE}KD^-CJS zl5Tbd=9c30EMk?a=0#RRal zsB0uzC+PaPiM}S+^L;%6mXxUg#`}+@qA{h)OFoHt2sKc*cr~5n@CXxo6SFeUujfXg zhg=wI;m11u|D+&D-tl*ZSTLWpbUo_){92%CqMZ2PceBM5Hs1C!-;gDy8T?qca*(2T zfX(tmZp|p=149qcD{WIk8Jy;0ZRO#uC01o^&evt{ZopoacslE@r{0fF_l7K1b`1EDMIo%81&iOdE(H7l6@tO}Y<A#d~a#dre6Ty5{F1P;C8%22#V;}2sik?26{D|wY5@)OK6Jc$lt}W#DzH?%{llW_E z4t@}jBqj$jzbgXWUF(0#-ZK7y=P-%j!QU6!yV zNYj18$NE7RE$L#YOHnye z+h@2e(+IH;j%fgx3rf`Mssq*BMmHby)H$?Yz06FmsD6zP2TC9ywk$(?d2m}qky9Sl z9%%KPcla3_$;|F0RD*BbTQ)(lvqr-OH}R3G5EX&|Mgq3fy+AU+KPK|T&Tzw zt$CCvZ0gfOJJ^KjWP@zyN==G#iw8GBIvJUybRA#a(f0$%1ba zZ3mF$rxo-L(1(3>9xK5SDg$ZynkdMA$B?0~HpVU&X)m&UofTJk_A91%0KV;f)l=&S zITBt_vuaCJb8jS#a@{Fs=*dy?H~Vvy%dOK;%$2BK_3@nj>qPB2u?)=(Y!pAiK(}0s zmlhNwb}Gf-rh|!)nrwQ-z{&wtV2N%o)0n0dZKmc+dL!y9XuTwG^Jp*;wFVG_l1D3Z zoZXSWCqLLjKNT*ZPbZn66o~Oh(E4*Ycw{Zuz!IjR->peDqaLLr|-mcijU2>~Q^e_d~{cl#M3=gZ0S$ubCVzfJuWl;QGl3%78b?X*%%bl6-eJ*9&O2t^n61DO9;vw!OD z8bCi+8L0ogL5j;|KRz#AzNNJVN|sfwZber2Z}dMFE|vxB-pP#=QTlhS)?r5`8I&aO z|DY}e$>W~@u6N*KrNXsp#6dfkQXfyg_ImxuZUbjy53F#Cv)W~?7iP9O8KZ+_olPMlhx6#xwjpn6XY38Uujz7NbDKqfN^z-$h|swae=}X6VJ?~;GRjc zKLiS-kI!)LOJ8N^wTbg{lf^Hz(ux4t15?qb=*8ktak{NMMlI&`*#PRP73*+#ifnfE z#R&iuc~s{c>CJ;>!m*0I*b6|SWkr9~4kbAnRA|#D377l)c`~-e8uXo3omw~nJ5nIT zVzO1YjJjE(RwPPLR$intMNs(m3?kSIG39u2@z->vq$iTb!SwPOT#3@wrKp2!A&jbU zPQx_h@ciEc7vNu@Az~R6pyHr0j~TM@nN|1y9+}my`xxP6@3icBX17ff4dy83*c$;e zc|fWsuBe1F>9_6Y{(?V9%0gGEz?H&>AoA+QkWN~>@X-1KQ$bD!ubpH=6|SHV`rp68 zB`jN(kNRifjT6Zu%?V}vY4{W+w#6CDgVOk38lx+_3o1&&yFb{RWO5Bm8#*lnZMGpI zQ3f?o9R?B{JF-RQj=tQ}Z!tE9?0i3R;cizw*mO8^R~RBn* zB3l`zVCH|N)R^pkT7=mZaW@RWY^eRJ#vPiQMxC-o$ zC>aC!6pr-&f&b^)HpuW%M@TEW$2>AjOSEY64LAS+G(hV`;K+-Fep$0qCP8@VUs+wnAF#>Bcyx=TLO>0EwR^DrpKU`VSo=|*gcbd35xDj%U7Esk zkJf>9zp4mCq}+g|^+n=b-Y~80eh29d;cF{v^T*JwY~?(2=iKv!nu1qP?xoNQ=C^xt z(ikuoR$A>!3Zn=_@SplNmy`J@(7#L#M=(X^)i))KGcHoQBmItqqpwk$=jfjULMEkj5d+iL9r9Ag-g*fH2QM_sD>Sekm>$7!fZI8m0#g5H5c9VgHd z)!9&*)A>A1uOLu*C=1L~?#QB5+O6oXNMTR@uq1nf`3o(gBLVNJFyHV#fmZ*4RH z^W%|0w;~XQ5jZX}^51+G&@@HX_f%f-=f7avk)9Fxm<(Z{V?4H_coDqXXXu?E;Tqsz zJri?U(>exN^J2dWus}KPSCJP78V)1jyS4c^bfS#wALB2nh~)<_o49ZlQP}ZNm(2Cb zC5s}OB!_|SO0pbpy1mgFQ8L%`Z-*gqM&M9pm=Dg`1m+&5(R`;lhwOnx();SRz7@&Hc}(5CqnV<4Btj2@C~Sa6$6k<8j*F7I`PSH|72# zJQ@pTr3`C`kXA0|2%YVBCwj*ed|<-rKg!D2t9&5PXJbvb7UnAvX4TTkdDZjQ$c%#c zy2N}YJ-0aY@~@qzE!7KP$?1g5fjucCb@1SD53i#A?rIqE^TMK3Ebw>U6S1{3mjMQD zT4iCr)N<-IN)iBDb4?KR5JuJ2UuEBZwiZ83uo>mD+~Tja$yg1U&KoqO`yc)iNRNEO zgY8Z?iwmE={LR?kF2eZvNCRu6=M=u6;=Iy1c;ORPLgA-qG0B)bBvyBMrWSaX zU#*q^fHVT91eRH&^1!U5!Ky_*ef;f0L*$7Ba#4khkal8&Y~Gl>3TMxcM;ab>=C!2U z)r8%WKJp$`(uK{-n)^2Va~)6=$$e9Dy)Df>%4P%3iFQLF7tcrS`!sR$`wn@(FgIR| zTGBSpJuWuR`a(;^`3QctUCqFfjSML{v9%#)1FUg$FLf051KOOlqu9|Dh?GxYMPc|b9%VobrWPf7hmZI+&CtA@uURe~ku#tL_FLOjFlfTQMvbZ~#HwJ5pqvTSA~t%}Uk=w^dw5;;N%P*Zz(n zv@a#$E-Z~o34EMMtdQOhY*8kRtmmr~1|Z}7j@3pAWG(`2RV;m`-69$RR4z5@-w>hmpnP=rfz8ZUFQVXCg#4@~ zVe(q2iMR-+P=rJg@y^izUN~(;rhelOEMY%6d$JRv;~6G)yBxkJzUrB=^^O?;MHG}q z@zoicR?oiwjM8-5ac|~c$oP0eyf6_8=SgOSpoc14h%1F!VMMA=^(;&M0u)_#TMsB& zu9MX%$MPz+Ec`{uS~=oUz@)mUOreAFPTRCICul9BB#Y2a!zZB+^OX+~t3agKqefBR zU12LA_IF?A5p?j2LxFOAVNM4q3`AdtQJ^ zcT^)H^%n=JP0W8W3-Sywj(R*Fzdw^NKvob6hLEb*8fvT>IOMkWF6G@@#H!nQ1`BBr z_x1Gukglnf>)REjH5;zHX0zp;z0HAG!E_7^%@%9YOEsIynHsA=);vuRZ{hP=!HbLr z0*sr1VRa%Btb1nK2y~%~m)&W$?Nlk_HkXTKT039P`3quPpnPQ<=OcAj6i^e4AC{cE|yEkEaVb zNzZxE4E>s&r>Ykoi!>uBx{2y|anwo~uk~4YJee`i`;54V=+t)n+gyq<95FH(KmqOr7x~6REq_L0Akm^OY4?nKK=p!nbyd~O zh07$(_1$?t-I)DNW0O8*M4@z*I(0`NP_gUwJACTH6n#E@EfJUks8E(5tGOmKh>&wU z5=)UGKAfel;|+lKWRWox`%0%p=zFG!#bX~Nz%FgbHViKm?%see;y&!sFKYRZ$lfUg z!l!9T^Ml$$p-Z?@JQb zNe)@6@rc7``Vq@O19?e_ySPq!wr=qmr>2BADF6cx<--lwflKq{bP2SsJY@f1AEwI8@4HuSgu)0?L+SyEJR8!`F$k`3F6-sq7S_dG$uHK_mt0% zo{H{uwXR(;K>YXu?AXmh>4=+HM1nR~X2W`_8&qQb(AKBP5S(QVW61J0_fn*N?skVo zGelBL2hA|xlx2=1*!Gg)mpf`J(W&%p)0KQN@G;KR>P;SgD(6eWE64z+GpB?*=Kj-n z7D)7ks{GWUpmz;`+eX!{e%iQUvQSOFawyY1oZYpm2vhC^xb{Q$%eY`N3#Di0f21@}`kdS8ofv(PFMY2}tLbK1R)Gpjqhg@)a0P}1 zt>`KQ9*x_OY$UnSx(S3sRYOu8OHn$-4OK2)8WgRj(|h<<11j!zkYXT1FY|tcawI%ua=nii~SEOIE>f*Lch3Ufm%eb_*S4osNtgy4FBtZdU6av=h0Z{Fgr4?@QEoZ9QoLY1u=a9<`GivV8IP2O%j#}SJ4 zm2Uxs`d1j$lc5$t%Fggbt#VPlFw6~^@{Z)GgHOqCPz`Xm_1n^a)99B?av#E^HFyR~ zcgSHxa%T;WzSyE<{mAo=QI~7ePDT?XNSc+2A>DN)7s^1_Y}I!c)&T8m0;C`LK?eys zZBwsvd(u-|zVKmF8ccV?NeYSflZ%6lB}V$BH3yzK*fT|ER<8HKu_&3ufC zF)zFn{lN@VSPn@~i9uM6-JyVewK6QqpH#!)ZbJr(ld{@d5@}(CTeCd_oxf?foA17Ixrp`Zc%ahkTowq2ZkO#NffvE4si)q zzz|HIJydnFRO?!C6FR0myNL|SF76;qXVN})4dyHuLVjO()uI6awOc`Bsxq_n96Y_& z7{-xi)y3^R|IcXP0``DfruQcz^upul&N18YRmC_uHLVs2B z-5buZxDgV4lhAQu(oj3*LznINLV>caa=S(U+b3+ucfb51>(K>}qx>o74 z9C9+whJh97lcIu+LEy5^#SXjiGCku?zW2TE-M9xgxecjj{b~}k7yy2Lq`#?SiUux zvR>=B%`NLyZu?_H{zfC(K_F5C=V$WyW^g8F>XzBPNK7z$5*6U6_%_u!R7n=AByut;56|x>+ z!nGAr#>u~hj5V*1#mlDj`ko_?qx4LmPc}m$MA}vg?fNGmyWT_@APr1Noh|Nd)M8@J zH_K6qm+k9Wt_>uA&2>@OaPQ`*{4(ngA<}%KsobEp%*%0Iua(2x^{pyiBT)5_!^#x{ zGU#gLOJ^>*tE>kTrK31aeS|7zTRKso5D&QFu52fKpZMj4Vd$6&JUnJqOO(y+IryH@ zjf7>9mpMG6tD`D=gDpOo+qNEV+F1Bw^lee+|NUi48BfdOX887idUU+o1KxT|Hu-ND zKYd;2Wb7R_%hW?kdou-!vFw>hRQi%PUN@44_pU=2FefHzTjZIo5`|5FaWK{2g7Q%|)#4qNIOzDiK$9-^NAsqBr06cs&Pmhtiw6Y&0O}PD#nn=- zmvG{!2KK}ras3P_=oC}s)eSddk+m?uwPc*)57iXvzTw+^2UgDv@YpIcv0SsNv9f## z?Td~3OIWUR*a!OS=l`&`H3-!@`hEqWuA`3>@?wTS(8r&p?Rkn7fH0gn{cA71CVT8o zoEgUt0pLx!TR?gPe7ikAfL-G@tLl=}Hn$!=6>}m>@h;$hn{W!>Z zb&PzuRRJ&n zeC1F6NDDi$QR${MkJ?KLPE7;UH4zCPc7mT~-C zQ%?L?MM{z_V7hG$_75QDeEIWuyJGagDvEJYa`@x?2+&O{_-qH)aCM~*Tsn1}whr!< ziZxn!q)*o}^}>4sXeZeaJxmQVr|LTreB*K@a4F;o*8mm|dp(_|8ie4N{Q^aDRAkh| z(v)2_QM~6Kc7{`)S$kIT)(eoL&g+f3Lj8U=nbtNdH-$W4K~SK*w-tiI?wCwzttVPY z={s^dM3`%lX7NduMUFd8u|N~jL&p~p6H>tl{yonTg5>WI*{WlAL-PovVa z%4wyI(gWENjR3?oMp;HO6Qt*}+G!g=2de?!2#7ku>>y<$l8RI8321;>V@4jf2)sKH z)DT-VSeIocAKEJe_8mmZ7h7ho3mzcD(1v4Qp2elgy?bEl8CDl2F_i?meRV>px)H6~DJ#h6U zo+4&9FInjnL48*1pl}u6q}ucHz3~FN({WsMvAs(1UjB6rdH9$CnX2!PQvoiw2dcg( zP(jQ6DyU{du^9WHq7ig^WakDLe#R~G550`sTxjHG<9w-$Iw!uq_O)2;PAafBvx^di z4%gGz-^S{GqWuseUv+uSd3qU!K+=^e`7Bf}4WcwOKTbt#2PinuQcp;e^6J8ct%xo3 zJ%wT3;D7NNJ+uaQh^MlXB^klL<{-cWOPMAIMPGD^011x`oZSHh^qu;L(pZkifdI6P z=ru`Md9C?!Pw5BB6Vrb&pu%H2ZL6`du|R2urbi#%P|L`N{_=o=DjgNhy`$bBdoq8n z9sU`_$@B^&26rwgnmFjGCHv{1`W~ak-I?9lWV>(zf=qR!Y)uDDb71FY>?CMQKvf+< z#!VfJOi2X!e)(nHz=IaRKG{LhS_$7)-ecJZpCNcfW`7E&p9^!(gDU~lU%^apvGcMR zt@3#-h^J%O>?lySXfjcDO}5e;UkcWK26*jF-TItK%kWkg-m-dRZdNC*wd|C#DxJyM zCK*ve0+|D9YwJ?l7CE^Lpn9-^m|KVv0X%(^4QE}ikz!+6n8~Ug{3AX+B~kegMRv;Z zXd`d%!J{`xbki~?o=5^CSqkEUV0=UV#F(?#PBu_VkxVMVajDyu`PkQGHfy#5v!@y% zrUe)Tm)T$Buc$aZVb3+lCnaTQ=R!JL(lb{mk}q)A?(F&tm`p%rJ73rA~tf|pZ) zFZZLx&(-Q{<&-a!MF|w<)H)s4G%Ls zv5iLsS@OVa!%fGn_k8<{O{vga+nl07ASHuS|NN(7U4TmOTCQ;#1ey}~(xHwQnVG)Y z%@waLy*Mycb0_4U{XRKY^#4l*s*Z6#+a{HF%)!$A9U2qC(`p2wB{U`#zEngOwQc3M z(QW@X!8hGa;@MDtvhg-LPC+x~LVE?oE;qRZpUK5WJ%X-IGy#aVg}upm_F6Y$tPU zIW<}?RfKojc1HskWP0wShGfMSR&tNsfz+Wv$xhZ+MH&$yAtZ-hxuo65`f|*OeE{4f zU_Kx0>fPG`*0g`8*%>N%oFv*q09ds+-f;-p4aQAF^yab_iEa8&T?jpgx7!vriBig+ z&z(?w3epmDBXA|#rP58raWVzs1Pk4Y;*G~o6X0W+U!L2G{N^howaI_A{#ZTI#Hsbi z8})ApjInbtELWi5@KNT#?({K|xCgKMg1T2X!Dc*7@+h>P+8H zi`IK##uFSskBfV#+s}kcJn!C&Ay7$m^2W8t7Mpcuz*IYdYqCJ*RKibhjc_0ma>2ViSA1fub+ZSKCic1cK~77T=&3u~hD6T{@)g&Or=0etN|OKCrI=fhx6>Z?nGP z>rYzLsJwM&viW)xIJyZ9tBWvNB?nPm*XL?6hzPq777AdF!fP9@0y0$CvY@+>YDU$t zHm2&uL^4fOQQEh`eG155**R}f{ZvALP|t*l3AjbWAyi+W7`sPXiJ_#4)QY}VEbW+H zjcc5FVrHzf72I5P-_|sYN%`z@dmL`y@@`?k;_)D;gTFn}J6EUB+@)c8AlH~+33@1E z^dMaxwrDIBA)I&|{lWwN`g$;}7AYsCLXnoLOp&Y>)T*^P_xr9|MCuG{gIX0-I9M)9 zSDTUO%*xO-qSY$u2-+2@85W$+pZ}_Wx~-x3FJFMht-Nu74+E)r?LrC{dD3h8#k!Ua zS~!yb#9YJwEj>Sam!>?g6pcp{bl)D6Xb~NE?4Lww@rhT>NLMU4B19J1z~so zQ2V4YhJJBTrj!z(|GGZriP!hB1YUrC>?q1*v5Hf=4^T2|X`ls~4AvAsS>;+SPSpTd zD)hQOp$sSaN~NaQIH|Z_n_{z>V|tsyhc+0Rj-~&TifJ$buc;1f3;-2Cz{`R98|XV< zM75&fe`2cY=l@KCK4in0uG}ud{fC&K=lM-21USVj5n;LVi$2FJDV|j1crb}hr7?KD z_pUBi!wu9%FU8#FWT>Mnt?FFP?=&%C{>;4bA4F2+&~6kRMF+kKuKNHl(NFgiSq}^o zN>;{mkEXa7u@-5&*PyL3LymN~oV-s%O&n?!P$(;pz+QLP736X64N|BqZBPq`98s_< zWlNo+uJcU-5w@e2Q)2@9UnlKvel7cDb22Q%hC=6}Jr#3I|6O@}8UOs}k0-W-Wg?#^m;E!ui;M|>LE zB$Ic6X9~o%84pxUH(!m;RS?1l1ye=!tm72R3Sfc;R(0LcZW!rxuwfRIT^b_JG$)sL z_qfK(!el$H{4fE=jg42T884I*BH|@;#xGEteu_e7eUBgMKz(zJDn)I0^K$k&}I#Q78A3 zs(;QLq*dFZheM{^LX`tc4dKl~O6&fvI-ll-NkWc<>3#@j%<{jQNn|aADt;Xb?an^9 zw>~W6CXNWIJwXzHNMK>aogc52T@2I3-Ilopn4Hvo}V#_*x)FtIzjBV9=Qco(ks1dy$ z&7rllMISWYKlLF3GswXb{%>*QrH&}h(~#$N_<64 zIe*6Jv(eIi!H_piSTcyEeIGw=Gp?=i>uu~?3UiKh{{U&S%{WJEeR49u2gN*En3PLo zv;O6@V1L`ezK+(W9Zu?#Y1k*F5heQBf7|Vz!~(>xUVEhGeFF?L)YfmhUiiNO5B0Le zwkmB6=sU@id$Ct^XbbD+QJvZw_<$w{t^lk3LKCR0cm^za8yJGyhIUwk23I9exzJuu znJy4N#M*HFAf9R!o)A7x96m_4Z|O@B7th3OErxa$+ax#{-Z$YGw`0={+;mUrf(~*U z`bxJiTrPwrl&PRBYFZf3Uy>~Q0r85|HH&+v1IMOpovH0)o>BU@=0#Sz&-mC zR&!jB3y4AfL@oW(G-cgYBYR;0wn40b&>#;Qwe~{nZ)Qru>dDDs+H>TcET>{1iV`SM znWC*oLL0SsQ`JG$&6TCrzr(@;sRRH^t6{P*iHojw~~p z0dB#g74lEhAIPG_7Bwo3ZX{>n*s{tX_%L6T+s5y9I=iA zT^?5}H*Ebc8ug$zcwu7!{3{>$F`|7mdvqZ9tlx-d%GgtkP2g|W!&J(D1&aSMRb)-@ z%NO->yxt1B+b%_IDPHJ0{2$x7Nj#E>!%vm=!TtEGvrbfvd!~AA+HH%)lq{eyET%C4Pr5Fm->L8lMW3^WNnm3wXmPfO1g|=|2lh5GD|3~_Q5x}Mf1g@q z<$Sx!Odz%!z+dpZN3RG_00`pU1>BahDCK`&8L;amW{YR(Cl>rL!16|}S4@`Nqd_f| zFm$~h0h&IPHS(>9h(uK(Z?Q$P4s>LDrt=1~JioR~3i^8=vXa@~XD{eLRg^Bsj<4 z|8g12CO_l;?ft%_uTRIFM4R7P5rUbK`kI6y^kmR1JXZv2#9G=;T_|T*Y%W4yVD<2y z1a%}pI#vjr3>kZDW}rbu3iu9lRAi#ILhBud5SHsKA*&CThK}fr4dn9Guve2u<>+*c zrv!H*{e^4l5eq@0^x)K~1m6I%odyz|ZB2YY_$bQ=R671L&AlmZ3fSKCzKU39iH!07f{& zPu*sR=R}eUR;?TC4-DL-ZPDSdk$L1{Kp+70NVF?QtfDzf4M|I3Ev9Q2Tx>OE)=8uK zFHUda_4A7tGd~2Qjgz8k*aURu?JYpt)_0D zMOzrm)9J19YSToV!N)EyKCuSBq@vWHPjsY)YG2=SL~8M8E7(_C`YzHW{wk-lu0b6X z8Bv?|0h8?z)O#lySf@&0mpg)2>HRMh`pgULo5;GHQe!px>ixJ?bP?=F+oY3%y&$m$U|{uwZ?!IaHG!;MZSFT8*1!8 z!#SLw9Lv1@Gm4TigB`nq-XW(fFWF0WMu#6E%PKguYZ_CY=COoyRZiVjN6FH$-4oD> z?*6b^IH3x;4`WjGuyU~PAn{Gu@MCVQzrV-RUIATe1B|L@4moak5pz|=@;Xi`cF8>( zyk~m@-ri!Qnhgo0xW*B=HE-0}ziKoC0|o`L_Q%X_LADbhJ9p2$rIdSf90T@h1w&ps zbQ9jsfZQ$5S?g(qmdQWRJ3UbUY~WM#W8e;kvC-NuTsX1qbCJAMFQNWEZ*HzKubFXH z@b0CkG7ZBh-7f>}ETZRl&xzpfT50@~c@j zpu&8ZO{g(#btY9^hhe=U5!!6vOlJPX*>`f|^p22ZVf8XkDTGL>rBKW+9_4)LpLg0;|{lLgX0}6+xH`0h%pM_?BAe$AHUZy zDr*?8Jl|u38d-yw)JEY)(zuxNuLB=-dB#WBCBJ?S(OVSG@h`lAgaw2_zR5dHX@l6Z zy#WW53b7tZ@jkU>G-p@RXhkbI5};qwH0-l_Qj?n|B6>fdk6d8hs1QW#t~gG0y)@Bc zF3l<6n2aOJUMz5MKrjrJnkk_YskI8#^#rE$6VaZeLs&7=56it~+C8Wqzj19q0?>tpp88DRAB)hoh#abAhC)KrV2!sswKprM*vUmB= z@$wFeLm*hW-9~K;Guqa7)QsEn&>nrKX~C3Rx?9(uht3L0RX;L--*!r0lql<^9`G`b z{AooNLBY}BRUK@kdU>z;-$ z0hBPS_$04>-9NW$ld)_H#5^Cdu`iFQmUvb(t>>{?HiWn5J*~nyxo{~u6{BqFFG587 zFfskq{jJvt0a(U6%ftV_VuDkq^$v9=NbMmS4*af!|hCu)? zECCCYqLO&al3&Fp z#*aOKX20vGeJlN3PK}3BCZ)RL08>$cTb%Fk#C57GtmhIq*$ruFDppF58MDV9t&|#K z!AuMZK+uz}qzgB2cDIM+nouSJ z!aDRz2B3eYE;EtSEJ8FYG+*m*WC|G zB9N%Po&Oyak*w8>B0ehy(N&SXiPwZH1APDfc?nq$9jD}|2u8N_-C?odQ(hg!DyD@K z35i+3p!&al1p-@w%1r|8g%%y6^eejY=j<5>tUe$8{I6OjA=VdtYXo-yKRxvm(bB?|R(6D~ndQmVmyxJ4G%*9qY-vW?|P?EhhiL$IjB1=e2kh@Ka zU4*2tQv$^QF&#u~ zUkp=c0m2MK$&Zr<>MC#2#g z!@?Ct%>j=vl2)15ZXV3ZY5qjk!mi^-IEF zhZ+g$PaSp^nnHcVp*AYYpWU+yAwitXfH7YvhmOdonUSsp$wL8*3zjqs;PAsHSw*2@ zpeIt7W~1oWS2o*28hrCX5?^v7F^+}JJ#p%F$ib)u^GLo-CWb0Vt3^x8dZTlwppF|& z#4k`BDCptAtZ>+JcuI!#jDE){?a!hm>Iuvx_Ego+)tf2kWTPe|tEv?0bsfE+Mw|l& zJF^**qMgN#R+dzM&BNB(S(Xh`flHd_$4d!8J_Grt81RTvFsGu833v!>AYfkli6Dea5ZJ^KK-HYac{O_a<~2+dI26`eJE-Z=}XJiIKj%RcSo{ ztJ!_JM;U*L<%*6)4Ibm0WH~f`8-1enIeRZ#mr!sOW$^73eSZ6*P zzb{~+QVq!W5+ud+`yl^V$-$j<1?jsAr+$U?Ec6HZ{K3z2qAN9>5r@oiUKCP!aU_NC z?{g)|Z7&Llx)oVXssQe}s7qkMUsFm2t*BU5up#BFo-q?F2{XYKgfsrazOu%9&E~tN z73)Oqi4H#@K#nxRS3jl%hS|InFC#amK$vtN(qo$5wqSTE-7qnF&v5W48_6eJ34UHy zW_KPx)DCNu;Ia~}a9|NjPMsPCh;hN#i1lM@>J zJCrOq5uqoDmTdgwYm5Z#H&dksKKhd%SW?#7X2jMVQ?qrc01eL@Nec)QS(q$$TCcU- zT5(9tamd5P429L^Z@F$wn{AXc>Qu0zEFJ?nz+i%IX(4QRX@p1lk4{vP;dQ~`t2l3R zGV>xi0YzpkPmK*XX4!%(pl!1yIEEGiRx?gX8%y>@2fbLn8J0LNXRNg!r)?S8|IRk= z@swI=tqR~r(tv^OJL}kVGC9}d&TnB7@t7wqz2aWbp=QkxwhwSTzxNbTI4XIR^>{xc zl*PmA{mT3!nN=1FuCb5ifOV#@Pf^I3_IwfE_n(6}kOeTO;yC9UQ*_~UTIR08FZpVa zEQGD4F+Nyq`XUXDQQ%l0C$LiL239R1B1!Ma4A~+Prkk@vIP|a5=LPud7@MFCz>a;S zhC!k8GP>C+a`q)81AZUwIL{B=<_$?v9x4#^obreCErad-Iz3w#$*$n^)@5Eo5lDMU z9yeJpDO;6xk8!mO0^YLc`lD*iuz?X!Dq^v#bW*$;=O?3?E;PpRFbgE(SJWIyXnIFKDs~;cgjP3-8YY zzNJY>D)LF;G0D_OP`VkiO}C4&@5)jy+fpo$ z3T8n@OKB?TBjT@vH*gO z0KX&4rOuGQ8U(=c3W*u_1)lpLO94Q5Mu(w*lj?S$FfgHhF!)WXFEw)%NQ~3RAExu~ zk_=a^9rm;rH--`S(zV~BnzkaZUGhVdUlXAz4mM2=?)x`BtqR=)Obig5_=@XO(j!z0 zG3v}5d4l(m$@OuJsCM`kfiv^H0IpWaFM)fBIr0ShjinE05}G6KKn(S|Mc`VMpQ>;I;~%Czz<6|o9r2O@7d*bycbyXh zUMH4QR}Hfx`9?!7|9;u9IW+o}+!f-j>ThLQ`7( z7NkF{Y~Av2wMLl4kk1i5`A^6Y{!nn2cbs)o`+Z^?N%9j!9Y<-8+57yBRCh)(rlh`A zUvV){{0yehZ`PMTn$>Ba)q@K5Z= zgCznmO)Z8wpOg>`^~u!(`GqIra9BD8=V#SP?|XXkUL@wT5C*r@u^mlN@+Qy<-@9HQN>xPZ^^LHLa)Ek!4`Mvlsz;%$F#$Me^ zB2kikvu$BON6I3*#Se7Dq`>USKR|ly>=6wA-lGgmGy|B>i(Ap(hY0{s80=H~NCH@FtRU5;g2Hv3%8_#!b(XF*N zh*emZr+g4I25EOC?_GF(BBPo3V;IvSMc1A1(2M(K;N>TnIA~Fz)RHg2GgX{@n!|b# zse9MCv*4K0IoLKC1JQRBrcx+)`mR`Ww%yBS$S-ZlDOUR%QKsB$*Q|6#tNPC|c9z0$ zZwr2S$WBCt?q2v4!=Dsv${xmviEH!On6wmU!k-!^a35_ral zyi(r?XZyMl8_r$&%1@Fhjqf>Ph}`x*DcN1XhmQh(Ve3WnPG!8yGB++r91$$+L(kcq zD%$%@ZJ`h5=cFtcHiUez8C# zo@t`5XZwib$mbXevEeM5D!QKbrMjV`ZA-< zRgZ(*FP+Xmo-$F^^XuqzC+(0W{^Mh5)@`?BfSgyyLRd;I7nxNssuR=Eyt8`qMX;cN zSA?c+y`bS7gMQVxbFnk>e*_6i+}`4%dFE$Np;@#0IY+;jWxf%#9Puc$m%px`B0bB= zcf09?rVS9#5}k5myXWd#C~I8o19g46E^J$GO4<%gRP=%7-N0b)8jSXIS+CR<@~>mB zJVy)ud84}c*&~-OpmzbZq~MuH2i-p}_aWxTuK|c>;&65Y)*A`TlvqP^o!2d2aVFzv*=1f#zOHM^>Zg#c=q!4Fesf&=j*?=Rq|~59gqn=OwX`My<)tRh`a5S8#zkjNb0D>@rAlbn5hBVM76z)KE1Dk{&&HTAm6dZ} z?{DfhdqK=k7%$!&hzVP{wi}O&_%_uahF=93!dcPB*R2sskGyrr?#Pq#dhb#iLwP7# zdYW%_!ShN22XUXo-K)2_OB2G)l6^^NF2^~n7bw|uS2&9Rs=}Cx41J6ar{_FmxlPn6 zio?+6oBo9$Sb!PyZWE?JBpLSogZ6lM^dKe|&`=Cp94QmtB7l7{PP5^euRJ1*-n9-= z-^a|4vwsS+HIXrqMPDx)=2j}Oo+1HGI&W9^Yo4xb&sos+H5yeg0;oi2-U-u8u*29; zgBQIg7N?*S9G^d2Tt83YEf&!0+jPeBKH%fu*62)=u|gMCptgG{b~>4qvlkcS?2w*~ zdv!!6erlWM2WV%Kkp>w@kUPrC;Lt%QEr0^0a9!)wuP7-=t%+QsI&(E?^zaDqYDYxM zDqdIonD(XnU|#~lcR9{gXl?PSdb7P}?THlhcS40SLJdGlPM8}KX_=<@hUV=(c~kR}9?f4j=0)1PO#PTrMji|HTkT(qejG(*b<^TZ z*$_<}R64@&)k?%6Xtn^kJ>CXs#z=wBH=8(s>qLMiS&ar+dSe@}SR^DHr_r>^n|Pz3 z#44QW@i$RgD}`nnP7N_1er%_ZLT79@m9TMt0eBf}Du|`A zE(EQ8L-IVBpznZMmt{fX@S33u~MaH%UJmG|d<$$67}3|(|TMr|SuG-i>1Sb3{VM4{#+lQRof zU-7W|93sC)B~F=mDXorIpASNevORZJEie2mYc`ENouH(h@WcYs?tiD?IZTmAPL2;! zVGW&Qdjao)z=A4->SGl``Wx!bE4KzV<}%!V@hZ(=%4jIj7nU>$REmZeV^I4WZ|b(Pej%sGZ62|TwFBo zIrBA$%f&u~o>7!G{4_(Kje1d$=6!B#uz^P&U-HUcRfJ{{;9r*!_4Nre3MSAsV)5|!f4 zb|sje8d0GOq$gGe0NiuVLmEgdNO;YZ_}SK^X-; z++7_~^D>J+TU4j|dP%K9LN?k;fI1HpayekH&AJTp7TpBU?tr zf9}9G%0PW7DFfF*SqsxhS!C9D5uPWFIB#Q<#vOYnUU-A9QhOJS8cv8piqtra5rvS; z<4$Vi>!$bB*u-ho&u4AIA_Gp0C9SgwZ*@d-OK>EX z(G7hF2hzRH&>D!`P}YOoh7nltgaLs~0a3yIeeR(6rQ^M)A1UE`U#XK7$yGEsPBWKU zIl$x>8%qWL~t($`$O_sYaG#2PW$w(H>5&{W`plJPG>OP5Zf^lNqKj%qRj91EvBM zrahlUMlY0N7C!mLMpdUEdY=C*qWW?=b*L!~P;E8oyXgEtB93QUKRpZE){z4`vAUh= z+o1JzJtYu?L>L&3QF2x<07D0pSi0&~m6K(PU8_)f>fTsEuq#0kebS9bLEsG-t_fxQ z4En~Ob8iXj8F}88FQ8QRSreM^+8PW%w>!OIJofCIFK(i74Ux<<&`3&*S&NO7@85XM zBJ(tcx(en7c8J4(JmF#GBS5(~AkA$Wu%Z(|pWF!ld0BbL$0369eO;|Ld}hOQyk4$S z`$fAHS66wk{PPD5Mz-4z+lhYt5h!OE=ZUl=TDM!DwTMd1vtOHwx7`c_M=r}S>Isz| zNaM|#I&_l3wXwn-Oq`cvO`p#jh@XM!I@44kgR6h{Fx3H*l&Fd&KS(nEM-I=PA10lf zi2=wgzsWGL(=ZY+?~UMNx)~gEK|IK=3=b0_>aTlNkYpny!_{Dxc7JmW_S-7r8|7}` z5oP$UaJr)|R7K4lX2R6Xp#9mC1lrg-$38Xb*mttN0{2M6u}_sa+H!r*ZaW5id5RT* zQ%Z>h4Moef1egOX3c>m)C@W)_x>C4jeP^`YVRHk(;FBO_wCE-U|HXn3m%vus^vR9wBoiG6V;QBHr zLXkR%L1r12%{k;WRJ5hQ8X3|Tf-83va)RZToppn>uvvpxWi*DsL$RM5xq+rWI6SzK z7>^zU{F%i7JJBBlyNCj&#r@I2Cg;ikx^b*?F7B1FevSL|gyb#75az)TaDp-Ac+kq5 z1dhhuvKqbh6n+i~6eVqZAo@kmC~kF);vi|iiydb0#=T_8XtTxNHeu_^ABM7YOIbYc z)^m!V*tp?gB$NjQ4)t(Xe0XmF=z&c{@bIiKwIx;D)8DCvH?#C$%FM<(}_wBprU!>>G8hz)g2N-?d{ShIL&ag# zb9TRH6b?;iMBqUkknL1?yUMLz=? z((2#*d)WLncC%L>{)phfBkhFU0eSefzLG*f92|tlcQCinqf#A4gvHg5M6lhJB)OL! z&G87@>AAz&oc!=?V#J94>nOEF-Vc!-6{j6!}MBi{lMOP z_yia!hdCiO?&b?M4!mu+>(f20GP&sx)x%Z>JKfpRs%}a%=4myL^3Q#~k7~>1Pc&oR z+=+Sua)4brjWFY*x;pA$eSg_N1gvU-inU_-jJ}45S^OhcC~4r0WQCzL zR(;GGIBkM;`1BP5muRHQbMl9Rs|Ii0cquq(a=%QeVeX!`|GoYt-1q1mDcE!IrwOgI zG|bO^l=@wY3)1BT>&%+b-%Fm&HfP}n0Tt=+gEdH*&y)e;EMM8x^$eUCS13n(Tpzx! zB=VS^#r>eQsD<3qi|WEy>(Wf|i+3U|o{PqUEftlUxV5B>YhE#K*o)#26{R6a&yG;; zFFRw>7|QI?0UGjhu$=I(KI5kr{-Q2=RCDym=rj7FeDWB=C)s1!Du>!%1Q;4{xgPq7M~@n+b-{6bcf3~U zexHdVw#j;o;Z1NV|8)$Z!xT!Ak%jokj?3Q^Hf`UXjB4kd&AgoDb zp$UdH8J-s^!^4Vv4=WO41ij5KJQc=CIcf3HsJQZULATS9E;)D$i$fCg-1)IGZ)$a% z>VE`2xmFhhazsGn3V}nr}> z`HtSLW=AOn7Kb@274?Iy>Gd8Fx~tH~QInq=CsT+GWcVIm+j<-o{+esAVp7n+J z7FpMmO9?kASyCHLA*l!A=oyJqVaW+&@v?+g*Ee?Pi52(rp|bK@Onx50 zzd(x-Q~K7=?B5N#<7ZoV-UG%4Kt0=pyvsf}bLmtIRiauDdbpB)6`Y!l-E81<>rPcw9Rc=gE>1S_E?(OG1|qgbbWg9T^}w(ow~xDad>Tbfn2(=f(}O1unR`56w#J@ z0{Y_7{v=X}mMQBSdbE%g?J+gv3z!+sdHP9kqY>r;T`=2)BG-Yz6SA47!{p!ZLZ2~` zP&l4GL!o2AYkhra7f|p7=Z0Eh|1)JC4}n)0mb)EJnCmw#uG7InXfoHz&p~R7*2j#N zQV_~}d4z+UXclyuwnMW`(;uCUS?nBsM@hsAUi#IBoisOVMi^QDIye z^C69U!~RYG7xC4bb5sA<>E)`|*^d5x4R$_!;(x)KmSRX8U--7nAK-*F^|vJTVF*(< zOZ^_rh(|X`It*p|fV-&c&j#WA&-Zp1p5Ec#4RG3TtG>t$3QnBu@wxLwpYuczDqBFE zY4%eI5L%LyTkpeVfCPQF67?cq^wg8hpUGcvlnr~Lh!EfDgi!7R6&w%U<7mjUU!;NR zO@Qj=$f}f*@@cW7_CS~TR&^SH?JwvLzE&cwdLSMMVivw1IUZ2oIC$;xV@?#w+ZuSy zx@@W7z;8n@yD1)l{MF&0@rOcU2MUNa_sHLXY;K_vjl%lu=31)T&`!L@ovpbNN~vN? zt*^GI6uBB~a29rbArzwuymU6)a`00WN$vVv$*hlV7u1!2vcN}H&T4vaNDGy=h-|pe zFebJQN=p5WOPD)GZO%6+> z9fh@sh7(|gj|_#|fU?Zazb@C^-rtFTof(Vz>0-rCJ;U#)V8P3)BDO%OiTGtt%Uo#X zF8=nVD+;dvRi$7}$oBY)uC%*oDUr!HcD|OtH^zl^xY+?CwZwMDcU&Me*%e(2r~2o% z_Ggf25xs;6i)qJ@SHDjUCzeX>2Y}NXOrYp!AFn2ej9*Gze1N%?czi08-e}qt+wQsN z4Rs9tCr-1A4htxK+72@R5~OXqcGKsPcYR-q_rHfVvl<*+y8ixB-<5&6A4LUlyIDLT zPJNv{k*m!=mXNqPBu~rqo@Q&)8Jn4OlyzD@t-HOlg>HCeS~^S#gs6)Gh<01-u%XB^ zw?>Z@r~K9Bjo7XHVI~D5)8TLgrxGIsNIRuO(|RW9qh{MZ6&O_GIUp$rf(|Aw5d5l! zBPwVlekFd#!EjTd-O4&&7G9@HT!Eg7GDiB;IeA=Yuetdfh}y3Nmj+q2_7D3e3qU%+ z&-A(eCr8Q{?c*a1VUD5w&kmo{^7B5vBi*qI{8(tNkB7&WM2VPy<7dE|^(3`mxn@YW zH3DZZuz$OhxGL~6&kBJrQl=4EwnAFjwhd}i_w&__6}?$IVh6sjao*~9Hh>0y_o$e^ z_FO5uuDBpjzOp~RGx96!C63{qIy>k81z^{??MDE~on zo<>&~YcW@yB6`$?;&}~n9#wfVQYp!e7UKU(8kFrMA}ltWq}wLnUvl?ergORup`8-k z3x{?CG47tf+?_w};V>4hMqj=*_?(^&CTGBC=HS2$PqIC!8oeY^%}nzJgXZ-oUctz# z6t9hvqcCMpk0lMBOgH#RC-I(H2)td2*>gmZ4#6e}L2Xf_wHXJ6D$D*RwoN8w6{1zJ z;E`cL1H>rh<@rVl(4aQ`C+#{Q)iI){M$I>`v6A66Thq6-E{9wrzs#|Ui-w)d){gc# zlPqS+6h=zvF+2w`Dy?vbX#uOa6K;9@dA#{I@a7*-ZdZi*2Jlu9oN_C4;((BKYv)%Ci|6_%c$HvakA@-gXaFaleCK~SD z4aJF_1>WKL6LR}_9a#@Q!rC{MzUv}2YCg`QBf35NhuT({BV(!%?GggxfLxO_vD_K< zF>g8~;Ed3*yI}qW0ko0c^{}H_zWgs?E$}hAO|340HO%~OJ41c$)?V)R<>pF%gyNcP z3UO`w9YyZu9O!@ojzHqQ`=r#D2N;kPEdxah?(NdN$`0r-a}mzRmSQZdWw7EQ@pRvW zK)}%9z&oJE2J6;POOUC?|1?QvTeR&1n`b01yh|+M;~xl2ENk_ zXkA&oVAVe*Bu-mu{6;uo=x|{%OKa~R3*ohfGLc?@26glCwd%Bg6d%(WIK!rRumoB~ zT2ufsMIwmmi@X@QMq1``MK^&Dy@aTW6|I7gD#gYzG8Yk`{&=Y%OiDBy(sCZ{q?>k% z0gl5tq!(+)blva%7Gm|3m5Vp^+hH0d$u%)0#pf9RMgtq;f<08M>-45zvCI2j#|!-P zFD%BA|82)Z79!L|HrjNa9Qx!#oT%3e8lwz;kDurP!B-7vz!hx@v` zzTWp=7Y%)!RZx&t4l4JZ0XUesyGuMwY=TDtb6%GTqS7K%d!<1Qmu3D`pSn1$U{hiW zBvS;%(ximnx>ph?j?wj?hBq$tcdHs97OsN;l-N9{h8q z=|ze}rJ&1;#DiQTylwPDQULUy-RrOGn1A*mf9@$kW6BhCCxOMj^k8oW;0^`3Slu1& ziP^*i*8LJfc^l$V*mi_)C3bwO!qK9H+s|qnK2&rHIgL&(q9lDCt+6*1ha4Q=bqd%} zrBJstMA)NChPwJ44+Xvvm3=Fw^vR)m#yU>1IuF( z@>UFvW>EerBg}8$`SFKsW+H`+oENemYiIsp$ws511nnN|j7y${0~oV;m~-Zhy-XQM zv?70kDlzA`T}Z>fSz&r&d4Bqo#UH>!$}1OgR4l=w`g~$*Ae_VD!w^f4a**W*fiRnQ zt$*OZ##peUn<>XT*Y1$$q*wumzYk!36LMu1M2%F_wh3xpUKlyVDr8BtP6yO%RpIDHG|bJaNIE9y?^XhdS;zRmMa{3(3-vIitYg63*R{N(20s0jF+Z3} z%}A538_m};Xa!-WSvEQTZ(87lQ4^~6w68g1uKDSOwHeTsbu&p}eKMu;F_xDWX?tSI z?a<(UO`nR9UJ2SP3M|XV9jRKUYj|iVgwLL!EZ`En$3|U}rR3mcux&B(ya}@DJn`ig zoKhZvW=<330^1y9-Fj3h*W-xQdlhX&JGN?{xJT*~xqFV2>)xcgFTuUjI0_y8v%l+R z*1&^oFpg@jz%k-V${~pVz8b@#9b)cz;SIChin)?^0~jE;4GNbPUqI6YYwo(OjUOB1 z#JM*G;}_(4b}0OKxjBu1FQZiRz8XpHcpPhFj*s$ zMgqPU-K?*-<1C9BkM;YX8^KeQTAuxF0I2m0_=DCMceO{C<`lZFEWRAmPuB|V5Txw? zzSPehTJSz+1L3Z-xz+bUs6{2fpj^7VxZaqcB4#UXy=DAxubZjOy>%P7?#Jk)ec*%y z23n=kYpR^FvIWKJb%b||+-k74UE$T+TQsxTW~9f(AGx;5|38-VagkLgTONIO zn^tu!p=!e>9AbWd#Iy~x@8Yhl1}v>dEz2R92o)cM|Ji}x!90IWg<6CjQjyIwEvJx% z`PwcrOO)K9ru5ThKxh-vi(S}{)K?rF43Zn{?Ua*gWXcq9d1gl|`evrayxS@*f<}ya zx3g6beH6pOlBubCIgxHb12L`^9Ka{iWgBK>hd!j@3$_IXA_e_lB%`}IcpHqWe)4uV zcdU6F`r$H$?6;{@o++Y2)%3SJO$BWeNpn*;iedsxSx_V(i&#it^W>2=VJaBUs7h)P z=)EmB^>R#fs;}c@j^HiG|Hh{zn7lz#p(X-e8_5(`mI)S8U<;#lr5DfJrJS09YE#no zSkGYv%R~@f!f{5eUDXt)UTGU;u%P1Y=mdNZZi(nypS9W69BKg#78sc5D08^Xn-A{j z2R})UjFlztY4}~J{p#EWbY1Zb(&<%g2rIw6X7w#!WUzj3q(k~|_Yj*UdK6*a4*pc} z!099-(4Qx7c_$9b?L(G0FszcdMuV`0S9lLz^`a06VmBc^$5#G}zZ;&5t@MRDQP1xV zljTr$c&G_JMR8vB*L#d)Vp9217swsZ0o@zMR&$}aE8R%u=Hhkowp6kD#o@&iM9d@DGPc%SC)=!P$m;KO{U_6> z{eKq4R&KdaN=%=N2H{!XBQHxG9l9TU;15swiLy6C;;hN$yXr*5@z-{USDk6+M6H4v zBxb;s*}ZaG3g3rE#FDaaRotR?8x693ZvlYDL#fEWb`OlQzd- z7E-$ZoGSfliS1$ePxGG}J6~E3#oX@()Z>b^`FT5E295fNeyEyn^mn#1<*J zhUP0yrz6qB8#@h^J;uI2Z94yleOX57GDGCH@jJ;w2sESICea0CUlNy#1db?S)Z+r? zZ3*oEl-$q7Wj*(?{am|jaSF;_11|w7W+%?;HD}mjNvHn3EV0h-?38*a8!&BzwhXsM zC#=y)oB;9G5PUfNbX<{jO9uIaI8&5KjSeC9GZb*B=^*%%We?jyRPEcXdzJ8*^~hBI zHkqM%r{k&CeTE=ERnBtoURli^jLg<~KvPAZG>^8u|o!wuV+$1Mg(MIYDod9j1EY?%WNI^5q zGO)4Odn~x-lgA`Nhw2@Y^zAGS%PD@u)i1icQtmQbXRGhHT0?NI88&wTOHFaLkT6`u zMySA)xHmci1F0kT?6k4SI`y|MtHW&GQ3rgAb+H8^lb&p*qApwL=tCGEr}i6f};%dU&#~DjeUyX#B zQ|Aw%eQK0=Dba8Tb8%)hm!6M)3%VA(FUMpQAElUKH37xasc^jWynQ(!d6lC*+&$X8 zh)WT9OGHWn11-d&Tf+@zL@DV-M&bi2N0bP9d%_6>Gzm+A^`c_MJ+V~x79=F#Ld{to z(J~TL+8iM1%6@74mC*n>OdI!_O&t-5`AO{_PqQ@xh+2BaSro20V(V>{g91<7zb=yZ z(pb^lnW7+ z)>61ylQ2X8!>J`}>Z*>#JOXMCuKJW4`d9tOxzb!3TK*&ur5&+IK_)uS*M-rLYlYCf z%~O=v>U~(rgT#uUud*Lqmz?JmunTIm+ekiJnl*v|36&|E=*$J#f3f8yjSsHx6^wvi zeZikIYCx2?m6g;@vR-kC4HMA_D^MTo#3#4Mi7GYfy&2(S&E_<&a0c_FW2!$7B(S5~ z3w6lO7$j0FY8!gDd;UwWfi&)&|+y0vU%af~59 zu-jNH{v%CxX!8ptlW}}BMro4v0%k6r_bOhfZIBPhb35zmG1c4wW$PB^n&sZ_rfd}8 zI~iL9AHm{cDa?8FXU1ix&yeJzet-GiK}nusnhJ-9(^t^@xPN(IiMaD;esb&k;b_i; z2LAJObAhN6-pb3ihkJR!_@rxVk6v9S24-rMc6@V7X4}+sdDrd5$OBHP1yNb9Z<$q) zUxOLU+TK6wPf_<;WlK`9dFH;tiA9O{OBbylG@cZ}l=2^-& zTit+bmz_oWht2k}$b)OweF_Wuc`g-%!$S~DeBDA&88XIfN)wXYIP)cP2lU=U2l7{T z^Phg#65v%iGRwE!t!TO^kAr)x~)j0+|19hYzLm&N-qdjs@VT};xKdtcGYc%=*TTGFi{RlF*! z8}{7n>-A0aEo+DB8!YfK>PldB@egloe`Q1(0gQiJp{5J5UA7&z!B`RcNKzpsc*TdU z(O&3CVi$4wld*YKwkCkiey)=dPtEu*r_N$3j#mW!J^K$_D{_I@l@-kaC{FfC%$cs* z^v{PIr!txSdyda9bG=I{?^IxWbeVPrauP)<|GY8E_E_fdbcO>R=le_{VYFaZUA2`_ zD%upJ>i9eQY~(n8g_^E_qd5^dSyfo5R)g0C8a8(WByU0GE7}J+((_-7fgQ`HvTEb} zcX4L}+j-2Ia&oR)Tik3;ihwVMZn?(PH8nC6Ua$IzA6 zJE8-gD;Mbi*Q&J%I}U9}bJ(F}fdWHFi*PX>32VYa$zlNi6$1CMmYkKcbM?N85F!IS z`+!}Q=q&^_s!@~^<`HaH0IpsK+U9bi(iF`vO|BAw?x)KIGcoZR7k{OfS%U9aBkJpK zOjFyowT6yN~oDW&@47hy@5oUdBP`XOT?9(dNM1iZH?*FtJssGYZQnb!Wq zGN*#X$d?2xx~~y1$5&YV2BdJR0KOZlXed@KtxD2?jSC^`rA`R>*)qbLDiv1E<*oym zCW>#HfLO6ynC$MoSYI8XkuJRy8F25rh?9-_(jn2I)4WIotGgu?Z7V^FD3EReh?Ltc z5w+IbGMrD8lbDKh^JCLhCe+IIk@P!={7XARi%I0j5+@gAv|D!`CEv%=12(RRyxw=I ziw!7&ESYqD(0U&*oZ!5hTzvjRtQa}m@?t`;J1~MA8iGVodCAX6zu?cp>2m0Mf%7$a z<9Vn!j!c~g?xt&XBnZ$pKoy@3)nVFpxgJm-Eb`SaoS#)E0?@O3t zou+rL554=3!RDwneAVv4rk>~!4gDLlyMJSrdj^URB(ypAs5qq;xl^qb2`GdEc;~|A z`3(?g7Hjblx~AOWP#qH`hZASzouC8DkZ_;ir$Wc#YYxnolO+HzK+wM)Q21@A4RwZ( zrc9Mbv3`^DMNXDOPKmB8?fqHsJec?awGzFF?{8uO`au4P-r-3T(vtDeD#yRtt7tFP zx+tZ!$+ePPWB|ttVt0pzeY~LUOEo1^_z3L``OH)JVEe*eO&v$}RhG6C%V)m^Q)EfO zJK!`)#%ZV=?rCchd^*tMpzoX>`AKzH)hpNAd2=eC40F0DXZSS8w4akhc-#X*lh~29 zZqR+BCSbS_4{DBaOR3itp`X>`i$)fxzsj_?K|wB9``$!jFX00AOXnu%`wpM>An3@a=KCAF&dW%^FG%N)ZXA^c4~EAQAkGsYOJG zyA3gNQ?L01Acl^AULk~v9r_#OaDIwp3kgO`F@nb^FGUHX^Wx z*oL!PFA%Nn71JG>wQ+*XP6C61^$pPY;8Oy*)SBC(7Z0g)X8S>aGUsSKB9 z(`>WseL!|JjM1k4^qE*QmfRMGJA{M`0f|Tp^XKYQEdaB+Olf5g3XEr!EUce1|CyE^~K_CIVHE z4xMJ7&W!;NP$GvAcncstG&cU_SIx(Pe1N^XoN-7Nr;G&$y4cVSew3M_bWg`TdOqVA z=4mN(a$VZukYH~ygEnp$sOzs z39EsN;G!dCW}#6CW)z6(Oh`3+(H$Xt1|hBW+?t-l7!6#kpQz7tCeK3Gyi?v$Gkd(k z#QyQkJP=cl|7*rsMJK4#2(u=eoIVtWg@_ z%UbDqvon0($5zFj66=PpXEmcEr8^OR>%F2A2Y{jTqtdOfF|W7 z6gII6SA$(_`}MpYNq}jp)28;w@UsieJBE@`t?Q8~xYKYA+b+_-xnv82G-1AtcXjhZ zlSmjeRyLD^3(AQUxA44*L6iQa*S6P8Lo!);J}j<{(S^=IGt4H_s-Ndk4Zx?ET*+IZ zBYvtb`Vz!b2)UfNm_~9pkGDxTO4Bk@tPpNoWq&~h9@k~(DR z>9V?DpwTD5h@+Camz*FwyF!ds)VhL3cawy{4mIi|XU9l_ngopg<{ZZgf?MG>zR1@L z&F5};luj;yD8w&6q8TJjiAdMD19cltbYk!9NtxCzM5Nh;g_Gx9$Ean}!m&o#XdIVE zFU-uq0_w~sbodJbCH+E%6Zs~7a;VMCeM!&8dM6`W;BUM-^ulXfGW|_O=#2tuH)BU1 z4;21z0~vOcY9G$?Ap>4?#V#;wJpFH2X5%VCVBC@DnD@gr3KNTK7wgv5`OBK)?T{cn z$k8Tt9P#(nMB&&Lq^ev^6og1X>H@V34kg{d9O8GUe$bryqw)grvndvo;(iPTVxT!1 z5_SPm$z_szn4I~%rS392f(0jm+)pao)snb!^o6e^M+lu`IE2^~b1QusvLc(P_L3`y zKShloFp9QE?IJ(EaE=_-u7*e$$l-PA8T?s@p&Pw!@mJMRjr|6kw`JI55}qqjkl0OY z9Q}dUdlj^_Mgrb1TFP)&AwA?B%tM%(1Pl*~ERa}kn0uNY1!l=-FAT;O>#kl{Up#wi zTH7j#EJkR`{P+374Ou#=`atS%)I4Cf z`Ji=}oJ?X_x9DS~Ih()g7lt^eN!!H!qzn04L&uBEL4&c3nY3TEE6DSFr=(HFRCE{D z?`_y+qqgJ7D^-q)&UvEm%rSh7dGao_Dy$nH*pwC>8|;jl@SPf{1*@o1uV;-NZLkod zyF#_+=8}<7NcmBIjfh5ZHhkbCu6nz9vuSAek&+<6FaX|Hk3D^GhFteDfCK~E&{u6d zP(#SUE(!Ijn`nD5u?a-RBXSot6MxSN3o!D;f{`tc3v94`zkyNuCN%JHq(C>k$Ldu3 zetA<)aVuQH5nT5xPgP%TM?Qg>))k0rOwTOZ++0MJ;I&F0Y#!>V>m`OtFWMPrA1x6T zt+J}{8^oDfzs(Lhdh8}&R$)xTgCAmrV16Tw)O{f02r+>$`PdbN>0dTIyp9fT&7f=g^ZPQYBz$l~gPWR(r?`U9>NZx1>n1(m-y?tpTf8+F5P4ER6jwh4 zQucdf8|E3|k%6@n<}0Z!FUTH=HsxTzMdlk`Z~zMDFVg6anp-=U-c8n99b2bOsy>+{3_$^7Q; z-?00nR08z;I)6p_9*L?d%*OW#5&CtwT0H;NoV$ywOk(xr4*ME(*F6s~NT#Hjq5H7C zhTQ}e(~wcpG&taI$loDocYGaL9NY$~=|hgQPLI>Rd~*}Cl72-6=~^ZUN~(b`&a6`v zkB$n)4k|Z~j}$5&pMydZ<4gjorI)nHhKU5D3GHq5%;O1$@J_j+1#r{28`mKWl||QJ zHV8XD7Dn%gF>JA{Mu!f%AK18@UhNcv@L>Bsai7CI>1M(!fd*(goKV)3!(1^L*_3(Q zmP)&dy9zhqde30O^7u^eQV!20*9e?z>k|bvk(LHcm9R_$tCFY##>}I8hwmmt+d;F; za0EXC-D^ML$MmOS{Rxxt`H;@V{Q2y{G%Ew!e+3wgS>Ti1nz)vvCZ_S&-P5ndWu5KK zIKq6&nI3Qron*`d^bC`6{kXz)jMiAFDPwi|B+zIu9aGaWplUQitKby@r&n}inS=eg z@Us`~NOmRLEIa@gX*wfllVN!_#ES%Oe3i+5$f>hb9HvIGhNO05v94|g`B;`&KbO8G zX-Y06rnc%ke^kmJ_^H5z1ag}! zR8Lx_4~R54QU&<402eSMr?h&Hp#*JkAWf(ih{E*YC;tROd4r+?xqvd2y?xR9)V|UF zmnWzgxqT;X7ww{Uzj|kGg1eN;?hu1r)KtYC`!uh;f*MV1!p8txv<_ZG6|f6Juj-}9 zASbu}j|3J5*tvBPk-Cx`VrUDqceLfD)@S90leUp-z_<%dI3dr}ei-b~;FQxs?uyNM zigt_hqMEHR>cjc+<%#6CHw)Aos;Cvfh%$Sxge4+=djxcE87_ z2K8HUd%PZdGQ~Klr(7d4xmYaASAQkz>9y2mrKiLqui@>JavO%kzdoH%`&FS)H8#)< zYsfrF6_a70y+!{PxxO7@gcAu6@?t;UZVDC=Y+v2JUuM*`GU2FA*M%b~%QtbJ9w{#W z)>^2#s7+|Uos1O|SS3}nL9)R9S7DY@B$XJXxvl10=pN3lrDnjvc;wNX(1Af#TeJBK zB{!T_P7wL&GbkL1UXFiWGa~aw?0ye5M$_tMc0L^J!P&Ja!?r;!x8fo%V8r)c1{Xx7 zX|p5k&1xu^SGknJB@~b~T846Oa1Ev#ryTvBH!;GV1pZ@%)X0_p!p0N$5ZE*geQ{NG z$T`;Jk?b1j!4o-H2ehR3ySz77tOt?#(???^67(J(LvM0ms+Ew7Y*EO z+EMUHQ!)lA{!lw+A<|1U);O?e;{Q<88~Ou;oW$g*wol?mYQE_PZ^>hh(-L{{WtlzP zs|Y(V0_%z?tLqc$tT8#O4b4y@eQ|Ht}Ga67Cg zsz|yYCuxmX^*HJ?`tqMBb=iR_VZ5?&pfNB|4<;jnQRf(BShS6eksFoN-PTFp6Ni#u z2q%m7#W!%o$h|AE9hX0(bvs#NLKAo6G{a|3m0o&UA?yz);-6z;7Td)-8J(xz>H-K@FbT#WEYV()8vmtN8xalX>?`0$#9{)eiewi z3!=ObLD)Gd&z3o=D@(R&`@j;a<*cnTm(?^o$7;dHP-^9t$*{`0J4N!RjW7E1M`HC% z8&MsOF1O>h%Ju2^?Hu_>fy5jRc|K{Qnd(&?6aCE*ZiiMi%^PyEH2dFo#*X(LwGRpF zyfRwX5JLc0I_qvYVRCtqfJ&&Aq<3a>tx#pp3KK4+v9e7zGxpH5H5Duz188W}u$0f3 z+1Nr$q5zk~SQ2OBltO0Vr;!iX!NOJpq%$Jg^ov-JxnaF){AkusseP;T4c&MVPk;YW z;>5HX@gc}{@2lo~DgD5AQHj>g*A`<-R zXo^hxA5+a4FivjgEs!6Mp*zO&%ozYp|2+_wB)kimKT{Da7R#0URFQxfCkv(`!a;6* z7I~sn&|Oy5ap9;hAlZW!3>_m8@z1QixFfD;VQFrs-r+i9)J$4C@c@EV+OAOL>&@%_ zNmr&6ql3Z-FdL4}bTFi_WM`d9giE_c>`1iYK;y-(Va;?4v<=f0-3+A<&gmck03w zVSKPioQ3;Q>72=$g-93btD20K#LmY8!9((zBj2sfsmuFX%S=(0)G)Wyp>>YSQ_1M8 zg2!2LN)$37aVa1)p*=v=bOIH{E(_Vv)VNzC086}hlKWVZgP7^nU`Knyf-UfvEqpDc zl*^TOPt6y`#3K%%fJeZYC~gL7ETZ!iGV7X5=)2W1Mq%R4x@>%g+WQVF6c|IMIFqvL z#@ur`n0`5H9p>NcB)Dmc;RfWLuk@v6aO->*ZCiD3Fg|txLr&3p5Fa z^{<81B~_4|X8Dk-oJz%7#y=%TyOb&#TYugGk)HAN$L0tn0A?(Zq9)FdKYK$I?EdL_ zumGo{&H~*{(tU^koqRSt2B>F5>q#Abs^SE_0&$bV=dS8fMV2#X6gAj-yTt&o(n1sg zUmCU!fa8#|gJ1H@p+h#Wqq@e>76*2YCzXT$jd#Znqtwq~1^b}XAI z)G=hwuW0{s^qI+~1HdTv0pRydzQ5T!K~{!VSSZPVfw*uM_N}*r0_qm;dOReg)&0`< zT-}S88!)x(t$|7Yn>J}*+RSVVSAevBrAh*^1ZEM!I&hb-4hb9)zCe-)#vVlN!GJCA&m`lY*(d=htgXsR4ZeX8MN87Z$Nw%Eh|Gxr z8gp{h0>9MS7zBDd%9HEG1i9j=#ibvqBA{Gp@h-o$^Y4rOU5j zjrWMIJyfkEQx*0UbP|s0?uKCRCdj^{EW_Aq>J%;mRx2yXx%toW3tCBNsH2)d+$71& z22){ANC^t|u94uhAc5>8YqZ+X&2uNO>tYUHg(@ZSSbJIcjlUM&UeQB%fk%r&pW+qd z!oUqeDhDDnZv<=QI1K_0L>sSSF#BcAwAAcP3aly1L2~i1b+9AZzl7_C3A=%^0!Sps0<`Q9oGSjgxleuxxopb@amHy!GqoAtPpd*pM&QD~_ z+&$keBne7BE(5=$0kMxtHDe}p6_|hLY$Q>1-z<3=>P$jlu~a41jw!SDN+TE97V z!W}-Br;EaR*>qiSm-bg@oz}tty9Pe*|{ZCGS5XX zj6-SDFLy0FqgW-r@@ghI>O>YH_f z*^TyxyLDch-YE+BnAJYz!F9~N$t49XPgQsJL8hb;mk7F%Q%WH;4Z_6=K;v;!OntEh z>=(=61tixO6dS7XTm}XEP$-ZgP3Vlg{DkjtyD-_-g%wA@U?Af#WOX1cZ3h^g=~`UH z-Rp!l2abmH4`j6+GhJLmL5_YKkV<}0f&sYHZjvH)<_RQ|#7-bHdRT4dK6KCW`aMGb z2H{k;7ZU3bPJ%5VuM1>d8q%l6@P^86-?6Z<9)>tboN}^l8(2as+W{bV-yo1ZrJiF7 zi-qOS%U0WWqPfd>96dQB|6=O?HH#ksiCPag=Ph-ql@|4c*iN7J$&*?dH;cuBe$MG+ z2d~I%e<8R!34WX|JkYmyrvS7+%vSo8h{f-VBF|A4-Z$(5c4Hxb;1-xpPtr@}@@EOb ze}YqzXYp5^7#w}4YgKWl`&l^m?QlDh7N9owU<^Qx`dFEvTJ*90NgDq!3+r(~T@+fx zO_NUv8ZW_lHN#taM!AuXFxJT!3Ta%FVd;7lh4Un5TjsQGC^Na!U+-s~(I0rmhwYUk zk6nNDQFF%khVtMv&w?%RrEG7oOuFd4zFPI<{M0j~UjK*}?)R7%h%T($9K#po%jMgE zM!7RqM5^znzM&3`Fr0^&ae=rRlH?dU3{8u)Y~}lh1O_rW0*lWJuul0%oI;;5iLh88 zNpsT?cM3_m{Fx<5?F=zpPbjOmZ9WE_{up%p0a{AG4h;%?mr#4rJhknGXhZu;9l&<( zO%?<(@@ZKW95iq`iF*OngB8*YxV#YAmDT1iC20*CrlpAFw~B=iFSD;@ocFDpU9@!e z?%#zXbBf+n1L)HJys)+sFyLode{4+_>y=<3rA&R!Qj$ZlP>>||Qa7X?{#8gW%>Gfy zj8+X|HHD$v;*~PL*?*v4*^~e32ib7dh{%nUYtv1{ZQoUoP?SGwE~LXH>@hkud&>kVmy*nA(eHO#rNEa;W;hQL?&CD!-b%sGFCcY6wsupkE#bse64+ZWz;>z4R#V0>7EjJi<;b92^MkGwZS z?8FD!giIvTPpQ)*k6vs&#Ig2PoIuJy5v6@GQV>cplrqjpySn5f1l$gNLezr`m`?#} z@<4~%3ajWj2jGx_{B%*SPu#HqlMKZ~`Kp7{lI2R1gc2i30W?0f6Vi9I$+;mgLaW7- zEro^@S>uH{1MohLM8eD>gr$1O0-C;ho6n*G$c5UQ_8sfSH=I)bKz{^y zERfqdm*|YZiHb^8RL07_y}_BaMU)N#+3xR88kYrm5c_M&Mr;O!4O?@CmT1(q=oZNi zVS=Omt0^rzRsa!BUqj3AiHSH`)G9=={(@Z!SpK!2cGm+Z#>tCLTc(ROhULpUv8zpW zDUl{P_yc-gD`Fa$i-0K**1VCTDzO?8_M)ZMO>u1S%~U9ZA?2U>=8v3tH_qd3tfYMn zeJc4dAIZ#tf%=?m*wt&_nNZ*1&Gp8-{3{%`s~R(ar_9U#T2D{p<^w8?_+ntF)a9O@ zm^4f0Yqs|mUNl#(1ytk?&XRO zIXTGUZBlk_KaKvx5B1m>m8$wm-iVMT{=dSnEy(b0#T0gyN)XJ9zU~%Ybwn&8md$a$ zCqO<_isER)Y74sIaM9C-@2-$crsKbDO*n5YO04-bw{6`nvR=T} z0=8poIVfwy5!M}5Qp)+mEL8m(9V17fmI@eOzzQ>+uu>f@70R@%B3<#sk)&0oppkmeh z#+A;vMjJBQM%d$eM8ruDYP7v!qI`V(h;#F|b_uG6^@W8VF9Q*gC;e;2H;wrB2#wka z-=56OtJ44`_)Gpc;!@uwu=!yH^?rNG*t=N#VeZk}UQ9xQav%pu(uN;8#zGE$RKv>t zobyG7Ao6Lo|BxBd&co1!OO*f7i|q&}h~5# z96UBKG2Is2&24Vu`iI*Sz&d>t$p%aqToj?Qrji9ET#$8NbquLtNf9UO8OORMm?AFG zC-mW>3`LaK$8?Ev5J={`p)-is(9u`v>Lg z9j$tTtLLtOc@Hw~ecVT=*1EB1@Hp(l?;V&PNg^QGc4nW2Hct&Xwjk8%buT){O(`+T z;=9=FOoYNnFexXz-GT|I5~d+(-(Tu7o$$7T@{!cz18;~Wf`j&v%XCAkad@SGE}JF5 z)eTUKU4r7Ab-g}+c3afjy=zQ$b~}?V6bNs(Sxvi{Vz}s14Le?I1)?iQ# zM@5iFbg}rpb`)Itt{`IQf_U+4zqv-=Hb^8M&-;cZr98 zm)0QPK!w?2?~dZDAmU&LwX2}5NdOy|&oZjE2KW2PN~hp1 z;IX!juhGERbR+%IZ<85p00SC}P_&SDl!|UoyBE$yx%I z2Jl0Qt&3l4C|Mwp>J4tVHJ@ATNZY+OB|6|KRf|u*Zy!~VcyMu@J}Xtk#A*P0w1Koj z$zH0;TA%K=U~9#|d_tckuF0Aup_GVoxAh9yY7#F=NMCc3nOWHD-;^S`;(lpiQ3oB=1~N?U|bosoMyO_0BCd69LKfdBTs1`VaAUOqh! zP&k4u1*kxEL!**Czw7*&AK~i#b2>Fo(-$;|v->>LWqX!_HT%aca0@XqK)TMJH3IUl zeN69#;r>FQf)A8i2K7(zT)V~tUOi;gG*3?b@||&Vxf0aVBWj1obF6+XAWTJz^9v)h z8yj9oWh@}`+a%!^tYJfl4B7&_e_)s~N zv3!5Vn)MCuset1jTBx?H97^NoP<%)TG{$1|gluk(%I^_)TnFBOyd6{E{<%KE=_0EX z+pqw@29`xn?y+;$SqHxg7eXKYi`N-CETz}#K%(P(A0E?!^iij==N-0Km;=R&_kG8H zX-Mv}K|-@>`K(y1Z%_8FA9z<=uDZ;bg&@3vUHd;rXQG83hYqR{@$=1GTGjW0`i@(!l6D5}~FLe>V*o!VPS zGtv4ECJ*9axZ=g8K4Q38PdixyY?e7`y`AyfThe4**uAv?2yLh+D^pd`~^7 z8L`qbCtHb!jpKOo4Ce$3&4V^E zPx5>+zO?%J{cQrTM~*7bbp+RCjrZ-SX_ip}3~G~c%TB!WA#P)aKh3S1`pwa!1-HrW zIxtIKRbDT)1^yWpnl4lE%S|ODOO}0L637G*Uq%oRfQGb@8|bLjE}i<*61iLU#7HE> zrWS*QO@7xe6pwY`sp4&+b)Tz7NaJt|7pzn5MN+{4-nC~C3B-`Y36LbcBS8 zlnzC=j``pt2Y!V=;Lf=?0yZv@oXu3MoB9RzH^>1(%j{I!;?)RR=xfYt+AD`yzQkGh zKnsPxT=bs6fIDdcRfzThLx!)F`o6dDR;nxe|?*CDrV z{MoIT;$3!v&z-cus;wXefgwFzaIOb!rKtlj4mSU2F>;nN2pr)On^-Jc+z&od3ypN!#M*w&0mB6d-iC@?Phy7-jEa}QLBxT> z);jDN^acVg1=J8$(Rv!c3~kT9!GF#QVK8cjx(Edg@v(2|xncEQLHMxn)~Ps4!PLL5 zr?j-dQ?KLk@5Nm-MR21L1sfSD;iHkp4Rc_+guWt@(37C(Q5_>FN{?&9QI%2t`CCn5 z!E7^Y4h6(Mu?xG_%FL zk696#LJ}>{`M_@(B<0eK){U2)aXLL8Jps6`D6Cxe-lr77;sC`0u+rjdr(UXQ$XM4Ct(p$@z-8RJ|fnwfD(Rh~}$w~AeEENuz zd63Kf0iS#)8RZ)}Gpd>H4f)|WT8GQ|1qu7hSM$Pq+V^ zv-5N32<=Fki$zid1J%w~b6I9rV^~qG;YtTj+17BdH9pL=OV&}>@kn-VE#CikreNdE zCPis}pDf3pLD8{eSQ#+#*m|H+>F8cTHrQ3~vYl0}sXSe1j4P0=ni4(#bW>=nZP_+O z4Y8dU%ll#sZ_q3r@dqIo=NGd@aZk_R@P0m`zs{Abe=ZJsUm9m= zvuT+F(?%d|`_;LW37(jBa5<%@wbmnNhCWP{^mTZ;ETm8MoLWUy{Tx3B8gQFkJlJ@! zHpgBlO?mQ`cT%e_1UeUPWZALA z!D{_sKk(qa@Kkp{m1TuwZE0b|oCGs&NPw_BFgE21oTbQH6&co>%teK)23SSB(FXgb zQoHCUp7CpjWf4lowMzI+lBvzue*?%>$pyfEJgwRXFW7Hh*3hK*nmIlykzjfn?h13( z#;_8R$+_GlOqXo}LqxJqCGF4CRzCsvI57+76npl3KY`61KR7?78}I7u##)!8;G6*E z3^uhVbjQzN2)_}v`fMF|bG^oT7X(^Y^pg%R$_){zWcYL}?225tNM`w*cW<6}1T8Y% zm1jap7%SoArn1#@oIU{;#p6yN)I`-YJnx1@n=K|5e$qQKL#iBMtf8seIW1$8Ht4S2 zsD4IsbF7f(z>0B&e&;oHgTuZOLHnTy*>$=z{7d$X!3n3b{MXNN0{Z0`?4#$HO zSF>{^jvZn49009ft=Z7>*8J~!Y3tx7tw92Tq*Np9jcFMO=Z?HHjL&u+s zdIR)rNdBzPGl8V>X{?k!U=Th5fo^a(jBi7AA`qZF00YLGi`^S#+tJ5;r>`g z3o54aL?q-x@`4y7X@P2_7T2DkRAq6}M)tN@)WEo<=;->XPUB_7GMWtq`PUD-r{nQ^ z!DoC^0_GegX4yoTAQAyDU@nH%f;-$Gfv&_bG}I+HGTP`$zH(cQ!UMJNko%#(;7xB+ zs(K+f390FyS*5b~0rtdfPhUeYrVd@g0hA88GysQsN$r7AW&H8tq`&9+SG*Rv%I{y7 z-9bHXdnQ<%{iRwN=$-T(JL{ztV&xwcu7Z*zisBXYgDj6s5;ZG$##ItQVf3$^%gn&o zrt!=+tg66brx57S-oi!F3n2__?S@(IK)2N5h{|(X#poAO@gb@}K5Vu;yFuBrJEau} zt`{7b>&@GdU?9|dHq=RabUcbUF z0>kX{Z4~I~wUmpl5^{F?PuMUNRleB*kgcI!bn9H?aG^%_ zmYSwe5R{&Idh6%P4r-dl+fT}qG00D-q#fWbVVgvA_-xv_i%*e$Y*i>*Sa%B<#KbyE zBU=3N=!`FfoagU$$o|+i&%p-wLC3-{9h~kRq4sl)@M1Y@SmC8b=M-Gb37$ssGh8H& z-yDn6lbi_o<%i>e;e&Epi9Fp6Cm^^B;Vc~mjMj*B*}#}e(lXII)BbwkVnPhIH(|qC z_D_n5{qT+|L@an;#xCU_=u&w$P0ooPbeKVmVF3n}bBp7LO30TooE;@WiuLqtM~goq z%l!qXsyRtAl=*8UKtC=x^LJI_9Pg%=bIUl9)1`}Thj1$J??Bk0O{mCfZon-aya(Qh zp+9EObH1*Ii6T3`JeFVPJQ>RSX-X>U>&1jz7UbNZG7`_BNBE#l^YjySB06%Bs=QoDm0^mDIKEcy#?=rzF_%bTTkT0a|*E1nC{j zCp8R>HC<4hKrc@6?qczLf{spWTpmg1adQC zgR51|7{1y`ZwU0Hom}&)jb}Mt=Iwsa<|x>g;$npu>`VNXq;w4u!qR@h;T$~V5|ujD4ce?v8t6MaVY?pj8~VnOd66P{%PE#sC^*Z#_8kAUq zlo@b?R0Dl*J<#nM_$FXH1V`(lki78-T~DliBoosXtaaX*S#`R(V}E>_KCoeuYxBcq zq3DrxW|Nc2Xh`-ygWn)`HIokot{zYpizt$5K|ZJRZkR` zpFDp|aVAtVqmB8rqxt>SLG?Hr2_7m$-7|#ZI-J91N?s**ROhvMtJI~TaDR)0ceDCc zgu;kdK{YuiQe~Sc{nUSIUnsiNhaC8IjN_R>61FuZ7^`awMe`GoD?aVF)R_CAh&?(d zFjb9eP7{O}F-3JZ+NJ$8jOI&i1hRrdsNO%|SXxaTQJ|6VTZ(hjGNA!D$&$GoN% zf#6><;^#8TRY5&7%|cMiCmGQ!q9TKB7Wmvy>zU(kBpcQZGfZ0=8m#rqvKcY~Ks$&l zg3}1XxVR5hUCrQQ6zdZe^L0jJ7fP^Po9rCtCF36UsfQHb5L=%SW=nIN+lo03X)Y`? z@eRsDXRL2|tGiOw&#)Rf`g$D$v)(-ICb>k7W;jFl4Kk7%mD1_uoEM9AI!$w?jEWEBv$`2?@75E5sQZ3mqkKLs5)$-->L{Q@y|8f8i#L><`wTNft~;&w zHFf0E*w*xZqkB>49=Sx5GSxUQPK-a*DUFz;_}B6A7A zhH80Owu2D-NvEggyhrV8pavTGJ3%rN@;4ZGi84)5No8#^sXi)RMq!$*rTDmHlv?w@+KbW+b_)i#$6w`^+dvLv3A`2YK!OS`FV?v}yi= zWY6xq`xI4Gle;zFTNQ<92UgPEdFr?{PeK-dMORPJbOoEmcxeM6;*~rJ&~XwwAieV_ zQLRyo8!JmiKM!^A)B43POm<@w*c(;zH>e40So$3h)(R6UeUuc0eTJ-gL^$9sSS!Y#zSYjV;Lp_&HW6UX z62=5VzCc8>G8gKW;Ah~^)#VHjV zYu`;ZIwp3~T(YQ8i(@DNkmLMUEuM7hkQ6AYo3|0o{yRwZtchJZ86L;oJmNa>k#w|~ zCR@77h!&V}P1FN-FcMk)vcq%a9H{J}vXms+>}=HAm+C}1{ii?4VlFt92qxbLJg=K) zKH(FKI;>EhY(pGYx4!A$ZjLIzU=e$UEF9~7y}Mo_^MqThLrRq@`D6z#YiWVDni0Nk z$)rM%D-S@^b<#G&zMWa4$NN<t7j zZT2T%A45vJ=Th>mmJAHA%ytkItxK<_X=s!y`dX@j;Vx*eb<;3Tos*mdHFL-D#_U`3 z{`@Pje+L0(d#^*;Ep81U&Y0ulR=uj?pT&{ec8AYt3 z5;RPH+#O!E)Hy6zS8F~pf5=#<8UeOeXO#U@M{b>tUC1Lj<>fsckkxs>_T1T6PlcNH zNb@>v<2MZe8v!SrzC9i!6@yUZ9{z&CuEos2DF5FfR-U2JY&?%4*ic&Af=$QOf7SaG zVz<(Q={sNN`ChXEht_Bx<>@}{S<8Sg4Ca?Y5@&`5YgvZ-1vcT=f)XRHR)UJyR`qg= z03b(IrT|+$)o$XSW2=n&+f)`;)>h*gN*?7{h&7EdwPRJ~K@8czA4R_`-|%6Eo{8u< zTneV3Ki{DAG{ngNEtdgbP>;Q>XEB>oY~p5Hqx_A;bPva0b0=R9jSKU?4Tfq@#YU4t{WIzIcf~$VBhJL`EMWQIn!Q}wbjMJ15ck6P4=I$ zP!PnMs_({!h$yKBE1}=-&eRqapr$A^RK#Ts5P@-iZwZk{o9r@oDN_3-5J#mFA?Dyy z6hc{@p72ebMu2Lqv!PW~5Hi5lb1v&hMwe1!CvE&p#^MZazbSwvG2Ykvc$rpgh8Bqw z*0{kJ1)odk91%;ck<%mw8su+%v?nX2)|6w~L<<%5R%O>F%&mw+uQ*?Zp}{;JbAex0 z+2rUKA?q{Ps_SXXUDtVp`0oRH!dG{8fasF9l%u#L{^qC5CvM?wN{z|=FQYL>bmD#N zEhNtT7@q~pb=ItgeoN1IYE`Pk7fFP-RIWm=00cwky)meq6q03%eE6hgG?PCQq=bS; zGVT+`P%3Iz+7P(XTEC`#ZQC zd^y=-Bb?$pWIuIW^~zFRzn_0v3t~_3!g$SqC|;95v(VmK+;;IE_$N%O9Ui`Ns-{!a zL=JtnXfOe%4X5XhI*ye9)tWiQyygEg`r@-GlJ-ko_F@>Qo%z9vA9ZbaE&kQ(>CHWx zh@2I?f;%}KWtQRqZBgm9iL4>b7>dql#5=xA1#txo^bWqm1h=kSjj9*-eji1gekxTy z9#jeb_BLE%vTX=79T{m4M%&3xO%glOIHT3_h z!gAxpW#++MYDaM8wFQ;{eYVw;Q#RA$(WnE_`!No*bSKc?M?K*aGOPbG|D#PY)P7df zCtO*5c)Rm@G77s>O-wVyl}|)fla|?tLzkKsCiIsYa*Om4vn>#~Q*|OW!rrp_ zGqi)J+DP|&w#radFzv7#{MXrCETZwyac&8?9k^&Rot?xg-$Wz^L38&)Y-3Dq_}2)% ze+@vZ=plYt${{;*@c-FD?-9g;4H#z>6Jh@rK7f<6iZ*R4!b=*fW|;@S)Hr$FN}}%} zjP=|(jJXp#?sxU)12US*MnZJ}BUm!%d7n4<^cJ-YA*&o|smN$A@(?#U%_x+}rr42C zT_|&y?$?XxtjkVyIGm@aA(du?T3kzj{ybleqE%V3P8M$%;?0r^S1-P!Ub`v5X`J$C z{BNYJtj+PZ6M0P=LAU=7s!3HA1YkGV$N9l&r?IgKl#6LS_zuL(Wjxt9o*)yu8sbz# zu|+tLB>96Lec+O#ZNT))6izI9g}PZ}J}a1mZvg+o^{%S3h-bpfdP=f@Bmq;h4@pj4 zoM+XK>Sewo1o)KZ&#=GYs%5|dT_VT?<$7Jc{+f5GX2k&z`4!zJ{= z))XSVA!90n>Abl;-ZYo2CM6OR=~18*P=W@ZSjndx1^~w)3%YzeheT7n%B9XaK2IsM zDXu$8wh@;u1%16mh{uB zP)ZBv+pouSCzdCx;Gn%NJTz`ghBgh?6bS65$5&jgT9jZI^mCHycPcyk@kSY`I{ECt z7hymk!A8)tW#*B6sP;YaSPu3ro*QY2Km}MA5eir)M3n*)ErwdPZva?A!eMp~?VA zQb=>#>2}9b7276Sy*a!fB8V1a@Iv1LdH{pc;dWM4g;5#tYAGjpPm0TkpNo>tUQ?>Q z5RliE7P-y_>Xj1sR=R1LvXqIUSxVpK*7KBh@?JX zqo$)f1V6fd7%gB~jMv2cG!HFvMkTek(fXw1smF&;j==lL}`0i z=RFAZg@#QwETV-GhuOT;cwox_CrdCc@M_q-nF1r)-4WT+o_))^ui8QsM|uDV^9HrG zZPGo%M(yY+cR8FUCjC%m1j+B|)bkc-y6Wre$08I62llntc7H&|UHZ&o~ zOiXIzDvBgo`>B!)IE0~Ya6TAJdu(ld4-xUgv`k|Fq!cNjf%^k2Ea*vF$5{jVV0hFh* z+Op7jD6K7+fIFo~uNlraA8JPb zeKCe63cYYO)i=h4;H`nwSG+j><#wF0_Uwo=B8pR~leHv8)If!n=PHbDD-HHZFVKx7 zQDj{uy(ax&Y~vp5)X7vaE#mrMvxXdw-G%^ijzdu)J>!V;2R5j`9R66cWQci;nk|Yi zitK21e#5s2eKJH|_M%JB0ACs`lF`OMaFrk8<|@Y8(S0ZE+Upac{rO>^H@|ytcA$kSK?$~PBgo#SY861>m=*pmC5b_#e3$?+ zkj$G87%3;nGsgl7NDzSFkw_ec$~NdePkj`qzVbt#nqAIw{U~VrY98XXSoGPI$%xTI=Kf?jmH}0OqgN^VVHL>_Op=g2_C4>mA#RbjUWpe> z|9hXrPOb(74tHFGS0%!iF{I1Ukoh*U?rd{Gd*=c$KR)+9`~xxTw$ zPu#7as^v5lMr6nwM_>DNW|gC;RskWp9%p-i|4@{m8#@TRA`^3-V`x4V?*cG-MQPsm z6_sROx1REgF(5Mi-@IO#7cQq3vAYL<|@|7s{YN&*H~P8 zAD3BZ1%U#Czbi$oktyd!b(+Qsw^|2Z&e4Kfi$s(gBV%j?HNOm6@$48tdNG==<9=TkNZRMZ4EG=*44-4va}n%6=}Z06w;?sm z@ZmCVWH6M`TW^ReY!^6+AxecGT_`=9ZC_b4-md+Ed1--FwJN)m5GXgbP=ByjsCydb z(lqMVIoqpYz-unTJP9RVcO?jiL@-a*WPhK~0FPEU_lU%cRTc6sETVy-Aw>%ZX1lG+ z&q18VLNz`?I8)Y_O{oD$9_}--KQ(6h>l%65NUb#ky@JHTUa3#Za*+RWMy401ny_HR z6!o>i_D1Sb+uaW3JTgpPxy_xYg zx1~t&hs<0nWC$XoHtTt(G5g47mDjdM=F++J8R7qN>j~yyD@QZ{RM*J3gNx@jsgj%w zYf|4Xzu3t4tkSPsEVc<>3#dRxLli3KAzZMlR=dm0u)gy)4m~8~S$wK_1o05O4_tgY zvvBoMu5*C(cv`%8&m|#%8e8Jgw+~-Jj3tiN(N-6#?pkz8Jj6U|*Bp`|tGCJa5I9e5 zb!ImLGxZ+3`M5rPI$<;F+}QKrj%m>c?s|&gRb_O;=(C;;qP5u^ zsUDbbCFuI5SzzjUMg?b94%`8Z*NY3{F)NS~%C36!0ZxMxMz*Vfp5)mo#3s+uqt4)w zhM2ogM|}v07(rvbHyWT0ovewwZKb6mT?0^OeOor0hnO3Ub;WtlU40giE?awEa2QvY z$M-cr76E9mac@z&)+RNr=esmuH{b#k8XUb-fd-o+BpC~iT^m+Y=S4LVEz*mt>nJB5 zg~HJ@RZtc ztbxd13Zi6fE>Ip)f{1e#PISJGwqAWgCshOx^!iHS$^cXC=_S%6b5b(oPfE+Ob0~6f z%F=X5rJMVpZE|nxRBww_L&MIL6UY^doQNRZD>lQok! zUj&<`4Pe~Cj^;K?LA;@UJ3D!G07XU(Z(>Br6Hd#$#YBx1cY6zulbJ0pjhmkrl|U*7 zDs_knFdvUWWh*5cRxsWuD+GL};ckO6cdW0^0ifjch0CXGxZ}fGv!V!#fAiuno~4It z7&D9-CPXgcJgWLn>vwqlKaJ^$j`2X0YWHLIMh3@UbJH*lIu!#uq$6N2!^KvzjJFR$ zMcGUsfV;3kWjmZY1nYM=7_Fqq+Xw88V%X`F;>`F9Hycv{iG}-ko|LFQ;^5xQsr_wc zeum$9jxI6{=&CfY4IT4zsq*{gicVYzre%!w8f_;+$#66@I==uqjZN1rX%irYrE_kS zz-GJ#6JB=U#a(0X5*N+pli}+77fap(0cS*!j%B+-r!fVUS#`Y5n)OPE`&!QB*CCDL zV!7^Y)!BTXIbHP|U5}^VzDBARzll64uZQ|cwCo?3Y!uK0V>2TgECC`$W$#M(LRlk% zF+J-pKt@b-1P<1aSaUnRslL7}w%#y1lRiyuMJ4jtz#Gt7%YVSd{W~iJd3bWmQ$eh(Qg?!5*FPT#yg+(9M zO0WYyHuip4LM+O9C=jnoA^fKJ(1`6BAuNT}-uuA6W4g|p?b_RnFg8u<^nKblW=UH7 z@%7hvQOzHyg@aX8cdQ)}IfT;N&tJSEj=hDeR~x9#MNuY(Pdm`9i+Pnk_{=0L=EbB; zAaIKBx^2&$l;Pg=_W?A-`UvD^XOOtJx#{7VMYJKYjd4I?!HXm2Hd#L};b)&->M3K+ zcu16jL?$Q$*d!_)ib$0z1`~2U_+YDG#Qq(DR08}aPPU9N9e_vZ5`rzhnESM&c`m18 z6wY}$z{%_+5Ukd-_1J2-b$QEvjv+h>Pw7$;*91)QKdl`3cZH2Fu4GXPbDwJd)V3y7 z#set%FYS6N&CsGos$Mw#s{(@gZAOm{t%#-V)EyDNL;p9}`wo9ks9eSn-~rABH)ToQo12K;j<(6?H><585(dH0|q z+&zRKhd@e%NSD^H%fpGDzyMJ^RCU%|A6~UgR6nLSvLfZ$9 z)L5v-sxpMI&{loR2WEnW#{RfxAGz&YiERncmo{NCEnri=>nVxE1X4CVa0+`gr5HvcmS4%v%uB7@ADnRg- z;>0;1ZW%VH2z`Ur_hD2%)txe^Zum(Sr}P1^NFS+Y`nHuJlWc?*zxUR`3_QKxNa=bPZmB66lBrxl zrj<{zIeOKf5YPqSoF%tF!F|uLYz% zfo3d&5|dA;!1`wbxhI&g^!ZMntvY&_`PpEhP8&c|pZ}Q~9y0b8K=*}qsg6w&2KczHSj(FBwJ7)1dz z*avYdY5KYt%4Kt($S$71xN?(+>UYo2-*zvMy51t^U0mz*%UmuSvw8 z3pP0PHgt$R5j_`zBd{oFe(y6}OuKeI=X>h*y5*)gY?oHbwjGN%PZk6WynuJOH_92u zFlQqk5_na64%+;YhB8Z>5W5|)J-|rvNMT|B_sOOAl|@;W?S)#~%~f|H$98*Dso+z@ zp^lsY`nZ^cfw=h_6EBxt8ZHW5cz{Rp?p9J4wEO@|e>R>_4{REoYxoBbL9+Yx~b zrn~hYIUl(3i8wG^Gh|DlPBXPm0OHQWn1kH=D~^Fpg!}jk{1oQy;GBz%_n3#PKpH^% zGu6Z{bC<$~dVL3FYbuu{j}~bv9{+Ac?Rf^ST6wImpq2<~@aMXy6)x0Jcvq4N%+nSZ zIDf#~iw^E7iT3^m8?C@XzknxHI3buzQD)T7wn=LUfBCyw?4NoLAbNiYq;lQ!R15b)cU=%G+mm?tgta%1mO*j!!@DC-2hlVE&&%L{kyHETbEm zK0^+7soRvj8L!Wxyzy-Cb43*b<;3<-B38~|jX&2&t;VPL&IdSUE&tYpe%aB+qC(EL3>7l_M}MbIJN{{G89~Ncg`Ar;-LSHl?3uh4Bc~au~n27 zyVUc*d$QalyonIw*}u!VsUG)A&1Z!>S+40|Z$O9HNF+hsCWj2j6Vr&qwB0F}iN~m9eOO71=k>KFhtRd8M=HYAU|dN1QKOe#Jy=!yO#x}b7%)pIsc z+>YH)9X0IBI4v@Kz#{@_xv4442nBFaxBANI(`p-(;%`}kkT;5^!?&AbyCAGNkY$-M zH4Xd7#KX0u|H8l2D375BaKdIj0XYSk&;7`JUqSym`65=C@26ZPIVM^}N&^z6I;CR@ zwOJ7Cn&SU}BwD$zL z*6irqIF@UY^Z!;*-D-|bZagQ`^E@d`LuvU`DoI;?od;Bne66_B1 z2~F9OjmkIC&^rY}?IN@k73WFodnn&_Z0Okog)8W~{?%#qQp_`As%hO$5zb(cWw+)6 zD&6rV(hX<_T5&mki!SeJIE+j_hLiNKnp}he+N%s_Du~Spo3*adMxUvJtFN?5rLXS& z{6{2B5C0q5o?_@!@ih7kWh14NvYZ5*BY$w> zPIEXH0#YVKo^V`b{%GKHxt*wDlpNgor~e#1nBqFSt2wflT2)sc*={zC!e}Hw4^`Xb zo$9yWo848{90#jxOj=mdAf(C!f^!Lnu+VnxRfbdOj7d7aMsK3lIgA~H^-map>ygrv zG-S!*rhj&AT<0E55|Z+_j=wE)9}pA2Lpf>lq$UszgW(uyfHrC_%espTXY?qoGyZU^ zY<4`+u^w`j%l4o&+WlMd=Prc0rHNAxBO>iuVe?>h`ID6KRQJS2FY}PKCLRi!%<=x( zqmq(o+eIWv&M2HI>CkMJZw|iCiJn)lzQ2)NCvF1Bf3aGe0?G0bcQb19t-g z2vl?y*xZCb^z%UaXu83py-SJ(1i7J)YAj^em{H6taMcpqsx#dO8Ll)$&1Wu8vKLwJ z*O+nfke==CovgLM;B^~YufG6?$Ex{Y9c*;gXa@_-z1Yt;@mYH8lh@=%7}du>&+3Eb zcy0w)DCcKB_WfQqvex}e)MbCI$z1}{YhZKwqv6>6mgxeIRd(XZaYgzI%-6Md(F2!LegBt$duyzl4c`7=cejT+ z?;`$*L|O7K>G!c(9O$T`-GwBLQ#yH>CTlngf!)^A|Grl1B>aDvD+;W3vHj5G1CN?l zU0p>L!!+^7YyjZqlbD>m)Z>ZqC5!HqUsK@VyqKwt8wGz)kQ?F-*P#E+EK|w zGL7lABIZErVKn`EGRT}OxBIAadmsXG*%2hpJ8WZitInfJ%Of9I^#p{BsxKEE?><`>oxhZfgK>j9)U=FiEKyy!@wChhOz;c-yMjXP&mLcI7BnUqG@t6we+HmvI-9<{GTOsuj~xaUp9K3-AA`~VQSTAJS#GDEgy>;pdNl#S8@9i2Zo`H)#!JM8B!XDN&POnh2- z!lf$VNfl;AHYPmENPnhSCEYAH$=$Ft2zs9uQ6ust5^1JmxaHO(xo*#N;TtdWW8%%fvv}sn?Y+L}6nzbFq zRYygO36h@`?tV&9ibFAhwe)RDI`{Fj!`~zk#WVr#u}i5$tFc)5G}GA0CvpKhD(7t@ z;nt+dzf2XdKd&RH2S0hLxWWhw+AJu3D#cBRfUbI;lO$Z7cwQtXRT4H4Sbj(1#xZr2NO3~(s=0ym!*l%e7qDF`Jb z8^{&?HBiBjp_+f$_oA-YoM!RQ{hELTO&i$BEpViW-38*^lT?uAjaRb22uLL+kY9Os!Z6c!qwzTr5MTr2e+*zPt5h>abv`u9MUdT%z+1^vfdU|>2U3FS0R`Y# z`$)CP;Sd1d_d0;CJkW-XipgxlO{*%QV9||*Pk7vt+QPcDRaRa10Xzp;^e=#7TIP(6 zUuS1*f1NfoXQ@9J4b9*gMqRdN%uNlxm2<~%g3D_&@4w%kP4Co@&67!f*guS+)MLa^ z&nEaf9bYT?nUs6`Et!iCrS!9@UQwFNH+|Q7*g6obub)E?n^cK3s$)ZApbPqnComQS z_X#Pl5cWn;X2pP4f9P3gSOUV&^5-?7rx6VDA##8TB4ulR%LnGcxYsY-KyZJ;$D64n zb$GY4aKfrP>8~u6L!_bbPgI37az~yEjpSq7J=}XjHto&UFG~sko7H*}TZsd(sjInk z@|X^niJw^Du=}70A#;4f38uyp&G(2U5E!0phzBYC^_jdCQj<|I8jKC=h6MRMh~GWBw}CFp4l#%{wX-DjVJv4{4SKEl0-69jFRgyw zOW%2@>n0FPe|fc7Rscq*^8fzC+89=r3wlkudWh(%1JaDyVNnmWN(5k>F}H7y05f;c8EW@EAmMUw`A$OR__qyw|@rw zs|y$->)YgoZrgF$eq=F|fz=Ces3if+(b{yA>~xi)Qz_Zj+x|fD9(0DzHh?flF4&b6 zJ_vbeRuVNy?mP)q_eB3IP~s`N$U&9SiFi~nm4egM()~0VApm~E7KybZ5TB6%4#QqZ zj?+2~&V|uRo`N$w?sNl<$uoW#L#gcGnBlbpjY0l#aJ7-QrdH>ev=P(ClmgmRJ6E<7 z{@uuW1mMg6r*;oFIY$+rA?3e~LG|0cYiSqHcf8oR%?tO^A``Jqx50=&_@8m8Lv$EP zExk35#<+GxaKqq~_`@CyXN5B@=95q>ffJ6v<-BO!0S*A8v;_rT0?!&jwH_&nup|lI zqpoX4cv%VEe#NcRf1af+?Q)8S_NAJ5JIpArdlOOG28}>}#1CJM9(+JN%L0k{=8sCv zZ7YMvh^-i+^=dvcPO(=iZ9$OR_*#iJVH0;^$#r8g0cwIxB!`W_Ug9t8m>*sV!C3nb zAYeD3vBOt+ZK=f@j+|sBYpMk#0`0(Ts{pZ{OA@<9A}J-o%sQB`w%a%$2}UIH0p()j}?q%PdP|^?V!zX&4CYOR28Rf>G+elMA@*A^j?~rl(Xyd|KZ`w%0$HwGG-#qVEj70v z$y{$^dt`KyqbGh4pn{t?A`GzAvp7-7YPjeH>qsOVIOBufe4E%NMIu z&27uuRu%1U2!k5Dzu4<-PwsFgQd;Vo@1>2JAE!^#<+rcSXkHf{&J-0%=E$KIX*M(U9P_d9cu z*EEBqZnKAAy4%g0;fcCWciB_a5*Vp!<6_jJ*#LW2|Cq3S;u?m^GmzWPq$N757IR)5 zHJ@R)GZEENbU9wNQ)U=7O!6ZXu+JD@{k015wa+_!a)BNS)6ZF)JZY!ojUE%Azx;=| z)J7`1i)Bb@Z=TRex3KBDDZ#p@w+X7>#iJmrV7IFcHorQ*$+edZfGf~CiZ~2ox(r{{ zRHux)j~iI~1j8wa(%f8N*c_pcjr~*Ma~HZxeBkkU%6YPq^c48Ph8lb%i$mLBM3EV< z&|7+0zDa1-2JQe`4Q((RbL40a zNu6g>-YA=tW1qyHgyGqa{7xo<@E&~aWk zCHaLa4hYVJ_IH^71OPwclB(-RpBfzL4RdKr6PCT{7H}vn*65rjp(;L4Dd`%@2vmv+ z*!MbHLgFgxScS{nybESk!?DVBi(zvV9s~R?uC>aaanfR3oHNO%4pQRiL0cw&b4&&rI0fuWO&SAHN) zfg&62U7Y4b99ynIgR-_y^<_aSgt28Cw%O%|KN!%a0 zUvXv2zRa{bHwXx9CTMqj!Ac~WzMLL~(|geZvSwLTQh-_a=wN_TluR6>;mY&dudV9146%sZ0QTGq3cdo3S9L zg{k#-s4b!O=T z7i)#h+|pnS3!7EcvJ!@uk}i0PpZ%(PIM98@Kg*3eE3FR98o+y_?GYGfl6q`lwat{O zYk19*Pz^Vw8E)CK${_>P<%;kXx4)j&dlS~CI~J~Pd`JMrJm;cYK0x;uRvG_)`Scyr zl&|4xqz!tZ%4^D>T@u<}Y_=FziTc=~3TSKs>-XsH4|UMJ0zl3_z$mJYWp%Pn@>{8w z&;xb2vTx5g8X7>(gv_Yr$RQ}@p=9~;k9O*#usj3s57FD}%BL4dpWoF`m( zbAg>^G0jI^F&cZ<{tH>>#$Gfo`|kS{9<(_SEV*x|EP3Z?Y4&J=YpR|Mm(8%^U5#n+ zqsB_h*QpnULyw-R_T*Sv_!l|J-~)YBirT;#&)9{3eL6I&^_g3r3~sb%zecU^iSSpz zFI98Gnu&T`z&&xQ(XEI~ z;)Q=vBYRzTo%e`G?QD#jM_Lxh%YPT#5xo*m=@AS5&IA2P)<`EgvI&1BTrKE~0}#Mn zs+9{PK7t2&1lAGKyGxXuT%6e5LrAI~Vj>refinsB=K*ktRg7n?)eX7a?at+-FA6ox zt6NS&Ko(H5)8iPl)icN**O&R839tV6W$J3GF9n(kNmZq2K`?rF5NDwNX^2KO3bfrS z=s$ucZwo_L*cS5*(uoXOLN5LwoGb-U$;fVm-!@kJ+U?-FeR{^({A82XtkYZe%(v@3 zseKgt6tQj=yiQh`MOU5Adh#T`c#{$h-pz-l;+pCCj8ggxV`++0cf4xKB0>ObCWe9@ z8?QFpc31kCb_p@txw6%jiCu7z|K9K351gHrWxhXeieob9re;I&9^7o2Jt@K7mwC$v z@4DCL{Boax@3!mEayQ*tVdBsM2CTu*2?@JBe9a?fMxx}Dkhe(G#5>nK($iR8Np=?B zv*Z{c)$g^a&H9;{mBAPL2WkK~%Cx4_QUnZ1_;%4~)C%^#7BpT*c|;)?dT~GhOG`9w ztT-dFYZ4cPsle%dx;4H41JzfP>D5#DKC4%D19Y+N_>zfPC}c;mwJL&fJvoLB{1kb!DPCr?QNx}$$;<;ZVP5>F~1@_O;PfF2Z%NH{petYfWe60d4IE>O_XEBJcZLnx5H^_x(eRu4ACF&H9m?Y zC`^TrUJ5N`a4M zqU~k^X>M;+{{_!$AIn>&g&)QwH)pX9iF9sJsCr9KS>g&x(hE{P>t=m+2M z7Hl`L;3oNr39knkkA7Sx3XOGn-)=dkkn-ky9kp|IA%zIifawB`rk?6H za~6$3fi1SJ{IT1DIA;DnFM18`Z!+v%@_q=iDX5y|Vsg6n0}4Q%cMBz@%~TXYWju|h zu?n&NT;!S?*_n}KU*_azr2UcUkMJk=LQK_!ExJOi-xw?EhrI8Tq%eD_-kbi7GR~_} z1PQv%V!Cxv5XAbX^-Gye)`IZRKuo+sL|W={F$B0srus%oi9 z7a_?5j{CZ@0PLY~1)fIFFv$%v+|kQ6<=mwZWT@=q3d*5?*F|vRFtOL-xM*ka1*#%a zTYxN$>Sn;J1hKh9l%jebwD?bj8yrtwcl7twnHu_SWpM@SUNS5IrYlCB4*{?|3?BY% zm{kLDwet&Iq?O_YgvCESZ?XYW6nC(CoF* zA5x3|)1u&92TtMj&CUPFwO|F)Ij|Rb5<%%^J!SxwNfT!z9%lQ41K(mx*uip%p=USVHM(QRad2vfz6INMPlSXq$yg$$ejUw*dxm~RpPvnk*m1x#$ z9?c>6H12^x>FsGxj{5R56?mWjxVtAcUd>2a`@d*Bm6h{otmJH0_AJjyo2^<5Y4{1IQo#79KnDxDN}dAd07c z9VIdVE!WRn&p%G=@B@J!uMMWhM|!H1uQ%VByQx&i z)uq73DR*3-#~CH_<6E9N;t5&02Ak*mzw1Hm+4`JLY^*gTcY;i`}tV{?7SLASP?5t zvpBETfzvQKm%>0yOa|ZfZ9wl|^l;kmX_bRPR{2S*I4?2$ZOclPKV9c4>%>dcVA4Z# zp!_x-KC0+3@p8-3q$+Jx7(sy1C zfM=h;x+L*d@5fLW>$=_>%OF3;5jD&*vO_I zSg+*uXQ746^>h~|7E>tiTh`>iJJpom)Vc*tvpGc%b>O!vSm(!uVUT?^O3Ho6FKs|h znoy|?iRM>$Oui(!O*rjE%``nb&kZHSit5E_En+LWH)KM-#M_QcH=A6c(B-IRyksMS z=`qtxnh_oX^=(qv)O>YzTva>+(xyufr5reCDyfV4RZL8G^Ke1h8FU$CA!20Jni!$ zq>M58@%7iM4HOs<(rQGTz`9?>_v9rEzT50PoGGGoIbvD735>v7ydMPIenglDc zEL-2a&x%96%{tRe>#FfM($IT?pxyW9lU$-CX4I!u7a;%dney=Z+#&U>sIQ8$tN_m! z=$r=l?~!9@EHmSvea){70m_cieHC!~H>VMUNP~n3l7};9)t$>{1c!#oGhta&g1PCM zUL#&OE}<`_WWSl|@%_y=#3L>1TVc~fl}`(mx^7s9+E2$K>Gny>*SKEk;_tAQECOEu zJrNqV8}1W*FVOvexEg&(#rs?#EavWaC*qyLzjz>%38kf)@86u(pm;TRD54M4=)!_w z{Gea%36$j!+8Ez{#l^UO_;2!a{V;th{M1J=%ypD##=~`2%$>2}`7A2kVg%xeYf%BP zWUt0d9v82Apps*V7f3zYY?2UR?V(3sv-JR@hnW#~kOd5O{xvI)tB?NEvfF+8G<0uo zZaAtmoIGFU>93AeAbSMh@KLg(Wap%m;8*}b|BkCt8u@qgRx}jxwy;9b`efjZNa`=Y z0&Ar_Jp8hOQyW4Ep4Z1Wl|FFB~BtO7cPD#qiELJ8^mTcOO`mI5Ob= zPxw}_iR=aE8R*mI$zrQ{jTzU0Sgh%Mc4`(m+sXnoo3-qJnDvmt1^)rT5=0{DshcDl zn<)s_wT+hh%+yi%8-JRk{KC_?c|pb&ZIHZa64)3j4)-$+cc`$tq)8`ND&+o85Dv?a z4&<}ZMvs&=@HbfaS?V9huu}me1)H3i$D%Gm+bZ47oOV=9534LIiX&O6Z7s;-S@SSR zti%O}w~!JKSsC3arW^~ChtLe&N}ULy(?sfPB});32F%EQ2!KBYF;Yf@mYcCGl-5xoS$J(3AU{a?Z~9maK-E2 zGKi6eSOa6$EGRt3IBTM|0?G(7t3V3m6tkxuaG^c~6dHp)y!YfB>$Fd8b?FF zi_1w>2NY9u@eOP?a`C#!FX{!m>0*{a5ks%tR-9nL4wj=67xErI)B)}8stxv5=Y+E| zb7bk#fqHGLjt+e_7w~B=ZEC)B%WxqALRk?kvvDSpeo>SzWNw17YnhuTkE-c=S-UZF z{fIXZI$tr*wxE-R4b>@#7yC1W6O-mX#JLdr*Gq~-@x}f33iG(R$A3?DC>jwi0Y0*@ zL3@R`ZeC_tZp@W_h;RdS#8O)P&rIe}SVbDiPh){t=wj6~{NOo5IUkg&M$HPssn=fb zgu+d6DE)UZD~&7GIPzWWdpBu6R5Uw1b#cT?c6Md7)+F+fnR`K9><7M5)kd@rW%-^j*@BsNwfdHF8>WN!h( zviBAFJs(xbu>eCryuajN&|w>yz)#4esZ^4#sKs-pM~X2%BuezxO-0R-j0iu2sE2|A zNCI#n@ZlESLVyaj2`;SEleTb-9Kgh+3hk|}!yiw>d&%UbfjW4)?)3gH4L!*u1t(TZ zZsoPmWi4pN`xnYAWDQ-309~R6%T47j>mD|~3RVzcn#qcphTOt==jbkx3z`(Ss_jWXA5UXJ;5=s&Y5XMih#QmnP_iYH21Q_vt*d zS0nrD<^dTVZ9zF{hMw9UfdQ9U*RjcN&4{hAhe3O^a*Dz45xE)jKb$~92Rt(EJCuKW zV(QnL%3%)8y~jfW66iv|JDi(}ZvPPjb$*o6;wiDV%Bs(oU^LoNC#Rf6i#D5a&Oaoj z;kVc5doKI07W)J2OQ^5h@NujlbhwDFWNqkss(=PhNw{wqI@36DI z-h&mqj`tXT_)ckJ4=Y~LCS~L=Dk zj`UY%bn5afQLef-VKMvWsR~)zo4M5w#Fc(Z*WCe(fi-0a2;n9jI*TnJA!-wF!1q|t zdP5KcXEH`oik<2t7%c|!ot6;oiy=>^CFi#H97XC#^EOD~n)&gsK^iVI_;Z?Rz+Pdf z@19@l??Fgr{h!%)FaD?XYb;K@(^(qj35rb+iu)uRK@@WmmN}@byA!Uvng=OS{%l@s zRZM6GWOm4-n3#^W$f(hpTN**GU?Knv`2aX&rTd{~w$EdgE<*5<9U5%9VNSvjr>b2J zTy95iq0&io0P6>;%eJ9_&l1P?_O`ehjpntxcncwsxzz#kNYs_kv)ECp+N1%BHKpwA zs(J_Dt2QKMfQO6W-j&f0>=MKZZwb2xf>n1V(e7oLqr;(G-T}FhkM);0NDqu5>2*;r zNBX0Xn-XxYY^OU97tdAa;}>NLH_*SbJZ>j{^$mDCn=eS2T-(Sd`iXdy`u~Nr{RJP6 z3JlV+227V?#~)}O(vt74T!vPr=6~v-ov{t4D?IPN8820#w03g+{O>HgUHz*SKtb5tg&WD;8oH0D_UlzjfGvhYV{&s@Z z^39bqh-x(CX#81-oFmBOkgUaL%?5f+2Ie+xhx!f@$b#E;>kjK}7$LELqc@Y$SN^lc z2ePbz?lc68rr?0UoYKWU%HhAO^c0b>DKsB*n>`Uu0Wx+)4XTX1D+5u68S3Yt0vG@1*bCWFkqU6se=~X<}yphl%=`scRm=#>kKa zPC5`%A#_>F0w{8UWg4E>6|Fgux}sMDfRP$GLHKZ2%GUVb_isMJL{Em04+S7fB7U%e z(wct};96+SEFlCMPrUbTQq(cQXp6~lD3JiWo=b2Sq@)g8zN`M|dKMa$vFa?IunJq2 zr8LwvtV=kMV7(iD}_y-<^!W}UThsYWx5EvBAx zy#QL3k?E!AgnSSeuyLp_UW@Y&Lg!)DO2ED!Yg)@R7TR0|UL$Fn4BAlH;F0&NWwUP5zjTyQ@4DdPPGhriE6n+O%kY;%vC?udchH|pNPv2 zk(2qcDcipL!bAe3`X|3Y16S|3ftxV1wIs1aw~yi43oYxrD#6E{0ca*jRKZIEh8EAQ zoRKF<>&oJ9XLtPE(XWSk-ksB~id}8(;3UIb_e@D45{ci#~lW^*?OLg5JLbP8tUmCCl1 zPx4Pi7whwK-%a%7?_amk^oyb+;B`rUSKbHtMh4i?*%gZ9$+oE0iUdgu9$hW$tA{b! zzmKeIr5a|&Jo8erKahznu`j`m?c3GKNe2SGj`RCX`Dd27$CpM9;$|_D1?t5!>1gi^ z^)&)TXZex)4X-UPV1y9q&UR|LleS-lr>W;tznMK#yBo}E@!4t*n4W-T8-&mg4duM3 zLFF$Gy--ghay^Kg@Y;KbzUA!K+@+}Q7*Hi&2r=2`^aa?kEfIld0hgWK?Ah}HypW^N z7~Tln0`*M~6T(Y{d^{>(otl)B$`s4e@ z`xkxN(y?Ja?Rl_2kY6%A(d(5gK#nO=oac7Dv93NSz`^+K*DR`QEO)Ff|EJ!CM~Wn{ zb)?w1sx%OJn3v=AtzKPZUOV0sBGw&p?06llPSPr4bpCAtKXXC7CD;y+Vq}E z9^p%!RHgsQSVvNKEJMd)EKa@iXY4V@ zj5r)o;Rt8HHmyPp+f5Oi2u-quNPUE^ zfnbg}e;R^uR%-*Je2xw!T>%RhoON7(qrDw|twc|VI=qsG;RZFWI{&+lZtK4C$PQz( z&H~je$g?*}HH2fiUfejYr3|c$@WltO0-k@x=s>BM*_#tR;mK`_wVljFJv&1621k+O zs$J>3E{-cPkZpR)R-hwk^mdi5`n>$uv3}jU!QkBJl2Z?K7=>k7QW3(%PzH0JZAE;C zt+TL^)6D!y;w{KbD8uF}3F3C;lgS0+Y?974i)O}nhGwQPjjE~C#Z-0~S9|b7Az%f0 z9qRge<%7R-}$}vCVN#L#Ay&UDGrYPQ`uR@;m6gE4;{25^3K13#$`PoLU z59Z3Kdvs41LlsQ`B)VJin*eI?pCOZj5w-Yxp6L$YkpIaMt||0ax!_3BQ|tSIEi|a_ za^N$$Ns^?33&oKGW^Uf17&{BSn69u8@aa(921lbBqxUBaCs#g}N|;UAitDBk7#@Jk zL5bp}$$0&-yol+ocb=Qyw>lqsER1t*I-m{Cy}^?~h4 zrnAylkoki%Sc)gsMN$Djqfn4k!!Vw_;)&(W^wBHIa)Z#})1__X(xi)}+r->$c5c~A z?(KCM%RK2)4<)=M0b$8~44lyAU2>uoF_YO*d z;+zn~)2UPyg2^n?ekce#eph)80mGAYtcSb#;r$YZ5P&c}AG2>CEO`7ft(Ttctl?Mv z+=Y7}2ptPXFcALCg(I;r`Y*iWIps9~Qlibbe~7nGGKxT6>zk^7;r0^mBe)nkFe8p$ zcA83Nq|mcfO$=f0*@vjj%< zK0Xp82hlS5yReR#L*%%vKxz~I8{Fd&t0yD)NAE;r)((kO-L#3!(PE*q9z`+B5huB$ zE!xZq1W9L|?=CeKe5#B+UjR3JV*KiW@|y>H*YoDc)`_@wRGmap%R+8J{eI2XU_&Ik&)S`M@ z*-W3b#wnAbM^a>}yHL{Fj*jLwno^ZSCX;RZoOJO2*nydvZD_X^D!BOWo^lx#CmMRLOe<5DIB5LV zj<$))p|v25o@Ay}+BOVMR&*hR)~?ql6tp&42#V{>$r=sHWa&tx?ru}a*7h`J?m&&e zIxZHj4T8Gam~mw-(!4H{l4WCe+1Jn**l6@z!x5J5N)xqTd~4R9Va7_c)$CX_M0;Z0K0%tAsibW%M5K08CGdcboj z$BPwGATdMZPw_eW|4wY~{xb&C2Q&EZQvaTcQdWCE|27@3@{--Hg949R(#PNX;v)v$ z`qP?Du#(tHsoaQqMMUPs6l|ZArpqMdXfKrrmYh(|x264N}$ z>m`HgR-mb9h8T}wAhl$*11!79G720R`WX*nMjX8WXt4qc3LtLWsm6=NQ==@Tih%qo{xzGf~(rprVTw0BUjn;A!J5v72jgxPPDvdxzBl=BL?}7{R=+t6$ zH2@Fc;l~ems7*7BPgB^ETaj`4Zz)4~r9{oQ_in!;+58lKpw%Uf@x}B-M3-iM_uZXR z5R};*7_a!+Pw25ys@$x1q3s@bZ91O=9|ZlNuP=3oYclk@uzmsO3%eC9!j`h$Na!2u zf>ckmZki{NTuIKlnLcmuC9nb;h5eIw+pFRDHQp`63xl2gu*#1AM+lLB0!+r_iWFV`XEAEaAE_lEY1 zl%|<3w;`qbls|7D1&SVGs_qr8vW9~^ZdTRamx|ZJz(WkY+XBsm$FEJ(V&0hkr;61T zCHBTvXDfqrz1wlIU&Olk*56}kyPwKn`&vC-CxkgWej+*d0=3hs`3>$X1Q|&_$)MZ5 zl|r*Eao%hks3AmQs_sr2B-ah*(*GT~8VC`-#@sVD>Pdan|6ZdmA0arin7Fmcv^4Hf zlm?FXtwjyPnmIZMv1$x)oj@{M05#3`{z^*%i{&^GUR2w|(j@rLmAmOEWhoiZwJ7-*8$Mm5~DS zW#=UurdeSQdEy$$@Y@H_l&Ab*!2B**sd9|&QZX>=r<$g&^lqbaX0k#aXNkpGgtO`* z3Or)#k=cIq1E^S(Jz!hhB!Iy$UXmM0S~~zZ#qcX5z`+HC%ATmxzEZjiv#*5BUT^0% zle73L5wnGODmnQbyWP2ouzbHe#tICzE^bp*|5z2`?py)*Y3eb1NPc$_v=f=mDVgcY z$VJ*IoyIPRv~Tc+Ct6;)(lV zyIvGlxw6AWIDLe`%5#z^$Mi<&`cQ-$kpNF^aTTX#?5-{TAXE@8 zX%CIkKW#xLr_xK2b>WH~ce;%IW$Lyo#1BRNa(q_2qb7tpalwTpKoo0`4O+-ND49un zqGvnJS94IUQ=jGL-d^Llu!Bp-&rU(NK>XpZSStK;0Jb?CV5=Y|jPEH3O9g>#M2ziAinlK}@;zOP%bFA*D&0 zepVvp9d%G5QfTWgVozv>r7>3Ny(D%*N8ofj^^sR;ApEi^;zCE2^2P*~lCCuRmmx5r zLV&j|mOFLcrYpA{tjGZ0%I{8Iy0 z{tPZ%1ekSNoH?VE=v(G}YEe@j1${U0`s9?P9|5z;hU_`#rJZeOMc7u%Fy!}0eV#Jojg`yaiAo_>#f@w+R%`y z89h~0>

#?u#6=Ug~QhY5jc-e(h%L4qu^DdlHd>w>dl&76Tge5d#X3s1Mx?pItU2rreMQ1}n}HLvlx} ze~Ft%5rOO`gqWb{xLRTcM)JoiOxk0>^s}|!;C3^ zE*jY~35A=oo}7jV!`c@=m|JrjD{5_C69nGC$2Y@oE7&G6XO}OXP$+Bd2!|Gg&^OGE z?_LeAu~we#kd`dl>o??X_>o?*YeOBG+zybj2RsuEi%COJ>Z*S*lJgy9A@9UP-q*w! zGqtUVQj?e&g*)D`U_x2ii;##dwZ|Pr?g_bL;k_h~M2{>YHs(|9=f&zdxU5w3@o+tvlyWUUGOwI?SI*D$O zlQJD4KY!!(sUTuoy_Dr_Saaf_E1)ebksU8V?>1kT_)R=ywv>Pwxyel97zK}&fFrOV1bqFKFiDs15>yzu zs!up&9>tQ@)-Xi{7Z#U|7Ox`SK2NK&-flDx#)wX`-A-2U@p3*#c-$~j{b8~z`Nr?* zQBM6NQn3in3b-;&?>$xZe^X$)5i~**SB|%L_H>g;#eqoXx{T3R6qy{(=)9az2J+nK zYc-on5V%^fR;+x)LD6vh&a-H#%#}2Eo5`?_X1g)JtzD4vXrD)w^Hii@bYjtp1pFT> zsgZ4`cg0If<;SO;bn}~GyzHJc<;gabR3nte##xNj$#Yl-)ZiIGV^xkeG(a3Vo|Kg* zAa3a?GAdC3v3$H~KNua?6XLnphFz)60~aI^f-z8YU`@gFqFw@ zIR>Rfu+3{VRWPwS7Mv_VhmD$B>$|jJbPXHF47^kMa^4X+)qvegm0G|FJXxu?{gH(2 z;R;iD9v^zlJZ+Z4nSXNoF1()6kh`f8K?XnrkV}8;w(M>%;bWF2y+7nOyY_-&K6kRd zdv}YgQ?~)xKZ@peknB%aQ|3_z5ZDN_x&$K0qyoD74fU4n87I*ftNFc%xwYF-#z1kf_=v7F87`#cAVBNji5$hjS` zdsLPmaV3MwAdT3-E77*_Q%%BHlqnUeE%Iab8j(d@yNMA9xPBd?Em0}+*kBNKNMsC) z4^ARHE_|UPcS^$(0>&*2+EAlU$$>8l2|Zn)A_c;t^ep%{pN(3VA0+`psYwRlvNa?TO| zXbtiJC5M0Nfo-CToUQaPUb=Zv-w58&R|yN+Xd`27<&;LS~ql+{a+ ziz@JBIY7{~Gfm?%n7H03zr+hIBIp4%LB>z=lW!ut%{IJT9l+xPl@9t!AS9($j$rl(fX^wq3{{atxIwWXFa z(g)yno5ygM)f8@O-`3iPP-PNyKQc$BZSW~@ElJO(vrT1W1FlU()M&L!Mb7~uIbr<$ zc@l-YumdVjOp-H@r^sqrZ=_W;PU`P@M2B5q{ZlDhpV?uP%WG>)={!)jFa;pjFFGOG z$-Y(Di9!LgXVqkJDYN(0#@D#!rynAtgea9kX8#{SRMLkU=KayImaq}^1`rL=AI_8= zm- z=o4H)6lW7f82;GaWfePG$Ymd&`7W6B`<3d^5pM!}y4%NO1)UN7)kZ8!mHS3?(!0xV zrd>cJ4g3RxX@eizLPL_{{#c(mE9@QP*|MRI*Y?8mGPg!(psmuldM*ds|We58?ybct`>zI?JzA1?VN8v+=W0{8_voR)vJku zd&sxRGdp4#o8e@hi;~cuomF06n5Vkro}CHv7W>dZ)Tg4QYv>z+;meX%f{1Kvf%h(`k2ye z(T>~6CLtFhL=8-*n%r&A$(+{x=mSuy?MA*vZ=xR~^w>tngw1Hv!9p*5`F(=}}^JF0&ktq1abhF8U-#O5_s@@)bE#v(o1I0A!9zk>ZExw-YXcw|&`J0F7 zLJxQS-&f{>D$bdyx=TD68~Tp|CRm#^$e@yW;zr+!c#nvDV1gHcae`^UwMp|D8k}j( z`8|Azif#)18JST~XlYJ$p>Dk;&I5tBf%>k>%=)Uu;b`Ei@G`97c&x@Q08(6p!7JQN zn?sY(jty_wA8i2$qSh@`iQ8`1(QUs6$vp0CkPA*-y@n+pWl(-IUPRJ)sI#4cX-25rG!xsz zmySq>wfI2poWF0nw9!roCo544D2#FR@BRk6`PeLd$Q^H1z!be%3^PNyOVj8Wgwi$ca1W}MN&#?X!s=bHIKoTET}9e5mcB>M0X&O{aD zx-}{7eSaVS_nY0gc>;6dvShKJ@)tK>9TH684ElA7*RlVx5J5$5uDL2zPSX$w(#kZ7 z&)+{0M{M0Sc_i!%AF(IHY+xNS`^Jm2R6V>>c7uNBj}O!4M<3Qn8tIHT*t56^es_ll zz@6DP%pR1scNs+rx2jdOPJ{txQDqQ_Iq6Wgm9uSybMml!Y*TOqA_Rku$nxl#49&$* zcKLS>f(vsYRf5n8dc>WOqq|yLU~-lFYoVf07I+?UG-grHh1g(A-u!9e%(18mE7qn2n@#~h8~K*>j{KHHZwm@G@B&^!G3d}ls_4(4HXTW_^&7;$n2AUot@BP* z=3Oul73DE!b!5<$ii_Qmflg~%a)Ok2?|0(gQT+^8&o1EoE0?+$&sNr@0508Y`?SCm zpkt4_3%OX;*3f7na0s+BzP_{Snp`!Y1?u_!{$~Bxo z;mAs9%)ynT(JHHbX)lwrMvavT_0-?hGq!JsYyadg!1|t>Lt?74;W68kQ_euz9ii6Z+;V{kaX!%p z^Xmy8X%s&&PPEOyc%V-;2Qi7vE+gpjJeDFpZ?SY z412*xxx^5g>_l2YJ54)|54%2itTmuG0*RaM5Wm0%hm1Qad8T>AmldDBnzeEbJLXkBPw1 z4hNw>Ic?sRy=q;7nv;&2j)T60;=^;}LJZi1d zM;bXE5CmTBkiLip6d;9Ky;?h4YcVtRFQl6V{Bok+H4I2E^_9#rE=+Du~~OBV)@dI}V*%)u~l>WF%CaBY0l$%08y zqMc90>;6G5 z8A$Y60GH+(*?j^7plS-Ye|wAt++!9rY7}R9zSNbuM?-Pcx;#jQYY?Bk5t9jBaK|7v zrbLoNsoxnmZ`IpaXurv{y*ACRl)|_C1RK~eBoznJcgAX}YdD(w#PZeM{E%kZ1qRH# z06}ZiIqi~yNB*5$O7q-O%=lZMgt~bv8QjmG6v@82|9C&QHN65f`}_Slxx^tg_6F~d zGpvPRu|#n!twDabbj;K(f(=4vhm8~~lh-Z#$@`LnRs3<<2ZCP#xwHpO5vMeM+Rj-@ zvz&l`VISI(AHh`}?kG6`aZ<8B@bah&G)oJwNQENVl?va#!FAbXryt$(I??OIid}YK zc6RUfV=2cLjXg5#Ve6&A_V9K1s~!j#NfW@P5p#uVOb=$!dA4zPLrblN6M{+*gi+yDUq30Cc$BTd3CTCbh!X~5#1JKiln zedb5_`t(Ow1&OLlZnQ+&At5Q3$l-xp6yrc`>rW*17}f?r&;9>SGVMi3r1@JI_EM`* z`GR>*l`ecmP`3Dc?O|Y73Pnz+`A(3U5;v zV1V1H80j)zaL-|+pG1~&IUXI28jnP{*6j2b2@zCPouFjWFx&HX)6`aeJG3=QvK3@3 zM$ey@MZU$idFYE#TPohL!iHf(9%{07am5L52|f&d{^%nOxJLld-u{@45|Z`M;&Jbmlda5 z9)x52=~cpj)MDsGmV{#k0lXEVt1yaIlix@Onu#gX7?G4_PXADf*3dKp>9s=_kpN8i z!jN060HQAEOt5BBw#Qmv6#uetvq!~NIAQuN_$@>L72dLgIl%JQE?XBDx-XGVv^SZUEUGq%fU7-sC#Kba&-SaxuWZWQSIV+)a z1h$_`*W*7w8VkzQE0@^mwF&nTmNvyR$O~2(qIFG2MG*u~3Lx+TjAz*VR^4s$;Zz#G z({Y)}ZwVr!e)WF3p~LnYxY1fc&X98%T1kh+Fo_w45r+orEJaG3i;Eg#5rF@!0Q|-= zM317=sY0gP@-(3Bd{`p!ymz51enM@wRP&ErC4Pm3>XDFfkWp2v4meGY--Es@Dbr!* z3mgQs>xEr8ftChay13~q>4YNsX8-PfkMCQkFh(p4&qVjC4jgA=U|IF*>o}C06gFZMkaeA1=s{B57t8kp(wd5yYr60~uS%^cpaqn+tpPhqe56(AXHd zW(v)%$!?$sq>%$A*+)~a?^W;cQ(XYF_cY`efx>lieCt8sVrQ^IEU7FHY`L84vj zBATXs-|OmK!OtawC_;=&W!es3Im7ZETq^jHZsS%-kvP3|OG+7`#`@dN1;-9H*{GAZ z!PIzKRTV>%!~5M528HF|Fsb~tA^3v3@u;^9ul~C8`f}jR+rr4wSJIv z)~?N{m=)@OACKF?^Z?fx-F8UIc55Q-7K9f!EA_cb+Lm_ZCY&`W4Q zY0d;CTQmVcwa8pCNxVzZeNZ>T!`aTl1&ija(bG+sWS~BGkt!Fg{CoyuAF5H)fAvkB zpj?bupvi$Fnr`T9`;EU09D8+C`?MO2G1k%8rV$QkMrpz;{XHe$v$GlHr4pQN!7ww1 zkZoYI>tHFYO3o=Jg0@ga^eZW)!g5?vOeC&2B=AL=-?q&iS+)%1=^j3eex8W*N2JT5 z0GP8!wrEIdw#TmwD1Xj^PKhscy^b#g!Ev4T7twM!ehowu6}HWCS{x)J<7>#USn2Q6 z9^>fiB|Y#J>vcP0pNN?nM>c&p?_i;0IKOh6D2ou0R&Mk}P-|l(;==;2JE5GOZLO+@ zWcTh28OuRORY1DJDN}{Fop$R-jsNF|=9Qa9iQ2MbS3&sRMR6HvxE4k7XZFK9_@{69 zdWHlefm`-)1yDupUc!Ri6Q!lFu-9UT+$Y$(JF0Pph?+t*u&2b?mpuvxkK3Eaz6!&( z1XqgKr-|*}wh$%#EM51zz3bDe)a+iE6H@Ak4kFQKG>|llZG|u92`jW1#Zu`zy4rRc z@mS7Pw}dj@%=u1~#u6aJz0LGVz;oaWNgDNeFEh}{IlQO9ltj=V3h*xgACtz&HWqw^ z0#4PPTkT>KT~?5qK2HwWHme(>;2y;mjN@xhuslXaE>yjXNJ)7+a%)=ry6BFGq_V2i z*2mqC&!+A{@4O$vq z$bk6PFhtJJSJ}?`RnQQDZDy;#)fR#ZnE7}^Zv?HH7)gULZ`P5g4l`5DWf-^nz5!i;+ zIPX3=DJ)d%nikFSa*NHG>~!zo(zNnzwno07!-l)64?C!n=?a5_?~Tqiy-@1N>$=@; z)hL(#N?`I=6yw81xsj{JBeg!T=%w}?jlthaxfe&ld=hn z{g>L!BQ29IIg`MkW&l~VF*gv`zKopGqVqnrkEIP;KD=AK6{#Z3!6$K~cpMh5{&2Uj zbC5C=_KD6|OBDR4YZ+kJj9Ma2AjsMm8_uy|t@YJP z3QM|&=C;b+-dk<>qi5SOTLulrOT&CjW7hFkURshe`unc0esqy-x4)1qz-rIm^`( zq1|aRHLKX`98N*JfJ1ql+&=(@b`<{XJ~8o!ODQe^7isj>7AVEI@k6z2~uc|aka$*+a;UPYHVAscQ7aN zE#bf=$hhRwz^QC0o{z2ViYQIsM$O(OmCKaw3ua8EnKhIZn*yr(E~TABiE!X1K?!gJ z%>yJ3fKo^x+rnCfay}TpZA!xE!%X5J#zZs4*>6dT25h~W^vfL5D5MS_$TDIb^Rfkn zOryn8L{9BN%9;Kb>Yr~!bdTyKwzu`^h>CX?#KcWNi{S+X;PCs;6u|(&;#F;}l{7*X zJx*3bM4VUeYN&7CyG1>ZtussH;U!H5FVr3yVTrQ}L;t_`K;6XUdk9fKn?O zfvC*7vtRu)Eg#W;S+e?^dnNNwtR+SBxWdGu57_eCAUkt>TTk;xGC8@1v^)b0C}?ni zDSVu(-YyEhjrn>Y{8!am)P~vFILXjHWx`qkVWR@q;C1`5^nvaNpG9X;8?u3*19KuY zKEDihCucL@d#y;xt2L3M-~I_(eMZwXDLZw=SOmi?^XEz-I>w^En$Y=^b-;ekqN7R> zju}b~1Gz;$64XlKfxik_Jw6)~9d10pXG%Fnn^yzL0Arz?uU!p&eTCUJlMGBS<$nWM zTvfTML-&4c2Vz1;Ekr-+7b=Mqq?6*NHEIBnA0RBQi>aY|uTxs!7FI1GbzJ?}Vy5 zv3#Bo@Yo`>B*oZoox<}DO3$f7l?u$gT zVVnL_g&!*}$$_4x^dpL&w@IiW=Z;pKuW-?-L?T)+lI@n-n8fB?NfnH`VU45NjwjBY z<_&;t1hJQXI8}E&{Ca>U)oxjQGoN$g6f=k_%&)=KXnfG?0aa?G=?xJ2P8PFm89-aC zagKUmunf3i%kwHl6_tJP6_inoK0V`p)0+~qngVx2>74NYf&>9s0QQ5kWD0;x=dqWc z5vq+qG|jqoya_tPFs~Xzn3nqt*3IWW$irpqWx8lwKAs{2AT+$5L`DTLulANSAocfI zVYxSg8w=2+gj?1X^ef`P_oQ{WssD`HihIpYu;Jv9>A|VDbfn_?0!~X}ssfYw8MnY= z9vQ*yD1i*1-MMvg9;whFFxmjUL#}3IL6;<^5hdL48Kh0#%)cb^I|S~Sa(6BHswN@! z%b3ZbExD@5K-)DQf%LejGz58SjwA|K)iFI9h(o&?j%0~qj%|aF9 z8I9FMoRM2wUn9EPDrbbGKetj=1PUEV?SVD5pnutocMp=3bcaJVH$IV{$kV%{Evq{v*B41a5Dt z+@K0td@y|!p&&-80e>6ZFnuerjN8=z67>gpd~5FxP#+ga>Wdfb+yN7((q`4umeGca z=gVP7>p9C|!C|}F&VcV@Ya{=}r-(tXeO(qB#*-TrSwEIdzi@n|lo(1N&ilIVLtOe> z+dVIXkC}N;spVtU6+Q&RIQd>O){>)W^t@k9z0 z9#yR55OiK_WzR2CGP;fch-VHiLm0xGO*R}luvgJb?*unbY>m2Qu+jf>@l?mk@#%Kt z-$&~#DE~eyc@wq9JlF9{Ni1<#z^tbfCw}(y$I-*Hsa@GoWcc zHc2PQAX-#5VKSoei;ZKs&G%WeuYm{y;y*XqMi1cYgB=MMnKTOL>VTU?qJYR@`C3@QrTc#lzTRp!5NSL z2AZgkv#BqiC3Dp1Vsh(->GIG6-L@6qW+(G1yaww-qjZ&VfNK7)(@*cxBF9qLPv8);M&1s+zar zC;OB(xBqHn{|$oiME%LU6n#}Yl6KWC8d=L)niP`))U5<;(#e)GiK=wlteiEa%0-m~ zM!RT8EM6et%HXo_-RAiL>`ZKvNP_G!>v;mxi2EP^TWXt8L{Qv%J#?9ZNuc~h{CEUh zMJ1*XI(OqA@<`&@wF3y=s^+r&r9;4-Rc_%^Vp#?Qta|yL+iapJ70D`5!G$MmfAFpJ zX}A#W($R5XJtRJ^tvuKh(|L&nXExjp598PTKewo@arfDPn1LYp7Kc8Tdo{)mMm;VcGGIX z+vzid=-H4~L6@GKI@sZYemwm-QHVvvL>UhjNE>x6%q5h|l3xr+TV;>-K}Bo8SA5+d zVf@P&9+`kPwxYNP{xaqv6JRPLO+Yxly zwIw?GOaNyJ%6RwC++x{CNSn45j#_%EdtmoHA(|GH_*8oitS(IDVU_1xH`P;W-%puh z;%P&XCUslu+HO=2pbdViZ(G`6U_I(P!4>)^fEjdq?Fi1{N~yYB%>jxnDiSJaPeA>M zd3+r3%Rjq1bM3M(*5bs+rUrrLCMInz#K6DG9L!BAE1u0bY^P4my+@|Bhv0T%+)ImQ zZc7+o5GK!qE&vH{`(=&y2@;^mfN9kp;L2N5G4|+P$*UU=?SWuTPpzg7K_Ex`@xDgF zzx5ipxh`*-Bba%8*e8KJNaZ)|8vwKFLAP)coEESO7rx22PX7kD@e-r>Qn0X`iA2M zIh>t&M+%GPMz5ehW{VnA`iz3VnLkw7DS>QObLQHArz!3ncY}cE`9yo+JH?rt7K{|L zBj=j1U_Nz3GtsgWmBZ;;%}p4u`4}U19?Ev}Bc^R6u8@5kEtM54G#w@4!D+&97V3@N z$5%aH_o0Y3eCzFP4gPKMJ5rseKPPnwy^Lc(-PxOCZSA)K?&@G#euQuwSJUodTT&;n zs|K6Q{6fbr=3oG)VTBYpOyuZmRg*i)o4y5LSe%yeTSW2t%)O5Hdb5&aDUMgNnje@z!7GPMkywj@-!bNG9)t*UiM3=8fp4Jz4YsHcfn1x;;Vw1 z`conqN6=c1A}QE#>CE7w(u#V4|A6t8d&Iz<4IFm8ObYzHcjAsl@vU>APub{A(l?mt z*LfdcWlBB4pu~tz1%OWFU&*vC4-o8TJdNb3FcuvD@DT`llf3^#r5{_@J;kE98D|$8 zZD3!Gy4eYR3m=5+VuUF7eo*SY;P7PkR112{EPQLPo42%#Xoy~T!@-|Mxq4Fi@>h~f z$tfKQcH^WZZ-XEcf>xtb`y>;`e1q{vjKtRcVoa^YA`qL0kEh{OJ!*OfR3Kp~-q4SIVB z8KLy;5<`Sp_CaIy?*I1>@Gtwa+TJb7bp>5!vBMcYG0S~Nr7oXsIpjN(0P~5gkw6Ur z;UWcgUZ?DN*$K;qc+S0$xeH|xc~m?dh{~044uX}%{{0d^8oE~SsQ-|q@mwfIj?10X z;;R5;Y*Ae{tB-|xN94I^Cg+447@5a#@}AEiiJt*IbSrgrPT*h6on-chZa8LRai4La z|519>R*TfJjkp-k+klPuhgTSzA>OM|_u0*5C#eai)EbB5HpxzKNdZu!Tfwcw7g8Q!dxH-t6&R&}g>}+GhhL*WEpQcRX6xT! zrzfWZ;SWx$_O5anuP2&Y>N#;OExXVJ{9JB12_2<5oPG*x1ehC`@)+4g5O-0$c3;C* z0pN`jsO65t7pFV@ay4p0N_mpuu;uuRRPv+%BI^9y0HLUuTtTSz*D7=l0%rB#mc_j2 z6f%M6^GL)Bc|!pae8i=__W*DB3Pn}6Q%|yP=I5Z8<|U1S+PiqXtEawsJ;ubq5si`B zg%}HSpKtDLUkbBqDe$)zv{S%rRUL^w;k=@ZaMnyf96UW@9$yEvng1%j%G&j=;V7DR)7f?G{%VDZx5OgEOP@1u$~plyV#P#H z;^U{%H(W{GqE0P|$>Ku8vwoh}$^2#E<)bYg?WWdVZ?RHcpX5#Xh~=={Pu#3%F+VjN zkphUKUHhD(^IKvWLBtFkz5#OQ>WkK7ZL98+c*I4RzYg@`vC3K##ysMcLEK0S3U(h5 z{!0J$n-4Wq*#m_jo`s=BM<9tz2HjKCu^gSHu1>J^E3 zU=>rczlWpbH#g2NCzA$}9p|>aPzPone(Z zw9->NS~!y*RDtMPv(u323kC~smj#8yvc;a((+W|s`@YVu>^E#5Ww(TXYwFKD&+=3u zM@?Jtf-X4VEBu@5k&QHT=8mIY7{N1jKvv-dsVSbDv?Uaeor#g1Y9?U5v`6X8E(c~3 z=*EfKbOfpuIGJDhddfkW>$FR62m&nU zl8i-S$J>7Gct3a$P>%`0*~)wC5Z56qC~^74{9Vf7!Q+^EP%r7%xmsmT!XI=*%&#r- zZ(jvjqD>wTUbX9op7@qauEeV8wt3f2(|Rj@4T*YwZ2J4RegDa31#-d>#)!cu(rqwFE{M0V@z3Ok8`b253>nbLYmDo`rIGCTTUFV_fdmv z%ey92J$QCVf9zf*ElEEI+j%;O)r+4CH@!{GnVI^bA{jiolo9R@_p*-}T7V9;@f|X^ zHo2K;=j!oGxX|N+BAhYG$LXK|zF{>TVGSFnyAcVyXI=$CE8BtBB5uZ$M!GnNt*N z*zIH7DVxchmI0KKTF2q);5!j-E2elKuXz_@i1RU=_vCVe;BQ&lAhW);OMS}d$^}O8 zXu|5)F8H>?s)q^kaRg!3xumw0vM%9KLn);X7msI#XBLoT8fh>V9iVD1{8zFu?)C! z%l(Mp%E04Ey6b7;(b=UT-IJvFFcRP}L!NXc#VZ{vu(AD`8*_{>3*xe9N()}C%VF!i zP~1#))ZWIE$_G(yuD_#jZjA=j%l{spS3aDu(U^Kf+WDxGCf3}IUD5n79yYN93kgks z3%(?O$=_IFl^Xw8x23eJ$t0v6>&u zXirh1-Af4uKtv%pyI-hn<3!%CP22M0ipb&O|z@peH2i4IG1rm>xxpkrr*{nYN>U_1@#wbC&zsZIyWe_cY4mZ5lM1kJJav<_Uk!mp;xC5v36Bc`ap63D@9I$?&0hAOt6rf zKdRCB{ktKEAJ3jw+ijKU{MeGJ>b90E)tA>>fhyZ<&5~dakAix%s`@Fai<8uJ7QsXm zZF``2NB^^(CS`gueX31a#05=WRHX)m`)OtUInJJxe{)ye4_0lUgs8qw^Q87=pD0?G zxZted@5j`!U0(oie)E*ZmBI{<_gViF`ZegPMPANeh)S4JP&DW7+>ks03MyfW`R%2+ z5KVimc@^D_^Q(8aPwMob8zaBUb<{&Tr0q<}`a#$vJB6ndYv98qUO4@4@aK7cftnn$ zZMo^-+f1~SJ7eH}Hm8c0-vEV`T`IgI#;DL5^o6b;J5=e@fxkvEcmZl$3?Z@;_iOd@ zgGNI1IjADP9s$k$I#M5%d}ZIf1K_p@ff9TdTtI6Bv0%+?->CK8=;H2>h!(I)~kQ|Ht$U{$FG}ai}5T z82=G|1x2ADYm1ONt8EXB)Y$zIq0XGa^5(pOKC6rCbQ6FcM0;2BJ^NZE*@7cV@?FYJ zC)whk15~QnIj6sgeDV0JC3fsaH~c!Smpub$3NW;*`RtJ`WCBm|4u%9^q8b`BY0ndD z(#(zzO!Rpp35ScFs3@RAL_KeD?)uJ`aVPo`IR^-ZS_%r=p#zW>w#tKyIkhz5_xaSN z;5-%x`lpG3TUe5vVTj*BtYu^B680n0}Rks`bnk0a-0jHWHTz;;>k@e46 zk`!2Sg*;{GstR>-iq|{Ri8xWNuMxi7qG8sBO5P4r1|c*pq)tlhIHE;XA8;1Sc2-=f zPo3CVuD|zXqzhPMrwm_35r=Qlrq|$deJ_&9OGV+pKKeV1*;qo`l(Z~}c|l!!N@?#k z5%fXLJ;GBdqC*Kr>R_yUBZ1^07k3#@`D7l$yoO92NIq(;cY+;EP;5YZk?1v{0tn0z zl8|%XXmk2sZp6y*Nv@o7ZaePqoHk~YJ<_I{iw68m64i8$<9A!Sz1@Cd3tJNabsULV z-l+A&G=yEPoyX5dpScHG%&D_;9#n_fM=N_@|R@J&U(_SLd9rcA8%%hQ@5{j7A!g~4Y<<{#XrK%lmS;Vqi zx?eNqXT7{ka;z$Cprj5+NBRACATUUcIC-N4;ecVSj(QBqk-9VMrru?_z=+DB_U)~D zdDf83ycoZ^pNo$3PSHWNSh$n+@X+oUGw%0lEkIyM8@$Ar*vl!!@b7)0KcWI1fSW|FL9V^>5#~c`X`URNmxZ#P-@(92jn?vtMs7v@uO_?>Y$?B9l)82+IKX^iLmOE~#f_`aYsT8}4HFM5&0}S!u82ak-*R4ic&xVFv63{K6S5gqHJNFxE#JD;AlE0;LNoP{cOt}d+S;E!^2 z4^^lC$4qoD@Bn~mPULa^`Y?Nf`f&7*ls}l_b74*$wTdQBPZ8FAw+@aCc0%X@NyYQ5@!CC;hJ zK`f#JNx#0vFqs(|ZYSX2*f30GtfaU#MTGsT6-1lGa2-8@>iW$;RGrlQ;UEGP!ExpV z{?T0K-26v>P9*3?ap2|Y-lmQD3|E&z5lyT98q)@I2(%gYXTHJ%ZLfkwp zk!SvROkf}-g~>d|f9=mEGLO#7r$PiDRUL;pf_(BsQYDu$wNm~2r~7sQ7to7e)#`=n znXa!`VVQ*uS%s$ly8hJE6N67$XSm>C$|35Y6><5@tDPt67`uR$dR0yuVoD%uC+apF zhmIU>Nq~KEE7&NFKoFv4&qP(7^d*90L1^nFo(JESbom#Z;V&{?KDnT?KK~g#B^kj& z(idQ%Ff8`M*NnD8k|+f#L`)YN^X~|E^g+ zh8lkwr{>347%cC&%N=B<>nvh=~m6)Va69am0T`FOflQ6DNZ~!$rCeE?MtAZgxAMQYQpXBO_7N-;6DSVurjvK* z|0)$0u?{L$!f29GR&c7Gunj&pm~S`h2b5_3TPrC3E0#K4Up+w)t6fMLoB=c>?m@!J zfA2^}K4q1vK+57m@AWcH*BFZZYybO>$t;89VEy?xc+TUAbjtayq>W4vdpD$ZA?jx= zS2nc~g$q!RJ8eSoiiAh7_Qjsdq61QBfXworXW;=%goJFDa7~sQTFLs9E~sODtmA5# z1Pkct4B8b9{fUFHz;9I;PK>fzI`{TNyCr$2o$Oh#RyZ=vq7?e*{B3c9qtR(g@uXnA zE$ZHk?{RS4u7p`B0kn=&SiK>TCYf${sijisB&TiJB2=?7p@lXYIkGR1^xEaR0eMQv z86VxVOaeK+Su^V*L9(&nCp3sOuZ)P8tLlY;F^XA{40{Bof3#w;{^)rvUk%HKNc&J2 z{GR21%iOi&XbTeG+Ja~{JDkw1wg@rFa0sM7bXyofbRa-I-fvs(FdRY~;*iFrdqQ-f zDEc%d;j(tmnDe zqOZ{aCV|V$lE?@mh&%iyKS@_}T8NY@o)Byi{qF9oX{Dyk&y4D+OU^YJVo-3{NSvR# zb5^MU5J=;3`~|r@%^qkHQ?fbK9X5OUi$IiS%s41swpvaGu5hKxw)5$K`B5)-DeUqF zBK`|_N>tt-lyV0_!>N&mbTTVud79%_xsolhNyGPCXnIg3h+=^h8>qCbE)qNJAf847jM?8DAOXU=Nq_`>3;IMUP6P`G${wW z$({`sWuQ@QEjBYv_cY15izS30oB-m{=uPyMtF!WPyw2vg+9Oc7+k6CR;mNZ4{^yN= zQ%l2Q5ZYx>VBFW&fSLSpN>W@f({k@F);fVTkfX4^RjwM1_p1z{HrUEunM)lw0K{zO zmRk_%c)4dDjly~c+}>=W^A64Kr0uGCVEJr51}g3oeaUU10GCR8-KL;&U{ayhbj`7r zgc=>$&6OE6-ar6MdV%i#e7TT|zVnb^=rukFFD{V_6Vc^dTft^h0!oBkAowP#s> zZe=>gSI71a)NSy9Qcyf20MSv{hO?i+tA&&K&@b+ajLV!ki!TQ8ljK_KjjYCda{bA- ze^(!$Vws)Ydr22oSDHauTTTej^;n1lxPvJy` zGbJaQZwT#uo@s%WrOx4>TWyD#d&#NB4(v^iM~PNf5>|(bT#2VF-G*NwXy&`Azo&f- zeD+^OD4P`z=S9h_&(n}k(W?3vx;je6vk@10(H&pO-_00w;7ygo<;Oi+GzfVa@kktV z1M?OOXk0(-(Y3}V4J(B5=kZ>j@M|chY<Vkm4@0)(oNkj_00A|F4vL3R#rTe-6j{Fn14 zlAd1jhNo=iRL$=t;8@x7`KD9}?c+-3^`&3LKSTM~LNNeuY2xLevU@!H?< zOY22cby$zQUxKWt(;81uj)f_8mM2rNbM7=qS*o%#tz|?eLlcoB6>C`i{rV%4EM~JT zy-~kNcCH-%=kQ$}1~){IfcDM5$K$N?UV~%2RtJfd0!B`W-9rvl1<_PK`p`+7WxA)j z-k+mc;Bkw(M8>Wux6talp|7xVZG+!atrfKvpE#fJYw`7~MhD+89JbbCNPsr$3|@G! zavDae2q6-@BG?zw$4xmw@i=D~K^qSKFPWRz4|GVlt^NyLtwANy$j;m?T4jUzg^Qq} z+nl1&l1-0}!CB*yA^=`pLKbrnS(Q2IP5mrYB_{Wi@AJ?6Luu+&3d+5{&i!K&C|A&0~|TK7Hfww1H4(y&2Mqp0F2Y zq7p)gN9v~WNwBl(m-mISSy%OaY-v_rK;l(pFlk7uCrHx|7VD_s>po0#Xp}ks72}Tu zS!G1fJo85uub-HbD5pGZW)YnRczzQ7U(uG@F0KlP!??9*c7XYsPgGSf1r^XxZuOC9 zcIcjZeGwLa^FTqNyim=+uco`1>s__gas=noTgY)ol)+}i45G<#MaXof1t~Ydzpqv8 zUE*Fkk;{p<%*9XCD zU{l4n%wHLheZYcmd1v1W0LU`hnPBRK9R&C99C;RSZ;_ke+raYhGe?SD4kv2SoPbBC zOCFn2j3(HFIOFR3@UPD`*vs51)sF~7yK_4BGDyUDD%~?NmFQ|HV>B%hjZ4bEFlZqE ztSY3@M;DNLY5S$;I&RT~yL8i*NjPechiVJ19K#1G=|kh)Ejf2PM)BC-+x))IMB%K5 zbe)s3OQFpoij-W0q*7G$QTITJUFHC)ofL|zgjO#f;M1*(+Q2>oikH@5Iz9r0Qh)j| z`&~KA7;nwN>^^rWnT^>eRLzc?5xS2${Nf{Je<2~Zba#`J3k)#Vp~8w_$WQi#{1XJICM{1&aJqV z<%f%~y!M}capr{z!;JA+(BcNxhd?&aC(CxX`zGy6E^*Ybuv2P(u^fIuvoET&xaz@!?+*2(Na< z4ITPuA*5K-l{pjU({Kf5wcu^E2fk{rXZC$ml1G)Qr521jxGrM$elblQ&7)qnINI#} zkO5(jJAl3IBpGEC4EcXix9}0z_Y8lTgrGh?>WrKrd-JM$dGYyc+A} zk~&ij(1ie)Fr^uX7$StBU1VfC`I<{Lsne7@x)^XqnTnp-c!l^f8g{^eV!G&v=T&b` zuymgpBS{dRAvL-1t6>vbP}A30#CRz}-}n%3CJ~544~r^xc?@^$)Inz$IY(2B+qI|D zx;Kq?r@v+Tg`%+IoCl;NbEW!q7 z%IKHZkre7&5cVb#8h~y;Z3?OOGn(LgZF|u?^4zhZz=<(omOKUM-m*U>=U#ll7<+@0 zDnAMK+3*&3F%xD$Sw=($9dnSNh;fps;RzVBn~rt!mF5X)MsRiN3m8F*!o9743sKK7 z9!6!LFIolTLiYoxKUt~UU0w;gb+K|d&vRT0#Ck{DT+17BNX%rq4;z3zb(pNle@A-a zq@f`=!K#4(M7dvpXEDWlTk}MPHAgJ2%kPw!>VpnAKY5o$ABV%mFpQB~rHjP#AXhgQ zJ0lOrn8M&yFUZ+5c?0ngz{#%@Kntti3i(h<6$8SlD9tvD=l3tIIPA3ER7G0h#mhyl z&LuoUO=$cp0hYk!G?P zf%+#&Ypcn&}6jV&@uuRnu4f>_kf`F@*eYcc4Ku?|c?BsJ)umCgGvAu^wTk z-LL)nZlDq;8GJ7xqxxEd|2x*Z2dR%|%AVC}6AqmrZv&P|U;&}GQoAu;0%&IHEF4Xf zLC4S-pH|1P32=?i-xSF;GOu5|#f^B^)tLwWA~_lqEe@wNzViAY>oOqb2!Hni&5WUX zWBCwE;Q%Q$h>EotwD6P=h9s|=O@RFgdhRRYZCtgu#)JWoK!hE*7}iR%CV)95&eaUZ zPAy8j15F-slT}7gKm1ACtDh@46K93t9j?^)9-?3L0cIY*(Ep%6*qjZq>!F3_$V3nDsE@?sYM9JxH6M1@H z5OHh`Q_#=A$`m&IOGL}AhfQ6aX5HHhD0CIKA%k*&CTNA=eR#iGUnT;&YZo?+g{@-N z>21+>H{Y5AkP_ z1+chwnDB^TNLU3vh1-D4eyqf#sTRAKSZXTvQ~nGmdy1Pnxh@3uk%~;RD!bpE?jaPA zoIdH$q7}dJUuips(eg(EjQ5hyeaxYuV~xHeG?oJs{z?1R+UJYj?u5C&D$R!_5w-kZ z<#=PoMsg(0jF+ig*Z{+x6)wV zDgR!Q){lEn!@pgw@A^-rYnYH#EfKnR#sF@N60x|n?LJ4V!`z9-Q>bYvoa2`(@KS?B zN)?{Umqw%4E`pCR@2Z1=A@UCG1}zmh1JMfB06lgMp#l&kIy2SCB+0N0y8e}f(@_Gq{BXsc``lQ6#+OS#4SiQ~a*5w0>*XB`2r z`)%fje*!gkU}EOa(>>fct|u(UeL^hMNYToh3an-t*dK8Nn#q_K_LO@6i~o_L)8V&* z#28CDoRK8PH-*W_%w*z2-@>@MNc~623O3_w6+FW-y)jpDj*BhN;^CW?i6YT7l)-Sv{hSOS0pwon zs7CN?UsL5BAx~d;>J;~_hHE^@Gr;I=Hm?iP&Q%1Z*F9s<%Qrh9fv=F-lg>@EeFafg zT?!>4ePZ`J0BLa*!3?AuDV8C}{1fDCQu$~0N(!=V1mYzQrw){xc<&1aED-=Hrl-@L znW%lngjRR(m#D@GWHTt>^Ii(6PTQhn_+gKiX70vfZ4?fd#{C`$?QYkIjS#O8DB`CI4~!fiZ%h+Eyx9CH7}Atph4I5{aT*f(H(TL9E%2ku?fCLe7xd zb0zttGfYIVqKP67n3Ehcd~0w7o7>@ff@){)_~_2kvq3V=DS+^MtlFv@@l%XL0iQSt z-SSF`zMyGj#PzgQCJKn<8A{o6c)SMj7+2h-xBCP(+RBm>S&l&>7~Y?a+^9id*Ar;R zukkNOWja{RH3#-DzrwwIEY8%F6NyzD9vnjO6{}F=Z_3=AurHrcApkhIelqvXkrP;B zXMX-zCVPO1NunVsG3jg~LSgsN*Nx|zrYrHFYna%CA=7}K zV|^CKyePy5L023(KWeh!J3z6(Ubi>~XRi>zLRT9htSQULQ9U(DFRd5KIXV1Hq^sK) z(o9t<)fWae^B`kO)Zc=HV4Tg^drmNQmRpj;ebio&e3Rd_7q-$bnE0?0L)tL&nM4(e z8>3R_JHgOEa8_mj#^*$R7@S}$U5)S-0v=GXt&UYTV6&(_xG6bRg{lQ0Qib;b(-WI~ ztOzU`sVm^)%B0*u!&F;E1v>qF8rx6!pyMh zDj_i5B9VWGx)1#K@2k?oeN5q^4-nOXy?VJ+`RQNP68}kaKE?$=JqQ@GN0mTo%)2?Z zyod+Tt^9+!cfTE`EgXfmW_*M29s1LwFhHa)*|jeBHRN9qSzM(_`X~=0untF*?G@FL zl2Ce~>%=8*_fixD5`Z%HedusB;O!5J*$hO>-REZ(ew5%U0Y1y`?NwrBh7b;r)5ADG zCS~be7atzEIgB~f8Rd891Ux)iYIW5j5)FUfG7hriM4a$RzPXCoAj#Z1i#$9MgHM6R z6uT5x+{D75OW@U6ZcWB}U@-vu-SyT^-HFz$P|OGUpg^UVsliu8?}xH&uzUZ;pz3Ej zqs#$9i(^xPquHT>(PUo2TAm^k(|2&^kRFXVJw8SX3s|i_5@(2dZb~moVPVl=^n3EA z{XJbzaY6sqAb@TlfPCUPx53{Oxo_%OwYw25sna>UO0eZwT$@RW-)9nPlwnb{)8^F( zvIe^QMhR#{SU4lvWnkREP&AMTm7?@$p)+*8@km&#)zsT8zU9n;a5sYOw~u)Y2+za<`8@a$=e~R+IVVzYc92HDf;vvNCs4@{gWd;38vw; zQZq`Fs3GV?GAqg9XV%!DE4Ehezx082%&|nq##mda3n-Q$fVzU#QrW_XBlM+oBc^5d zyOPm2i9DD$-Cp{}j4lWLZZ0=YM5{B4EC1|6=27wvGg0e^lA)@oaIX>O+&8W!p$T)% zuEW?@0H|L@CZG4??*3}i=Rk`!ZIis+z7sm}--RkMUEaK;&5v&8FZ<3qRVL|^`^noy zyslFM7KQwHDq1b*v2o2gHQ1}h@1*2q!smNHy3euf)2D4iAkCv6;heCf@@xaP(UGiL z|Ho{iqtwp-GGQ(gb&Vd1g{Ua(e0iU3a-N}Tm}iH=*$LGfI9{B?zauS&Gu2d^cL0FM zM7bDWPKH_9K|WO*&iTC(&}KqQ7G|P%dTn2)r1cdPza?p6r#D~uM5D$!#oeE0{}}vT zk-i;r0_<&aH>D;quxbO)<)-%u{PWw8vnH)zcKu(sRPkw|{)y>ni)ni`QP5q!YR7>grXkqd+Kue?8;P0%4EMrGZs% z9)v($pozk|Hs_UEc6Olu9)te;oj>#5FvjjR-Zif_W87B-1nfYc7u#_1`&-OC5hwO_ zw<>4{T!c_>gV$iW&0zAWCXFb(XA3;U;D6HF>HxO*Z<-Ec9%La*v#Oa|IkfPZYlU>@ zzgR%yB*5YV%%4-KG8NcM{|WLFcC{u0i_#)Dx1H9KL0Ae%_J$SW!(*P(=U^-oftyh% z{gtKU3RSowb=sYxX2uYhKx-gewiZ;;usO~;XNF3FGk1H%(};4ByK=*C_q!cWCx3-M zs!ZdB2%*E)X2Yt70KFNuZph^@_3CetSMLM3OOGs^G2|4@_&jIH>2U026ED@j&V%Ms zeXU;P+VB{P!bA+1evE(-;tf5WKi`BtJMUrO= z26;Ac-N6N^POnTojHVVz9>7LVDk<$*i*3$dHO(q{iS(y(hf+4qgyZ@GOwuX zR;xdu!^9ylN(YXy5`;+STs99SCPc@xR=))>~5>eaaI~@ zF^jFnRp+-SfIX6xJfoy^jTE0`v1mwV3t0!@=XOk3e$fu8gO~ew(tRsZ@PJDl)CAlAB~8`YMcA>`%yb@7i}GD*6l4k{0&~MT zvkCa&&iYHt0b302o!mJRrSU=?(DVYXjd;xur{`>RW|Jm8@Wm-)*G9d>YHf@P6-7}- z89{b-z1aevu4%fQeFtv~@_HA7;VMfe*FikloL7jtWu5Bg zj-y*YRG+6YYztE>%OAr4vCMck0)kSRqh|DU5FM6L)LN8wL?)wsTNN->xqhjucRekC zy8B39>;#}z*CpsqING12iGA4-_Q6n>HF}MoB&>{)gdS#`vAZC0CDSwYw_d*2s$tbA z(`7`=kTC$3BO%66`o6;X+shPDWl0|q2v)Ta_RhozyZJ$!+UDdd()O-wRXC`lM_o8Q z-;O9s!L;FbXIjX1S;)Yuge=9tpa=7nOxoc^ajBJpGG18uMa>dsS?c*5@F)5^G}-)8 z7977904Q)(ORG06g1Ni$Wl`6KlgA_>$7fkj9jNinT6Q z3F+Qfg9Eoq586oE66%i;q|Pp3p*olSw?++}{fL73S%& zPsDz$PT+FV8et?Qay1o(0?L>PHk;kBf6r@hKn!B0-JutFQ23ET_>J=<&~%meUzmp& zh$m`FV))L#BjdzF&~`+TzD`JwvS^{-Ar?N z0@@MsR57UizXk+GF)#)5dv3J1jk*J{YZJxSG)IAr74<0 z)YAs%-36QhKgQ0*%NKYS^6F|ZZ)9G$HKZqbr@-$6bmOw5FZOiN_eVI*?Ne)Ab-P}~ zHqaN*arsAYqG52i1y*pBl%Q|sB9XC0vYU^fCX>Zb(}HUrEAAbJS&U+(r`BM~45dqC z=IG>1{68N88^`yH%i=9{snsnZP4lNGzw~kpB7|Joze3# zWNoGhy43HYIL9$8?09}FFe^lRrM~Y@QADdW6g)vcW036bS2yS#AXvZ~`G-N+o^g@v zoriEjC&+qI`L4oaxTpIySSJHVi+;$N>8M)=ni||mID0|Rrit*@_jk^gX9REbs9PB8 z`@*TTqK$`h7oqR!3I{N6=AkqYlPtXGW+MYWKvj#`el70ifF zJf#bjuKz4V#~DLa2VnpXY3X&%(!pDezF$pZoO3rA zF3#tgVx3#W3iY+nd9(xiZI=|JB;giuPw&RCSJ%`1fU^?b) zgR4r(cLw>}T4k2UrQFCLfmWhXANgb1abL%mCoRRlUuw6r+mr;piOG}F@F*s@vng;t z>|@iuW4DRU4h)g!KX#N0KD<`W8mHq1`;V%JDi?qi1U;G=`d~t8rjaQ=Xs6bY7kxCG z>i+lQEx}e>_M6xIR*cuNkSe^PT!y7g*=QZJSDIgyNV!MpSkacS4;DxG9X)viNqmd} zE)j<9od|^n)@>afCg3RcDy5GdmDt~DcOW6CY+%!%_g?mxwf=A_^uKNTEcf1Sy$|$q zE7A7D`jSvm8a;AN%DY#H96E%6QQ(cyB2i#Ao&1qLW{PqcnNjuFZ z+{l~DM7-s+lM-Fq!zYC9`ynqB&+7#rWkjyK8wX%8KcM-X8Ue+>_&=I}0;i|!7e8?7!9W8}UpUSk0ztO^{TK7ZDc;MTlXhEy+A3-Cj6(Z-`L^fOQ5nPlKx zIc#swguqWHMycQjl142vzKXC4R9&_l>tB}65-f+LT#KErs%q-+5>NIvrWmF_rNbPp z0UH>swti>e>i$HDPd#qLG&>dKP+{&?nxnnBve=kSYQ9qb0U9zsFP_pUkX`M{uDN7X!=AG|1k@JP^uwSf^wf zT+e@YClmF2&BuC}{t;$gDq3YZd6n8D5uEp8lWSa4YkX^AY%KfB3XFk?of2f7>fbpj_ z(S*%1zSh*tQarc}>=)312xxPw?bM^HL#=2C^oED+u`$wS?Qg#VDKXPV=bHGL*o$^r zQNE~tL;&eL9eeGSM(>m;QM5U))l8-vqd)L;CD2wOc%$y%tFgIEA9JqnRuotuLoi@W?wn6=#IfoY;9)Jkp*?3{uADM>@7LbWd zLH`;}hDc8|VR|g$Bq*nOm{C_ghFOkmv?R}P>Sj(;e34J{@y4{|9-OfwqGdw2yPFd&m-Uk^# zQy3Wq9pZ+bIi-{LtB-Wo;}K8fInb{Dpr!q>_>IkSE{6c8iuQ~1&Rz51006r20s%n6 z0sO=K)FA~<@6I^NAsi8JD1CTjIY7~ZIob=ClK~5g@qxj>0|s?xQt0*i0L2!~bVtkr zxG$}5-{oK+>YRYN#UTQMMLp{B#L<#F`IPvJgSh}ptzl8Ql=vW*Q-#181gH504N^Cb zLPYG0*lI$X`m=n2cC5B!TLS%x9$2ZI-4B~xMSS#tGFC%tc_dDR&>7R{sV_u%$1>Lq z2P}9q2Bvj8%u8g4a(E{Rg^xE&(dXK9iCV)%=Ozo+?F`L!R`fzDhvWr-= zOL4F6^Xw%AHuL5bdLJo%m?fNR!P6Z*Ltw4lQC7`W{0@4!QF^Dphg;MPTA&;@kEKV^ z8f^K}17N(x&RfmCg{7cYp8Lt^PR%)Kbc*Ltm+gMpiyP|Oh+$Q+H|QvL9D9# z|1MK^3klP>?4NR|3M_WbZf*Kx2!~T(lFZ~@z>kVI{9$3R$%3A#;IfM9PjoE0PBrcL z9jc_{^j#(IFC3^|m&(_i+^=LEem$!df*`RtrKht*#4_AUt?_~~{=Q>}^YhC|uzD&q z^tbYCI4I}RS2FLAS_%Am3F(|(g~xo{Nj&-fDHOn>oPHDu{`9KDMt=K5$5?}&c5(j7 zesHKN%1uZ}lO5XHo9qC^i3`|;$B#U<-7=!BSVkghS{A1RN1H0_GQ65tvF@3o6^yZ+ z1pt%Kc+YFL*n2(^yID}343TW)Q!1g0{|Z!sRqLcU z^}2UiWb#nAahD?KhB)F} ztHLZ}|Dx6GiJqGSZ%S+0%gkfCxF0=bH^I+&H5O}cw*J~VvwZ8R7B+&@a`d_a_J)=t zX>TtSk`S=)kGG1i2=x=W!x0ng@G8E-W?dcF`NQHT(yP|I>rIEijewZ$RPh0Z>EjA` zC%8Vk3xdMyb6vT`CfSY~MmkH*ZdYM}GKEN-zu2518(C))__nY%J@RY=TJ^tq*cd+5 z1<_^(#^h{ITIB<9v{m4D1t*B}j%nEc5+Egf*?x(8bq-OMwnVgQy2wp|^|oDto?Tb) zNLe%KbJS?+2xP1fT}h3)hu18>m+*o}X1JDzlA|Zb3JXcf4dZyVVI33JSM@gM@N(LH zb|=(6&;g_XCplbZ+75RHbDq*UcUH|xlrxgoijh!cpqIqEMG8#1GFc@Mw?HoUmQsbU zaMspXT-g<(cgD(QvC@7Oo+?L-vJJqnq8FixZ%Xlh@5lU$)Ju%Q-R~VBz$;F4udf*}skwiWW87K! zG&3lrXYtsciv}09ph-%LbE#s<>N4%DiW^WYnAe|+P@)$K@ysD1k+8JreMNP)f;AfT zj#`4seu@-tnzM9P0iNp4W9Wy*$<E8ejxm7nD}N zVN+=Gi!jT+acdn{l)*@%6Cmr0Hd++n#(1Q2-hFrv08Q}p z$wFxgD~~+vazu=9_Pp=e`-cWOHNqW*N2i^S0xFl5vg1v3@@zUJ_gol0C6K3uF5so@ z0N%)>cv88!|JRn$G%4b{4DmxN0!#2%mRa9J;F1hyhf1cPuZxOH8DVND@EF12~}6Pgc15iQc(#<%zbS=IY7xm>A8tMbZviJIyj zjc6SA{w^I~3gt0VSRBC>FMhcpm)@n2jx%W!ifB&H_v9@(g>Y|s_xj`RKmxsL2#Dw< zW*qc(Y-KsCnsp$;FK8Y6w@Uxch`Eki`AjfCe${wv7G8^13)w$E za{z}XW7NyPeB-2?j^`dZuOIoV`e5v0Y{_4T`Ko!Cde`TB)l(Y%fAo>`PunNj$ZLK7 zsPJ;7K?B4JC_)lUSB8|TM<)Dpsq-N6J ztR9$^FU7KcbpO|CWNu4&E%D+H;5K3b>n4er306g%p7m4N29jphppbSYd!*23)8k!^=ikdbwni3pe*ES8(R z2q%igjHpr&6fBTb!7BkN)Rx+bz9QQ8#5LG9W-ccVcdk5vZFhB0^PqGZ)2*nl;RP`P zv;5yaW5jnG`^&Zgv}rq4_9m%*l|^k^fW&52q*u73NF1B$ z)i-uPxO47_1KiIM{ehTuY;=(9*dSF`6hM37UfTYzJwhTY_TL(nyPxc9BvIq6_}j6f zRsUH2++6P3C|(KM>^d;C_=0Fxl=zCCyvK2qf?+Bjnzm0yXJ2YxS0-N9}o0j<3}p&IB_B$ z7qqx*->~jY{ckh4Yf5qpS73H($7%!N2O{go*U5m4vgLX2Pvl}kenwYO^5M_%btrGd z+?O1uEVC$+Z(qVZ`&qCZofQ2WBhBAAS}+-XZ=fMs{LiXV$nFcXgg{=FbL@|UCa>yO!poHJpVDg#+Yp;-k&0IQ52)Rei* zUbpFj$|nFBm^#36u%AD)uAI8pP3Y%!O~5kAAN=<2+=om|D#YU|u!UF|g^J;HJ-1{$ z7ItIxx4HaC`79R_e*84#=6CH+q%fi;{ey8j3h|4Y)00YtMgAmkA%vg;Cg@g3Hc|<~ zuip&J=VcHx*fk&5bk%xSF&gSsc00v8E!dBjZE~^OL}LZ^jN19aV)9%R-7<Y4ojmQA`6_;@ac71X0DfSSgNE!Ua&J0w=>Yn^O7)(`a4@s!7=E|ttu~x zolApy4ev(F(r>NBF%r{&H@=vIA$LV*#S`l*FyHxyw2yk}^VM7#h}lNuPptZ)41;ZV zNXR@4lLOmXNijXUap;_{muJgR`}oaI)9$nbMjo)r|An+mu&pENTAT855YEYXr=7$% z>+Y+s5UVH}i+plmiyGIblo-L(G+CHJu`yPXOsCQ1ZxNTF3~e5ROj{QsUr=GQRN4t0j}60x{f z`!mE-a2Xfr9=z*LxAb=`0Is|aCI9j)>cDZc4EW<3bC;=nqfGW=-^g}%iohzHg{vlA znRESIL22gb3Q1AT61!-Hwrn%gYXR?%btO&k_zAP7NKTPe<`$UdARz6tlQB}-B20H2wAZ+sm?JLx?QEV9l zL9<&a^;3@K!t8*Bzg}}w9BNBKd5QkY^aPlnVJxmapNZ-j#l|K!bk3b^Q|x?5G#(SK zvX~_G@tc#r8yGyc)MyBTzInUqu`^~Gn{&_h`FO?|tjM)WR`{}kC$;w?wi=)2FdR}) z9mtoKp8Pp$S4;ARq#h8VKSxqQqvj?#tW}2A(=kA{3zHpbGNhYlAAO;8;)um@%nOrI zq!(L77J)kV&4KK|L|b?OhNVPs4AVU}MF6ZMqM+$Gt@Mkyf=$4I1~OR+iUX6atAc7r z$OR!PVx!%pN%IG|UvYp@P93)JHlb<{j)IWeZWW=khDv@RJGE}Uy5`0)8VUC2(Rb>^ z33;@EHS>(Fw6u=hUOe>U(mawyXSfhPayyx-W4Z1P7kWH#k~o6+I8>A%Ttgtu4amB1 zV9End#D^1}%(vuHWqa?7mSxE#HH5Pa6e-C$1rIvE};EWhhu#aO*Y zF;l>}Ez1g@@MzQJKXp|QZJpYWfXh#&&LW4Q0(c=*uZ`-GHK%xsW=dIR#V2X|w+=Cu zaBkc?JyVPRl$hv6A?Zp(rzYCEJ|_az44zj}u|IJcv(yllW%qkET zdY{FRFTm7{i;$G?J^Zlus5Gn%oYxh`err0?9&91ITZx&^Ycasq)1|h2kac9Qe@yMv zCdY!co7fB98j%*rqH ztD%pdJ=#){2Nk}QyqE)#7GM}??x$v80DwZP7i1x}8?09Gj6XWj>djBeu~XwqNEGD) zL#X|zKN?n#gw4gZl%o##1708d5ZdO6;hR)9kT?<90GAV9Ksqj{N5T2`r}QBbyOfv@ zD!{eFj{gn$d;`KAk(7S>tk;M~uR}^-o_#34Eqinf*cht7^Er*V3H5XsmB}a3k%QSS zJ=BI}#hvXQhWw;!XHnCRD}}~D=PTtHAuS(er_KDk6+O0v(&VY?$Y6**d^vi?0)|g%X%w3fk{%yt(e@z}*b`G03iXj_TnO@;AeM zNYKm6o-0zEbL0l`+$7eG(SFPJmZ9aX>{_FiNQq8i-ktnepWOrNvB={2j2vcq!WE!F z;nx!H=?RP;IMJCy8%!?>{dpebiRDh9R`%)!r3prO>H_Wg5bq~gXGEf%k4eoq8ikbS zOxzX}Sh%YwGNhoNaHyb+{*wVH!$bvv6wEJ>2@KD3#Rc$ldM)8S1=4Q?=|%X75m@!< zn#<1k#fSC$XonXh-Y+=b3U8Lg)Zei{BJH-hmWckfjRD&=Jd2PGGU`hUSKaz1zgCLj zF@en)@`IWOt8D+YHtT=&c|=Keq~664xPm z*bDxBBiYxEC{M#ZF#S||Y4`{^U&Z!n zGli88`^BiS39d^-N$c?xT&R*I9S3F0==1bsQ^7is=vYsf5X@}%Fa?xrkMf04wqCH*yGUos$Qmpo z*YD=ylqt&f+Wq*NfbEa3{-kOkG{}aHo>7^g{V}S7ednd}E!S-N69<=^#3%6*GN5`? z!Z7~f7+R)lN5!^MmvT>0@z0jdufIX|Ee}HGG7)a>-IE-Xy{{bI$sWZ1t`eYY`Z%Aq zmF4>Q#y7{dZ>6;Xy;UKZmFWeLX%N7+snf_JHOi&`%+b{3{k#kS@)NMO6;02I;=}Q( z`a*BEq1A%^#qM{h(+31@;Uh&0hAjEZ81-&h4jvV7b8$$H;f#jjHA%j#lS0hwK! zUQ}OsDEx!t$alUB+xkU>Gdi5v-&RWv?P*Ud`bg)r1sLCOr&8icAmI*3Jkok?Z4RF5 zw1GlIg*_-Dd@F1!f5B<^(9j~-6i6tUnZy?nDrNpf--LBeJNPd*cp=JUCJj`N6hXTl z7@M!i2lM9vB7Lzfc!#!{fFj4RVunnig8jR`a0n$=IXK{i{z_Y6CZ)1>&Ic}c9;%pz zft$%8UO;}7s1|C}?9nS!F`0eDO_)0eDm>H+dla>>A$wo8PDBiIk0ni`*_|uf#%3_< z%rx?PQD6}mb0!3JipMAe93^QBK#93y4^m@3z;2RvXD|hmbS3q_tEu%Z58hPwEZMOs z_OWt*;2B>kcPc-KCHJ`w1SRsaAe|lmM639r!1r6KFL#X@!>1e^0WBABm|rq>patug zb4eVdxae29J^Xif;`Q#%o1~Q$Lpf?3To2<(;pZ`UUJ#ZLv(LDsiwKdZmFRP`MSBsL z{9M~)M^|@TCxkXBJEz|yHTdLKD7h7K5M0zJJVN&NG5I~-7CyrI2dkf9+wd*#ur!>h z#!~Z{mNd0DW%&q%Bn%Nz+a zGdFq*&*+1U_JtTwxj2bZ8i+gtF~_||KQg{8!JqjBXr1HVR<*TLSF_=0qd3R@rrg(e z==Ub#>G$>UNAEp<>kNDmItgGLM376v3<8l@lj6}%ln2Y8t_B4u3# zqIrnJ=*;8?zK2v(1j5$xzmD0#Aj1{Ww^-DaId{h)$f*#SW@H)VZf6bVF=flVmf>bC z+I;{@rMoRypp&9yHo?zTHCK03fsgH#ik~EfQT-{DC1T_~jH<^DBfWoO4eq(yc1y_b z5N8%fq-jg^Yv_gYjze1U7MyrTq{NHun6yWu{DhPxH`7Wu|wpZ zTyaq}YJvd*8U_FcaEbmigL-6Kr8vNwH;J`-;#L4;botZ>JIA7z(~7(h&Q23GOMe%v zRzkzB$uF%*#)ILmfU`28jaI;5ctQ`%s_oJkPr9RE_vgH1XWc9fSmaZD!@fUr+nJ73 z1gH{x_L`N~KSDDo*>s$}yu^~56pu$QI{4r}a_s6h8KeQPy|hx*@$#>U3L%!w#>w#< z$711Pw=~T|Ku&R%Epd+a#L*};2o6z$y_s5>yeWDv>EnOHI1r~Ft`k174JsI%KSas7_ zX|#Hn(P&F)SG>F6#RX%W+-%*n{<_Iuk3N>i!7=c{3fr*IcK)&3#f|@B*NuW3gh|5zPb$_hDj%)FW#2~UQyQIKbSm9 z1ap{X-%VbkO1lMa(E9Iq))e3U4|P8>VQNu6`h04eu_1lwq$3RpBUf%k6Fyv2DR@KQ z7|?dTMkYNQE`d~sL^jqV=kJI(u-1!wKiDf__6~^uNlykh9!*W`(EI(aC)x=F0X0U$ zm>s@}MW;Q{rQ1ZrOk0Uavg3E~b?JtAYhH>Netz*Q(u#iobTJfPyL!OKm^5}xgY;hvIt1uK@4Z8m;0i zBfb9caGsBW#;9rhKcS>!!ybxfM2CTG+f1uzD*Qa? z{mo{JLjCvcoIc!{t6NnZsf^A9r+(EsK_fXdl#~* zuWN6>Oo6jBi@M|e#|ln{5UrQ(tn>eax?KTZz(&mz4AG2@_M_ zftHfP;K#np+yKdnL^4`H(7Fz|(Sx|!eMtcLb@?LQF#K=PI3FC*ZNi>Hl#6rLPFrQA z|1QZ6!S9d$wgKBG|4qYe)Vk{ZC2|mZCJ+nnM?%q?%NV=rt6X>3zi_(_a$qnTr+?QY z-L>GDQCx(lGLM5}D-8t%idGZFR`BSfpKg=!4JU=(!Rx$uk1I&tzXqu)L}fF0PDG8U z<%JM#L6>oUVIj+yv2y)QQgSq zgs26KM$?@x%mzmc5vL>jk%I1kPl>9G!p5?-L4+M*BJrTZ;E?L%{y4P8`^v&{uwt4-^%JXbl zD|k`PzLNPgoGqNaG^Sgz>in@Bl8A9%=>4|TskaqZWX-o%2gj?l)ONv_Y6imki3u2d zKl`^vRV+|gHB!TOxVT%=fIj2}An&JK?-~)&A5SkZ%o3icj^X!Df_1nFI6XatFh#k2 z60qsn`4S-NGtX4z@wRVlzz+UXCQgPGetTj%=h?quk%jDF|%4x*+nq_00pCeW(w<&q$}JB@;#$|y=D$05$qf8u;}E{2H}(vTTFeUTVd4& z=2~pz-m*`ig{<(qXPCkg5CuKzT$Til-G0GO;<;T(GEyZjM@^SUz41;m=>IRoHv09! zWT*Dobbmvh6F+LhCkPs@=~z`JHBOs<&6orCX#gh$75ALv;_rokY~o53$=wa|#mw(E znmA=gUNMp=yCD6|-Nez$4FgPH+wj~xLs2&vs$Ot3$Dm2W=9%d7rvzks zKH$B;Z0C&fwHZKs=^hre3G_&bFoK^i|N2mOrZ6IZ1UmyD&k__DQ`_@hxh0>DVfUFH znyzyj>1R!)FjWi_I6fWWqg##I76dP=K=9eP=aYgtT0vSDq)?ZqDI&ych%@| zxtA|R$$O091bYWqSc~MR;cMq#p4=tnR!CsaQx_tS#IX$D&95T}*}vx=xRco4-EbCu zdT$uXfzc^g)lv2KS`)Uqp^BOnaYEP*(D|ST2jG(JxHPvU6lAC7NTqD z7kA=&{ZGs$Itdj&6a5IDW3ThkLJCC(>A;lDZy6G70i__6mtqtaW8VPMx!s?w)W$TK zcgtb2Q&@$*&=+Fr`x4* zq3GtvxLV7{X3$A`F2Y||Ou_CeEOo^J0( z*gZ;j{W2=eq?6)%K#Yi37A zLNCI>0p5(wQ>)UU;(4_c#@r+R@8!$B)q51s2?>jsZcI-Twi$L|>8HgHwlI#}=KX!F zf6|#`|Mjm?N7!fi2}O+80J$ILlL)htz@om}2Vj{Ai}bdbcDR_y!st7WKSB3~=!U)v z0>PKCFcRad=LH6l^^TRv zY~_$i8j9Hi>L_AE{1q@bnp~hNwzbC)C#U-XTkSU3w9^WcIey8-RX%c`u@S7epX?kl z5GiK-u#w6Xp0!o6gR0RG*9u1D=kaeRHwwd{MV|Xnl6lXp_yy4!`CaAp$nD~)s8LG0Pyht_=uL@F@-u@d(QX6 zZie)cYWY%wUSu-0{YU-!cOtep)4TA^P^uUQNdG+4uG6+2U#`2eqX5SScFY<@h078En^c1tDxW^zpSnun!|7 z*7km1k|oQn;hK%{>+DByaU+&b|50-s`dr&ZB!a)J9($cU~8w4c>=BdxTCO0dF1!PMaG}mI7}~o zvOG0scB7cEWw{d&yOFcbw6Z9eww#m(j3yNiHKP?Ts%iAXUengUp6IxuTdD$Gwe6T+ zT7C8U@QtL;7hZO)^5P(NUttwKDsde=p&^uLHOE{+t<|~SV{ptz-4WwY=q7=}o_vJL za7J^4Se>ZE3j>Xs9Gme=(Z$H{F0!KKX3#%gsL8m7=NTj3G$`pkBQ z6XIlGbCgbe&E_6$FMfhu`TYmxhtMRG8k(J`fI>nL=U-1Rnllyx9w?Hk&F5Z-B+28e zzADxr_-{tl!<}h(6JQu?z;0$xEM?0>3mq~j`FA=qUTL7Z>t^xQM^=q- z3%##);*rIysxOTCm9i~R@*UgkgV5-nBArZfu`pAnxGkdn*c2#TGf;}n`R1R8SycPN zJB&_Uwu4MZqnf%(Uiy3vL9<(T|N6nU3#W~**RTsc(sgk0ki=ODmdm9ITt*568|sVYJws5c5H4m%$bs0A{dhs^=f{o64%|8!%)v@1@@Y_T zS!^cWfiI^nHtHur%WA)zgQI?H1LVf7eH|{SIv{xfWS&hFcsjWjdlcE~7CXWiPyo&- z2(by0TX9=Y_%Jeob|Ga6J09V(M8+t??m$cTl^0j=2<^pxrccfE|&FSbGWo?T`B?KdP(OVSZ zNo8&nHsIWe9>3gAeh;3cTpwLQASoz+6tzNa#gyop$8natx^=4#OA_c^4r^omhAMUXv;)1|Z`UZ6dsMKvon!MKDw@&gb%lwMJ`?oprmIin`qZ~!_2+7&Xe?;4< z>EE%qr?y6Xh(f;SnzJjA;aFB)TuZ&BJ`5Y+Z8xAG0vZ~lf;NZipi{$UDqM^?C;qz5 zimb(BO7ODR4xl)WM6ne{WPT+M#zya{WAWl+(0OC5W0&-zK@w0ZrlQR zx7h0gZ01x;05TykqDF@h1QSc2Nt?~pUV(YRTL_A}oJ|I*YyuMX=bk(cOKp*=ZDmhF zL?|7i!Z`0L-1%%4%rYTm)jp4*&cn}C!KxX!J9Yj+uH7ckAk%tQc?3vnO7~bSF&x#}%9$pUvi!2XFpupw89^g| z6M=8GCtxi+T`JWEDYXfYM{$gJ%vLæXL3+0MGE(u0MnOL--=O+&TIVokfe`De^ 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 z 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 literal 0 HcmV?d00001 diff --git a/libraries/transformer/build.gradle b/libraries/transformer/build.gradle index 7bb9aaca8a8..66662035b04 100644 --- a/libraries/transformer/build.gradle +++ b/libraries/transformer/build.gradle @@ -39,6 +39,7 @@ dependencies { implementation project(modulePrefix + 'lib-datasource') implementation project(modulePrefix + 'lib-exoplayer') implementation project(modulePrefix + 'lib-effect') + implementation project(modulePrefix + 'lib-muxer') compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java index ababf582026..016f93483ae 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java @@ -15,6 +15,7 @@ */ package androidx.media3.transformer; +import static androidx.media3.common.MimeTypes.VIDEO_AV1; import static androidx.media3.common.MimeTypes.VIDEO_H264; import static androidx.media3.common.MimeTypes.VIDEO_H265; import static androidx.media3.common.util.Assertions.checkNotNull; @@ -74,6 +75,15 @@ public final class AndroidTestUtil { .setCodecs("avc1.64001F") .build(); + public static final String MP4_ASSET_AV1_VIDEO_URI_STRING = "asset:///media/mp4/sample_av1.mp4"; + public static final Format MP4_ASSET_AV1_VIDEO_FORMAT = + new Format.Builder() + .setSampleMimeType(VIDEO_AV1) + .setWidth(1080) + .setHeight(720) + .setFrameRate(30.0f) + .build(); + public static final String MP4_ASSET_WITH_INCREASING_TIMESTAMPS_URI_STRING = "asset:///media/mp4/sample_with_increasing_timestamps.mp4"; public static final Format MP4_ASSET_WITH_INCREASING_TIMESTAMPS_FORMAT = diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerWithInAppMuxerEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerWithInAppMuxerEndToEndTest.java new file mode 100644 index 00000000000..bd013cdb901 --- /dev/null +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerWithInAppMuxerEndToEndTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.transformer; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.net.Uri; +import androidx.media3.common.Effect; +import androidx.media3.common.MediaItem; +import androidx.media3.common.audio.ChannelMixingAudioProcessor; +import androidx.media3.common.audio.ChannelMixingMatrix; +import androidx.media3.effect.RgbFilter; +import androidx.test.core.app.ApplicationProvider; +import com.google.common.collect.ImmutableList; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +/** End-to-end instrumentation test for {@link Transformer} with {@link InAppMuxer}. */ +@RunWith(Parameterized.class) +public class TransformerWithInAppMuxerEndToEndTest { + private static final String MP4_FILE_ASSET_DIRECTORY = "asset:///media/mp4/"; + private static final String H264_MP4 = "sample.mp4"; + private static final String H265_MP4 = "h265_with_metadata_track.mp4"; + + @Parameters(name = "{0}") + public static ImmutableList mediaFiles() { + return ImmutableList.of(H264_MP4, H265_MP4); + } + + @Parameter public @MonotonicNonNull String inputFile; + + private final Context context = ApplicationProvider.getApplicationContext(); + + @Test + public void videoEditing_completesSuccessfully() throws Exception { + String testId = "videoEditing_completesSuccessfully"; + Transformer transformer = + new Transformer.Builder(context) + .setMuxerFactory( + new InAppMuxer.Factory(DefaultMuxer.Factory.DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS)) + .build(); + ImmutableList videoEffects = ImmutableList.of(RgbFilter.createGrayscaleFilter()); + MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_FILE_ASSET_DIRECTORY + inputFile)); + EditedMediaItem editedMediaItem = + new EditedMediaItem.Builder(mediaItem) + .setEffects(new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects)) + .build(); + + ExportTestResult result = + new TransformerAndroidTestRunner.Builder(context, transformer) + .build() + .run(testId, editedMediaItem); + + assertThat(result.exportResult.exportException).isNull(); + } + + @Test + public void audioEditing_completesSuccessfully() throws Exception { + String testId = "audioEditing_completesSuccessfully"; + Transformer transformer = + new Transformer.Builder(context) + .setMuxerFactory( + new InAppMuxer.Factory(DefaultMuxer.Factory.DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS)) + .build(); + ChannelMixingAudioProcessor channelMixingAudioProcessor = new ChannelMixingAudioProcessor(); + channelMixingAudioProcessor.putChannelMixingMatrix( + ChannelMixingMatrix.create(/* inputChannelCount= */ 1, /* outputChannelCount= */ 2)); + MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_FILE_ASSET_DIRECTORY + H264_MP4)); + EditedMediaItem editedMediaItem = + new EditedMediaItem.Builder(mediaItem) + .setEffects( + new Effects( + ImmutableList.of(channelMixingAudioProcessor), + /* videoEffects= */ ImmutableList.of())) + .build(); + + ExportTestResult result = + new TransformerAndroidTestRunner.Builder(context, transformer) + .build() + .run(testId, editedMediaItem); + + assertThat(result.exportResult.exportException).isNull(); + } +} diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerWithInAppMuxerEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerWithInAppMuxerEndToEndTest.java new file mode 100644 index 00000000000..5a68a30f8f5 --- /dev/null +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerWithInAppMuxerEndToEndTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.transformer.mh; + +import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_AV1_VIDEO_FORMAT; +import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_AV1_VIDEO_URI_STRING; +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.net.Uri; +import androidx.media3.common.Effect; +import androidx.media3.common.MediaItem; +import androidx.media3.effect.RgbFilter; +import androidx.media3.transformer.AndroidTestUtil; +import androidx.media3.transformer.DefaultMuxer; +import androidx.media3.transformer.EditedMediaItem; +import androidx.media3.transformer.Effects; +import androidx.media3.transformer.ExportTestResult; +import androidx.media3.transformer.InAppMuxer; +import androidx.media3.transformer.Transformer; +import androidx.media3.transformer.TransformerAndroidTestRunner; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** End-to-end instrumentation test for {@link Transformer} with {@link InAppMuxer}. */ +@RunWith(AndroidJUnit4.class) +public class TransformerWithInAppMuxerEndToEndTest { + @Test + public void videoEditing_forAv1Video_completesSuccessfully() throws Exception { + String testId = "videoEditing_forAv1Video_completesSuccessfully"; + Context context = ApplicationProvider.getApplicationContext(); + if (AndroidTestUtil.skipAndLogIfFormatsUnsupported( + context, testId, /* inputFormat= */ MP4_ASSET_AV1_VIDEO_FORMAT, /* outputFormat= */ null)) { + return; + } + Transformer transformer = + new Transformer.Builder(context) + .setMuxerFactory( + new InAppMuxer.Factory(DefaultMuxer.Factory.DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS)) + .build(); + ImmutableList videoEffects = ImmutableList.of(RgbFilter.createGrayscaleFilter()); + MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_AV1_VIDEO_URI_STRING)); + EditedMediaItem editedMediaItem = + new EditedMediaItem.Builder(mediaItem) + .setEffects(new Effects(/* audioProcessors= */ ImmutableList.of(), videoEffects)) + .build(); + + ExportTestResult result = + new TransformerAndroidTestRunner.Builder(context, transformer) + .build() + .run(testId, editedMediaItem); + + assertThat(result.exportResult.exportException).isNull(); + } +} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java index 718813d1467..a78234984b9 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java @@ -162,7 +162,7 @@ public void writeSampleData( int offset = data.position(); int size = data.limit() - offset; - bufferInfo.set(offset, size, presentationTimeUs, getMediaMuxerFlags(flags)); + bufferInfo.set(offset, size, presentationTimeUs, TransformerUtil.getMediaCodecFlags(flags)); long lastSamplePresentationTimeUs = trackIndexToLastPresentationTimeUs.get(trackIndex); // writeSampleData blocks on old API versions, so check here to avoid calling the method. checkState( @@ -231,17 +231,6 @@ public long getMaxDelayBetweenSamplesMs() { return maxDelayBetweenSamplesMs; } - private static int getMediaMuxerFlags(@C.BufferFlags int flags) { - int mediaMuxerFlags = 0; - if ((flags & C.BUFFER_FLAG_KEY_FRAME) == C.BUFFER_FLAG_KEY_FRAME) { - mediaMuxerFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME; - } - if ((flags & C.BUFFER_FLAG_END_OF_STREAM) == C.BUFFER_FLAG_END_OF_STREAM) { - mediaMuxerFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; - } - return mediaMuxerFlags; - } - // Accesses MediaMuxer state via reflection to ensure that muxer resources can be released even // if stopping fails. @SuppressLint("PrivateApi") diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java new file mode 100644 index 00000000000..42e9f9118bc --- /dev/null +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java @@ -0,0 +1,146 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.transformer; + +import static androidx.media3.muxer.Mp4Muxer.SUPPORTED_AUDIO_SAMPLE_MIME_TYPES; +import static androidx.media3.muxer.Mp4Muxer.SUPPORTED_VIDEO_SAMPLE_MIME_TYPES; + +import android.media.MediaCodec.BufferInfo; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.Metadata; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.extractor.metadata.mp4.Mp4LocationData; +import androidx.media3.muxer.Mp4Muxer; +import androidx.media3.muxer.Mp4Muxer.TrackToken; +import com.google.common.collect.ImmutableList; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +/** {@link Muxer} implementation that uses a {@link Mp4Muxer}. */ +@UnstableApi +public final class InAppMuxer implements Muxer { + /** {@link Muxer.Factory} for {@link InAppMuxer}. */ + public static final class Factory implements Muxer.Factory { + private final long maxDelayBetweenSamplesMs; + + /** {@link Muxer.Factory} for {@link InAppMuxer}. */ + public Factory(long maxDelayBetweenSamplesMs) { + this.maxDelayBetweenSamplesMs = maxDelayBetweenSamplesMs; + } + + @Override + public InAppMuxer create(String path) throws MuxerException { + FileOutputStream outputStream; + try { + outputStream = new FileOutputStream(path); + } catch (FileNotFoundException e) { + throw new MuxerException("Error creating file output stream", e); + } + + Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(outputStream).build(); + return new InAppMuxer(mp4Muxer, maxDelayBetweenSamplesMs); + } + + @Override + public ImmutableList getSupportedSampleMimeTypes(@C.TrackType int trackType) { + if (trackType == C.TRACK_TYPE_VIDEO) { + return SUPPORTED_VIDEO_SAMPLE_MIME_TYPES; + } else if (trackType == C.TRACK_TYPE_AUDIO) { + return SUPPORTED_AUDIO_SAMPLE_MIME_TYPES; + } + return ImmutableList.of(); + } + } + + private final Mp4Muxer mp4Muxer; + private final long maxDelayBetweenSamplesMs; + private final List trackTokenList; + private final BufferInfo bufferInfo; + + private InAppMuxer(Mp4Muxer mp4Muxer, long maxDelayBetweenSamplesMs) { + this.mp4Muxer = mp4Muxer; + this.maxDelayBetweenSamplesMs = maxDelayBetweenSamplesMs; + trackTokenList = new ArrayList<>(); + bufferInfo = new BufferInfo(); + } + + @Override + public int addTrack(Format format) { + // Keep same sort key as no specific sort order is required. + TrackToken trackToken = mp4Muxer.addTrack(/* sortKey= */ 0, format); + trackTokenList.add(trackToken); + + if (MimeTypes.isVideo(format.sampleMimeType)) { + mp4Muxer.setOrientation(format.rotationDegrees); + } + + return trackTokenList.size() - 1; + } + + @Override + public void writeSampleData( + int trackIndex, ByteBuffer data, long presentationTimeUs, @C.BufferFlags int flags) + throws MuxerException { + + int size = data.remaining(); + bufferInfo.set( + data.position(), size, presentationTimeUs, TransformerUtil.getMediaCodecFlags(flags)); + + try { + mp4Muxer.writeSampleData(trackTokenList.get(trackIndex), data, bufferInfo); + } catch (IOException e) { + throw new MuxerException( + "Failed to write sample for trackIndex=" + + trackIndex + + ", presentationTimeUs=" + + presentationTimeUs + + ", size=" + + size, + e); + } + } + + @Override + public void addMetadata(Metadata metadata) { + for (int i = 0; i < metadata.length(); i++) { + Metadata.Entry entry = metadata.get(i); + if (entry instanceof Mp4LocationData) { + mp4Muxer.setLocation( + ((Mp4LocationData) entry).latitude, ((Mp4LocationData) entry).longitude); + } + } + } + + @Override + public void release(boolean forCancellation) throws MuxerException { + try { + mp4Muxer.close(); + } catch (IOException e) { + throw new MuxerException("Error closing muxer", e); + } + } + + @Override + public long getMaxDelayBetweenSamplesMs() { + return maxDelayBetweenSamplesMs; + } +} diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java index a2b85fd9a58..0431f83153a 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/TransformerUtil.java @@ -16,6 +16,7 @@ package androidx.media3.transformer; +import android.media.MediaCodec; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.Effect; @@ -84,4 +85,16 @@ public static boolean containsSlowMotionData(Format format) { } return false; } + + /** Returns {@link MediaCodec} flags corresponding to {@link C.BufferFlags}. */ + public static int getMediaCodecFlags(@C.BufferFlags int flags) { + int mediaCodecFlags = 0; + if ((flags & C.BUFFER_FLAG_KEY_FRAME) == C.BUFFER_FLAG_KEY_FRAME) { + mediaCodecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME; + } + if ((flags & C.BUFFER_FLAG_END_OF_STREAM) == C.BUFFER_FLAG_END_OF_STREAM) { + mediaCodecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; + } + return mediaCodecFlags; + } } From 80aac22cf934bad871e5b2b2395fc8e855ab647f Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Fri, 12 May 2023 15:07:44 +0100 Subject: [PATCH 002/516] Add test file with metadata track Mp4Muxer supports adding Metadata track. Added test file to cover this scenario. PiperOrigin-RevId: 531496409 (cherry picked from commit b3fd202e113949146f6155984665a411164da9e3) --- .../media3/muxer/Mp4MuxerEndToEndTest.java | 3 +- .../h265_with_metadata_track.mp4.dump | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 libraries/test_data/src/test/assets/muxerdumps/h265_with_metadata_track.mp4.dump diff --git a/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java b/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java index e12670ed2d0..438be392514 100644 --- a/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java +++ b/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java @@ -45,11 +45,12 @@ public class Mp4MuxerEndToEndTest { private static final String H264_MP4 = "sample.mp4"; private static final String H265_HDR10_MP4 = "hdr10-720p.mp4"; + private static final String H265_WITH_METADATA_TRACK_MP4 = "h265_with_metadata_track.mp4"; private static final String AV1_MP4 = "sample_av1.mp4"; @Parameters(name = "{0}") public static ImmutableList mediaSamples() { - return ImmutableList.of(H264_MP4, H265_HDR10_MP4, AV1_MP4); + return ImmutableList.of(H264_MP4, H265_HDR10_MP4, H265_WITH_METADATA_TRACK_MP4, AV1_MP4); } @Parameter public String inputFile; diff --git a/libraries/test_data/src/test/assets/muxerdumps/h265_with_metadata_track.mp4.dump b/libraries/test_data/src/test/assets/muxerdumps/h265_with_metadata_track.mp4.dump new file mode 100644 index 00000000000..e22e0b240b2 --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/h265_with_metadata_track.mp4.dump @@ -0,0 +1,85 @@ +seekMap: + isSeekable = true + duration = 66700 + getPosition(0) = [[timeUs=0, position=44]] + getPosition(1) = [[timeUs=0, position=44]] + getPosition(33350) = [[timeUs=0, position=44]] + getPosition(66700) = [[timeUs=0, position=44]] +numberOfTracks = 3 +track 0: + total output bytes = 387 + sample count = 3 + format 0: + id = 1 + sampleMimeType = application/meta + maxInputSize = 161 + sample 0: + time = 0 + flags = 1 + data = length 131, hash 37A90BF3 + sample 1: + time = 33344 + flags = 1 + data = length 125, hash 1F92C1BF + sample 2: + time = 66700 + flags = 536870913 + data = length 131, hash 47B06CA4 +track 1: + total output bytes = 1726 + sample count = 3 + format 0: + averageBitrate = 192000 + peakBitrate = 192000 + id = 2 + sampleMimeType = audio/mp4a-latm + codecs = mp4a.40.2 + maxInputSize = 627 + channelCount = 2 + sampleRate = 48000 + language = ``` + initializationData: + data = length 2, hash 560 + sample 0: + time = 0 + flags = 1 + data = length 597, hash 36BCB84D + sample 1: + time = 38041 + flags = 1 + data = length 571, hash FD73DCB0 + sample 2: + time = 59375 + flags = 536870913 + data = length 558, hash 6A62DD7F +track 2: + total output bytes = 678996 + sample count = 3 + format 0: + id = 3 + sampleMimeType = video/hevc + codecs = hvc1.1.6.L153.B0 + maxInputSize = 420494 + width = 1920 + height = 1080 + frameRate = 44.977512 + colorInfo: + colorSpace = 2 + colorRange = 1 + colorTransfer = 3 + hdrStaticInfo = length 0, hash 0 + initializationData: + data = length 85, hash 6F3CAA16 + sample 0: + time = 0 + flags = 1 + data = length 420464, hash 9C6E9B09 + sample 1: + time = 33344 + flags = 0 + data = length 131591, hash A1895540 + sample 2: + time = 66700 + flags = 536870912 + data = length 126941, hash 7B26BBA6 +tracksEnded = true From f15e5733ce8b4e91c95c1ba60c7cc32194bd2d64 Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Fri, 12 May 2023 15:35:37 +0100 Subject: [PATCH 003/516] Add container module This module will contain functionalities common to extractor and muxer. PiperOrigin-RevId: 531501602 (cherry picked from commit eb8ec87a5cbf26998ee17d9d6b1d4979ee0c28f6) --- core_settings.gradle | 3 ++ libraries/container/README.md | 25 ++++++++++ libraries/container/build.gradle | 50 +++++++++++++++++++ .../src/androidTest/AndroidManifest.xml | 34 +++++++++++++ .../container/src/main/AndroidManifest.xml | 19 +++++++ .../media3/container/package-info.java | 19 +++++++ .../container/src/test/AndroidManifest.xml | 19 +++++++ 7 files changed, 169 insertions(+) create mode 100644 libraries/container/README.md create mode 100644 libraries/container/build.gradle create mode 100644 libraries/container/src/androidTest/AndroidManifest.xml create mode 100644 libraries/container/src/main/AndroidManifest.xml create mode 100644 libraries/container/src/main/java/androidx/media3/container/package-info.java create mode 100644 libraries/container/src/test/AndroidManifest.xml diff --git a/core_settings.gradle b/core_settings.gradle index 0f0b1a4a2d0..e2935c9110b 100644 --- a/core_settings.gradle +++ b/core_settings.gradle @@ -26,6 +26,9 @@ rootProject.name = gradle.ext.androidxMediaProjectName include modulePrefix + 'lib-common' project(modulePrefix + 'lib-common').projectDir = new File(rootDir, 'libraries/common') +include modulePrefix + 'lib-container' +project(modulePrefix + 'lib-container').projectDir = new File(rootDir, 'libraries/container') + include modulePrefix + 'lib-session' project(modulePrefix + 'lib-session').projectDir = new File(rootDir, 'libraries/session') diff --git a/libraries/container/README.md b/libraries/container/README.md new file mode 100644 index 00000000000..0789dec444f --- /dev/null +++ b/libraries/container/README.md @@ -0,0 +1,25 @@ +# Container module + +Provides functionality for media containers. + +## Getting the module + +The easiest way to get the module is to add it as a gradle dependency: + +```gradle +implementation 'androidx.media3:media3-container:1.X.X' +``` + +where `1.X.X` is the version, which must match the version of the other media +modules being used. + +Alternatively, you can clone this GitHub project and depend on the module +locally. Instructions for doing this can be found in the [top level README][]. + +[top level README]: ../../README.md + +## Links + +* [Javadoc][] + +[Javadoc]: https://developer.android.com/reference/androidx/media3/container/package-summary diff --git a/libraries/container/build.gradle b/libraries/container/build.gradle new file mode 100644 index 00000000000..c6f77dad956 --- /dev/null +++ b/libraries/container/build.gradle @@ -0,0 +1,50 @@ +// Copyright 2023 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle" +android { + + defaultConfig { + // The following argument makes the Android Test Orchestrator run its + // "pm clear" command after each test invocation. This command ensures + // that the app's state is completely cleared between tests. + testInstrumentationRunnerArguments clearPackageData: 'true' + } + + buildTypes { + debug { + testCoverageEnabled = true + } + } + + sourceSets { + androidTest.assets.srcDir '../test_data/src/test/assets/' + test.assets.srcDir '../test_data/src/test/assets/' + } +} + +ext { + javadocTitle = 'Container module' +} + +dependencies { + implementation project(modulePrefix + 'lib-common') +} +apply from: '../../javadoc_library.gradle' + +ext { + releaseArtifactId = 'media3-container' + releaseName = 'Media3 Container module' +} +apply from: '../../publish.gradle' diff --git a/libraries/container/src/androidTest/AndroidManifest.xml b/libraries/container/src/androidTest/AndroidManifest.xml new file mode 100644 index 00000000000..f6d9eef2e06 --- /dev/null +++ b/libraries/container/src/androidTest/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/libraries/container/src/main/AndroidManifest.xml b/libraries/container/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..3b012e26f7d --- /dev/null +++ b/libraries/container/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/libraries/container/src/main/java/androidx/media3/container/package-info.java b/libraries/container/src/main/java/androidx/media3/container/package-info.java new file mode 100644 index 00000000000..2f988f9c8ab --- /dev/null +++ b/libraries/container/src/main/java/androidx/media3/container/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@NonNullApi +package androidx.media3.container; + +import androidx.media3.common.util.NonNullApi; diff --git a/libraries/container/src/test/AndroidManifest.xml b/libraries/container/src/test/AndroidManifest.xml new file mode 100644 index 00000000000..e812c9d4a03 --- /dev/null +++ b/libraries/container/src/test/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + From 932bed1f79d15ff550851ea9c33dc20f265129b8 Mon Sep 17 00:00:00 2001 From: bachinger Date: Fri, 12 May 2023 16:41:29 +0100 Subject: [PATCH 004/516] Implement SystemUI contract for media resumption When a `MediaButtonReceiver` is found in the manifest, the library can implement the contract of SystemUI to signal that the app wants a playback resumption notification to be displayed. And, vice versa, if no `MediaButtonReceiver` is in the manifest, the library will signal to not show the notification after the app has been terminated. #minor-release PiperOrigin-RevId: 531516023 (cherry picked from commit 9bf6b7ea2062435970cea132c295659b48508b4e) --- .../session/MediaLibrarySessionImpl.java | 43 +++++++++ .../media3/session/MediaSessionImpl.java | 4 + .../session/MediaSessionLegacyStub.java | 4 + .../MediaLibrarySessionCallbackTest.java | 87 +++++++++++++++++++ .../src/main/AndroidManifest.xml | 6 +- 5 files changed, 141 insertions(+), 3 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java index e8287f65c02..5fda9c6e82f 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java @@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Util.postOrRun; +import static androidx.media3.session.LibraryResult.RESULT_ERROR_NOT_SUPPORTED; import static androidx.media3.session.LibraryResult.RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED; import static androidx.media3.session.LibraryResult.RESULT_SUCCESS; import static androidx.media3.session.MediaConstants.ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT; @@ -33,6 +34,7 @@ import androidx.annotation.Nullable; import androidx.collection.ArrayMap; import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaMetadata; import androidx.media3.common.Player; import androidx.media3.common.util.BitmapLoader; import androidx.media3.common.util.Log; @@ -41,10 +43,12 @@ import androidx.media3.session.MediaSession.ControllerCb; import androidx.media3.session.MediaSession.ControllerInfo; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -52,12 +56,16 @@ /* package */ class MediaLibrarySessionImpl extends MediaSessionImpl { + private static final String RECENT_LIBRARY_ROOT_MEDIA_ID = "androidx.media3.session.recent.root"; + private static final String SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"; + private final MediaLibrarySession instance; private final MediaLibrarySession.Callback callback; @GuardedBy("lock") private final ArrayMap> subscriptions; + /** Creates an instance. */ public MediaLibrarySessionImpl( MediaLibrarySession instance, Context context, @@ -123,6 +131,24 @@ public void notifySearchResultChanged( public ListenableFuture> onGetLibraryRootOnHandler( ControllerInfo browser, @Nullable LibraryParams params) { + if (params != null + && params.isRecent + && Objects.equals(browser.getPackageName(), SYSTEM_UI_PACKAGE_NAME)) { + // Advertise support for playback resumption, if enabled. + return !canResumePlaybackOnStart() + ? Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_NOT_SUPPORTED)) + : Futures.immediateFuture( + LibraryResult.ofItem( + new MediaItem.Builder() + .setMediaId(RECENT_LIBRARY_ROOT_MEDIA_ID) + .setMediaMetadata( + new MediaMetadata.Builder() + .setIsBrowsable(true) + .setIsPlayable(false) + .build()) + .build(), + params)); + } ListenableFuture> future = callback.onGetLibraryRoot(instance, browser, params); future.addListener( @@ -142,6 +168,23 @@ public ListenableFuture>> onGetChildrenOn int page, int pageSize, @Nullable LibraryParams params) { + if (Objects.equals(parentId, RECENT_LIBRARY_ROOT_MEDIA_ID)) { + // Advertise support for playback resumption, if enabled. + return !canResumePlaybackOnStart() + ? Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_NOT_SUPPORTED)) + : Futures.immediateFuture( + LibraryResult.ofItemList( + ImmutableList.of( + new MediaItem.Builder() + .setMediaId("androidx.media3.session.recent.item") + .setMediaMetadata( + new MediaMetadata.Builder() + .setIsBrowsable(false) + .setIsPlayable(true) + .build()) + .build()), + params)); + } ListenableFuture>> future = callback.onGetChildren(instance, browser, parentId, page, pageSize, params); future.addListener( diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index e82f64fc474..db506142904 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -560,6 +560,10 @@ protected MediaSessionServiceLegacyStub getLegacyBrowserService() { } } + /* package */ boolean canResumePlaybackOnStart() { + return sessionLegacyStub.canResumePlaybackOnStart(); + } + /* package */ void setMediaSessionListener(MediaSession.Listener listener) { this.mediaSessionListener = listener; } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java index 4c9dbf43531..cc9075b3df8 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java @@ -630,6 +630,10 @@ public ConnectedControllersManager getConnectedControllersManage return connectedControllersManager; } + /* package */ boolean canResumePlaybackOnStart() { + return canResumePlaybackOnStart; + } + private void dispatchSessionTaskWithPlayerCommand( @Player.Command int command, SessionTask task, @Nullable RemoteUserInfo remoteUserInfo) { if (sessionImpl.isReleased()) { diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibrarySessionCallbackTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibrarySessionCallbackTest.java index 7c61699150c..3eedbacb6cd 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibrarySessionCallbackTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibrarySessionCallbackTest.java @@ -21,6 +21,8 @@ import android.content.Context; import androidx.annotation.Nullable; +import androidx.media3.common.MediaItem; +import androidx.media3.common.MediaMetadata; import androidx.media3.session.MediaLibraryService.LibraryParams; import androidx.media3.session.MediaLibraryService.MediaLibrarySession; import androidx.media3.session.MediaSession.ControllerInfo; @@ -29,8 +31,11 @@ import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import org.junit.Before; import org.junit.ClassRule; @@ -132,4 +137,86 @@ public ListenableFuture> onUnsubscribe( browser.unsubscribe(testParentId); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); } + + @Test + public void onGetLibraryRoot_callForRecentRootNonSystemUiPackageName_notIntercepted() + throws Exception { + MediaItem mediaItem = + new MediaItem.Builder() + .setMediaId("rootMediaId") + .setMediaMetadata( + new MediaMetadata.Builder().setIsPlayable(false).setIsBrowsable(true).build()) + .build(); + MockMediaLibraryService service = new MockMediaLibraryService(); + service.attachBaseContext(context); + CountDownLatch latch = new CountDownLatch(1); + MediaLibrarySession.Callback callback = + new MediaLibrarySession.Callback() { + @Override + public ListenableFuture> onGetLibraryRoot( + MediaLibrarySession session, ControllerInfo browser, @Nullable LibraryParams params) { + if (params != null && params.isRecent) { + latch.countDown(); + } + return Futures.immediateFuture(LibraryResult.ofItem(mediaItem, params)); + } + }; + MediaLibrarySession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaLibrarySession.Builder(service, player, callback) + .setId("onGetChildren_callForRecentRootNonSystemUiPackageName_notIntercepted") + .build()); + RemoteMediaBrowser browser = controllerTestRule.createRemoteBrowser(session.getToken()); + + LibraryResult libraryRoot = + browser.getLibraryRoot(new LibraryParams.Builder().setRecent(true).build()); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(libraryRoot.value).isEqualTo(mediaItem); + } + + @Test + public void onGetChildren_systemUiCallForRecentItems_returnsRecentItems() throws Exception { + ArrayList mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); + MockMediaLibraryService service = new MockMediaLibraryService(); + service.attachBaseContext(context); + CountDownLatch latch = new CountDownLatch(1); + MediaLibrarySession.Callback callback = + new MediaLibrarySession.Callback() { + @Override + public ListenableFuture>> onGetChildren( + MediaLibrarySession session, + ControllerInfo browser, + String parentId, + int page, + int pageSize, + @Nullable LibraryParams params) { + latch.countDown(); + return Futures.immediateFuture( + LibraryResult.ofItemList(mediaItems, /* params= */ null)); + } + }; + MediaLibrarySession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaLibrarySession.Builder(service, player, callback) + .setId("onGetChildren_systemUiCallForRecentItems_returnsRecentItems") + .build()); + RemoteMediaBrowser browser = controllerTestRule.createRemoteBrowser(session.getToken()); + + LibraryResult> recentItem = + browser.getChildren( + "androidx.media3.session.recent.root", + /* page= */ 0, + /* pageSize= */ 100, + /* params= */ null); + // Load children of a non recent root that must not be intercepted. + LibraryResult> children = + browser.getChildren("children", /* page= */ 0, /* pageSize= */ 100, /* params= */ null); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(recentItem.resultCode).isEqualTo(LibraryResult.RESULT_SUCCESS); + assertThat(Lists.transform(recentItem.value, (item) -> item.mediaId)) + .containsExactly("androidx.media3.session.recent.item"); + assertThat(children.value).isEqualTo(mediaItems); + } } diff --git a/libraries/test_session_current/src/main/AndroidManifest.xml b/libraries/test_session_current/src/main/AndroidManifest.xml index 457b099fd8c..b873a58120a 100644 --- a/libraries/test_session_current/src/main/AndroidManifest.xml +++ b/libraries/test_session_current/src/main/AndroidManifest.xml @@ -27,9 +27,9 @@ - + From 2ab9c0c5567ff2c5da9f1e69effffc15ad91e339 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Fri, 12 May 2023 17:06:02 +0100 Subject: [PATCH 005/516] Transformer: Add latest input format to DebugTraceUtil This can provide more information to help debug muxer errors PiperOrigin-RevId: 531521974 (cherry picked from commit 2736b118458c1ead0cbac2457700c1333f7c86c7) --- .../androidx/media3/effect/DebugTraceUtil.java | 15 ++++++++++++++- .../transformer/ExoAssetLoaderVideoRenderer.java | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DebugTraceUtil.java b/libraries/effect/src/main/java/androidx/media3/effect/DebugTraceUtil.java index 741be3bef59..52b060dbaab 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DebugTraceUtil.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DebugTraceUtil.java @@ -17,7 +17,9 @@ package androidx.media3.effect; import androidx.annotation.GuardedBy; +import androidx.annotation.Nullable; import androidx.media3.common.C; +import androidx.media3.common.Format; import androidx.media3.common.util.SystemClock; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; @@ -68,6 +70,10 @@ public final class DebugTraceUtil { @GuardedBy("DebugTraceUtil.class") private static final Queue MUXER_TRACK_END_TIMES_MS = new ArrayDeque<>(); + /** The input {@link Format} of the latest media item. */ + @GuardedBy("DebugTraceUtil.class") + private static @Nullable Format latestVideoInputFormat = null; + /** The number of decoded frames. */ @GuardedBy("DebugTraceUtil.class") private static int numberOfDecodedFrames = 0; @@ -91,6 +97,7 @@ public final class DebugTraceUtil { private DebugTraceUtil() {} public static synchronized void reset() { + latestVideoInputFormat = null; numberOfDecodedFrames = 0; numberOfFramesRenderedToVideoFrameProcessorInput = 0; numberOfFramesRenderedToVideoFrameProcessorOutput = 0; @@ -105,6 +112,10 @@ public static synchronized void reset() { MUXER_TRACK_END_TIMES_MS.clear(); } + public static synchronized void recordLatestVideoInputFormat(Format format) { + latestVideoInputFormat = format; + } + public static synchronized void recordDecodedFrame() { numberOfDecodedFrames++; } @@ -164,7 +175,9 @@ public static synchronized void recordMuxerTrackEnded(@C.TrackType int trackType } public static synchronized String generateTrace() { - return "Decoded: " + return "Video input format: " + + latestVideoInputFormat + + ", Decoded: " + numberOfDecodedFrames + ", Rendered to VFP: " + numberOfFramesRenderedToVideoFrameProcessorInput diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/ExoAssetLoaderVideoRenderer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/ExoAssetLoaderVideoRenderer.java index 22c6189f523..2e982883b47 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/ExoAssetLoaderVideoRenderer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/ExoAssetLoaderVideoRenderer.java @@ -71,6 +71,7 @@ protected Format overrideFormat(Format inputFormat) { @Override protected void onInputFormatRead(Format inputFormat) { + DebugTraceUtil.recordLatestVideoInputFormat(inputFormat); if (flattenForSlowMotion) { sefVideoSlowMotionFlattener = new SefSlowMotionFlattener(inputFormat); } From 07ca741eb143eeda4e25deaa97825866bad8a9a5 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 12 May 2023 17:43:38 +0100 Subject: [PATCH 006/516] Rollback of https://github.com/androidx/media/commit/509830f38f12998485038af6edecd0f99df5594b *** Original commit *** Rollback of https://github.com/androidx/media/commit/221a56da38d52ae3f963dfd9e38de11262c0b725 *** Original commit *** Rollback of https://github.com/androidx/media/commit/749d77b1d9989d091dc11269087093bc2c48d216 *** Original commit *** PiperOrigin-RevId: 531530885 (cherry picked from commit 9366b4e50a0dbb1d8a59940944dd732b3f3c3f9b) --- constants.gradle | 1 - libraries/ui/build.gradle | 3 - .../java/androidx/media3/ui/PlayerView.java | 84 ------------------- 3 files changed, 88 deletions(-) diff --git a/constants.gradle b/constants.gradle index 654f1e9c324..bfac06c19b3 100644 --- a/constants.gradle +++ b/constants.gradle @@ -57,7 +57,6 @@ project.ext { androidxTestRulesVersion = '1.5.0' androidxTestServicesStorageVersion = '1.4.2' androidxTestTruthVersion = '1.5.0' - androidxWindowVersion = '1.0.0' truthVersion = '1.1.3' okhttpVersion = '4.11.0' modulePrefix = ':' diff --git a/libraries/ui/build.gradle b/libraries/ui/build.gradle index ea6f32f6da1..35dba78fb34 100644 --- a/libraries/ui/build.gradle +++ b/libraries/ui/build.gradle @@ -25,10 +25,7 @@ dependencies { implementation project(modulePrefix + 'lib-common') implementation 'androidx.media:media:' + androidxMediaVersion implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion - implementation 'androidx.core:core:' + androidxCoreVersion implementation 'androidx.recyclerview:recyclerview:' + androidxRecyclerViewVersion - implementation 'androidx.window:window:' + androidxWindowVersion - implementation 'androidx.window:window-java:' + androidxWindowVersion compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion testImplementation project(modulePrefix + 'test-utils') diff --git a/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java index ebe6f441598..6b6b9a7cb3e 100644 --- a/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java +++ b/libraries/ui/src/main/java/androidx/media3/ui/PlayerView.java @@ -26,10 +26,7 @@ import static java.lang.annotation.ElementType.TYPE_USE; import android.annotation.SuppressLint; -import android.app.Activity; import android.content.Context; -import android.content.ContextWrapper; -import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -56,7 +53,6 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.core.content.ContextCompat; -import androidx.core.util.Consumer; import androidx.media3.common.AdOverlayInfo; import androidx.media3.common.AdViewProvider; import androidx.media3.common.C; @@ -75,13 +71,6 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.ui.AspectRatioFrameLayout.ResizeMode; -import androidx.window.java.layout.WindowInfoTrackerCallbackAdapter; -import androidx.window.layout.DisplayFeature; -import androidx.window.layout.FoldingFeature; -import androidx.window.layout.FoldingFeature.Orientation; -import androidx.window.layout.FoldingFeature.State; -import androidx.window.layout.WindowInfoTracker; -import androidx.window.layout.WindowLayoutInfo; import com.google.common.collect.ImmutableList; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -90,7 +79,6 @@ import java.util.ArrayList; import java.util.List; import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull; /** @@ -279,9 +267,6 @@ public interface FullscreenButtonClickListener { private boolean controllerHideOnTouch; private int textureViewRotation; private boolean isTouching; - private boolean isTabletop; - private final WindowInfoTrackerCallbackAdapter windowInfoTracker; - @MonotonicNonNull private Consumer layoutStateChangeCallback; public PlayerView(Context context) { this(context, /* attrs= */ null); @@ -295,9 +280,6 @@ public PlayerView(Context context, @Nullable AttributeSet attrs) { public PlayerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - isTabletop = false; - windowInfoTracker = - new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(context)); componentListener = new ComponentListener(); if (isInEditMode()) { @@ -595,25 +577,6 @@ public void setVisibility(int visibility) { } } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - layoutStateChangeCallback = this::consumeWindowLayoutInfo; - @Nullable Activity activity = getActivity(); - if (activity != null) { - windowInfoTracker.addWindowLayoutInfoListener( - activity, ContextCompat.getMainExecutor(getContext()), layoutStateChangeCallback); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (layoutStateChangeCallback != null) { - windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback); - } - } - /** * Sets the {@link ResizeMode}. * @@ -1560,53 +1523,6 @@ private boolean isDpadKey(int keyCode) { || keyCode == KeyEvent.KEYCODE_DPAD_CENTER; } - @Nullable - private Activity getActivity() { - Context context = getContext(); - while (context instanceof ContextWrapper) { - if (context instanceof Activity) { - return (Activity) context; - } - context = ((ContextWrapper) context).getBaseContext(); - } - return null; - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - isTabletop = false; - } - - private void consumeWindowLayoutInfo(WindowLayoutInfo windowLayoutInfo) { - List features = windowLayoutInfo.getDisplayFeatures(); - int foldingFeatureIndex = C.INDEX_UNSET; - - for (int i = 0; i < features.size(); i++) { - DisplayFeature feature = features.get(i); - if (feature instanceof FoldingFeature) { - if (foldingFeatureIndex != C.INDEX_UNSET) { - // For now, we are only handling single folding features like tabletop and book fold. - return; - } - foldingFeatureIndex = i; - } - } - if (foldingFeatureIndex != C.INDEX_UNSET) { - handleFoldingFeature((FoldingFeature) features.get(foldingFeatureIndex)); - } - } - - private void handleFoldingFeature(FoldingFeature feature) { - boolean isTabletopFeature = - feature.getOrientation() == Orientation.HORIZONTAL - && feature.getState() == State.HALF_OPENED; - // Only enter or exit tabletop if different than current orientation and state - if (isTabletopFeature != isTabletop) { - isTabletop = isTabletopFeature; - } - } - // Implementing the deprecated PlayerControlView.VisibilityListener and // PlayerControlView.OnFullScreenModeChangedListener for now. @SuppressWarnings("deprecation") From 7e30091196e578cf3eca2b56acabe83e14268736 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 15 May 2023 10:41:09 +0100 Subject: [PATCH 007/516] Remove two deprecated `SimpleCache` constructors Use a non-deprecated constructor that takes a `DatabaseProvider` instead for better performance. #minor-release PiperOrigin-RevId: 532046598 (cherry picked from commit 0a86790be25527c45b0070cd0d4a1089b2069108) --- RELEASENOTES.md | 4 ++ .../media3/datasource/cache/SimpleCache.java | 40 +----------- .../datasource/cache/SimpleCacheTest.java | 62 ------------------- 3 files changed, 6 insertions(+), 100 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 5ff4de3f607..ac3dc63cf95 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -7,6 +7,10 @@ implement playback resumption with media button events sent by, for example, a Bluetooth headset ([#167](https://github.com/androidx/media/issues/167)). +* Remove deprecated symbols: + * Remove two deprecated `SimpleCache` constructors, use a non-deprecated + constructor that takes a `DatabaseProvider` instead for better + performance. ### 1.1.0-alpha01 (2023-05-10) diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/SimpleCache.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/SimpleCache.java index 5bb81d239b7..a6268598a14 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/SimpleCache.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/SimpleCache.java @@ -138,48 +138,12 @@ public static void delete(File cacheDir, @Nullable DatabaseProvider databaseProv @Deprecated @SuppressWarnings("deprecation") public SimpleCache(File cacheDir, CacheEvictor evictor) { - this(cacheDir, evictor, null, false); - } - - /** - * Constructs the cache. The cache will delete any unrecognized files from the directory. Hence - * the directory cannot be used to store other files. - * - * @param cacheDir A dedicated cache directory. - * @param evictor The evictor to be used. For download use cases where cache eviction should not - * occur, use {@link NoOpCacheEvictor}. - * @param secretKey If not null, cache keys will be stored encrypted on filesystem using AES/CBC. - * The key must be 16 bytes long. - * @deprecated Use a constructor that takes a {@link DatabaseProvider} for improved performance. - */ - @Deprecated - @SuppressWarnings("deprecation") - public SimpleCache(File cacheDir, CacheEvictor evictor, @Nullable byte[] secretKey) { - this(cacheDir, evictor, secretKey, secretKey != null); - } - - /** - * Constructs the cache. The cache will delete any unrecognized files from the directory. Hence - * the directory cannot be used to store other files. - * - * @param cacheDir A dedicated cache directory. - * @param evictor The evictor to be used. For download use cases where cache eviction should not - * occur, use {@link NoOpCacheEvictor}. - * @param secretKey If not null, cache keys will be stored encrypted on filesystem using AES/CBC. - * The key must be 16 bytes long. - * @param encrypt Whether the index will be encrypted when written. Must be false if {@code - * secretKey} is null. - * @deprecated Use a constructor that takes a {@link DatabaseProvider} for improved performance. - */ - @Deprecated - public SimpleCache( - File cacheDir, CacheEvictor evictor, @Nullable byte[] secretKey, boolean encrypt) { this( cacheDir, evictor, /* databaseProvider= */ null, - secretKey, - encrypt, + /* legacyIndexSecretKey= */ null, + /* legacyIndexEncrypt= */ false, /* preferLegacyIndex= */ true); } diff --git a/libraries/datasource/src/test/java/androidx/media3/datasource/cache/SimpleCacheTest.java b/libraries/datasource/src/test/java/androidx/media3/datasource/cache/SimpleCacheTest.java index 8d384d7e562..758921e2d3e 100644 --- a/libraries/datasource/src/test/java/androidx/media3/datasource/cache/SimpleCacheTest.java +++ b/libraries/datasource/src/test/java/androidx/media3/datasource/cache/SimpleCacheTest.java @@ -44,7 +44,6 @@ @RunWith(AndroidJUnit4.class) public class SimpleCacheTest { - private static final byte[] ENCRYPTED_INDEX_KEY = Util.getUtf8Bytes("Bar12345Bar12345"); private static final String KEY_1 = "key1"; private static final String KEY_2 = "key2"; @@ -204,61 +203,6 @@ public void newInstance_withExistingCacheDirectory_withDatabase_resolvesInconsis assertThat(simpleCache.getCachedSpans(KEY_1)).isEmpty(); } - @Test - @SuppressWarnings("deprecation") // Encrypted index is deprecated - public void newInstance_withEncryptedIndex() throws Exception { - SimpleCache simpleCache = getEncryptedSimpleCache(ENCRYPTED_INDEX_KEY); - CacheSpan holeSpan = simpleCache.startReadWrite(KEY_1, 0, LENGTH_UNSET); - addCache(simpleCache, KEY_1, 0, 15); - simpleCache.releaseHoleSpan(holeSpan); - simpleCache.release(); - - // Create a new instance pointing to the same directory. - simpleCache = getEncryptedSimpleCache(ENCRYPTED_INDEX_KEY); - - // Read the cached data back. - CacheSpan fileSpan = simpleCache.startReadWrite(KEY_1, 0, LENGTH_UNSET); - assertCachedDataReadCorrect(fileSpan); - } - - @Test - @SuppressWarnings("deprecation") // Encrypted index is deprecated - public void newInstance_withEncryptedIndexAndWrongKey_clearsCache() throws Exception { - SimpleCache simpleCache = getEncryptedSimpleCache(ENCRYPTED_INDEX_KEY); - - // Write data. - CacheSpan holeSpan = simpleCache.startReadWrite(KEY_1, 0, LENGTH_UNSET); - addCache(simpleCache, KEY_1, 0, 15); - simpleCache.releaseHoleSpan(holeSpan); - simpleCache.release(); - - // Create a new instance pointing to the same directory, with a different key. - simpleCache = getEncryptedSimpleCache(Util.getUtf8Bytes("Foo12345Foo12345")); - - // Cache should be cleared. - assertThat(simpleCache.getKeys()).isEmpty(); - assertNoCacheFiles(cacheDir); - } - - @Test - @SuppressWarnings("deprecation") // Encrypted index is deprecated - public void newInstance_withEncryptedIndexAndNoKey_clearsCache() throws Exception { - SimpleCache simpleCache = getEncryptedSimpleCache(ENCRYPTED_INDEX_KEY); - - // Write data. - CacheSpan holeSpan = simpleCache.startReadWrite(KEY_1, 0, LENGTH_UNSET); - addCache(simpleCache, KEY_1, 0, 15); - simpleCache.releaseHoleSpan(holeSpan); - simpleCache.release(); - - // Create a new instance pointing to the same directory, with no key. - simpleCache = getSimpleCache(); - - // Cache should be cleared. - assertThat(simpleCache.getKeys()).isEmpty(); - assertNoCacheFiles(cacheDir); - } - @Test public void write_oneLock_oneFile_thenRead() throws Exception { SimpleCache simpleCache = getSimpleCache(); @@ -689,12 +633,6 @@ private SimpleCache getSimpleCache() { return new SimpleCache(cacheDir, new NoOpCacheEvictor(), databaseProvider); } - @Deprecated - @SuppressWarnings("deprecation") // Testing deprecated behaviour. - private SimpleCache getEncryptedSimpleCache(byte[] secretKey) { - return new SimpleCache(cacheDir, new NoOpCacheEvictor(), secretKey); - } - private static void addCache(SimpleCache simpleCache, String key, int position, int length) throws IOException { File file = simpleCache.startFile(key, position, length); From b0f8a8f316ff6f4e0746238286a7fe1393e0916d Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 15 May 2023 11:25:24 +0100 Subject: [PATCH 008/516] Signal end-of-stream after creating latch The output end-of-stream notification from the last shader could theoretically arrive before the latch for detecting it is created, which might cause waiting on the latch indefinitely. Create the latch before signaling end of stream so that it's guaranteed to be set before the end-of-stream signal arrives. PiperOrigin-RevId: 532056472 (cherry picked from commit 857e6ebee814cb1a92875dc055dd5aff0ef07453) --- .../java/androidx/media3/effect/DefaultVideoFrameProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index 228b483253f..850d5b733e5 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -374,9 +374,9 @@ public void registerInputStream(@InputType int inputType) { } } - checkNotNull(textureManager).signalEndOfCurrentInputStream(); // Wait until the current input stream is processed before continuing to the next input. latch = new CountDownLatch(1); + checkNotNull(textureManager).signalEndOfCurrentInputStream(); try { latch.await(); } catch (InterruptedException e) { From 5e926cdce5a1c5fd1710944783db6590678c3b16 Mon Sep 17 00:00:00 2001 From: kimvde Date: Mon, 15 May 2023 12:10:21 +0100 Subject: [PATCH 009/516] Refactor SequenceAssetLoader release In the past, the SequenceAssetLoader was released in TransformerInternal when the export ended. https://github.com/androidx/media/commit/fc539da0613d8703e2176fe5aa225df8cefb9c8f was made to release the SequenceAssetLoader earlier, when loading ended. This was causing player release timeouts because the last AssetLoader in the sequence was released before the SamplePipelines (see https://github.com/androidx/media/commit/0b40bc37ab0405dc7673702a9ac035a0a5c4cb63 for more information). The code that was releasing the SequenceAssetLoader was first commented out because we didn't have an understanding of what was happening. This change removes the early SequenceAssetLoader release all together. It doesn't have any effect as this code was already commented out. PiperOrigin-RevId: 532065673 (cherry picked from commit 6dfb387117b2a12d5141014dd49fe32afcec3886) --- .../transformer/SequenceAssetLoader.java | 36 ++++++------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java index ab3af97d50f..73bad80e7aa 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SequenceAssetLoader.java @@ -167,10 +167,8 @@ public ImmutableList getProcessedInputs() { @Override public void release() { - if (!released) { - currentAssetLoader.release(); - released = true; - } + currentAssetLoader.release(); + released = true; } private void addCurrentProcessedInput() { @@ -373,9 +371,7 @@ public boolean queueInputBuffer() { // SampleConsumer so there is no need to handle the case where the sample wasn't queued. checkState(sampleConsumer.queueInputBuffer()); audioLoopingEnded = true; - if (nonEndedTracks.decrementAndGet() == 0) { - release(); - } + nonEndedTracks.decrementAndGet(); } return false; } @@ -388,13 +384,8 @@ public boolean queueInputBuffer() { if (nonEndedTracks.get() == 0) { switchAssetLoader(); } - } else { - checkState(sampleConsumer.queueInputBuffer()); - if (nonEndedTracks.get() == 0) { - release(); - } + return true; } - return true; } checkState(sampleConsumer.queueInputBuffer()); @@ -472,17 +463,13 @@ public boolean registerVideoFrame(long presentationTimeUs) { @Override public void signalEndOfVideoInput() { + nonEndedTracks.decrementAndGet(); boolean videoEnded = isLooping ? videoLoopingEnded : currentMediaItemIndex == editedMediaItems.size() - 1; if (videoEnded) { sampleConsumer.signalEndOfVideoInput(); - } - if (nonEndedTracks.decrementAndGet() == 0) { - if (videoEnded) { - release(); - } else { - switchAssetLoader(); - } + } else if (nonEndedTracks.get() == 0) { + switchAssetLoader(); } } @@ -490,6 +477,9 @@ private void switchAssetLoader() { handler.post( () -> { try { + if (released) { + return; + } addCurrentProcessedInput(); totalDurationUs += currentAssetDurationUs; currentAssetLoader.release(); @@ -512,11 +502,5 @@ private void switchAssetLoader() { } }); } - - private void release() { - // TODO(b/276415739): releasing the player earlier causes more release timeouts on emulator - // tests. Figure out what the cause is and uncomment the line below once fixed. - // handler.post(SequenceAssetLoader.this::release); - } } } From 1ebf5c2a6ad0b4bd15364780999802f0f3852a6c Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 15 May 2023 12:33:13 +0100 Subject: [PATCH 010/516] Delete deprecated zero-arg `DefaultBandwidthMeter` constructor Use `DefaultBandwidthMeter.Builder` instead. #minor-release PiperOrigin-RevId: 532069549 (cherry picked from commit 8f29a5eba9d1cd67e4eef2ef6f13d91fd8846668) --- RELEASENOTES.md | 2 ++ .../exoplayer/upstream/DefaultBandwidthMeter.java | 13 ------------- .../upstream/DefaultBandwidthMeterTest.java | 14 ++++---------- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ac3dc63cf95..02807708b85 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -11,6 +11,8 @@ * Remove two deprecated `SimpleCache` constructors, use a non-deprecated constructor that takes a `DatabaseProvider` instead for better performance. + * Remove `DefaultBandwidthMeter` constructor, use + `DefaultBandwidthMeter.Builder` instead. ### 1.1.0-alpha01 (2023-05-10) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java index 924b9d3ac0b..43a4f0f6db6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeter.java @@ -297,19 +297,6 @@ public static synchronized DefaultBandwidthMeter getSingletonInstance(Context co private boolean networkTypeOverrideSet; private @C.NetworkType int networkTypeOverride; - /** - * @deprecated Use {@link Builder} instead. - */ - @Deprecated - public DefaultBandwidthMeter() { - this( - /* context= */ null, - /* initialBitrateEstimates= */ ImmutableMap.of(), - DEFAULT_SLIDING_WINDOW_MAX_WEIGHT, - Clock.DEFAULT, - /* resetOnNetworkTypeChange= */ false); - } - private DefaultBandwidthMeter( @Nullable Context context, Map initialBitrateEstimates, diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeterTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeterTest.java index bd3e3cbf1c4..87aea55c920 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeterTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/upstream/DefaultBandwidthMeterTest.java @@ -688,19 +688,13 @@ public void networkTypeOverride_doesFullReset() { } @Test - @SuppressWarnings("deprecation") public void defaultInitialBitrateEstimate_withoutContext_isReasonable() { - DefaultBandwidthMeter bandwidthMeterWithBuilder = + DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder(/* context= */ null).build(); - long initialEstimateWithBuilder = bandwidthMeterWithBuilder.getBitrateEstimate(); - - DefaultBandwidthMeter bandwidthMeterWithoutBuilder = new DefaultBandwidthMeter(); - long initialEstimateWithoutBuilder = bandwidthMeterWithoutBuilder.getBitrateEstimate(); + long initialEstimate = bandwidthMeter.getBitrateEstimate(); - assertThat(initialEstimateWithBuilder).isGreaterThan(100_000L); - assertThat(initialEstimateWithBuilder).isLessThan(50_000_000L); - assertThat(initialEstimateWithoutBuilder).isGreaterThan(100_000L); - assertThat(initialEstimateWithoutBuilder).isLessThan(50_000_000L); + assertThat(initialEstimate).isGreaterThan(100_000L); + assertThat(initialEstimate).isLessThan(50_000_000L); } private void setActiveNetworkInfo(NetworkInfo networkInfo) { From 5722e6472a3f94e560a2a39f4284aa0f95f41ccb Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 15 May 2023 14:16:53 +0100 Subject: [PATCH 011/516] Fix lost shader input capacity after end-of-stream When exporting compositions with multiple images in a row, transformation could get stuck if a shader was ready to accept input when end-of-stream was already signaled and queued from upstream. Fix accounting for the downstream capacity. Manually tested on concatenations with several images and several videos in a row, by adding logging and verifying the capacity updates as expected across edited media item transitions. PiperOrigin-RevId: 532088793 (cherry picked from commit 6850391e45ed571f0190643189661b0920292084) --- .../java/androidx/media3/effect/FrameConsumptionManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FrameConsumptionManager.java b/libraries/effect/src/main/java/androidx/media3/effect/FrameConsumptionManager.java index 14b0d834ca4..866412cf066 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FrameConsumptionManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FrameConsumptionManager.java @@ -65,6 +65,7 @@ public synchronized void onReadyToAcceptInputFrame() { long presentationTimeUs = pendingFrame.second; if (presentationTimeUs == C.TIME_END_OF_SOURCE) { + consumingGlShaderProgramInputCapacity++; videoFrameProcessingTaskExecutor.submit( consumingGlShaderProgram::signalEndOfCurrentInputStream); } else { From c7004d43af743a97f8b66ab5da96c20a15127ae7 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 15 May 2023 14:19:03 +0100 Subject: [PATCH 012/516] Add consistency check to sending and receiving position updates The periodic updates are only meant to happen while we are in the same period or ad. This was already guaranteed except for two cases: 1. The Player in a session has updated its state without yet calling its listeners 2. The session scheduled a PlayerInfo update that hasn't been sent yet ... and in both cases, the following happened: - The change updated the mediaItemIndex to an index that didn't exist in a previous Timeline known to the Controller - One of the period position updates happened to be sent at exactly this time This problem can be avoided by only scheduling the update if we are still in the same period/ad and haven't scheduled a normal PlayerInfo update already. Since new MediaControllers may still connect to old sessons with this bug, we need an equivalent change on the controller side to ignore such buggy updates. PiperOrigin-RevId: 532089328 (cherry picked from commit 96dd0ae5837a5fd82d7407623bedb5fd4d1e9252) --- RELEASENOTES.md | 3 ++ .../session/MediaControllerImplBase.java | 7 +++ .../media3/session/MediaSessionImpl.java | 19 ++++++-- .../androidx/media3/session/MediaUtils.java | 13 ++++++ .../media3/session/MediaControllerTest.java | 46 +++++++++++++++++++ 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 02807708b85..a7fc67516a7 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -7,6 +7,9 @@ implement playback resumption with media button events sent by, for example, a Bluetooth headset ([#167](https://github.com/androidx/media/issues/167)). + * Fix bug where a combined `Timeline` and position update in a + `MediaSession` may cause a `MediaController` to throw an + `IllegalStateException`. * Remove deprecated symbols: * Remove two deprecated `SimpleCache` constructors, use a non-deprecated constructor that takes a `DatabaseProvider` instead for better diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index f1128e8969b..5a4b33be2b3 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -2613,6 +2613,13 @@ public void onRenderedFirstFrame() { private void updateSessionPositionInfoIfNeeded(SessionPositionInfo sessionPositionInfo) { if (pendingMaskingSequencedFutureNumbers.isEmpty() && playerInfo.sessionPositionInfo.eventTimeMs < sessionPositionInfo.eventTimeMs) { + if (!MediaUtils.areSessionPositionInfosInSamePeriodOrAd( + sessionPositionInfo, playerInfo.sessionPositionInfo)) { + // MediaSessionImpl before version 1.0.2 has a bug that may send position info updates for + // new periods too early. Ignore these updates to avoid an inconsistent state (see + // [internal b/277301159]). + return; + } playerInfo = playerInfo.copyWithSessionPositionInfo(sessionPositionInfo); } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index db506142904..74d660bbf7d 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -807,7 +807,16 @@ private void notifyPeriodicSessionPositionInfoChangesOnHandler() { } } SessionPositionInfo sessionPositionInfo = playerWrapper.createSessionPositionInfoForBundling(); - dispatchOnPeriodicSessionPositionInfoChanged(sessionPositionInfo); + if (!onPlayerInfoChangedHandler.hasPendingPlayerInfoChangedUpdate() + && MediaUtils.areSessionPositionInfosInSamePeriodOrAd( + sessionPositionInfo, playerInfo.sessionPositionInfo)) { + // Send a periodic position info only if a PlayerInfo update is not already already pending + // and the player state is still corresponding to the currently known PlayerInfo. Both + // conditions will soon trigger a new PlayerInfo update with the latest position info anyway + // and we also don't want to send a new position info early if the corresponding Timeline + // update hasn't been sent yet (see [internal b/277301159]). + dispatchOnPeriodicSessionPositionInfoChanged(sessionPositionInfo); + } schedulePeriodicSessionPositionInfoChanges(); } @@ -1362,11 +1371,15 @@ public void handleMessage(Message msg) { } } + public boolean hasPendingPlayerInfoChangedUpdate() { + return hasMessages(MSG_PLAYER_INFO_CHANGED); + } + public void sendPlayerInfoChangedMessage(boolean excludeTimeline, boolean excludeTracks) { this.excludeTimeline = this.excludeTimeline && excludeTimeline; this.excludeTracks = this.excludeTracks && excludeTracks; - if (!onPlayerInfoChangedHandler.hasMessages(MSG_PLAYER_INFO_CHANGED)) { - onPlayerInfoChangedHandler.sendEmptyMessage(MSG_PLAYER_INFO_CHANGED); + if (!hasMessages(MSG_PLAYER_INFO_CHANGED)) { + sendEmptyMessage(MSG_PLAYER_INFO_CHANGED); } } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java index 0fda511e7c0..a15d5fe0c33 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java @@ -1476,6 +1476,19 @@ public static void setMediaItemsWithStartIndexAndPosition( } } + /** + * Returns whether the two provided {@link SessionPositionInfo} describe a position in the same + * period or ad. + */ + public static boolean areSessionPositionInfosInSamePeriodOrAd( + SessionPositionInfo info1, SessionPositionInfo info2) { + // TODO: b/259220235 - Use UIDs instead of mediaItemIndex and periodIndex + return info1.positionInfo.mediaItemIndex == info2.positionInfo.mediaItemIndex + && info1.positionInfo.periodIndex == info2.positionInfo.periodIndex + && info1.positionInfo.adGroupIndex == info2.positionInfo.adGroupIndex + && info1.positionInfo.adIndexInAdGroup == info2.positionInfo.adIndexInAdGroup; + } + private static byte[] convertToByteArray(Bitmap bitmap) throws IOException { try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { bitmap.compress(Bitmap.CompressFormat.PNG, /* ignored */ 0, stream); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java index 4a4c01db4fc..5405c0f6d71 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java @@ -34,6 +34,7 @@ import android.content.Context; import android.os.Bundle; import android.os.RemoteException; +import androidx.annotation.Nullable; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; import androidx.media3.common.Format; @@ -1064,6 +1065,51 @@ public void getBufferedPosition_whilePausedAndNotLoading_isNotUpdatedPeriodicall assertThat(bufferedPositionAfterDelay.get()).isNotEqualTo(testBufferedPosition); } + @Test + public void + getCurrentMediaItemIndex_withPeriodicUpdateOverlappingTimelineChanges_updatesIndexCorrectly() + throws Exception { + Bundle playerConfig = + new RemoteMediaSession.MockPlayerConfigBuilder() + .setPlayWhenReady(true) + .setPlaybackState(Player.STATE_READY) + .build(); + remoteSession.setPlayer(playerConfig); + MediaController controller = controllerTestRule.createController(remoteSession.getToken()); + ArrayList transitionMediaItemIndices = new ArrayList<>(); + controller.addListener( + new Player.Listener() { + @Override + public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) { + transitionMediaItemIndices.add(controller.getCurrentMediaItemIndex()); + } + }); + + // Intentionally trigger update often to ensure there is a likely overlap with Timeline updates. + remoteSession.setSessionPositionUpdateDelayMs(1L); + // Trigger many timeline and position updates that are incompatible with any previous updates. + for (int i = 1; i <= 100; i++) { + remoteSession.getMockPlayer().createAndSetFakeTimeline(/* windowCount= */ i); + remoteSession.getMockPlayer().setCurrentMediaItemIndex(i - 1); + remoteSession + .getMockPlayer() + .notifyTimelineChanged(Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); + remoteSession + .getMockPlayer() + .notifyMediaItemTransition( + /* index= */ i - 1, Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + } + PollingCheck.waitFor(TIMEOUT_MS, () -> transitionMediaItemIndices.size() == 100); + + ImmutableList.Builder expectedMediaItemIndices = ImmutableList.builder(); + for (int i = 0; i < 100; i++) { + expectedMediaItemIndices.add(i); + } + assertThat(transitionMediaItemIndices) + .containsExactlyElementsIn(expectedMediaItemIndices.build()) + .inOrder(); + } + @Test public void getContentBufferedPosition_byDefault_returnsZero() throws Exception { MediaController controller = controllerTestRule.createController(remoteSession.getToken()); From a1fbb12ade6d1073bbe36415792877bf6e776bc0 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Mon, 15 May 2023 14:43:30 +0100 Subject: [PATCH 013/516] Effect: Multiple Texture output Allow the VideoFrameProcessor to output multiple textures at a time, so that lifetime of textures is up to the consumer calling VFP.releaseOutputFrame. The FinalShaderProgramWrapper also has a new maxCapacity limit added, to ensure the a reasonable amount of textures is used and avoid using up memory. PiperOrigin-RevId: 532094256 (cherry picked from commit 07ec1eaa480146942e8019a1db25661201a2f3e0) --- .../media3/common/VideoFrameProcessor.java | 14 ++- .../effect/DefaultVideoFrameProcessor.java | 78 +++++++++++++--- .../effect/FinalShaderProgramWrapper.java | 93 +++++++++++++------ .../utils/VideoFrameProcessorTestRunner.java | 21 ++++- ...oFrameProcessorTextureOutputPixelTest.java | 44 ++++++--- 5 files changed, 188 insertions(+), 62 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java index d22209f9cb7..1ad966e5d00 100644 --- a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java +++ b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java @@ -145,7 +145,7 @@ interface Listener { */ void onError(VideoFrameProcessingException exception); - /** Called after the {@link VideoFrameProcessor} has produced its final output frame. */ + /** Called after the {@link VideoFrameProcessor} has rendered its final output frame. */ void onEnded(); } @@ -291,6 +291,18 @@ interface Listener { */ void renderOutputFrame(long renderTimeNs); + /** + * Releases resources associated with all output frames with presentation time less than or equal + * to {@code presentationTimeUs}. + * + *

Not needed for outputting to an {@linkplain #setOutputSurfaceInfo output surface}, but may + * be required for other outputs. + * + * @param presentationTimeUs The presentation time where all frames before and at this time should + * be released, in microseconds. + */ + void releaseOutputFrame(long presentationTimeUs); + /** * Informs the {@code VideoFrameProcessor} that no further input frames should be accepted. * diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index 850d5b733e5..16d1eba3bee 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -31,6 +31,7 @@ import android.opengl.GLES30; import android.view.Surface; import androidx.annotation.GuardedBy; +import androidx.annotation.IntRange; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.media3.common.C; @@ -73,7 +74,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { public interface TextureOutputListener { /** Called when a texture has been rendered to. */ void onTextureRendered(GlTextureInfo outputTexture, long presentationTimeUs) - throws GlUtil.GlException, VideoFrameProcessingException; + throws VideoFrameProcessingException; } /** A factory for {@link DefaultVideoFrameProcessor} instances. */ @@ -84,6 +85,7 @@ public static final class Builder { private boolean enableColorTransfers; private GlObjectsProvider glObjectsProvider; @Nullable private TextureOutputListener textureOutputListener; + private int textureOutputCapacity; /** Creates an instance. */ public Builder() { @@ -114,39 +116,55 @@ public Builder setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { } /** - * Sets the {@link TextureOutputListener}. + * Sets texture output settings. * - *

If set, the {@link VideoFrameProcessor} will output to an OpenGL texture, accessible via - * {@link TextureOutputListener#onTextureRendered}. Otherwise, no texture will be rendered to. + *

If set, the {@link VideoFrameProcessor} will output to OpenGL textures, accessible via + * {@link TextureOutputListener#onTextureRendered}. Textures will stop being output when + * {@code textureOutputCapacity} is reached, until they're released via {@link + * #releaseOutputFrame}. Output textures must be released using {@link #releaseOutputFrame}. * - *

If an {@linkplain #setOutputSurfaceInfo output surface} is set, the texture output will - * be be adjusted as needed, to match the output surface's output. + *

If not set, there will be no texture output. + * + *

This must not be set if the {@linkplain #setOutputSurfaceInfo output surface info} is + * also set. + * + * @param textureOutputListener The {@link TextureOutputListener}. + * @param textureOutputCapacity The amount of output textures that may be allocated at a time + * before texture output blocks. Must be greater than or equal to 1. */ @VisibleForTesting @CanIgnoreReturnValue - public Builder setOnTextureRenderedListener(TextureOutputListener textureOutputListener) { + public Builder setTextureOutput( + TextureOutputListener textureOutputListener, + @IntRange(from = 1) int textureOutputCapacity) { + // TODO: http://b/262694346 - Add tests for multiple texture output. this.textureOutputListener = textureOutputListener; + checkArgument(textureOutputCapacity >= 1); + this.textureOutputCapacity = textureOutputCapacity; return this; } /** Builds an {@link DefaultVideoFrameProcessor.Factory} instance. */ public DefaultVideoFrameProcessor.Factory build() { return new DefaultVideoFrameProcessor.Factory( - enableColorTransfers, glObjectsProvider, textureOutputListener); + enableColorTransfers, glObjectsProvider, textureOutputListener, textureOutputCapacity); } } private final boolean enableColorTransfers; private final GlObjectsProvider glObjectsProvider; @Nullable private final TextureOutputListener textureOutputListener; + private final int textureOutputCapacity; private Factory( boolean enableColorTransfers, GlObjectsProvider glObjectsProvider, - @Nullable TextureOutputListener textureOutputListener) { + @Nullable TextureOutputListener textureOutputListener, + int textureOutputCapacity) { this.enableColorTransfers = enableColorTransfers; this.glObjectsProvider = glObjectsProvider; this.textureOutputListener = textureOutputListener; + this.textureOutputCapacity = textureOutputCapacity; } /** @@ -231,7 +249,8 @@ public DefaultVideoFrameProcessor create( listenerExecutor, listener, glObjectsProvider, - textureOutputListener)); + textureOutputListener, + textureOutputCapacity)); try { return defaultVideoFrameProcessorFuture.get(); @@ -411,11 +430,23 @@ public int getPendingInputFrameCount() { return checkNotNull(textureManager).getPendingFrameCount(); } + /** + * {@inheritDoc} + * + *

This must not be set on an instance where {@linkplain Factory.Builder#setTextureOutput + * texture output} is set. + */ @Override public void setOutputSurfaceInfo(@Nullable SurfaceInfo outputSurfaceInfo) { finalShaderProgramWrapper.setOutputSurfaceInfo(outputSurfaceInfo); } + /** + * {@inheritDoc} + * + *

This may also be used for rendering from an output texture, if a {@link + * TextureOutputListener} {@linkplain Factory.Builder#setTextureOutput is set} + */ @Override public void renderOutputFrame(long renderTimeNs) { checkState( @@ -425,6 +456,21 @@ public void renderOutputFrame(long renderTimeNs) { () -> finalShaderProgramWrapper.renderOutputFrame(renderTimeNs)); } + /** + * {@inheritDoc} + * + *

If a {@link TextureOutputListener} {@linkplain Factory.Builder#setTextureOutput is set}, + * this must be called to release the output information stored in the {@link GlTextureInfo} + * instances. + */ + @Override + public void releaseOutputFrame(long presentationTimeUs) { + // TODO(b/262694346): Add Compositor system tests exercising this code path after GL texture + // input is possible. + videoFrameProcessingTaskExecutor.submit( + () -> finalShaderProgramWrapper.releaseOutputFrame(presentationTimeUs)); + } + @Override public void signalEndOfInput() { DebugTraceUtil.recordVideoFrameProcessorReceiveDecoderEos(); @@ -511,7 +557,8 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( Executor executor, Listener listener, GlObjectsProvider glObjectsProvider, - @Nullable TextureOutputListener textureOutputListener) + @Nullable TextureOutputListener textureOutputListener, + int textureOutputCapacity) throws GlUtil.GlException, VideoFrameProcessingException { checkState(Thread.currentThread().getName().equals(THREAD_NAME)); @@ -568,7 +615,8 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( executor, listener, glObjectsProvider, - textureOutputListener); + textureOutputListener, + textureOutputCapacity); inputSwitcher.registerInput(INPUT_TYPE_SURFACE); if (!ColorInfo.isTransferHdr(inputColorInfo)) { @@ -618,7 +666,8 @@ private static ImmutableList getGlShaderProgramsForGlEffects( Executor executor, Listener listener, GlObjectsProvider glObjectsProvider, - @Nullable TextureOutputListener textureOutputListener) + @Nullable TextureOutputListener textureOutputListener, + int textureOutputCapacity) throws VideoFrameProcessingException { ImmutableList.Builder shaderProgramListBuilder = new ImmutableList.Builder<>(); ImmutableList.Builder matrixTransformationListBuilder = @@ -670,7 +719,8 @@ private static ImmutableList getGlShaderProgramsForGlEffects( executor, listener, glObjectsProvider, - textureOutputListener)); + textureOutputListener, + textureOutputCapacity)); return shaderProgramListBuilder.build(); } diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java index 20dacf686bf..16eb5255581 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java @@ -50,8 +50,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * Wrapper around a {@link DefaultShaderProgram} that renders to the provided output surface or - * texture. + * Wrapper around a {@link DefaultShaderProgram} that renders to either the provided output surface + * or texture. * *

Also renders to a debug surface, if provided. * @@ -87,17 +87,20 @@ interface OnInputStreamProcessedListener { private final Executor videoFrameProcessorListenerExecutor; private final VideoFrameProcessor.Listener videoFrameProcessorListener; private final Queue> availableFrames; + private final Queue> outputTextures; @Nullable private final DefaultVideoFrameProcessor.TextureOutputListener textureOutputListener; + private final int textureOutputCapacity; private int inputWidth; private int inputHeight; + private int outputWidth; + private int outputHeight; @Nullable private DefaultShaderProgram defaultShaderProgram; @Nullable private SurfaceViewWrapper debugSurfaceViewWrapper; private GlObjectsProvider glObjectsProvider; private InputListener inputListener; private @MonotonicNonNull Size outputSizeBeforeSurfaceTransformation; @Nullable private SurfaceView debugSurfaceView; - @Nullable private GlTextureInfo outputTexture; @Nullable private OnInputStreamProcessedListener onInputStreamProcessedListener; private boolean frameProcessingStarted; @@ -125,7 +128,8 @@ public FinalShaderProgramWrapper( Executor videoFrameProcessorListenerExecutor, VideoFrameProcessor.Listener videoFrameProcessorListener, GlObjectsProvider glObjectsProvider, - @Nullable DefaultVideoFrameProcessor.TextureOutputListener textureOutputListener) { + @Nullable DefaultVideoFrameProcessor.TextureOutputListener textureOutputListener, + int textureOutputCapacity) { this.context = context; this.matrixTransformations = matrixTransformations; this.rgbMatrices = rgbMatrices; @@ -139,9 +143,11 @@ public FinalShaderProgramWrapper( this.videoFrameProcessorListener = videoFrameProcessorListener; this.glObjectsProvider = glObjectsProvider; this.textureOutputListener = textureOutputListener; + this.textureOutputCapacity = textureOutputCapacity; inputListener = new InputListener() {}; availableFrames = new ConcurrentLinkedQueue<>(); + outputTextures = new ConcurrentLinkedQueue<>(); } @Override @@ -155,7 +161,7 @@ public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { @Override public void setInputListener(InputListener inputListener) { this.inputListener = inputListener; - inputListener.onReadyToAcceptInputFrame(); + maybeOnReadyToAcceptInputFrame(); } @Override @@ -193,20 +199,40 @@ public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) frameProcessingStarted = true; videoFrameProcessorListenerExecutor.execute( () -> videoFrameProcessorListener.onOutputFrameAvailableForRendering(presentationTimeUs)); - if (renderFramesAutomatically) { - renderFrame(inputTexture, presentationTimeUs, /* renderTimeNs= */ presentationTimeUs * 1000); + if (textureOutputListener == null) { + if (renderFramesAutomatically) { + renderFrame( + inputTexture, presentationTimeUs, /* renderTimeNs= */ presentationTimeUs * 1000); + } else { + availableFrames.add(Pair.create(inputTexture, presentationTimeUs)); + } } else { - availableFrames.add(Pair.create(inputTexture, presentationTimeUs)); + checkState(outputTextures.size() < textureOutputCapacity); + renderFrame(inputTexture, presentationTimeUs, /* renderTimeNs= */ presentationTimeUs * 1000); } - inputListener.onReadyToAcceptInputFrame(); + maybeOnReadyToAcceptInputFrame(); } @Override public void releaseOutputFrame(GlTextureInfo outputTexture) { - // The final shader program writes to a surface so there is no texture to release. + // FinalShaderProgramWrapper cannot release output textures using GlTextureInfo. throw new UnsupportedOperationException(); } + public void releaseOutputFrame(long presentationTimeUs) throws VideoFrameProcessingException { + while (!outputTextures.isEmpty() + && checkNotNull(outputTextures.peek()).second <= presentationTimeUs) { + GlTextureInfo outputTexture = outputTextures.remove().first; + try { + GlUtil.deleteTexture(outputTexture.texId); + GlUtil.deleteFbo(outputTexture.fboId); + } catch (GlUtil.GlException exception) { + throw new VideoFrameProcessingException(exception); + } + maybeOnReadyToAcceptInputFrame(); + } + } + public void renderOutputFrame(long renderTimeNs) { frameProcessingStarted = true; checkState(!renderFramesAutomatically); @@ -226,7 +252,7 @@ public void flush() { defaultShaderProgram.flush(); } inputListener.onFlush(); - inputListener.onReadyToAcceptInputFrame(); + maybeOnReadyToAcceptInputFrame(); } @Override @@ -235,8 +261,8 @@ public synchronized void release() throws VideoFrameProcessingException { defaultShaderProgram.release(); } try { - if (outputTexture != null) { - GlTextureInfo outputTexture = checkNotNull(this.outputTexture); + while (!outputTextures.isEmpty()) { + GlTextureInfo outputTexture = outputTextures.remove().first; GlUtil.deleteTexture(outputTexture.texId); GlUtil.deleteFbo(outputTexture.fboId); } @@ -252,6 +278,7 @@ public synchronized void release() throws VideoFrameProcessingException { * @see VideoFrameProcessor#setOutputSurfaceInfo(SurfaceInfo) */ public synchronized void setOutputSurfaceInfo(@Nullable SurfaceInfo outputSurfaceInfo) { + checkState(textureOutputListener == null); if (Util.areEqual(this.outputSurfaceInfo, outputSurfaceInfo)) { return; } @@ -276,18 +303,23 @@ public synchronized void setOutputSurfaceInfo(@Nullable SurfaceInfo outputSurfac this.outputSurfaceInfo = outputSurfaceInfo; } + private void maybeOnReadyToAcceptInputFrame() { + if (textureOutputListener == null || outputTextures.size() < textureOutputCapacity) { + inputListener.onReadyToAcceptInputFrame(); + } + } + private synchronized void renderFrame( GlTextureInfo inputTexture, long presentationTimeUs, long renderTimeNs) { try { if (renderTimeNs == VideoFrameProcessor.DROP_OUTPUT_FRAME || !ensureConfigured(inputTexture.width, inputTexture.height)) { inputListener.onInputFrameProcessed(inputTexture); - return; // Drop frames when requested, or there is no output surface. + return; // Drop frames when requested, or there is no output surface and output texture. } if (outputSurfaceInfo != null) { renderFrameToOutputSurface(inputTexture, presentationTimeUs, renderTimeNs); - } - if (textureOutputListener != null) { + } else if (textureOutputListener != null) { renderFrameToOutputTexture(inputTexture, presentationTimeUs); } } catch (VideoFrameProcessingException | GlUtil.GlException e) { @@ -331,12 +363,25 @@ private synchronized void renderFrameToOutputSurface( private void renderFrameToOutputTexture(GlTextureInfo inputTexture, long presentationTimeUs) throws GlUtil.GlException, VideoFrameProcessingException { - GlTextureInfo outputTexture = checkNotNull(this.outputTexture); + // TODO(b/262694346): Use a texture pool instead of creating a new texture on every frame. + int outputTexId = + GlUtil.createTexture( + outputWidth, + outputHeight, + /* useHighPrecisionColorComponents= */ ColorInfo.isTransferHdr(outputColorInfo)); + GlTextureInfo outputTexture = + glObjectsProvider.createBuffersForTexture(outputTexId, outputWidth, outputHeight); + GlUtil.focusFramebufferUsingCurrentContext( outputTexture.fboId, outputTexture.width, outputTexture.height); GlUtil.clearOutputFrame(); checkNotNull(defaultShaderProgram).drawFrame(inputTexture.texId, presentationTimeUs); + // TODO(b/262694346): If Compositor's VFPs all use the same context, media3 should be able to + // avoid calling glFinish, and require the onTextureRendered listener to decide whether to + // glFinish. Consider removing glFinish and requiring onTextureRendered to handle + // synchronization. GLES20.glFinish(); + outputTextures.add(Pair.create(outputTexture, presentationTimeUs)); checkNotNull(textureOutputListener).onTextureRendered(outputTexture, presentationTimeUs); } @@ -381,11 +426,11 @@ private synchronized boolean ensureConfigured(int inputWidth, int inputHeight) return false; } - int outputWidth = + outputWidth = outputSurfaceInfo == null ? outputSizeBeforeSurfaceTransformation.getWidth() : outputSurfaceInfo.width; - int outputHeight = + outputHeight = outputSurfaceInfo == null ? outputSizeBeforeSurfaceTransformation.getHeight() : outputSurfaceInfo.height; @@ -411,16 +456,6 @@ private synchronized boolean ensureConfigured(int inputWidth, int inputHeight) } this.debugSurfaceView = debugSurfaceView; - if (textureOutputListener != null) { - int outputTexId = - GlUtil.createTexture( - outputWidth, - outputHeight, - /* useHighPrecisionColorComponents= */ ColorInfo.isTransferHdr(outputColorInfo)); - outputTexture = - glObjectsProvider.createBuffersForTexture(outputTexId, outputWidth, outputHeight); - } - if (defaultShaderProgram != null && (outputSurfaceInfoChanged || inputSizeChanged)) { defaultShaderProgram.release(); defaultShaderProgram = null; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java index 2331810c6a5..f6b74328e9d 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java @@ -285,13 +285,14 @@ private VideoFrameProcessorTestRunner( new VideoFrameProcessor.Listener() { @Override public void onOutputSizeChanged(int width, int height) { + boolean useHighPrecisionColorComponents = ColorInfo.isTransferHdr(outputColorInfo); @Nullable Surface outputSurface = bitmapReader.getSurface( width, height, - /* useHighPrecisionColorComponents= */ ColorInfo.isTransferHdr( - outputColorInfo)); + useHighPrecisionColorComponents, + checkNotNull(videoFrameProcessor)::releaseOutputFrame); if (outputSurface != null) { checkNotNull(videoFrameProcessor) .setOutputSurfaceInfo(new SurfaceInfo(outputSurface, width, height)); @@ -406,10 +407,18 @@ public interface OnOutputFrameAvailableForRenderingListener { /** Reads a {@link Bitmap} from {@link VideoFrameProcessor} output. */ public interface BitmapReader { + /** Wraps a callback for {@link VideoFrameProcessor#releaseOutputFrame}. */ + interface ReleaseOutputFrameListener { + void releaseOutputFrame(long releaseTimeUs); + } /** Returns the {@link VideoFrameProcessor} output {@link Surface}, if one is needed. */ @Nullable - Surface getSurface(int width, int height, boolean useHighPrecisionColorComponents); + Surface getSurface( + int width, + int height, + boolean useHighPrecisionColorComponents, + ReleaseOutputFrameListener listener); /** Returns the output {@link Bitmap}. */ Bitmap getBitmap(); @@ -429,7 +438,11 @@ public static final class SurfaceBitmapReader @Override @SuppressLint("WrongConstant") @Nullable - public Surface getSurface(int width, int height, boolean useHighPrecisionColorComponents) { + public Surface getSurface( + int width, + int height, + boolean useHighPrecisionColorComponents, + ReleaseOutputFrameListener listener) { imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1); return imageReader.getSurface(); diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java index 107464e9bbf..eec751a942f 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java @@ -138,10 +138,11 @@ public void noEffects_textureInput_matchesGoldenFile() throws Exception { TextureBitmapReader consumersBitmapReader = new TextureBitmapReader(); DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = new DefaultVideoFrameProcessor.Factory.Builder() - .setOnTextureRenderedListener( + .setTextureOutput( (outputTexture, presentationTimeUs) -> inputTextureIntoVideoFrameProcessor( - testId, consumersBitmapReader, outputTexture, presentationTimeUs)) + testId, consumersBitmapReader, outputTexture, presentationTimeUs), + /* textureOutputCapacity= */ 1) .build(); VideoFrameProcessorTestRunner texIdProducingVideoFrameProcessorTestRunner = new VideoFrameProcessorTestRunner.Builder() @@ -206,10 +207,11 @@ public void bitmapOverlay_textureInput_matchesGoldenFile() throws Exception { TextureBitmapReader consumersBitmapReader = new TextureBitmapReader(); DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = new DefaultVideoFrameProcessor.Factory.Builder() - .setOnTextureRenderedListener( + .setTextureOutput( (outputTexture, presentationTimeUs) -> inputTextureIntoVideoFrameProcessor( - testId, consumersBitmapReader, outputTexture, presentationTimeUs)) + testId, consumersBitmapReader, outputTexture, presentationTimeUs), + /* textureOutputCapacity= */ 1) .build(); VideoFrameProcessorTestRunner texIdProducingVideoFrameProcessorTestRunner = new VideoFrameProcessorTestRunner.Builder() @@ -380,7 +382,7 @@ private void inputTextureIntoVideoFrameProcessor( new DefaultGlObjectsProvider(GlUtil.getCurrentContext()); DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = new DefaultVideoFrameProcessor.Factory.Builder() - .setOnTextureRenderedListener(bitmapReader::readBitmapFromTexture) + .setTextureOutput(bitmapReader::readBitmapFromTexture, /* textureOutputCapacity= */ 1) .setGlObjectsProvider(contextSharingGlObjectsProvider) .build(); videoFrameProcessorTestRunner = @@ -405,7 +407,8 @@ private VideoFrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunner TextureBitmapReader textureBitmapReader = new TextureBitmapReader(); DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = new DefaultVideoFrameProcessor.Factory.Builder() - .setOnTextureRenderedListener(textureBitmapReader::readBitmapFromTexture) + .setTextureOutput( + textureBitmapReader::readBitmapFromTexture, /* textureOutputCapacity= */ 1) .build(); return new VideoFrameProcessorTestRunner.Builder() .setTestId(testId) @@ -422,13 +425,19 @@ private VideoFrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunner private static final class TextureBitmapReader implements BitmapReader { // TODO(b/239172735): This outputs an incorrect black output image on emulators. private boolean useHighPrecisionColorComponents; + private @MonotonicNonNull ReleaseOutputFrameListener releaseOutputFrameListener; private @MonotonicNonNull Bitmap outputBitmap; - @Override @Nullable - public Surface getSurface(int width, int height, boolean useHighPrecisionColorComponents) { + @Override + public Surface getSurface( + int width, + int height, + boolean useHighPrecisionColorComponents, + ReleaseOutputFrameListener releaseOutputFrameListener) { this.useHighPrecisionColorComponents = useHighPrecisionColorComponents; + this.releaseOutputFrameListener = releaseOutputFrameListener; return null; } @@ -438,12 +447,19 @@ public Bitmap getBitmap() { } public void readBitmapFromTexture(GlTextureInfo outputTexture, long presentationTimeUs) - throws GlUtil.GlException { - GlUtil.focusFramebufferUsingCurrentContext( - outputTexture.fboId, outputTexture.width, outputTexture.height); - outputBitmap = - createBitmapFromCurrentGlFrameBuffer( - outputTexture.width, outputTexture.height, useHighPrecisionColorComponents); + throws VideoFrameProcessingException { + try { + GlUtil.focusFramebufferUsingCurrentContext( + outputTexture.fboId, outputTexture.width, outputTexture.height); + outputBitmap = + createBitmapFromCurrentGlFrameBuffer( + outputTexture.width, outputTexture.height, useHighPrecisionColorComponents); + GlUtil.deleteTexture(outputTexture.texId); + GlUtil.deleteFbo(outputTexture.fboId); + } catch (GlUtil.GlException e) { + throw new VideoFrameProcessingException(e); + } + checkNotNull(releaseOutputFrameListener).releaseOutputFrame(presentationTimeUs); } private static Bitmap createBitmapFromCurrentGlFrameBuffer( From 7ac9cf0ec7d258c77ec0afe6b77a80f6fdb28bdd Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 15 May 2023 15:21:03 +0100 Subject: [PATCH 014/516] Remove deprecated `DefaultDrmSessionManager` constructors Use `DefaultDrmSessionManager.Builder` instead. #minor-release PiperOrigin-RevId: 532102375 (cherry picked from commit 8a5cebb54dd17e40f0d10700645659450dd7d934) --- RELEASENOTES.md | 2 + .../drm/DefaultDrmSessionManager.java | 83 ------------------- 2 files changed, 2 insertions(+), 83 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index a7fc67516a7..efb1ef0ef0f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -16,6 +16,8 @@ performance. * Remove `DefaultBandwidthMeter` constructor, use `DefaultBandwidthMeter.Builder` instead. + * Remove `DefaultDrmSessionManager` constructors, use + `DefaultDrmSessionManager.Builder` instead. ### 1.1.0-alpha01 (2023-05-10) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java index 05ab52a0395..4fd04f9c573 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/drm/DefaultDrmSessionManager.java @@ -322,89 +322,6 @@ private MissingSchemeDataException(UUID uuid) { /* package */ @Nullable volatile MediaDrmHandler mediaDrmHandler; - /** - * @param uuid The UUID of the drm scheme. - * @param exoMediaDrm An underlying {@link ExoMediaDrm} for use by the manager. - * @param callback Performs key and provisioning requests. - * @param keyRequestParameters An optional map of parameters to pass as the last argument to - * {@link ExoMediaDrm#getKeyRequest(byte[], List, int, HashMap)}. May be null. - * @deprecated Use {@link Builder} instead. - */ - @SuppressWarnings("deprecation") - @Deprecated - public DefaultDrmSessionManager( - UUID uuid, - ExoMediaDrm exoMediaDrm, - MediaDrmCallback callback, - @Nullable HashMap keyRequestParameters) { - this( - uuid, - exoMediaDrm, - callback, - keyRequestParameters == null ? new HashMap<>() : keyRequestParameters, - /* multiSession= */ false, - INITIAL_DRM_REQUEST_RETRY_COUNT); - } - - /** - * @param uuid The UUID of the drm scheme. - * @param exoMediaDrm An underlying {@link ExoMediaDrm} for use by the manager. - * @param callback Performs key and provisioning requests. - * @param keyRequestParameters An optional map of parameters to pass as the last argument to - * {@link ExoMediaDrm#getKeyRequest(byte[], List, int, HashMap)}. May be null. - * @param multiSession A boolean that specify whether multiple key session support is enabled. - * Default is false. - * @deprecated Use {@link Builder} instead. - */ - @SuppressWarnings("deprecation") - @Deprecated - public DefaultDrmSessionManager( - UUID uuid, - ExoMediaDrm exoMediaDrm, - MediaDrmCallback callback, - @Nullable HashMap keyRequestParameters, - boolean multiSession) { - this( - uuid, - exoMediaDrm, - callback, - keyRequestParameters == null ? new HashMap<>() : keyRequestParameters, - multiSession, - INITIAL_DRM_REQUEST_RETRY_COUNT); - } - - /** - * @param uuid The UUID of the drm scheme. - * @param exoMediaDrm An underlying {@link ExoMediaDrm} for use by the manager. - * @param callback Performs key and provisioning requests. - * @param keyRequestParameters An optional map of parameters to pass as the last argument to - * {@link ExoMediaDrm#getKeyRequest(byte[], List, int, HashMap)}. May be null. - * @param multiSession A boolean that specify whether multiple key session support is enabled. - * Default is false. - * @param initialDrmRequestRetryCount The number of times to retry for initial provisioning and - * key request before reporting error. - * @deprecated Use {@link Builder} instead. - */ - @Deprecated - public DefaultDrmSessionManager( - UUID uuid, - ExoMediaDrm exoMediaDrm, - MediaDrmCallback callback, - @Nullable HashMap keyRequestParameters, - boolean multiSession, - int initialDrmRequestRetryCount) { - this( - uuid, - new ExoMediaDrm.AppManagedProvider(exoMediaDrm), - callback, - keyRequestParameters == null ? new HashMap<>() : keyRequestParameters, - multiSession, - /* useDrmSessionsForClearContentTrackTypes= */ new int[0], - /* playClearSamplesWithoutKeys= */ false, - new DefaultLoadErrorHandlingPolicy(initialDrmRequestRetryCount), - DEFAULT_SESSION_KEEPALIVE_MS); - } - private DefaultDrmSessionManager( UUID uuid, ExoMediaDrm.Provider exoMediaDrmProvider, From 7f1c1185e7088b6c6bdc4ac115d674c7d68ea5bb Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 15 May 2023 16:05:42 +0100 Subject: [PATCH 015/516] Remove unnecessary warning suppression PiperOrigin-RevId: 532114601 (cherry picked from commit 79657692e41d168c559a8a5412aa3cfd4040d80b) --- .../src/main/java/androidx/media3/session/MediaSessionImpl.java | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index 74d660bbf7d..b1858656b74 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -129,7 +129,6 @@ // Should be only accessed on the application looper private long sessionPositionUpdateDelayMs; - @SuppressWarnings("StaticAssignmentInConstructor") // TODO(b/277754694): Remove mutable constants public MediaSessionImpl( MediaSession instance, Context context, From 27becc028d83c6efe9cec81ffb34ef74a7090dab Mon Sep 17 00:00:00 2001 From: jbibik Date: Mon, 15 May 2023 17:30:50 +0100 Subject: [PATCH 016/516] Allow ExoPlayer to opt into volume device control, forbidden by default PiperOrigin-RevId: 532136692 (cherry picked from commit 1c6b894e8880480918babaf0a6f6b2038faf2e81) --- RELEASENOTES.md | 29 ++- .../androidx/media3/exoplayer/ExoPlayer.java | 16 ++ .../media3/exoplayer/ExoPlayerImpl.java | 82 ++++++--- .../media3/exoplayer/ExoPlayerTest.java | 172 ++++++++++++++++-- .../test/utils/TestExoPlayerBuilder.java | 17 +- 5 files changed, 269 insertions(+), 47 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index efb1ef0ef0f..93bdd43a99d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -47,9 +47,17 @@ objects that are dispatched by the dispatcher. * Rename `ExoTrackSelection.blacklist` to `excludeTrack` and `isBlacklisted` to `isTrackExcluded`. - * Deprecate `Player.COMMAND_GET_MEDIA_ITEMS_METADATA` and - `COMMAND_SET_MEDIA_ITEMS_METADATA`. Use `COMMAND_GET_METADATA` and - `COMMAND_SET_PLAYLIST_METADATA` instead. + * Add commands to Player: + * `COMMAND_GET_METADATA` + * `COMMAND_SET_PLAYLIST_METADATA` + * `COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS` + * `COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS` + * Add overloaded methods to Player which allow users to specify volume + flags: + * `void setDeviceVolume(int, int)` + * `void increaseDeviceVolume(int)` + * `void decreaseDeviceVolume(int)` + * `void setDeviceMuted(boolean, int)` * Add `Buffer.isLastSample()` that denotes if `Buffer` contains flag `C.BUFFER_FLAG_LAST_SAMPLE`. * Fix issue where last frame may not be rendered if the last sample with @@ -64,13 +72,16 @@ * Fix parsing of H.265 SPS in MPEG-TS files by re-using the parsing logic already used by RTSP and MP4 extractors ([#303](https://github.com/androidx/media/issues/303)). +* ExoPlayer: + * Allow ExoPlayer to have control of device volume methods only if + explicitly opted in. Use + `ExoPlayer.Builder.setDeviceVolumeControlEnabled` to have access to: + * `getDeviceVolume()` + * `isDeviceMuted()` + * `setDeviceVolume(int)` and `setDeviceVolume(int, int)` + * `increaseDeviceVolume(int)` and `increaseDeviceVolume(int, int)` + * `decreaseDeviceVolume(int)` and `decreaseDeviceVolume(int, int)` * Session: - * Deprecate 4 volume-controlling methods in `Player` and add overloaded - methods which allow users to specify volume flags: - * `void setDeviceVolume(int, int)` - * `void increaseDeviceVolume(int)` - * `void decreaseDeviceVolume(int)` - * `void setDeviceMuted(boolean, int)` * Fix issue where `MediaController` doesn't update its available commands when connected to a legacy `MediaSessionCompat` that updates its actions. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index bb40ee654a5..7bc70b0f1b5 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -479,6 +479,7 @@ final class Builder { @C.WakeMode /* package */ int wakeMode; /* package */ boolean handleAudioBecomingNoisy; /* package */ boolean skipSilenceEnabled; + /* package */ boolean deviceVolumeControlEnabled; @C.VideoScalingMode /* package */ int videoScalingMode; @C.VideoChangeFrameRateStrategy /* package */ int videoChangeFrameRateStrategy; /* package */ boolean useLazyPreparation; @@ -918,6 +919,21 @@ public Builder setSkipSilenceEnabled(boolean skipSilenceEnabled) { return this; } + /** + * Sets whether the player is allowed to set, increase, decrease or mute device volume. + * + * @param deviceVolumeControlEnabled Whether controlling device volume is enabled. + * @return This builder. + * @throws IllegalStateException If {@link #build()} has already been called. + */ + @CanIgnoreReturnValue + @UnstableApi + public Builder setDeviceVolumeControlEnabled(boolean deviceVolumeControlEnabled) { + checkState(!buildCalled); + this.deviceVolumeControlEnabled = deviceVolumeControlEnabled; + return this; + } + /** * Sets the {@link C.VideoScalingMode} that will be used by the player. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 2bdbb02e577..e063f6c53b3 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -168,7 +168,7 @@ private final FrameMetadataListener frameMetadataListener; private final AudioBecomingNoisyManager audioBecomingNoisyManager; private final AudioFocusManager audioFocusManager; - private final StreamVolumeManager streamVolumeManager; + @Nullable private final StreamVolumeManager streamVolumeManager; private final WakeLockManager wakeLockManager; private final WifiLockManager wifiLockManager; private final long detachSurfaceTimeoutMs; @@ -228,6 +228,7 @@ private long maskingWindowPositionMs; @SuppressLint("HandlerLeak") + @SuppressWarnings("deprecation") // Control flow for old volume commands public ExoPlayerImpl(ExoPlayer.Builder builder, @Nullable Player wrappingPlayer) { constructorFinished = new ConditionVariable(); try { @@ -306,17 +307,17 @@ public ExoPlayerImpl(ExoPlayer.Builder builder, @Nullable Player wrappingPlayer) COMMAND_GET_TRACKS, COMMAND_GET_AUDIO_ATTRIBUTES, COMMAND_GET_VOLUME, - COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_VOLUME, - COMMAND_SET_DEVICE_VOLUME, - COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, - COMMAND_ADJUST_DEVICE_VOLUME, - COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, COMMAND_SET_VIDEO_SURFACE, COMMAND_GET_TEXT, COMMAND_RELEASE) .addIf( COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported()) + .addIf(COMMAND_GET_DEVICE_VOLUME, builder.deviceVolumeControlEnabled) + .addIf(COMMAND_SET_DEVICE_VOLUME, builder.deviceVolumeControlEnabled) + .addIf(COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, builder.deviceVolumeControlEnabled) + .addIf(COMMAND_ADJUST_DEVICE_VOLUME, builder.deviceVolumeControlEnabled) + .addIf(COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, builder.deviceVolumeControlEnabled) .build(); availableCommands = new Commands.Builder() @@ -381,9 +382,13 @@ public ExoPlayerImpl(ExoPlayer.Builder builder, @Nullable Player wrappingPlayer) audioBecomingNoisyManager.setEnabled(builder.handleAudioBecomingNoisy); audioFocusManager = new AudioFocusManager(builder.context, eventHandler, componentListener); audioFocusManager.setAudioAttributes(builder.handleAudioFocus ? audioAttributes : null); - streamVolumeManager = - new StreamVolumeManager(builder.context, eventHandler, componentListener); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); + if (builder.deviceVolumeControlEnabled) { + streamVolumeManager = + new StreamVolumeManager(builder.context, eventHandler, componentListener); + streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(audioAttributes.usage)); + } else { + streamVolumeManager = null; + } wakeLockManager = new WakeLockManager(builder.context); wakeLockManager.setEnabled(builder.wakeMode != C.WAKE_MODE_NONE); wifiLockManager = new WifiLockManager(builder.context); @@ -999,7 +1004,9 @@ public void release() { keepSessionIdAudioTrack = null; } audioBecomingNoisyManager.setEnabled(false); - streamVolumeManager.release(); + if (streamVolumeManager != null) { + streamVolumeManager.release(); + } wakeLockManager.setStayAwake(false); wifiLockManager.setStayAwake(false); audioFocusManager.release(); @@ -1421,7 +1428,10 @@ public void setAudioAttributes(AudioAttributes newAudioAttributes, boolean handl if (!Util.areEqual(this.audioAttributes, newAudioAttributes)) { this.audioAttributes = newAudioAttributes; sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_AUDIO_ATTRIBUTES, newAudioAttributes); - streamVolumeManager.setStreamType(Util.getStreamTypeForAudioUsage(newAudioAttributes.usage)); + if (streamVolumeManager != null) { + streamVolumeManager.setStreamType( + Util.getStreamTypeForAudioUsage(newAudioAttributes.usage)); + } // Queue event only and flush after updating playWhenReady in case both events are triggered. listeners.queueEvent( EVENT_AUDIO_ATTRIBUTES_CHANGED, @@ -1703,13 +1713,21 @@ public DeviceInfo getDeviceInfo() { @Override public int getDeviceVolume() { verifyApplicationThread(); - return streamVolumeManager.getVolume(); + if (streamVolumeManager != null) { + return streamVolumeManager.getVolume(); + } else { + return 0; + } } @Override public boolean isDeviceMuted() { verifyApplicationThread(); - return streamVolumeManager.isMuted(); + if (streamVolumeManager != null) { + return streamVolumeManager.isMuted(); + } else { + return false; + } } /** @@ -1719,13 +1737,17 @@ public boolean isDeviceMuted() { @Override public void setDeviceVolume(int volume) { verifyApplicationThread(); - streamVolumeManager.setVolume(volume, C.VOLUME_FLAG_SHOW_UI); + if (streamVolumeManager != null) { + streamVolumeManager.setVolume(volume, C.VOLUME_FLAG_SHOW_UI); + } } @Override public void setDeviceVolume(int volume, @C.VolumeFlags int flags) { verifyApplicationThread(); - streamVolumeManager.setVolume(volume, flags); + if (streamVolumeManager != null) { + streamVolumeManager.setVolume(volume, flags); + } } /** @@ -1735,13 +1757,17 @@ public void setDeviceVolume(int volume, @C.VolumeFlags int flags) { @Override public void increaseDeviceVolume() { verifyApplicationThread(); - streamVolumeManager.increaseVolume(C.VOLUME_FLAG_SHOW_UI); + if (streamVolumeManager != null) { + streamVolumeManager.increaseVolume(C.VOLUME_FLAG_SHOW_UI); + } } @Override public void increaseDeviceVolume(@C.VolumeFlags int flags) { verifyApplicationThread(); - streamVolumeManager.increaseVolume(flags); + if (streamVolumeManager != null) { + streamVolumeManager.increaseVolume(flags); + } } /** @@ -1751,13 +1777,17 @@ public void increaseDeviceVolume(@C.VolumeFlags int flags) { @Override public void decreaseDeviceVolume() { verifyApplicationThread(); - streamVolumeManager.decreaseVolume(C.VOLUME_FLAG_SHOW_UI); + if (streamVolumeManager != null) { + streamVolumeManager.decreaseVolume(C.VOLUME_FLAG_SHOW_UI); + } } @Override public void decreaseDeviceVolume(@C.VolumeFlags int flags) { verifyApplicationThread(); - streamVolumeManager.decreaseVolume(flags); + if (streamVolumeManager != null) { + streamVolumeManager.decreaseVolume(flags); + } } /** @@ -1767,13 +1797,17 @@ public void decreaseDeviceVolume(@C.VolumeFlags int flags) { @Override public void setDeviceMuted(boolean muted) { verifyApplicationThread(); - streamVolumeManager.setMuted(muted, C.VOLUME_FLAG_SHOW_UI); + if (streamVolumeManager != null) { + streamVolumeManager.setMuted(muted, C.VOLUME_FLAG_SHOW_UI); + } } @Override public void setDeviceMuted(boolean muted, @C.VolumeFlags int flags) { verifyApplicationThread(); - streamVolumeManager.setMuted(muted, flags); + if (streamVolumeManager != null) { + streamVolumeManager.setMuted(muted, flags); + } } @Override @@ -2797,10 +2831,10 @@ private void updatePriorityTaskManagerForIsLoadingChange(boolean isLoading) { } } - private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { + private static DeviceInfo createDeviceInfo(@Nullable StreamVolumeManager streamVolumeManager) { return new DeviceInfo.Builder(DeviceInfo.PLAYBACK_TYPE_LOCAL) - .setMinVolume(streamVolumeManager.getMinVolume()) - .setMaxVolume(streamVolumeManager.getMaxVolume()) + .setMinVolume(streamVolumeManager != null ? streamVolumeManager.getMinVolume() : 0) + .setMaxVolume(streamVolumeManager != null ? streamVolumeManager.getMaxVolume() : 0) .build(); } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index c2a093a1be1..dd74cd6b9e0 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -100,6 +100,7 @@ import androidx.media3.common.AdPlaybackState; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; +import androidx.media3.common.DeviceInfo; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; @@ -9155,6 +9156,7 @@ public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) { player.release(); } + @SuppressWarnings("deprecation") // Checking old volume commands @Test public void isCommandAvailable_isTrueForAvailableCommands() { ExoPlayer player = new TestExoPlayerBuilder(context).build(); @@ -9184,10 +9186,7 @@ public void isCommandAvailable_isTrueForAvailableCommands() { assertThat(player.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_VOLUME)).isTrue(); - assertThat(player.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_VOLUME)).isTrue(); - assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isTrue(); - assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_TEXT)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_TRACK_SELECTION_PARAMETERS)).isTrue(); @@ -9195,6 +9194,33 @@ public void isCommandAvailable_isTrueForAvailableCommands() { assertThat(player.isCommandAvailable(COMMAND_RELEASE)).isTrue(); } + @SuppressWarnings("deprecation") // Checking old volume commands + @Test + public void isCommandAvailable_withDeviceVolumeControlEnabled_isTrueForDeviceVolumeCommands() { + ExoPlayer player = + new TestExoPlayerBuilder(context).setDeviceVolumeControlEnabled(true).build(); + + assertThat(player.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS)).isTrue(); + } + + @SuppressWarnings("deprecation") // Checking old volume commands + @Test + public void + isCommandAvailable_withoutDeviceVolumeControlEnabled_isFalseForDeviceVolumeCommands() { + ExoPlayer player = + new TestExoPlayerBuilder(context).setDeviceVolumeControlEnabled(false).build(); + + assertThat(player.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isFalse(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isFalse(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isFalse(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS)).isFalse(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS)).isFalse(); + } + @Test public void isCommandAvailable_duringAd_isFalseForSeekCommands() throws Exception { AdPlaybackState adPlaybackState = @@ -9529,7 +9555,9 @@ public void removeMediaItem_atTheEnd_notifiesAvailableCommandsChanged() { Player.Commands defaultCommands = createWithDefaultCommands(); Player.Commands commandsWithSeekToNextWindow = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT); - Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty= */ true); + Player.Commands emptyTimelineCommands = + createWithDefaultCommands( + /* isTimelineEmpty= */ true, /* allowDeviceVolumeControl= */ false); Player.Listener mockListener = mock(Player.Listener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -9557,7 +9585,9 @@ public void removeMediaItem_atTheStart_notifiesAvailableCommandsChanged() { Player.Commands defaultCommands = createWithDefaultCommands(); Player.Commands commandsWithSeekToPreviousWindow = createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); - Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty= */ true); + Player.Commands emptyTimelineCommands = + createWithDefaultCommands( + /* isTimelineEmpty= */ true, /* allowDeviceVolumeControl= */ false); Player.Listener mockListener = mock(Player.Listener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -12323,7 +12353,9 @@ public void releaseAfterVolumeChanges_triggerPendingVolumeEventInListener() thro @Test public void releaseAfterVolumeChanges_triggerPendingDeviceVolumeEventsInListener() { ExoPlayer player = - new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()).build(); + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(true) + .build(); Player.Listener listener = mock(Player.Listener.class); player.addListener(listener); @@ -12343,6 +12375,113 @@ public void releaseAfterVolumeChanges_triggerPendingDeviceVolumeEventsInListener verify(listener, atLeast(2)).onDeviceVolumeChanged(anyInt(), anyBoolean()); } + @Test + public void setDeviceMutedWithoutDeviceVolumeControl_noEffectDeviceRemainsUnmuted() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.setDeviceMuted(/* muted= */ true, /* flags= */ 0); // no volume control, no effect + boolean isActuallyMuted = player.isDeviceMuted(); + player.release(); + + assertThat(isActuallyMuted).isFalse(); + verify(listener, times(0)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void setDeviceMutedWithDeviceVolumeControl_deviceGetsMuted() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(true) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.setDeviceMuted(/* muted= */ true, /* flags= */ 0); + boolean isActuallyMuted = player.isDeviceMuted(); + player.release(); + + assertThat(isActuallyMuted).isTrue(); + verify(listener).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void increaseDeviceVolumeWithoutDeviceVolumeControl_deviceVolumeUnchanged() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.increaseDeviceVolume(/* flags= */ 0); // no volume control, no effect + player.release(); + + verify(listener, times(0)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void decreaseDeviceVolumeWithoutDeviceVolumeControl_deviceVolumeUnchanged() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + player.decreaseDeviceVolume(/* flags= */ 0); // no volume control, no effect + player.release(); + + verify(listener, times(0)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void getDeviceVolumeWithoutDeviceVolumeControl_returnsZero() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + int initialDeviceVolume = player.getDeviceVolume(); + player.setDeviceVolume(10, /* flags= */ 0); + int setDeviceVolumeAt10 = player.getDeviceVolume(); + player.increaseDeviceVolume(/* flags= */ 0); + player.increaseDeviceVolume(/* flags= */ 0); + int setDeviceVolumeAt12 = player.getDeviceVolume(); + player.decreaseDeviceVolume(/* flags= */ 0); + int setDeviceVolumeAt11 = player.getDeviceVolume(); + player.release(); + + assertThat(initialDeviceVolume).isEqualTo(0); + assertThat(setDeviceVolumeAt10).isEqualTo(0); + assertThat(setDeviceVolumeAt12).isEqualTo(0); + assertThat(setDeviceVolumeAt11).isEqualTo(0); + verify(listener, times(0)).onDeviceVolumeChanged(anyInt(), anyBoolean()); + } + + @Test + public void getDeviceInfoWithoutDeviceVolumeControl_returnsZeroForMinMaxVolume() { + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setDeviceVolumeControlEnabled(false) + .build(); + Player.Listener listener = mock(Player.Listener.class); + player.addListener(listener); + + DeviceInfo deviceInfo = player.getDeviceInfo(); + int minVolume = deviceInfo.minVolume; + int maxVolume = deviceInfo.maxVolume; + + assertThat(minVolume).isEqualTo(0); + assertThat(maxVolume).isEqualTo(0); + } + @Test public void loadControlBackBuffer_withInsufficientMemoryLimits_stillContinuesPlayback() throws Exception { @@ -12482,8 +12621,11 @@ private static boolean containsEvent(List eventsList, @Player.Eve return false; } + @SuppressWarnings("deprecation") // Control flow for the old volume commands private static Player.Commands createWithDefaultCommands( - boolean isTimelineEmpty, @Player.Command int... additionalCommands) { + boolean isTimelineEmpty, + boolean allowDeviceVolumeControl, + @Player.Command int... additionalCommands) { Player.Commands.Builder builder = new Player.Commands.Builder(); builder.addAll( COMMAND_PLAY_PAUSE, @@ -12502,12 +12644,7 @@ private static Player.Commands createWithDefaultCommands( COMMAND_SET_MEDIA_ITEM, COMMAND_GET_AUDIO_ATTRIBUTES, COMMAND_GET_VOLUME, - COMMAND_GET_DEVICE_VOLUME, COMMAND_SET_VOLUME, - COMMAND_SET_DEVICE_VOLUME, - COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, - COMMAND_ADJUST_DEVICE_VOLUME, - COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, COMMAND_SET_VIDEO_SURFACE, COMMAND_GET_TEXT, COMMAND_SET_TRACK_SELECTION_PARAMETERS, @@ -12516,13 +12653,22 @@ private static Player.Commands createWithDefaultCommands( if (!isTimelineEmpty) { builder.add(COMMAND_SEEK_TO_PREVIOUS); } + if (allowDeviceVolumeControl) { + builder.addAll( + COMMAND_GET_DEVICE_VOLUME, + COMMAND_SET_DEVICE_VOLUME, + COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, + COMMAND_ADJUST_DEVICE_VOLUME, + COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS); + } builder.addAll(additionalCommands); return builder.build(); } private static Player.Commands createWithDefaultCommands( @Player.Command int... additionalCommands) { - return createWithDefaultCommands(/* isTimelineEmpty= */ false, additionalCommands); + return createWithDefaultCommands( + /* isTimelineEmpty= */ false, /* allowDeviceVolumeControl= */ false, additionalCommands); } // Internal classes. diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java index afc58e3ef8c..2f9dda142c1 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java @@ -54,6 +54,7 @@ public class TestExoPlayerBuilder { private @MonotonicNonNull Looper looper; private long seekBackIncrementMs; private long seekForwardIncrementMs; + private boolean deviceVolumeControlEnabled; public TestExoPlayerBuilder(Context context) { this.context = context; @@ -67,6 +68,7 @@ public TestExoPlayerBuilder(Context context) { } seekBackIncrementMs = C.DEFAULT_SEEK_BACK_INCREMENT_MS; seekForwardIncrementMs = C.DEFAULT_SEEK_FORWARD_INCREMENT_MS; + deviceVolumeControlEnabled = false; } /** @@ -282,6 +284,18 @@ public TestExoPlayerBuilder setSeekForwardIncrementMs(long seekForwardIncrementM return this; } + /** + * Sets the variable controlling player's ability to get/set device volume. + * + * @param deviceVolumeControlEnabled Whether the player can get/set device volume. + * @return This builder. + */ + @CanIgnoreReturnValue + public TestExoPlayerBuilder setDeviceVolumeControlEnabled(boolean deviceVolumeControlEnabled) { + this.deviceVolumeControlEnabled = deviceVolumeControlEnabled; + return this; + } + /** Returns the seek forward increment used by the player. */ public long getSeekForwardIncrementMs() { return seekForwardIncrementMs; @@ -322,7 +336,8 @@ public ExoPlayer build() { .setUseLazyPreparation(useLazyPreparation) .setLooper(looper) .setSeekBackIncrementMs(seekBackIncrementMs) - .setSeekForwardIncrementMs(seekForwardIncrementMs); + .setSeekForwardIncrementMs(seekForwardIncrementMs) + .setDeviceVolumeControlEnabled(deviceVolumeControlEnabled); if (mediaSourceFactory != null) { builder.setMediaSourceFactory(mediaSourceFactory); } From a8c56f2c60c60c5030a3aef98abac9e14d897bba Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 15 May 2023 18:20:16 +0100 Subject: [PATCH 017/516] Add Player.replaceMediaItem(s) This methods allows to replace single items or a range of items directly without using separate operations for add and remove. The advantage is more readable code for apps and the potential for player implementations to optimize this process (e.g. only replace values without interrupting playback). The current change just introduces the API with its default behavior. The default logic will be removed again in the future in favor of better logic in the Player implementations. Issue: google/ExoPlayer#8046 PiperOrigin-RevId: 532151471 (cherry picked from commit b1cfeb04a0e9a1e0f08917e621bd40968db68c01) --- RELEASENOTES.md | 4 +++ api.txt | 2 ++ .../media3/common/ForwardingPlayer.java | 12 +++++++ .../java/androidx/media3/common/Player.java | 35 +++++++++++++++++++ .../media3/common/SimpleBasePlayer.java | 12 +++++++ 5 files changed, 65 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 93bdd43a99d..19abe555823 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,10 @@ ### Unreleased changes +* Core library: + * Add `Player.replaceMediaItem(s)` as a shortcut to adding and removing + items at the same position + ([#8046](https://github.com/google/ExoPlayer/issues/8046)). * Session: * Add `androidx.media3.session.MediaButtonReceiver` to enable apps to implement playback resumption with media button events sent by, for diff --git a/api.txt b/api.txt index 14a72e75967..d6dc7db144f 100644 --- a/api.txt +++ b/api.txt @@ -751,6 +751,8 @@ package androidx.media3.common { method public void removeListener(androidx.media3.common.Player.Listener); method public void removeMediaItem(int); method public void removeMediaItems(int, int); + method public default void replaceMediaItem(int, androidx.media3.common.MediaItem); + method public default void replaceMediaItems(int, int, java.util.List); method public void seekBack(); method public void seekForward(); method public void seekTo(long); diff --git a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java index 9695c998b72..d2162e56111 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java @@ -147,6 +147,18 @@ public void moveMediaItems(int fromIndex, int toIndex, int newIndex) { player.moveMediaItems(fromIndex, toIndex, newIndex); } + /** Calls {@link Player#replaceMediaItem(int, MediaItem)} on the delegate. */ + @Override + public void replaceMediaItem(int index, MediaItem mediaItem) { + player.replaceMediaItem(index, mediaItem); + } + + /** Calls {@link Player#replaceMediaItems(int, int, List)} on the delegate. */ + @Override + public void replaceMediaItems(int fromIndex, int toIndex, List mediaItems) { + player.replaceMediaItems(fromIndex, toIndex, mediaItems); + } + /** Calls {@link Player#removeMediaItem(int)} on the delegate. */ @Override public void removeMediaItem(int index) { diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 98756906589..8d8e440175d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -37,6 +37,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -1754,6 +1755,8 @@ default void onMetadata(Metadata metadata) {} *

  • {@link #setMediaItems(List)} *
  • {@link #setMediaItems(List, boolean)} *
  • {@link #setMediaItems(List, int, long)} + *
  • {@link #replaceMediaItem(int, MediaItem)} + *
  • {@link #replaceMediaItems(int, int, List)} * */ int COMMAND_CHANGE_MEDIA_ITEMS = 20; @@ -2055,6 +2058,38 @@ default void onMetadata(Metadata metadata) {} */ void moveMediaItems(int fromIndex, int toIndex, int newIndex); + /** + * Replaces the media item at the given index of the playlist. + * + *

    This method must only be called if {@link #COMMAND_CHANGE_MEDIA_ITEMS} is {@linkplain + * #getAvailableCommands() available}. + * + * @param index The index at which to replace the media item. If the index is larger than the size + * of the playlist, the request is ignored. + * @param mediaItem The new {@link MediaItem}. + */ + default void replaceMediaItem(int index, MediaItem mediaItem) { + replaceMediaItems( + /* fromIndex= */ index, /* toIndex= */ index + 1, ImmutableList.of(mediaItem)); + } + + /** + * Replaces the media items at the given range of the playlist. + * + *

    This method must only be called if {@link #COMMAND_CHANGE_MEDIA_ITEMS} is {@linkplain + * #getAvailableCommands() available}. + * + * @param fromIndex The start of the range. If the index is larger than the size of the playlist, + * the request is ignored. + * @param toIndex The first item not to be included in the range (exclusive). If the index is + * larger than the size of the playlist, items up to the end of the playlist are replaced. + * @param mediaItems The {@linkplain MediaItem media items} to replace the range with. + */ + default void replaceMediaItems(int fromIndex, int toIndex, List mediaItems) { + addMediaItems(toIndex, mediaItems); + removeMediaItems(fromIndex, toIndex); + } + /** * Removes the media item at the given index of the playlist. * diff --git a/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java b/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java index 63ec4f7dc51..a7bde0bc878 100644 --- a/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/SimpleBasePlayer.java @@ -2141,6 +2141,18 @@ public final void moveMediaItems(int fromIndex, int toIndex, int newIndex) { }); } + @Override + public final void replaceMediaItem(int index, MediaItem mediaItem) { + replaceMediaItems( + /* fromIndex= */ index, /* toIndex= */ index + 1, ImmutableList.of(mediaItem)); + } + + @Override + public final void replaceMediaItems(int fromIndex, int toIndex, List mediaItems) { + addMediaItems(toIndex, mediaItems); + removeMediaItems(fromIndex, toIndex); + } + @Override public final void removeMediaItems(int fromIndex, int toIndex) { verifyApplicationThreadAndInitState(); From fc6429b2d4532271140f214aa5b4c8eb55c29e58 Mon Sep 17 00:00:00 2001 From: rohks Date: Mon, 15 May 2023 20:30:50 +0100 Subject: [PATCH 018/516] Remove two deprecated `InvalidResponseCodeException` constructors Use a non-deprecated constructor that accepts additional fields(`cause`, `responseBody`) to enhance error logging. #minor-release PiperOrigin-RevId: 532190896 (cherry picked from commit b120ef65ed6425c3193bb4d68a11ce9ac2372347) --- RELEASENOTES.md | 3 ++ .../media3/datasource/HttpDataSource.java | 38 ------------------- 2 files changed, 3 insertions(+), 38 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 19abe555823..fba52768991 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -22,6 +22,9 @@ `DefaultBandwidthMeter.Builder` instead. * Remove `DefaultDrmSessionManager` constructors, use `DefaultDrmSessionManager.Builder` instead. + * Remove two deprecated `HttpDataSource.InvalidResponseCodeException` + constructors, use a non-deprecated constructor that accepts additional + fields(`cause`, `responseBody`) to enhance error logging. ### 1.1.0-alpha01 (2023-05-10) diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java index e83e21b4fd8..ac54956a088 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/HttpDataSource.java @@ -22,7 +22,6 @@ import androidx.annotation.Nullable; import androidx.media3.common.PlaybackException; import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; import com.google.common.base.Ascii; import com.google.common.base.Predicate; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -425,43 +424,6 @@ final class InvalidResponseCodeException extends HttpDataSourceException { /** The response body. */ public final byte[] responseBody; - /** - * @deprecated Use {@link #InvalidResponseCodeException(int, String, IOException, Map, DataSpec, - * byte[])}. - */ - @UnstableApi - @Deprecated - public InvalidResponseCodeException( - int responseCode, Map> headerFields, DataSpec dataSpec) { - this( - responseCode, - /* responseMessage= */ null, - /* cause= */ null, - headerFields, - dataSpec, - /* responseBody= */ Util.EMPTY_BYTE_ARRAY); - } - - /** - * @deprecated Use {@link #InvalidResponseCodeException(int, String, IOException, Map, DataSpec, - * byte[])}. - */ - @UnstableApi - @Deprecated - public InvalidResponseCodeException( - int responseCode, - @Nullable String responseMessage, - Map> headerFields, - DataSpec dataSpec) { - this( - responseCode, - responseMessage, - /* cause= */ null, - headerFields, - dataSpec, - /* responseBody= */ Util.EMPTY_BYTE_ARRAY); - } - @UnstableApi public InvalidResponseCodeException( int responseCode, From 04a00c7ac4a3204f116c4ee62f056f7352c719c9 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Mon, 15 May 2023 21:04:56 +0100 Subject: [PATCH 019/516] Test: Suppress SSIM timeouts on galaxy fold/flip. This is a failure only in SSIM, so it seems unlikely we'll prioritize this over other work or bugs soon. Suppress test failures to reduce triage burden. PiperOrigin-RevId: 532200729 (cherry picked from commit 62afbe87bb05890bbbb60ff4e3f0b2fd65aea433) --- .../media3/transformer/mh/ExportTest.java | 15 ++++++++++++--- .../transformer/mh/TranscodeQualityTest.java | 6 ++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/ExportTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/ExportTest.java index dc0d777f0d6..65263b2a353 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/ExportTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/ExportTest.java @@ -73,8 +73,11 @@ public void export() throws Exception { .build(); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_URI_STRING)); + // TODO: b/239983127 - Make requestCalculateSsim always true. + boolean requestCalculateSsim = + !Util.MODEL.equals("SM-F711U1") && !Util.MODEL.equals("SM-F926U1"); new TransformerAndroidTestRunner.Builder(context, transformer) - .setRequestCalculateSsim(true) + .setRequestCalculateSsim(requestCalculateSsim) .build() .run(testId, mediaItem); } @@ -114,8 +117,11 @@ public void exportToSpecificBitrate() throws Exception { MediaItem.fromUri(Uri.parse(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_URI_STRING)); EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build(); + // TODO: b/239983127 - Make requestCalculateSsim always true. + boolean requestCalculateSsim = + !Util.MODEL.equals("SM-F711U1") && !Util.MODEL.equals("SM-F926U1"); new TransformerAndroidTestRunner.Builder(context, transformer) - .setRequestCalculateSsim(true) + .setRequestCalculateSsim(requestCalculateSsim) .build() .run(testId, editedMediaItem); } @@ -187,8 +193,11 @@ public void exportNoAudio() throws Exception { MediaItem.fromUri(Uri.parse(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_URI_STRING)); EditedMediaItem editedMediaItem = new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build(); + // TODO: b/239983127 - Make requestCalculateSsim always true. + boolean requestCalculateSsim = + !Util.MODEL.equals("SM-F711U1") && !Util.MODEL.equals("SM-F926U1"); new TransformerAndroidTestRunner.Builder(context, transformer) - .setRequestCalculateSsim(true) + .setRequestCalculateSsim(requestCalculateSsim) .build() .run(testId, editedMediaItem); } diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TranscodeQualityTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TranscodeQualityTest.java index 3ae1c6ff7f6..76d6d2ada3c 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TranscodeQualityTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TranscodeQualityTest.java @@ -17,11 +17,13 @@ package androidx.media3.transformer.mh; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeTrue; import android.content.Context; import android.net.Uri; import androidx.media3.common.MediaItem; import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Util; import androidx.media3.transformer.AndroidTestUtil; import androidx.media3.transformer.DefaultEncoderFactory; import androidx.media3.transformer.EditedMediaItem; @@ -51,6 +53,8 @@ public void exportHighQualityTargetingAvcToAvc1920x1080_ssimIsGreaterThan95Perce /* outputFormat= */ AndroidTestUtil.MP4_ASSET_WITH_INCREASING_TIMESTAMPS_FORMAT)) { return; } + // TODO: b/239983127 - Remove this test skip on these devices. + assumeTrue(!Util.MODEL.equals("SM-F711U1") && !Util.MODEL.equals("SM-F926U1")); Transformer transformer = new Transformer.Builder(context) @@ -96,6 +100,8 @@ public void transcodeAvcToHevc_ssimIsGreaterThan90Percent() throws Exception { .build())) { return; } + // TODO: b/239983127 - Remove this test skip on these devices. + assumeTrue(!Util.MODEL.equals("SM-F711U1") && !Util.MODEL.equals("SM-F926U1")); Transformer transformer = new Transformer.Builder(context) From 43f8d080dfc3e8958715e50685b33c4248eceee5 Mon Sep 17 00:00:00 2001 From: tofunmi Date: Tue, 16 May 2023 10:28:04 +0100 Subject: [PATCH 020/516] Update mixed input test to include images of different aspect ratios Makes this test a little more thorough. PiperOrigin-RevId: 532386515 (cherry picked from commit 88642587ac9faa4ba0d7ea9870fa1898ef90277e) --- .../media3/transformer/AndroidTestUtil.java | 1 + .../TransformerMixedInputEndToEndTest.java | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java index 016f93483ae..7d24c881f19 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/AndroidTestUtil.java @@ -64,6 +64,7 @@ public final class AndroidTestUtil { public static final String PNG_ASSET_URI_STRING = "asset:///media/bitmap/input_images/media3test.png"; + public static final String JPG_ASSET_URI_STRING = "asset:///media/bitmap/input_images/london.jpg"; public static final String MP4_ASSET_URI_STRING = "asset:///media/mp4/sample.mp4"; public static final Format MP4_ASSET_FORMAT = diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMixedInputEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMixedInputEndToEndTest.java index d57403da2e7..146c75ee144 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMixedInputEndToEndTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerMixedInputEndToEndTest.java @@ -17,6 +17,7 @@ package androidx.media3.transformer; +import static androidx.media3.transformer.AndroidTestUtil.JPG_ASSET_URI_STRING; import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_URI_STRING; import static androidx.media3.transformer.AndroidTestUtil.PNG_ASSET_URI_STRING; import static com.google.common.truth.Truth.assertThat; @@ -89,8 +90,6 @@ public void videoEditing_withVideoThenImageInputs_completesWithCorrectFrameCount int imageFrameCount = 32; EditedMediaItem imageEditedMediaItem = createImageEditedMediaItem(PNG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - // Result of the following command: - // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames sample.mp4 EditedMediaItem videoEditedMediaItem = createVideoEditedMediaItem(MP4_ASSET_URI_STRING, /* height= */ 480); ExportTestResult result = @@ -117,10 +116,10 @@ public void videoEditing_withVideoThenImageInputs_completesWithCorrectFrameCount .build(); int imageFrameCount = 33; - EditedMediaItem imageEditedMediaItem = + EditedMediaItem imageEditedMediaItem1 = createImageEditedMediaItem(PNG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); - // Result of the following command: - // ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames sample.mp4 + EditedMediaItem imageEditedMediaItem2 = + createImageEditedMediaItem(JPG_ASSET_URI_STRING, /* frameCount= */ imageFrameCount); EditedMediaItem videoEditedMediaItem = createVideoEditedMediaItem(MP4_ASSET_URI_STRING, /* height= */ 360); ExportTestResult result = @@ -132,10 +131,10 @@ public void videoEditing_withVideoThenImageInputs_completesWithCorrectFrameCount ImmutableList.of( videoEditedMediaItem, videoEditedMediaItem, - imageEditedMediaItem, - imageEditedMediaItem, + imageEditedMediaItem1, + imageEditedMediaItem2, videoEditedMediaItem, - imageEditedMediaItem, + imageEditedMediaItem1, videoEditedMediaItem))); assertThat(result.exportResult.videoFrameCount) From 3633ad15cc65a4791ea2104b871ec2be09eade5f Mon Sep 17 00:00:00 2001 From: ibaker Date: Tue, 16 May 2023 11:48:31 +0100 Subject: [PATCH 021/516] Update release notes for Media3 1.0.2 #minor-release PiperOrigin-RevId: 532404001 (cherry picked from commit 1a38a0c41ebad512e9baab14d562f06de934177f) --- RELEASENOTES.md | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index fba52768991..c4589684799 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -11,9 +11,6 @@ implement playback resumption with media button events sent by, for example, a Bluetooth headset ([#167](https://github.com/androidx/media/issues/167)). - * Fix bug where a combined `Timeline` and position update in a - `MediaSession` may cause a `MediaController` to throw an - `IllegalStateException`. * Remove deprecated symbols: * Remove two deprecated `SimpleCache` constructors, use a non-deprecated constructor that takes a `DatabaseProvider` instead for better @@ -26,6 +23,41 @@ constructors, use a non-deprecated constructor that accepts additional fields(`cause`, `responseBody`) to enhance error logging. +### 1.0.2 (2023-05-18) + +This release corresponds to the +[ExoPlayer 2.18.7 release](https://github.com/google/ExoPlayer/releases/tag/r2.18.7). + +This release contains the following changes since the +[1.0.1 release](#101-2023-04-18): + +* Core library: + * Add `Buffer.isLastSample()` that denotes if `Buffer` contains flag + `C.BUFFER_FLAG_LAST_SAMPLE`. + * Fix issue where last frame may not be rendered if the last sample with + frames is dequeued without reading the 'end of stream' sample. + ([#11079](https://github.com/google/ExoPlayer/issues/11079)). +* Extractors: + * Fix parsing of H.265 SPS in MPEG-TS files by re-using the parsing logic + already used by RTSP and MP4 extractors + ([#303](https://github.com/androidx/media/issues/303)). +* Text: + * SSA: Add support for UTF-16 files if they start with a byte order mark + ([#319](https://github.com/androidx/media/issues/319)). +* Session: + * Fix issue where `MediaController` doesn't update its available commands + when connected to a legacy `MediaSessionCompat` that updates its + actions. + * Fix bug that prevented the `MediaLibraryService` from returning null for + a call from System UI to `Callback.onGetLibraryRoot` with + `params.isRecent == true` on API 30 + ([#355](https://github.com/androidx/media/issues/355)). + * Fix memory leak of `MediaSessionService` or `MediaLibraryService` + ([#346](https://github.com/androidx/media/issues/346)). + * Fix bug where a combined `Timeline` and position update in a + `MediaSession` may cause a `MediaController` to throw an + `IllegalStateException`. + ### 1.1.0-alpha01 (2023-05-10) * Core library: From 1510710c46c3759eba585c5331bda76a301b4305 Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 16 May 2023 12:06:03 +0100 Subject: [PATCH 022/516] Enable live DASH stream for IMA DAI This change enables the IMA extension to play live DASH streams with DAI support. Samples streams can be found and played in the main demo app. Issue: google/ExoPlayer#10912 #minor-release PiperOrigin-RevId: 532407708 (cherry picked from commit dab1353aadd4a093d29e1d418f012d42964e9d50) --- RELEASENOTES.md | 4 +++ demos/main/src/main/assets/media.exolist.json | 26 +++++++++---------- .../ImaServerSideAdInsertionUriBuilder.java | 4 --- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c4589684799..eff2bba439c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -6,6 +6,10 @@ * Add `Player.replaceMediaItem(s)` as a shortcut to adding and removing items at the same position ([#8046](https://github.com/google/ExoPlayer/issues/8046)). +* IMA extension: + * Enable multi-period live DASH streams for DAI. Please note that the + current implementation does not yet support seeking in live streams + ([#10912](https://github.com/google/ExoPlayer/issues/10912)). * Session: * Add `androidx.media3.session.MediaButtonReceiver` to enable apps to implement playback resumption with media button events sent by, for diff --git a/demos/main/src/main/assets/media.exolist.json b/demos/main/src/main/assets/media.exolist.json index ac7b5ce7492..3acef825d1b 100644 --- a/demos/main/src/main/assets/media.exolist.json +++ b/demos/main/src/main/assets/media.exolist.json @@ -406,6 +406,18 @@ "name": "DASH VOD: Tears of Steel (11 periods, pre/mid/post), 2/5/2 ads [5/10s]", "uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1" }, + { + "name": "DASH live: Tears of Steel (mid), 3 ads each [10 s]", + "uri": "ssai://dai.google.com/?assetKey=jNVjPZwzSkyeGiaNQTPqiQ&format=0&adsId=1" + }, + { + "name": "DASH live: New Tears of Steel (mid), 3 ads each [10 s]", + "uri": "ssai://dai.google.com/?assetKey=PSzZMzAkSXCmlJOWDmRj8Q&format=0&adsId=12" + }, + { + "name": "DASH live: Unencrypted stream with 30s ad breaks every minute", + "uri": "ssai://dai.google.com/?assetKey=0ndl1dJcRmKDUPxTRjvdog&format=0&adsId=21" + }, { "name": "Playlist: No ads - HLS VOD: Demo (skippable pre/post) - No ads", "playlist": [ @@ -434,20 +446,6 @@ } ] }, - { - "name": "Playlist: No ads - HLS Live: Big Buck Bunny (mid) - No ads", - "playlist": [ - { - "uri": "https://html5demos.com/assets/dizzy.mp4" - }, - { - "uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3" - }, - { - "uri": "https://html5demos.com/assets/dizzy.mp4" - } - ] - }, { "name": "Playlist: No ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads", "playlist": [ diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java index 6a20ed5b5b1..2342d6acead 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionUriBuilder.java @@ -381,10 +381,6 @@ public Uri build() { if (streamActivityMonitorId != null) { streamRequest.setStreamActivityMonitorId(streamActivityMonitorId); } - checkState( - streamRequest.getFormat() != StreamFormat.DASH - || TextUtils.isEmpty(streamRequest.getAssetKey()), - "DASH live streams are not supported yet."); return streamRequest; } } From 06c8433ce8eac073e9b4eea7786a74459e3fed88 Mon Sep 17 00:00:00 2001 From: ibaker Date: Tue, 16 May 2023 12:37:23 +0100 Subject: [PATCH 023/516] Remove deprecated `DownloadHelper` format-specific methods Use `forMediaItem` instead. PiperOrigin-RevId: 532414060 (cherry picked from commit 57479dd397f25fc38d4cb32cf44b63536bcdb094) --- RELEASENOTES.md | 3 + .../exoplayer/offline/DownloadHelper.java | 149 ------------------ 2 files changed, 3 insertions(+), 149 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index eff2bba439c..d318ee31cac 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -26,6 +26,9 @@ * Remove two deprecated `HttpDataSource.InvalidResponseCodeException` constructors, use a non-deprecated constructor that accepts additional fields(`cause`, `responseBody`) to enhance error logging. + * Remove `DownloadHelper.forProgressive`, `DownloadHelper.forHls`, + `DownloadHelper.forDash`, and `DownloadHelper.forSmoothStreaming`, use + `DownloadHelper.forMediaItem` instead. ### 1.0.2 (2023-05-18) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java index 2874601a705..0c66ae45c85 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadHelper.java @@ -19,7 +19,6 @@ import static androidx.media3.common.util.Util.castNonNull; import android.content.Context; -import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; @@ -27,7 +26,6 @@ import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.MediaItem; -import androidx.media3.common.MimeTypes; import androidx.media3.common.StreamKey; import androidx.media3.common.Timeline; import androidx.media3.common.TrackGroup; @@ -166,153 +164,6 @@ public static RendererCapabilities[] getRendererCapabilities(RenderersFactory re return capabilities; } - /** - * @deprecated Use {@link #forMediaItem(Context, MediaItem)} - */ - @Deprecated - public static DownloadHelper forProgressive(Context context, Uri uri) { - return forMediaItem(context, new MediaItem.Builder().setUri(uri).build()); - } - - /** - * @deprecated Use {@link #forMediaItem(Context, MediaItem)} - */ - @Deprecated - public static DownloadHelper forProgressive(Context context, Uri uri, @Nullable String cacheKey) { - return forMediaItem( - context, new MediaItem.Builder().setUri(uri).setCustomCacheKey(cacheKey).build()); - } - - /** - * @deprecated Use {@link #forMediaItem(MediaItem, TrackSelectionParameters, RenderersFactory, - * DataSource.Factory)} instead. - */ - @SuppressWarnings("deprecation") - @Deprecated - public static DownloadHelper forDash( - Context context, - Uri uri, - DataSource.Factory dataSourceFactory, - RenderersFactory renderersFactory) { - return forDash( - uri, - dataSourceFactory, - renderersFactory, - /* drmSessionManager= */ null, - getDefaultTrackSelectorParameters(context)); - } - - /** - * @deprecated Use {@link #forMediaItem(MediaItem, TrackSelectionParameters, RenderersFactory, - * DataSource.Factory, DrmSessionManager)} instead. - */ - @Deprecated - public static DownloadHelper forDash( - Uri uri, - DataSource.Factory dataSourceFactory, - RenderersFactory renderersFactory, - @Nullable DrmSessionManager drmSessionManager, - TrackSelectionParameters trackSelectionParameters) { - return forMediaItem( - new MediaItem.Builder().setUri(uri).setMimeType(MimeTypes.APPLICATION_MPD).build(), - trackSelectionParameters, - renderersFactory, - dataSourceFactory, - drmSessionManager); - } - - /** - * @deprecated Use {@link #forMediaItem(MediaItem, TrackSelectionParameters, RenderersFactory, - * DataSource.Factory)} instead. - */ - @SuppressWarnings("deprecation") - @Deprecated - public static DownloadHelper forHls( - Context context, - Uri uri, - DataSource.Factory dataSourceFactory, - RenderersFactory renderersFactory) { - return forHls( - uri, - dataSourceFactory, - renderersFactory, - /* drmSessionManager= */ null, - getDefaultTrackSelectorParameters(context)); - } - - /** - * @deprecated Use {@link #forMediaItem(MediaItem, TrackSelectionParameters, RenderersFactory, - * DataSource.Factory, DrmSessionManager)} instead. - */ - @Deprecated - public static DownloadHelper forHls( - Uri uri, - DataSource.Factory dataSourceFactory, - RenderersFactory renderersFactory, - @Nullable DrmSessionManager drmSessionManager, - TrackSelectionParameters trackSelectionParameters) { - return forMediaItem( - new MediaItem.Builder().setUri(uri).setMimeType(MimeTypes.APPLICATION_M3U8).build(), - trackSelectionParameters, - renderersFactory, - dataSourceFactory, - drmSessionManager); - } - - /** - * @deprecated Use {@link #forMediaItem(MediaItem, TrackSelectionParameters, RenderersFactory, - * DataSource.Factory)} instead. - */ - @SuppressWarnings("deprecation") - @Deprecated - public static DownloadHelper forSmoothStreaming( - Uri uri, DataSource.Factory dataSourceFactory, RenderersFactory renderersFactory) { - return forSmoothStreaming( - uri, - dataSourceFactory, - renderersFactory, - /* drmSessionManager= */ null, - DEFAULT_TRACK_SELECTOR_PARAMETERS_WITHOUT_CONTEXT); - } - - /** - * @deprecated Use {@link #forMediaItem(MediaItem, TrackSelectionParameters, RenderersFactory, - * DataSource.Factory)} instead. - */ - @SuppressWarnings("deprecation") - @Deprecated - public static DownloadHelper forSmoothStreaming( - Context context, - Uri uri, - DataSource.Factory dataSourceFactory, - RenderersFactory renderersFactory) { - return forSmoothStreaming( - uri, - dataSourceFactory, - renderersFactory, - /* drmSessionManager= */ null, - getDefaultTrackSelectorParameters(context)); - } - - /** - * @deprecated Use {@link #forMediaItem(MediaItem, TrackSelectionParameters, RenderersFactory, - * DataSource.Factory, DrmSessionManager)} instead. - */ - @Deprecated - public static DownloadHelper forSmoothStreaming( - Uri uri, - DataSource.Factory dataSourceFactory, - RenderersFactory renderersFactory, - @Nullable DrmSessionManager drmSessionManager, - TrackSelectionParameters trackSelectionParameters) { - return forMediaItem( - new MediaItem.Builder().setUri(uri).setMimeType(MimeTypes.APPLICATION_SS).build(), - trackSelectionParameters, - renderersFactory, - dataSourceFactory, - drmSessionManager); - } - /** * Creates a {@link DownloadHelper} for the given progressive media item. * From 5a72333554167c7f1121655ee1b9c27b8a7d446a Mon Sep 17 00:00:00 2001 From: rohks Date: Tue, 16 May 2023 15:23:41 +0100 Subject: [PATCH 024/516] Remove deprecated `DownloadService` constructor Use a non deprecated constructor that includes the option to provide a `channelDescriptionResourceId` parameter. #minor-release PiperOrigin-RevId: 532450975 (cherry picked from commit 022a05c376918eec0628c0eed32251882fc6f767) --- RELEASENOTES.md | 3 +++ .../exoplayer/offline/DownloadService.java | 17 ----------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d318ee31cac..8adbba7966e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -29,6 +29,9 @@ * Remove `DownloadHelper.forProgressive`, `DownloadHelper.forHls`, `DownloadHelper.forDash`, and `DownloadHelper.forSmoothStreaming`, use `DownloadHelper.forMediaItem` instead. + * Remove deprecated `DownloadService` constructor, use a non deprecated + constructor that includes the option to provide a + `channelDescriptionResourceId` parameter. ### 1.0.2 (2023-05-18) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadService.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadService.java index 9b6e63be00e..b66d7996b57 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadService.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DownloadService.java @@ -236,23 +236,6 @@ protected DownloadService( /* channelDescriptionResourceId= */ 0); } - /** - * @deprecated Use {@link #DownloadService(int, long, String, int, int)}. - */ - @Deprecated - protected DownloadService( - int foregroundNotificationId, - long foregroundNotificationUpdateInterval, - @Nullable String channelId, - @StringRes int channelNameResourceId) { - this( - foregroundNotificationId, - foregroundNotificationUpdateInterval, - channelId, - channelNameResourceId, - /* channelDescriptionResourceId= */ 0); - } - /** * Creates a DownloadService. * From 7fdae1ad0d0cec7fd93b8b16d30e799ec06cbf2d Mon Sep 17 00:00:00 2001 From: tofunmi Date: Tue, 16 May 2023 16:12:34 +0100 Subject: [PATCH 025/516] Adjust image input ForPixelWidthHeightRatio PiperOrigin-RevId: 532463400 (cherry picked from commit 63ee5ccb286727445542b11d99a455162cee49f6) --- .../media3/effect/BitmapTextureManager.java | 27 ++++++++++--------- .../effect/DefaultVideoFrameProcessor.java | 2 +- .../media3/effect/ExternalTextureManager.java | 7 ----- .../media3/effect/TextureManager.java | 2 +- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java b/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java index 3ed088b57f3..8c5371669c1 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java @@ -24,6 +24,7 @@ import android.opengl.GLUtils; import androidx.annotation.Nullable; import androidx.media3.common.C; +import androidx.media3.common.FrameInfo; import androidx.media3.common.GlTextureInfo; import androidx.media3.common.VideoFrameProcessingException; import androidx.media3.common.util.GlUtil; @@ -81,10 +82,10 @@ public void onReadyToAcceptInputFrame() { @Override public void queueInputBitmap( - Bitmap inputBitmap, long durationUs, long offsetUs, float frameRate, boolean useHdr) { + Bitmap inputBitmap, long durationUs, FrameInfo frameInfo, float frameRate, boolean useHdr) { videoFrameProcessingTaskExecutor.submit( () -> { - setupBitmap(inputBitmap, durationUs, offsetUs, frameRate, useHdr); + setupBitmap(inputBitmap, durationUs, frameInfo, frameRate, useHdr); currentInputStreamEnded = false; }); } @@ -126,9 +127,10 @@ public void release() { } }); } + // Methods that must be called on the GL thread. private void setupBitmap( - Bitmap bitmap, long durationUs, long offsetUs, float frameRate, boolean useHdr) + Bitmap bitmap, long durationUs, FrameInfo frameInfo, float frameRate, boolean useHdr) throws VideoFrameProcessingException { if (Util.SDK_INT >= 26) { checkState( @@ -141,7 +143,8 @@ private void setupBitmap( this.useHdr = useHdr; int framesToAdd = round(frameRate * (durationUs / (float) C.MICROS_PER_SECOND)); double frameDurationUs = C.MICROS_PER_SECOND / frameRate; - pendingBitmaps.add(new BitmapFrameSequenceInfo(bitmap, offsetUs, frameDurationUs, framesToAdd)); + pendingBitmaps.add( + new BitmapFrameSequenceInfo(bitmap, frameInfo, frameDurationUs, framesToAdd)); maybeQueueToShaderProgram(); } @@ -153,7 +156,7 @@ private void maybeQueueToShaderProgram() throws VideoFrameProcessingException { if (framesToQueueForCurrentBitmap == 0) { Bitmap bitmap = currentBitmapInfo.bitmap; framesToQueueForCurrentBitmap = currentBitmapInfo.numberOfFrames; - currentPresentationTimeUs = currentBitmapInfo.offsetUs; + currentPresentationTimeUs = currentBitmapInfo.frameInfo.offsetToAddUs; int currentTexId; try { if (currentGlTextureInfo != null) { @@ -161,8 +164,8 @@ private void maybeQueueToShaderProgram() throws VideoFrameProcessingException { } currentTexId = GlUtil.createTexture( - bitmap.getWidth(), - bitmap.getHeight(), + currentBitmapInfo.frameInfo.width, + currentBitmapInfo.frameInfo.height, /* useHighPrecisionColorComponents= */ useHdr); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, currentTexId); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, /* level= */ 0, bitmap, /* border= */ 0); @@ -175,8 +178,8 @@ private void maybeQueueToShaderProgram() throws VideoFrameProcessingException { currentTexId, /* fboId= */ C.INDEX_UNSET, /* rboId= */ C.INDEX_UNSET, - bitmap.getWidth(), - bitmap.getHeight()); + currentBitmapInfo.frameInfo.width, + currentBitmapInfo.frameInfo.height); } framesToQueueForCurrentBitmap--; downstreamShaderProgramCapacity--; @@ -196,14 +199,14 @@ private void maybeQueueToShaderProgram() throws VideoFrameProcessingException { /** Information to generate all the frames associated with a specific {@link Bitmap}. */ private static final class BitmapFrameSequenceInfo { public final Bitmap bitmap; - public final long offsetUs; + public final FrameInfo frameInfo; public final double frameDurationUs; public final int numberOfFrames; public BitmapFrameSequenceInfo( - Bitmap bitmap, long offsetUs, double frameDurationUs, int numberOfFrames) { + Bitmap bitmap, FrameInfo frameInfo, double frameDurationUs, int numberOfFrames) { this.bitmap = bitmap; - this.offsetUs = offsetUs; + this.frameInfo = frameInfo; this.frameDurationUs = frameDurationUs; this.numberOfFrames = numberOfFrames; } diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index 16d1eba3bee..0e811e1c586 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -362,7 +362,7 @@ public void queueInputBitmap(Bitmap inputBitmap, long durationUs, float frameRat .queueInputBitmap( inputBitmap, durationUs, - checkNotNull(nextInputFrameInfo).offsetToAddUs, + checkNotNull(nextInputFrameInfo), frameRate, /* useHdr= */ false); hasRefreshedNextInputFrameInfo = false; diff --git a/libraries/effect/src/main/java/androidx/media3/effect/ExternalTextureManager.java b/libraries/effect/src/main/java/androidx/media3/effect/ExternalTextureManager.java index 4361e4246f4..79fd28de7db 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/ExternalTextureManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/ExternalTextureManager.java @@ -18,7 +18,6 @@ import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; -import android.graphics.Bitmap; import android.graphics.SurfaceTexture; import android.view.Surface; import androidx.annotation.Nullable; @@ -115,12 +114,6 @@ public void setDefaultBufferSize(int width, int height) { surfaceTexture.setDefaultBufferSize(width, height); } - @Override - public void queueInputBitmap( - Bitmap inputBitmap, long durationUs, long offsetUs, float frameRate, boolean useHdr) { - throw new UnsupportedOperationException(); - } - @Override public Surface getInputSurface() { return surface; diff --git a/libraries/effect/src/main/java/androidx/media3/effect/TextureManager.java b/libraries/effect/src/main/java/androidx/media3/effect/TextureManager.java index 9137e4ca960..8d1d97bddd4 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/TextureManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/TextureManager.java @@ -49,7 +49,7 @@ default void setDefaultBufferSize(int width, int height) { * @param useHdr Whether input and/or output colors are HDR. */ default void queueInputBitmap( - Bitmap inputBitmap, long durationUs, long offsetUs, float frameRate, boolean useHdr) { + Bitmap inputBitmap, long durationUs, FrameInfo frameInfo, float frameRate, boolean useHdr) { throw new UnsupportedOperationException(); } From fa152ddc80393d9cbb86f383c543ca21e7d797f3 Mon Sep 17 00:00:00 2001 From: rohks Date: Tue, 16 May 2023 16:34:01 +0100 Subject: [PATCH 026/516] Remove deprecated String constants for Charsets Use Kotlin Charsets from the `kotlin.text` package, the `java.nio.charset.StandardCharsets` or the `com.google.common.base.Charsets` instead. #minor-release PiperOrigin-RevId: 532469103 (cherry picked from commit 1061135cfd68dec4d2e073c1ca9e08d57b5c6875) --- RELEASENOTES.md | 5 ++++ .../main/java/androidx/media3/common/C.java | 30 ------------------- 2 files changed, 5 insertions(+), 30 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8adbba7966e..454de6c58e6 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -32,6 +32,11 @@ * Remove deprecated `DownloadService` constructor, use a non deprecated constructor that includes the option to provide a `channelDescriptionResourceId` parameter. + * Remove deprecated String constants for Charsets (`ASCII_NAME`, + `UTF8_NAME`, `ISO88591_NAME`, `UTF16_NAME` and `UTF16LE_NAME`), use + Kotlin Charsets from the `kotlin.text` package, the + `java.nio.charset.StandardCharsets` or the + `com.google.common.base.Charsets` instead. ### 1.0.2 (2023-05-18) diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index f2cfb1ac410..075569f2b8d 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -95,36 +95,6 @@ private C() {} /** The number of bytes per float. */ @UnstableApi public static final int BYTES_PER_FLOAT = 4; - /** - * @deprecated Use {@link java.nio.charset.StandardCharsets} or {@link - * com.google.common.base.Charsets} instead. - */ - @UnstableApi @Deprecated public static final String ASCII_NAME = "US-ASCII"; - - /** - * @deprecated Use {@link java.nio.charset.StandardCharsets} or {@link - * com.google.common.base.Charsets} instead. - */ - @UnstableApi @Deprecated public static final String UTF8_NAME = "UTF-8"; - - /** - * @deprecated Use {@link java.nio.charset.StandardCharsets} or {@link - * com.google.common.base.Charsets} instead. - */ - @UnstableApi @Deprecated public static final String ISO88591_NAME = "ISO-8859-1"; - - /** - * @deprecated Use {@link java.nio.charset.StandardCharsets} or {@link - * com.google.common.base.Charsets} instead. - */ - @UnstableApi @Deprecated public static final String UTF16_NAME = "UTF-16"; - - /** - * @deprecated Use {@link java.nio.charset.StandardCharsets} or {@link - * com.google.common.base.Charsets} instead. - */ - @UnstableApi @Deprecated public static final String UTF16LE_NAME = "UTF-16LE"; - /** The name of the serif font family. */ @UnstableApi public static final String SERIF_NAME = "serif"; From 7773c884044f971ab1ce0407874dbd06f8fde8fd Mon Sep 17 00:00:00 2001 From: rohks Date: Tue, 16 May 2023 20:14:01 +0100 Subject: [PATCH 027/516] Remove deprecated `WorkManagerScheduler` constructor Use a non deprecated constructor that includes the option to provide a `Context` parameter instead. #minor-release PiperOrigin-RevId: 532535770 (cherry picked from commit df52864420da49e97405c905c7e218e4112573df) --- RELEASENOTES.md | 3 +++ .../exoplayer/workmanager/WorkManagerScheduler.java | 10 ---------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 454de6c58e6..3c5f6a5a15b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -37,6 +37,9 @@ Kotlin Charsets from the `kotlin.text` package, the `java.nio.charset.StandardCharsets` or the `com.google.common.base.Charsets` instead. + * Remove deprecated `WorkManagerScheduler` constructor, use a non + deprecated constructor that includes the option to provide a `Context` + parameter instead. ### 1.0.2 (2023-05-18) diff --git a/libraries/exoplayer_workmanager/src/main/java/androidx/media3/exoplayer/workmanager/WorkManagerScheduler.java b/libraries/exoplayer_workmanager/src/main/java/androidx/media3/exoplayer/workmanager/WorkManagerScheduler.java index 0d9f748f905..969e73b15e4 100644 --- a/libraries/exoplayer_workmanager/src/main/java/androidx/media3/exoplayer/workmanager/WorkManagerScheduler.java +++ b/libraries/exoplayer_workmanager/src/main/java/androidx/media3/exoplayer/workmanager/WorkManagerScheduler.java @@ -56,16 +56,6 @@ public final class WorkManagerScheduler implements Scheduler { private final WorkManager workManager; private final String workName; - /** - * @deprecated Call {@link #WorkManagerScheduler(Context, String)} instead. - */ - @Deprecated - @SuppressWarnings("deprecation") - public WorkManagerScheduler(String workName) { - this.workName = workName; - workManager = WorkManager.getInstance(); - } - /** * @param context A context. * @param workName A name for work scheduled by this instance. If the same name was used by a From a5031a688bebfe4668fe0c2e08a31f5289a374b9 Mon Sep 17 00:00:00 2001 From: tianyifeng Date: Tue, 16 May 2023 20:15:00 +0100 Subject: [PATCH 028/516] Avoid using @see in the summary fragment in C.java PiperOrigin-RevId: 532536037 (cherry picked from commit 5d4c73eee55ce40548dd89265d56755b9ef169c8) --- .../main/java/androidx/media3/common/C.java | 208 +++++------------- 1 file changed, 52 insertions(+), 156 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index 075569f2b8d..d69a757a22b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -142,17 +142,11 @@ private C() {} @IntDef({CRYPTO_MODE_UNENCRYPTED, CRYPTO_MODE_AES_CTR, CRYPTO_MODE_AES_CBC}) @UnstableApi public @interface CryptoMode {} - /** - * @see MediaCodec#CRYPTO_MODE_UNENCRYPTED - */ + /** See {@link MediaCodec#CRYPTO_MODE_UNENCRYPTED}. */ @UnstableApi public static final int CRYPTO_MODE_UNENCRYPTED = MediaCodec.CRYPTO_MODE_UNENCRYPTED; - /** - * @see MediaCodec#CRYPTO_MODE_AES_CTR - */ + /** See {@link MediaCodec#CRYPTO_MODE_AES_CTR}. */ @UnstableApi public static final int CRYPTO_MODE_AES_CTR = MediaCodec.CRYPTO_MODE_AES_CTR; - /** - * @see MediaCodec#CRYPTO_MODE_AES_CBC - */ + /** See {@link MediaCodec#CRYPTO_MODE_AES_CBC}. */ @UnstableApi public static final int CRYPTO_MODE_AES_CBC = MediaCodec.CRYPTO_MODE_AES_CBC; /** @@ -222,17 +216,11 @@ private C() {} ENCODING_PCM_FLOAT }) public @interface PcmEncoding {} - /** - * @see AudioFormat#ENCODING_INVALID - */ + /** See {@link AudioFormat#ENCODING_INVALID}. */ @UnstableApi public static final int ENCODING_INVALID = AudioFormat.ENCODING_INVALID; - /** - * @see AudioFormat#ENCODING_PCM_8BIT - */ + /** See {@link AudioFormat#ENCODING_PCM_8BIT}. */ @UnstableApi public static final int ENCODING_PCM_8BIT = AudioFormat.ENCODING_PCM_8BIT; - /** - * @see AudioFormat#ENCODING_PCM_16BIT - */ + /** See {@link AudioFormat#ENCODING_PCM_16BIT}. */ @UnstableApi public static final int ENCODING_PCM_16BIT = AudioFormat.ENCODING_PCM_16BIT; /** Like {@link #ENCODING_PCM_16BIT}, but with the bytes in big endian order. */ @UnstableApi public static final int ENCODING_PCM_16BIT_BIG_ENDIAN = 0x10000000; @@ -240,67 +228,37 @@ private C() {} @UnstableApi public static final int ENCODING_PCM_24BIT = 0x20000000; /** PCM encoding with 32 bits per sample. */ @UnstableApi public static final int ENCODING_PCM_32BIT = 0x30000000; - /** - * @see AudioFormat#ENCODING_PCM_FLOAT - */ + /** See {@link AudioFormat#ENCODING_PCM_FLOAT}. */ @UnstableApi public static final int ENCODING_PCM_FLOAT = AudioFormat.ENCODING_PCM_FLOAT; - /** - * @see AudioFormat#ENCODING_MP3 - */ + /** See {@link AudioFormat#ENCODING_MP3}. */ @UnstableApi public static final int ENCODING_MP3 = AudioFormat.ENCODING_MP3; - /** - * @see AudioFormat#ENCODING_AAC_LC - */ + /** See {@link AudioFormat#ENCODING_AAC_LC}. */ @UnstableApi public static final int ENCODING_AAC_LC = AudioFormat.ENCODING_AAC_LC; - /** - * @see AudioFormat#ENCODING_AAC_HE_V1 - */ + /** See {@link AudioFormat#ENCODING_AAC_HE_V1}. */ @UnstableApi public static final int ENCODING_AAC_HE_V1 = AudioFormat.ENCODING_AAC_HE_V1; - /** - * @see AudioFormat#ENCODING_AAC_HE_V2 - */ + /** See {@link AudioFormat#ENCODING_AAC_HE_V2}. */ @UnstableApi public static final int ENCODING_AAC_HE_V2 = AudioFormat.ENCODING_AAC_HE_V2; - /** - * @see AudioFormat#ENCODING_AAC_XHE - */ + /** See {@link AudioFormat#ENCODING_AAC_XHE}. */ @UnstableApi public static final int ENCODING_AAC_XHE = AudioFormat.ENCODING_AAC_XHE; - /** - * @see AudioFormat#ENCODING_AAC_ELD - */ + /** See {@link AudioFormat#ENCODING_AAC_ELD}. */ @UnstableApi public static final int ENCODING_AAC_ELD = AudioFormat.ENCODING_AAC_ELD; /** AAC Error Resilient Bit-Sliced Arithmetic Coding. */ @UnstableApi public static final int ENCODING_AAC_ER_BSAC = 0x40000000; - /** - * @see AudioFormat#ENCODING_AC3 - */ + /** See {@link AudioFormat#ENCODING_AC3}. */ @UnstableApi public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3; - /** - * @see AudioFormat#ENCODING_E_AC3 - */ + /** See {@link AudioFormat#ENCODING_E_AC3}. */ @UnstableApi public static final int ENCODING_E_AC3 = AudioFormat.ENCODING_E_AC3; - /** - * @see AudioFormat#ENCODING_E_AC3_JOC - */ + /** See {@link AudioFormat#ENCODING_E_AC3_JOC}. */ @UnstableApi public static final int ENCODING_E_AC3_JOC = AudioFormat.ENCODING_E_AC3_JOC; - /** - * @see AudioFormat#ENCODING_AC4 - */ + /** See {@link AudioFormat#ENCODING_AC4}. */ @UnstableApi public static final int ENCODING_AC4 = AudioFormat.ENCODING_AC4; - /** - * @see AudioFormat#ENCODING_DTS - */ + /** See {@link AudioFormat#ENCODING_DTS}. */ @UnstableApi public static final int ENCODING_DTS = AudioFormat.ENCODING_DTS; - /** - * @see AudioFormat#ENCODING_DTS_HD - */ + /** See {@link AudioFormat#ENCODING_DTS_HD}. */ @UnstableApi public static final int ENCODING_DTS_HD = AudioFormat.ENCODING_DTS_HD; - /** - * @see AudioFormat#ENCODING_DOLBY_TRUEHD - */ + /** See {@link AudioFormat#ENCODING_DOLBY_TRUEHD}. */ @UnstableApi public static final int ENCODING_DOLBY_TRUEHD = AudioFormat.ENCODING_DOLBY_TRUEHD; - /** - * @see AudioFormat#ENCODING_OPUS - */ + /** See {@link AudioFormat#ENCODING_OPUS}. */ @UnstableApi public static final int ENCODING_OPUS = AudioFormat.ENCODING_OPUS; /** @@ -313,14 +271,10 @@ private C() {} @IntDef({SPATIALIZATION_BEHAVIOR_AUTO, SPATIALIZATION_BEHAVIOR_NEVER}) public @interface SpatializationBehavior {} - /** - * @see AudioAttributes#SPATIALIZATION_BEHAVIOR_AUTO - */ + /** See {@link AudioAttributes#SPATIALIZATION_BEHAVIOR_AUTO}. */ public static final int SPATIALIZATION_BEHAVIOR_AUTO = AudioAttributes.SPATIALIZATION_BEHAVIOR_AUTO; - /** - * @see AudioAttributes#SPATIALIZATION_BEHAVIOR_NEVER - */ + /** See {@link AudioAttributes#SPATIALIZATION_BEHAVIOR_NEVER}. */ public static final int SPATIALIZATION_BEHAVIOR_NEVER = AudioAttributes.SPATIALIZATION_BEHAVIOR_NEVER; @@ -348,33 +302,19 @@ private C() {} STREAM_TYPE_DEFAULT }) public @interface StreamType {} - /** - * @see AudioManager#STREAM_ALARM - */ + /** See {@link AudioManager#STREAM_ALARM}. */ @UnstableApi public static final int STREAM_TYPE_ALARM = AudioManager.STREAM_ALARM; - /** - * @see AudioManager#STREAM_DTMF - */ + /** See {@link AudioManager#STREAM_DTMF}. */ @UnstableApi public static final int STREAM_TYPE_DTMF = AudioManager.STREAM_DTMF; - /** - * @see AudioManager#STREAM_MUSIC - */ + /** See {@link AudioManager#STREAM_MUSIC}. */ @UnstableApi public static final int STREAM_TYPE_MUSIC = AudioManager.STREAM_MUSIC; - /** - * @see AudioManager#STREAM_NOTIFICATION - */ + /** See {@link AudioManager#STREAM_NOTIFICATION}. */ @UnstableApi public static final int STREAM_TYPE_NOTIFICATION = AudioManager.STREAM_NOTIFICATION; - /** - * @see AudioManager#STREAM_RING - */ + /** See {@link AudioManager#STREAM_RING}. */ @UnstableApi public static final int STREAM_TYPE_RING = AudioManager.STREAM_RING; - /** - * @see AudioManager#STREAM_SYSTEM - */ + /** See {@link AudioManager#STREAM_SYSTEM}. */ @UnstableApi public static final int STREAM_TYPE_SYSTEM = AudioManager.STREAM_SYSTEM; - /** - * @see AudioManager#STREAM_VOICE_CALL - */ + /** See {@link AudioManager#STREAM_VOICE_CALL}. */ @UnstableApi public static final int STREAM_TYPE_VOICE_CALL = AudioManager.STREAM_VOICE_CALL; /** The default stream type used by audio renderers. Equal to {@link #STREAM_TYPE_MUSIC}. */ @UnstableApi public static final int STREAM_TYPE_DEFAULT = STREAM_TYPE_MUSIC; @@ -483,9 +423,7 @@ private C() {} flag = true, value = {FLAG_AUDIBILITY_ENFORCED}) public @interface AudioFlags {} - /** - * @see android.media.AudioAttributes#FLAG_AUDIBILITY_ENFORCED - */ + /** See {@link android.media.AudioAttributes#FLAG_AUDIBILITY_ENFORCED}. */ public static final int FLAG_AUDIBILITY_ENFORCED = android.media.AudioAttributes.FLAG_AUDIBILITY_ENFORCED; @@ -523,78 +461,46 @@ private C() {} USAGE_VOICE_COMMUNICATION_SIGNALLING }) public @interface AudioUsage {} - /** - * @see android.media.AudioAttributes#USAGE_ALARM - */ + /** See {@link android.media.AudioAttributes#USAGE_ALARM}. */ public static final int USAGE_ALARM = android.media.AudioAttributes.USAGE_ALARM; - /** - * @see android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY - */ + /** See {@link android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY}. */ public static final int USAGE_ASSISTANCE_ACCESSIBILITY = android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY; - /** - * @see android.media.AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE - */ + /** See {@link android.media.AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}. */ public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; - /** - * @see android.media.AudioAttributes#USAGE_ASSISTANCE_SONIFICATION - */ + /** See {@link android.media.AudioAttributes#USAGE_ASSISTANCE_SONIFICATION}. */ public static final int USAGE_ASSISTANCE_SONIFICATION = android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; - /** - * @see android.media.AudioAttributes#USAGE_ASSISTANT - */ + /** See {@link android.media.AudioAttributes#USAGE_ASSISTANT}. */ public static final int USAGE_ASSISTANT = android.media.AudioAttributes.USAGE_ASSISTANT; - /** - * @see android.media.AudioAttributes#USAGE_GAME - */ + /** See {@link android.media.AudioAttributes#USAGE_GAME}. */ public static final int USAGE_GAME = android.media.AudioAttributes.USAGE_GAME; - /** - * @see android.media.AudioAttributes#USAGE_MEDIA - */ + /** See {@link android.media.AudioAttributes#USAGE_MEDIA}. */ public static final int USAGE_MEDIA = android.media.AudioAttributes.USAGE_MEDIA; - /** - * @see android.media.AudioAttributes#USAGE_NOTIFICATION - */ + /** See {@link android.media.AudioAttributes#USAGE_NOTIFICATION}. */ public static final int USAGE_NOTIFICATION = android.media.AudioAttributes.USAGE_NOTIFICATION; - /** - * @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED - */ + /** See {@link android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED}. */ public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED; - /** - * @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT - */ + /** See {@link android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT}. */ public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT; - /** - * @see android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST - */ + /** See {@link android.media.AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST}. */ public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = android.media.AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST; - /** - * @see android.media.AudioAttributes#USAGE_NOTIFICATION_EVENT - */ + /** See {@link android.media.AudioAttributes#USAGE_NOTIFICATION_EVENT}. */ public static final int USAGE_NOTIFICATION_EVENT = android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT; - /** - * @see android.media.AudioAttributes#USAGE_NOTIFICATION_RINGTONE - */ + /** See {@link android.media.AudioAttributes#USAGE_NOTIFICATION_RINGTONE}. */ public static final int USAGE_NOTIFICATION_RINGTONE = android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; - /** - * @see android.media.AudioAttributes#USAGE_UNKNOWN - */ + /** See {@link android.media.AudioAttributes#USAGE_UNKNOWN}. */ public static final int USAGE_UNKNOWN = android.media.AudioAttributes.USAGE_UNKNOWN; - /** - * @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION - */ + /** See {@link android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION}. */ public static final int USAGE_VOICE_COMMUNICATION = android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION; - /** - * @see android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING - */ + /** See {@link android.media.AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING}. */ public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING; @@ -1066,17 +972,11 @@ private C() {} @Target(TYPE_USE) @IntDef({Format.NO_VALUE, COLOR_SPACE_BT601, COLOR_SPACE_BT709, COLOR_SPACE_BT2020}) public @interface ColorSpace {} - /** - * @see MediaFormat#COLOR_STANDARD_BT601_PAL - */ + /** See {@link MediaFormat#COLOR_STANDARD_BT601_PAL}. */ @UnstableApi public static final int COLOR_SPACE_BT601 = MediaFormat.COLOR_STANDARD_BT601_PAL; - /** - * @see MediaFormat#COLOR_STANDARD_BT709 - */ + /** See {@link MediaFormat#COLOR_STANDARD_BT709}. */ @UnstableApi public static final int COLOR_SPACE_BT709 = MediaFormat.COLOR_STANDARD_BT709; - /** - * @see MediaFormat#COLOR_STANDARD_BT2020 - */ + /** See {@link MediaFormat#COLOR_STANDARD_BT2020}. */ @UnstableApi public static final int COLOR_SPACE_BT2020 = MediaFormat.COLOR_STANDARD_BT2020; // LINT.IfChange(color_transfer) @@ -1131,13 +1031,9 @@ private C() {} @Target(TYPE_USE) @IntDef({Format.NO_VALUE, COLOR_RANGE_LIMITED, COLOR_RANGE_FULL}) public @interface ColorRange {} - /** - * @see MediaFormat#COLOR_RANGE_LIMITED - */ + /** See {@link MediaFormat#COLOR_RANGE_LIMITED}. */ @UnstableApi public static final int COLOR_RANGE_LIMITED = MediaFormat.COLOR_RANGE_LIMITED; - /** - * @see MediaFormat#COLOR_RANGE_FULL - */ + /** See {@link MediaFormat#COLOR_RANGE_FULL}. */ @UnstableApi public static final int COLOR_RANGE_FULL = MediaFormat.COLOR_RANGE_FULL; /** From 6abc1a71554993c9bee8b3618702bace2cee6753 Mon Sep 17 00:00:00 2001 From: kimvde Date: Wed, 17 May 2023 11:55:49 +0100 Subject: [PATCH 029/516] Simplify FrameConsumptionManager onReadyToAcceptInputFrame logic Propagate the "end of current stream" signal directly after queueing the last frame, instead of waiting for the next onReadyToAcceptInputFrame() call. PiperOrigin-RevId: 532739462 (cherry picked from commit 028b3a73120b755733f58f8c8d9764146c470437) --- .../media3/effect/FrameConsumptionManager.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FrameConsumptionManager.java b/libraries/effect/src/main/java/androidx/media3/effect/FrameConsumptionManager.java index 866412cf066..7957504b6ba 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FrameConsumptionManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FrameConsumptionManager.java @@ -63,16 +63,16 @@ public synchronized void onReadyToAcceptInputFrame() { return; } - long presentationTimeUs = pendingFrame.second; - if (presentationTimeUs == C.TIME_END_OF_SOURCE) { - consumingGlShaderProgramInputCapacity++; + videoFrameProcessingTaskExecutor.submit( + () -> + consumingGlShaderProgram.queueInputFrame( + /* inputTexture= */ pendingFrame.first, + /* presentationTimeUs= */ pendingFrame.second)); + @Nullable Pair nextPendingFrame = availableFrames.peek(); + if (nextPendingFrame != null && nextPendingFrame.second == C.TIME_END_OF_SOURCE) { videoFrameProcessingTaskExecutor.submit( consumingGlShaderProgram::signalEndOfCurrentInputStream); - } else { - videoFrameProcessingTaskExecutor.submit( - () -> - consumingGlShaderProgram.queueInputFrame( - /* inputTexture= */ pendingFrame.first, presentationTimeUs)); + availableFrames.remove(); } } From f5ec1bb6f92fc01b59b38c42f189e3e028606722 Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Wed, 17 May 2023 12:55:14 +0100 Subject: [PATCH 030/516] Move NAL unit related methods from common to container module PiperOrigin-RevId: 532750099 (cherry picked from commit e0d6f67dd90acbe8f1c41ffd9550d18eea2ca129) --- libraries/container/build.gradle | 5 +++++ .../main/java/androidx/media3/container}/NalUnitUtil.java | 5 ++++- .../androidx/media3/container}/ParsableNalUnitBitArray.java | 5 ++++- .../java/androidx/media3/container}/NalUnitUtilTest.java | 4 +++- .../media3/container}/ParsableNalUnitBitArrayTest.java | 3 ++- libraries/exoplayer/build.gradle | 1 + .../media3/exoplayer/mediacodec/MediaCodecRenderer.java | 2 +- .../java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java | 4 ++-- .../media3/exoplayer/rtsp/reader/RtpH264Reader.java | 2 +- .../media3/exoplayer/rtsp/reader/RtpH265Reader.java | 2 +- libraries/extractor/build.gradle | 1 + .../src/main/java/androidx/media3/extractor/AvcConfig.java | 4 ++-- .../src/main/java/androidx/media3/extractor/HevcConfig.java | 2 +- .../media3/extractor/flv/VideoTagPayloadReader.java | 2 +- .../androidx/media3/extractor/mkv/MatroskaExtractor.java | 2 +- .../media3/extractor/mp4/FragmentedMp4Extractor.java | 2 +- .../java/androidx/media3/extractor/mp4/Mp4Extractor.java | 2 +- .../main/java/androidx/media3/extractor/ts/H262Reader.java | 2 +- .../main/java/androidx/media3/extractor/ts/H263Reader.java | 2 +- .../main/java/androidx/media3/extractor/ts/H264Reader.java | 6 +++--- .../main/java/androidx/media3/extractor/ts/H265Reader.java | 2 +- libraries/muxer/build.gradle | 1 + .../muxer/src/main/java/androidx/media3/muxer/Boxes.java | 2 +- .../androidx/media3/transformer/SefSlowMotionFlattener.java | 2 +- 24 files changed, 41 insertions(+), 24 deletions(-) rename libraries/{common/src/main/java/androidx/media3/common/util => container/src/main/java/androidx/media3/container}/NalUnitUtil.java (99%) rename libraries/{common/src/main/java/androidx/media3/common/util => container/src/main/java/androidx/media3/container}/ParsableNalUnitBitArray.java (98%) rename libraries/{common/src/test/java/androidx/media3/common/util => container/src/test/java/androidx/media3/container}/NalUnitUtilTest.java (98%) rename libraries/{common/src/test/java/androidx/media3/common/util => container/src/test/java/androidx/media3/container}/ParsableNalUnitBitArrayTest.java (98%) diff --git a/libraries/container/build.gradle b/libraries/container/build.gradle index c6f77dad956..fb0b1619b92 100644 --- a/libraries/container/build.gradle +++ b/libraries/container/build.gradle @@ -40,6 +40,11 @@ ext { dependencies { implementation project(modulePrefix + 'lib-common') + implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion + testImplementation 'androidx.test.ext:junit:' + androidxTestJUnitVersion + testImplementation 'com.google.truth:truth:' + truthVersion + testImplementation 'junit:junit:' + junitVersion + testImplementation 'org.robolectric:robolectric:' + robolectricVersion } apply from: '../../javadoc_library.gradle' diff --git a/libraries/common/src/main/java/androidx/media3/common/util/NalUnitUtil.java b/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java similarity index 99% rename from libraries/common/src/main/java/androidx/media3/common/util/NalUnitUtil.java rename to libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java index 92ae0db1212..5889e8152a6 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/NalUnitUtil.java +++ b/libraries/container/src/main/java/androidx/media3/container/NalUnitUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package androidx.media3.common.util; +package androidx.media3.container; import static java.lang.Math.min; @@ -22,6 +22,9 @@ import androidx.media3.common.ColorInfo; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.UnstableApi; import java.nio.ByteBuffer; import java.util.Arrays; diff --git a/libraries/common/src/main/java/androidx/media3/common/util/ParsableNalUnitBitArray.java b/libraries/container/src/main/java/androidx/media3/container/ParsableNalUnitBitArray.java similarity index 98% rename from libraries/common/src/main/java/androidx/media3/common/util/ParsableNalUnitBitArray.java rename to libraries/container/src/main/java/androidx/media3/container/ParsableNalUnitBitArray.java index e4896020b9a..79d3bf8734b 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/ParsableNalUnitBitArray.java +++ b/libraries/container/src/main/java/androidx/media3/container/ParsableNalUnitBitArray.java @@ -13,7 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package androidx.media3.common.util; +package androidx.media3.container; + +import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.UnstableApi; /** * Wraps a byte array, providing methods that allow it to be read as a NAL unit bitstream. diff --git a/libraries/common/src/test/java/androidx/media3/common/util/NalUnitUtilTest.java b/libraries/container/src/test/java/androidx/media3/container/NalUnitUtilTest.java similarity index 98% rename from libraries/common/src/test/java/androidx/media3/common/util/NalUnitUtilTest.java rename to libraries/container/src/test/java/androidx/media3/container/NalUnitUtilTest.java index f8f730e61b6..2ab0a7c7f1b 100644 --- a/libraries/common/src/test/java/androidx/media3/common/util/NalUnitUtilTest.java +++ b/libraries/container/src/test/java/androidx/media3/container/NalUnitUtilTest.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package androidx.media3.common.util; +package androidx.media3.container; import static com.google.common.truth.Truth.assertThat; +import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.Util; import androidx.test.ext.junit.runners.AndroidJUnit4; import java.nio.ByteBuffer; import java.util.Arrays; diff --git a/libraries/common/src/test/java/androidx/media3/common/util/ParsableNalUnitBitArrayTest.java b/libraries/container/src/test/java/androidx/media3/container/ParsableNalUnitBitArrayTest.java similarity index 98% rename from libraries/common/src/test/java/androidx/media3/common/util/ParsableNalUnitBitArrayTest.java rename to libraries/container/src/test/java/androidx/media3/container/ParsableNalUnitBitArrayTest.java index f325bd1c4b5..f4745fd97ce 100644 --- a/libraries/common/src/test/java/androidx/media3/common/util/ParsableNalUnitBitArrayTest.java +++ b/libraries/container/src/test/java/androidx/media3/container/ParsableNalUnitBitArrayTest.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package androidx.media3.common.util; +package androidx.media3.container; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import androidx.media3.common.util.Assertions; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/libraries/exoplayer/build.gradle b/libraries/exoplayer/build.gradle index b7fef71ab03..61f13db560c 100644 --- a/libraries/exoplayer/build.gradle +++ b/libraries/exoplayer/build.gradle @@ -35,6 +35,7 @@ android { dependencies { api project(modulePrefix + 'lib-common') + api project(modulePrefix + 'lib-container') // TODO(b/203754886): Revisit which modules are exported as API dependencies. api project(modulePrefix + 'lib-datasource') api project(modulePrefix + 'lib-decoder') diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index 59320e1d87f..8c0c1366948 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -53,11 +53,11 @@ import androidx.media3.common.PlaybackException; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Log; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.TimedValueQueue; import androidx.media3.common.util.TraceUtil; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import androidx.media3.decoder.CryptoConfig; import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.decoder.DecoderInputBuffer.InsufficientCapacityException; diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java index d14439cc01c..92d1d4b78e0 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java @@ -17,8 +17,8 @@ import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.common.util.NalUnitUtil.NAL_START_CODE; import static androidx.media3.common.util.Util.castNonNull; +import static androidx.media3.container.NalUnitUtil.NAL_START_CODE; import static androidx.media3.exoplayer.rtsp.MediaDescription.MEDIA_TYPE_AUDIO; import static androidx.media3.exoplayer.rtsp.RtpPayloadFormat.getMimeTypeFromRtpMediaType; import static androidx.media3.exoplayer.rtsp.SessionDescription.ATTR_CONTROL; @@ -33,10 +33,10 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.ParserException; import androidx.media3.common.util.CodecSpecificDataUtil; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableBitArray; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import androidx.media3.extractor.AacUtil; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java index 94ed41a518b..0403ecf6bc4 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH264Reader.java @@ -23,9 +23,9 @@ import androidx.media3.common.C; import androidx.media3.common.ParserException; import androidx.media3.common.util.Log; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import androidx.media3.exoplayer.rtsp.RtpPacket; import androidx.media3.exoplayer.rtsp.RtpPayloadFormat; import androidx.media3.extractor.ExtractorOutput; diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH265Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH265Reader.java index f3b6d25c3ec..2f77918dae2 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH265Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH265Reader.java @@ -22,9 +22,9 @@ import androidx.media3.common.C; import androidx.media3.common.ParserException; import androidx.media3.common.util.Log; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import androidx.media3.exoplayer.rtsp.RtpPacket; import androidx.media3.exoplayer.rtsp.RtpPayloadFormat; import androidx.media3.extractor.ExtractorOutput; diff --git a/libraries/extractor/build.gradle b/libraries/extractor/build.gradle index 6cd3a81b28e..80ebd2a69fb 100644 --- a/libraries/extractor/build.gradle +++ b/libraries/extractor/build.gradle @@ -26,6 +26,7 @@ android { dependencies { implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion implementation project(modulePrefix + 'lib-common') + implementation project(modulePrefix + 'lib-container') // TODO(b/203752187): Remove this dependency. implementation project(modulePrefix + 'lib-decoder') compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/AvcConfig.java b/libraries/extractor/src/main/java/androidx/media3/extractor/AvcConfig.java index e6b274cb865..0de91bb6494 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/AvcConfig.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/AvcConfig.java @@ -20,10 +20,10 @@ import androidx.media3.common.Format; import androidx.media3.common.ParserException; import androidx.media3.common.util.CodecSpecificDataUtil; -import androidx.media3.common.util.NalUnitUtil; -import androidx.media3.common.util.NalUnitUtil.SpsData; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; +import androidx.media3.container.NalUnitUtil; +import androidx.media3.container.NalUnitUtil.SpsData; import java.util.ArrayList; import java.util.List; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/HevcConfig.java b/libraries/extractor/src/main/java/androidx/media3/extractor/HevcConfig.java index 421a9a1d6fd..fa9674ac74d 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/HevcConfig.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/HevcConfig.java @@ -20,9 +20,9 @@ import androidx.media3.common.Format; import androidx.media3.common.ParserException; import androidx.media3.common.util.CodecSpecificDataUtil; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; +import androidx.media3.container.NalUnitUtil; import java.util.Collections; import java.util.List; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/flv/VideoTagPayloadReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/flv/VideoTagPayloadReader.java index 7a9f5a767ea..35fdc6f3e9b 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/flv/VideoTagPayloadReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/flv/VideoTagPayloadReader.java @@ -19,8 +19,8 @@ import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.ParserException; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; +import androidx.media3.container.NalUnitUtil; import androidx.media3.extractor.AvcConfig; import androidx.media3.extractor.TrackOutput; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index 7dc3f48b823..a678e8cb82e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -37,10 +37,10 @@ import androidx.media3.common.ParserException; import androidx.media3.common.util.Log; import androidx.media3.common.util.LongArray; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import androidx.media3.extractor.AacUtil; import androidx.media3.extractor.AvcConfig; import androidx.media3.extractor.ChunkIndex; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index 22b9192cee5..9427ba12633 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -34,11 +34,11 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.ParserException; import androidx.media3.common.util.Log; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.TimestampAdjuster; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import androidx.media3.extractor.Ac4Util; import androidx.media3.extractor.CeaUtil; import androidx.media3.extractor.ChunkIndex; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java index a12211adc14..f3082ee1385 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java @@ -31,9 +31,9 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.ParserException; import androidx.media3.common.util.Assertions; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; +import androidx.media3.container.NalUnitUtil; import androidx.media3.extractor.Ac3Util; import androidx.media3.extractor.Ac4Util; import androidx.media3.extractor.Extractor; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H262Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H262Reader.java index 90d9c57071a..11b301d1ff0 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H262Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H262Reader.java @@ -23,10 +23,10 @@ import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import androidx.media3.extractor.ExtractorOutput; import androidx.media3.extractor.TrackOutput; import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java index 92200855e0d..b7964576d14 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H263Reader.java @@ -26,10 +26,10 @@ import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Log; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableBitArray; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; +import androidx.media3.container.NalUnitUtil; import androidx.media3.extractor.ExtractorOutput; import androidx.media3.extractor.TrackOutput; import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H264Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H264Reader.java index c93f8a21584..528fa28a848 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H264Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H264Reader.java @@ -24,12 +24,12 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.CodecSpecificDataUtil; -import androidx.media3.common.util.NalUnitUtil; -import androidx.media3.common.util.NalUnitUtil.SpsData; import androidx.media3.common.util.ParsableByteArray; -import androidx.media3.common.util.ParsableNalUnitBitArray; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; +import androidx.media3.container.NalUnitUtil.SpsData; +import androidx.media3.container.ParsableNalUnitBitArray; import androidx.media3.extractor.ExtractorOutput; import androidx.media3.extractor.TrackOutput; import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java index d7724b06326..9187bfb0a8b 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/H265Reader.java @@ -21,10 +21,10 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.CodecSpecificDataUtil; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import androidx.media3.extractor.ExtractorOutput; import androidx.media3.extractor.TrackOutput; import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator; diff --git a/libraries/muxer/build.gradle b/libraries/muxer/build.gradle index 7c0ce562b4f..6d9ce162aaf 100644 --- a/libraries/muxer/build.gradle +++ b/libraries/muxer/build.gradle @@ -40,6 +40,7 @@ ext { dependencies { implementation project(modulePrefix + 'lib-common') + implementation project(modulePrefix + 'lib-container') implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java index ca939c880cb..6c35ca9574c 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java @@ -28,8 +28,8 @@ import androidx.media3.common.ColorInfo; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; -import androidx.media3.common.util.NalUnitUtil; import androidx.media3.common.util.Util; +import androidx.media3.container.NalUnitUtil; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.primitives.Bytes; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/SefSlowMotionFlattener.java b/libraries/transformer/src/main/java/androidx/media3/transformer/SefSlowMotionFlattener.java index eea33006940..5499da5c58b 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/SefSlowMotionFlattener.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/SefSlowMotionFlattener.java @@ -18,7 +18,7 @@ import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkState; -import static androidx.media3.common.util.NalUnitUtil.NAL_START_CODE; +import static androidx.media3.container.NalUnitUtil.NAL_START_CODE; import static java.lang.Math.min; import androidx.annotation.Nullable; From ce5b57e03ccf54922641256bc53adbb0f7d0781e Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Wed, 17 May 2023 13:17:51 +0100 Subject: [PATCH 031/516] Effect: Make TexturePool and use in FinalWrapper. Have the FinalShaderProgramWrapper / VideoFrameProcessor texture output access textures provided through a texture pool, that recycles used textures. Also, add the TexturePool interface to generally re-use textures. PiperOrigin-RevId: 532754377 (cherry picked from commit 94efcd7917ad5c175aac52280ee82f054d24a953) --- .../androidx/media3/common/GlTextureInfo.java | 1 + .../androidx/media3/common/util/GlUtil.java | 4 +- .../media3/effect/BaseGlShaderProgram.java | 95 ++--------- .../effect/FinalShaderProgramWrapper.java | 52 +++--- .../androidx/media3/effect/TexturePool.java | 159 ++++++++++++++++++ 5 files changed, 196 insertions(+), 115 deletions(-) create mode 100644 libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java diff --git a/libraries/common/src/main/java/androidx/media3/common/GlTextureInfo.java b/libraries/common/src/main/java/androidx/media3/common/GlTextureInfo.java index 7857932cd62..f073e9d1377 100644 --- a/libraries/common/src/main/java/androidx/media3/common/GlTextureInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/GlTextureInfo.java @@ -20,6 +20,7 @@ /** Contains information describing an OpenGL texture. */ @UnstableApi public final class GlTextureInfo { + // TODO: b/262694346 - Add a release() method for GlTextureInfo. /** A {@link GlTextureInfo} instance with all fields unset. */ public static final GlTextureInfo UNSET = diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java index c7e3de1a6f4..e20da85644e 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java @@ -558,8 +558,8 @@ public static int createExternalTexture() throws GlException { * * @param width The width of the new texture in pixels. * @param height The height of the new texture in pixels. - * @param useHighPrecisionColorComponents If {@code false}, uses 8-bit unsigned bytes. If {@code - * true}, use 16-bit (half-precision) floating-point. + * @param useHighPrecisionColorComponents If {@code false}, uses colors with 8-bit unsigned bytes. + * If {@code true}, use 16-bit (half-precision) floating-point. * @throws GlException If the texture allocation fails. * @return The texture identifier for the newly-allocated texture. */ diff --git a/libraries/effect/src/main/java/androidx/media3/effect/BaseGlShaderProgram.java b/libraries/effect/src/main/java/androidx/media3/effect/BaseGlShaderProgram.java index ab7554658ad..3ceb2924ec8 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/BaseGlShaderProgram.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/BaseGlShaderProgram.java @@ -24,10 +24,7 @@ import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.Size; import androidx.media3.common.util.UnstableApi; -import com.google.common.collect.Iterables; import com.google.common.util.concurrent.MoreExecutors; -import java.util.ArrayDeque; -import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.Executor; @@ -47,13 +44,7 @@ */ @UnstableApi public abstract class BaseGlShaderProgram implements GlShaderProgram { - - private final ArrayDeque freeOutputTextures; - private final ArrayDeque inUseOutputTextures; - private final int texturePoolCapacity; - private final boolean useHdr; - - private GlObjectsProvider glObjectsProvider; + private final TexturePool outputTexturePool; protected InputListener inputListener; private OutputListener outputListener; private ErrorListener errorListener; @@ -69,11 +60,8 @@ public abstract class BaseGlShaderProgram implements GlShaderProgram { * texture cache, the size should be the number of textures to cache. */ public BaseGlShaderProgram(boolean useHdr, int texturePoolCapacity) { - freeOutputTextures = new ArrayDeque<>(texturePoolCapacity); - inUseOutputTextures = new ArrayDeque<>(texturePoolCapacity); - this.useHdr = useHdr; - this.texturePoolCapacity = texturePoolCapacity; - glObjectsProvider = GlObjectsProvider.DEFAULT; + outputTexturePool = + new TexturePool(/* useHighPrecisionColorComponents= */ useHdr, texturePoolCapacity); inputListener = new InputListener() {}; outputListener = new OutputListener() {}; errorListener = (frameProcessingException) -> {}; @@ -114,15 +102,7 @@ public abstract void drawFrame(int inputTexId, long presentationTimeUs) @Override public void setInputListener(InputListener inputListener) { this.inputListener = inputListener; - int numberOfFreeFramesToNotify; - if (getIteratorToAllTextures().hasNext()) { - // The frame buffers have already been allocated. - numberOfFreeFramesToNotify = freeOutputTextures.size(); - } else { - // Defer frame buffer allocation to when queueing input frames. - numberOfFreeFramesToNotify = texturePoolCapacity; - } - for (int i = 0; i < numberOfFreeFramesToNotify; i++) { + for (int i = 0; i < outputTexturePool.freeTextureCount(); i++) { inputListener.onReadyToAcceptInputFrame(); } } @@ -143,22 +123,19 @@ public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { checkState( !frameProcessingStarted, "The GlObjectsProvider cannot be set after frame processing has started."); - this.glObjectsProvider = glObjectsProvider; + outputTexturePool.setGlObjectsProvider(glObjectsProvider); } @Override public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) { try { - configureAllOutputTextures(inputTexture.width, inputTexture.height); - checkState( - !freeOutputTextures.isEmpty(), - "The GlShaderProgram does not currently accept input frames. Release prior output frames" - + " first."); + Size outputTextureSize = configure(inputTexture.width, inputTexture.height); + outputTexturePool.ensureConfigured( + outputTextureSize.getWidth(), outputTextureSize.getHeight()); frameProcessingStarted = true; // Focus on the next free buffer. - GlTextureInfo outputTexture = freeOutputTextures.remove(); - inUseOutputTextures.add(outputTexture); + GlTextureInfo outputTexture = outputTexturePool.useTexture(); // Copy frame to fbo. GlUtil.focusFramebufferUsingCurrentContext( @@ -176,9 +153,7 @@ public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) @Override public void releaseOutputFrame(GlTextureInfo outputTexture) { frameProcessingStarted = true; - checkState(inUseOutputTextures.contains(outputTexture)); - inUseOutputTextures.remove(outputTexture); - freeOutputTextures.add(outputTexture); + outputTexturePool.freeTexture(outputTexture); inputListener.onReadyToAcceptInputFrame(); } @@ -192,10 +167,9 @@ public void signalEndOfCurrentInputStream() { @CallSuper public void flush() { frameProcessingStarted = true; - freeOutputTextures.addAll(inUseOutputTextures); - inUseOutputTextures.clear(); + outputTexturePool.freeAllTextures(); inputListener.onFlush(); - for (int i = 0; i < freeOutputTextures.size(); i++) { + for (int i = 0; i < outputTexturePool.capacity(); i++) { inputListener.onReadyToAcceptInputFrame(); } } @@ -205,52 +179,9 @@ public void flush() { public void release() throws VideoFrameProcessingException { frameProcessingStarted = true; try { - deleteAllOutputTextures(); + outputTexturePool.deleteAllTextures(); } catch (GlUtil.GlException e) { throw new VideoFrameProcessingException(e); } } - - private void configureAllOutputTextures(int inputWidth, int inputHeight) - throws GlUtil.GlException, VideoFrameProcessingException { - Iterator allTextures = getIteratorToAllTextures(); - if (!allTextures.hasNext()) { - createAllOutputTextures(inputWidth, inputHeight); - return; - } - GlTextureInfo outputGlTextureInfo = allTextures.next(); - if (outputGlTextureInfo.width != inputWidth || outputGlTextureInfo.height != inputHeight) { - deleteAllOutputTextures(); - createAllOutputTextures(inputWidth, inputHeight); - } - } - - private void createAllOutputTextures(int width, int height) - throws GlUtil.GlException, VideoFrameProcessingException { - checkState(freeOutputTextures.isEmpty()); - checkState(inUseOutputTextures.isEmpty()); - Size outputSize = configure(width, height); - for (int i = 0; i < texturePoolCapacity; i++) { - int outputTexId = GlUtil.createTexture(outputSize.getWidth(), outputSize.getHeight(), useHdr); - GlTextureInfo outputTexture = - glObjectsProvider.createBuffersForTexture( - outputTexId, outputSize.getWidth(), outputSize.getHeight()); - freeOutputTextures.add(outputTexture); - } - } - - private void deleteAllOutputTextures() throws GlUtil.GlException { - Iterator allTextures = getIteratorToAllTextures(); - while (allTextures.hasNext()) { - GlTextureInfo textureInfo = allTextures.next(); - GlUtil.deleteTexture(textureInfo.texId); - GlUtil.deleteFbo(textureInfo.fboId); - } - freeOutputTextures.clear(); - inUseOutputTextures.clear(); - } - - private Iterator getIteratorToAllTextures() { - return Iterables.concat(freeOutputTextures, inUseOutputTextures).iterator(); - } } diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java index 16eb5255581..d78696cb996 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java @@ -44,6 +44,7 @@ import androidx.media3.common.util.Size; import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; +import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; @@ -87,9 +88,9 @@ interface OnInputStreamProcessedListener { private final Executor videoFrameProcessorListenerExecutor; private final VideoFrameProcessor.Listener videoFrameProcessorListener; private final Queue> availableFrames; - private final Queue> outputTextures; + private final TexturePool outputTexturePool; + private final Queue outputTextureTimestamps; // Synchronized with outputTexturePool. @Nullable private final DefaultVideoFrameProcessor.TextureOutputListener textureOutputListener; - private final int textureOutputCapacity; private int inputWidth; private int inputHeight; @@ -143,11 +144,13 @@ public FinalShaderProgramWrapper( this.videoFrameProcessorListener = videoFrameProcessorListener; this.glObjectsProvider = glObjectsProvider; this.textureOutputListener = textureOutputListener; - this.textureOutputCapacity = textureOutputCapacity; inputListener = new InputListener() {}; availableFrames = new ConcurrentLinkedQueue<>(); - outputTextures = new ConcurrentLinkedQueue<>(); + + boolean useHighPrecisionColorComponents = ColorInfo.isTransferHdr(outputColorInfo); + outputTexturePool = new TexturePool(useHighPrecisionColorComponents, textureOutputCapacity); + outputTextureTimestamps = new ArrayDeque<>(textureOutputCapacity); } @Override @@ -156,6 +159,7 @@ public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { !frameProcessingStarted, "The GlObjectsProvider cannot be set after frame processing has started."); this.glObjectsProvider = glObjectsProvider; + outputTexturePool.setGlObjectsProvider(glObjectsProvider); } @Override @@ -207,7 +211,7 @@ public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) availableFrames.add(Pair.create(inputTexture, presentationTimeUs)); } } else { - checkState(outputTextures.size() < textureOutputCapacity); + checkState(outputTexturePool.freeTextureCount() > 0); renderFrame(inputTexture, presentationTimeUs, /* renderTimeNs= */ presentationTimeUs * 1000); } maybeOnReadyToAcceptInputFrame(); @@ -219,16 +223,11 @@ public void releaseOutputFrame(GlTextureInfo outputTexture) { throw new UnsupportedOperationException(); } - public void releaseOutputFrame(long presentationTimeUs) throws VideoFrameProcessingException { - while (!outputTextures.isEmpty() - && checkNotNull(outputTextures.peek()).second <= presentationTimeUs) { - GlTextureInfo outputTexture = outputTextures.remove().first; - try { - GlUtil.deleteTexture(outputTexture.texId); - GlUtil.deleteFbo(outputTexture.fboId); - } catch (GlUtil.GlException exception) { - throw new VideoFrameProcessingException(exception); - } + public void releaseOutputFrame(long presentationTimeUs) { + while (outputTexturePool.freeTextureCount() < outputTexturePool.capacity() + && checkNotNull(outputTextureTimestamps.peek()) <= presentationTimeUs) { + outputTexturePool.freeTexture(); + outputTextureTimestamps.remove(); maybeOnReadyToAcceptInputFrame(); } } @@ -261,11 +260,7 @@ public synchronized void release() throws VideoFrameProcessingException { defaultShaderProgram.release(); } try { - while (!outputTextures.isEmpty()) { - GlTextureInfo outputTexture = outputTextures.remove().first; - GlUtil.deleteTexture(outputTexture.texId); - GlUtil.deleteFbo(outputTexture.fboId); - } + outputTexturePool.deleteAllTextures(); GlUtil.destroyEglSurface(eglDisplay, outputEglSurface); } catch (GlUtil.GlException e) { throw new VideoFrameProcessingException(e); @@ -304,7 +299,7 @@ public synchronized void setOutputSurfaceInfo(@Nullable SurfaceInfo outputSurfac } private void maybeOnReadyToAcceptInputFrame() { - if (textureOutputListener == null || outputTextures.size() < textureOutputCapacity) { + if (textureOutputListener == null || outputTexturePool.freeTextureCount() > 0) { inputListener.onReadyToAcceptInputFrame(); } } @@ -363,15 +358,8 @@ private synchronized void renderFrameToOutputSurface( private void renderFrameToOutputTexture(GlTextureInfo inputTexture, long presentationTimeUs) throws GlUtil.GlException, VideoFrameProcessingException { - // TODO(b/262694346): Use a texture pool instead of creating a new texture on every frame. - int outputTexId = - GlUtil.createTexture( - outputWidth, - outputHeight, - /* useHighPrecisionColorComponents= */ ColorInfo.isTransferHdr(outputColorInfo)); - GlTextureInfo outputTexture = - glObjectsProvider.createBuffersForTexture(outputTexId, outputWidth, outputHeight); - + GlTextureInfo outputTexture = outputTexturePool.useTexture(); + outputTextureTimestamps.add(presentationTimeUs); GlUtil.focusFramebufferUsingCurrentContext( outputTexture.fboId, outputTexture.width, outputTexture.height); GlUtil.clearOutputFrame(); @@ -381,7 +369,6 @@ private void renderFrameToOutputTexture(GlTextureInfo inputTexture, long present // glFinish. Consider removing glFinish and requiring onTextureRendered to handle // synchronization. GLES20.glFinish(); - outputTextures.add(Pair.create(outputTexture, presentationTimeUs)); checkNotNull(textureOutputListener).onTextureRendered(outputTexture, presentationTimeUs); } @@ -445,6 +432,9 @@ private synchronized boolean ensureConfigured(int inputWidth, int inputHeight) // Frames are only rendered automatically when outputting to an encoder. /* isEncoderInputSurface= */ renderFramesAutomatically); } + if (textureOutputListener != null) { + outputTexturePool.ensureConfigured(outputWidth, outputHeight); + } @Nullable SurfaceView debugSurfaceView = diff --git a/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java b/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java new file mode 100644 index 00000000000..88cb54a2ed2 --- /dev/null +++ b/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java @@ -0,0 +1,159 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.effect; + +import static androidx.media3.common.util.Assertions.checkState; + +import androidx.media3.common.GlObjectsProvider; +import androidx.media3.common.GlTextureInfo; +import androidx.media3.common.util.GlUtil; +import com.google.common.collect.Iterables; +import java.util.ArrayDeque; +import java.util.Iterator; +import java.util.Queue; + +/** Holds {@code capacity} textures, to re-use textures. */ +/* package */ final class TexturePool { + private final Queue freeTextures; + private final Queue inUseTextures; + private final int capacity; + private final boolean useHighPrecisionColorComponents; + + private GlObjectsProvider glObjectsProvider; + + /** + * Creates a {@code TexturePool} instance. + * + * @param useHighPrecisionColorComponents If {@code false}, uses colors with 8-bit unsigned bytes. + * If {@code true}, use 16-bit (half-precision) floating-point. + * @param capacity The capacity of the texture pool. + */ + public TexturePool(boolean useHighPrecisionColorComponents, int capacity) { + this.capacity = capacity; + this.useHighPrecisionColorComponents = useHighPrecisionColorComponents; + + freeTextures = new ArrayDeque<>(capacity); + inUseTextures = new ArrayDeque<>(capacity); + + glObjectsProvider = new DefaultGlObjectsProvider(/* sharedEglContext= */ null); + } + + /** Sets the {@link GlObjectsProvider}. */ + public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { + checkState(!isConfigured()); + this.glObjectsProvider = glObjectsProvider; + } + + /** Returns whether the instance has been {@linkplain #ensureConfigured configured}. */ + public boolean isConfigured() { + return getIteratorToAllTextures().hasNext(); + } + + /** Returns the {@code capacity} of the instance. */ + public int capacity() { + return capacity; + } + + /** Returns the number of free textures available to {@link #useTexture}. */ + public int freeTextureCount() { + if (!isConfigured()) { + return capacity; + } + return freeTextures.size(); + } + + /** + * Ensures that this instance is configured with the {@code width} and {@code height}. + * + *

    Reconfigures backing textures as needed. + */ + public void ensureConfigured(int width, int height) throws GlUtil.GlException { + if (!isConfigured()) { + createTextures(width, height); + return; + } + GlTextureInfo texture = getIteratorToAllTextures().next(); + if (texture.width != width || texture.height != height) { + deleteAllTextures(); + createTextures(width, height); + } + } + + /** Returns a {@link GlTextureInfo} and marks it as in-use. */ + public GlTextureInfo useTexture() { + if (freeTextures.isEmpty()) { + throw new IllegalStateException( + "Textures are all in use. Please release in-use textures before calling useTexture."); + } + GlTextureInfo texture = freeTextures.remove(); + inUseTextures.add(texture); + return texture; + } + + /** + * Frees the texture represented by {@code textureInfo}. + * + *

    Throws {@link IllegalStateException} if {@code textureInfo} isn't in use. + */ + public void freeTexture(GlTextureInfo textureInfo) { + checkState(inUseTextures.contains(textureInfo)); + inUseTextures.remove(textureInfo); + freeTextures.add(textureInfo); + } + + /** + * Frees the oldest in-use texture. + * + *

    Throws {@link IllegalStateException} if there's no textures in use to free. + */ + public void freeTexture() { + checkState(!inUseTextures.isEmpty()); + GlTextureInfo texture = inUseTextures.remove(); + freeTextures.add(texture); + } + + /** Free all in-use textures. */ + public void freeAllTextures() { + freeTextures.addAll(inUseTextures); + inUseTextures.clear(); + } + + /** Deletes all textures. */ + public void deleteAllTextures() throws GlUtil.GlException { + Iterator allTextures = getIteratorToAllTextures(); + while (allTextures.hasNext()) { + GlTextureInfo textureInfo = allTextures.next(); + GlUtil.deleteTexture(textureInfo.texId); + GlUtil.deleteFbo(textureInfo.fboId); + } + freeTextures.clear(); + inUseTextures.clear(); + } + + private void createTextures(int width, int height) throws GlUtil.GlException { + checkState(freeTextures.isEmpty()); + checkState(inUseTextures.isEmpty()); + for (int i = 0; i < capacity; i++) { + int texId = GlUtil.createTexture(width, height, useHighPrecisionColorComponents); + GlTextureInfo texture = glObjectsProvider.createBuffersForTexture(texId, width, height); + freeTextures.add(texture); + } + } + + private Iterator getIteratorToAllTextures() { + return Iterables.concat(freeTextures, inUseTextures).iterator(); + } +} From 962499c9d8fd36716d81e34b8e0e61f26bc271d2 Mon Sep 17 00:00:00 2001 From: ibaker Date: Wed, 17 May 2023 14:15:48 +0100 Subject: [PATCH 032/516] Add Media3 1.0.2 and ExoPlayer 2.18.7 to `bug.yml` template #minor-release PiperOrigin-RevId: 532765549 (cherry picked from commit 4ede3d600718969e6c62e855fcdf73805352a323) --- .github/ISSUE_TEMPLATE/bug.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 3763576fe5a..fd4047fc7e8 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -21,6 +21,7 @@ body: description: What version of Media3 (or ExoPlayer) are you using? options: - Media3 1.1.0-alpha01 + - Media3 1.0.2 - Media3 1.0.1 - Media3 1.0.0 - Media3 1.0.0-rc02 @@ -31,6 +32,7 @@ body: - Media3 1.0.0-alpha03 - Media3 1.0.0-alpha02 - Media3 1.0.0-alpha01 + - ExoPlayer 2.18.7 - ExoPlayer 2.18.6 - ExoPlayer 2.18.5 - ExoPlayer 2.18.4 From 514f032afcf36ecd19dd855a76d53ebcc40a4cd6 Mon Sep 17 00:00:00 2001 From: ibaker Date: Wed, 17 May 2023 14:21:21 +0100 Subject: [PATCH 033/516] Add `main`/`dev-v2` branch options to `bug.yml` template #minor-release PiperOrigin-RevId: 532766676 (cherry picked from commit 84d0206c767526802aa3798dad7742ea807d5bc6) --- .github/ISSUE_TEMPLATE/bug.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index fd4047fc7e8..c32439333a4 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -32,6 +32,7 @@ body: - Media3 1.0.0-alpha03 - Media3 1.0.0-alpha02 - Media3 1.0.0-alpha01 + - Media3 `main` branch - ExoPlayer 2.18.7 - ExoPlayer 2.18.6 - ExoPlayer 2.18.5 @@ -49,6 +50,7 @@ body: - ExoPlayer 2.14.2 - ExoPlayer 2.14.1 - ExoPlayer 2.14.0 + - ExoPlayer `dev-v2` branch - Older (unsupported) validations: required: true From 7adca46e4b85a5ebb71a495193224721f6285baa Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Wed, 17 May 2023 15:41:16 +0100 Subject: [PATCH 034/516] Move Mp4LocationData from extractor module to container module This class will be shared between extractor and muxer module. PiperOrigin-RevId: 532784415 (cherry picked from commit 7b62b33127a617cc3b7b23f21c385f2329e402a3) --- .../main/java/androidx/media3/container}/Mp4LocationData.java | 2 +- .../main/java/androidx/media3/extractor/mp4/AtomParsers.java | 2 +- libraries/transformer/build.gradle | 1 + .../main/java/androidx/media3/transformer/FrameworkMuxer.java | 2 +- .../src/main/java/androidx/media3/transformer/InAppMuxer.java | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) rename libraries/{extractor/src/main/java/androidx/media3/extractor/metadata/mp4 => container/src/main/java/androidx/media3/container}/Mp4LocationData.java (98%) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/metadata/mp4/Mp4LocationData.java b/libraries/container/src/main/java/androidx/media3/container/Mp4LocationData.java similarity index 98% rename from libraries/extractor/src/main/java/androidx/media3/extractor/metadata/mp4/Mp4LocationData.java rename to libraries/container/src/main/java/androidx/media3/container/Mp4LocationData.java index 7ae6dd25e6b..f645dc2eca3 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/metadata/mp4/Mp4LocationData.java +++ b/libraries/container/src/main/java/androidx/media3/container/Mp4LocationData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package androidx.media3.extractor.metadata.mp4; +package androidx.media3.container; import android.os.Parcel; import android.os.Parcelable; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java index 6c79e5f3d6c..e950696b2bf 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java @@ -33,6 +33,7 @@ import androidx.media3.common.util.Log; import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.Util; +import androidx.media3.container.Mp4LocationData; import androidx.media3.extractor.AacUtil; import androidx.media3.extractor.Ac3Util; import androidx.media3.extractor.Ac4Util; @@ -42,7 +43,6 @@ import androidx.media3.extractor.GaplessInfoHolder; import androidx.media3.extractor.HevcConfig; import androidx.media3.extractor.OpusUtil; -import androidx.media3.extractor.metadata.mp4.Mp4LocationData; import androidx.media3.extractor.metadata.mp4.SmtaMetadataEntry; import androidx.media3.extractor.mp4.Atom.LeafAtom; import com.google.common.base.Function; diff --git a/libraries/transformer/build.gradle b/libraries/transformer/build.gradle index 66662035b04..790effdffdf 100644 --- a/libraries/transformer/build.gradle +++ b/libraries/transformer/build.gradle @@ -37,6 +37,7 @@ android { dependencies { implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion implementation project(modulePrefix + 'lib-datasource') + implementation project(modulePrefix + 'lib-container') implementation project(modulePrefix + 'lib-exoplayer') implementation project(modulePrefix + 'lib-effect') implementation project(modulePrefix + 'lib-muxer') diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java index a78234984b9..6a3a86f49ec 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/FrameworkMuxer.java @@ -31,7 +31,7 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.util.MediaFormatUtil; import androidx.media3.common.util.Util; -import androidx.media3.extractor.metadata.mp4.Mp4LocationData; +import androidx.media3.container.Mp4LocationData; import com.google.common.collect.ImmutableList; import java.io.IOException; import java.lang.reflect.Field; diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java index 42e9f9118bc..31d5cefbb44 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java @@ -24,7 +24,7 @@ import androidx.media3.common.Metadata; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; -import androidx.media3.extractor.metadata.mp4.Mp4LocationData; +import androidx.media3.container.Mp4LocationData; import androidx.media3.muxer.Mp4Muxer; import androidx.media3.muxer.Mp4Muxer.TrackToken; import com.google.common.collect.ImmutableList; From 55c9d10022d5fb1986cf9a9c11e74148fc6a83d0 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Wed, 17 May 2023 15:32:20 +0000 Subject: [PATCH 035/516] HDR: Blocklist Galaxy Z Fold 4 for HLG tone mapping. This device failed on HdrEditingTest's exportAndTranscode_hlg10File_whenHdrEditingUnsupported_toneMapsOrThrows before this CL, and succeeds on that test after this CL. PiperOrigin-RevId: 532796897 (cherry picked from commit 83190a0fe9b7f0cd30398ecc6d1246412ec555d7) --- .../transformer/DefaultDecoderFactory.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java index 5f11677c8e5..6c143d154aa 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultDecoderFactory.java @@ -27,6 +27,7 @@ import android.util.Pair; import android.view.Surface; import androidx.annotation.Nullable; +import androidx.media3.common.C; import androidx.media3.common.ColorInfo; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; @@ -85,7 +86,10 @@ public Codec createForVideoDecoding( checkNotNull(format.sampleMimeType); if (ColorInfo.isTransferHdr(format.colorInfo)) { - if (requestSdrToneMapping && (SDK_INT < 31 || deviceNeedsNoToneMappingWorkaround())) { + if (requestSdrToneMapping + && (SDK_INT < 31 + || deviceNeedsDisableToneMappingWorkaround( + checkNotNull(format.colorInfo).colorTransfer))) { throw createExportException( format, /* reason= */ "Tone-mapping HDR is not supported on this device."); } @@ -128,10 +132,20 @@ public Codec createForVideoDecoding( context, format, mediaFormat, mediaCodecName, /* isDecoder= */ true, outputSurface); } - private static boolean deviceNeedsNoToneMappingWorkaround() { - // Some Pixel 6 builds report support for tone mapping but the feature doesn't work - // (see http://b/249297370#comment8). - return Util.MANUFACTURER.equals("Google") && Build.ID.startsWith("TP1A"); + private static boolean deviceNeedsDisableToneMappingWorkaround( + @C.ColorTransfer int colorTransfer) { + if (Util.MANUFACTURER.equals("Google") && Build.ID.startsWith("TP1A")) { + // Some Pixel 6 builds report support for tone mapping but the feature doesn't work + // (see b/249297370#comment8). + return true; + } + if (colorTransfer == C.COLOR_TRANSFER_HLG + && (Util.MODEL.startsWith("SM-F936") || Util.MODEL.startsWith("SM-F916"))) { + // Some Samsung Galaxy Z Fold devices report support for HLG tone mapping but the feature only + // works on PQ (see b/282791751#comment7). + return true; + } + return false; } @RequiresNonNull("#1.sampleMimeType") From 61a5dd76e3fc5c5ca95a658d6cdf97043da6bfbb Mon Sep 17 00:00:00 2001 From: rohks Date: Wed, 17 May 2023 16:13:02 +0000 Subject: [PATCH 036/516] Remove deprecated methods that create an instance of `Format` Use `Format.Builder` instead. #minor-release PiperOrigin-RevId: 532808478 (cherry picked from commit 18aa664cb8574d1bad4aca24917ffeb74181309a) --- RELEASENOTES.md | 4 + .../java/androidx/media3/common/Format.java | 178 ------------------ 2 files changed, 4 insertions(+), 178 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3c5f6a5a15b..8a2a49c8afd 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -40,6 +40,10 @@ * Remove deprecated `WorkManagerScheduler` constructor, use a non deprecated constructor that includes the option to provide a `Context` parameter instead. + * Remove the deprecated methods `createVideoSampleFormat`, + `createAudioSampleFormat`, `createContainerFormat`, and + `createSampleFormat`, which were used to instantiate the `Format` class. + Instead use `Format.Builder` for creating instances of `Format`. ### 1.0.2 (2023-05-18) diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index b9b935c239c..e2e7e52f0b9 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -846,184 +846,6 @@ public Format build() { // Lazily initialized hashcode. private int hashCode; - // Video. - - /** - * @deprecated Use {@link Format.Builder}. - */ - @UnstableApi - @Deprecated - public static Format createVideoSampleFormat( - @Nullable String id, - @Nullable String sampleMimeType, - @Nullable String codecs, - int bitrate, - int maxInputSize, - int width, - int height, - float frameRate, - @Nullable List initializationData, - @Nullable DrmInitData drmInitData) { - return new Builder() - .setId(id) - .setAverageBitrate(bitrate) - .setPeakBitrate(bitrate) - .setCodecs(codecs) - .setSampleMimeType(sampleMimeType) - .setMaxInputSize(maxInputSize) - .setInitializationData(initializationData) - .setDrmInitData(drmInitData) - .setWidth(width) - .setHeight(height) - .setFrameRate(frameRate) - .build(); - } - - /** - * @deprecated Use {@link Format.Builder}. - */ - @UnstableApi - @Deprecated - public static Format createVideoSampleFormat( - @Nullable String id, - @Nullable String sampleMimeType, - @Nullable String codecs, - int bitrate, - int maxInputSize, - int width, - int height, - float frameRate, - @Nullable List initializationData, - int rotationDegrees, - float pixelWidthHeightRatio, - @Nullable DrmInitData drmInitData) { - return new Builder() - .setId(id) - .setAverageBitrate(bitrate) - .setPeakBitrate(bitrate) - .setCodecs(codecs) - .setSampleMimeType(sampleMimeType) - .setMaxInputSize(maxInputSize) - .setInitializationData(initializationData) - .setDrmInitData(drmInitData) - .setWidth(width) - .setHeight(height) - .setFrameRate(frameRate) - .setRotationDegrees(rotationDegrees) - .setPixelWidthHeightRatio(pixelWidthHeightRatio) - .build(); - } - - // Audio. - - /** - * @deprecated Use {@link Format.Builder}. - */ - @UnstableApi - @Deprecated - public static Format createAudioSampleFormat( - @Nullable String id, - @Nullable String sampleMimeType, - @Nullable String codecs, - int bitrate, - int maxInputSize, - int channelCount, - int sampleRate, - @Nullable List initializationData, - @Nullable DrmInitData drmInitData, - @C.SelectionFlags int selectionFlags, - @Nullable String language) { - return new Builder() - .setId(id) - .setLanguage(language) - .setSelectionFlags(selectionFlags) - .setAverageBitrate(bitrate) - .setPeakBitrate(bitrate) - .setCodecs(codecs) - .setSampleMimeType(sampleMimeType) - .setMaxInputSize(maxInputSize) - .setInitializationData(initializationData) - .setDrmInitData(drmInitData) - .setChannelCount(channelCount) - .setSampleRate(sampleRate) - .build(); - } - - /** - * @deprecated Use {@link Format.Builder}. - */ - @UnstableApi - @Deprecated - public static Format createAudioSampleFormat( - @Nullable String id, - @Nullable String sampleMimeType, - @Nullable String codecs, - int bitrate, - int maxInputSize, - int channelCount, - int sampleRate, - @C.PcmEncoding int pcmEncoding, - @Nullable List initializationData, - @Nullable DrmInitData drmInitData, - @C.SelectionFlags int selectionFlags, - @Nullable String language) { - return new Builder() - .setId(id) - .setLanguage(language) - .setSelectionFlags(selectionFlags) - .setAverageBitrate(bitrate) - .setPeakBitrate(bitrate) - .setCodecs(codecs) - .setSampleMimeType(sampleMimeType) - .setMaxInputSize(maxInputSize) - .setInitializationData(initializationData) - .setDrmInitData(drmInitData) - .setChannelCount(channelCount) - .setSampleRate(sampleRate) - .setPcmEncoding(pcmEncoding) - .build(); - } - - // Generic. - - /** - * @deprecated Use {@link Format.Builder}. - */ - @UnstableApi - @Deprecated - public static Format createContainerFormat( - @Nullable String id, - @Nullable String label, - @Nullable String containerMimeType, - @Nullable String sampleMimeType, - @Nullable String codecs, - int bitrate, - @C.SelectionFlags int selectionFlags, - @C.RoleFlags int roleFlags, - @Nullable String language) { - return new Builder() - .setId(id) - .setLabel(label) - .setLanguage(language) - .setSelectionFlags(selectionFlags) - .setRoleFlags(roleFlags) - .setAverageBitrate(bitrate) - .setPeakBitrate(bitrate) - .setCodecs(codecs) - .setContainerMimeType(containerMimeType) - .setSampleMimeType(sampleMimeType) - .build(); - } - - /** - * @deprecated Use {@link Format.Builder}. - */ - @UnstableApi - @Deprecated - public static Format createSampleFormat(@Nullable String id, @Nullable String sampleMimeType) { - return new Builder().setId(id).setSampleMimeType(sampleMimeType).build(); - } - private Format(Builder builder) { id = builder.id; label = builder.label; From b97ec5edfc758bc9ed0c3b71f425acb6a1dc3071 Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Wed, 17 May 2023 17:49:53 +0000 Subject: [PATCH 037/516] Add default constructor for InAppMuxer.Factory PiperOrigin-RevId: 532838813 (cherry picked from commit 410840c9e19836fe1fc5c38001a6150fefe29027) --- .../TransformerWithInAppMuxerEndToEndTest.java | 10 ++-------- .../mh/TransformerWithInAppMuxerEndToEndTest.java | 6 +----- .../java/androidx/media3/transformer/InAppMuxer.java | 10 ++++++++++ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerWithInAppMuxerEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerWithInAppMuxerEndToEndTest.java index bd013cdb901..00432e19057 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerWithInAppMuxerEndToEndTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/TransformerWithInAppMuxerEndToEndTest.java @@ -53,10 +53,7 @@ public static ImmutableList mediaFiles() { public void videoEditing_completesSuccessfully() throws Exception { String testId = "videoEditing_completesSuccessfully"; Transformer transformer = - new Transformer.Builder(context) - .setMuxerFactory( - new InAppMuxer.Factory(DefaultMuxer.Factory.DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS)) - .build(); + new Transformer.Builder(context).setMuxerFactory(new InAppMuxer.Factory()).build(); ImmutableList videoEffects = ImmutableList.of(RgbFilter.createGrayscaleFilter()); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_FILE_ASSET_DIRECTORY + inputFile)); EditedMediaItem editedMediaItem = @@ -76,10 +73,7 @@ public void videoEditing_completesSuccessfully() throws Exception { public void audioEditing_completesSuccessfully() throws Exception { String testId = "audioEditing_completesSuccessfully"; Transformer transformer = - new Transformer.Builder(context) - .setMuxerFactory( - new InAppMuxer.Factory(DefaultMuxer.Factory.DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS)) - .build(); + new Transformer.Builder(context).setMuxerFactory(new InAppMuxer.Factory()).build(); ChannelMixingAudioProcessor channelMixingAudioProcessor = new ChannelMixingAudioProcessor(); channelMixingAudioProcessor.putChannelMixingMatrix( ChannelMixingMatrix.create(/* inputChannelCount= */ 1, /* outputChannelCount= */ 2)); diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerWithInAppMuxerEndToEndTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerWithInAppMuxerEndToEndTest.java index 5a68a30f8f5..2a6fd461935 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerWithInAppMuxerEndToEndTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/TransformerWithInAppMuxerEndToEndTest.java @@ -25,7 +25,6 @@ import androidx.media3.common.MediaItem; import androidx.media3.effect.RgbFilter; import androidx.media3.transformer.AndroidTestUtil; -import androidx.media3.transformer.DefaultMuxer; import androidx.media3.transformer.EditedMediaItem; import androidx.media3.transformer.Effects; import androidx.media3.transformer.ExportTestResult; @@ -50,10 +49,7 @@ public void videoEditing_forAv1Video_completesSuccessfully() throws Exception { return; } Transformer transformer = - new Transformer.Builder(context) - .setMuxerFactory( - new InAppMuxer.Factory(DefaultMuxer.Factory.DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS)) - .build(); + new Transformer.Builder(context).setMuxerFactory(new InAppMuxer.Factory()).build(); ImmutableList videoEffects = ImmutableList.of(RgbFilter.createGrayscaleFilter()); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(MP4_ASSET_AV1_VIDEO_URI_STRING)); EditedMediaItem editedMediaItem = diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java index 31d5cefbb44..f80f7f4e8bb 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java @@ -42,6 +42,16 @@ public final class InAppMuxer implements Muxer { public static final class Factory implements Muxer.Factory { private final long maxDelayBetweenSamplesMs; + /** + * Creates an instance with {@link Muxer#getMaxDelayBetweenSamplesMs() maxDelayBetweenSamplesMs} + * set to {@link DefaultMuxer.Factory#DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS}. + */ + public Factory() { + this( + /* maxDelayBetweenSamplesMs= */ DefaultMuxer.Factory + .DEFAULT_MAX_DELAY_BETWEEN_SAMPLES_MS); + } + /** {@link Muxer.Factory} for {@link InAppMuxer}. */ public Factory(long maxDelayBetweenSamplesMs) { this.maxDelayBetweenSamplesMs = maxDelayBetweenSamplesMs; From 633c339dc18a8a6d5039f1f2a20d0c6bda401094 Mon Sep 17 00:00:00 2001 From: rohks Date: Wed, 17 May 2023 17:54:57 +0000 Subject: [PATCH 038/516] Remove deprecated methods `Format.copyWithXXX` Use `Format.buildUpon()` and `setXXX` instead. #minor-release PiperOrigin-RevId: 532840625 (cherry picked from commit 538524e579496aa75469ddd8fea76a963236da42) --- RELEASENOTES.md | 6 ++ .../java/androidx/media3/common/Format.java | 93 ------------------- 2 files changed, 6 insertions(+), 93 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8a2a49c8afd..c3c3677ae3e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -44,6 +44,12 @@ `createAudioSampleFormat`, `createContainerFormat`, and `createSampleFormat`, which were used to instantiate the `Format` class. Instead use `Format.Builder` for creating instances of `Format`. + * Remove the deprecated methods `copyWithMaxInputSize`, + `copyWithSubsampleOffsetUs`, `copyWithLabel`, + `copyWithManifestFormatInfo`, `copyWithGaplessInfo`, + `copyWithFrameRate`, `copyWithDrmInitData`, `copyWithMetadata`, + `copyWithBitrate` and `copyWithVideoSize`, use `Format.buildUpon()` and + setter methods instead. ### 1.0.2 (2023-05-18) diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index e2e7e52f0b9..d9e50851fdb 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -902,42 +902,6 @@ public Builder buildUpon() { return new Builder(this); } - /** - * @deprecated Use {@link #buildUpon()} and {@link Builder#setMaxInputSize(int)}. - */ - @UnstableApi - @Deprecated - public Format copyWithMaxInputSize(int maxInputSize) { - return buildUpon().setMaxInputSize(maxInputSize).build(); - } - - /** - * @deprecated Use {@link #buildUpon()} and {@link Builder#setSubsampleOffsetUs(long)}. - */ - @UnstableApi - @Deprecated - public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) { - return buildUpon().setSubsampleOffsetUs(subsampleOffsetUs).build(); - } - - /** - * @deprecated Use {@link #buildUpon()} and {@link Builder#setLabel(String)} . - */ - @UnstableApi - @Deprecated - public Format copyWithLabel(@Nullable String label) { - return buildUpon().setLabel(label).build(); - } - - /** - * @deprecated Use {@link #withManifestFormatInfo(Format)}. - */ - @UnstableApi - @Deprecated - public Format copyWithManifestFormatInfo(Format manifestFormat) { - return withManifestFormatInfo(manifestFormat); - } - @UnstableApi @SuppressWarnings("ReferenceEquality") public Format withManifestFormatInfo(Format manifestFormat) { @@ -1006,63 +970,6 @@ public Format withManifestFormatInfo(Format manifestFormat) { .build(); } - /** - * @deprecated Use {@link #buildUpon()}, {@link Builder#setEncoderDelay(int)} and {@link - * Builder#setEncoderPadding(int)}. - */ - @UnstableApi - @Deprecated - public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) { - return buildUpon().setEncoderDelay(encoderDelay).setEncoderPadding(encoderPadding).build(); - } - - /** - * @deprecated Use {@link #buildUpon()} and {@link Builder#setFrameRate(float)}. - */ - @UnstableApi - @Deprecated - public Format copyWithFrameRate(float frameRate) { - return buildUpon().setFrameRate(frameRate).build(); - } - - /** - * @deprecated Use {@link #buildUpon()} and {@link Builder#setDrmInitData(DrmInitData)}. - */ - @UnstableApi - @Deprecated - public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) { - return buildUpon().setDrmInitData(drmInitData).build(); - } - - /** - * @deprecated Use {@link #buildUpon()} and {@link Builder#setMetadata(Metadata)}. - */ - @UnstableApi - @Deprecated - public Format copyWithMetadata(@Nullable Metadata metadata) { - return buildUpon().setMetadata(metadata).build(); - } - - /** - * @deprecated Use {@link #buildUpon()} and {@link Builder#setAverageBitrate(int)} and {@link - * Builder#setPeakBitrate(int)}. - */ - @UnstableApi - @Deprecated - public Format copyWithBitrate(int bitrate) { - return buildUpon().setAverageBitrate(bitrate).setPeakBitrate(bitrate).build(); - } - - /** - * @deprecated Use {@link #buildUpon()}, {@link Builder#setWidth(int)} and {@link - * Builder#setHeight(int)}. - */ - @UnstableApi - @Deprecated - public Format copyWithVideoSize(int width, int height) { - return buildUpon().setWidth(width).setHeight(height).build(); - } - /** Returns a copy of this format with the specified {@link #cryptoType}. */ @UnstableApi public Format copyWithCryptoType(@C.CryptoType int cryptoType) { From 431b985a0493a4c791e0c96aed69acb7b071bce9 Mon Sep 17 00:00:00 2001 From: tofunmi Date: Wed, 17 May 2023 18:11:48 +0000 Subject: [PATCH 039/516] HDR texture asset loading PiperOrigin-RevId: 532846248 (cherry picked from commit 5fe10d76520137d860fe9f847f467cb4926427e0) --- ...hader_transformation_external_yuv_es3.glsl | 2 +- ...hader_transformation_hdr_internal_es3.glsl | 232 +++++++++++++++ .../media3/effect/DefaultShaderProgram.java | 56 ++-- .../effect/DefaultVideoFrameProcessor.java | 10 +- ...oFrameProcessorTextureOutputPixelTest.java | 269 ++++++++++++++---- 5 files changed, 482 insertions(+), 87 deletions(-) create mode 100644 libraries/effect/src/main/assets/shaders/fragment_shader_transformation_hdr_internal_es3.glsl diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_external_yuv_es3.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_external_yuv_es3.glsl index a195f5de204..84d05c0f979 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_external_yuv_es3.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_external_yuv_es3.glsl @@ -29,7 +29,7 @@ // for outputting to intermediate shaders, or COLOR_TRANSFER_ST2084 / // COLOR_TRANSFER_HLG to output electrical colors via an OETF (e.g. to an // encoder). -// The output will be red if an error has occurred. +// The output will be red or blue if an error has occurred. #extension GL_OES_EGL_image_external : require #extension GL_EXT_YUV_target : require diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_hdr_internal_es3.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_hdr_internal_es3.glsl new file mode 100644 index 00000000000..6b0e377a29a --- /dev/null +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_hdr_internal_es3.glsl @@ -0,0 +1,232 @@ +#version 300 es +// Copyright 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// ES 3 fragment shader that: +// 1. Samples electrical (HLG or PQ) BT.2020 RGB from an internal texture. +// 2. Applies an EOTF based on uInputColorTransfer, yielding optical linear +// BT.2020 RGB. +// 3. Optionally applies a BT2020 to BT709 OOTF, if OpenGL tone-mapping is +// requested via uApplyHdrToSdrToneMapping. +// 4. Applies a 4x4 RGB color matrix to change the pixel colors. +// 5. Outputs as requested by uOutputColorTransfer. Use COLOR_TRANSFER_LINEAR +// for outputting to intermediate shaders, or COLOR_TRANSFER_ST2084 / +// COLOR_TRANSFER_HLG to output electrical colors via an OETF (e.g. to an +// encoder). +// The output will be red or blue if an error has occurred. + +precision mediump float; +uniform sampler2D uTexSampler; +uniform mat4 uRgbMatrix; +// C.java#ColorTransfer value. +// Only COLOR_TRANSFER_ST2084 and COLOR_TRANSFER_HLG are allowed. +uniform int uInputColorTransfer; +uniform int uApplyHdrToSdrToneMapping; +// C.java#ColorTransfer value. +// Only COLOR_TRANSFER_LINEAR, COLOR_TRANSFER_GAMMA_2_2, COLOR_TRANSFER_ST2084, +// and COLOR_TRANSFER_HLG are allowed. +uniform int uOutputColorTransfer; +in vec2 vTexSamplingCoord; +out vec4 outColor; + +// LINT.IfChange(color_transfer) +const int COLOR_TRANSFER_LINEAR = 1; +const int COLOR_TRANSFER_GAMMA_2_2 = 10; +const int COLOR_TRANSFER_ST2084 = 6; +const int COLOR_TRANSFER_HLG = 7; + +// TODO(b/227624622): Consider using mediump to save precision, if it won't lead +// to noticeable quantization errors. + +// BT.2100 / BT.2020 HLG EOTF for one channel. +highp float hlgEotfSingleChannel(highp float hlgChannel) { + // Specification: + // https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG + // Reference implementation: + // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=265-279;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa + const highp float a = 0.17883277; + const highp float b = 0.28466892; + const highp float c = 0.55991073; + return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0 : + (b + exp((hlgChannel - c) / a)) / 12.0; +} + +// BT.2100 / BT.2020 HLG EOTF. +highp vec3 hlgEotf(highp vec3 hlgColor) { + return vec3( + hlgEotfSingleChannel(hlgColor.r), + hlgEotfSingleChannel(hlgColor.g), + hlgEotfSingleChannel(hlgColor.b) + ); +} + +// BT.2100 / BT.2020 PQ EOTF. +highp vec3 pqEotf(highp vec3 pqColor) { + // Specification: + // https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ + // Reference implementation: + // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=250-263;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa + const highp float m1 = (2610.0 / 16384.0); + const highp float m2 = (2523.0 / 4096.0) * 128.0; + const highp float c1 = (3424.0 / 4096.0); + const highp float c2 = (2413.0 / 4096.0) * 32.0; + const highp float c3 = (2392.0 / 4096.0) * 32.0; + + highp vec3 temp = pow(clamp(pqColor, 0.0, 1.0), 1.0 / vec3(m2)); + temp = max(temp - c1, 0.0) / (c2 - c3 * temp); + return pow(temp, 1.0 / vec3(m1)); +} + +// Applies the appropriate EOTF to convert nonlinear electrical values to linear +// optical values. Input and output are both normalized to [0, 1]. +highp vec3 applyEotf(highp vec3 electricalColor) { + if (uInputColorTransfer == COLOR_TRANSFER_ST2084) { + return pqEotf(electricalColor); + } else if (uInputColorTransfer == COLOR_TRANSFER_HLG) { + return hlgEotf(electricalColor); + } else { + // Output red as an obviously visible error. + return vec3(1.0, 0.0, 0.0); + } +} + +// Apply the HLG BT2020 to BT709 OOTF. +highp vec3 applyHlgBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) { + // Reference ("HLG Reference OOTF" section): + // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf + // Matrix values based on computeXYZMatrix(BT2020Primaries, BT2020WhitePoint) + // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/utils/HostColorSpace.cpp;l=200-232;drc=86bd214059cd6150304888a285941bf74af5b687 + const mat3 RGB_TO_XYZ_BT2020 = mat3( + 0.63695805f, 0.26270021f, 0.00000000f, + 0.14461690f, 0.67799807f, 0.02807269f, + 0.16888098f, 0.05930172f, 1.06098506f); + // Matrix values based on computeXYZMatrix(BT709Primaries, BT709WhitePoint) + const mat3 XYZ_TO_RGB_BT709 = mat3( + 3.24096994f, -0.96924364f, 0.05563008f, + -1.53738318f, 1.87596750f, -0.20397696f, + -0.49861076f, 0.04155506f, 1.05697151f); + // hlgGamma is 1.2 + 0.42 * log10(nominalPeakLuminance/1000); + // nominalPeakLuminance was selected to use a 500 as a typical value, used + // in https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/tonemap/tonemap.cpp;drc=7a577450e536aa1e99f229a0cb3d3531c82e8a8d;l=62, + // b/199162498#comment35, and + // https://www.microsoft.com/applied-sciences/uploads/projects/investigation-of-hdr-vs-tone-mapped-sdr/investigation-of-hdr-vs-tone-mapped-sdr.pdf. + const float hlgGamma = 1.0735674018211279; + + vec3 linearXyzBt2020 = RGB_TO_XYZ_BT2020 * linearRgbBt2020; + vec3 linearXyzBt709 = + linearXyzBt2020 * pow(linearXyzBt2020[1], hlgGamma - 1.0); + vec3 linearRgbBt709 = clamp((XYZ_TO_RGB_BT709 * linearXyzBt709), 0.0, 1.0); + return linearRgbBt709; +} + +// Apply the PQ BT2020 to BT709 OOTF. +highp vec3 applyPqBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) { + float pqPeakLuminance = 10000.0; + float sdrPeakLuminance = 500.0; + + return linearRgbBt2020 * pqPeakLuminance / sdrPeakLuminance; +} + +highp vec3 applyBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) { + if (uInputColorTransfer == COLOR_TRANSFER_ST2084) { + return applyPqBt2020ToBt709Ootf(linearRgbBt2020); + } else if (uInputColorTransfer == COLOR_TRANSFER_HLG) { + return applyHlgBt2020ToBt709Ootf(linearRgbBt2020); + } else { + // Output green as an obviously visible error. + return vec3(0.0, 1.0, 0.0); + } +} + +// BT.2100 / BT.2020 HLG OETF for one channel. +highp float hlgOetfSingleChannel(highp float linearChannel) { + // Specification: + // https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_HLG + // Reference implementation: + // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=529-543;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa + const highp float a = 0.17883277; + const highp float b = 0.28466892; + const highp float c = 0.55991073; + + return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel) : + a * log(12.0 * linearChannel - b) + c; +} + +// BT.2100 / BT.2020 HLG OETF. +highp vec3 hlgOetf(highp vec3 linearColor) { + return vec3( + hlgOetfSingleChannel(linearColor.r), + hlgOetfSingleChannel(linearColor.g), + hlgOetfSingleChannel(linearColor.b) + ); +} + +// BT.2100 / BT.2020, PQ / ST2084 OETF. +highp vec3 pqOetf(highp vec3 linearColor) { + // Specification: + // https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.inline.html#TRANSFER_PQ + // Reference implementation: + // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/renderengine/gl/ProgramCache.cpp;l=514-527;drc=de09f10aa504fd8066370591a00c9ff1cafbb7fa + const highp float m1 = (2610.0 / 16384.0); + const highp float m2 = (2523.0 / 4096.0) * 128.0; + const highp float c1 = (3424.0 / 4096.0); + const highp float c2 = (2413.0 / 4096.0) * 32.0; + const highp float c3 = (2392.0 / 4096.0) * 32.0; + + highp vec3 temp = pow(linearColor, vec3(m1)); + temp = (c1 + c2 * temp) / (1.0 + c3 * temp); + return pow(temp, vec3(m2)); +} + +// BT.709 gamma 2.2 OETF for one channel. +float gamma22OetfSingleChannel(highp float linearChannel) { + // Reference: + // https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_GAMMA2_2 + return pow(linearChannel, (1.0 / 2.2)); +} + +// BT.709 gamma 2.2 OETF. +vec3 gamma22Oetf(highp vec3 linearColor) { + return vec3( + gamma22OetfSingleChannel(linearColor.r), + gamma22OetfSingleChannel(linearColor.g), + gamma22OetfSingleChannel(linearColor.b)); +} + +// Applies the appropriate OETF to convert linear optical signals to nonlinear +// electrical signals. Input and output are both normalized to [0, 1]. +highp vec3 applyOetf(highp vec3 linearColor) { + if (uOutputColorTransfer == COLOR_TRANSFER_ST2084) { + return pqOetf(linearColor); + } else if (uOutputColorTransfer == COLOR_TRANSFER_HLG) { + return hlgOetf(linearColor); + } else if (uOutputColorTransfer == COLOR_TRANSFER_GAMMA_2_2) { + return gamma22Oetf(linearColor); + } else if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR) { + return linearColor; + } else { + // Output blue as an obviously visible error. + return vec3(0.0, 0.0, 1.0); + } +} + +void main() { + vec3 opticalColorBt2020 = applyEotf( + texture(uTexSampler, vTexSamplingCoord).xyz); + vec4 opticalColor = (uApplyHdrToSdrToneMapping == 1) + ? vec4(applyBt2020ToBt709Ootf(opticalColorBt2020), 1.0) + : vec4(opticalColorBt2020, 1.0); + vec4 transformedColors = uRgbMatrix * opticalColor; + outColor = vec4(applyOetf(transformedColors.rgb), 1.0); +} diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java index 0e1a94deea3..61a4b8faa8f 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java @@ -71,6 +71,8 @@ "shaders/fragment_shader_transformation_external_yuv_es3.glsl"; private static final String FRAGMENT_SHADER_TRANSFORMATION_SDR_EXTERNAL_PATH = "shaders/fragment_shader_transformation_sdr_external_es2.glsl"; + private static final String FRAGMENT_SHADER_TRANSFORMATION_HDR_INTERNAL_ES3_PATH = + "shaders/fragment_shader_transformation_hdr_internal_es3.glsl"; private static final String FRAGMENT_SHADER_TRANSFORMATION_SDR_INTERNAL_PATH = "shaders/fragment_shader_transformation_sdr_internal_es2.glsl"; private static final ImmutableList NDC_SQUARE = @@ -204,16 +206,18 @@ public static DefaultShaderProgram createWithInternalSampler( boolean enableColorTransfers, @InputType int inputType) throws VideoFrameProcessingException { - checkState( - !ColorInfo.isTransferHdr(inputColorInfo), - "DefaultShaderProgram doesn't support HDR internal sampler input yet."); checkState( inputColorInfo.colorTransfer != C.COLOR_TRANSFER_SRGB || inputType == INPUT_TYPE_BITMAP); - GlProgram glProgram = - createGlProgram( - context, - VERTEX_SHADER_TRANSFORMATION_PATH, - FRAGMENT_SHADER_TRANSFORMATION_SDR_INTERNAL_PATH); + boolean isInputTransferHdr = ColorInfo.isTransferHdr(inputColorInfo); + String vertexShaderFilePath = + isInputTransferHdr + ? VERTEX_SHADER_TRANSFORMATION_ES3_PATH + : VERTEX_SHADER_TRANSFORMATION_PATH; + String fragmentShaderFilePath = + isInputTransferHdr + ? FRAGMENT_SHADER_TRANSFORMATION_HDR_INTERNAL_ES3_PATH + : FRAGMENT_SHADER_TRANSFORMATION_SDR_INTERNAL_PATH; + GlProgram glProgram = createGlProgram(context, vertexShaderFilePath, fragmentShaderFilePath); glProgram.setIntUniform("uInputColorTransfer", inputColorInfo.colorTransfer); return createWithSampler( glProgram, @@ -268,8 +272,23 @@ public static DefaultShaderProgram createWithExternalSampler( isInputTransferHdr ? FRAGMENT_SHADER_TRANSFORMATION_EXTERNAL_YUV_ES3_PATH : FRAGMENT_SHADER_TRANSFORMATION_SDR_EXTERNAL_PATH; + GlProgram glProgram = createGlProgram(context, vertexShaderFilePath, fragmentShaderFilePath); + if (isInputTransferHdr) { + // In HDR editing mode the decoder output is sampled in YUV. + if (!GlUtil.isYuvTargetExtensionSupported()) { + throw new VideoFrameProcessingException( + "The EXT_YUV_target extension is required for HDR editing input."); + } + glProgram.setFloatsUniform( + "uYuvToRgbColorTransform", + inputColorInfo.colorRange == C.COLOR_RANGE_FULL + ? BT2020_FULL_RANGE_YUV_TO_RGB_COLOR_TRANSFORM_MATRIX + : BT2020_LIMITED_RANGE_YUV_TO_RGB_COLOR_TRANSFORM_MATRIX); + glProgram.setIntUniform("uInputColorTransfer", inputColorInfo.colorTransfer); + } + return createWithSampler( - createGlProgram(context, vertexShaderFilePath, fragmentShaderFilePath), + glProgram, matrixTransformations, rgbMatrices, inputColorInfo, @@ -343,31 +362,16 @@ private static DefaultShaderProgram createWithSampler( List rgbMatrices, ColorInfo inputColorInfo, ColorInfo outputColorInfo, - boolean enableColorTransfers) - throws VideoFrameProcessingException { + boolean enableColorTransfers) { boolean isInputTransferHdr = ColorInfo.isTransferHdr(inputColorInfo); @C.ColorTransfer int outputColorTransfer = outputColorInfo.colorTransfer; if (isInputTransferHdr) { checkArgument(inputColorInfo.colorSpace == C.COLOR_SPACE_BT2020); checkArgument(enableColorTransfers); - - // In HDR editing mode the decoder output is sampled in YUV. - if (!GlUtil.isYuvTargetExtensionSupported()) { - throw new VideoFrameProcessingException( - "The EXT_YUV_target extension is required for HDR editing input."); - } - glProgram.setFloatsUniform( - "uYuvToRgbColorTransform", - inputColorInfo.colorRange == C.COLOR_RANGE_FULL - ? BT2020_FULL_RANGE_YUV_TO_RGB_COLOR_TRANSFORM_MATRIX - : BT2020_LIMITED_RANGE_YUV_TO_RGB_COLOR_TRANSFORM_MATRIX); - - checkArgument(ColorInfo.isTransferHdr(inputColorInfo)); - glProgram.setIntUniform("uInputColorTransfer", inputColorInfo.colorTransfer); // TODO(b/239735341): Add a setBooleanUniform method to GlProgram. glProgram.setIntUniform( "uApplyHdrToSdrToneMapping", - /* value= */ (outputColorInfo.colorSpace != C.COLOR_SPACE_BT2020) ? 1 : 0); + /* value= */ (outputColorInfo.colorSpace != C.COLOR_SPACE_BT2020) ? GL_TRUE : GL_FALSE); checkArgument( outputColorTransfer != Format.NO_VALUE && outputColorTransfer != C.COLOR_TRANSFER_SDR); glProgram.setIntUniform("uOutputColorTransfer", outputColorTransfer); diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index 0e811e1c586..f29eda1147c 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -620,12 +620,12 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( inputSwitcher.registerInput(INPUT_TYPE_SURFACE); if (!ColorInfo.isTransferHdr(inputColorInfo)) { - // HDR bitmap or texture input is not supported. + // HDR bitmap input is not supported. inputSwitcher.registerInput(INPUT_TYPE_BITMAP); - if (inputColorInfo.colorTransfer != C.COLOR_TRANSFER_SRGB) { - // Image and textureId concatenation not supported. - inputSwitcher.registerInput(INPUT_TYPE_TEXTURE_ID); - } + } + if (inputColorInfo.colorTransfer != C.COLOR_TRANSFER_SRGB) { + // Image and textureId concatenation not supported. + inputSwitcher.registerInput(INPUT_TYPE_TEXTURE_ID); } inputSwitcher.setDownstreamShaderProgram(effectsShaderPrograms.get(0)); diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java index eec751a942f..63064b47463 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java @@ -15,6 +15,7 @@ */ package androidx.media3.transformer.mh; +import static androidx.media3.common.ColorInfo.SDR_BT709_LIMITED; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; @@ -34,6 +35,7 @@ import android.graphics.Bitmap; import android.view.Surface; import androidx.media3.common.ColorInfo; +import androidx.media3.common.Effect; import androidx.media3.common.Format; import androidx.media3.common.GlObjectsProvider; import androidx.media3.common.GlTextureInfo; @@ -55,6 +57,7 @@ import androidx.media3.transformer.EncoderUtil; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; +import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.junit.After; @@ -89,6 +92,8 @@ public final class DefaultVideoFrameProcessorTextureOutputPixelTest { /** Input HLG video of which we only use the first frame. */ private static final String INPUT_HLG10_MP4_ASSET_STRING = "media/mp4/hlg-1080p.mp4"; + // A passthrough effect allows for testing having an intermediate effect injected, which uses + // different OpenGL shaders from having no effects. private static final GlEffect NO_OP_EFFECT = new GlEffectWrapper(new ScaleAndRotateTransformation.Builder().build()); @@ -111,7 +116,7 @@ public void noEffects_matchesGoldenFile() throws Exception { /* outputFormat= */ null)) { return; } - videoFrameProcessorTestRunner = getDefaultFrameProcessorTestRunnerBuilder(testId).build(); + videoFrameProcessorTestRunner = getSurfaceInputFrameProcessorTestRunnerBuilder(testId).build(); Bitmap expectedBitmap = readBitmap(ORIGINAL_PNG_ASSET_PATH); videoFrameProcessorTestRunner.processFirstFrameAndEnd(); @@ -134,23 +139,14 @@ public void noEffects_textureInput_matchesGoldenFile() throws Exception { /* outputFormat= */ null)) { return; } - TextureBitmapReader producersBitmapReader = new TextureBitmapReader(); TextureBitmapReader consumersBitmapReader = new TextureBitmapReader(); - DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = - new DefaultVideoFrameProcessor.Factory.Builder() - .setTextureOutput( - (outputTexture, presentationTimeUs) -> - inputTextureIntoVideoFrameProcessor( - testId, consumersBitmapReader, outputTexture, presentationTimeUs), - /* textureOutputCapacity= */ 1) - .build(); VideoFrameProcessorTestRunner texIdProducingVideoFrameProcessorTestRunner = - new VideoFrameProcessorTestRunner.Builder() - .setTestId(testId) - .setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactory) - .setVideoAssetPath(INPUT_SDR_MP4_ASSET_STRING) - .setBitmapReader(producersBitmapReader) - .build(); + getTexIdProducingFrameProcessorTestRunner( + testId, + consumersBitmapReader, + INPUT_SDR_MP4_ASSET_STRING, + SDR_BT709_LIMITED, + ImmutableList.of()); Bitmap expectedBitmap = readBitmap(ORIGINAL_PNG_ASSET_PATH); texIdProducingVideoFrameProcessorTestRunner.processFirstFrameAndEnd(); @@ -177,7 +173,7 @@ public void bitmapOverlay_matchesGoldenFile() throws Exception { Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH); BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(overlayBitmap); videoFrameProcessorTestRunner = - getDefaultFrameProcessorTestRunnerBuilder(testId) + getSurfaceInputFrameProcessorTestRunnerBuilder(testId) .setEffects(new OverlayEffect(ImmutableList.of(bitmapOverlay))) .build(); Bitmap expectedBitmap = readBitmap(BITMAP_OVERLAY_PNG_ASSET_PATH); @@ -203,28 +199,16 @@ public void bitmapOverlay_textureInput_matchesGoldenFile() throws Exception { } Bitmap overlayBitmap = readBitmap(OVERLAY_PNG_ASSET_PATH); BitmapOverlay bitmapOverlay = BitmapOverlay.createStaticBitmapOverlay(overlayBitmap); - TextureBitmapReader producersBitmapReader = new TextureBitmapReader(); + ImmutableList effects = + ImmutableList.of(new OverlayEffect(ImmutableList.of(bitmapOverlay))); TextureBitmapReader consumersBitmapReader = new TextureBitmapReader(); - DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = - new DefaultVideoFrameProcessor.Factory.Builder() - .setTextureOutput( - (outputTexture, presentationTimeUs) -> - inputTextureIntoVideoFrameProcessor( - testId, consumersBitmapReader, outputTexture, presentationTimeUs), - /* textureOutputCapacity= */ 1) - .build(); VideoFrameProcessorTestRunner texIdProducingVideoFrameProcessorTestRunner = - new VideoFrameProcessorTestRunner.Builder() - .setTestId(testId) - .setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactory) - .setVideoAssetPath(INPUT_SDR_MP4_ASSET_STRING) - .setBitmapReader(producersBitmapReader) - .setEffects(new OverlayEffect(ImmutableList.of(bitmapOverlay))) - .build(); - texIdProducingVideoFrameProcessorTestRunner.processFirstFrameAndEnd(); - texIdProducingVideoFrameProcessorTestRunner.release(); + getTexIdProducingFrameProcessorTestRunner( + testId, consumersBitmapReader, INPUT_SDR_MP4_ASSET_STRING, SDR_BT709_LIMITED, effects); Bitmap expectedBitmap = readBitmap(BITMAP_OVERLAY_PNG_ASSET_PATH); + texIdProducingVideoFrameProcessorTestRunner.processFirstFrameAndEnd(); + texIdProducingVideoFrameProcessorTestRunner.release(); Bitmap actualBitmap = consumersBitmapReader.getBitmap(); // TODO(b/207848601): Switch to using proper tooling for testing against golden data. @@ -249,7 +233,7 @@ public void noEffects_hlg10Input_matchesGoldenFile() throws Exception { } ColorInfo colorInfo = checkNotNull(format.colorInfo); videoFrameProcessorTestRunner = - getDefaultFrameProcessorTestRunnerBuilder(testId) + getSurfaceInputFrameProcessorTestRunnerBuilder(testId) .setInputColorInfo(colorInfo) .setOutputColorInfo(colorInfo) .setVideoAssetPath(INPUT_HLG10_MP4_ASSET_STRING) @@ -267,11 +251,9 @@ public void noEffects_hlg10Input_matchesGoldenFile() throws Exception { .isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16); } - // A passthrough effect allows for testing having an intermediate effect injected, which uses - // different OpenGL shaders from having no effects. @Test - public void noOpEffect_hlg10Input_matchesGoldenFile() throws Exception { - String testId = "noOpEffect_hlg10Input_matchesGoldenFile"; + public void noEffects_hlg10TextureInput_matchesGoldenFile() throws Exception { + String testId = "noEffects_hlg10TextureInput_matchesGoldenFile"; Context context = getApplicationContext(); Format format = MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT; if (!deviceSupportsHdrEditing(format)) { @@ -283,14 +265,49 @@ public void noOpEffect_hlg10Input_matchesGoldenFile() throws Exception { return; } ColorInfo colorInfo = checkNotNull(format.colorInfo); + TextureBitmapReader consumersBitmapReader = new TextureBitmapReader(); + VideoFrameProcessorTestRunner texIdProducingVideoFrameProcessorTestRunner = + getTexIdProducingFrameProcessorTestRunner( + testId, + consumersBitmapReader, + INPUT_HLG10_MP4_ASSET_STRING, + colorInfo, + ImmutableList.of()); + Bitmap expectedBitmap = readBitmap(ORIGINAL_HLG10_PNG_ASSET_PATH); + + texIdProducingVideoFrameProcessorTestRunner.processFirstFrameAndEnd(); + texIdProducingVideoFrameProcessorTestRunner.release(); + Bitmap actualBitmap = consumersBitmapReader.getBitmap(); + + // TODO(b/207848601): Switch to using proper tooling for testing against golden data. + float averagePixelAbsoluteDifference = + BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceFp16( + expectedBitmap, actualBitmap); + assertThat(averagePixelAbsoluteDifference) + .isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16); + } + + @Test + public void noEffects_hdr10Input_matchesGoldenFile() throws Exception { + String testId = "noEffects_hdr10Input_matchesGoldenFile"; + Context context = getApplicationContext(); + Format format = MP4_ASSET_720P_4_SECOND_HDR10_FORMAT; + if (!deviceSupportsHdrEditing(format)) { + recordTestSkipped(context, testId, "No PQ editing support"); + return; + } + if (AndroidTestUtil.skipAndLogIfFormatsUnsupported( + context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) { + return; + } + ColorInfo colorInfo = checkNotNull(format.colorInfo); videoFrameProcessorTestRunner = - getDefaultFrameProcessorTestRunnerBuilder(testId) + getSurfaceInputFrameProcessorTestRunnerBuilder(testId) .setInputColorInfo(colorInfo) .setOutputColorInfo(colorInfo) - .setVideoAssetPath(INPUT_HLG10_MP4_ASSET_STRING) - .setEffects(NO_OP_EFFECT) + .setVideoAssetPath(INPUT_PQ_MP4_ASSET_STRING) .build(); - Bitmap expectedBitmap = readBitmap(ORIGINAL_HLG10_PNG_ASSET_PATH); + Bitmap expectedBitmap = readBitmap(ORIGINAL_HDR10_PNG_ASSET_PATH); videoFrameProcessorTestRunner.processFirstFrameAndEnd(); Bitmap actualBitmap = videoFrameProcessorTestRunner.getOutputBitmap(); @@ -304,10 +321,46 @@ public void noOpEffect_hlg10Input_matchesGoldenFile() throws Exception { } @Test - public void noEffects_hdr10Input_matchesGoldenFile() throws Exception { - String testId = "noEffects_hdr10Input_matchesGoldenFile"; + public void noEffects_hdr10TextureInput_matchesGoldenFile() throws Exception { + String testId = "noEffects_hdr10TextureInput_matchesGoldenFile"; Context context = getApplicationContext(); Format format = MP4_ASSET_720P_4_SECOND_HDR10_FORMAT; + if (!deviceSupportsHdrEditing(format)) { + recordTestSkipped(context, testId, "No PQ editing support"); + return; + } + if (AndroidTestUtil.skipAndLogIfFormatsUnsupported( + context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) { + return; + } + ColorInfo colorInfo = checkNotNull(format.colorInfo); + TextureBitmapReader consumersBitmapReader = new TextureBitmapReader(); + VideoFrameProcessorTestRunner texIdProducingVideoFrameProcessorTestRunner = + getTexIdProducingFrameProcessorTestRunner( + testId, + consumersBitmapReader, + INPUT_PQ_MP4_ASSET_STRING, + colorInfo, + ImmutableList.of()); + Bitmap expectedBitmap = readBitmap(ORIGINAL_HDR10_PNG_ASSET_PATH); + + texIdProducingVideoFrameProcessorTestRunner.processFirstFrameAndEnd(); + texIdProducingVideoFrameProcessorTestRunner.release(); + Bitmap actualBitmap = consumersBitmapReader.getBitmap(); + + // TODO(b/207848601): Switch to using proper tooling for testing against golden data. + float averagePixelAbsoluteDifference = + BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceFp16( + expectedBitmap, actualBitmap); + assertThat(averagePixelAbsoluteDifference) + .isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16); + } + + @Test + public void noOpEffect_hlg10Input_matchesGoldenFile() throws Exception { + String testId = "noOpEffect_hlg10Input_matchesGoldenFile"; + Context context = getApplicationContext(); + Format format = MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT; if (!deviceSupportsHdrEditing(format)) { recordTestSkipped(context, testId, "No HLG editing support"); return; @@ -318,12 +371,13 @@ public void noEffects_hdr10Input_matchesGoldenFile() throws Exception { } ColorInfo colorInfo = checkNotNull(format.colorInfo); videoFrameProcessorTestRunner = - getDefaultFrameProcessorTestRunnerBuilder(testId) + getSurfaceInputFrameProcessorTestRunnerBuilder(testId) .setInputColorInfo(colorInfo) .setOutputColorInfo(colorInfo) - .setVideoAssetPath(INPUT_PQ_MP4_ASSET_STRING) + .setVideoAssetPath(INPUT_HLG10_MP4_ASSET_STRING) + .setEffects(NO_OP_EFFECT) .build(); - Bitmap expectedBitmap = readBitmap(ORIGINAL_HDR10_PNG_ASSET_PATH); + Bitmap expectedBitmap = readBitmap(ORIGINAL_HLG10_PNG_ASSET_PATH); videoFrameProcessorTestRunner.processFirstFrameAndEnd(); Bitmap actualBitmap = videoFrameProcessorTestRunner.getOutputBitmap(); @@ -336,15 +390,49 @@ public void noEffects_hdr10Input_matchesGoldenFile() throws Exception { .isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16); } - // A passthrough effect allows for testing having an intermediate effect injected, which uses - // different OpenGL shaders from having no effects. + @Test + public void noOpEffect_hlg10TextureInput_matchesGoldenFile() throws Exception { + String testId = "noOpEffect_hlg10TextureInput_matchesGoldenFile"; + Context context = getApplicationContext(); + Format format = MP4_ASSET_1080P_5_SECOND_HLG10_FORMAT; + if (!deviceSupportsHdrEditing(format)) { + recordTestSkipped(context, testId, "No HLG editing support"); + return; + } + if (AndroidTestUtil.skipAndLogIfFormatsUnsupported( + context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) { + return; + } + ColorInfo colorInfo = checkNotNull(format.colorInfo); + TextureBitmapReader consumersBitmapReader = new TextureBitmapReader(); + VideoFrameProcessorTestRunner texIdProducingVideoFrameProcessorTestRunner = + getTexIdProducingFrameProcessorTestRunner( + testId, + consumersBitmapReader, + INPUT_HLG10_MP4_ASSET_STRING, + colorInfo, + ImmutableList.of(NO_OP_EFFECT)); + Bitmap expectedBitmap = readBitmap(ORIGINAL_HLG10_PNG_ASSET_PATH); + + texIdProducingVideoFrameProcessorTestRunner.processFirstFrameAndEnd(); + texIdProducingVideoFrameProcessorTestRunner.release(); + Bitmap actualBitmap = consumersBitmapReader.getBitmap(); + + // TODO(b/207848601): Switch to using proper tooling for testing against golden data. + float averagePixelAbsoluteDifference = + BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceFp16( + expectedBitmap, actualBitmap); + assertThat(averagePixelAbsoluteDifference) + .isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16); + } + @Test public void noOpEffect_hdr10Input_matchesGoldenFile() throws Exception { String testId = "noOpEffect_hdr10Input_matchesGoldenFile"; Context context = getApplicationContext(); Format format = MP4_ASSET_720P_4_SECOND_HDR10_FORMAT; if (!deviceSupportsHdrEditing(format)) { - recordTestSkipped(context, testId, "No HLG editing support"); + recordTestSkipped(context, testId, "No PQ editing support"); return; } if (AndroidTestUtil.skipAndLogIfFormatsUnsupported( @@ -353,7 +441,7 @@ public void noOpEffect_hdr10Input_matchesGoldenFile() throws Exception { } ColorInfo colorInfo = checkNotNull(format.colorInfo); videoFrameProcessorTestRunner = - getDefaultFrameProcessorTestRunnerBuilder(testId) + getSurfaceInputFrameProcessorTestRunnerBuilder(testId) .setInputColorInfo(colorInfo) .setOutputColorInfo(colorInfo) .setVideoAssetPath(INPUT_PQ_MP4_ASSET_STRING) @@ -372,9 +460,78 @@ public void noOpEffect_hdr10Input_matchesGoldenFile() throws Exception { .isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16); } + @Test + public void noOpEffect_hdr10TextureInput_matchesGoldenFile() throws Exception { + String testId = "noOpEffect_hdr10TextureInput_matchesGoldenFile"; + Context context = getApplicationContext(); + Format format = MP4_ASSET_720P_4_SECOND_HDR10_FORMAT; + if (!deviceSupportsHdrEditing(format)) { + recordTestSkipped(context, testId, "No PQ editing support"); + return; + } + if (AndroidTestUtil.skipAndLogIfFormatsUnsupported( + context, testId, /* inputFormat= */ format, /* outputFormat= */ null)) { + return; + } + ColorInfo colorInfo = checkNotNull(format.colorInfo); + TextureBitmapReader consumersBitmapReader = new TextureBitmapReader(); + VideoFrameProcessorTestRunner texIdProducingVideoFrameProcessorTestRunner = + getTexIdProducingFrameProcessorTestRunner( + testId, + consumersBitmapReader, + INPUT_PQ_MP4_ASSET_STRING, + colorInfo, + ImmutableList.of(NO_OP_EFFECT)); + Bitmap expectedBitmap = readBitmap(ORIGINAL_HDR10_PNG_ASSET_PATH); + + texIdProducingVideoFrameProcessorTestRunner.processFirstFrameAndEnd(); + texIdProducingVideoFrameProcessorTestRunner.release(); + Bitmap actualBitmap = consumersBitmapReader.getBitmap(); + + // TODO(b/207848601): Switch to using proper tooling for testing against golden data. + float averagePixelAbsoluteDifference = + BitmapPixelTestUtil.getBitmapAveragePixelAbsoluteDifferenceFp16( + expectedBitmap, actualBitmap); + assertThat(averagePixelAbsoluteDifference) + .isAtMost(MAXIMUM_AVERAGE_PIXEL_ABSOLUTE_DIFFERENCE_DIFFERENT_DEVICE_FP16); + } + + private VideoFrameProcessorTestRunner getTexIdProducingFrameProcessorTestRunner( + String testId, + TextureBitmapReader consumersBitmapReader, + String videoAssetPath, + ColorInfo colorInfo, + List effects) + throws VideoFrameProcessingException { + TextureBitmapReader producersBitmapReader = new TextureBitmapReader(); + DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = + new DefaultVideoFrameProcessor.Factory.Builder() + .setTextureOutput( + (outputTexture, presentationTimeUs) -> + inputTextureIntoVideoFrameProcessor( + testId, + consumersBitmapReader, + colorInfo, + effects, + outputTexture, + presentationTimeUs), + /* textureOutputCapacity= */ 1) + .build(); + return new VideoFrameProcessorTestRunner.Builder() + .setTestId(testId) + .setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactory) + .setVideoAssetPath(videoAssetPath) + .setInputColorInfo(colorInfo) + .setOutputColorInfo(colorInfo) + .setBitmapReader(producersBitmapReader) + .build(); + } + private void inputTextureIntoVideoFrameProcessor( String testId, TextureBitmapReader bitmapReader, + ColorInfo colorInfo, + List effects, GlTextureInfo texture, long presentationTimeUs) throws VideoFrameProcessingException { @@ -389,9 +546,11 @@ private void inputTextureIntoVideoFrameProcessor( new VideoFrameProcessorTestRunner.Builder() .setTestId(testId) .setVideoFrameProcessorFactory(defaultVideoFrameProcessorFactory) - .setVideoAssetPath(INPUT_SDR_MP4_ASSET_STRING) + .setInputColorInfo(colorInfo) + .setOutputColorInfo(colorInfo) .setBitmapReader(bitmapReader) .setInputType(VideoFrameProcessor.INPUT_TYPE_TEXTURE_ID) + .setEffects(effects) .build(); videoFrameProcessorTestRunner.queueInputTexture(texture, presentationTimeUs); @@ -402,7 +561,7 @@ private void inputTextureIntoVideoFrameProcessor( } } - private VideoFrameProcessorTestRunner.Builder getDefaultFrameProcessorTestRunnerBuilder( + private VideoFrameProcessorTestRunner.Builder getSurfaceInputFrameProcessorTestRunnerBuilder( String testId) { TextureBitmapReader textureBitmapReader = new TextureBitmapReader(); DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = From cd9ff2408682755987e955ad22d499cf05c7f084 Mon Sep 17 00:00:00 2001 From: Tofunmi Adigun-Hameed Date: Thu, 18 May 2023 18:04:45 +0000 Subject: [PATCH 040/516] Remove FfmpegVideoRenderer from 1.1.0 release --- .../media3/decoder/ffmpeg/FfmpegLibrary.java | 6 +- .../decoder/ffmpeg/FfmpegVideoRenderer.java | 136 ------------------ .../ffmpeg/DefaultRenderersFactoryTest.java | 9 +- libraries/exoplayer/proguard-rules.txt | 4 - .../exoplayer/DefaultRenderersFactory.java | 25 ---- 5 files changed, 2 insertions(+), 178 deletions(-) delete mode 100644 libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegVideoRenderer.java diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java index bc86776cac1..19155d8b763 100644 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java +++ b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegLibrary.java @@ -50,7 +50,7 @@ private FfmpegLibrary() {} /** * Override the names of the FFmpeg native libraries. If an application wishes to call this * method, it must do so before calling any other method defined by this class, and before - * instantiating a {@link FfmpegAudioRenderer} or {@link FfmpegVideoRenderer} instance. + * instantiating a {@link FfmpegAudioRenderer} instance. * * @param libraries The names of the FFmpeg native libraries. */ @@ -148,10 +148,6 @@ public static boolean supportsFormat(String mimeType) { return "pcm_mulaw"; case MimeTypes.AUDIO_ALAW: return "pcm_alaw"; - case MimeTypes.VIDEO_H264: - return "h264"; - case MimeTypes.VIDEO_H265: - return "hevc"; default: return null; } diff --git a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegVideoRenderer.java b/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegVideoRenderer.java deleted file mode 100644 index 395b21b33f1..00000000000 --- a/libraries/decoder_ffmpeg/src/main/java/androidx/media3/decoder/ffmpeg/FfmpegVideoRenderer.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package androidx.media3.decoder.ffmpeg; - -import static androidx.media3.exoplayer.DecoderReuseEvaluation.DISCARD_REASON_MIME_TYPE_CHANGED; -import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_NO; -import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION; - -import android.os.Handler; -import android.view.Surface; -import androidx.annotation.Nullable; -import androidx.media3.common.C; -import androidx.media3.common.Format; -import androidx.media3.common.util.TraceUtil; -import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; -import androidx.media3.decoder.CryptoConfig; -import androidx.media3.decoder.Decoder; -import androidx.media3.decoder.DecoderInputBuffer; -import androidx.media3.decoder.VideoDecoderOutputBuffer; -import androidx.media3.exoplayer.DecoderReuseEvaluation; -import androidx.media3.exoplayer.RendererCapabilities; -import androidx.media3.exoplayer.video.DecoderVideoRenderer; -import androidx.media3.exoplayer.video.VideoRendererEventListener; - -// TODO: Remove the NOTE below. -/** - * NOTE: This class if under development and is not yet functional. - * - *

    Decodes and renders video using FFmpeg. - */ -@UnstableApi -public final class FfmpegVideoRenderer extends DecoderVideoRenderer { - - private static final String TAG = "FfmpegVideoRenderer"; - - /** - * Creates a new instance. - * - * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer - * can attempt to seamlessly join an ongoing playback. - * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be - * null if delivery of events is not required. - * @param eventListener A listener of events. May be null if delivery of events is not required. - * @param maxDroppedFramesToNotify The maximum number of frames that can be dropped between - * invocations of {@link VideoRendererEventListener#onDroppedFrames(int, long)}. - */ - public FfmpegVideoRenderer( - long allowedJoiningTimeMs, - @Nullable Handler eventHandler, - @Nullable VideoRendererEventListener eventListener, - int maxDroppedFramesToNotify) { - super(allowedJoiningTimeMs, eventHandler, eventListener, maxDroppedFramesToNotify); - // TODO: Implement. - } - - @Override - public String getName() { - return TAG; - } - - @Override - public final @RendererCapabilities.Capabilities int supportsFormat(Format format) { - // TODO: Remove this line and uncomment the implementation below. - return C.FORMAT_UNSUPPORTED_TYPE; - /* - String mimeType = Assertions.checkNotNull(format.sampleMimeType); - if (!FfmpegLibrary.isAvailable() || !MimeTypes.isVideo(mimeType)) { - return FORMAT_UNSUPPORTED_TYPE; - } else if (!FfmpegLibrary.supportsFormat(format.sampleMimeType)) { - return RendererCapabilities.create(FORMAT_UNSUPPORTED_SUBTYPE); - } else if (format.exoMediaCryptoType != null) { - return RendererCapabilities.create(FORMAT_UNSUPPORTED_DRM); - } else { - return RendererCapabilities.create( - FORMAT_HANDLED, - ADAPTIVE_SEAMLESS, - TUNNELING_NOT_SUPPORTED); - } - */ - } - - @SuppressWarnings("nullness:return") - @Override - protected Decoder - createDecoder(Format format, @Nullable CryptoConfig cryptoConfig) - throws FfmpegDecoderException { - TraceUtil.beginSection("createFfmpegVideoDecoder"); - // TODO: Implement, remove the SuppressWarnings annotation, and update the return type to use - // the concrete type of the decoder (probably FfmepgVideoDecoder). - TraceUtil.endSection(); - return null; - } - - @Override - protected void renderOutputBufferToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface) - throws FfmpegDecoderException { - // TODO: Implement. - } - - @Override - protected void setDecoderOutputMode(@C.VideoOutputMode int outputMode) { - // TODO: Uncomment the implementation below. - /* - if (decoder != null) { - decoder.setOutputMode(outputMode); - } - */ - } - - @Override - protected DecoderReuseEvaluation canReuseDecoder( - String decoderName, Format oldFormat, Format newFormat) { - boolean sameMimeType = Util.areEqual(oldFormat.sampleMimeType, newFormat.sampleMimeType); - // TODO: Ability to reuse the decoder may be MIME type dependent. - return new DecoderReuseEvaluation( - decoderName, - oldFormat, - newFormat, - sameMimeType ? REUSE_RESULT_YES_WITHOUT_RECONFIGURATION : REUSE_RESULT_NO, - sameMimeType ? 0 : DISCARD_REASON_MIME_TYPE_CHANGED); - } -} diff --git a/libraries/decoder_ffmpeg/src/test/java/androidx/media3/decoder/ffmpeg/DefaultRenderersFactoryTest.java b/libraries/decoder_ffmpeg/src/test/java/androidx/media3/decoder/ffmpeg/DefaultRenderersFactoryTest.java index a393d7d826f..9b12771285e 100644 --- a/libraries/decoder_ffmpeg/src/test/java/androidx/media3/decoder/ffmpeg/DefaultRenderersFactoryTest.java +++ b/libraries/decoder_ffmpeg/src/test/java/androidx/media3/decoder/ffmpeg/DefaultRenderersFactoryTest.java @@ -22,8 +22,7 @@ import org.junit.runner.RunWith; /** - * Unit test for {@link DefaultRenderersFactoryTest} with {@link FfmpegAudioRenderer} and {@link - * FfmpegVideoRenderer}. + * Unit test for {@link DefaultRenderersFactoryTest} with {@link FfmpegAudioRenderer}. */ @RunWith(AndroidJUnit4.class) public final class DefaultRenderersFactoryTest { @@ -33,10 +32,4 @@ public void createRenderers_instantiatesFfmpegAudioRenderer() { DefaultRenderersFactoryAsserts.assertExtensionRendererCreated( FfmpegAudioRenderer.class, C.TRACK_TYPE_AUDIO); } - - @Test - public void createRenderers_instantiatesFfmpegVideoRenderer() { - DefaultRenderersFactoryAsserts.assertExtensionRendererCreated( - FfmpegVideoRenderer.class, C.TRACK_TYPE_VIDEO); - } } diff --git a/libraries/exoplayer/proguard-rules.txt b/libraries/exoplayer/proguard-rules.txt index 2a4d945bcaf..8a9222e52e6 100644 --- a/libraries/exoplayer/proguard-rules.txt +++ b/libraries/exoplayer/proguard-rules.txt @@ -9,10 +9,6 @@ -keepclassmembers class androidx.media3.decoder.av1.Libgav1VideoRenderer { (long, android.os.Handler, androidx.media3.exoplayer.video.VideoRendererEventListener, int); } --dontnote androidx.media3.decoder.ffmpeg.FfmpegVideoRenderer --keepclassmembers class androidx.media3.decoder.ffmpeg.FfmpegVideoRenderer { - (long, android.os.Handler, androidx.media3.exoplayer.video.VideoRendererEventListener, int); -} -dontnote androidx.media3.decoder.opus.LibopusAudioRenderer -keepclassmembers class androidx.media3.decoder.opus.LibopusAudioRenderer { (android.os.Handler, androidx.media3.exoplayer.audio.AudioRendererEventListener, androidx.media3.exoplayer.audio.AudioSink); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java index f3443e1307d..96d6aa3ce8e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/DefaultRenderersFactory.java @@ -425,31 +425,6 @@ protected void buildVideoRenderers( // The extension is present, but instantiation failed. throw new RuntimeException("Error instantiating AV1 extension", e); } - - try { - // Full class names used for constructor args so the LINT rule triggers if any of them move. - Class clazz = Class.forName("androidx.media3.decoder.ffmpeg.FfmpegVideoRenderer"); - Constructor constructor = - clazz.getConstructor( - long.class, - android.os.Handler.class, - androidx.media3.exoplayer.video.VideoRendererEventListener.class, - int.class); - Renderer renderer = - (Renderer) - constructor.newInstance( - allowedVideoJoiningTimeMs, - eventHandler, - eventListener, - MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY); - out.add(extensionRendererIndex++, renderer); - Log.i(TAG, "Loaded FfmpegVideoRenderer."); - } catch (ClassNotFoundException e) { - // Expected if the app was built without the extension. - } catch (Exception e) { - // The extension is present, but instantiation failed. - throw new RuntimeException("Error instantiating FFmpeg extension", e); - } } /** From dd425d53c9712152935724846f241ec05f706802 Mon Sep 17 00:00:00 2001 From: claincly Date: Thu, 18 May 2023 15:18:35 +0100 Subject: [PATCH 041/516] Always use sRGB/BT.709 for bitmap inputs PiperOrigin-RevId: 533117700 (cherry picked from commit 24a43969908dc7823a497ec193b544ea7783cc7a) --- .../androidx/media3/common/VideoFrameProcessor.java | 4 ++-- .../androidx/media3/effect/DefaultShaderProgram.java | 1 - .../media3/effect/DefaultVideoFrameProcessor.java | 10 ++++------ .../java/androidx/media3/effect/InputSwitcher.java | 9 +++++---- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java index 1ad966e5d00..164483c40b8 100644 --- a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java +++ b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java @@ -85,8 +85,8 @@ interface Factory { * @param effects The {@link Effect} instances to apply to each frame. Applied on the {@code * outputColorInfo}'s color space. * @param debugViewProvider A {@link DebugViewProvider}. - * @param inputColorInfo The {@link ColorInfo} for input frames. - * @param outputColorInfo The {@link ColorInfo} for output frames. + * @param inputColorInfo The {@link ColorInfo} for the input frames. + * @param outputColorInfo The {@link ColorInfo} for the output frames. * @param renderFramesAutomatically If {@code true}, the instance will render output frames to * the {@linkplain #setOutputSurfaceInfo(SurfaceInfo) output surface} automatically as * {@link VideoFrameProcessor} is done processing them. If {@code false}, the {@link diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java index 61a4b8faa8f..90e889ae964 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultShaderProgram.java @@ -377,7 +377,6 @@ private static DefaultShaderProgram createWithSampler( glProgram.setIntUniform("uOutputColorTransfer", outputColorTransfer); } else { glProgram.setIntUniform("uEnableColorTransfer", enableColorTransfers ? GL_TRUE : GL_FALSE); - checkArgument(inputColorInfo.colorSpace == outputColorInfo.colorSpace); checkArgument( outputColorTransfer == C.COLOR_TRANSFER_SDR || outputColorTransfer == C.COLOR_TRANSFER_LINEAR); diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index f29eda1147c..f468bf22baf 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -44,7 +44,6 @@ import androidx.media3.common.SurfaceInfo; import androidx.media3.common.VideoFrameProcessingException; import androidx.media3.common.VideoFrameProcessor; -import androidx.media3.common.VideoFrameProcessor.OnInputFrameProcessedListener; import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; @@ -596,7 +595,6 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( InputSwitcher inputSwitcher = new InputSwitcher( context, - inputColorInfo, /* outputColorInfo= */ linearColorInfo, glObjectsProvider, videoFrameProcessingTaskExecutor, @@ -618,14 +616,14 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( textureOutputListener, textureOutputCapacity); - inputSwitcher.registerInput(INPUT_TYPE_SURFACE); + inputSwitcher.registerInput(inputColorInfo, INPUT_TYPE_SURFACE); if (!ColorInfo.isTransferHdr(inputColorInfo)) { - // HDR bitmap input is not supported. - inputSwitcher.registerInput(INPUT_TYPE_BITMAP); + // HDR bitmap input is not supported. Bitmaps are always sRGB/Full range/BT.709. + inputSwitcher.registerInput(ColorInfo.SRGB_BT709_FULL, INPUT_TYPE_BITMAP); } if (inputColorInfo.colorTransfer != C.COLOR_TRANSFER_SRGB) { // Image and textureId concatenation not supported. - inputSwitcher.registerInput(INPUT_TYPE_TEXTURE_ID); + inputSwitcher.registerInput(inputColorInfo, INPUT_TYPE_TEXTURE_ID); } inputSwitcher.setDownstreamShaderProgram(effectsShaderPrograms.get(0)); diff --git a/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java b/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java index 20ffa92c372..4b3d33aba3a 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java @@ -39,7 +39,6 @@ */ /* package */ final class InputSwitcher { private final Context context; - private final ColorInfo inputColorInfo; private final ColorInfo outputColorInfo; private final GlObjectsProvider glObjectsProvider; private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor; @@ -52,13 +51,11 @@ public InputSwitcher( Context context, - ColorInfo inputColorInfo, ColorInfo outputColorInfo, GlObjectsProvider glObjectsProvider, VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor, boolean enableColorTransfers) { this.context = context; - this.inputColorInfo = inputColorInfo; this.outputColorInfo = outputColorInfo; this.glObjectsProvider = glObjectsProvider; this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor; @@ -78,8 +75,12 @@ public InputSwitcher( * *

    Creates an {@link TextureManager} and an appropriate {@linkplain DefaultShaderProgram * sampler} to sample from the input. + * + * @param inputColorInfo The {@link ColorInfo} for the input frames. + * @param inputType The {@linkplain VideoFrameProcessor.InputType type} of the input being + * registered. */ - public void registerInput(@VideoFrameProcessor.InputType int inputType) + public void registerInput(ColorInfo inputColorInfo, @VideoFrameProcessor.InputType int inputType) throws VideoFrameProcessingException { // TODO(b/274109008): Investigate lazy instantiating the texture managers. DefaultShaderProgram samplingShaderProgram; From 400218c018e446f28b02bccf3d24b1c0188e44bc Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Thu, 18 May 2023 15:33:49 +0100 Subject: [PATCH 042/516] Add argument validation in Mp4LocationData PiperOrigin-RevId: 533121451 (cherry picked from commit 209783bdf24e946356c4cfd48a76a195762bbe71) --- .../media3/container/Mp4LocationData.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/libraries/container/src/main/java/androidx/media3/container/Mp4LocationData.java b/libraries/container/src/main/java/androidx/media3/container/Mp4LocationData.java index f645dc2eca3..5697b9a932b 100644 --- a/libraries/container/src/main/java/androidx/media3/container/Mp4LocationData.java +++ b/libraries/container/src/main/java/androidx/media3/container/Mp4LocationData.java @@ -15,8 +15,11 @@ */ package androidx.media3.container; +import static androidx.media3.common.util.Assertions.checkArgument; + import android.os.Parcel; import android.os.Parcelable; +import androidx.annotation.FloatRange; import androidx.annotation.Nullable; import androidx.media3.common.Metadata; import androidx.media3.common.util.UnstableApi; @@ -28,8 +31,18 @@ public final class Mp4LocationData implements Metadata.Entry { public final float latitude; public final float longitude; - /** Creates an instance. */ - public Mp4LocationData(float latitude, float longitude) { + /** + * Creates an instance. + * + * @param latitude The latitude, in degrees. Its value must be in the range [-90, 90]. + * @param longitude The longitude, in degrees. Its value must be in the range [-180, 180]. + */ + public Mp4LocationData( + @FloatRange(from = -90.0, to = 90.0) float latitude, + @FloatRange(from = -180.0, to = 180.0) float longitude) { + checkArgument( + latitude >= -90.0f && latitude <= 90.0f && longitude >= -180.0f && longitude <= 180.0f, + "Invalid latitude or longitude"); this.latitude = latitude; this.longitude = longitude; } From 9c1a80082fbd83bea510256e24d83dc61eaa9090 Mon Sep 17 00:00:00 2001 From: jbibik Date: Thu, 18 May 2023 16:06:43 +0100 Subject: [PATCH 043/516] Use TestUtil.getPublicMethods instead of getDeclaredMethods JaCoCo introduces private synthetic methods (even on interfaces) which have to be skipped when checking that a 'forwarding' implementation does forward everything. Instead we can use the existing `getPublicMethods()` method which implicitly skips these (since they're private). PiperOrigin-RevId: 533130932 (cherry picked from commit 620b9e15403b5138e7b3f44194b10e5ae7f72a8c) --- .../test/java/androidx/media3/common/SimpleBasePlayerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/common/src/test/java/androidx/media3/common/SimpleBasePlayerTest.java b/libraries/common/src/test/java/androidx/media3/common/SimpleBasePlayerTest.java index 7bf3a16dc8e..5a108550d58 100644 --- a/libraries/common/src/test/java/androidx/media3/common/SimpleBasePlayerTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/SimpleBasePlayerTest.java @@ -65,7 +65,7 @@ public class SimpleBasePlayerTest { @Test public void allPlayerInterfaceMethods_declaredFinal() throws Exception { - for (Method method : Player.class.getDeclaredMethods()) { + for (Method method : TestUtil.getPublicMethods(Player.class)) { assertThat( SimpleBasePlayer.class .getMethod(method.getName(), method.getParameterTypes()) From 2b53139c5f86162cd2d7cab99ce68560d3c5691a Mon Sep 17 00:00:00 2001 From: ibaker Date: Thu, 18 May 2023 17:53:37 +0100 Subject: [PATCH 044/516] Allow `mock(Random.class)` to work with Java 17 https://stackoverflow.com/questions/70993863/mockito-can-not-mock-random-in-java-17 #minor-release PiperOrigin-RevId: 533161221 (cherry picked from commit 04106da932fd9fb64ae4792f5cffce04f78c7a89) --- .../media3/exoplayer/dash/BaseUrlExclusionListTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/BaseUrlExclusionListTest.java b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/BaseUrlExclusionListTest.java index 845cdedeea9..4e98d8b6cad 100644 --- a/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/BaseUrlExclusionListTest.java +++ b/libraries/exoplayer_dash/src/test/java/androidx/media3/exoplayer/dash/BaseUrlExclusionListTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import androidx.media3.exoplayer.dash.manifest.BaseUrl; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -61,7 +62,7 @@ public void selectBaseUrl_excludeByServiceLocation_excludesAllBaseUrlOfSameServi @Test public void selectBaseUrl_excludeByPriority_excludesAllBaseUrlsOfSamePriority() { - Random mockRandom = mock(Random.class); + Random mockRandom = mock(Random.class, withSettings().withoutAnnotations()); when(mockRandom.nextInt(anyInt())).thenReturn(0); BaseUrlExclusionList baseUrlExclusionList = new BaseUrlExclusionList(mockRandom); List baseUrls = @@ -89,7 +90,7 @@ public void selectBaseUrl_samePriority_choiceIsRandom() { /* url= */ "a", /* serviceLocation= */ "a", /* priority= */ 1, /* weight= */ 99), new BaseUrl( /* url= */ "b", /* serviceLocation= */ "b", /* priority= */ 1, /* weight= */ 1)); - Random mockRandom = mock(Random.class); + Random mockRandom = mock(Random.class, withSettings().withoutAnnotations()); when(mockRandom.nextInt(anyInt())).thenReturn(99); assertThat(new BaseUrlExclusionList(mockRandom).selectBaseUrl(baseUrls)) @@ -114,7 +115,7 @@ public void selectBaseUrl_samePriority_choiceFromSameElementsRandomOnlyOnceSameA /* url= */ "a/a", /* serviceLocation= */ "a", /* priority= */ 1, /* weight= */ 99), new BaseUrl( /* url= */ "b/a", /* serviceLocation= */ "b", /* priority= */ 1, /* weight= */ 1)); - Random mockRandom = mock(Random.class); + Random mockRandom = mock(Random.class, withSettings().withoutAnnotations()); BaseUrlExclusionList baseUrlExclusionList = new BaseUrlExclusionList(mockRandom); when(mockRandom.nextInt(anyInt())).thenReturn(99); From 3ac01d43dd1c44de90ee7ec7ba8565e615b5b3b6 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Thu, 18 May 2023 20:03:27 +0100 Subject: [PATCH 045/516] Effect: Use callback to release texture This allows us to avoid needing a reference to the VideoFrameProcessor, which can be especially difficult if an App only has a reference to the VideoFrameProcessor.Factory it passes into Transformer/ExoPlayer. PiperOrigin-RevId: 533205983 (cherry picked from commit 25fa2df2dedf45e8a060ee28324460429b9bb279) --- .../media3/common/VideoFrameProcessor.java | 14 +------ .../effect/DefaultVideoFrameProcessor.java | 42 ++++++++++--------- .../effect/FinalShaderProgramWrapper.java | 30 ++++++++----- .../androidx/media3/effect/TexturePool.java | 6 +++ .../utils/VideoFrameProcessorTestRunner.java | 22 ++-------- ...oFrameProcessorTextureOutputPixelTest.java | 27 ++++++------ 6 files changed, 63 insertions(+), 78 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java index 164483c40b8..53adf76bafc 100644 --- a/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java +++ b/libraries/common/src/main/java/androidx/media3/common/VideoFrameProcessor.java @@ -49,7 +49,7 @@ public interface VideoFrameProcessor { /** A listener for frame processing events. */ @UnstableApi - public interface OnInputFrameProcessedListener { + interface OnInputFrameProcessedListener { /** Called when the given input frame has been processed. */ void onInputFrameProcessed(int textureId) throws VideoFrameProcessingException; @@ -291,18 +291,6 @@ interface Listener { */ void renderOutputFrame(long renderTimeNs); - /** - * Releases resources associated with all output frames with presentation time less than or equal - * to {@code presentationTimeUs}. - * - *

    Not needed for outputting to an {@linkplain #setOutputSurfaceInfo output surface}, but may - * be required for other outputs. - * - * @param presentationTimeUs The presentation time where all frames before and at this time should - * be released, in microseconds. - */ - void releaseOutputFrame(long presentationTimeUs); - /** * Informs the {@code VideoFrameProcessor} that no further input frames should be accepted. * diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index f468bf22baf..58f9146f172 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -71,11 +71,24 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor { /** Listener interface for texture output. */ @VisibleForTesting(otherwise = PACKAGE_PRIVATE) public interface TextureOutputListener { - /** Called when a texture has been rendered to. */ - void onTextureRendered(GlTextureInfo outputTexture, long presentationTimeUs) + /** + * Called when a texture has been rendered to. {@code releaseOutputTextureCallback} must be + * called to release the {@link GlTextureInfo}. + */ + void onTextureRendered( + GlTextureInfo outputTexture, + long presentationTimeUs, + ReleaseOutputTextureCallback releaseOutputTextureCallback) throws VideoFrameProcessingException; } + /** + * Releases the output information stored for textures before and at {@code presentationTimeUs}. + */ + public interface ReleaseOutputTextureCallback { + void release(long presentationTimeUs); + } + /** A factory for {@link DefaultVideoFrameProcessor} instances. */ public static final class Factory implements VideoFrameProcessor.Factory { @@ -118,9 +131,10 @@ public Builder setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { * Sets texture output settings. * *

    If set, the {@link VideoFrameProcessor} will output to OpenGL textures, accessible via - * {@link TextureOutputListener#onTextureRendered}. Textures will stop being output when - * {@code textureOutputCapacity} is reached, until they're released via {@link - * #releaseOutputFrame}. Output textures must be released using {@link #releaseOutputFrame}. + * {@link TextureOutputListener#onTextureRendered}. Textures will stop being outputted when + * the number of output textures available reaches the {@code textureOutputCapacity}. To + * regain capacity, output textures must be released using {@link + * ReleaseOutputTextureCallback}. * *

    If not set, there will be no texture output. * @@ -455,21 +469,6 @@ public void renderOutputFrame(long renderTimeNs) { () -> finalShaderProgramWrapper.renderOutputFrame(renderTimeNs)); } - /** - * {@inheritDoc} - * - *

    If a {@link TextureOutputListener} {@linkplain Factory.Builder#setTextureOutput is set}, - * this must be called to release the output information stored in the {@link GlTextureInfo} - * instances. - */ - @Override - public void releaseOutputFrame(long presentationTimeUs) { - // TODO(b/262694346): Add Compositor system tests exercising this code path after GL texture - // input is possible. - videoFrameProcessingTaskExecutor.submit( - () -> finalShaderProgramWrapper.releaseOutputFrame(presentationTimeUs)); - } - @Override public void signalEndOfInput() { DebugTraceUtil.recordVideoFrameProcessorReceiveDecoderEos(); @@ -610,6 +609,7 @@ private static DefaultVideoFrameProcessor createOpenGlObjectsAndFrameProcessor( outputColorInfo, enableColorTransfers, renderFramesAutomatically, + videoFrameProcessingTaskExecutor, executor, listener, glObjectsProvider, @@ -661,6 +661,7 @@ private static ImmutableList getGlShaderProgramsForGlEffects( ColorInfo outputColorInfo, boolean enableColorTransfers, boolean renderFramesAutomatically, + VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor, Executor executor, Listener listener, GlObjectsProvider glObjectsProvider, @@ -714,6 +715,7 @@ private static ImmutableList getGlShaderProgramsForGlEffects( outputColorInfo, enableColorTransfers, renderFramesAutomatically, + videoFrameProcessingTaskExecutor, executor, listener, glObjectsProvider, diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java index d78696cb996..4805fb10174 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java @@ -85,6 +85,7 @@ interface OnInputStreamProcessedListener { private final ColorInfo outputColorInfo; private final boolean enableColorTransfers; private final boolean renderFramesAutomatically; + private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor; private final Executor videoFrameProcessorListenerExecutor; private final VideoFrameProcessor.Listener videoFrameProcessorListener; private final Queue> availableFrames; @@ -126,6 +127,7 @@ public FinalShaderProgramWrapper( ColorInfo outputColorInfo, boolean enableColorTransfers, boolean renderFramesAutomatically, + VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor, Executor videoFrameProcessorListenerExecutor, VideoFrameProcessor.Listener videoFrameProcessorListener, GlObjectsProvider glObjectsProvider, @@ -140,6 +142,7 @@ public FinalShaderProgramWrapper( this.outputColorInfo = outputColorInfo; this.enableColorTransfers = enableColorTransfers; this.renderFramesAutomatically = renderFramesAutomatically; + this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor; this.videoFrameProcessorListenerExecutor = videoFrameProcessorListenerExecutor; this.videoFrameProcessorListener = videoFrameProcessorListener; this.glObjectsProvider = glObjectsProvider; @@ -224,6 +227,10 @@ public void releaseOutputFrame(GlTextureInfo outputTexture) { } public void releaseOutputFrame(long presentationTimeUs) { + videoFrameProcessingTaskExecutor.submit(() -> releaseOutputFrameInternal(presentationTimeUs)); + } + + private void releaseOutputFrameInternal(long presentationTimeUs) { while (outputTexturePool.freeTextureCount() < outputTexturePool.capacity() && checkNotNull(outputTextureTimestamps.peek()) <= presentationTimeUs) { outputTexturePool.freeTexture(); @@ -232,16 +239,6 @@ && checkNotNull(outputTextureTimestamps.peek()) <= presentationTimeUs) { } } - public void renderOutputFrame(long renderTimeNs) { - frameProcessingStarted = true; - checkState(!renderFramesAutomatically); - Pair oldestAvailableFrame = availableFrames.remove(); - renderFrame( - /* inputTexture= */ oldestAvailableFrame.first, - /* presentationTimeUs= */ oldestAvailableFrame.second, - renderTimeNs); - } - @Override public void flush() { frameProcessingStarted = true; @@ -267,6 +264,16 @@ public synchronized void release() throws VideoFrameProcessingException { } } + public void renderOutputFrame(long renderTimeNs) { + frameProcessingStarted = true; + checkState(!renderFramesAutomatically); + Pair oldestAvailableFrame = availableFrames.remove(); + renderFrame( + /* inputTexture= */ oldestAvailableFrame.first, + /* presentationTimeUs= */ oldestAvailableFrame.second, + renderTimeNs); + } + /** * Sets the output {@link SurfaceInfo}. * @@ -369,7 +376,8 @@ private void renderFrameToOutputTexture(GlTextureInfo inputTexture, long present // glFinish. Consider removing glFinish and requiring onTextureRendered to handle // synchronization. GLES20.glFinish(); - checkNotNull(textureOutputListener).onTextureRendered(outputTexture, presentationTimeUs); + checkNotNull(textureOutputListener) + .onTextureRendered(outputTexture, presentationTimeUs, this::releaseOutputFrame); } /** diff --git a/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java b/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java index 88cb54a2ed2..e86e6fdb8d7 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java @@ -109,6 +109,8 @@ public GlTextureInfo useTexture() { *

    Throws {@link IllegalStateException} if {@code textureInfo} isn't in use. */ public void freeTexture(GlTextureInfo textureInfo) { + // TODO(b/262694346): Check before adding to freeTexture, that this texture wasn't released + // already. checkState(inUseTextures.contains(textureInfo)); inUseTextures.remove(textureInfo); freeTextures.add(textureInfo); @@ -120,6 +122,8 @@ public void freeTexture(GlTextureInfo textureInfo) { *

    Throws {@link IllegalStateException} if there's no textures in use to free. */ public void freeTexture() { + // TODO(b/262694346): Check before adding to freeTexture, that this texture wasn't released + // already. checkState(!inUseTextures.isEmpty()); GlTextureInfo texture = inUseTextures.remove(); freeTextures.add(texture); @@ -127,6 +131,8 @@ public void freeTexture() { /** Free all in-use textures. */ public void freeAllTextures() { + // TODO(b/262694346): Check before adding to freeTexture, that this texture wasn't released + // already. freeTextures.addAll(inUseTextures); inUseTextures.clear(); } diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java index f6b74328e9d..17d1dce682e 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java @@ -288,11 +288,7 @@ public void onOutputSizeChanged(int width, int height) { boolean useHighPrecisionColorComponents = ColorInfo.isTransferHdr(outputColorInfo); @Nullable Surface outputSurface = - bitmapReader.getSurface( - width, - height, - useHighPrecisionColorComponents, - checkNotNull(videoFrameProcessor)::releaseOutputFrame); + bitmapReader.getSurface(width, height, useHighPrecisionColorComponents); if (outputSurface != null) { checkNotNull(videoFrameProcessor) .setOutputSurfaceInfo(new SurfaceInfo(outputSurface, width, height)); @@ -407,18 +403,10 @@ public interface OnOutputFrameAvailableForRenderingListener { /** Reads a {@link Bitmap} from {@link VideoFrameProcessor} output. */ public interface BitmapReader { - /** Wraps a callback for {@link VideoFrameProcessor#releaseOutputFrame}. */ - interface ReleaseOutputFrameListener { - void releaseOutputFrame(long releaseTimeUs); - } /** Returns the {@link VideoFrameProcessor} output {@link Surface}, if one is needed. */ @Nullable - Surface getSurface( - int width, - int height, - boolean useHighPrecisionColorComponents, - ReleaseOutputFrameListener listener); + Surface getSurface(int width, int height, boolean useHighPrecisionColorComponents); /** Returns the output {@link Bitmap}. */ Bitmap getBitmap(); @@ -438,11 +426,7 @@ public static final class SurfaceBitmapReader @Override @SuppressLint("WrongConstant") @Nullable - public Surface getSurface( - int width, - int height, - boolean useHighPrecisionColorComponents, - ReleaseOutputFrameListener listener) { + public Surface getSurface(int width, int height, boolean useHighPrecisionColorComponents) { imageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, /* maxImages= */ 1); return imageReader.getSurface(); diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java index 63064b47463..3aec9e33eb1 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java @@ -507,14 +507,15 @@ private VideoFrameProcessorTestRunner getTexIdProducingFrameProcessorTestRunner( DefaultVideoFrameProcessor.Factory defaultVideoFrameProcessorFactory = new DefaultVideoFrameProcessor.Factory.Builder() .setTextureOutput( - (outputTexture, presentationTimeUs) -> + (outputTexture, presentationTimeUs, releaseOutputTextureCallback) -> inputTextureIntoVideoFrameProcessor( testId, consumersBitmapReader, colorInfo, effects, outputTexture, - presentationTimeUs), + presentationTimeUs, + releaseOutputTextureCallback), /* textureOutputCapacity= */ 1) .build(); return new VideoFrameProcessorTestRunner.Builder() @@ -533,7 +534,8 @@ private void inputTextureIntoVideoFrameProcessor( ColorInfo colorInfo, List effects, GlTextureInfo texture, - long presentationTimeUs) + long presentationTimeUs, + DefaultVideoFrameProcessor.ReleaseOutputTextureCallback releaseOutputTextureCallback) throws VideoFrameProcessingException { GlObjectsProvider contextSharingGlObjectsProvider = new DefaultGlObjectsProvider(GlUtil.getCurrentContext()); @@ -559,6 +561,7 @@ private void inputTextureIntoVideoFrameProcessor( } catch (InterruptedException e) { throw new VideoFrameProcessingException(e); } + releaseOutputTextureCallback.release(presentationTimeUs); } private VideoFrameProcessorTestRunner.Builder getSurfaceInputFrameProcessorTestRunnerBuilder( @@ -584,19 +587,12 @@ private VideoFrameProcessorTestRunner.Builder getSurfaceInputFrameProcessorTestR private static final class TextureBitmapReader implements BitmapReader { // TODO(b/239172735): This outputs an incorrect black output image on emulators. private boolean useHighPrecisionColorComponents; - private @MonotonicNonNull ReleaseOutputFrameListener releaseOutputFrameListener; - private @MonotonicNonNull Bitmap outputBitmap; @Nullable @Override - public Surface getSurface( - int width, - int height, - boolean useHighPrecisionColorComponents, - ReleaseOutputFrameListener releaseOutputFrameListener) { + public Surface getSurface(int width, int height, boolean useHighPrecisionColorComponents) { this.useHighPrecisionColorComponents = useHighPrecisionColorComponents; - this.releaseOutputFrameListener = releaseOutputFrameListener; return null; } @@ -605,7 +601,10 @@ public Bitmap getBitmap() { return checkStateNotNull(outputBitmap); } - public void readBitmapFromTexture(GlTextureInfo outputTexture, long presentationTimeUs) + public void readBitmapFromTexture( + GlTextureInfo outputTexture, + long presentationTimeUs, + DefaultVideoFrameProcessor.ReleaseOutputTextureCallback releaseOutputTextureCallback) throws VideoFrameProcessingException { try { GlUtil.focusFramebufferUsingCurrentContext( @@ -613,12 +612,10 @@ public void readBitmapFromTexture(GlTextureInfo outputTexture, long presentation outputBitmap = createBitmapFromCurrentGlFrameBuffer( outputTexture.width, outputTexture.height, useHighPrecisionColorComponents); - GlUtil.deleteTexture(outputTexture.texId); - GlUtil.deleteFbo(outputTexture.fboId); } catch (GlUtil.GlException e) { throw new VideoFrameProcessingException(e); } - checkNotNull(releaseOutputFrameListener).releaseOutputFrame(presentationTimeUs); + releaseOutputTextureCallback.release(presentationTimeUs); } private static Bitmap createBitmapFromCurrentGlFrameBuffer( From 5e98c938c05f02760caec6338b1b966a9278d5e9 Mon Sep 17 00:00:00 2001 From: tonihei Date: Fri, 19 May 2023 10:14:13 +0100 Subject: [PATCH 046/516] Add FilteringMediaSource that only provides tracks of given types This is useful for cases where only certain types (e.g. only video) from a source are needed and other tracks should be filtered out completely to avoid later track selection issues. #minor-release PiperOrigin-RevId: 533394658 (cherry picked from commit c44b3828caed79351955b761a84db804da8691c5) --- RELEASENOTES.md | 3 + .../source/FilteringMediaSource.java | 192 ++++++++++++++++++ .../source/FilteringMediaSourceTest.java | 84 ++++++++ 3 files changed, 279 insertions(+) create mode 100644 libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/FilteringMediaSource.java create mode 100644 libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/FilteringMediaSourceTest.java diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c3c3677ae3e..565207b37a7 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -10,6 +10,9 @@ * Enable multi-period live DASH streams for DAI. Please note that the current implementation does not yet support seeking in live streams ([#10912](https://github.com/google/ExoPlayer/issues/10912)). +* ExoPlayer: + * Add `FilteringMediaSource` that allows to filter available track types + from a `MediaSource`. * Session: * Add `androidx.media3.session.MediaButtonReceiver` to enable apps to implement playback resumption with media button events sent by, for diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/FilteringMediaSource.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/FilteringMediaSource.java new file mode 100644 index 00000000000..2e7b8b185ee --- /dev/null +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/FilteringMediaSource.java @@ -0,0 +1,192 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.source; + +import static androidx.media3.common.util.Assertions.checkNotNull; + +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.StreamKey; +import androidx.media3.common.TrackGroup; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.exoplayer.SeekParameters; +import androidx.media3.exoplayer.trackselection.ExoTrackSelection; +import androidx.media3.exoplayer.upstream.Allocator; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import java.io.IOException; +import java.util.List; +import java.util.Set; +import org.checkerframework.checker.nullness.compatqual.NullableType; + +/** + * A {@link MediaSource} that filters the available {@linkplain C.TrackType track types}. + * + *

    Media sources loading muxed media, e.g. progressive streams with muxed video and audio, are + * still likely to parse all of these streams even if the tracks are not made available to the + * player. + */ +@UnstableApi +public class FilteringMediaSource extends WrappingMediaSource { + + private final ImmutableSet<@C.TrackType Integer> trackTypes; + + /** + * Creates a filtering {@link MediaSource} that only publishes tracks of one type. + * + * @param mediaSource The wrapped {@link MediaSource}. + * @param trackType The only {@link C.TrackType} to provide from this source. + */ + public FilteringMediaSource(MediaSource mediaSource, @C.TrackType int trackType) { + this(mediaSource, ImmutableSet.of(trackType)); + } + + /** + * Creates a filtering {@link MediaSource} that only publishes tracks of the given types. + * + * @param mediaSource The wrapped {@link MediaSource}. + * @param trackTypes The {@linkplain C.TrackType track types} to provide from this source. + */ + public FilteringMediaSource(MediaSource mediaSource, Set<@C.TrackType Integer> trackTypes) { + super(mediaSource); + this.trackTypes = ImmutableSet.copyOf(trackTypes); + } + + @Override + public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) { + MediaPeriod wrappedPeriod = super.createPeriod(id, allocator, startPositionUs); + return new FilteringMediaPeriod(wrappedPeriod, trackTypes); + } + + @Override + public void releasePeriod(MediaPeriod mediaPeriod) { + MediaPeriod wrappedPeriod = ((FilteringMediaPeriod) mediaPeriod).mediaPeriod; + super.releasePeriod(wrappedPeriod); + } + + private static final class FilteringMediaPeriod implements MediaPeriod, MediaPeriod.Callback { + + public final MediaPeriod mediaPeriod; + + private final ImmutableSet<@C.TrackType Integer> trackTypes; + + @Nullable private Callback callback; + @Nullable private TrackGroupArray filteredTrackGroups; + + public FilteringMediaPeriod( + MediaPeriod mediaPeriod, ImmutableSet<@C.TrackType Integer> trackTypes) { + this.mediaPeriod = mediaPeriod; + this.trackTypes = trackTypes; + } + + @Override + public void prepare(Callback callback, long positionUs) { + this.callback = callback; + mediaPeriod.prepare(/* callback= */ this, positionUs); + } + + @Override + public void maybeThrowPrepareError() throws IOException { + mediaPeriod.maybeThrowPrepareError(); + } + + @Override + public TrackGroupArray getTrackGroups() { + return checkNotNull(filteredTrackGroups); + } + + @Override + public List getStreamKeys(List trackSelections) { + return mediaPeriod.getStreamKeys(trackSelections); + } + + @Override + public long selectTracks( + @NullableType ExoTrackSelection[] selections, + boolean[] mayRetainStreamFlags, + @NullableType SampleStream[] streams, + boolean[] streamResetFlags, + long positionUs) { + return mediaPeriod.selectTracks( + selections, mayRetainStreamFlags, streams, streamResetFlags, positionUs); + } + + @Override + public void discardBuffer(long positionUs, boolean toKeyframe) { + mediaPeriod.discardBuffer(positionUs, toKeyframe); + } + + @Override + public long readDiscontinuity() { + return mediaPeriod.readDiscontinuity(); + } + + @Override + public long seekToUs(long positionUs) { + return mediaPeriod.seekToUs(positionUs); + } + + @Override + public long getAdjustedSeekPositionUs(long positionUs, SeekParameters seekParameters) { + return mediaPeriod.getAdjustedSeekPositionUs(positionUs, seekParameters); + } + + @Override + public long getBufferedPositionUs() { + return mediaPeriod.getBufferedPositionUs(); + } + + @Override + public long getNextLoadPositionUs() { + return mediaPeriod.getNextLoadPositionUs(); + } + + @Override + public boolean continueLoading(long positionUs) { + return mediaPeriod.continueLoading(positionUs); + } + + @Override + public boolean isLoading() { + return mediaPeriod.isLoading(); + } + + @Override + public void reevaluateBuffer(long positionUs) { + mediaPeriod.reevaluateBuffer(positionUs); + } + + @Override + public void onPrepared(MediaPeriod mediaPeriod) { + TrackGroupArray trackGroups = mediaPeriod.getTrackGroups(); + ImmutableList.Builder trackGroupsBuilder = ImmutableList.builder(); + for (int i = 0; i < trackGroups.length; i++) { + TrackGroup trackGroup = trackGroups.get(i); + if (trackTypes.contains(trackGroup.type)) { + trackGroupsBuilder.add(trackGroup); + } + } + filteredTrackGroups = + new TrackGroupArray(trackGroupsBuilder.build().toArray(new TrackGroup[0])); + checkNotNull(callback).onPrepared(/* mediaPeriod= */ this); + } + + @Override + public void onContinueLoadingRequested(MediaPeriod source) { + checkNotNull(callback).onContinueLoadingRequested(/* source= */ this); + } + } +} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/FilteringMediaSourceTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/FilteringMediaSourceTest.java new file mode 100644 index 00000000000..8c054ac125b --- /dev/null +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/FilteringMediaSourceTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.source; + +import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.runUntilPlaybackState; +import static com.google.common.truth.Truth.assertThat; + +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; +import androidx.media3.common.Player; +import androidx.media3.common.Timeline; +import androidx.media3.common.Tracks; +import androidx.media3.exoplayer.ExoPlayer; +import androidx.media3.test.utils.FakeMediaSource; +import androidx.media3.test.utils.FakeRenderer; +import androidx.media3.test.utils.FakeTimeline; +import androidx.media3.test.utils.TestExoPlayerBuilder; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableSet; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link FilteringMediaSource}. */ +@RunWith(AndroidJUnit4.class) +public class FilteringMediaSourceTest { + + @Test + public void playbackWithFilteredMediaSource_onlyPublishesAndPlaysAllowedTypes() throws Exception { + Timeline timeline = new FakeTimeline(); + FakeMediaSource videoSource = + new FakeMediaSource( + timeline, new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_H264).build()); + FakeMediaSource audioSource = + new FakeMediaSource( + timeline, new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).build()); + FakeMediaSource textSource = + new FakeMediaSource( + timeline, + new Format.Builder() + .setSampleMimeType(MimeTypes.TEXT_VTT) + .setSelectionFlags(C.SELECTION_FLAG_FORCED) + .build()); + FakeRenderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO); + FakeRenderer audioRenderer = new FakeRenderer(C.TRACK_TYPE_AUDIO); + FakeRenderer textRenderer = new FakeRenderer(C.TRACK_TYPE_TEXT); + ExoPlayer player = + new TestExoPlayerBuilder(ApplicationProvider.getApplicationContext()) + .setRenderers(videoRenderer, audioRenderer, textRenderer) + .build(); + FilteringMediaSource mediaSourceWithVideoAndTextOnly = + new FilteringMediaSource( + new MergingMediaSource(textSource, audioSource, videoSource), + ImmutableSet.of(C.TRACK_TYPE_VIDEO, C.TRACK_TYPE_TEXT)); + player.setMediaSource(mediaSourceWithVideoAndTextOnly); + + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + Tracks tracks = player.getCurrentTracks(); + player.play(); + runUntilPlaybackState(player, Player.STATE_ENDED); + player.release(); + + assertThat(tracks.getGroups()).hasSize(2); + assertThat(tracks.containsType(C.TRACK_TYPE_AUDIO)).isFalse(); + assertThat(videoRenderer.enabledCount).isEqualTo(1); + assertThat(textRenderer.enabledCount).isEqualTo(1); + assertThat(audioRenderer.enabledCount).isEqualTo(0); + } +} From 1f8caa508fcd5e14beb44683abe1ebaca62b1cdf Mon Sep 17 00:00:00 2001 From: claincly Date: Fri, 19 May 2023 10:55:07 +0100 Subject: [PATCH 047/516] Add a getter method for texture manager PiperOrigin-RevId: 533402277 (cherry picked from commit d7ad431cfc892a3f1d2fab51f7af7bda19065642) --- .../effect/DefaultVideoFrameProcessor.java | 32 +++++++++---------- .../androidx/media3/effect/InputSwitcher.java | 25 +++++++++++---- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java index 58f9146f172..82056d65dd6 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/DefaultVideoFrameProcessor.java @@ -298,9 +298,6 @@ public DefaultVideoFrameProcessor create( // CountDownLatch to wait for the current input stream to finish processing. private volatile @MonotonicNonNull CountDownLatch latch; - // TODO(b/274109008) Use InputSwither to interact with texture manager. - // Owned and released by inputSwitcher. - private @MonotonicNonNull TextureManager textureManager; private volatile @MonotonicNonNull FrameInfo nextInputFrameInfo; private volatile boolean inputStreamEnded; private volatile boolean hasRefreshedNextInputFrameInfo; @@ -363,7 +360,7 @@ public VideoFrameProcessingTaskExecutor getTaskExecutor() { * @param height The default height for input buffers, in pixels. */ public void setInputDefaultBufferSize(int width, int height) { - checkNotNull(textureManager).setDefaultBufferSize(width, height); + inputSwitcher.activeTextureManager().setDefaultBufferSize(width, height); } @Override @@ -371,7 +368,8 @@ public void queueInputBitmap(Bitmap inputBitmap, long durationUs, float frameRat checkState( hasRefreshedNextInputFrameInfo, "setInputFrameInfo must be called before queueing another bitmap"); - checkNotNull(textureManager) + inputSwitcher + .activeTextureManager() .queueInputBitmap( inputBitmap, durationUs, @@ -383,24 +381,24 @@ public void queueInputBitmap(Bitmap inputBitmap, long durationUs, float frameRat @Override public void queueInputTexture(int textureId, long presentationTimeUs) { - checkNotNull(textureManager).queueInputTexture(textureId, presentationTimeUs); + inputSwitcher.activeTextureManager().queueInputTexture(textureId, presentationTimeUs); } @Override public void setOnInputFrameProcessedListener(OnInputFrameProcessedListener listener) { - checkNotNull(textureManager).setOnInputFrameProcessedListener(listener); + inputSwitcher.activeTextureManager().setOnInputFrameProcessedListener(listener); } @Override public Surface getInputSurface() { - return checkNotNull(textureManager).getInputSurface(); + return inputSwitcher.activeTextureManager().getInputSurface(); } @Override public void registerInputStream(@InputType int inputType) { synchronized (lock) { if (unprocessedInputStreams.isEmpty()) { - textureManager = inputSwitcher.switchToInput(inputType); + inputSwitcher.switchToInput(inputType); unprocessedInputStreams.add(inputType); return; } @@ -408,14 +406,14 @@ public void registerInputStream(@InputType int inputType) { // Wait until the current input stream is processed before continuing to the next input. latch = new CountDownLatch(1); - checkNotNull(textureManager).signalEndOfCurrentInputStream(); + inputSwitcher.activeTextureManager().signalEndOfCurrentInputStream(); try { latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); listenerExecutor.execute(() -> listener.onError(VideoFrameProcessingException.from(e))); } - textureManager = inputSwitcher.switchToInput(inputType); + inputSwitcher.switchToInput(inputType); synchronized (lock) { unprocessedInputStreams.add(inputType); } @@ -424,7 +422,7 @@ public void registerInputStream(@InputType int inputType) { @Override public void setInputFrameInfo(FrameInfo inputFrameInfo) { nextInputFrameInfo = adjustForPixelWidthHeightRatio(inputFrameInfo); - checkNotNull(textureManager).setInputFrameInfo(nextInputFrameInfo); + inputSwitcher.activeTextureManager().setInputFrameInfo(nextInputFrameInfo); hasRefreshedNextInputFrameInfo = true; } @@ -434,13 +432,13 @@ public void registerInputFrame() { checkStateNotNull( nextInputFrameInfo, "setInputFrameInfo must be called before registering input frames"); - checkNotNull(textureManager).registerInputFrame(nextInputFrameInfo); + inputSwitcher.activeTextureManager().registerInputFrame(nextInputFrameInfo); hasRefreshedNextInputFrameInfo = false; } @Override public int getPendingInputFrameCount() { - return checkNotNull(textureManager).getPendingFrameCount(); + return inputSwitcher.activeTextureManager().getPendingFrameCount(); } /** @@ -481,7 +479,7 @@ public void signalEndOfInput() { if (allInputStreamsProcessed) { inputSwitcher.signalEndOfInput(); } else { - checkNotNull(textureManager).signalEndOfCurrentInputStream(); + inputSwitcher.signalEndOfCurrentInputStream(); } } @@ -490,10 +488,10 @@ public void flush() { try { videoFrameProcessingTaskExecutor.flush(); CountDownLatch latch = new CountDownLatch(1); - checkNotNull(textureManager).setOnFlushCompleteListener(latch::countDown); + inputSwitcher.activeTextureManager().setOnFlushCompleteListener(latch::countDown); videoFrameProcessingTaskExecutor.submit(finalShaderProgramWrapper::flush); latch.await(); - checkNotNull(textureManager).setOnFlushCompleteListener(null); + inputSwitcher.activeTextureManager().setOnFlushCompleteListener(null); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } diff --git a/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java b/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java index 4b3d33aba3a..eadcf9cecdc 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/InputSwitcher.java @@ -23,7 +23,6 @@ import android.content.Context; import android.util.SparseArray; -import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.ColorInfo; import androidx.media3.common.GlObjectsProvider; @@ -46,6 +45,7 @@ private final boolean enableColorTransfers; private @MonotonicNonNull GlShaderProgram downstreamShaderProgram; + private @MonotonicNonNull TextureManager activeTextureManager; private boolean inputEnded; private int activeInputType; @@ -159,17 +159,15 @@ public void setDownstreamShaderProgram(GlShaderProgram downstreamShaderProgram) * registered}. * * @param newInputType The new {@link VideoFrameProcessor.InputType} to switch to. - * @return The {@link TextureManager} associated with the {@code newInputType}. */ - public TextureManager switchToInput(@VideoFrameProcessor.InputType int newInputType) { + public void switchToInput(@VideoFrameProcessor.InputType int newInputType) { checkStateNotNull(downstreamShaderProgram); checkState(inputs.indexOfKey(newInputType) >= 0, "Input type not registered: " + newInputType); if (newInputType == activeInputType) { - return inputs.get(activeInputType).textureManager; + activeTextureManager = inputs.get(activeInputType).textureManager; } - @Nullable TextureManager activeTextureManager = null; for (int i = 0; i < inputs.size(); i++) { @VideoFrameProcessor.InputType int inputType = inputs.keyAt(i); Input input = inputs.get(inputType); @@ -181,11 +179,26 @@ public TextureManager switchToInput(@VideoFrameProcessor.InputType int newInputT input.setActive(false); } } - activeInputType = newInputType; + } + + /** + * Returns the {@link TextureManager} that is currently being used. + * + *

    Must call {@link #switchToInput} before calling this method. + */ + public TextureManager activeTextureManager() { return checkNotNull(activeTextureManager); } + /** + * Invokes {@link TextureManager#signalEndOfCurrentInputStream} on the active {@link + * TextureManager}. + */ + public void signalEndOfCurrentInputStream() { + checkNotNull(activeTextureManager).signalEndOfCurrentInputStream(); + } + /** Signals end of input to all {@linkplain #registerInput registered inputs}. */ public void signalEndOfInput() { checkState(!inputEnded); From c26659184c0717e36ea98ddc50139c43b76a71a1 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 19 May 2023 11:02:33 +0100 Subject: [PATCH 048/516] Add empty headings to the 'unreleased changes' section of release notes PiperOrigin-RevId: 533403520 (cherry picked from commit 0e09c0041f109b08b247c286f853f2e79519bcb7) --- RELEASENOTES.md | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 565207b37a7..d59ab865e29 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,22 +2,44 @@ ### Unreleased changes -* Core library: +* Common Library: * Add `Player.replaceMediaItem(s)` as a shortcut to adding and removing items at the same position ([#8046](https://github.com/google/ExoPlayer/issues/8046)). -* IMA extension: - * Enable multi-period live DASH streams for DAI. Please note that the - current implementation does not yet support seeking in live streams - ([#10912](https://github.com/google/ExoPlayer/issues/10912)). * ExoPlayer: * Add `FilteringMediaSource` that allows to filter available track types from a `MediaSource`. +* Transformer: +* Track Selection: +* Extractors: +* Audio: +* Video: +* Text: +* Metadata: +* DRM: +* Effect: +* Muxers: * Session: * Add `androidx.media3.session.MediaButtonReceiver` to enable apps to implement playback resumption with media button events sent by, for example, a Bluetooth headset ([#167](https://github.com/androidx/media/issues/167)). +* UI: +* Downloads: +* OkHttp Extension: +* Cronet Extension: +* RTMP Extension: +* DASH Extension: +* HLS Extension: +* Smooth Streaming Extension: +* RTSP Extension: +* Decoder Extensions (FFmpeg, VP9, AV1, etc.): +* IMA Extension: + * Enable multi-period live DASH streams for DAI. Please note that the + current implementation does not yet support seeking in live streams + ([#10912](https://github.com/google/ExoPlayer/issues/10912)). +* Cast Extension: +* Test Utilities: * Remove deprecated symbols: * Remove two deprecated `SimpleCache` constructors, use a non-deprecated constructor that takes a `DatabaseProvider` instead for better From 7934eaf88259956bda5b2ec6e808411571a3c119 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 19 May 2023 16:10:18 +0100 Subject: [PATCH 049/516] Add logging where we were swallowing failed futures in `media3.session` This may not be completely exhaustive, but it's better than before! #minor-release PiperOrigin-RevId: 533457167 (cherry picked from commit fe19dc421decccf283e503aab9375a04f5dcdeb7) --- .../androidx/media3/session/MediaController.java | 1 + .../media3/session/MediaControllerImplBase.java | 6 ++++-- .../media3/session/MediaControllerImplLegacy.java | 2 +- .../session/MediaLibraryServiceLegacyStub.java | 13 ++++++++----- .../media3/session/MediaLibrarySessionImpl.java | 3 ++- .../media3/session/MediaSessionLegacyStub.java | 8 +++++--- .../androidx/media3/session/MediaSessionStub.java | 10 +++++++--- 7 files changed, 28 insertions(+), 15 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaController.java b/libraries/session/src/main/java/androidx/media3/session/MediaController.java index 05e5f39fce9..54f6145d32a 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaController.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaController.java @@ -529,6 +529,7 @@ public static void releaseFuture(Future controllerFut try { controller = Futures.getDone(controllerFuture); } catch (CancellationException | ExecutionException e) { + Log.w(TAG, "MediaController future failed (so we couldn't release it)", e); return; } controller.release(); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index 5a4b33be2b3..7ad7b1d91e3 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -2282,9 +2282,11 @@ private void sendControllerResultWhenReady(int seq, ListenableFuture void cancelAllFutures(List<@NullableType ListenableFuture> try { bitmap = Futures.getDone(bitmapFuture); } catch (CancellationException | ExecutionException e) { - Log.d(TAG, "failed to get bitmap"); + Log.d(TAG, "failed to get bitmap", e); } outputFuture.set(MediaUtils.convertToBrowserItem(mediaItem, bitmap)); }, diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java index 5fda9c6e82f..6759d78b442 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java @@ -359,7 +359,8 @@ private static T tryGetFutureResult(Future future) { checkState(future.isDone()); try { return future.get(); - } catch (CancellationException | ExecutionException | InterruptedException unused) { + } catch (CancellationException | ExecutionException | InterruptedException e) { + Log.w(TAG, "Library operation failed", e); return null; } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java index cc9075b3df8..49d9723fe08 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionLegacyStub.java @@ -889,9 +889,11 @@ private static void sendCustomCommandResultWhenReady( SessionResult result; try { result = checkNotNull(future.get(), "SessionResult must not be null"); - } catch (CancellationException unused) { + } catch (CancellationException e) { + Log.w(TAG, "Custom command cancelled", e); result = new SessionResult(RESULT_INFO_SKIPPED); - } catch (ExecutionException | InterruptedException unused) { + } catch (ExecutionException | InterruptedException e) { + Log.w(TAG, "Custom command failed", e); result = new SessionResult(RESULT_ERROR_UNKNOWN); } receiver.send(result.resultCode, result.extras); @@ -1208,7 +1210,7 @@ private void handleBitmapFuturesAllCompletedAndSetQueue( try { bitmap = Futures.getDone(future); } catch (CancellationException | ExecutionException e) { - Log.d(TAG, "Failed to get bitmap"); + Log.d(TAG, "Failed to get bitmap", e); } } queueItemList.add(MediaUtils.convertToQueueItem(mediaItems.get(i), i, bitmap)); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java index a336f69373f..cfe20c02477 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionStub.java @@ -178,9 +178,11 @@ SessionTask, K> sendSessionResultWhenReady( SessionResult result; try { result = checkNotNull(future.get(), "SessionResult must not be null"); - } catch (CancellationException unused) { + } catch (CancellationException e) { + Log.w(TAG, "Session operation cancelled", e); result = new SessionResult(SessionResult.RESULT_INFO_SKIPPED); } catch (ExecutionException | InterruptedException exception) { + Log.w(TAG, "Session operation failed", exception); result = new SessionResult( exception.getCause() instanceof UnsupportedOperationException @@ -265,9 +267,11 @@ SessionTask, K> sendLibraryResultWhenReady( LibraryResult result; try { result = checkNotNull(future.get(), "LibraryResult must not be null"); - } catch (CancellationException unused) { + } catch (CancellationException e) { + Log.w(TAG, "Library operation cancelled", e); result = LibraryResult.ofError(LibraryResult.RESULT_INFO_SKIPPED); - } catch (ExecutionException | InterruptedException unused) { + } catch (ExecutionException | InterruptedException e) { + Log.w(TAG, "Library operation failed", e); result = LibraryResult.ofError(LibraryResult.RESULT_ERROR_UNKNOWN); } sendLibraryResult(controller, sequenceNumber, result); From 41de418c3df2f3a4076718ceee63d6e8442c1c42 Mon Sep 17 00:00:00 2001 From: ibaker Date: Fri, 19 May 2023 16:39:14 +0100 Subject: [PATCH 050/516] Remove deprecated `ExoPlayer.retry()`, use `prepare()` instead. #minor-release PiperOrigin-RevId: 533463348 (cherry picked from commit 50112c685b093964be1da3a513bba5e0c86f3882) --- RELEASENOTES.md | 1 + .../java/androidx/media3/exoplayer/ExoPlayer.java | 7 ------- .../java/androidx/media3/exoplayer/ExoPlayerImpl.java | 7 ------- .../androidx/media3/exoplayer/SimpleExoPlayer.java | 11 ----------- .../androidx/media3/test/utils/StubExoPlayer.java | 9 --------- 5 files changed, 1 insertion(+), 34 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d59ab865e29..36b27199b68 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -75,6 +75,7 @@ `copyWithFrameRate`, `copyWithDrmInitData`, `copyWithMetadata`, `copyWithBitrate` and `copyWithVideoSize`, use `Format.buildUpon()` and setter methods instead. + * Remove deprecated `ExoPlayer.retry()`, use `prepare()` instead. ### 1.0.2 (2023-05-18) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index 7bc70b0f1b5..77cccb41abf 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -1354,13 +1354,6 @@ public ExoPlayer build() { @UnstableApi Clock getClock(); - /** - * @deprecated Use {@link #prepare()} instead. - */ - @UnstableApi - @Deprecated - void retry(); - /** * @deprecated Use {@link #setMediaSource(MediaSource)} and {@link #prepare()} instead. */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index e063f6c53b3..77b0252ce78 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -520,13 +520,6 @@ public ExoPlaybackException getPlayerError() { return playbackInfo.playbackError; } - @Override - @Deprecated - public void retry() { - verifyApplicationThread(); - prepare(); - } - @Override public void prepare() { verifyApplicationThread(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index 1ccc62c0ad0..edda6b71cbc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -816,17 +816,6 @@ public ExoPlaybackException getPlayerError() { return player.getPlayerError(); } - /** - * @deprecated Use {@link #prepare()} instead. - */ - @Deprecated - @Override - @SuppressWarnings("deprecation") // Calling deprecated method. - public void retry() { - blockUntilConstructorFinished(); - player.retry(); - } - @Override public Commands getAvailableCommands() { blockUntilConstructorFinished(); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java index 9cfbac5a7d0..730fefd7e1b 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java @@ -130,15 +130,6 @@ public ExoPlaybackException getPlayerError() { throw new UnsupportedOperationException(); } - /** - * @deprecated Use {@link #prepare()} instead. - */ - @Deprecated - @Override - public void retry() { - throw new UnsupportedOperationException(); - } - /** * @deprecated Use {@link #setMediaSource(MediaSource)} and {@link #prepare()} instead. */ From ef6772b946a6902d9d66437fba7cd7fc0a704d72 Mon Sep 17 00:00:00 2001 From: bachinger Date: Fri, 19 May 2023 16:49:30 +0100 Subject: [PATCH 051/516] Return full media item when SystemUI calls service on device boot time #minor-release PiperOrigin-RevId: 533465595 (cherry picked from commit 3fa666c687c62b0a1d8b0b0fca31d008aba378de) --- .../session/MediaLibrarySessionImpl.java | 49 ++++- .../MediaLibrarySessionCallbackTest.java | 173 +++++++++++++++++- 2 files changed, 218 insertions(+), 4 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java index 6759d78b442..9aae93f6719 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaLibrarySessionImpl.java @@ -24,6 +24,8 @@ import static androidx.media3.session.LibraryResult.RESULT_SUCCESS; import static androidx.media3.session.MediaConstants.ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT; import static androidx.media3.session.MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT; +import static java.lang.Math.max; +import static java.lang.Math.min; import android.app.PendingIntent; import android.content.Context; @@ -43,9 +45,11 @@ import androidx.media3.session.MediaSession.ControllerCb; import androidx.media3.session.MediaSession.ControllerInfo; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; +import com.google.common.util.concurrent.SettableFuture; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -169,9 +173,14 @@ public ListenableFuture>> onGetChildrenOn int pageSize, @Nullable LibraryParams params) { if (Objects.equals(parentId, RECENT_LIBRARY_ROOT_MEDIA_ID)) { - // Advertise support for playback resumption, if enabled. - return !canResumePlaybackOnStart() - ? Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_NOT_SUPPORTED)) + if (!canResumePlaybackOnStart()) { + return Futures.immediateFuture(LibraryResult.ofError(RESULT_ERROR_NOT_SUPPORTED)); + } + // Advertise support for playback resumption. If STATE_IDLE, the request arrives at boot time + // to get the full item data to build a notification. If not STATE_IDLE we don't need to + // deliver the full media item, so we do the minimal viable effort. + return getPlayerWrapper().getPlaybackState() == Player.STATE_IDLE + ? getRecentMediaItemAtDeviceBootTime(browser, params) : Futures.immediateFuture( LibraryResult.ofItemList( ImmutableList.of( @@ -386,4 +395,38 @@ private void removeSubscription(ControllerCb controllerCb, String parentId) { } } } + + private ListenableFuture>> + getRecentMediaItemAtDeviceBootTime( + ControllerInfo controller, @Nullable LibraryParams params) { + SettableFuture>> settableFuture = + SettableFuture.create(); + ListenableFuture future = + callback.onPlaybackResumption(instance, controller); + Futures.addCallback( + future, + new FutureCallback() { + @Override + public void onSuccess(MediaSession.MediaItemsWithStartPosition playlist) { + if (playlist.mediaItems.isEmpty()) { + settableFuture.set( + LibraryResult.ofError(LibraryResult.RESULT_ERROR_INVALID_STATE, params)); + return; + } + int sanitizedStartIndex = + max(0, min(playlist.startIndex, playlist.mediaItems.size() - 1)); + settableFuture.set( + LibraryResult.ofItemList( + ImmutableList.of(playlist.mediaItems.get(sanitizedStartIndex)), params)); + } + + @Override + public void onFailure(Throwable t) { + settableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_UNKNOWN, params)); + Log.e(TAG, "Failed fetching recent media item at boot time: " + t.getMessage(), t); + } + }, + MoreExecutors.directExecutor()); + return settableFuture; + } } diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibrarySessionCallbackTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibrarySessionCallbackTest.java index 3eedbacb6cd..924bf4fa140 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibrarySessionCallbackTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaLibrarySessionCallbackTest.java @@ -23,6 +23,7 @@ import androidx.annotation.Nullable; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; +import androidx.media3.common.Player; import androidx.media3.session.MediaLibraryService.LibraryParams; import androidx.media3.session.MediaLibraryService.MediaLibrarySession; import androidx.media3.session.MediaSession.ControllerInfo; @@ -176,7 +177,176 @@ public ListenableFuture> onGetLibraryRoot( } @Test - public void onGetChildren_systemUiCallForRecentItems_returnsRecentItems() throws Exception { + public void onGetChildren_systemUiCallForRecentItemsWhenIdle_callsOnPlaybackResumption() + throws Exception { + ArrayList mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); + MockMediaLibraryService service = new MockMediaLibraryService(); + service.attachBaseContext(context); + CountDownLatch latch = new CountDownLatch(2); + MediaLibrarySession.Callback callback = + new MediaLibrarySession.Callback() { + @Override + public ListenableFuture onPlaybackResumption( + MediaSession mediaSession, ControllerInfo controller) { + latch.countDown(); + return Futures.immediateFuture( + new MediaSession.MediaItemsWithStartPosition( + mediaItems, /* startIndex= */ 1, /* startPositionMs= */ 1000L)); + } + + @Override + public ListenableFuture>> onGetChildren( + MediaLibrarySession session, + ControllerInfo browser, + String parentId, + int page, + int pageSize, + @Nullable LibraryParams params) { + latch.countDown(); + return Futures.immediateFuture( + LibraryResult.ofItemList(mediaItems, /* params= */ null)); + } + }; + MediaLibrarySession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaLibrarySession.Builder(service, player, callback) + .setId("onGetChildren_systemUiCallForRecentItems_returnsRecentItems") + .build()); + RemoteMediaBrowser browser = controllerTestRule.createRemoteBrowser(session.getToken()); + + LibraryResult> recentItem = + browser.getChildren( + "androidx.media3.session.recent.root", + /* page= */ 0, + /* pageSize= */ 100, + /* params= */ null); + // Load children of a non recent root that must not be intercepted. + LibraryResult> children = + browser.getChildren("children", /* page= */ 0, /* pageSize= */ 100, /* params= */ null); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(recentItem.resultCode).isEqualTo(LibraryResult.RESULT_SUCCESS); + assertThat(Lists.transform(recentItem.value, (item) -> item.mediaId)) + .containsExactly("mediaItem_2"); + assertThat(children.value).isEqualTo(mediaItems); + } + + @Test + public void + onGetChildren_systemUiCallForRecentItemsWhenIdleWithEmptyResumptionPlaylist_resultInvalidState() + throws Exception { + MockMediaLibraryService service = new MockMediaLibraryService(); + service.attachBaseContext(context); + CountDownLatch latch = new CountDownLatch(1); + MediaLibrarySession.Callback callback = + new MediaLibrarySession.Callback() { + @Override + public ListenableFuture onPlaybackResumption( + MediaSession mediaSession, ControllerInfo controller) { + latch.countDown(); + return Futures.immediateFuture( + new MediaSession.MediaItemsWithStartPosition( + ImmutableList.of(), /* startIndex= */ 11, /* startPositionMs= */ 1000L)); + } + }; + MediaLibrarySession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaLibrarySession.Builder(service, player, callback) + .setId("onGetChildren_systemUiCallForRecentItems_returnsRecentItems") + .build()); + RemoteMediaBrowser browser = controllerTestRule.createRemoteBrowser(session.getToken()); + + LibraryResult> recentItem = + browser.getChildren( + "androidx.media3.session.recent.root", + /* page= */ 0, + /* pageSize= */ 100, + /* params= */ null); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(recentItem.resultCode).isEqualTo(LibraryResult.RESULT_ERROR_INVALID_STATE); + } + + @Test + public void + onGetChildren_systemUiCallForRecentItemsWhenIdleStartIndexTooHigh_setToLastItemItemInList() + throws Exception { + ArrayList mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); + MockMediaLibraryService service = new MockMediaLibraryService(); + service.attachBaseContext(context); + CountDownLatch latch = new CountDownLatch(1); + MediaLibrarySession.Callback callback = + new MediaLibrarySession.Callback() { + @Override + public ListenableFuture onPlaybackResumption( + MediaSession mediaSession, ControllerInfo controller) { + latch.countDown(); + return Futures.immediateFuture( + new MediaSession.MediaItemsWithStartPosition( + mediaItems, /* startIndex= */ 11, /* startPositionMs= */ 1000L)); + } + }; + MediaLibrarySession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaLibrarySession.Builder(service, player, callback) + .setId("onGetChildren_systemUiCallForRecentItems_returnsRecentItems") + .build()); + RemoteMediaBrowser browser = controllerTestRule.createRemoteBrowser(session.getToken()); + + LibraryResult> recentItem = + browser.getChildren( + "androidx.media3.session.recent.root", + /* page= */ 0, + /* pageSize= */ 100, + /* params= */ null); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(recentItem.resultCode).isEqualTo(LibraryResult.RESULT_SUCCESS); + assertThat(Lists.transform(recentItem.value, (item) -> item.mediaId)) + .containsExactly("mediaItem_3"); + } + + @Test + public void onGetChildren_systemUiCallForRecentItemsWhenIdleStartIndexNegative_setToZero() + throws Exception { + ArrayList mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); + MockMediaLibraryService service = new MockMediaLibraryService(); + service.attachBaseContext(context); + CountDownLatch latch = new CountDownLatch(1); + MediaLibrarySession.Callback callback = + new MediaLibrarySession.Callback() { + @Override + public ListenableFuture onPlaybackResumption( + MediaSession mediaSession, ControllerInfo controller) { + latch.countDown(); + return Futures.immediateFuture( + new MediaSession.MediaItemsWithStartPosition( + mediaItems, /* startIndex= */ -11, /* startPositionMs= */ 1000L)); + } + }; + MediaLibrarySession session = + sessionTestRule.ensureReleaseAfterTest( + new MediaLibrarySession.Builder(service, player, callback) + .setId("onGetChildren_systemUiCallForRecentItems_returnsRecentItems") + .build()); + RemoteMediaBrowser browser = controllerTestRule.createRemoteBrowser(session.getToken()); + + LibraryResult> recentItem = + browser.getChildren( + "androidx.media3.session.recent.root", + /* page= */ 0, + /* pageSize= */ 100, + /* params= */ null); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertThat(recentItem.resultCode).isEqualTo(LibraryResult.RESULT_SUCCESS); + assertThat(Lists.transform(recentItem.value, (item) -> item.mediaId)) + .containsExactly("mediaItem_1"); + } + + @Test + public void onGetChildren_systemUiCallForRecentItemsWhenNotIdle_returnsRecentItems() + throws Exception { ArrayList mediaItems = MediaTestUtils.createMediaItems(/* size= */ 3); MockMediaLibraryService service = new MockMediaLibraryService(); service.attachBaseContext(context); @@ -196,6 +366,7 @@ public ListenableFuture>> onGetChildren( LibraryResult.ofItemList(mediaItems, /* params= */ null)); } }; + player.playbackState = Player.STATE_READY; MediaLibrarySession session = sessionTestRule.ensureReleaseAfterTest( new MediaLibrarySession.Builder(service, player, callback) From b066a0912e7431d503ec770df846f8cd3c09be64 Mon Sep 17 00:00:00 2001 From: bachinger Date: Fri, 19 May 2023 17:48:32 +0100 Subject: [PATCH 052/516] Set video size to 0/0 when video render is disabled In terms of MCVR with a `VideoRendererEventListener`, the video size is set to 0/0 right after `onVideoDisabled()` is called and is set to the actual size as soon as the video size is known after 'onVideoEnabled()`. For ExoPlayer and in terms of the `Player` interface, `Player.getVideoSize()` returns a video size of 0/0 when `Player.getCurrentTracks()` does not support `C.TRACK_TYPE_VIDEO`. This is ensured by the masking behavior of `ExoPlayerImpl` that sets an empty track selection result when the playing period changes due to a seek or timeline removal. When transitioning playback from a video media item to the next, or when seeking within the same video media item, the renderer is not disabled. #minor-release PiperOrigin-RevId: 533479600 (cherry picked from commit 2a6f893fba763077cae85b0255d6bd05f0887709) --- RELEASENOTES.md | 12 ++++ demos/main/src/main/assets/media.exolist.json | 14 +++- .../java/androidx/media3/common/Player.java | 6 +- .../video/MediaCodecVideoRenderer.java | 1 + .../media3/exoplayer/ExoPlayerTest.java | 71 +++++++++++++++++++ .../DefaultAnalyticsCollectorTest.java | 50 ++++++++----- .../session/common/MediaSessionConstants.java | 3 + .../session/MediaControllerListenerTest.java | 52 +++++++++----- .../session/MediaSessionProviderService.java | 9 +++ .../media3/session/MediaTestUtils.java | 51 +++++++++++++ .../media3/test/utils/FakeVideoRenderer.java | 30 +++++--- 11 files changed, 250 insertions(+), 49 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 36b27199b68..ca369970833 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -19,6 +19,18 @@ * DRM: * Effect: * Muxers: +* IMA extension: + * Enable multi-period live DASH streams for DAI. Please note that the + current implementation does not yet support seeking in live streams + ([#10912](https://github.com/google/ExoPlayer/issues/10912)). +* ExoPlayer: + * Make `MediaCodecVideoRenderer` report a `VideoSize` with a width and + height of 0 when the renderer is disabled. + `Player.Listener.onVideoSizeChanged` is called accordingly when + `Player.getVideoSize()` changes. With this change, ExoPlayer's video + size with `MediaCodecVideoRenderer` has a width and height of 0 when + `Player.getCurrentTracks` does not support video, or the size of the + supported video track is not yet determined. * Session: * Add `androidx.media3.session.MediaButtonReceiver` to enable apps to implement playback resumption with media button events sent by, for diff --git a/demos/main/src/main/assets/media.exolist.json b/demos/main/src/main/assets/media.exolist.json index 3acef825d1b..f37103c8fc5 100644 --- a/demos/main/src/main/assets/media.exolist.json +++ b/demos/main/src/main/assets/media.exolist.json @@ -492,7 +492,7 @@ ] }, { - "name": "Audio -> Video -> Audio", + "name": "Audio -> Video (MKV) -> Video (MKV) -> Audio -> Video (MKV) -> Video (DASH) -> Audio", "playlist": [ { "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4" @@ -500,6 +500,18 @@ { "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv" }, + { + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv" + }, + { + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4" + }, + { + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv" + }, + { + "uri": "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd" + }, { "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4" } diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 8d8e440175d..0b3ee058178 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -990,7 +990,7 @@ default void onDeviceInfoChanged(DeviceInfo deviceInfo) {} default void onDeviceVolumeChanged(int volume, boolean muted) {} /** - * Called each time there's a change in the size of the video being rendered. + * Called each time when {@link Player#getVideoSize()} changes. * *

    {@link #onEvents(Player, Events)} will also be called to report this event along with * other events that happen in the same {@link Looper} message queue iteration. @@ -3093,8 +3093,8 @@ default void replaceMediaItems(int fromIndex, int toIndex, List media /** * Gets the size of the video. * - *

    The video's width and height are {@code 0} if there is no video or its size has not been - * determined yet. + *

    The video's width and height are {@code 0} if there is {@linkplain + * Tracks#isTypeSupported(int) no supported video track} or its size has not been determined yet. * * @see Listener#onVideoSizeChanged(VideoSize) */ diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index 999d559e9d1..3d848899ce6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -634,6 +634,7 @@ protected void onDisabled() { super.onDisabled(); } finally { eventDispatcher.disabled(decoderCounters); + eventDispatcher.videoSizeChanged(VideoSize.UNKNOWN); } } diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index dd74cd6b9e0..b3ffa1fcee5 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -116,6 +116,7 @@ import androidx.media3.common.Timeline.Window; import androidx.media3.common.TrackGroup; import androidx.media3.common.Tracks; +import androidx.media3.common.VideoSize; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Clock; import androidx.media3.common.util.HandlerWrapper; @@ -342,6 +343,76 @@ public void playMultiPeriodTimeline() throws Exception { assertThat(renderer.isEnded).isTrue(); } + @Test + public void play_audioVideoAudioVideoTransition_videoSizeChangedCalledCorrectly() + throws Exception { + Timeline timeline = new FakeTimeline(/* windowCount= */ 1); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 0))); + Player.Listener mockPlayerListener = mock(Player.Listener.class); + player.addListener(mockPlayerListener); + AnalyticsListener mockAnalyticsListener = mock(AnalyticsListener.class); + player.addAnalyticsListener(mockAnalyticsListener); + player.setMediaSources( + ImmutableList.of( + new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT), + new FakeMediaSource( + timeline, ExoPlayerTestRunner.VIDEO_FORMAT, ExoPlayerTestRunner.AUDIO_FORMAT), + new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT), + new FakeMediaSource( + timeline, ExoPlayerTestRunner.VIDEO_FORMAT, ExoPlayerTestRunner.AUDIO_FORMAT))); + player.prepare(); + List videoSizesFromGetter = new ArrayList<>(); + player.addListener( + new Listener() { + @Override + public void onVideoSizeChanged(VideoSize videoSize) { + videoSizesFromGetter.add(player.getVideoSize()); + } + }); + + player.play(); + runUntilPlaybackState(player, Player.STATE_READY); + // Get the video size right after the first audio item was prepared. + videoSizesFromGetter.add(player.getVideoSize()); + runUntilPlaybackState(player, Player.STATE_ENDED); + videoSizesFromGetter.add(player.getVideoSize()); + player.release(); + ShadowLooper.runMainLooperToNextTask(); + + InOrder playerListenerOrder = inOrder(mockPlayerListener); + playerListenerOrder.verify(mockPlayerListener).onVideoSizeChanged(new VideoSize(1280, 720)); + playerListenerOrder.verify(mockPlayerListener).onVideoSizeChanged(VideoSize.UNKNOWN); + playerListenerOrder.verify(mockPlayerListener).onVideoSizeChanged(new VideoSize(1280, 720)); + playerListenerOrder.verify(mockPlayerListener).onPlaybackStateChanged(STATE_ENDED); + verify(mockPlayerListener, times(3)).onVideoSizeChanged(any()); + // Verify calls to analytics listener. + verify(mockAnalyticsListener, times(2)).onVideoEnabled(any(), any()); + verify(mockAnalyticsListener, times(2)).onVideoDisabled(any(), any()); + verify(mockAnalyticsListener).onAudioEnabled(any(), any()); + verify(mockAnalyticsListener).onAudioDisabled(any(), any()); + InOrder inOrder = Mockito.inOrder(mockAnalyticsListener); + inOrder.verify(mockAnalyticsListener).onAudioEnabled(any(), any()); + inOrder.verify(mockAnalyticsListener).onVideoEnabled(any(), any()); + inOrder.verify(mockAnalyticsListener).onVideoSizeChanged(any(), eq(new VideoSize(1280, 720))); + inOrder.verify(mockAnalyticsListener).onVideoDisabled(any(), any()); + inOrder.verify(mockAnalyticsListener).onVideoSizeChanged(any(), eq(VideoSize.UNKNOWN)); + inOrder.verify(mockAnalyticsListener).onVideoEnabled(any(), any()); + inOrder.verify(mockAnalyticsListener).onVideoSizeChanged(any(), eq(new VideoSize(1280, 720))); + inOrder.verify(mockAnalyticsListener).onVideoDisabled(any(), any()); + inOrder.verify(mockAnalyticsListener).onAudioDisabled(any(), any()); + verify(mockAnalyticsListener, times(3)).onVideoSizeChanged(any(), any()); + // Verify video sizes from getter. + assertThat(videoSizesFromGetter) + .containsExactly( + VideoSize.UNKNOWN, // When first item starts playing + new VideoSize(1280, 720), // When onVideoSizeChanged() called + VideoSize.UNKNOWN, // When onVideoSizeChanged() called + new VideoSize(1280, 720), // When onVideoSizeChanged() called + new VideoSize(1280, 720)) // In STATE_ENDED + .inOrder(); + } + /** Tests playback of periods with very short duration. */ @Test public void playShortDurationPeriods() throws Exception { diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java index 84f60366835..eb5daa2d0ab 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/analytics/DefaultAnalyticsCollectorTest.java @@ -381,9 +381,7 @@ public void automaticPeriodTransition() throws Exception { .containsExactly(period0, period1) .inOrder(); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period1); - assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly(period0, period1) - .inOrder(); + assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) .containsExactly(period0, period1) .inOrder(); @@ -460,7 +458,11 @@ public void periodTransitionWithRendererChange() throws Exception { assertThat(listener.getEvents(EVENT_VIDEO_INPUT_FORMAT_CHANGED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_DISABLED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0); - assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0); + assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) + .containsExactly( + period0, // First frame rendered of first video item + period1) // width=0, height=0 for audio only media source + .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET)).containsExactly(period0); listener.assertNoMoreEvents(); @@ -554,7 +556,11 @@ public void seekToOtherPeriod() throws Exception { assertThat(listener.getEvents(EVENT_VIDEO_DECODER_INITIALIZED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_INPUT_FORMAT_CHANGED)).containsExactly(period0); assertThat(listener.getEvents(EVENT_VIDEO_DISABLED)).containsExactly(period0); - assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)).containsExactly(period0); + assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) + .containsExactly( + period0, // First frame rendered of first video item + period1) // width=0, height=0 for audio only media source + .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)).containsExactly(period0); listener.assertNoMoreEvents(); } @@ -663,7 +669,10 @@ public void seekBackAfterReadingAhead() throws Exception { .containsExactly(period0, period1Seq2) .inOrder(); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly(period0, period1Seq1, period0, period1Seq2) + .containsExactly( + period0, // First frame rendered + period1Seq1, // Renderer disabled after seek + period0) // First frame rendered after seek .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) .containsExactly(period0, period1Seq1, period0, period1Seq2) @@ -766,7 +775,10 @@ public void prepareNewSource() throws Exception { assertThat(listener.getEvents(EVENT_VIDEO_DISABLED)).containsExactly(period0Seq0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0Seq1); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly(period0Seq0, period0Seq1) + .containsExactly( + period0Seq0, // First frame rendered + period0Seq0, // Renderer disabled after timeline changed + period0Seq1) // First frame rendered of new source .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) .containsExactly(period0Seq0, period0Seq1) @@ -851,7 +863,7 @@ public void reprepareAfterError() throws Exception { assertThat(listener.getEvents(EVENT_VIDEO_DISABLED)).containsExactly(period0Seq0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0Seq0); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly(period0Seq0, period0Seq0); + .containsExactly(period0Seq0, period0Seq0, period0Seq0); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) .containsExactly(period0Seq0, period0Seq0); assertThat(listener.getEvents(EVENT_VIDEO_FRAME_PROCESSING_OFFSET)) @@ -938,7 +950,9 @@ public void dynamicTimelineChange() throws Exception { .containsExactly(window0Period1Seq0, period1Seq0) .inOrder(); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly(window0Period1Seq0, window1Period0Seq1) + .containsExactly( + window0Period1Seq0, // First frame rendered + window0Period1Seq0) // Renderer disabled after timeline update .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) .containsExactly(window0Period1Seq0, window1Period0Seq1) @@ -1036,7 +1050,10 @@ public void playlistOperations() throws Exception { assertThat(listener.getEvents(EVENT_VIDEO_DISABLED)).containsExactly(period0Seq0); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(period0Seq1); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly(period0Seq0, period1Seq1, period0Seq1) + .containsExactly( + period0Seq0, // First frame rendered + period0Seq1, // Renderer disabled after media item removal + period0Seq1) // First frame rendered after removal .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) .containsExactly(period0Seq0, period1Seq1, period0Seq1); @@ -1282,13 +1299,7 @@ public void onPositionDiscontinuity( .containsExactly(contentAfterPreroll, contentAfterMidroll, contentAfterPostroll) .inOrder(); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly( - prerollAd, - contentAfterPreroll, - midrollAd, - contentAfterMidroll, - postrollAd, - contentAfterPostroll) + .containsExactly(prerollAd) // First frame rendered .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) .containsExactly( @@ -1439,7 +1450,10 @@ public void seekAfterMidroll() throws Exception { assertThat(listener.getEvents(EVENT_VIDEO_DISABLED)).containsExactly(contentBeforeMidroll); assertThat(listener.getEvents(EVENT_DROPPED_VIDEO_FRAMES)).containsExactly(contentAfterMidroll); assertThat(listener.getEvents(EVENT_VIDEO_SIZE_CHANGED)) - .containsExactly(contentBeforeMidroll, midrollAd, contentAfterMidroll) + .containsExactly( + contentBeforeMidroll, // First frame rendered + midrollAd, // Renderer disabled for seek + midrollAd) // First frame rendered after seek .inOrder(); assertThat(listener.getEvents(EVENT_RENDERED_FIRST_FRAME)) .containsExactly(contentBeforeMidroll, midrollAd, contentAfterMidroll) diff --git a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java index 480fe012acf..58e14349250 100644 --- a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java +++ b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaSessionConstants.java @@ -24,6 +24,9 @@ public class MediaSessionConstants { public static final String TEST_CONTROLLER_LISTENER_SESSION_REJECTS = "connection_sessionRejects"; public static final String TEST_IS_SESSION_COMMAND_AVAILABLE = "testIsSessionCommandAvailable"; public static final String TEST_COMMAND_GET_TRACKS = "testCommandGetTracksUnavailable"; + public static final String TEST_ON_VIDEO_SIZE_CHANGED = "onVideoSizeChanged"; + public static final String TEST_ON_TRACKS_CHANGED_VIDEO_TO_AUDIO_TRANSITION = + "onTracksChanged_videoToAudioTransition"; // Bundle keys public static final String KEY_AVAILABLE_SESSION_COMMANDS = "availableSessionCommands"; diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java index c09d5501973..1cdf994b662 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java @@ -25,6 +25,7 @@ import static androidx.media3.test.session.common.MediaSessionConstants.KEY_CONTROLLER; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_COMMAND_GET_TRACKS; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_CONTROLLER_LISTENER_SESSION_REJECTS; +import static androidx.media3.test.session.common.MediaSessionConstants.TEST_ON_VIDEO_SIZE_CHANGED; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_WITH_CUSTOM_COMMANDS; import static androidx.media3.test.session.common.TestUtils.LONG_TIMEOUT_MS; import static androidx.media3.test.session.common.TestUtils.NO_RESPONSE_TIMEOUT_MS; @@ -2545,40 +2546,53 @@ public void onSessionActivityChanged( @Test public void onVideoSizeChanged() throws Exception { - VideoSize testVideoSize = - new VideoSize( - /* width= */ 100, - /* height= */ 42, - /* unappliedRotationDegrees= */ 90, - /* pixelWidthHeightRatio= */ 1.2f); - MediaController controller = controllerTestRule.createController(remoteSession.getToken()); - CountDownLatch latch = new CountDownLatch(2); - AtomicReference videoSizeFromParamRef = new AtomicReference<>(); - AtomicReference videoSizeFromGetterRef = new AtomicReference<>(); - AtomicReference eventsRef = new AtomicReference<>(); + VideoSize defaultVideoSize = MediaTestUtils.createDefaultVideoSize(); + RemoteMediaSession session = createRemoteMediaSession(TEST_ON_VIDEO_SIZE_CHANGED); + MediaController controller = controllerTestRule.createController(session.getToken()); + List videoSizeFromGetterList = new ArrayList<>(); + List videoSizeFromParamList = new ArrayList<>(); + List eventsList = new ArrayList<>(); + CountDownLatch latch = new CountDownLatch(6); Player.Listener listener = new Player.Listener() { @Override public void onVideoSizeChanged(VideoSize videoSize) { - videoSizeFromParamRef.set(videoSize); - videoSizeFromGetterRef.set(controller.getVideoSize()); + videoSizeFromParamList.add(videoSize); + videoSizeFromGetterList.add(controller.getVideoSize()); latch.countDown(); } @Override public void onEvents(Player player, Player.Events events) { - eventsRef.set(events); + eventsList.add(events); latch.countDown(); } }; - threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener)); + threadTestRule + .getHandler() + .postAndSync( + () -> { + controller.addListener(listener); + // Verify initial controller state. + assertThat(controller.getVideoSize()).isEqualTo(defaultVideoSize); + }); - remoteSession.getMockPlayer().notifyVideoSizeChanged(testVideoSize); + session.getMockPlayer().notifyVideoSizeChanged(VideoSize.UNKNOWN); + session.getMockPlayer().notifyVideoSizeChanged(defaultVideoSize); + session.getMockPlayer().notifyVideoSizeChanged(defaultVideoSize); + session.getMockPlayer().notifyVideoSizeChanged(VideoSize.UNKNOWN); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(videoSizeFromParamRef.get()).isEqualTo(testVideoSize); - assertThat(videoSizeFromGetterRef.get()).isEqualTo(testVideoSize); - assertThat(getEventsAsList(eventsRef.get())).containsExactly(Player.EVENT_VIDEO_SIZE_CHANGED); + assertThat(videoSizeFromParamList) + .containsExactly(VideoSize.UNKNOWN, defaultVideoSize, VideoSize.UNKNOWN) + .inOrder(); + assertThat(videoSizeFromGetterList) + .containsExactly(VideoSize.UNKNOWN, defaultVideoSize, VideoSize.UNKNOWN) + .inOrder(); + assertThat(eventsList).hasSize(3); + assertThat(getEventsAsList(eventsList.get(0))).containsExactly(Player.EVENT_VIDEO_SIZE_CHANGED); + assertThat(getEventsAsList(eventsList.get(1))).containsExactly(Player.EVENT_VIDEO_SIZE_CHANGED); + assertThat(getEventsAsList(eventsList.get(2))).containsExactly(Player.EVENT_VIDEO_SIZE_CHANGED); } @Test diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java index 65885ed84f0..4fc87705c81 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java @@ -62,6 +62,8 @@ import static androidx.media3.test.session.common.MediaSessionConstants.TEST_CONTROLLER_LISTENER_SESSION_REJECTS; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_GET_SESSION_ACTIVITY; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_IS_SESSION_COMMAND_AVAILABLE; +import static androidx.media3.test.session.common.MediaSessionConstants.TEST_ON_TRACKS_CHANGED_VIDEO_TO_AUDIO_TRANSITION; +import static androidx.media3.test.session.common.MediaSessionConstants.TEST_ON_VIDEO_SIZE_CHANGED; import static androidx.media3.test.session.common.MediaSessionConstants.TEST_WITH_CUSTOM_COMMANDS; import android.app.PendingIntent; @@ -257,6 +259,13 @@ public MediaSession.ConnectionResult onConnect( }); break; } + case TEST_ON_TRACKS_CHANGED_VIDEO_TO_AUDIO_TRANSITION: + case TEST_ON_VIDEO_SIZE_CHANGED: + { + mockPlayer.videoSize = MediaTestUtils.createDefaultVideoSize(); + mockPlayer.currentTracks = MediaTestUtils.createDefaultVideoTracks(); + break; + } default: // fall out } diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java index 990637680fd..46196951768 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaTestUtils.java @@ -31,14 +31,21 @@ import android.support.v4.media.session.MediaSessionCompat; import androidx.annotation.Nullable; import androidx.media.MediaBrowserServiceCompat.BrowserRoot; +import androidx.media3.common.C; +import androidx.media3.common.Format; import androidx.media3.common.MediaItem; import androidx.media3.common.MediaMetadata; +import androidx.media3.common.MimeTypes; import androidx.media3.common.Timeline; +import androidx.media3.common.TrackGroup; +import androidx.media3.common.Tracks; +import androidx.media3.common.VideoSize; import androidx.media3.common.util.Log; import androidx.media3.session.MediaLibraryService.LibraryParams; import androidx.media3.session.MediaSession.ControllerInfo; import androidx.media3.test.session.common.TestUtils; import androidx.test.core.app.ApplicationProvider; +import com.google.common.collect.ImmutableList; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -50,6 +57,50 @@ public final class MediaTestUtils { private static final String TEST_IMAGE_PATH = "media/png/non-motion-photo-shortened.png"; + private static final VideoSize DEFAULT_VIDEO_SIZE = new VideoSize(640, 480); + private static final Format VIDEO_FORMAT = + new Format.Builder() + .setSampleMimeType(MimeTypes.VIDEO_H264) + .setAverageBitrate(2_400_000) + .setWidth(DEFAULT_VIDEO_SIZE.width) + .setHeight(DEFAULT_VIDEO_SIZE.height) + .build(); + private static final Format AUDIO_FORMAT = + new Format.Builder() + .setSampleMimeType(MimeTypes.AUDIO_AAC) + .setAverageBitrate(320_000) + .setChannelCount(2) + .setSampleRate(44100) + .build(); + + /** + * Tracks with {@linkplain C#TRACK_TYPE_VIDEO a single video} track and {@linkplain + * C#TRACK_TYPE_AUDIO a single audio} track for testing purpose. + */ + public static Tracks createDefaultVideoTracks() { + return new Tracks( + ImmutableList.of( + new Tracks.Group( + new TrackGroup(VIDEO_FORMAT), + /* adaptiveSupported= */ false, + new int[] {C.FORMAT_HANDLED}, + /* trackSelected= */ new boolean[] {true}), + new Tracks.Group( + new TrackGroup(AUDIO_FORMAT), + /* adaptiveSupported= */ false, + new int[] {C.FORMAT_HANDLED}, + /* trackSelected= */ new boolean[] {true}))); + } + + /** Returns a new {@link VideoSize} instance for testing purpose. */ + public static VideoSize createDefaultVideoSize() { + return new VideoSize( + DEFAULT_VIDEO_SIZE.width, + DEFAULT_VIDEO_SIZE.height, + DEFAULT_VIDEO_SIZE.unappliedRotationDegrees, + DEFAULT_VIDEO_SIZE.pixelWidthHeightRatio); + } + /** Create a media item with the mediaId for testing purpose. */ public static MediaItem createMediaItem(String mediaId) { MediaMetadata mediaMetadata = diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeVideoRenderer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeVideoRenderer.java index 45de4ecad12..99cdeaaa382 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeVideoRenderer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeVideoRenderer.java @@ -28,6 +28,8 @@ import androidx.media3.exoplayer.ExoPlaybackException; import androidx.media3.exoplayer.Renderer; import androidx.media3.exoplayer.video.VideoRendererEventListener; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** A {@link FakeRenderer} that supports {@link C#TRACK_TYPE_VIDEO}. */ @@ -37,6 +39,7 @@ public class FakeVideoRenderer extends FakeRenderer { private final HandlerWrapper handler; private final VideoRendererEventListener eventListener; private final DecoderCounters decoderCounters; + private final AtomicReference videoSizeRef = new AtomicReference<>(); private @MonotonicNonNull Format format; @Nullable private Object output; private long streamOffsetUs; @@ -49,6 +52,7 @@ public FakeVideoRenderer(HandlerWrapper handler, VideoRendererEventListener even this.handler = handler; this.eventListener = eventListener; decoderCounters = new DecoderCounters(); + videoSizeRef.set(VideoSize.UNKNOWN); } @Override @@ -81,7 +85,12 @@ protected void onStopped() { @Override protected void onDisabled() { super.onDisabled(); - handler.post(() -> eventListener.onVideoDisabled(decoderCounters)); + videoSizeRef.set(VideoSize.UNKNOWN); + handler.post( + () -> { + eventListener.onVideoDisabled(decoderCounters); + eventListener.onVideoSizeChanged(VideoSize.UNKNOWN); + }); } @Override @@ -141,13 +150,18 @@ protected boolean shouldProcessBuffer(long bufferTimeUs, long playbackPositionUs if (shouldProcess && !renderedFirstFrameAfterReset && output != null) { @MonotonicNonNull Format format = Assertions.checkNotNull(this.format); handler.post( - () -> - eventListener.onVideoSizeChanged( - new VideoSize( - format.width, - format.height, - format.rotationDegrees, - format.pixelWidthHeightRatio))); + () -> { + VideoSize videoSize = + new VideoSize( + format.width, + format.height, + format.rotationDegrees, + format.pixelWidthHeightRatio); + if (!Objects.equals(videoSize, videoSizeRef.get())) { + eventListener.onVideoSizeChanged(videoSize); + videoSizeRef.set(videoSize); + } + }); handler.post( () -> eventListener.onRenderedFirstFrame( From d40f37158a59d8bfacc26d957501ef32d557fe2f Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Fri, 19 May 2023 18:30:30 +0100 Subject: [PATCH 053/516] Move MdtaMetadataEntry class into container module This class is to be shared between extractor, transformer and muxer module. PiperOrigin-RevId: 533490888 (cherry picked from commit b382a7c2da1452e15d5829113fb39ebc98b87a17) --- .../java/androidx/media3/container}/MdtaMetadataEntry.java | 2 +- .../androidx/media3/container}/MdtaMetadataEntryTest.java | 2 +- .../java/androidx/media3/exoplayer/MetadataRetrieverTest.java | 4 ++-- .../main/java/androidx/media3/extractor/mp4/MetadataUtil.java | 2 +- .../media3/test/utils/robolectric/PlaybackOutput.java | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename libraries/{extractor/src/main/java/androidx/media3/extractor/metadata/mp4 => container/src/main/java/androidx/media3/container}/MdtaMetadataEntry.java (98%) rename libraries/{extractor/src/test/java/androidx/media3/extractor/metadata/mp4 => container/src/test/java/androidx/media3/container}/MdtaMetadataEntryTest.java (96%) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/metadata/mp4/MdtaMetadataEntry.java b/libraries/container/src/main/java/androidx/media3/container/MdtaMetadataEntry.java similarity index 98% rename from libraries/extractor/src/main/java/androidx/media3/extractor/metadata/mp4/MdtaMetadataEntry.java rename to libraries/container/src/main/java/androidx/media3/container/MdtaMetadataEntry.java index ca8dd41acad..694a73ae783 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/metadata/mp4/MdtaMetadataEntry.java +++ b/libraries/container/src/main/java/androidx/media3/container/MdtaMetadataEntry.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package androidx.media3.extractor.metadata.mp4; +package androidx.media3.container; import android.os.Parcel; import android.os.Parcelable; diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/metadata/mp4/MdtaMetadataEntryTest.java b/libraries/container/src/test/java/androidx/media3/container/MdtaMetadataEntryTest.java similarity index 96% rename from libraries/extractor/src/test/java/androidx/media3/extractor/metadata/mp4/MdtaMetadataEntryTest.java rename to libraries/container/src/test/java/androidx/media3/container/MdtaMetadataEntryTest.java index b5ecbf3e710..1243df3e491 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/metadata/mp4/MdtaMetadataEntryTest.java +++ b/libraries/container/src/test/java/androidx/media3/container/MdtaMetadataEntryTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package androidx.media3.extractor.metadata.mp4; +package androidx.media3.container; import static com.google.common.truth.Truth.assertThat; diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MetadataRetrieverTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MetadataRetrieverTest.java index b9b5756503b..9749d7e55e5 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MetadataRetrieverTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MetadataRetrieverTest.java @@ -16,8 +16,8 @@ package androidx.media3.exoplayer; +import static androidx.media3.container.MdtaMetadataEntry.KEY_ANDROID_CAPTURE_FPS; import static androidx.media3.exoplayer.MetadataRetriever.retrieveMetadata; -import static androidx.media3.extractor.metadata.mp4.MdtaMetadataEntry.KEY_ANDROID_CAPTURE_FPS; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; @@ -26,8 +26,8 @@ import androidx.media3.common.C; import androidx.media3.common.MediaItem; import androidx.media3.common.MimeTypes; +import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.exoplayer.source.TrackGroupArray; -import androidx.media3.extractor.metadata.mp4.MdtaMetadataEntry; import androidx.media3.extractor.metadata.mp4.MotionPhotoMetadata; import androidx.media3.extractor.metadata.mp4.SlowMotionData; import androidx.media3.extractor.metadata.mp4.SmtaMetadataEntry; diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/MetadataUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/MetadataUtil.java index 023d573e29b..be8c295220d 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/MetadataUtil.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/MetadataUtil.java @@ -24,13 +24,13 @@ import androidx.media3.common.Metadata; import androidx.media3.common.util.Log; import androidx.media3.common.util.ParsableByteArray; +import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.extractor.GaplessInfoHolder; import androidx.media3.extractor.metadata.id3.ApicFrame; import androidx.media3.extractor.metadata.id3.CommentFrame; import androidx.media3.extractor.metadata.id3.Id3Frame; import androidx.media3.extractor.metadata.id3.InternalFrame; import androidx.media3.extractor.metadata.id3.TextInformationFrame; -import androidx.media3.extractor.metadata.mp4.MdtaMetadataEntry; import com.google.common.collect.ImmutableList; import org.checkerframework.checker.nullness.compatqual.NullableType; diff --git a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/PlaybackOutput.java b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/PlaybackOutput.java index f5f1bfb8ece..4e22bf88c9f 100644 --- a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/PlaybackOutput.java +++ b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/PlaybackOutput.java @@ -25,6 +25,7 @@ import androidx.media3.common.text.CueGroup; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; +import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.extractor.metadata.dvbsi.AppInfoTable; import androidx.media3.extractor.metadata.emsg.EventMessage; @@ -32,7 +33,6 @@ import androidx.media3.extractor.metadata.icy.IcyHeaders; import androidx.media3.extractor.metadata.icy.IcyInfo; import androidx.media3.extractor.metadata.id3.Id3Frame; -import androidx.media3.extractor.metadata.mp4.MdtaMetadataEntry; import androidx.media3.extractor.metadata.mp4.MotionPhotoMetadata; import androidx.media3.extractor.metadata.mp4.SlowMotionData; import androidx.media3.extractor.metadata.mp4.SmtaMetadataEntry; From 992c9aa470669f868cf92057271e43533dbe35f0 Mon Sep 17 00:00:00 2001 From: jbibik Date: Sat, 20 May 2023 18:57:22 +0100 Subject: [PATCH 054/516] Make DrmConfiguration Bundleable PiperOrigin-RevId: 533721679 (cherry picked from commit 5008417c8cd12680a3945945e00399cca05915c5) --- .../androidx/media3/common/MediaItem.java | 81 +++++++++++++++- .../media3/common/util/BundleableUtil.java | 44 +++++++++ .../androidx/media3/common/MediaItemTest.java | 19 ++++ .../common/util/BundleableUtilTest.java | 96 +++++++++++++++++++ 4 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 libraries/common/src/test/java/androidx/media3/common/util/BundleableUtilTest.java diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaItem.java b/libraries/common/src/main/java/androidx/media3/common/MediaItem.java index 4db28ca61fd..0ffcec87493 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaItem.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaItem.java @@ -23,6 +23,7 @@ import androidx.annotation.IntRange; import androidx.annotation.Nullable; import androidx.media3.common.util.Assertions; +import androidx.media3.common.util.BundleableUtil; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; @@ -598,7 +599,7 @@ public MediaItem build() { } /** DRM configuration for a media item. */ - public static final class DrmConfiguration { + public static final class DrmConfiguration implements Bundleable { /** Builder for {@link DrmConfiguration}. */ public static final class Builder { @@ -773,7 +774,6 @@ public Builder setKeySetId(@Nullable byte[] keySetId) { } public DrmConfiguration build() { - return new DrmConfiguration(this); } } @@ -888,6 +888,83 @@ public int hashCode() { result = 31 * result + Arrays.hashCode(keySetId); return result; } + + // Bundleable implementation + + private static final String FIELD_SCHEME = Util.intToStringMaxRadix(0); + private static final String FIELD_LICENSE_URI = Util.intToStringMaxRadix(1); + private static final String FIELD_LICENSE_REQUEST_HEADERS = Util.intToStringMaxRadix(2); + private static final String FIELD_MULTI_SESSION = Util.intToStringMaxRadix(3); + private static final String FIELD_PLAY_CLEAR_CONTENT_WITHOUT_KEY = Util.intToStringMaxRadix(4); + private static final String FIELD_FORCE_DEFAULT_LICENSE_URI = Util.intToStringMaxRadix(5); + private static final String FIELD_FORCED_SESSION_TRACK_TYPES = Util.intToStringMaxRadix(6); + private static final String FIELD_KEY_SET_ID = Util.intToStringMaxRadix(7); + + /** Object that can restore {@link DrmConfiguration} from a {@link Bundle}. */ + @UnstableApi + public static final Creator CREATOR = DrmConfiguration::fromBundle; + + @UnstableApi + private static DrmConfiguration fromBundle(Bundle bundle) { + UUID scheme = UUID.fromString(checkNotNull(bundle.getString(FIELD_SCHEME))); + @Nullable Uri licenseUri = bundle.getParcelable(FIELD_LICENSE_URI); + Bundle licenseMapAsBundle = + BundleableUtil.getBundleWithDefault(bundle, FIELD_LICENSE_REQUEST_HEADERS, Bundle.EMPTY); + ImmutableMap licenseRequestHeaders = + BundleableUtil.bundleToStringImmutableMap(licenseMapAsBundle); + boolean multiSession = bundle.getBoolean(FIELD_MULTI_SESSION, false); + boolean playClearContentWithoutKey = + bundle.getBoolean(FIELD_PLAY_CLEAR_CONTENT_WITHOUT_KEY, false); + boolean forceDefaultLicenseUri = bundle.getBoolean(FIELD_FORCE_DEFAULT_LICENSE_URI, false); + ArrayList<@C.TrackType Integer> forcedSessionTrackTypesArray = + BundleableUtil.getIntegerArrayListWithDefault( + bundle, FIELD_FORCED_SESSION_TRACK_TYPES, new ArrayList<>()); + ImmutableList<@C.TrackType Integer> forcedSessionTrackTypes = + ImmutableList.copyOf(forcedSessionTrackTypesArray); + @Nullable byte[] keySetId = bundle.getByteArray(FIELD_KEY_SET_ID); + + Builder builder = new Builder(scheme); + return builder + .setLicenseUri(licenseUri) + .setLicenseRequestHeaders(licenseRequestHeaders) + .setMultiSession(multiSession) + .setForceDefaultLicenseUri(forceDefaultLicenseUri) + .setPlayClearContentWithoutKey(playClearContentWithoutKey) + .setForcedSessionTrackTypes(forcedSessionTrackTypes) + .setKeySetId(keySetId) + .build(); + } + + @UnstableApi + @Override + public Bundle toBundle() { + Bundle bundle = new Bundle(); + bundle.putString(FIELD_SCHEME, scheme.toString()); + if (licenseUri != null) { + bundle.putParcelable(FIELD_LICENSE_URI, licenseUri); + } + if (!licenseRequestHeaders.isEmpty()) { + bundle.putBundle( + FIELD_LICENSE_REQUEST_HEADERS, BundleableUtil.stringMapToBundle(licenseRequestHeaders)); + } + if (multiSession) { + bundle.putBoolean(FIELD_MULTI_SESSION, multiSession); + } + if (playClearContentWithoutKey) { + bundle.putBoolean(FIELD_PLAY_CLEAR_CONTENT_WITHOUT_KEY, playClearContentWithoutKey); + } + if (forceDefaultLicenseUri) { + bundle.putBoolean(FIELD_FORCE_DEFAULT_LICENSE_URI, forceDefaultLicenseUri); + } + if (!forcedSessionTrackTypes.isEmpty()) { + bundle.putIntegerArrayList( + FIELD_FORCED_SESSION_TRACK_TYPES, new ArrayList<>(forcedSessionTrackTypes)); + } + if (keySetId != null) { + bundle.putByteArray(FIELD_KEY_SET_ID, keySetId); + } + return bundle; + } } /** Configuration for playing back linear ads with a media item. */ diff --git a/libraries/common/src/main/java/androidx/media3/common/util/BundleableUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/BundleableUtil.java index e3451b78dff..fe040458eb2 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/BundleableUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/BundleableUtil.java @@ -23,9 +23,12 @@ import androidx.annotation.Nullable; import androidx.media3.common.Bundleable; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** Utilities for {@link Bundleable}. */ @UnstableApi @@ -94,6 +97,47 @@ public static SparseArray toBundleSparseArray( return sparseArray; } + public static Bundle stringMapToBundle(Map bundleableMap) { + Bundle bundle = new Bundle(); + for (Map.Entry entry : bundleableMap.entrySet()) { + bundle.putString(entry.getKey(), entry.getValue()); + } + return bundle; + } + + public static HashMap bundleToStringHashMap(Bundle bundle) { + HashMap map = new HashMap<>(); + if (bundle == Bundle.EMPTY) { + return map; + } + for (String key : bundle.keySet()) { + @Nullable String value = bundle.getString(key); + if (value != null) { + map.put(key, value); + } + } + return map; + } + + public static ImmutableMap bundleToStringImmutableMap(Bundle bundle) { + if (bundle == Bundle.EMPTY) { + return ImmutableMap.of(); + } + HashMap map = bundleToStringHashMap(bundle); + return ImmutableMap.copyOf(map); + } + + public static Bundle getBundleWithDefault(Bundle bundle, String field, Bundle defaultValue) { + @Nullable Bundle result = bundle.getBundle(field); + return result != null ? result : defaultValue; + } + + public static ArrayList getIntegerArrayListWithDefault( + Bundle bundle, String field, ArrayList defaultValue) { + @Nullable ArrayList result = bundle.getIntegerArrayList(field); + return result != null ? result : defaultValue; + } + /** * Sets the application class loader to the given {@link Bundle} if no class loader is present. * diff --git a/libraries/common/src/test/java/androidx/media3/common/MediaItemTest.java b/libraries/common/src/test/java/androidx/media3/common/MediaItemTest.java index 4df2c9d0b75..c8bfd2d11c6 100644 --- a/libraries/common/src/test/java/androidx/media3/common/MediaItemTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/MediaItemTest.java @@ -247,6 +247,25 @@ public void builderSetDrmUuid_notCalled_throwsIllegalStateException() { .build()); } + @Test + public void createDrmConfigurationInstance_roundTripViaBundle_yieldsEqualInstance() { + MediaItem.DrmConfiguration drmConfiguration = + new MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) + .setLicenseUri(URI_STRING + "/license") + .setLicenseRequestHeaders(ImmutableMap.of("Referer", "http://www.google.com")) + .setMultiSession(true) + .setForceDefaultLicenseUri(true) + .setPlayClearContentWithoutKey(true) + .setForcedSessionTrackTypes(ImmutableList.of(C.TRACK_TYPE_AUDIO)) + .setKeySetId(new byte[] {1, 2, 3}) + .build(); + + MediaItem.DrmConfiguration drmConfigurationFromBundle = + MediaItem.DrmConfiguration.CREATOR.fromBundle(drmConfiguration.toBundle()); + + assertThat(drmConfigurationFromBundle).isEqualTo(drmConfiguration); + } + @Test public void builderSetCustomCacheKey_setsCustomCacheKey() { MediaItem mediaItem = diff --git a/libraries/common/src/test/java/androidx/media3/common/util/BundleableUtilTest.java b/libraries/common/src/test/java/androidx/media3/common/util/BundleableUtilTest.java new file mode 100644 index 00000000000..239485e3c11 --- /dev/null +++ b/libraries/common/src/test/java/androidx/media3/common/util/BundleableUtilTest.java @@ -0,0 +1,96 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.common.util; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Bundle; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link BundleableUtil}. */ +@RunWith(AndroidJUnit4.class) +public class BundleableUtilTest { + + @Test + public void testStringMapToBundle() { + Map originalMap = new HashMap<>(); + originalMap.put("firstKey", "firstValue"); + originalMap.put("secondKey", "repeatedValue"); + originalMap.put("thirdKey", "repeatedValue"); + originalMap.put("", "valueOfEmptyKey"); + + Bundle mapAsBundle = BundleableUtil.stringMapToBundle(originalMap); + Map restoredMap = BundleableUtil.bundleToStringHashMap(mapAsBundle); + + assertThat(restoredMap).isEqualTo(originalMap); + } + + @Test + public void testGetBundleWithDefault() { + Bundle fullInnerBundle = new Bundle(); + fullInnerBundle.putString("0", "123"); + fullInnerBundle.putBoolean("1", false); + fullInnerBundle.putInt("2", 123); + Bundle defaultBundle = new Bundle(); + defaultBundle.putString("0", "I am default"); + Bundle outerBundle = new Bundle(); + outerBundle.putBundle("0", fullInnerBundle); + outerBundle.putBundle("1", Bundle.EMPTY); + outerBundle.putBundle("2", null); + + Bundle restoredInnerBundle = + BundleableUtil.getBundleWithDefault(outerBundle, "0", defaultBundle); + Bundle restoredEmptyBundle = + BundleableUtil.getBundleWithDefault(outerBundle, "1", defaultBundle); + Bundle restoredNullBundle = + BundleableUtil.getBundleWithDefault(outerBundle, "2", defaultBundle); + + assertThat(restoredInnerBundle).isEqualTo(fullInnerBundle); + assertThat(restoredEmptyBundle).isEqualTo(Bundle.EMPTY); + assertThat(restoredNullBundle).isEqualTo(defaultBundle); + } + + @Test + public void testGetIntegerArrayListWithDefault() { + ArrayList normalArray = new ArrayList<>(); + normalArray.add(4); + normalArray.add(8); + normalArray.add(16); + ArrayList emptyArray = new ArrayList<>(); + ArrayList defaultIntegerArray = new ArrayList<>(); + defaultIntegerArray.add(0); + Bundle bundle = new Bundle(); + bundle.putIntegerArrayList("0", normalArray); + bundle.putIntegerArrayList("1", emptyArray); + bundle.putIntegerArrayList("2", null); + + ArrayList restoredIntegerArray = + BundleableUtil.getIntegerArrayListWithDefault(bundle, "0", defaultIntegerArray); + ArrayList restoredEmptyIntegerArray = + BundleableUtil.getIntegerArrayListWithDefault(bundle, "1", defaultIntegerArray); + ArrayList restoredNullIntegerArray = + BundleableUtil.getIntegerArrayListWithDefault(bundle, "2", defaultIntegerArray); + + assertThat(restoredIntegerArray).isEqualTo(normalArray); + assertThat(restoredEmptyIntegerArray).isEqualTo(emptyArray); + assertThat(restoredNullIntegerArray).isEqualTo(defaultIntegerArray); + } +} From 5c42c25ad3090b2488d2b080f2150ee9725e3838 Mon Sep 17 00:00:00 2001 From: ibaker Date: Mon, 22 May 2023 09:04:39 +0100 Subject: [PATCH 055/516] Remove deprecated zero-arg `DefaultTrackSelector` constructor Use `DefaultTrackSelector(Context)` instead. #minor-release PiperOrigin-RevId: 533985937 (cherry picked from commit 2b409da881750328bffd276e53b027a6b1a96d06) --- RELEASENOTES.md | 2 ++ .../exoplayer/trackselection/DefaultTrackSelector.java | 10 ++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ca369970833..1a8830f5c32 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -88,6 +88,8 @@ `copyWithBitrate` and `copyWithVideoSize`, use `Format.buildUpon()` and setter methods instead. * Remove deprecated `ExoPlayer.retry()`, use `prepare()` instead. + * Remove deprecated zero-arg `DefaultTrackSelector` constructor, use + `DefaultTrackSelector(Context)` instead. ### 1.0.2 (2023-05-18) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java index 0e8738b14bd..0782dd976a0 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/DefaultTrackSelector.java @@ -2225,14 +2225,6 @@ public Bundle toBundle() { @GuardedBy("lock") private AudioAttributes audioAttributes; - /** - * @deprecated Use {@link #DefaultTrackSelector(Context)} instead. - */ - @Deprecated - public DefaultTrackSelector() { - this(Parameters.DEFAULT_WITHOUT_CONTEXT, new AdaptiveTrackSelection.Factory()); - } - /** * @param context Any {@link Context}. */ @@ -2260,6 +2252,8 @@ public DefaultTrackSelector(Context context, TrackSelectionParameters parameters * @deprecated Use {@link #DefaultTrackSelector(Context, TrackSelectionParameters, * ExoTrackSelection.Factory)} */ + // TODO: When this constructor is deleted, this.context can be made non-null and all + // null-conditional can be removed (including the warning logging when it's null). @Deprecated public DefaultTrackSelector( TrackSelectionParameters parameters, ExoTrackSelection.Factory trackSelectionFactory) { From aa4f84d89ab3a4cfc024bc4cf6f4da25ffa038b2 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 22 May 2023 11:47:16 +0100 Subject: [PATCH 056/516] Run `clang-format` on GLSL PiperOrigin-RevId: 534015933 (cherry picked from commit 2cbc2c6176128bd70aa1595ec5c461e1a849e915) --- .../shaders/fragment_shader_hsl_es2.glsl | 49 ++++---- .../shaders/fragment_shader_lut_es2.glsl | 108 +++++++++--------- .../shaders/fragment_shader_oetf_es3.glsl | 12 +- .../fragment_shader_transformation_es2.glsl | 6 +- ...hader_transformation_external_yuv_es3.glsl | 61 +++++----- ...hader_transformation_hdr_internal_es3.glsl | 65 +++++------ ...hader_transformation_sdr_external_es2.glsl | 33 +++--- ...hader_transformation_sdr_internal_es2.glsl | 59 +++++----- ...nt_shader_transformation_sdr_oetf_es2.glsl | 38 +++--- .../vertex_shader_transformation_es2.glsl | 3 +- .../vertex_shader_transformation_es3.glsl | 3 +- 11 files changed, 206 insertions(+), 231 deletions(-) diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_hsl_es2.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_hsl_es2.glsl index 9c330f43d37..019527e88db 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_hsl_es2.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_hsl_es2.glsl @@ -34,44 +34,41 @@ varying vec2 vTexSamplingCoord; const float epsilon = 1e-10; vec3 rgbToHcv(vec3 rgb) { - vec4 p = (rgb.g < rgb.b) - ? vec4(rgb.bg, -1.0, 2.0 / 3.0) - : vec4(rgb.gb, 0.0, -1.0 / 3.0); - vec4 q = (rgb.r < p.x) - ? vec4(p.xyw, rgb.r) - : vec4(rgb.r, p.yzx); - float c = q.x - min(q.w, q.y); - float h = abs((q.w - q.y) / (6.0 * c + epsilon) + q.z); - return vec3(h, c, q.x); + vec4 p = (rgb.g < rgb.b) ? vec4(rgb.bg, -1.0, 2.0 / 3.0) + : vec4(rgb.gb, 0.0, -1.0 / 3.0); + vec4 q = (rgb.r < p.x) ? vec4(p.xyw, rgb.r) : vec4(rgb.r, p.yzx); + float c = q.x - min(q.w, q.y); + float h = abs((q.w - q.y) / (6.0 * c + epsilon) + q.z); + return vec3(h, c, q.x); } vec3 rgbToHsl(vec3 rgb) { - vec3 hcv = rgbToHcv(rgb); - float l = hcv.z - hcv.y * 0.5; - float s = hcv.y / (1.0 - abs(l * 2.0 - 1.0) + epsilon); - return vec3(hcv.x, s, l); + vec3 hcv = rgbToHcv(rgb); + float l = hcv.z - hcv.y * 0.5; + float s = hcv.y / (1.0 - abs(l * 2.0 - 1.0) + epsilon); + return vec3(hcv.x, s, l); } vec3 hueToRgb(float hue) { - float r = abs(hue * 6.0 - 3.0) - 1.0; - float g = 2.0 - abs(hue * 6.0 - 2.0); - float b = 2.0 - abs(hue * 6.0 - 4.0); - return clamp(vec3(r, g, b), 0.0, 1.0); + float r = abs(hue * 6.0 - 3.0) - 1.0; + float g = 2.0 - abs(hue * 6.0 - 2.0); + float b = 2.0 - abs(hue * 6.0 - 4.0); + return clamp(vec3(r, g, b), 0.0, 1.0); } vec3 hslToRgb(vec3 hsl) { - vec3 rgb = hueToRgb(hsl.x); - float c = (1.0 - abs(2.0 * hsl.z - 1.0)) * hsl.y; - return (rgb - 0.5) * c + hsl.z; + vec3 rgb = hueToRgb(hsl.x); + float c = (1.0 - abs(2.0 * hsl.z - 1.0)) * hsl.y; + return (rgb - 0.5) * c + hsl.z; } void main() { - vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord); - vec3 hslColor = rgbToHsl(inputColor.rgb); + vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord); + vec3 hslColor = rgbToHsl(inputColor.rgb); - hslColor.x = mod(hslColor.x + uHueAdjustmentDegrees, 1.0); - hslColor.y = clamp(hslColor.y + uSaturationAdjustment, 0.0, 1.0); - hslColor.z = clamp(hslColor.z + uLightnessAdjustment, 0.0, 1.0); + hslColor.x = mod(hslColor.x + uHueAdjustmentDegrees, 1.0); + hslColor.y = clamp(hslColor.y + uSaturationAdjustment, 0.0, 1.0); + hslColor.z = clamp(hslColor.z + uLightnessAdjustment, 0.0, 1.0); - gl_FragColor = vec4(hslToRgb(hslColor), inputColor.a); + gl_FragColor = vec4(hslToRgb(hslColor), inputColor.a); } diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_lut_es2.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_lut_es2.glsl index 7200e43e7bf..f8100ce10e6 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_lut_es2.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_lut_es2.glsl @@ -31,69 +31,67 @@ varying vec2 vTexSamplingCoord; // Applies the color lookup using uLut based on the input colors. vec3 applyLookup(vec3 color) { - // Reminder: Inside OpenGL vector.xyz is the same as vector.rgb. - // Here we use mentions of x and y coordinates to references to - // the position to sample from inside the 2D LUT plane and - // rgb to create the 3D coordinates based on the input colors. + // Reminder: Inside OpenGL vector.xyz is the same as vector.rgb. + // Here we use mentions of x and y coordinates to references to + // the position to sample from inside the 2D LUT plane and + // rgb to create the 3D coordinates based on the input colors. - // To sample from the 3D LUT we interpolate bilinearly twice in the 2D LUT - // to replicate the trilinear interpolation in a 3D LUT. Thus we sample - // from the plane of position redCoordLow and on the plane above. - // redCoordLow points to the lower plane to sample from. - float redCoord = color.r * (uColorLutLength - 1.0); - // Clamping to uColorLutLength - 2 is only needed if redCoord points to the - // most upper plane. In this case there would not be any plane above - // available to sample from. - float redCoordLow = clamp(floor(redCoord), 0.0, uColorLutLength - 2.0); + // To sample from the 3D LUT we interpolate bilinearly twice in the 2D LUT + // to replicate the trilinear interpolation in a 3D LUT. Thus we sample + // from the plane of position redCoordLow and on the plane above. + // redCoordLow points to the lower plane to sample from. + float redCoord = color.r * (uColorLutLength - 1.0); + // Clamping to uColorLutLength - 2 is only needed if redCoord points to the + // most upper plane. In this case there would not be any plane above + // available to sample from. + float redCoordLow = clamp(floor(redCoord), 0.0, uColorLutLength - 2.0); - // lowerY is indexed in two steps. First redCoordLow defines the plane to - // sample from. Next the green color component is added to index the row in - // the found plane. As described in the NVIDIA blog article about LUTs - // https://developer.nvidia.com/gpugems/gpugems2/part-iii-high-quality-rendering/chapter-24-using-lookup-tables-accelerate-color - // (Section 24.2), we sample from color * scale + offset, where offset is - // defined by 1 / (2 * uColorLutLength) and the scale is defined by - // (uColorLutLength - 1.0) / uColorLutLength. + // lowerY is indexed in two steps. First redCoordLow defines the plane to + // sample from. Next the green color component is added to index the row in + // the found plane. As described in the NVIDIA blog article about LUTs + // https://developer.nvidia.com/gpugems/gpugems2/part-iii-high-quality-rendering/chapter-24-using-lookup-tables-accelerate-color + // (Section 24.2), we sample from color * scale + offset, where offset is + // defined by 1 / (2 * uColorLutLength) and the scale is defined by + // (uColorLutLength - 1.0) / uColorLutLength. - // The following derives the equation of lowerY. For this let - // N = uColorLutLenght. The general formula to sample at row y - // is defined as y = N * r + g. - // Using the offset and scale as described in NVIDIA's blog article we get: - // y = offset + (N * r + g) * scale - // y = 1 / (2 * N) + (N * r + g) * (N - 1) / N - // y = 1 / (2 * N) + N * r * (N - 1) / N + g * (N - 1) / N - // We have defined redCoord as r * (N - 1) if we excluded the clamping for - // now, giving us: - // y = 1 / (2 * N) + N * redCoord / N + g * (N - 1) / N - // This simplifies to: - // y = 0.5 / N + (N * redCoord + g * (N - 1)) / N - // y = (0.5 + N * redCoord + g * (N - 1)) / N - // This formula now assumes a coordinate system in the range of [0, N] but - // OpenGL uses a [0, 1] unit coordinate system internally. Thus dividing - // by N gives us the final formula for y: - // y = ((0.5 + N * redCoord + g * (N - 1)) / N) / N - // y = (0.5 + redCoord * N + g * (N - 1)) / (N * N) - float lowerY = - (0.5 - + redCoordLow * uColorLutLength - + color.g * (uColorLutLength - 1.0)) - / (uColorLutLength * uColorLutLength); - // The upperY is the same position moved up by one LUT plane. - float upperY = lowerY + 1.0 / uColorLutLength; + // The following derives the equation of lowerY. For this let + // N = uColorLutLenght. The general formula to sample at row y + // is defined as y = N * r + g. + // Using the offset and scale as described in NVIDIA's blog article we get: + // y = offset + (N * r + g) * scale + // y = 1 / (2 * N) + (N * r + g) * (N - 1) / N + // y = 1 / (2 * N) + N * r * (N - 1) / N + g * (N - 1) / N + // We have defined redCoord as r * (N - 1) if we excluded the clamping for + // now, giving us: + // y = 1 / (2 * N) + N * redCoord / N + g * (N - 1) / N + // This simplifies to: + // y = 0.5 / N + (N * redCoord + g * (N - 1)) / N + // y = (0.5 + N * redCoord + g * (N - 1)) / N + // This formula now assumes a coordinate system in the range of [0, N] but + // OpenGL uses a [0, 1] unit coordinate system internally. Thus dividing + // by N gives us the final formula for y: + // y = ((0.5 + N * redCoord + g * (N - 1)) / N) / N + // y = (0.5 + redCoord * N + g * (N - 1)) / (N * N) + float lowerY = (0.5 + redCoordLow * uColorLutLength + + color.g * (uColorLutLength - 1.0)) / + (uColorLutLength * uColorLutLength); + // The upperY is the same position moved up by one LUT plane. + float upperY = lowerY + 1.0 / uColorLutLength; - // The x position is the blue color channel (x-axis in LUT[R][G][B]). - float x = (0.5 + color.b * (uColorLutLength - 1.0)) / uColorLutLength; + // The x position is the blue color channel (x-axis in LUT[R][G][B]). + float x = (0.5 + color.b * (uColorLutLength - 1.0)) / uColorLutLength; - vec3 lowerRgb = texture2D(uColorLut, vec2(x, lowerY)).rgb; - vec3 upperRgb = texture2D(uColorLut, vec2(x, upperY)).rgb; + vec3 lowerRgb = texture2D(uColorLut, vec2(x, lowerY)).rgb; + vec3 upperRgb = texture2D(uColorLut, vec2(x, upperY)).rgb; - // Linearly interpolate between lowerRgb and upperRgb based on the - // distance of the actual in the plane and the lower sampling position. - return mix(lowerRgb, upperRgb, redCoord - redCoordLow); + // Linearly interpolate between lowerRgb and upperRgb based on the + // distance of the actual in the plane and the lower sampling position. + return mix(lowerRgb, upperRgb, redCoord - redCoordLow); } void main() { - vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord); + vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord); - gl_FragColor.rgb = applyLookup(inputColor.rgb); - gl_FragColor.a = inputColor.a; + gl_FragColor.rgb = applyLookup(inputColor.rgb); + gl_FragColor.a = inputColor.a; } diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_oetf_es3.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_oetf_es3.glsl index d660bfd9286..c10a0766d95 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_oetf_es3.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_oetf_es3.glsl @@ -44,17 +44,15 @@ highp float hlgOetfSingleChannel(highp float linearChannel) { const highp float b = 0.28466892; const highp float c = 0.55991073; - return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel) : - a * log(12.0 * linearChannel - b) + c; + return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel) + : a * log(12.0 * linearChannel - b) + c; } // BT.2100 / BT.2020 HLG OETF. highp vec3 hlgOetf(highp vec3 linearColor) { - return vec3( - hlgOetfSingleChannel(linearColor.r), - hlgOetfSingleChannel(linearColor.g), - hlgOetfSingleChannel(linearColor.b) - ); + return vec3(hlgOetfSingleChannel(linearColor.r), + hlgOetfSingleChannel(linearColor.g), + hlgOetfSingleChannel(linearColor.b)); } // BT.2100 / BT.2020, PQ / ST2084 OETF. diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_es2.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_es2.glsl index 9ebda595269..8759c5d0423 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_es2.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_es2.glsl @@ -23,7 +23,7 @@ uniform mat4 uRgbMatrix; varying vec2 vTexSamplingCoord; void main() { - vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord); - gl_FragColor = uRgbMatrix * vec4(inputColor.rgb, 1); - gl_FragColor.a = inputColor.a; + vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord); + gl_FragColor = uRgbMatrix * vec4(inputColor.rgb, 1); + gl_FragColor.a = inputColor.a; } diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_external_yuv_es3.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_external_yuv_es3.glsl index 84d05c0f979..8d698384cbd 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_external_yuv_es3.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_external_yuv_es3.glsl @@ -66,17 +66,15 @@ highp float hlgEotfSingleChannel(highp float hlgChannel) { const highp float a = 0.17883277; const highp float b = 0.28466892; const highp float c = 0.55991073; - return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0 : - (b + exp((hlgChannel - c) / a)) / 12.0; + return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0 + : (b + exp((hlgChannel - c) / a)) / 12.0; } // BT.2100 / BT.2020 HLG EOTF. highp vec3 hlgEotf(highp vec3 hlgColor) { - return vec3( - hlgEotfSingleChannel(hlgColor.r), - hlgEotfSingleChannel(hlgColor.g), - hlgEotfSingleChannel(hlgColor.b) - ); + return vec3(hlgEotfSingleChannel(hlgColor.r), + hlgEotfSingleChannel(hlgColor.g), + hlgEotfSingleChannel(hlgColor.b)); } // BT.2100 / BT.2020 PQ EOTF. @@ -115,18 +113,17 @@ highp vec3 applyHlgBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) { // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf // Matrix values based on computeXYZMatrix(BT2020Primaries, BT2020WhitePoint) // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/utils/HostColorSpace.cpp;l=200-232;drc=86bd214059cd6150304888a285941bf74af5b687 - const mat3 RGB_TO_XYZ_BT2020 = mat3( - 0.63695805f, 0.26270021f, 0.00000000f, - 0.14461690f, 0.67799807f, 0.02807269f, - 0.16888098f, 0.05930172f, 1.06098506f); + const mat3 RGB_TO_XYZ_BT2020 = + mat3(0.63695805f, 0.26270021f, 0.00000000f, 0.14461690f, 0.67799807f, + 0.02807269f, 0.16888098f, 0.05930172f, 1.06098506f); // Matrix values based on computeXYZMatrix(BT709Primaries, BT709WhitePoint) - const mat3 XYZ_TO_RGB_BT709 = mat3( - 3.24096994f, -0.96924364f, 0.05563008f, - -1.53738318f, 1.87596750f, -0.20397696f, - -0.49861076f, 0.04155506f, 1.05697151f); + const mat3 XYZ_TO_RGB_BT709 = + mat3(3.24096994f, -0.96924364f, 0.05563008f, -1.53738318f, 1.87596750f, + -0.20397696f, -0.49861076f, 0.04155506f, 1.05697151f); // hlgGamma is 1.2 + 0.42 * log10(nominalPeakLuminance/1000); // nominalPeakLuminance was selected to use a 500 as a typical value, used - // in https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/tonemap/tonemap.cpp;drc=7a577450e536aa1e99f229a0cb3d3531c82e8a8d;l=62, + // in + // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/tonemap/tonemap.cpp;drc=7a577450e536aa1e99f229a0cb3d3531c82e8a8d;l=62, // b/199162498#comment35, and // https://www.microsoft.com/applied-sciences/uploads/projects/investigation-of-hdr-vs-tone-mapped-sdr/investigation-of-hdr-vs-tone-mapped-sdr.pdf. const float hlgGamma = 1.0735674018211279; @@ -167,17 +164,15 @@ highp float hlgOetfSingleChannel(highp float linearChannel) { const highp float b = 0.28466892; const highp float c = 0.55991073; - return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel) : - a * log(12.0 * linearChannel - b) + c; + return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel) + : a * log(12.0 * linearChannel - b) + c; } // BT.2100 / BT.2020 HLG OETF. highp vec3 hlgOetf(highp vec3 linearColor) { - return vec3( - hlgOetfSingleChannel(linearColor.r), - hlgOetfSingleChannel(linearColor.g), - hlgOetfSingleChannel(linearColor.b) - ); + return vec3(hlgOetfSingleChannel(linearColor.r), + hlgOetfSingleChannel(linearColor.g), + hlgOetfSingleChannel(linearColor.b)); } // BT.2100 / BT.2020, PQ / ST2084 OETF. @@ -199,17 +194,16 @@ highp vec3 pqOetf(highp vec3 linearColor) { // BT.709 gamma 2.2 OETF for one channel. float gamma22OetfSingleChannel(highp float linearChannel) { - // Reference: - // https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_GAMMA2_2 - return pow(linearChannel, (1.0 / 2.2)); + // Reference: + // https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_GAMMA2_2 + return pow(linearChannel, (1.0 / 2.2)); } // BT.709 gamma 2.2 OETF. vec3 gamma22Oetf(highp vec3 linearColor) { - return vec3( - gamma22OetfSingleChannel(linearColor.r), - gamma22OetfSingleChannel(linearColor.g), - gamma22OetfSingleChannel(linearColor.b)); + return vec3(gamma22OetfSingleChannel(linearColor.r), + gamma22OetfSingleChannel(linearColor.g), + gamma22OetfSingleChannel(linearColor.b)); } // Applies the appropriate OETF to convert linear optical signals to nonlinear @@ -237,9 +231,10 @@ vec3 yuvToRgb(vec3 yuv) { void main() { vec3 srcYuv = texture(uTexSampler, vTexSamplingCoord).xyz; vec3 opticalColorBt2020 = applyEotf(yuvToRgb(srcYuv)); - vec4 opticalColor = (uApplyHdrToSdrToneMapping == 1) - ? vec4(applyBt2020ToBt709Ootf(opticalColorBt2020), 1.0) - : vec4(opticalColorBt2020, 1.0); + vec4 opticalColor = + (uApplyHdrToSdrToneMapping == 1) + ? vec4(applyBt2020ToBt709Ootf(opticalColorBt2020), 1.0) + : vec4(opticalColorBt2020, 1.0); vec4 transformedColors = uRgbMatrix * opticalColor; outColor = vec4(applyOetf(transformedColors.rgb), 1.0); } diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_hdr_internal_es3.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_hdr_internal_es3.glsl index 6b0e377a29a..97273bcf9f4 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_hdr_internal_es3.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_hdr_internal_es3.glsl @@ -58,17 +58,15 @@ highp float hlgEotfSingleChannel(highp float hlgChannel) { const highp float a = 0.17883277; const highp float b = 0.28466892; const highp float c = 0.55991073; - return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0 : - (b + exp((hlgChannel - c) / a)) / 12.0; + return hlgChannel <= 0.5 ? hlgChannel * hlgChannel / 3.0 + : (b + exp((hlgChannel - c) / a)) / 12.0; } // BT.2100 / BT.2020 HLG EOTF. highp vec3 hlgEotf(highp vec3 hlgColor) { - return vec3( - hlgEotfSingleChannel(hlgColor.r), - hlgEotfSingleChannel(hlgColor.g), - hlgEotfSingleChannel(hlgColor.b) - ); + return vec3(hlgEotfSingleChannel(hlgColor.r), + hlgEotfSingleChannel(hlgColor.g), + hlgEotfSingleChannel(hlgColor.b)); } // BT.2100 / BT.2020 PQ EOTF. @@ -107,18 +105,17 @@ highp vec3 applyHlgBt2020ToBt709Ootf(highp vec3 linearRgbBt2020) { // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf // Matrix values based on computeXYZMatrix(BT2020Primaries, BT2020WhitePoint) // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/libs/hwui/utils/HostColorSpace.cpp;l=200-232;drc=86bd214059cd6150304888a285941bf74af5b687 - const mat3 RGB_TO_XYZ_BT2020 = mat3( - 0.63695805f, 0.26270021f, 0.00000000f, - 0.14461690f, 0.67799807f, 0.02807269f, - 0.16888098f, 0.05930172f, 1.06098506f); + const mat3 RGB_TO_XYZ_BT2020 = + mat3(0.63695805f, 0.26270021f, 0.00000000f, 0.14461690f, 0.67799807f, + 0.02807269f, 0.16888098f, 0.05930172f, 1.06098506f); // Matrix values based on computeXYZMatrix(BT709Primaries, BT709WhitePoint) - const mat3 XYZ_TO_RGB_BT709 = mat3( - 3.24096994f, -0.96924364f, 0.05563008f, - -1.53738318f, 1.87596750f, -0.20397696f, - -0.49861076f, 0.04155506f, 1.05697151f); + const mat3 XYZ_TO_RGB_BT709 = + mat3(3.24096994f, -0.96924364f, 0.05563008f, -1.53738318f, 1.87596750f, + -0.20397696f, -0.49861076f, 0.04155506f, 1.05697151f); // hlgGamma is 1.2 + 0.42 * log10(nominalPeakLuminance/1000); // nominalPeakLuminance was selected to use a 500 as a typical value, used - // in https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/tonemap/tonemap.cpp;drc=7a577450e536aa1e99f229a0cb3d3531c82e8a8d;l=62, + // in + // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/tonemap/tonemap.cpp;drc=7a577450e536aa1e99f229a0cb3d3531c82e8a8d;l=62, // b/199162498#comment35, and // https://www.microsoft.com/applied-sciences/uploads/projects/investigation-of-hdr-vs-tone-mapped-sdr/investigation-of-hdr-vs-tone-mapped-sdr.pdf. const float hlgGamma = 1.0735674018211279; @@ -159,17 +156,15 @@ highp float hlgOetfSingleChannel(highp float linearChannel) { const highp float b = 0.28466892; const highp float c = 0.55991073; - return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel) : - a * log(12.0 * linearChannel - b) + c; + return linearChannel <= 1.0 / 12.0 ? sqrt(3.0 * linearChannel) + : a * log(12.0 * linearChannel - b) + c; } // BT.2100 / BT.2020 HLG OETF. highp vec3 hlgOetf(highp vec3 linearColor) { - return vec3( - hlgOetfSingleChannel(linearColor.r), - hlgOetfSingleChannel(linearColor.g), - hlgOetfSingleChannel(linearColor.b) - ); + return vec3(hlgOetfSingleChannel(linearColor.r), + hlgOetfSingleChannel(linearColor.g), + hlgOetfSingleChannel(linearColor.b)); } // BT.2100 / BT.2020, PQ / ST2084 OETF. @@ -191,17 +186,16 @@ highp vec3 pqOetf(highp vec3 linearColor) { // BT.709 gamma 2.2 OETF for one channel. float gamma22OetfSingleChannel(highp float linearChannel) { - // Reference: - // https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_GAMMA2_2 - return pow(linearChannel, (1.0 / 2.2)); + // Reference: + // https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_GAMMA2_2 + return pow(linearChannel, (1.0 / 2.2)); } // BT.709 gamma 2.2 OETF. vec3 gamma22Oetf(highp vec3 linearColor) { - return vec3( - gamma22OetfSingleChannel(linearColor.r), - gamma22OetfSingleChannel(linearColor.g), - gamma22OetfSingleChannel(linearColor.b)); + return vec3(gamma22OetfSingleChannel(linearColor.r), + gamma22OetfSingleChannel(linearColor.g), + gamma22OetfSingleChannel(linearColor.b)); } // Applies the appropriate OETF to convert linear optical signals to nonlinear @@ -222,11 +216,12 @@ highp vec3 applyOetf(highp vec3 linearColor) { } void main() { - vec3 opticalColorBt2020 = applyEotf( - texture(uTexSampler, vTexSamplingCoord).xyz); - vec4 opticalColor = (uApplyHdrToSdrToneMapping == 1) - ? vec4(applyBt2020ToBt709Ootf(opticalColorBt2020), 1.0) - : vec4(opticalColorBt2020, 1.0); + vec3 opticalColorBt2020 = + applyEotf(texture(uTexSampler, vTexSamplingCoord).xyz); + vec4 opticalColor = + (uApplyHdrToSdrToneMapping == 1) + ? vec4(applyBt2020ToBt709Ootf(opticalColorBt2020), 1.0) + : vec4(opticalColorBt2020, 1.0); vec4 transformedColors = uRgbMatrix * opticalColor; outColor = vec4(applyOetf(transformedColors.rgb), 1.0); } diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_external_es2.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_external_es2.glsl index 33a5b197324..30065f55808 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_external_es2.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_external_es2.glsl @@ -13,7 +13,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - // ES 2 fragment shader that: // 1. Samples from an external texture with uTexSampler copying from this // texture to the current output. @@ -39,22 +38,21 @@ const float gamma = 1.0 / inverseGamma; const int GL_FALSE = 0; const int GL_TRUE = 1; -// Transforms a single channel from electrical to optical SDR using the SMPTE +// Transforms a single channel from electrical to optical SDR using the SMPTE // 170M OETF. float smpte170mEotfSingleChannel(float electricalChannel) { // Specification: // https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en return electricalChannel < 0.0812 - ? electricalChannel / 4.500 - : pow((electricalChannel + 0.099) / 1.099, gamma); + ? electricalChannel / 4.500 + : pow((electricalChannel + 0.099) / 1.099, gamma); } // Transforms electrical to optical SDR using the SMPTE 170M EOTF. vec3 smpte170mEotf(vec3 electricalColor) { - return vec3( - smpte170mEotfSingleChannel(electricalColor.r), - smpte170mEotfSingleChannel(electricalColor.g), - smpte170mEotfSingleChannel(electricalColor.b)); + return vec3(smpte170mEotfSingleChannel(electricalColor.r), + smpte170mEotfSingleChannel(electricalColor.g), + smpte170mEotfSingleChannel(electricalColor.b)); } // Transforms a single channel from optical to electrical SDR. @@ -62,16 +60,15 @@ float smpte170mOetfSingleChannel(float opticalChannel) { // Specification: // https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en return opticalChannel < 0.018 - ? opticalChannel * 4.500 - : 1.099 * pow(opticalChannel, inverseGamma) - 0.099; + ? opticalChannel * 4.500 + : 1.099 * pow(opticalChannel, inverseGamma) - 0.099; } // Transforms optical SDR colors to electrical SDR using the SMPTE 170M OETF. vec3 smpte170mOetf(vec3 opticalColor) { - return vec3( - smpte170mOetfSingleChannel(opticalColor.r), - smpte170mOetfSingleChannel(opticalColor.g), - smpte170mOetfSingleChannel(opticalColor.b)); + return vec3(smpte170mOetfSingleChannel(opticalColor.r), + smpte170mOetfSingleChannel(opticalColor.g), + smpte170mOetfSingleChannel(opticalColor.b)); } // Applies the appropriate OETF to convert linear optical signals to nonlinear @@ -80,8 +77,8 @@ highp vec3 applyOetf(highp vec3 linearColor) { // LINT.IfChange(color_transfer) const int COLOR_TRANSFER_LINEAR = 1; const int COLOR_TRANSFER_SDR_VIDEO = 3; - if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR - || uEnableColorTransfer == GL_FALSE) { + if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR || + uEnableColorTransfer == GL_FALSE) { return linearColor; } else if (uOutputColorTransfer == COLOR_TRANSFER_SDR_VIDEO) { return smpte170mOetf(linearColor); @@ -91,8 +88,8 @@ highp vec3 applyOetf(highp vec3 linearColor) { } } -vec3 applyEotf(vec3 electricalColor){ - if (uEnableColorTransfer == GL_TRUE){ +vec3 applyEotf(vec3 electricalColor) { + if (uEnableColorTransfer == GL_TRUE) { return smpte170mEotf(electricalColor); } else if (uEnableColorTransfer == GL_FALSE) { return electricalColor; diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_internal_es2.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_internal_es2.glsl index 4fb21bfec05..7f3d389ff55 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_internal_es2.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_internal_es2.glsl @@ -13,7 +13,6 @@ // See the License for the specific language governing permissions and // limitations under the License. - // ES 2 fragment shader that: // 1. Samples from an input texture created from an internal texture (e.g. a // texture created from a bitmap), with uTexSampler copying from this texture @@ -50,17 +49,15 @@ float srgbEotfSingleChannel(float electricalChannel) { // Specification: // https://developer.android.com/ndk/reference/group/a-data-space#group___a_data_space_1gga2759ad19cae46646cc5f7002758c4a1cac1bef6aa3a72abbf4a651a0bfb117f96 return electricalChannel <= 0.04045 - ? electricalChannel / 12.92 - : pow((electricalChannel + 0.055) / 1.055, 2.4); + ? electricalChannel / 12.92 + : pow((electricalChannel + 0.055) / 1.055, 2.4); } // Transforms electrical to optical SDR using the sRGB EOTF. vec3 srgbEotf(const vec3 electricalColor) { - return vec3( - srgbEotfSingleChannel(electricalColor.r), - srgbEotfSingleChannel(electricalColor.g), - srgbEotfSingleChannel(electricalColor.b) - ); + return vec3(srgbEotfSingleChannel(electricalColor.r), + srgbEotfSingleChannel(electricalColor.g), + srgbEotfSingleChannel(electricalColor.b)); } // Transforms a single channel from electrical to optical SDR using the SMPTE @@ -69,16 +66,15 @@ float smpte170mEotfSingleChannel(float electricalChannel) { // Specification: // https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en return electricalChannel < 0.0812 - ? electricalChannel / 4.500 - : pow((electricalChannel + 0.099) / 1.099, gamma); + ? electricalChannel / 4.500 + : pow((electricalChannel + 0.099) / 1.099, gamma); } // Transforms electrical to optical SDR using the SMPTE 170M EOTF. vec3 smpte170mEotf(vec3 electricalColor) { - return vec3( - smpte170mEotfSingleChannel(electricalColor.r), - smpte170mEotfSingleChannel(electricalColor.g), - smpte170mEotfSingleChannel(electricalColor.b)); + return vec3(smpte170mEotfSingleChannel(electricalColor.r), + smpte170mEotfSingleChannel(electricalColor.g), + smpte170mEotfSingleChannel(electricalColor.b)); } // Transforms a single channel from optical to electrical SDR. @@ -86,23 +82,22 @@ float smpte170mOetfSingleChannel(float opticalChannel) { // Specification: // https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en return opticalChannel < 0.018 - ? opticalChannel * 4.500 - : 1.099 * pow(opticalChannel, inverseGamma) - 0.099; + ? opticalChannel * 4.500 + : 1.099 * pow(opticalChannel, inverseGamma) - 0.099; } // Transforms optical SDR colors to electrical SDR using the SMPTE 170M OETF. vec3 smpte170mOetf(vec3 opticalColor) { - return vec3( - smpte170mOetfSingleChannel(opticalColor.r), - smpte170mOetfSingleChannel(opticalColor.g), - smpte170mOetfSingleChannel(opticalColor.b)); + return vec3(smpte170mOetfSingleChannel(opticalColor.r), + smpte170mOetfSingleChannel(opticalColor.g), + smpte170mOetfSingleChannel(opticalColor.b)); } -// Applies the appropriate EOTF to convert nonlinear electrical signals to linear -// optical signals. Input and output are both normalized to [0, 1]. -vec3 applyEotf(vec3 electricalColor){ - if (uEnableColorTransfer == GL_TRUE){ - if (uInputColorTransfer == COLOR_TRANSFER_SRGB){ - return srgbEotf(electricalColor) ; +// Applies the appropriate EOTF to convert nonlinear electrical signals to +// linear optical signals. Input and output are both normalized to [0, 1]. +vec3 applyEotf(vec3 electricalColor) { + if (uEnableColorTransfer == GL_TRUE) { + if (uInputColorTransfer == COLOR_TRANSFER_SRGB) { + return srgbEotf(electricalColor); } else if (uInputColorTransfer == COLOR_TRANSFER_SDR_VIDEO) { return smpte170mEotf(electricalColor); } else { @@ -120,8 +115,8 @@ vec3 applyEotf(vec3 electricalColor){ // Applies the appropriate OETF to convert linear optical signals to nonlinear // electrical signals. Input and output are both normalized to [0, 1]. highp vec3 applyOetf(highp vec3 linearColor) { - if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR - || uEnableColorTransfer == GL_FALSE) { + if (uOutputColorTransfer == COLOR_TRANSFER_LINEAR || + uEnableColorTransfer == GL_FALSE) { return linearColor; } else if (uOutputColorTransfer == COLOR_TRANSFER_SDR_VIDEO) { return smpte170mOetf(linearColor); @@ -131,8 +126,8 @@ highp vec3 applyOetf(highp vec3 linearColor) { } } -vec2 getAdjustedTexSamplingCoord(vec2 originalTexSamplingCoord){ - if (uInputColorTransfer == COLOR_TRANSFER_SRGB){ +vec2 getAdjustedTexSamplingCoord(vec2 originalTexSamplingCoord) { + if (uInputColorTransfer == COLOR_TRANSFER_SRGB) { // Whereas the Android system uses the top-left corner as (0,0) of the // coordinate system, OpenGL uses the bottom-left corner as (0,0), so the // texture gets flipped. We flip the texture vertically to ensure the @@ -144,8 +139,8 @@ vec2 getAdjustedTexSamplingCoord(vec2 originalTexSamplingCoord){ } void main() { - vec4 inputColor = texture2D( - uTexSampler, getAdjustedTexSamplingCoord(vTexSamplingCoord)); + vec4 inputColor = + texture2D(uTexSampler, getAdjustedTexSamplingCoord(vTexSamplingCoord)); vec3 linearInputColor = applyEotf(inputColor.rgb); vec4 transformedColors = uRgbMatrix * vec4(linearInputColor, 1); diff --git a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_oetf_es2.glsl b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_oetf_es2.glsl index bfa58318512..ad73508bab2 100644 --- a/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_oetf_es2.glsl +++ b/libraries/effect/src/main/assets/shaders/fragment_shader_transformation_sdr_oetf_es2.glsl @@ -30,37 +30,35 @@ uniform int uOutputColorTransfer; const float inverseGamma = 0.4500; -// Transforms a single channel from optical to electrical SDR using the SMPTE +// Transforms a single channel from optical to electrical SDR using the SMPTE // 170M OETF. float smpte170mOetfSingleChannel(float opticalChannel) { - // Specification: - // https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en - return opticalChannel < 0.018 - ? opticalChannel * 4.500 - : 1.099 * pow(opticalChannel, inverseGamma) - 0.099; + // Specification: + // https://www.itu.int/rec/R-REC-BT.1700-0-200502-I/en + return opticalChannel < 0.018 + ? opticalChannel * 4.500 + : 1.099 * pow(opticalChannel, inverseGamma) - 0.099; } // Transforms optical SDR colors to electrical SDR using the SMPTE 170M OETF. vec3 smpte170mOetf(vec3 opticalColor) { - return vec3( - smpte170mOetfSingleChannel(opticalColor.r), - smpte170mOetfSingleChannel(opticalColor.g), - smpte170mOetfSingleChannel(opticalColor.b)); + return vec3(smpte170mOetfSingleChannel(opticalColor.r), + smpte170mOetfSingleChannel(opticalColor.g), + smpte170mOetfSingleChannel(opticalColor.b)); } // BT.709 gamma 2.2 OETF for one channel. float gamma22OetfSingleChannel(highp float linearChannel) { - // Reference: - // https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_gamma22 - return pow(linearChannel, (1.0 / 2.2)); + // Reference: + // https://developer.android.com/reference/android/hardware/DataSpace#TRANSFER_gamma22 + return pow(linearChannel, (1.0 / 2.2)); } // BT.709 gamma 2.2 OETF. vec3 gamma22Oetf(highp vec3 linearColor) { - return vec3( - gamma22OetfSingleChannel(linearColor.r), - gamma22OetfSingleChannel(linearColor.g), - gamma22OetfSingleChannel(linearColor.b)); + return vec3(gamma22OetfSingleChannel(linearColor.r), + gamma22OetfSingleChannel(linearColor.g), + gamma22OetfSingleChannel(linearColor.b)); } // Applies the appropriate OETF to convert linear optical signals to nonlinear @@ -80,8 +78,8 @@ highp vec3 applyOetf(highp vec3 linearColor) { } void main() { - vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord); - vec4 transformedColors = uRgbMatrix * vec4(inputColor.rgb, 1); + vec4 inputColor = texture2D(uTexSampler, vTexSamplingCoord); + vec4 transformedColors = uRgbMatrix * vec4(inputColor.rgb, 1); - gl_FragColor = vec4(applyOetf(transformedColors.rgb), inputColor.a); + gl_FragColor = vec4(applyOetf(transformedColors.rgb), inputColor.a); } diff --git a/libraries/effect/src/main/assets/shaders/vertex_shader_transformation_es2.glsl b/libraries/effect/src/main/assets/shaders/vertex_shader_transformation_es2.glsl index 06164bad5e6..79313ca59c1 100644 --- a/libraries/effect/src/main/assets/shaders/vertex_shader_transformation_es2.glsl +++ b/libraries/effect/src/main/assets/shaders/vertex_shader_transformation_es2.glsl @@ -22,6 +22,7 @@ uniform mat4 uTexTransformationMatrix; varying vec2 vTexSamplingCoord; void main() { gl_Position = uTransformationMatrix * aFramePosition; - vec4 texturePosition = vec4(aFramePosition.x * 0.5 + 0.5, aFramePosition.y * 0.5 + 0.5, 0.0, 1.0); + vec4 texturePosition = vec4(aFramePosition.x * 0.5 + 0.5, + aFramePosition.y * 0.5 + 0.5, 0.0, 1.0); vTexSamplingCoord = (uTexTransformationMatrix * texturePosition).xy; } diff --git a/libraries/effect/src/main/assets/shaders/vertex_shader_transformation_es3.glsl b/libraries/effect/src/main/assets/shaders/vertex_shader_transformation_es3.glsl index c99b31112e8..6615d2b5226 100644 --- a/libraries/effect/src/main/assets/shaders/vertex_shader_transformation_es3.glsl +++ b/libraries/effect/src/main/assets/shaders/vertex_shader_transformation_es3.glsl @@ -22,6 +22,7 @@ uniform mat4 uTexTransformationMatrix; out vec2 vTexSamplingCoord; void main() { gl_Position = uTransformationMatrix * aFramePosition; - vec4 texturePosition = vec4(aFramePosition.x * 0.5 + 0.5, aFramePosition.y * 0.5 + 0.5, 0.0, 1.0); + vec4 texturePosition = vec4(aFramePosition.x * 0.5 + 0.5, + aFramePosition.y * 0.5 + 0.5, 0.0, 1.0); vTexSamplingCoord = (uTexTransformationMatrix * texturePosition).xy; } From 8a9503c01a04a0e55dbbb6c6beb61c65d848479a Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 22 May 2023 11:49:46 +0100 Subject: [PATCH 057/516] Use `Ascii` for conversion to lower case PiperOrigin-RevId: 534016337 (cherry picked from commit b0418f1a2ad04add963aab0c98477dc729cbe86d) --- .../media3/transformer/DefaultAssetLoaderFactory.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultAssetLoaderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultAssetLoaderFactory.java index 1f77e5f3f67..32d9bf499ad 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultAssetLoaderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultAssetLoaderFactory.java @@ -26,8 +26,8 @@ import androidx.media3.common.util.Clock; import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.source.MediaSource; +import com.google.common.base.Ascii; import com.google.common.collect.ImmutableList; -import java.util.Locale; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** The default {@link AssetLoader.Factory} implementation. */ @@ -133,7 +133,7 @@ private static boolean isImage(@Nullable MediaItem.LocalConfiguration localConfi if (fileExtensionStart < 0) { return false; } - String extension = uriPath.substring(fileExtensionStart).toLowerCase(Locale.ENGLISH); + String extension = Ascii.toLowerCase(uriPath.substring(fileExtensionStart)); return supportedImageTypes.contains(extension); } } From 8df047108c1ce510378385492c949507c325050b Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 22 May 2023 12:50:02 +0100 Subject: [PATCH 058/516] Remove unnecessary `volatile` This field is always accessed with the mutex held. PiperOrigin-RevId: 534027096 (cherry picked from commit ea0b94c0eb165f6673a5dd693009ae0b1a76c66d) --- .../java/androidx/media3/effect/FinalShaderProgramWrapper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java index 4805fb10174..35af4c3b332 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java @@ -106,7 +106,8 @@ interface OnInputStreamProcessedListener { @Nullable private OnInputStreamProcessedListener onInputStreamProcessedListener; private boolean frameProcessingStarted; - private volatile boolean outputSurfaceInfoChanged; + @GuardedBy("this") + private boolean outputSurfaceInfoChanged; @GuardedBy("this") @Nullable From 3704c7fc8d45fa300a0cc18104c73879a7e20710 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 22 May 2023 12:50:07 +0100 Subject: [PATCH 059/516] Extend main Player Javadoc The main interface documentation hasn't been updated substantially since 2017 and is missing notes for many of its current features and requirements. Also change the recommendation for implementors from BasePlayer to SimpleBasePlayer to ensure new classes are more likely to cover all of the interface requirements. #minor-release PiperOrigin-RevId: 534027117 (cherry picked from commit 6de6bd9c4f23b87427b7d2c4cb2c330aef5fba49) --- .../java/androidx/media3/common/Player.java | 127 ++++++++++++++++-- 1 file changed, 113 insertions(+), 14 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/Player.java b/libraries/common/src/main/java/androidx/media3/common/Player.java index 0b3ee058178..822cb506ef8 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Player.java +++ b/libraries/common/src/main/java/androidx/media3/common/Player.java @@ -47,26 +47,125 @@ import java.util.List; /** - * A media player interface defining traditional high-level functionality, such as the ability to - * play, pause, seek and query properties of the currently playing media. + * A media player interface defining high-level functionality, such as the ability to play, pause, + * seek and query properties of the currently playing media. * - *

    All methods must be called from a single {@linkplain #getApplicationLooper() application - * thread} unless indicated otherwise. Callbacks in registered listeners are called on the same - * thread. - * - *

    This interface includes some convenience methods that can be implemented by calling other - * methods in the interface. {@link BasePlayer} implements these convenience methods so inheriting - * {@link BasePlayer} is recommended when implementing the interface so that only the minimal set of - * required methods can be implemented. + *

    Player features and usage

    * *

    Some important properties of media players that implement this interface are: * *

      - *
    • They can provide a {@link Timeline} representing the structure of the media being played, - * which can be obtained by calling {@link #getCurrentTimeline()}. - *
    • They can provide a {@link Tracks} defining the currently available tracks and which are - * selected to be rendered, which can be obtained by calling {@link #getCurrentTracks()}. + *
    • All methods must be called from a single {@linkplain #getApplicationLooper() application + * thread} unless indicated otherwise. Callbacks in registered listeners are called on the + * same thread. + *
    • The available functionality can be limited. Player instances provide a set of {@link + * #getAvailableCommands() availabe commands} to signal feature support and users of the + * interface must only call methods if the corresponding {@link Command} is available. + *
    • Users can register {@link Player.Listener} callbacks that get informed about state changes. + *
    • Player instances need to update the visible state immediately after each method call, even + * if the actual changes are handled on background threads or even other devices. This + * simplifies the usage for callers of methods as no asynchronous handling needs to be + * considered. + *
    • Player instances can provide playlist operations, like 'set', 'add', 'remove', 'move' or + * 'replace' of {@link MediaItem} instances. The player can also support {@linkplain + * RepeatMode repeat modes} and shuffling within this playlist. The player provides a {@link + * Timeline} representing the structure of the playlist and all its items, which can be + * obtained by calling {@link #getCurrentTimeline()} + *
    • Player instances can provide seeking within the currently playing item and to other items, + * using the various {@code seek...} methods. + *
    • Player instances can provide {@link Tracks} defining the currently available and selected + * tracks, which can be obtained by calling {@link #getCurrentTracks()}. Users can also modify + * track selection behavior by setting {@link TrackSelectionParameters} with {@link + * #setTrackSelectionParameters}. + *
    • Player instances can provide {@link MediaMetadata} about the currently playing item, which + * can be obtained by calling {@link #getMediaMetadata()}. + *
    • Player instances can provide information about ads in its media structure, for example via + * {@link #isPlayingAd()}. + *
    • Player instances can accept different types of video outputs, like {@link + * #setVideoSurfaceView SurfaceView} or {@link #setVideoTextureView TextureView} for video + * rendering. + *
    • Player instances can handle {@linkplain #setPlaybackSpeed playback speed}, {@linkplain + * #getAudioAttributes audio attributes}, and {@linkplain #setVolume audio volume}. + *
    • Player instances can provide information about the {@linkplain #getDeviceInfo playback + * device}, which may be remote, and allow to change the device's volume. + *
    + * + *

    API stability guarantees

    + * + *

    The majority of the Player interface and its related classes are part of the stable API that + * guarantees backwards-compatibility for users of the API. Only more advances use cases may need to + * rely on {@link UnstableApi} classes and methods that are subject to incompatible changes or even + * removal in a future release. Implementors of the Player interface are not covered by these API + * stability guarantees. + * + *

    Player state

    + * + *

    Users can listen to state changes by adding a {@link Player.Listener} with {@link + * #addListener}. + * + *

    The main elements of the overall player state are: + * + *

      + *
    • Playlist + *
        + *
      • {@link MediaItem} instances can be added with methods like {@link #setMediaItem} to + * define what the player will be playing. + *
      • The current playlist can be obtained via {@link #getCurrentTimeline} and convenience + * methods like {@link #getMediaItemCount} or {@link #getCurrentMediaItem}. + *
      • With an empty playlist, the player can only be in {@link #STATE_IDLE} or {@link + * #STATE_ENDED}. + *
      + *
    • Playback state + *
        + *
      • {@link #STATE_IDLE}: This is the initial state, the state when the player is + * {@linkplain #stop stopped}, and when playback {@linkplain #getPlayerError failed}. + * The player will hold only limited resources in this state. {@link #prepare} must be + * called to transition away from this state. + *
      • {@link #STATE_BUFFERING}: The player is not able to immediately play from its current + * position. This mostly happens because more data needs to be loaded. + *
      • {@link #STATE_READY}: The player is able to immediately play from its current + * position. + *
      • {@link #STATE_ENDED}: The player finished playing all media, or there is no media to + * play. + *
      + *
    • Play/Pause, playback suppression and isPlaying + *
        + *
      • {@linkplain #getPlayWhenReady() playWhenReady}: Indicates the user intention to play. + * It can be set with {@link #play} or {@link #pause}. + *
      • {@linkplain #getPlaybackSuppressionReason() playback suppression}: Defines a reason + * for which playback will be suppressed even if {@linkplain #getPlayWhenReady() + * playWhenReady} is {@code true}. + *
      • {@link #isPlaying()}: Whether the player is playing (that is, its position is + * advancing and media is being presented). This will only be {@code true} if playback + * state is {@link #STATE_READY}, {@linkplain #getPlayWhenReady() playWhenReady} is + * {@code true}, and playback is not suppressed. + *
      + *
    • Playback position + *
        + *
      • {@linkplain #getCurrentMediaItemIndex() media item index}: The index in the playlist. + *
      • {@linkplain #isPlayingAd() ad insertion}: Whether an inserted ad is playing and which + * {@linkplain #getCurrentAdGroupIndex() ad group index} and {@linkplain + * #getCurrentAdIndexInAdGroup() ad index in the group} it belongs to + *
      • {@linkplain #getCurrentPosition() current position}: The current position of the + * playback. This is the same as the {@linkplain #getContentPosition() content position} + * unless an ad is playing, where this indicates the position in the inserted ad. + *
      *
    + * + *

    Note that there are no callbacks for normal playback progression, only for {@linkplain + * Listener#onMediaItemTransition transitions between media items} and other {@linkplain + * Listener#onPositionDiscontinuity position discontinuities}. Code that needs to monitor playback + * progress (for example, an UI progress bar) should query the current position in appropriate + * intervals. + * + *

    Implementing the Player interface

    + * + *

    Implementing the Player interface is complex, as the interface includes many convenience + * methods that need to provide a consistent state and behavior, requires correct handling of + * listeners and available commands, and expects immediate state changes even if methods are + * internally handled asynchronously. For this reason, implementations are advised to inherit {@link + * SimpleBasePlayer} that handles all of these complexities and provides a simpler integration point + * for implementors of the interface. */ public interface Player { From 44ffea2477057e9440fbc79414a395e5030863ad Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 22 May 2023 13:32:46 +0100 Subject: [PATCH 060/516] Keep pending initial position when setting empty playlist MediaControllerImplBase currently drops the pending initial seek position when a user sets an empty playlist. When seeking in empty playlists and setting new empty playlists, the class also drops the the period index (and wrongly assigns zero instead of the windowIndex). #minor-release PiperOrigin-RevId: 534035046 (cherry picked from commit caf1c77af1822d0268d7f028f358374bbe2ba1c8) --- .../session/MediaControllerImplBase.java | 6 +-- .../MediaControllerStateMaskingTest.java | 37 ++++++++++++++++++- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index 7ad7b1d91e3..58870ee93c8 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -1804,7 +1804,7 @@ private void setMediaItemsInternal( } else if (startIndex == C.INDEX_UNSET) { startIndex = playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex; startPositionMs = playerInfo.sessionPositionInfo.positionInfo.positionMs; - if (startIndex >= newTimeline.getWindowCount()) { + if (!newTimeline.isEmpty() && startIndex >= newTimeline.getWindowCount()) { correctedStartIndex = true; startIndex = newTimeline.getFirstWindowIndex(playerInfo.shuffleModeEnabled); startPositionMs = C.TIME_UNSET; @@ -1821,7 +1821,7 @@ private void setMediaItemsInternal( startIndex, /* mediaItem= */ null, /* periodUid= */ null, - /* periodIndex= */ 0, + startIndex, /* positionMs= */ startPositionMs == C.TIME_UNSET ? 0 : startPositionMs, /* contentPositionMs= */ startPositionMs == C.TIME_UNSET ? 0 : startPositionMs, /* adGroupIndex= */ C.INDEX_UNSET, @@ -1987,7 +1987,7 @@ private void seekToInternal(int windowIndex, long positionMs) { windowIndex, /* mediaItem= */ null, /* periodUid= */ null, - /* periodIndex= */ 0, + windowIndex, /* positionMs= */ positionMs == C.TIME_UNSET ? 0 : positionMs, /* contentPositionMs= */ positionMs == C.TIME_UNSET ? 0 : positionMs, /* adGroupIndex= */ C.INDEX_UNSET, diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java index 03642c0d857..086930523f5 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java @@ -1308,7 +1308,7 @@ public void seekTo_withEmptyTimeline() throws Exception { int initialBufferedPosition = 0; int initialTotalBufferedPosition = 0; int testMediaItemIndex = 3; - int testPeriodIndex = initialPeriodIndex; + int testPeriodIndex = 3; long testSeekPositionMs = 3_000; long testPosition = testSeekPositionMs; long testBufferedPosition = testSeekPositionMs; @@ -1576,6 +1576,39 @@ public void onEvents(Player player, Player.Events events) { assertThat(totalBufferedDurationRef.get()).isEqualTo(testTotalBufferedDuration); } + @Test + public void setMediaItems_toEmptyListAndResetPositionFalse_correctMasking() throws Exception { + Bundle playerConfig = + new RemoteMediaSession.MockPlayerConfigBuilder() + .setCurrentMediaItemIndex(2) + .setCurrentPeriodIndex(2) + .setCurrentPosition(8000) + .setContentPosition(8000) + .build(); + remoteSession.setPlayer(playerConfig); + MediaController controller = controllerTestRule.createController(remoteSession.getToken()); + + AtomicReference currentTimelineRef = new AtomicReference<>(); + AtomicInteger currentMediaItemIndexRef = new AtomicInteger(); + AtomicLong currentPositionRef = new AtomicLong(); + AtomicInteger currentPeriodIndexRef = new AtomicInteger(); + threadTestRule + .getHandler() + .postAndSync( + () -> { + controller.setMediaItems(ImmutableList.of(), /* resetPosition= */ false); + currentTimelineRef.set(controller.getCurrentTimeline()); + currentMediaItemIndexRef.set(controller.getCurrentMediaItemIndex()); + currentPositionRef.set((int) controller.getCurrentPosition()); + currentPeriodIndexRef.set(controller.getCurrentPeriodIndex()); + }); + + assertThat(currentPositionRef.get()).isEqualTo(8000); + assertThat(currentTimelineRef.get().isEmpty()).isTrue(); + assertThat(currentMediaItemIndexRef.get()).isEqualTo(2); + assertThat(currentPeriodIndexRef.get()).isEqualTo(2); + } + @Test public void setMediaItems_withStartMediaItemIndexAndStartPosition() throws Exception { int initialMediaItemIndex = 2; @@ -1687,7 +1720,7 @@ public void setMediaItems_withEmptyList() throws Exception { long initialTotalBufferedDuration = 1_200; List testMediaItemList = new ArrayList<>(); int testMediaItemIndex = 1; - int testPeriodIndex = 0; + int testPeriodIndex = 1; long testPosition = 1_000; long testBufferedPosition = 1_000; long testTotalBufferedDuration = 0; From 1b007deca097a6ea1fb88e8d988f1ff48aa3cf98 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 22 May 2023 13:41:39 +0100 Subject: [PATCH 061/516] Untangle PlayerInfo/PlaybackInfo updates The methods in ExoPlayerImpl and MediaControllerImplBase that determine the new PlayerInfo/PlaybackInfo currently have a hard-to-reason-about setup where the method generating the new info accesses other methods that rely on the existing class field instead of working with the passed in PlayerInfo/PlaybackInfo. This prevents reuse of the util methods (e.g. for replaceMediaItems) because they access potentially stale state. This change untangles these methods a bit by making the util methods either static or at least ensure that they don't rely on existing class fields of PlayerInfo/PlaybackInfo. Overall, the change is a complete no-op. #minor-release PiperOrigin-RevId: 534036633 (cherry picked from commit 1fa790348e7e90c987d3385a79a2c7c150cd6824) --- .../media3/exoplayer/ExoPlayerImpl.java | 109 +++++--- .../session/MediaControllerImplBase.java | 263 +++++++++--------- 2 files changed, 201 insertions(+), 171 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 77b0252ce78..8301ab99e1e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -659,16 +659,7 @@ public void addMediaSources(int index, List mediaSources) { setMediaSources(mediaSources, /* resetPosition= */ maskingWindowIndex == C.INDEX_UNSET); return; } - Timeline oldTimeline = getCurrentTimeline(); - pendingOperationAcks++; - List holders = addMediaSourceHolders(index, mediaSources); - Timeline newTimeline = createMaskingTimeline(); - PlaybackInfo newPlaybackInfo = - maskTimelineAndPosition( - playbackInfo, - newTimeline, - getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline)); - internalPlayer.addMediaSources(index, holders, shuffleOrder); + PlaybackInfo newPlaybackInfo = addMediaSourcesInternal(playbackInfo, index, mediaSources); updatePlaybackInfo( newPlaybackInfo, /* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, @@ -691,7 +682,7 @@ public void removeMediaItems(int fromIndex, int toIndex) { // Do nothing. return; } - PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(fromIndex, toIndex); + PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(playbackInfo, fromIndex, toIndex); boolean positionDiscontinuity = !newPlaybackInfo.periodId.periodUid.equals(playbackInfo.periodId.periodUid); updatePlaybackInfo( @@ -725,7 +716,11 @@ public void moveMediaItems(int fromIndex, int toIndex, int newFromIndex) { maskTimelineAndPosition( playbackInfo, newTimeline, - getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline)); + getPeriodPositionUsAfterTimelineChanged( + oldTimeline, + newTimeline, + getCurrentWindowIndexInternal(playbackInfo), + getContentPositionInternal(playbackInfo))); internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder); updatePlaybackInfo( newPlaybackInfo, @@ -1057,7 +1052,7 @@ public int getCurrentPeriodIndex() { @Override public int getCurrentMediaItemIndex() { verifyApplicationThread(); - int currentWindowIndex = getCurrentWindowIndexInternal(); + int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo); return currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex; } @@ -1117,17 +1112,7 @@ public int getCurrentAdIndexInAdGroup() { @Override public long getContentPosition() { verifyApplicationThread(); - if (isPlayingAd()) { - playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period); - return playbackInfo.requestedContentPositionUs == C.TIME_UNSET - ? playbackInfo - .timeline - .getWindow(getCurrentMediaItemIndex(), window) - .getDefaultPositionMs() - : period.getPositionInWindowMs() + Util.usToMs(playbackInfo.requestedContentPositionUs); - } else { - return getCurrentPosition(); - } + return getContentPositionInternal(playbackInfo); } @Override @@ -1853,13 +1838,25 @@ private void stopInternal(@Nullable ExoPlaybackException error) { /* repeatCurrentMediaItem= */ false); } - private int getCurrentWindowIndexInternal() { + private int getCurrentWindowIndexInternal(PlaybackInfo playbackInfo) { if (playbackInfo.timeline.isEmpty()) { return maskingWindowIndex; - } else { - return playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period) - .windowIndex; } + return playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period) + .windowIndex; + } + + private long getContentPositionInternal(PlaybackInfo playbackInfo) { + if (playbackInfo.periodId.isAd()) { + playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period); + return playbackInfo.requestedContentPositionUs == C.TIME_UNSET + ? playbackInfo + .timeline + .getWindow(getCurrentWindowIndexInternal(playbackInfo), window) + .getDefaultPositionMs() + : period.getPositionInWindowMs() + Util.usToMs(playbackInfo.requestedContentPositionUs); + } + return Util.usToMs(getCurrentPositionUsInternal(playbackInfo)); } private long getCurrentPositionUsInternal(PlaybackInfo playbackInfo) { @@ -1874,10 +1871,9 @@ private long getCurrentPositionUsInternal(PlaybackInfo playbackInfo) { if (playbackInfo.periodId.isAd()) { return positionUs; - } else { - return periodPositionUsToWindowPositionUs( - playbackInfo.timeline, playbackInfo.periodId, positionUs); } + return periodPositionUsToWindowPositionUs( + playbackInfo.timeline, playbackInfo.periodId, positionUs); } private List createMediaSources(List mediaItems) { @@ -2276,7 +2272,7 @@ private void setMediaSourcesInternal( int startWindowIndex, long startPositionMs, boolean resetToDefaultPosition) { - int currentWindowIndex = getCurrentWindowIndexInternal(); + int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo); long currentPositionMs = getCurrentPosition(); pendingOperationAcks++; if (!mediaSourceHolderSnapshots.isEmpty()) { @@ -2347,9 +2343,30 @@ private List addMediaSourceHolders( return holders; } - private PlaybackInfo removeMediaItemsInternal(int fromIndex, int toIndex) { - int currentIndex = getCurrentMediaItemIndex(); - Timeline oldTimeline = getCurrentTimeline(); + private PlaybackInfo addMediaSourcesInternal( + PlaybackInfo playbackInfo, int index, List mediaSources) { + Timeline oldTimeline = playbackInfo.timeline; + pendingOperationAcks++; + List holders = addMediaSourceHolders(index, mediaSources); + Timeline newTimeline = createMaskingTimeline(); + PlaybackInfo newPlaybackInfo = + maskTimelineAndPosition( + playbackInfo, + newTimeline, + getPeriodPositionUsAfterTimelineChanged( + oldTimeline, + newTimeline, + getCurrentWindowIndexInternal(playbackInfo), + getContentPositionInternal(playbackInfo))); + internalPlayer.addMediaSources(index, holders, shuffleOrder); + return newPlaybackInfo; + } + + private PlaybackInfo removeMediaItemsInternal( + PlaybackInfo playbackInfo, int fromIndex, int toIndex) { + int currentIndex = getCurrentWindowIndexInternal(playbackInfo); + long contentPositionMs = getContentPositionInternal(playbackInfo); + Timeline oldTimeline = playbackInfo.timeline; int currentMediaSourceCount = mediaSourceHolderSnapshots.size(); pendingOperationAcks++; removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex); @@ -2358,7 +2375,8 @@ private PlaybackInfo removeMediaItemsInternal(int fromIndex, int toIndex) { maskTimelineAndPosition( playbackInfo, newTimeline, - getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline)); + getPeriodPositionUsAfterTimelineChanged( + oldTimeline, newTimeline, currentIndex, contentPositionMs)); // Player transitions to STATE_ENDED if the current index is part of the removed tail. final boolean transitionsToEnded = newPlaybackInfo.playbackState != STATE_IDLE @@ -2387,7 +2405,9 @@ private Timeline createMaskingTimeline() { private PlaybackInfo maskTimelineAndPosition( PlaybackInfo playbackInfo, Timeline timeline, @Nullable Pair periodPositionUs) { checkArgument(timeline.isEmpty() || periodPositionUs != null); + // Get the old timeline and position before updating playbackInfo. Timeline oldTimeline = playbackInfo.timeline; + long oldContentPositionMs = getContentPositionInternal(playbackInfo); // Mask the timeline. playbackInfo = playbackInfo.copyWithTimeline(timeline); @@ -2415,7 +2435,7 @@ private PlaybackInfo maskTimelineAndPosition( MediaPeriodId newPeriodId = playingPeriodChanged ? new MediaPeriodId(periodPositionUs.first) : playbackInfo.periodId; long newContentPositionUs = periodPositionUs.second; - long oldContentPositionUs = Util.msToUs(getContentPosition()); + long oldContentPositionUs = Util.msToUs(oldContentPositionMs); if (!oldTimeline.isEmpty()) { oldContentPositionUs -= oldTimeline.getPeriodByUid(oldPeriodUid, period).getPositionInWindowUs(); @@ -2491,20 +2511,21 @@ private PlaybackInfo maskTimelineAndPosition( @Nullable private Pair getPeriodPositionUsAfterTimelineChanged( - Timeline oldTimeline, Timeline newTimeline) { - long currentPositionMs = getContentPosition(); + Timeline oldTimeline, + Timeline newTimeline, + int currentWindowIndexInternal, + long contentPositionMs) { if (oldTimeline.isEmpty() || newTimeline.isEmpty()) { boolean isCleared = !oldTimeline.isEmpty() && newTimeline.isEmpty(); return maskWindowPositionMsOrGetPeriodPositionUs( newTimeline, - isCleared ? C.INDEX_UNSET : getCurrentWindowIndexInternal(), - isCleared ? C.TIME_UNSET : currentPositionMs); + isCleared ? C.INDEX_UNSET : currentWindowIndexInternal, + isCleared ? C.TIME_UNSET : contentPositionMs); } - int currentMediaItemIndex = getCurrentMediaItemIndex(); @Nullable Pair oldPeriodPositionUs = oldTimeline.getPeriodPositionUs( - window, period, currentMediaItemIndex, Util.msToUs(currentPositionMs)); + window, period, currentWindowIndexInternal, Util.msToUs(contentPositionMs)); Object periodUid = castNonNull(oldPeriodPositionUs).first; if (newTimeline.getIndexOfPeriod(periodUid) != C.INDEX_UNSET) { // The old period position is still available in the new timeline. @@ -2556,7 +2577,7 @@ private long periodPositionUsToWindowPositionUs( } private PlayerMessage createMessageInternal(Target target) { - int currentWindowIndex = getCurrentWindowIndexInternal(); + int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo); return new PlayerMessage( internalPlayer, target, diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index 58870ee93c8..cfe1a1175f5 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -920,7 +920,19 @@ private void addMediaItemsInternal(int index, List mediaItems) { } // Add media items to the end of the timeline if the index exceeds the window count. index = min(index, playerInfo.timeline.getWindowCount()); + PlayerInfo newPlayerInfo = maskPlaybackInfoForAddedItems(playerInfo, index, mediaItems); + updatePlayerInfo( + newPlayerInfo, + /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* positionDiscontinuity= */ false, + /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, + /* mediaItemTransition= */ playerInfo.timeline.isEmpty(), + Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + } + private static PlayerInfo maskPlaybackInfoForAddedItems( + PlayerInfo playerInfo, int index, List mediaItems) { Timeline oldTimeline = playerInfo.timeline; List newWindows = new ArrayList<>(); List newPeriods = new ArrayList<>(); @@ -948,21 +960,12 @@ private void addMediaItemsInternal(int index, List mediaItems) { ? playerInfo.sessionPositionInfo.positionInfo.periodIndex + mediaItems.size() : playerInfo.sessionPositionInfo.positionInfo.periodIndex; } - PlayerInfo newPlayerInfo = - maskTimelineAndPositionInfo( - playerInfo, - newTimeline, - newMediaItemIndex, - newPeriodIndex, - Player.DISCONTINUITY_REASON_INTERNAL); - updatePlayerInfo( - newPlayerInfo, - /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ false, - /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, - /* mediaItemTransition= */ oldTimeline.isEmpty(), - Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + return maskTimelineAndPositionInfo( + playerInfo, + newTimeline, + newMediaItemIndex, + newPeriodIndex, + Player.DISCONTINUITY_REASON_INTERNAL); } @Override @@ -1004,13 +1007,29 @@ public void clearMediaItems() { } private void removeMediaItemsInternal(int fromIndex, int toIndex) { - Timeline oldTimeline = playerInfo.timeline; int playlistSize = playerInfo.timeline.getWindowCount(); toIndex = min(toIndex, playlistSize); - if (fromIndex >= playlistSize || fromIndex == toIndex) { + if (fromIndex >= playlistSize || fromIndex == toIndex || playlistSize == 0) { return; } + boolean currentItemRemoved = + getCurrentMediaItemIndex() >= fromIndex && getCurrentMediaItemIndex() < toIndex; + PlayerInfo newPlayerInfo = maskPlayerInfoForRemovedItems(playerInfo, fromIndex, toIndex); + updatePlayerInfo( + newPlayerInfo, + /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* positionDiscontinuity= */ currentItemRemoved, + Player.DISCONTINUITY_REASON_REMOVE, + /* mediaItemTransition= */ playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex + >= fromIndex + && playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex < toIndex, + Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + } + private static PlayerInfo maskPlayerInfoForRemovedItems( + PlayerInfo playerInfo, int fromIndex, int toIndex) { + Timeline oldTimeline = playerInfo.timeline; List newWindows = new ArrayList<>(); List newPeriods = new ArrayList<>(); for (int i = 0; i < oldTimeline.getWindowCount(); i++) { @@ -1021,124 +1040,109 @@ private void removeMediaItemsInternal(int fromIndex, int toIndex) { rebuildPeriods(oldTimeline, newWindows, newPeriods); Timeline newTimeline = createMaskingTimeline(newWindows, newPeriods); - int oldMediaItemIndex = getCurrentMediaItemIndex(); + int oldMediaItemIndex = getCurrentMediaItemIndexInternal(playerInfo); int newMediaItemIndex = oldMediaItemIndex; int oldPeriodIndex = playerInfo.sessionPositionInfo.positionInfo.periodIndex; int newPeriodIndex = oldPeriodIndex; - boolean currentItemRemoved = - getCurrentMediaItemIndex() >= fromIndex && getCurrentMediaItemIndex() < toIndex; Window window = new Window(); - if (oldTimeline.isEmpty()) { - // No masking required. Just forwarding command to session. + boolean currentItemRemoved = oldMediaItemIndex >= fromIndex && oldMediaItemIndex < toIndex; + if (newTimeline.isEmpty()) { + newMediaItemIndex = C.INDEX_UNSET; + newPeriodIndex = 0; } else { - if (newTimeline.isEmpty()) { - newMediaItemIndex = C.INDEX_UNSET; - newPeriodIndex = 0; - } else { - if (currentItemRemoved) { - int oldNextMediaItemIndex = - resolveSubsequentMediaItemIndex( - getRepeatMode(), - getShuffleModeEnabled(), - oldMediaItemIndex, - oldTimeline, - fromIndex, - toIndex); - if (oldNextMediaItemIndex == C.INDEX_UNSET) { - newMediaItemIndex = newTimeline.getFirstWindowIndex(getShuffleModeEnabled()); - } else if (oldNextMediaItemIndex >= toIndex) { - newMediaItemIndex = oldNextMediaItemIndex - (toIndex - fromIndex); - } else { - newMediaItemIndex = oldNextMediaItemIndex; - } - newPeriodIndex = newTimeline.getWindow(newMediaItemIndex, window).firstPeriodIndex; - } else if (oldMediaItemIndex >= toIndex) { - newMediaItemIndex -= (toIndex - fromIndex); - newPeriodIndex = - getNewPeriodIndexWithoutRemovedPeriods( - oldTimeline, oldPeriodIndex, fromIndex, toIndex); - } - } - - PlayerInfo newPlayerInfo; if (currentItemRemoved) { - PositionInfo newPositionInfo; - if (newMediaItemIndex == C.INDEX_UNSET) { - newPositionInfo = SessionPositionInfo.DEFAULT_POSITION_INFO; - newPlayerInfo = - maskTimelineAndPositionInfo( - playerInfo, - newTimeline, - newPositionInfo, - SessionPositionInfo.DEFAULT, - Player.DISCONTINUITY_REASON_REMOVE); + int oldNextMediaItemIndex = + resolveSubsequentMediaItemIndex( + playerInfo.repeatMode, + playerInfo.shuffleModeEnabled, + oldMediaItemIndex, + oldTimeline, + fromIndex, + toIndex); + if (oldNextMediaItemIndex == C.INDEX_UNSET) { + newMediaItemIndex = newTimeline.getFirstWindowIndex(playerInfo.shuffleModeEnabled); + } else if (oldNextMediaItemIndex >= toIndex) { + newMediaItemIndex = oldNextMediaItemIndex - (toIndex - fromIndex); } else { - Window newWindow = newTimeline.getWindow(newMediaItemIndex, new Window()); - long defaultPositionMs = newWindow.getDefaultPositionMs(); - long durationMs = newWindow.getDurationMs(); - newPositionInfo = - new PositionInfo( - /* windowUid= */ null, - newMediaItemIndex, - newWindow.mediaItem, - /* periodUid= */ null, - newPeriodIndex, - /* positionMs= */ defaultPositionMs, - /* contentPositionMs= */ defaultPositionMs, - /* adGroupIndex= */ C.INDEX_UNSET, - /* adIndexInAdGroup= */ C.INDEX_UNSET); - newPlayerInfo = - maskTimelineAndPositionInfo( - playerInfo, - newTimeline, - newPositionInfo, - new SessionPositionInfo( - newPositionInfo, - /* isPlayingAd= */ false, - /* eventTimeMs= */ SystemClock.elapsedRealtime(), - /* durationMs= */ durationMs, - /* bufferedPositionMs= */ defaultPositionMs, - /* bufferedPercentage= */ calculateBufferedPercentage( - defaultPositionMs, durationMs), - /* totalBufferedDurationMs= */ 0, - /* currentLiveOffsetMs= */ C.TIME_UNSET, - /* contentDurationMs= */ durationMs, - /* contentBufferedPositionMs= */ defaultPositionMs), - Player.DISCONTINUITY_REASON_REMOVE); + newMediaItemIndex = oldNextMediaItemIndex; } - } else { + newPeriodIndex = newTimeline.getWindow(newMediaItemIndex, window).firstPeriodIndex; + } else if (oldMediaItemIndex >= toIndex) { + newMediaItemIndex -= (toIndex - fromIndex); + newPeriodIndex = + getNewPeriodIndexWithoutRemovedPeriods(oldTimeline, oldPeriodIndex, fromIndex, toIndex); + } + } + + PlayerInfo newPlayerInfo; + if (currentItemRemoved) { + PositionInfo newPositionInfo; + if (newMediaItemIndex == C.INDEX_UNSET) { + newPositionInfo = SessionPositionInfo.DEFAULT_POSITION_INFO; newPlayerInfo = maskTimelineAndPositionInfo( playerInfo, newTimeline, + newPositionInfo, + SessionPositionInfo.DEFAULT, + Player.DISCONTINUITY_REASON_REMOVE); + } else { + Window newWindow = newTimeline.getWindow(newMediaItemIndex, new Window()); + long defaultPositionMs = newWindow.getDefaultPositionMs(); + long durationMs = newWindow.getDurationMs(); + newPositionInfo = + new PositionInfo( + /* windowUid= */ null, newMediaItemIndex, + newWindow.mediaItem, + /* periodUid= */ null, newPeriodIndex, - Player.DISCONTINUITY_REASON_REMOVE); - } - - // Player transitions to Player.STATE_ENDED if the current index is part of the removed tail. - final boolean transitionsToEnded = - newPlayerInfo.playbackState != Player.STATE_IDLE - && newPlayerInfo.playbackState != Player.STATE_ENDED - && fromIndex < toIndex - && toIndex == oldTimeline.getWindowCount() - && getCurrentMediaItemIndex() >= fromIndex; - if (transitionsToEnded) { + /* positionMs= */ defaultPositionMs, + /* contentPositionMs= */ defaultPositionMs, + /* adGroupIndex= */ C.INDEX_UNSET, + /* adIndexInAdGroup= */ C.INDEX_UNSET); newPlayerInfo = - newPlayerInfo.copyWithPlaybackState(Player.STATE_ENDED, /* playerError= */ null); + maskTimelineAndPositionInfo( + playerInfo, + newTimeline, + newPositionInfo, + new SessionPositionInfo( + newPositionInfo, + /* isPlayingAd= */ false, + /* eventTimeMs= */ SystemClock.elapsedRealtime(), + /* durationMs= */ durationMs, + /* bufferedPositionMs= */ defaultPositionMs, + /* bufferedPercentage= */ calculateBufferedPercentage( + defaultPositionMs, durationMs), + /* totalBufferedDurationMs= */ 0, + /* currentLiveOffsetMs= */ C.TIME_UNSET, + /* contentDurationMs= */ durationMs, + /* contentBufferedPositionMs= */ defaultPositionMs), + Player.DISCONTINUITY_REASON_REMOVE); } + } else { + newPlayerInfo = + maskTimelineAndPositionInfo( + playerInfo, + newTimeline, + newMediaItemIndex, + newPeriodIndex, + Player.DISCONTINUITY_REASON_REMOVE); + } - updatePlayerInfo( - newPlayerInfo, - /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ currentItemRemoved, - Player.DISCONTINUITY_REASON_REMOVE, - /* mediaItemTransition= */ playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex - >= fromIndex - && playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex < toIndex, - Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + // Player transitions to Player.STATE_ENDED if the current index is part of the removed tail. + final boolean transitionsToEnded = + newPlayerInfo.playbackState != Player.STATE_IDLE + && newPlayerInfo.playbackState != Player.STATE_ENDED + && fromIndex < toIndex + && toIndex == oldTimeline.getWindowCount() + && oldMediaItemIndex >= fromIndex; + if (transitionsToEnded) { + newPlayerInfo = + newPlayerInfo.copyWithPlaybackState(Player.STATE_ENDED, /* playerError= */ null); } + + return newPlayerInfo; } @Override @@ -1176,9 +1180,7 @@ public int getCurrentPeriodIndex() { @Override public int getCurrentMediaItemIndex() { - return playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex == C.INDEX_UNSET - ? 0 - : playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex; + return getCurrentMediaItemIndexInternal(playerInfo); } // TODO(b/184479406): Get the index directly from Player rather than Timeline. @@ -1773,7 +1775,7 @@ public MediaBrowserCompat getBrowserCompat() { return null; } - private Timeline createMaskingTimeline(List windows, List periods) { + private static Timeline createMaskingTimeline(List windows, List periods) { return new RemotableTimeline( new ImmutableList.Builder().addAll(windows).build(), new ImmutableList.Builder().addAll(periods).build(), @@ -2751,7 +2753,7 @@ private PeriodInfo getPeriodInfo(Timeline timeline, int windowIndex, long window } @Nullable - private PeriodInfo getPeriodInfo( + private static PeriodInfo getPeriodInfo( Timeline timeline, Window window, Period period, int windowIndex, long windowPositionUs) { checkIndex(windowIndex, 0, timeline.getWindowCount()); timeline.getWindow(windowIndex, window); @@ -2773,7 +2775,13 @@ private PeriodInfo getPeriodInfo( return new PeriodInfo(periodIndex, periodPositionUs); } - private PlayerInfo maskTimelineAndPositionInfo( + private static int getCurrentMediaItemIndexInternal(PlayerInfo playerInfo) { + return playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex == C.INDEX_UNSET + ? 0 + : playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex; + } + + private static PlayerInfo maskTimelineAndPositionInfo( PlayerInfo playerInfo, Timeline timeline, int newMediaItemIndex, @@ -2808,7 +2816,7 @@ private PlayerInfo maskTimelineAndPositionInfo( discontinuityReason); } - private PlayerInfo maskTimelineAndPositionInfo( + private static PlayerInfo maskTimelineAndPositionInfo( PlayerInfo playerInfo, Timeline timeline, PositionInfo newPositionInfo, @@ -2853,14 +2861,15 @@ private void maybeUpdateCurrentPositionMs() { currentPositionMs = estimatedPositionMs; } - private Period getPeriodWithNewWindowIndex(Timeline timeline, int periodIndex, int windowIndex) { + private static Period getPeriodWithNewWindowIndex( + Timeline timeline, int periodIndex, int windowIndex) { Period period = new Period(); timeline.getPeriod(periodIndex, period); period.windowIndex = windowIndex; return period; } - private int getNewPeriodIndexWithoutRemovedPeriods( + private static int getNewPeriodIndexWithoutRemovedPeriods( Timeline timeline, int oldPeriodIndex, int fromIndex, int toIndex) { if (oldPeriodIndex == C.INDEX_UNSET) { return oldPeriodIndex; @@ -2906,7 +2915,7 @@ private static Period createNewPeriod(int windowIndex) { /* isPlaceholder= */ true); } - private void rebuildPeriods( + private static void rebuildPeriods( Timeline oldTimeline, List newWindows, List newPeriods) { for (int i = 0; i < newWindows.size(); i++) { Window window = newWindows.get(i); From 5cdbd5975620492c0111c78dd209d8474c77565c Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 22 May 2023 13:53:52 +0100 Subject: [PATCH 062/516] Improve handling of adding items to empty playlist in MediaController This is a follow-up to https://github.com/androidx/media/commit/99dac0be0f522586ca41911ca266cc5761f9dc1a where we made the same change in ExoPlayerImpl and SimpleBasePlayer, but for consistency it makes sense to also update the masking code in MediaControllerImplBase to assume the same logic. Note: MediaControllerImplLegacy already handles this case via setMediaItems and doesn't need to be updated further. #minor-release PiperOrigin-RevId: 534038759 (cherry picked from commit 33af245465ac9a8441a58a6fd6bf75c34e2efa26) --- .../session/MediaControllerImplBase.java | 10 ++ .../MediaControllerStateMaskingTest.java | 102 ++++++++++++++++-- 2 files changed, 106 insertions(+), 6 deletions(-) diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index cfe1a1175f5..8b037dfde99 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -918,6 +918,16 @@ private void addMediaItemsInternal(int index, List mediaItems) { if (mediaItems.isEmpty()) { return; } + if (playerInfo.timeline.isEmpty()) { + // Handle initial items in a playlist as a set operation to ensure state changes and initial + // position are updated correctly. + setMediaItemsInternal( + mediaItems, + /* startIndex= */ C.INDEX_UNSET, + /* startPositionMs= */ C.TIME_UNSET, + /* resetToDefaultPosition= */ false); + return; + } // Add media items to the end of the timeline if the index exceeds the window count. index = min(index, playerInfo.timeline.getWindowCount()); PlayerInfo newPlayerInfo = maskPlaybackInfoForAddedItems(playerInfo, index, mediaItems); diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java index 086930523f5..15d3dd5a65f 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerStateMaskingTest.java @@ -1833,16 +1833,21 @@ public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) { } @Test - public void addMediaItems_toEmptyTimeline() throws Exception { + public void addMediaItems_withIdleStateAndEmptyTimeline() throws Exception { int testMediaItemCount = 2; - int testCurrentMediaItemIndex = 0; - int testNextMediaItemIndex = 1; - int testPreviousMediaItemIndex = C.INDEX_UNSET; - int testCurrentPeriodIndex = 0; + int testCurrentMediaItemIndex = 1; + int testNextMediaItemIndex = C.INDEX_UNSET; + int testPreviousMediaItemIndex = 0; + int testCurrentPeriodIndex = 1; List testMediaItems = createMediaItems(testMediaItemCount); MediaItem testMediaItem = testMediaItems.get(testCurrentPeriodIndex); - Bundle playerConfig = new RemoteMediaSession.MockPlayerConfigBuilder().build(); + Bundle playerConfig = + new RemoteMediaSession.MockPlayerConfigBuilder() + .setPlaybackState(Player.STATE_IDLE) + .setCurrentMediaItemIndex(1) + .setCurrentPeriodIndex(1) + .build(); remoteSession.setPlayer(playerConfig); MediaController controller = controllerTestRule.createController(remoteSession.getToken()); @@ -1876,6 +1881,7 @@ public void onEvents(Player player, Player.Events events) { AtomicInteger nextMediaItemIndexRef = new AtomicInteger(); AtomicInteger previousMediaItemIndexRef = new AtomicInteger(); AtomicInteger currentPeriodIndexRef = new AtomicInteger(); + AtomicInteger playbackStateRef = new AtomicInteger(); threadTestRule .getHandler() .postAndSync( @@ -1885,6 +1891,7 @@ public void onEvents(Player player, Player.Events events) { nextMediaItemIndexRef.set(controller.getNextMediaItemIndex()); previousMediaItemIndexRef.set(controller.getPreviousMediaItemIndex()); currentPeriodIndexRef.set(controller.getCurrentPeriodIndex()); + playbackStateRef.set(controller.getPlaybackState()); }); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); @@ -1901,6 +1908,89 @@ public void onEvents(Player player, Player.Events events) { assertThat(previousMediaItemIndexRef.get()).isEqualTo(testPreviousMediaItemIndex); assertThat(currentPeriodIndexRef.get()).isEqualTo(testCurrentPeriodIndex); assertThat(newMediaItemRef.get()).isEqualTo(testMediaItem); + assertThat(playbackStateRef.get()).isEqualTo(Player.STATE_IDLE); + } + + @Test + public void addMediaItems_withEndedStateAndEmptyTimeline() throws Exception { + int testMediaItemCount = 2; + int testCurrentMediaItemIndex = 1; + int testNextMediaItemIndex = C.INDEX_UNSET; + int testPreviousMediaItemIndex = 0; + int testCurrentPeriodIndex = 1; + List testMediaItems = createMediaItems(testMediaItemCount); + MediaItem testMediaItem = testMediaItems.get(testCurrentPeriodIndex); + + Bundle playerConfig = + new RemoteMediaSession.MockPlayerConfigBuilder() + .setPlaybackState(Player.STATE_ENDED) + .setCurrentMediaItemIndex(1) + .setCurrentPeriodIndex(1) + .build(); + remoteSession.setPlayer(playerConfig); + + MediaController controller = controllerTestRule.createController(remoteSession.getToken()); + CountDownLatch latch = new CountDownLatch(3); + AtomicReference newTimelineRef = new AtomicReference<>(); + AtomicReference onEventsRef = new AtomicReference<>(); + AtomicReference newMediaItemRef = new AtomicReference<>(); + Player.Listener listener = + new Player.Listener() { + @Override + public void onTimelineChanged(Timeline timeline, int reason) { + newTimelineRef.set(timeline); + latch.countDown(); + } + + @Override + public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) { + newMediaItemRef.set(mediaItem); + latch.countDown(); + } + + @Override + public void onEvents(Player player, Player.Events events) { + onEventsRef.set(events); + latch.countDown(); + } + }; + threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener)); + + AtomicInteger currentMediaItemIndexRef = new AtomicInteger(); + AtomicInteger nextMediaItemIndexRef = new AtomicInteger(); + AtomicInteger previousMediaItemIndexRef = new AtomicInteger(); + AtomicInteger currentPeriodIndexRef = new AtomicInteger(); + AtomicInteger playbackStateRef = new AtomicInteger(); + threadTestRule + .getHandler() + .postAndSync( + () -> { + controller.addMediaItems(testMediaItems); + currentMediaItemIndexRef.set(controller.getCurrentMediaItemIndex()); + nextMediaItemIndexRef.set(controller.getNextMediaItemIndex()); + previousMediaItemIndexRef.set(controller.getPreviousMediaItemIndex()); + currentPeriodIndexRef.set(controller.getCurrentPeriodIndex()); + playbackStateRef.set(controller.getPlaybackState()); + }); + + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + assertTimeline( + newTimelineRef.get(), + testMediaItemCount, + testCurrentMediaItemIndex, + /* testFirstPeriodIndex= */ testCurrentPeriodIndex, + /* testLastPeriodIndex= */ testCurrentPeriodIndex); + assertThat(getEventsAsList(onEventsRef.get())) + .containsExactly( + Player.EVENT_TIMELINE_CHANGED, + Player.EVENT_MEDIA_ITEM_TRANSITION, + Player.EVENT_PLAYBACK_STATE_CHANGED); + assertThat(currentMediaItemIndexRef.get()).isEqualTo(testCurrentMediaItemIndex); + assertThat(nextMediaItemIndexRef.get()).isEqualTo(testNextMediaItemIndex); + assertThat(previousMediaItemIndexRef.get()).isEqualTo(testPreviousMediaItemIndex); + assertThat(currentPeriodIndexRef.get()).isEqualTo(testCurrentPeriodIndex); + assertThat(newMediaItemRef.get()).isEqualTo(testMediaItem); + assertThat(playbackStateRef.get()).isEqualTo(Player.STATE_BUFFERING); } @Test From 3d231cce05ac6f05170311d79b7a39a20bbf0c7f Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Mon, 22 May 2023 14:24:57 +0100 Subject: [PATCH 063/516] ExoPlayer: Add setVideoFrameProcessorFactory(). This allows apps to use a custom VideoFrameProcessor implementation for video playback. This may be useful, for example, when outputting to a texture. PiperOrigin-RevId: 534044831 (cherry picked from commit 438ae0ed6ac1059e8aaa9380f476c1403f24a986) --- RELEASENOTES.md | 3 + .../androidx/media3/exoplayer/ExoPlayer.java | 14 ++ .../media3/exoplayer/ExoPlayerImpl.java | 10 ++ .../androidx/media3/exoplayer/Renderer.java | 15 +- .../media3/exoplayer/SimpleExoPlayer.java | 8 ++ .../video/MediaCodecVideoRenderer.java | 133 ++++++++++-------- .../media3/test/utils/StubExoPlayer.java | 7 + 7 files changed, 127 insertions(+), 63 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1a8830f5c32..30ceb87a22c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -90,6 +90,9 @@ * Remove deprecated `ExoPlayer.retry()`, use `prepare()` instead. * Remove deprecated zero-arg `DefaultTrackSelector` constructor, use `DefaultTrackSelector(Context)` instead. +* Core library: + * Add `ExoPlayer.setVideoFrameProcessorFactory()` for using `Effect` with + a custom `VideoFrameProcessor.Factory` during video playback. ### 1.0.2 (2023-05-18) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index 77cccb41abf..0c3dc7fc632 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -44,6 +44,7 @@ import androidx.media3.common.PriorityTaskManager; import androidx.media3.common.Timeline; import androidx.media3.common.Tracks; +import androidx.media3.common.VideoFrameProcessor; import androidx.media3.common.VideoSize; import androidx.media3.common.text.CueGroup; import androidx.media3.common.util.Clock; @@ -1562,6 +1563,19 @@ void setMediaSources( @UnstableApi void setVideoEffects(List videoEffects); + /** + * Sets a {@link VideoFrameProcessor.Factory} to create the {@link VideoFrameProcessor} that + * applies video effects set in {@link #setVideoEffects}. + * + *

    See {@link #setVideoEffects} for limitations. + * + * @param videoFrameProcessorFactory The {@link VideoFrameProcessor.Factory} to use to apply the + * video effects. + */ + @RequiresApi(18) + @UnstableApi + void setVideoFrameProcessorFactory(VideoFrameProcessor.Factory videoFrameProcessorFactory); + /** * Sets the {@link C.VideoScalingMode}. * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 8301ab99e1e..3deac14e16b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -32,6 +32,7 @@ import static androidx.media3.exoplayer.Renderer.MSG_SET_SKIP_SILENCE_ENABLED; import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_EFFECTS; import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER; +import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_FRAME_PROCESSOR_FACTORY; import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT; import static androidx.media3.exoplayer.Renderer.MSG_SET_VIDEO_OUTPUT_RESOLUTION; import static androidx.media3.exoplayer.Renderer.MSG_SET_VOLUME; @@ -77,6 +78,7 @@ import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.Tracks; +import androidx.media3.common.VideoFrameProcessor; import androidx.media3.common.VideoSize; import androidx.media3.common.text.Cue; import androidx.media3.common.text.CueGroup; @@ -1238,6 +1240,14 @@ public void setVideoEffects(List videoEffects) { sendRendererMessage(TRACK_TYPE_VIDEO, MSG_SET_VIDEO_EFFECTS, videoEffects); } + @Override + public void setVideoFrameProcessorFactory( + VideoFrameProcessor.Factory videoFrameProcessorFactory) { + verifyApplicationThread(); + sendRendererMessage( + TRACK_TYPE_VIDEO, MSG_SET_VIDEO_FRAME_PROCESSOR_FACTORY, videoFrameProcessorFactory); + } + @Override public void setVideoScalingMode(@C.VideoScalingMode int videoScalingMode) { verifyApplicationThread(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java index 739337df6d5..28e58f282b4 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/Renderer.java @@ -27,6 +27,7 @@ import androidx.media3.common.Effect; import androidx.media3.common.Format; import androidx.media3.common.Player; +import androidx.media3.common.VideoFrameProcessor; import androidx.media3.common.util.Size; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; @@ -90,9 +91,9 @@ interface WakeupListener { * #MSG_SET_SCALING_MODE}, {@link #MSG_SET_CHANGE_FRAME_RATE_STRATEGY}, {@link * #MSG_SET_AUX_EFFECT_INFO}, {@link #MSG_SET_VIDEO_FRAME_METADATA_LISTENER}, {@link * #MSG_SET_CAMERA_MOTION_LISTENER}, {@link #MSG_SET_SKIP_SILENCE_ENABLED}, {@link - * #MSG_SET_AUDIO_SESSION_ID}, {@link #MSG_SET_WAKEUP_LISTENER}, {@link #MSG_SET_VIDEO_EFFECTS} or - * {@link #MSG_SET_VIDEO_OUTPUT_RESOLUTION}. May also be an app-defined value (see {@link - * #MSG_CUSTOM_BASE}). + * #MSG_SET_AUDIO_SESSION_ID}, {@link #MSG_SET_WAKEUP_LISTENER}, {@link #MSG_SET_VIDEO_EFFECTS}, + * {@link #MSG_SET_VIDEO_FRAME_PROCESSOR_FACTORY} or {@link #MSG_SET_VIDEO_OUTPUT_RESOLUTION}. May + * also be an app-defined value (see {@link #MSG_CUSTOM_BASE}). */ @Documented @Retention(RetentionPolicy.SOURCE) @@ -112,6 +113,7 @@ interface WakeupListener { MSG_SET_AUDIO_SESSION_ID, MSG_SET_WAKEUP_LISTENER, MSG_SET_VIDEO_EFFECTS, + MSG_SET_VIDEO_FRAME_PROCESSOR_FACTORY, MSG_SET_VIDEO_OUTPUT_RESOLUTION }) public @interface MessageType {} @@ -219,12 +221,17 @@ interface WakeupListener { * {@link List} containing {@linkplain Effect video effects}. */ int MSG_SET_VIDEO_EFFECTS = 13; + /** + * The type of a message that can be passed to a video renderer. The message payload should be a + * {@link VideoFrameProcessor.Factory}. + */ + int MSG_SET_VIDEO_FRAME_PROCESSOR_FACTORY = 14; /** * The type of a message that can be passed to a video renderer to set the desired output * resolution. The message payload should be a {@link Size} of the desired output width and * height. Use this method only when playing with video {@linkplain Effect effects}. */ - int MSG_SET_VIDEO_OUTPUT_RESOLUTION = 14; + int MSG_SET_VIDEO_OUTPUT_RESOLUTION = 15; /** * Applications or extensions may define custom {@code MSG_*} constants that can be passed to * renderers. These custom constants must be greater than or equal to this value. diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index edda6b71cbc..6e2ff0c857f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -43,6 +43,7 @@ import androidx.media3.common.Timeline; import androidx.media3.common.TrackSelectionParameters; import androidx.media3.common.Tracks; +import androidx.media3.common.VideoFrameProcessor; import androidx.media3.common.VideoSize; import androidx.media3.common.text.CueGroup; import androidx.media3.common.util.Clock; @@ -671,6 +672,13 @@ public void setVideoEffects(List videoEffects) { player.setVideoEffects(videoEffects); } + @Override + public void setVideoFrameProcessorFactory( + VideoFrameProcessor.Factory videoFrameProcessorFactory) { + blockUntilConstructorFinished(); + player.setVideoFrameProcessorFactory(videoFrameProcessorFactory); + } + @Override public void setSkipSilenceEnabled(boolean skipSilenceEnabled) { blockUntilConstructorFinished(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index 3d848899ce6..7f72e8cf11e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -687,6 +687,11 @@ public void handleMessage(@MessageType int messageType, @Nullable Object message List videoEffects = (List) checkNotNull(message); videoFrameProcessorManager.setVideoEffects(videoEffects); break; + case MSG_SET_VIDEO_FRAME_PROCESSOR_FACTORY: + VideoFrameProcessor.Factory videoFrameProcessorFactory = + (VideoFrameProcessor.Factory) checkNotNull(message); + videoFrameProcessorManager.setVideoFrameProcessorFactory(videoFrameProcessorFactory); + break; case MSG_SET_VIDEO_OUTPUT_RESOLUTION: Size outputResolution = (Size) checkNotNull(message); if (outputResolution.getWidth() != 0 @@ -1876,6 +1881,7 @@ private static final class VideoFrameProcessorManager { private final ArrayDeque> pendingFrameFormats; private @MonotonicNonNull Handler handler; + private VideoFrameProcessor.@MonotonicNonNull Factory videoFrameProcessorFactory; @Nullable private VideoFrameProcessor videoFrameProcessor; @Nullable private CopyOnWriteArrayList videoEffects; @Nullable private Format inputFormat; @@ -1937,6 +1943,12 @@ public void setVideoEffects(List videoEffects) { this.videoEffects.addAll(videoEffects); } + /** Sets the {@link VideoFrameProcessor.Factory}. */ + public void setVideoFrameProcessorFactory( + VideoFrameProcessor.Factory videoFrameProcessorFactory) { + this.videoFrameProcessorFactory = videoFrameProcessorFactory; + } + /** Returns whether video frame processing is enabled. */ public boolean isEnabled() { return videoFrameProcessor != null; @@ -2009,66 +2021,69 @@ public boolean maybeEnable(Format inputFormat, long initialStreamOffsetUs) VideoFrameProcessorAccessor.createRotationEffect(inputFormat.rotationDegrees)); } + videoFrameProcessorFactory = + videoFrameProcessorFactory == null + ? VideoFrameProcessorAccessor.getFrameProcessorFactory() + : videoFrameProcessorFactory; videoFrameProcessor = - VideoFrameProcessorAccessor.getFrameProcessorFactory() - .create( - renderer.context, - checkNotNull(videoEffects), - DebugViewProvider.NONE, - inputAndOutputColorInfos.first, - inputAndOutputColorInfos.second, - /* renderFramesAutomatically= */ false, - /* listenerExecutor= */ handler::post, - new VideoFrameProcessor.Listener() { - @Override - public void onOutputSizeChanged(int width, int height) { - @Nullable Format inputFormat = VideoFrameProcessorManager.this.inputFormat; - checkStateNotNull(inputFormat); - // TODO(b/264889146): Handle Effect that changes output size based on pts. - processedFrameSize = - new VideoSize( - width, - height, - // VideoFrameProcessor is configured to produce rotation free - // frames. - /* unappliedRotationDegrees= */ 0, - // VideoFrameProcessor always outputs pixelWidthHeightRatio 1. - /* pixelWidthHeightRatio= */ 1.f); - pendingOutputSizeChange = true; - } - - @Override - public void onOutputFrameAvailableForRendering(long presentationTimeUs) { - if (registeredLastFrame) { - checkState(lastCodecBufferPresentationTimestampUs != C.TIME_UNSET); - } - processedFramesTimestampsUs.add(presentationTimeUs); - // TODO(b/257464707) Support extensively modified media. - if (registeredLastFrame - && presentationTimeUs >= lastCodecBufferPresentationTimestampUs) { - processedLastFrame = true; - } - if (pendingOutputSizeChange) { - // Report the size change on releasing this frame. - pendingOutputSizeChange = false; - pendingOutputSizeChangeNotificationTimeUs = presentationTimeUs; - } - } - - @Override - public void onError(VideoFrameProcessingException exception) { - renderer.setPendingPlaybackException( - renderer.createRendererException( - exception, - inputFormat, - PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED)); - } - - @Override - public void onEnded() { - throw new IllegalStateException(); - } - }); + videoFrameProcessorFactory.create( + renderer.context, + checkNotNull(videoEffects), + DebugViewProvider.NONE, + inputAndOutputColorInfos.first, + inputAndOutputColorInfos.second, + /* renderFramesAutomatically= */ false, + /* listenerExecutor= */ handler::post, + new VideoFrameProcessor.Listener() { + @Override + public void onOutputSizeChanged(int width, int height) { + @Nullable Format inputFormat = VideoFrameProcessorManager.this.inputFormat; + checkStateNotNull(inputFormat); + // TODO(b/264889146): Handle Effect that changes output size based on pts. + processedFrameSize = + new VideoSize( + width, + height, + // VideoFrameProcessor is configured to produce rotation free + // frames. + /* unappliedRotationDegrees= */ 0, + // VideoFrameProcessor always outputs pixelWidthHeightRatio 1. + /* pixelWidthHeightRatio= */ 1.f); + pendingOutputSizeChange = true; + } + + @Override + public void onOutputFrameAvailableForRendering(long presentationTimeUs) { + if (registeredLastFrame) { + checkState(lastCodecBufferPresentationTimestampUs != C.TIME_UNSET); + } + processedFramesTimestampsUs.add(presentationTimeUs); + // TODO(b/257464707) Support extensively modified media. + if (registeredLastFrame + && presentationTimeUs >= lastCodecBufferPresentationTimestampUs) { + processedLastFrame = true; + } + if (pendingOutputSizeChange) { + // Report the size change on releasing this frame. + pendingOutputSizeChange = false; + pendingOutputSizeChangeNotificationTimeUs = presentationTimeUs; + } + } + + @Override + public void onError(VideoFrameProcessingException exception) { + renderer.setPendingPlaybackException( + renderer.createRendererException( + exception, + inputFormat, + PlaybackException.ERROR_CODE_VIDEO_FRAME_PROCESSING_FAILED)); + } + + @Override + public void onEnded() { + throw new IllegalStateException(); + } + }); videoFrameProcessor.registerInputStream(VideoFrameProcessor.INPUT_TYPE_SURFACE); this.initialStreamOffsetUs = initialStreamOffsetUs; } catch (Exception e) { diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java index 730fefd7e1b..b2e20db5e91 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java @@ -24,6 +24,7 @@ import androidx.media3.common.Format; import androidx.media3.common.Player; import androidx.media3.common.PriorityTaskManager; +import androidx.media3.common.VideoFrameProcessor; import androidx.media3.common.util.Clock; import androidx.media3.common.util.UnstableApi; import androidx.media3.exoplayer.DecoderCounters; @@ -249,6 +250,12 @@ public void setVideoEffects(List videoEffects) { throw new UnsupportedOperationException(); } + @Override + public void setVideoFrameProcessorFactory( + VideoFrameProcessor.Factory videoFrameProcessorFactory) { + throw new UnsupportedOperationException(); + } + @Override public void setVideoScalingMode(int videoScalingMode) { throw new UnsupportedOperationException(); From 9d04b11d4963066dbe655aaa983ce83417303e08 Mon Sep 17 00:00:00 2001 From: huangdarwin Date: Mon, 22 May 2023 14:32:43 +0100 Subject: [PATCH 064/516] Effect: Add GlTextureInfo release() and accessor methods. This allows us to disallow access after release. PiperOrigin-RevId: 534046475 (cherry picked from commit a6897aedaa540dd10ba3be72b4e871ef0f241fd8) --- .../transformer/MediaPipeShaderProgram.java | 3 +- .../androidx/media3/common/GlTextureInfo.java | 77 ++++++++++++++----- .../androidx/media3/common/util/GlUtil.java | 73 ++++++++++-------- .../media3/effect/BaseGlShaderProgram.java | 6 +- .../media3/effect/BitmapTextureManager.java | 4 +- .../effect/FinalShaderProgramWrapper.java | 12 +-- .../media3/effect/TexIdTextureManager.java | 2 +- .../androidx/media3/effect/TexturePool.java | 6 +- .../utils/VideoFrameProcessorTestRunner.java | 4 +- ...oFrameProcessorTextureOutputPixelTest.java | 6 +- 10 files changed, 120 insertions(+), 73 deletions(-) diff --git a/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeShaderProgram.java b/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeShaderProgram.java index b2cdc988f8f..48f216da757 100644 --- a/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeShaderProgram.java +++ b/demos/transformer/src/withMediaPipe/java/androidx/media3/demo/transformer/MediaPipeShaderProgram.java @@ -166,7 +166,8 @@ public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) {} @Override public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) { AppTextureFrame appTextureFrame = - new AppTextureFrame(inputTexture.texId, inputTexture.width, inputTexture.height); + new AppTextureFrame( + inputTexture.getTexId(), inputTexture.getWidth(), inputTexture.getHeight()); // TODO(b/238302213): Handle timestamps restarting from 0 when applying effects to a playlist. // MediaPipe will fail if the timestamps are not monotonically increasing. // Also make sure that a MediaPipe graph producing additional frames only starts producing diff --git a/libraries/common/src/main/java/androidx/media3/common/GlTextureInfo.java b/libraries/common/src/main/java/androidx/media3/common/GlTextureInfo.java index f073e9d1377..3ceda67e8e8 100644 --- a/libraries/common/src/main/java/androidx/media3/common/GlTextureInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/GlTextureInfo.java @@ -15,13 +15,14 @@ */ package androidx.media3.common; +import static androidx.media3.common.util.Assertions.checkState; + +import androidx.media3.common.util.GlUtil; import androidx.media3.common.util.UnstableApi; /** Contains information describing an OpenGL texture. */ @UnstableApi public final class GlTextureInfo { - // TODO: b/262694346 - Add a release() method for GlTextureInfo. - /** A {@link GlTextureInfo} instance with all fields unset. */ public static final GlTextureInfo UNSET = new GlTextureInfo( @@ -31,22 +32,13 @@ public final class GlTextureInfo { /* width= */ C.LENGTH_UNSET, /* height= */ C.LENGTH_UNSET); - /** The OpenGL texture identifier, or {@link C#INDEX_UNSET} if not specified. */ - public final int texId; - /** - * Identifier of a framebuffer object associated with the texture, or {@link C#INDEX_UNSET} if not - * specified. - */ - public final int fboId; - /** - * Identifier of a renderbuffer object attached with the framebuffer, or {@link C#INDEX_UNSET} if - * not specified. - */ - public final int rboId; - /** The width of the texture, in pixels, or {@link C#LENGTH_UNSET} if not specified. */ - public final int width; - /** The height of the texture, in pixels, or {@link C#LENGTH_UNSET} if not specified. */ - public final int height; + private final int texId; + private final int fboId; + private final int rboId; + private final int width; + private final int height; + + private boolean isReleased; /** * Creates a new instance. @@ -66,4 +58,53 @@ public GlTextureInfo(int texId, int fboId, int rboId, int width, int height) { this.width = width; this.height = height; } + + /** The OpenGL texture identifier, or {@link C#INDEX_UNSET} if not specified. */ + public int getTexId() { + checkState(!isReleased); + return texId; + } + + /** + * Identifier of a framebuffer object associated with the texture, or {@link C#INDEX_UNSET} if not + * specified. + */ + public int getFboId() { + checkState(!isReleased); + return fboId; + } + + /** + * Identifier of a renderbuffer object attached with the framebuffer, or {@link C#INDEX_UNSET} if + * not specified. + */ + public int getRboId() { + checkState(!isReleased); + return rboId; + } + + /** The width of the texture, in pixels, or {@link C#LENGTH_UNSET} if not specified. */ + public int getWidth() { + checkState(!isReleased); + return width; + } + + /** The height of the texture, in pixels, or {@link C#LENGTH_UNSET} if not specified. */ + public int getHeight() { + checkState(!isReleased); + return height; + } + + public void release() throws GlUtil.GlException { + isReleased = true; + if (texId != C.INDEX_UNSET) { + GlUtil.deleteTexture(texId); + } + if (fboId != C.INDEX_UNSET) { + GlUtil.deleteFbo(fboId); + } + if (rboId != C.INDEX_UNSET) { + GlUtil.deleteRbo(rboId); + } + } } diff --git a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java index e20da85644e..d9072b9cca5 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/GlUtil.java @@ -494,36 +494,6 @@ public static void focusFramebufferUsingCurrentContext(int framebuffer, int widt Api17.focusFramebufferUsingCurrentContext(framebuffer, width, height); } - /** - * Deletes a GL texture. - * - * @param textureId The ID of the texture to delete. - */ - public static void deleteTexture(int textureId) throws GlException { - GLES20.glDeleteTextures(/* n= */ 1, new int[] {textureId}, /* offset= */ 0); - checkGlError(); - } - - /** - * Destroys the {@link EGLContext} identified by the provided {@link EGLDisplay} and {@link - * EGLContext}. - */ - @RequiresApi(17) - public static void destroyEglContext( - @Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException { - Api17.destroyEglContext(eglDisplay, eglContext); - } - - /** - * Destroys the {@link EGLSurface} identified by the provided {@link EGLDisplay} and {@link - * EGLSurface}. - */ - @RequiresApi(17) - public static void destroyEglSurface( - @Nullable EGLDisplay eglDisplay, @Nullable EGLSurface eglSurface) throws GlException { - Api17.destroyEglSurface(eglDisplay, eglSurface); - } - /** * Allocates a FloatBuffer with the given data. * @@ -656,11 +626,46 @@ public static int createFboForTexture(int texId) throws GlException { return fboId[0]; } - /** Deletes a framebuffer. */ + /** + * Deletes a GL texture. + * + * @param textureId The ID of the texture to delete. + */ + public static void deleteTexture(int textureId) throws GlException { + GLES20.glDeleteTextures(/* n= */ 1, new int[] {textureId}, /* offset= */ 0); + checkGlError(); + } + + /** + * Destroys the {@link EGLContext} identified by the provided {@link EGLDisplay} and {@link + * EGLContext}. + */ + @RequiresApi(17) + public static void destroyEglContext( + @Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException { + Api17.destroyEglContext(eglDisplay, eglContext); + } + + /** + * Destroys the {@link EGLSurface} identified by the provided {@link EGLDisplay} and {@link + * EGLSurface}. + */ + @RequiresApi(17) + public static void destroyEglSurface( + @Nullable EGLDisplay eglDisplay, @Nullable EGLSurface eglSurface) throws GlException { + Api17.destroyEglSurface(eglDisplay, eglSurface); + } + + /** Deletes a framebuffer, or silently ignores the method call if {@code fboId} is unused. */ public static void deleteFbo(int fboId) throws GlException { - int[] fboIdArray = new int[1]; - fboIdArray[0] = fboId; - GLES20.glDeleteFramebuffers(/* n= */ 1, fboIdArray, /* offset= */ 0); + GLES20.glDeleteFramebuffers(/* n= */ 1, new int[] {fboId}, /* offset= */ 0); + checkGlError(); + } + + /** Deletes a renderbuffer, or silently ignores the method call if {@code rboId} is unused. */ + public static void deleteRbo(int rboId) throws GlException { + GLES20.glDeleteRenderbuffers( + /* n= */ 1, /* renderbuffers= */ new int[] {rboId}, /* offset= */ 0); checkGlError(); } diff --git a/libraries/effect/src/main/java/androidx/media3/effect/BaseGlShaderProgram.java b/libraries/effect/src/main/java/androidx/media3/effect/BaseGlShaderProgram.java index 3ceb2924ec8..9bb384c36be 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/BaseGlShaderProgram.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/BaseGlShaderProgram.java @@ -129,7 +129,7 @@ public void setGlObjectsProvider(GlObjectsProvider glObjectsProvider) { @Override public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) { try { - Size outputTextureSize = configure(inputTexture.width, inputTexture.height); + Size outputTextureSize = configure(inputTexture.getWidth(), inputTexture.getHeight()); outputTexturePool.ensureConfigured( outputTextureSize.getWidth(), outputTextureSize.getHeight()); frameProcessingStarted = true; @@ -139,9 +139,9 @@ public void queueInputFrame(GlTextureInfo inputTexture, long presentationTimeUs) // Copy frame to fbo. GlUtil.focusFramebufferUsingCurrentContext( - outputTexture.fboId, outputTexture.width, outputTexture.height); + outputTexture.getFboId(), outputTexture.getWidth(), outputTexture.getHeight()); GlUtil.clearOutputFrame(); - drawFrame(inputTexture.texId, presentationTimeUs); + drawFrame(inputTexture.getTexId(), presentationTimeUs); inputListener.onInputFrameProcessed(inputTexture); outputListener.onOutputFrameAvailable(outputTexture, presentationTimeUs); } catch (VideoFrameProcessingException | GlUtil.GlException | NoSuchElementException e) { diff --git a/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java b/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java index 8c5371669c1..4c521155cb6 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/BitmapTextureManager.java @@ -123,7 +123,7 @@ public void release() { videoFrameProcessingTaskExecutor.submit( () -> { if (currentGlTextureInfo != null) { - GlUtil.deleteTexture(currentGlTextureInfo.texId); + currentGlTextureInfo.release(); } }); } @@ -160,7 +160,7 @@ private void maybeQueueToShaderProgram() throws VideoFrameProcessingException { int currentTexId; try { if (currentGlTextureInfo != null) { - GlUtil.deleteTexture(currentGlTextureInfo.texId); + currentGlTextureInfo.release(); } currentTexId = GlUtil.createTexture( diff --git a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java index 35af4c3b332..844ce074549 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/FinalShaderProgramWrapper.java @@ -316,7 +316,7 @@ private synchronized void renderFrame( GlTextureInfo inputTexture, long presentationTimeUs, long renderTimeNs) { try { if (renderTimeNs == VideoFrameProcessor.DROP_OUTPUT_FRAME - || !ensureConfigured(inputTexture.width, inputTexture.height)) { + || !ensureConfigured(inputTexture.getWidth(), inputTexture.getHeight())) { inputListener.onInputFrameProcessed(inputTexture); return; // Drop frames when requested, or there is no output surface and output texture. } @@ -352,7 +352,7 @@ private synchronized void renderFrameToOutputSurface( outputSurfaceInfo.width, outputSurfaceInfo.height); GlUtil.clearOutputFrame(); - defaultShaderProgram.drawFrame(inputTexture.texId, presentationTimeUs); + defaultShaderProgram.drawFrame(inputTexture.getTexId(), presentationTimeUs); EGLExt.eglPresentationTimeANDROID( eglDisplay, @@ -369,9 +369,9 @@ private void renderFrameToOutputTexture(GlTextureInfo inputTexture, long present GlTextureInfo outputTexture = outputTexturePool.useTexture(); outputTextureTimestamps.add(presentationTimeUs); GlUtil.focusFramebufferUsingCurrentContext( - outputTexture.fboId, outputTexture.width, outputTexture.height); + outputTexture.getFboId(), outputTexture.getWidth(), outputTexture.getHeight()); GlUtil.clearOutputFrame(); - checkNotNull(defaultShaderProgram).drawFrame(inputTexture.texId, presentationTimeUs); + checkNotNull(defaultShaderProgram).drawFrame(inputTexture.getTexId(), presentationTimeUs); // TODO(b/262694346): If Compositor's VFPs all use the same context, media3 should be able to // avoid calling glFinish, and require the onTextureRendered listener to decide whether to // glFinish. Consider removing glFinish and requiring onTextureRendered to handle @@ -519,10 +519,10 @@ private void renderFrameToDebugSurface(GlTextureInfo inputTexture, long presenta int configuredColorTransfer = defaultShaderProgram.getOutputColorTransfer(); defaultShaderProgram.setOutputColorTransfer( debugSurfaceViewWrapper.outputColorTransfer); - defaultShaderProgram.drawFrame(inputTexture.texId, presentationTimeUs); + defaultShaderProgram.drawFrame(inputTexture.getTexId(), presentationTimeUs); defaultShaderProgram.setOutputColorTransfer(configuredColorTransfer); } else { - defaultShaderProgram.drawFrame(inputTexture.texId, presentationTimeUs); + defaultShaderProgram.drawFrame(inputTexture.getTexId(), presentationTimeUs); } }, glObjectsProvider); diff --git a/libraries/effect/src/main/java/androidx/media3/effect/TexIdTextureManager.java b/libraries/effect/src/main/java/androidx/media3/effect/TexIdTextureManager.java index c2fe8951936..41474dc7c65 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/TexIdTextureManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/TexIdTextureManager.java @@ -61,7 +61,7 @@ public void onReadyToAcceptInputFrame() { @Override public void onInputFrameProcessed(GlTextureInfo inputTexture) { videoFrameProcessingTaskExecutor.submit( - () -> checkNotNull(frameProcessedListener).onInputFrameProcessed(inputTexture.texId)); + () -> checkNotNull(frameProcessedListener).onInputFrameProcessed(inputTexture.getTexId())); } @Override diff --git a/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java b/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java index e86e6fdb8d7..78f8f5eb509 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/TexturePool.java @@ -86,7 +86,7 @@ public void ensureConfigured(int width, int height) throws GlUtil.GlException { return; } GlTextureInfo texture = getIteratorToAllTextures().next(); - if (texture.width != width || texture.height != height) { + if (texture.getWidth() != width || texture.getHeight() != height) { deleteAllTextures(); createTextures(width, height); } @@ -141,9 +141,7 @@ public void freeAllTextures() { public void deleteAllTextures() throws GlUtil.GlException { Iterator allTextures = getIteratorToAllTextures(); while (allTextures.hasNext()) { - GlTextureInfo textureInfo = allTextures.next(); - GlUtil.deleteTexture(textureInfo.texId); - GlUtil.deleteFbo(textureInfo.fboId); + allTextures.next().release(); } freeTextures.clear(); inUseTextures.clear(); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java index 17d1dce682e..ed00d9c5cb7 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/VideoFrameProcessorTestRunner.java @@ -353,7 +353,7 @@ public void queueInputBitmap( public void queueInputTexture(GlTextureInfo inputTexture, long pts) { videoFrameProcessor.setInputFrameInfo( - new FrameInfo.Builder(inputTexture.width, inputTexture.height) + new FrameInfo.Builder(inputTexture.getWidth(), inputTexture.getHeight()) .setPixelWidthHeightRatio(pixelWidthHeightRatio) .build()); videoFrameProcessor.registerInputStream(INPUT_TYPE_TEXTURE_ID); @@ -365,7 +365,7 @@ public void queueInputTexture(GlTextureInfo inputTexture, long pts) { throw new VideoFrameProcessingException(e); } }); - videoFrameProcessor.queueInputTexture(inputTexture.texId, pts); + videoFrameProcessor.queueInputTexture(inputTexture.getTexId(), pts); } public void endFrameProcessing() throws InterruptedException { diff --git a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java index 3aec9e33eb1..f900f803afd 100644 --- a/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java +++ b/libraries/transformer/src/androidTest/java/androidx/media3/transformer/mh/DefaultVideoFrameProcessorTextureOutputPixelTest.java @@ -608,10 +608,12 @@ public void readBitmapFromTexture( throws VideoFrameProcessingException { try { GlUtil.focusFramebufferUsingCurrentContext( - outputTexture.fboId, outputTexture.width, outputTexture.height); + outputTexture.getFboId(), outputTexture.getWidth(), outputTexture.getHeight()); outputBitmap = createBitmapFromCurrentGlFrameBuffer( - outputTexture.width, outputTexture.height, useHighPrecisionColorComponents); + outputTexture.getWidth(), + outputTexture.getHeight(), + useHighPrecisionColorComponents); } catch (GlUtil.GlException e) { throw new VideoFrameProcessingException(e); } From cebdb171346c01ac72d6383f1cc9f627d8156fa8 Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Mon, 22 May 2023 17:24:34 +0100 Subject: [PATCH 065/516] Write metadata to Mp4Muxer in the release() method Earlier metadata was written multiple times as it came. With new changes, all the distinct metadata entries will get collected and will be written at once in the end. PiperOrigin-RevId: 534088401 (cherry picked from commit a9e3f5def4fa17aa82f47a8d3a1f1452c2e6f245) --- .../media3/transformer/InAppMuxer.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java index f80f7f4e8bb..b44490d7f77 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java @@ -33,7 +33,9 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; /** {@link Muxer} implementation that uses a {@link Mp4Muxer}. */ @UnstableApi @@ -85,12 +87,14 @@ public ImmutableList getSupportedSampleMimeTypes(@C.TrackType int trackT private final long maxDelayBetweenSamplesMs; private final List trackTokenList; private final BufferInfo bufferInfo; + private final Set metadataEntries; private InAppMuxer(Mp4Muxer mp4Muxer, long maxDelayBetweenSamplesMs) { this.mp4Muxer = mp4Muxer; this.maxDelayBetweenSamplesMs = maxDelayBetweenSamplesMs; trackTokenList = new ArrayList<>(); bufferInfo = new BufferInfo(); + metadataEntries = new LinkedHashSet<>(); } @Override @@ -133,15 +137,16 @@ public void writeSampleData( public void addMetadata(Metadata metadata) { for (int i = 0; i < metadata.length(); i++) { Metadata.Entry entry = metadata.get(i); + // Keep only supported metadata. if (entry instanceof Mp4LocationData) { - mp4Muxer.setLocation( - ((Mp4LocationData) entry).latitude, ((Mp4LocationData) entry).longitude); + metadataEntries.add(entry); } } } @Override public void release(boolean forCancellation) throws MuxerException { + writeMetadata(); try { mp4Muxer.close(); } catch (IOException e) { @@ -153,4 +158,13 @@ public void release(boolean forCancellation) throws MuxerException { public long getMaxDelayBetweenSamplesMs() { return maxDelayBetweenSamplesMs; } + + private void writeMetadata() { + for (Metadata.Entry entry : metadataEntries) { + if (entry instanceof Mp4LocationData) { + mp4Muxer.setLocation( + ((Mp4LocationData) entry).latitude, ((Mp4LocationData) entry).longitude); + } + } + } } From fbd0bae265ad92f3d000a27fb6646bced9956866 Mon Sep 17 00:00:00 2001 From: christosts Date: Mon, 22 May 2023 19:24:30 +0100 Subject: [PATCH 066/516] Fix seeking bug in opus Fix a bug when seeking in an opus container. The calculations inside DefaultOggSeeker may overflow a long primitive. Issue: androidx/media#391 #minor-release PiperOrigin-RevId: 534128513 (cherry picked from commit b9a4e614f7cfe863e72dec779b7beb1a8c1e4e2d) --- RELEASENOTES.md | 2 ++ .../androidx/media3/extractor/ogg/DefaultOggSeeker.java | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 30ceb87a22c..48976897bda 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -9,6 +9,8 @@ * ExoPlayer: * Add `FilteringMediaSource` that allows to filter available track types from a `MediaSource`. + * Fix bug seeking in files with long opus audio + ([#391](https://github.com/androidx/media/issues/391)). * Transformer: * Track Selection: * Extractors: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ogg/DefaultOggSeeker.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ogg/DefaultOggSeeker.java index 2dfa2323989..39d2214a9d8 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ogg/DefaultOggSeeker.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ogg/DefaultOggSeeker.java @@ -28,6 +28,7 @@ import androidx.media3.extractor.SeekPoint; import java.io.EOFException; import java.io.IOException; +import java.math.BigInteger; /** Seeks in an Ogg stream. */ /* package */ final class DefaultOggSeeker implements OggSeeker { @@ -260,7 +261,12 @@ public SeekPoints getSeekPoints(long timeUs) { long targetGranule = streamReader.convertTimeToGranule(timeUs); long estimatedPosition = payloadStartPosition - + (targetGranule * (payloadEndPosition - payloadStartPosition) / totalGranules) + // Use BigInteger arithmetic to avoid long overflow + // https://github.com/androidx/media/issues/391 + + BigInteger.valueOf(targetGranule) + .multiply(BigInteger.valueOf(payloadEndPosition - payloadStartPosition)) + .divide(BigInteger.valueOf(totalGranules)) + .longValue() - DEFAULT_OFFSET; estimatedPosition = Util.constrainValue(estimatedPosition, payloadStartPosition, payloadEndPosition - 1); From 5f3cb861fc5b9c7238ee355b4ce16ea220b6f326 Mon Sep 17 00:00:00 2001 From: bachinger Date: Mon, 22 May 2023 20:44:05 +0100 Subject: [PATCH 067/516] Minor session demo app improvements Basically this change removes a bug that makes video playback stuck when a video is playing and the user taps the UMO notification to get to the player activity. - Use `launchMode="singleTop"` for `PlayerActivity` - Change session activity to a back stacked activity on service `onDestroy`. Using a back stacked activity `onDestroy()` will be useful once this demo app implements playback resumption. The rest of the changes are aesthetic: - clean up and optimize screen space usage in UI of `PlayerActivity` - changed some colors, paddings and spacings - adds a default artwork for the `PlayerView` PiperOrigin-RevId: 534152052 (cherry picked from commit 96a4ae7e40732dccd2fdded4cb8d0cc7f25f8cdd) --- demos/session/src/main/AndroidManifest.xml | 4 +- .../media3/demo/session/MainActivity.kt | 7 +- .../demo/session/PlayableFolderActivity.kt | 10 +- .../media3/demo/session/PlaybackService.kt | 35 +++-- .../media3/demo/session/PlayerActivity.kt | 125 ++++++------------ .../main/res/drawable/artwork_placeholder.png | Bin 0 -> 40772 bytes .../src/main/res/layout/activity_player.xml | 70 +++------- .../src/main/res/layout/playlist_items.xml | 3 + demos/session/src/main/res/values/colors.xml | 5 + 9 files changed, 104 insertions(+), 155 deletions(-) create mode 100644 demos/session/src/main/res/drawable/artwork_placeholder.png diff --git a/demos/session/src/main/AndroidManifest.xml b/demos/session/src/main/AndroidManifest.xml index 90557fc6c7f..d3f9ba16204 100644 --- a/demos/session/src/main/AndroidManifest.xml +++ b/demos/session/src/main/AndroidManifest.xml @@ -40,7 +40,9 @@ + android:exported="true" + android:launchMode="singleTop" + android:theme="@style/Theme.AppCompat.NoActionBar"/> (R.id.open_player_floating_button) .setOnClickListener { - // display the playing media items - val intent = Intent(this, PlayerActivity::class.java) - startActivity(intent) + // Start the session activity that shows the playback activity. The System UI uses the same + // intent in the same way to start the activity from the notification. + browser?.sessionActivity?.send() } onBackPressedDispatcher.addCallback( diff --git a/demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt b/demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt index 9c6f3e5b4a6..f0dbec48fcc 100644 --- a/demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt +++ b/demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt @@ -51,6 +51,7 @@ class PlayableFolderActivity : AppCompatActivity() { companion object { private const val MEDIA_ITEM_ID_KEY = "MEDIA_ITEM_ID_KEY" + fun createIntent(context: Context, mediaItemID: String): Intent { val intent = Intent(context, PlayableFolderActivity::class.java) intent.putExtra(MEDIA_ITEM_ID_KEY, mediaItemID) @@ -77,8 +78,7 @@ class PlayableFolderActivity : AppCompatActivity() { browser.shuffleModeEnabled = false browser.prepare() browser.play() - val intent = Intent(this, PlayerActivity::class.java) - startActivity(intent) + browser.sessionActivity?.send() } } @@ -104,9 +104,9 @@ class PlayableFolderActivity : AppCompatActivity() { findViewById(R.id.open_player_floating_button) .setOnClickListener { - // display the playing media items - val intent = Intent(this, PlayerActivity::class.java) - startActivity(intent) + // Start the session activity that shows the playback activity. The System UI uses the same + // intent in the same way to start the activity from the notification. + browser?.sessionActivity?.send() } } diff --git a/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt b/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt index eff81c8beb8..26d396a7d6f 100644 --- a/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt +++ b/demos/session/src/main/java/androidx/media3/demo/session/PlaybackService.kt @@ -18,6 +18,7 @@ package androidx.media3.demo.session import android.annotation.SuppressLint import android.app.NotificationChannel import android.app.NotificationManager +import android.app.PendingIntent import android.app.PendingIntent.* import android.app.TaskStackBuilder import android.content.Intent @@ -55,6 +56,7 @@ class PlaybackService : MediaLibraryService() { "android.media3.session.demo.SHUFFLE_OFF" private const val NOTIFICATION_ID = 123 private const val CHANNEL_ID = "demo_session_notification_channel_id" + private val immutableFlag = if (Build.VERSION.SDK_INT >= 23) FLAG_IMMUTABLE else 0 } override fun onCreate() { @@ -78,14 +80,15 @@ class PlaybackService : MediaLibraryService() { } override fun onTaskRemoved(rootIntent: Intent?) { - if (!player.playWhenReady) { + if (!player.playWhenReady || player.mediaItemCount == 0) { stopSelf() } } override fun onDestroy() { - player.release() + mediaLibrarySession.setSessionActivity(getBackStackedActivity()) mediaLibrarySession.release() + player.release() clearListener() super.onDestroy() } @@ -235,18 +238,9 @@ class PlaybackService : MediaLibraryService() { .build() MediaItemTree.initialize(assets) - val sessionActivityPendingIntent = - TaskStackBuilder.create(this).run { - addNextIntent(Intent(this@PlaybackService, MainActivity::class.java)) - addNextIntent(Intent(this@PlaybackService, PlayerActivity::class.java)) - - val immutableFlag = if (Build.VERSION.SDK_INT >= 23) FLAG_IMMUTABLE else 0 - getPendingIntent(0, immutableFlag or FLAG_UPDATE_CURRENT) - } - mediaLibrarySession = MediaLibrarySession.Builder(this, player, librarySessionCallback) - .setSessionActivity(sessionActivityPendingIntent) + .setSessionActivity(getSingleTopActivity()) .setBitmapLoader(CacheBitmapLoader(DataSourceBitmapLoader(/* context= */ this))) .build() if (!customLayout.isEmpty()) { @@ -255,6 +249,23 @@ class PlaybackService : MediaLibraryService() { } } + private fun getSingleTopActivity(): PendingIntent { + return getActivity( + this, + 0, + Intent(this, PlayerActivity::class.java), + immutableFlag or FLAG_UPDATE_CURRENT + ) + } + + private fun getBackStackedActivity(): PendingIntent { + return TaskStackBuilder.create(this).run { + addNextIntent(Intent(this@PlaybackService, MainActivity::class.java)) + addNextIntent(Intent(this@PlaybackService, PlayerActivity::class.java)) + getPendingIntent(0, immutableFlag or FLAG_UPDATE_CURRENT) + } + } + private fun getShuffleCommandButton(sessionCommand: SessionCommand): CommandButton { val isOn = sessionCommand.customAction == CUSTOM_COMMAND_TOGGLE_SHUFFLE_MODE_ON return CommandButton.Builder() diff --git a/demos/session/src/main/java/androidx/media3/demo/session/PlayerActivity.kt b/demos/session/src/main/java/androidx/media3/demo/session/PlayerActivity.kt index 452b901da5d..b769bd3967d 100644 --- a/demos/session/src/main/java/androidx/media3/demo/session/PlayerActivity.kt +++ b/demos/session/src/main/java/androidx/media3/demo/session/PlayerActivity.kt @@ -19,7 +19,6 @@ import android.content.ComponentName import android.content.Context import android.os.Bundle import android.view.LayoutInflater -import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter @@ -46,41 +45,33 @@ class PlayerActivity : AppCompatActivity() { get() = if (controllerFuture.isDone) controllerFuture.get() else null private lateinit var playerView: PlayerView - private lateinit var mediaList: ListView - private lateinit var mediaListAdapter: PlayingMediaItemArrayAdapter - private val subItemMediaList: MutableList = mutableListOf() + private lateinit var mediaItemListView: ListView + private lateinit var mediaItemListAdapter: MediaItemListAdapter + private val mediaItemList: MutableList = mutableListOf() + private var lastMediaItemId: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_player) playerView = findViewById(R.id.player_view) - mediaList = findViewById(R.id.current_playing_list) - mediaListAdapter = PlayingMediaItemArrayAdapter(this, R.layout.folder_items, subItemMediaList) - mediaList.adapter = mediaListAdapter - mediaList.setOnItemClickListener { _, _, position, _ -> + mediaItemListView = findViewById(R.id.current_playing_list) + mediaItemListAdapter = MediaItemListAdapter(this, R.layout.folder_items, mediaItemList) + mediaItemListView.adapter = mediaItemListAdapter + mediaItemListView.setOnItemClickListener { _, _, position, _ -> run { val controller = this.controller ?: return@run - controller.seekToDefaultPosition(/* windowIndex= */ position) - mediaListAdapter.notifyDataSetChanged() - } - } - - findViewById(R.id.shuffle_switch).setOnClickListener { - val controller = this.controller ?: return@setOnClickListener - controller.shuffleModeEnabled = !controller.shuffleModeEnabled - } - - findViewById(R.id.repeat_switch).setOnClickListener { - val controller = this.controller ?: return@setOnClickListener - when (controller.repeatMode) { - Player.REPEAT_MODE_ALL -> controller.repeatMode = Player.REPEAT_MODE_OFF - Player.REPEAT_MODE_OFF -> controller.repeatMode = Player.REPEAT_MODE_ONE - Player.REPEAT_MODE_ONE -> controller.repeatMode = Player.REPEAT_MODE_ALL + if (controller.currentMediaItemIndex == position) { + controller.playWhenReady = !controller.playWhenReady + if (controller.playWhenReady) { + playerView.hideController() + } + } else { + controller.seekToDefaultPosition(/* mediaItemIndex= */ position) + mediaItemListAdapter.notifyDataSetChanged() + } } } - - supportActionBar!!.setDisplayHomeAsUpEnabled(true) } override fun onStart() { @@ -94,14 +85,6 @@ class PlayerActivity : AppCompatActivity() { releaseController() } - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - onBackPressed() - return true - } - return super.onOptionsItemSelected(item) - } - private fun initializeController() { controllerFuture = MediaController.Builder( @@ -123,8 +106,6 @@ class PlayerActivity : AppCompatActivity() { updateCurrentPlaylistUI() updateMediaMetadataUI(controller.mediaMetadata) - updateShuffleSwitchUI(controller.shuffleModeEnabled) - updateRepeatSwitchUI(controller.repeatMode) playerView.setShowSubtitleButton(controller.currentTracks.isTypeSupported(TRACK_TYPE_TEXT)) controller.addListener( @@ -133,14 +114,6 @@ class PlayerActivity : AppCompatActivity() { updateMediaMetadataUI(mediaItem?.mediaMetadata ?: MediaMetadata.EMPTY) } - override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) { - updateShuffleSwitchUI(shuffleModeEnabled) - } - - override fun onRepeatModeChanged(repeatMode: Int) { - updateRepeatSwitchUI(repeatMode) - } - override fun onTracksChanged(tracks: Tracks) { playerView.setShowSubtitleButton(tracks.isTypeSupported(TRACK_TYPE_TEXT)) } @@ -148,48 +121,26 @@ class PlayerActivity : AppCompatActivity() { ) } - private fun updateShuffleSwitchUI(shuffleModeEnabled: Boolean) { - val resId = - if (shuffleModeEnabled) R.drawable.exo_styled_controls_shuffle_on - else R.drawable.exo_styled_controls_shuffle_off - findViewById(R.id.shuffle_switch) - .setImageDrawable(ContextCompat.getDrawable(this, resId)) - } - - private fun updateRepeatSwitchUI(repeatMode: Int) { - val resId: Int = - when (repeatMode) { - Player.REPEAT_MODE_OFF -> R.drawable.exo_styled_controls_repeat_off - Player.REPEAT_MODE_ONE -> R.drawable.exo_styled_controls_repeat_one - Player.REPEAT_MODE_ALL -> R.drawable.exo_styled_controls_repeat_all - else -> R.drawable.exo_styled_controls_repeat_off - } - findViewById(R.id.repeat_switch) - .setImageDrawable(ContextCompat.getDrawable(this, resId)) - } - private fun updateMediaMetadataUI(mediaMetadata: MediaMetadata) { - val title: CharSequence = mediaMetadata.title ?: getString(R.string.no_item_prompt) + val title: CharSequence = mediaMetadata.title ?: "getString(R.string.no_item_prompt)" - findViewById(R.id.video_title).text = title - findViewById(R.id.video_album).text = mediaMetadata.albumTitle - findViewById(R.id.video_artist).text = mediaMetadata.artist - findViewById(R.id.video_genre).text = mediaMetadata.genre + findViewById(R.id.media_title).text = title + findViewById(R.id.media_artist).text = mediaMetadata.artist // Trick to update playlist UI - mediaListAdapter.notifyDataSetChanged() + mediaItemListAdapter.notifyDataSetChanged() } private fun updateCurrentPlaylistUI() { val controller = this.controller ?: return - subItemMediaList.clear() + mediaItemList.clear() for (i in 0 until controller.mediaItemCount) { - subItemMediaList.add(controller.getMediaItemAt(i)) + mediaItemList.add(controller.getMediaItemAt(i)) } - mediaListAdapter.notifyDataSetChanged() + mediaItemListAdapter.notifyDataSetChanged() } - private inner class PlayingMediaItemArrayAdapter( + private inner class MediaItemListAdapter( context: Context, viewID: Int, mediaItemList: List @@ -201,22 +152,30 @@ class PlayerActivity : AppCompatActivity() { returnConvertView.findViewById(R.id.media_item).text = mediaItem.mediaMetadata.title + val deleteButton = returnConvertView.findViewById

  • EL&UxUD|u+)X=w&_0nz8M#Dq{B@<29HtX5`5F}i| zn32TWTuZ2{yC^FBRs}-bK7|-rqVc1}5S?>D$K`+KsqxB*o{+7DN0i87A1$$Ce?(LTf-duyarjT$FA zf-SHrnQl2e@+@W}8nO20DWbA7fqjdWyo?r^4;ifvccA-*JHx?^ClrNi?f-C{2-THv zxC%XiO-r2Hz1wA+gxnU%nE+SvOHSZ@zzRK|u=|C86W7TgMyjAz>VU9toeMsbCkPrR z;3bEVUcr>G%dW70sCTxH9lKD)wzL??g$ot?{f-sPHIiBh&Ck1~;)@YW**nHAmXw~c zsFj`)`cyr_0{dzc$~{9JNgoX@nl7b>&QaKKN(3bjLg~t!AzQY=s&?#C)C1K3Qo7&W z*7XZBYJ$MF4JoUBRhq(1^MDqU8)i5QFhZP64iW8g#mlC38gB; zae0klp0CpZ`lpWqi6)RJ2vA3KCa55(@BDMlW`ICblz+m}Gme=I1EV{ceDof5+Xl%7 zTUI;;QP*=-g`c~0&9n5Rm&AYYXCDu632;+Y98GRH^1I62{Asw;I<8{UKBN7CHUI|G zYU9MDP4|(e7Y~WZ&~SXa)R1;(Phg*G`RJWYW8{~Tl)7P8ogv(|k zQ(e3i7vu@c?3U9CX-#?_xC$Yhhoak6pr(BTyT)z>X3Uk#@H);MGwgk=*Y7cvd#!HHj`kN{WBh z%S*a_3~VH^4dEqheZ6nGTm|RBc#K6H;d-u4-E0D?DUaw=c)B0Q#gb@apNu-Uc>suq z{%NvS1EY=6cOwvQ^XQwiwpiz_O2ob8#P{o#=S&QwWWr zoyPem-!X71?ah^a=bPCdxwkcV=xDWou!>5{5jNFHTg{FiNm*`*$(%E641XZVA^d_T zQ1ZjvkeV`W?-N|ot-s~>9UgHwsmth;LK1f|E=p#ep)igr!wsN2jZ_3bYKi<>_N*Op z!VBg+*LOF*EAu7~1~)`7R-rct?mPJ(&`bUMvg5|IfX$NpvBeM`DG>N#kPvr_`?ABw z=yODS2*(RFd@PE6LH; zI7IYWPb6$WyLl>``gh`qJm~shPrf6LLX|wo7Q93VI9HnuidD4;slb(B#)?#%aO^QH zdhrpmaPcG&2JsN94-$=#WW0wBB0q^K$<+&RJRvQtCNCBaejdBgZQ|AByHc7U!--m& zAHo?Qwq^$c)+|EZxMF+Gi|HX3S@o>Mm1tMV^%4Z z4aR`+++-w%81bwmOBglr*~ddwPqf%QMac1`8h+z?n+w|k&9_z_NKZ@@SNee~K{CyG z9MjwR1?|r%MSf&OtQ=q==juTMGk`w_RyS-nkzp?^I9`q9GC96ih^K5v{>aA7J0wh| zJ8_K*$ABF%*Eq}bZne)O240uC(Uf&=E>}t7(0 zAsya`*`4|a92QjI49WrmUT_QRDEnE~6kjxys~0H3VR0uw@R&BPcKM#}XJ5|u4F=PK zw~>q4c9jr@KKsUA8fv66XJV2mJqD5yt$4QRqTcTt^URkHAr^PSWYE-+IZ(czy)*-< z!1!YGEH;(N9MA@w87~N=`8J1H&`o%G9H96p#bz1F9+!R-<=!Jke#ZT^tJxDQkId!@ zv9g`eP_Zf~8~%skU|yOEtq!^hb3mt-Ca6@qLynxljpXZ=)*D?IZE(vVZ>J9zm;zGB z3LUU!P{OCp=Hta}qG><4mt5mkNx=exkH7hq6d$!zAr^Fycj8G`vGq$xu_=RuR?{vb z!vl5h`tW)Jf+&GK=`tr+#YpzG>Bh7_QYVg&jT|#c?_EG0%~sQ)VgaQjfwiuoaWUsu zi5qme&`cynpJUnP&a(D!^`EU;CMe^4O)WWrUtyJHKdbnK>Zek|5ALuj&xcUwc! zjIh$1IanE_$luA4@b#+-eJf0_*mMIK3Dfn4`RFP7E;Ez=3L)F&G^B8D0at|ZM2fsu zn`MMW_d_+Ns~t#l-kkOXGRT^#$yLc=w>*1*7EiN&nmZL%{!h-GdN5xjAw&p9KJ&Ga>wY#&`P6| zLYP@}rCwzYlAQ9Sz>?xX{i58b705qH9k>WC2J8+_b}iAKQc3mp(8i5ksWnf!WIPn*j$V0`Q5)e}-zTBDXLB8a`k2bUhb!SwKpXqYfRDfW)8Jf>Qt z^@$cI!p6w#y**s_6YdL#1{1|)>~DnMx(4!odO})|np;D+X~P6^Ef}_Z`yUz)b)2Ja z(UIf4oPdV%Q`NP_gpeM+WA8zkxX=5+3$i@6P#2anX;mXPf#97`LJ$@iVTayL=qwgj zVsQ&FZ1{kMa=O-n5ZEn=4Kq#Qpu%5rNRvAI(L#6FZ&H)Xz=E68 zvYTjQID!4|8JW12`O|2M&c}~&srV<8Ae+$cP_lkVD$1XwK!$t=!&>`%#?o7NnuYDR z4V6ajpiyT)OHT2o!xKJny{7~$flw-sT9|y8eslieD13yPe8R>^r%-fgfsNhX%C`B) z;6|4(co+HnrL;6PG`zr*7#@P5>_~T_gw{Ll#-7FZDNwm;Dt!Pi<@KLl*8Do=`?D?_ zAXRu`*}lu`-M@l_WUp~FkBm1CmQ0vC#$|w2r_RRx-e=fi`yYje8~@P8h&VD|KIc9TQi3u>(pS#V{tfI^Wp{SpGM9-j3)_3QXDZS zcuWk)uNzoN)T2y1oo8O*z;%iFM0=!M{6>p!$H37H} zYj7oUz|(543YdN`$|_)bp1&8mz`+~c<{rrKQX7GgKC%?pT`p=Wi5V6D+ieGw=f!(D z`fgW|BW~D)3vY*ZxM9S+9}MdGn_L`~FI<@~tj={c&4ipr8W2o6H~rEApBb%&AVwh; z3)Tw+c6!Q4J;Y3R!QV(I&GHq}IXSk~p+@mAp6kXa++{!@j7tkKGoQY3GN!usNVQl( zIJq1g=fhB6VpVPNlLVku-dIBq{}@Ph6Rg#cj6Bk@*`_Cju-KNwVk{0K8>`wKk@b`7 zZYM&XXHo6Xttn0*wMWz+m^jK6ea5(q8keA=Nm>^NY6qkv@BRuF>*Ao8@s5S|kI)BJ zu8HZFL{;FRbMMAc?#7JrQPuC;QL@Q=|NczA6b+$$YF(O<@o74!&8lDRz3-&_Fy2lW!kR zprX75fBQDVr3B|UIm(axc zz)$PLyaxjku(k(w$QM1#u+^dLrQD7Z{TUTcymW3)SiF$1>(yn-w1mmH|Vf z`;|2tinF0NUI^H=P-;&ibat~HOWCZ5hiqoE0@bPBWrBv;TwiqxgaT!es+QmI(&phf zk@qp?v7eezigh1{i*Y7?8ws zW7XJ6cL^y+)TF+AEwY`XW4XLOeK#57SAOXx{Cjct79Gd;PI4U6CsKC_PGGVx z57KszF`U{3sMN$AJ;6rQiPMOWEyI3B+@V+mRBevfvGK; zZ&8!3KSB|kl7J;i+|ZUgO~6a+KZ64;^g3ToIX{KI@zCy2 zSk~mVpxk#o-6fqTZC{#X{n*W6^qp^-7dmr>f-5=mN~#@L?vttf;y~SxOm1kmTGZ-x z?Vs<=&EBO1_%b{9y+}|fj!Lk{P6=nsHi`!r;}?a*`2wpA?v6}*y>d&Vg@3An4Jz^XxgfTBZsag+whU;mVYygxvCYnA61b482SFDQHV&WxuWA$qz71L6JYP{mEs+ri@d*DU048 zyLQQ;m)rM9{Vd6kQ ztg*R1!KrS+2Q$V-<;N`4Qj!)sY_=zbh(4peeBcfO1Ni?UXMX05l?*f`AHbwMS39mC!4wA}m6q zT!RlNap!@s$YC|M!^z!8Y{|4UQ-ALu24|e6DpMgr@VqD0z)Y)GYzoprNHGR>_oWACCVWbh#l$^x|d@j5?YzeY40AK30IElS#5j zY;@j6?`F$&VQp8TtiiT2bG#-K@OF(AixCM7A>{2)BkdVx)hvA#Oi<>Wi+rGWzh9PT=3O_!NWSbzp1 z5UO%Ps1P5VIYo4IHWTsS)Jxy@xzF){_O#-@nt0(m{e9zzF1f9-z0E zqZOkL)JVwY$>cEXVUm#g9qVXlR{X`>1D#gad#-?%IKs$n&xS8}Vm5!f5cgrkVGigP zF(i}u*GrWxiuLa4&U7^-6-9flPF+Nh_Yg;u^cA5tLfM~oV zp)&=Gt{P-Z1PL)RMZd{)etjChyqNK~S9cIkxEXmRv=WSUll~xI50@qx5KR;8lMS&A z*)gV$jpQ+YFi~%&{@VyOfeff=IcEiFHxk5M#2vSjNGi8j8wxx8(mV6{lov z2~`g}-{Kg+EXrfXZl!Ds<5by;&p~^tzo8+&WdD{dR=tICUvmSXKlp#dDF?kVOaWACa)G#SYbC0acYA5u{EKRnI=HA}IpK+6;-+Kh@v0T5o{D z868M`2Zn+7KsBX;ncU$+yQC7*f}OH+NDc;@FT(D)zETf0d*A4M?bPKuE7=;~c~lx? z;jSi*%Q#QI4YV;)e8yke2aZhjpya7@xBrkHw!i)!T#NtZT`)Q%lvVv?Qdgsp78){q z>bd~~s92q{I1!-G^K}Ho31G(2)(vNjN_085s1gzxt7$Ge3&8E+Gg;_QlN?DoX^pRR zS&+b)n`xw=)R)y;ce1`dd{~pY!sUDfK;fZhJJK>Qz8rxmTB-5}bL2h-9^~_WtoOTzNS79KUY+F)0E-Enk>%8oamq#v7{bC}md z=Wo{L1OINY5f)oCvVI~Apm}NJUNBnidsh?~uU=F-@TY$PrhnoOGWU%J zqIrPEWG1XBl=qHTd=x{Qc})WWFblh6SV7mGbHwT&fhGssH6MERy`OKPuQy&V@ov+b zH>cbzztBm}$G{PzbZ#3(VN2gm9mDf(QjV6d#RNSJEG?qZJO--Y5v*-)d9)>oFzX@K zn;cJbm}IRAiP>|MrZ1PJnTo*V6j`OZQ@J>tujes#M~ZSN>l$v1$)upbLJbFO%}uGO z+k=yl|Gy5DYb$B%YdyWoH8hfAM={0ZEiI&s=rZM5BB&>)c-db4`mEXsEGa1&jwxa$ z6gN{If%iftl^dY8MEFUUZAJZs<{;s0#M3Ie0M$7ceyMb_hvW!o<@<`&1g;6u}pYx+uB17Ee)i@PC=&P#DdA@ol!OWaGwtZ+7mB@tjm=$&f;J?Q4py zpjslpE3-a@aoq0V=#WnKpp)%j`amsx9DGtN;Xdg@PX4TK*^3Ec*lAt9uf7%oO%5H> zfgF1W78@9{uJf}iSxHi^!_3&VPL~mV>e~|!))huPm_qYdX^|EM6{=VTfgFG0B3;05qmZZdJ zo&|0}x3jDv>FWM7231Igw4<~l*pkxXh#!9*`y#E{@TC7Fm_VJ(&6kW=7#b+L-CUa> z-WhXsNf=sr3bSikgO8xblG0Vv@9gzEMq{ z*^(i=^Pr5`g_0^+pnOJ$!U@YUm$s!TysL|Ej?ewaju@D+DzEd${|BfCzsOKe64cLe4Hls8d3pu}>536%d&z&ifRAT&Q4`D20Xd0* zJ-Ce|XN1;AvDn*z&8l%=ZS<~gF`jlu+QL`n?&m?P$txslnnFI_C1~`_?2AzfgdOU1 zt>SswLC}NU?d>QA3BL#rAgEw?2#jwne_YrOrv9L}5&`Af9sa^VOYoz*;0qknaSE?h z$v)$=VYU0at_{bqF3*j-PD%aQfgJ6EowS;syzdIiI6cUs`vHe61)Pl%nmm|jMoZhr zhuw|b>2F(rIJzIMK0+HFgLvR^)&JU7gt0_@fi8&fWBzIUGpna|ZEs|^da~SV?6*p_ zx@x!nbRptz9Cr^G| zFK@7QXuz#u$9dY~zJrd9k7>5om0 z)e?)fUtXZ2Wa>_c>x@GTiB#oopZ?P^4`;4{^y+h&h4z^>NIm_NVYGc9JFPUCZ63|n zJ&{%$Osf!+<8<{J9hto=d9n&)M#D4M;tqKs+DX~U>$M4A&|`gnI0L}<6+7g8D4;a% z3cOO5({m57k6F|a4(*oLz>|)U+i#4u*iPX~oOEewcPmpa#AO!((uX_v741NJ?f(3}Fsm!cR3JFRD3Fv3_TKA8 zpF*`o#UilM1E6IAGW2vQmoAJ-t@j&=c3gxL^<>Y}TkT}Nf=n`T!%MHf=q+1W$!{0o z6I* z(GZc?crOr=8xnDJR{pgWXD8B*BY;Iu^YeP$jR~E^9zZbmd|e5q@Z@+|Yoe~SBm1GS z9c#OaKi@EnNxE7Uq&YiEgO5B<+TLvQ7V9wD6wsQSG(;tX=AEE9RyP)2mcT9`yUQbG z=^I!qr4XJH5oN$@C4gn0Kv`c}=E`2=G zFC`J1mNxCMCfeKXIM$}6m}XeD`(vghTpW=_yrb+|8P(`jM!(C8&gm%U*7kY=mu^HZ zcdLI+H?a7N6bX@19DA!2OBWe^t6_tQ9VS3VJ5n-1BWkUaCD-oIdHT-MIDalZS8qDL zud1EAm^K>>47!Y^8CPY<_`7h7-AKpKzz#J|SMZ69xN3jLL=iI(p>@}1Wdno#sDnj| zq58fqG0eD1B*N0soV%J~4(pq;=6Sj#vj_SjceaK+e58=EVRV}iCQ-ce z5A17#sw1X5DOt#z)W!!9Jp}?m;9!TyBLx@PL-@WG^QCu`K@&0uIdn8C5Xe*E*QtK< zD5A#7rBja_TO6Rp;<`uXA5%t`8=iy<-);a(ff;7ZRgiExA)HC7V~BGtZaGS@x`BA2 zwh;eP8mmH%W0T?5myVOqFO#AL?GT||-a*FP8xBvid^mu6oaLzn@#`w5|C{&QC&g_g zVQogGO~UX?It;j79nsH<{ldh8W1ZHPS4S<7OQfJG>Vutr4%nloi zw6`nGdjh2HLKnO+H(j)S+hk>yPpydqGwcbf|^Vg_l)LQn={m^Ge z;9RD88l8{ND)+;hEzwgGnGqRY^egpwb%`?d1B6>OTAb4YkKs)?KYR+FZ88LkGyj(i zD>aO|Huzd`+|pa>UV1RUxvZ+BiL#R>$7FRy{^6uk%63l@!C18E5%@6qh^c`zFx|Rc zWH)gP_DLEirB-YK{N6?B=H><V6Mo2HEX*LXxIh&;EtqL8 zqA25z(%8ip!?RJ${%s&H@>PeE7WSeeJjDuEln_fZ;Ua8=BD)H~-w>Jfm)P9FI1<5x zV~c>tuZp&NB-xH0Hwmy;(kJvuvsC%1k*KE|tU|>j4Q|5)yA01ijJho=>x3VY>Zsku z@{R;4!l5>fGG|aml2Ct>tqhm(7YU`_-z=}UGUAZ+OsQDygJyY!*qO8%z^kWu#bV`6 zjj26aOHFMAn-*p&iDUnNa?Kaq!nv>v-`gV!2+gC%Auxv_v7CI37heUXPDxvt>nCEW z&&Qd(KLB$d9$T$K*4L6^QXT#krJ$Dz1 z#e3E0(3mhDw@rxnl~g#B^mTahU_Gy2dtO&YiuFB9&0dF7b7niiObkT@g?;eV54Z6vUm zj4K~%yh`P-+*_y*+1>+5!g@*eJv+@);)yqtj9DT|Dm(I=o9};@QK#SZ6ayf!9|66J zG@6&7Ia=|AU)i^~c@xnCH<8W7X3A$r*v%dBV8Liff$=H?=QfwvT=M(v!r9Wo)%IHI zHz}H~ti_p^Kx~lhJ9$&Fs+dPOCMd{s9LK{dL8}X8xqt{6%dCxKoe_M=NY&51Xz3=T z(QL{oILs7>eYF7#>UyD|AFuHc2;t2jZ8cTm;j3-&2fDkI{c_s8er_=pMvby3_yO=z zy<-kF+Wl&882;axfHpJo#4=s8!XRGOQacZrG~dppU6+A-m@R?!FKi12h1C3FOcp#< zuMK6ZaBVkNx@LE3^#3a0x;O3q&OXRcy)lBtkgm}V3NvnIrEe=o-^eWTD@&gIIYFGO z8-Z`|XPvAzNVyakI#H|xP}P`-M6>!rHy`rh&E0}-4JnPg@jtuoXIBjPjs;oK3gVRN zSmXLjoM8^wTre=}6OP^+b-DvDPnSxiXL&T#gRyQbL6?<&IQ@zi)JT5(aFQ^h=Co4p zt}=&4<;Iwz8guMgFq#UV$xtQDo0Ver3I8^ilv6+}S-Igc+)JX_WusjyQdri_L=vT0 z8<`qIWjkz7UTw0|MAqq3i2IN>8A%e%t(_opPxnNTa|RPGgjs-yHaTP~Y$WNg`ftn` z<|?FxT5b%=6aBQbG-TLjO)<^?fyE1@Tmi`xw?}9kqJ~i$6d+Fo=u>h4qdllczQ((k z@x%$1?Ub_0<3<_W|9gvO)`*AHwtkmMwr+{B1qR90Ni_wjSma=B*`j_~t%_B}AJ&i* zm&xj!u~{(4wQcFGw?fdhFZ%zV!qL@4T!cXEat=S-{*jNVIk6Wnwnx_dtu+g7UM-{kX7{$ z=&x$`d7HXqS`0(0rZ?Rj!uv^aBeD!SUrbNjA3HEpdxgIhx0S2JW}mrr6k@HS3T-o$v`2`f zwp}8N!Tu%s_g(}$^hrt>vhY5@l<_YDcjhQpAoKm@>Uq5*x@@pc_cJ+#h^4K{&&_QK zqDLeVu4u~|6txkg8uf%Q#lMwgcc87|2xT9*g|aFnpl9QQWH=wz2vhXGttPU$CbcWy ze}hBpk$7eV`pOUuDQuCR#<81LN9%K-T$;P?{umc+$7*3mM)(@k%oiI{)XGFDtQ1C@ zpmcV}e;?FMk)vQY403yJUFYlu-tPB*J%6|UCx%lvqBosE%+CgAOv#R9;38n`LM_l) zDGnDudqLS*R=63FZ*nIoDxgf`iyO#`%+hl@^pVz)4gX#WwR_3Gog+>ytOmyoazJ_) z#<0zc;tvb|m?b(Yx1`yS`+<+(P+7O=N`7bL&1hBujI!~y1$qt#Bh zhEAlQP^2TlhSj9r%Jis~X!k@_6Xp_cSeR&#Z+nWHHD8#h8IJ!WgYf9H|JOCwD@9)v z`wv2d0BsRBg>2p{?$bdJg~Azc;n>X3uQ{^)Y@~ZRP=_lsL6Hn&O=jfF`9u&zNrETf z!6>l)>A~}oe8sdtB)$pDQRdIv<1J-K7JKa=p&4!m!z1u!*>mT-v$C8G`_P)qF6#M> z5>F@-jnI0Lh{E;Hgo_hkLkLo*CC$9AeOP^@7xF-%(cF!uNdj#h(EK0g%p;|RbXLfM|g?z}@pm_dU|DtzJqJ^Q8qLiHWl z3F?_+QDM^c*s@bXZ6n}wujUMQhL($Ub>sUrC94EJj9Ei}6P*!aZnTSMj{(;ccAViPPe=#7aMF4}gzkply2?O_RK z&s0A%WxIkAig4}_LFdB6vage;dzUC2L1He@0|nu=n@B~=alwHQujkvxVS#SLf1|78 zq!6n9KdnDSbfH6By{PqzxrdOUCPu}zZ98p-fRg?62EkgJjGC41S}mc^vN zo5kN58PULHullzh4AZ-**^GHd#zA5#bIj8z`%Ob^u4R=Tr`Ouk{*b6UL7d60qjN40 zR8GvAuhqaxuW(`MJ#l*MKI5r6Oi+DD9^v%`k zlrXA&c$>*Pa5mm^*r(r~#H`reRf5+w=gGPKCfLbftDCnevKe^jq=%k$GiFuW;}loz zsdB^-v?uv}?~pzRU)stu@)ZaQirM6F`hA3|P_ zA#=MQ-KPU#zE42hmi`Up1w$)fRV67ZyXqoSw8v$3m0kPmG+fW<|pbSs?Lcwq^5IrSd^sy*5Dn z;UOD71a5o3*3K$H@SKt*$gG+p{hbA13~!Mf!%JIx2tdMIh>#C00its$bYc!SF<;FP ztZ0G14<~+UbUIQOM%cif%OOZ0ekFZy94Q$`W->IppI;c|M{%Sy1HWS z#fV7b26!2pg?Pj+GvFU7l-K-?NBQhY{?=-EI^%+UY0+-|Rw*ft-&|K?c{^WPr|tW* z>Klp#5EVCgN%nqo3#wN54V$3R>4C^O6M_d(ZXouLiqvjQ=3uKx0?uMv*8JrS@p=P?Z%? zIG)nTge+d(?<1PE>(K#`DP8Jn7_M(olZz}yu!wkhB8tW*=*!_W^84(jSl7NFeL4f( zK`E*2BusuY_=s)fc&q%8=@1Q{3~Rz0H!X=4qUv9q|6%&$zFhV|@8+Ozll>-?IfI9N z8>yrQ?8rwa{YEWJXkwSDlqQKFFt%KNBc7ukFFgqEuoyVCIu_ig40e@c86?Yf;qd#3 z7cJQv039;)SFVdW&e(eqw<*T{{yW&rMy7w=MiVN6mKX+4rY2^K!`)r@sEjQ&1 z<91IjS2TU+(WOsEm*Eh&3Ce_dfxAjfZaj(tHuJ_Tnw48yrGL>OiKXbjg$V0{UapW) z#Ba&=_2PRDpKa;1PZ!9{X%F|AtMj&q8tUvV=RxUKW1z5Ri*easNS^t6frn3sZieUL zNM#4oYfD_1+`r-0G zZ(wbE9UO0w$ox&F1A7ipt{NSkh1G5ktDp9^OS>PCWcUy~_;lLOVijS?S$j*!t#%;- zpd$|VU+{+BcP$|h+M|sC9B^>{l`5}lwE0MEDfZQ(X0j%YT1IwMc?-uWf01fu(l6F5 zOZnz|acriR?)VIKnjY`j(s~ zGs{@ur3 z(S@m09a6Wvdgdvyc+#1^l*W-X*XwOH>DmO!@ipzJ^;-ov)t!VnmE0H3r+3Vx$X%vGFI&0{hCEU ztR}q-P!Y&(8R9C z0IfYap+KjuyU~bdBZAeI&wjF^Sg(I4?o|+wGKl+DPu@21Zcp;UCY2aojVwVs3DV*s z+ZY-)aLP$XJ50paW;Uasyf9e$aJxJn#*yu-joyH^rHAtm<-0 zVJJi=pokDA!$Q@h6yEr4K%Qmj*jPnE&;blPxo+LMam^B*p0v}ZHAn@bp#)`$`YmYh zaAgD|e*d)tw13JLfj{X9lg44x+%{sO&Kae|(p>J7pkFiu#~j$SN8EW1Nfk|rYVQG6 zD{pBcWe{V*#(n9Qf9xvWIo`u8QB7zWMuk&dWSQ zYruJM5J3qH@AH^Q^rR4xqx|2pLmN|jbvd*ubJxW_iSb=g*+!`-FE4?4Ux@Tr-mX6= zLEj!T^`|EF)^CRdD$cn84Vud3VFvo6)~O?eTffATSTvwRVgSM|Dw)!8~RUz|GfLbx5R^$Jb6gC zDmrP6S4j@lq#a8OfPccc!rdFNI3CYaJtMN(V9fJ2B}Joz%9WBbu7xnByiGYja(TOO z(YH9)P1@&Xz9ktax8sR(oT7& za>$x!h=J}lF^A|V4%c2Q9~P7nd<||0OS{bLjvi)DNXF z6GDkNzAo&}rO2?sD0qk?y)7{JEgQ)xJv3T5!Yze|w+?ASu}_Jjhn2AKR{wB&5CK@d zX`IWVyK8Z*`)A*==2N6ackR+sQG^?#VoD&EDF!vQ<~dq*9Pqj2(zVK$&=h)E7fZu? z^O;NZs$4kM@iM7-*%FjjxDuZZPD*B>$gyY2WQ>PJzMG2P7|t;Moq*gon90|hDqmn8 zJZdwV2>VzpyLTgTks~nw*syM9QH*=kN;Ei(cex)W^4N6&~ey?$)gr@^;!5BX#XYjb%9y zPMKP9>p+bROe~JD0-xC1+Ix&i2>7eEY%+fRZviG`OTVqdRFWYgr#%ooAxIfy*;SL> zl4!d?ViKhTtB6a;&;Nw3AuNuaJ2kYCo6_a0w&S8WUrnDdtg*x$2IU1@$`l1M-bOn| z?BR0Dbux)4;7zszp3UZW$N2JTCl5fdF1Ui3Z@)XoK5i!DJg9wgMe9sY$Fqd+ycD=6 zb3W&MxB2f|L!RM2)R!|WxcHmy^{cm93!ZQwz=f3(N7L-#pR+8&2|To4yFvtsS}2LO zL{ch?5*r#+a-rdtv^0#kRO&c%73*4+rkHvB0N0-nW1lSHo2w9h24M8$?^FJ%zNR6n z>btq0TYsum5=S+)#3ga#0G3s)S8Mvj(fn*NLl_>D&YSVIc0+eTfQ6hyfKk=#~d9UKI?w&#qrPz3CcUJ z@ZM|QiJG&6s)!9Nf>V$mX@lPD>9=dTUk&`n8k<$&U1fYw=b*%o6b^e2h=LBKVp_kU zhDB*c_n$!UUj1@bdL1}t*h+}g`vE#9$z-4&*)YSG6NbC5P}^CEg^%e_-c+2{myBF7 z%--`_`yk(%LA+4?2wQG4v`;bpNfyH}Dikv2WucCVQ?w{`6W5}{A0`*%u&}*|*>~JI z%3oh@cp!^y>|mc~?4x<8=_>x(ZuWnJ!&)`PSL88~OVd}C&CdM;(2`2ZCJ;o`fwoUa}p=&f{WhhfOV`rCI*&6M~$!?0?OWT8RZXa(j@rb zO<5ku7{xU%L=lfnU}wFPo~M`sW1M45?sx_bw!O79${-_7VK;pb%1+)pfb(!EyC^&( zIp9FNi|#+(R4W!u{&ua=Hr8+L%7?p|%R)@`a?=rfO&HS3_(YD95S4KDWReJ{b3D!( z;-g%2iJ?!|sjhdk^EbDb6FJlis7J*iY5icXc5+Aycwd73HbUS-VH`W8YLtLb&*Vem z%N!yP?Y98XCE=OasIt}Rb~gk3GB;}qeCVs;$XeD~u|H7`lgUR?cCxh6Rz$TSqOv_$ z7WLl6Xq-IHwnT!tt+J;mD%C+wUuNA*==%xuj5~e(49gr^cF2pF?&ZbvK{aP;O&~nZ!@R z$(l5NaaX;`%QpQ3T)%@0k1?fggTSXJOHXh=U2hhJaUJ*wzB1q6ir!vVA$AVAK=IJXxPnW0&_uZ-onCY&t*qNCiQHDt z0x1RSLdH*B81IzF@*!Ww$7%BBkgsCf65dI!1q#JnCm-t1gm|svtc+pIeNA0o^k@mD zL!2)O(@0H;hHfQ?nZQ5qu{kNdP@JPGz84mrnW1Y}8@rWTb*ArOlQw-iEeYRHe@0k* z57ALJ>Ocl|4n^Q0rL;VIB!9L!WW;9Jw8}RK0qMb#{%RtWFfXr|6D1xeqCz|k#t)Cq z=7aJr`-vVPIM$4Ko7a5=wsv4=We=3`nXI(rg5^7PD0%uTAcKrr< z&0|s5W-kdAZuaOgYdidpl_zb+7QWl$901P<+1n#RGPBp;+(ixb;&Y10zp_lKad@d- z4t6}nR_DaA;YF}5j{5ewlgvDkvnqIZc~)39Z7)8is6FOh4&)W2S%{E`V27l)bh_db zB#0fJR!1Tm@@b4kTbC)pM$XkQ}FhGLtwPpgy1Rl7# zb4H?&fV7&TWc^0}4>>;O_POmJa&s);w=t8>plIIWIGTR1y}|(GjR`Mh->qhvzMtA+ zd3nD_A2~|3EI&6%l-2RaXxaV29RDir|IiwfZ=6)~kcoI(O5j-c30L|FMN(un1c8zk zhUNoDYTa)z@vA`wgrkkK4_oKEP!0{Z!ihlog9mMkuCakS32weXs2F2pZiVB*oyo-J zR-YU`tRy_$K zxX!Ei8|o;2_Q;*fK95Bm_YQDp6I1cQ|LohX(IkI8VLmMT&xdtTjgV+A?YEJ|LIpKI zZ8+2=L9xEPCFe;kUE;D~Wtu3)w% znQZDSl6Pb@ufi;|jMo&RYq6MSaQt8AAvx#IZ!_e);OuST1#p9g_nqt|o)yTb z@f>F&=0YY4%~FvfYCzR@(WLM1y%0ji-|T~Moa%oenk`$Uw3T!PB45ypm*49|ZK6k3 z<^@^%>-jlhKz}oCMb}ADrpeAPGc_anR4C+K!3aqgXc*o!y*x!-(MRv z!-YC%<+jCr_kR%j7Xc`1l#E|W!`su>EA`zw3WLBym_)8}3TG}I$7Ft#11-n_2kFXk z%m^CP^LqZZ`4%{Zj9k`8Tn0%1>BY4Yx@CI+nn8w3RwerMV z-}$oN%2+AFC{VL`tB%XGup^cO!4%J)!h1&N#xd?eW&lSh*i#iXWAV1?@4+mN4)e2S zZw|Z+eQYOn9;mIcskv$`9e2H6>7BtHX^rWfm)A?t&1rl@d*jVWDfm%M^vnhJeYt0+ zNa{e(O19#m_5G?-6b5JN2Uq0F>H+J^$t#ElbZMqRFR2!qT%>a{bl^m@N>)f+!*G4g zgdUD;FUyvgDaX*X0T;@mD!_@SyK&GZdv4X0DcCu6$onBdQpyaI!Q_+6G!wDz2>Sc_ z%|JY-a1?02Or7p6_Rz>f7)D)7)1GGGNm0-->EV#|3!Mtdkl@6m#j!%B8O2LT!CC{F zMv$`N7|P?;K>Mv`AO8$>4VMphjHITVM`2W9yNFKXQ8*gjbb^ya@OrD`Q8eDbu~w#T zp{=tS(Z=cvKCTrjSw3&V^6A2a5k+r!br{0TWHO-hbzBwTKt%e0JYQg-xug=Kj!ScL zJ9u?C_V)K2>fz(^vki?c1rA55-f}Dl#Q7XG+1#o(%3@Y^-g=b~>FgCH7Qv%SB&1UU zQxCX6=^03!STZXiXUlH1>R|UIn){@!#qtf38#3goli~_Ob*+a)z_YLD%99AB;1K8! z2s*vQoHChAXX|`e$IfbenqglC7;LrXQ_(4Bsz;|mS4xSc^;CmaT6T zoL>v0(J;K4d0ib;n~*n2Kt>&*@E6J-5OF}hnRBn;hK!bElOSdk@ zu=}zHc&0?J2!NLD^z&;+RwuOjZXvRGg~pU2rCMLCE_u@@yN6e3x_3IGh%z#Br@Ypl z)vNJoSxae-q(f}&I6(B3t`^dB{@fd^X%^?Zn}&x}+luM8%AKdp^aj^LlD@P zZ>RH9OhcXE=c5b{x9jAhga0}2-I^r!+_AP(m;UkG1|J6qF?d1gO7nOqeBR!~%+cv3 z=apa(QIU&EpqhDPVZkvre4_+(pS>-Xf4bc|itbKaHH!0!&AvSh@;SaLvt2I*1!}{@ z?wXjac~!%&sK(kfZ4D7*YTSz>GEk0#W-0O^tgq*W5NJ@FUq5k$@iKP0a)){EJT8V7 zOC#HrL;S1!#rBw2UD{gIr3_7xx0Vx4W;GT4wq}$++&}6-yn{wxUU^xCYsi}nSFfHk z^sFeQ#C|RB@u1tdMDRvXAAUr7MAuTp7Z97vrGP9_a#cPDggt1@yK{`-5(%@P(%nT@Jgx^^G8AMn0wh1VkgEQl&Oz>fp;f=$eiYbLCl zbVoFxFL&!yL94}}E^!TjAdDYZcf zb~Tt2{A+X1heZB&se?Y?vrUb|d@egj1Xn?=aGx;YVjeyGOU?NZ)BmV7Nt%Qx6G%WH-y8yi2!# zWAM2-t@{{IpxViz7)K+}4=Qx+1y(IfC*ZDErtw#b(IfPLy$I2>cKMd)C`kKB(ld3w zCnF_bwIr}CO}KTpL-q9o|1NSQsw^-+I`Uz;o&Hd}{BRZ)0P&KIgf>PX5*0e2!O2%) zO|O&_IiwBhB@a&3jO~*IqINfIs<5!uv~Wt!%4(NYB3u%1Sd zuCF+_U49kCy#thWa|sKuG@iC>oEsPmJ>uNBtPjNPy2fp@JSh=xg7d1k6R}JRTZFc| z3K8U5hla~rLTNG%?e4&?Poql0jVB$Q&rrEg%R;VsBCsc_6uw==v>j~GuV_l9Yy4Mi z5v`-9k#dsXg5n*gsDR7uBKRoaZchA3&RsSBE26cwc;M#Z0ze9vVS^~5vU2o|;BEXY~PKPSU2HA37Ey9uk|DLdttq22P91*rnCD8+{>5%vU=v zX@I<5kxMP+oj2fmy$5OA8i7#n!^>}C)CT-F0=_FTqU?5fVK(6OzN`k67vsX^WuPP% z=DJ_4VA3tISp2klc9AwamW6hz9kL_lC5!!2o;3M1%n!`gb$ z5*88C&1bH9OA$7xK9=;OincRU2^ZnJmOin}M`p~!ljQ70_& zoaP=TPTnO9TWX)Ty{Wrz_X_)4A5a9^1Eb>H|2C7bJPM0qW!V2idaQ8$q+co4;w0E{ zZ*=mt9^AsZL{pecuJv?+z3A4p=WZ`|X(Rp7)gpnwaN4I^pMwN|X-n4p4v4#-4leRC zZEtb?_vzBv70uNSO{MAxDiIFxrf8ad*v4IyAYS?H8sQs613)IYvq6shh{y$!`s3y4 zf=pX{REOhVLJjw+rjJpRsb9M}O-49XeX=oV*y6K#wf}j5xskUm5J|-wgXgK#}!03F?0Mtz)Fu2*1@;(Hd@45ync`#&9(XKnW6O zU>?dYhztrF!IXAjZZQv-4SP8uV~v`Z>J08nJ}iu9U<8|px3X!T@zQ)_KLWIHP#-#; za;J$9z7CR_>?(Fh58ooU_!romS+q-9LBX}s)ZPQydn7fc{%!hcdoyKhJz!iXTw8&3 zl|=b{N;_0!;fTR?&nF8yvs`vNj@V81@6}vJ!Ij;VzbJEMT=XV)*hqCynK|c@AgGl`5(fUEA@WmXabQyw)$p46HCkO%fh} z3vZm|B~7p5p^YFtvea=a0qhg&N7n||#^FL5chY5z&9?(TUZag|wMWi&ENI-H)uuVJ zXIvft(!@z{y-3!yvkts$M>pz5%zt{sfm76wG|N>E94JEWE}A;xGuoqqrqEy5kXkY6 zF>#4(sGQUvN|5rabJ@m&BhaM-GK$v#k&w&2{DthA9a=JXi> z71Op)a#2DjMhR0n0r;a$AhbrTVF-bda!L-9+*#`fudfQHRM`^+oQ0M}q$62L$(^1w77Q;v`e*=)w_|g z6@_{N;?KjdQ7h1$>`Wy&AU0ZAuK+o<+<%%89>;+3Lhh#YPpAy%t+f85w|XdRs#NBni+#a#IA`Qzk^uQ>RSG_juo?W8z?xfuETLPM za4Fv;y|TCoyC3Wwj<$R&Yy}c^y2t0vYAP0<51XDP5uIz6E>hf7#=8Q2+u58$uFH9b zBfli+{xaaYyZi*WdFY(AV-}v1UD+mgL*W(6`k2PGwDUF@YtdM@BTM7#g+R!yQFSuc z$zGU}k8*8dC?mXH^76HRlx=k4g877|tK2ujUzGJ_#7FKsM8bnpw^%udz~#DhB0mTs zhraUYr8##9J*Q==`D;VRKQVedh1d4Xxa^#^DF)d>BNW9^4U1@Z`EXpP1&4k!MpAcV zEvY*5iLsN}*x{&V)%H@WKg(@`$s>U(NT=yJ7C1Fl}gxDavC*%K{8h3 z-7~6@|Xcv|I$yzQ0`Nk zxM9;*et3VXp~5Cjcq%oHv-p(_8Zb0yNcVufK{>MNddFFD3Jr3Y8l(%#o2Z@SoGPgF zj;vvgg@;p@s#319?lEJQMIm+_G`HD~j}|gbFSdgK zq4B~yUIQ;TB`!u{3tC7OjgDs9rZhtXQ3Oqz@3=toI2*LTGUN3EfR5OK!;Z^viEkTn zjlwZqGC-K?H)AQ!Fpvu@_L_Q=f>+`8EOU~iLS<3cuH-PgRW3=kbVR&TNR-q>SB^Lc_Gb5p=_@p=qyQ#kPWA!VCh z9F%Pm3p;FK{8f&?5IiVwroMc{%P3lqmwHsccCC;=hSh4ofIHws6uQQC3l|^bP81%I zy5HU0PdhK;nYFT|r(u{NsUaBU)Cyq}oBja;)8Et-uznYngmbM=Ge=FIf8<{kPbYtZ z4tW){7ZV7mm|}PiAHIMc5(LimK@jlH^0$uY@l9UV@1gG~ahR2{e{g^{ZT$&QmWN$O zq(CAD<+hZ)t-~hMv%dGXFx+_U6jhvq2SZwsBQFy&toVi;J=k7JARx6g$kL&dMw^e= zUoFmuw-%2}l0A^CV`L-h+k{bqOxz>O`z5LI!ne?O;jPsNVeRO)JlMvDNEtAl{=>cT z=mG{s%RP=_+xng8SGV5qqSWI^ggP81Mt591`@Fc;zfAfP$va6Z9Q<$rRSkLq*yz!9 z6Vso&YpVut@jv92oh#4Y8AYqhN11n};lCV6O95UcVNsPy=={99D;MU05E!(fhT1}< zKPRcs9rI6P{A|=H2LJ$|6sV9g>xbE-tVvm2nrrd32`E8Wa5*%WK|aa8FXsCVnB8Sk zIt;h;Q!n?gOi9J}J8|R@T~Z9n9ZMP)I%~HJt>qd0h|@Up_hQJX`ARq_EEQ*{ zxOh*H7Juj6$oh>~m0nzXA5E(^G_8KCsV*vOU-JW4b1xqbA4m;!&vvCfz4V0FA0{st zB=uA+z4PFoDUt{t(#+tqlH-le=SpkI$3322AY|E<2l=@h5nZ%N?!86gyo38gqEQSi zE);o5inXIm0<}vrb4X-it7U*-NFm0;?bssAkzBxOK1FuF-~E_l2i5M_FF%$!pKw$;D4KQKBqE zOe0LcGqL?6${IJq<^ZpcZ2s^mA~jqqWmErgumwDCTFcXF3Q`gH9qkD%ge-|C^&0B0 zRoeh-{?HFFtT~mRb6nx3n=><>Sof!8j@-kh+gF!L9mu2ybprAw?~$2;ahc?UPwq{4 zBKhJylJ{p%L5Q(@M$z)~Qbnc+f;xM@dVr1=Gjh!`U{RONRX?P%Q=5lr#%Gy%ZH)ku zrO}jtyg!jftlP79V)5}5z~$2+^>uXs0zmJL6lE)yXuTGNfUrZ$S7kCEelMIAmhpWu z3H~Y*G^pxY3FuF(q{_}KuDbxU^`o}FKXDup?@$si+7nPXc68n%%fiaJw}yV%<52{K zo_A}!nNLJL@f13Wenpw^g`Ms`ubC*U*aT<{=!)tK(y8h#tw=Y%^tSjF9G)Iz;Nggs z>1sMAn~dpuWHi$F*6QGI*O|pXk1YEv)1t`J*xY~kyA9hXpLxuXo7!)f&-`);jL_$_ z3cdv@RA)Lu2sRR`I+LlqE6CZO>aBT?6zkWCltT0rdFjj=$>5u?OaR=hpTtu+9#c_Fhl+QW6g+ZFsS z+0zDBG)!FECsBexRzXKEIQ8`?C9T*J2&ZGFhsGa6xLAN9V7 z*lUd-->`R{a}lnVTF{S+ac75O@FJ{-N!X$pkc}&F%3upGn$q+00&kJ|HdjaK&{6M0 zQ;0VRj=c)sDceMglR|xqv_0oII1+Qig%4(rU*kXms69sz{PHMbW&y~ni39Gv`+~dS z@<5$VdsA>4#tW84{Cd07|6MIW0u%=fve=X}qT1ljwQC`Wv!LsI$`?Phk$V;d>Z4ytUJVhY2cp`spWhri&Iv1=6ZVz01Vw>O6R*grnyz*P0wb9YG@DgK<9 zw%MJE5Z?$FMVC)Vq1BGS>LG{vOKAV+tKnK`y4Z`>3A3m3(Y}t+8PNAGW^WAlT zv}+p>+l}uMqrC*Cj}#z1J7i$5=xmq!-0nw0Qn9s0-}SODZO{~w0I_SKcDNjO=qX9w z@6ko(Hy-yGHpMAEVHrt=d4~Jtnv9A9&u^6J2o-3TT3-OBnUCjU8gN!AdWgFfI@NO! zEYSV5Y>dFjZ2YR9?}VIrwu>D#4Iw0X|Are^{JQ45;4(~UVjUOY#%N*ytDOC(D5 zKnzmG{*hWAGK-Ys=%`2_*)W-LgDl%o8@$u%+V$rZ%u1L#L*piVq|-8=W8DYEPt>aF zrAsn#Eocss= zq*T1}Qnz30T5p_dYo%#n>&$i9wh#xBuyS?&Ejh$bfwv``%}munmwG&#B<{!L%!*rR z@B-2&PwrR^IbTgWX#KdXoijfOkF@if&wFWiRS zc|e}rN*c!go5!#qb*(d75;p^yDc8U%RnUUQdR=oQd?x7|!MR!{&|b9U_%>PtaC>R~ zX0)ZpW1)ec(Af`EYRQL*RlxnS;|0ku>=yTY$Vd~=f1B{b^XR>x@6C%O1!`i7BpW1i zvg)an_whUuC0E3wqz6^PrsF)?bd3q>SyA&qg${db#Mv^_0&u%p!mYlbEQ~H1ZuNB^ z+!n>d3qYk0a+Bl9z{5b*K7PlVr0?KLvOx^0elG(6x*Z?3bh-^KENOm$Euf4M#G14& zy7REOFJ{+!g6{NT2YJ3>w+EULgb;ZYv+M}osYSw`9MfS6sK}4C=}8yi~3wsQaWwGCeISmGe zvVSFVa9L|TueYvV7E=FE6kYo zF0e8OjpH6+WR+Pu|PqgiXI-@yl{`?m+Q8gnB=%Vy4;ZZP~ zQd}MqOuYcK5iwlj5ayC&K=d}d5Cf=wIi$FZ{HZ6@^#RG-kQa02OOn!>-^9HA)cF1m zGD8!r04r*guDhBi3aPg|x`i%*#^vQJ83#nx$%4WtWZ)BDKJG7hPvTXv9Ap0eSwYg; zIIhb*x<)=Ar5l!G#Ec_+n*9hzy3^5btpox*g~QNS6!)gioD?dVsuYo$0tP?zQXdQ~ z^0~2JkwF;gpUMaS1yrc13%vVg8~(iB=_PP#MUXmD-E3YhVcmVin3YPQBW%MEmK>=h zHyHc7hV*q0nUpcmiQ#0l;CEQ;3!1K1SOea=%E+I)&ni_ErZ0t0P1&&bNR$BkqzXx- zN>*9zz1kESGL9`@TY0Pj8ExE~tj_LM>U4KJ8DB=_AnqZLxUw%s*+ncsiitC>xZ?hR zwKrX|9$cI7LvYW@jY!h*JqkM5gb(sSrO3JCZDbvBOp~u+GekvDr2%`@*SqwL^~LIgAaZ%EA7TYjJ`65cb?HWRO5S6 z&WVJVM2T4%M6UQMFK9LsHpZ~I-BVEvL!9g^lEu{=B`CY{y)erI&QBc9_-)-;%kI2$}!5KCl1E5z3u39ioY7%fzR|^ zPpq6Z6o&W9wp+Z6z^mO@BBwGAW0m2Jn9hzU^HkgOw>yk-q;vEZin zE=t&qr578E2ad`?r?T#Or16!R@$lS-8m^SU-;Y%i49cWH;lC-%aJ3yK*?y4+r!YZ_ z5mWtK2@lMW2V)~ZJk!UiYqeE7d#_XZvPKZ6a$twk94czij(k$UOL&ks)kG>k^)5%- z`d?WU&MN~@{2a(2KZ^m}n1H2dk5~2rPkw!?6^FhK2ZUGhmcpJ~F9Gf08#Pp#L=D*V z;@_P+(1iu=>8!p@I;s`&zQHPuS|YbnlZK1`5=x7@S7MW4U@PwfBk?n9{9~h0uU}$x zHWN;%pK)P$wINN}>_Yu@IBS!Z2} zSmA;Bm(3gT$60ETc6rOI-f{}uze;&kRITQiAA+9dvd+{Atjy%fHJ zSc^V0Pjeh_>6v_ZY`jX99V5A?ZbXwkPBar%g^OJj^P3aES3)V~WpNZ83z~ggpAY3e zCs#2ThAX5#BcB!7%zm%?JGa|iqWW_R3MEYp`q0SX=QJ4Dstr{&SfIWaeY6XM-uek{ zdK+d2@^?S@8(z*pgx!ZHuK#x+zGNMkxV@tUoDZY4&SALC?%ryRxn6p@xyrAUa4-rz zJ|pHTI^jj~^rwT#6F>kJow2^D6`s4x3^vDas_bQGw9}SGsUKD&_6-H3k9h9#VAMzu z>;Jkaa&R~M)6T7+dSwD%a_b=xC6!9#ySU-W7`#hvL!-(qvXFlTC@1|qpIxlrv6nK3 zRWAlR;+>mg2*cX%OmON|Sk!xBi3{Y{!QsYa+{z5f4zcH28K3^GfrxS))FlX*a{J7M?hZgbB=% zlume*p)JPhs;{K)&QwgCeW-S}fPnAVJ{{`K>_$Ql<(JQ00dM4$uBZ$oN=e%``0Ffn3)_?gD=1%w`kV6O z)lL3;DCQ;mC`2tbh5$E#yX?n_sr4Wo8aIU; z3+(Y5>b>51;Jo9oM59lHHb%btE3u(1x3&eRPo1z?u`7U<Jcz2zNHTEJc89+6KeM*jRK1?aw|5vKIwDfo-&C$yzvGo>p7E~I0xWX<%MwzN zg05V+_S_H6vU9bNDTdXAXO2|uP8z_bp1;{023D-i*ZD3ghMq&-YY6+<)*v=O-(FR+ zwFGtxd%MVW?{cWjV7ieL<}{!6xuX?D(f2V4N<{` zY`^tGDz~)dDo0+DnP=~%aL-oo<;P_@6HQ&S`W3A!`0FpreoB0n)sWH zwseAykl8V6xfK_!IOjDw0#S@qXYF+#gYYUgf5-^sQ35Hb)CjWNuhcVbeDrU%M3i%7 z&Ea2JF7dVzS@^0Ho@O!m7!$q}nMgJa8b~U=_dAWoSt_{ril=pSHLUj}#)W?OsbHRc zmj`V8`k0o2i^*)+b_7-3mzg5}OpGWbJ|Wb}Z5>M|dcKTXpOxvJ9(h_VWp!OXqC@bVJRWB-eR9$4er|y0jdUj3)Z>`PD%48%@gE@aLJ?rvNc6 zl}ihGbaNQYhn5EZCqh9D4LCPdVcMjL3`sB54l8;1N!N)(3&!fx<`A2OH!*oN9n~1z zLBi?cZ-r%t=E9%`8#<28+YutXdA}zx@J)l+vrF`62RO-ICnLWV{fCN_LTTVQh`i|# z(dc)^;aU4-r9_~=md z=W#$>yYx~l@yfz7VUtrd7Tusa?(dFpnXVyl?aJbXfcihT5XPYD5CYL}zfGI!popR- zs{czI${m}d?OVqAJ;SN$>ccQNe*m))rnLT&bVKDayUr#agmRVD!vaoB_wzeX&-<^d zGkp*ei;w2-;!I|5))YQ2W+ro(BR6*`nTA}P)pQ+qQK|CjX$j9#3;UKdYzkHoc;!Om z%KL2EJuV5Z$P^{5F#)8Uf@sd9al2Gpnu#eD{cene$}SJ%4U6{7H)kn`gHvZOo;l#g z^R?`mssvWVjpFo1YQphyoNSRrFJEMy(`T{ykqx>D$mbXFk?lu04)dadhaH%`Gk8eC z;4&ZXL|fVIW{=Cz>ihNl*xDNOs?uyE6TiDA$`3<5%7FnxMj{gN={GdF9Mi4`QDN}$ zQG5^jz@4)>xTCpG5fu4c?M`mvo?C@G%{G7$xN4H$zz6) zZ8_8qx7vD^u_kTL|Hr;OSR=U4c`?bNc%v10a{UvGe7y)KQP&F8jeLLhIM-tpG;6QL zDBq7d@-{zbZYrs?3GsYuG>P6w)zC%~;WJq`}h3R^JI__Bd>@WOsqaR-AH&t^2iRHw55o{vb;F(<@~2+8!gnkHjl zGSV_k)Xm2Dt0OF3o8DYK?3+Ens(-4g3)TQAl=TTVHC7y~|h}9+}y`q@xts<2$kA>~O)1pw4qf#m~dzge^FRh+_P+wE*ndr2i zEQuZLBH_Q_v6ePql2oVW3WaEuF&y7#SeI;Dl9Wt}NgDzHu4&mPs|DgIT381l6y9H) zkE|^PMf}d#iG%upUDA%6f79 zhavAG9i6dCwu**|cWD8#q!dHoUE9yenY>QV5~x7M9J8MFBV~jm>TY!d<6dD>J;50+ zIPb?a#}1@I!#;<9ydbvK|AD<1hB{xXDa;xMvB%`B<10c61f>$CWO>a3XVf>Kh4dp! z^P}6Q*%saf0sM)~?$dAma}Yg4q(<#Ju3aTfHdh}-^F<$ zkFGBvM9|zI-_w8CNlql+o@1>#%F!Z(G_@^w7|+OQ=}>QLxSU8O4iLY^yQ+_l1DnyA zqT=N^Zgi}SOj>?kcgD->b~P$z_xS7;z?q5G8HL4)YKtyadLOK@sYrMvGb#l)^3^Lx z>)eqkTGIWTGM!x%cb^t~pjOt4S$V5arSSuVphHIcmLahm8%AP`sD^NH$7zo&5}k(5 zFYI=D)I>|6szwR*siE8%GM;01N6(o1CiiXNoDh;O27oJ_A!UTDt3Uk~a|HQv>Ps7` z2fj)^(E3%~q7q=Xp*G7A@UF~VXY>j|&COnb9kQ?b>MSM9S_L#p3*nP0GCSk_`)4%v zS}xoS-TyAvFd|Z=ZzV(da5k2)_ITtEDBtM)MS~9N(@szj z;`51AG1ePfj;a~BokL*DIb<q;rU3rv&8e#O1sXuFRNoJ!=0#Xf zNR}yxjs_@ndd-fuw1K4{G!YU+-3BrLZ1()eLj1h3LA{%hOn~KmN$m41Ig~zS?Dx0S zO>Va7Q<(f)JETiB^3U5EAJ~5}CH|4VmmwNZ2uAkLOYIdi)t{tLRmg!$WJ#c|zIcJl zAsCi>#qglbW)c%e_+3lLm!WV)dqgKjf->1y$U*7WyRune^`Qm{`d|Oc6?S!FJ~&Bm z!9U`jey~2d|Aku)9;Rf-?S|oENOt|4t5g+<-Rx_|KFEh&>nU+m(4|_Md(M-*TbVa;lPg?UkbfX}=`zn7wDpArEX=9W)umhl{ce&QS zVy%_5X_mtIsLhYh90skmr)>c7;2ndnD21>TLU3k)f`5njI}?d?a2RgX z_UT^K>9s3g;rYl$xB?Lbd&hCuGbyHS@_J@5;9O!B z@0~D_#sEcKbW(6@N*_Yf9GxvhF*pW_02+KwbJqEfIobp?A|=gq<)2FsUaA%=3_+C* zxeb^4cP3JnsA#|~-Ij5h$m$c1_t?Ajn&$8Fe?rEQa%+}Kk<&Fp%CVr{mRJ`i1DraW zr6fh;sGLiKlY56JqC~`N6e#ncXLI|XVw>lRYMhpIg-|WH?l?8Ktr;k@dxWS>4Gbqp zZQoImZ|qtgH{&+1^}`JLHXTM3$2?N!N%UHio+MTC%=^uz#fQK+(Wm~-P8=b#Ny~xw zA(IZp??UqQS{_y!gvGqyoJ?1rj+(2PQK*N7R#sx2U^NM2@uxN483l`vuFxw1iUcM^)x zHF{4JYsH^L`+`wB&fHVHr0BJjK%Hh01SQ;J;t1k-c3@g}wUWU?uSK>m%)&Era3ahL0w)psfe3P7kMN(Vp|}ZwK?asz}oiiwP$>UWLx~7+o8CngqYpLpEWVFSMe_OR+#4JUA^GAwKi? zQx5TdFzH-b{7yI(Ytml zxGLVr7LIPMP@!3>Y&4MqwSjh;FEsJj#TCmb*=$;o{m}}RRo$9Dt@A^sva!v=KoP3O zVf#L^=~e(#knPsn2f)WUHdIyPyesI#ayNvGfqVK^>_B|w!`#CVVZM3SA3;AscwkL- zHEtj~&3aa#(SZFfNMQ#qHwqyw5XF_o5qj?*5o>yOPUg)VM`~G35@$oSPR|#5P5`bx zBvaSSOa4z%r|i7U%Y&>JFr;xp9$C!PYFlzWCz^)XEIRS`*MYbv0ShpUa@-Fp(Xo=; zav8s;ZtzgEi59Gk+=^JLOEM_~=fj9H%}Q~u*114t@NdY9f`x`(Wm+yz7r}O)ceGZb zGGo21KqkL_w>C39+NjbBD&TFCm2jQ_vzGB&nQFqPZj5>p;QW{5_^!5gFsH_eK^iTT zof&q_$GAQQgt_Jg`d89jJ0_bL{b)zDW$&$QyxKZD8qq!2G$B3YW4h+5++2v$fSw32 zE5%Av8uA%kbhzK0Lj&La?-Qd+xi8p1I>W7m=JjKw{ zJn5O6d!gR-(Hys)aVBe()@FL_#uZ|m)LfDlPwq%2iRCD_CNKb>7=D~>Wl58chL$e; z_X(sj%;#neIqC8)l!=1EW*!Ufb10SfA0105ohSDRopK2 zEFF6JN3C%RGCC|U(F-}@j`&Hf6GCxvb`=H1(>c`Vw(3G9A)=NhD-Yr154 z_+PwIIo`x-YC<$uHdZuFi%lhgCeIi!-SAlcaW~sHzOa9ZP?1FQET@}JGWDOu2bGaj zMP+qt|-{o zyvp%{Y&;#i{|-=}HfBsw^;sdQyp|9TL)slt{o3aNvmX|@*4I2)6n7Wf;Fdp^tmth| zu0)nnhgGg(jvYobE^dA?7Yg%pQ?a0!NyMA3Yg)7VO;(B zjp*g!X)EFI-n6d~H%xW&2Fl!vGu@N_HvAc$FGbc4q&3nk`~1LZ=rDVS(z68I;Vg$P za2I3pI&UJ?R)s@hm}S#-NheQYRy?kn{}2qse>pLW_gc>T%D(5(>lu`1_BCmy`w=}R zt#7jvE+;s769`a)L&ILau#g_%hF8=5)%73^^EezV(P@*|3mBcPg_|=7!MGN%=1Y7y zchU7H9M_MUAc;2u58y>oWXz$W3~Nt9p`=@+4U(i>;=#{Z zG-Sv5KmGKP<3Pq=2Rv?T(Ty2!w-^hS$rCeb(v_Z(>=he{;utx=J>-#isV$HMm zvvx=S|B9rM6ZCK^pn;QN=2dp-Z(+LfJ)`Jq-7KelToMdfvw2){?v>PrRHWuv)|`8~ zm7IhSOOOA<*p;kf0^%QVUv+%skB_TE%35kJ^9DMc6&Vutgtl3l%IH{}YXK61CqcgE zg%XBBa+^*&pn3~Y3qefJl^lT3rHgaYJRnT7h6SjHJnV0BnHR!kl$-Z%kIdt#BIJeV z+#lP}x3MFogr#IV@mg`94lbH+PYkr{!LA;xa z7X6ZWM*&Hs*%}eCq8ZvDFM9v!vh^@@1e?68(enYZukvb_BtOZP#`2-3;_f8lPbqnd zHHO=0-|}mb@-oThXm2?UrS6FeiM~)(O~`@j2azK9#QLA@NqtVQrRs^(Hd4FwqPlmt z+~ICNhS3lBubkjRT$Fd$?xl)e(BFFY!b)Vhg$qE|Pr=S~g+V~w5>w|8)>t_0T zTS>Nt&SAndk%hmr7Z?JK%k;{q|H`q6D-}p%Oi$dUhop7Cz%PARTCiv=D?yP|A1pn~ zK_gQ>$QK^lrE+==zk4)E+>c6wb?K7+MtfxZDfbi6d;S0${?2iSN$#T@k#S9kjYs|W zED)kk2Y8B_*J^@J4B8x?J z5jJl<7j{$`x-WUVB7LMJ2hIBYrv>1!4)4q9oqBU}06-%$akz6{J68B8)N|uQ8h~{b z47~$@aDG8D(soqZ02Vu8tJba5N@bOLWm?pq?{p$;=?Nv>g5G2$9=a<+xc>k-*6=s- zTpF|eGnydBw=ZW~;{Am4I`ePX*~o!1;i|(tw=w~`PsT6Y)fk(WPo2+&-ure38W#9D zH^{0OoTb043RSFl%q9871gU5WDT}g(J_@NdeetzHm(1L~3r&f$PIgt7R(Y(7V~ooB z+3+F{YL^0MFtV{2hH2cW#EsLR{`QZAUp;ruB1!!8k8RRs*V@`A2ajI zs8z>-HCa5R3KcTJSG22+u6J#CxbEfX8-S>agTm>pyK|?7n}C#y6JvN~DH;gn)@E=9 zpKR*NX~&7Ey`P~NYq4_Deumnu&darEKI+%19ibInTo`4JFhmeJRu(OEiHPQlo(wf@ zINL)}_tQ~GachS#EvV9$xat`Mg23w&RHTHT<+sq&V8@?SwnWGO#V9k6zvjp-wHbdGJl*4#`f}e-aZ`wg9$j6WPN+{(0 z^IKvo!xV0lC@Vsnu6}nNC*Mo$8BoC-I->w0dHy`Qzqk3-*t4G|8!`K7Ad_z04H0&B&>($Y9o_8=g1L4{^(-nwy|R17l@gKB z#m;AZ>~e}0#4@DIEon<_QmCtm8XUEmF>S(VA+Z{$nk*D;aONYBN;=jRGZQiq?+CF0 z2l<4p#{r=fWd%H-kIrYqS;KVC~xR6rKbmk$Po~M&Oa1MWV#p0Snup#3@xC%(-htc&dtKC* z_EE)E_hGdrNkuHS>$>q5KkO!P$SZ*j_Kos=vhe=g}R=*AC%W3B_;*T>=+`5kYW^N;msIzs&jq-c#e2XALWyL!-iJ0%hNBHW}WS zA5AO#(%d%a%}%k7Ahi~aQdZ%~t73f(wXV24!(rbsSn*H|$;TS1=xnaV<1n0J8j6p? zg5~J>20tIa$n#?#2n&YMTZNBj4nIXpJOz)Z2C5NyW-wkp&2<~iA1H`p$&|vjiDO-b zC?Om@u#)RB5U2Uq@qK3mPru7m|0T^51RR5*=P=GdeGmm=z=+ zV*6{!gFe@h863_u-NN+~`0p&IbB^mAInwV<@v7qR+8pgDVouLv z{=nxIR&-mHYA85fplGZT5EyZ3AH&hJUEn7wVqRl3;bt}ni)Vf=e0jWkA6mXdmerfJ z+CNpGhcq;_s2f*{TJzmm#l-+VOu3ne9`B?hTQ=CD$&4>=f5E7noDApNiO6!j?Fjvf zJ&F))h>{tS!0Jx-;o_tTz(5cAfN)OsCL zo3f@*eUbV`#5Il!O{{?i9Hg{Wp#NFWQMCdza9=`?*j=H`qVg_Pri64ba#olfCol5g zZr@QMeH4^+n`Y8J74&4Y`7>BcF8{Y3cWJOPV-gxr&vgd0z^|hKEK|ao6ZFXlZWjHF@L zQV^W1L~NBP%3eY}4pA?h5Qt!+vO%^jN>;4!>2Ux#<>o>%*y=ri1F5 zBPHgMVTbmW2uG($?F)ZgTj@=2LR0l_vlIBcO>Lcg6HX^%2f0WAfCK6{OxdH3mtEttZj{VG$}lANGrT~YkZnyFh`(=Y~njZErpF0Nu^-_{B8KkRGs`=b-yHNCVqjc z3V0<9%(xd&Ei4Ibp*$LgPS~A>4iDN{v2MucggudSw(8~Ci9F{t^b-|7_s8 zO>Tz!_g3n@(RvJ7qo-#WvZ}-lTZXmVX7~7OQ-rC&bZIHlhAr;F{6MJeDXVvIqZZec zn5l|Y!U#_V*2i-%vmUk?=29r_!||cu-*d zy_%gD$&es(v~Zr&mK;xw*}KZuK8N*gCS69B-U0r^fy;|LuMT5Itd|pC?1uP%n{-+t1Toe~M^VQIR>4H8V}HuS~h2 zO1)iO1g7FH7`YdFRFG!C(&r=lz?4P6tw{x^~HL-lS zKP;~g^818=nb(C2Wf#*lXk~2sHV5GYgXaOD`4q(W{b(&Lo@sCRu3myHibFaMl(!ri z${Wx3(XSco7|I5G$?z_~?LJroLQ7+j%7&hauMA{t>g**J>9B!36bH%)a1@+q(DFCJ#-wlwBLWDceiiui|G~n>6 zBq;;1zd{pd#4wDm_DR(;Z_pjY+T+494bHo!9N5lRN|UyJ^n}SwV=i8G(Ir`^BC&l@ zMlgjS4@ob9+Z20DuIgCvq6-H4T&%(yQ~XpKa&Q++O8B3P+glm1u{h>qbZn7M4g#A& zZ6@#6afOscruB9=OigwgkJ?V&GC+BCSmheY8VnTwgU5@wODC7W)}E*vQ)WpMCnE+( zeYu;%PEbxL)?R6axOCd!YZ!mnnHF^JG(Ljk6XXfd%q8HflR=ZIoCRH?sUJJX5dd<) zYyga5$?E?|0~wCn!@%B2Mg@l?yDhmuI;xWjuik#kG=iWC zlTw!P4h&J7w6 zeV1n$w@S~dO)c%_7-h2$CY?RbdQ5~JQ$p5RlaXTJ-~oy2^I!^qITSm+$lAvy$0Bz@ z>Ehhb|2z&!)5)Za7)0)!tVLXovvmUwVRin~z#+&`V4v_G>sy0u$A-;)(8KVui65dGAF~ahIy(YX#N@Fl4p}(&un%=%uUF7ZgdUmq)n!-pYP7)Gl9^bD zG~*>?#197y-WjyJB-RZNo6o%X&^-C3xui1=6WB8TOcW`YB!BGA{$s9VsLSC<;KR3j zKQK0oycL~9ZTjsst#>PW#ieriq$8m{il)Se66^~8UN6!T_R6PnWlcP>>FUtMDXZY z&L-H=M^j(i6}GgMnt=e(nSv+><3lNd{mDW%>PW%6*~ZH z*Is``QKn(KE(E-e!$nuy;cH-?8D09#lUyq5dtoEqFhCnCEzi~spBR(R!;fgU8u%6M zbeEU#?Jyw@B6#J}L2zh{U=038R@!C=KQ=cU^^jJ59&9pC*xiLq1b7;&M@wqGX}b23 z5N>7Q1ST1p&MVq6Mh2BZ2~1f!N_Nz7U(y5$3lBa|(fzfo$v)h~cbv4RYN`<5vRo4sy^&M}}@eE5-k-T>2zNEQ>A9IWZF3s+DpSN_%B zk6`4*K&b7H)db4VUwb!(vpee^*6yzOl;#|u!{I{kbB&z8`P8QcR!*VV)5rXvLgLck zX;{82GbBCFgn+?Ek{k70?GU$?-=>f$-9tr*bO>zcvX%o&0ojmvWx$a)qEW`~(eDmR z1rcCe{>gIxrx^PWsFf3n1utfhH94Q;UL`|1#wLG~7#yPE`lvZ5Xe3~|Z-iwlPfWXP zC9WCGZEO;kRd5{}ZJHc|82gHL46A0f#sveZW*m{bN4RwQ$xOu`tgT%1!L>~qh)}Wx z?r78#w!8-6r*GyoF%Y)KOZ=X4ItVmUmgLPZNY76UMWq%C$Nof2smUflBip^-Bt#82Mc^m>-Rs#l1wi3r1e4jRCu(T1KTR z{i9`@w$Z-enW!hxez^B7 z#C;*&yoBOt%<+rk#cgj`(T)S8wj}gHBe0xLN9C1mMYcJ~klwH8w{_KKQA72hmtLC^XD*ziO6y&w-Gd0LPHsr6kB7iBV&keK+s9de z@z`hr$=5SJ_|&G3wOl!PlG3H1QJ71}_S&aoW3|AR_1n?=4gJ8B^Yyamskm=@;{rz- zjyAF|O!~{X4Pm1tCUf*ypCSi&to*O3K2ReAVS1u;t6Dm|j-iyNGR&&6jB3xMi_(y| z0DeLTchG7VNJHsqVuycDQnL{GaZA7fZG{_OnX>YD|9^Z^g++9U!a2Gc?~wNoi+BnR zOBq{^t_OU91CBAVi6qs3Vg1cZ9w7?XB;De-0uAde9F?glc*)M&a!JoJYoyDW*)lZi z7{N?17b{#GZldXA6d-1H7M7SD%vcZQ<22Zb*c-WWDMF8zx9Nc*V93y6!IFjpSW@TR zaV8-Bt2-e6Z#_!ue=|W@6Yv=LKhxTshsBsOlpczs(NC|*BOC(l}~Ud zs2`h+81rw0DrM=B>Rw)tra~9{ju=_8yZ;((ug|KeKkXu2(rGl1)`i;MIwCm09lnu0F~D3giY zUQ&BeoBO$Zme|(p7-T<_!U*G=Z%W|kuN9YVB%-p9)SNg?e_1Vn1Z>>_my%!Gr{_)sh~9bn&ag&i2$9Vp+(D+uZZA4p^d6Gxak~P^_S*&Hr(WY2>OlQGO2H$d0OcTgCc`|9uVp30VE>G0 zJ{OsbsyULWq-(3XBjya9k%}Sm_Y50F2taQ0Q}HdU>w7B%t%+?|O$L~Qa247`l{>pf z^Tqi~NQyU!9(y9uIYz>vXL$Psw+Ty`)3Edxld{EOQ@3$FM-9+DU#WPGbe`f>_wN9- zf3i+%2m(Bt1wQ%Ejnnmh*SkOZgnSb7r{zQoQrf-7dA-dymDFPcRBb<37Ne&#bfZ(c zY6T!8&k8yxnR)9oD!>osUH0p72|@n)91Cr{N;stsk{sVi1`m|hMfNPAgk=RFgHipB z@{~^%W+)Pk+Z)Q@U{N-ri8}?WHQ_o zu-4kqu~N?lway;+b+lzig{Fn{{JIcp74@eNxVSX{+hrn35NuI9Z+W^ z&^qJ68d6JVpHOa8Ggaoaf_14oVaS?-@1MZ_sJ4NP6?N57;l}@Lx9JZV;Ku`CNE`N# zmkj>gStH>Uib@Dy>a2U1Ra7s}(3{Nx(}dZyIsD1JbCAe+P)nAX0A&IR6k%f_387du zmIn1paT1*g*on@mqzu5-IwY;Hh};QCzS3MQvR~?T$m==`Wc4){viL8pE;vwTlAxU} zr?Mdo0(4bIFI-A}4mr-z_Xv(fcF${rwVssg&B&UAu#R{5rl}Q7MBa&aHA(!p=pq(x04o+TXR2?Cp|-E78%uc9t3Ef~B8MC1Uv>0hW>4^NvN0a+G!XCn zM5Emv;>Wuf9yP1fIM`^pI9!-@z?<5MN>9|ASS9l{HX8h_Hw1Y*U9Ai#eb=*;SxBSM-e&6SAtdy*=ZhOGt}PL8wOgztFth< zo@J5S(LZHbp9kcSoqrcJ?Ntc<`d$N=r*N5{m>g?7{Hm3w-%`%`Od6*`Ax}SK9H)suR=OJpy(fdp)V;|!yHafuq7a8Opi`zTZIUqw10Iq zWlq?^O};KgJszmBD<6XA_4Mng;p9L?5K`qdj`BQ6tKrhmS<_#O_uyO5jBW_^Veu3C2YTG4j*Wr%0if$x?G>Rlf@r7}WLqt9O|COEY z0wm-MXWzK7fea?}7U@u8z{V(FSq>Ot3l!F2b5T7m^Em71W*DhgT-@kaeTIluo0ZD! z*JW9+QKqfY@>&{w&Fdtn=k|No2KI}~>mo+otI?0Z&|RzyVx-VO+bc-g21%2AtHeR6 zYbw@e$`hm|uflIeN`vNkuK)tYod$iI)P?6o3o%7_zEA&SF z@KmpQVI#Y4%5B}n!970fl|wh$qDhVtNy3lIwr~|61(PsfbR^X0g|1CXV6rhTYa@UV z8g%lVr4)jCBs7=Z|J-*<;;2P<$(lEn+M3U1HuPpP`Ua%OdWosTlE3 zpO06IOYZ@8B5Tvn9|=lRLsB>m#cy5Q0!+eiWWpD4IOtt2hPk|5NRAtw3JEo@o)~Z9%EvD}bXV%m{3xX)J ztbxqt+aA38CM_jQqs&GZS|SnPe?^sH{v%5Y;t}y8M34$7Qxx&(c9B&=ddd>TS+K5U z({zrG02MU2xU3YckcB=Bf#BP+eW8x;iFjaDD27iF-kT$hl7CCp`~9Io!0i`sP!tB} zM^Zhzd0DwJt{db{GL<7}pMrb>TrZBh-uev9KU)e)g3b6 zqLAGWmJOboY9KjKja}t7K|<>T8?)6|xIzBhlxIrST#aXDzO{e=~0P9hA$=;N#xz8Z0Y$P&nu1x{Va@MW-t>av}Q|COr4&o_FMvyHvsK zXW%@-mDlb1LSF1n(4RkYKlGT}f2g5Do18kXtrp%S^mw~U@Qy=&Xc;FrCCG_Smcn3TLFZy zD_p|;?39itnr*{ycmAi#f!$tWrvg4J_m8;Vi@01&_Y}KN&fQSj|40@WW&bvj&TqDV z!)`sI_Cbl(wzc0H&JsBmj5B4le=8deCN=S1f1Qg`8`U-Lnh*;{wPE(dwZ)Ry$!MQk zsAV9Yu;T8y6P2AcOJjP6qOd0YZ_mI9?fYmLK(nO?PA=GF-LS-FkI&r)c;&v7B5Rb# zLkpBxXszXkKT9yJpd5kJ>>kvxeHZe&wd6sYsCFc7na2Vz8EF)aEJqg4W-fkajed2& z3o3d4_fJ~4jngQX3mCfABn57qaonn#6k#o#Vh3%076Q^{q7v_|;{WQ{JuxgjfwClG zFNK6Qk|KtX$$x7%FlZl3&1Nh?gl~>kThsS^R66}2A=f4J!f%`Wmq-BBB3;%|?x_Q| zRP~#hz!FtTGU0j(Ko3BS&hmO^2qu?NgW4{H2bx!N!mpNFOVe3_T<$ng&sX5=+R0?= zBXYM1Jqh5bjJrD5=G9Y}rTg!CTBEsrTF#A5AO#qjP_mZF8>4e=?p4=&Fn-M##>W{P z;Yy#)7c?ZAuG>IPbIC_`y z)C3HPNqFy@0{AZ9aUQ;&mT0z@-A~-u$JrV#BXdmycMCKSI{6TGwvJbFQ zYI<8=cdNyh?7WiYLH>89la&RwsC;X_jcF&fVi4VuKIXWuRoh-6N_jlBbE@Z$EBNw7 zksG)t?EsVsT(4I!jUpE8smTCXl3Xj_0v{*tq9q4Br}g%2>nY%T{sVd1%mq#VdaSqm z=@e0COElNnaXV|dJSCpC`@U&7l+3u;#NxGwxL?oS-)!a`Onl0kFq%Lj92Y{s7PF($ZUi3@dkjf*E17*Te=!P9DpMg%jlOe z!ShVMjx1I0!}53}o$3tb8-#JpsQb#s%paQieuyFn>3bEMf>h{s=kHyVYz5<0OPzB;HhDHtd7G%kB3 z${BLaIf(ePk`wtgXQp)h`TY;RQKzjzK)I}}wA#`pLkzs?BGn_;TQ-j!JxLT*tdo+R zLJ{O!t_I#98b`;9b9|a#&o&P1=lc2*A`DwGZY_8$NzzEe+)i zLg1kjz?}FUSBSv+5uF;x{Y!<~J$Q#xlz-tZI^C);K%uP=x}-n}s`O2}Av;y{{S3J* zl%P8R)oN5!WQk<(ir!zWA%(UD*9F$<+3AN^fGJW!z+2o!TCh%EJ}UgR6=1f%?jcpb z%?35`rV-b+pXX|;J$DMB$hu06_c!?nfE8+Jm3MwkaClRP&@X0jY=~%&W(mrK!3+6c z;fVE)#CctpW#Wat)hjgxSs7tInt_rY8fVJMfd|2w=^K%NMH|}kp0F(>FF(g;g?@9j z%-sZn=6v>PxlYYMXb;bGTRR+WXx{BDD;$;^;Opmj)?eoLDLPyjhWwsbbFvMkDfkdCWkBdL1X>J zq+Sj^&Os`UR2N3wse-@9&l6tXkd+Aa-EfkGe(6HJS$s}MR}yfW1W*y=O7@6qg)C=<%ftq{RYpXKijj2z;cq~ z5WH~3Ajbi28l^Z7<`ze*tPd~)GqJLb#HQ$>t8x~q5uKk7h$*2Oi|iMDpJfJSBDEQ@KE_@z0^MgwYllk3iSCk8())FSHW4C~ z3w3^ysC>*;@f4WpE!v@IF3stGm&>~=a`(tXWosQ(r?1YR%L;o(385p`C-%pK-3u11v9{yitYlpAFOei~3rPLpb zBlrZq)E$OYS%gEhVVN|7X&P0-!MLxA;S-q*`QfaT;5x3pP0~cWdsV&}~Ou{c?*p>T?)7imWWiRUX9!TrZqJcry z{8N*}aSgt>s;!`qj1*bzAq@(wLg0Pe9B{z<5izdd4#0Vd2CA}^Cx9qxfaz0>?PU>* zi8Xa>gY}8%F=AK-GwiO+-o`WmVRy6(*S4>6kKwIFggVWM0v%v;ja0Tg^Aq+MI(Ewq zq;-6pehR;oXZ<50T_eJNnqeTKor-=v*o?sdU*DRfGx4vuMhxys(DC7rK8`if7{&GY zL+eyvuyhd3>m!S{VSEfWgf+peXyo`)y9N&Q=`T59WXIcfshF;4kSzj3XJ&4&c~I3l z#q<*CWe_U1X(buvI1Ankd8!#Hgpt2ZV-h2nKpXdZStE5q_ZZ_zfQC2qnZ!}G2K@F> zMXVeWgQvo@+gp_#AebanoK4YWF2a@w;m5`MZw+SIx?g=+O~y4kdsck02PA+lT9wBG zKuEV?{N=%Ev#hpZ$G2R?(e3ssX5!Di3sU^BKga4lF9GqJ^;QL93tDOREUoYC(kZ$Y zBgq}}cqBQhu^dO%yv>{+ruE*6jxQa^`dJuL>aZqFO~aXcqdQbczzE(;-L)Ac>D7KW z-7lspeNgluSDN2TN$8kcdDbVE-Ln8sK(N0=*V793RH)7fX<$rlL$4VqmwkgJ1R?m@ zd$|%N?%?cgR$vdJVFOM?pxn{B<(fdkokaUy4XBvxtWim;$#UvG@#_NLyG>v3CIU)^ z;@zSrbpAyL@Z2gBH2L%nf`+9F*8;mjYgTx2qm{*&NfCk5qW-ljpu=%o*Gqb@ntEu~ zyr+XN3*GIWXVn3&PttwjDfrnCC5Sx5`t=(=JzKIr3&KgfR7^fH5pp*{6 zkMA_wC@=5(J;_1h+QtF!EWt#Pe8>->mY7wdt@!)-3N@Uv0CJS5xjaDYt}EpbV0>S+ zhzH@<1-Wz{7_jIpKu2g$cl#*^B8;^Dd}vlm*xp5!7SgrP?NQP#Yq;PI0DRHckj{Up zFgk&CJRTV*|$brI62zR2!oRUhpb6-c9Eh<=k zp|8iRsMjzUQowR>Ea;WwMm!;C3EcVc^5oY1m}(cwgFOoxDj1LL<&~6~I{7WD=43wPaLz>8bb9y7GaZ>!yU|#&! z>r#>X^Pl7#^DG>7y94+e?@n3LWt&-0^^?-6rPD|l^U)7n;#W!V4%W1%oSm!O*Z&xj zFf^qrDV$gh9*Qi_U@%`%jQ8&5jc>+$5v7b4nBuas=&Rjr2?&!`Kx6D7o^OgscdMK8 z)3N=4Exo`?%Iz0ZviH36y`g3wf#{5z--OWgBi18F7Chh9DSrfO5dYbC zBSfvlwg1S@kQ0&<^r3t}5ZxNmhSOa%0H#?4{N9J+pC+EkbG%=)7NnGgw!vFzX*?pJ zFysJ9nG4w8b>?YMYqEc~y0NAU9a{XoW^>!yOAfGIL$f0(zpgSHqZ(a|HUrqY?WIJ~ zUH|L9nXiVz7EW(GLB1B(fri2?b~;5!p>+4j{^%;#?qY5y1|7ZV&N9r{a{Q_~gvN`7?Sb=LBr)%r|ZLrb{Apey1jZ z=ymHp=B&?_ye96FEe6R-W3gBBt}k^52eH>>=pM9%m>J0 zKV}k?x=HGSv*5K+>FOztn-p&Ek}2zc6IgIqA6NP`XvdUGKGig4V-p|TVcXt8PNdOc zhxKY3TZn0W?CyP;Bk6Cb!vDYN?K!OqdQgA{w6NKnOnlV|{DjT=0VtS^R{{y7 zWeXJ1{!2>$P2AMf41SLa)caYV;--A>-AST>rBD~vp=s$2sAKXQr|;$7}eom(@o9XU|t3#LSrs`v)kM& zr(58{$=Ogf@gV!<7mZ*?LHj<{PxuOZNDBbzc7=e=H1Zd8Q3>~cHj>3(KslR|AO3y6 z2)t|wBhWogXcp!ZV;TWNrSHZ*3@|qy@5UsI+{LK)1u70&RkC!~sFFmz%BxQ8Ec|Ae zRfUllD=kTlIGh^LNKKeRHe2t?OrC851&G}v4J{R)xO@P;AA>CJO%yQ_RhgwxdlYwY z^z!ykDE|MCO*nw(f;yvCtV$g=hMUHCCaa&Mmr>a7!q~Cx3cZ49_t6SmjrcgGBpu>N zENAo;(#{|HH1#Svrv1&4l4S(s5|ipvBvI>@fcB(oRS4R|HMt}^X&(Id_z?mh#QzNA zF!v)!i^CnW4~RXFxOE0EFaRqooCCL^--(W~wr16fIxd@qT}E1TjVkIstQ?%^&h4*l z0w_CT(Q>-^X-Y_V9Xw?HoHZbk^>K#!%5q@2BqbGXRH4#IV_DJb$*$R&T%EaPM4Vso z_gCCgvHO%Y%oCFxJ{(LU!_1Y?AS1U7bZ)7|(GnRzYd^q?cH*3AwHh*r?!Z2?MswDo z!Rh(0{I3&U{OS!kisQVT2*E555A0OVph*76$+v+xuw?>R zMA<~wy6px-9{UE;Vxrn_5Xv~7#G1``wphKZkUQP{0U@n^j0(0Es^?Mf^?buJvG4{3 zjewvi?OXP^9^DH;X1ki2yfy!f2%%87FS4mF^gUd=t$K7mRCiMJHb_n4ZoXpT4V+2!VLL1 zGt`#_#x{33*=Ixdz$XBZ0+5{%tp7a3+|gqy)MNJ-FIgVPMx5PqWkdS86{X63=Tp^+Hkc z-fN4@Ek2P1eUYxjp?}wUYw#o^+gbd`mtOAIA=F^JOPm`EG#?RvLv!9@MQ{-t%w+h7 zw6F6xNx$WQEjijHU9GrvIvYP=f6H!ZzY#jpKf}M%19u;h^Z>4;N7;*yfU#kG+)|L* zp3IrNv@Y^HlQv4$;r;gd^gGB2x{nl3k-Fx)2z$r+{jf?R#?j}2;LE>7NHG$cEl9sVJAU2dBmO~y7MMdfN@14>+Sa$3VGY-O4wuL~VjGTxIWAW#vHnA;z5Ekbsz_Ou6D^bJrB`kz-D%V2bb|F1RckI@zG| zg6QVgTri_tnS-RUuj4J-1=bi?ixM`eYGH6n0wV6K9SqCGrsR*5v8PT|qE&nZ|9vQ)p&{fxe6W8DQu7<5&`YiA)-VHwwpj zf)+&OEBOPTxHQl-wR5=+f}~>R`sDCpwC1e$JubH1mn)EUPI6o zwGncX91U+0z*>AKZKz)MuSp|lD@ityRl$|di!AIq(&9qa#BTWk?B(=#d|yaFs+KBFDRc>bA2`3xy;!366d6Q?`P(T)92dBg zt8R6ax9ZTrZG1=ID+)|eKka(Sh2BK{beAY<@0x6k9^mi5yzF|jz6>TUwsx4Y*DrA4 zsRx3nK<{$ACa%=4dn@ux)UMEOP++)!#egZh10})wivz1)eujm`*s|1X1S3~@w$?E} zNZ+&-qY;t$aS4a$wUd$%00@0zl0^XtZoZdckY@4!4OCKt`#6izNJu~s_LM9US;&)9!AMj|R1O%)Qd;l| zD|@l3Xb-qnfZF~)-^5%jqG&M-Htp^p+M?D`pn4cv{RDa8A^a&2*L-EYMweaM{3zNQ z0k~sk>#r>|MWkkguft1D2UEVgvqmCKRW%q-)r!VlvdT|g$Je~Pvb9(;$$~!ZQz~Pv zjQqS~ff{?$CAhT1UUTii*0n${$5gZ9A6<<4gs*w}lXV zBT-Ws15Y2m3cmIyAbzQ*VbgW9U^ie_(wR<;Z3?5DQ9Tgwo(OgZkY>!5eHn;;H=vj? zg?Xj83i=5d;c9S!$&)Hu1nN~Q*F#x}O1eu*^D&{TnLFZXA1+RxXCtrJDnHSC#?T~Uv#naGHNEX_ z%1y?wi8H|Dcp#vqWpPPR7cvOhN7$GXGqf=tPK7WxXp~;hNek*`_ZmNrorOb9bRBVB zqhUm91=>Y^y}}KTKliw!C6@gDKkGjDt=r=sN4x`v-_%4Xx~=mb9B0aqm$teC?Pw<@ z%#^jFcYlXo3nre!ZsbVRpdG36h2sEcJt;LPGK)gt8H#|mGO}8#P#ZsnoDS;>DxJ0Q z{`2Z_vEettp!uWPGFru2wgaDjWy%(HBG&NU0SN+|H*A*<-)7Cr;ZtEvy$GuB%D^g5 zpxBiM>zF#_F$#&00)ZrNc-;XIRBG^vCqtv{?oA!bm&2d~r>LW5r>%N`d6koFC^zc#|$rv<$LqDdV12he$SlEwMDHmYWoOP@wUD)S2hSevN1c|3qQc zrxmUp<9)#@OoP$dxO7-z*oJEfhd~LJ&&h%vSX@~w&xg%Nw<2=6qw~S*H#}n@<>lyH zho{Ych9V^x-1O95`A8LoDfQY}hJ7jsGm>K(wgBE9QqM_PA1E8pP>N=w^b4wxK?R>r zI19q+qVw#~#H2kBm$petV3zapaF9h9E1LqMBb=S}eII~}S-)(t8qp<(O+$Yy{s zyyg_g#=(_4jx#$QIH}Gp#-v+xJ%;wCoSi`7I6P)9K zr$P9Jld|Ti?Ak7gossR%UIb=75ft}t;?~7Hlfl`%+bo)NL(y4u2z)ePvUb&VozE2e z2IyZZgdqEUwL|a?bAkQ8s>N2Vgj1oLAod;J2>~(G5Hc2k)5%MX)|D28u~-^r3FQP7 zvo!3jGY(6oOw5|ip~u#)g~i+Hdb&i}8vLbuT3VfBVtcEN=%TiEA?^Cv{;|RwTh#e# zRL-~XV(>FFhpy@e3bCx~eLobt8Nt9v$x>YLbPE;1Nuza@fbH7^E@Ytiy%6ms1S-Hw z=(rv(ANznNatEWTORI+fY_Gm&)|3lTmw(i~EBvz#2NKErS5AV?*ecB2Y!Q9?Zc@@I zge+s2`j88^a_>l+G~9>zb1>agH?(N_%z$C51^0Uy6`{qAbdmx^YJ#ZMm|7!y8kn~!PC?;!H%`@^yCKE|zd7tl(Ydfr{tNbHvz15UL4A`yYORIa*e2 zpktk~8%(epo(V_8McxOA3nT2{PPUP!VyrYaNBh@=pH}~u>q=ovj$VG&zvB^ys~g70 zjn%GSn>RUSVlPU3sLFn4*wD+`0IIlD#QaOH0Eg-Vq+4IHs1_3mJW;Al%QcS;k;rP{ zD$20Ix4yAPp=x~V87xocBklbztw&ovF6(qI+PIVwQo+cK|eo zkq-3L+AxR;$u;}OV$i^%RQ50DI4mdn?~!xB0Ots8LP9=MLn#C~8i8a~91L#!FIM$| z*PkA*BzQiw)7>NM+UCn7+5^W-y9$vkuQvD_Nh?*hM;P><#+w9R>Kc!qKv0s4Wu+bx zyonq89N;hYNOG1TzV1M#xQl;`%o*hGdH*&hU>`T=*SnC4Q~>5I%mMw!6WzA)vzyP{ z^mfoK7q+1~l^l-Y(5ZTMtsZ4PGE(VAt6R!V8$mr@MAyB4Q z>e|ZJ>DmopIZH0W@%rN~Pf*N1%f!W8rM-@ktTBMsUnyHs*{Q{XEe})K{~us(zP=2G zsfo|QSF`tW5O^_b1DjYqKRO0Z&ODHZpWq{@aCJfts0gCGKlT@kB^4dADub2_7R%O~ zwa{}xgr1TF!Wou#`-UbupTOLwi;)!QWHfB|f{O_Y+$Cjk?Ys1>D!0!sJ#RmX4`+KP zH@%9=j3N)~FL)=+aTHELFv|LR#-P2Z-7}*M{2ktT6J_CV6%(;RL_btuYVe^RlCICr z`w&i-ht^P%AEFmPwG=C}p)?t2<2baYD0qFNH)Dxv%k3ZV)UPJ>dbRM=O>RTJo&76? z+Q1(8yR1wY{o~GP_$ZPh2d9A3fi_FIsAMyR<7T{q$Qg%%oJ4OG_1AE{FH038!%s@t z?r6f5kOB9nDosRaVsS&RiuN~UZvXUr?9}=moTU%Kr6%)|9JxSHlnSl4Tv@qO={cdB zX7&n6Zl^co>3U9*R}~d6RF=_Zvldk`SL4$@(PvV{?NnlxAZ4nr{XyY4}JF?qC`7geXNPFs?>)8|It< zYoZGz)R+RzXE}P-7R>@tYpWFPa8%zT^r(gGRtd2!;Z^u(w}5L_&`u737LW8`H(4x< zuw@Eu5l?t3o{gz;8E9!ti?~hoT@~h>4C6?0zO+O4nMsTIm1zCE^{HOOj%*PPkBD%@ zyt5l7KY)>NfwC8xg2= zb(gEw;wTAE*dGEg3)Q>O7Lr~(*@3x@vYZJ1kMd5=ia7VA;~cELx(~+9s#C=c41j-B zn|}87`UV1apa(Fh#?6_OCKVv-y{ge1V);G>1tsB~1$e#6@5 z5w%y4z6Fgp1@kxOfI-dneHoD^lQOvyTL zVkxOCUM=fP2BkZ#ku|0l- zbM$I`q>mgAI;{Somloz<4r>VV=Tr_@$MIq_QnIMD%3rS8tmccyug8%R*#ARfCDH*I zg8%XN@tQ=wB|Re9$IwaL4BsMe7qHh!dHew>LY??ivo}Sn#_6UdP`drD`q~2sS#^}) zk=?{1*mEYL?Call)qRboh>jRLk6g#lceT}r!GYRegjL$_ZM><)<;T7;C+E)+2->IP zyZefeBuV>g6B$Ln&V#0S>3es*69p`2Io&R-mN7vc&v&QBvwugeWdPcl^=)b6%|XCp zZ~SikfdI*Ok%NE}r|W4J!qr9Hwm8tl#$eiNIJ&rT-yawBvmO}iX2l&mc0j(hM`c!# zV?COD1!T`VgmoIY+Ymj}(4A3YeMumexi2f(wfGIf!b6y8N6Lx^0rGT4n#E@ZZ_UCL ze#2ear}iS`Dz$Ut+60wQsVmcXa(>>GJ9FmYs){-TGx>nW)JZE~XF0tD8$odcRBUtd zzydh4Uc|8#F;wt}UNFM2XKFp6}qPSdtXyGc_5`rH$G9@Nz7WvAPJ6pFREYKo~ z$c;t=NAbllc)O49XcpRJ(SG~C08Y1h>RdVc%D6tYOi1&^+@~j;i|5ZvAaS31m;ceG1Zl(iVNZS40qL5 znur$T!?6{TMkw>O7!E`U_MAv1XX?L^H>i-`uNG3V zdW}gAf$EJPf4C4Y`+67%O!E%?)QmOc*SmYj&!h3j+R2R!%ig7j|N9TwEAES_(YqYM z+d?~qw{RbHwPhLRPU2Qn7PA9FZJ!7m07d2`kOeM=0Lwb@cq*ZSG(2=okTQbV$>qm zllExj0e_Ki0i~Mk^p-v$O4$>SM-9Js>G}W#S`H%tv}guOnyoh`QlV9Y7=Cz+;6HED`=;vc~0eFK94r>$^Fszgx_@ zRqqC$gkVRwdA}X5chc)BHZuLF=F7&aT|Lr77YPsAbf96)rGgH4$MSstW4%L10Hx%h zqVr+y7gr34U214-&JYh4`d9Vm(+Z6eQp9v-%CyM8+ z0i6Z@LK#yRE(?G(7zi};S4K^{bX?EB+4sL&pUi(@YN{g&6=Nq9U5X1x@;dg6e|&c2 zA-+f$K)Lkb!Abogd?IL1MK?b?4!x8l!CRusBMiYgWDE>f-HCK|lv>%1kJhl(cvu6b z(mt;zJ97}x4ctPQa7>!n&HOF!jC2Pztz8brmIu53511 zOvp*ljYmBB{QywTny9w^qC1FYYJliN`=f+uJ&N}>j5zu zFfE}{8~*k{NW-lA2~Q~y-$oJDQbl&4o9Ghdnp>_K4kJ(}97yH*GI&lNOKU2Xnho8_ zz0IeI9kus6X=gtB_#wur93PN|x#iPCv?5g;f(?o-TdnP1Kvu!RCk6DPtQAmSf1_GCkr?!##A#YK-5ADxj zVnSLGQ{#+$Mdt-(;F!#+L2BeMxMShMk7v>u3P)H41Z*H^X|;pe@Y$@jBsX>l#&Brh zdp&Z0zZ_yL>}dkA;~wr=jv|clAI*xj8-wVeGrn!il*jJV>%BA4-2v1H-Xf%W$^xHv zl6<=mja0WbiI`;luD)@tBHJU0{UDB9Jx`lsz_1Q+S>$Me?!1c^DhqV0=5|*d_JhtM z;~j?j8uPsh(FH(E-x@;cb3j@dAP5%XX=As4yYs#Dx;YDP6;*-w{emuKp*3&{=(?mZ z+avaq4;Zv><{tT+B$;KzW3S_u_aK=!-gbx5$oFB5eLquF(g26Y7%A!O_OtpVt`~6H7UOZYBuc67pe>%Tp zNZ(fSXL~=Sue8vT1ZmD&-QvH1c;**Rz+~t!!c8c?uT@RVQDjDLd5RviDZrX1UhNsV zf?z-}OZ=a37kcRyV;w{yc2^I7yj2qut;@1q&w}e|ypteUNoBQl2f21q$JUk69urz#RS^Udqqumj6 z8y`R|21+}$gZ!9*56C5cAj!Zi-uJPh&_#><8HoJ_L;SzqScvY`Ygk}c18M&h8xlF% zu*FfFU1THV)(Y!-BwhS1`%9MvB11I4$tS_stt`UOL-3>W{9}fCBGuaEn+66nzi!A? zPy8Mg3&r4Cm_%!Gk68f!k~5BC_NR>iAmMGZ;606~k$H(vdIjRY?YbUgZfW8!6MYvv zcAoDF10ZDxV!u|m*MOBEh8#hVLTDO@Y3BXySt(?897JQ;))$G|4jz$|)RrsOt z*@4S5z%dO!B}QpZLrD6c-dsjv8>C0doQL#DNaxSA7h_otqto6o~)|uD|tN`Q9ayv)o!t^(i9?W1K5Vk)aG}nNlI7Gg>vtM94hQp{a0T*SAwR z==@RkMGm%@YsEvPulNcDv!ft$-E(BvrsO2Y_|!?K)M07eLAmh0>gNA}olSHh9Pd8Y3t!y+z_JWIFkrA35YB_;Oc^{N_IuHl=9pbMksN}Z@}-vKSyUK3ZTn7_Xy{pH#QRnd z|G4n=5pk`TBO>31SK-TIamHzoL!?N5aYd}8MwXK>E3K1IY zAH%K4f>q1ILdx0qLHk{<=vRi0>@A9J3k zonF_^4M1Cr_EAoTp}K!z1>E${5TU7wr*ZTNNDg0Zwx1Ww9QW0Q!e7i-u!h_*!k3@v z$;56VHf6;+(f0M7*=AKXK3>QpTZX*L{#@%_hY3iLaYKX)4Mr^)wJ3e;y3EX{GxJnFfA~>q2e-j%RFiQ-x++ ze+f~~EvKKH6k5`9f&khn0^jk0oQu!`!auJP9H24K(Up=O-dL9kMeAW&Z6IP9Th2ou zFHKc*daBTLS~Vo5Y53e7!58UWgvNM$0qt2+-G8_V>B1eOa-rVIg><=aGbbih<9ggU z@F7GT3~f+_wjL7uKE;D8Z}w4cuPo9CpTOMvGXHu%Aige^YiBVQ`^Wo*{kgSl5_R4A zLeD*AGo4sc1yHMxvPxI^u#pA;eEU+r_i}_}APY@dx~3VTvf# zu(?X3@@0&MfUTlPf=tysk^)+ov*FJrhqI;g;7$X0O@ijGlW#<%PUnFtFSbFG|3!fM z;Q_?NIor)F(<2*g*Xt|%?U3m4kTiq4zVUqi(ho9S!V3Vfnzi?v;hE-db)juB%x6?8-}hj8M51y+e8U97^b%Rg|L(eGV8 zM9lJ?3-_r$HfRC2#oeSsmR+ijB!*`t!}9%||4rf{Ke? zb9`ZxMp<~4<)q%eox`oljYhMVMdW$>*DBS{BkW6138Cr@B!@gXHwpvwSPW$uywlj> zA-2uo=#w-qYP%$PxY&s&JCMPJcp5U5tx!vEE340?PV@DR7ft{#*6>$Qjde6s-h&wA zKJ-(iDi!nNGMqsPnHvl(kx5@sHq_Ygld4*zfqc#J)^eGzXjl^U#bIc~85nMD`1k?# z;ET?t`+1N=T7EEB1)#LHMfj@K8C`DkzMX{d%#`~B$`tz}^l`}zk4?gYP>KA~paDUj zd{b!9p?=?@b*VeDI)3KH9_3J~fqCw+?sJRIM&eM*dltGHZU{wPG4earMy(xl^dLMZ z3>+Qj?J_@@9K>noahEn94pu%{svL5PNL5e~VVx-)wT(y0K-&9xo4&Rp&*m~S(l0WH zJ0M@ZAa|pi&wYI}YQo9j{o)_|!c}UrQ%QVLr7LB;TqUtey;A zyk48DsBA8n-X-58sT=-{0uT6qTf3CRxrzYe;b2_`TQulk`-SW#fCmtg8vjOBG(QeK z)nm6|Cnq%ARraP0U#xY*SNw5DP@}_lWIV5r8TI9D)5g%>?}LN~k52IdVH42K2JZgn z^G0`fttKiq6DF;t5qQG&7oQl@>fbj>pz0idn?;==mQgl)X>~l$cz%!>{5Bg459QUb$O6gx@7vF_f(M?%aO<}j)7*iqF$a;{;s2Z zy!aM&C!x%(QFQ`Jgj*JCNie-?1IQ|AbX~ML>CuF(5GkEeD#^A+1l_h!q~iR~SpcE> zBzIZRGZ)fkRonATb}e$tdkv(CZo0oPju0N|(_o$IV0J2pY(I~-o9U)ijn?YQo|(5P z^4kFP9jzhVOo_9PPWTE!c4prb8XYwa_^d5Gx|+RdgL?9>QUyVCXFHe-3548xYVm7N z9UFE^DO6xbd_D+0yZf`<7TGau0H3pX&@_-rDzVzjk2SysP37{(tO!Qh@ zV!nIA%a=>`>{R)f^7Bb|^BI^kZM%4k9GvSG#813c@?A#WWfVQ!VEex)mB^le7>E3{ zCgAm7vkm6AmP$J7%F;@_)A6eO*lXAM{y0MYtp`}B;wpD#4H}7`WJDDOWb5`{7X&x4 zvXVAT3YtbVWY`huxyT;`QC{J`i&g5cq0E4uVk{u_$U_C*De99{%@{MV-xLxh&JNHi z2l69cDf);zwj>7(8PndiV)gay54tP;;Ejpxe3`=i6pllhV6v5U!~D%Rq6eJ(z3>p! zJSWsa1qG-4GF#O{y@2gR&a{?L?M`4sT+L4iI61>{VsBR>9e-8Yiye(!txZaMM%QB4 zo$Mr&DD{^g*BFT7yq}`jUa9{z{{a|e($<4^!vEIb42IV0{3|y7r{rXP7g7zY5OqY; zQ!<@yz2G>hmK)G8xrv-xb9Y@ep7vNjwLw9Svc<{hD-FdJ zx8d}_+QQ-FUsSS|2XE(vYAi&-fP~p(P0)y~YdGO!;{B;6nGV;jJ<*+$Her)@&qk$!uO$$zHkSwB;9}7&tY+ z;|yo=*c1=`an*L7k;D^MwwS{T>Q&krj-757m&9+CzJ(_U5iuUaYS8@uNF}xy4XB0W z-QMQESwaV)05wdC;Z~#w z*6^=a0~|i9kX7A>ST}x#+&zM#CvDy;Wk0M_?YZNz|WRj&B7hmFibObv5vD zhQM&+@Jw=y)}TV;E%` zx38}G($&X82KIIWetcvn5o-?haKmPYd41v0uRTnRB8IK}W zy1N??FS-~=V6Xj+ry?{Tvp7}bi_+y8cV#_CmVSV6pKgl9Ns~{+-Akwk+N+f36Dek+ z1uiu#K^G z3OWejTX0!y9O{Oxsa|_EFLJBmmj($lw=zz@N5jZ{7_zXBu(Zkz8gO46bp^)2PGJ#q zCbGbs&RckiJ@6HJfkUtU`9DZlj~w*Bnb{~i<1SQT+&H|rP&N=@tntShQirU~1{%v~ zGRSM2a!ps&|b$I7h$0RguZeMPmQ`of^5f5f! z1sUDeAQbaADRP00?xnzjq=Z?7CWqrjDr82i9wlK}cGw)qm@|eOrNsJtUyRSGNFWyXh4eIp*~d?OC}%)){q$#PKfOfp&H2 zL=8j)=+ifLqAV)1_W!c9fx%bCEfq>Tg=ocHj!w9R6K1<`ys?$`L$0_A&2DMFc8DTB zqq_=XMjlou43u%H{6D*qHRP5WV~dkdYAC@qWB)gmTK6YgkBuGtUeXODUs#Hs`229I z-<%P2Hjk=Kv;_S+b}oZOO;ZD*))*&4Y?zt9lOT(R`gi+IPTjyg-jf$WWwlB2 zY%4IlYVBg*s2OQKP=iUI231k4Z=wuPOulKkb~$x|(}zJ%$s<+=vz($}YUE#X3WF@O6p};i>b6uEoOzob%Cg?6ZBtcWgsv*MM6~ijT8}34d`oEaIn-v_? z4hy$%6dm8w_u~P z)@TXT-7UeFpT1nPl4LuLx;tPrSX0Toy~3Wgqud0lpwA$FfI{eE0nj!OA5J26r;24y zsQMo)+Kd4UE-22m{^K{}U$TZ2YmGMR=Zvg8zsO}+y|W&|>dr?QR4^o9j zO63uBn6)-G-LF=i_V9BaIzao$)nzT=RIIO9tcU6-?zeBP@S}raLbmeS2P62ul*ps9zokC?0CMtS z?q3gN36*{-^)XpytMZsJ2R|Y5n5wZKUJ*l46!U`J*bKI!5u^qLCTk~58t#rSlCIg& zCq#3p`PPUu8%EK2%0|c{=2gMIW>8`{n3fNAW(m%ChG8$2eeP^P`<<0(H_B*Rq$56c z^uH87-(Us+v+>AIY2~o3VTI7qfAS94$UwLYpv!zzQJipD%sZCQ2F4bI#cHq^QnEIun)b*_r4+f{3OHRLQ_hM>BrnXIpqcv28to%>j4O;Ql`)v9k-*BdalP;X~nF`KO$kDnHB}sU7E$0-UxJcwywzO%@ zpL?F6c7GF0)K3wqQuT&CvmQpRCahK!V5if8DwfTWCx@X8V=2%}-qM+oYRp?6jf+W~ z$MzfjD?I0#r2^U$#K54?{h{n8G+5Z#ool4I=M?6QYi&0Vpz0PSyr=8h&zZ>ap2wM( zI8)MjwcSXoI)Ms{&@58l zHjs-JK<>izN|MeoU=0p7(AIK1BW$i^Q^l@UfLw`IpfzUsqcBg?p43b0#Q*r)4lDO+JRy$~;qc?WkKq#j@*5-va>V1s!V7$FG_Z z(`5qV1Dq1O;FlzgQb4`!1BsXx5!p5PsdCs3$o*OBVss$&QDS!^Y;u(_jME5^rv#C0 z=#C{BcU(n8HTU|BZDW@B7Nd}WS5UYjeFNc-q>Ni#Jcx>^*?)X?Xt!0NbISv;J;|Mn zv-R`z)aa}{{T)lvx}Xu^Pgb!8gpr*P{5e$=NcK&48}Rx*??}zdjTrL0XFKe!Hq={I zj)1zTbx1m(lDVyjZL4J_Cgx|d8c=w1f=>{}yLG1zr1IAm$kaBHA{i%X;Fyt`=yd^4 zaxsx0!e6yXUXSq&+q-32P!R&koqAV4B>q*-P?~c<&t>RsgGY8%Mz?0sb1GpW85pKhJ**<<~eH< z1mKvrOe-#IGS_vqbac3Jp>ca?U*+Z%WR{Ko#(&TSx)A!`D?wvH;!lsjuxKBlZBu*3 zw8FV;1hAslMTA9`ZdYxXNRGO@&*A95{^cnX){sy}d>O6DLGa%yFDLDbqPG=Z>e~ja zm+_j%pF~YKu@pItPG`m)>gG8=;xKN$=pnMvVQ9v3pJ~HPOqfD_kF~3B@g!n>ET49T z&AMjBWsxoddA6FS@ywwX%HlXH1P!~%2thAe8l!<{?jx^VD0M@HTNJ&(Cnl{b*BzWp zcg8B~?{t*G(ZO=#Vx98$vbKkta6L#ac@wOVYgHDQkD34T&(NX|9E739c@tPB19Z?18)F^qt@D@3*8(DOI6MZ# z{SbGFIyXLxu3yO)K=2l(%H7Y-7YO~^rQ4n}A#x!f@}1-x4oI<1=e@ymI&%w72FH_{ z3&ni`rqSA=0g4Std1dRmvQ(85+c%%1g6_=u@YqI2IEXBoq(Q8Ee8%_f zK0rZO_M2wzf&9)Z61=1IIm)=x@FQlA$8f&K>i_|RHv^BIro!7ca(TrSm4@ec@GvPz z%#5VURtn1qsDX?zSGd_Qd?KQrR4#IQY$yHku{cv~Ve8C6SQR>O(mDnmx>u{Pzk})W z!gVYqv;XwX{9}W?_?kakG1lu+1ZUVf?j_;-rPs_Vz##eyEL3SN2aJ!s6*w~9$AGLh zIJwe=#ssfrR%xHMQ(TtYaOP;-CzZO|dmG@QoLEvXZ-JWM45~8L0-umj_ATBj8som2 zH>qs~WO;%+XGDxOel^byyM6i)`iWx*dv=sR9z~L_H+@wH*3r6NpVFgm|Ip+MIlY=_ zn#f|4TM{zEfIWjkD;LQd&%{P{hJ5$+bOBbwh6$zYm&Y^omeqzbHXB&G5`r3Upym{T z6`S>eKB79qy}3iB=mS~Kaf&Zq&Oalsnq+k2s1VNFF+&ClyFc)U63QBx+_Vk8mBS5M zP+7J5dx3|>^F5g(TeQg0sTZhN>Gn>vSrp#q*I4V=>qRd=ZC1Zl^8 z_nV3-r#!M$5S>wc5Z>E^HfEe>2r@J()p%3-Tw?+$l8H#uJY1@w4n{L`?k48jPxC;5 zvKnFfgjGgCbe85S6huO=I?XSpr5J23A8|h&+=wCRW@^mGt%ikeeSb)v@(G;Iq)^!j z2?XVDGJ+kQ7el1e!N?e&dC=O{mP{|`)1f1H+LU&(Zf;~Ms4*C%n_@C=hZM>_;HAnv zWtI+44f35J0P4Pr?Zt!=bP7AiFhx>*Ld;m3CPd+t@X5pR0vf5chX2v4aLLP#R)JWB z8)A_4-x#C_4RB#rlR@8pQ0+^YzVpAwpwxDb zLm+6e5;@9k%dB_^Pc?TlNF{M8PHuBw+g-@)7*g=#$(*8V_y%w1#2b>)*QDHGxhUAf zOc4fdT>n{7K{x0i4u&5DJOkYp6OG8NAC$)oq}ldqD#3&1MkK-)_Xv7KA1OIp$JV7O zgzZQT`<%DCkbq3t?Cugm{xuS=fOqD+(Eagxb2K$!a1XN;awJs#e zL{TUelUra2YjwAgg7=H;pXwnGQkOd8^hhq=&}{KU@Sl36_b>z7eHBU*99{1eSGf2i z$|qVmGTwQygB!vGgEqcdG;$rTNSv$$k^@uHjR0aC^)+h!TQ5RJHMf%#IyswIqKG2N zCo`<}O7CiqSg|y#g-lp zOkZ)|Sx|<~i>>a-{mNItlo2w2%I7ljtkMo>Y@_(sy8#p)xLhsfUara!qZ!v)%9BkR z!piKS`VFwpC<%O;IZ%<3V7@byc(*mkUn62c?I_Z#ZT`-ex6KoQn#1mg zlx}WqI%vlny>wE<-F}IIPM@5oPI@@SY<~z)>H-#HjF{qxC2e7iY){)eYfk3v zlrXM5v%cpe#$VZA_`oKJolC7wam95IJ5=LM=h^QcqBhQb{d}S248^mhGxs0HXKcmME*=a2tRx%!CH!LMO0;vL_qxa z3fGa*Ru$ZgqsP|^rqc3N?um6-Jx7K&1kc0igVXtd?r$Nlfez$_kt4hFPwyuUD`8LZ zrN(RJj6YHqMN5*J8^Tb?`F_c17?S6&-x)}{S)FFm2k>=*A>G2> zXq(Et*p$?K3%w6D{gB=EV(Dik&0@37E$V$u|2lA)nOY@^M}r5JE1BbvmGk*x8Zf-C zJrE9VB02K6q$}Jldc2LtS7kHi3ovIhymS$BX{E^WB*SS&CwuPAi=6Fl;n-{dHPGFd z?bhEUun#io(uE#LDuw$XmURO zu@evN(eIyHYV70QyBz#pPEZMJ#vdDQrUI(uf z8|~80Qw68YU+r;_$uC7tNyE;*yhvJ-c2o3g_-!2qJN0@Ix8 ztyV=XYc`Oj^OI@83Pt);x`j7u-1pxVG3JZ<5`o>;b=`e94rnjhPhN~(#xAQ>rO0T{ zSvUdvj%&<2SEWK}CkIh&{5qq%y zMEcfsOm&M5254G!dJML|6P$Pm;d(nQKK0k4;A;`*Es9NGG=hA1NTV2pIjq+)Wx{B6 zA=w4$9`@%oaWv>_<3{i6W{xso#|^&6fIrxM5%Z0}4o}MPO)@)V7fhP|U_pE_yzsHh zu`gTl@byijm|1PKybs3O<54YX;4=5o+bW1*58@PCmHmeMdd1{acCj+kqq@lffGjye z*zxFJU?3~P(yOh=u_9WMiQ+dGr$Gdv`MS-LWva^g!z7Vqc?O1sEc|WQ5@g*zTJ=sS zFVS)wG&%4L^0du+>?5C3${*0@S+jgK?E5GWPMbs=L4)>oLu@W(5 z!@qg2qOKCov{lY~9q(UjHkF1EJVrV`h3N1pva^m~7P!DC{iH3FKtOwdD zACuu6m*lpu{iYUTu*ad14~H(HQ$t}r&T7TskXK+p#Fk(nw)a4M`4e5OO?Sh`^2r5{ z6b!vZObM^4T3&TC!K1kpUp!O3FLE5wg!E(bzdmiwW??7B91esA)4@JeE zltdf<{bu#{u2~Dd4JIp3UYhSAr6zmcUdjWC%%{a)$_hxhM32tq` z89A;C)rR|H_b~R6-xFB5lO$0AedM$6Wap8h0|}FnuoO*@1lJcesuNQra?aCa_UeiS zwHx99OK~3`fPW)q{SWw+P;(z`E5S<#?W_qVtGoArS9ZuCsTr6>nGoC=Tj)q-lr5Dq zQKu%J->7I_TwZcY)1OQBL4gaP4RB!CI{#2@zw5!u#wLF3Z6dq1b&smuADLar3Rvl8 zF$6ro^g+0kJJWs#$TFqiXXgf+YG?3$v&oivZbztE8wWF?&dQg$FrLmUqmR~SpmvA6 z5wiNZS`Jryh_<2$QG_2B7Zw4pKs!0$}Y@Lqn#dP@!zfJ7Ut4u_;m4narPMKo284+|1|aQWx&i@1%!OtUS(#k5AEQDpRnp; z{Ovadfo9VLY$S=JACPK?Yw-%r=-*Fgg=6a&<=CAtFcPd2lojd>+}z)qv+e$ z@cBujX~eR0GOq1APxcw*_4b0SkYnb@S!4i@SogY&34K|YWzCT)+l@}2M_|&l;Y}XH z^`c*RnDS1;oV{0wNsrF>t$oI&B%)c_6NAJ@&MPZfOyFO%Oia4EbjIT+td0tJ>diA< zWgI!03nu(GTOKBwKpRq@sV?s1>Bg&nnmKRijdcNtD3g|9$E9jB;(<&m&XkTyCH=Ci9}9-Y z;kxOqz}{UUMCue(wz+mR&tkaMZlzLmBI)|N4!Ls!x2?nJCuU}*p>jS$1@8pJ{m+PvK>c!3Kc}*`Pv?T7YeCOh-37AT z4cu5h^jabR-t=&+M^eh+i&QOgVvW;N0n#C`e?YwgDExqYqL8SNy6R)*N#urT4|6&o z`zf;9+wW>3FQYXZD5OKf7Bz#i;eAJNp@YbJ7=J50DVj+y+G1&lK5&$OJI-Zv7sWFH zsR(A+mLWDysH0L$eV*UayVQOBysg9nOuNm^C#P#0rL{^ZolT+fbkRApBDg!zMlH1< z0Q1CT1~p5q!hBlAfRfdEp>a!zOr#=UI*;I&qzT@}JE@M&N z1@K!(-w9IIL8**mI3A0)YD@6<97I8pG&vN~Tq-fg8gFG@)X$X8Lw5D-TGr0E@O8kT zoAmBGQ(J-1x*TQnsOu{`rXYI}D)_2rg`her)^a)OsruGd3s1{nt>RxqX{cyOR_r#A z@i>T!v(wmGfk0JR@$d+HzI67Fd|`A~TLo~n0p?%&h8i>>>|Ai*w%o&Jgz1z&a`*Qq z;-seB+V{Go0g;t)tWRDw(zQvIk&%~yn3=?D?M6JNVa49D3}kAf>&!#`bD&DV9xuAd zWscdEtnkvPhP>;f=il}4A~FXJ*a*p~;vN8Pz3;2>liZD$=6>C!&%Nv7CrpO;TP8)x zK{R4&sFNufq}`pj zN=@PFpoP}u2xnp|J&3#VJmHdJ-KA*{K@>Q;THig-t01BfefV5$Z$q)NM4ya1{~6o@ z0t8OdK(gZNHPEJR!^CU|L$d0i1KV37rqbu?NNoKEsYORNK582*xeG2*P210tm|f{BT3ZRAwN4eufW*jTed>Gg{G}=RUcD?z@o6|F zRD(b~=eH-4wOuwwjqpn8sV;>uhmZj2&mOfsFRx+~00dLXOW;iQWqp88WS6BVFmV4O z+9~HE^RUuKtz)gqS|j<2$Njo1rIh+Sn=l!e8VDPhNikCw_!o0UVr&o6i17+Q2r6EN zbrTe;f890C>4NuUft0N9M`MXcp_eT*@eb0_JuQhZMM`EYw^n(nmCPYpau@n_sNF1J-u?Hh7<~z1MvA2S5+Z~O+SY+7t-;x^ zJ2@RlV5ZmOuX>U9^R+gClnlh+bYKlOZ)0N%*9p5K1&20xPB^_mM%