Now that you mention it I think it was the hardware decoder setting that fixed my micro-stutters as well. Not sure which setting I ended up with, but I'll check on my lunch break and report back!
I think the solution was Software Decoding. Hybrid and Hardware I can reproduce this issue but I'm having a really hard time reproing it with software.
When I was running shields I occasionally had the black screen like you mention. I always chalked it up to an hdmi handshake issue with my Yamaha AVR. I always used hardware because that was required for atsc3 channels to work. I’m much happier with ATV 4ks now.
That's good to know!
I'm nearly certain it's down to Software/Hardware decoding. I needed another box for my bedroom and really wanted to get rid of the Apple TV so it's going back to Walmart later. This ended up finally working out for me!
I always love a story with a happy ending! 
I've been testing it for the last hour with software decoding and I can't reproduce the issue anymore, so I think that was what it was.
Last night when I was testing, I forgot to try software. I only tried hybrid and hardware. Regardless, I needed another streamer for my bedroom, so it's kind of perfect.
The 2017 will go in the bedroom and the old 2019 will go in my office. So it kind of works out. The Apple TV goes back to Walmart. 
multiview is a killer feature on appleTV tho.........
Checked my Shield at lunch and it's set to Hardware. 
Maybe it just has something to do with my source.
I am using the HDMI encoders. I wonder if it's not an issue with OTA.
Yeah. Mine is set to Hardware too. I generally don't watch live. Especially not through the ADBTuner and LinkPi encoder. I have noticed an occasional glitch/stutter/pixelization, though. So I changed it to "Software" playback, as an experiment to monitor over the next several days. 
Yeah, software seems to be way more reliable.
Just like with Android in general, it seems to be more reliable.
Unfortunately, got the black screen on software again, but at this point I'm just going to live with it unless someone from Channels wants diagnostics.
This has been my philosophy about Channels DVR in general. For my type of viewing and recording, It is so far superior to any other DVR alternative that I am happy to accept any little idiosyncrasies I happen to notice. It simply adds character to the beauty of having the ability to consolidate several diverse providers into a single platform. Not to mention the fantastic and responsive CDVR developers and additional support of this amazing community.
So far Apps -> Special App Access -> Energy Optimization -> Channels = Not Optimized and Developer Options Logger Buffer Sizes = 16 MB seemed to maybe help. Also keeping Dolby audio processing on may have made a difference as well. I normally shut that off, but then I was reading that it's supposed to be on.
I am starting to get the feeling that this whole issue is because I had to Dolby audio processing off. Hardware decoding needs to be on also. I think having both Dolby Audio processing on and hardware decoding on has resolved it. It's going to take a few days to feel confident, but I think I got it.
Ironically, these are the defaults. 
It's not totally fixed. I basically have it down to a race condition. It seems to happen when the Shield is initializing. Like after waking from sleep. Not all of the time anymore. I'm going to call that a win! It's pretty deliberate to run into.
"main" prio=5 tid=1 Waiting
| group="main" sCount=1 dsCount=0 flags=1 obj=0x722b6458 self=0x25c7aca7b0
| sysTid=15958 nice=-10 cgrp=default sched=0/0 handle=0x23d12b74f8
| state=S schedstat=( 13971324808 3304792203 27586 ) utm=1210 stm=186 core=0 HZ=100
| stack=0x7fecc44000-0x7fecc46000 stackSize=8192KB
| held mutexes=
at java.lang.Object.wait(Native method)
- waiting on <0x0c4905ba> (a java.lang.Object)
at java.lang.Object.wait(Object.java:442)
at java.lang.Thread.join(Thread.java:1438)
- locked <0x0c4905ba> (a java.lang.Object)
at java.lang.Thread.join(Thread.java:1519)
at com.getchannels.android.dvr.K.U2(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:28)
- locked <0x02b24c6b> (a com.getchannels.android.dvr.K)
at com.getchannels.android.ChannelsApp.onAppStop(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:32)
at java.lang.reflect.Method.invoke(Native method)
at androidx.lifecycle.c$b.a(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:42)
at androidx.lifecycle.c$a.b(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:17)
at androidx.lifecycle.c$a.a(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:9)
at androidx.lifecycle.J.g(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:5)
at androidx.lifecycle.x$b.a(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:25)
at androidx.lifecycle.x.e(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:86)
at androidx.lifecycle.x.o(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:47)
at androidx.lifecycle.x.k(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:33)
at androidx.lifecycle.x.i(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:15)
at androidx.lifecycle.ProcessLifecycleOwner.l(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:13)
at androidx.lifecycle.ProcessLifecycleOwner.h(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:7)
at androidx.lifecycle.ProcessLifecycleOwner$c.onActivityStopped(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:8)
at android.app.Application.dispatchActivityStopped(Application.java:496)
at android.app.Activity.dispatchActivityStopped(Activity.java:1445)
at android.app.Activity.onStop(Activity.java:2591)
at androidx.fragment.app.u.onStop(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:1)
at androidx.appcompat.app.c.onStop(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:1)
at com.getchannels.android.PlayerActivity.onStop(r8-map-id-e4614b936a4aff5bd4b0975d07cb0a99900dbe681a762f7f7779fd46633759e7:34)
at android.app.Instrumentation.callActivityOnStop(Instrumentation.java:1476)
at android.app.Activity.performStop(Activity.java:8215)
at android.app.ActivityThread.callActivityOnStop(ActivityThread.java:4852)
at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:4831)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:4905)
at android.app.servertransaction.StopActivityItem.execute(StopActivityItem.java:40)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2067)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7668)
at java.lang.reflect.Method.invoke(Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
"Thread-28" prio=10 tid=2 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x13bc1680 self=0x25c7bcbbf0
| sysTid=15997 nice=-10 cgrp=default sched=0/0 handle=0x26dc851cc0
| state=S schedstat=( 56683323744 10254328129 559376 ) utm=4642 stm=1026 core=1 HZ=100
| stack=0x26dc75a000-0x26dc75c000 stackSize=995KB
| held mutexes=
native: #00 pc 000000000004b350 /apex/com.android.runtime/lib64/bionic/libc.so (syscall+32)
native: #01 pc 000000000004ef34 /apex/com.android.runtime/lib64/bionic/libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+144)
native: #02 pc 00000000000af1d8 /apex/com.android.runtime/lib64/bionic/libc.so (pthread_cond_timedwait+120)
native: #03 pc 000000000012b770 /data/app/~~Y7Ssl31icEay0KNF6l7sag==/com.getchannels.dvr.app-T8V01p27uJoNS188n64f7A==/split_config.arm64_v8a.apk!libmpv.so (offset 7690000) (mp_dispatch_queue_process+280)
native: #04 pc 000000000015f614 /data/app/~~Y7Ssl31icEay0KNF6l7sag==/com.getchannels.dvr.app-T8V01p27uJoNS188n64f7A==/split_config.arm64_v8a.apk!libmpv.so (offset 7690000) (mp_wait_events+76)
native: #05 pc 000000000016213c /data/app/~~Y7Ssl31icEay0KNF6l7sag==/com.getchannels.dvr.app-T8V01p27uJoNS188n64f7A==/split_config.arm64_v8a.apk!libmpv.so (offset 7690000) (mp_idle+96)
native: #06 pc 00000000001621d8 /data/app/~~Y7Ssl31icEay0KNF6l7sag==/com.getchannels.dvr.app-T8V01p27uJoNS188n64f7A==/split_config.arm64_v8a.apk!libmpv.so (offset 7690000) (idle_loop+88)
native: #07 pc 0000000000156e74 /data/app/~~Y7Ssl31icEay0KNF6l7sag==/com.getchannels.dvr.app-T8V01p27uJoNS188n64f7A==/split_config.arm64_v8a.apk!libmpv.so (offset 7690000) (mp_play_files+524)
native: #08 pc 0000000000141f4c /data/app/~~Y7Ssl31icEay0KNF6l7sag==/com.getchannels.dvr.app-T8V01p27uJoNS188n64f7A==/split_config.arm64_v8a.apk!libmpv.so (offset 7690000) (core_thread+40)
native: #09 pc 00000000000afd4c /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+64)
native: #10 pc 0000000000050288 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
(no managed stack frames)
I was able to reproduce this again this morning and this is what came out of the stack trace from the bug report I ran on my Shield. I was assisted by Claude in terms of finding the actual relevant part of this stack trace to share. It's a little hard to reproduce, but at this point, it seems to mostly happen after waking the Shield from sleep. It was much easier to reproduce with Dolby Audio processing turned off.
I just wanted to sincerely thank the developers for fixing this bug. It was driving me absolutely batty and I really appreciate solving it so quickly.
So, this latest Android client update did the trick?
v2026.01.22.2303
• FIXED: Unusual crash that could happen
So it started happening again and I realized as the black screen was sitting on my TV, if I restarted the docker container running Channels, it would come right back. So I started digging and it looks like I changed a setting from a default on my server ages ago and never changed it back, probably troubleshooting something else.
Essentially, I had NIC Offload disabled. I believe this was causing an issue that was leading to stale connections being established with the Channels DVR server and then never being released. The smoking gun that was a networking issue was when I restarted the server and without touching the Shield, Channels came right back.
The question is, did I actually fix it with this setting that I set back to default? Or do I have more testing to do? I don't know. It's just going to take some time for me to thoroughly test to make sure it's resolved.
Regardless for their help and for how quick they released a patch, I donated to the dev team. They really deserve it. These guys rock.
Edit: There was more to it. I had to disable bridging and use eth0 and enable macvlan. Now I can see the connections timing out instead of piling up, which seemed to be what was causing the ANRs. I think my issue is a very complex Docker networking issue.
It appears that I found a solution. Both sets of fingers crossed.
It's not the Shield, it's Unraid. Specifically, Docker.
I had to put my container into privileged mode. Evidently some syscalls were being blocked and a TCP connection to the Shield was essentially getting stuck and Channels could not see it essentially.
What would resolve the blank screen was restarting the docker container and that led me down a whole rabbit hole to eventually putting it into privileged mode which resolved it and I was able to confirm the syscalls being blocked with strace.
So if anyone else is running into random blank screens on Unraid with an Nvidia Shield or Android TV device, try throwing the Docker container into privileged mode with the switch in Dockerman.
From strace
This is without privileged mode:
epoll_ctl(4, EPOLL_CTL_ADD, 14, ...) = -1 EPERM (Operation not permitted)
epoll_ctl(4, EPOLL_CTL_ADD, 16, ...) = -1 EPERM (Operation not permitted)
This is with:
epoll_ctl(4, EPOLL_CTL_ADD, 12, ...) = 0
epoll_ctl(4, EPOLL_CTL_DEL, 12, ...) = 0
epoll_ctl(4, EPOLL_CTL_ADD, 12, ...) = 0
epoll_ctl(4, EPOLL_CTL_DEL, 12, ...) = 0
This now deprecated container used this particular flag which is also invoked by privileged mode. Privileged mode just does a whole lot more stuff. This other method is probably a lot more secure, but honestly if it's working, it's working and I'm not touching it.
security_opt:
- seccomp=unconfined
Also, FancyBits has an old repo for Unraid that has Privileged enabled in the XML. Evidently for Unraid, privileged is very much needed.
Edit: Had a black screen... and a recovery after 10 seconds or so!!
Edit 1:
There may be one more piece to this. ipvlan + br0 and macvlan bridging off and Channels directly on eth0. The former config is a seemingly problematic default Unraid ships with. Version 6.12.4 2023-08-31 | Unraid Docs
With this change channels runs on eth0 instead of br0. As soon as I switched back to stock ipvlan and br0 I started having issues after hours of stability. Going to test more but it makes logical sense from a network perspective since it's it's basically a wedged socket and this is now binding to the physical NIC on port 8089.
Edit 2: It gets weirder. I had it black screen again, sit for five minutes, then come back on it's own. The shield has a very buried setting in developer options to Automatically Reconnect on ARP Failure. I'm thinking that it's just too coincidental that this is clearly happening, where a connection is getting sort of stuck, and there’s an automatically reconnect developer option clearly to address something like this. It's just way too convenient, so I am repeating my reproduction steps and testing again to see if it happens. But I feel pretty good about this one, and I think this is a multi-pronged issue involving Docker networking and ARP resolution. It seems pretty Shield specific though and maybe less specific to Unraid.