Fixing Missing Chinese Subtitles in Jellyfin Android TV

Fixing Missing Chinese Subtitles in Jellyfin Android TV
From Kannagi

Problem Discovery

I had previously deployed a Docker version of Jellyfin on my NAS. The issue of Chinese subtitles not displaying could be resolved by setting up a fallback font in Jellyfin.

In short:

  1. Download the Noto Sans CJK font package.
  2. In the Jellyfin Dashboard → Playback → Transcoding, enable Enable Fallback Font and place the downloaded font package in the fallback font file path.

Recently, I purchased a Fire TV Stick to pair with my projector. After downloading Jellyfin on it, I found that the Chinese subtitles — which displayed correctly on the browser and mobile clients — failed to render properly on the Android TV version of Jellyfin.


Investigation

After the first round of searching online, I initially thought the issue was that the Android TV version didn't support subtitle burn-in, causing problems with external .ass subtitles. However, the latest Android TV version seems to have already fixed this issue.

Additionally, when I tried playing Kannagi with external .ass subtitles, the subtitles didn't display. But playing Owarimonogatari, which had subtitles muxed into the .mkv file, worked fine. By inspecting the .mkv file of Owarimonogatari, I discovered that the fonts specified in the subtitle file were also embedded as attachments inside the .mkv. So I tried using MKVToolNix to mux Kannagi's specified fonts and subtitles into the .mkv as well, but the Chinese characters still failed to display.


By analyzing the Jellyfin logs, I found some clues.

Owarimonogatari — partial log:ffmpeg version 7.0.2-Jellyfin Copyright (c) 2000-2024 the FFmpeg developers
  built with gcc 12 (Debian 12.2.0-14)
  configuration: --prefix=/usr/lib/jellyfin-ffmpeg --target-os=linux --extra-version=Jellyfin --disable-doc --disable-ffplay --disable-ptx-compression --disable-static --disable-libxcb --disable-sdl2 --disable-xlib --enable-lto=auto --enable-gpl --enable-version3 --enable-shared --enable-gmp --enable-gnutls --enable-chromaprint --enable-opencl --enable-libdrm --enable-libxml2 --enable-libass --enable-libfreetype --enable-libfribidi --enable-libfontconfig --enable-libharfbuzz --enable-libbluray --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libopenmpt --enable-libdav1d --enable-libsvtav1 --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libzvbi --enable-libzimg --enable-libfdk-aac --arch=amd64 --enable-libshaderc --enable-libplacebo --enable-vulkan --enable-vaapi --enable-amf --enable-libvpl --enable-ffnvcodec --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc
  libavutil      59.  8.100 / 59.  8.100
  libavcodec     61.  3.100 / 61.  3.100
  libavformat    61.  1.100 / 61.  1.100
  libavdevice    61.  1.100 / 61.  1.100
  libavfilter    10.  1.100 / 10.  1.100
  libswscale      8.  1.100 /  8.  1.100
  libswresample   5.  1.100 /  5.  1.100
  libpostproc    58.  1.100 / 58.  1.100
