shithub: mcfs

ref: 24a65311b538504923aaee04393fd363c2a63356
dir: /ogg.c/

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

static int sgszs[256];

static int
packet(Biobuf *out, Framectx *ctx, int htype, u8int *buf, int sz, 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;

	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;
	n = 1 + (sz > 0 ? (sz-1)/255 : 0);
	h[26] = n;
	if(n+27 >= nelem(h)){
		werrstr("frame is too large");
		return -1;
	}
	for(i = 0, psz = sz; i < n && i < nelem(h)-27; i++){
		h[27+i] = min(255, psz);
		psz -= h[27+i];
		if(psz == 0)
			break;
	}
	n += 27;
	crc = crc32(crc32(0, h, n), buf, sz);
	h[22] = crc >> 24;
	h[23] = crc >> 16;
	h[24] = crc >> 8;
	h[25] = crc;
	if(Bwrite(out, h, n) != n || Bwrite(out, buf, sz) != sz)
		return -1;

	return 0;
}

int
oggpacket(Biobuf *out, Framectx *ctx, u8int *buf, int sz, uvlong ts)
{
	/*                     magic                            vendor len   list len */
	u8int opuscomment[] = {'O','p','u','s','T','a','g','s', 0,0,0,0,     0,0,0,0};
	int r, i, nsg, dsz, total;
	u8int *d;
	u64int gr;

	gr = ts * 48 / 1000000;
	r = 0;
	if(ctx->frid == 0){ /* first packet? */
		ctx->frid;

		d = ctx->codec.priv.data;
		dsz = ctx->codec.priv.sz;

		/* id/codec setup header always goes first */
		if(ctx->codec.priv.sz > 0){ /* if we have codec setup data, write it */
			if(ctx->fmt == FmtVorbis){
				nsg = *d++;
				dsz--;
				for(i = 0, total = 0; i < nsg && dsz > 0; i++, total += sgszs[i]){
					sgszs[i] = 0;
					do{
						sgszs[i] += *d;
						dsz--;
					}while(*d++ == 0xff);
				}
				if(total > dsz){
					werrstr("vorbis setup data out of bounds");
					goto err;
				}
				for(i = 0; i < nsg && dsz > 0; d += sgszs[i], dsz -= sgszs[i], i++){
					if(packet(out, ctx, i == 0 ? 2 : 0, d, sgszs[i], 0) != 0)
						goto err;
				}
				if(dsz > 0)
					r = packet(out, ctx, 0, d, dsz, 0);
			}else{
				r = packet(out, ctx, 2, d, dsz, 0);
			}
			sz = 0; /* don't duplicate it */
		}else{ /* otherwise let's hope the first packet has that data itself */
			r = packet(out, ctx, 2, buf, sz, 0);
		}
		if(r != 0)
			goto err;

		/* comment */
		if(ctx->fmt == FmtOpus)
			r = packet(out, ctx, 0, opuscomment, sizeof(opuscomment), 0);
		if(r != 0)
			goto err;
	}

	if(sz > 0 && packet(out, ctx, 0, buf, sz, gr) != 0)
		goto err;

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