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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

FFmpeg优化 苏宁PP体育视频剪切效率提升技巧

發(fā)布時(shí)間:2024/4/11 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 FFmpeg优化 苏宁PP体育视频剪切效率提升技巧 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


FFmpeg功能強(qiáng)大,社區(qū)活躍,在多媒體處理業(yè)務(wù)中扮演著不可或缺的角色。但沒有優(yōu)化過的FFmpeg在生產(chǎn)環(huán)境下有很多性能瓶頸,因此對其進(jìn)行優(yōu)化勢在必行。蘇寧旗下PP體育音視頻技術(shù)負(fù)責(zé)人田釗撰文分享了團(tuán)隊(duì)在處理海量視頻切割過程中遇到的挑戰(zhàn)及優(yōu)化方法。感謝OnVideo視頻創(chuàng)作云平臺(tái)聯(lián)合創(chuàng)始人、FFmpeg Maintainer劉歧對本文的技術(shù)審校。


文 / 田釗

審校 / 劉歧


一、前言


蘇寧旗下PP體育所在的直播行業(yè),每天有無數(shù)視頻原始數(shù)據(jù)需要進(jìn)行分類存儲(chǔ)、渲染處理。處理這些視頻,一個(gè)很重要的方面,就是要將長時(shí)段的直播視頻切割成不定時(shí)長,不定畫面組的短視頻,以匹配現(xiàn)代用戶碎片化的消費(fèi)時(shí)間。尤其是體育賽事直播行業(yè),在直播前的墊場片花、直播中的即時(shí)快看、直播后的全場集錦和精華鏡頭,都需要對大量的視頻作剪切/壓制處理。而且因?yàn)轶w育賽事直播行業(yè)的特殊性,對于直播中和直播后的精彩鏡頭,集錦類視頻片段,要求必須能及時(shí)處理視頻,并發(fā)布到用戶端。這對視頻的處理效率提出了非常高的要求。


在PP體育,我們在使用與業(yè)界同樣高效的設(shè)計(jì)模式和優(yōu)化方案的同時(shí),另外嘗試了換一種角度來思考這個(gè)問題,并進(jìn)行了實(shí)踐。下面我們來針對這部分的構(gòu)思和實(shí)踐中碰到的問題,來做個(gè)分享。


二、背景基礎(chǔ)知識(shí)


先簡單說一下我們對視頻在數(shù)據(jù)層面上的理解。對于視頻來說,無論是何種編碼,何種封裝格式,拆分開看,都是由音頻流和視頻流來組合而成的。從數(shù)據(jù)的最低層級(jí)往上推,會(huì)發(fā)現(xiàn)一個(gè)視頻文件會(huì)由以下幾個(gè)層面的數(shù)據(jù)組成。



1. 第一層是亂序的二進(jìn)制數(shù)據(jù)層。基本看不出來是啥數(shù)據(jù)。

2. 第二層是未經(jīng)編碼的音視頻數(shù)據(jù)層。這里就有了數(shù)據(jù)源出來的原始音頻、視頻等數(shù)據(jù)。原始音視頻流數(shù)據(jù)量很大。

3. 第三層是編碼數(shù)據(jù)層。通常音頻使用AAC編碼,視頻使用H.264/265編碼后,音視頻流數(shù)據(jù)量就已經(jīng)比較小了。

4. 第四層是封裝層。將編碼后的音視頻數(shù)據(jù)”打包“封裝成不同的封裝格式。這里就是我們通常所看到的.ts/.mp4/.flv/.mkv等視頻文件。這些文件里封裝著M路編碼的視頻流和N路編碼的音頻流。當(dāng)然也可以有其它的數(shù)據(jù)流,如字幕流,附加信息流等。


三、常規(guī)做法簡述


