採編式AIGC視訊生產流程編排實踐

語言: CN / TW / HK

作者 | 百度人工智慧創作團隊

導讀

本文從業務出發,系統介紹了採編式 TTV的實現邏輯和實現路徑。結合業務拆解,實現了一個輕量級服務編排引擎,有效實現業務訴求、高效支援業務擴充套件。

全文6451字,預計閱讀時間17分鐘。

01 背景

近年來,內容視訊化趨勢仍在持續,短視訊的市場規模持續增長,2022年8月CNNIC釋出的資料顯示,截至2022年6月,我國網民規模為10.51億,佔網民整體的91.5%。隨著大量短視訊內容充斥網路,提高視訊生產效率和效果的半智慧化、輔助創作工具如視訊剪輯、視訊美化等如雨後春筍般湧現,視訊生產形態不斷升級。百家號作為百度為內容創作者打造的內容生產平臺,在內容生產方面深耕多年,如能利用百度強大的 AI 能力,以當前百家號圖文內容為指令碼,實現視訊智慧化自動、半自動生產,將會進一步降低視訊創作者的創作成本,帶來視訊創作的進一步發展。

自 AIGC 專案啟動之後,我們對視訊自動生產方案進行了一系列摸索試驗,最終沉澱出一套完整的解決方案——採編式視訊自動生產。該方案基於一系列微服務的配合執行,如何高效、穩定地完成整個流程的組織與排程是其中一個重要的課題。另外,在早期,整個專案的迭代非常迅速,業務發展變化比較大,如何較好地支援系統擴充套件與升級,也是我們關注的重點。本文將系統介紹採編式 AIGC 視訊生產流程的實現方案。

02 採編式視訊生產

所謂的採編式視訊生產,顧名思義,即基於圖文,進行相關視訊和圖片素材的補充和新增。由圖文到視訊的過程,看似簡單,但作為完全不同的兩種內容形態,這其中還有許多工作要做,按照一般處理方法,主要有以下內容:

  • 文字處理:由於整個視訊是用圖文做指令碼來完成的,所以,視訊主體抽取(這個視訊講述的是什麼內容)、視訊調性確認(陽春白雪還是下里巴人)、視訊字幕/旁白生成等,都需要基於充分的內容理解,再進行精準的文章主體識別、文章風格識別、口播逐字稿改寫、字幕拆分等工作的進行;

  • 素材處理:採編式視訊生產的核心,是要將碎片化的素材基於圖文指令碼進行合理的編排,故而進行視訊和圖片素材的線上檢索、剪裁、清洗等必不可少;

  • 語音處理:語音播報作為視訊的關鍵元素,在視訊生產中是必不可少的一環,需要基於圖文進行合理的語音合成與新增;

  • 其他視訊元素新增:視訊標註、水印、動效、背景音樂、背景視訊、前置氛圍渲染等元素的新增,能夠更好地豐富視訊效果;

  • 視訊合成:將採編好的指令碼檔案,利用視訊合成技術進行視訊渲染輸出。

圖片

△圖1 採編式視訊生產

如圖1,不同於一般的業務流程,採編式視訊生產需要基於大量的媒體資料處理,整個處理過程是無人工干預的全自動化過程,如何將這些服務進行有效地編排與排程,是整個視訊生產的關鍵問題。

03 服務編排常見方案

3.1『狀態機』流程排程

常見的服務編排,一般都採取利用定時任務、訊息佇列、持久化儲存等工具進行微服務的拼接串聯。這個方案需要在流程中定義關鍵的狀態節點,來標記每個微服務的執行狀態,並將狀態記錄到 MySQL 等 持久化儲存中,再通過定時任務或者訊息佇列來驅動整個流程的流轉。

圖片

△圖2 狀態機流程排程 可以看到,該方案是一個可控性較高的流程編排與排程的方案,整個系統的複雜度、穩定性與業務複雜度、系統設計合理性息息相關,更適合一些變動較大、相對輕量級的業務。

3.2 服務編排引擎

隨著網際網路技術的不斷髮展、微服務的普及,服務編排的解決方案也日益成熟,湧現出一批成熟優秀的服務編排引擎。業內比較成熟的服務編排引擎有 Cadence、Temporal、Conductor等。

