shithub: mcfs

Download patch

ref: ace30eb522ce18622b465e550c6926d895101cb0
parent: aad9511a85b15ced2fde315c693ce12a05f1f315
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Tue Dec 15 16:41:48 EST 2020

move stuff around to make the code better

--- /dev/null
+++ b/aac.c
@@ -1,0 +1,24 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "common.h"
+#include "packet.h"
+
+int
+aacpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts)
+{
+	int i;
+
+	USED(ts);
+	if(ctx->codec.priv.sz > 0 && Bwrite(out, ctx->codec.priv.data, ctx->codec.priv.sz) != ctx->codec.priv.sz)
+		goto err;
+	for(i = 0; i < np; i++, p++){
+		if(Bwrite(out, p->data, p->sz) != p->sz)
+			goto err;
+	}
+
+	return 0;
+err:
+	werrstr("aacpacket: %r");
+	return -1;
+}
--- a/common.h
+++ b/common.h
@@ -16,64 +16,8 @@
 	FmtMp3 = 0x006d7033u,
 };
 
-typedef struct Ebml Ebml;
-typedef struct Packet Packet;
-typedef struct Packetctx Packetctx;
-typedef int (*packet_f)(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
+#define min(a,b) ((a)<=(b)?(a):(b))
 
-struct Packet {
-	uchar *data;
-	int sz;
-};
-
-struct Packetctx {
-	uvlong frid;
-	uvlong duration;
-	uvlong seekpreroll;
-	vlong discardpad;
-	vlong blockdur;
-	struct {
-		char name[16];
-		vlong delay;
-		struct {
-			uchar *data;
-			int sz;
-		}priv;
-	}codec;
-	u32int trackuid;
-	u32int fmt;
-
-	struct {
-		int width, height;
-		struct {
-			int left, right, top, bottom;
-		}crop;
-		struct {
-			int width, height;
-			int unit;
-			int aspectmode;
-		}display;
-	}video;
-	struct {
-		float samplerate;
-		int channels;
-		int bps;
-	}audio;
-};
-
-struct Ebml {
-	Packetctx;
-
-	packet_f fpacket;
-
-	vlong timestampscale;
-	vlong perframe;
-	int tracknum;
-	int tracktype;
-	vlong codecdelay;
-	char lang[8];
-};
-
 extern Biobuf stderr, out;
 extern int dflag, trackdump;
 
@@ -84,77 +28,6 @@
 int srttsfmt(Fmt *f);
 
 int isorun(Biobuf *f);
-int ebmlrun(Biobuf *f);
+int matroskarun(Biobuf *f);
 
 u32int crc32(u32int init, u8int *d, ulong len);
-
-int ivfpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
-int oggpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
-int srtpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
-
-int ebmlint(Biobuf *f, vlong *out, int isid);
-vlong ebmlel(Biobuf *f, vlong sz, vlong *id, vlong *esz);
-vlong ebmlrawint(Biobuf *f, vlong sz, vlong *dst);
-
-#define ebmlgetnumber(expid, dest) \
-	if(id == expid){ \
-		vlong x; \
-		if(ebmlrawint(f, sz, &x) < 0) \
-			return -1; \
-		dest = x; \
-		left -= sz; \
-		continue; \
-	}
-
-#define ebmlgetstring(expid, dest) \
-	if(id == expid){ \
-		n = min(sizeof(dest)-1, sz); \
-		if(Bread(f, dest, n) != n) \
-			return -1; \
-		dest[n] = 0; \
-		if(n != sz) \
-			Bseek(f, sz-n, 1); \
-		left -= sz; \
-		continue; \
-	}
-
-#define ebmlgetbytes(expid, dest) \
-	if(id == expid){ \
-		dest.data = malloc(sz); \
-		if(Bread(f, dest.data, sz) != sz) \
-			return -1; \
-		dest.sz = sz; \
-		left -= sz; \
-		continue; \
-	}
-
-#define ebmlgetfloat(expid, dest) \
-	if(id == expid){ \
-		u32int u; \
-		union { \
-			uchar b[8]; \
-			u32int u[2]; \
-			float f; \
-			double d; \
-		}x; \
-		if(sz == 4){ \
-			if(Bread(f, x.b, 4) != 4) \
-				return -1; \
-			x.u[0] = (x.u[0]&0xff000000)>>24 | (x.u[0]&0x00ff0000)>>8 | (x.u[0]&0x0000ff00)<<8 | (x.u[0]&0x000000ff)<<24; \
-			dest = x.f; \
-		}else if(sz == 8){ \
-			if(Bread(f, x.b, 8) != 8) \
-				return -1; \
-			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; \
-			dest = x.d; \
-		}else{ \
-			werrstr("invalid float size"); \
-			break; \
-		} \
-		left -= sz; \
-		continue; \
-	}
-
-#define min(a,b) ((a)<=(b)?(a):(b))
--- a/ebml.c
+++ b/ebml.c
@@ -3,23 +3,288 @@
 #include <bio.h>
 #include <ctype.h>
 #include "common.h"
-#include "elem.h"
+#include "ebml.h"
 
-enum
-{
-	Trackvideo = 1,
-	Trackaudio = 2,
-	Trackcomplex = 3,
-	Tracklogo = 16,
-	Tracksubtitles = 17,
-	Trackbuttons = 18,
-	Trackcontrol = 32,
-	Trackmetadata = 33,
+static Elspec els[] = {
+	{EChapterDisplay, "ChapterDisplay", Emaster},
+	{ETrackType, "TrackType", Eunsigned},
+	{EChapString, "ChapString", Eunicode},
+	{ECodecID, "CodecID", Esigned},
+	{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", Esigned},
+	{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 Packet packets[256];
-static vlong stack[32];
+Elspec *
+ebmlelspec(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, int sz, vlong *out)
 {
@@ -61,8 +326,8 @@
 	return n;
 }
 
-int
-ebmluint(Biobuf *f, vlong *out, int isid)
+static int
+_ebmluint(Biobuf *f, vlong *out, int isid)
 {
 	uvlong v, m;
 	int c, n;
@@ -91,11 +356,23 @@
 }
 
 int
+ebmluint(Biobuf *f, vlong *out)
+{
+	return _ebmluint(f, out, 0);
+}
+
+int
+ebmlid(Biobuf *f, vlong *out)
+{
+	return _ebmluint(f, out, 1);
+}
+
+int
 ebmlsint(Biobuf *f, vlong *out)
 {
 	int n;
 
-	if((n = ebmluint(f, out, 0)) < 0)
+	if((n = ebmluint(f, out)) < 0)
 		return -1;
 	*out -= (1 << n*7-1) - 1;
 
@@ -107,15 +384,13 @@
 {
 	vlong x, n, r;
 
-	if(sz < 2)
-		return -1;
 	n = 0;
-	if((n += (r = ebmluint(f, &x, 1))) >= sz || r < 0){
+	if((n += (r = ebmlid(f, &x))) >= sz || r < 0){
 		werrstr("id: %r");
 		return -1;
 	}
 	*id = x;
-	if((n += (r = ebmluint(f, &x, 0))) >= sz || r < 0 || sz-n < x){
+	if((n += (r = ebmluint(f, &x))) >= sz || r < 0 || sz-n < x){
 		werrstr("sz: (sz=%zd n=%zd r=%zd x=%zd): %r", sz, n, r, x);
 		return -1;
 	}
@@ -143,413 +418,19 @@
 	return 0;
 }
 
-static char *
-format(Ebml *e)
+char *
+ebmltracktype(int t)
 {
-	static char t[16];
-	char *s;
-	int n;
-
-	if(e->tracktype == Tracksubtitles){
-		if(strcmp(e->codec.name, "S_TEXT/UTF8") == 0)
-			return "srt";
-	}else if(e->tracktype == Trackaudio){
-		if(strcmp(e->codec.name, "A_MPEG/L3") == 0)
-			return "mp3";
-	}else if(e->tracktype == Trackvideo){
-		if(strcmp(e->codec.name, "V_MPEG4/ISO/AVC") == 0)
-			return "h264";
-	}
-
-	n = snprint(t, sizeof(t), "%s", e->codec.name+2);
-	for(n -= 1; n >= 0; n--)
-		t[n] = tolower(t[n]);
-	if((s = strchr(t, '/')) != nil)
-		*s = 0;
-
-	return t;
-}
-
-static int
-asispacket(Biobuf *out, Packetctx *, Packet *p, int np, uvlong)
-{
-	int i;
-
-	for(i = 0; i < np; i++, p++)
-		Bwrite(out, p->data, p->sz);
-
-	return 0;
-}
-
-static int
-initctx(Ebml *e, double duration)
-{
-	char *c;
-
-	e->duration = duration;
-	e->duration *= e->timestampscale;
-	c = format(e);
-	if(e->tracktype == Trackvideo){
-		if(strcmp(c, "vp9") == 0)
-			e->fmt = FmtVp09;
-		else if(strcmp(c, "vp8") == 0)
-			e->fmt = FmtVp08;
-		else if(strcmp(c, "h264") == 0)
-			e->fmt = FmtAvc1;
-		else
-			goto err;
-		e->fpacket = ivfpacket;
-		return 0;
-	}else if(e->tracktype == Trackaudio){
-		if(strcmp(c, "vorbis") == 0){
-			e->fmt = FmtVorbis;
-			e->fpacket = oggpacket;
-		}else if(strcmp(c, "opus") == 0){
-			e->fmt = FmtOpus;
-			e->fpacket = oggpacket;
-		}else if(strcmp(c, "mp3") == 0){
-			e->fmt = FmtMp3;
-			e->fpacket = asispacket;
-		}else if(strcmp(c, "aac") == 0){
-			e->fmt = FmtMp4a;
-			e->fpacket = asispacket;
-		}else
-			goto err;
-		return 0;
-	}else if(e->tracktype == Tracksubtitles){
-		if(strcmp(c, "srt") == 0){
-			e->fmt = FmtSrt;
-			e->fpacket = srtpacket;
-		}else
-			goto err;
-		return 0;
-	}
-
-err:
-	werrstr("don't know how to remux %s (track type %d)", e->codec.name, e->tracktype);
-
-	return -1;
-}
-
-static char *
-tracktype(Ebml *e)
-{
 	static char *types[] = {
-		[Trackvideo] = "video",
-		[Trackaudio] = "audio",
-		[Trackcomplex] = "complex",
-		[Tracklogo] = "logo",
-		[Tracksubtitles] = "subtitle",
-		[Trackbuttons] = "buttons",
-		[Trackcontrol] = "control",
-		[Trackmetadata] = "metadata",
+		[Etrackvideo] = "video",
+		[Etrackaudio] = "audio",
+		[Etrackcomplex] = "complex",
+		[Etracklogo] = "logo",
+		[Etracksubtitles] = "subtitle",
+		[Etrackbuttons] = "buttons",
+		[Etrackcontrol] = "control",
+		[Etrackmetadata] = "metadata",
 	};
-	char *t;
 
-	t = e->tracktype < nelem(types) ? types[e->tracktype] : "???";
-	if(t == nil)
-		t = "???";
-
-	return t;
-}
-
-static void
-trackinfo(Biobuf *o, Ebml *e)
-{
-	char *t;
-
-	if(dflag){
-		Bprint(o, "per frame: %zd\n", e->perframe);
-		Bprint(o, "track number: %d\n", e->tracknum);
-		Bprint(o, "track type: %d\n", e->tracktype);
-		Bprint(o, "codec name: %s\n", e->codec.name);
-		Bprint(o, "codec priv data: %d bytes [%.*H]\n", e->codec.priv.sz, e->codec.priv.sz, e->codec.priv.data);
-		Bprint(o, "codec delay: %zdns\n", e->codec.delay);
-		if(e->tracktype == Trackvideo){
-			Bprint(o, "video: %dx%d [%d,%d,%d,%d]\n", e->video.width, e->video.height, e->video.crop.left, e->video.crop.top, e->video.crop.right, e->video.crop.bottom);
-			Bprint(o, "display: %dx%d unit=%d aspectmode=%d\n", e->video.display.width, e->video.display.height, e->video.display.unit, e->video.display.aspectmode);
-		}else if(e->tracktype == Trackaudio){
-			Bprint(o, "audio: samplerate=%g, channels=%d, bps=%d\n", e->audio.samplerate, e->audio.channels, e->audio.bps);
-		}
-		Bprint(o, "seek pre-roll: %zd\n", e->seekpreroll);
-	}
-
-	t = tracktype(e);
-	Bprint(o, "%d\t%s\t%s\t", e->tracknum, t, format(e));
-	if(e->tracktype == 1)
-		Bprint(o, "%d\t%d", e->video.width, e->video.height);
-	else if(e->tracktype == 2)
-		Bprint(o, "%d\t%d", e->audio.channels, (int)e->audio.samplerate);
-	else if(e->tracktype == 17)
-		Bprint(o, "%s", e->lang);
-	else
-		Bprint(o, "???");
-	Bprint(o, "\n");
-}
-
-int
-ebmlrun(Biobuf *f)
-{
-	int isebml, npackets, i, sti;
-	vlong left, id, n, sz, bufsz, track, off, packetsz, x, endtracks;
-	uvlong ts, timestamp, timestampscale;
-	uchar *buf;
-	double duration;
-	Ebml e, te;
-	Element *el;
-
-	buf = nil;
-	bufsz = 0;
-	track = -1;
-	timestamp = 0;
-	left = (1ULL<<63)-1;
-	endtracks = -1;
-	te.tracknum = -1;
-	timestampscale = 1000000;
-	e.tracknum = -1;
-	duration = 0;
-	ts = 0;
-	sti = 0;
-	for(isebml = 0; left != 0;){
-		if(id == EBlockDuration)
-			te.blockdur *= timestampscale;
-		if(endtracks > 0 && left < endtracks && trackdump == Nodump){
-			/* early exit */
-			left = 0;
-			break;
-		}
-
-		off = Boffset(f);
-		if((n = ebmlel(f, left, &id, &sz)) < 0){
-			werrstr("invalid ebml: %r at %#zx", off);
-			goto err;
-		}
-		while(sti > 0 && off >= stack[sti-1])
-			sti--;
-		left -= n;
-		if(dflag > 1){
-			if((el = ebmlid2elem(id)) != nil){
-				for(i = 0; i < sti; i++)
-					Bputc(&stderr, '\t');
-				Bprint(&stderr, "%s\n", el->name);
-				if(el->type == Emaster && sti < nelem(stack))
-					stack[sti++] = off+n+sz;
-			}
-			else
-				Bprint(&stderr, "%#llx\n", id);
-		}
-
-		if(id == EEBML){ /* EBML comes first */
-			if(isebml != 0){
-				werrstr("double EBML?");
-				goto err;
-			}
-			isebml++;
-		}else if(id == ESegment){
-			left = sz;
-			if(isebml != 1){
-				werrstr("invalid ebml");
-				goto err;
-			}
-			isebml++; /* make sure we don't see more segments */
-			continue; /* go in */
-		}else if(id == EInfo){ /* segment info */
-			continue;
-		}else if(id == ETracks){
-			endtracks = left - sz; /* to skip early in case track dump not required */
-			continue;
-		}else if(id == ECluster){
-			if(trackdump != Nodump) /* skip it entirely if no dump required */
-				continue;
-		}else if(id == ETrackEntry){ /* track entry */
-			if(e.tracknum > 0){
-				if(trackdump == Nodump)
-					trackinfo(&out, &e);
-				else if(trackdump == Dumpvideo && e.tracktype == Trackvideo)
-					trackdump = e.tracknum;
-				else if(trackdump == Dumpaudio && e.tracktype == Trackaudio)
-					trackdump = e.tracknum;
-			}
-			if(e.tracknum == trackdump)
-				memmove(&te, &e, sizeof(e));
-			memset(&e, 0, sizeof(e));
-			e.timestampscale = timestampscale;
-			continue;
-		}else if(id == EVideo || id == EAudio){
-			continue;
-		}else if(id == EBlockGroup && trackdump != Nodump){
-			continue;
-		}else if((id == ESimpleBlock || id == EBlock) && trackdump != Nodump){
-			if(te.tracknum == -1)
-				memmove(&te, &e, sizeof(e));
-			if((n = ebmluint(f, &track, 0)) < 0){
-				werrstr("block: %r");
-				goto err;
-			}
-			left -= n;
-			sz -= n;
-			if(trackdump == Dumpvideo && e.tracktype == Trackvideo)
-				trackdump = te.tracknum;
-			else if(trackdump == Dumpaudio && e.tracktype == Trackaudio)
-				trackdump = te.tracknum;
-			if(track == trackdump && track == te.tracknum){
-				if(te.fpacket == nil && initctx(&te, duration) != 0){
-					werrstr("packet: %r");
-					goto err;
-				}
-
-				if(bufsz < sz){
-					buf = realloc(buf, sz);
-					bufsz = sz;
-				}
-				if(Bread(f, buf, sz) != sz){
-					werrstr("short read");
-					goto err;
-				}
-				left -= sz;
-				sz -= 3;
-				npackets = buf[3]+1;
-				switch((buf[2] >> 1) & 3){ /* lacing */
-				case 0: /* none */
-					packets[0].data = buf+3;
-					packets[0].sz = sz;
-					npackets = 1;
-					break;
-				case 1: /* xiph */
-					sz--;
-					off = 4;
-					for(i = 0; i < npackets-1; i++){
-						packets[i].sz = 0;
-						do{
-							packets[i].sz += buf[off];
-						}while(buf[off++] == 0xff);
-					}
-					for(i = 0; i < npackets-1; i++){
-						packets[i].data = buf+off;
-						off += packets[i].sz;
-						sz -= packets[i].sz;
-					}
-					packets[i].data = buf+off;
-					packets[i].sz = sz;
-					break;
-				case 2: /* fixed-size */
-					sz--;
-					if((sz % npackets) != 0){
-						werrstr("invalid number of frames with fixed-size lacing");
-						goto err;
-					}
-					packets[0].sz = sz / npackets;
-					for(i = 0; i < npackets; i++){
-						packets[i].data = buf+4 + i*packets[0].sz;
-						packets[i].sz = packets[0].sz;
-						sz -= packets[0].sz;
-					}
-					break;
-				case 3: /* ebml */
-					sz--;
-					packetsz = 0;
-					off = 4;
-					for(i = 0; i < npackets-1; i++){
-						if((n = (i == 0 ? ebmluintb : ebmlsintb)(buf+off, sz, &x)) < 0)
-							goto err;
-						packetsz += x;
-						if(packetsz < 0){
-							werrstr("invalid frame size %zd", packetsz);
-							goto err;
-						}
-						packets[i].sz = packetsz;
-						off += n;
-						sz -= n;
-					}
-					for(i = 0; i < npackets-1; i++){
-						if(packets[i].sz > sz){
-							werrstr("frame %d/%d out of bounds: %d > %zd", i, npackets-1, packets[i].sz, sz);
-							goto err;
-						}
-						packets[i].data = buf+off;
-						off += packets[i].sz;
-						sz -= packets[i].sz;
-					}
-					packets[i].data = buf+off;
-					packets[i].sz = sz;
-					break;
-				}
-
-				if(te.fpacket(&out, &te, packets, npackets, ts) != 0)
-					goto err;
-				/* ns timestamp */
-				ts = (timestamp + (s16int)(buf[0]<<8 | buf[1])) * timestampscale - te.codec.delay;
-				continue;
-			}
-		}else{
-				ebmlgetnumber(ETimestampScale, timestampscale)
-			else
-				ebmlgetfloat(ESamplingFrequency, e.audio.samplerate)
-			else
-				ebmlgetnumber(EChannels, e.audio.channels)
-			else
-				ebmlgetnumber(EBitDepth, e.audio.bps)
-			else
-				ebmlgetnumber(ETrackNumber, e.tracknum)
-			else
-				ebmlgetnumber(ETrackType, e.tracktype)
-			else
-				ebmlgetstring(ECodecID, e.codec.name)
-			else
-				ebmlgetbytes(ECodecPrivate, e.codec.priv)
-			else
-				ebmlgetnumber(ECodecDelay, e.codec.delay)
-			else
-				ebmlgetnumber(EPixelWidth, e.video.width)
-			else
-				ebmlgetnumber(EPixelHeight, e.video.height)
-			else
-				ebmlgetnumber(EPixelCropBottom, e.video.crop.bottom)
-			else
-				ebmlgetnumber(EPixelCropTop, e.video.crop.top)
-			else
-				ebmlgetnumber(EPixelCropLeft, e.video.crop.left)
-			else
-				ebmlgetnumber(EPixelCropRight, e.video.crop.right)
-			else
-				ebmlgetnumber(EDisplayWidth, e.video.display.width)
-			else
-				ebmlgetnumber(EDisplayHeight, e.video.display.height)
-			else
-				ebmlgetnumber(EDisplayUnit, e.video.display.unit)
-			else
-				ebmlgetnumber(EAspectRatioType, e.video.display.aspectmode)
-			else
-				ebmlgetnumber(ETimestamp, timestamp)
-			else
-				ebmlgetnumber(EDefaultDuration, e.perframe)
-			else
-				ebmlgetnumber(ESeekPreRoll, e.seekpreroll)
-			else
-				ebmlgetfloat(EDuration, duration)
-			else
-				ebmlgetnumber(ETrackUID, e.trackuid)
-			else
-				ebmlgetnumber(EDiscardPadding, te.discardpad)
-			else
-				ebmlgetnumber(EBlockDuration, te.blockdur)
-			else
-				ebmlgetstring(ELanguage, e.lang)
-		}
-
-		if(Bseek(f, sz, 1) < 0)
-			return -1;
-		left -= sz;
-	}
-
-	if(isebml == 2 && left == 0){
-		if(e.tracknum > 0){
-			if(trackdump == Nodump)
-				trackinfo(&out, &e);
-		}
-		return 0;
-	}
-
-err:
-	return -1;
+	return t < nelem(types) ? types[t] : "???";
 }
--- /dev/null
+++ b/ebml.h
@@ -1,0 +1,296 @@
+typedef struct Elspec Elspec;
+
+struct Elspec {
+	vlong id;
+	char *name;
+	int type;
+};
+
+enum {
+	/* ebml element value types */
+	Emaster,
+	Ebinary,
+	Efloat,
+	Eunsigned,
+	Esigned,
+	Eunicode,
+	Eascii = Eunicode,
+	Etimestamp,
+
+	/* known track types */
+	Etrackvideo = 1,
+	Etrackaudio = 2,
+	Etrackcomplex = 3,
+	Etracklogo = 16,
+	Etracksubtitles = 17,
+	Etrackbuttons = 18,
+	Etrackcontrol = 32,
+	Etrackmetadata = 33,
+
+	/* ids */
+	EChapterDisplay = 0x80,
+	ETrackType = 0x83,
+	EChapString = 0x85,
+	ECodecID = 0x86,
+	EFlagDefault = 0x88,
+	EChapterTrackUID = 0x89,
+	ESlices = 0x8e,
+	EChapterTrack = 0x8f,
+	EChapterTimeStart = 0x91,
+	EChapterTimeEnd = 0x92,
+	ECueRefTime = 0x96,
+	ECueRefCluster = 0x97,
+	EChapterFlagHidden = 0x98,
+	EFlagInterlaced = 0x9a,
+	EBlockDuration = 0x9b,
+	EFlagLacing = 0x9c,
+	EFieldOrder = 0x9d,
+	EChannels = 0x9f,
+	EBlockGroup = 0xa0,
+	EBlock = 0xa1,
+	EBlockVirtual = 0xa2,
+	ESimpleBlock = 0xa3,
+	ECodecState = 0xa4,
+	EBlockAdditional = 0xa5,
+	EBlockMore = 0xa6,
+	EPosition = 0xa7,
+	ECodecDecodeAll = 0xaa,
+	EPrevSize = 0xab,
+	ETrackEntry = 0xae,
+	EEncryptedBlock = 0xaf,
+	EPixelWidth = 0xb0,
+	ECueDuration = 0xb2,
+	ECueTime = 0xb3,
+	ESamplingFrequency = 0xb5,
+	EChapterAtom = 0xb6,
+	ECueTrackPositions = 0xb7,
+	EFlagEnabled = 0xb9,
+	EPixelHeight = 0xba,
+	ECuePoint = 0xbb,
+	ECRC32 = 0xbf,
+	EReferenceFrame = 0xc8,
+	EReferenceOffset = 0xc9,
+	EReferenceTimestamp = 0xca,
+	EBlockAdditionID = 0xcb,
+	ELaceNumber = 0xcc,
+	EFrameNumber = 0xcd,
+	EDelay = 0xce,
+	ESliceDuration = 0xcf,
+	ETrackNumber = 0xd7,
+	ECueReference = 0xdb,
+	EVideo = 0xe0,
+	EAudio = 0xe1,
+	ETrackOperation = 0xe2,
+	ETrackCombinePlanes = 0xe3,
+	ETrackPlane = 0xe4,
+	ETrackPlaneUID = 0xe5,
+	ETrackPlaneType = 0xe6,
+	ETimestamp = 0xe7,
+	ETimeSlice = 0xe8,
+	ETrackJoinBlocks = 0xe9,
+	ECueCodecState = 0xea,
+	ECueRefCodecState = 0xeb,
+	EVoid = 0xec,
+	ETrackJoinUID = 0xed,
+	EBlockAddID = 0xee,
+	ECueRelativePosition = 0xf0,
+	ECueClusterPosition = 0xf1,
+	ECueTrack = 0xf7,
+	EReferencePriority = 0xfa,
+	EReferenceBlock = 0xfb,
+	EReferenceVirtual = 0xfd,
+	EBlockAddIDName = 0x41a4,
+	EBlockAdditionMapping = 0x41e4,
+	EBlockAddIDType = 0x41e7,
+	EBlockAddIDExtraData = 0x41ed,
+	EBlockAddIDValue = 0x41f0,
+	EContentCompAlgo = 0x4254,
+	EContentCompSettings = 0x4255,
+	EDocTypeExtension = 0x4281,
+	EDocType = 0x4282,
+	EDocTypeExtensionName = 0x4283,
+	EDocTypeExtensionVersion = 0x4284,
+	EDocTypeReadVersion = 0x4285,
+	EEBMLVersion = 0x4286,
+	EDocTypeVersion = 0x4287,
+	EEBMLMaxIDLength = 0x42f2,
+	EEBMLMaxSizeLength = 0x42f3,
+	EEBMLReadVersion = 0x42f7,
+	EChapLanguage = 0x437c,
+	EChapLanguageIETF = 0x437d,
+	EChapCountry = 0x437e,
+	ESegmentFamily = 0x4444,
+	EDateUTC = 0x4461,
+	ETagLanguage = 0x447a,
+	ETagLanguageIETF = 0x447b,
+	ETagDefault = 0x4484,
+	ETagBinary = 0x4485,
+	ETagString = 0x4487,
+	EDuration = 0x4489,
+	EChapProcessPrivate = 0x450d,
+	EChapterFlagEnabled = 0x4598,
+	ETagName = 0x45a3,
+	EEditionEntry = 0x45b9,
+	EEditionUID = 0x45bc,
+	EEditionFlagHidden = 0x45bd,
+	EEditionFlagDefault = 0x45db,
+	EEditionFlagOrdered = 0x45dd,
+	EFileData = 0x465c,
+	EFileMimeType = 0x4660,
+	EFileUsedStartTime = 0x4661,
+	EFileUsedEndTime = 0x4662,
+	EFileName = 0x466e,
+	EFileReferral = 0x4675,
+	EFileDescription = 0x467e,
+	EFileUID = 0x46ae,
+	EContentEncAlgo = 0x47e1,
+	EContentEncKeyID = 0x47e2,
+	EContentSignature = 0x47e3,
+	EContentSigKeyID = 0x47e4,
+	EContentSigAlgo = 0x47e5,
+	EContentSigHashAlgo = 0x47e6,
+	EContentAESSettings = 0x47e7,
+	EAESSettingsCipherMode = 0x47e8,
+	EMuxingApp = 0x4d80,
+	ESeek = 0x4dbb,
+	EContentEncodingOrder = 0x5031,
+	EContentEncodingScope = 0x5032,
+	EContentEncodingType = 0x5033,
+	EContentCompression = 0x5034,
+	EContentEncryption = 0x5035,
+	ECueRefNumber = 0x535f,
+	EName = 0x536e,
+	ECueBlockNumber = 0x5378,
+	ETrackOffset = 0x537f,
+	ESeekID = 0x53ab,
+	ESeekPosition = 0x53ac,
+	EStereoMode = 0x53b8,
+	EOldStereoMode = 0x53b9,
+	EAlphaMode = 0x53c0,
+	EPixelCropBottom = 0x54aa,
+	EDisplayWidth = 0x54b0,
+	EDisplayUnit = 0x54b2,
+	EAspectRatioType = 0x54b3,
+	EDisplayHeight = 0x54ba,
+	EPixelCropTop = 0x54bb,
+	EPixelCropLeft = 0x54cc,
+	EPixelCropRight = 0x54dd,
+	EFlagForced = 0x55aa,
+	EColour = 0x55b0,
+	EMatrixCoefficients = 0x55b1,
+	EBitsPerChannel = 0x55b2,
+	EChromaSubsamplingHorz = 0x55b3,
+	EChromaSubsamplingVert = 0x55b4,
+	ECbSubSamplingHorz = 0x55b5,
+	ECbSubSamplingVert = 0x55b6,
+	EChromaSitingHorz = 0x55b7,
+	EChromaSitingVert = 0x55b8,
+	ERange = 0x55b9,
+	ETransferCharacteristics = 0x55ba,
+	EPrimaries = 0x55bb,
+	EMaxCLL = 0x55bc,
+	EMaxFALL = 0x55bd,
+	EMasteringMetadata = 0x55d0,
+	EPrimaryRChromaticityX = 0x55d1,
+	EPrimaryRChromaticityY = 0x55d2,
+	EPrimaryGChromaticityX = 0x55d3,
+	EPrimaryGChromaticityY = 0x55d4,
+	EPrimaryBChromaticityX = 0x55d5,
+	EPrimaryBChromaticityY = 0x55d6,
+	EWhitePointChromaticityX = 0x55d7,
+	EWhitePointChromaticityY = 0x55d8,
+	ELuminanceMax = 0x55d9,
+	ELuminanceMin = 0x55da,
+	EMaxBlockAdditionID = 0x55ee,
+	EChapterStringUID = 0x5654,
+	ECodecDelay = 0x56aa,
+	ESeekPreRoll = 0x56bb,
+	EWritingApp = 0x5741,
+	ESilentTracks = 0x5854,
+	ESilentTrackNumber = 0x58d7,
+	EAttachedFile = 0x61a7,
+	EContentEncoding = 0x6240,
+	EBitDepth = 0x6264,
+	ECodecPrivate = 0x63a2,
+	ETargets = 0x63c0,
+	EChapterPhysicalEquiv = 0x63c3,
+	ETagChapterUID = 0x63c4,
+	ETagTrackUID = 0x63c5,
+	ETagAttachmentUID = 0x63c6,
+	ETagEditionUID = 0x63c9,
+	ETargetType = 0x63ca,
+	ETrackTranslate = 0x6624,
+	ETrackTranslateTrackID = 0x66a5,
+	ETrackTranslateCodec = 0x66bf,
+	ETrackTranslateEditionUID = 0x66fc,
+	ESimpleTag = 0x67c8,
+	ETargetTypeValue = 0x68ca,
+	EChapProcessCommand = 0x6911,
+	EChapProcessTime = 0x6922,
+	EChapterTranslate = 0x6924,
+	EChapProcessData = 0x6933,
+	EChapProcess = 0x6944,
+	EChapProcessCodecID = 0x6955,
+	EChapterTranslateID = 0x69a5,
+	EChapterTranslateCodec = 0x69bf,
+	EChapterTranslateEditionUID = 0x69fc,
+	EContentEncodings = 0x6d80,
+	EMinCache = 0x6de7,
+	EMaxCache = 0x6df8,
+	EChapterSegmentUID = 0x6e67,
+	EChapterSegmentEditionUID = 0x6ebc,
+	ETrackOverlay = 0x6fab,
+	ETag = 0x7373,
+	ESegmentFilename = 0x7384,
+	ESegmentUID = 0x73a4,
+	EChapterUID = 0x73c4,
+	ETrackUID = 0x73c5,
+	EAttachmentLink = 0x7446,
+	EBlockAdditions = 0x75a1,
+	EDiscardPadding = 0x75a2,
+	EProjection = 0x7670,
+	EProjectionType = 0x7671,
+	EProjectionPrivate = 0x7672,
+	EProjectionPoseYaw = 0x7673,
+	EProjectionPosePitch = 0x7674,
+	EProjectionPoseRoll = 0x7675,
+	EOutputSamplingFrequency = 0x78b5,
+	ETitle = 0x7ba9,
+	EChannelPositions = 0x7d7b,
+	ELanguage = 0x22b59c,
+	ELanguageIETF = 0x22b59d,
+	ETrackTimestampScale = 0x23314f,
+	EDefaultDecodedFieldDuration = 0x234e7a,
+	EFrameRate = 0x2383e3,
+	EDefaultDuration = 0x23e383,
+	ECodecName = 0x258688,
+	ECodecDownloadURL = 0x26b240,
+	ETimestampScale = 0x2ad7b1,
+	EColourSpace = 0x2eb524,
+	EGammaValue = 0x2fb523,
+	ECodecSettings = 0x3a9697,
+	ECodecInfoURL = 0x3b4040,
+	EPrevFilename = 0x3c83ab,
+	EPrevUID = 0x3cb923,
+	ENextFilename = 0x3e83bb,
+	ENextUID = 0x3eb923,
+	EChapters = 0x1043a770,
+	ESeekHead = 0x114d9b74,
+	ETags = 0x1254c367,
+	EInfo = 0x1549a966,
+	ETracks = 0x1654ae6b,
+	ESegment = 0x18538067,
+	EAttachments = 0x1941a469,
+	EEBML = 0x1a45dfa3,
+	ECues = 0x1c53bb6b,
+	ECluster = 0x1f43b675,
+};
+
+int ebmluintb(u8int *b, int sz, vlong *out);
+int ebmlsintb(u8int *b, int sz, vlong *out);
+int ebmluint(Biobuf *f, vlong *out);
+int ebmlid(Biobuf *f, vlong *out);
+vlong ebmlel(Biobuf *f, vlong sz, vlong *id, vlong *esz);
+vlong ebmlrawint(Biobuf *f, vlong sz, vlong *dst);
+char *ebmltracktype(int t);
+Elspec *ebmlelspec(vlong id);
--- a/elem.c
+++ /dev/null
@@ -1,283 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include "elem.h"
-
-static Element els[] = {
-	{EChapterDisplay, "ChapterDisplay", Emaster},
-	{ETrackType, "TrackType", Eunsigned},
-	{EChapString, "ChapString", Eunicode},
-	{ECodecID, "CodecID", Esigned},
-	{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", Esigned},
-	{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},
-};
-
-Element *
-ebmlid2elem(vlong id)
-{
-	Element *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;
-}
--- a/elem.h
+++ /dev/null
@@ -1,279 +1,0 @@
-typedef struct Element Element;
-
-struct Element {
-	vlong id;
-	char *name;
-	int type;
-};
-
-enum {
-	/* ebml element value types */
-	Emaster,
-	Ebinary,
-	Efloat,
-	Eunsigned,
-	Esigned,
-	Eunicode,
-	Eascii = Eunicode,
-	Etimestamp,
-
-	/* ids */
-	EChapterDisplay = 0x80,
-	ETrackType = 0x83,
-	EChapString = 0x85,
-	ECodecID = 0x86,
-	EFlagDefault = 0x88,
-	EChapterTrackUID = 0x89,
-	ESlices = 0x8e,
-	EChapterTrack = 0x8f,
-	EChapterTimeStart = 0x91,
-	EChapterTimeEnd = 0x92,
-	ECueRefTime = 0x96,
-	ECueRefCluster = 0x97,
-	EChapterFlagHidden = 0x98,
-	EFlagInterlaced = 0x9a,
-	EBlockDuration = 0x9b,
-	EFlagLacing = 0x9c,
-	EFieldOrder = 0x9d,
-	EChannels = 0x9f,
-	EBlockGroup = 0xa0,
-	EBlock = 0xa1,
-	EBlockVirtual = 0xa2,
-	ESimpleBlock = 0xa3,
-	ECodecState = 0xa4,
-	EBlockAdditional = 0xa5,
-	EBlockMore = 0xa6,
-	EPosition = 0xa7,
-	ECodecDecodeAll = 0xaa,
-	EPrevSize = 0xab,
-	ETrackEntry = 0xae,
-	EEncryptedBlock = 0xaf,
-	EPixelWidth = 0xb0,
-	ECueDuration = 0xb2,
-	ECueTime = 0xb3,
-	ESamplingFrequency = 0xb5,
-	EChapterAtom = 0xb6,
-	ECueTrackPositions = 0xb7,
-	EFlagEnabled = 0xb9,
-	EPixelHeight = 0xba,
-	ECuePoint = 0xbb,
-	ECRC32 = 0xbf,
-	EReferenceFrame = 0xc8,
-	EReferenceOffset = 0xc9,
-	EReferenceTimestamp = 0xca,
-	EBlockAdditionID = 0xcb,
-	ELaceNumber = 0xcc,
-	EFrameNumber = 0xcd,
-	EDelay = 0xce,
-	ESliceDuration = 0xcf,
-	ETrackNumber = 0xd7,
-	ECueReference = 0xdb,
-	EVideo = 0xe0,
-	EAudio = 0xe1,
-	ETrackOperation = 0xe2,
-	ETrackCombinePlanes = 0xe3,
-	ETrackPlane = 0xe4,
-	ETrackPlaneUID = 0xe5,
-	ETrackPlaneType = 0xe6,
-	ETimestamp = 0xe7,
-	ETimeSlice = 0xe8,
-	ETrackJoinBlocks = 0xe9,
-	ECueCodecState = 0xea,
-	ECueRefCodecState = 0xeb,
-	EVoid = 0xec,
-	ETrackJoinUID = 0xed,
-	EBlockAddID = 0xee,
-	ECueRelativePosition = 0xf0,
-	ECueClusterPosition = 0xf1,
-	ECueTrack = 0xf7,
-	EReferencePriority = 0xfa,
-	EReferenceBlock = 0xfb,
-	EReferenceVirtual = 0xfd,
-	EBlockAddIDName = 0x41a4,
-	EBlockAdditionMapping = 0x41e4,
-	EBlockAddIDType = 0x41e7,
-	EBlockAddIDExtraData = 0x41ed,
-	EBlockAddIDValue = 0x41f0,
-	EContentCompAlgo = 0x4254,
-	EContentCompSettings = 0x4255,
-	EDocTypeExtension = 0x4281,
-	EDocType = 0x4282,
-	EDocTypeExtensionName = 0x4283,
-	EDocTypeExtensionVersion = 0x4284,
-	EDocTypeReadVersion = 0x4285,
-	EEBMLVersion = 0x4286,
-	EDocTypeVersion = 0x4287,
-	EEBMLMaxIDLength = 0x42f2,
-	EEBMLMaxSizeLength = 0x42f3,
-	EEBMLReadVersion = 0x42f7,
-	EChapLanguage = 0x437c,
-	EChapLanguageIETF = 0x437d,
-	EChapCountry = 0x437e,
-	ESegmentFamily = 0x4444,
-	EDateUTC = 0x4461,
-	ETagLanguage = 0x447a,
-	ETagLanguageIETF = 0x447b,
-	ETagDefault = 0x4484,
-	ETagBinary = 0x4485,
-	ETagString = 0x4487,
-	EDuration = 0x4489,
-	EChapProcessPrivate = 0x450d,
-	EChapterFlagEnabled = 0x4598,
-	ETagName = 0x45a3,
-	EEditionEntry = 0x45b9,
-	EEditionUID = 0x45bc,
-	EEditionFlagHidden = 0x45bd,
-	EEditionFlagDefault = 0x45db,
-	EEditionFlagOrdered = 0x45dd,
-	EFileData = 0x465c,
-	EFileMimeType = 0x4660,
-	EFileUsedStartTime = 0x4661,
-	EFileUsedEndTime = 0x4662,
-	EFileName = 0x466e,
-	EFileReferral = 0x4675,
-	EFileDescription = 0x467e,
-	EFileUID = 0x46ae,
-	EContentEncAlgo = 0x47e1,
-	EContentEncKeyID = 0x47e2,
-	EContentSignature = 0x47e3,
-	EContentSigKeyID = 0x47e4,
-	EContentSigAlgo = 0x47e5,
-	EContentSigHashAlgo = 0x47e6,
-	EContentAESSettings = 0x47e7,
-	EAESSettingsCipherMode = 0x47e8,
-	EMuxingApp = 0x4d80,
-	ESeek = 0x4dbb,
-	EContentEncodingOrder = 0x5031,
-	EContentEncodingScope = 0x5032,
-	EContentEncodingType = 0x5033,
-	EContentCompression = 0x5034,
-	EContentEncryption = 0x5035,
-	ECueRefNumber = 0x535f,
-	EName = 0x536e,
-	ECueBlockNumber = 0x5378,
-	ETrackOffset = 0x537f,
-	ESeekID = 0x53ab,
-	ESeekPosition = 0x53ac,
-	EStereoMode = 0x53b8,
-	EOldStereoMode = 0x53b9,
-	EAlphaMode = 0x53c0,
-	EPixelCropBottom = 0x54aa,
-	EDisplayWidth = 0x54b0,
-	EDisplayUnit = 0x54b2,
-	EAspectRatioType = 0x54b3,
-	EDisplayHeight = 0x54ba,
-	EPixelCropTop = 0x54bb,
-	EPixelCropLeft = 0x54cc,
-	EPixelCropRight = 0x54dd,
-	EFlagForced = 0x55aa,
-	EColour = 0x55b0,
-	EMatrixCoefficients = 0x55b1,
-	EBitsPerChannel = 0x55b2,
-	EChromaSubsamplingHorz = 0x55b3,
-	EChromaSubsamplingVert = 0x55b4,
-	ECbSubSamplingHorz = 0x55b5,
-	ECbSubSamplingVert = 0x55b6,
-	EChromaSitingHorz = 0x55b7,
-	EChromaSitingVert = 0x55b8,
-	ERange = 0x55b9,
-	ETransferCharacteristics = 0x55ba,
-	EPrimaries = 0x55bb,
-	EMaxCLL = 0x55bc,
-	EMaxFALL = 0x55bd,
-	EMasteringMetadata = 0x55d0,
-	EPrimaryRChromaticityX = 0x55d1,
-	EPrimaryRChromaticityY = 0x55d2,
-	EPrimaryGChromaticityX = 0x55d3,
-	EPrimaryGChromaticityY = 0x55d4,
-	EPrimaryBChromaticityX = 0x55d5,
-	EPrimaryBChromaticityY = 0x55d6,
-	EWhitePointChromaticityX = 0x55d7,
-	EWhitePointChromaticityY = 0x55d8,
-	ELuminanceMax = 0x55d9,
-	ELuminanceMin = 0x55da,
-	EMaxBlockAdditionID = 0x55ee,
-	EChapterStringUID = 0x5654,
-	ECodecDelay = 0x56aa,
-	ESeekPreRoll = 0x56bb,
-	EWritingApp = 0x5741,
-	ESilentTracks = 0x5854,
-	ESilentTrackNumber = 0x58d7,
-	EAttachedFile = 0x61a7,
-	EContentEncoding = 0x6240,
-	EBitDepth = 0x6264,
-	ECodecPrivate = 0x63a2,
-	ETargets = 0x63c0,
-	EChapterPhysicalEquiv = 0x63c3,
-	ETagChapterUID = 0x63c4,
-	ETagTrackUID = 0x63c5,
-	ETagAttachmentUID = 0x63c6,
-	ETagEditionUID = 0x63c9,
-	ETargetType = 0x63ca,
-	ETrackTranslate = 0x6624,
-	ETrackTranslateTrackID = 0x66a5,
-	ETrackTranslateCodec = 0x66bf,
-	ETrackTranslateEditionUID = 0x66fc,
-	ESimpleTag = 0x67c8,
-	ETargetTypeValue = 0x68ca,
-	EChapProcessCommand = 0x6911,
-	EChapProcessTime = 0x6922,
-	EChapterTranslate = 0x6924,
-	EChapProcessData = 0x6933,
-	EChapProcess = 0x6944,
-	EChapProcessCodecID = 0x6955,
-	EChapterTranslateID = 0x69a5,
-	EChapterTranslateCodec = 0x69bf,
-	EChapterTranslateEditionUID = 0x69fc,
-	EContentEncodings = 0x6d80,
-	EMinCache = 0x6de7,
-	EMaxCache = 0x6df8,
-	EChapterSegmentUID = 0x6e67,
-	EChapterSegmentEditionUID = 0x6ebc,
-	ETrackOverlay = 0x6fab,
-	ETag = 0x7373,
-	ESegmentFilename = 0x7384,
-	ESegmentUID = 0x73a4,
-	EChapterUID = 0x73c4,
-	ETrackUID = 0x73c5,
-	EAttachmentLink = 0x7446,
-	EBlockAdditions = 0x75a1,
-	EDiscardPadding = 0x75a2,
-	EProjection = 0x7670,
-	EProjectionType = 0x7671,
-	EProjectionPrivate = 0x7672,
-	EProjectionPoseYaw = 0x7673,
-	EProjectionPosePitch = 0x7674,
-	EProjectionPoseRoll = 0x7675,
-	EOutputSamplingFrequency = 0x78b5,
-	ETitle = 0x7ba9,
-	EChannelPositions = 0x7d7b,
-	ELanguage = 0x22b59c,
-	ELanguageIETF = 0x22b59d,
-	ETrackTimestampScale = 0x23314f,
-	EDefaultDecodedFieldDuration = 0x234e7a,
-	EFrameRate = 0x2383e3,
-	EDefaultDuration = 0x23e383,
-	ECodecName = 0x258688,
-	ECodecDownloadURL = 0x26b240,
-	ETimestampScale = 0x2ad7b1,
-	EColourSpace = 0x2eb524,
-	EGammaValue = 0x2fb523,
-	ECodecSettings = 0x3a9697,
-	ECodecInfoURL = 0x3b4040,
-	EPrevFilename = 0x3c83ab,
-	EPrevUID = 0x3cb923,
-	ENextFilename = 0x3e83bb,
-	ENextUID = 0x3eb923,
-	EChapters = 0x1043a770,
-	ESeekHead = 0x114d9b74,
-	ETags = 0x1254c367,
-	EInfo = 0x1549a966,
-	ETracks = 0x1654ae6b,
-	ESegment = 0x18538067,
-	EAttachments = 0x1941a469,
-	EEBML = 0x1a45dfa3,
-	ECues = 0x1c53bb6b,
-	ECluster = 0x1f43b675,
-};
-
-Element *ebmlid2elem(vlong id);
--- a/ivf.c
+++ b/ivf.c
@@ -2,6 +2,7 @@
 #include <libc.h>
 #include <bio.h>
 #include "common.h"
+#include "packet.h"
 
 enum {
 	Timedenum = 1000ULL,
--- a/main.c
+++ b/main.c
@@ -76,9 +76,9 @@
 		Bprint(&stderr, "seek back failed: %r\n");
 		status = "seek failed";
 	}else if(memcmp(d, "\x1a\x45\xdf\xa3", 4) == 0){
-		if(ebmlrun(&f) != 0){
-			Bprint(&stderr, "embl: %r\n");
-			status = "ebml failed";
+		if(matroskarun(&f) != 0){
+			Bprint(&stderr, "matroska: %r\n");
+			status = "matroska failed";
 		}
 	}else if(isorun(&f) != 0){
 		Bprint(&stderr, "iso: %r\n");
--- /dev/null
+++ b/matroska.c
@@ -1,0 +1,445 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "common.h"
+#include "ebml.h"
+#include "packet.h"
+
+typedef struct Ebml Ebml;
+
+struct Ebml {
+	Packetctx;
+
+	packet_f fpacket;
+
+	vlong timestampscale;
+	vlong perframe;
+	int tracknum;
+	int tracktype;
+	vlong codecdelay;
+	char lang[8];
+};
+
+static Packet packets[256];
+static vlong stack[32];
+
+#define ebmlgetnumber(expid, dest) \
+	if(id == expid){ \
+		vlong x; \
+		if(ebmlrawint(f, sz, &x) < 0) \
+			return -1; \
+		dest = x; \
+		left -= sz; \
+		continue; \
+	}
+
+#define ebmlgetstring(expid, dest) \
+	if(id == expid){ \
+		n = min(sizeof(dest)-1, sz); \
+		if(Bread(f, dest, n) != n) \
+			return -1; \
+		dest[n] = 0; \
+		if(n != sz) \
+			Bseek(f, sz-n, 1); \
+		left -= sz; \
+		continue; \
+	}
+
+#define ebmlgetbytes(expid, dest) \
+	if(id == expid){ \
+		dest.data = malloc(sz); \
+		if(Bread(f, dest.data, sz) != sz) \
+			return -1; \
+		dest.sz = sz; \
+		left -= sz; \
+		continue; \
+	}
+
+#define ebmlgetfloat(expid, dest) \
+	if(id == expid){ \
+		u32int u; \
+		union { \
+			uchar b[8]; \
+			u32int u[2]; \
+			float f; \
+			double d; \
+		}x; \
+		if(sz == 4){ \
+			if(Bread(f, x.b, 4) != 4) \
+				return -1; \
+			x.u[0] = (x.u[0]&0xff000000)>>24 | (x.u[0]&0x00ff0000)>>8 | (x.u[0]&0x0000ff00)<<8 | (x.u[0]&0x000000ff)<<24; \
+			dest = x.f; \
+		}else if(sz == 8){ \
+			if(Bread(f, x.b, 8) != 8) \
+				return -1; \
+			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; \
+			dest = x.d; \
+		}else{ \
+			werrstr("invalid float size"); \
+			break; \
+		} \
+		left -= sz; \
+		continue; \
+	}
+
+static char *
+format(Ebml *e)
+{
+	static char t[16];
+	char *s;
+	int n;
+
+	if(e->tracktype == Etracksubtitles){
+		if(strcmp(e->codec.name, "S_TEXT/UTF8") == 0)
+			return "srt";
+	}else if(e->tracktype == Etrackaudio){
+		if(strcmp(e->codec.name, "A_MPEG/L3") == 0)
+			return "mp3";
+	}else if(e->tracktype == Etrackvideo){
+		if(strcmp(e->codec.name, "V_MPEG4/ISO/AVC") == 0)
+			return "h264";
+	}
+
+	n = snprint(t, sizeof(t), "%s", e->codec.name+2);
+	for(n -= 1; n >= 0; n--)
+		t[n] = tolower(t[n]);
+	if((s = strchr(t, '/')) != nil)
+		*s = 0;
+
+	return t;
+}
+
+static int
+asispacket(Biobuf *out, Packetctx *, Packet *p, int np, uvlong)
+{
+	int i;
+
+	for(i = 0; i < np; i++, p++)
+		Bwrite(out, p->data, p->sz);
+
+	return 0;
+}
+
+static int
+initctx(Ebml *e, double duration)
+{
+	char *c;
+
+	e->duration = duration;
+	e->duration *= e->timestampscale;
+	c = format(e);
+	if(e->tracktype == Etrackvideo){
+		if(strcmp(c, "vp9") == 0)
+			e->fmt = FmtVp09;
+		else if(strcmp(c, "vp8") == 0)
+			e->fmt = FmtVp08;
+		else if(strcmp(c, "h264") == 0)
+			e->fmt = FmtAvc1;
+		else
+			goto err;
+		e->fpacket = ivfpacket;
+		return 0;
+	}else if(e->tracktype == Etrackaudio){
+		if(strcmp(c, "vorbis") == 0){
+			e->fmt = FmtVorbis;
+			e->fpacket = oggpacket;
+		}else if(strcmp(c, "opus") == 0){
+			e->fmt = FmtOpus;
+			e->fpacket = oggpacket;
+		}else if(strcmp(c, "mp3") == 0){
+			e->fmt = FmtMp3;
+			e->fpacket = asispacket;
+		}else if(strcmp(c, "aac") == 0){
+			e->fmt = FmtMp4a;
+			e->fpacket = aacpacket;
+		}else
+			goto err;
+		return 0;
+	}else if(e->tracktype == Etracksubtitles){
+		if(strcmp(c, "srt") == 0){
+			e->fmt = FmtSrt;
+			e->fpacket = srtpacket;
+		}else
+			goto err;
+		return 0;
+	}
+
+err:
+	werrstr("don't know how to remux %s (track type %d)", e->codec.name, e->tracktype);
+
+	return -1;
+}
+
+static void
+trackinfo(Biobuf *o, Ebml *e)
+{
+	char *t;
+
+	t = ebmltracktype(e->tracktype);
+	Bprint(o, "%d\t%s\t%s\t", e->tracknum, t, format(e));
+	if(e->tracktype == Etrackvideo)
+		Bprint(o, "%d\t%d", e->video.width, e->video.height);
+	else if(e->tracktype == Etrackaudio)
+		Bprint(o, "%d\t%d", e->audio.channels, (int)e->audio.samplerate);
+	else if(e->tracktype == Etracksubtitles)
+		Bprint(o, "%s", e->lang);
+	else
+		Bprint(o, "???");
+	Bprint(o, "\n");
+}
+
+int
+matroskarun(Biobuf *f)
+{
+	int isebml, npackets, i, sti, skipdata;
+	vlong left, id, n, sz, bufsz, track, off, packetsz, x, endtracks;
+	uvlong ts, timestamp, timestampscale;
+	uchar *buf;
+	double duration;
+	Ebml e, te;
+	Elspec *el;
+
+	buf = nil;
+	bufsz = 0;
+	track = -1;
+	timestamp = 0;
+	left = (1ULL<<63)-1;
+	endtracks = -1;
+	te.tracknum = -1;
+	timestampscale = 1000000;
+	e.tracknum = -1;
+	duration = 0;
+	ts = 0;
+	sti = 0;
+	skipdata = trackdump == Nodump && dflag < 2;
+	for(isebml = 0; left != 0;){
+		if(id == EBlockDuration)
+			te.blockdur *= timestampscale;
+		if(endtracks > 0 && left < endtracks && skipdata){
+			/* early exit */
+			left = 0;
+			break;
+		}
+
+		off = Boffset(f);
+		if((n = ebmlel(f, left, &id, &sz)) < 0){
+			werrstr("invalid ebml: %r at %#zx (size %zd)", off, sz);
+			goto err;
+		}
+		if(n == 0) /* eof */
+			break;
+		while(sti > 0 && off >= stack[sti-1])
+			sti--;
+		left -= n;
+		if(dflag > 1){
+			if((el = ebmlelspec(id)) != nil){
+				for(i = 0; i < sti; i++)
+					Bputc(&stderr, '\t');
+				Bprint(&stderr, "%s\n", el->name);
+				if(el->type == Emaster && sti < nelem(stack))
+					stack[sti++] = off+n+sz;
+			}
+			else
+				Bprint(&stderr, "%#llx\n", id);
+		}
+
+		if(id == EEBML){ /* EBML comes first */
+			if(isebml != 0){
+				werrstr("double EBML?");
+				goto err;
+			}
+			isebml++;
+		}else if(id == ESegment){
+			left = sz;
+			if(isebml != 1){
+				werrstr("invalid ebml");
+				goto err;
+			}
+			isebml++; /* make sure we don't see more segments */
+			continue; /* go in */
+		}else if(id == EInfo){ /* segment info */
+			continue;
+		}else if(id == ETracks){
+			endtracks = left - sz; /* to skip early in case track dump not required */
+			continue;
+		}else if(id == ECluster){
+			if(!skipdata) /* skip it entirely if no dump required */
+				continue;
+		}else if(id == ETrackEntry){ /* track entry */
+			if(e.tracknum > 0){
+				if(trackdump == Nodump)
+					trackinfo(&out, &e);
+				else if(trackdump == Dumpvideo && e.tracktype == Etrackvideo)
+					trackdump = e.tracknum;
+				else if(trackdump == Dumpaudio && e.tracktype == Etrackaudio)
+					trackdump = e.tracknum;
+			}
+			if(e.tracknum == trackdump)
+				memmove(&te, &e, sizeof(e));
+			memset(&e, 0, sizeof(e));
+			e.timestampscale = timestampscale;
+			continue;
+		}else if(id == EVideo || id == EAudio){
+			continue;
+		}else if(id == EBlockGroup && !skipdata){
+			continue;
+		}else if((id == ESimpleBlock || id == EBlock) && !skipdata){
+			if(te.tracknum == -1)
+				memmove(&te, &e, sizeof(e));
+			if((n = ebmluint(f, &track)) < 0){
+				werrstr("block: %r");
+				goto err;
+			}
+			left -= n;
+			sz -= n;
+			if(trackdump == Dumpvideo && e.tracktype == Etrackvideo)
+				trackdump = te.tracknum;
+			else if(trackdump == Dumpaudio && e.tracktype == Etrackaudio)
+				trackdump = te.tracknum;
+			if(track == trackdump && track == te.tracknum){
+				if(te.fpacket == nil && initctx(&te, duration) != 0){
+					werrstr("packet: %r");
+					goto err;
+				}
+
+				if(bufsz < sz){
+					buf = realloc(buf, sz);
+					bufsz = sz;
+				}
+				if(Bread(f, buf, sz) != sz){
+					werrstr("short read");
+					goto err;
+				}
+				left -= sz;
+				sz -= 3;
+				npackets = buf[3]+1;
+				switch((buf[2] >> 1) & 3){ /* lacing */
+				case 0: /* none */
+					packets[0].data = buf+3;
+					packets[0].sz = sz;
+					npackets = 1;
+					break;
+				case 1: /* xiph */
+					sz--;
+					off = 4;
+					for(i = 0; i < npackets-1; i++){
+						packets[i].sz = 0;
+						do{
+							packets[i].sz += buf[off];
+						}while(buf[off++] == 0xff);
+					}
+					for(i = 0; i < npackets-1; i++){
+						packets[i].data = buf+off;
+						off += packets[i].sz;
+						sz -= packets[i].sz;
+					}
+					packets[i].data = buf+off;
+					packets[i].sz = sz;
+					break;
+				case 2: /* fixed-size */
+					sz--;
+					if((sz % npackets) != 0){
+						werrstr("invalid number of frames with fixed-size lacing");
+						goto err;
+					}
+					packets[0].sz = sz / npackets;
+					for(i = 0; i < npackets; i++){
+						packets[i].data = buf+4 + i*packets[0].sz;
+						packets[i].sz = packets[0].sz;
+						sz -= packets[0].sz;
+					}
+					break;
+				case 3: /* ebml */
+					sz--;
+					packetsz = 0;
+					off = 4;
+					for(i = 0; i < npackets-1; i++){
+						if((n = (i == 0 ? ebmluintb : ebmlsintb)(buf+off, sz, &x)) < 0)
+							goto err;
+						packetsz += x;
+						if(packetsz < 0){
+							werrstr("invalid frame size %zd", packetsz);
+							goto err;
+						}
+						packets[i].sz = packetsz;
+						off += n;
+						sz -= n;
+					}
+					for(i = 0; i < npackets-1; i++){
+						if(packets[i].sz > sz){
+							werrstr("frame %d/%d out of bounds: %d > %zd", i, npackets-1, packets[i].sz, sz);
+							goto err;
+						}
+						packets[i].data = buf+off;
+						off += packets[i].sz;
+						sz -= packets[i].sz;
+					}
+					packets[i].data = buf+off;
+					packets[i].sz = sz;
+					break;
+				}
+
+				if(te.fpacket(&out, &te, packets, npackets, ts) != 0)
+					goto err;
+				/* ns timestamp */
+				ts = (timestamp + (s16int)(buf[0]<<8 | buf[1])) * timestampscale - te.codec.delay;
+				continue;
+			}
+		}else{
+				ebmlgetnumber(ETimestampScale, timestampscale)
+			else
+				ebmlgetfloat(ESamplingFrequency, e.audio.samplerate)
+			else
+				ebmlgetnumber(EChannels, e.audio.channels)
+			else
+				ebmlgetnumber(EBitDepth, e.audio.bps)
+			else
+				ebmlgetnumber(ETrackNumber, e.tracknum)
+			else
+				ebmlgetnumber(ETrackType, e.tracktype)
+			else
+				ebmlgetstring(ECodecID, e.codec.name)
+			else
+				ebmlgetbytes(ECodecPrivate, e.codec.priv)
+			else
+				ebmlgetnumber(ECodecDelay, e.codec.delay)
+			else
+				ebmlgetnumber(EPixelWidth, e.video.width)
+			else
+				ebmlgetnumber(EPixelHeight, e.video.height)
+			else
+				ebmlgetnumber(ETimestamp, timestamp)
+			else
+				ebmlgetnumber(EDefaultDuration, e.perframe)
+			else
+				ebmlgetnumber(ESeekPreRoll, e.seekpreroll)
+			else
+				ebmlgetfloat(EDuration, duration)
+			else
+				ebmlgetnumber(ETrackUID, e.trackuid)
+			else
+				ebmlgetnumber(EDiscardPadding, te.discardpad)
+			else
+				ebmlgetnumber(EBlockDuration, te.blockdur)
+			else
+				ebmlgetstring(ELanguage, e.lang)
+		}
+
+		if(Bseek(f, sz, 1) < 0)
+			return -1;
+		left -= sz;
+	}
+
+	if(isebml == 2 && left == 0){
+		if(e.tracknum > 0){
+			if(trackdump == Nodump)
+				trackinfo(&out, &e);
+		}
+		return 0;
+	}
+
+err:
+	return -1;
+}
--- a/mkfile
+++ b/mkfile
@@ -6,17 +6,19 @@
 BIN=/$objtype/bin
 
 OFILES=\
+	aac.$O\
 	crc32.$O\
 	ebml.$O\
-	elem.$O\
 	iso.$O\
 	ivf.$O\
 	main.$O\
+	matroska.$O\
 	ogg.$O\
 	srt.$O\
 
 HFILES=\
 	common.h\
+	ebml.h\
 
 default:V:	all
 
--- a/ogg.c
+++ b/ogg.c
@@ -2,6 +2,7 @@
 #include <libc.h>
 #include <bio.h>
 #include "common.h"
+#include "packet.h"
 
 static int
 packet(Biobuf *out, Packetctx *ctx, int htype, Packet *p, int np, uvlong granule)
--- /dev/null
+++ b/packet.h
@@ -1,0 +1,42 @@
+typedef struct Packet Packet;
+typedef struct Packetctx Packetctx;
+
+typedef int (*packet_f)(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
+
+struct Packet {
+	uchar *data;
+	int sz;
+};
+
+struct Packetctx {
+	uvlong frid;
+	uvlong duration;
+	uvlong seekpreroll;
+	vlong discardpad;
+	vlong blockdur;
+	struct {
+		char name[16];
+		vlong delay;
+		struct {
+			uchar *data;
+			int sz;
+		}priv;
+	}codec;
+	u32int trackuid;
+	u32int fmt;
+
+	struct {
+		int width;
+		int height;
+	}video;
+	struct {
+		float samplerate;
+		int channels;
+		int bps;
+	}audio;
+};
+
+int aacpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
+int ivfpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
+int oggpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
+int srtpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts);
--- a/srt.c
+++ b/srt.c
@@ -2,6 +2,7 @@
 #include <libc.h>
 #include <bio.h>
 #include "common.h"
+#include "packet.h"
 
 enum {
 	Timedenum = 1000ULL,