~ft/treason

ref: c6f38fccec03ed1c226e423bdbe2efd6fa02d224 treason/decoder_h264.c -rw-r--r-- 3.9 KiB
c6f38fcc — Sigrid Solveig Haflínudóttir h264: move decoder-specific logic where it belongs (more) 11 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#include <decoder/core/inc/decoder.h>
#include <decoder/core/inc/manage_dec_ref.h>
#include <decoder/core/inc/decoder9.h>
#include <thread.h>
#include "frame.h"
#include "stream.h"
#include "decoder.h"
#include "misc.h"

#pragma lib "../openh264/codec/libopenh264.$M.a"

static void
logfun(void *ctx, const int32_t level, const char *fmt, va_list argv)
{
	USED(ctx);

	if(debug >= level){
		fprint(2, "h264: ");
		vfprint(2, fmt, argv);
		fprint(2, "\n");
	}
}

static int
sendframe(H264Aux *a, uvlong dt, Channel *c)
{
	int w, h, *stride;
	Frame *f;

	w = a->info.UsrData.sSystemBuffer.iWidth;
	h = a->info.UsrData.sSystemBuffer.iHeight;
	stride = a->info.UsrData.sSystemBuffer.iStride;

	if((f = newframe(w, h, a->data, stride[0], stride[1])) == nil)
		return -1;

	f->crop.left = a->ctx.sFrameCrop.iLeftOffset;
	f->crop.top = a->ctx.sFrameCrop.iTopOffset;
	f->crop.right = w - a->ctx.sFrameCrop.iRightOffset;
	f->crop.bottom = h - a->ctx.sFrameCrop.iBottomOffset;
	f->dt = dt;

	if(sendp(c, f) < 0){
		free(f);
		return -1;
	}

	return 0;
}

static void
decode(void *x)
{
	uvlong start, framenum, lasttimestamp, ts;
	Streamframe sf;
	Decoder *d;
	Channel *c;
	int res, n;
	H264Aux *a;

	threadsetname("decoder/h264");
	d = x;
	a = d->aux;
	lasttimestamp = 0;
	for(res = 0, framenum = 0; res >= 0 && (res = Sread(d->s, &sf)) == 0 && sf.sz > 0; framenum++){
		if(sf.sz > 4 && sf.buf[0] == 0 && sf.buf[1] == 0){
			if(sf.buf[2] == 1)
				n = sf.buf[3];
			else if(sf.buf[3] == 1)
				n = sf.buf[4];
			else
				n = 0;
			n &= 0x1f;
			if(n == 7 || n == 8){
				/*
				 * FIXME not sure whether that is correct (most likely not)
				 * flush everything on sps/pps
				 * obviously, there might be multiple NALs in a single stream frame
				 */
				while(a->iNumOfPicts > 0){
					h264flush(a);
					ts = a->info.uiOutYuvTimeStamp;
					if(ts < lasttimestamp){
						/* FIXME this happens in some videos... */
						//werrstr("wrong timestamp at flush: %llud < %llud", ts, lasttimestamp);
						//res = -1;
						//break;
						continue;
					}
					if(sendframe(a, (ts - lasttimestamp) * d->timebase * 1000000000ULL, d->frames) < 0)
						goto done;
					lasttimestamp = ts;
				}
				if(res < 0)
					break;
			}
		}

		start = nanosec();
		if((res = h264decode(a, sf.buf, sf.sz, &sf.timestamp)) != 0)
			break;
		sf.buf = nil;
		sf.sz = 0;
		if((res = h264decode(a, sf.buf, sf.sz, &sf.timestamp)) != 0)
			break;
		d->decodetime = nanosec() - start;

		if(a->data[0] == nil || sf.timestamp < lasttimestamp)
			continue;

		if(sendframe(a, (sf.timestamp - lasttimestamp) * d->timebase * 1000000000ULL, d->frames) < 0)
			goto done;
		lasttimestamp = sf.timestamp;
	}
	if(res != 0)
		fprint(2, "h264: frame %llud: %r\n", framenum);
	else{
		/* FIXME the frames are finished but there might still be left-overs */
		ResetReorderingPictureBuffers(a, a->pics, false);
		WelsResetRefPic(&a->ctx);
	}

done:
	WelsEndDecoder(&a->ctx);
	free(a);
	c = d->finished;
	sendp(c, res == 0 ? nil : "error");
	chanclose(c);

	threadexits(nil);
}

static int
h264open(Decoder *d)
{
	H264Aux *a;
	int res;

	a = calloc(1, sizeof(*a));
	a->ctx.pLastDecPicInfo = &a->pic;
	a->ctx.pVlcTable = &a->vlctbl;
	a->ctx.pDecoderStatistics = &a->stat;
	a->ctx.pMemAlign = &cMemoryAlign;
	a->ctx.pPictInfoList = a->pics;
	a->ctx.pPictReoderingStatus = a;
	a->logctx.pfLog = logfun;
	a->ctx.sLogCtx = a->logctx;

	WelsDecoderDefaults(&a->ctx, &a->logctx);
	WelsDecoderSpsPpsDefaults(&a->ctx.sSpsPpsCtx);

	a->ctx.pParam = calloc(1, sizeof(SDecodingParam));
	a->ctx.pParam->sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;

	if((res = WelsInitDecoder(&a->ctx, &a->logctx)) != 0){
		werrstr("WelsInitDecoder: %s", h264err2s(res));
		free(a);
		return -1;
	}
	ResetReorderingPictureBuffers(a, a->pics, true);
	d->aux = a;
	proccreate(decode, d, 65536);

	return res;
}

static void
h264close(Decoder *d)
{
	USED(d);
}

Decoderops h264ops = {
	.open = h264open,
	.close = h264close,
};