【问题标题】:After converting an HLS M3U8 into an MP4, how to find out if there were TS segments missing, originally?将 HLS M3U8 转换为 MP4 后,如何找出是否缺少 TS 段?
【发布时间】:2021-11-12 22:06:48
【问题描述】:

我有几个.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

【问题讨论】:

  • 我会 ffprobe -show_packets 并为每个流提取每个数据包的 dtsptsduration 字段。如果有任何间隙,则意味着缺少一个片段。不过,不确定这是否可行。
  • 请提供一个示例 MP4 文件。
  • @user3840170 嘿,谢谢你的想法,我刚刚研究了一下,但我无法理解这些值。在任何情况下,无论是正确的还是有缺陷的输出,似乎都存在差距——我是否删除一个片段并不重要。
  • @finefoot 间隔是指 [pts, pts+duration) 间隔加在一起不形成连接线?
  • @user3840170 没错。这是我为此编写的一个简短的 Python 脚本:anonpaste.org/… dts 中有 2 个空白,pts 中有很多错位

标签: video ffmpeg http-live-streaming m3u8 transport-stream


【解决方案1】:

您可以检查 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_rateavg_frame_rate 可能相等,而 VFR 不为零!

老实说,检查 VFR 是最好的选择,并且可能会奏效。很糟糕,您没有提供任何示例文件。我假设你的流应该有 VFR:0.000000,但那是在读茶叶。

据我所知,没有通用的方法可以满足您的需求。我希望我的任何示例都适合,但它确实取决于并且需要在实践中在正确的文件上进行测试。正是这些,你想检查一下。


根据您在我的回答下方的回复,我已经实现了简单的脚本:

  1. 下载 ts 文件
  2. 将这些转换为正确的 mp4
  3. 损坏 ts 文件之一
  4. 将这些(缺少 ts 文件)转换为损坏的 mp4
  5. 使用我的第一种方法测试每个 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

对我来说它工作正常。

【讨论】:

  • 哈,非常感谢您写下答案 :) 我喜欢帧速率的想法。但是,VFR 不起作用:它显示两个文件的非零值 - 无论是正确还是有缺陷。
  • 我确实提供了一个脚本来生成两个示例文件:anonpaste.org/…
  • 知道 VFR 检查失败的原因吗?知道如何使用 dts、pts 和持续时间使 ffprobe -show_packets 起作用吗?
  • @finefoot 有趣...更多,因为我们正在处理相同的文件!只是比特率不同。根据你的脚本,我准备了简单的脚本来检查我的第一种方法。它运作良好。为了保持一致性并使其持久化(对于其他用户),我会将其粘贴到我的主要答案中。
  • @finefoot 好的,我刚刚更新了我的答案。请检查脚本。
猜你喜欢
  • 2016-01-11
  • 1970-01-01
  • 2016-07-27
  • 1970-01-01
  • 2018-11-18
  • 2013-11-09
  • 2015-09-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多