AndroidHDMI for Channels (ah4c): A virtual channel tuner using HDMI Encoder(s) + streaming stick(s)

Yes -- all good. Nice work! This project is so fantastic.

1 Like

Oh, I'm so glad it's working.
Thank you so much!

I actually, for the first time, used Codex to do that, and it worked really well.

You should also be able to remove transcribe.cpp as well as individual drivers. I forgot to add that in the initial release.

I noticed a possible mistake in the Docker Compose. HEARTBEAT_INTERVAL defaults to zero out of nowhere. And that can break configs randomly. I believe 180 is an acceptable default. That what I have mine set to is just three minutes. And that keeps random issues from occurring for those that are affected. When I copied the new compose today, I was wondering what was breaking.

OK, so your feeling is that this should be an opt-out for Osprey users, rather than an opt-in -- where someone uses the env var override to set HEARTBEAT_INTERVAL=180?

I'm good either way, since this is strictly an env var used in scripts, and only in the Osprey scripts currently.

My feeling is it's been since June. I have reset every single one of the 10 boxes in my Osprey fleet more than once. There have also been two updates. I've also found a report on Reddit of the same channel sort of retuning issue, and this is the only thing that's worked around it. It's five minutes to the wall clock, so pushing it up every three minutes is the only thing that stops it. So I think it should be opt-in to prevent it from affecting users that are affected.

I also noticed that for some reason, some ospreys will just never update ever, which is something that I've heard of and have never experienced, so maybe that's why some people don't ever have the issue.

I'd rather prevent confusion for people who don't know what's happening and didn't do like a month of digging on it.

To me an opt-in would be set the default to 0 HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-0} and then users would use the env var override to set it to 180 HEARTBEAT_INTERVAL=180. But your post reads more like you think it should be the opposite.

Like I said, I'm fine either way, I just want to be sure I understand how you'd like to see it. We could also set HEARTBEAT_INTERVAL=${HEARTBEAT_INTERVAL:-180}, and then leave the env var override unset HEARTBEAT_INTERVAL=. I would consider this an opt-out approach, as the user would then have to specifically set the override value to 0 if they wanted to disable HEARTBEAT_INTERVAL -- is this what you have in mind?

I would say maybe just leave it unset. Let the user set it if they want and leave the default. It doesn't do any harm leaving it there and can only benefit if they do hit that specific weirdness with DirecTV. Most people will never notice it, and the ones that do notice it and care can opt out.

OK, so set the default to 180 and the override unset -- do I have that right? If so, I'll push that change sometime today.

I'm looking at FastChannels new ah4c support atm, to see if there are any tweaks we should make to help support that effort.

2 Likes

Yea, I agree. I think just as long as for users who might not know any better they are getting a heartbeat with that keycode every 3 mins by default that's the best path for sanity. :rofl:

I was also looking last night into the playback detection code and also possibly a faster way of powering on devices than KEYCODE_WAKEKUP if you want to take a look. I opened a much smaller PR.

@mackid1993 You have to check out what @KineticMan has done with the FastChannels ah4c bridge. I've been using it most of the day, including submitting a couple of PRs, and it is pretty fast. Tune times for Philo, have been around 4 seconds. It also has me thinking about a "warm" channels concept, for those of us with a generous number of tuners. More here:

and here:

and here:

and here :-):

One question you might want to answer for yourself, is whether his custom Android app works on an Osprey.

1 Like

I'll take a look tonight. Just please take a look at my DM.
My test Dockerfile and GitHub Actions build.yml got pulled into yesterday's PR and polluted the upstream repo.

You might need to just go back into Git history and restore yours and delete that yml file.

My local Dockerfile looks fine, and there's nothing to fetch from the repo. I haven't merged your PR from last night, so maybe you can fix that?:

#docker buildx build --platform linux/amd64,linux/arm64 -f Dockerfile -t bnhf/ah4c:latest . --push --no-cache

# First Stage: Build ws-scrcpy and ah4c
FROM golang:trixie AS builder

ARG TARGETARCH
ENV DEBIAN_FRONTEND=noninteractive

# Install dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
    git nodejs npm python3 make g++ \
    && rm -rf /var/lib/apt/lists/*

# Build ws-scrcpy application
WORKDIR /ws-scrcpy
RUN git clone https://github.com/NetrisTV/ws-scrcpy.git . \
    && npm install && npm run dist

WORKDIR /ws-scrcpy/dist
RUN npm install

# Build ah4c application
WORKDIR /go/src/github.com/mackid1993
ARG AH4C_SHA=main
RUN git clone https://github.com/mackid1993/ah4c . \
    && git checkout ${AH4C_SHA} \
    && sh bump-version.sh \
    && go build -o /opt/ah4c

# Second Stage: Create the Runtime Environment
FROM debian:trixie-slim AS runner
LABEL maintainer="The Slayer <[email protected]>"

ARG TARGETARCH
ENV DEBIAN_FRONTEND=noninteractive

# Add contrib/non-free/non-free-firmware components
RUN sed -i 's/^Components: .*/Components: main contrib non-free non-free-firmware/' /etc/apt/sources.list.d/debian.sources