[Parsed_subtitles_2 @ 0x55d1b14d8ec0] libass API version: 0x1703000
[Parsed_subtitles_2 @ 0x55d1b14d8ec0] libass source: commit: 0.17.3-0-ge46aedea0a0d17da4c4ef49d84b94a7994664ab5
[Parsed_subtitles_2 @ 0x55d1b14d8ec0] Shaper: FriBidi 1.0.16 (SIMPLE) HarfBuzz-ng 10.2.0 (COMPLEX)
[Parsed_subtitles_2 @ 0x55d1b14d8ec0] Loading font file '/cache/attachments/a73e2ec42fa9f3014c86bcc6c27b1f80/FOT-MatissePro-DB.otf'
[Parsed_subtitles_2 @ 0x55d1b14d8ec0] Loading font file '/cache/attachments/a73e2ec42fa9f3014c86bcc6c27b1f80/FZZYSK.TTF'
[Parsed_subtitles_2 @ 0x55d1b14d8ec0] Loading font file '/cache/attachments/a73e2ec42fa9f3014c86bcc6c27b1f80/方正粗宋_GBK.ttf'
[Parsed_subtitles_2 @ 0x55d1b14d8ec0] Loading font file '/cache/attachments/a73e2ec42fa9f3014c86bcc6c27b1f80/方正准圆_GBK.ttf'
[Parsed_subtitles_2 @ 0x55d1b14d8ec0] Using font provider fontconfig
Input #0, matroska,webm, from 'file:/video/终物语/终物语 E10.mkv':
  Metadata:
    encoder         : libebml v1.4.0 + libmatroska v1.6.0
    creation_time   : 2020-07-22T07:55:35.000000Z
  Duration: 00:24:13.12, start: 0.000000, bitrate: 2972 kb/s
  Stream #0:0: Video: hevc (Main 10), yuv420p10le(tv), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn (default)
      Metadata:
        BPS-eng         : 2317138
        DURATION-eng    : 00:24:13.119000000
        NUMBER_OF_FRAMES-eng: 34840
        NUMBER_OF_BYTES-eng: 420884760
        _STATISTICS_WRITING_APP-eng: mkvmerge v48.0.0 ('Fortress Around Your Heart') 64-bit
        _STATISTICS_WRITING_DATE_UTC-eng: 2020-07-22 07:55:35
        _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:1(jpn): Audio: opus, 48000 Hz, stereo, fltp (default)
      Metadata:
        BPS-eng         : 264135
        DURATION-eng    : 00:24:13.120000000
        NUMBER_OF_FRAMES-eng: 72656
        NUMBER_OF_BYTES-eng: 47977509
        _STATISTICS_WRITING_APP-eng: mkvmerge v48.0.0 ('Fortress Around Your Heart') 64-bit
        _STATISTICS_WRITING_DATE_UTC-eng: 2020-07-22 07:55:35
        _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:2(jpn): Audio: opus, 48000 Hz, stereo, fltp
      Metadata:
        title           : Commentary
        BPS-eng         : 186030
        DURATION-eng    : 00:24:13.120000000
        NUMBER_OF_FRAMES-eng: 72656
        NUMBER_OF_BYTES-eng: 33790607
        _STATISTICS_WRITING_APP-eng: mkvmerge v48.0.0 ('Fortress Around Your Heart') 64-bit
        _STATISTICS_WRITING_DATE_UTC-eng: 2020-07-22 07:55:35
        _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:3: Subtitle: ass (ssa) (default)
      Metadata:
        title           : SC
        BPS-eng         : 135
        DURATION-eng    : 00:24:04.420000000
        NUMBER_OF_FRAMES-eng: 347
        NUMBER_OF_BYTES-eng: 24530
        _STATISTICS_WRITING_APP-eng: mkvmerge v48.0.0 ('Fortress Around Your Heart') 64-bit
        _STATISTICS_WRITING_DATE_UTC-eng: 2020-07-22 07:55:35
        _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:4: Subtitle: ass (ssa)
      Metadata:
        title           : TC
        BPS-eng         : 135
        DURATION-eng    : 00:24:04.420000000
        NUMBER_OF_FRAMES-eng: 347
        NUMBER_OF_BYTES-eng: 24536
        _STATISTICS_WRITING_APP-eng: mkvmerge v48.0.0 ('Fortress Around Your Heart') 64-bit
        _STATISTICS_WRITING_DATE_UTC-eng: 2020-07-22 07:55:35
        _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:5: Subtitle: ass (ssa)
      Metadata:
        title           : Commentary SC
        BPS-eng         : 179
        DURATION-eng    : 00:24:07.880000000
        NUMBER_OF_FRAMES-eng: 389
        NUMBER_OF_BYTES-eng: 32502
        _STATISTICS_WRITING_APP-eng: mkvmerge v48.0.0 ('Fortress Around Your Heart') 64-bit
        _STATISTICS_WRITING_DATE_UTC-eng: 2020-07-22 07:55:35
        _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:6: Subtitle: ass (ssa)
      Metadata:
        title           : Commentary TC
        BPS-eng         : 181
        DURATION-eng    : 00:24:07.880000000
        NUMBER_OF_FRAMES-eng: 389
        NUMBER_OF_BYTES-eng: 32885
        _STATISTICS_WRITING_APP-eng: mkvmerge v48.0.0 ('Fortress Around Your Heart') 64-bit
        _STATISTICS_WRITING_DATE_UTC-eng: 2020-07-22 07:55:35
        _STATISTICS_TAGS-eng: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:7: Attachment: otf
      Metadata:
        filename        : FOT-MatissePro-DB.otf
        mimetype        : application/vnd.ms-opentype
  Stream #0:8: Attachment: ttf
      Metadata:
        filename        : FZZYSK.TTF
        mimetype        : application/x-truetype-font
  Stream #0:9: Attachment: ttf
      Metadata:
        filename        : 方正粗宋_GBK.ttf
        mimetype        : application/x-truetype-font
  Stream #0:10: Attachment: ttf
      Metadata:
        filename        : 方正准圆_GBK.ttf
        mimetype        : application/x-truetype-font
