shithub: mcfs

ref: b1c20b110bb60ffba60baa27d7c076055438b8e2
dir: /ogg.c/

View raw version
#include <u.h>
#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)
{
	/*                 magic            ver  htype  granule          serial   seq      checksum  page segments  segsz */
	/*         segsz   0                4    5      6                14       18       22        26             27 */
	u8int h[28+255] = {'O','g','g','S', 0,   0,     0,0,0,0,0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,  0,             0};
	u32int crc;
	int psz, i, n, ntot, ip;

	h[5] = htype;
	h[6] = granule;
	h[7] = granule >> 8;
	h[8] = granule >> 16;
	h[9] = granule >> 24;
	h[10] = granule >> 32;
	h[11] = granule >> 40;
	h[12] = granule >> 48;
	h[13] = granule >> 56;
	h[14] = ctx->trackuid;
	h[15] = ctx->trackuid >> 8;
	h[16] = ctx->trackuid >> 16;
	h[17] = ctx->trackuid >> 24;
	h[18] = ctx->frid;
	h[19] = ctx->frid >> 8;
	h[20] = ctx->frid >> 16;
	h[21] = ctx->frid >> 24;
	ctx->frid++;
	h[22] = 0;
	h[23] = 0;
	h[24] = 0;
	h[25] = 0;

	for(ntot = ip = 0; ip < np; ip++){
		n = 1 + p[ip].sz/255;
		if(27+ntot+n >= nelem(h)){
			werrstr("frame is too large");
			return -1;
		}
		for(i = 0, psz = p[ip].sz; i < n; i++){
			h[27+ntot+i] = min(255, psz);
			if(psz == 0)
				break;
			psz -= h[27+ntot+i];
		}
		ntot += n;
	}
	h[26] = ntot;
	ntot += 27;

	for(ip = 0, crc = crc32(0, h, ntot); ip < np; ip++)
		crc = crc32(crc, p[ip].data, p[ip].sz);

	h[22] = crc >> 24;
	h[23] = crc >> 16;
	h[24] = crc >> 8;
	h[25] = crc;
	if(Bwrite(out, h, ntot) != ntot)
		return -1;
	for(ip = 0; ip < np; ip++){
		if(Bwrite(out, p[ip].data, p[ip].sz) != p[ip].sz)
			return -1;
	}

	return 0;
}

int
oggpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts, int key)
{
	/*                     magic                            vendor len   list len */
	u8int opuscomment[] = {'O','p','u','s','T','a','g','s', 0,0,0,0,     0,0,0,0};
	int i, nsg, dsz, total;
	u8int *d;
	u64int gr;
	int sgszs[4];
	Packet onep;

	USED(key);
	ts += ctx->seekpreroll - ctx->discardpad;

	if(ctx->frid == 0){ /* first packet? */
		d = ctx->codec.priv.data;
		dsz = ctx->codec.priv.sz;

		if(dsz < 1){
			werrstr("codec private data missing");
			goto err;
		}

		/* id/codec setup header always goes first */
		if(ctx->fmt == FmtVorbis || ctx->fmt == FmtTheora){
			i = *d++;
			dsz--;
			for(nsg = 0, total = 0; i > 0 && dsz > 0; total += sgszs[nsg], nsg++){
				sgszs[nsg] = 0;
				do{
					sgszs[nsg] += *d;
					dsz--;
					i--;
				}while(*d++ == 0xff);
			}
			if(total > dsz){
				werrstr("setup data out of bounds");
				goto err;
			}
			for(i = 0; i < nsg && dsz > 0; d += sgszs[i], dsz -= sgszs[i], i++){
				onep.data = d;
				onep.sz = sgszs[i];
				if(packet(out, ctx, i == 0 ? 2 : 0, &onep, 1, 0) != 0)
					goto err;
			}
		}
		if(dsz > 0){ /* either leftovers or other codecs, write as is */
			onep.data = d;
			onep.sz = dsz;
			if(packet(out, ctx, 2, &onep, 1, 0) != 0)
				goto err;
		}
		if(ctx->fmt == FmtOpus){ /* Opus requires a comment */
			onep.data = opuscomment;
			onep.sz = sizeof(opuscomment);
			if(packet(out, ctx, 0, &onep, 1, 0) != 0)
				goto err;
		}
	}

	if(ctx->fmt == FmtTheora){
		gr = 0;
	}else{
		gr = (ts * 48) / 1000000;
	}

	if(np > 0 && packet(out, ctx, ctx->discardpad ? 4 : 0, p, np, gr) != 0)
		goto err;

	return 0;
err:
	werrstr("oggpacket: %r");
	return -1;
}