shithub: mcfs

ref: 74533c8ff1ab84ff0949d52c3726ccfcfc1bf76a
dir: mcfs/ebml.c

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include "ebml.h"

static Elspec els[] = {
	{EChapterDisplay, "ChapterDisplay", Emaster},
	{ETrackType, "TrackType", Eunsigned},
	{EChapString, "ChapString", Eunicode},
	{ECodecID, "CodecID", Eascii},
	{EFlagDefault, "FlagDefault", Eunsigned},
	{EChapterTrackUID, "ChapterTrackUID", Eunsigned},
	{ESlices, "Slices", Emaster},
	{EChapterTrack, "ChapterTrack", Emaster},
	{EChapterTimeStart, "ChapterTimeStart", Eunsigned},
	{EChapterTimeEnd, "ChapterTimeEnd", Eunsigned},
	{ECueRefTime, "CueRefTime", Eunsigned},
	{ECueRefCluster, "CueRefCluster", Eunsigned},
	{EChapterFlagHidden, "ChapterFlagHidden", Eunsigned},
	{EFlagInterlaced, "FlagInterlaced", Eunsigned},
	{EBlockDuration, "BlockDuration", Eunsigned},
	{EFlagLacing, "FlagLacing", Eunsigned},
	{EFieldOrder, "FieldOrder", Eunsigned},
	{EChannels, "Channels", Eunsigned},
	{EBlockGroup, "BlockGroup", Emaster},
	{EBlock, "Block", Ebinary},
	{EBlockVirtual, "BlockVirtual", Ebinary},
	{ESimpleBlock, "SimpleBlock", Ebinary},
	{ECodecState, "CodecState", Ebinary},
	{EBlockAdditional, "BlockAdditional", Ebinary},
	{EBlockMore, "BlockMore", Emaster},
	{EPosition, "Position", Eunsigned},
	{ECodecDecodeAll, "CodecDecodeAll", Eunsigned},
	{EPrevSize, "PrevSize", Eunsigned},
	{ETrackEntry, "TrackEntry", Emaster},
	{EEncryptedBlock, "EncryptedBlock", Ebinary},
	{EPixelWidth, "PixelWidth", Eunsigned},
	{ECueDuration, "CueDuration", Eunsigned},
	{ECueTime, "CueTime", Eunsigned},
	{ESamplingFrequency, "SamplingFrequency", Efloat},
	{EChapterAtom, "ChapterAtom", Emaster},
	{ECueTrackPositions, "CueTrackPositions", Emaster},
	{EFlagEnabled, "FlagEnabled", Eunsigned},
	{EPixelHeight, "PixelHeight", Eunsigned},
	{ECuePoint, "CuePoint", Emaster},
	{ECRC32, "CRC-32", Ebinary},
	{EReferenceFrame, "ReferenceFrame", Emaster},
	{EReferenceOffset, "ReferenceOffset", Eunsigned},
	{EReferenceTimestamp, "ReferenceTimestamp", Eunsigned},
	{EBlockAdditionID, "BlockAdditionID", Eunsigned},
	{ELaceNumber, "LaceNumber", Eunsigned},
	{EFrameNumber, "FrameNumber", Eunsigned},
	{EDelay, "Delay", Eunsigned},
	{ESliceDuration, "SliceDuration", Eunsigned},
	{ETrackNumber, "TrackNumber", Eunsigned},
	{ECueReference, "CueReference", Emaster},
	{EVideo, "Video", Emaster},
	{EAudio, "Audio", Emaster},
	{ETrackOperation, "TrackOperation", Emaster},
	{ETrackCombinePlanes, "TrackCombinePlanes", Emaster},
	{ETrackPlane, "TrackPlane", Emaster},
	{ETrackPlaneUID, "TrackPlaneUID", Eunsigned},
	{ETrackPlaneType, "TrackPlaneType", Eunsigned},
	{ETimestamp, "Timestamp", Eunsigned},
	{ETimeSlice, "TimeSlice", Emaster},
	{ETrackJoinBlocks, "TrackJoinBlocks", Emaster},
	{ECueCodecState, "CueCodecState", Eunsigned},
	{ECueRefCodecState, "CueRefCodecState", Eunsigned},
	{EVoid, "Void", Ebinary},
	{ETrackJoinUID, "TrackJoinUID", Eunsigned},
	{EBlockAddID, "BlockAddID", Eunsigned},
	{ECueRelativePosition, "CueRelativePosition", Eunsigned},
	{ECueClusterPosition, "CueClusterPosition", Eunsigned},
	{ECueTrack, "CueTrack", Eunsigned},
	{EReferencePriority, "ReferencePriority", Eunsigned},
	{EReferenceBlock, "ReferenceBlock", Esigned},
	{EReferenceVirtual, "ReferenceVirtual", Esigned},
	{EBlockAddIDName, "BlockAddIDName", Eascii},
	{EBlockAdditionMapping, "BlockAdditionMapping", Emaster},
	{EBlockAddIDType, "BlockAddIDType", Eunsigned},
	{EBlockAddIDExtraData, "BlockAddIDExtraData", Ebinary},
	{EBlockAddIDValue, "BlockAddIDValue", Eunsigned},
	{EContentCompAlgo, "ContentCompAlgo", Eunsigned},
	{EContentCompSettings, "ContentCompSettings", Ebinary},
	{EDocTypeExtension, "DocTypeExtension", Emaster},
	{EDocType, "DocType", Eascii},
	{EDocTypeExtensionName, "DocTypeExtensionName", Eascii},
	{EDocTypeExtensionVersion, "DocTypeExtensionVersion", Eunsigned},
	{EDocTypeReadVersion, "DocTypeReadVersion", Eunsigned},
	{EEBMLVersion, "EBMLVersion", Eunsigned},
	{EDocTypeVersion, "DocTypeVersion", Eunsigned},
	{EEBMLMaxIDLength, "EBMLMaxIDLength", Eunsigned},
	{EEBMLMaxSizeLength, "EBMLMaxSizeLength", Eunsigned},
	{EEBMLReadVersion, "EBMLReadVersion", Eunsigned},
	{EChapLanguage, "ChapLanguage", Eascii},
	{EChapLanguageIETF, "ChapLanguageIETF", Eascii},
	{EChapCountry, "ChapCountry", Eascii},
	{ESegmentFamily, "SegmentFamily", Ebinary},
	{EDateUTC, "DateUTC", Etimestamp},
	{ETagLanguage, "TagLanguage", Eascii},
	{ETagLanguageIETF, "TagLanguageIETF", Eascii},
	{ETagDefault, "TagDefault", Eunsigned},
	{ETagBinary, "TagBinary", Ebinary},
	{ETagString, "TagString", Eunicode},
	{EDuration, "Duration", Efloat},
	{EChapProcessPrivate, "ChapProcessPrivate", Ebinary},
	{EChapterFlagEnabled, "ChapterFlagEnabled", Eunsigned},
	{ETagName, "TagName", Eunicode},
	{EEditionEntry, "EditionEntry", Emaster},
	{EEditionUID, "EditionUID", Eunsigned},
	{EEditionFlagHidden, "EditionFlagHidden", Eunsigned},
	{EEditionFlagDefault, "EditionFlagDefault", Eunsigned},
	{EEditionFlagOrdered, "EditionFlagOrdered", Eunsigned},
	{EFileData, "FileData", Ebinary},
	{EFileMimeType, "FileMimeType", Eascii},
	{EFileUsedStartTime, "FileUsedStartTime", Eunsigned},
	{EFileUsedEndTime, "FileUsedEndTime", Eunsigned},
	{EFileName, "FileName", Eunicode},
	{EFileReferral, "FileReferral", Ebinary},
	{EFileDescription, "FileDescription", Eunicode},
	{EFileUID, "FileUID", Eunsigned},
	{EContentEncAlgo, "ContentEncAlgo", Eunsigned},
	{EContentEncKeyID, "ContentEncKeyID", Ebinary},
	{EContentSignature, "ContentSignature", Ebinary},
	{EContentSigKeyID, "ContentSigKeyID", Ebinary},
	{EContentSigAlgo, "ContentSigAlgo", Eunsigned},
	{EContentSigHashAlgo, "ContentSigHashAlgo", Eunsigned},
	{EContentAESSettings, "ContentAESSettings", Emaster},
	{EAESSettingsCipherMode, "AESSettingsCipherMode", Eunsigned},
	{EMuxingApp, "MuxingApp", Eunicode},
	{ESeek, "Seek", Emaster},
	{EContentEncodingOrder, "ContentEncodingOrder", Eunsigned},
	{EContentEncodingScope, "ContentEncodingScope", Eunsigned},
	{EContentEncodingType, "ContentEncodingType", Eunsigned},
	{EContentCompression, "ContentCompression", Emaster},
	{EContentEncryption, "ContentEncryption", Emaster},
	{ECueRefNumber, "CueRefNumber", Eunsigned},
	{EName, "Name", Eunicode},
	{ECueBlockNumber, "CueBlockNumber", Eunsigned},
	{ETrackOffset, "TrackOffset", Esigned},
	{ESeekID, "SeekID", Ebinary},
	{ESeekPosition, "SeekPosition", Eunsigned},
	{EStereoMode, "StereoMode", Eunsigned},
	{EOldStereoMode, "OldStereoMode", Eunsigned},
	{EAlphaMode, "AlphaMode", Eunsigned},
	{EPixelCropBottom, "PixelCropBottom", Eunsigned},
	{EDisplayWidth, "DisplayWidth", Eunsigned},
	{EDisplayUnit, "DisplayUnit", Eunsigned},
	{EAspectRatioType, "AspectRatioType", Eunsigned},
	{EDisplayHeight, "DisplayHeight", Eunsigned},
	{EPixelCropTop, "PixelCropTop", Eunsigned},
	{EPixelCropLeft, "PixelCropLeft", Eunsigned},
	{EPixelCropRight, "PixelCropRight", Eunsigned},
	{EFlagForced, "FlagForced", Eunsigned},
	{EColour, "Colour", Emaster},
	{EMatrixCoefficients, "MatrixCoefficients", Eunsigned},
	{EBitsPerChannel, "BitsPerChannel", Eunsigned},
	{EChromaSubsamplingHorz, "ChromaSubsamplingHorz", Eunsigned},
	{EChromaSubsamplingVert, "ChromaSubsamplingVert", Eunsigned},
	{ECbSubSamplingHorz, "CbSubSamplingHorz", Eunsigned},
	{ECbSubSamplingVert, "CbSubSamplingVert", Eunsigned},
	{EChromaSitingHorz, "ChromaSitingHorz", Eunsigned},
	{EChromaSitingVert, "ChromaSitingVert", Eunsigned},
	{ERange, "Range", Eunsigned},
	{ETransferCharacteristics, "TransferCharacteristics", Eunsigned},
	{EPrimaries, "Primaries", Eunsigned},
	{EMaxCLL, "MaxCLL", Eunsigned},
	{EMaxFALL, "MaxFALL", Eunsigned},
	{EMasteringMetadata, "MasteringMetadata", Emaster},
	{EPrimaryRChromaticityX, "PrimaryRChromaticityX", Efloat},
	{EPrimaryRChromaticityY, "PrimaryRChromaticityY", Efloat},
	{EPrimaryGChromaticityX, "PrimaryGChromaticityX", Efloat},
	{EPrimaryGChromaticityY, "PrimaryGChromaticityY", Efloat},
	{EPrimaryBChromaticityX, "PrimaryBChromaticityX", Efloat},
	{EPrimaryBChromaticityY, "PrimaryBChromaticityY", Efloat},
	{EWhitePointChromaticityX, "WhitePointChromaticityX", Efloat},
	{EWhitePointChromaticityY, "WhitePointChromaticityY", Efloat},
	{ELuminanceMax, "LuminanceMax", Efloat},
	{ELuminanceMin, "LuminanceMin", Efloat},
	{EMaxBlockAdditionID, "MaxBlockAdditionID", Eunsigned},
	{EChapterStringUID, "ChapterStringUID", Eunicode},
	{ECodecDelay, "CodecDelay", Eunsigned},
	{ESeekPreRoll, "SeekPreRoll", Eunsigned},
	{EWritingApp, "WritingApp", Eunicode},
	{ESilentTracks, "SilentTracks", Emaster},
	{ESilentTrackNumber, "SilentTrackNumber", Eunsigned},
	{EAttachedFile, "AttachedFile", Emaster},
	{EContentEncoding, "ContentEncoding", Emaster},
	{EBitDepth, "BitDepth", Eunsigned},
	{ECodecPrivate, "CodecPrivate", Ebinary},
	{ETargets, "Targets", Emaster},
	{EChapterPhysicalEquiv, "ChapterPhysicalEquiv", Eunsigned},
	{ETagChapterUID, "TagChapterUID", Eunsigned},
	{ETagTrackUID, "TagTrackUID", Eunsigned},
	{ETagAttachmentUID, "TagAttachmentUID", Eunsigned},
	{ETagEditionUID, "TagEditionUID", Eunsigned},
	{ETargetType, "TargetType", Eascii},
	{ETrackTranslate, "TrackTranslate", Emaster},
	{ETrackTranslateTrackID, "TrackTranslateTrackID", Ebinary},
	{ETrackTranslateCodec, "TrackTranslateCodec", Eunsigned},
	{ETrackTranslateEditionUID, "TrackTranslateEditionUID", Eunsigned},
	{ESimpleTag, "SimpleTag", Emaster},
	{ETargetTypeValue, "TargetTypeValue", Eunsigned},
	{EChapProcessCommand, "ChapProcessCommand", Emaster},
	{EChapProcessTime, "ChapProcessTime", Eunsigned},
	{EChapterTranslate, "ChapterTranslate", Emaster},
	{EChapProcessData, "ChapProcessData", Ebinary},
	{EChapProcess, "ChapProcess", Emaster},
	{EChapProcessCodecID, "ChapProcessCodecID", Eunsigned},
	{EChapterTranslateID, "ChapterTranslateID", Ebinary},
	{EChapterTranslateCodec, "ChapterTranslateCodec", Eunsigned},
	{EChapterTranslateEditionUID, "ChapterTranslateEditionUID", Eunsigned},
	{EContentEncodings, "ContentEncodings", Emaster},
	{EMinCache, "MinCache", Eunsigned},
	{EMaxCache, "MaxCache", Eunsigned},
	{EChapterSegmentUID, "ChapterSegmentUID", Ebinary},
	{EChapterSegmentEditionUID, "ChapterSegmentEditionUID", Eunsigned},
	{ETrackOverlay, "TrackOverlay", Eunsigned},
	{ETag, "Tag", Emaster},
	{ESegmentFilename, "SegmentFilename", Eunicode},
	{ESegmentUID, "SegmentUID", Ebinary},
	{EChapterUID, "ChapterUID", Eunsigned},
	{ETrackUID, "TrackUID", Eunsigned},
	{EAttachmentLink, "AttachmentLink", Eunsigned},
	{EBlockAdditions, "BlockAdditions", Emaster},
	{EDiscardPadding, "DiscardPadding", Esigned},
	{EProjection, "Projection", Emaster},
	{EProjectionType, "ProjectionType", Eunsigned},
	{EProjectionPrivate, "ProjectionPrivate", Ebinary},
	{EProjectionPoseYaw, "ProjectionPoseYaw", Efloat},
	{EProjectionPosePitch, "ProjectionPosePitch", Efloat},
	{EProjectionPoseRoll, "ProjectionPoseRoll", Efloat},
	{EOutputSamplingFrequency, "OutputSamplingFrequency", Efloat},
	{ETitle, "Title", Eunicode},
	{EChannelPositions, "ChannelPositions", Ebinary},
	{ELanguage, "Language", Eascii},
	{ELanguageIETF, "LanguageIETF", Eascii},
	{ETrackTimestampScale, "TrackTimestampScale", Efloat},
	{EDefaultDecodedFieldDuration, "DefaultDecodedFieldDuration", Eunsigned},
	{EFrameRate, "FrameRate", Efloat},
	{EDefaultDuration, "DefaultDuration", Eunsigned},
	{ECodecName, "CodecName", Eunicode},
	{ECodecDownloadURL, "CodecDownloadURL", Eascii},
	{ETimestampScale, "TimestampScale", Eunsigned},
	{EColourSpace, "ColourSpace", Ebinary},
	{EGammaValue, "GammaValue", Efloat},
	{ECodecSettings, "CodecSettings", Eunicode},
	{ECodecInfoURL, "CodecInfoURL", Eascii},
	{EPrevFilename, "PrevFilename", Eunicode},
	{EPrevUID, "PrevUID", Ebinary},
	{ENextFilename, "NextFilename", Eunicode},
	{ENextUID, "NextUID", Ebinary},
	{EChapters, "Chapters", Emaster},
	{ESeekHead, "SeekHead", Emaster},
	{ETags, "Tags", Emaster},
	{EInfo, "Info", Emaster},
	{ETracks, "Tracks", Emaster},
	{ESegment, "Segment", Emaster},
	{EAttachments, "Attachments", Emaster},
	{EEBML, "EBML", Emaster},
	{ECues, "Cues", Emaster},
	{ECluster, "Cluster", Emaster},
};