Stream mapping:
  Stream #0:0 (hevc) -> setparams:default (graph 0)
  overlay_vaapi:default (graph 0) -> Stream #0:0 (hevc_vaapi)
  Stream #0:1 -> #0:1 (opus (native) -> aac (libfdk_aac))
Press [q] to stop, [?] for help
[Parsed_subtitles_2 @ 0x7fc0f0006a80] libass API version: 0x1703000
[Parsed_subtitles_2 @ 0x7fc0f0006a80] libass source: commit: 0.17.3-0-ge46aedea0a0d17da4c4ef49d84b94a7994664ab5
[Parsed_subtitles_2 @ 0x7fc0f0006a80] Shaper: FriBidi 1.0.16 (SIMPLE) HarfBuzz-ng 10.2.0 (COMPLEX)
[Parsed_subtitles_2 @ 0x7fc0f0006a80] Loading font file '/cache/attachments/a73e2ec42fa9f3014c86bcc6c27b1f80/FOT-MatissePro-DB.otf'
[Parsed_subtitles_2 @ 0x7fc0f0006a80] Loading font file '/cache/attachments/a73e2ec42fa9f3014c86bcc6c27b1f80/FZZYSK.TTF'
[Parsed_subtitles_2 @ 0x7fc0f0006a80] Loading font file '/cache/attachments/a73e2ec42fa9f3014c86bcc6c27b1f80/方正粗宋_GBK.ttf'
[Parsed_subtitles_2 @ 0x7fc0f0006a80] Loading font file '/cache/attachments/a73e2ec42fa9f3014c86bcc6c27b1f80/方正准圆_GBK.ttf'
[Parsed_subtitles_2 @ 0x7fc0f0006a80] Using font provider fontconfig
[Parsed_subtitles_2 @ 0x7fc0f0006a80] fontselect: (方正准圆_GBK, 700, 0) -> FZY3K--GBK1-0, 0, FZY3K--GBK1-0
Output #0, hls, to '/cache/transcodes/36df184ee5eeb6c0c6299ceb87021897.m3u8':
  Metadata:
    encoder         : Lavf61.1.100
  Stream #0:0: Video: hevc (Main) (hvc1 / 0x31637668), vaapi(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 5945 kb/s, 23.98 fps, 90k tbn
      Metadata:
        encoder         : Lavc61.3.100 hevc_vaapi
  Stream #0:1: Audio: aac, 48000 Hz, stereo, s16, 256 kb/s (default)
      Metadata:
        encoder         : Lavc61.3.100 libfdk_aac
