2024-11-10 18:44:58 +08:00

95 lines
3.6 KiB
C#

using N_m3u8DL_RE.Common.Util;
using System.Security.Cryptography;
namespace Mp4SubtitleParser
{
public class ParsedMP4Info
{
public string? PSSH;
public string? KID;
public string? Scheme;
public bool isMultiDRM;
}
public class MP4InitUtil
{
private static readonly byte[] SYSTEM_ID_WIDEVINE = { 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
private static readonly byte[] SYSTEM_ID_PLAYREADY = { 0x9A, 0x04, 0xF0, 0x79, 0x98, 0x40, 0x42, 0x86, 0xAB, 0x92, 0xE6, 0x5B, 0xE0, 0x88, 0x5F, 0x95 };
public static ParsedMP4Info ReadInit(byte[] data)
{
var info = new ParsedMP4Info();
// parse init
new MP4Parser()
.Box("moov", MP4Parser.Children)
.Box("trak", MP4Parser.Children)
.Box("mdia", MP4Parser.Children)
.Box("minf", MP4Parser.Children)
.Box("stbl", MP4Parser.Children)
.FullBox("stsd", MP4Parser.SampleDescription)
.FullBox("pssh", (box) =>
{
if (!(box.Version == 0 || box.Version == 1))
throw new Exception("PSSH version can only be 0 or 1");
var systemId = box.Reader.ReadBytes(16);
if (SYSTEM_ID_WIDEVINE.SequenceEqual(systemId))
{
var dataSize = box.Reader.ReadUInt32();
var psshData = box.Reader.ReadBytes((int)dataSize);
info.PSSH = Convert.ToBase64String(psshData);
if (info.KID == "00000000000000000000000000000000")
{
info.KID = HexUtil.BytesToHex(psshData[2..18]).ToLower();
info.isMultiDRM = true;
}
}
})
.FullBox("encv", MP4Parser.AllData(data => ReadBox(data, info)))
.FullBox("enca", MP4Parser.AllData(data => ReadBox(data, info)))
.FullBox("enct", MP4Parser.AllData(data => ReadBox(data, info)))
.FullBox("encs", MP4Parser.AllData(data => ReadBox(data, info)))
.Parse(data, stopOnPartial: true);
return info;
}
private static void ReadBox(byte[] data, ParsedMP4Info info)
{
// find schm
var schmBytes = new byte[4] { 0x73, 0x63, 0x68, 0x6d };
var schmIndex = 0;
for (int i = 0; i < data.Length - 4; i++)
{
if (new byte[4] { data[i], data[i + 1], data[i + 2], data[i + 3] }.SequenceEqual(schmBytes))
{
schmIndex = i;
break;
}
}
if (schmIndex + 8 < data.Length)
{
info.Scheme = System.Text.Encoding.UTF8.GetString(data[schmIndex..][8..12]);
}
// if (info.Scheme != "cenc") return;
// find KID
var tencBytes = new byte[4] { 0x74, 0x65, 0x6E, 0x63 };
var tencIndex = -1;
for (int i = 0; i < data.Length - 4; i++)
{
if (new byte[4] { data[i], data[i + 1], data[i + 2], data[i + 3] }.SequenceEqual(tencBytes))
{
tencIndex = i;
break;
}
}
if (tencIndex != -1 && tencIndex + 12 < data.Length)
{
info.KID = HexUtil.BytesToHex(data[tencIndex..][12..28]).ToLower();
}
}
}
}