最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

flutter使用tauri實現(xiàn)一個一鍵視頻轉(zhuǎn)4K軟件

 更新時間:2022年09月01日 16:27:33   作者:天平  
這篇文章主要為大家介紹了flutter使用tauri實現(xiàn)一個一鍵視頻轉(zhuǎn)4K軟件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

先說結(jié)論,tauri是一個非常優(yōu)秀的前端桌面開發(fā)框架,但是,rust門檻太高了。

一開始我是用electron來開發(fā)的,但是打包后發(fā)現(xiàn)軟件運行不是很流暢,有那么一點卡頓。于是為了所謂的性能,我嘗試用tauri來做我的軟件。在學了兩星期的rust后,我發(fā)現(xiàn)rust真的太難學了,最后硬是邊做邊查勉強做出來了。

軟件運行起來是比electron做的絲滑很多,但是寫rust真的是很痛苦,rust的寫法和其他語言是截然不同的,在不知道之前我是個rust吹,覺得rust就是牛逼,真的上手后發(fā)現(xiàn)rust的門檻真的太高了,各種逆天的寫法直接把我勸退,tauri無縫銜接前端真的又很爽。

如果golang也出一個和tauri一樣的桌面端框架,那么golang將會是未來開發(fā)中的不二語言。

開發(fā)原因

我平時喜歡看一些動漫視頻或者是收藏一些做得不錯的動漫MAD,但是有時候因為番劇出的年代久遠的問題,就算找最高清的資源,視頻也不過720P,又或者是在b站上看一些動漫MAD的時候,up主雖然用的轉(zhuǎn)場技巧比較不錯,但是使用的動漫素材的質(zhì)量比較差,十分可惜。

于是我想能不能做一個視頻轉(zhuǎn)4K的軟件?類似于修復視頻的功能。雖然網(wǎng)絡(luò)上的修復視頻軟件有很多了,但是效果還是達不到我的要求,于是說干就干。

工作原理

視頻其實就是一幀一幀的圖片組成,如果要把視頻轉(zhuǎn)成4K,那么只要把視頻分解成圖片,再將圖片轉(zhuǎn)4K圖片,最后將4K圖片合并成4K視頻就可以了。
于是我搜了一圈,了解到有Real-ESRGAN]這樣的一個將圖片轉(zhuǎn)成4K的軟件。并且里面也提供好了視頻轉(zhuǎn)4K的案例。

先用ffmpeg將視頻分解成圖片:

ffmpeg -i 原視頻 -qscale:v 1 -qmin 1 -qmax 1 -vsync 0 臨時圖片路徑/frame%08d.png

再用Real-ESRGAN將圖片轉(zhuǎn)4K圖片:

./realesrgan-ncnn-vulkan.exe -i 臨時圖片目錄 -o 4K圖片目錄 -n realesr-animevideov3 -s 2 -f jpg

最后查看原視頻的幀數(shù),然后用ffmpeg將4K圖片合成4K視頻:

ffmpeg -i 原視頻
ffmpeg -r 23.98 -i 4K圖片路徑/frame%08d.jpg -c:v libx264 -r 幀數(shù) -pix_fmt yuv420p 4K視頻

只不過這樣操作起來非常繁瑣,并且只能一個個轉(zhuǎn),不能批量操作,也不能看到進度。雖然可以寫一個cmd腳本批量操作,但是看不到進度,體驗不是很好的。于是說干就干,開發(fā)軟件!

開發(fā)過程

tauri提供了一些后端的操作權(quán)限給前端,也就是在前端就能完成讀寫文件,這就非常方便了!但是也是有一定限制的,比如要讀取任意文件,就要rust去操作。

前提工作先準備一個文件,導出一個數(shù)組,pids,以便關(guān)閉軟件時殺死所有windows進程。

export const pids: number[] = []