frame=   20 fps=0.0 q=-0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
[Parsed_subtitles_2 @ 0x7fc0f0006a80] fontselect: (方正准圆_GBK, 400, 0) -> FZY3K--GBK1-0, 0, FZY3K--GBK1-0
frame=   48 fps= 48 q=-0.0 size=N/A time=00:00:01.16 bitrate=N/A speed=1.17x    
[hls @ 0x55d1b1514700] Opening '/cache/transcodes/36df184ee5eeb6c0c6299ceb870218970.ts' for writing
frame=   75 fps= 50 q=-0.0 size=N/A time=00:00:02.29 bitrate=N/A speed=1.53x    
frame=  102 fps= 51 q=-0.0 size=N/A time=00:00:03.42 bitrate=N/A speed=1.71x    
frame=  129 fps= 52 q=-0.0 size=N/A time=00:00:04.54 bitrate=N/A speed=1.82x    
[hls @ 0x55d1b1514700] Opening '/cache/transcodes/36df184ee5eeb6c0c6299ceb870218971.ts' for writing
frame=  157 fps= 52 q=-0.0 size=N/A time=00:00:05.71 bitrate=N/A speed= 1.9x    
frame=  190 fps= 54 q=-0.0 size=N/A time=00:00:07.09 bitrate=N/A speed=2.03x    
[hls @ 0x55d1b1514700] Opening '/cache/transcodes/36df184ee5eeb6c0c6299ceb870218972.ts' for writing
frame=  223 fps= 56 q=-0.0 size=N/A time=00:00:08.46 bitrate=N/A speed=2.12x    
frame=  249 fps= 55 q=-0.0 size=N/A time=00:00:09.55 bitrate=N/A speed=2.12x    
frame=  284 fps= 57 q=-0.0 size=N/A time=00:00:11.01 bitrate=N/A speed= 2.2x    

Owarimonogatari - partial log

ffmpeg version 7.0.2-Jellyfin Copyright (c) 2000-2024 the FFmpeg developers
  built with gcc 12 (Debian 12.2.0-14)
  configuration: --prefix=/usr/lib/jellyfin-ffmpeg --target-os=linux --extra-version=Jellyfin --disable-doc --disable-ffplay --disable-ptx-compression --disable-static --disable-libxcb --disable-sdl2 --disable-xlib --enable-lto=auto --enable-gpl --enable-version3 --enable-shared --enable-gmp --enable-gnutls --enable-chromaprint --enable-opencl --enable-libdrm --enable-libxml2 --enable-libass --enable-libfreetype --enable-libfribidi --enable-libfontconfig --enable-libharfbuzz --enable-libbluray --enable-libmp3lame --enable-libopus --enable-libtheora --enable-libvorbis --enable-libopenmpt --enable-libdav1d --enable-libsvtav1 --enable-libwebp --enable-libvpx --enable-libx264 --enable-libx265 --enable-libzvbi --enable-libzimg --enable-libfdk-aac --arch=amd64 --enable-libshaderc --enable-libplacebo --enable-vulkan --enable-vaapi --enable-amf --enable-libvpl --enable-ffnvcodec --enable-cuda --enable-cuda-llvm --enable-cuvid --enable-nvdec --enable-nvenc
  libavutil      59.  8.100 / 59.  8.100
  libavcodec     61.  3.100 / 61.  3.100
  libavformat    61.  1.100 / 61.  1.100
  libavdevice    61.  1.100 / 61.  1.100
  libavfilter    10.  1.100 / 10.  1.100
  libswscale      8.  1.100 /  8.  1.100
  libswresample   5.  1.100 /  5.  1.100
  libpostproc    58.  1.100 / 58.  1.100
