FFmpeg 「Non-monotonous DTS」エラー徹底対策:muxer 選定と再エンコード条件
動画変換の作業中に突然「Non-monotonous DTS in output stream」というエラーに遭遇したことはありませんか?このエラーは FFmpeg を使った動画処理で頻繁に発生するトラブルの一つです。特に複数の動画を結合したり、配信用にフォーマット変換を行う際によく現れます。本記事では、このエラーの本質を理解し、適切な muxer 選定や再エンコード条件の設定方法まで、実践的な対策を詳しく解説していきます。
背景
DTS と PTS の役割
動画ファイルには、フレームの表示順序と処理順序を管理する 2 つの重要なタイムスタンプが存在します。
typescript// タイムスタンプの概念を表す型定義
type Timestamp = {
pts: number; // Presentation Time Stamp(表示時刻)
dts: number; // Decoding Time Stamp(デコード時刻)
};
PTS(Presentation Time Stamp) は、そのフレームをいつ画面に表示するかを示す時刻です。 一方、DTS(Decoding Time Stamp) は、そのフレームをいつデコードすべきかを示します。
typescript// フレームデータの構造例
interface VideoFrame {
data: Buffer; // フレームの実データ
pts: number; // 表示タイムスタンプ
dts: number; // デコードタイムスタンプ
keyframe: boolean; // キーフレームかどうか
frameType: 'I' | 'P' | 'B'; // フレームタイプ
}
なぜ DTS と PTS が異なるのか
動画圧縮では、I フレーム(キーフレーム)、P フレーム(前方予測フレーム)、B フレーム(双方向予測フレーム)という 3 種類のフレームを使います。
mermaidflowchart LR
i1["I フレーム<br/>(基準)"] --> p1["P フレーム<br/>(I から予測)"]
i1 --> b1["B フレーム<br/>(I と P から予測)"]
p1 --> b1
p1 --> p2["P フレーム<br/>(P から予測)"]
b1 -.表示順序.-> i1
b1 -.表示順序.-> p1
上図は、フレーム間の依存関係を示しています。B フレームは前後のフレームを参照するため、デコード順序と表示順序が異なるのです。
B フレームは前後両方のフレームを参照するため、表示順序とデコード順序が一致しません。
typescript// フレームの表示順序とデコード順序の違い
const displayOrder = ['I', 'B', 'B', 'P', 'B', 'B', 'P']; // 表示順
const decodeOrder = ['I', 'P', 'B', 'B', 'P', 'B', 'B']; // デコード順
// 実際のタイムスタンプ例
const frames = [
{ type: 'I', pts: 0, dts: 0 }, // 最初は一致
{ type: 'P', pts: 400, dts: 100 }, // デコードは早く
{ type: 'B', pts: 100, dts: 200 }, // 表示は P より前
{ type: 'B', pts: 200, dts: 300 },
];
このように、DTS は必ず単調増加(常に増え続ける)する必要がありますが、PTS は前後することがあります。
コンテナフォーマットと muxer の関係
FFmpeg における muxer(マルチプレクサ)は、エンコードされた動画・音声データをコンテナフォーマットに格納する役割を担います。
mermaidflowchart TB
video["動画ストリーム<br/>(H.264/H.265)"] --> muxer["Muxer<br/>(mp4/mov/mkv)"]
audio["音声ストリーム<br/>(AAC/MP3)"] --> muxer
subtitle["字幕ストリーム"] --> muxer
metadata["メタデータ"] --> muxer
muxer --> container["コンテナファイル<br/>(*.mp4/*.mkv)"]
各コンテナフォーマットには、タイムスタンプの扱いに関して異なる仕様や制約があります。
typescript// 主要な muxer とその特徴
interface MuxerConfig {
name: string;
extension: string;
timestampPrecision: number; // タイムスタンプ精度
supportsBFrames: boolean; // B フレームのサポート
strictDTS: boolean; // DTS の厳格性
}
const muxers: MuxerConfig[] = [
{
name: 'mp4',
extension: '.mp4',
timestampPrecision: 1000, // ミリ秒単位
supportsBFrames: true,
strictDTS: true, // DTS の単調性を厳格にチェック
},
{
name: 'matroska',
extension: '.mkv',
timestampPrecision: 1000000, // マイクロ秒単位
supportsBFrames: true,
strictDTS: false, // より柔軟
},
];
MP4 コンテナは DTS の単調性を厳格にチェックするため、「Non-monotonous DTS」エラーが発生しやすいのです。
課題
エラーが発生する典型的なケース
「Non-monotonous DTS in output stream」エラーは、DTS が単調増加していない状態で muxer がデータを受け取ったときに発生します。
ケース 1:動画の結合
複数の動画ファイルを concat フィルタで結合する際、各ファイルのタイムスタンプがリセットされず、DTS が逆転することがあります。
bash# エラーが発生しやすいコマンド例
ffmpeg -i video1.mp4 -i video2.mp4 \
-filter_complex "[0:v][1:v]concat=n=2:v=1[v]" \
-map "[v]" output.mp4
上記のコマンドでは、video1 の最後のフレームと video2 の最初のフレームの DTS が連続しないため、エラーが発生する可能性が高まります。
typescript// 結合時のタイムスタンプの問題
interface ConcatIssue {
video1LastFrame: { pts: 10000; dts: 9800 };
video2FirstFrame: { pts: 0; dts: 0 }; // DTS が逆転!
// 期待値: { pts: 10100, dts: 9900 }
}
ケース 2:ストリームコピー時のフォーマット変換
-c copy オプションで再エンコードせずにコンテナだけを変換する場合、元のタイムスタンプがそのまま使われます。
bash# MKV から MP4 へのストリームコピー
ffmpeg -i input.mkv -c copy output.mp4
MKV ファイルが緩い DTS 管理で作られていた場合、MP4 の厳格な要件を満たせずエラーになります。
typescript// フォーマット変換時の互換性問題
interface FormatConversionIssue {
sourceFormat: {
container: 'mkv';
dtsCheck: 'loose'; // 緩いチェック
timestamps: [0, 100, 90, 200]; // 90 が逆転
};
targetFormat: {
container: 'mp4';
dtsCheck: 'strict'; // 厳格なチェック
result: 'ERROR'; // エラー発生
};
}
ケース 3:可変フレームレート(VFR)動画の処理
画面録画ソフトやゲーム録画で生成される VFR 動画は、フレーム間隔が不均一なため、タイムスタンプの計算が複雑になります。
typescript// VFR 動画のフレーム間隔例
const vfrFrames = [
{ frameNumber: 0, pts: 0, dts: 0 },
{ frameNumber: 1, pts: 33, dts: 33 }, // 通常の間隔
{ frameNumber: 2, pts: 66, dts: 66 },
{ frameNumber: 3, pts: 150, dts: 140 }, // 長い間隔
{ frameNumber: 4, pts: 180, dts: 170 }, // 短い間隔
{ frameNumber: 5, pts: 200, dts: 199 },
{ frameNumber: 6, pts: 210, dts: 198 }, // DTS が逆転!
];
エラーメッセージの詳細解析
実際に表示されるエラーメッセージを見てみましょう。
bash# 典型的なエラー出力
[mp4 @ 0x7f8b4c004000] Non-monotonous DTS in output stream 0:0;
previous: 12345, current: 12340 offset = 1234567890
このエラーメッセージから読み取れる情報は以下の通りです。
typescript// エラーメッセージの構造
interface DTSError {
muxer: 'mp4'; // 使用している muxer
stream: '0:0'; // 対象ストリーム(ファイル:ストリーム番号)
previousDTS: 12345; // 直前のフレームの DTS
currentDTS: 12340; // 現在のフレームの DTS(より小さい)
offset: 1234567890; // 内部的なオフセット値
problem: 'DTS decreased'; // 問題:DTS が減少している
}
| # | 項目 | 説明 | 重要度 |
|---|---|---|---|
| 1 | muxer 名 | エラーを検出したコンテナ形式 | ★★★ |
| 2 | stream | 問題が発生したストリーム番号 | ★★☆ |
| 3 | previous DTS | 直前の DTS 値 | ★★★ |
| 4 | current DTS | 現在の DTS 値(小さい) | ★★★ |
| 5 | offset | タイムベースのオフセット | ★☆☆ |
このエラーの本質は、「12345 → 12340」という DTS の減少にあります。
解決策
解決アプローチの全体像
「Non-monotonous DTS」エラーへの対策は、大きく分けて 3 つのアプローチがあります。
mermaidflowchart TD
error["Non-monotonous DTS<br/>エラー発生"] --> check{"エラーの<br/>原因は?"}
check -->|"結合・編集"| approach1["アプローチ 1<br/>タイムスタンプの<br/>再計算"]
check -->|"フォーマット変換"| approach2["アプローチ 2<br/>寛容な muxer<br/>への変更"]
check -->|"破損データ"| approach3["アプローチ 3<br/>完全な<br/>再エンコード"]
approach1 --> solution1["fpsfilter, setpts<br/>使用"]
approach2 --> solution2["MKV, MOV<br/>への変更"]
approach3 --> solution3["libx264/libx265<br/>で再エンコード"]
上図のように、原因に応じて適切な対策を選択することで、効率的にエラーを解決できます。
それぞれの方法について、具体的なコマンドと設定を見ていきましょう。
アプローチ 1:タイムスタンプの再計算
fpsfilter による固定フレームレート化
VFR 動画を CFR(固定フレームレート)に変換することで、タイムスタンプの一貫性を保ちます。
bash# fps フィルタで 30fps に固定
ffmpeg -i input.mp4 \
-vf "fps=30" \
-c:v libx264 -preset medium \
-c:a copy \
output.mp4
このコマンドは以下の処理を実行します。
typescript// fps フィルタの動作イメージ
interface FpsFilterProcess {
input: {
frameRate: 'variable';
frames: [
{ time: 0.0; pts: 0 },
{ time: 0.033; pts: 33 },
{ time: 0.15; pts: 150 } // 長い間隔
];
};
output: {
frameRate: 30; // 固定
frameInterval: 33.33; // 1/30秒 = 33.33ms
frames: [
{ time: 0.0; pts: 0 },
{ time: 0.033; pts: 1000 },
{ time: 0.067; pts: 2000 }, // 均等間隔
{ time: 0.1; pts: 3000 }
];
};
}
setpts による PTS の再設定
setpts フィルタを使うと、PTS を明示的に再計算できます。
bash# PTS をフレーム番号から再計算
ffmpeg -i input.mp4 \
-vf "setpts=N/(30*TB)" \
-c:v libx264 -preset medium \
output.mp4
setpts のパラメータ説明です。
typescript// setpts の式の意味
interface SetptsFormula {
N: 'フレーム番号(0から開始)';
TB: 'タイムベース(通常は 1/fps)';
formula: 'N/(30*TB)';
example: {
frame0: { N: 0; result: '0/(30*TB) = 0' };
frame1: { N: 1; result: '1/(30*TB) = 0.0333...' };
frame2: { N: 2; result: '2/(30*TB) = 0.0666...' };
};
}
| # | オプション | 説明 | 効果 |
|---|---|---|---|
| 1 | N | 現在のフレーム番号 | 0, 1, 2, 3... |
| 2 | TB | タイムベース | 1/フレームレート |
| 3 | N/(30*TB) | 30fps での PTS | 均等な間隔 |
動画結合時のタイムスタンプ調整
複数動画を結合する際は、concat demuxer を使うとタイムスタンプが自動調整されます。
bash# concat demuxer を使った安全な結合
# 1. 結合リストファイルを作成
echo "file 'video1.mp4'" > concat_list.txt
echo "file 'video2.mp4'" >> concat_list.txt
echo "file 'video3.mp4'" >> concat_list.txt
# 2. concat demuxer で結合
ffmpeg -f concat -safe 0 -i concat_list.txt \
-c copy output.mp4
もし上記でエラーが出る場合は、再エンコードを組み合わせます。
bash# タイムスタンプを再計算しながら結合
ffmpeg -f concat -safe 0 -i concat_list.txt \
-vf "fps=30,setpts=N/(30*TB)" \
-c:v libx264 -preset medium \
-c:a aac -b:a 128k \
output.mp4
typescript// concat demuxer の動作
interface ConcatDemuxerProcess {
input: [
{ file: 'video1.mp4'; duration: 10.0; lastDTS: 9800 },
{ file: 'video2.mp4'; duration: 15.0; lastDTS: 14700 }
];
process: {
step1: 'video1 のタイムスタンプをそのまま使用';
step2: 'video2 の開始 DTS を video1 の終了 DTS + 100 に調整';
adjustment: {
video2OriginalStart: 0;
video2NewStart: 9900; // 9800 + 100
};
};
output: {
continuousDTS: true; // DTS が連続
duration: 25.0;
};
}
アプローチ 2:寛容な muxer の選択
Matroska(MKV)形式の利用
MKV コンテナは DTS のチェックが MP4 より寛容なため、エラーを回避できることがあります。
bash# MP4 の代わりに MKV を使用
ffmpeg -i input.mp4 \
-c copy \
output.mkv
各フォーマットの DTS チェックの違いを比較してみましょう。
| # | フォーマット | 拡張子 | DTS チェック | B フレーム | 推奨用途 |
|---|---|---|---|---|---|
| 1 | MP4 | .mp4 | ★★★ 厳格 | ○ | Web 配信 |
| 2 | Matroska | .mkv | ★☆☆ 寛容 | ○ | 編集作業 |
| 3 | MOV | .mov | ★★☆ 中程度 | ○ | Apple 環境 |
| 4 | AVI | .avi | ★☆☆ 寛容 | △ | レガシー |
typescript// フォーマットごとの特性
interface ContainerComparison {
mp4: {
standard: 'ISO/IEC 14496-14';
dtsValidation: 'strict';
errorOnNonMonotonous: true;
webSupport: 'excellent';
useCase: ['Web配信', 'モバイル', 'ストリーミング'];
};
mkv: {
standard: 'Matroska';
dtsValidation: 'lenient';
errorOnNonMonotonous: false; // 寛容
webSupport: 'limited';
useCase: ['編集作業', 'アーカイブ', '高品質保存'];
};
}
MOV 形式の活用
Apple の QuickTime コンテナである MOV も、比較的柔軟にタイムスタンプを扱えます。
bash# MOV 形式で出力
ffmpeg -i input.mp4 \
-c:v libx264 \
-c:a aac \
-movflags +faststart \
output.mov
movflags +faststart オプションは、メタデータをファイルの先頭に配置し、Web でのストリーミング再生を可能にします。
typescript// MOV のメタデータ配置
interface MovFlagsEffect {
withoutFaststart: {
structure: [
'動画データ',
'音声データ',
'メタデータ(moov atom)'
];
streamingStart: '全ダウンロード後'; // 遅い
};
withFaststart: {
structure: [
'メタデータ(moov atom)',
'動画データ',
'音声データ'
];
streamingStart: '即座に開始可能'; // 速い
};
}
アプローチ 3:完全な再エンコード
GOP 構造の最適化
GOP(Group of Pictures)の構造を調整することで、タイムスタンプの問題を根本から解決できます。
bash# GOP サイズを指定して再エンコード
ffmpeg -i input.mp4 \
-c:v libx264 \
-g 60 \
-keyint_min 60 \
-sc_threshold 0 \
-bf 2 \
-c:a aac -b:a 128k \
output.mp4
各オプションの意味を解説します。
typescript// GOP 関連オプションの詳細
interface GopSettings {
options: {
g: {
name: '-g';
value: 60;
meaning: 'GOP サイズ(キーフレーム間隔)';
unit: 'フレーム数';
effect: '60 フレームごとに I フレームを挿入';
};
keyint_min: {
name: '-keyint_min';
value: 60;
meaning: '最小キーフレーム間隔';
effect: 'シーンチェンジでも 60 フレーム以下では I フレームを作らない';
};
sc_threshold: {
name: '-sc_threshold';
value: 0;
meaning: 'シーンチェンジ検出の閾値';
effect: '0 = シーンチェンジ検出を無効化';
};
bf: {
name: '-bf';
value: 2;
meaning: 'B フレームの最大連続数';
effect: 'B フレームを最大 2 枚まで連続使用';
};
};
}
| # | オプション | 値 | 効果 | DTS への影響 |
|---|---|---|---|---|
| 1 | -g | 60 | GOP サイズ固定 | ★★★ 高 |
| 2 | -keyint_min | 60 | 最小間隔固定 | ★★☆ 中 |
| 3 | -sc_threshold | 0 | シーン検出無効 | ★★★ 高 |
| 4 | -bf | 2 | B フレーム制限 | ★★☆ 中 |
B フレームを使わない設定
B フレームが原因でタイムスタンプの問題が起きている場合は、B フレームを無効化します。
bash# B フレームなしで再エンコード
ffmpeg -i input.mp4 \
-c:v libx264 \
-bf 0 \
-g 30 \
-preset medium \
-c:a copy \
output.mp4
B フレームの有無による違いを見てみましょう。
typescript// B フレームあり/なしの比較
interface BframeComparison {
withBframes: {
gopPattern: ['I', 'B', 'B', 'P', 'B', 'B', 'P'];
compression: 'excellent'; // 圧縮率高い
fileSize: '100MB';
dtsComplexity: 'high'; // DTS 計算複雑
displayOrder: [0, 3, 1, 2, 6, 4, 5]; // 表示順序
decodeOrder: [0, 1, 2, 3, 4, 5, 6]; // デコード順序
timestamps: {
frame0: { type: 'I'; pts: 0; dts: 0 };
frame1: { type: 'B'; pts: 100; dts: 300 }; // DTS と PTS が大きく異なる
frame2: { type: 'B'; pts: 200; dts: 400 };
frame3: { type: 'P'; pts: 300; dts: 100 };
};
};
withoutBframes: {
gopPattern: ['I', 'P', 'P', 'P', 'P', 'P'];
compression: 'good'; // 圧縮率やや低い
fileSize: '130MB';
dtsComplexity: 'low'; // DTS 計算シンプル
displayOrder: [0, 1, 2, 3, 4, 5]; // 表示順序
decodeOrder: [0, 1, 2, 3, 4, 5]; // デコード順序(同じ)
timestamps: {
frame0: { type: 'I'; pts: 0; dts: 0 };
frame1: { type: 'P'; pts: 100; dts: 100 }; // DTS と PTS が一致
frame2: { type: 'P'; pts: 200; dts: 200 };
frame3: { type: 'P'; pts: 300; dts: 300 };
};
};
}
B フレームを無効化すると、ファイルサイズは増えますが、DTS と PTS の関係がシンプルになり、エラーが起きにくくなります。
ツーパスエンコーディングによる品質維持
再エンコードで品質を維持したい場合は、ツーパスエンコードを使用しましょう。
bash# 1 パス目:解析
ffmpeg -i input.mp4 \
-c:v libx264 \
-b:v 2M \
-pass 1 \
-g 60 -bf 2 \
-an \
-f null /dev/null
1 パス目では、動画の特性を解析してログファイルを生成します。
bash# 2 パス目:本エンコード
ffmpeg -i input.mp4 \
-c:v libx264 \
-b:v 2M \
-pass 2 \
-g 60 -bf 2 \
-c:a aac -b:a 128k \
output.mp4
2 パス目では、1 パス目の解析結果を使って最適なビットレート配分を行います。
typescript// ツーパスエンコードの仕組み
interface TwoPassEncoding {
pass1: {
purpose: '動画の複雑さを解析';
output: 'ffmpeg2pass-0.log'; // ログファイル
processedData: {
sceneComplexity: [10, 50, 90, 30, 20]; // シーンごとの複雑度
motionVectors: 'analyzed';
optimalBitrate: 'calculated';
};
encoding: false; // 実際のエンコードはしない
};
pass2: {
purpose: '最適なビットレート配分でエンコード';
input: 'ffmpeg2pass-0.log'; // 1 パス目の結果
processedData: {
bitrateAllocation: {
simpleScene: '1.5Mbps'; // 簡単なシーンは低ビットレート
complexScene: '3.0Mbps'; // 複雑なシーンは高ビットレート
};
};
encoding: true; // 実際にエンコード
output: 'output.mp4';
};
}
アプローチ 4:エラーの無視(最終手段)
**警告:この方法は推奨されません。**データの破損や再生不良の原因となる可能性があります。
bash# -fflags +igndts でエラーを無視(非推奨)
ffmpeg -fflags +igndts -i input.mp4 \
-c copy \
output.mp4
このオプションは、DTS のエラーを無視して処理を続行しますが、出力ファイルに問題が残る可能性があります。
typescript// エラー無視のリスク
interface IgnoreDTSRisks {
option: '-fflags +igndts';
behavior: 'DTS エラーを無視して処理継続';
risks: [
'再生時にフレームスキップが発生',
'音声と映像の同期ずれ',
'一部プレイヤーで再生不可',
'シークが正常に動作しない',
'動画編集ソフトで読み込めない'
];
useCase: [
'一時的な確認用',
'他の方法が全て失敗した場合のみ'
];
recommendation: '他の解決策を優先すべき';
}
具体例
実例 1:複数動画の結合で発生するエラー
問題の状況
3 つの動画ファイルを結合しようとした際に、以下のエラーが発生しました。
bash# エラーが発生したコマンド
ffmpeg -i video1.mp4 -i video2.mp4 -i video3.mp4 \
-filter_complex "[0:v][1:v][2:v]concat=n=3:v=1[outv]" \
-map "[outv]" output.mp4
# エラーメッセージ
[mp4 @ 0x7f9a8c004000] Non-monotonous DTS in output stream 0:0;
previous: 337000, current: 0 offset = 0
各ファイルの情報を確認してみます。
bash# ファイル情報の確認
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 video1.mp4
# 出力: 11.233
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 video2.mp4
# 出力: 8.967
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 video3.mp4
# 出力: 15.442
typescript// 問題の分析
interface ConcatProblemAnalysis {
video1: {
duration: 11.233;
lastFramePTS: 337000; // 最後のフレームの PTS
lastFrameDTS: 336000; // 最後のフレームの DTS
};
video2: {
duration: 8.967;
firstFramePTS: 0; // リセットされている
firstFrameDTS: 0; // ここで DTS が逆転
};
problem: {
dtsJump: '336000 → 0';
reason: 'concat フィルタがタイムスタンプを調整しない';
};
}
解決方法:concat demuxer の使用
concat demuxer を使って、タイムスタンプを自動調整します。
bash# 手順 1:結合リストファイルの作成
cat > concat_list.txt << EOF
file 'video1.mp4'
file 'video2.mp4'
file 'video3.mp4'
EOF
bash# 手順 2:concat demuxer で結合
ffmpeg -f concat -safe 0 -i concat_list.txt \
-c copy \
output.mp4
もしストリームコピーでエラーが出る場合は、再エンコードします。
bash# 再エンコード版
ffmpeg -f concat -safe 0 -i concat_list.txt \
-vf "fps=30,setpts=N/(30*TB)" \
-c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k \
output.mp4
typescript// 解決後のタイムスタンプ
interface ConcatSolution {
video1: {
startDTS: 0;
endDTS: 336000;
duration: 11.233;
};
video2: {
startDTS: 337000; // 自動調整された
endDTS: 606000;
duration: 8.967;
adjustment: '+337000'; // video1 の終了 DTS + α
};
video3: {
startDTS: 607000; // さらに調整
endDTS: 1070000;
duration: 15.442;
adjustment: '+607000';
};
result: {
continuousDTS: true; // DTS が連続
totalDuration: 35.642;
error: null;
};
}
実例 2:MKV から MP4 への変換エラー
問題の状況
アニメやゲーム実況動画など、MKV 形式で配布されている動画を MP4 に変換する際によく発生します。
bash# エラーが発生したコマンド
ffmpeg -i anime_episode01.mkv -c copy output.mp4
# エラーメッセージ
[mp4 @ 0x7f8a9c004000] Non-monotonous DTS in output stream 0:0;
previous: 450120, current: 449880 offset = 0
元ファイルの詳細を確認します。
bash# ストリーム情報の確認
ffprobe -v error -select_streams v:0 \
-show_entries stream=codec_name,r_frame_rate,avg_frame_rate \
-of default=noprint_wrappers=1 anime_episode01.mkv
# 出力例
codec_name=h264
r_frame_rate=24000/1001 # 23.976fps
avg_frame_rate=24000/1001
typescript// 問題の原因分析
interface MKVtoMP4Problem {
sourceFile: {
container: 'mkv';
videoCodec: 'h264';
frameRate: '23.976fps (VFR の可能性)';
dtsManagement: 'lenient'; // 緩い管理
bFrames: true;
timestamps: [
{ pts: 449880; dts: 449500 },
{ pts: 450000; dts: 449880 }, // MKV では許容
{ pts: 450120; dts: 450120 }
];
};
targetContainer: {
container: 'mp4';
dtsRequirement: 'strict'; // 厳格な要件
result: 'ERROR: DTS not monotonous';
};
}
解決方法:MKV のまま使用 or 再エンコード
方法 1:MKV のまま使用する(推奨)
Web 配信でなければ、MKV のまま使用するのが最もシンプルです。
bash# 変換不要
# MKV をそのまま利用
MKV の互換性について確認しておきましょう。
| # | 用途 | MKV | MP4 | 推奨 |
|---|---|---|---|---|
| 1 | ローカル再生 | ○ | ○ | MKV |
| 2 | Web ブラウザ再生 | △ | ○ | MP4 |
| 3 | スマートフォン | △ | ○ | MP4 |
| 4 | 動画編集 | ○ | ○ | どちらも |
| 5 | アーカイブ保存 | ○ | ○ | MKV |
方法 2:MOV に変換する
MP4 より寛容な MOV を使います。
bash# MOV へストリームコピー
ffmpeg -i anime_episode01.mkv -c copy output.mov
typescript// MOV 変換の利点
interface MOVConversion {
advantages: [
'MP4 より DTS チェックが緩い',
'ストリームコピーで高速',
'品質劣化なし'
];
disadvantages: [
'Web ブラウザでの再生サポートが限定的',
'ファイルサイズが若干大きい'
];
useCase: 'ローカル編集や確認用';
}
方法 3:完全再エンコード
どうしても MP4 が必要な場合は、再エンコードします。
bash# 固定 GOP で再エンコード
ffmpeg -i anime_episode01.mkv \
-c:v libx264 \
-preset slow \
-crf 18 \
-g 48 \
-keyint_min 48 \
-sc_threshold 0 \
-bf 3 \
-c:a aac -b:a 192k \
output.mp4
各パラメータの意味です。
typescript// アニメに最適なエンコード設定
interface AnimeEncodingSettings {
preset: {
name: '-preset slow';
meaning: 'エンコード速度と品質のバランス';
options: [
'ultrafast',
'fast',
'medium',
'slow',
'veryslow'
];
recommended: 'slow';
reason: 'アニメは flat な領域が多いため、slow で効率的に圧縮';
};
crf: {
name: '-crf 18';
meaning: '品質設定(低いほど高品質)';
range: '0-51';
recommended: '18-23';
value: 18;
reason: 'アニメの線画をクリアに保つため低めに設定';
};
gopSize: {
name: '-g 48';
meaning: 'GOP サイズ(23.976fps × 2秒)';
calculation: '24 * 2 = 48';
reason: 'シーンチェンジが多いアニメに適した間隔';
};
bframes: {
name: '-bf 3';
meaning: 'B フレーム最大数';
value: 3;
reason: 'flat な領域が多いアニメは B フレームで効率圧縮';
};
}
実例 3:画面録画動画の VFR 問題
問題の状況
OBS Studio などで録画したゲーム実況動画を編集しようとすると、DTS エラーが頻発します。
bash# エラーが発生したコマンド
ffmpeg -i gameplay_recording.mp4 -c copy edited.mp4
# エラーメッセージ(複数回発生)
[mp4 @ 0x7f9a8c004000] Non-monotonous DTS in output stream 0:0;
previous: 123456, current: 123450 offset = 0
[mp4 @ 0x7f9a8c004000] Non-monotonous DTS in output stream 0:0;
previous: 234567, current: 234560 offset = 0
VFR の状態を確認します。
bash# フレームレート情報の詳細確認
ffprobe -v error -select_streams v:0 \
-show_entries stream=r_frame_rate,avg_frame_rate,time_base \
-show_entries packet=pts_time,dts_time,duration_time \
-of csv=p=0 gameplay_recording.mp4 | head -20
typescript// VFR 動画の特徴
interface VFRCharacteristics {
frameRates: {
declared: '60fps'; // 宣言されているフレームレート
actual: 'variable'; // 実際は可変
samples: [
{ frameNumber: 0; interval: 16.67; fps: 60 },
{ frameNumber: 1; interval: 16.67; fps: 60 },
{ frameNumber: 2; interval: 50.0; fps: 20 }, // 処理落ち
{ frameNumber: 3; interval: 16.67; fps: 60 },
{ frameNumber: 4; interval: 33.33; fps: 30 } // 処理落ち
];
};
cause: {
recording: 'ゲームの FPS 変動を忠実に記録';
benefit: 'ファイルサイズ削減';
problem: 'タイムスタンプが不規則になる';
};
}
解決方法:CFR への変換
VFR を CFR(固定フレームレート)に変換します。
bash# fps フィルタで 60fps に固定
ffmpeg -i gameplay_recording.mp4 \
-vf "fps=60" \
-c:v libx264 \
-preset medium \
-crf 23 \
-c:a copy \
output_cfr.mp4
より詳細な制御が必要な場合は、setpts と組み合わせます。
bash# setpts でタイムスタンプを完全にリセット
ffmpeg -i gameplay_recording.mp4 \
-vf "fps=60,setpts=N/(60*TB)" \
-c:v libx264 \
-preset medium \
-crf 23 \
-g 120 \
-keyint_min 120 \
-sc_threshold 0 \
-c:a aac -b:a 192k \
output_fixed.mp4
typescript// CFR 変換の効果
interface CFRConversionEffect {
before: {
type: 'VFR';
frameIntervals: [16.67, 16.67, 50.0, 16.67, 33.33]; // ms
dtsSequence: [0, 1000, 1500, 3500, 5000, 5500]; // 不規則
problems: ['DTS 逆転', 'シーク不安定', '編集困難'];
};
after: {
type: 'CFR';
frameIntervals: [16.67, 16.67, 16.67, 16.67, 16.67]; // 固定
dtsSequence: [0, 1000, 2000, 3000, 4000, 5000]; // 規則的
benefits: ['DTS 単調増加', 'シーク正確', '編集可能'];
};
tradeoff: {
fileSize: '+10-20%'; // ファイルサイズ増加
quality: '±0%'; // 品質は維持
compatibility: '大幅改善';
};
}
実例 4:ライブ配信アーカイブの処理
問題の状況
YouTube や Twitch からダウンロードした配信アーカイブを編集する際に発生します。
bash# エラーが発生したコマンド
ffmpeg -ss 00:10:30 -to 00:25:45 -i stream_archive.mp4 \
-c copy highlight.mp4
# エラーメッセージ
[mp4 @ 0x7f8a9c004000] Non-monotonous DTS in output stream 0:0;
previous: 1890000, current: 1889800 offset = 630000
ライブ配信アーカイブ特有の問題を分析します。
typescript// ライブ配信の特性
interface LiveStreamCharacteristics {
encoding: {
method: 'リアルタイムエンコード';
priority: 'レイテンシ優先(品質より速度)';
timestamps: {
source: 'エンコーダのシステムクロック';
accuracy: 'やや不正確';
adjustments: 'リアルタイムで補正';
};
};
issues: [
'ネットワーク遅延によるタイムスタンプずれ',
'エンコーダの処理落ちによる DTS の乱れ',
'シーンの切り替わりでの GOP 構造の変化',
'音声との同期を優先した調整'
];
specificProblem: {
seeking: '-ss オプションで途中から切り出すと DTS がずれる';
reason: 'キーフレームの位置とタイムスタンプの不一致';
};
}
解決方法:再エンコードによる安定化
シーク位置から再エンコードすることで、タイムスタンプを正規化します。
bash# 正確な切り出しと再エンコード
ffmpeg -ss 00:10:30 -to 00:25:45 -i stream_archive.mp4 \
-vf "fps=30,setpts=N/(30*TB)" \
-c:v libx264 \
-preset medium \
-crf 23 \
-g 60 \
-keyint_min 60 \
-sc_threshold 0 \
-c:a aac -b:a 128k \
highlight.mp4
もっと高速に処理したい場合は、ツーステップで行います。
bash# ステップ 1:キーフレームで大まかに切り出し(高速)
ffmpeg -ss 00:10:25 -to 00:25:50 -i stream_archive.mp4 \
-c copy temp.mp4
# ステップ 2:正確な位置で再エンコード
ffmpeg -ss 00:00:05 -to 00:15:20 -i temp.mp4 \
-vf "fps=30,setpts=N/(30*TB)" \
-c:v libx264 \
-preset medium \
-crf 23 \
-g 60 \
-c:a aac -b:a 128k \
highlight.mp4
typescript// ツーステップ処理の利点
interface TwoStepProcessing {
step1: {
operation: 'キーフレームで大まかに切り出し';
method: 'ストリームコピー';
speed: '非常に高速(リアルタイムの 10-20 倍)';
accuracy: '±数秒のずれ';
purpose: '処理対象を大幅に削減';
};
step2: {
operation: '正確な位置で再エンコード';
method: 'フルエンコード';
speed: '通常速度';
accuracy: '完全に正確';
duration: 'step1 で削減された部分のみ処理';
};
totalTime: {
oneStep: '15 分の動画を 15 分で処理';
twoStep: 'step1: 10 秒 + step2: 2 分 = 2 分 10 秒';
improvement: '約 7 倍高速';
};
}
まとめ
「Non-monotonous DTS」エラーは、動画処理において頻繁に遭遇するトラブルですが、原因を正しく理解すれば適切に対処できます。
対策の選択フロー
エラーに遭遇したときは、以下の順序で対策を検討してください。
mermaidflowchart TD
start["Non-monotonous DTS<br/>エラー発生"] --> question1{"Web 配信が<br/>必須?"}
question1 -->|"いいえ"| solution1["MKV/MOV 形式に変更<br/>(ストリームコピー)"]
question1 -->|"はい"| question2{"動画の結合<br/>または編集?"}
question2 -->|"はい"| solution2["concat demuxer<br/>または setpts 使用"]
question2 -->|"いいえ"| question3{"VFR 動画?"}
question3 -->|"はい"| solution3["fps フィルタで<br/>CFR に変換"]
question3 -->|"いいえ"| solution4["GOP 固定で<br/>完全再エンコード"]
solution1 --> check["動作確認"]
solution2 --> check
solution3 --> check
solution4 --> check
check -->|"成功"| end_success["完了"]
check -->|"失敗"| fallback["より厳格な<br/>再エンコード"]
fallback --> end_success
重要ポイントの再確認
本記事で解説した重要なポイントをまとめます。
| # | ポイント | 内容 | 重要度 |
|---|---|---|---|
| 1 | DTS の性質 | DTS は必ず単調増加する必要がある | ★★★ |
| 2 | muxer の選択 | MP4 は厳格、MKV/MOV は寛容 | ★★★ |
| 3 | VFR vs CFR | VFR 動画は CFR に変換すると安定 | ★★☆ |
| 4 | GOP 設定 | 固定 GOP でタイムスタンプが安定 | ★★☆ |
| 5 | concat 方法 | demuxer を使うとタイムスタンプ自動調整 | ★★★ |
| 6 | B フレーム | 無効化すると DTS がシンプルに | ★☆☆ |
推奨ワークフロー
動画処理の目的別に、推奨するワークフローをまとめました。
typescript// 目的別の推奨設定
interface RecommendedWorkflows {
webDelivery: {
purpose: 'Web ブラウザでの配信',
format: 'MP4',
workflow: [
'1. fps フィルタで CFR 化',
'2. setpts でタイムスタンプリセット',
'3. 固定 GOP で libx264 エンコード',
'4. movflags +faststart 付与'
],
command: `
ffmpeg -i input.mp4 \\
-vf "fps=30,setpts=N/(30*TB)" \\
-c:v libx264 -preset medium -crf 23 \\
-g 60 -keyint_min 60 -sc_threshold 0 \\
-c:a aac -b:a 128k \\
-movflags +faststart \\
output.mp4
`.trim()
};
localArchive: {
purpose: 'ローカル保存・アーカイブ',
format: 'MKV',
workflow: [
'1. ストリームコピーで MKV に変換',
'2. エラーが出なければ完了',
'3. エラーが出たら最小限の再エンコード'
],
command: `
ffmpeg -i input.mp4 -c copy output.mkv
`.trim()
};
editing: {
purpose: '動画編集用の中間ファイル',
format: 'MOV (ProRes)',
workflow: [
'1. ProRes コーデックで再エンコード',
'2. I フレームのみ(全フレームがキーフレーム)',
'3. タイムスタンプは自動的に正規化'
],
command: `
ffmpeg -i input.mp4 \\
-c:v prores_ks -profile:v 2 \\
-c:a pcm_s16le \\
output.mov
`.trim()
};
concatenation: {
purpose: '複数動画の結合',
format: 'MP4',
workflow: [
'1. concat_list.txt を作成',
'2. concat demuxer で結合',
'3. エラーが出たら fps + setpts で再エンコード'
],
command: `
# まずはストリームコピーを試す
ffmpeg -f concat -safe 0 -i concat_list.txt -c copy output.mp4
# エラーが出たら再エンコード
ffmpeg -f concat -safe 0 -i concat_list.txt \\
-vf "fps=30,setpts=N/(30*TB)" \\
-c:v libx264 -preset medium -crf 23 \\
-g 60 -c:a aac -b:a 128k \\
output.mp4
`.trim()
};
}
トラブルシューティングチェックリスト
エラーが解決しない場合は、以下のチェックリストを確認してください。
-
元ファイルの確認
ffprobeで元ファイルのストリーム情報を確認しましたか?- フレームレートは CFR ですか、VFR ですか?
- B フレームは使われていますか?
-
コマンドの確認
- ストリームコピー(
-c copy)を使っていませんか? - タイムスタンプのリセット(
setpts)を試しましたか? - GOP 設定(
-g,-keyint_min,-sc_threshold)を指定しましたか?
- ストリームコピー(
-
出力フォーマットの確認
- MP4 以外のフォーマット(MKV, MOV)を試しましたか?
- Web 配信が本当に必須ですか?
-
エンコード設定の確認
- B フレームを無効化(
-bf 0)してみましたか? - プリセットを変更(
-preset slow)してみましたか?
- B フレームを無効化(
typescript// トラブルシューティングの順序
interface TroubleshootingOrder {
level1: {
difficulty: '簡単';
time: '数秒';
actions: [
'MKV 形式に変更してストリームコピー',
'MOV 形式に変更してストリームコピー'
];
};
level2: {
difficulty: '中程度';
time: '数分';
actions: [
'fps フィルタで CFR 化',
'setpts でタイムスタンプリセット',
'concat demuxer の使用'
];
};
level3: {
difficulty: '高度';
time: '十数分';
actions: [
'GOP 固定での完全再エンコード',
'B フレーム無効化',
'ツーパスエンコード'
];
};
}
このエラーに遭遇したときは、焦らずに原因を特定し、状況に応じた適切な対策を選択することが大切です。本記事で紹介した方法を組み合わせることで、ほとんどのケースで解決できるはずです。
動画処理は試行錯誤が必要な場面も多いですが、タイムスタンプの仕組みを理解しておけば、効率的にトラブルシューティングできるようになります。ぜひ本記事を参考にして、快適な動画処理環境を構築してください。
関連リンク
articleFFmpeg 「Non-monotonous DTS」エラー徹底対策:muxer 選定と再エンコード条件
articleFFmpeg コーデック地図 2025:H.264/H.265/AV1/VP9/ProRes/DNxHR の使いどころ
articleFFmpeg バッチ運用プレイブック:監視・再試行・キュー管理を systemd/tmux で回す
articleFFmpeg フィルタグラフ設計術:複雑合成を filtergraph ファイルで可読・再利用化
articleFFmpeg コマンド 100 連発:入力・出力・フィルタ・ストリーム指定の定番集
articleFFmpeg を macOS で最適導入:Homebrew +オプション選定で機能漏れゼロ
articleOllama コマンドチートシート:`run`/`pull`/`list`/`ps`/`stop` の虎の巻
articletRPC とは?型安全なフルスタック通信を実現する仕組みとメリット【2025 年版】
articleJest の “Cannot use import statement outside a module” を根治する手順
articleObsidian プラグイン相性問題の切り分け:セーフモード/最小再現/ログの活用
articleGitHub Copilot が無反応・遅延する時の診断手順:拡張ログ・ネットワーク・プロキシ
articleGitHub Actions のキャッシュがヒットしない原因 10 と対処レシピ
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来