static Elspec *
elspec(vlong id)
{
	Elspec *p, *t;
	int m, n;

	n = nelem(els);
	t = els;
	while(n > 1){
		m = n/2;
		p = t + m;
		if(id >= p->id) {
			t = p;
			n = n-m;
		} else
			n = m;
	}

	return id == t->id ? t : nil;
}

int
ebmluintb(u8int *b, vlong sz, vlong *out)
{
	uvlong v, m;
	int c, n;

	*out = 0;
	for(n = 1, m = 0x80, v = 0; n <= 8; v <<= 8, m <<= 7, n++){
		if(n-1 >= sz){
			werrstr("eof");
			return -1;
		}
		c = b[n-1];
		if(n == 1 && c == 0){
			werrstr("invalid number: %02x", c);
			return -1;
		}
		v |= c;
		if(v & m){
			*out = v & ~m;
			return n;
		}
		v &= ~m;
	}
	werrstr("number overflow");

	return -1;
}

int
ebmlsintb(u8int *b, vlong sz, vlong *out)
{
	int n;

	if((n = ebmluintb(b, sz, out)) < 0)
		return -1;
	*out -= (1 << n*7-1) - 1;

	return n;
}

static int
_ebmluint(Biobuf *f, vlong *out, vlong sz, int isid)
{
	uvlong v, m;
	int c, n, i;

	*out = 0;
	for(i = 0, n = 1, m = 0x80, v = 0; i < sz && n <= 8; v <<= 8, m <<= 7, n++){
		if((c = Bgetc(f)) < 0){
			werrstr("eof");
			return -1;
		}
		if(n == 1 && c == 0){
			werrstr("invalid number: %02x at %#llux", c, Boffset(f));
			return -1;
		}
		v |= c;
		if(v & m){
			*out = isid ? v : (v & ~m);
			if(!isid && *out == (1<<(7*n))-1) /* unknown size */
				*out = -1;
			return n;
		}
		if(!isid)
			v &= ~m;
	}
	if(i >= sz)
		werrstr("ebmluint: short read");
	else
		werrstr("ebmluint: overflow");

	return -1;
}

