17 Commits

13 changed files with 234 additions and 70 deletions

View File

@ -90,10 +90,6 @@ RUN set -eux; \
adduser $USERNAME video; \ adduser $USERNAME video; \
adduser $USERNAME pulse; \ adduser $USERNAME pulse; \
# #
# setup pulseaudio
mkdir -p /home/$USERNAME/.config/pulse/; \
echo "default-server=unix:/tmp/pulseaudio.socket" > /home/$USERNAME/.config/pulse/client.conf; \
#
# workaround for an X11 problem: http://blog.tigerteufel.de/?p=476 # workaround for an X11 problem: http://blog.tigerteufel.de/?p=476
mkdir /tmp/.X11-unix; \ mkdir /tmp/.X11-unix; \
chmod 1777 /tmp/.X11-unix; \ chmod 1777 /tmp/.X11-unix; \
@ -120,6 +116,7 @@ COPY .docker/base/xorg.conf /etc/neko/xorg.conf
# set default envs # set default envs
ENV USER=$USERNAME ENV USER=$USERNAME
ENV DISPLAY=:99.0 ENV DISPLAY=:99.0
ENV PULSE_SERVER=unix:/tmp/pulseaudio.socket
ENV NEKO_PASSWORD=neko ENV NEKO_PASSWORD=neko
ENV NEKO_PASSWORD_ADMIN=admin ENV NEKO_PASSWORD_ADMIN=admin
ENV NEKO_BIND=:8080 ENV NEKO_BIND=:8080

View File

@ -96,10 +96,6 @@ RUN set -eux; \
adduser $USERNAME video; \ adduser $USERNAME video; \
adduser $USERNAME pulse; \ adduser $USERNAME pulse; \
# #
# setup pulseaudio
mkdir -p /home/$USERNAME/.config/pulse/; \
echo "default-server=unix:/tmp/pulseaudio.socket" > /home/$USERNAME/.config/pulse/client.conf; \
#
# workaround for an X11 problem: http://blog.tigerteufel.de/?p=476 # workaround for an X11 problem: http://blog.tigerteufel.de/?p=476
mkdir /tmp/.X11-unix; \ mkdir /tmp/.X11-unix; \
chmod 1777 /tmp/.X11-unix; \ chmod 1777 /tmp/.X11-unix; \
@ -126,6 +122,7 @@ COPY .docker/base/xorg.conf /etc/neko/xorg.conf
# set default envs # set default envs
ENV USER=$USERNAME ENV USER=$USERNAME
ENV DISPLAY=:99.0 ENV DISPLAY=:99.0
ENV PULSE_SERVER=unix:/tmp/pulseaudio.socket
ENV NEKO_PASSWORD=neko ENV NEKO_PASSWORD=neko
ENV NEKO_PASSWORD_ADMIN=admin ENV NEKO_PASSWORD_ADMIN=admin
ENV NEKO_BIND=:8080 ENV NEKO_BIND=:8080

View File

@ -99,10 +99,6 @@ RUN set -eux; \
adduser $USERNAME video; \ adduser $USERNAME video; \
adduser $USERNAME pulse; \ adduser $USERNAME pulse; \
# #
# setup pulseaudio
mkdir -p /home/$USERNAME/.config/pulse/; \
echo "default-server=unix:/tmp/pulseaudio.socket" > /home/$USERNAME/.config/pulse/client.conf; \
#
# workaround for an X11 problem: http://blog.tigerteufel.de/?p=476 # workaround for an X11 problem: http://blog.tigerteufel.de/?p=476
mkdir /tmp/.X11-unix; \ mkdir /tmp/.X11-unix; \
chmod 1777 /tmp/.X11-unix; \ chmod 1777 /tmp/.X11-unix; \
@ -130,6 +126,7 @@ COPY .docker/base/intel/add-render-group.sh /usr/bin/add-render-group.sh
# set default envs # set default envs
ENV USER=$USERNAME ENV USER=$USERNAME
ENV DISPLAY=:99.0 ENV DISPLAY=:99.0
ENV PULSE_SERVER=unix:/tmp/pulseaudio.socket
ENV NEKO_PASSWORD=neko ENV NEKO_PASSWORD=neko
ENV NEKO_PASSWORD_ADMIN=admin ENV NEKO_PASSWORD_ADMIN=admin
ENV NEKO_BIND=:8080 ENV NEKO_BIND=:8080