服務編排引擎會進行基礎的流程、任務、節點等基礎元素的定義,提供流程啟動、任務排程、狀態監控等基礎能力,具備對於編排完成的服務或者流程在執行時進行動態、端到端視覺化監控的能力。以 Cadence 程式設計模型為例說明一般編排引擎的程式設計模型:

圖片

△圖3. Cadence程式設計模型 服務編排引擎一般都有一箇中央排程系統,同時提供一些外部可調 api,開發人員只需要通過對框架能力的呼叫來實現業務邏輯而不需要關注系統的排程執行,甚至包括系統的超時處理、失敗重試、異常兜底,框架都會代為處理,提升業務研發的效率。相應地,成熟的框架都有一定的接入門檻和運維成本,比較適合大型專案。

04 採編式AIGC 視訊生產流程編排實踐

由於 AIGC 視訊生產業務發展迅速,迭代速度非常快,對成熟流程排程框架的調研中,遇到了系統利用率低、問題追查成本高的問題,為了快速支援業務、保障系統的穩定性與可用性,我們謹慎地選擇了基於狀態的流程排程方案,並在此基礎上參考流程編排框架的思想,建設一套底層中央編排器,驅動上層微服務的執行。整體思想可以概括為:

  1. 從上而下地,基於功能對整個流程進行模組拆分、基於實現對模組進行元件拆分,對模組進行狀態管理、對元件進行位值管理

  2. 利用訊息佇列實現流程串聯,通過對狀態與位值的判斷實現流程排程

  3. 通過對模組與元件的組合配置實現流程組織

4.1 模組與元件的拆分與管理

首先基於對需求的理解,對整個編排流程進行了模組拆分,並對每一個模組進行相關的狀態賦值,拆分的模組有:

  • 圖文接入模組:接入上層業務或者外部業務的文字內容輸入,進行基礎的資料解析、校驗、打平與過濾功能;

  • 指令碼編排模組:實現從圖文到視訊指令碼的生成功能,該模組的輸入為圖文,輸出為編排好的視訊指令碼,包含三條時間軸:①素材軸②文字與語音軸③掛件軸,定義了視訊任意一個時間點對應的文字、素材與相關掛件。視訊指令碼不僅可以用於視訊渲染,還可輸出給使用者作為視訊編輯的草稿;

  • 視訊合成模組:實現從視訊指令碼到視訊檔案的生成,該模組執行完成之後就已經產生了可播放的視訊檔案,標誌著視訊生成完成;

  • 視訊輸出模組:將視訊檔案按照業務需求輸出,包括但不限於釋出到百家號、回傳業務方等。

在整個生產流程中,完成一個視訊的生產,所需要的功能模組是固定的,但是實現的方式與方法可能會持續地擴充套件與迭代,為了便於後續狀態的管理與功能的擴充套件,採取了大的功能模組包含小的功能元件的方式,這種方式的優點有二:

  • 方便資料輸出:在指令碼編排模組完成之後進行視訊指令碼的輸出並提供給多個業務方使用,無論模組內部如何擴充套件,指令碼輸出的時機是固定的,視訊檔案的輸出同理;

  • 方便功能擴充套件:隨著業務的發展,功能實現的方案升級甚至替換是不可避免的,模組內部提供原子化功能元件,可以方便地進行單功能的升級迭代或者新增,而不影響整體其他元件

為了方便微服務的呼叫狀態管理,我們又為每個微服務賦予了位值,所謂位值是當前元件在一個64位整數所處的二進位制位次,每個元件佔據兩位,列舉標記成功和失敗狀態,我們只需要校驗對應位次的值,即可判斷當前元件的呼叫狀態與返回狀態。

圖片

△圖4 模組與元件拆分

至此,我們通過『狀態』實現了對整個生產流程模組的管理,又通過『呼叫位』、『返回位』實現了對具體元件的管理。其中,狀態管理較好理解,主要是通過持久化儲存一個狀態欄位,來標記當前流程所處模組,如圖3所示,當某一條視訊生成任務狀態值為INIT時即可知當前任務處於視訊指令碼編排模組,但是具體在執行哪個或者哪些微服務呢?如上文所言是通過位值來確認的,對於位值的應用相對較為複雜,下面我們就詳細闡述一下位值的應用。

圖片

△圖5槽位值原理示意圖

如圖5所示,『呼叫位』、『返回位』都是一個 UINT64整數,每兩位組合可以有4個狀態,我們取前三個狀態進行呼叫或返回狀態的表示。每一個元件在註冊進入系統時,都會先分配一個位次(如圖3所示,1即表示佔據槽位值的低兩位),如此一來,某個元件狀態發生變更時通過二進位制操作修改對應二進位制位的值即可。

該方案的優點是能夠通過一個整形值管理32個元件的請求或返回狀態,且每個元件的狀態修改互不影響。當然這也帶來一個問題,即該方案最多隻能管理32個元件,更多元件需要管理時就要擴充套件欄位或者採取其他方案,同時雖然變更某個元件槽位值不影響其他元件,但當出現服務並行需要將修改後的槽位值更新儲存時,需要確保更新的事務性,這個問題的解決我們會在後面的流程排程中完成。

4.2 流程配置

在完成了元件與模組的拆分與確定之後,即可根據業務邏輯,基於元件之間的相互依賴關係進行流程編排配置。流程搭建採取配置化、插拔式方案,將業務所需元件放進對應模組,編排出所需的視訊生產流程,如圖5所示為當前採編式 AIGC 視訊生產流程的流程圖,在當前業務狀態下,存在相互依賴關係的元件如圖文理解、外掛選擇、文字處理在整個流程中序列執行,有相同前置依賴但彼此不依賴的元件如素材生成、素材檢索、語音合成則應該並行執行:

圖片

△圖6 採編式 AIGC 視訊生產流程

如要實現一個任務流程,按照上述流程圖執行,那麼首先需要有這樣一個流程描述檔案,該檔案按照一定的規則組織,包含一個流程完成所需的所有元件,並能夠準確描述這些元件的執行順序與相互依賴關係,在此基礎上,如能描述當前元件所處模組、狀態,那麼對於流程理解以及後續流程執行都有很大助益。基於以上考慮,我們採取以元件為最小單位,組合生成配置檔案:

{
    ……
    { // 指令碼編排模組
        "module_name":"ScriptAssign",
        "status":"init",
        "next_status":"generating",
        "components":[
            ……
            { 
            "component_name" : "TextProcessor",          // 元件名稱,文字處理元件
            "slot_index":2,                              // 元件位次,第三位(index從0開始),表示低第五六兩個二進位制位
            "slot_num_success": 16,                      // 2^(2*slot_index) 成功時,要將『低第五位』置為1,同時確保『低第六位』為0,具體在進行位置計算時實現
            "slot_num_fail":32,                          // 2^(2*slot_index+1) 失敗時,要將『低第六位』置為1,同時確保『低第五位』為0
            "depends":["TextUnderstanding","WidgetInit"] // 文字處理元件執行,依賴文字理解與外掛選擇元件執行完成
            },
            ……
            { 
            "component_name" : "FootageGenerator", // 素材生成元件
            "slot_index":3, 
            "slot_num_success":64,
            "slot_num_fail":128,
            "depends":["TextUnderstanding","WidgetInit","TextProcessor"] // 依賴前面三個元件
            },
            { 
            "component_name" : "MaterialSearch", // 素材檢索元件
            "slot_index":4, 
            "slot_num_success":256,
            "slot_num_fail":512,
            "depends":["TextUnderstanding","WidgetInit","TextProcessor"] // 也只依賴前面三個元件
            },
            ……
        ]
    },
    { // 視訊生成模組
        "module_name":"VideoGenerator",
        "status":"generating",
        "next_status":"draft",
        "components":[
            { 
            "component_name" : "VideoRender",  
            "slot_index":7,
            "slot_num_success": "2^14",// 2的14次方
            "slot_num_fail":"2^15",// 2的15次方
            "depends":[""] //  在當前模組內,沒有前置依賴
            }
        ]
    }
    ……
}

流程描述檔案的組織邏輯為:

  1. 基本描述單元為元件,說明元件在流程中所在位次與對應的槽位值、元件執行的前置依賴元件

  2. 每個元件只關注自身執行所需關鍵資訊,不關注其他元件的執行邏輯

  3. 在同一個模組內的元件,組合成為模組單元,模組單元關注當前模組狀態,以及當前模組執行完畢之後的下一個狀態

  4. 所有模組按照執行順序(因為模組是絕對序列的)組織成完整流程描述檔案

