TSパケットからはじめる(6) - SDT
SDT(Service Description Table)
前回文字列のデコード処理は解決したのでSDTやEITの情報を取得してみます。SDTのPIDは0x11でtable_idが0x42(自ストリーム)、0x46(他ストリーム)となります。今回は自ストリームを取得したいので0x42のテーブルIDのSDTを取得します。
![](https://assets.st-note.com/img/1643610597516-STHBmM0t3h.png?width=800)
SDTのdescriptor()という項目がありますがこの部分はタグ記述子と言われます。SDTには0x48(サービス記述子)と言われる情報が含まれています。
![](https://assets.st-note.com/img/1643611422385-ZGeWgexEnN.png?width=800)
TSファイル読み込み処理のクラスも作成しておきたいと思います。188バイト毎読み込みするのは効率的ではないので、一括(94000個)を読み込みしています。
public class TsReader: IDisposable
{
private FileStream FStream { get; set; } = null;
private BinaryReader Reader { get; set; } = null;
public string Filename {
get {
if (this.FStream != null)
{
return this.FStream.Name;
}
return string.Empty;
} }
private int Packet_Size { get; set; } = 188;
private int StockCount { get; set; } = 94000;
private byte[] StockPacket { get; set; }
private int StockPositon { get; set; } = -1;
private int ReadCount { get; set; } = 0;
private int Correction { get; set; } = 0;
public Int64 TSLength
{
get { return (this.FStream == null) ? -1 : this.FStream.Length / this.Packet_Size; }
}
public Int64 TSPosition
{
get { return (this.FStream == null) ? -1 : this.FStream.Position / this.Packet_Size; }
set
{
if (this.FStream != null)
{
this.FStream.Position = this.Packet_Size * value;
this.StockPositon = -1;
}
}
}
public TsReader(string Filename) : this(Filename, 188)
{
}
public TsReader(string Filename, int Packet_Length)
{
this.FStream = new FileStream(Filename, FileMode.Open, FileAccess.Read);
this.Reader = new BinaryReader(this.FStream);
this.Packet_Size = Packet_Length;
this.ReadCount = 0;
}
public TsPacket Read()
{
if (this.StockPositon == -1 || (this.StockPacket.Length == this.StockPositon * this.Packet_Size))
{
this.StockPacket = this.Reader.ReadBytes(this.StockCount * this.Packet_Size);
this.StockPositon = 0;
if (this.StockPacket.Length == 0)
{
return null;
}
}
// Console.WriteLine("StockLength = {0}, StockPosion x PacketSize = {1}, {2}", this.StockPacket.Length, this.StockPositon * this.Packet_Size, ReadCount);
byte[] rows = new byte[this.Packet_Size];
Array.Copy(this.StockPacket, this.StockPositon * this.Packet_Size, rows, 0, this.Packet_Size);
if (rows[0] != 0x47)
{
// 位置ずれを補正処理はまだ入れていない
return null;
}
this.StockPositon++;
this.ReadCount++;
return new TsPacket(rows);
}
private bool CheckTsFormat()
{
if ((this.StockPacket[0] == 0x47) && (this.StockPacket[this.Packet_Size * 1] == 0x47) && (this.StockPacket[this.Packet_Size * 2] == 0x47))
{
return true;
}
return false;
}
public void Close()
{
this.Dispose();
}
public void Dispose()
{
if (this.Reader != null)
{
this.Reader.Close();
this.Reader = null;
}
if (this.FStream != null)
{
this.FStream.Close();
this.FStream = null;
}
}
}
SDTデータ構造からTsSDTクラスを作成してみます。
public class TsSDT
{
public Dictionary<int, string> ServiceNames { get; set; }
public int transport_stream_id { get; private set; }
public int tableid { get; private set; }
public TsSDT(byte[] rows)
{
using (MemoryStream ms = new MemoryStream(rows))
{
this.tableid = ms.ReadByte();
int section_length = (ms.ReadByte() & 0x0f) << 8 | ms.ReadByte();
this.transport_stream_id = ms.ReadByte() << 8 | ms.ReadByte();
ms.Position += 6;
section_length -= (8 + 4);
B24Decoder b24convert;
this.ServiceNames = new Dictionary<int, string>();
while (section_length != 0)
{
int service_id = ms.ReadByte() << 8 | ms.ReadByte();
ms.Position++;
byte b = (byte)ms.ReadByte();
int running_status = (b & 0xe0) >> 5;
bool free_CA_mode = ((b & 0x20) != 0) ? true : false;
int descriptor_loop_length = (b & 0x0f) << 8 | ms.ReadByte();
section_length -= (5 + descriptor_loop_length);
while (descriptor_loop_length != 0)
{
int tag = ms.ReadByte();
int tag_length = ms.ReadByte();
switch (tag)
{
case 0x48: // サービス記述子
int service_type = ms.ReadByte();
b24convert = new B24Decoder();
string service_provider_name = b24convert.AtriToString(Utils.ReadBytes(ms, ms.ReadByte()));
this.ServiceNames.Add(service_id, b24convert.AtriToString(Utils.ReadBytes(ms, ms.ReadByte())));
break;
default:
ms.Position += tag_length;
break;
}
descriptor_loop_length -= (tag_length + 2);
}
}
}
}
}
PAT、PMT、SDTを取得する処理で読み出しする。
public bool GetProgramInfo()
{
int count = 0;
List<byte> rows = new List<byte>();
// TSファイルの真ん中に移動
tsr.TSPosition = tsr.TSLength / 2;
TsPacket pktt = null;
PSICollector pat_collector = new PSICollector(0x00);
PSICollector pmt_collector = null;
PSICollector sdt_collector = new PSICollector(0x11, 0x42);
Dictionary<PSICollector, bool> collects = new Dictionary<PSICollector, bool>();
collects.Add(sdt_collector, false);
while ((pktt = tsr.Read()) != null)
{
count++;
if (!pat_collector.IsFinished && pat_collector.AddPacket(pktt))
{
TsPAT pat = new TsPAT(pat_collector.Payload.ToArray());
pmt_collector = new PSICollector(pat.program_pid[pat.program_pid.Keys.First()]);
collects.Add(pmt_collector, false);
}
if (pat_collector.IsFinished)
{
bool IsFinish = true;
for (int i = 0; i < collects.Count; i++)
{
if (!collects[collects.Keys.ElementAt(i)])
{
if (collects.Keys.ElementAt(i).AddPacket(pktt))
{
collects[collects.Keys.ElementAt(i)] = true;
}
else
{
IsFinish = false;
}
}
}
if (IsFinish) { break; }
}
if (count >= limit_packet)
{
break;
}
}
this.SDT = new TsSDT(collects.Keys.ElementAt(0).Payload.ToArray());
this.PMT = new TsPMT(collects.Keys.ElementAt(2).Payload.ToArray());
return true;
}
SDTではチャンネル名称を取得することができます。
![](https://assets.st-note.com/img/1643613697434-kDmYzi8poH.png?width=800)
この記事が気に入ったらサポートをしてみませんか?