Skip to content

devgsantos/kbore

Repository files navigation

What is Kboré?

A powerfull UX first IPTV player app for Nintendo Switch. Add M3U URL or XTream playlists (M3U files support coming soon) and watch content anywhere. Just download the lates release and enjoy!

Kboré - Homebrew Development and Player Build

This repository contains the native Kboré homebrew app, including the app UI, playlist/cache handling, and the native media player pipeline backed by a custom FFmpeg build with nvtegra patches.

Current player state:

  • Native UI, controller input, stream cache, and stream listing are working.
  • Custom FFmpeg builds locally and exports the SDK into external/player-sdk.
  • The nvtegra FFmpeg backend is present and detected by the native probe.
  • AVHWDeviceContext creation succeeds.
  • Video opens through NVTEGRA hardware decode.
  • Audio runs through SDL Audio.
  • SD and HD playback are usable with the current SDL texture path.
  • FHD playback still needs the future direct native renderer path to avoid CPU frame transfers.

Some low-level toolchain package names, filesystem mount points, build defines, and source files still contain target-specific or legacy identifiers because the homebrew SDK and existing code use those exact names.


Repository Layout

Expected project root:

~/kbore

Relevant paths:

kbore/
├── Makefile
├── Makefile.switch
├── include/
├── source/
├── scripts/
│   ├── build-ffmpeg-nx.sh
│   ├── check-ffmpeg-nx.sh
│   ├── patch-ffmpeg-nx-switch.sh
│   ├── apply-ffmpeg-nvtegra-0001-manual.sh
│   ├── apply-ffmpeg-nvtegra-0002-manual.sh
│   └── apply-ffmpeg-nvtegra-from-0003-safe.sh
├── patches/
│   └── ffmpeg-nvtegra/
├── external/
│   ├── ffmpeg-nx/
│   ├── mpv-nx/
│   └── player-sdk/
├── romfs/
└── data/

external/ffmpeg-nx and external/mpv-nx are Git submodules that point to the Kboré forks:

https://github.com/devgsantos/ffmpeg-nx.git
https://github.com/devgsantos/mpv-nx.git

external/player-sdk is generated by the FFmpeg build script.


Clone and Submodules

For a fresh checkout:

git clone --recurse-submodules https://github.com/devgsantos/kbore.git
cd kbore

If the repository was cloned without submodules:

git submodule update --init --recursive

To refresh the external forks to the commits recorded by this repository:

git submodule sync --recursive
git submodule update --init --recursive

To intentionally update a submodule to a newer fork commit:

cd external/ffmpeg-nx
git fetch origin
git checkout feat/updated-readme
git pull --ff-only
cd ../..
git add external/ffmpeg-nx

Repeat the same flow for external/mpv-nx when needed.


Development Environment

The homebrew build uses devkitPro, devkitA64, libnx, SDL2, curl, mbedTLS, zlib, bzip2, xz/lzma, deko3d, and the static FFmpeg SDK generated from the fork.

Expected toolchain paths:

/opt/devkitpro
/opt/devkitpro/devkitA64
/opt/devkitpro/libnx
/opt/devkitpro/portlibs/switch

Set the environment:

export DEVKITPRO=/opt/devkitpro
export DEVKITA64=$DEVKITPRO/devkitA64
export PATH=$DEVKITA64/bin:$PATH

Add the same lines to ~/.bashrc or ~/.zshrc if you want them loaded for each terminal session.

Verify the compiler:

echo $DEVKITPRO
echo $DEVKITA64
which aarch64-none-elf-gcc
aarch64-none-elf-gcc --version

Install the base packages:

sudo dkp-pacman -Syu
sudo dkp-pacman -S switch-dev switch-sdl2 switch-sdl2_ttf switch-sdl2_image switch-curl switch-mbedtls switch-zlib switch-bzip2 switch-xz switch-deko3d switch-mesa

Package names can vary by devkitPro repository state. If a package name is not found, search the package database:

dkp-pacman -Ss switch | grep -Ei 'sdl2|curl|mbedtls|zlib|bzip2|xz|lzma|deko3d|mesa'

The app also expects host tools commonly available on a Linux development machine:

git
make
bash
pkg-config
python3
grep
sed

External Player Sources

The custom FFmpeg fork is the source of truth for the patched nvtegra backend. Do not copy the full FFmpeg or mpv source trees into the main repository as regular files.

The main repository should track only the submodule commit references:

git status --short
git submodule status

Expected submodule branches in this checkout:

external/ffmpeg-nx -> feat/updated-readme
external/mpv-nx    -> main

If you change FFmpeg patches:

cd external/ffmpeg-nx
git status
git add configure libavcodec libavformat libavutil
git commit -m "Update Kboré NVTEGRA player patches"
git push origin feat/updated-readme
cd ../..
git add external/ffmpeg-nx

If you change mpv integration code:

cd external/mpv-nx
git status
git add .
git commit -m "Update Kboré mpv homebrew integration"
git push origin main
cd ../..
git add external/mpv-nx