[Parsed_subtitles_2 @ 0x55c596fb1f00] libass API version: 0x1703000
[Parsed_subtitles_2 @ 0x55c596fb1f00] libass source: commit: 0.17.3-0-ge46aedea0a0d17da4c4ef49d84b94a7994664ab5
[Parsed_subtitles_2 @ 0x55c596fb1f00] Shaper: FriBidi 1.0.16 (SIMPLE) HarfBuzz-ng 10.2.0 (COMPLEX)
[Parsed_subtitles_2 @ 0x55c596fb1f00] Using font provider fontconfig
Input #0, matroska,webm, from 'file:/video/神薙/[VCB-Studio] Kannagi [01][Ma10p_1080p][x265_flac_2aac].mkv':
  Metadata:
    encoder         : libebml v1.4.4 + libmatroska v1.7.1
    creation_time   : 2025-08-28T07:04:44.000000Z
  Duration: 00:23:41.03, start: 0.000000, bitrate: 6448 kb/s
  Chapters:
    Chapter #0:0: start 0.000000, end 117.017000
      Metadata:
        title           : Chapter 01
    Chapter #0:1: start 117.017000, end 207.040000
      Metadata:
        title           : Chapter 02
    Chapter #0:2: start 207.040000, end 864.997000
      Metadata:
        title           : Chapter 03
    Chapter #0:3: start 864.997000, end 1324.990000
      Metadata:
        title           : Chapter 04
    Chapter #0:4: start 1324.990000, end 1421.034000
      Metadata:
        title           : Chapter 05
  Stream #0:0: Video: hevc (Main 10), yuv420p10le(tv, bt709/unknown/unknown), 1920x1080, SAR 1:1 DAR 16:9, 23.98 fps, 23.98 tbr, 1k tbn (default)
      Metadata:
        BPS             : 5423267
        DURATION        : 00:23:41.026000000
        NUMBER_OF_FRAMES: 35185
        NUMBER_OF_BYTES : 963325545
        _STATISTICS_WRITING_APP: mkvmerge v80.0 ('Roundabout') 64-bit
        _STATISTICS_WRITING_DATE_UTC: 2025-08-28 07:04:44
        _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:1(jpn): Audio: flac, 48000 Hz, stereo, s16 (default)
      Metadata:
        BPS             : 583827
        DURATION        : 00:23:41.020000000
        NUMBER_OF_FRAMES: 16653
        NUMBER_OF_BYTES : 103703864
        _STATISTICS_WRITING_APP: mkvmerge v80.0 ('Roundabout') 64-bit
        _STATISTICS_WRITING_DATE_UTC: 2025-08-28 07:04:44
        _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:2(jpn): Audio: aac (LC), 48000 Hz, stereo, fltp
      Metadata:
        BPS             : 169647
        DURATION        : 00:23:41.034000000
        NUMBER_OF_FRAMES: 66611
        NUMBER_OF_BYTES : 30134360
        _STATISTICS_WRITING_APP: mkvmerge v80.0 ('Roundabout') 64-bit
        _STATISTICS_WRITING_DATE_UTC: 2025-08-28 07:04:44
        _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:3(jpn): Audio: aac (LC), 48000 Hz, stereo, fltp
      Metadata:
        BPS             : 169785
        DURATION        : 00:23:41.034000000
        NUMBER_OF_FRAMES: 66611
        NUMBER_OF_BYTES : 30158858
        _STATISTICS_WRITING_APP: mkvmerge v80.0 ('Roundabout') 64-bit
        _STATISTICS_WRITING_DATE_UTC: 2025-08-28 07:04:44
        _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:4(srd): Subtitle: ass (ssa) (default)
      Metadata:
        BPS             : 108
        DURATION        : 00:23:09.900000000
        NUMBER_OF_FRAMES: 343
        NUMBER_OF_BYTES : 18791
        _STATISTICS_WRITING_APP: mkvmerge v80.0 ('Roundabout') 64-bit
        _STATISTICS_WRITING_DATE_UTC: 2025-08-28 07:04:44
        _STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
  Stream #0:5: Attachment: ttf
      Metadata:
        filename        : 方正行楷_GBK.ttf
        mimetype        : application/x-truetype-font
  Stream #0:6: Attachment: ttf
      Metadata:
        filename        : 方正准圆_GBK.TTF
        mimetype        : application/x-truetype-font
Stream mapping:
  Stream #0:0 (hevc) -> setparams:default (graph 0)
  overlay_vaapi:default (graph 0) -> Stream #0:0 (hevc_vaapi)
  Stream #0:1 -> #0:1 (flac (native) -> aac (libfdk_aac))
Press [q] to stop, [?] for help
[Parsed_subtitles_2 @ 0x7fc0cc006a80] libass API version: 0x1703000
[Parsed_subtitles_2 @ 0x7fc0cc006a80] libass source: commit: 0.17.3-0-ge46aedea0a0d17da4c4ef49d84b94a7994664ab5
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Shaper: FriBidi 1.0.16 (SIMPLE) HarfBuzz-ng 10.2.0 (COMPLEX)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Using font provider fontconfig
Output #0, hls, to '/cache/transcodes/62249dc233533765607b5d0c01e5893a.m3u8':
  Metadata:
    encoder         : Lavf61.1.100
  Stream #0:0: Video: hevc (Main) (hvc1 / 0x31637668), vaapi(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 6448 kb/s, 23.98 fps, 90k tbn
      Metadata:
        encoder         : Lavc61.3.100 hevc_vaapi
  Stream #0:1: Audio: aac, 48000 Hz, stereo, s16, 256 kb/s (default)
      Metadata:
        encoder         : Lavc61.3.100 libfdk_aac
