Skip to content

feat: initial packet-stats implementation - #185

Draft
vroad wants to merge 2 commits into
mainfrom
packet-stats-new
Draft

feat: initial packet-stats implementation#185
vroad wants to merge 2 commits into
mainfrom
packet-stats-new

Conversation

@vroad

@vroad vroad commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

概要

#77 の、mirakc-arib record-serviceにおけるpacket statsを再実装しました。
旧実装では全PIDのcontinuity counter(CC)を単一のカウンターで集計していましたが、映像・音声・字幕などのカテゴリ別にドロップ数を集計するように変更しました。

#77 のレビューコメントでmasnagamさんが指摘したとおり、
PSI/SIは再送される一方でPESのドロップは映像・音声の乱れに直結するなど、パケットの種類によってドロップの影響が異なるのが理由です。
ドロップ数は以下のカテゴリ別で集計します。

  • 映像
  • 音声
  • 字幕(ARIB字幕・文字スーパーの両方)
  • 選択したサービスのPMT

ドロップ数だけでなく、ServiceFilter通過後の全TSパケットについて
TEI(transport_error_indicator)付きのパケット数とスクランブル化されたままのパケット数も集計します。

stopメッセージのヘルプ文言も修正

実際にはrecord-serviceでの録画終了時はstopメッセージが送られるのに、
ヘルプ文言ではendと表記されているのも同時に修正しています。

実装詳細

record-serviceに新オプション--packet-statsを追加しました。
指定時は、次の形式のpacket-statsメッセージを送信します。

{
  "type": "packet-stats",
  "data": {
    "errorPackets": 0,
    "scrambledPackets": 0,
    "droppedPackets": {
      "video": 0,
      "audio": 0,
      "subtitle": 0,
      "pmt": 0
    }
  }
}
  • errorPackets: TEI付きのパケット数
  • scrambledPackets: TSCが0以外のパケット数(NULL PIDとTEI付きパケットは除外)
  • droppedPackets: 不連続のCCから推定したカテゴリ別のドロップ数

packet-statsメッセージは、各chunkevent-endstopメッセージの直前に送信します。
ただし録画開始直後に送信される最初のchunkメッセージの直前でpacket-statsメッセージの送信を行う事はしません。
集計は録画開始後に記録されたパケットのみが対象で、録画開始直後はまだ1パケットも集計されていないからです。

各カウンターは0から開始し、packet-statsメッセージの送信ごとにリセットされます。
各メッセージの値は、前回のpacket-stats(初回は録画開始)からの区間の集計値です。

集計上の制約

ServiceFilterで除外されるパケット

ServiceFilterの後段に来るServiceRecorderで集計を行うため、ServiceFilterで除外されたパケットはerrorPacketsscrambledPacketsに含まれません。

稀なケースかも知れませんが、現在ServiceFilterはTEI付きパケットを特別扱いしていないので、
エラーによりServiceFilterの段階でPIDが既に化けていた場合は過剰(または過小)にerrorPacketsが計上される可能性もあります。

16パケット以上の連続ドロップ

CCは4bitのため、ドロップ数は(CC - 期待値) & 0x0Fで推定しており、正しく数えられるのは15パケットまでです。
16パケット以上が連続してドロップした場合は16で割った余りが計上され、ちょうど16の倍数であればドロップなしと見なされます。

重複パケット

#77 の旧実装は前パケットとの比較で重複を検出していましたが、今回はそれをやめました。
MPEG-2 TSは同一パケットの2回送出を規格上は許していますが、日本のデジタル放送で重複パケットが送られたという報告は見つからず、対応する価値が低いと判断したためです。
前パケットを保持していないため、重複とドロップが区別できず、仮にペイロードを持つ重複パケットが送られた場合は15パケットのドロップとして計上されます。

PAT PID

ServiceFilterはPATを書き換えてからServiceRecorderに渡します。
この際CCは必ず連続している状態になるため、入力TSにおけるPATのドロップは追跡できません。

PCR PID

映像PIDなどと共有されないPCR専用PIDは、ペイロードがなくCCが増加しないため、CCからドロップ数を推定できません。

映像PIDなどとPCR PIDが共有される場合は、そのカテゴリとしての追跡はされますが、
私の手持ちのARIB MPEG2-TSはPCR専用PIDしか使用していませんでした。
「映像PIDなどとPCR PIDが共有されていた」というネット上での報告も私が探した限りは無かったです。