Build FFmpeg SDK

The app links against external/player-sdk, generated from external/ffmpeg-nx. Rebuild it whenever the FFmpeg fork changes or when the SDK is missing.

From the repository root:

rm -rf external/player-sdk
./scripts/build-ffmpeg-nx.sh
./scripts/check-ffmpeg-nx.sh

The FFmpeg configure step must include:

--target-os=horizon
--enable-gpl
--enable-nvtegra
--enable-pic

The build flags must keep PIE/PIC enabled:

-fPIC
-fPIE
-specs=$DEVKITPRO/libnx/switch.specs

Recommended decoder/parser coverage:

--enable-parser=h264,hevc,aac,ac3,mpeg4video,mpegaudio,vp8,vp9
--enable-decoder=h264,hevc,aac,mp3,ac3,eac3,mpeg2video,mpeg4,vp8,vp9

Verify the generated SDK:

./scripts/check-ffmpeg-nx.sh
aarch64-none-elf-nm external/player-sdk/lib/libavcodec.a | grep -Ei 'nvtegra|h264_nvtegra|hevc_nvtegra'
aarch64-none-elf-nm external/player-sdk/lib/libavutil.a | grep -Ei 'nvtegra|hwcontext_nvtegra'

Expected symbols include:

h264_nvtegra
hevc_nvtegra
vp8_nvtegra
vp9_nvtegra
hwcontext_nvtegra

Apply FFmpeg Patches

The patched fork should normally already contain the required NVTEGRA changes. Use the patch scripts only when rebuilding or replaying the patch set from a clean FFmpeg base.

Patch files live in:

patches/ffmpeg-nvtegra/

Confirm they are real patch files:

grep -n "diff --git" patches/ffmpeg-nvtegra/*.patch

Apply the patch helpers from the repository root:

./scripts/patch-ffmpeg-nx-switch.sh
./scripts/apply-ffmpeg-nvtegra-0001-manual.sh
./scripts/apply-ffmpeg-nvtegra-0002-manual.sh
./scripts/apply-ffmpeg-nvtegra-from-0003-safe.sh

Verify the FFmpeg tree:

cd external/ffmpeg-nx
grep -R "enable-nvtegra\|nvtegra\|AV_HWDEVICE_TYPE_NVTEGRA" configure libavcodec libavutil -n | head -80
cd ../..

Build Kboré

Make sure external/player-sdk exists before building the app.

make clean
make switch

Expected outputs:

kbore.elf
kbore.nro
kbore.nacp

Makefile.switch should include the generated SDK before portlibs:

PLAYER_SDK := $(APP_ROOT)/external/player-sdk
PLAYER_SDK_INC := $(PLAYER_SDK)/include
PLAYER_SDK_LIB := $(PLAYER_SDK)/lib

export INCLUDE := -I$(PLAYER_SDK_INC) ...
export LIBPATHS := -L$(PLAYER_SDK_LIB) ...

The player build currently enables the native hardware path in Makefile.switch.


Runtime Files

Place the generated app under the homebrew app folder on the SD card:

sdmc:/switch/kbore/kbore.nro

Suggested SD layout:

sdmc:/switch/kbore/
├── kbore.nro
├── config.json
├── fonts/
│   └── OpenSans-Regular.ttf
├── manifests/
└── cache/

Example config.json:

{
  "parserApiBaseUrl": "https://your-parser-api.example",
  "apiKey": "your-api-key",
  "active": "my-m3u-list",
  "pageSize": 20,
  "preloadThreshold": 8,
  "useUnicodeIcons": false,
  "playlists": [
    {
      "id": "my-m3u-list",
      "name": "My M3U List",
      "type": "m3u",
      "active": true,
      "m3u_url": "http://host:port/get.php?username=USER&password=PASS&type=m3u_plus&output=ts",
      "epg_url": "http://host:port/xmltv.php?username=USER&password=PASS"
    },
    {
      "id": "my-xtream-list",
      "name": "My Xtream List",
      "type": "xtream",
      "server_url": "http://host:port",
      "username": "USER",
      "password": "PASS"
    }
  ],
  "defaultXtreamUrl": "http://server:port",
  "defaultPlaylistUrl": "http://host:port/playlist.m3u"
}

defaultXtreamUrl and defaultPlaylistUrl are legacy compatibility fields. New saved lists should live in playlists, and the selected list is stored in active / active_playlist_id.

If text renders as boxes, verify the font file exists:

sdmc:/switch/kbore/fonts/OpenSans-Regular.ttf

For playlist names, channel names, and categories with unsupported icon glyphs, normalize or remove the icons before rendering.


Playlist Loading and Cache

Kboré loads M3U and Xtream sources through the configured parser API. The app expects the parser to return a lightweight initial manifest and to load deeper folders through child-page requests instead of returning a full recursive tree on first import.

The important parser endpoints are:

POST /api/nodes/parse-url
POST /api/nodes/xtream/manifest
POST /api/nodes/children
POST /api/xtream/epg/short

The app stores cached data by playlist id so switching saved lists does not force a full re-download every time.

Current cache paths:

sdmc:/switch/kbore/manifests/<playlist-id>_manifest.json
sdmc:/switch/kbore/manifests/<playlist-id>_manifest.json.gz
sdmc:/switch/kbore/cache/<playlist-id>_<provider>_<type>_<category>_page_<n>.json
sdmc:/switch/kbore/cache/<playlist-id>_node_<node-id>_page_<n>.json
sdmc:/switch/kbore/cache/<playlist-id>_epg_<channel-key>.json

Legacy manifest files are still read for compatibility:

sdmc:/switch/kbore/manifests/active-manifest.json
sdmc:/switch/kbore/active-manifest.json
sdmc:/switch/kbore/manifest-<playlist-id>.json
sdmc:/switch/kbore/cache/<playlist-id>_manifest.json
sdmc:/switch/kbore/cache/<playlist-id>_manifest.json.gz

If an existing list keeps loading stale data, remove only that playlist's cached manifest and page files from manifests/ and cache/, then import it again.


Player Pipeline

Current video path:

NativeDemuxer
-> custom FFmpeg + nvtegra
-> AVHWDeviceContext nvtegra
-> NativeDecoder::openVideoHardware()
-> AVFrame pix_fmt=nvtegra
-> av_hwframe_transfer_data()
-> YUVFrame
-> SDL renderer

Current audio path:

NativeDemuxer
-> audio packets
-> FFmpeg audio decoder
-> SwrContext
-> S16 stereo 48 kHz
-> SDL_QueueAudio()

The current renderer still transfers decoded frames back through CPU memory. That is acceptable for SD/HD, but FHD needs a direct native render path:

NVTEGRA frame
-> native surface/texture import
-> deko3d renderer
-> present without av_hwframe_transfer_data()

Native Probe Expectations

After the FFmpeg SDK is built with NVTEGRA support, the native probe should show:

configs=1
pix_fmt=nvtegra
device=nvtegra
methods=none
usableDeviceConfig=yes
createdDevice=yes
selected=#0

The nvtegra backend can report methods=none. The probe should still accept the config when device=nvtegra and pix_fmt=nvtegra are present.

Useful log filters:

[KBORE][NVTEGRA]
[KBORE][DEKO3D]

On Switch, runtime diagnostics are appended to:

sdmc:/switch/kbore/kbore.log

Common Build Issues

TCP_MAXSEG undeclared

Run:

./scripts/patch-ffmpeg-nx-switch.sh

read-only segment has dynamic relocations

The FFmpeg static libraries were likely built without PIE/PIC. Rebuild the SDK with:

--enable-pic
-fPIC
-fPIE

For temporary validation only, test without assembly optimizations:

--disable-asm
--disable-neon

configs=0

The app is not using an FFmpeg SDK with the NVTEGRA backend. Rebuild and verify:

rm -rf external/player-sdk
./scripts/build-ffmpeg-nx.sh
./scripts/check-ffmpeg-nx.sh

Also confirm Makefile.switch uses external/player-sdk before portlibs:

grep -n "PLAYER_SDK\|LIBPATHS\|INCLUDE\|LIBS" Makefile.switch

usableDeviceConfig=no with pix_fmt=nvtegra

The probe must accept nvtegra even when methods=none.

could not initialize SwrContext: Invalid argument

Likely audio stream metadata is missing or unexpected. Check the audio fallback helpers:

safeAudioSampleRate()
safeAudioSampleFormat()
safeInputLayout()
makeDefaultLayout()

Also confirm the FFmpeg build includes the required audio decoders and parsers.


Quick Commands

Rebuild the FFmpeg SDK:

rm -rf external/player-sdk
./scripts/build-ffmpeg-nx.sh
./scripts/check-ffmpeg-nx.sh

Rebuild Kboré:

make clean
make switch

Check submodules:

git submodule status
git -C external/ffmpeg-nx status --short --branch
git -C external/mpv-nx status --short --branch

Check generated symbols:

aarch64-none-elf-nm external/player-sdk/lib/libavcodec.a | grep -Ei 'nvtegra|h264_nvtegra|hevc_nvtegra'
aarch64-none-elf-nm external/player-sdk/lib/libavutil.a | grep -Ei 'nvtegra|hwcontext_nvtegra'

License Note

The NVTEGRA FFmpeg backend requires:

--enable-gpl

If Kboré is distributed with binaries linked against that FFmpeg build, treat the distribution as GPL-compatible and provide the corresponding source according to the applicable FFmpeg license terms.

Kboré does not provide IPTV lists, channels, movies, series, servers, or streaming content. The app is only a player. All playlists, sources, and content are provided by the user and are the user’s responsibility. Kboré is not affiliated with, sponsored by, or endorsed by any IPTV provider or streaming server.

About

Kboré was built with a focus on performance, usability, and a better playback experience. The app supports live TV streams and VOD content, offering a simple interface that makes it easy to navigate through categories, channels, movies, and shows.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Sponsor this project

Contributors

Languages