返回列表 发布新帖
查看: 167|回复: 1

近期很多视频出现丢帧导致后续播放音画不同步

213

金钱

20

回帖

36

积分

Lv3 圆转纯熟

积分
36
发表于 昨天 20:06 | 显示全部楼层 |阅读模式
本帖最后由 dsyszy 于 2026-11-14 20:10 编辑

使用 chrome 播放 mp4 很容易发现问题。

如果不快进,后续的播放会一直都是音画不同步,所以必须快进才能让音画重新同步。

使用专门的视频播放器软件播放可能无法及时发现问题,因为播放器做了处理,遇到丢帧自动快进。

由于大家看视频都是快进,所以更难发现视频源出问题的地方。

我也是最近看某堂 自译字幕区,很多视频出现 原档待补,还提到坏档链接,但是没有提到具体出问题的地方。

自己也下载了几部视频也没发现问题,直到今天下载了 4k2 论坛的 mikr-133 视频 ,播放过程发现了问题。

第一次发现画面卡顿的地方 01:25:30 开始,画面卡顿几秒,之后就是音画不停步。(我也是快进随机看的,反复几次才发现问题)

由于人工找容易漏掉,加上近期确实出现非常多的视频有音画不同步的问题。

于是写一个php脚本用于检测视频是否有问题,脚本不太完善,大家可以利用ai优化或者转换成其他的编程语言进行测试。

mikr-133 有问题的地方(后续发现有问题的视频会在 4k2 对应的视频下面评论,大家注意一下就可以):

  1. [视频间隔异常] 00:37:30.683(2250.683s)-> 00:37:32.751(2252.751s),间隔 2.069s
  2. [视频间隔异常] 00:49:30.902(2970.902s)-> 00:49:32.971(2972.971s),间隔 2.069s
  3. [视频间隔异常] 01:01:30.621(3690.621s)-> 01:01:32.690(3692.690s),间隔 2.069s
  4. [视频间隔异常] 01:13:30.841(4410.841s)-> 01:13:32.909(4412.909s),间隔 2.069s
  5. [视频间隔异常] 01:25:30.560(5130.560s)-> 01:25:32.628(5132.628s),间隔 2.069s
复制代码