視頻的切割/轉(zhuǎn)碼/壓制,目前業(yè)界通常的處理方式是在云端服務(wù)器,直接通過云轉(zhuǎn)碼模塊集成的視頻剪切服務(wù)來處理。通常使用FFmpeg套件改造而成。而且部分視頻云服務(wù)廠商為提升轉(zhuǎn)碼效率,會(huì)用到云端轉(zhuǎn)碼集群。通過將完整的長段視頻先進(jìn)行切割,再將切割完的小段視頻再通過分布式集群進(jìn)行轉(zhuǎn)碼,合并,壓制操作。其中,轉(zhuǎn)碼壓制部分,由蘇寧視頻云服務(wù)提供的業(yè)界領(lǐng)先的分布式轉(zhuǎn)碼集群來完成。基礎(chǔ)的轉(zhuǎn)碼業(yè)務(wù)圖如下:



其中,轉(zhuǎn)碼部分,多數(shù)視頻云服務(wù)廠商采用了分布式轉(zhuǎn)碼服務(wù),來進(jìn)行效率優(yōu)化的提升。對于切割部分,卻不一定重視。部分方案會(huì)和轉(zhuǎn)碼模塊合并到一起,也有的廠商兩樣將分析視頻的結(jié)果列表,也利用服務(wù)器集群來進(jìn)行并發(fā)的切割操作。通常這種方案會(huì)直接使用FFmpeg套件來完成切割的動(dòng)作。所以,對視頻云廠商來說,FFmpeg套件切割視頻功能的優(yōu)化是提升切割效率的核心。各大廠商的業(yè)界大牛們?yōu)榇俗隽瞬煌膰L試,也取得了不錯(cuò)的效果。


典型的切割服務(wù),多在音視頻分層圖的第三層作數(shù)據(jù)拷貝處理,典型如下列指令:

ffmpeg?-ss?00:10:24?-i?input.mp4?-vcodec?copy?-acodec?copy?-t?00:95:27?output.mp4

此切割指令使用FFmpeg套件對視頻數(shù)據(jù)中的音視頻,按音視頻幀級(jí)數(shù)據(jù)包直接拷貝來處理。此種方式有優(yōu)點(diǎn)也有缺陷。


缺點(diǎn)在于:經(jīng)常會(huì)有比較明顯的視頻切割誤差。因?yàn)橐曨lGOP長度因素存在,經(jīng)常會(huì)出現(xiàn)起始點(diǎn)視頻幀并非關(guān)鍵幀。而FFmpeg切割程序代碼需要找到切割起始點(diǎn)的視頻關(guān)鍵幀,才能正常完成視頻幀層面的切割動(dòng)作。所以FFmpeg程序會(huì)計(jì)算查找當(dāng)前視頻幀的GOP關(guān)鍵幀后,再以此GOP關(guān)鍵幀為起始點(diǎn)來作為切割起始點(diǎn)。此種方式下會(huì)導(dǎo)致真實(shí)切割點(diǎn)與原始需求切割點(diǎn)是不一致的情況。導(dǎo)致切割出來的視頻起止點(diǎn)并不精確。


優(yōu)點(diǎn)也很明顯:因?yàn)椴粚σ丫幋a的音視頻數(shù)據(jù)進(jìn)行解碼再編碼的操作,所以效率已經(jīng)非常不錯(cuò)。并且在此基礎(chǔ)上,進(jìn)一步的優(yōu)化方案,可以將FFmpeg套件按多進(jìn)程模型來使用,利用服務(wù)器的多核性能來并行調(diào)用多個(gè)FFmpeg進(jìn)程進(jìn)行多路切割操作,縮短總體切割時(shí)間,以提升切割性能;再利用服務(wù)器集群,進(jìn)行多服務(wù)器規(guī)模并行處理,進(jìn)一步提高切割效率。


四、優(yōu)化方法與實(shí)踐


我們的優(yōu)化做法,與上述情況在原理上是一致的,但是在細(xì)節(jié)上有做了微創(chuàng)新。