首先是創(chuàng)建3個文件夾,臨時圖片文件夾,圖片轉(zhuǎn)4K圖片文件夾,輸出視頻文件夾,用來存放輸出的資源的:

    await readDir(`${basePath.value}/img_temp`).catch(() => {
        createDir(`${basePath.value}/img_temp`)
    })
    await readDir(`${basePath.value}/img_out`).catch(() => {
        createDir(`${basePath.value}/img_out`)
    })
    await readDir(`${basePath.value}/output`).catch(() => {
        createDir(`${basePath.value}/output`)
    })

然后是選定一個input文件夾,然后讀取文件夾下面的視頻,是rust實現(xiàn):

fn read_dir_file(path: String) -> Vec<String> {
    let mut arr: Vec<String> = vec![];
    for item in read_dir(path).unwrap() {
        if !item.as_ref().unwrap().path().is_dir() {
            arr.push(item.unwrap().file_name().into_string().unwrap());
        }
    }
    return arr;
}

因為返回的是一個數(shù)組,前端獲取到之后,就遍歷去操作。但后面為了方便,我則是遍歷這個數(shù)組,然后創(chuàng)建子組件,傳入文件路徑,讓子組件去操作:

import { invoke } from '@tauri-apps/api/tauri'
const fileList = await invoke<string[]>('read_dir_file', { path: path.value })

首先還是遍歷創(chuàng)建子文件夾,方便管理:

    await readDir(`${props.basePath}/img_temp/${fileName}`).catch(() => {
        createDir(`${props.basePath}/img_temp/${fileName}`)
    })
    await readDir(`${props.basePath}/img_out/${fileName}`).catch(() => {
        createDir(`${props.basePath}/img_out/${fileName}`)
    })

接著調(diào)用tauri提供的shell指令: 不過在此之前,先要配置tauri.conf.json,讓tauri支持任意命令

"tauri": {
        "allowlist": {
            "shell": {
                "scope": [
                    {
                        "name": "ffmpeg",
                        "cmd": "cmd",
                        "args": ["/C", { "validator": "\\S+" }]
                    }
                ]
            },
        },
    }

然后先執(zhí)行讀取視頻的信息得到幀數(shù)和視頻的總秒數(shù),以便計算進度,并且把返回的pid存到數(shù)組中:

import { Command } from '@tauri-apps/api/shell'
const fps = ref('5')
const duration = ref('')
const cmd1 = `ffmpeg -i ${props.basePath}/input/${props.file}`
const command1 = new Command('ffmpeg', ['/C', cmd1])
const child1 = await command1.spawn()
    command1.stderr.on('data', (line) => {
        const fpsResult = line.match(/\w{2}\.?\w{0,2}(?= fps)/)
        /** 匹配視頻持續(xù)時間的信息 */
        const durationResult = line.match(/(?<=Duration: ).+(?=, start)/)
        if (fpsResult) {
            fps.value = fpsResult[0]
            console.log('fps', fps.value)
        }
        if (durationResult) {
            duration.value = durationResult[0]
            console.log('duration', duration.value)
        }
    })
    pids.push(child1.pid)

用正則匹配幀數(shù)和持續(xù)時間,存到變量中。在命令執(zhí)行完畢后,接著執(zhí)行將視頻分解圖片的任務(wù):

command1.on('close', async () => {
const cmd2 = `${props.ffmpegPath}  -i ${props.basePath}/input/${props.file} -qscale:v 1 -qmin 1 -qmax 1 -vsync 0 ${props.basePath}/img_temp/${fileName}/frame%08d.png`
    const command2 = new Command('ffmpeg', ['/C', cmd2])
    const child2 = await command2.spawn()
    pids.push(child2.pid)
})

至于監(jiān)聽進度,圖片的總數(shù)是可以通過幀數(shù)和視頻總秒數(shù)計算出來的,總秒數(shù)乘以幀數(shù),就是要轉(zhuǎn)換的圖片總數(shù)。由于得到的持續(xù)時間是'00:04:32'這種格式的,先寫一個函數(shù)將時間轉(zhuǎn)成秒數(shù):

/**
 * @description  將字符串的時間轉(zhuǎn)成總秒數(shù)的時間 00:04:35
 * @param time   字符串的時間
 * @returns      返回秒數(shù)的時間
 */
