1 
2 module decoder;
3 
4 import std.array;
5 import std.exception;
6 import std.math;
7 import std.stdio;
8 import std.string;
9 import stdint;
10 import bitstream;
11 import vlc;
12 import macroblock;
13 import matrix;
14 import dct;
15 import fast_dct;
16 import math;
17 
18 const MACROBLOCK_SIZE = 16;
19 const ZERO_MV = (short[2]).init;
20 
21 class Plane
22 {
23 	private short[] _pixels;
24 	private size_t _width;
25 	private size_t _height;
26 	ubyte _scalex;
27 	ubyte _scaley;
28 
29 	this(size_t width, size_t height, ubyte scalex, ubyte scaley)
30 	{
31 		_width = width;
32 		_height = height;
33 		_pixels = new short[width * height];
34 		_scalex = scalex;
35 		_scaley = scaley;
36 	}
37 
38 	ref short opIndex(size_t x, size_t y)
39 	{
40 		return _pixels[y * _width + x];
41 	}
42 
43 	short opIndex(size_t x, size_t y) const
44 	{
45 		return _pixels[y * _width + x];
46 	}
47 
48 	const(short)[] pixels() const @property
49 	{
50 		return _pixels;
51 	}
52 
53 	short[] pixels() @property
54 	{
55 		return _pixels;
56 	}
57 
58 	ubyte scalex() const @property
59 	{
60 		return _scalex;
61 	}
62 
63 	ubyte scaley() const @property
64 	{
65 		return _scaley;
66 	}
67 
68 	size_t width() const @property
69 	{
70 		return _width;
71 	}
72 
73 	size_t height() const @property
74 	{
75 		return _height;
76 	}
77 }
78 
79 class Picture
80 {
81 	Plane[3] planes; // YUV
82 
83 	uint dts;
84 	uint pts;
85 	size_t width;
86 	size_t height;
87 
88 	this(size_t width, size_t height, ChromaFormat cf)
89 	{
90 		this.width = width;
91 		this.height = height;
92 
93 		ubyte scalex, scaley;
94 
95 		final switch(cf)
96 		{
97 			case ChromaFormat.C444:
98 				scalex = 0;
99 				scaley = 0;
100 				break;
101 			case ChromaFormat.C422:
102 				scalex = 0;
103 				scaley = 1;
104 				break;
105 			case ChromaFormat.C420:
106 				scalex = 1;
107 				scaley = 1;
108 				break;
109 		}
110 
111 		planes[0] = new Plane(width, height, 0, 0);
112 
113 		foreach(i; 1..3)
114 		{
115 			planes[i] = new Plane(width >> scalex, height >> scaley, scalex, scaley);
116 		}
117 	}
118 
119 	uint width_in_mb() @property const
120 	{
121 		return cast(uint) width / MACROBLOCK_SIZE;
122 	}
123 
124 	uint height_in_mb() @property const
125 	{
126 		return cast(uint) height / MACROBLOCK_SIZE;
127 	}
128 
129 	void dump_block(const short[64] block, int mba, ulong comp, string comment = "")
130 	{
131 		writefln("MBA #%d block #%d%s: ", mba, comp, comment);
132 
133 		for(size_t i=0; i<8; ++i)
134 		{
135 			writef("    ");
136 			for(size_t j=0; j<8; ++j)
137 			{
138 				writef("%3d ", block[i*8+j]);
139 			}
140 			writeln();
141 		}
142 		writeln();
143 	}
144 }
145 
146 class PictureBuilder
147 {
148 	int previous_macroblock_address = -1;
149 	PictureHeader ph;
150 	short[3] dct_pred;
151 	short[2][2][2] PMV;
152 	Picture pic;
153 	Picture[] dpb;
154 
155 	this(PictureHeader ph, Picture[] dpb)
156 	{
157 		this.ph = ph;
158 		this.dpb = dpb;
159 		this.pic = new Picture(ph.si.width, ph.si.height, ph.si.chroma_format);
160 	}
161 
162 	void process(const ref MacroBlock mb)
163 	{
164 		if(mb.incr > 1)
165 		{
166 			process_skipped_mbs(mb.incr - 1);
167 		}
168 
169 		int mba = previous_macroblock_address + mb.incr;
170 		previous_macroblock_address = mba;
171 		//mb.dump2(mba, true);
172 		//mb.dump();
173 
174 		motion_compensate(mb);
175 
176 		if(ph.picture_coding_type == PictureType.I
177 			&& (!mb.p.intra || mb.incr > 1))
178 		{
179 			reset_dc_predictors();
180 		}
181 		uint bx = (mba % pic.width_in_mb) * MACROBLOCK_SIZE;
182 		uint by = (mba / pic.width_in_mb) * MACROBLOCK_SIZE;
183 
184 		foreach(uint bi; 0..mb.block_count)
185 		{
186 			uint cc = (bi < 4)? 0 : ((bi & 1) + 1);
187 
188 			short[64] block;
189 
190 			//dump_block(b.coeffs, mba, bi);
191 
192 			block = reorder_coefs(mb.blocks[bi].coeffs, ph.alternate_scan);
193 
194 			block[0] += dct_pred[cc];
195 			dct_pred[cc] = block[0];
196 
197 			iquant(block, bi, mb);
198 			//dump_block(block, mba, bi, " (dct)");
199 			fast_idct_annexA(block);
200 			//dump_block(block, mba, bi, " (rec samples)");
201 
202 			add_block(block, cc, bi, bx, by);
203 		}
204 
205 	}
206 
207 	private void process_skipped_mbs(int num)
208 	{
209 		if(ph.picture_coding_type == PictureType.P)
210 		{
211 			reset_mv_predictors();
212 			for(int s = previous_macroblock_address + 1; s<previous_macroblock_address + num + 1; ++s)
213 			{
214 				apply_mv(s, ZERO_MV);
215 			}
216 		}
217 	}
218 
219 	private void motion_compensate(const ref MacroBlock mb)
220 	{
221 		enforce(mb.predinfo.type == PredictionType.Frame);
222 		enforce(mb.predinfo.mv_format == MvFormat.Frame);
223 		enforce(mb.predinfo.dmv == false);
224 
225 		if(ph.picture_coding_type == PictureType.P && mb.p.motion_forward == false)
226 		{
227 			apply_mv(previous_macroblock_address, ZERO_MV);
228 			reset_mv_predictors();
229 			return;
230 		}
231 
232 		foreach(r; 0..mb.predinfo.motion_vector_count)
233 		{
234 			if(mb.p.motion_forward)
235 			{
236 				motion_compensate(mb, r, 0);
237 			}
238 			if(mb.p.motion_backward)
239 			{
240 				motion_compensate(mb, r, 1);
241 			}
242 		}
243 	}
244 
245 	void motion_compensate(const ref MacroBlock mb, int r, int s)
246 	{
247 		foreach(t; 0..2)
248 		{
249 			auto r_size = ph.f_code[s][t] - 1;
250 			auto f = (1 << r_size);
251 			auto high = 16 * f - 1;
252 			auto low = -16 * f;
253 			auto range = 32 * f;
254 
255 			short delta;
256 			if(f == 1 || mb.motion_code[r][s][t] == 0)
257 			{
258 				delta = mb.motion_code[r][s][t];
259 			}
260 			else
261 			{
262 				delta = cast(short)(((abs(mb.motion_code[r][s][t]) - 1) * f) + mb.motion_residual[r][s][t] + 1);
263 				if (mb.motion_code[r][s][t] < 0)
264 					delta = -delta;
265 			}
266 
267 			short prediction = PMV[r][s][t];
268 
269 			auto v = cast(short)(prediction + delta);
270 			if(v < low) v += range;
271 			if(v > high) v -= range;
272 
273 			PMV[r][s][t] = v;
274 		}
275 
276 		// form block prediction
277 
278 		int mba = previous_macroblock_address;
279 
280 		apply_mv(mba, PMV[r][s]);
281 	}
282 
283 	private void apply_mv(int mba, const ref short[2] mv0)
284 	{
285 		enforce(dpb.length > 0);
286 		auto rf = dpb.back;
287 
288 		uint bx0 = (mba % pic.width_in_mb) * MACROBLOCK_SIZE;
289 		uint by0 = (mba / pic.width_in_mb) * MACROBLOCK_SIZE;
290 		//writefln("mv[%s][%s][%d][%d]: %d %d", bx, by, r, s, mvx1, mvy1);
291 
292 		foreach(cc; 0..3)
293 		{
294 			auto plane = pic.planes[cc];
295 
296 			uint bx = bx0 >> plane.scalex;
297 			uint by = by0 >> plane.scaley;
298 
299 			uint bw = 16 >> plane.scalex;
300 			uint bh = 16 >> plane.scaley;
301 
302 			short[2] mv = mv0;
303 
304 			mv[0] >>= plane.scalex;
305 			mv[1] >>= plane.scaley;
306 
307 			short[2] mv1 = mv[] / 2;
308 			short[2] mv2 = mv1[] + mv[] % 2;
309 
310 			auto p = plane.pixels.ptr + by * plane.width + bx;
311 			auto pp1 = rf.planes[cc].pixels.ptr + (by + mv1[1]) * plane.width + bx + mv1[0];
312 			auto pp2 = rf.planes[cc].pixels.ptr + (by + mv2[1]) * plane.width + bx + mv2[0];
313 
314 			for(int y=by; y<by + bh; ++y)
315 			{
316 				for(int x=bx; x<bx + bw; ++x, ++p, ++pp1, ++pp2)
317 				{
318 					const auto v = (*pp1 + *pp2) / 2;
319 					*p += v;
320 				}
321 
322 				p   += plane.width - bw;
323 				pp1 += plane.width - bw;
324 				pp2 += plane.width - bw;
325 			}
326 		}
327 	}
328 
329 	void add_block(const ref short[64] block, uint cc, uint comp, uint bx, uint by)
330 	{
331 		auto plane = pic.planes[cc];
332 		short base = 0;
333 		if(ph.picture_coding_type == PictureType.I)
334 		{
335 			base = 128;
336 		}
337 
338 		if(cc > 0)
339 		{
340 			bx >>= plane.scalex;
341 			by >>= plane.scaley;
342 		}
343 
344 		if(cc == 0 || ph.si.chroma_format == ChromaFormat.C444)
345 		{
346 			int cx = (comp % 2) * 8;
347 			int cy = (comp / 2) * 8;
348 
349 			for(int i=0; i<8; ++i)
350 			{
351 				for(int j=0; j<8; ++j)
352 				{
353 					plane[bx + cx + j, by + cy + i] += cast(short) saturate!(int, 0, 255)(block[i * 8 + j] + base);
354 				}
355 			}
356 		}
357 		else if(ph.si.chroma_format == ChromaFormat.C420)
358 		{
359 			for(int i=0; i<8; ++i)
360 			{
361 				for(int j=0; j<8; ++j)
362 				{
363 					const auto v = cast(short) saturate!(int, 0, 255)(block[i * 8 + j] + base);
364 
365 					plane[bx + j, by + i] += v;
366 				}
367 			}
368 		}
369 		else if(ph.si.chroma_format == ChromaFormat.C422)
370 		{
371 			int cy = (comp / 2) * 8;
372 
373 			for(int i=0; i<8; ++i)
374 			{
375 				for(int j=0; j<8; ++j)
376 				{
377 					const auto v = cast(short) saturate!(int, 0, 255)(block[i * 8 + j] + base);
378 					plane[bx + 0 + j, by + cy + i] += v;
379 					plane[bx + 8 + j, by + cy + i] += v;
380 				}
381 			}
382 		}
383 	}
384 
385 	private void reset_dc_predictors()
386 	{
387 		foreach(i; 0..3)
388 		{
389 			//dct_pred[i] = DCT_PRED_DEFAULT[ph.intra_dc_precision];
390 			dct_pred[i] = 0;
391 		}
392 	}
393 
394 	private void reset_mv_predictors()
395 	{
396 		PMV = PMV.init;
397 	}
398 
399 	void process_new_slice(Slice s)
400 	{
401 		reset_dc_predictors();
402 		previous_macroblock_address = int(s.slice_vert_pos * pic.width_in_mb) - 1;
403 	}
404 
405 	private short[64] reorder_coefs(short[64] c, int scan_id)
406 	{
407 		short[64] r;
408 
409 		for(int i=0; i<64; ++i)
410 		{
411 			r[SCAN_MATRIX[scan_id][i]] = c[i];
412 		}
413 
414 		return r;
415 	}
416 
417 	alias quant_saturate = saturate!(short, -2048, 2047);
418 
419 	private void iquant(ref short[64] b, ulong c, const ref MacroBlock mb)
420 	{
421 		auto quantiser_scale = QUANTISER_SCALE_MATRIX[ph.q_scale_type][mb.s.quantiser_scale_code];
422 		int cc = ph.si.chroma_format == ChromaFormat.C420? 0: (c >= 4);
423 		int w = (mb.p.intra? 0: 1) + 2 * cc;
424 
425 		b[0] *=  INTRA_DC_MULT[ph.intra_dc_precision];
426 		b[0] = quant_saturate(b[0]);
427 
428 		short sum = b[0]; // for mismatch control
429 		for(int i=1; i<64; ++i)
430 		{
431 			auto k = mb.p.intra?0:sign(b[i]);
432 			b[i] = cast(short)((2 * b[i] + k) * DEFAULT_QUANT_MATRIX[w][i] * quantiser_scale / 32);
433 
434 			b[i] = quant_saturate(b[i]);
435 
436 			sum |= b[i];
437 		}
438 
439 		// mismatch control
440 		if((sum & 1) == 0)
441 		{
442 			b[63] += (b[63]&1)? -1: 1;
443 		}
444 	}
445 }
446 
447 enum PictureStructure
448 {
449 	Reserved    = 0,
450 	TopField    = 1,
451 	BottomField = 2,
452 	Frame       = 3,
453 }
454 
455 enum PictureType
456 {
457 	I = 1,
458 	P = 2,
459 	B = 3,
460 }
461 
462 class PictureHeader
463 {
464 	SequenceInfo si;
465 
466 	uint temporal_reference;
467 	PictureType picture_coding_type;
468 	uint vbv_delay;
469 
470 	ubyte[2][2] f_code;
471 	ubyte intra_dc_precision;
472 	ubyte picture_structure;
473 	bool top_field_first;
474 	bool frame_pred_frame_dct;
475 	bool concealment_motion_vectors;
476 	bool q_scale_type;
477 	bool intra_vlc_format;
478 	bool alternate_scan;
479 	bool repeat_first_field;
480 	bool chroma_420_type;
481 	bool progressive_frame;
482 	bool composite_display_flag;
483 
484 	// if composite_display_flag
485 	bool v_axis;
486 	ubyte field_sequence;
487 	bool sub_carrier;
488 	ubyte burst_amplitude;
489 	ubyte sub_carrier_phase;
490 
491 	this(SequenceInfo si)
492 	{
493 		this.si = si;
494 	}
495 
496 	void parse(BitstreamReader bs)
497 	{
498 		temporal_reference = bs.read_u(10);
499 		picture_coding_type = cast(PictureType) bs.read_b(3);
500 		vbv_delay = bs.read_u(16);
501 
502 		if(picture_coding_type == 2 || picture_coding_type == 3)
503 		{
504 			bs.skip_u(4);
505 		}
506 
507 		if(picture_coding_type == 3)
508 		{
509 			bs.skip_u(4);
510 		}
511 
512 		ubyte extra_bit = bs.read_u1;
513 		while(extra_bit == 1)
514 		{
515 			bs.skip_u(8);
516 		}
517 	}
518 
519 	void parse_extension(BitstreamReader bs)
520 	{
521 		f_code[0][0] = bs.read_b(4);
522 		f_code[0][1] = bs.read_b(4);
523 		f_code[1][0] = bs.read_b(4);
524 		f_code[1][1] = bs.read_b(4);
525 
526 		intra_dc_precision = bs.read_b(2);
527 		picture_structure = bs.read_b(2);
528 
529 		top_field_first = bs.read_bool();
530 		frame_pred_frame_dct = bs.read_bool();
531 		concealment_motion_vectors = bs.read_bool();
532 		q_scale_type = bs.read_bool();
533 		intra_vlc_format = bs.read_bool();
534 		alternate_scan = bs.read_bool();
535 		repeat_first_field = bs.read_bool();
536 		chroma_420_type = bs.read_bool();
537 		progressive_frame = bs.read_bool();
538 		composite_display_flag = bs.read_bool();
539 
540 		if(composite_display_flag)
541 		{
542 			v_axis = bs.read_bool;
543 			field_sequence = bs.read_b(3);
544 			sub_carrier = bs.read_bool;
545 			burst_amplitude = bs.read_b(7);
546 			sub_carrier_phase = bs.read_b(8);
547 		}
548 
549 		enforce(!concealment_motion_vectors, "concealment_motion_vectors are not implemented");
550 	}
551 
552 	void dump()
553 	{
554 		string pictype;
555 		final switch(picture_coding_type)
556 		{
557 			case PictureType.I: pictype = "I"; break;
558 			case PictureType.P: pictype = "P"; break;
559 			case PictureType.B: pictype = "B"; break;
560 		}
561 
562 		string picstruct;
563 		final switch(picture_structure)
564 		{
565 			case 0b01: picstruct = "Top Field"; break;
566 			case 0b10: picstruct = "Bottom Field"; break;
567 			case 0b11: picstruct = "Frame"; break;
568 		}
569 
570 		writefln("pic #%04d %s %s", temporal_reference, pictype, picstruct);
571 	}
572 }
573 
574 enum Profile
575 {
576 	Simple,
577 	Main,
578 	SnrScalable,
579 	SpartialyScalable,
580 	High,
581 }
582 
583 enum Level
584 {
585 	Low,
586 	Main,
587 	High1440,
588 	High,
589 }
590 
591 enum ChromaFormat
592 {
593 	C420 = 1,
594 	C422 = 2,
595 	C444 = 3,
596 }
597 
598 Profile parse_profile(ubyte profile)
599 {
600 	final switch(profile)
601 	{
602 		case 0b101:
603 			return Profile.Simple;
604 		case 0b100:
605 			return Profile.Main;
606 		case 0b011:
607 			return Profile.SnrScalable;
608 		case 0b010:
609 			return Profile.SpartialyScalable;
610 		case 0b001:
611 			return Profile.High;
612 	}
613 }
614 
615 Level parse_level(ubyte level)
616 {
617 	final switch(level)
618 	{
619 		case 0b1010:
620 			return Level.Low;
621 		case 0b1000:
622 			return Level.Main;
623 		case 0b0110:
624 			return Level.High1440;
625 		case 0b0100:
626 			return Level.High;
627 	}
628 }
629 
630 ChromaFormat parse_chroma_format(ubyte b)
631 {
632 	final switch(b)
633 	{
634 		case 0x01:
635 			return ChromaFormat.C420;
636 		case 0x02:
637 			return ChromaFormat.C422;
638 		case 0x03:
639 			return ChromaFormat.C444;
640 	}
641 }
642 
643 class SequenceInfo
644 {
645 	int width;
646 	int height;
647 	int aspect_ratio;
648 	int frame_rate;
649 	int bitrate;
650 	int vbv_buffer_size;
651 
652 	bool load_intra_quantizer_matrix;
653 	bool load_non_intra_quantizer_matrix;
654 	ubyte[64] intra_quantizer_matrix;
655 	ubyte[64] non_intra_quantizer_matrix;
656 
657 	Profile profile;
658 	Level level;
659 
660 	bool progressive;
661 	bool low_delay;
662 	ChromaFormat chroma_format;
663 
664 	void dump()
665 	{
666 		writefln("SEQUENCE HEADER:");
667 		writefln("\tsize: %sx%s", width, height);
668 		writefln("\tfr: %s", frame_rate);
669 		writefln("\tar: %s", aspect_ratio);
670 		writefln("\tbitrate: %s Mbps", bitrate * 400 / 1000000);
671 		writefln("\tvbv_buffer_size: %s",vbv_buffer_size);
672 
673 		if(load_intra_quantizer_matrix)
674 		{
675 			writefln("\tintra qmatrix:");
676 			foreach(i; 0..8)
677 			{
678 				write("\t\t");
679 				foreach(j; 0..8)
680 				{
681 					write("%02d ", intra_quantizer_matrix[i*8 + j]);
682 				}
683 				writeln("");
684 			}
685 
686 		}
687 
688 		if(load_non_intra_quantizer_matrix)
689 		{
690 			writefln("\tnon_intra qmatrix:");
691 			foreach(i; 0..8)
692 			{
693 				write("\t\t");
694 				foreach(j; 0..8)
695 				{
696 					write("%02d ", non_intra_quantizer_matrix[i*8 + j]);
697 				}
698 				writeln("");
699 			}
700 
701 		}
702 
703 		writefln("profile: %s", profile);
704 		writefln("level:   %s", level);
705 		writefln("progressive:   %s", progressive);
706 		writefln("low_delay:   %s", low_delay);
707 		writefln("chroma_format:   %s", chroma_format);
708 	}
709 }
710 
711 class Slice
712 {
713 	this(PictureHeader ph)
714 	{
715 		this.ph = ph;
716 	}
717 
718 	void parse(BitstreamReader bs, uint start_code)
719 	{
720 		slice_vert_pos = start_code - 1;
721 
722 		if(ph.si.height > 2800)
723 		{
724 			slice_vert_pos = bs.read_u(3) << slice_vert_pos;
725 		}
726 
727 		//TODO: implement priority_breakpoint
728 
729 		quantiser_scale_code = bs.read_u(5);
730 
731 		if(bs.peek_u1)
732 		{
733 			slice_extension_flag = bs.read_bool;
734 			intra_slice = bs.read_bool;
735 			slice_picture_id_enable = bs.read_bool;
736 			slice_picture_id = bs.read_b(6);
737 
738 			while(bs.peek_u1)
739 			{
740 				bs.read_u1;
741 				bs.skip_u(8);
742 			}
743 		}
744 
745 		bs.skip_u1;
746 	}
747 
748 	void dump()
749 	{
750 		writefln("slice %02d:", slice_vert_pos);
751 		writefln("\tquantiser_scale_code:    %d", quantiser_scale_code);
752 		writefln("\tslice_extension_flag:    %d", slice_extension_flag);
753 		writefln("\tintra_slice:             %d", intra_slice);
754 		writefln("\tslice_picture_id_enable: %d", slice_picture_id_enable);
755 		writefln("\tslice_picture_id:        %d", slice_picture_id);
756 	}
757 
758 	PictureHeader ph;
759 	int slice_vert_pos;
760 	int quantiser_scale_code;
761 	bool slice_extension_flag;
762 	bool intra_slice;
763 	bool slice_picture_id_enable;
764 	ubyte slice_picture_id;
765 
766 }
767 
768 struct GopInfo
769 {
770 	int time_code;
771 	bool closed_gop;
772 	bool broken_link;
773 }
774 
775 class Decoder
776 {
777 	this(string filename)
778 	{
779 		_init_parsers();
780 
781 		auto f = File(filename, "rb");
782 
783 		auto content = new ubyte[f.size()];
784 
785 		content = f.rawRead(content);
786 
787 		this.bs = new BitstreamReader(content);
788 	}
789 
790 	Picture decode()
791 	{
792 		while(!bs.eof && !_picture_ready())
793 		{
794 			_read_syntax_element();
795 			//_process_syntax_element(se);
796 		}
797 
798 		if(_pics.length == 0)
799 		{
800 			return null;
801 		}
802 
803 		auto f = _pics[0];
804 		_pics.popFront();
805 		return f;
806 	}
807 
808 	void _read_syntax_element()
809 	{
810 		bs.align_to_next_byte();
811 
812 		int state = 0;
813 		int cnt = 0;
814 
815 		immutable int[][] xlat = [
816 			[1, 0],
817 			[2, 0],
818 			[0, 3],
819 		];
820 
821 		while(state < 3 && !bs.eof)
822 		{
823 			ubyte b = bs.read_u8();
824 			++cnt;
825 
826 			if(b != 0 && b != 1)
827 			{
828 				state = 0;
829 				continue;
830 			}
831 
832 			state = xlat[state][b];
833 		}
834 
835 		if(bs.eof)
836 		{
837 			return;
838 		}
839 
840 		if(cnt > 3) writefln("warn: skipped %s bytes to next high level syntax element", cnt - 3);
841 
842 		auto start_code = bs.read_u8();
843 
844 		//writefln("found syntax element %X (%s) at position %s", start_code, start_code_str(start_code), bs.bits_read() / 8);
845 		if(start_code !in _parsers)
846 		{
847 			writefln("unknown syntax element %X (%s) at position %s", start_code, start_code_str(start_code), bs.bits_read() / 8);
848 			return;
849 		}
850 
851 		_parsers[start_code](start_code);
852 	}
853 
854 	private void _maybe_flush_picture()
855 	{
856 		if(picture_builder !is null)
857 		{
858 			_pics ~= picture_builder.pic;
859 			dpb ~= picture_builder.pic;
860 			if(dpb.length > 2)
861 			{
862 				dpb = dpb[$-2..$];
863 			}
864 			picture_builder = null;
865 		}
866 	}
867 
868 	private void _parse_sequence_header(ubyte start_code)
869 	{
870 		_maybe_flush_picture();
871 
872 		si = new SequenceInfo;
873 
874 		si.width = bs.read_u(12);
875 		si.height = bs.read_u(12);
876 		si.aspect_ratio = bs.read_u(4);
877 
878 		int frame_rate_code = bs.read_u(4);
879 
880 		si.bitrate = bs.read_u(18);
881 		bs.skip_u1();
882 		si.vbv_buffer_size = bs.read_u(10);
883 		bs.skip_u1();
884 
885 		si.load_intra_quantizer_matrix = cast(bool) bs.read_u1;
886 
887 		if(si.load_intra_quantizer_matrix)
888 		{
889 			bs.read_bytes(si.intra_quantizer_matrix);
890 			throw new Exception("custom quantizer matrixes are not supported");
891 		}
892 
893 		si.load_non_intra_quantizer_matrix = cast(bool) bs.read_u1;
894 
895 		if(si.load_non_intra_quantizer_matrix)
896 		{
897 			bs.read_bytes(si.non_intra_quantizer_matrix);
898 			throw new Exception("custom quantizer matrixes are not supported");
899 		}
900 
901 		//si.dump();
902 	}
903 
904 	private void _parse_sequence_extention()
905 	{
906 		ubyte pal = bs.read_u8(); // profile_and_level
907 
908 		if(pal & 0x80)
909 		{
910 			throw new Exception("escape code not implemented");
911 		}
912 		else
913 		{
914 			si.profile = parse_profile((pal & 0x70) >> 4);
915 			si.level = parse_level(pal & 0x0f);
916 		}
917 
918 		si.progressive = cast(bool) bs.read_u1;
919 		si.chroma_format = parse_chroma_format(cast(ubyte) bs.read_u(2));
920 
921 		si.width = (si.width & 0x0fff) | (bs.read_u(2) << 12);
922 		si.height = (si.height & 0x0fff) | (bs.read_u(2) << 12);
923 		si.bitrate = (si.bitrate & 0x03ffff) | (bs.read_u(12) << 18);
924 		enforce(bs.read_u1 == 1, "marker bit");
925 		si.vbv_buffer_size = (si.vbv_buffer_size & 0x03ff) | (bs.read_u(8) << 10);
926 
927 		si.low_delay = cast(bool) bs.read_u1;
928 
929 		// frame_rate
930 		bs.read_u(2);
931 		bs.read_u(5);
932 
933 		enforce(bs.is_byte_aligned);
934 		//si.dump();
935 	}
936 
937 	private void _parse_extension(ubyte start_code)
938 	{
939 		ubyte extension_start_code = bs.read_b(4);
940 
941 		_ext_parsers[extension_start_code]();
942 	}
943 
944 	private void _parse_picture_extension()
945 	{
946 		ph.parse_extension(bs);
947 		//writefln("================== pic #%03d =======================", ph.temporal_reference);
948 		//ph.dump();
949 		//expected_extension = ExpectedExtension.ExtensionAndUserData;
950 		//extension_i = 2;
951 	}
952 
953 	private void _parse_slice(ubyte start_code)
954 	{
955 		auto s = new Slice(ph);
956 
957 		s.parse(bs, start_code);
958 		//s.dump();
959 
960 		picture_builder.process_new_slice(s);
961 
962 		do {
963 			auto mb = MacroBlock(s);
964 			auto oldp = bs.bits_read;
965 			mb.parse(bs);
966 			auto newp = bs.bits_read;
967 			//writefln("mb: %d -> %d (%d)", oldp, newp, newp - oldp);
968 			picture_builder.process(mb);
969 		} while( bs.nextbits(23) != 0);
970 	}
971 
972 	private void _parse_picture_header(ubyte start_code)
973 	{
974 		_maybe_flush_picture();
975 		ph = new PictureHeader(si);
976 		ph.parse(bs);
977 		picture_builder = new PictureBuilder(ph, dpb);
978 	}
979 
980 	private void _parse_extension_and_user_data(int i)
981 	{
982 		writefln("extension_and_user_data(%d)", i);
983 	}
984 
985 	private void _parse_group_of_picture_header(ubyte start_code)
986 	{
987 		gopi.time_code = bs.read_u(25);
988 		gopi.closed_gop = bs.read_bool;
989 		gopi.broken_link = bs.read_bool;
990 
991 		//expected_extension = ExpectedExtension.ExtensionAndUserData;
992 		//extension_i = 1;
993 	}
994 
995 	private bool _picture_ready()
996 	{
997 		return _pics.length > 0;
998 	}
999 
1000 	private void _init_parsers()
1001 	{
1002 		_parsers[0x00] = &_parse_picture_header;
1003 		_parsers[0xb3] = &_parse_sequence_header;
1004 		_parsers[0xb5] = &_parse_extension;
1005 		_parsers[0xb8] = &_parse_group_of_picture_header;
1006 
1007 		foreach(ubyte sc; 0x01..0xaf)
1008 		{
1009 			_parsers[sc] = &this._parse_slice;
1010 		}
1011 
1012 		_ext_parsers[0x01] = &_parse_sequence_extention;
1013 		_ext_parsers[0x08] = &_parse_picture_extension;
1014 	}
1015 
1016 	private alias Parser = void delegate (ubyte);
1017 	private alias ExtParser = void delegate ();
1018 
1019 	private BitstreamReader bs;
1020 	private Parser[ubyte] _parsers;
1021 	private ExtParser[ubyte] _ext_parsers;
1022 	private SequenceInfo si;
1023 	private GopInfo gopi;
1024 	private PictureHeader ph;
1025 	private PictureBuilder picture_builder;
1026 	private Picture[] _pics;
1027 	private Picture[] dpb;
1028 }
1029 
1030 enum ExpectedExtension
1031 {
1032 	Sequence,
1033 	Picture,
1034 	ExtensionAndUserData,
1035 }
1036 
1037 extern string start_code_str(ubyte start_code)
1038 {
1039 	byte	number;
1040 	string str = null;
1041 	switch (start_code)
1042 	{
1043 		// H.262 start codes
1044 		case 0x00: str = "Picture"; break;
1045 		case 0xB0: str = "Reserved"; break;
1046 		case 0xB1: str = "Reserved"; break;
1047 		case 0xB2: str = "User data"; break;
1048 		case 0xB3: str = "SEQUENCE HEADER"; break;
1049 		case 0xB4: str = "Sequence error"; break;
1050 		case 0xB5: str = "Extension start"; break;
1051 		case 0xB6: str = "Reserved"; break;
1052 		case 0xB7: str = "SEQUENCE END"; break;
1053 		case 0xB8: str = "Group start"; break;
1054 
1055 		// System start codes - 13818-1 p32 Table 2-18 stream_id
1056 		// If these occur, then we're seeing PES headers
1057 		// - maybe we're looking at transport stream data?
1058 		case 0xBC: str = "SYSTEM START: Program stream map"; break;
1059 		case 0xBD: str = "SYSTEM START: Private stream 1"; break;
1060 		case 0xBE: str = "SYSTEM START: Padding stream"; break;
1061 		case 0xBF: str = "SYSTEM START: Private stream 2"; break;
1062 		case 0xF0: str = "SYSTEM START: ECM stream"; break;
1063 		case 0xF1: str = "SYSTEM START: EMM stream"; break;
1064 		case 0xF2: str = "SYSTEM START: DSMCC stream"; break;
1065 		case 0xF3: str = "SYSTEM START: 13522 stream"; break;
1066 		case 0xF4: str = "SYSTEM START: H.222 A stream"; break;
1067 		case 0xF5: str = "SYSTEM START: H.222 B stream"; break;
1068 		case 0xF6: str = "SYSTEM START: H.222 C stream"; break;
1069 		case 0xF7: str = "SYSTEM START: H.222 D stream"; break;
1070 		case 0xF8: str = "SYSTEM START: H.222 E stream"; break;
1071 		case 0xF9: str = "SYSTEM START: Ancillary stream"; break;
1072 		case 0xFF: str = "SYSTEM START: Program stream directory"; break;
1073 
1074 		default: str = null; break;
1075 	}
1076 
1077 	if (str != null)
1078 	{
1079 
1080 	}
1081 	else if (start_code == 0x47)
1082 		str = "TRANSPORT STREAM sync byte";
1083 	else if (start_code >= 0x01 && start_code <= 0xAF)
1084 		str = format("Slice, vertical posn %d", start_code);
1085 	else if (start_code >= 0xC0 && start_code <=0xDF)
1086 	{
1087 		number = start_code & 0x1F;
1088 		str = format("SYSTEM START: Audio stream %02x",number);
1089 	}
1090 	else if (start_code >= 0xE0 && start_code <= 0xEF)
1091 	{
1092 		number = start_code & 0x0F;
1093 		str = format("SYSTEM START: Video stream %x",number);
1094 	}
1095 	else if (start_code >= 0xFC && start_code <= 0xFE)
1096 		str = "SYSTEM START: Reserved data stream";
1097 	else
1098 		str = "SYSTEM START: Unrecognised stream id";
1099 
1100 	return str;
1101 }