日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

基于 Blazor 打造一款实时字幕

發布時間:2023/12/4 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于 Blazor 打造一款实时字幕 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

早先在錄制視頻的時候一直使用的是 obs-auto-subtitle 作為實時字幕展示功能。不過這個是以 OBS 插件的形式存在,不管是語言和功能上都有一定的限制。故而使用 Blazor server 實現一個。

總體思路

  • 實時字幕自然需要語音轉文字的功能。考察了一些服務之后,發現同時具備有一定免費額度和有 C# SDK 兩個條件的,就只有 Azure Cognitive Service 了。故而選擇了它。

  • 使用 Blazor server 從服務端實時刷新頁面到前端是非常簡單的事情。因此,渲染一個簡單的列表文本,然后通過 OBS 的 browser 組件接入畫面即可。

快樂編碼

有了基本的思路,我們就可以開始快樂的編碼了。

簡要設計

一般來說,語音轉文字服務是一個與服務端進行持續交互的過程。因此需要一個對象來保持和服務端之間的溝通。我們可以設計一個ILiveCaptioningProvider來表示這種行為:

using?System; using?System.Threading.Tasks;namespace?Newbe.LiveCaptioning.Services {public?interface?ILiveCaptioningProvider?:?IAsyncDisposable{Task?StartAsync();void?AddCallBack(Func<CaptionItem,?Task>?captionCallBack);} }

為了擴展可能適配不同提供商的可能,我們同樣設計一個ILiveCaptioningProviderFactory用于表現創建ILiveCaptioningProvider的行為:

namespace?Newbe.LiveCaptioning.Services {public?interface?ILiveCaptioningProviderFactory{ILiveCaptioningProvider?Create();} }

有了這樣兩個接口,在頁面上只要通過ILiveCaptioningProviderFactory創建ILiveCaptioningProvider,然后不斷的接收回調展示在頁面上即可。

將內容展示在頁面上

有了基本的項目結構和接口,便可以嘗試將內容綁定到頁面上。要將實時轉換的內容展示到界面上需要進行一定的算法轉換。

在此之前,我們需要確定一下頁面展示的預期:

  • 在頁面上展示至少兩行文本

  • 當一句話超過一行文本的寬度時自動進行換行

  • 當一句話結束時,下一句話自動換行

例如,上面這句話進行連續閱讀時,可能會出現如下效果:

https://www.newbe.pro/images/20210724-001.gif

live caption display

主要需要注意的是,在判斷是要更新當前行還是進行換行,這部分邏輯需要注意進行處理。

填充實現

  • 通過 Azure SDK 提供的SpeechRecognizer對象來進行語音識別

  • 通過 Subject 將事件轉換為一個簡單的可觀測流,簡化業務回調的處理

using?System; using?System.Collections.Generic; using?System.Linq; using?System.Reactive.Linq; using?System.Reactive.Subjects; using?System.Threading.Tasks; using?Microsoft.CognitiveServices.Speech; using?Microsoft.CognitiveServices.Speech.Audio; using?Microsoft.Extensions.Logging; using?Microsoft.Extensions.Options;namespace?Newbe.LiveCaptioning.Services {public?class?AzureLiveCaptioningProvider?:?ILiveCaptioningProvider{private?readonly?ILogger<AzureLiveCaptioningProvider>?_logger;private?readonly?IOptions<LiveCaptionOptions>?_options;private?AudioConfig?_audioConfig;private?SpeechRecognizer?_recognizer;private?readonly?List<Func<CaptionItem,?Task>>?_callbacks?=?new();private?Subject<CaptionItem>?_sub;public?AzureLiveCaptioningProvider(ILogger<AzureLiveCaptioningProvider>?logger,IOptions<LiveCaptionOptions>?options){_logger?=?logger;_options?=?options;}public?async?Task?StartAsync(){var?azureProviderOptions?=?_options.Value.Azure;var?speechConfig?=?SpeechConfig.FromSubscription(azureProviderOptions.Key,?azureProviderOptions.Region);speechConfig.SpeechRecognitionLanguage?=?azureProviderOptions.Language;_audioConfig?=?AudioConfig.FromDefaultMicrophoneInput();_recognizer?=?new?SpeechRecognizer(speechConfig,?_audioConfig);_sub?=?new?Subject<CaptionItem>();_sub.Select(item?=>?Observable.FromAsync(async?()?=>{try{await?Task.WhenAll(_callbacks.Select(f?=>?f.Invoke(item)));}catch?(Exception?e){_logger.LogError(e,?"failed?to?recognize");}})).Merge().Subscribe();_recognizer.Recognizing?+=?(sender,?args)?=>{_sub.OnNext(new?CaptionItem{Text?=?args.Result.Text,LineEnd?=?false});};_recognizer.Recognized?+=?(sender,?args)?=>{_sub.OnNext(new?CaptionItem{Text?=?args.Result.Text,LineEnd?=?true});};await?_recognizer.StartContinuousRecognitionAsync();}public?void?AddCallBack(Func<CaptionItem,?Task>?captionCallBack){_callbacks.Add(captionCallBack);}public?ValueTask?DisposeAsync(){_recognizer?.Dispose();_audioConfig?.Dispose();_sub?.Dispose();return?ValueTask.CompletedTask;}} }
  • 實現工廠的方式非常多,這里采用 Autofac 來協助完成對象的創建