frame=    0 fps=0.0 q=0.0 size=N/A time=N/A bitrate=N/A speed=N/A    
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: (方正准圆_GBK, 700, 0) -> /usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf, 0, DejaVuSans-Bold
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Glyph 0x6293 not found, selecting one more font for (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: failed to find any fallback with glyph 0x6293 for font: (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Glyph 0x5230 not found, selecting one more font for (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: failed to find any fallback with glyph 0x5230 for font: (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Glyph 0x4E86 not found, selecting one more font for (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: failed to find any fallback with glyph 0x4E86 for font: (方正准圆_GBK, 700, 0)
frame=   24 fps= 24 q=-0.0 size=N/A time=00:00:00.00 bitrate=N/A speed=   0x    
frame=   58 fps= 39 q=-0.0 size=N/A time=00:00:01.41 bitrate=N/A speed=0.945x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a0.ts' for writing
frame=   88 fps= 44 q=-0.0 size=N/A time=00:00:02.66 bitrate=N/A speed=1.33x    
frame=  115 fps= 46 q=-0.0 size=N/A time=00:00:03.79 bitrate=N/A speed=1.52x    
frame=  142 fps= 47 q=-0.0 size=N/A time=00:00:04.92 bitrate=N/A speed=1.64x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a1.ts' for writing
frame=  168 fps= 48 q=-0.0 size=N/A time=00:00:06.00 bitrate=N/A speed=1.72x    
frame=  194 fps= 48 q=-0.0 size=N/A time=00:00:07.09 bitrate=N/A speed=1.77x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a2.ts' for writing
frame=  219 fps= 49 q=-0.0 size=N/A time=00:00:08.13 bitrate=N/A speed=1.81x    
frame=  250 fps= 50 q=-0.0 size=N/A time=00:00:09.42 bitrate=N/A speed=1.88x    
frame=  276 fps= 50 q=-0.0 size=N/A time=00:00:10.51 bitrate=N/A speed=1.91x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a3.ts' for writing
frame=  300 fps= 50 q=-0.0 size=N/A time=00:00:11.51 bitrate=N/A speed=1.92x    
frame=  329 fps= 51 q=-0.0 size=N/A time=00:00:12.72 bitrate=N/A speed=1.96x    
frame=  354 fps= 51 q=-0.0 size=N/A time=00:00:13.76 bitrate=N/A speed=1.97x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a4.ts' for writing
frame=  376 fps= 50 q=-0.0 size=N/A time=00:00:14.68 bitrate=N/A speed=1.96x    
frame=  400 fps= 50 q=-0.0 size=N/A time=00:00:15.68 bitrate=N/A speed=1.96x    
frame=  424 fps= 50 q=-0.0 size=N/A time=00:00:16.68 bitrate=N/A speed=1.96x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a5.ts' for writing
frame=  448 fps= 50 q=-0.0 size=N/A time=00:00:17.68 bitrate=N/A speed=1.96x    
frame=  471 fps= 50 q=-0.0 size=N/A time=00:00:18.64 bitrate=N/A speed=1.96x    
frame=  495 fps= 49 q=-0.0 size=N/A time=00:00:19.64 bitrate=N/A speed=1.96x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a6.ts' for writing
frame=  518 fps= 49 q=-0.0 size=N/A time=00:00:20.60 bitrate=N/A speed=1.96x    
frame=  543 fps= 49 q=-0.0 size=N/A time=00:00:21.64 bitrate=N/A speed=1.97x    
frame=  569 fps= 49 q=-0.0 size=N/A time=00:00:22.73 bitrate=N/A speed=1.98x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a7.ts' for writing
frame=  596 fps= 50 q=-0.0 size=N/A time=00:00:23.85 bitrate=N/A speed=1.99x    
frame=  624 fps= 50 q=-0.0 size=N/A time=00:00:25.02 bitrate=N/A speed=   2x    
[hls @ 0x55c596f0f880] Opening '/cache/transcodes/62249dc233533765607b5d0c01e5893a8.ts' for writing
frame=  652 fps= 50 q=-0.0 size=N/A time=00:00:26.19 bitrate=N/A speed=2.01x    
frame=  680 fps= 50 q=-0.0 size=N/A time=00:00:27.36 bitrate=N/A speed=2.03x    
frame=  709 fps= 51 q=-0.0 size=N/A time=00:00:28.57 bitrate=N/A speed=2.04x    
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Glyph 0x6293 not found, selecting one more font for (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: failed to find any fallback with glyph 0x6293 for font: (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Glyph 0x5230 not found, selecting one more font for (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: failed to find any fallback with glyph 0x5230 for font: (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Glyph 0x4E86 not found, selecting one more font for (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: failed to find any fallback with glyph 0x4E86 for font: (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Glyph 0x6293 not found, selecting one more font for (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: failed to find any fallback with glyph 0x6293 for font: (方正准圆_GBK, 700, 0)
[Parsed_subtitles_2 @ 0x7fc0cc006a80] Glyph 0x5230 not found, selecting one more font for (方正准圆_GBK, 700, 0)

Kannagi - partial log

By comparing the two, we can see that in Owarimonogatari, which displayed subtitles successfully:


[Parsed_subtitles_2 @ 0x7fc0f0006a80] fontselect: (方正准圆_GBK, 700, 0) -> FZY3K--GBK1-0, 0, FZY3K--GBK1-0

The system successfully found the font specified by the subtitle file from the attachments.

Whereas in Kannagi:

[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: (方正准圆_GBK, 700, 0) -> /usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf, 0, DejaVuSans-Bold
[Parsed_subtitles_2 @ 0x7fc0cc006a80] fontselect: failed to find any fallback with glyph 0x5230 for font: (方正准圆_GBK, 700, 0)

The system did not attempt to find the font from the attachments and instead went straight to the fonts inside the Jellyfin container. Since the Jellyfin container doesn't come with any fonts that support Chinese characters, the subtitles could not be rendered properly.

Unlike clients on other platforms, the Android TV version of Jellyfin apparently does not look for fonts in the fallback font directory.


Solution

Now that we know the issue is caused by missing fonts, the fix is straightforward — just add the Noto Sans CJK font package to the /usr/share/fonts/truetype/ path as well. We can use symbolic links so that when the system looks for fonts in /usr/share/fonts/truetype/, it automatically redirects to our fallback font path.

  1. Install fontconfig to manage fonts:
# Enter the container (use sudo if you have permission issues)
docker exec -it jellyfin-jellyfin-1 bash

# Install
apt-get update
apt-get install -y fontconfig
  1. Create the font directory and set up symbolic links:
mkdir -p /usr/share/fonts/custom
ln -sf /config/config/font/* /usr/share/fonts/custom/
  1. Configure fontconfig to prioritize Simplified Chinese fonts:
cat > /etc/fonts/local.conf <<'XML'
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <dir>/usr/share/fonts/custom</dir>

  <alias>
    <family>sans-serif</family>
    <prefer>
      <family>Noto Sans CJK SC</family>
      <family>Noto Sans CJK TC</family>
    </prefer>
  </alias>
</fontconfig>
XML
  1. Refresh the cache and verify:
# Refresh font cache
fc-cache -f -v

# Exit the Docker container
exit

# Verify
docker exec jellyfin-jellyfin-1 fc-match "方正准圆_GBK"
docker exec jellyfin-jellyfin-1 fc-match "Noto Sans CJK SC"
docker exec jellyfin-jellyfin-1 fc-match ":lang=zh,sans-serif"

If the output shows a NotoSansCJK font, it means the setup was successful.

Now play the video again on the Android TV Jellyfin client, and the Chinese subtitles should display correctly.

Note that these changes may be lost after a Jellyfin version upgrade. You can either redo the steps manually or combine them into a script to run when needed.


Hope this blog post helps anyone who ran into the same issue as me :)

Thanks for reading~