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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > HTML >内容正文

HTML

踩坑ffmpeg录制的mp4无法在浏览器上播放

發(fā)布時(shí)間:2024/1/8 HTML 36 coder
生活随笔 收集整理的這篇文章主要介紹了 踩坑ffmpeg录制的mp4无法在浏览器上播放 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

使用ffmpeg編譯好的程序在電腦上進(jìn)行音視頻轉(zhuǎn)換,可以參考這篇:《windows電腦FFmpeg安裝教程手把手詳解_windows安裝ffmpeg》,而我們要做的是在游戲引擎中集成ffmpeg源碼用來(lái)錄制游戲視頻。

我們游戲目前只支持錄制avi格式的視頻,但是近期有個(gè)運(yùn)營(yíng)需求:在上架商品的時(shí)候在游戲內(nèi)錄制一段視頻提供給網(wǎng)頁(yè)端進(jìn)行播放。

首先簡(jiǎn)單的了解了一下,ffmpeg是支持錄制mp4格式的,于是簡(jiǎn)短地改了幾行代碼就實(shí)現(xiàn)了錄制mp4,然后把錄制出來(lái)的視頻發(fā)給網(wǎng)頁(yè)同學(xué)部署測(cè)試。

第二天收到反饋我們錄制的視頻無(wú)法在網(wǎng)頁(yè)上播放,由于我也是第一次接觸ffmpeg,不知道為什么mp4無(wú)法在瀏覽器上播放,整個(gè)過(guò)程就是不斷通過(guò)chatgpt查閱資料,不斷修改代碼調(diào)試,最終在某個(gè)夜晚跑通了。

問(wèn)題:瀏覽器上無(wú)法播放mp4

我們游戲錄制出來(lái)的mp4,右鍵 - 打開(kāi)方式,選擇瀏覽器,或者直接拖動(dòng)mp4文件到瀏覽器里面,讓它打開(kāi),表現(xiàn)為無(wú)法播放

查看視頻詳細(xì)信息

通過(guò)ffmpeg工具提供的一些指令用來(lái)查看視頻的詳細(xì)信息,有助于調(diào)試

ffprobe -v quiet -print_format json -show_format -show_streams 你的文件名

ffprobe -v quiet -print_format json -show_format -show_streams video.mp4

查看視頻每一幀的信息:

ffprobe -show_packets -of xml -i video.mp4

使用ffmpeg將mp4轉(zhuǎn)為h264文件

我的第一個(gè)驗(yàn)證想法,使用ffmpeg把游戲錄制的視頻轉(zhuǎn)換看看轉(zhuǎn)換之后的視頻是否在瀏覽器上播放,結(jié)果:轉(zhuǎn)換后就可以在瀏覽器中就可以播放了

指令:ffmpeg -i video.mp4 -vcodec h264 -crf 10 test.mp4

說(shuō)明:-crf 的數(shù)值是0~51,代表壓縮等級(jí),值越大畫(huà)質(zhì)越差,體積越小

使用h264編碼

通過(guò)chatgpt查閱資料,了解到需要把mp4使用h264編碼,于是就改了這個(gè)接口,這樣來(lái)看格式雖然是h264了,但是仍然無(wú)法在網(wǎng)頁(yè)上播放

avformat_alloc_output_context2(&oc, NULL, NULL, file_name);
//把第三個(gè)參數(shù),輸出格式強(qiáng)制指定為H264
avformat_alloc_output_context2(&oc, NULL, "h264", file_name);

對(duì)比差異

既然通過(guò)格式轉(zhuǎn)換是可以播放的,那就對(duì)比一下兩個(gè)視頻文件的詳細(xì)對(duì)比差異,差異如下所示:

可以播放的視頻 游戲錄制的視頻
"codec_tag_string": "avc1", "codec_tag_string": "[0][0][0][0]",
"is_avc": "true", "is_avc": "false",

游戲錄制的缺少了以下部分字段

"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 45824,
"duration": "3.580000",
"bit_rate": "2161030",
"nb_frames": "179",

手動(dòng)設(shè)置codec_tag

一開(kāi)始我的重點(diǎn)方向是:codec_tag

