From f481f5a764884a466342a208061cb6243917c024 Mon Sep 17 00:00:00 2001 From: Yuzuki Aida Date: Sun, 19 May 2024 01:20:03 +0900 Subject: [PATCH] Update --- apps/push-serverless/src/services/encoder.ts | 8 +-- apps/web/locales/en.json | 4 +- apps/web/locales/ja.json | 4 +- apps/web/organisms/live/admin/index.tsx | 5 ++ apps/web/utils/hooks/use-video-stream.ts | 57 +++++++++++--------- 5 files changed, 46 insertions(+), 32 deletions(-) diff --git a/apps/push-serverless/src/services/encoder.ts b/apps/push-serverless/src/services/encoder.ts index f397e52a..4b1526c5 100644 --- a/apps/push-serverless/src/services/encoder.ts +++ b/apps/push-serverless/src/services/encoder.ts @@ -254,7 +254,7 @@ export class Encoder { const stream = ffmpeg(this.rtmp) .audioCodec('aac') .audioBitrate('128k') - .audioChannels(2) + // .audioChannels(2) .videoCodec('libx264') .videoBitrate('800k') .size('640x360') @@ -262,7 +262,7 @@ export class Encoder { .format('hls') .outputOptions([ '-g 30', - '-hls_time 1', + '-hls_time 2', '-hls_list_size 10', '-hls_flags delete_segments+omit_endlist', `-hls_segment_filename ${path}/${idx}-%d.ts`, @@ -351,9 +351,9 @@ export class Encoder { const stream = ffmpeg(pipe) .audioCodec('aac') .audioBitrate('128k') - .audioChannels(2) + // .audioChannels(2) .videoCodec('libx264') - .videoBitrate('1500k') + .videoBitrate('1000k') .format('flv') .inputOptions(['-re']) .outputOptions(['-preset', 'ultrafast', '-tune', 'zerolatency']) diff --git a/apps/web/locales/en.json b/apps/web/locales/en.json index b54a4f84..51b71a52 100644 --- a/apps/web/locales/en.json +++ b/apps/web/locales/en.json @@ -97,9 +97,9 @@ "video.recording-deleted": "This live was not recorded.", "live.player.settings.type": "Change streaming method", "live.player.settings.type.flv.title": "Source quality", - "live.player.settings.type.flv.note": "Low latency (iOS is not supported)", + "live.player.settings.type.flv.note": "Ultra-Low latency (iOS is not supported)", "live.player.settings.type.hlsHq.title": "High quality", - "live.player.settings.type.hlsHq.note": "For iOS", + "live.player.settings.type.hlsHq.note": "Low latency (For iOS)", "live.player.settings.type.hlsLq.title": "Low quality", "live.player.settings.type.hlsLq.note": "For mobile network", "live.player.settings.type.audio.title": "Audio only", diff --git a/apps/web/locales/ja.json b/apps/web/locales/ja.json index c1d010eb..39c39f2a 100644 --- a/apps/web/locales/ja.json +++ b/apps/web/locales/ja.json @@ -97,9 +97,9 @@ "video.recording-deleted": "この配信の録画はありません。", "live.player.settings.type": "再生方式を変更", "live.player.settings.type.flv.title": "ソース画質", - "live.player.settings.type.flv.note": "低遅延 (iOS 非対応)", + "live.player.settings.type.flv.note": "超低遅延 (iOS 非対応)", "live.player.settings.type.hlsHq.title": "高画質", - "live.player.settings.type.hlsHq.note": "iOS 向け", + "live.player.settings.type.hlsHq.note": "低遅延 (iOS 向け)", "live.player.settings.type.hlsLq.title": "低画質", "live.player.settings.type.hlsLq.note": "携帯回線向け", "live.player.settings.type.audio.title": "音声のみ", diff --git a/apps/web/organisms/live/admin/index.tsx b/apps/web/organisms/live/admin/index.tsx index 57ce22e8..9b865b02 100644 --- a/apps/web/organisms/live/admin/index.tsx +++ b/apps/web/organisms/live/admin/index.tsx @@ -118,6 +118,11 @@ export const Admin: FC = ({ キーフレーム間隔: 1s (重要: 間隔が大きい/オートだと視聴がカクつきます) + + 映像エンコーダ: H264{' '} + の任意のハードウェア/ソフトウェアエンコーダ (重要: + H265(HEVC) は対応していません) + 出力解像度: 1920x1080 ビットレート: 2000 kbps 前後 diff --git a/apps/web/utils/hooks/use-video-stream.ts b/apps/web/utils/hooks/use-video-stream.ts index 15163e40..b87f75e3 100644 --- a/apps/web/utils/hooks/use-video-stream.ts +++ b/apps/web/utils/hooks/use-video-stream.ts @@ -4,6 +4,7 @@ import { RefObject, useCallback, useEffect, useRef, useState } from 'react'; import { LiveUrls } from 'api-types/api/v1/lives/_liveId@number/url'; import { useAPIError } from './api/use-api-error'; import { VideoUrls } from 'api-types/api/v1/videos/_liveId@number'; +import type { HlsConfig } from 'hls.js'; export enum LivePlayType { Flv = 'flv', @@ -12,6 +13,8 @@ export enum LivePlayType { Audio = 'audio' } +const LowLatency = [LivePlayType.Flv, LivePlayType.HlsHq, LivePlayType.Audio]; + export enum VideoPlayType { HlsHq = 'hlsHq' } @@ -69,7 +72,12 @@ export const useVideoStream = ( autoCleanupSourceBuffer: true, liveBufferLatencyChasing: true, liveBufferLatencyMaxLatency: 2, - liveBufferLatencyMinRemain: 0.5 + liveBufferLatencyMinRemain: 0.5, + // @ts-expect-error: type error + enableWorkerForMSE: true, + liveBufferLatencyChasingOnPaused: true, + liveSync: true, + liveSyncTargetLatency: 0.5 } ); mpegtsPlayerRef.current = player; @@ -88,7 +96,11 @@ export const useVideoStream = ( ); const handleHls = useCallback( - async (url: string, isLive: boolean) => { + async ( + url: string, + playType: LivePlayType | VideoPlayType, + isLive: boolean + ) => { if (!videoTagRef.current) { return; } @@ -103,26 +115,23 @@ export const useVideoStream = ( if (Hls.isSupported()) { if (isLive) { - player = new Hls({ + let hlsConfig: Partial = { enableWorker: true, - maxBufferLength: 1, - liveBackBufferLength: 0, - liveSyncDuration: 0, - liveMaxLatencyDuration: 5, liveDurationInfinity: true, - highBufferWatchdogPeriod: 1, - manifestLoadingTimeOut: 3000, - manifestLoadingMaxRetry: 10, - manifestLoadingMaxRetryTimeout: 3000, - levelLoadingTimeOut: 3000, - levelLoadingMaxRetry: 10, - levelLoadingMaxRetryTimeout: 3000, - fragLoadingTimeOut: 3000, - fragLoadingMaxRetry: 10, - fragLoadingMaxRetryTimeout: 3000, - startFragPrefetch: false, + startFragPrefetch: true, progressive: true - }); + }; + + if (LowLatency.includes(playType as LivePlayType)) { + hlsConfig = { + ...hlsConfig, + liveSyncDuration: 0, + liveMaxLatencyDuration: 5, + highBufferWatchdogPeriod: 1 + }; + } + + player = new Hls(hlsConfig); } else { player = new Hls({ enableWorker: true @@ -193,16 +202,16 @@ export const useVideoStream = ( await handleFlv(live.flv); } catch (e) { if (e instanceof FlvNotSupportedError) { - setPlayType(LivePlayType.HlsLq as PlayType); + setPlayType(LivePlayType.HlsHq as PlayType); } } })(); } else if (playType === LivePlayType.HlsHq) { - void handleHls(live.hlsHq, true); + void handleHls(live.hlsHq, playType, true); } else if (playType === LivePlayType.HlsLq) { - void handleHls(live.hlsLq, true); + void handleHls(live.hlsLq, playType, true); } else if (playType === LivePlayType.Audio) { - void handleHls(live.audio, true); + void handleHls(live.audio, playType, true); } else { setPlayType(LivePlayType.Flv as PlayType); } @@ -211,7 +220,7 @@ export const useVideoStream = ( if (playType === VideoPlayType.HlsHq) { // todo: lq でフォールバック? if (video.hlsHq) { - void handleHls(video.hlsHq, false); + void handleHls(video.hlsHq, playType, false); } } else { setPlayType(VideoPlayType.HlsHq as PlayType);