using?Autofac; using?Microsoft.Extensions.Options;namespace?Newbe.LiveCaptioning.Services {public?class?LiveCaptioningProviderFactory?:?ILiveCaptioningProviderFactory{private?readonly?ILifetimeScope?_lifetimeScope;private?readonly?IOptions<LiveCaptionOptions>?_options;public?LiveCaptioningProviderFactory(ILifetimeScope?lifetimeScope,IOptions<LiveCaptionOptions>?options){_lifetimeScope?=?lifetimeScope;_options?=?options;}public?ILiveCaptioningProvider?Create(){var?liveCaptionProviderType?=?_options.Value.Provider;switch?(liveCaptionProviderType){case?LiveCaptionProviderType.Azure:var?liveCaptioningProvider?=?_lifetimeScope.Resolve<AzureLiveCaptioningProvider>();return?liveCaptioningProvider;default:throw?new?ProviderNotFoundException();}}} }
  • 對頁面邏輯進行填充,完成效果

using?System; using?System.Collections.Generic; using?System.Linq; using?System.Threading.Tasks; using?Microsoft.AspNetCore.Components; using?Microsoft.Extensions.Logging; using?Newbe.LiveCaptioning.Services;namespace?Newbe.LiveCaptioning.Pages {public?partial?class?Index?:?IAsyncDisposable{[Inject]?public?ILiveCaptioningProviderFactory?LiveCaptioningProviderFactory?{?get;?set;?}[Inject]?public?ILogger<Index>?Logger?{?get;?set;?}private?ILiveCaptioningProvider?_liveCaptioningProvider;private?readonly?List<CaptionDisplayItem>?_captionList?=?new();protected?override?async?Task?OnAfterRenderAsync(bool?firstRender){await?base.OnAfterRenderAsync(firstRender);if?(firstRender){_liveCaptioningProvider?=?LiveCaptioningProviderFactory.Create();_liveCaptioningProvider.AddCallBack(CaptionCallBack);await?_liveCaptioningProvider.StartAsync();}}private?int?maxCount?=?20;private?Task?CaptionCallBack(CaptionItem?arg){return?InvokeAsync(()?=>{Logger.LogDebug("Received:?{Text}",?arg.Text);var?last?=?_captionList.FirstOrDefault();var?newLine?=?false;var?text?=?arg.Text;var?skipPage?=?0;if?(arg.Text.Length?>?maxCount){skipPage?=?(int)?Math.Floor(text.Length?*?1.0?/?maxCount);text?=?arg.Text[(skipPage?*?maxCount)..];}if?(last?==?null?||?skipPage?>?last.TagCount){newLine?=?true;}if?(newLine?||?_captionList.Count?==?0){_captionList.Insert(0,?new?CaptionDisplayItem{Text?=?text,TagCount?=?arg.LineEnd???-1?:?skipPage});}else{_captionList[0].Text?=?text;if?(arg.LineEnd){_captionList[0].TagCount?=?-1;}}if?(_captionList.Count?>?4){_captionList.RemoveRange(4,?_captionList.Count?-?4);}StateHasChanged();});}private?record?CaptionDisplayItem{public?string?Text?{?get;?set;?}public?int?TagCount?{?get;?set;?}}public?async?ValueTask?DisposeAsync(){if?(_liveCaptioningProvider?!=?null){await?_liveCaptioningProvider.DisposeAsync();}}} }