テスト

実ストリームでの確認方法

指定したカテゴリのパケットを実際にドロップさせて、対応するカウンターが増えることを確認できます。
ちょうど良いツールが見当たらなかったため、標準入力のTSの一部パケットをドロップしたり、TEIやスクランブルを付けたりするts-corruptをAIで作成しました。
ts-pids.pytsresynctstablesを使うため、TSDuckが必要です。

ts-pids.pyはPMTを読んでカテゴリに対応するPIDを--pid引数の形で出力し、ts-corrupt.pyがそのPIDのパケットの一部をドロップします。

実行コマンドの例を以下で示します。
mirakc-aribは~/Git/mirakc-arib/でビルドしていて、ts-corruptは~/Git/ts-corruptにあり、mirakcはhttp://ts262:40772で動作している前提です。

export PATH=$PATH:~/Git/mirakc-arib/build/bin/:~/Git/ts-corrupt/
url='http://ts262:40772/api/channels/GR/22/stream?decode=1'
sid=43008

dir=$(mktemp -d)
file="$dir/ring.m2ts"
pids=$(ts-pids.py -u "$url" -s $sid -c video)
echo "file: $file"
echo "pids: $pids"

curl -sN "$url" \
  | ts-corrupt.py --rate 0.001 --burst 4 $pids \
  | mirakc-arib record-service --sid=$sid --file="$file" \
      --chunk-size=$((8192 * 1024)) --num-chunks=8 --packet-stats \
  | jq -c

ts-pids.py-cに渡すカテゴリを変えると、増加するdroppedPacketsのカウンターも変わります。
videoaudiosubtitlepmtdroppedPacketsの同名フィールドに対応します。

otherはPMTに列挙された残りのPID(PCR専用PIDなど)で、カテゴリの分類対象外のためdroppedPacketsのどのカウンターも増えません。
分類対象外のPIDでのドロップが、誤って他のカテゴリに計上されていないかの確認に使えます。

ts-corrupt.py--teiを付けると、パケットをドロップする代わりにTEIを立てるためerrorPacketsが増えます。
--scrambleを付けた場合はTSCにeven key(10)を設定するためscrambledPacketsが増えます。

テスト一覧

AIを使用し、テスト一覧と、テストの意図を示す日本語での簡潔な説明文を生成しました。
(テスト名だけですぐ意図分かりそうな簡単な内容のテストもありますが、全部に対し説明文を生成しました)

PacketStatsCollectorのテスト一覧

TEI

テスト 内容
TeiPacketIsCountedAsErrorAndExcludedFromContinuity TEI付きパケットをエラーに計上し、CCとスクランブルの判定から除外
TeiWithDiscontinuityIndicatorIsStillCountedAsError discontinuity indicatorがあってもTEIを計上
TeiPacketIsCountedEvenWhenPidIsIgnored 未分類PIDのTEIも計上

スクランブル

テスト 内容
ScrambledPacketIsCounted スクランブルされたパケットを計上
ScrambledPacketIsCountedEvenWhenPidIsUnclassified 未分類PIDのスクランブルも計上
ScrambledNullPacketIsNotCounted NULL PIDのスクランブルは除外

PIDの分類

テスト 内容
PmtPidIsClassifiedAsPmt PMT PIDをpmtに分類
UnclassifiedPidIsIgnored 未分類PIDをドロップ数の集計から除外
PmtClassifiesStreamsByCategory PMTの内容から映像・音声・字幕に分類
PmtPidCategoryIsNotUpdatedByInvalidPmt PMT PIDを映像ストリームとして列挙する不正なPMTがPMT PIDのカテゴリを上書きしない
PmtUpdateClassifiesPreviouslyIgnoredPid PMT更新前の未分類PIDのCCを引き継がない
PmtUpdateResetsCategoryOfPidNoLongerReferenced PMTから外れたPIDを追跡対象から除外

PMT PIDの変更

テスト 内容
SetPmtPidResetsCategoryOfPreviousPmtPid 変更前のPMT PIDを追跡対象から除外
ChangingPmtPidKeepsStreamClassificationUntilNewPmt 新しいPMTの受信までは既存のストリーム分類を維持

