Proxmox-for-Channels: Step-by-Step for Creating a Chrome-Capture-for-Channels (cc4c) LXC Container

Thanks for the terrifc guide - I have this deployed in a ProxMox Container and it does work but I can't seem to get hardware encoding working, decode does work according to Chrome's GPU page.

For reference I am using a system with an i7-13700H. Encoding works for other containters (channels, scrypted & plex). Am I missing something or is it not possible? There is a lot of dated discussions saying it's not possible yet I see other people saying it works.
image

AFAIK it is not possible for chrome to use h/w encoding. Many people have tried but it does not work.

1 Like

Thanks for clearing that up!

since i'm fairly new to LXC containers - since this is headless how can i run a display or chrome on the container to add user credentials

Both this LXC version, and the Docker version of cc4c support VNC, so once you have an active stream -- connect using a VNC client and enter your credentials.

1 Like

@bnhf directed me to this thread after I tried rolling my own Proxmox LXC using the Dockerfile as a guide. Alas, I get the same result doing it this way as I did with own attempt. Specifically, the browser exits with the following whenever I attempt to start playback by connecting to http://hostname:5589/stream?url=...

root@cc4c2:/opt/chrome-capture-for-channels# Xvfb :99 -screen 0 1920x1080x16 & x11vnc -quiet -nopw -display :99 -forever & bun main.js -v 9500000 -a 256000 
[1] 348
[2] 349
Xlib:  extension "DPMS" missing on display ":99".

