Sigrid Solveig Haflínudóttirmatroska: handle av149dc43f4 years agoHistory
524 lines · 11.4 KB · C
RawDownload
#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];	char name[256];}; static Packet packets[256]; #define getnumber(expid, dest) \	if(el.id == expid){ \		vlong x; \		if(ebmlrawuint(f, sz, &x) < 0) \			return -1; \		dest = x; \		left -= sz; \		continue; \	} #define getsigned(expid, dest) \	if(el.id == expid){ \		vlong x; \		if(ebmlrawsint(f, sz, &x) < 0) \			return -1; \		dest = x; \		left -= sz; \		continue; \	} #define getfloat(expid, dest) \	if(el.id == expid){ \		double x; \		if(ebmlfloat(f, sz, &x) < 0) \			return -1; \		dest = x; \		left -= sz; \		continue; \	} #define getstring(expid, dest) \	if(el.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 getbytes(expid, dest) \	if(el.id == expid){ \		dest.data = malloc(sz); \		if(Bread(f, dest.data, sz) != sz) \			return -1; \		dest.sz = sz; \		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(strcmp(e->codec.name, "S_TEXT/ASS") == 0)			return "ass";	}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 intasispacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong, int){	int i; 	if(ctx->frid == 0)		Bwrite(out, ctx->codec.priv.data, ctx->codec.priv.sz); 	for(i = 0; i < np; i++, p++)		Bwrite(out, p->data, p->sz);	ctx->frid++; 	return 0;} static intinitctx(Ebml *e, double duration){	char *c; 	e->duration = duration;	e->duration *= e->timestampscale;	c = format(e);	if(e->tracktype == Etrackvideo){		e->fpacket = ivfpacket;		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 if(strcmp(c, "av1") == 0)			e->fmt = FmtAv01;		else if(strcmp(c, "theora") == 0){			e->fmt = FmtTheora;			e->fpacket = oggpacket;		}else			goto err;		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 if(strcmp(c, "flac") == 0){			e->fmt = FmtFlac;			e->fpacket = asispacket;		}else			goto err;		return 0;	}else if(e->tracktype == Etracksubtitles){		if(strcmp(c, "srt") == 0){			e->fmt = FmtSrt;			e->fpacket = srtpacket;		}else if(strcmp(c, "ass") == 0){			e->fmt = FmtAss;			e->fpacket = asspacket;		}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 voidtrackinfo(Biobuf *o, Ebml *e){	char *t;	int rate; 	rate = e->audio.outsamplerate;	if(rate == 0)		rate = e->audio.samplerate;	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\t%s\t%s", e->audio.channels, rate, e->lang, e->name);	else if(e->tracktype == Etracksubtitles)		Bprint(o, "%s\t%s", e->lang, e->name);	else		Bprint(o, "???");	Bprint(o, "\n");} intmatroskarun(Biobuf *f){	vlong left, n, sz, nsz, bufsz, track, off, packetsz, x, endtracks, bgend;	int isebml, npackets, i, skipdata, lacing, refblock, key;	uvlong ts, timestamp, timestampscale;	double duration;	s16int timecode;	uchar *buf, *b;	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;	e.comp.algo = -1;	duration = 0;	skipdata = trackdump == Nodump;	el.id = 0;	refblock = 0;	bgend = 0;	npackets = 0;	ts = 0;	key = 0;	for(isebml = 0; left != 0;){		if(el.id == EBlockDuration)			te.blockdur *= timestampscale;		if(endtracks > 0 && left < endtracks && skipdata){			/* early exit */			left = 0;			break;		} 		off = Boffset(f); 		if(off >= bgend && !skipdata){			if(npackets > 0){				if(te.fpacket(&out, &te, packets, npackets, ts, key || !refblock) != 0)					goto err;				npackets = 0;			}		} 		n = ebmlel(f, left, &el, &sz);		if(n < 0){			werrstr("invalid ebml: %r at %#llx (size %lld)", off, sz);			goto err;		}		if(el.type < 0)			continue;		if(n == 0){ /* eof */			left = 0;			break;		}		left -= n; 		if(el.id == EEBML){ /* EBML comes first */			if(isebml != 0){				werrstr("double EBML?");				goto err;			}			isebml++;		}else if(el.id == ESegment){			if(sz > 0)				left = sz;			if(isebml != 1){				werrstr("invalid ebml");				goto err;			}			isebml++; /* make sure we don't see more segments */			continue; /* go in */		}else if(el.id == EInfo){ /* segment info */			continue;		}else if(el.id == ETracks){			endtracks = left - sz; /* to skip early in case track dump not required */			continue;		}else if(el.id == ECluster){			if(!skipdata) /* skip it entirely if no dump required */				continue;		}else if(el.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.comp.algo = -1;			e.timestampscale = timestampscale;			continue;		}else if(el.id == EVideo){			continue;		}else if(el.id == EAudio){			e.audio.samplerate = 8000;			e.audio.channels = 1;			continue;		}else if(el.id == EContentEncodings || el.id == EContentEncoding || el.id == EContentCompression){			continue;		}else if(el.id == EBlockGroup && !skipdata){			refblock = 0;			bgend = off+n+sz;			npackets = 0;			continue;		}else if((el.id == ESimpleBlock || el.id == EBlock) && !skipdata){			if(te.tracknum == -1)				memmove(&te, &e, sizeof(e));			n = ebmluint(f, sz, &track);			if(n < 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;				timecode = buf[0]<<8 | buf[1];				lacing = (buf[2] >> 1) & 3;				key = buf[2] & 0x80;				npackets = buf[3]+1; 				if(te.comp.algo == 3){ /* header stripping, need to put bytes back */					if(te.comp.sz == 0)						te.comp.algo = -1;					else{						nsz = sz + npackets*te.comp.sz;						if(bufsz < nsz){							buf = realloc(buf, nsz);							bufsz = nsz;						}					}				}else if(te.comp.algo >= 0){					werrstr("unsupported: comp algo %d, please report a bug", te.comp.algo);					goto err;				} 				b = buf+3;				switch(lacing){ /* lacing */				case 0: /* none */					packets[0].data = b;					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];							sz--;						}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++){						n = (i == 0 ? ebmluintb : ebmlsintb)(buf+off, sz, &x);						if(n < 0)							goto err;						packetsz += x;						if(packetsz < 0){							werrstr("invalid frame size %lld", 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 > %lld", 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.comp.algo == 3){ /* prepend stripped data to each packet */					b = buf+bufsz;					for(i = npackets-1; i >= 0; i--){						b -= packets[i].sz;						memmove(b, packets[i].data, packets[i].sz);						b -= te.comp.sz;						memmove(b, te.comp.data, te.comp.sz);						packets[i].data = b;						packets[i].sz += te.comp.sz;					}				} 				/* ns timestamp */				ts = (timestamp + timecode) * timestampscale - te.codec.delay;				if(el.id != EBlock){					if(te.fpacket(&out, &te, packets, npackets, ts, key) != 0)						goto err;					npackets = 0;				}				continue;			}		}else			getnumber(EReferenceBlock, refblock)		else			getnumber(ETimestampScale, timestampscale)		else			getfloat(ESamplingFrequency, e.audio.samplerate)		else			getfloat(EOutputSamplingFrequency, e.audio.outsamplerate)		else			getnumber(EChannels, e.audio.channels)		else			getnumber(EBitDepth, e.audio.bps)		else			getnumber(ETrackNumber, e.tracknum)		else			getnumber(ETrackType, e.tracktype)		else			getstring(ECodecID, e.codec.name)		else			getbytes(ECodecPrivate, e.codec.priv)		else			getnumber(ECodecDelay, e.codec.delay)		else			getnumber(EContentCompAlgo, e.comp.algo)		else			getbytes(EContentCompSettings, e.comp)		else			getnumber(EPixelWidth, e.video.width)		else			getnumber(EPixelHeight, e.video.height)		else			getnumber(ETimestamp, timestamp)		else			getnumber(EDefaultDuration, e.perframe)		else			getnumber(ESeekPreRoll, e.seekpreroll)		else			getfloat(EDuration, duration)		else			getnumber(ETrackUID, e.trackuid)		else			getsigned(EDiscardPadding, te.discardpad)		else

Lines 1–500 of 524.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t