shithub: mcfs

ref: 74533c8ff1ab84ff0949d52c3726ccfcfc1bf76a
dir: mcfs/ivf.c

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

enum {
	Timedenum = 1000ULL,
};

static int
ivfframe(Biobuf *out, u8int *p, int sz, uvlong ts, int avcsplit)
{
	u8int d[12];
	int n, len;

	d[0] = sz;
	d[1] = sz >> 8;
	d[2] = sz >> 16;
	d[3] = sz >> 24;
	d[4] = ts;
	d[5] = ts >> 8;
	d[6] = ts >> 16;
	d[7] = ts >> 24;
	d[8] = ts >> 32;
	d[9] = ts >> 40;
	d[10] = ts >> 48;
	d[11] = ts >> 56;
	if(Bwrite(out, d, 12) != 12)
		return -1;
	if(avcsplit){
		for(n = 0; n < sz; n += len+4){
			len = p[n+0]<<24 | p[n+1]<<16 | p[n+2]<<8 | p[n+3];
			p[n+0] = 0;
			p[n+1] = 0;
			p[n+2] = 0;
			p[n+3] = 1;
		}
	}

	return sz > 0 ? (Bwrite(out, p, sz) != sz ? -1 : 0) : 0;
}

int
ivfpacket(Biobuf *out, Packetctx *ctx, Packet *p, int np, uvlong ts, int key)
{
	u8int d[0x20], *x, *y, *e;
	int i, n, len;
	uvlong dur;

	dur = ctx->duration / 1000000ULL;
	ts = ts / 1000000ULL;

	for(; np > 0; np--, p++){
		if(ctx->frid == 0){
			memmove(d, "DKIF", 4);
			d[4] = 0;
			d[5] = 0;
			d[6] = 0x20;
			d[7] = 0;
			if(ctx->fmt == FmtAv01)
				memmove(d+8, "AV01", 4);
			else if(ctx->fmt == FmtAvc1)
				memmove(d+8, "AVC1", 4);
			else if(ctx->fmt == FmtVp08)
				memmove(d+8, "VP80", 4);
			else if(ctx->fmt == FmtVp09)
				memmove(d+8, "VP90", 4);
			else{
				werrstr("unsupported video format %T", ctx->fmt);
				goto err;
			}
			d[12] = ctx->video.width;
			d[13] = ctx->video.width >> 8;
			d[14] = ctx->video.height;
			d[15] = ctx->video.height >> 8;
			/* timebase denum */
			d[16] = Timedenum;
			d[17] = Timedenum >> 8;
			d[18] = Timedenum >> 16;
			d[19] = Timedenum >> 24;
			/* timebase num */
			d[20] = 1;
			d[21] = 0;
			d[22] = 0;
			d[23] = 0;
			/* FIXME is it a "number of frames" or "total duration?" */
			d[24] = dur;
			d[25] = dur >> 8;
			d[26] = dur >> 16;
			d[27] = dur >> 24;
			d[28] = dur >> 32; /* FIXME is it 64 bits? */
			d[29] = dur >> 40;
			d[30] = dur >> 48;
			d[31] = dur >> 56;

			if(Bwrite(out, d, 0x20) != 0x20)
				goto err;

			if(ctx->codec.priv.sz > 0){
				if(ctx->fmt == FmtAv01)
					goto skip;
				if(ctx->fmt != FmtAvc1){
					werrstr("no idea what to do with codec private data for non-AVC1");
					goto err;
				}

				x = ctx->codec.priv.data;
				e = x + ctx->codec.priv.sz;
				if(x[0] != 1){
					werrstr("avc config: invalid version %d", x[0]);
					goto err;
				}
				if((x[4] & 3) != 3){
					werrstr("avc config: NAL size %d not implemented", (x[4] & 3)+1);
					goto err;
				}
				/* sps */
				x[0] = 0;
				x[1] = 0;
				x[2] = 0;
				x[3] = 1;
				n = x[5] & 0x1f;
				y = x+6;
				for(i = 0; i < n; i++){
					len = y[0] << 8 | y[1];
					y += 2;
					if(y+len > e){
						werrstr("sps out of range");
						goto err;
					}
					ctx->ps[ctx->nps].p = malloc(4+len);
					memmove(ctx->ps[ctx->nps].p, x, 4);
					memmove(ctx->ps[ctx->nps].p+4, y, len);
					ctx->ps[ctx->nps++].n = 4+len;
					ctx->frid++;
					y += len;
				}
				/* pps */
				n = *y++;
				for(i = 0; i < n; i++){
					len = y[0] << 8 | y[1];
					y += 2;
					if(y+len > e){
						werrstr("pps out of range");
						goto err;
					}
					ctx->ps[ctx->nps].p = malloc(4+len);
					memmove(ctx->ps[ctx->nps].p, x, 4);
					memmove(ctx->ps[ctx->nps].p+4, y, len);
					ctx->ps[ctx->nps++].n = 4+len;
					ctx->frid++;
					y += len;
				}
			}
		}

skip:
		if(key && ctx->fmt == FmtAvc1){
			for(i = 0; i < ctx->nps; i++)
				ivfframe(out, ctx->ps[i].p, ctx->ps[i].n, 0, 0);
		}
		if(ivfframe(out, p->data, p->sz, ts, ctx->fmt == FmtAvc1) != 0)
			goto err;
		ctx->frid++;
	}

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