The VNC desktop is:      cc4c2:0
PORT=5900
[2025/12/26 23:18:17.592] Selected settings:
[2025/12/26 23:18:17.595] Video Bitrate: 9500000 bps (9.5Mbps)
[2025/12/26 23:18:17.595] Audio Bitrate: 256000 bps (256kbps)
[2025/12/26 23:18:17.595] Minimum Frame Rate: 30 fps
[2025/12/26 23:18:17.595] Port: 5589
[2025/12/26 23:18:17.595] Resolution: 1920x1080
[2025/12/26 23:18:17.601] Chrome Capture server listening on port 5589
[2025/12/26 23:19:01.854] Launching Browser, Opts {
  executablePath: '/usr/bin/google-chrome',
  pipe: true,
  headless: false,
  defaultViewport: null,
  userDataDir: '/opt/chrome-capture-for-channels/chromedata',
  args: [
    '--no-first-run',
    '--hide-crash-restore-bubble',
    '--allow-running-insecure-content',
    '--autoplay-policy=no-user-gesture-required',
    '--disable-blink-features=AutomationControlled',
    '--hide-scrollbars',
    '--window-size=1920,1080',
    '--disable-notifications',
    '--disable-background-networking',
    '--disable-background-timer-throttling',
    '--disable-background-media-suspend',
    '--disable-backgrounding-occluded-windows',
    '--auto-accept-this-tab-capture',
    '--allowlisted-extension-id=jjndjgheafjngoipoacpjgeicjeomjli',
    '--use-gl=angle',
    '--use-angle=gl-egl',
root@cc4c2:/opt/chrome-capture-for-channels# bun main.js -v 9500000 -a 256000
[2025/12/26 23:21:11.923] Selected settings:
[2025/12/26 23:21:11.924] Video Bitrate: 9500000 bps (9.5Mbps)
[2025/12/26 23:21:11.924] Audio Bitrate: 256000 bps (256kbps)
[2025/12/26 23:21:11.924] Minimum Frame Rate: 30 fps
[2025/12/26 23:21:11.924] Port: 5589
[2025/12/26 23:21:11.924] Resolution: 1920x1080
[2025/12/26 23:21:11.930] Chrome Capture server listening on port 5589
[2025/12/26 23:21:30.402] Launching Browser, Opts {
  executablePath: '/usr/bin/google-chrome',
  pipe: true,
  headless: false,
  defaultViewport: null,
  userDataDir: '/opt/chrome-capture-for-channels/chromedata',
  args: [
    '--no-first-run',
    '--hide-crash-restore-bubble',
    '--allow-running-insecure-content',
    '--autoplay-policy=no-user-gesture-required',
    '--disable-blink-features=AutomationControlled',
    '--hide-scrollbars',
    '--window-size=1920,1080',
    '--disable-notifications',
    '--disable-background-networking',
    '--disable-background-timer-throttling',
    '--disable-background-media-suspend',
    '--disable-backgrounding-occluded-windows',
    '--auto-accept-this-tab-capture',
    '--allowlisted-extension-id=jjndjgheafjngoipoacpjgeicjeomjli',
    '--use-gl=angle',
    '--use-angle=gl-egl',
    '--enable-features=VaapiVideoDecoder,VaapiVideoEncoder',
    '--ignore-gpu-blocklist',
    '--enable-zero-copy',
    '--enable-drdc',
    '--no-sandbox'
  ],
  ignoreDefaultArgs: [
    '--enable-automation',
    '--disable-extensions',
    '--disable-default-apps',
    '--disable-component-update',
    '--disable-component-extensions-with-background-pages',
    '--enable-blink-features=IdleDetection',
    '--mute-audio'
  ],
  enableExtensions: [
    '/opt/chrome-capture-for-channels/node_modules/puppeteer-stream/extension'
  ]
}
[2025/12/26 23:21:30.597] [Info] Restarting following first-run puppeteer-stream extension installation

OK, let's start with the obvious stuff first:

cc4c doesn't use http:// for its scheme, it's chrome://, as shown in the first post:

None of what you posted here indicates an error, the [Info] message is just to give you a heads-up on why there's a restart of cc4c on the first run of a new installation:

The fact that you're running this command line, suggests you missed the post later on with additional detail on setting up the startup script and additional environment variables. Since that post contains refinements to the restart process, it's important:

Lastly, just to confirm no issues have crept into the process, I created a brand new LXC this morning and ran through the process myself. Here's the console output from a tune to a Sling channel, after the initial run and Sling login process via VNC:

cc4c3 login: root
Password: 
Linux cc4c3 6.8.12-14-pve #1 SMP PREEMPT_DYNAMIC PMX 6.8.12-14 (2025-08-26T22:25Z) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Dec 27 07:59:25 UTC 2025 on pts/1
Google Chrome 143.0.7499.169 
Starting xvfb, x11vnc, and npm...
The VNC desktop is:      cc4c3:0
The VNC port is:         5900
[2025/12/27 01:12:43.678] Selected settings:
[2025/12/27 01:12:43.680] Video Bitrate: 9500000 bps (9.5Mbps)
[2025/12/27 01:12:43.680] Audio Bitrate: 256000 bps (256kbps)
[2025/12/27 01:12:43.680] Minimum Frame Rate: 60 fps
[2025/12/27 01:12:43.680] Port: 5589
[2025/12/27 01:12:43.680] Resolution: 1920x1080
[2025/12/27 01:12:43.686] Chrome Capture server listening on port 5589
[2025/12/27 01:20:02.640] Launching Browser, Opts {
  executablePath: '/usr/bin/google-chrome',
  pipe: true,
  headless: false,
  defaultViewport: null,
  userDataDir: '/opt/chrome-capture-for-channels/chromedata',
  args: [
    '--no-first-run',
    '--hide-crash-restore-bubble',
    '--allow-running-insecure-content',
    '--autoplay-policy=no-user-gesture-required',
    '--disable-blink-features=AutomationControlled',
    '--hide-scrollbars',
    '--window-size=1920,1080',
    '--disable-notifications',
    '--disable-background-networking',
    '--disable-background-timer-throttling',
    '--disable-background-media-suspend',
    '--disable-backgrounding-occluded-windows',
    '--auto-accept-this-tab-capture',
    '--allowlisted-extension-id=jjndjgheafjngoipoacpjgeicjeomjli',
    '--use-gl=angle',
    '--use-angle=gl-egl',
    '--enable-features=VaapiVideoDecoder,VaapiVideoEncoder',
    '--ignore-gpu-blocklist',
    '--enable-zero-copy',
    '--enable-drdc',
    '--no-sandbox'
  ],
  ignoreDefaultArgs: [
    '--enable-automation',
    '--disable-extensions',
    '--disable-default-apps',
    '--disable-component-update',
    '--disable-component-extensions-with-background-pages',
    '--enable-blink-features=IdleDetection',
    '--mute-audio'
  ],
  enableExtensions: [
    '/opt/chrome-capture-for-channels/node_modules/puppeteer-stream/extension'
  ]
}
[2025/12/27 01:20:02.797] New target page created: about:blank
[2025/12/27 01:20:02.808] Target page changed: chrome-extension://jjndjgheafjngoipoacpjgeicjeomjli/options.html#55200
[2025/12/27 01:20:02.808] New target page created: chrome-extension://jjndjgheafjngoipoacpjgeicjeomjli/background.js
[2025/12/27 01:20:03.812] Need to initialize stream capabilities
[2025/12/27 01:20:03.960] streaming https://watch.sling.com/1/channel/1a3c345b84b149918e0bad8f797df70a/watch
[2025/12/27 01:20:04.098] Target page changed: https://watch.sling.com/1/channel/1a3c345b84b149918e0bad8f797df70a/watch
[2025/12/27 01:20:04.112] URL contains watch.sling.com
[2025/12/27 01:20:05.203] Target page changed: https://watch.sling.com/dashboard/home
[2025/12/27 01:20:05.527] Target page changed: https://watch.sling.com/modal
[2025/12/27 01:20:30.720] Target page changed: https://watch.sling.com/dashboard/home
[2025/12/27 01:20:31.323] New target page created: blob:https://watch.sling.com/2638aa17-62cd-497c-a988-d44e7a824db8
[2025/12/27 01:20:31.422] Browser page closed: blob:https://watch.sling.com/2638aa17-62cd-497c-a988-d44e7a824db8
[2025/12/27 01:20:32.149] Target page changed: https://watch.sling.com/1/asset/69ef64da41ded9d9a9622bb1fcdebb57/watch
[2025/12/27 01:20:32.811] Browser page closed: chrome-extension://jjndjgheafjngoipoacpjgeicjeomjli/background.js
[2025/12/27 01:20:34.309] Set Sling to Full Screen and Volume to max

All is working as expected.

What is the difference between the 'chrome://' and 'http://' schemes? I have always used 'http://' with @tmm1's Docker image without issue, including the latest image for the last several weeks in my production use.

I do this because I normally use other software besides Channels DVR that doesn't understand the 'chrome://' scheme. I only use (and pay for) Channels DVR for evaluation and comparison purposes and to recommend to less tech savvy friends and acquaintances who want something similar to what I run.

I use mpv and sometimes vlc for testing and MythTV for production and development recording. I do that because I have been a MythTV dev for 22+ years and it has features I rely on that Channels DVR doesn't have. If and when MythTV becomes untenable and Channels is still around, I would probably switch to it but that time isn't now.

No, I saw it but chose to go in a different direction. My intent is to start CC4C from the command line for testing. After it is working that way, I will write a proper systemd unit file for it and contribute it back here. That will allow CC4C to be started and stopped like a normal service and not co-opt the SSH login.
[/quote]

I added some extra debugging, which was interesting but ultimately not helpful. I then decided to reboot the LXC, not just log out and back in, before proceeding.

Lo and behold, it all started working! Well, except for the video not going full screen issue. I will try to work on that soonish.

In addition, 1080 video was smooth in limited testing so far. I always had to drop down to 720 using the Docker images to get smooth video.

I will also try to work on the systemd unit file soonish and post it back here when ready.

Here is my initial cut. First, create the following files.

/etc/systemd/system/xvfb.service.

[Unit]
Description=X Virtual Frame Buffer Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/Xvfb :99 -screen 0 1920x1080x16
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

/etc/systemd/system/x11vnc.service.

[Unit]
Description=x11vnc VNC Server for X11
Requires=xvfb.service

[Service]
ExecStart=/usr/bin/x11vnc -quiet -nopw -display :99 -forever
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

/etc/systemd/system/cc4c.service. Edit the ExecStart line as needed for your situation.

[Unit]
Description=Chrome Capture for Channels
Requires=xvfb.service

[Service]
Type=Simple
WorkingDirectory=/opt/chrome-capture-for-channels/EnvironmentFile=/etc/environment
# Options:
#   -v, --videoBitrate  Video bitrate in bits per second  [number] [default: 6000000]
#   -a, --audioBitrate  Audio bitrate in bits per second  [number] [default: 256000]
#   -f, --frameRate     Minimum frame rate  [number] [default: 30]
#   -p, --port          Port number for the server  [number] [default: 5589]
#   -w, --width         Video width in pixels (e.g., 1920 for 1080p)  [number] [default: 1920]
#   -h, --height        Video height in pixels (e.g., 1080 for 1080p)  [number] [default: 1080]
#   -i, --videoCodec    Video codec (e.g., h264_nvenc, h264_qsv, h264_amf, h264_vaapi)  [string] [default: "h264_nvenc"]
#   -u, --audioCodec    Audio codec (e.g., aac, opus)  [string] [default: "aac"]
#   -?, --help          Show help  [boolean]
ExecStart=/usr/bin/node main.js -v 9500000 -f 60
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

Next, run the following commands.

systemctl enable xvfb.service
systemctl enable x11vnc.service
systemctl enable cc4c.service
systemctl start x11vnc.service
systemctl start cc4c.service
1 Like