Skip to content

Commit

Permalink
Adapt to SpawnDev.EBML v2 API
Browse files Browse the repository at this point in the history
  • Loading branch information
Zeugma440 committed Aug 22, 2024
1 parent d802fe6 commit 0f61549
Showing 1 changed file with 62 additions and 49 deletions.
111 changes: 62 additions & 49 deletions ATL/AudioData/IO/MKA.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
using ATL.Logging;
using Commons;
using SpawnDev.EBML;
using SpawnDev.EBML.Matroska;
using SpawnDev.EBML.WebM;
using SpawnDev.EBML.Elements;
using SpawnDev.EBML.Segments;
using static ATL.ChannelsArrangements;
using static ATL.TagData;

Expand All @@ -24,6 +24,8 @@ partial class MKA : MetaDataIO, IAudioDataIO
{
private const uint EBML_MAGIC_NUMBER = 0x1A45DFA3; // EBML header

private const int TRACKTYPE_AUDIO = 2;

// Mapping between MKV format ID and ATL format IDs
private static readonly Dictionary<string, int> codecsMapping = new Dictionary<string, int>
{
Expand Down Expand Up @@ -175,23 +177,27 @@ public static bool IsValidHeader(byte[] data)

// ---------- SUPPORT METHODS

private void readPhysicalData(WebMDocumentReader doc)
private void readPhysicalData(Document doc)
{
MasterElement audio = null;
var tracks = doc.Tracks.Where(t => t is { TrackType: TrackType.Audio, FlagEnabled: true, FlagDefault: true }).ToList();
var tracks = doc.GetContainers(@"Segment\Tracks\TrackEntry")
.Where(t => t.GetElement<UintElement>("TrackType")!.Data == TRACKTYPE_AUDIO)
.Where(t => (t.GetElement<UintElement>("FlagEnabled")?.Data ?? 1) == 1)
.Where(t => (t.GetElement<UintElement>("FlagDefault")?.Data ?? 1) == 1)
.ToList();
if (tracks.Count > 0)
{
var track = tracks[0];

var codecId = track.CodecID.ToUpper();
var codecId = track.GetElement<StringElement>("CodecID")!.Data.ToUpper();
if (codecsMapping.TryGetValue(codecId, out var value))
{
var formats = AudioDataIOFactory.GetInstance().getFormats();
var format = formats.Where(f => f.ID == value).ToList();
if (format.Count > 0) containeeAudioFormat = format[0];
}

audio = track.GetContainer(MatroskaId.Audio);
audio = track.GetContainer("Audio");
}
else
{
Expand All @@ -201,30 +207,31 @@ private void readPhysicalData(WebMDocumentReader doc)
// Find AudioDataOffset using Clusters' timecodes
// Try consuming less memory assuming cluster zero has timecode 0
MasterElement startCluster;
var clusterZero = doc.GetContainer(MatroskaId.Segment, MatroskaId.Cluster)!;
if (0 == clusterZero.GetElement<UintElement>(MatroskaId.Timecode)!.Data) startCluster = clusterZero;
var clusterZero = doc.GetContainer(@"Segment\Cluster")!;
if (0 == clusterZero.GetElement<UintElement>("Timestamp")!.Data) startCluster = clusterZero;
else
{
// Search through all clusters
startCluster = doc.GetContainers(MatroskaId.Segment, MatroskaId.Cluster)
.Find(c => 0 == c.GetElement<UintElement>(MatroskaId.Timecode)!.Data);
startCluster = doc.GetContainers(@"Segment\Cluster")
.FirstOrDefault(c => 0 == c.GetElement<UintElement>("Timecode")!.Data);
}

SegmentSource blockStream = null;
var blocks = startCluster?.GetElements<BlockElement>(MatroskaId.BlockGroup, MatroskaId.Block)
.Find(b => 0 == b.Timecode);
if (null == blocks)
var firstBlock = startCluster?.GetElements<BlockElement>(@"BlockGroup\Block")
.FirstOrDefault(b => 0 == b.Timecode);
if (null == firstBlock)
{
var simpleBlocks = startCluster?.GetElements<SimpleBlockElement>(MatroskaId.SimpleBlock)
.Find(sb => 0 == sb.Timecode);
if (simpleBlocks?.Stream != null)
firstBlock = startCluster?.GetElements<SimpleBlockElement>("SimpleBlock")
.FirstOrDefault(sb => 0 == sb.Timecode);

if (firstBlock != null)
{
// Additional offset to remove MKV metadata (e.g. SimpleBlock header & lacing information)
var streamOffset = 4; // 4 for "No Lacing"; does it vary according to lacing ?
blockStream = simpleBlocks.Stream.Slice(streamOffset, simpleBlocks.Stream.Length - streamOffset);
blockStream = firstBlock.SegmentSource.Slice(streamOffset, firstBlock.SegmentSource.Length - streamOffset);
}
}
else blockStream = blocks.Stream;
else blockStream = firstBlock.SegmentSource;

if (blockStream != null)
{
Expand Down Expand Up @@ -260,28 +267,28 @@ private void readPhysicalData(WebMDocumentReader doc)
// Try getting Duration from MKA metadata
if (Utils.ApproxEquals(Duration, 0))
{
var info = doc.SegmentInfo;
var info = doc.GetContainer(@"Segment\Info");
if (info != null)
{
var duration = info.GetElement<FloatElement>(MatroskaId.Duration)?.Data ?? 0;
var scale = info.GetElement<UintElement>(MatroskaId.TimecodeScale)?.Data ?? 0;
var duration = info.GetElement<FloatElement>("Duration")?.Data ?? 0;
var scale = info.GetElement<UintElement>("TimestampScale")?.Data ?? 0;
// Convert ns to ms
Duration = duration * scale / 1000000.0;
}
}

if (audio != null && (0 == SampleRate || null == ChannelsArrangement || UNKNOWN == ChannelsArrangement))
{
if (0 == SampleRate) SampleRate = (int)(audio.GetElement<FloatElement>(MatroskaId.SamplingFrequency)?.Data ?? 0);
ChannelsArrangement ??= GuessFromChannelNumber((int)(audio.GetElement<UintElement>(MatroskaId.Channels)?.Data ?? 0));
if (0 == SampleRate) SampleRate = (int)(audio.GetElement<FloatElement>("SamplingFrequency")?.Data ?? 0);
ChannelsArrangement ??= GuessFromChannelNumber((int)(audio.GetElement<UintElement>("Channels")?.Data ?? 0));
}
}

private void readTag(MasterElement tag)
{
var targets = tag.GetContainer(MatroskaId.Targets)!;
var targetTypeValue = targets.GetElement<UintElement>(MatroskaId.TargetTypeValue)?.Data ?? 0;
var simpleTags = tag.GetContainers(MatroskaId.SimpleTag);
var targets = tag.GetContainer("Targets")!;
var targetTypeValue = targets.GetElement<UintElement>("TargetTypeValue")?.Data ?? 0;
var simpleTags = tag.GetContainers("SimpleTag");

switch (targetTypeValue)
{
Expand All @@ -294,21 +301,21 @@ private void readTag(MasterElement tag)
}
}

private void readSimpleTags(string prefix, List<MasterElement> tags)
private void readSimpleTags(string prefix, IEnumerable<MasterElement> tags)
{
foreach (var tag in tags)
{
var tagName = tag.GetElement<UTF8StringElement>(MatroskaId.TagName)?.Data ?? "";
var tagValue = tag.GetElement<UTF8StringElement>(MatroskaId.TagString)?.Data ?? "";
var tagName = tag.GetElement<UTF8Element>("TagName")?.Data ?? "";
var tagValue = tag.GetElement<UTF8Element>("TagString")?.Data ?? "";
SetMetaField(prefix + "." + tagName.ToLower(), tagValue, true);
}
}

private void readChapters(MasterElement editionEntry)
{
var chapters = editionEntry.GetContainers(MatroskaId.ChapterAtom)
.FindAll(c => 1 == c.GetElement<UintElement>(MatroskaId.ChapterFlagEnabled)!.Data)
.FindAll(c => 0 == c.GetElement<UintElement>(MatroskaId.ChapterFlagHidden)!.Data);
var chapters = editionEntry.GetContainers("ChapterAtom")
.Where(c => 1 == c.GetElement<UintElement>("ChapterFlagEnabled")!.Data)
.Where(c => 0 == c.GetElement<UintElement>("ChapterFlagHidden")!.Data);

tagData.Chapters = new List<ChapterInfo>();
foreach (var chp in chapters) tagData.Chapters.Add(readChapter(chp));
Expand All @@ -317,36 +324,36 @@ private void readChapters(MasterElement editionEntry)
// Only reads 1st level chapters (not nested ChapterAtoms)
private ChapterInfo readChapter(MasterElement chapterAtom)
{
var timeStart = chapterAtom.GetElement<UintElement>(MatroskaId.ChapterTimeStart)!.Data;
var timeEnd = chapterAtom.GetElement<UintElement>(MatroskaId.ChapterTimeEnd)?.Data ?? 0;
var timeStart = chapterAtom.GetElement<UintElement>("ChapterTimeStart")!.Data;
var timeEnd = chapterAtom.GetElement<UintElement>("ChapterTimeEnd")?.Data ?? 0;
var result = new ChapterInfo((uint)(timeStart / 1000000.0));
if (timeEnd > 0) result.EndTime = (uint)(timeEnd / 1000000.0);

// Get the first available title
var display = chapterAtom.GetContainers(MatroskaId.ChapterDisplay);
var display = chapterAtom.GetContainers("ChapterDisplay").ToList();
if (display.Count > 0)
{
result.Title = display[0].GetElement<UTF8StringElement>(MatroskaId.ChapString)!.Data;
result.Title = display[0].GetElement<UTF8Element>("ChapString")!.Data;
}
return result;
}

private void readAttachedFile(MasterElement file)
{
var data = file.GetElement<BinaryElement>(MatroskaId.FileData);
var data = file.GetElement<BinaryElement>("FileData");
if (data != null)
{
Stream stream = data.Stream;
Stream stream = data.SegmentSource;
if (stream != null)
{
var description = file.GetElement<UTF8StringElement>(MatroskaId.FileDescription)?.Data ?? "";
var name = file.GetElement<UTF8StringElement>(MatroskaId.FileName)?.Data ?? "";
var description = file.GetElement<UTF8Element>("FileDescription")?.Data ?? "";
var name = file.GetElement<UTF8Element>("FileName")?.Data ?? "";

var picType = PictureInfo.PIC_TYPE.Generic;
if (name.Contains("cover", StringComparison.InvariantCultureIgnoreCase)) picType = PictureInfo.PIC_TYPE.Front;

stream.Position = 0;
var pic = PictureInfo.fromBinaryData(stream, (int)data.Length, picType, MetaDataIOFactory.TagType.NATIVE, 0);
var pic = PictureInfo.fromBinaryData(stream, (int)data.DataSize, picType, MetaDataIOFactory.TagType.NATIVE, 0);
pic.NativePicCodeStr = name;
pic.Description = description;
tagData.Pictures.Add(pic);
Expand All @@ -365,23 +372,29 @@ protected override bool read(Stream source, ReadTagParams readTagParams)
{
ResetData();
source.Seek(0, SeekOrigin.Begin);
var doc = new WebMDocumentReader(source);

var schemaSet = new EBMLParser();
schemaSet.LoadDefaultSchemas();
schemaSet.RegisterDocumentEngine<MatroskaDocumentEngine>();

var doc = new Document(schemaSet, source);

// Physical data
readPhysicalData(doc);


// Tags
foreach (var tag in doc.GetContainers(MatroskaId.Segment, MatroskaId.Tags, MatroskaId.Tag))
readTag(tag);
foreach (var tag in doc.GetContainers(@"Segment\Tags\Tag")) readTag(tag);

// Chapters
var defaultEdition = doc.GetContainers(MatroskaId.Segment, MatroskaId.Chapters, MatroskaId.EditionEntry)
.FindAll(ee => 1 == ee.GetElement<UintElement>(MatroskaId.EditionFlagDefault)!.Data)
.Find(ee => 0 == ee.GetElement<UintElement>(MatroskaId.EditionFlagHidden)!.Data);
if (null != defaultEdition) readChapters(defaultEdition);
var defaultEdition = doc
.GetContainers(@"Segment\Chapters\EditionEntry")
.Where(ee => 1 == ee.GetElement<UintElement>("EditionFlagDefault")!.Data)
.FirstOrDefault(ee => 0 == ee.GetElement<UintElement>("EditionFlagHidden")!.Data);
if (defaultEdition != null) readChapters(defaultEdition);

// Embedded pictures
foreach (var attachedFile in doc.GetContainers(MatroskaId.Segment, MatroskaId.Attachments, MatroskaId.AttachedFile))
foreach (var attachedFile in doc.GetContainers(@"Segment\Attachments\AttachedFile"))
readAttachedFile(attachedFile);

return true;
Expand Down

0 comments on commit 0f61549

Please sign in to comment.