後續的整體流程排程,將以該檔案為藍本執行。同時,可以看到,一個描述檔案即規定了一個流程,如果我們有不同的業務場景需要不同的執行流程,那麼只需要再編排一個流程排程檔案即可,事實上,我們的AIGC 業務也確實存在多條流程,整體編排邏輯同理,不多贅述。

4.3 流程排程

服務編排框架的核心,是流程排程部分,該部分負責維持與推動資料流的運轉。如上文所述,每個元件的狀態都通過相對應的位值來維護,流程排程的關鍵就在於對位值的管理。整體流程如圖4所示,整個流程排程通過訊息佇列串聯,主要操作步驟如下:

①任務建立:該步驟在一個任務執行全流程中只執行一次,主要在前置的引數檢查校驗工作完成之後,進行資料的入庫操作,並將任務下發流程排程訊息佇列,觸發整體流程。

②查詢可執行元件並執行:該步驟在一個任務執行全流程中會執行多次,在正常情況下,與元件個數等同。該步驟主要負責從訊息佇列中拉取資料,遍歷流程描述檔案,通過計算當前任務的呼叫/返回槽位值,推算出各個元件執行狀態,若某個元件未執行、且其依賴的前置元件已執行完畢,則將該元件加入執行佇列;若未找到可執行元件,則本次不執行。在這一步中,若元件內部存在非同步微服務,則僅作微服務觸發,若為同步元件,則會在執行完畢之後,將任務再次加入流程排程訊息佇列。

③非同步回撥:我們大部分元件都是非同步微服務,故而在第二步中觸發微服務呼叫之後,這一環節主要功能是接收微服務回撥,並做相關後置業務處理,處理完成之後,再將任務再次加入流程排程訊息佇列。

圖片

△圖7 任務排程流程圖

在這個流程裡我們通過訊息佇列的排程解耦了元件之間的相互依賴,僅通過槽位值查詢與校驗來實現流程的流轉與執行,這使得系統具備了元件的併發性,只要定義好每個元件執行的前置依賴,那麼當一個元件執行完成之後所有依賴這個元件的後置元件都可以開始執行。那麼,這時候會出現另外一個問題,我們如何保證並行執行完成之後的槽位值更新不彼此覆蓋?如果兩個元件同時執行完成,但每個元件只會計算並修改自身槽位值,如何保證後更新的槽位值不覆蓋前一個元件的槽位值?這個問題的解決我們是通過利用訊息佇列的重試做後置更新結合更新鎖來完成的:在每個元件執行完成之後只會更新自身涉及的業務欄位,而不更新狀態及槽位值,狀態管理的三個值是在步驟二中前置執行的,每次從訊息佇列中拉取一個任務後會先進性狀態的檢查和槽位值的更新,更新前會先加唯一鎖,若加鎖失敗則可能其他元件正在做狀態更新,則退出執行,該任務依然在訊息佇列裡未消費,待下一次繼續執行。

05 總結

採編式 AIGC 視訊生產流程2022年5月上線以來,已經根據不同的業務場景,通過對基礎模組和元件的組合配置建設起5條不同的生產流程,很好地支援萬級日產的業務發展。隨著業務的迭代深入,相關元件的功能及程式碼量都在日益膨脹,我們成功地在當前框架下進行元件的拆分與擴充套件,在不觸動底層排程框架基礎上,安全高效地完成了元件的擴充套件。雖然當前框架對目前的業務支援良好,但是整個流程的優化和迭代還在繼續,對成熟服務編排引擎的調研也在繼續,希望後續在借鑑成熟框架的基礎上,能夠沉澱出更為穩定高效的視訊生產流程。

——END——

參考資料:

[1]http://cadenceworkflow.io/docs/get-started/

[2]http://docs.temporal.io/temporal/

[3]http://conductor.netflix.com/architecture/overview.html

[4]http://netflixtechblog.com/netflix-conductor-a-microservices-orchestrator-2e8d4771bf40

推薦閱讀

百度工程師漫談視訊理解

百度工程師帶你瞭解Module Federation

巧用Golang泛型,簡化程式碼編寫

Go語言DDD實戰初級篇

Diffie-Hellman金鑰協商演算法探究

貼吧低程式碼高效能規則引擎設計