# Install runtime dependencies (adb for Android-based tuners, nodejs/npm for ws-scrcpy, python3 for pyatv)
# ffmpeg/ffprobe come from the Debian repo; trixie's 7:7.1.5-0+deb13u1 carries the
# CVE-2026-8461 (MagicYUV "PixelSmash") fix on amd64 and arm64 (DSA-6361-1).
RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates curl bash dnsutils procps nano tzdata jq bc \
    android-tools-adb tesseract-ocr \
    ffmpeg \
    nodejs npm \
    python3 python3-pip \
    libva2 libva-drm2 vainfo \
    && rm -rf /var/lib/apt/lists/*

# Install pyatv from PyPI for PYATV=true (build-essential/python3-dev needed to compile miniaudio on arm64)
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential python3-dev \
    && pip3 install --break-system-packages pyatv \
    && pip3 show pyatv \
    && apt-get purge -y --auto-remove build-essential python3-dev \
    && rm -rf /var/lib/apt/lists/*

# Add Intel VA driver & QSV runtime only on amd64
# trixie dropped the legacy Media SDK (libmfx1); QSV in ffmpeg 7.x uses oneVPL:
# libvpl2 (dispatcher) + libmfx-gen1.2 (GPU runtime, Gen11/Ice Lake and newer).
RUN if [ "$TARGETARCH" = "amd64" ]; then \
      apt-get update && apt-get install -y --no-install-recommends \
        intel-media-va-driver-non-free libvpl2 libmfx-gen1.2 && \
      rm -rf /var/lib/apt/lists/* ; \
    fi

# (Optional) set for Intel VA driver name
ENV LIBVA_DRIVER_NAME=iHD

# Set up working directories
RUN mkdir -p /opt/scripts /tmp/scripts /tmp/m3u /opt/html /opt/static

WORKDIR /opt

# Copy built files from builder
COPY --from=builder /ws-scrcpy/dist /opt/ws-scrcpy
COPY --from=builder /opt/ah4c /opt/ah4c

# Copy necessary scripts and static files
COPY docker-start.sh adbpackages.sh /opt/
COPY scripts /tmp/scripts/
COPY m3u/* /tmp/m3u/
COPY html/* /opt/html/
RUN sed -i '/href="\/config"/d; /href="\/env"/d' /opt/html/index.html
COPY static /opt/static/

# Ensure start script is executable
RUN chmod +x /opt/docker-start.sh \
    && groupadd render || true

# Expose needed ports
EXPOSE 7654 8000

# Run start script -- PYATV=true (case-insensitive) selects atvremote/pyatv tuners internally
CMD ["./docker-start.sh"]

Yep, I will fix that in my PR. Sorry about that. It's currently live in the repo, so it'll just get merged in with my changes.

@bnhf https://github.com/sullrich/ah4c/pull/30

Dockerfile is back to sullrich. Sorry about that mistake.

1 Like

No worries. Thanks for sorting it out.

2 Likes

@bnhf @KineticMan

Went to try the new fastchannels ah4c bridge this morning. Im using an ONN box. I was able to authenticate the adb keys and then downloaded the fastchannels app to my ONN device. I really like the test button - nice feature!

All is well so far.

My question is regarding these tuning scripts and where to place them.

How do we handle a mix of PYATV based scripts and adb based scripts located in the same HOST_DIR?

I was about to save the generated scripts from fastchannels to my machine, but I dont want to blow away my working prebmitune, bmitune, and stopbmitune scripts. All are highly customized.

Is this another PYATV=true issue to be flushed out?

Do we have two sets of bmitune, named differently perhaps - like ATVbmitune.sh that handles the PYATV case.

@KineticMan once this is flushed out I'll install a NBC universal app and test the bridge with my setup on an ONN box. But so far it is looking brilliant. It installed and tested very smoothly from within fastchannels on the ONN box as far as I can tell...

Im eager also to update my ah4c container to latest, but Im holding off for the time being until my second linkpi arrives - its late...

I want to test the preroll. My tuners are very fast, and channels reliably commercial skips right over them. However there are some edge cases where the tuner has to auto correct and it takes time, and the preroll would be really cool

My question is this

Could we have multiple preroll files based on the channel being called. If we can grab the appropriate station logo based on the M3U and then call the matching preroll file, it could be really really slick!

Looking forward to playing with this soon.

I did play with the playback detection and it worked with my PYATV tuner..

ah4c, as written, treats the defined tuners as a pool of interchangeable virtual tuners. So this would represent a significant shift in how tuners are managed. For now, there are no plans to move in this direction.

So, the good news is that we have a single image that can be used for adb OR atv -- but not adb AND atv. However, if you'd like to be able to use both, it can be done via two containers.

:sob: Pre-roll was an absolute nightmare to get working in the state that it's in right now. So, this request is not something I would personally entertain myself. That was just one of the hardest things to implement. It was harder than closed captions, just getting the video to stitch.