export function formatTime(time: string) {
    const hours = Number(time.split(':')[0])
    const mimutes = Number(time.split(':')[1])
    const seconds = Number(time.split(':')[2])
    return hours * 60 * 60 + mimutes * 60 + seconds
}

總圖片就可以計算出來了,然后在輸出時,使用節(jié)流,每隔1秒讀取一次該文件夾下面的圖片數(shù)量,則進度就是當前的圖片數(shù)量/圖片總數(shù)。

讀取文件數(shù)量需要rust操作"

fn read_dir_file_count(path: String) -> i32 {
    let dir = read_dir(path).unwrap();
    let mut count: i32 = 0;
    for _ in dir {
        count += 1;
    }
    return count;
}

則整體是:

const total = formatTime(duration.value) * Number(fps.value)
            command2.stderr.on('data', async (line) => {
                const current = await invoke<number>('read_dir_file_count', {
                    path: `${props.basePath}/img_temp/${fileName}`,
                })
                console.log(current, total)
                precent1.value = Math.round((current / total) * 100)
            })

precent1就是綁定的進度條的變量。

在任務(wù)關(guān)閉后,執(zhí)行優(yōu)化圖片的命令:

command2.on('close', async () => {
    const cmd3 = `${props.realesrgan} -i ${props.basePath}/img_temp/${fileName} -o ${props.basePath}/img_out/${fileName} -n realesr-animevideov3 -s 2 -f jpg`
    const command3 = new Command('ffmpeg', ['/C', cmd3])
    const child3 = await command3.spawn()
    pids.push(child3.pid)
})

監(jiān)聽轉(zhuǎn)換的進度仍是讀取文件夾下面當前的圖片數(shù)量,用節(jié)流函數(shù),優(yōu)化性能:

command3.stderr.on('data', throttle(fn, 2000))
                async function fn() {
                    const current = await invoke<number>('read_dir_file_count', {
                        path: `${props.basePath}/img_out/${fileName}`,
                    })
                    precent2.value = Math.round((current / total) * 100)
                    console.log(current, total, (current / total) * 100)
                    // console.log(line)
}

最后在命令完成后,執(zhí)行4K圖片轉(zhuǎn)4K視頻的命令:

command3.on('close', async () => {
const cmd4 = `${props.ffmpegPath}  -r ${fps.value} -i  ${props.basePath}/img_out/${fileName}/frame%08d.jpg -i  ${props.basePath}/input/${props.file} -map 0:v:0 -map 1:a:0 -c:a copy -c:v ${props.model} -r ${fps.value} -pix_fmt yuv420p ${props.basePath}/output/${props.file}`
const command4 = new Command('ffmpeg', ['/C', cmd4])
const child4 = await command4.spawn()
pids.push(child4.pid)
})

監(jiān)聽進度此時則是去獲取stderr輸出的信息,然后匹配到當前轉(zhuǎn)換的時間,再除以總時間

const total = formatTime(duration.value)
command4.stderr.on('data', throttle(fn, 200))
                    async function fn(data: string) {
                        /** 控制臺的信息 */
                        const result = data.match(/(?<=time=).+(?= bitrate)/)
                        if (result) {
                            const current = formatTime(result[0])
                            console.log(current, total)
                            precent3.value = Math.round((current / total) * 100)
                        }
                    }

最后,如果關(guān)閉軟件時,則是先把所有的任務(wù)都殺死,再關(guān)閉:

async function closeApp() {
    await Promise.all(
        pids.map(async (pid) => {
            return new Promise((resolve) => {
                const cmd = `taskkill /f /t /pid ${pid}`
                const command = new Command('ffmpeg', ['/C', cmd])
                command.spawn()
                command.on('close', () => {
                    resolve(0)
                })
            })
        })
    )
    appWindow.close()
}

以下是我的演示視頻,不過文件有點大

www.bilibili.com/video/BV1EW…