在 MP4 文件中,codec_tag 是一個(gè)用于標(biāo)識(shí)視頻和音頻編解碼器的標(biāo)簽。它通常是一個(gè)四個(gè)字母的代碼,例如“avc1”表示 H.264 視頻編解碼器,“mp4a”表示 AAC 音頻編解碼器。codec_tag 可以幫助播放器確定正確的解碼器來(lái)解碼視頻和音頻流。在使用 MP4 文件時(shí),確保你的播放器支持所使用的 codec_tag。

然后使用chatgpt查到示例代碼加到游戲內(nèi)但代碼編譯不通過(guò),原因我們是自己編譯的ffmpeg.lib,還需要修改export才能用某些接口,這個(gè)問(wèn)題后面再說(shuō)

但是手動(dòng)設(shè)置tag之后,問(wèn)題依賴(lài)存在

斷點(diǎn)查證

在斷點(diǎn)的時(shí)候發(fā)現(xiàn)調(diào)用:avcodec_find_encoder傳入的格式并不是h264,而是mpeg4

于是手動(dòng)在上面添加了一 行用來(lái)修改編碼格式,但是還是一樣的結(jié)果

其實(shí)在這個(gè)時(shí)候,我還是有些分不清楚codec_tag和codec的關(guān)系

查一些正確的示例

所以讓chatgpt給我舉例一些使用ffmpeg來(lái)編碼h264的視頻,然后對(duì)照我們的代碼來(lái)分析是問(wèn)題出在那里,后面了解到某位同事對(duì)ffmpeg比較懂,已于向他請(qǐng)教,大大加速了查證過(guò)程。

視頻每一幀的數(shù)據(jù)中無(wú)pts和dst

ffprobe -show_packets -of xml -i video.mp4,使用這個(gè)指令來(lái)查看視頻中每幀的數(shù)據(jù),發(fā)現(xiàn)錄制出來(lái)的視頻沒(méi)有pts和dst

<ffprobe>
Input #0, h264, from 'video.mp4':
  Duration: N/A, bitrate: N/A
  Stream #0:0: Video: h264 (Main), yuv420p(progressive), 1904x1002 [SAR 1:1 DAR 952:501], 25 fps, 100 tbr, 1200k tbn
    <packets>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="34080" pos="0" flags="K__"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="4995" pos="34080" flags="___"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="817" pos="39075" flags="___"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="428" pos="39892" flags="___"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="89" pos="40320" flags="___"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="169" pos="40409" flags="___"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="75" pos="40578" flags="___"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="71" pos="40653" flags="___"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="71" pos="40724" flags="___"/>
        <packet codec_type="video" stream_index="0" duration="24000" duration_time="0.020000" size="672" pos="40795" flags="___"/>
    </packets>
</ffprobe>

正常情況下每幀的視頻數(shù)據(jù)中是有pts和dst的,于是在寫(xiě)入每幀數(shù)據(jù)時(shí)手動(dòng)給pts和dst賦值

av_packet_rescale_ts(pkt, *time_base, st->time_base);
pkt->stream_index = st->index;
this->video_st.write_size += pkt->size;
int step = 1000 / this->fps;//ptk.pts每幀加的數(shù)值=1000/幀率
pkt->pts = step * m_frameNum;
pkt->dts = step * m_frameNum;
/* Write the compressed frame to the media file. */
return av_interleaved_write_frame(fmt_ctx, pkt);

在av_interleaved_write_frame之前寫(xiě)入pts,之后pts的值就無(wú)效了

手動(dòng)計(jì)算pts播放起來(lái)非常卡

這樣改完之后pts的值是有了,但是播放速度不正常,表現(xiàn)會(huì)非常卡,原因就是pts計(jì)算錯(cuò)誤

assert中斷

試過(guò)強(qiáng)制修改codec_id,但是碰到問(wèn)題:在結(jié)束錄制的時(shí)候會(huì)中多線(xiàn)程的Assert導(dǎo)致錄制得到的視頻數(shù)據(jù)是空的,代碼:lib\cstdmf\concurrency.hpp void grab() MF_ASSERT(id_ != gid);