int
ebmluint(Biobuf *f, vlong sz, vlong *out)
{
	return _ebmluint(f, out, sz, 0);
}

int
ebmlid(Biobuf *f, vlong sz, vlong *out)
{
	return _ebmluint(f, out, sz, 1);
}

int
ebmlsint(Biobuf *f, vlong sz, vlong *out)
{
	int n;

	if((n = ebmluint(f, sz, out)) < 0)
		return -1;
	*out -= (1 << n*7-1) - 1;

	return n;
}

int
ebmlfloat(Biobuf *f, vlong sz, double *out)
{
	u32int u;
	union {
		uchar b[8];
		u32int u[2];
		float f;
		double d;
	}x;

	if(sz != 4 && sz != 8){
		werrstr("invalid float size %lld", sz);
		return -1;
	}
	if(Bread(f, x.b, sz) != sz)
		return -1;

	if(sz == 4){
		x.u[0] = (x.u[0]&0xff000000)>>24 | (x.u[0]&0x00ff0000)>>8 | (x.u[0]&0x0000ff00)<<8 | (x.u[0]&0x000000ff)<<24;
		*out = x.f;
	}else if(sz == 8){
		u = (x.u[0]&0xff000000)>>24 | (x.u[0]&0x00ff0000)>>8 | (x.u[0]&0x0000ff00)<<8 | (x.u[0]&0x000000ff)<<24;
		x.u[0] = (x.u[1]&0xff000000)>>24 | (x.u[1]&0x00ff0000)>>8 | (x.u[1]&0x0000ff00)<<8 | (x.u[1]&0x000000ff)<<24;
		x.u[1] = u;
		*out = x.d;
	}

	return 0;
}