测试脚本:


  1. <?php

  2. // ===== 配置 =====
  3. $file = __DIR__ . '/test.mp4';
  4. $videoGapThreshold = 0.1;  // 视频帧间隔超过 100ms 报警
  5. $audioGapTolerance = 0.002; // 音频间断容差 2ms


  6. function formatTime($seconds) {
  7.     $hours = (int) floor($seconds / 3600);
  8.     $minutes = (int) floor(fmod($seconds, 3600) / 60);
  9.     $seconds = fmod($seconds, 60);

  10.     return sprintf('%02d:%02d:%06.3f', $hours, $minutes, $seconds);
  11. }

  12. // ===== 视频 Packet 检测 =====
  13. echo "开始检测视频 Packet...\n";

  14. $videoPackets = [];

  15. $cmd = 'ffprobe -v error'
  16.     . ' -select_streams v:0'
  17.     . ' -show_packets'
  18.     . ' -show_entries packet=pts_time,duration_time'
  19.     . ' -of csv=p=0 '
  20.     . escapeshellarg($file);

  21. $handle = popen($cmd . ' 2>&1', 'r');

  22. if ($handle) {
  23.     while (($line = fgets($handle)) !== false) {
  24.         $data = str_getcsv(trim($line), ',', '"', '');

  25.         if (!isset($data[0]) || !is_numeric($data[0])) {
  26.             continue;
  27.         }

  28.         $pts = (float)$data[0];
  29.         $duration = isset($data[1]) && is_numeric($data[1])
  30.             ? (float)$data[1]
  31.             : 0;

  32.         $videoPackets[] = [
  33.             'pts' => $pts,
  34.             'duration' => $duration
  35.         ];

  36.         if (count($videoPackets) % 5000 === 0) {
  37.             echo "视频已读取 " . count($videoPackets) . " 个 Packet\n";
  38.             flush();
  39.         }
  40.     }

  41.     $result = pclose($handle);
  42.     echo "视频 Packet 读取完成,数量:" . count($videoPackets) . "\n";
  43. }

  44. // 按显示时间 PTS 排序,避免 B 帧导致顺序错乱
  45. usort($videoPackets, function ($a, $b) {
  46.     return $a['pts'] <=> $b['pts'];
  47. });

  48. // 检测视频时间间隔
  49. $lastPts = null;
  50. $videoErrors = 0;

  51. foreach ($videoPackets as $packet) {
  52.     $pts = $packet['pts'];

  53.     if ($lastPts !== null) {
  54.         $gap = $pts - $lastPts;

  55.         if ($gap > $videoGapThreshold) {
  56.             printf(
  57.                 "[视频间隔异常] %s(%.3fs)-> %s(%.3fs),间隔 %.3fs\n",
  58.                 formatTime($lastPts),
  59.                 $lastPts,
  60.                 formatTime($pts),
  61.                 $pts,
  62.                 $gap
  63.             );
  64.             $videoErrors++;
  65.         } elseif ($gap <= 0) {
  66.             printf(
  67.                 "[视频时间戳重复] %s(%.3fs)\n",
  68.                 formatTime($pts),
  69.                 $pts
  70.             );
  71.             $videoErrors++;
  72.         }
  73.     }

  74.     $lastPts = $pts;
  75. }

  76. echo "视频检测完成,异常数量:{$videoErrors}\n";

  77. // 释放视频 Packet 数据
  78. unset($videoPackets);


  79. // ===== 音频 Packet 检测 =====
  80. echo "\n开始检测音频 Packet...\n";

  81. $audioCount = 0;
  82. $audioErrors = 0;
  83. $lastPts = null;
  84. $lastDuration = 0;

  85. $cmd = 'ffprobe -v error'
  86.     . ' -select_streams a:0'
  87.     . ' -show_packets'
  88.     . ' -show_entries packet=pts_time,duration_time'
  89.     . ' -of csv=p=0 '
  90.     . escapeshellarg($file);

  91. $handle = popen($cmd . ' 2>&1', 'r');

  92. if ($handle) {
  93.     while (($line = fgets($handle)) !== false) {
  94.         $data = str_getcsv(trim($line), ',', '"', '');

  95.         if (!isset($data[0]) || !is_numeric($data[0])) {
  96.             continue;
  97.         }

  98.         $pts = (float)$data[0];
  99.         $duration = isset($data[1]) && is_numeric($data[1])
  100.             ? (float)$data[1]
  101.             : 0;

  102.         $audioCount++;

  103.         if ($lastPts !== null && $lastDuration > 0) {
  104.             $gap = $pts - ($lastPts + $lastDuration);

  105.             if ($gap > $audioGapTolerance) {
  106.                 printf(
  107.                     "[音频间断] %s(%.3fs),间断 %.3fs\n",
  108.                     formatTime($pts),
  109.                     $pts,
  110.                     $gap
  111.                 );
  112.                 $audioErrors++;
  113.             } elseif ($gap < -$audioGapTolerance) {
  114.                 printf(
  115.                     "[音频 Packet 重叠] %s(%.3fs),重叠 %.3fs\n",
  116.                     formatTime($pts),
  117.                     $pts,
  118.                     abs($gap)
  119.                 );
  120.                 $audioErrors++;
  121.             }
  122.         }

  123.         $lastPts = $pts;
  124.         $lastDuration = $duration;

  125.         if ($audioCount % 5000 === 0) {
  126.             echo "音频已检测 {$audioCount} 个 Packet\n";
  127.             flush();
  128.         }
  129.     }

  130.     $result = pclose($handle);
  131.     echo "音频 Packet 读取完成,数量:{$audioCount}\n";
  132. }

  133. echo "\n全部检测完成。\n";
  134. echo "视频异常:{$videoErrors}\n";
  135. echo "音频异常:{$audioErrors}\n";
复制代码




226

金钱

353

回帖

394

积分

Lv6 渐入佳境

臻绮帧享

积分
394
发表于 昨天 22:05 来自手机 | 显示全部楼层
有几个会用脚本都是问题,哈哈。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2026 4ktwo.com All Rights Reserved.
关灯 快速发帖返回顶部
快速回复 返回顶部 返回列表