首先,我們沒用使用FFmpeg套件來做核心切割功能服務(wù)。如上所述,業(yè)界通常利用FFmpeg套件切割視頻文件時(shí),是在視頻分層圖的第三層編碼數(shù)據(jù)層對視頻文件按”幀“級(jí)數(shù)據(jù)作拷貝處理。我們對生產(chǎn)環(huán)境及直播鏈路進(jìn)行梳理后發(fā)現(xiàn),視頻的數(shù)據(jù)封裝格式基本只有MP4/FLV/TS三種。而此三種封裝格式里,除MP4封裝稍復(fù)雜外,FLV/TS的封裝相對容易分析處理。所以我們大膽地嘗試了在視頻分層圖的第四層——封裝層做分析處理。將視頻切割動(dòng)作分解為對封裝數(shù)據(jù)的切分。


1. 分析視頻封裝里的詳細(xì)描述信息;

2. 根據(jù)封裝詳細(xì)描述信息,對起止切割點(diǎn)進(jìn)行計(jì)算;

3. 找到切割點(diǎn)二進(jìn)制數(shù)據(jù)起止點(diǎn);

4. 復(fù)制出起止點(diǎn)間二進(jìn)制數(shù)據(jù);

5. 重新描述起止切割點(diǎn)的封裝信息,并與復(fù)制出的二進(jìn)制數(shù)據(jù)進(jìn)行拼合。


上述操作完成后,最終得到切割后的視頻。這種操作方法,實(shí)際是將視頻文件分解為兩層,封裝層和二進(jìn)制數(shù)據(jù)層。切割工具從封裝層得到描述信息后,對視頻數(shù)據(jù)進(jìn)行最底層的二進(jìn)制數(shù)據(jù)拷貝,其中不涉及任何幀的處理。切割起始點(diǎn)與終止點(diǎn)的計(jì)算,以及拷貝數(shù)據(jù)拼合成新的視頻,是這里的關(guān)鍵。典型代碼片段如下:?


func?CopyStructureData(src?*demux.VideoStructure,?dst?*demux.VideoStructure)?{?
????copy(dst.FTYP.CompatibleBrands,?src.FTYP.CompatibleBrands)?

????copy(dst.MOOV.MVHD.Flags,?src.MOOV.MVHD.Flags)?

????copy(dst.MOOV.MVHD.Reserved,?src.MOOV.MVHD.Reserved)?

????copy(dst.MOOV.MVHD.Matrix,?src.MOOV.MVHD.Matrix)?

????copy(dst.MOOV.MVHD.PreDefined,?src.MOOV.MVHD.PreDefined)?

????for?i?:=?0;?i?<?len(src.MOOV.TRAK);?i?++?{?

????????copy(dst.MOOV.TRAK[i].TKHD.Flags,?src.MOOV.TRAK[i].TKHD.Flags)?

????????copy(dst.MOOV.TRAK[i].TKHD.Reserved1,?src.MOOV.TRAK[i].TKHD.Reserved1)?

????????copy(dst.MOOV.TRAK[i].TKHD.Reserved2,?src.MOOV.TRAK[i].TKHD.Reserved2)?

????????copy(dst.MOOV.TRAK[i].TKHD.Reserved3,?src.MOOV.TRAK[i].TKHD.Reserved3)?

????????copy(dst.MOOV.TRAK[i].TKHD.Matrix,?src.MOOV.TRAK[i].TKHD.Matrix)?

????????copy(dst.MOOV.TRAK[i].EDTS.ELST.Flags,?src.MOOV.TRAK[i].EDTS.ELST.Flags)?

????????copy(dst.MOOV.TRAK[i].EDTS.ELST.TrackDurations,?src.MOOV.TRAK[i].EDTS.ELST.TrackDurations)?

????????copy(dst.MOOV.TRAK[i].EDTS.ELST.Times,?src.MOOV.TRAK[i].EDTS.ELST.Times)?

????????copy(dst.MOOV.TRAK[i].EDTS.ELST.Speeds,?src.MOOV.TRAK[i].EDTS.ELST.Speeds)?

????????copy(dst.MOOV.TRAK[i].MDIA.MDHD.Flags,?src.MOOV.TRAK[i].MDIA.MDHD.Flags)?

????????copy(dst.MOOV.TRAK[i].MDIA.HDLR.ComponentName,?src.MOOV.TRAK[i].MDIA.HDLR.ComponentName)?

????????copy(dst.MOOV.TRAK[i].MDIA.MINF.SMHD.Flags,?src.MOOV.TRAK[i].MDIA.MINF.SMHD.Flags)?

????????copy(dst.MOOV.TRAK[i].MDIA.MINF.SMHD.Balance,?src.MOOV.TRAK[i].MDIA.MINF.SMHD.Balance)?

????????copy(dst.MOOV.TRAK[i].MDIA.MINF.SMHD.Reserved,?src.MOOV.TRAK[i].MDIA.MINF.SMHD.Reserved)?

????????copy(dst.MOOV.TRAK[i].MDIA.MINF.VMHD.Flags,?src.MOOV.TRAK[i].MDIA.MINF.VMHD.Flags)?

????????......?
????}?
}