這是我的項目地址:github.com/Minori-ty/m…

軟件也已經(jīng)打包好了,開箱即用github.com/Minori-ty/m…

以上就是flutter使用tauri實現(xiàn)一個一鍵視頻轉(zhuǎn)4K軟件的詳細內(nèi)容,更多關(guān)于flutter tauri視頻轉(zhuǎn)4K軟件的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue3時間軸組件問題記錄(時間信息收集組件)

    Vue3時間軸組件問題記錄(時間信息收集組件)

    本文介紹了如何在Vue3項目中封裝一個時間信息收集組件,采用雙向綁定響應(yīng)式數(shù)據(jù),通過對Element-Plus的Slider組件二次封裝,實現(xiàn)時間軸功能,解決了小數(shù)計算導致匹配問題和v-model綁定組件無效問題,感興趣的朋友跟隨小編一起看看吧
    2024-09-09
  • 全站最詳細的Vuex教程

    全站最詳細的Vuex教程

    vuex是一個專門為vue.js設(shè)計的集中式狀態(tài)管理架構(gòu)。這篇文章主要介紹了全站最詳細的Vuex教程,需要的朋友可以參考下
    2018-04-04
  • 淺談Vue頁面級緩存解決方案feb-alive(上)

    淺談Vue頁面級緩存解決方案feb-alive(上)

    這篇文章主要介紹了淺談Vue頁面級緩存解決方案feb-alive(上),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • vue.js項目打包上線全流程

    vue.js項目打包上線全流程

    這篇文章主要介紹了vue.js項目打包上線全流程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • vue?首頁加載,速度優(yōu)化及解決首頁白屏的問題

    vue?首頁加載,速度優(yōu)化及解決首頁白屏的問題

    這篇文章主要介紹了vue?首頁加載,速度優(yōu)化及解決首頁白屏的問題,具有很好的參考價值,希望對大家有所幫助。
    2022-10-10
  • vue項目刷新當前頁面的三種方法

    vue項目刷新當前頁面的三種方法

    這篇文章主要介紹了vue項目刷新當前頁面的三種方法,本文圖文并茂給大家介紹的非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-12-12
  • 案例實操vue事件修飾符帶你快速了解與應(yīng)用

    案例實操vue事件修飾符帶你快速了解與應(yīng)用

    這篇文章主要介紹了vue常見的事件修飾符,在平時無論是面試還是學習工作中都會經(jīng)常遇到的,本文就帶你快速上手,需要的朋友可以參考下
    2023-03-03
  • 詳解Vue中l(wèi)ocalstorage和sessionstorage的使用

    詳解Vue中l(wèi)ocalstorage和sessionstorage的使用

    這篇文章主要介紹了詳解Vue中l(wèi)ocalstorage和sessionstorage的使用方法和經(jīng)驗心得,有需要的朋友跟著小編參考學習下吧。
    2017-12-12
  • vue父組件監(jiān)聽子組件數(shù)據(jù)更新方式(hook)

    vue父組件監(jiān)聽子組件數(shù)據(jù)更新方式(hook)

    這篇文章主要介紹了vue父組件監(jiān)聽子組件數(shù)據(jù)更新方式(hook),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • VueCli4項目配置反向代理proxy的方法步驟

    VueCli4項目配置反向代理proxy的方法步驟

    這篇文章主要介紹了VueCli4項目配置反向代理proxy的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05

最新評論

银川市| 赤水市| 高阳县| 方正县| 新津县| 屯留县| 西青区| 辰溪县| 阿城市| 平阴县| 平阴县| 平顶山市| 奉新县| 洪泽县| 巴东县| 阳泉市| 梁山县| 麦盖提县| 丰原市| 嵊州市| 盈江县| 新沂市| 彭水| 黎川县| 文水县| 南宫市| 宁强县| 同仁县| 贺兰县| 潢川县| 宁远县| 霍林郭勒市| 犍为县| 浮山县| 昂仁县| 乌什县| 定陶县| 蒲城县| 凌云县| 鞍山市| 田阳县|