通過以上核心的代碼,就可以完成從識別到展示相關的內容。

下載與安裝

在嘗試進行源碼了解之前,你可以通過以下步驟來初步體驗一下項目的效果。

首先,你可以從 Release 頁面下載和你操作系統對應的版本:

https://github.com/newbe36524/Newbe.LiveCaptioning/releases

release

然后,將這個軟件包解壓到預先創建好的文件夾。

unzip

接著,在 Azure Portal 中創建一個 Cognitive Services。

提示 1:語音轉文字每個月有 5 個小時的免費額度,可以參見

https://azure.microsoft.com/pricing/details/cognitive-services/speech-services/?WT.mc_id=DX-MVP-5003606

提示 2:你可以通過這個幫助來創建一個免費的 Azure 賬號,新賬號包含有 12 個月的免費大禮包,參見

https://docs.microsoft.com/en-us/dynamics-nav/how-to--sign-up-for-a-microsoft-azure-subscription?WT.mc_id=DX-MVP-5003606

create serviceregion and key

隨后,將生成好的 region 和 key 填入到 appsettings.Production.json 中。

記得同時修改 Language 選項,例如美式英語為 en-us,簡體中文為 zh-cn。你可以通過以下鏈接來查看所有支持的語言:

https://docs.microsoft.com/azure/cognitive-services/speech-service/language-support?WT.mc_id=DX-MVP-5003606

update appsettings.Production.json

繼而,啟動 Newbe.LiveCaptioning.exe,你可以看到如下這樣的提示信息,就說明一切已經正常。

region and key

最后,你可以使用瀏覽器打開http://localhost:5000,并對著你的話筒說話,這樣便可以實時產生字幕了。

live caption

在 OBS 中加入字幕

首先,打開你的 OBS,并添加一個 browser 組件。

add browser

在組件的 url 中填入 http://localhost:5000,并設置一個合適的寬度和高度。

add browser

對著你的話筒話說,字幕就出來了。

https://www.newbe.pro/images/20210725-010.gif

test

輔助資料

Azure Speech to Text

可以通過以下鏈接在初步體驗一下識別的效果:

https://azure.microsoft.com/services/cognitive-services/speech-to-text/?WT.mc_id=DX-MVP-5003606#overview

可以通過以下鏈接找到 C# SDK 的對接方案:

https://docs.microsoft.com/azure/cognitive-services/speech-service/get-started-speech-to-text?WT.mc_id=DX-MVP-5003606

Blazor server

可以通過以下鏈接來了解,如何通過服務端來推送 UI 變化到前端:

https://swimburger.net/blog/dotnet/pushing-ui-changes-from-blazor-server-to-browser-on-server-raised-events

可以通過以下鏈接來了解,如何在 UI 線程之外來出發 UI 變化(這不就是 winform 再現):

https://docs.microsoft.com/aspnet/core/blazor/components/rendering?view=aspnetcore-5.0&WT.mc_id=DX-MVP-5003606#receiving-a-call-from-something-external-to-the-blazor-rendering-and-event-handling-system

.Net core publish

通過這里了解如何將 dotnet core 程序發布為一個單文件應用

https://docs.microsoft.com/dotnet/core/deploying/single-file?WT.mc_id=DX-MVP-5003606

了解不同操作系統下發布使用的 RID

https://docs.microsoft.com/dotnet/core/rid-catalog?WT.mc_id=DX-MVP-5003606

Github

了解如何通過 github action 打包發布內容到 release 中:

https://github.com/gittools/gitreleasemanager

小結

這是一個非常簡單的項目應用,開發者可以通過該項目初步的了解 Blazor 的使用方法。你可以通過以下地址來獲取本項目的源代碼:

https://github.com/newbe36524/Newbe.LiveCaptioning

總結

以上是生活随笔為你收集整理的基于 Blazor 打造一款实时字幕的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。