//?生成片段視頻文件?
func?Generate(clipVideo?*demux.VideoStructure,?videoSampleOffsets,?audioSampleOffsets?*SampleOffsets,?videoSampleIndexRange,?audioSampleIndexRange?*SampleIndexRange,?clipPath,?clipPrefix?string,?clipTime?*ClipTime,?videoFilePath?string)?{?
????//?拼接完整路徑名稱?
????clipVideoPath?:=?fmt.Sprintf("%s%s%d-%d.mp4",?clipPath,?clipPrefix,?clipTime.Start,?clipTime.Stop)?
????//?創(chuàng)建文件寫入對象?
????writer,?err?:=?NewFileWriter(clipVideoPath)?
????if?err?!=?nil?{?
????????fmt.Print(err)?
????????return?
????}?
????defer?writer.Close()?

????writeFTYP(writer,?clipVideo)?
????writeFREE(writer,?clipVideo)?
????writeMDAT(writer,?clipVideo,?videoSampleOffsets,?audioSampleOffsets,?videoSampleIndexRange,?audioSampleIndexRange,?videoFilePath)?
????writeMOOV(writer,?clipVideo)?
????writeMVHD(writer,?clipVideo)?
????for?_,?track?:=?range?clipVideo.MOOV.TRAK?{?
????????if?track.MDIA.HDLR.ComponentSubtype?==?"vide"?{?
????????????writeTRAK(writer,?&track,?true)?
????????}?
????????if?track.MDIA.HDLR.ComponentSubtype?==?"soun"?{?
????????????writeTRAK(writer,?&track,?false)?
????????}?
????}?
}?


//?從原視頻結(jié)構(gòu)體中取出片段幀的偏移,再從原視頻中拷貝幀數(shù)據(jù)到片段視頻?
func?writeMDAT(writer?*FileWriter,?clipVideo?*demux.VideoStructure,?videoSampleOffsets,?audioSampleOffsets?*SampleOffsets,?videoSampleIndexRange,?audioSampleIndexRange?*SampleIndexRange,?videoFilePath?string)?{?
????//?重算音視頻幀總長度?
????sampleTotalSize?:=?uint32(0)?
????for?_,?track?:=?range?clipVideo.MOOV.TRAK?{?
????????for?_,?sampleSize?:=?range?track.MDIA.MINF.STBL.STSZ.SampleSize?{?
????????????sampleTotalSize?+=?sampleSize?
????????}?
????}?
????writer.WriteUint32BE(8?+?sampleTotalSize)?
????writer.WriteString("mdat")?
????//?從原視頻中拷貝幀數(shù)據(jù)?
????reader,?err?:=?NewRawReader(videoFilePath)?
????if?err?!=?nil?{?
????????fmt.Print(err)?
????????return?
????}?
????//?視頻數(shù)據(jù)如果連續(xù),則合并長度,減少讀取次數(shù)?
????currentOffset?:=?int64(0)?
????currentLength?:=?int64(0)?
????for?index,?offset?:=?range?videoSampleOffsets.Offset[videoSampleIndexRange.Start:videoSampleIndexRange.Stop]?{?
????????for?_,?track?:=?range?clipVideo.MOOV.TRAK?{?
????????????//?視頻track?
????????????if?track.MDIA.HDLR.ComponentSubtype?==?"vide"?{?
????????????????if?currentOffset?==?0?{?
????????????????????currentOffset?=?int64(offset)?
????????????????????currentLength?=?int64(track.MDIA.MINF.STBL.STSZ.SampleSize[index])?
????????????????}?
????????????????//?如果內(nèi)存是連續(xù)的則合并長度待最后一次性讀取?
????????????????if?index+1?<=?videoSampleIndexRange.Stop-videoSampleIndexRange.Start?&&?uint64(track.MDIA.MINF.STBL.STSZ.SampleSize[index])+offset?==?videoSampleOffsets.Offset[index+1]?{?
????????????????????if?currentOffset?>?0?{?
????????????????????????currentLength?+=?int64(track.MDIA.MINF.STBL.STSZ.SampleSize[index])?
????????????????????}?
????????????????}?else?{?
????????????????????sampleContent?:=?reader.ReadBytesAt(currentLength,?currentOffset)?
????????????????????writer.WriteBytes(sampleContent)?
????????????????????currentOffset?=?0?
????????????????????currentLength?=?0?
????????????????}?
????????????????break?
????????????}?
????????}?
????}?
????//?音頻數(shù)據(jù)如果連續(xù),則合并長度,減少讀取次數(shù)?
????currentOffset?=?int64(0)?
????currentLength?=?int64(0)?

????//多音軌視頻,某音軌長度不足造成越界,直接補(bǔ)0?
????if?audioSampleIndexRange.Start?>?len(audioSampleOffsets.Offset)?||?audioSampleIndexRange.Stop?>?len(audioSampleOffsets.Offset)?{?
????????log.Println("Current?audio?track?length?not?enough?to?fit?the?cut?range!")?

????????for?_,?track?:=?range?clipVideo.MOOV.TRAK?{?
????????????if?track.MDIA.HDLR.ComponentSubtype?==?"soun"?{?
????????????????for?_,?sampleSize?:=?range?track.MDIA.MINF.STBL.STSZ.SampleSize?{?
????????????????????buf?:=?make([]byte,?sampleSize)?
????????????????????writer.WriteBytes(buf)?
????????????????}?
????????????}?
????????}?

????????return?
????}?

????for?index,?offset?:=?range?audioSampleOffsets.Offset[audioSampleIndexRange.Start:audioSampleIndexRange.Stop]?{?
????????for?_,?track?:=?range?clipVideo.MOOV.TRAK?{?
????????????//?音頻track?
????????????if?track.MDIA.HDLR.ComponentSubtype?==?"soun"?{?
????????????????if?currentOffset?==?0?{?
????????????????????currentOffset?=?int64(offset)?
????????????????????currentLength?=?int64(track.MDIA.MINF.STBL.STSZ.SampleSize[index])?
????????????????}?
????????????????//?如果內(nèi)存是連續(xù)的則合并長度待最后一次性讀取?
????????????????if?index+1?<=?audioSampleIndexRange.Stop-audioSampleIndexRange.Start?&&?uint64(track.MDIA.MINF.STBL.STSZ.SampleSize[index])+offset?==?audioSampleOffsets.Offset[index+1]?{?
????????????????????if?currentOffset?>?0?{?
????????????????????????currentLength?+=?int64(track.MDIA.MINF.STBL.STSZ.SampleSize[index])?
????????????????????}?
????????????????}?else?{?
????????????????????sampleContent?:=?reader.ReadBytesAt(currentLength,?currentOffset)?
????????????????????writer.WriteBytes(sampleContent)?
????????????????????currentOffset?=?0?
????????????????????currentLength?=?0?
????????????????}?
????????????????break?
????????????}?
????????}?
????}?
}

這樣就模擬了最原始的數(shù)據(jù)拷貝動(dòng)作。實(shí)際應(yīng)用效果對比看,優(yōu)化后的切割方式,比使用FFmpeg套件,效率提升了近2倍。這是對切割操作思路的一種轉(zhuǎn)換。


但是,這并不是優(yōu)化的結(jié)束。我們前面談到業(yè)界通行做法,都用到了服務(wù)器的多核處理。多核優(yōu)化利用了機(jī)器的最大性能,是最基本的優(yōu)化方式。那么,我們能不能在這方面再考慮入手呢?


是的,我們又在編程語言上微創(chuàng)新了一下。巧合的是,我們當(dāng)時(shí)正在準(zhǔn)備用Golang來做長鏈接系統(tǒng)的服務(wù)。程序員靈光乍現(xiàn),用Golang實(shí)現(xiàn)了上述操作邏輯,順便開了“一些” goroutine來做復(fù)制切割數(shù)據(jù)的動(dòng)作。把每個(gè)goroutine模擬成一個(gè)FFmpeg切割進(jìn)程,這樣在同一臺(tái)服務(wù)器上,每個(gè)內(nèi)核線程上就運(yùn)行著多個(gè)"goroutine形式的FFmpeg"切割JOB。簡略的主流程代碼如下:


func?main()?{?
//?解碼源視頻?
????rawVideo?:=?new(demux.VideoStructure)?
????demux.Demux(rawVideo,?videoFilePath)?

????//?并發(fā)編碼多個(gè)片段視頻?
????start?=?time.Now()?
????pool?:=?util.NewRoutinePool(len(clipTimes))?
????for?_,?clipTime?:=?range?clipTimes?{?
????????go?func(clipTime?*remux.ClipTime)?{?
????????????pool.AddOne()?
????????????defer?pool.DelOne()?

????????????//?編碼片段視頻?
????????????clipVideo?:=?new(demux.VideoStructure)?
????????????videoOffsets,?audioOffsets,?videoRange,?audioRange,?err?:=?remux.Remux(rawVideo,?clipVideo,?clipTime)?

????????????//?導(dǎo)出片段視頻?
????????????remux.Generate(clipVideo,videoOffsets,audioOffsets,videoRange,audioRange,clipPath,?clipPrefix,?clipTime,?videoFilePath)?
????????}(clipTime)?
????}?
????pool.Wait()?
}


經(jīng)過此番轉(zhuǎn)換后,一臺(tái)服務(wù)器上的剪切視頻操作,就從FFmpeg切割方案的“單進(jìn)程/M線程”轉(zhuǎn)換成“M線程xN協(xié)程"模式。(M為CPU內(nèi)核數(shù),N為單內(nèi)核上的goroutine數(shù))


在編程語言層面上的”誤打誤撞“并發(fā)處理后,切割效率又得到了進(jìn)一步的提升。經(jīng)過效果對比驗(yàn)證,比使用FFmpeg套件的單進(jìn)程方式,效率提升了20~80倍。最終影響整個(gè)切割效率,成為瓶頸的,是硬盤的IO性能。



在此基礎(chǔ)上,將單臺(tái)服務(wù)器擴(kuò)展至分布式服務(wù)集群。這樣的視頻切割JOB集群,帶來的是超高效率的視頻切割處理流程。


五、存在的問題


方案經(jīng)過優(yōu)化后,在視頻切割方面,已經(jīng)將效率提高了至少10倍以上。但同時(shí)優(yōu)化過程中也有一些問題呈現(xiàn)出來。


1. 首先,就是適配的視頻封裝格式單一的問題。因?yàn)槲覀兊臄?shù)據(jù)源比較單一,基本是MP4封裝格式,所以在初期,切割程序只需要解析MP4封裝格式相關(guān)定義字段即可。不過網(wǎng)絡(luò)上視頻流媒體格式非常豐富,即使常用的也有4、5種。對此,我們后續(xù)添加了對另2種比較常見的FLV與TS封裝格式的支持,滿足了業(yè)務(wù)的正常需求。但是,仍然與FFmpeg套件的廣泛適用性相去甚遠(yuǎn)。畢竟FFmpeg積累這么些年兼容了幾乎所有的媒體格式,這也是用FFmpeg套件被廣泛選擇,且相對更簡潔易用的原因。


2. 另外,在實(shí)際計(jì)算起止切割點(diǎn)時(shí),往往會(huì)出現(xiàn)當(dāng)前切割點(diǎn)的時(shí)間上并不是關(guān)鍵幀,導(dǎo)致部分?jǐn)?shù)據(jù)無法被正確解碼的問題。對此,我們也做了簡單的處理:對于切割點(diǎn)上非關(guān)鍵幀的情況,我們的程序會(huì)自動(dòng)往前/往后找到上一個(gè)/下一個(gè)關(guān)鍵幀的時(shí)間點(diǎn),并以此時(shí)間點(diǎn)為基準(zhǔn),重新計(jì)算數(shù)據(jù)后再行切割。這樣才能保證所有切割出來的視頻是確定能被解碼的。經(jīng)過測試,對切割效率的影響幾乎可以忽略不計(jì)。并且,我們也正在著手進(jìn)行優(yōu)化的“補(bǔ)幀”形式的精確起止點(diǎn)方案。


3. 還有,視頻媒體源文件非標(biāo)的處理問題。實(shí)際生產(chǎn)過程中,經(jīng)常會(huì)發(fā)現(xiàn)數(shù)據(jù)源提供的視頻文件里,有1路以上的音頻流,而且經(jīng)常性出現(xiàn)幾路音頻流中,都是無效的錯(cuò)誤數(shù)據(jù)。這種情況在實(shí)際生產(chǎn)中會(huì)影響到數(shù)據(jù)切割后的音視頻同步出錯(cuò),導(dǎo)致無法切割成功,或者播放失敗。我們對不同的情況進(jìn)行分析后,找到幾種思路/模式來解決:


(1)分析并保留正確的音頻流數(shù)據(jù)。這對部分非現(xiàn)場錄制的視頻文件比較有效,絕大多數(shù)PGC生產(chǎn)的視頻文件均可適用此模式。


(2)切割拷貝數(shù)據(jù)時(shí)不包括音頻流數(shù)據(jù)。這意味著切割后的視頻沒有聲音。大多數(shù)賽事直播現(xiàn)場錄制的視頻可應(yīng)用此模式。


(3)對于無法分析正確且不能丟棄原始音頻流數(shù)據(jù)的文件,作“降級(jí)”處理,改用FFmpeg套件接手切割工作,保證生產(chǎn)出正確的視頻文件。


六、分析與小結(jié)


從解決方案的拆分模塊角度看,任何環(huán)節(jié)的優(yōu)化提升都是對整個(gè)方案的效率有積極的促進(jìn)作用。故而,我們對整個(gè)視頻剪切流程進(jìn)行梳理劃分。整理出視頻數(shù)據(jù)切割操作中的不同模塊。


優(yōu)化方案的核心思路,主要是對數(shù)據(jù)處理模塊進(jìn)行效率提升。其關(guān)鍵點(diǎn)在于:


1. 單個(gè)剪切需求轉(zhuǎn)換為數(shù)據(jù)拷貝的JOB。

2. JOB由進(jìn)程轉(zhuǎn)換為協(xié)程化處理。

3. 集群分布式處理JOB列表。


雖然在實(shí)際生產(chǎn)使用過程中,仍然不斷有出現(xiàn)或大或小的坑,但是這都不影響我們在追求更高生產(chǎn)效率的路上繼續(xù)前行。只要能提升效率,任何微小的創(chuàng)新都在我們的持續(xù)不懈的優(yōu)化范圍之中,這也正是蘇寧的造極精神的體現(xiàn)。



總結(jié)

以上是生活随笔為你收集整理的FFmpeg优化 苏宁PP体育视频剪切效率提升技巧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 欧美五月婷婷 | 日韩免费福利 | 亚洲av成人精品一区二区三区 | 中文字幕在线视频不卡 | 国产午夜免费视频 | 中文字幕av一区二区三区 | 欧美浓毛大泬视频 | 一个色综合网站 | 扶她futa粗大做到怀孕 | 人人妻人人做人人爽 | 日韩精品一区二区在线观看 | 久久国产经典视频 | 国产精品性 | 国产成人精 | 亚洲午夜一区二区三区 | 有奶水的迷人少妇 | 国产嫩草视频 | 欧美一区二区三区四 | 女人被灌满精子 | 午夜视频在线观看免费视频 | 国产三级影院 | 91中文 | 丁香九月婷婷 | 亚洲亚洲人成综合网络 | 性生交大片免费看 | 17c在线视频 | 毛片.com | 亚洲精品国产一区二区 | 尤物国产视频 | 国产精九九网站漫画 | 日韩中文字幕免费 | 双性皇帝高h喷汁呻吟 | 久草视频在 | 午夜精品美女久久久久av福利 | 国产三级三级在线观看 | www四虎com | 操一操视频 | 99精品人妻少妇一区二区 | 六月丁香婷婷激情 | 日本三级黄在线观看 | 夜夜草网站 | 久久久久99精品成人片三人毛片 | 欧美顶级毛片在线播放 | 日韩成人精品在线观看 | av男人天堂av | 久操福利视频 | 一区二区三区欧美精品 | 免费啪视频在线观看 | 日韩av在线网站 | 男女在线视频 | h视频在线观看网站 | 粉嫩av.com| 饥渴丰满的少妇喷潮 | 国产精品高潮呻吟视频 | 搡8o老女人老妇人老熟 | 日本福利小视频 | 艳妇臀荡乳欲伦交换gif | 日本精品一区二区视频 | 国产自偷自拍视频 | 欧美xxxx精品 | 国产aⅴ| 欧美激情午夜 | 欧美日韩一区二区不卡 | 国产欧美日韩久久 | xxx国产在线观看 | 精品亚洲天堂 | 蜜臀久久99精品久久久无需会员 | 99亚洲精品 | 大咪咪av | 蜜臀中文字幕 | 国产香蕉在线观看 | 国产精品五月天 | 五十路av在线 | 成人精品视频一区二区 | 亚洲欧美在线播放 | xxxx性视频 | 国产三级大片 | 国产夫妻性生活视频 | 国产又粗又长又硬免费视频 | 国产精品久久久久久久午夜 | 青青草手机视频在线观看 | av高清| 天天色天天 | 精品色 | 日本大尺度床戏揉捏胸 | 成人免费观看在线视频 | 国产人澡人澡澡澡人碰视频 | 丰满少妇在线观看网站 | 天天槽 | 国产夫绿帽单男3p精品视频 | 成人免费公开视频 | 亚洲老女人视频 | 爱爱爱免费视频 | 天天超碰 | 少妇一级淫片免费放播放 | 四虎影视精品 | 欧美精品18videosex性欧美 | 亚洲制服丝袜在线播放 | 精品久久久网站 |