View File

@ -1,10 +1,62 @@
ARG UBUNTU_RELEASE=20.04 ARG UBUNTU_RELEASE=20.04
ARG CUDA_VERSION=11.2.2 ARG CUDA_VERSION=11.4.3
ARG VIRTUALGL_VERSION=3.1
ARG GSTREAMER_VERSION=1.20
#
# STAGE 0: Build gstreamer with nvidia plugins.
#
FROM ubuntu:${UBUNTU_RELEASE} AS gstreamer
ARG GSTREAMER_VERSION
#
# install dependencies
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
# Install essentials
curl build-essential ca-certificates git \
# Install pip and ninja
python3-pip python-gi-dev ninja-build \
# Install build deps
autopoint autoconf automake autotools-dev libtool gettext bison flex gtk-doc-tools \
# Install libraries
librtmp-dev \
libvo-aacenc-dev \
libtool-bin \
libgtk2.0-dev \
libgl1-mesa-dev \
libopus-dev \
libpulse-dev \
libssl-dev \
libx264-dev \
libvpx-dev; \
# Install meson
pip3 install meson; \
#
# clean up
apt-get clean -y; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
#
# build gstreamer
RUN set -eux; \
git clone --depth 1 --branch $GSTREAMER_VERSION https://gitlab.freedesktop.org/gstreamer/gstreamer.git /gstreamer/src; \
cd /gstreamer/src; \
mkdir -p /opt/gstreamer; \
meson --prefix /opt/gstreamer \
-Dgpl=enabled \
-Dugly=enabled \
-Dgst-plugins-ugly:x264=enabled \
build; \
ninja -C build; \
meson install -C build;
# #
# STAGE 1: SERVER # STAGE 1: SERVER
# #
FROM golang:1.18-bullseye as server FROM golang:1.20-bullseye as server
WORKDIR /src WORKDIR /src
# #
@ -35,7 +87,7 @@ RUN go get -v -t -d . && go build -o bin/neko cmd/neko/main.go
# #
# STAGE 2: CLIENT # STAGE 2: CLIENT
# #
FROM node:14-bullseye-slim as client FROM node:18-bullseye-slim as client
WORKDIR /src WORKDIR /src
# #
@ -51,13 +103,13 @@ RUN npm run build
# #
# STAGE 3: RUNTIME # STAGE 3: RUNTIME
# #
FROM nvcr.io/nvidia/cudagl:${CUDA_VERSION}-runtime-ubuntu${UBUNTU_RELEASE} as runtime FROM nvidia/cuda:${CUDA_VERSION}-runtime-ubuntu${UBUNTU_RELEASE} as runtime
ARG UBUNTU_RELEASE ARG UBUNTU_RELEASE
ARG CUDA_VERSION ARG VIRTUALGL_VERSION
# Make all NVIDIA GPUs visible by default # Make all NVIDIA GPUs visible by default
ENV NVIDIA_VISIBLE_DEVICES=all ENV NVIDIA_VISIBLE_DEVICES all
# All NVIDIA driver capabilities should preferably be used, check `NVIDIA_DRIVER_CAPABILITIES` inside the container if things do not work # All NVIDIA driver capabilities should preferably be used, check `NVIDIA_DRIVER_CAPABILITIES` inside the container if things do not work
ENV NVIDIA_DRIVER_CAPABILITIES all ENV NVIDIA_DRIVER_CAPABILITIES all
@ -75,20 +127,55 @@ ARG USERNAME=neko
ARG USER_UID=1000 ARG USER_UID=1000
ARG USER_GID=$USER_UID ARG USER_GID=$USER_UID
RUN set -eux; \
dpkg --add-architecture i386; \
apt-get update; \
apt-get install -y --no-install-recommends \
# opengl base: https://gitlab.com/nvidia/container-images/opengl/-/blob/ubuntu20.04/base/Dockerfile
libxau6 libxau6:i386 \
libxdmcp6 libxdmcp6:i386 \
libxcb1 libxcb1:i386 \
libxext6 libxext6:i386 \
libx11-6 libx11-6:i386 \
# opengl runtime: https://gitlab.com/nvidia/container-images/opengl/-/blob/ubuntu20.04/glvnd/runtime/Dockerfile
libglvnd0 libglvnd0:i386 \
libgl1 libgl1:i386 \
libglx0 libglx0:i386 \
libegl1 libegl1:i386 \
libgles2 libgles2:i386 \
# hardware accleration utilities
libglu1 libglu1:i386 \
libvulkan-dev libvulkan-dev:i386 \
mesa-utils mesa-utils-extra \
mesa-va-drivers mesa-vulkan-drivers \
vainfo vdpauinfo; \
#
# install vulkan-utils or vulkan-tools depending on ubuntu release
if [ "${UBUNTU_RELEASE}" = "18.04" ]; then \
apt-get install -y --no-install-recommends vulkan-utils; \
else \
apt-get install -y --no-install-recommends vulkan-tools; \
fi; \
#
# create symlink for libnvrtc.so (needed for cudaconvert)
find /usr/local/cuda/lib64/ -maxdepth 1 -type l -name "*libnvrtc.so.*" -exec sh -c 'ln -sf {} /usr/local/cuda/lib64/libnvrtc.so' \;; \
#
# clean up
apt-get clean -y; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
#
# add cuda to ld path, for gstreamer cuda plugins
ENV LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/usr/lib/i386-linux-gnu${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}:/usr/local/cuda/lib:/usr/local/cuda/lib64"
RUN set -eux; \ RUN set -eux; \
apt-get update; \ apt-get update; \
# #
# install dependencies # install dependencies
apt-get install -y --no-install-recommends wget ca-certificates supervisor; \ apt-get install -y --no-install-recommends wget ca-certificates supervisor; \
apt-get install -y --no-install-recommends pulseaudio dbus-x11 xserver-xorg-video-dummy; \ apt-get install -y --no-install-recommends pulseaudio dbus-x11 xserver-xorg-video-dummy; \
apt-get install -y --no-install-recommends libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx6; \ apt-get install -y --no-install-recommends libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx6 libx264-155 libvo-aacenc0 librtmp1; \
# apt-get install -y --no-install-recommends libgtk-3-bin software-properties-common cabextract aptitude vim curl; \
# hardware acclerations utilities
apt-get install -y --no-install-recommends libgtk-3-bin mesa-utils mesa-utils-extra mesa-va-drivers mesa-vulkan-drivers libvulkan-dev libvulkan-dev:i386 vdpauinfo; \
#
# gst
apt-get install -y --no-install-recommends libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-pulseaudio; \
# #
# install fonts # install fonts
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
@ -108,10 +195,6 @@ RUN set -eux; \
adduser $USERNAME video; \ adduser $USERNAME video; \
adduser $USERNAME pulse; \ adduser $USERNAME pulse; \
# #
# setup pulseaudio
mkdir -p /home/$USERNAME/.config/pulse/; \
echo "default-server=unix:/tmp/pulseaudio.socket" > /home/$USERNAME/.config/pulse/client.conf; \
#
# workaround for an X11 problem: http://blog.tigerteufel.de/?p=476 # workaround for an X11 problem: http://blog.tigerteufel.de/?p=476
mkdir /tmp/.X11-unix; \ mkdir /tmp/.X11-unix; \
chmod 1777 /tmp/.X11-unix; \ chmod 1777 /tmp/.X11-unix; \
@ -128,18 +211,18 @@ RUN set -eux; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/* rm -rf /var/lib/apt/lists/* /var/cache/apt/*
# #
# install and configure Vulkan manually # configure EGL and Vulkan manually
RUN set -eux; \ RUN VULKAN_API_VERSION=$(dpkg -s libvulkan1 | grep -oP 'Version: [0-9|\.]+' | grep -oP '[0-9]+(\.[0-9]+)(\.[0-9]+)') && \
apt-get update; \ # Configure EGL manually
if [ "${UBUNTU_RELEASE}" = "18.04" ]; then apt-get install -y --no-install-recommends vulkan-utils; else apt-get install -y --no-install-recommends vulkan-tools; fi; \ mkdir -p /usr/share/glvnd/egl_vendor.d/ && \
# echo "{\n\
# clean up \"file_format_version\" : \"1.0.0\",\n\
apt-get clean -y; \ \"ICD\": {\n\
rm -rf /var/lib/apt/lists/* /var/cache/apt/*; \ \"library_path\": \"libEGL_nvidia.so.0\"\n\
# }\n\
# configure vulkan }" > /usr/share/glvnd/egl_vendor.d/10_nvidia.json && \
VULKAN_API_VERSION=$(dpkg -s libvulkan1 | grep -oP 'Version: [0-9|\.]+' | grep -oP '[0-9]+(\.[0-9]+)(\.[0-9]+)'); \ # Configure Vulkan manually
mkdir -p /etc/vulkan/icd.d/; \ mkdir -p /etc/vulkan/icd.d/ && \
echo "{\n\ echo "{\n\
\"file_format_version\" : \"1.0.0\",\n\ \"file_format_version\" : \"1.0.0\",\n\
\"ICD\": {\n\ \"ICD\": {\n\
@ -150,7 +233,6 @@ RUN set -eux; \
# #
# install VirtualGL and make libraries available for preload # install VirtualGL and make libraries available for preload
ARG VIRTUALGL_VERSION=3.1
RUN set -eux; \ RUN set -eux; \
apt-get update; \ apt-get update; \
wget "https://sourceforge.net/projects/virtualgl/files/virtualgl_${VIRTUALGL_VERSION}_amd64.deb"; \ wget "https://sourceforge.net/projects/virtualgl/files/virtualgl_${VIRTUALGL_VERSION}_amd64.deb"; \
@ -180,10 +262,21 @@ COPY .docker/base/nvidia/entrypoint.sh /bin/entrypoint.sh
# set default envs # set default envs
ENV USER=$USERNAME ENV USER=$USERNAME
ENV DISPLAY=:99.0 ENV DISPLAY=:99.0
ENV PULSE_SERVER=unix:/tmp/pulseaudio.socket
ENV NEKO_PASSWORD=neko ENV NEKO_PASSWORD=neko
ENV NEKO_PASSWORD_ADMIN=admin ENV NEKO_PASSWORD_ADMIN=admin
ENV NEKO_BIND=:8080 ENV NEKO_BIND=:8080
#
# set gstreamer envs
ENV PATH="/opt/gstreamer/bin:${PATH}"
ENV LD_LIBRARY_PATH="/opt/gstreamer/lib/x86_64-linux-gnu${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
ENV PKG_CONFIG_PATH="/opt/gstreamer/lib/x86_64-linux-gnu/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}"
#
# copy gstreamer from previous stage
COPY --from=gstreamer /opt/gstreamer /opt/gstreamer
# #
# copy static files from previous stages # copy static files from previous stages
COPY --from=server /src/bin/neko /usr/bin/neko COPY --from=server /src/bin/neko /usr/bin/neko

View File

@ -181,6 +181,11 @@
shakeKbd = false shakeKbd = false
get volume() {
const numberParam = parseFloat(new URL(location.href).searchParams.get('volume') || '1.0')
return Math.max(0.0, Math.min(!isNaN(numberParam) ? numberParam * 100 : 100, 100))
}
get isCastMode() { get isCastMode() {
return !!new URL(location.href).searchParams.get('cast') return !!new URL(location.href).searchParams.get('cast')
} }
@ -197,6 +202,11 @@
return this.isCastMode || this.isEmbedMode return this.isCastMode || this.isEmbedMode
} }
@Watch('volume', { immediate: true })
onVolume(volume: number) {
this.$accessor.video.setVolume(volume)
}
@Watch('hideControls', { immediate: true }) @Watch('hideControls', { immediate: true })
onHideControls(enabled: boolean) { onHideControls(enabled: boolean) {
if (enabled) { if (enabled) {

View File

@ -21,6 +21,9 @@
@mouseup.stop.prevent="onMouseUp" @mouseup.stop.prevent="onMouseUp"
@mouseenter.stop.prevent="onMouseEnter" @mouseenter.stop.prevent="onMouseEnter"
@mouseleave.stop.prevent="onMouseLeave" @mouseleave.stop.prevent="onMouseLeave"
@touchmove.stop.prevent="onTouchHandler"
@touchstart.stop.prevent="onTouchHandler"
@touchend.stop.prevent="onTouchHandler"
/> />
<div v-if="!playing && playable" class="player-overlay" @click.stop.prevent="playAndUnmute"> <div v-if="!playing && playable" class="player-overlay" @click.stop.prevent="playAndUnmute">
<i class="fas fa-play-circle" /> <i class="fas fa-play-circle" />
@ -688,6 +691,35 @@
} }
} }
onTouchHandler(e: TouchEvent) {
let first = e.changedTouches[0]
let type = ''
switch (e.type) {
case 'touchstart':
type = 'mousedown'
break
case 'touchmove':
type = 'mousemove'
break
case 'touchend':
type = 'mouseup'
break
default:
return
}
const simulatedEvent = new MouseEvent(type, {
bubbles: true,
cancelable: true,
view: window,
screenX: first.screenX,
screenY: first.screenY,
clientX: first.clientX,
clientY: first.clientY,
})
first.target.dispatchEvent(simulatedEvent)
}
onMouseDown(e: MouseEvent) { onMouseDown(e: MouseEvent) {
if (!this.hosting) { if (!this.hosting) {
this.$emit('control-attempt', e) this.$emit('control-attempt', e)

View File

@ -2,11 +2,17 @@
## master branch ## master branch
## [n.eko v2.8.0](https://github.com/m1k1o/neko/releases/tag/v2.8.0)
### New Features ### New Features
- Added AV1 tag, metadata and pipeline. Unfortunately does not work yet, since the encoding is way too slow (by @mbattista). - Added AV1 tag, metadata and pipeline. Unfortunately does not work yet, since the encoding is way too slow (by @mbattista).
- Added `m1k1o/neko:kde` tag as an alternative to `m1k1o/neko:xfce`. - Added `m1k1o/neko:kde` tag as an alternative to `m1k1o/neko:xfce`.
- New VirtualGL version 3.1 was released, adding support for Chromium browsers to use Nvidia GPU acceleration! - New VirtualGL version 3.1 was released, adding support for Chromium browsers to use Nvidia GPU acceleration!
- Added `?embed=1` parameter to the URL, which will hide the sidebar and the top bar, so that it can be embedded in other websites. - Added `?embed=1` parameter to the URL, which will hide the sidebar and the top bar, so that it can be embedded in other websites.
- Added `?volume=<0-1>` parameter to the URL, which will set the inital volume of the player (by @urbanekpj).
- Touch events are now supported on mobile devices (by @urbanekpj).
- Added NVENC support, hardware h264 encoding for Nvidia GPUs!
- Fixed an issue where `nvh264enc` did not send SPS and PPS NAL units (by @mbattista).
### Bugs ### Bugs
- Fixed TCP mux occasional freeze by adding write buffer to it. - Fixed TCP mux occasional freeze by adding write buffer to it.
@ -23,6 +29,7 @@
- Disabled autolock for kde, so that it does not lock the screen when you are not using it. - Disabled autolock for kde, so that it does not lock the screen when you are not using it.
- Refactored autoplay, so that it will start playing audio, if it's allowed by the browser (by @urbanekpj). - Refactored autoplay, so that it will start playing audio, if it's allowed by the browser (by @urbanekpj).
- Renamed pulseaudio sink from `auto_null` to `audio_output`, because it was ignored by KDE. - Renamed pulseaudio sink from `auto_null` to `audio_output`, because it was ignored by KDE.
- Pulseaudio is now configured using environment variables, so that users can mount `/home/neko` without losing audio configuration.
## [n.eko v2.7](https://github.com/m1k1o/neko/releases/tag/v2.7) ## [n.eko v2.7](https://github.com/m1k1o/neko/releases/tag/v2.7)

View File

@ -164,7 +164,7 @@ services:
### Nvidia GPU acceleration ### Nvidia GPU acceleration
You need to have nvidia-docker installed, start the container with `--gpus all` flag and use images built for nvidia (see above). You need to have [nvidia-docker](https://github.com/NVIDIA/nvidia-docker) installed, start the container with `--gpus all` flag and use images built for nvidia (see above).
```bash ```bash
docker run -d --gpus all \ docker run -d --gpus all \
@ -176,6 +176,8 @@ docker run -d --gpus all \
-e NEKO_EPR=56000-56100 \ -e NEKO_EPR=56000-56100 \
-e NEKO_NAT1TO1=192.168.1.10 \ -e NEKO_NAT1TO1=192.168.1.10 \
-e NEKO_ICELITE=1 \ -e NEKO_ICELITE=1 \
-e NEKO_VIDEO_CODEC=h264 \
-e NEKO_HWENC=nvenc \
--shm-size=2gb \ --shm-size=2gb \
--cap-add=SYS_ADMIN \ --cap-add=SYS_ADMIN \
--name neko \ --name neko \
@ -202,6 +204,8 @@ services:
NEKO_PASSWORD_ADMIN: admin NEKO_PASSWORD_ADMIN: admin
NEKO_EPR: 56000-56100 NEKO_EPR: 56000-56100
NEKO_NAT1TO1: 192.168.1.10 NEKO_NAT1TO1: 192.168.1.10
NEKO_VIDEO_CODEC: h264
NEKO_HWENC: nvenc
deploy: deploy:
resources: resources:
reservations: reservations:
@ -211,7 +215,9 @@ services:
capabilities: [gpu] capabilities: [gpu]
``` ```
Note, currently only browser GPU acceleration is supported, not encoding. - You can verify that GPU is available inside the container by running `docker exec -it neko nvidia-smi` command.
- You can verify that GPU is used for encoding by searching for `nvh264enc` in `docker logs neko` output.
- If you don'ŧ specify `NEKO_HWENC: nvenc` environment variable, CPU encoding will be used but GPU will still be available for browser rendering.
### Want to use VPN for your n.eko browsing? ### Want to use VPN for your n.eko browsing?
- Check this out: https://github.com/m1k1o/neko-vpn - Check this out: https://github.com/m1k1o/neko-vpn
@ -231,6 +237,7 @@ Note, currently only browser GPU acceleration is supported, not encoding.
- Adding `?usr=<display-name>` will prefill username. - Adding `?usr=<display-name>` will prefill username.
- Adding `?cast=1` will hide all control and show only video. - Adding `?cast=1` will hide all control and show only video.
- Adding `?embed=1` will hide most additional components and show only video. - Adding `?embed=1` will hide most additional components and show only video.
- Adding `?volume=<0-1>` will set volume to given value.
- e.g. `http(s)://<URL:Port>/?pwd=neko&usr=guest&cast=1` - e.g. `http(s)://<URL:Port>/?pwd=neko&usr=guest&cast=1`
### Screen size ### Screen size

View File

@ -79,13 +79,14 @@ nat1to1: <ip>
- `gstreamer1.0-plugins-good` - `gstreamer1.0-plugins-good`
- `gstreamer1.0-plugins-bad` - `gstreamer1.0-plugins-bad`
- `gstreamer1.0-plugins-ugly` - `gstreamer1.0-plugins-ugly`
- e.g. `ximagesrc display-name=%s show-pointer=true use-damage=false ! video/x-raw,framerate=30/1 ! videoconvert ! queue ! video/x-raw,format=NV12 ! x264enc threads=4 bitrate=3500 key-int-max=60 vbv-buf-capacity=4000 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream` - e.g. `ximagesrc display-name=%s show-pointer=true use-damage=false ! video/x-raw,framerate=30/1 ! videoconvert ! queue ! video/x-raw,format=NV12 ! x264enc threads=4 bitrate=3500 key-int-max=60 vbv-buf-capacity=4000 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline`
#### `NEKO_MAX_FPS`: #### `NEKO_MAX_FPS`:
- The resulting stream frames per seconds should be capped *(0 for uncapped)*. - The resulting stream frames per seconds should be capped *(0 for uncapped)*.
- e.g. `0` - e.g. `0`
#### `NEKO_HWENC`: #### `NEKO_HWENC`:
- Use hardware accelerated encoding, for now supported only `VAAPI`. - none *(default CPU encoding)*
- e.g. `VAAPI` - vaapi
- nvenc
### Audio ### Audio

View File

@ -95,9 +95,9 @@ services:
! videoconvert ! videoconvert
! queue ! queue
! video/x-raw,framerate=30/1,format=NV12 ! video/x-raw,framerate=30/1,format=NV12
! v4l2h264enc extra-controls="controls,h264_profile=0,video_bitrate=1250000;" ! v4l2h264enc extra-controls="controls,h264_profile=1,video_bitrate=1250000;"
! h264parse config-interval=3 ! h264parse config-interval=3
! video/x-h264,profile=baseline,stream-format=byte-stream ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline
NEKO_VIDEO_CODEC: h264 NEKO_VIDEO_CODEC: h264
``` ```

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"m1k1o/neko/internal/capture/gst" "m1k1o/neko/internal/capture/gst"
"m1k1o/neko/internal/config"
"m1k1o/neko/internal/types/codec" "m1k1o/neko/internal/types/codec"
) )
@ -52,7 +53,7 @@ func NewBroadcastPipeline(device string, display string, pipelineSrc string, url
return pipelineStr, nil return pipelineStr, nil
} }
func NewVideoPipeline(rtpCodec codec.RTPCodec, display string, pipelineSrc string, fps int16, bitrate uint, hwenc string) (string, error) { func NewVideoPipeline(rtpCodec codec.RTPCodec, display string, pipelineSrc string, fps int16, bitrate uint, hwenc config.HwEnc) (string, error) {
pipelineStr := " ! appsink name=appsinkvideo" pipelineStr := " ! appsink name=appsinkvideo"
// if using custom pipeline // if using custom pipeline
@ -68,7 +69,7 @@ func NewVideoPipeline(rtpCodec codec.RTPCodec, display string, pipelineSrc strin
switch rtpCodec.Name { switch rtpCodec.Name {
case codec.VP8().Name: case codec.VP8().Name:
if hwenc == "VAAPI" { if hwenc == config.HwEncVAAPI {
if err := gst.CheckPlugins([]string{"ximagesrc", "vaapi"}); err != nil { if err := gst.CheckPlugins([]string{"ximagesrc", "vaapi"}); err != nil {
return "", err return "", err
} }
@ -138,35 +139,40 @@ func NewVideoPipeline(rtpCodec codec.RTPCodec, display string, pipelineSrc strin
return "", err return "", err
} }
if hwenc == "VAAPI" { vbvbuf := uint(1000)
if bitrate > 1000 {
vbvbuf = bitrate
}
if hwenc == config.HwEncVAAPI {
if err := gst.CheckPlugins([]string{"vaapi"}); err != nil { if err := gst.CheckPlugins([]string{"vaapi"}); err != nil {
return "", err return "", err
} }
pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=NV12 ! vaapih264enc rate-control=vbr bitrate=%d keyframe-period=180 quality-level=7 ! video/x-h264,stream-format=byte-stream"+pipelineStr, display, fps, bitrate) pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=NV12 ! vaapih264enc rate-control=vbr bitrate=%d keyframe-period=180 quality-level=7 ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline"+pipelineStr, display, fps, bitrate)
} else if hwenc == config.HwEncNVENC {
if err := gst.CheckPlugins([]string{"nvcodec"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=NV12 ! nvh264enc name=encoder preset=2 gop-size=25 spatial-aq=true temporal-aq=true bitrate=%d vbv-buffer-size=%d rc-mode=6 ! h264parse config-interval=-1 ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline"+pipelineStr, display, fps, bitrate, vbvbuf)
} else { } else {
// https://gstreamer.freedesktop.org/documentation/openh264/openh264enc.html?gi-language=c#openh264enc // https://gstreamer.freedesktop.org/documentation/openh264/openh264enc.html?gi-language=c#openh264enc
// gstreamer1.0-plugins-bad // gstreamer1.0-plugins-bad
// openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000 // openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000
if err := gst.CheckPlugins([]string{"openh264"}); err == nil { if err := gst.CheckPlugins([]string{"openh264"}); err == nil {
pipelineStr = fmt.Sprintf(videoSrc+"openh264enc multi-thread=4 complexity=high bitrate=%d max-bitrate=%d ! video/x-h264,stream-format=byte-stream"+pipelineStr, display, fps, bitrate*1000, (bitrate+1024)*1000) pipelineStr = fmt.Sprintf(videoSrc+"openh264enc multi-thread=4 complexity=high bitrate=%d max-bitrate=%d ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline"+pipelineStr, display, fps, bitrate*1000, (bitrate+1024)*1000)
break break
} }
// https://gstreamer.freedesktop.org/documentation/x264/index.html?gi-language=c // https://gstreamer.freedesktop.org/documentation/x264/index.html?gi-language=c
// gstreamer1.0-plugins-ugly // gstreamer1.0-plugins-ugly
// video/x-raw,format=I420 ! x264enc bframes=0 key-int-max=60 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream // video/x-raw,format=I420 ! x264enc bframes=0 key-int-max=60 byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline
if err := gst.CheckPlugins([]string{"x264"}); err != nil { if err := gst.CheckPlugins([]string{"x264"}); err != nil {
return "", err return "", err
} }
vbvbuf := uint(1000) pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=NV12 ! x264enc threads=4 bitrate=%d key-int-max=60 vbv-buf-capacity=%d byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline"+pipelineStr, display, fps, bitrate, vbvbuf)
if bitrate > 1000 {
vbvbuf = bitrate
}
pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=NV12 ! x264enc threads=4 bitrate=%d key-int-max=60 vbv-buf-capacity=%d byte-stream=true tune=zerolatency speed-preset=veryfast ! video/x-h264,stream-format=byte-stream"+pipelineStr, display, fps, bitrate, vbvbuf)
} }
default: default:
return "", fmt.Errorf("unknown codec %s", rtpCodec.Name) return "", fmt.Errorf("unknown codec %s", rtpCodec.Name)

View File

@ -2,6 +2,7 @@ package config
import ( import (
"m1k1o/neko/internal/types/codec" "m1k1o/neko/internal/types/codec"
"strings"
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -9,13 +10,21 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
type HwEnc int
const (
HwEncNone HwEnc = iota
HwEncVAAPI
HwEncNVENC
)
type Capture struct { type Capture struct {
// video // video
Display string Display string
VideoCodec codec.RTPCodec VideoCodec codec.RTPCodec
VideoHWEnc string // TODO: Pipeline builder. VideoHWEnc HwEnc // TODO: Pipeline builder.
VideoBitrate uint // TODO: Pipeline builder. VideoBitrate uint // TODO: Pipeline builder.
VideoMaxFPS int16 // TODO: Pipeline builder. VideoMaxFPS int16 // TODO: Pipeline builder.
VideoPipeline string VideoPipeline string
// audio // audio
@ -184,11 +193,19 @@ func (s *Capture) Set() {
log.Warn().Msg("you are using deprecated config setting 'NEKO_AV1=true', use 'NEKO_VIDEO_CODEC=av1' instead") log.Warn().Msg("you are using deprecated config setting 'NEKO_AV1=true', use 'NEKO_VIDEO_CODEC=av1' instead")
} }
videoHWEnc := "" videoHWEnc := strings.ToLower(viper.GetString("hwenc"))
if viper.GetString("hwenc") == "VAAPI" { switch videoHWEnc {
videoHWEnc = "VAAPI" case "":
fallthrough
case "none":
s.VideoHWEnc = HwEncNone
case "vaapi":
s.VideoHWEnc = HwEncVAAPI
case "nvenc":
s.VideoHWEnc = HwEncNVENC
default:
log.Warn().Str("hwenc", videoHWEnc).Msgf("unknown video hw encoder, using CPU")
} }
s.VideoHWEnc = videoHWEnc
s.VideoBitrate = viper.GetUint("video_bitrate") s.VideoBitrate = viper.GetUint("video_bitrate")
s.VideoMaxFPS = int16(viper.GetInt("max_fps")) s.VideoMaxFPS = int16(viper.GetInt("max_fps"))

View File

@ -39,7 +39,7 @@ var (
// Major version when you make incompatible API changes, // Major version when you make incompatible API changes,
major = "2" major = "2"
// Minor version when you add functionality in a backwards-compatible manner, and // Minor version when you add functionality in a backwards-compatible manner, and
minor = "7" minor = "8"
// Patch version when you make backwards-compatible bug fixes. // Patch version when you make backwards-compatible bug fixes.
patch = "0" patch = "0"
) )