
Huly Stream 轉碼服務實踐指南基于 TUS 協議的可斷點續傳 HTTP 轉碼架構【免費下載鏈接】platformHuly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)項目地址: https://gitcode.com/GitHub_Trending/platform80/platform本篇技術指南圍繞 Huly 倉庫中foundations/stream目錄下的Stream 服務展開。Stream 是一個基于 Go 語言編寫的高性能 HTTP 轉碼服務通過TUS 協議實現可靠、可斷點續傳resumable的視頻轉碼能力支持將mp4、webm等輸入實時轉碼為hls輸出并直接上傳至 S3 或 Datalake 存儲。讀完本文你將掌握 Stream 的架構脈絡、全部環境變量與元數據配置、兩類 HTTP 接口/recording與/transcoding的調用方式以及底層 ffmpeg 轉碼參數與調度機制的源碼級原理能夠在本地或容器環境中獨立部署并驗證這一轉碼流水線。Stream 是什么面向 Huly 生態的媒體處理中間件Stream 位于倉庫 foundations/stream 目錄是 Huly 平臺All-in-One Project Management Platform媒體能力的關鍵一環。它被設計為一個獨立、輕量的 HTTP 服務對外只暴露有限的接口內部則串聯起上傳、轉碼、存儲三大環節上傳側采用 tusdTUS 協議的官方 Go 實現作為上傳入口天然支持斷點續傳與分片上傳適合大文件、弱網環境轉碼側調度 ffmpeg 與 ffprobe 完成轉碼、縮略圖生成、流信息探測存儲側通過統一的存儲抽象同時支持 S3 與 Datalake 兩種后端。從目錄結構看foundations/stream其內部按職責清晰分層internal/pkg/api/v1/存放 HTTP 處理器internal/pkg/mediaconvert/是轉碼核心邏輯internal/pkg/storage/封裝存儲后端另有config、token、manifest、uploader、queue、sharedpipe、resconv、profile、tracing、pprof等支撐包。核心特性README 中明確了以下能力TUS 協議支持借助 TUS 協議實現可靠的、可斷點續傳的轉碼桶bucket處理輸入格式mp4、webm輸出格式hls上傳選項直接上傳到 S3s3 Upload或上傳到 Datalakedatalake Upload實時轉碼、極短上傳等待流傳輸完成后即可獲取轉碼結果轉碼取消可實時取消或暫停正在進行的轉碼轉碼恢復高效恢復未完成的轉碼任務轉碼調度Transcoding scheduling支持任務隊列與并發調度。安裝與構建前置依賴根據 README.md本地構建需要Go推薦 v1.23ffmpeg必須安裝并確保其位于系統PATH中。補充說明從 Dockerfile 可以看到官方容器鏡像實際使用golang:1.24.4作為構建基座并在alpine運行鏡像內通過apk add --no-cache ffmpeg安裝 ffmpeg因此容器方式構建可以免去手動安裝依賴的步驟。運行鏡像默認暴露1080端口EXPOSE 1080以非 root 用戶streamuid/gid 1000運行。構建步驟方式一本地依賴整理go mod tidy方式二Docker 構建docker build . -t hcengineering/stream:latest配置詳解環境變量App Env ConfigurationStream 的配置全部通過環境變量注入前綴統一為STREAM_。其解析實現在 internal/pkg/config/config.go 中使用kelseyhightower/envconfig庫完成。完整參數表如下KEYTYPEDEFAULTDESCRIPTIONSTREAM_LOG_LEVELStringdebug設置應用日志級別STREAM_SERVER_SECRETString空生成和校驗 token 所需的服務端密鑰STREAM_PPROF_ENABLEDTrue/Falsetrue為 true 時在 localhost:6060 啟動 pprof 性能剖析服務STREAM_INSECURETrue/Falsefalse為 true 時跳過鑒權檢查STREAM_SERVE_URLString0.0.0.0:1080HTTP 服務監聽地址STREAM_ENDPOINT_URLURLs3://127.0.0.1:9000S3 或 Datalake 端點例如s3://my-ip-address、datalake://my-ip-addressSTREAM_MAX_PARALLEL_SCALING_COUNTInteger2可并行處理的轉碼任務數STREAM_MAX_THREAD_COUNTInteger4單個轉碼任務的最大線程數STREAM_OUTPUT_DIRString/tmp/transcoding/轉碼結果存放目錄STREAM_SENTRY_DSNString錯誤追蹤的 Sentry DSN源碼層面的補充說明對照 config.go 可以補充以下 README 未列出的細節STREAM_QUEUE_CONFIGQueueConfig默認空隊列配置字符串STREAM_REGIONRegion默認空服務所在區域STREAM_TIMEOUTTimeout默認5m上傳超時時間。該值會透傳給 TUS handler 的NetworkTimeout并用于上傳器uploader與流超時管理OpenTelemetry 觀測配置默認大多開啟OTEL_ENABLED默認 true、OTEL_SERVICE_NAME默認stream、OTEL_SERVICE_VERSION默認1.0.0、OTEL_TRACES_ENABLED默認 true、OTEL_METRICS_ENABLED默認 true、OTEL_LOGS_ENABLED默認 false。注意這類變量使用split_words解析例如OtelServiceName對應環境變量OTEL_SERVICE_NAME。關鍵校驗邏輯FromEnv見 config.go若EndpointURL為零值則置為nil若STREAM_INSECUREfalse且STREAM_SERVER_SECRET為空啟動會直接報錯server secret must be provided for secure configuration。也就是說生產環境非 insecure必須顯式配置服務端密鑰。此外Config.Endpoint()方法會根據Insecure標志自動決定存儲端點使用https默認或http協議insecure 時scheme 取自EndpointURL。Metadata元數據在通過 TUS 上傳/recording時客戶端可以在上傳請求中攜帶以下元數據resolution若傳入則設置輸出分辨率例如resolution: 1920:1080token訪問 Huly Datalake 服務所需的鑒權 tokendatalake 類型存儲必填workspace上傳內容到 Datalake 存儲所必需的工作區標識。源碼佐證在 mediaconvert/coordinator.go 的NewUpload中服務會讀取info.MetaData[width]、info.MetaData[height]、info.MetaData[contentType]構造VideoMeta隨后用info.MetaData[token]與info.MetaData[workspace]創建存儲后端coordinator.go。而storage.NewStorageByURL中同樣強制校驗workspace缺失直接報錯datalake 類型下token缺失也會報錯storage/storage.go。S3 環境配置若使用 S3 類型存儲還必須提供以下兩個環境變量AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY存儲后端的創建邏輯位于 storage/storage.go根據EndpointURL的 scheme 分發——datalake走NewDatalakeStorages3走NewS3其他 scheme 直接報unknown scheme。HTTP API 使用服務對外暴露 HTTP API監聽地址由STREAM_SERVE_URL決定默認0.0.0.0:1080。兩個主要端點分別對應錄制上傳與任務調度。通過 TUS 上傳并實時轉碼/recordingcurl -X POST http://localhost:1080/recording \ -H Tus-Resumable: 1.0.0 \ -H Upload-Length: file-size \ --data-binary path/to/your/file.mp4注意要在本地與 Stream 交互需要 TUS 客戶端。官方 README 推薦使用 tus-js-client 的瀏覽器示例video demo進行聯調。源碼實現該端點由 api/v1/recording/handler.go 提供。recordingHandler在首次請求時惰性初始化sync.Once一個基于tusd的 TUS handlerhandler.go其關鍵配置BasePath: /recording通過StoreComposer組合掛載了自定義的StreamCoordinator并啟用其Core、Terminater、Concater、LengthDeferrer四種能力分別對應創建上傳、終止上傳、拼接上傳、延遲聲明長度RespectForwardedHeaders: true且非 insecure 模式下會強制設置X-Forwarded-Proto: httpsDisableDownload: true關閉下載通道轉碼結果不回傳原始文件NetworkTimeout: h.cfg.Timeout沿用STREAM_TIMEOUT配置。流式轉碼的核心機制StreamCoordinator的NewUploadcoordinator.go會為每個上傳創建一個Stream實例其中包含一個sharedpipe共享管道Writer。客戶端上傳的每個分片通過Stream.WriteChunk寫入管道mediaconvert/stream.go而轉碼消費側從管道的Reader端讀取——這就是邊傳邊轉碼、上傳完成后轉碼結果即可用的實現基礎README 所稱Live transcoding with minimal upload time。同一切片數據在存儲后端支持MultipartStorage時還會并行寫入 multipart 上傳。取消 / 恢復機制Stream實現了TerminatableUploadstream.go與ConcatableUpload。終止上傳時會先關閉 writer 通知讀取端 EOF再異步取消進行中的 multipart 上傳最后關閉done通道StreamCoordinator.manageTimeout則負責空閑超時后的自動清理coordinator.go。ConcatUploads當前返回not implemented源碼注釋標明后續計劃從備份桶重新加載原始數據并重啟處理即斷點恢復的未來演進方向。調度一次轉碼/transcodingcurl -X POST http://localhost:1080/transcoding \ -H Content-Type: application/json \ -H Authorization: Bearer token \ -d { source: input file name, format: hls, workspace: test }請求處理流程api/v1/transcoding/handler.go校驗請求路徑必須為空否則返回400 Bad Request校驗Authorization頭存在否則返回401 Unauthorized解碼請求體為mediaconvert.Task解碼失敗返回400校驗format字段目前僅接受hlsisSupportedFormat函數否則返回415 Unsupported Media Type調用scheduler.Schedule(task)入隊若任務隊列已滿taskCh緩沖為 128見 mediaconvert/scheduler.go返回429 Too Many Requests成功則返回200 OK。調度與并發模型Schedulermediaconvert/scheduler.go采用經典的 worker 池模型任務結構Task{ID, Status, Source, Format, Workspace, Metadata}Schedule時分配 UUID 并將Status置為planned內部taskCh緩沖容量 128啟動MaxParallelTranscodingCount默認 2個 worker goroutine 并發消費任務這正是可并行處理的轉碼數的語義每個任務在獨立 spanOpenTelemetry內執行processTask失敗僅記錄日志不會阻塞隊列。轉碼流水線從任務到 HLS 產物無論走processTaskscheduler還是Transcoder.Transcodemediaconvert/transcoder.go轉碼主流程高度一致可劃分為如下階段獲取 token用ServerSecret為指定 workspace 簽發 stream 專用 tokentoken.NewToken用于訪問遠端存儲準備本地文件系統在OutputDir下為任務創建臨時目錄并下載源文件獲取遠端文件通過存儲抽象StatFile校驗媒體類型IsSupportedMediaType支持video/mp4、video/webm、video/quicktime明確拒絕video/mp2t與video/x-mpegurl見 transcoder.go 與 scheduler.go然后GetFile下載到本地ffprobe 探測解析視頻流必須存在否則報錯與音頻流允許缺失取得 codec、寬高等信息構造VideoMeta確定轉碼檔位調用DefaultTranscodingProfilesmediaconvert/strategy.go——保留原始分辨率檔再根據分辨率子級resconv.SubLevels追加若干降檔 profile若源 codec 已受 HLS 支持h264/h265/avc1*/av1*見IsHLSSupportedVideoCodec原始檔直接復制否則轉碼異步上傳與生成 playlist先GenerateHLSPlaylist生成 master playlistuploadID_master.m3u8內含各檔BANDWIDTH與RESOLUTION見 manifest/hls.go隨后啟動uploader異步上傳產物執行 ffmpeg 命令并行執行縮略圖命令與視頻轉碼命令詳見下節清理與元數據回寫上傳停止后清理臨時目錄若存儲支持MetaProvider則將hls源地址、縮略圖、寬高寫回源文件的元數據TaskResult{Playlist, Thumbnail, Width, Height}。ffmpeg 命令構建細節命令構建集中在 mediaconvert/command.go是理解轉碼質量與格式的關鍵公共參數buildCommonCommand-y覆蓋輸出、-err_detect ignore_err、-fflags discardcorrupt丟棄損壞幀、-threads MaxThreadCount限定線程數若輸入是 HTTP(S) URL還會追加-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5提升網絡穩定性HLS 參數buildHLSCommand-f hls、-hls_time 5每 5 秒一個分片、-hls_flags split_by_timetemp_file允許非關鍵幀切分、分片先寫臨時文件再原子改名避免播放器讀到半截分片、-hls_list_size 0不限制 playlist 中分片數量適用于 VOD、-hls_segment_filename命名規則為uploadID_序號_profile.ts視頻參數buildVideoCommand-map 0:v:0 -map 0:a?只取第一個視頻流與可選音頻流、-c:a音頻編碼、-c:v視頻編碼、-preset veryfastH.264 編碼速度檔位、-crf默認 23、-g 60關鍵幀間隔 60 幀便于 HLS 分片對齊當編碼非copy且需要縮放時追加-vf scale-2:height寬度自動取偶數縮略圖BuildThumbnailCommand-vframes 1 -update 1從輸入中提取首幀寫為uploadID.jpg。輸出產物以uploadID為目錄組織產物包括master playlistuploadID_master.m3u8manifest/hls.go各分辨率檔 playlistuploadID_profile.m3u8TS 分片uploadID_序號_profile.ts縮略圖uploadID.jpg。存儲側上傳時會按擴展名設置 Content-Type.ts→video/mp2t、.m3u8→video/x-mpegurl其余為application/octet-streamstorage/storage.go。存儲后端S3 與 Datalake 的統一抽象storage/storage.go 定義了統一的Storage接口PutFile/DeleteFile/GetFile/StatFile/SetParent以及可選的MetaProvider元數據讀寫與MultipartStorage分片上傳擴展接口。具體實現見 storage/s3.go 與 storage/datalake.goS3 模式需要AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY端點由STREAM_ENDPOINT_URL如s3://127.0.0.1:9000給出Datalake 模式使用datalake://前綴端點且每個請求都必須攜帶有效的token與workspace。Config.Endpoint()會自動把s3://host:port或datalake://host:port規范化為http(s)://host:port供存儲客戶端使用協議由Insecure決定。觀測與排障日志基于 zap 實現internal/pkg/log/zap.go級別由STREAM_LOG_LEVEL控制默認debug便于追蹤每個任務的分階段日志從 phase 1 到 phase 9 均有明確日志pprofSTREAM_PPROF_ENABLED默認 true時在localhost:6060啟動 Go 性能剖析服務可用go tool pprof排查內存與 CPU 熱點OpenTelemetry默認開啟 traces 與 metricsOTEL_TRACES_ENABLED/OTEL_METRICS_ENABLED默認 true轉碼、ffprobe、ffmpeg 執行均包有獨立 span可接入標準 OTel Collector 觀測全鏈路Sentry通過STREAM_SENTRY_DSN接入錯誤追蹤超時控制STREAM_TIMEOUT默認 5m同時作用于上傳網絡超時與流空閑超時StreamCoordinator.manageTimeout會在超時后自動Terminate并清理流。常見問題與最佳實踐本地快速聯調由于STREAM_ENDPOINT_URL默認指向s3://127.0.0.1:9000本地 MinIO 等 S3 兼容服務可以先本地起一個 S3 兼容存儲再以STREAM_INSECUREtrue啟動 Stream省去 token 與 TLS 配置生產環境必配密鑰只要STREAM_INSECUREfalseSTREAM_SERVER_SECRET缺省會導致啟動失敗這是服務自帶的安全護欄Datalake 模式鑒權調度接口需要Authorization: Bearer tokenTUS 上傳則依賴 metadata 中的token/workspace二者缺一不可并發與資源規劃STREAM_MAX_PARALLEL_SCALING_COUNT控制并行轉碼任務數STREAM_MAX_THREAD_COUNT控制單任務 ffmpeg 線程數二者共同決定 CPU 占用與隊列積壓情況需結合實例規格調整輸入格式注意雖然 README 列出mp4/webm源碼還額外允許video/quicktimemov并明確拒絕已封裝為 TS/MPEG-TS 的流避免重復轉碼所有任務最終統一輸出為 HLS。小結Stream 以上傳即轉碼的設計把 TUS 的斷點續傳能力與 ffmpeg 的轉碼能力、S3/Datalake 的存儲能力組合成一個獨立可部署的 HTTP 服務。通過 foundations/stream/README.md 提供的配置與接口說明結合 internal/pkg/config/config.go、api/v1/recording/handler.go、api/v1/transcoding/handler.go、mediaconvert/scheduler.go 等源碼即可完整復現其TUS 上傳 → 管道流轉碼 → HLS 產物 → 對象存儲的端到端鏈路并將其接入 Huly 的媒體處理流程中。【免費下載鏈接】platformHuly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)項目地址: https://gitcode.com/GitHub_Trending/platform80/platform創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考