vlong
ebmlel(Biobuf *f, vlong sz, Elspec *el, vlong *esz)
{
	vlong x, n, r;
	Elspec *s;

	if(Bgetc(f) < 0)
		return 0;
	Bungetc(f);

	r = ebmlid(f, sz, &x);
	n = r;
	if(r >= sz || r < 0){
		werrstr("id: %r");
		return -1;
	}
	if((s = elspec(x)) != nil){
		*el = *s;
		el->id = x;
		r = ebmluint(f, sz-n, &x);
		n += r;
		if(n >= sz || r < 0 || sz-n < x){
			werrstr("sz: (sz=%lld n=%lld r=%lld x=%lld): %r", sz, n, r, x);
			return -1;
		}
		*esz = x;
	}else{ /* skip invalid elements */
		while(--n > 0)
			Bungetc(f);
		el->id = x;
		el->type = -1;
		*esz = 0;
		return 0;
	}

	return n;
}

vlong
ebmlrawuint(Biobuf *f, vlong sz, vlong *dst)
{
	vlong i;
	int c;

	*dst = 0;
	for(i = 0; i < sz; i++){
		*dst <<= 8;
		if((c = Bgetc(f)) < 0){
			werrstr("eof");
			return -1;
		}
		*dst |= c;
	}

	return 0;
}

vlong
ebmlrawsint(Biobuf *f, vlong sz, vlong *dst)
{
	vlong i, x;
	int c;

	x = 0;
	for(i = 0; i < sz; i++){
		x <<= 8;
		if((c = Bgetc(f)) < 0){
			werrstr("eof");
			return -1;
		}
		x |= c;
	}
	if(x & (0x80ULL<<(i-1)))
		*dst = -(~x + 1);
	else
		*dst = x;

	return 0;
}

char *
ebmltracktype(int t)
{
	static char *types[] = {
		[Etrackvideo] = "video",
		[Etrackaudio] = "audio",
		[Etrackcomplex] = "complex",
		[Etracklogo] = "logo",
		[Etracksubtitles] = "subtitle",
		[Etrackbuttons] = "buttons",
		[Etrackcontrol] = "control",
		[Etrackmetadata] = "metadata",
	};

	return t < nelem(types) ? types[t] : "???";
}