讓ffmpeg自動(dòng)選擇輸出格式

反復(fù)閱讀代碼然后不斷嘗試,發(fā)現(xiàn)在調(diào)用avformat_alloc_output_context2接口不指定格式,而只給輸出文件的后綴,錄制出來(lái)的視頻是有pts和dts的!!!

這一下就回到最初的地方,在最早的時(shí)候我就是沒(méi)有設(shè)置這個(gè)選項(xiàng)的。

接口定義和解釋如下:

int avformat_alloc_output_context2(AVFormatContext **ctx, AVOutputFormat *oformat, const char *format_name, const char *filename);

ctx:輸出格式上下文的指針,函數(shù)執(zhí)行成功后會(huì)將創(chuàng)建的上下文賦值給該指針。
oformat:輸出格式,可以為 NULL,表示讓 FFmpeg 自動(dòng)選擇輸出格式。
format_name:輸出格式的名稱(chēng),可以為 NULL,表示讓 FFmpeg 自動(dòng)選擇輸出格式。
filename:輸出文件名,可以為 NULL,表示不需要輸出到文件。

pts有了但格式是mpeg4

然后修改接口,再次編譯驗(yàn)證,這一次pts在視頻幀數(shù)據(jù)中有了,但為啥視頻格式會(huì)變成mpeg4???

測(cè)試了一下mpeg4在瀏覽器上也無(wú)法播放,但是可以在win10自帶的播放上可以播放。

mp4好了但avi壞了

查看代碼,確實(shí)有一處地方指定了mpeg4,代碼:video_codec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);

于是把它改為video_codec = avcodec_find_encoder(AV_CODEC_ID_H264);,這樣終于好了。

再來(lái)驗(yàn)證一下以前的錄制avi格式,結(jié)果發(fā)現(xiàn)壞了,以前的avi格式錄制不了,斷點(diǎn)一下,發(fā)現(xiàn)中上面提到的assert了。盲猜是avi不能使用h264,于是修改了一下mp4使用h264,其它格式使用mpeg4,再編譯驗(yàn)證,這樣就好了。

總結(jié)一下:

  1. 初始化avformat_alloc_output_context2不要指定格式,讓ffmpeg自動(dòng)調(diào)用,但需要輸出的文件后綴為mp4
  2. 調(diào)用avcodec_find_encoder把mp4設(shè)置h246格式,但是對(duì)于avi改為AV_CODEC_ID_MPEG4
  3. 指定視頻的文件頭
  4. 其它地方不要再手動(dòng)去修改codec_id,否則在結(jié)束錄制的時(shí)候會(huì)出錯(cuò),導(dǎo)致視頻為空
  5. 多觀察ffmpeg每一個(gè)接口的返回值,特別是非成功的情況下要進(jìn)行處理

這幾個(gè)接口需要關(guān)注返回值是否成功:

  1. avformat_alloc_output_context2 初始化
  2. avcodec_find_encoder 查找編碼器的函數(shù)
  3. avformat_write_header 寫(xiě)入視頻的header
  4. av_interleaved_write_frame 寫(xiě)入視頻每一幀的數(shù)據(jù)
  5. av_write_trailer 結(jié)束錄制

參考內(nèi)容

ffmpeg實(shí)現(xiàn)將H264裸流封裝成.mp4或.avi文件_ffmpeg對(duì)裸流封裝-CSDN博客

[原]零基礎(chǔ)學(xué)習(xí)視頻解碼之FFMpeg中比較重要的函數(shù)以及數(shù)據(jù)結(jié)構(gòu) - 雪夜&流星 - 博客園 (cnblogs.com)

FFmpeg從入門(mén)到入魔(3):提取MP4中的H.264和AAC - 掘金 (juejin.cn)

YUV編碼為H264 H264封裝為MP4 - 知乎 (zhihu.com)

使用工具

MP4封裝格式—音視頻基礎(chǔ)知識(shí) · FFmpeg原理 (xianwaizhiyin.net)

下載 Mp4 Explorer (apponic.com)

總結(jié)

以上是生活随笔為你收集整理的踩坑ffmpeg录制的mp4无法在浏览器上播放的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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