video - 将 HLS M3U8 转换为 MP4 后,如何找出是否缺少 TS 段?
问题描述
我有一些.ts
文件和相应的index.m3u8
文件,看起来像这样:
#EXTM3U
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:15
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:3.170,
seg-1-v1-a1.ts
#EXTINF:3.170,
seg-2-v1-a1.ts
#EXTINF:3.170,
seg-3-v1-a1.ts
#EXTINF:3.170,
seg-4-v1-a1.ts
#EXTINF:3.170,
seg-5-v1-a1.ts
#EXT-X-ENDLIST
因此,要将其转换为单个output.mp4
文件,我运行了以下命令:
$ ffmpeg -i index.m3u8 -c copy output.mp4
...
[hls @ 0x55d78a3a6280] Opening 'seg-1-v1-a1.ts' for reading
...
[hls @ 0x55d78a3a6280] Opening 'seg-2-v1-a1.ts' for reading
[hls @ 0x55d78a3a6280] Opening 'seg-3-v1-a1.ts' for reading
[hls @ 0x55d78a3a6280] Opening 'seg-4-v1-a1.ts' for reading
[hls @ 0x55d78a3a6280] Opening 'seg-5-v1-a1.ts' for reading
...
之后,我删除了.ts
文件,index.m3u8
并且只保留了output.mp4
. 然而,事实证明,我没有所有.ts
文件,输出最初更像这样:
$ rm seg-3-v1-a1.ts
$ ffmpeg -i index.m3u8 -c copy output.mp4
...
[hls @ 0x5555d0d86280] Opening 'seg-1-v1-a1.ts' for reading
...
[hls @ 0x5555d0d86280] Opening 'seg-2-v1-a1.ts' for reading
[hls @ 0x5555d0d86280] Opening 'seg-3-v1-a1.ts' for reading
[hls @ 0x5555d0d86280] Failed to open segment 3 of playlist 0
[hls @ 0x5555d0d86280] Opening 'seg-4-v1-a1.ts' for reading
[hls @ 0x5555d0d86280] Opening 'seg-5-v1-a1.ts' for reading
...
观看视频时,在缺失片段的位置,画面冻结几秒钟,然后视频继续播放。我有几个.mp4
文件,我想找出其中哪些文件受到此问题的影响。在没有原始日志的情况下,如何在运行ffmpeg -i index.m3u8 -c copy output.mp4
命令时找出是否缺少任何段?
我在https://superuser.com/questions/100288/how-can-i-check-the-integrity-of-a-video-file-avi-mpeg-mp4找到了一个命令,不幸的是没有发现任何问题:
$ ffmpeg -v error -i output.mp4 -f null -
$ echo $?
0
解决方案
您可以检查 r_frame_rate 和 avg_frame_rate 是否相等。
下面的命令:
ffprobe -v error -show_streams output.mp4 | grep "frame_rate"
将输出(对于正确的文件)是这样的:
r_frame_rate=24/1
avg_frame_rate=24/1
和(对于损坏的文件)是这样的:
r_frame_rate=24/1
avg_frame_rate=886/37
这种方法并不完美,但在很多情况下应该完全没问题。
第二种方法,直观且在我看来仅在特定条件下工作,正在寻找冻结。例如:
ffmpeg -i output.mp4 -vf "freezedetect=n=-60dB:d=2" -f null - 1>/dev/null 2>&1 \
| grep "duration"
它可以工作,但前提是流确实是特定的。
另一种方法,类似于第一种方法,是寻找 VFR。
命令:
ffmpeg -i output.mp4 -vf vfrdet -an -f null -
将输出:
[Parsed_vfrdet_0 @ 0x55563547bf00] VFR:0.000094 (2/21261) 最小值:3750 最大值:183750 平均值:93750
对于损坏的文件。为了正确,它将是:
[Parsed_vfrdet_0 @ 0x564c33d53140] VFR:0.000000 (0/21311)
这与第一种方法相似,但与外表相反——不同!
据我所知,r_frame_rate
和是可能的avg_frame_rate
,而 VFR 不为零!
在我看来,检查 VFR 是最好的选择,并且可能会奏效。很糟糕,您没有提供任何示例文件。我假设您的流应该有 VFR:0.000000,但那是在阅读茶叶。
据我所知,没有通用的方法可以满足您的需求。我希望我的任何示例都适合,但它确实取决于并且需要在实践中在正确的文件上进行测试。正是这些,您要检查。
根据您在我的回答下方的回复,我已经实现了简单的脚本:
- 下载ts文件
- 将这些转换为正确的 mp4
- 损坏 ts 文件之一
- 将这些(缺少 ts 文件)转换为损坏的 mp4
- 使用我的第一种方法测试每个 mp4 文件(比较 r_frame_rate == avg_frame_rate)
#!/usr/bin/bash
# Blender demo movie https://en.wikipedia.org/wiki/Sintel
# Full URL: https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8
bURL=https://bitdash-a.akamaihd.net/content/sintel/hls/video/
# I'm checking each available bitrate. You can modify that.
bitratesArray=("250" "500" "800" "1100" "1500" "4000" "6000" "10000")
# Here you can choose which ts file you want to remove from
# the ffmpeg conversion (it'll be temporary renamed, doesn't matter).
tsToCorrupt="14"
for i in ${bitratesArray[@]}
do
# Download all files.
# To be nice, I'm preventing unnecessary server load.
if [ ! -d "${i}kbit" ]
then
echo -e "\nDownloading ${i}kbit version"
curl "${bURL}${i}kbit.m3u8" | tee -a "${i}kbit.m3u8" | grep '\.ts' | \
sed -e "s|^|$bURL|g" -e "s/\.ts/\.ts /g" > "${i}kbit_fullURL.m3u8"
aria2c -q -c -j 16 -x 16 -d "${i}kbit" -i"${i}kbit_fullURL.m3u8"
echo -e "\n"
fi
# Convert proper
echo "Converting: ${i}kbit_proper.mp4"
ffmpeg -y -hide_banner -loglevel error \
-i "${i}kbit.m3u8" -c copy "${i}kbit_proper.mp4"
# Make temporal source defect
mv "${i}kbit/seq-${tsToCorrupt}.ts" "${i}kbit/seq-${tsToCorrupt}.ts_CORRUPTED"
# Convert defective
echo "Converting: ${i}kbit_defective.mp4"
ffmpeg -y -hide_banner -loglevel error \
-i "${i}kbit.m3u8" -c copy "${i}kbit_defective.mp4"
# Repair defected source (obviously converted file is still corrupted)
mv "${i}kbit/seq-${tsToCorrupt}.ts_CORRUPTED" "${i}kbit/seq-${tsToCorrupt}.ts"
done
# Examine generated files
echo -e "\n\nREPORT"
for i in *.mp4
do
echo -e "\n---------------------------\nFilename: \n${i}"
report=$(ffprobe -v error -show_streams $i | grep "frame_rate")
echo -e "\ncalculated frame rates:\n${report}"
echo -e "\nVerdict: \nFile ${i} is "
echo ${report} | \
awk -F'[=\n ]' ' { if ( $2 == $4 ) { print "OK" } else { print "WRONG" }; } '
done
对我来说,它工作正常。
推荐阅读
- python - 我可以在某些条件下使用opencv图像侵蚀来侵蚀RGB吗?
- android - Presentation model in clean architecture
- python - how to solve sequence item 0: expected str instance, tuple found
- python - Convention to distinguish type aliases from actual classes?
- java - 如何将图像放置在另一个图像上的确切位置?
- uwp - 为 UWP 应用程序获取离线 HTTPesponse
- git - 计算维护者的工作量(在集成过程中查找谁合并了补丁)
- emacs - 如何让 Phoenix live reload 忽略临时文件?
- bash - 通过 ssh 运行以本地文件为参数的本地脚本
- sql-server - 使用动态 SQL 的过程的 SQL Server 注入