PCR PIDとNULL PID

テスト 内容
PcrPidIsIgnored PCR専用PIDを追跡しない
PcrPidSharedWithVideoStreamIsClassifiedAsVideo 映像と共有されるPCR PIDを映像に分類
NullPidInPmtIsIgnored PMTに含まれるNULL PIDを追跡しない

CCとドロップ数

テスト 内容
RegularPacketsProduceZeroErrorStatistics 正常な連番パケットでは全カウンターが0
SameCcRecordsDroppedPackets ペイロードありでCCが変化しない場合は15パケットのドロップとして計上
MultipleMissingPacketsIncreasesDropped 複数パケットドロップ時の計算
WrapAroundMissingPacketsIncreasesDropped CCのラップアラウンドを含むドロップ数の計算
NoPayloadSameCcDoesNotIncreaseDropped ペイロードなし・CC不変はドロップとして計上しない
NoPayloadCcChangedIncreasesDropped ペイロードなし・CC変化はドロップとして計上
DiscontinuityIndicatorDoesNotIncreaseDropped discontinuity indicatorがある場合はドロップとして計上しない

PMT更新時のCC状態

テスト 内容
RecategorizedPidDropsItsCcState 追跡対象から外れたPIDのCC状態を破棄
RepeatedPmtKeepsCcState 同一PMTの再受信ではCC状態を維持

リセット

テスト 内容
ResetPacketStatsClearsAllCounters 全集計カウンターをリセット
ServiceRecorderのテスト
  • EventProgress: --packet-statsの有無によるchunk / stop前のpacket-statsメッセージ送信と、最初のchunkの前では送信しないことを確認
  • EventEnd: event-end / stop前のpacket-statsメッセージ送信を確認
  • EventEndDoesNotIncludeItsTriggeringPacketInPacketStats: event-endを発生させた未書き込みパケットが直前のpacket-statsの集計に含まれないことを確認
CLIのテスト
  • cli_tests.sh: record-service--packet-statsを受け付け、空の標準入力に対して終了コード0で終了することを確認

vroad added 2 commits August 5, 2026 13:56
`SendStopMessage()` emits `"type": "stop"`, but the help text documented the
message as `end`.
@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.45088% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.74%. Comparing base (315180a) to head (4cbee39).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/packet_stats_collector.hh 72.00% 3 Missing and 18 partials ⚠️
src/service_recorder.hh 92.50% 0 Missing and 3 partials ⚠️
test/service_recorder_test.cc 96.29% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #185      +/-   ##
==========================================
+ Coverage   75.39%   76.74%   +1.34%     
==========================================
  Files          38       40       +2     
  Lines        4755     5142     +387     
  Branches      477      512      +35     
==========================================
+ Hits         3585     3946     +361     
- Misses        876      879       +3     
- Partials      294      317      +23     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@masnagam

Copy link
Copy Markdown
Member

LGTM.

copyrightがmasnagamになっていますが,あとでmirakc Contributorsに一括置換しておきます

Comment on lines +203 to +206
} else if (stream.isSubtitles() || IsAribSubtitle(stream) ||
IsAribSuperimposedText(stream)) {
SetCategory(pid, PacketCategory::kSubtitle);
}

@vroad vroad Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@masnagam
自分で見ていて気づきましたが、
isSubtitlesはARIBのMPEG2-TSでは絶対にtrueにはならないかと思うので、削除しようと思います。
tsduck-aribのisSubtitles()は、ARIBでは使用しないDVBのDescriptorの内容を見ています。mirakc-aribの既存コードにも使用箇所はありますが、同じ理由でtrueにはならないのではと思いました。

あとカテゴリ名がsubtitleなのにsuperimposed textも含めてしまっていますが、
名称に偽りのある状態なので
これは対象外としても良いかと思いました。

なら別途superimposed textのカテゴリを作るか?と言う論点も出てくると思いますが
一旦カウントしない方針にしようかと思っています。
めったにsuperimposed textは実TSに入らないとは言え視聴に影響あるとは思いますが、
(めったに実TSに入らないので)確認が難しいためです。

https://github.com/mirakc/tsduck-arib/blob/c400025b7d31e26c0c15471e81adf2ad50632281/src/libtsduck/dtv/tables/tsPMT.cpp#L228-L233

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants