mirror of
				https://github.com/jart/cosmopolitan.git
				synced 2025-10-27 03:16:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1116 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1116 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8   -*-│
 | |
| │vi: set et ft=c ts=4 sw=4 fenc=utf-8                                       :vi│
 | |
| ╞══════════════════════════════════════════════════════════════════════════════╡
 | |
| │  PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer           │
 | |
| │  Dominic Szablewski - https://phoboslab.org                                  │
 | |
| │                                                                              │
 | |
| │  The MIT License(MIT)                                                        │
 | |
| │  Copyright(c) 2019 Dominic Szablewski                                        │
 | |
| │                                                                              │
 | |
| │  Permission is hereby granted, free of charge, to any person obtaining       │
 | |
| │  a copy of this software and associated documentation files(the              │
 | |
| │  "Software"), to deal in the Software without restriction, including         │
 | |
| │  without limitation the rights to use, copy, modify, merge, publish,         │
 | |
| │  distribute, sublicense, and / or sell copies of the Software, and to        │
 | |
| │  permit persons to whom the Software is furnished to do so, subject to       │
 | |
| │  the following conditions:                                                   │
 | |
| │                                                                              │
 | |
| │    The above copyright notice and this permission notice shall be            │
 | |
| │    included in all copies or substantial portions of the Software.           │
 | |
| │                                                                              │
 | |
| │  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,             │
 | |
| │  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF          │
 | |
| │  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                       │
 | |
| │  NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE       │
 | |
| │  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN             │
 | |
| │  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN           │
 | |
| │  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE            │
 | |
| │  SOFTWARE.                                                                   │
 | |
| ╚─────────────────────────────────────────────────────────────────────────────*/
 | |
| #include "dsp/mpeg/blockset.h"
 | |
| #include "dsp/mpeg/buffer.h"
 | |
| #include "dsp/mpeg/idct.h"
 | |
| #include "dsp/mpeg/mpeg.h"
 | |
| #include "dsp/mpeg/video.h"
 | |
| #include "libc/bits/initializer.internal.h"
 | |
| #include "libc/fmt/conv.h"
 | |
| #include "libc/log/log.h"
 | |
| #include "libc/macros.internal.h"
 | |
| #include "libc/math.h"
 | |
| #include "libc/mem/mem.h"
 | |
| #include "libc/str/str.h"
 | |
| #include "libc/time/time.h"
 | |
| #include "libc/x/x.h"
 | |
| 
 | |
| asm(".ident\t\"\\n\\n\
 | |
| PL_MPEG (MIT License)\\n\
 | |
| Copyright(c) 2019 Dominic Szablewski\\n\
 | |
| https://phoboslab.org\"");
 | |
| asm(".include \"libc/disclaimer.inc\"");
 | |
| 
 | |
| // -----------------------------------------------------------------------------
 | |
| // plm_video implementation
 | |
| 
 | |
| // Inspired by Java MPEG-1 Video Decoder and Player by Zoltan Korandi
 | |
| // https://sourceforge.net/projects/javampeg1video/
 | |
| 
 | |
| #define GETCONST(ARRAY, DEFAULT)
 | |
| 
 | |
| static const int PLM_VIDEO_PICTURE_TYPE_INTRA = 1;
 | |
| static const int PLM_VIDEO_PICTURE_TYPE_PREDICTIVE = 2;
 | |
| static const int PLM_VIDEO_PICTURE_TYPE_B = 3;
 | |
| 
 | |
| static const int PLM_START_SEQUENCE = 0xB3;
 | |
| static const int PLM_START_SLICE_FIRST = 0x01;
 | |
| static const int PLM_START_SLICE_LAST = 0xAF;
 | |
| static const int PLM_START_PICTURE = 0x00;
 | |
| static const int PLM_START_EXTENSION = 0xB5;
 | |
| static const int PLM_START_USER_DATA = 0xB2;
 | |
| 
 | |
| static const float PLM_VIDEO_PIXEL_ASPECT_RATIO[] = {
 | |
|     1.0000, /* square pixels */
 | |
|     0.6735, /* 3:4? */
 | |
|     0.7031, /* MPEG-1 / MPEG-2 video encoding divergence? */
 | |
|     0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815,
 | |
|     1.0255, 1.0695, 1.0950, 1.1575, 1.2051};
 | |
| 
 | |
| static const float PLM_VIDEO_PICTURE_RATE[] = {
 | |
|     23.976, /* NTSC-Film */
 | |
|     24.000, /* NTSC-Film (enriched for foreign nations) */
 | |
|     25.000, /* PAL (Britain, Africa, China, etc.) */
 | |
|     29.970, /* NTSC */
 | |
|     30.000, /* NTSC (enriched for foreign nations) */
 | |
|     50.000, /* PAL? */
 | |
|     59.940, /* NTSC-Wow */
 | |
|     60.000  /* NTSC-Wow (enriched for foreign nations) */
 | |
| };
 | |
| 
 | |
| static const uint8_t PLM_VIDEO_ZIG_ZAG[] = /* clang-format off */ {
 | |
|      0,  1,  8, 16,  9,  2,  3, 10,
 | |
| 	17, 24, 32, 25, 18, 11,  4,  5,
 | |
|     12, 19, 26, 33, 40, 48, 41, 34,
 | |
| 	27, 20, 13,  6,  7, 14, 21, 28,
 | |
|     35, 42, 49, 56, 57, 50, 43, 36,
 | |
| 	29, 22, 15, 23, 30, 37, 44, 51,
 | |
|     58, 59, 52, 45, 38, 31, 39, 46,
 | |
| 	53, 60, 61, 54, 47, 55, 62, 63,
 | |
| } /* clang-format on */;
 | |
| 
 | |
| static const uint8_t PLM_VIDEO_INTRAQUANT_MATRIX[] = /* clang-format off */ {
 | |
|      8, 16, 19, 22, 26, 27, 29, 34,
 | |
| 	16, 16, 22, 24, 27, 29, 34, 37,
 | |
|     19, 22, 26, 27, 29, 34, 34, 38,
 | |
| 	22, 22, 26, 27, 29, 34, 37, 40,
 | |
|     22, 26, 27, 29, 32, 35, 40, 48,
 | |
| 	26, 27, 29, 32, 35, 40, 48, 58,
 | |
|     26, 27, 29, 34, 38, 46, 56, 69,
 | |
| 	27, 29, 35, 38, 46, 56, 69, 83,
 | |
| } /* clang-format on */;
 | |
| 
 | |
| static const uint8_t PLM_VIDEO_NONINTRAQUANT_MATRIX[] = /* clang-format off */ {
 | |
|     16, 16, 16, 16, 16, 16, 16, 16,
 | |
| 	16, 16, 16, 16, 16, 16, 16, 16,
 | |
|     16, 16, 16, 16, 16, 16, 16, 16,
 | |
| 	16, 16, 16, 16, 16, 16, 16, 16,
 | |
|     16, 16, 16, 16, 16, 16, 16, 16,
 | |
| 	16, 16, 16, 16, 16, 16, 16, 16,
 | |
|     16, 16, 16, 16, 16, 16, 16, 16,
 | |
| 	16, 16, 16, 16, 16, 16, 16, 16,
 | |
| } /* clang-format on */;
 | |
| 
 | |
| static const uint8_t PLM_VIDEO_PREMULTIPLIER_MATRIX[] = /* clang-format off */ {
 | |
|     32, 44, 42, 38, 32, 25, 17,  9,
 | |
| 	44, 62, 58, 52, 44, 35, 24, 12,
 | |
|     42, 58, 55, 49, 42, 33, 23, 12,
 | |
| 	38, 52, 49, 44, 38, 30, 20, 10,
 | |
|     32, 44, 42, 38, 32, 25, 17,  9,
 | |
| 	25, 35, 33, 30, 25, 20, 14,  7,
 | |
|     17, 24, 23, 20, 17, 14,  9,  5,
 | |
| 	 9, 12, 12, 10,  9,  7,  5,  2,
 | |
| } /* clang-format on */;
 | |
| 
 | |
| static const plm_vlc_t PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT[] = {
 | |
|     {1 << 1, 0},  {0, 1},        //   0: x
 | |
|     {2 << 1, 0},  {3 << 1, 0},   //   1: 0x
 | |
|     {4 << 1, 0},  {5 << 1, 0},   //   2: 00x
 | |
|     {0, 3},       {0, 2},        //   3: 01x
 | |
|     {6 << 1, 0},  {7 << 1, 0},   //   4: 000x
 | |
|     {0, 5},       {0, 4},        //   5: 001x
 | |
|     {8 << 1, 0},  {9 << 1, 0},   //   6: 0000x
 | |
|     {0, 7},       {0, 6},        //   7: 0001x
 | |
|     {10 << 1, 0}, {11 << 1, 0},  //   8: 0000 0x
 | |
|     {12 << 1, 0}, {13 << 1, 0},  //   9: 0000 1x
 | |
|     {14 << 1, 0}, {15 << 1, 0},  //  10: 0000 00x
 | |
|     {16 << 1, 0}, {17 << 1, 0},  //  11: 0000 01x
 | |
|     {18 << 1, 0}, {19 << 1, 0},  //  12: 0000 10x
 | |
|     {0, 9},       {0, 8},        //  13: 0000 11x
 | |
|     {-1, 0},      {20 << 1, 0},  //  14: 0000 000x
 | |
|     {-1, 0},      {21 << 1, 0},  //  15: 0000 001x
 | |
|     {22 << 1, 0}, {23 << 1, 0},  //  16: 0000 010x
 | |
|     {0, 15},      {0, 14},       //  17: 0000 011x
 | |
|     {0, 13},      {0, 12},       //  18: 0000 100x
 | |
|     {0, 11},      {0, 10},       //  19: 0000 101x
 | |
|     {24 << 1, 0}, {25 << 1, 0},  //  20: 0000 0001x
 | |
|     {26 << 1, 0}, {27 << 1, 0},  //  21: 0000 0011x
 | |
|     {28 << 1, 0}, {29 << 1, 0},  //  22: 0000 0100x
 | |
|     {30 << 1, 0}, {31 << 1, 0},  //  23: 0000 0101x
 | |
|     {32 << 1, 0}, {-1, 0},       //  24: 0000 0001 0x
 | |
|     {-1, 0},      {33 << 1, 0},  //  25: 0000 0001 1x
 | |
|     {34 << 1, 0}, {35 << 1, 0},  //  26: 0000 0011 0x
 | |
|     {36 << 1, 0}, {37 << 1, 0},  //  27: 0000 0011 1x
 | |
|     {38 << 1, 0}, {39 << 1, 0},  //  28: 0000 0100 0x
 | |
|     {0, 21},      {0, 20},       //  29: 0000 0100 1x
 | |
|     {0, 19},      {0, 18},       //  30: 0000 0101 0x
 | |
|     {0, 17},      {0, 16},       //  31: 0000 0101 1x
 | |
|     {0, 35},      {-1, 0},       //  32: 0000 0001 00x
 | |
|     {-1, 0},      {0, 34},       //  33: 0000 0001 11x
 | |
|     {0, 33},      {0, 32},       //  34: 0000 0011 00x
 | |
|     {0, 31},      {0, 30},       //  35: 0000 0011 01x
 | |
|     {0, 29},      {0, 28},       //  36: 0000 0011 10x
 | |
|     {0, 27},      {0, 26},       //  37: 0000 0011 11x
 | |
|     {0, 25},      {0, 24},       //  38: 0000 0100 00x
 | |
|     {0, 23},      {0, 22},       //  39: 0000 0100 01x
 | |
| };
 | |
| 
 | |
| static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_INTRA[] = {
 | |
|     {1 << 1, 0},
 | |
|     {0, 0x01},  //   0: x
 | |
|     {-1, 0},
 | |
|     {0, 0x11},  //   1: 0x
 | |
| };
 | |
| 
 | |
| static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_PREDICTIVE[] = {
 | |
|     {1 << 1, 0}, {0, 0x0a},    //   0: x
 | |
|     {2 << 1, 0}, {0, 0x02},    //   1: 0x
 | |
|     {3 << 1, 0}, {0, 0x08},    //   2: 00x
 | |
|     {4 << 1, 0}, {5 << 1, 0},  //   3: 000x
 | |
|     {6 << 1, 0}, {0, 0x12},    //   4: 0000x
 | |
|     {0, 0x1a},   {0, 0x01},    //   5: 0001x
 | |
|     {-1, 0},     {0, 0x11},    //   6: 0000 0x
 | |
| };
 | |
| 
 | |
| static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_B[] = {
 | |
|     {1 << 1, 0}, {2 << 1, 0},   //   0: x
 | |
|     {3 << 1, 0}, {4 << 1, 0},   //   1: 0x
 | |
|     {0, 0x0c},   {0, 0x0e},     //   2: 1x
 | |
|     {5 << 1, 0}, {6 << 1, 0},   //   3: 00x
 | |
|     {0, 0x04},   {0, 0x06},     //   4: 01x
 | |
|     {7 << 1, 0}, {8 << 1, 0},   //   5: 000x
 | |
|     {0, 0x08},   {0, 0x0a},     //   6: 001x
 | |
|     {9 << 1, 0}, {10 << 1, 0},  //   7: 0000x
 | |
|     {0, 0x1e},   {0, 0x01},     //   8: 0001x
 | |
|     {-1, 0},     {0, 0x11},     //   9: 0000 0x
 | |
|     {0, 0x16},   {0, 0x1a},     //  10: 0000 1x
 | |
| };
 | |
| 
 | |
| static const plm_vlc_t PLM_VIDEO_CODE_BLOCK_PATTERN[] = {
 | |
|     {1 << 1, 0},  {2 << 1, 0},   //   0: x
 | |
|     {3 << 1, 0},  {4 << 1, 0},   //   1: 0x
 | |
|     {5 << 1, 0},  {6 << 1, 0},   //   2: 1x
 | |
|     {7 << 1, 0},  {8 << 1, 0},   //   3: 00x
 | |
|     {9 << 1, 0},  {10 << 1, 0},  //   4: 01x
 | |
|     {11 << 1, 0}, {12 << 1, 0},  //   5: 10x
 | |
|     {13 << 1, 0}, {0, 60},       //   6: 11x
 | |
|     {14 << 1, 0}, {15 << 1, 0},  //   7: 000x
 | |
|     {16 << 1, 0}, {17 << 1, 0},  //   8: 001x
 | |
|     {18 << 1, 0}, {19 << 1, 0},  //   9: 010x
 | |
|     {20 << 1, 0}, {21 << 1, 0},  //  10: 011x
 | |
|     {22 << 1, 0}, {23 << 1, 0},  //  11: 100x
 | |
|     {0, 32},      {0, 16},       //  12: 101x
 | |
|     {0, 8},       {0, 4},        //  13: 110x
 | |
|     {24 << 1, 0}, {25 << 1, 0},  //  14: 0000x
 | |
|     {26 << 1, 0}, {27 << 1, 0},  //  15: 0001x
 | |
|     {28 << 1, 0}, {29 << 1, 0},  //  16: 0010x
 | |
|     {30 << 1, 0}, {31 << 1, 0},  //  17: 0011x
 | |
|     {0, 62},      {0, 2},        //  18: 0100x
 | |
|     {0, 61},      {0, 1},        //  19: 0101x
 | |
|     {0, 56},      {0, 52},       //  20: 0110x
 | |
|     {0, 44},      {0, 28},       //  21: 0111x
 | |
|     {0, 40},      {0, 20},       //  22: 1000x
 | |
|     {0, 48},      {0, 12},       //  23: 1001x
 | |
|     {32 << 1, 0}, {33 << 1, 0},  //  24: 0000 0x
 | |
|     {34 << 1, 0}, {35 << 1, 0},  //  25: 0000 1x
 | |
|     {36 << 1, 0}, {37 << 1, 0},  //  26: 0001 0x
 | |
|     {38 << 1, 0}, {39 << 1, 0},  //  27: 0001 1x
 | |
|     {40 << 1, 0}, {41 << 1, 0},  //  28: 0010 0x
 | |
|     {42 << 1, 0}, {43 << 1, 0},  //  29: 0010 1x
 | |
|     {0, 63},      {0, 3},        //  30: 0011 0x
 | |
|     {0, 36},      {0, 24},       //  31: 0011 1x
 | |
|     {44 << 1, 0}, {45 << 1, 0},  //  32: 0000 00x
 | |
|     {46 << 1, 0}, {47 << 1, 0},  //  33: 0000 01x
 | |
|     {48 << 1, 0}, {49 << 1, 0},  //  34: 0000 10x
 | |
|     {50 << 1, 0}, {51 << 1, 0},  //  35: 0000 11x
 | |
|     {52 << 1, 0}, {53 << 1, 0},  //  36: 0001 00x
 | |
|     {54 << 1, 0}, {55 << 1, 0},  //  37: 0001 01x
 | |
|     {56 << 1, 0}, {57 << 1, 0},  //  38: 0001 10x
 | |
|     {58 << 1, 0}, {59 << 1, 0},  //  39: 0001 11x
 | |
|     {0, 34},      {0, 18},       //  40: 0010 00x
 | |
|     {0, 10},      {0, 6},        //  41: 0010 01x
 | |
|     {0, 33},      {0, 17},       //  42: 0010 10x
 | |
|     {0, 9},       {0, 5},        //  43: 0010 11x
 | |
|     {-1, 0},      {60 << 1, 0},  //  44: 0000 000x
 | |
|     {61 << 1, 0}, {62 << 1, 0},  //  45: 0000 001x
 | |
|     {0, 58},      {0, 54},       //  46: 0000 010x
 | |
|     {0, 46},      {0, 30},       //  47: 0000 011x
 | |
|     {0, 57},      {0, 53},       //  48: 0000 100x
 | |
|     {0, 45},      {0, 29},       //  49: 0000 101x
 | |
|     {0, 38},      {0, 26},       //  50: 0000 110x
 | |
|     {0, 37},      {0, 25},       //  51: 0000 111x
 | |
|     {0, 43},      {0, 23},       //  52: 0001 000x
 | |
|     {0, 51},      {0, 15},       //  53: 0001 001x
 | |
|     {0, 42},      {0, 22},       //  54: 0001 010x
 | |
|     {0, 50},      {0, 14},       //  55: 0001 011x
 | |
|     {0, 41},      {0, 21},       //  56: 0001 100x
 | |
|     {0, 49},      {0, 13},       //  57: 0001 101x
 | |
|     {0, 35},      {0, 19},       //  58: 0001 110x
 | |
|     {0, 11},      {0, 7},        //  59: 0001 111x
 | |
|     {0, 39},      {0, 27},       //  60: 0000 0001x
 | |
|     {0, 59},      {0, 55},       //  61: 0000 0010x
 | |
|     {0, 47},      {0, 31},       //  62: 0000 0011x
 | |
| };
 | |
| 
 | |
| static const plm_vlc_t PLM_VIDEO_MOTION[] = {
 | |
|     {1 << 1, 0},  {0, 0},        //   0: x
 | |
|     {2 << 1, 0},  {3 << 1, 0},   //   1: 0x
 | |
|     {4 << 1, 0},  {5 << 1, 0},   //   2: 00x
 | |
|     {0, 1},       {0, -1},       //   3: 01x
 | |
|     {6 << 1, 0},  {7 << 1, 0},   //   4: 000x
 | |
|     {0, 2},       {0, -2},       //   5: 001x
 | |
|     {8 << 1, 0},  {9 << 1, 0},   //   6: 0000x
 | |
|     {0, 3},       {0, -3},       //   7: 0001x
 | |
|     {10 << 1, 0}, {11 << 1, 0},  //   8: 0000 0x
 | |
|     {12 << 1, 0}, {13 << 1, 0},  //   9: 0000 1x
 | |
|     {-1, 0},      {14 << 1, 0},  //  10: 0000 00x
 | |
|     {15 << 1, 0}, {16 << 1, 0},  //  11: 0000 01x
 | |
|     {17 << 1, 0}, {18 << 1, 0},  //  12: 0000 10x
 | |
|     {0, 4},       {0, -4},       //  13: 0000 11x
 | |
|     {-1, 0},      {19 << 1, 0},  //  14: 0000 001x
 | |
|     {20 << 1, 0}, {21 << 1, 0},  //  15: 0000 010x
 | |
|     {0, 7},       {0, -7},       //  16: 0000 011x
 | |
|     {0, 6},       {0, -6},       //  17: 0000 100x
 | |
|     {0, 5},       {0, -5},       //  18: 0000 101x
 | |
|     {22 << 1, 0}, {23 << 1, 0},  //  19: 0000 0011x
 | |
|     {24 << 1, 0}, {25 << 1, 0},  //  20: 0000 0100x
 | |
|     {26 << 1, 0}, {27 << 1, 0},  //  21: 0000 0101x
 | |
|     {28 << 1, 0}, {29 << 1, 0},  //  22: 0000 0011 0x
 | |
|     {30 << 1, 0}, {31 << 1, 0},  //  23: 0000 0011 1x
 | |
|     {32 << 1, 0}, {33 << 1, 0},  //  24: 0000 0100 0x
 | |
|     {0, 10},      {0, -10},      //  25: 0000 0100 1x
 | |
|     {0, 9},       {0, -9},       //  26: 0000 0101 0x
 | |
|     {0, 8},       {0, -8},       //  27: 0000 0101 1x
 | |
|     {0, 16},      {0, -16},      //  28: 0000 0011 00x
 | |
|     {0, 15},      {0, -15},      //  29: 0000 0011 01x
 | |
|     {0, 14},      {0, -14},      //  30: 0000 0011 10x
 | |
|     {0, 13},      {0, -13},      //  31: 0000 0011 11x
 | |
|     {0, 12},      {0, -12},      //  32: 0000 0100 00x
 | |
|     {0, 11},      {0, -11},      //  33: 0000 0100 01x
 | |
| };
 | |
| 
 | |
| static const plm_vlc_t PLM_VIDEO_DCT_SIZE_LUMINANCE[] = {
 | |
|     {1 << 1, 0}, {2 << 1, 0},  //   0: x
 | |
|     {0, 1},      {0, 2},       //   1: 0x
 | |
|     {3 << 1, 0}, {4 << 1, 0},  //   2: 1x
 | |
|     {0, 0},      {0, 3},       //   3: 10x
 | |
|     {0, 4},      {5 << 1, 0},  //   4: 11x
 | |
|     {0, 5},      {6 << 1, 0},  //   5: 111x
 | |
|     {0, 6},      {7 << 1, 0},  //   6: 1111x
 | |
|     {0, 7},      {8 << 1, 0},  //   7: 1111 1x
 | |
|     {0, 8},      {-1, 0},      //   8: 1111 11x
 | |
| };
 | |
| 
 | |
| static const plm_vlc_t PLM_VIDEO_DCT_SIZE_CHROMINANCE[] = {
 | |
|     {1 << 1, 0}, {2 << 1, 0},  //   0: x
 | |
|     {0, 0},      {0, 1},       //   1: 0x
 | |
|     {0, 2},      {3 << 1, 0},  //   2: 1x
 | |
|     {0, 3},      {4 << 1, 0},  //   3: 11x
 | |
|     {0, 4},      {5 << 1, 0},  //   4: 111x
 | |
|     {0, 5},      {6 << 1, 0},  //   5: 1111x
 | |
|     {0, 6},      {7 << 1, 0},  //   6: 1111 1x
 | |
|     {0, 7},      {8 << 1, 0},  //   7: 1111 11x
 | |
|     {0, 8},      {-1, 0},      //   8: 1111 111x
 | |
| };
 | |
| 
 | |
| //  dct_coeff bitmap:
 | |
| //    0xff00  run
 | |
| //    0x00ff  level
 | |
| 
 | |
| //  Decoded values are unsigned. Sign bit follows in the stream.
 | |
| 
 | |
| static const plm_vlc_uint_t PLM_VIDEO_DCT_COEFF[] = {
 | |
|     {1 << 1, 0},   {0, 0x0001},    //   0: x
 | |
|     {2 << 1, 0},   {3 << 1, 0},    //   1: 0x
 | |
|     {4 << 1, 0},   {5 << 1, 0},    //   2: 00x
 | |
|     {6 << 1, 0},   {0, 0x0101},    //   3: 01x
 | |
|     {7 << 1, 0},   {8 << 1, 0},    //   4: 000x
 | |
|     {9 << 1, 0},   {10 << 1, 0},   //   5: 001x
 | |
|     {0, 0x0002},   {0, 0x0201},    //   6: 010x
 | |
|     {11 << 1, 0},  {12 << 1, 0},   //   7: 0000x
 | |
|     {13 << 1, 0},  {14 << 1, 0},   //   8: 0001x
 | |
|     {15 << 1, 0},  {0, 0x0003},    //   9: 0010x
 | |
|     {0, 0x0401},   {0, 0x0301},    //  10: 0011x
 | |
|     {16 << 1, 0},  {0, 0xffff},    //  11: 0000 0x
 | |
|     {17 << 1, 0},  {18 << 1, 0},   //  12: 0000 1x
 | |
|     {0, 0x0701},   {0, 0x0601},    //  13: 0001 0x
 | |
|     {0, 0x0102},   {0, 0x0501},    //  14: 0001 1x
 | |
|     {19 << 1, 0},  {20 << 1, 0},   //  15: 0010 0x
 | |
|     {21 << 1, 0},  {22 << 1, 0},   //  16: 0000 00x
 | |
|     {0, 0x0202},   {0, 0x0901},    //  17: 0000 10x
 | |
|     {0, 0x0004},   {0, 0x0801},    //  18: 0000 11x
 | |
|     {23 << 1, 0},  {24 << 1, 0},   //  19: 0010 00x
 | |
|     {25 << 1, 0},  {26 << 1, 0},   //  20: 0010 01x
 | |
|     {27 << 1, 0},  {28 << 1, 0},   //  21: 0000 000x
 | |
|     {29 << 1, 0},  {30 << 1, 0},   //  22: 0000 001x
 | |
|     {0, 0x0d01},   {0, 0x0006},    //  23: 0010 000x
 | |
|     {0, 0x0c01},   {0, 0x0b01},    //  24: 0010 001x
 | |
|     {0, 0x0302},   {0, 0x0103},    //  25: 0010 010x
 | |
|     {0, 0x0005},   {0, 0x0a01},    //  26: 0010 011x
 | |
|     {31 << 1, 0},  {32 << 1, 0},   //  27: 0000 0000x
 | |
|     {33 << 1, 0},  {34 << 1, 0},   //  28: 0000 0001x
 | |
|     {35 << 1, 0},  {36 << 1, 0},   //  29: 0000 0010x
 | |
|     {37 << 1, 0},  {38 << 1, 0},   //  30: 0000 0011x
 | |
|     {39 << 1, 0},  {40 << 1, 0},   //  31: 0000 0000 0x
 | |
|     {41 << 1, 0},  {42 << 1, 0},   //  32: 0000 0000 1x
 | |
|     {43 << 1, 0},  {44 << 1, 0},   //  33: 0000 0001 0x
 | |
|     {45 << 1, 0},  {46 << 1, 0},   //  34: 0000 0001 1x
 | |
|     {0, 0x1001},   {0, 0x0502},    //  35: 0000 0010 0x
 | |
|     {0, 0x0007},   {0, 0x0203},    //  36: 0000 0010 1x
 | |
|     {0, 0x0104},   {0, 0x0f01},    //  37: 0000 0011 0x
 | |
|     {0, 0x0e01},   {0, 0x0402},    //  38: 0000 0011 1x
 | |
|     {47 << 1, 0},  {48 << 1, 0},   //  39: 0000 0000 00x
 | |
|     {49 << 1, 0},  {50 << 1, 0},   //  40: 0000 0000 01x
 | |
|     {51 << 1, 0},  {52 << 1, 0},   //  41: 0000 0000 10x
 | |
|     {53 << 1, 0},  {54 << 1, 0},   //  42: 0000 0000 11x
 | |
|     {55 << 1, 0},  {56 << 1, 0},   //  43: 0000 0001 00x
 | |
|     {57 << 1, 0},  {58 << 1, 0},   //  44: 0000 0001 01x
 | |
|     {59 << 1, 0},  {60 << 1, 0},   //  45: 0000 0001 10x
 | |
|     {61 << 1, 0},  {62 << 1, 0},   //  46: 0000 0001 11x
 | |
|     {-1, 0},       {63 << 1, 0},   //  47: 0000 0000 000x
 | |
|     {64 << 1, 0},  {65 << 1, 0},   //  48: 0000 0000 001x
 | |
|     {66 << 1, 0},  {67 << 1, 0},   //  49: 0000 0000 010x
 | |
|     {68 << 1, 0},  {69 << 1, 0},   //  50: 0000 0000 011x
 | |
|     {70 << 1, 0},  {71 << 1, 0},   //  51: 0000 0000 100x
 | |
|     {72 << 1, 0},  {73 << 1, 0},   //  52: 0000 0000 101x
 | |
|     {74 << 1, 0},  {75 << 1, 0},   //  53: 0000 0000 110x
 | |
|     {76 << 1, 0},  {77 << 1, 0},   //  54: 0000 0000 111x
 | |
|     {0, 0x000b},   {0, 0x0802},    //  55: 0000 0001 000x
 | |
|     {0, 0x0403},   {0, 0x000a},    //  56: 0000 0001 001x
 | |
|     {0, 0x0204},   {0, 0x0702},    //  57: 0000 0001 010x
 | |
|     {0, 0x1501},   {0, 0x1401},    //  58: 0000 0001 011x
 | |
|     {0, 0x0009},   {0, 0x1301},    //  59: 0000 0001 100x
 | |
|     {0, 0x1201},   {0, 0x0105},    //  60: 0000 0001 101x
 | |
|     {0, 0x0303},   {0, 0x0008},    //  61: 0000 0001 110x
 | |
|     {0, 0x0602},   {0, 0x1101},    //  62: 0000 0001 111x
 | |
|     {78 << 1, 0},  {79 << 1, 0},   //  63: 0000 0000 0001x
 | |
|     {80 << 1, 0},  {81 << 1, 0},   //  64: 0000 0000 0010x
 | |
|     {82 << 1, 0},  {83 << 1, 0},   //  65: 0000 0000 0011x
 | |
|     {84 << 1, 0},  {85 << 1, 0},   //  66: 0000 0000 0100x
 | |
|     {86 << 1, 0},  {87 << 1, 0},   //  67: 0000 0000 0101x
 | |
|     {88 << 1, 0},  {89 << 1, 0},   //  68: 0000 0000 0110x
 | |
|     {90 << 1, 0},  {91 << 1, 0},   //  69: 0000 0000 0111x
 | |
|     {0, 0x0a02},   {0, 0x0902},    //  70: 0000 0000 1000x
 | |
|     {0, 0x0503},   {0, 0x0304},    //  71: 0000 0000 1001x
 | |
|     {0, 0x0205},   {0, 0x0107},    //  72: 0000 0000 1010x
 | |
|     {0, 0x0106},   {0, 0x000f},    //  73: 0000 0000 1011x
 | |
|     {0, 0x000e},   {0, 0x000d},    //  74: 0000 0000 1100x
 | |
|     {0, 0x000c},   {0, 0x1a01},    //  75: 0000 0000 1101x
 | |
|     {0, 0x1901},   {0, 0x1801},    //  76: 0000 0000 1110x
 | |
|     {0, 0x1701},   {0, 0x1601},    //  77: 0000 0000 1111x
 | |
|     {92 << 1, 0},  {93 << 1, 0},   //  78: 0000 0000 0001 0x
 | |
|     {94 << 1, 0},  {95 << 1, 0},   //  79: 0000 0000 0001 1x
 | |
|     {96 << 1, 0},  {97 << 1, 0},   //  80: 0000 0000 0010 0x
 | |
|     {98 << 1, 0},  {99 << 1, 0},   //  81: 0000 0000 0010 1x
 | |
|     {100 << 1, 0}, {101 << 1, 0},  //  82: 0000 0000 0011 0x
 | |
|     {102 << 1, 0}, {103 << 1, 0},  //  83: 0000 0000 0011 1x
 | |
|     {0, 0x001f},   {0, 0x001e},    //  84: 0000 0000 0100 0x
 | |
|     {0, 0x001d},   {0, 0x001c},    //  85: 0000 0000 0100 1x
 | |
|     {0, 0x001b},   {0, 0x001a},    //  86: 0000 0000 0101 0x
 | |
|     {0, 0x0019},   {0, 0x0018},    //  87: 0000 0000 0101 1x
 | |
|     {0, 0x0017},   {0, 0x0016},    //  88: 0000 0000 0110 0x
 | |
|     {0, 0x0015},   {0, 0x0014},    //  89: 0000 0000 0110 1x
 | |
|     {0, 0x0013},   {0, 0x0012},    //  90: 0000 0000 0111 0x
 | |
|     {0, 0x0011},   {0, 0x0010},    //  91: 0000 0000 0111 1x
 | |
|     {104 << 1, 0}, {105 << 1, 0},  //  92: 0000 0000 0001 00x
 | |
|     {106 << 1, 0}, {107 << 1, 0},  //  93: 0000 0000 0001 01x
 | |
|     {108 << 1, 0}, {109 << 1, 0},  //  94: 0000 0000 0001 10x
 | |
|     {110 << 1, 0}, {111 << 1, 0},  //  95: 0000 0000 0001 11x
 | |
|     {0, 0x0028},   {0, 0x0027},    //  96: 0000 0000 0010 00x
 | |
|     {0, 0x0026},   {0, 0x0025},    //  97: 0000 0000 0010 01x
 | |
|     {0, 0x0024},   {0, 0x0023},    //  98: 0000 0000 0010 10x
 | |
|     {0, 0x0022},   {0, 0x0021},    //  99: 0000 0000 0010 11x
 | |
|     {0, 0x0020},   {0, 0x010e},    // 100: 0000 0000 0011 00x
 | |
|     {0, 0x010d},   {0, 0x010c},    // 101: 0000 0000 0011 01x
 | |
|     {0, 0x010b},   {0, 0x010a},    // 102: 0000 0000 0011 10x
 | |
|     {0, 0x0109},   {0, 0x0108},    // 103: 0000 0000 0011 11x
 | |
|     {0, 0x0112},   {0, 0x0111},    // 104: 0000 0000 0001 000x
 | |
|     {0, 0x0110},   {0, 0x010f},    // 105: 0000 0000 0001 001x
 | |
|     {0, 0x0603},   {0, 0x1002},    // 106: 0000 0000 0001 010x
 | |
|     {0, 0x0f02},   {0, 0x0e02},    // 107: 0000 0000 0001 011x
 | |
|     {0, 0x0d02},   {0, 0x0c02},    // 108: 0000 0000 0001 100x
 | |
|     {0, 0x0b02},   {0, 0x1f01},    // 109: 0000 0000 0001 101x
 | |
|     {0, 0x1e01},   {0, 0x1d01},    // 110: 0000 0000 0001 110x
 | |
|     {0, 0x1c01},   {0, 0x1b01},    // 111: 0000 0000 0001 111x
 | |
| };
 | |
| 
 | |
| long plmpegdecode_latency_;
 | |
| 
 | |
| static plm_vlc_t *PLM_VIDEO_MACROBLOCK_TYPE[4];
 | |
| static plm_vlc_t *PLM_VIDEO_DCT_SIZE[3];
 | |
| 
 | |
| #define plm_clamp(n) MIN(255, MAX(0, n))
 | |
| 
 | |
| void plm_video_destroy(plm_video_t *self) {
 | |
|   if (self->destroy_buffer_when_done) {
 | |
|     plm_buffer_destroy(self->buffer);
 | |
|   }
 | |
|   if (self->has_sequence_header) {
 | |
|     free(self->frames_data);
 | |
|   }
 | |
|   free(self);
 | |
| }
 | |
| 
 | |
| double plm_video_get_pixel_aspect_ratio(plm_video_t *self) {
 | |
|   return self->pixel_aspect_ratio;
 | |
| }
 | |
| 
 | |
| double plm_video_get_framerate(plm_video_t *self) {
 | |
|   return self->framerate;
 | |
| }
 | |
| 
 | |
| int plm_video_get_width(plm_video_t *self) {
 | |
|   return self->width;
 | |
| }
 | |
| 
 | |
| int plm_video_get_height(plm_video_t *self) {
 | |
|   return self->height;
 | |
| }
 | |
| 
 | |
| void plm_video_set_no_delay(plm_video_t *self, int no_delay) {
 | |
|   self->assume_no_b_frames = no_delay;
 | |
| }
 | |
| 
 | |
| double plm_video_get_time(plm_video_t *self) {
 | |
|   return self->time;
 | |
| }
 | |
| 
 | |
| void plm_video_rewind(plm_video_t *self) {
 | |
|   plm_buffer_rewind(self->buffer);
 | |
|   self->time = 0;
 | |
|   self->frames_decoded = 0;
 | |
|   self->has_reference_frame = false;
 | |
| }
 | |
| 
 | |
| void plm_video_init_frame(plm_video_t *self, plm_frame_t *frame,
 | |
|                           uint8_t *base) {
 | |
|   size_t plane_size = self->luma_width * self->luma_height;
 | |
|   frame->width = self->width;
 | |
|   frame->height = self->height;
 | |
|   frame->y.width = self->luma_width;
 | |
|   frame->y.height = self->luma_height;
 | |
|   frame->y.data = base;
 | |
|   frame->cr.width = self->chroma_width;
 | |
|   frame->cr.height = self->chroma_height;
 | |
|   frame->cr.data = base + plane_size;
 | |
|   frame->cb.width = self->chroma_width;
 | |
|   frame->cb.height = self->chroma_height;
 | |
|   frame->cb.data = base + plane_size * 2;
 | |
| }
 | |
| 
 | |
| void plm_video_decode_sequence_header(plm_video_t *self) {
 | |
|   int previous_width = self->width;
 | |
|   int previous_height = self->height;
 | |
| 
 | |
|   self->width = plm_buffer_read(self->buffer, 12);
 | |
|   self->height = plm_buffer_read(self->buffer, 12);
 | |
| 
 | |
|   int pixel_aspect_ratio_code;
 | |
|   pixel_aspect_ratio_code = plm_buffer_read(self->buffer, 4);
 | |
|   pixel_aspect_ratio_code -= 1;
 | |
|   pixel_aspect_ratio_code = MAX(pixel_aspect_ratio_code, 0);
 | |
|   pixel_aspect_ratio_code =
 | |
|       MIN(pixel_aspect_ratio_code, ARRAYLEN(PLM_VIDEO_PIXEL_ASPECT_RATIO) - 1);
 | |
|   self->pixel_aspect_ratio =
 | |
|       PLM_VIDEO_PIXEL_ASPECT_RATIO[pixel_aspect_ratio_code];
 | |
| 
 | |
|   int framerate_code;
 | |
|   framerate_code = plm_buffer_read(self->buffer, 4);
 | |
|   framerate_code -= 1;
 | |
|   framerate_code = MAX(framerate_code, 0);
 | |
|   framerate_code = MIN(framerate_code, ARRAYLEN(PLM_VIDEO_PICTURE_RATE) - 1);
 | |
|   self->framerate = PLM_VIDEO_PICTURE_RATE[framerate_code];
 | |
| 
 | |
|   // skip bitRate, marker, bufferSize and constrained bit
 | |
|   plm_buffer_skip(self->buffer, 18 + 1 + 10 + 1);
 | |
| 
 | |
|   if (plm_buffer_read(self->buffer, 1)) {  // load custom intra quant matrix?
 | |
|     for (int i = 0; i < 64; i++) {
 | |
|       int idx = PLM_VIDEO_ZIG_ZAG[i];
 | |
|       self->intra_quant_matrix[idx] = plm_buffer_read(self->buffer, 8);
 | |
|     }
 | |
|   } else {
 | |
|     memcpy(self->intra_quant_matrix, PLM_VIDEO_INTRAQUANT_MATRIX, 64);
 | |
|   }
 | |
| 
 | |
|   if (plm_buffer_read(self->buffer,
 | |
|                       1)) {  // load custom non intra quant matrix?
 | |
|     for (int i = 0; i < 64; i++) {
 | |
|       int idx = PLM_VIDEO_ZIG_ZAG[i];
 | |
|       self->non_intra_quant_matrix[idx] = plm_buffer_read(self->buffer, 8);
 | |
|     }
 | |
|   } else {
 | |
|     memcpy(self->non_intra_quant_matrix, PLM_VIDEO_NONINTRAQUANT_MATRIX, 64);
 | |
|   }
 | |
| 
 | |
|   if (self->has_sequence_header) {
 | |
|     if (self->width == previous_width && self->height == previous_height) {
 | |
|       // We already had a sequence header with the same width/height;
 | |
|       // nothing else to do here.
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // We had a sequence header but with different dimensions;
 | |
|     // delete the previous planes and allocate new.
 | |
|     free(self->frames_data);
 | |
|   }
 | |
| 
 | |
|   self->mb_width = (self->width + 15) >> 4;
 | |
|   self->mb_height = (self->height + 15) >> 4;
 | |
|   self->mb_size = self->mb_width * self->mb_height;
 | |
|   self->luma_width = self->mb_width << 4;
 | |
|   self->luma_height = self->mb_height << 4;
 | |
|   self->chroma_width = self->mb_width << 3;
 | |
|   self->chroma_height = self->mb_height << 3;
 | |
| 
 | |
|   size_t plane_size = self->luma_width * self->luma_height;
 | |
|   self->frames_data = memalign(64, plane_size * 9);
 | |
|   plm_video_init_frame(self, &self->frame_current,
 | |
|                        self->frames_data + plane_size * 0);
 | |
|   plm_video_init_frame(self, &self->frame_forward,
 | |
|                        self->frames_data + plane_size * 3);
 | |
|   plm_video_init_frame(self, &self->frame_backward,
 | |
|                        self->frames_data + plane_size * 6);
 | |
| 
 | |
|   self->has_sequence_header = true;
 | |
| 
 | |
|   LOGF("%s:\n"
 | |
|        "\t%-20s = %15d;\n"
 | |
|        "\t%-20s = %15d;\n"
 | |
|        "\t%-20s = %15f;\n"
 | |
|        "\t%-20s = %15f;\n"
 | |
|        "\t%-20s = %15d;\n"
 | |
|        "\t%-20s = %15d;\n"
 | |
|        "\t%-20s = %15d;\n"
 | |
|        "\t%-20s = %15d;\n"
 | |
|        "\t%-20s = %15d;\n"
 | |
|        "\t%-20s = %15d;\n"
 | |
|        "\t%-20s = %15d;",
 | |
|        "New MPEG Sequence", "width", self->width, "height", self->height,
 | |
|        "framerate", self->framerate, "pixel_aspect_ratio",
 | |
|        self->pixel_aspect_ratio, "mb_size", self->mb_size, "mb_width",
 | |
|        self->mb_width, "mb_height", self->mb_height, "luma_width",
 | |
|        self->luma_width, "luma_height", self->luma_height, "chroma_width",
 | |
|        self->chroma_width, "chroma_height", self->chroma_height);
 | |
| }
 | |
| 
 | |
| static void plm_video_copy_macroblock(plm_video_t *self, int motion_h,
 | |
|                                       int motion_v, plm_frame_t *d) {
 | |
|   plm_frame_t *s = &self->frame_current;
 | |
|   plm_video_process_macroblock_16(self, s->y.data, d->y.data, motion_h,
 | |
|                                   motion_v, false);
 | |
|   plm_video_process_macroblock_8(self, s->cr.data, d->cr.data, motion_h / 2,
 | |
|                                  motion_v / 2, false);
 | |
|   plm_video_process_macroblock_8(self, s->cb.data, d->cb.data, motion_h / 2,
 | |
|                                  motion_v / 2, false);
 | |
| }
 | |
| 
 | |
| static void plm_video_interpolate_macroblock(plm_video_t *self, int motion_h,
 | |
|                                              int motion_v, plm_frame_t *d) {
 | |
|   plm_frame_t *s = &self->frame_current;
 | |
|   plm_video_process_macroblock_16(self, s->y.data, d->y.data, motion_h,
 | |
|                                   motion_v, true);
 | |
|   plm_video_process_macroblock_8(self, s->cr.data, d->cr.data, motion_h / 2,
 | |
|                                  motion_v / 2, true);
 | |
|   plm_video_process_macroblock_8(self, s->cb.data, d->cb.data, motion_h / 2,
 | |
|                                  motion_v / 2, true);
 | |
| }
 | |
| 
 | |
| static int plm_video_decode_motion_vector(plm_video_t *self, int r_size,
 | |
|                                           int motion) {
 | |
|   int fscale = 1u << r_size;
 | |
|   int m_code = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MOTION);
 | |
|   int r = 0;
 | |
|   int d;
 | |
|   if ((m_code != 0) && (fscale != 1)) {
 | |
|     r = plm_buffer_read(self->buffer, r_size);
 | |
|     d = ((abs(m_code) - 1) << r_size) + r + 1;
 | |
|     if (m_code < 0) {
 | |
|       d = -d;
 | |
|     }
 | |
|   } else {
 | |
|     d = m_code;
 | |
|   }
 | |
|   motion += d;
 | |
|   if (motion > (fscale << 4) - 1) {
 | |
|     motion -= fscale << 5;
 | |
|   } else if (motion < (int)(((unsigned)-fscale) << 4)) {
 | |
|     motion += fscale << 5;
 | |
|   }
 | |
|   return motion;
 | |
| }
 | |
| 
 | |
| static void plm_video_decode_motion_vectors(plm_video_t *self) {
 | |
|   // Forward
 | |
|   if (self->motion_forward.is_set) {
 | |
|     int r_size = self->motion_forward.r_size;
 | |
|     self->motion_forward.h =
 | |
|         plm_video_decode_motion_vector(self, r_size, self->motion_forward.h);
 | |
|     self->motion_forward.v =
 | |
|         plm_video_decode_motion_vector(self, r_size, self->motion_forward.v);
 | |
|   } else if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE) {
 | |
|     // No motion information in P-picture, reset vectors
 | |
|     self->motion_forward.h = 0;
 | |
|     self->motion_forward.v = 0;
 | |
|   }
 | |
|   if (self->motion_backward.is_set) {
 | |
|     int r_size = self->motion_backward.r_size;
 | |
|     self->motion_backward.h =
 | |
|         plm_video_decode_motion_vector(self, r_size, self->motion_backward.h);
 | |
|     self->motion_backward.v =
 | |
|         plm_video_decode_motion_vector(self, r_size, self->motion_backward.v);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void plm_video_predict_macroblock(plm_video_t *self) {
 | |
|   int fw_h = self->motion_forward.h;
 | |
|   int fw_v = self->motion_forward.v;
 | |
|   if (self->motion_forward.full_px) {
 | |
|     fw_h <<= 1;
 | |
|     fw_v <<= 1;
 | |
|   }
 | |
|   if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) {
 | |
|     int bw_h = self->motion_backward.h;
 | |
|     int bw_v = self->motion_backward.v;
 | |
|     if (self->motion_backward.full_px) {
 | |
|       bw_h <<= 1;
 | |
|       bw_v <<= 1;
 | |
|     }
 | |
|     if (self->motion_forward.is_set) {
 | |
|       plm_video_copy_macroblock(self, fw_h, fw_v, &self->frame_forward);
 | |
|       if (self->motion_backward.is_set) {
 | |
|         plm_video_interpolate_macroblock(self, bw_h, bw_v,
 | |
|                                          &self->frame_backward);
 | |
|       }
 | |
|     } else {
 | |
|       plm_video_copy_macroblock(self, bw_h, bw_v, &self->frame_backward);
 | |
|     }
 | |
|   } else {
 | |
|     plm_video_copy_macroblock(self, fw_h, fw_v, &self->frame_forward);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void plm_video_decode_block(plm_video_t *self, int block) {
 | |
|   int n = 0;
 | |
|   uint8_t *quant_matrix;
 | |
| 
 | |
|   // Decode DC coefficient of intra-coded blocks
 | |
|   if (self->macroblock_intra) {
 | |
|     int predictor;
 | |
|     int dct_size;
 | |
| 
 | |
|     // DC prediction
 | |
|     int plane_index = block > 3 ? block - 3 : 0;
 | |
|     predictor = self->dc_predictor[plane_index];
 | |
|     dct_size =
 | |
|         plm_buffer_read_vlc(self->buffer, PLM_VIDEO_DCT_SIZE[plane_index]);
 | |
| 
 | |
|     // Read DC coeff
 | |
|     if (dct_size > 0) {
 | |
|       int differential = plm_buffer_read(self->buffer, dct_size);
 | |
|       if ((differential & (1 << (dct_size - 1))) != 0) {
 | |
|         self->block_data[0] = predictor + differential;
 | |
|       } else {
 | |
|         self->block_data[0] =
 | |
|             predictor + ((-1u << dct_size) | (differential + 1));
 | |
|       }
 | |
|     } else {
 | |
|       self->block_data[0] = predictor;
 | |
|     }
 | |
| 
 | |
|     // Save predictor value
 | |
|     self->dc_predictor[plane_index] = self->block_data[0];
 | |
| 
 | |
|     // Dequantize + premultiply
 | |
|     self->block_data[0] <<= (3 + 5);
 | |
| 
 | |
|     quant_matrix = self->intra_quant_matrix;
 | |
|     n = 1;
 | |
|   } else {
 | |
|     quant_matrix = self->non_intra_quant_matrix;
 | |
|   }
 | |
| 
 | |
|   // Decode AC coefficients (+DC for non-intra)
 | |
|   int level = 0;
 | |
|   while (true) {
 | |
|     int run = 0;
 | |
|     uint16_t coeff =
 | |
|         plm_buffer_read_vlc_uint(self->buffer, PLM_VIDEO_DCT_COEFF);
 | |
| 
 | |
|     if ((coeff == 0x0001) && (n > 0) &&
 | |
|         (plm_buffer_read(self->buffer, 1) == 0)) {
 | |
|       // end_of_block
 | |
|       break;
 | |
|     }
 | |
|     if (coeff == 0xffff) {
 | |
|       // escape
 | |
|       run = plm_buffer_read(self->buffer, 6);
 | |
|       level = plm_buffer_read(self->buffer, 8);
 | |
|       if (level == 0) {
 | |
|         level = plm_buffer_read(self->buffer, 8);
 | |
|       } else if (level == 128) {
 | |
|         level = plm_buffer_read(self->buffer, 8) - 256;
 | |
|       } else if (level > 128) {
 | |
|         level = level - 256;
 | |
|       }
 | |
|     } else {
 | |
|       run = coeff >> 8;
 | |
|       level = coeff & 0xff;
 | |
|       if (plm_buffer_read(self->buffer, 1)) {
 | |
|         level = -level;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     n += run;
 | |
|     if (n < 0 || n >= 64) {
 | |
|       return;  // invalid
 | |
|     }
 | |
| 
 | |
|     int de_zig_zagged = PLM_VIDEO_ZIG_ZAG[n];
 | |
|     n++;
 | |
| 
 | |
|     // Dequantize, oddify, clip
 | |
|     level = (unsigned)level << 1;
 | |
|     if (!self->macroblock_intra) {
 | |
|       level += (level < 0 ? -1 : 1);
 | |
|     }
 | |
|     level = (level * self->quantizer_scale * quant_matrix[de_zig_zagged]) >> 4;
 | |
|     if ((level & 1) == 0) {
 | |
|       level -= level > 0 ? 1 : -1;
 | |
|     }
 | |
|     if (level > 2047) {
 | |
|       level = 2047;
 | |
|     } else if (level < -2048) {
 | |
|       level = -2048;
 | |
|     }
 | |
| 
 | |
|     // Save premultiplied coefficient
 | |
|     self->block_data[de_zig_zagged] =
 | |
|         level * PLM_VIDEO_PREMULTIPLIER_MATRIX[de_zig_zagged];
 | |
|   }
 | |
| 
 | |
|   // Move block to its place
 | |
|   uint8_t *d;
 | |
|   int dw;
 | |
|   int di;
 | |
| 
 | |
|   if (block < 4) {
 | |
|     d = self->frame_current.y.data;
 | |
|     dw = self->luma_width;
 | |
|     di = (self->mb_row * self->luma_width + self->mb_col) << 4;
 | |
|     if ((block & 1) != 0) {
 | |
|       di += 8;
 | |
|     }
 | |
|     if ((block & 2) != 0) {
 | |
|       di += self->luma_width << 3;
 | |
|     }
 | |
|   } else {
 | |
|     d = (block == 4) ? self->frame_current.cb.data
 | |
|                      : self->frame_current.cr.data;
 | |
|     dw = self->chroma_width;
 | |
|     di = ((self->mb_row * self->luma_width) << 2) + (self->mb_col << 3);
 | |
|   }
 | |
| 
 | |
|   int *s = self->block_data;
 | |
|   int si = 0;
 | |
|   if (self->macroblock_intra) {
 | |
|     // Overwrite (no prediction)
 | |
|     if (n == 1) {
 | |
|       int clamped = plm_clamp((s[0] + 128) >> 8);
 | |
|       PLM_BLOCK_SET(d, di, dw, si, 8, 8, clamped);
 | |
|       s[0] = 0;
 | |
|     } else {
 | |
|       plm_video_idct(s);
 | |
|       PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(s[si]));
 | |
|       memset(self->block_data, 0, sizeof(self->block_data));
 | |
|     }
 | |
|   } else {
 | |
|     // Add data to the predicted macroblock
 | |
|     if (n == 1) {
 | |
|       int value = (s[0] + 128) >> 8;
 | |
|       PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(d[di] + value));
 | |
|       s[0] = 0;
 | |
|     } else {
 | |
|       plm_video_idct(s);
 | |
|       PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(d[di] + s[si]));
 | |
|       memset(self->block_data, 0, sizeof(self->block_data));
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void plm_video_decode_macroblock(plm_video_t *self) {
 | |
|   // Decode self->macroblock_address_increment
 | |
|   int increment = 0;
 | |
|   int t =
 | |
|       plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT);
 | |
| 
 | |
|   while (t == 34) {
 | |
|     // macroblock_stuffing
 | |
|     t = plm_buffer_read_vlc(self->buffer,
 | |
|                             PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT);
 | |
|   }
 | |
|   while (t == 35) {
 | |
|     // macroblock_escape
 | |
|     increment += 33;
 | |
|     t = plm_buffer_read_vlc(self->buffer,
 | |
|                             PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT);
 | |
|   }
 | |
|   increment += t;
 | |
| 
 | |
|   // Process any skipped macroblocks
 | |
|   if (self->slice_begin) {
 | |
|     // The first self->macroblock_address_increment of each slice is relative
 | |
|     // to beginning of the preverious row, not the preverious macroblock
 | |
|     self->slice_begin = false;
 | |
|     self->macroblock_address += increment;
 | |
|   } else {
 | |
|     if (self->macroblock_address + increment >= self->mb_size) {
 | |
|       return;  // invalid
 | |
|     }
 | |
|     if (increment > 1) {
 | |
|       // Skipped macroblocks reset DC predictors
 | |
|       self->dc_predictor[0] = 128;
 | |
|       self->dc_predictor[1] = 128;
 | |
|       self->dc_predictor[2] = 128;
 | |
| 
 | |
|       // Skipped macroblocks in P-pictures reset motion vectors
 | |
|       if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE) {
 | |
|         self->motion_forward.h = 0;
 | |
|         self->motion_forward.v = 0;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Predict skipped macroblocks
 | |
|     while (increment > 1) {
 | |
|       self->macroblock_address++;
 | |
|       self->mb_row = self->macroblock_address / self->mb_width;
 | |
|       self->mb_col = self->macroblock_address % self->mb_width;
 | |
| 
 | |
|       plm_video_predict_macroblock(self);
 | |
|       increment--;
 | |
|     }
 | |
|     self->macroblock_address++;
 | |
|   }
 | |
| 
 | |
|   self->mb_row = self->macroblock_address / self->mb_width;
 | |
|   self->mb_col = self->macroblock_address % self->mb_width;
 | |
| 
 | |
|   if (self->mb_col >= self->mb_width || self->mb_row >= self->mb_height) {
 | |
|     return;  // corrupt stream;
 | |
|   }
 | |
| 
 | |
|   // Process the current macroblock
 | |
|   // static const s16 *mbTable = MACROBLOCK_TYPE[self->picture_type];
 | |
|   // macroblock_type = read_huffman(self->bits, mbTable);
 | |
| 
 | |
|   const plm_vlc_t *table = PLM_VIDEO_MACROBLOCK_TYPE[self->picture_type];
 | |
|   self->macroblock_type = plm_buffer_read_vlc(self->buffer, table);
 | |
| 
 | |
|   self->macroblock_intra = (self->macroblock_type & 0x01);
 | |
|   self->motion_forward.is_set = (self->macroblock_type & 0x08);
 | |
|   self->motion_backward.is_set = (self->macroblock_type & 0x04);
 | |
| 
 | |
|   // Quantizer scale
 | |
|   if ((self->macroblock_type & 0x10) != 0) {
 | |
|     self->quantizer_scale = plm_buffer_read(self->buffer, 5);
 | |
|   }
 | |
| 
 | |
|   if (self->macroblock_intra) {
 | |
|     // Intra-coded macroblocks reset motion vectors
 | |
|     self->motion_backward.h = self->motion_forward.h = 0;
 | |
|     self->motion_backward.v = self->motion_forward.v = 0;
 | |
|   } else {
 | |
|     // Non-intra macroblocks reset DC predictors
 | |
|     self->dc_predictor[0] = 128;
 | |
|     self->dc_predictor[1] = 128;
 | |
|     self->dc_predictor[2] = 128;
 | |
| 
 | |
|     plm_video_decode_motion_vectors(self);
 | |
|     plm_video_predict_macroblock(self);
 | |
|   }
 | |
| 
 | |
|   // Decode blocks
 | |
|   int cbp =
 | |
|       ((self->macroblock_type & 0x02) != 0)
 | |
|           ? plm_buffer_read_vlc(self->buffer, PLM_VIDEO_CODE_BLOCK_PATTERN)
 | |
|           : (self->macroblock_intra ? 0x3f : 0);
 | |
| 
 | |
|   for (int block = 0, mask = 0x20; block < 6; block++) {
 | |
|     if ((cbp & mask) != 0) {
 | |
|       plm_video_decode_block(self, block);
 | |
|     }
 | |
|     mask >>= 1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void plm_video_decode_slice(plm_video_t *self, int slice) {
 | |
|   self->slice_begin = true;
 | |
|   self->macroblock_address = (slice - 1) * self->mb_width - 1;
 | |
|   // Reset motion vectors and DC predictors
 | |
|   self->motion_backward.h = self->motion_forward.h = 0;
 | |
|   self->motion_backward.v = self->motion_forward.v = 0;
 | |
|   self->dc_predictor[0] = 128;
 | |
|   self->dc_predictor[1] = 128;
 | |
|   self->dc_predictor[2] = 128;
 | |
|   self->quantizer_scale = plm_buffer_read(self->buffer, 5);
 | |
|   // Skip extra
 | |
|   while (plm_buffer_read(self->buffer, 1)) {
 | |
|     plm_buffer_skip(self->buffer, 8);
 | |
|   }
 | |
|   do {
 | |
|     plm_video_decode_macroblock(self);
 | |
|   } while (self->macroblock_address < self->mb_size - 1 &&
 | |
|            plm_buffer_no_start_code(self->buffer));
 | |
| }
 | |
| 
 | |
| static void plm_video_decode_picture(plm_video_t *self) {
 | |
|   plm_buffer_skip(self->buffer, 10);  // skip temporalReference
 | |
|   self->picture_type = plm_buffer_read(self->buffer, 3);
 | |
|   plm_buffer_skip(self->buffer, 16);  // skip vbv_delay
 | |
| 
 | |
|   // D frames or unknown coding type
 | |
|   if (self->picture_type <= 0 ||
 | |
|       self->picture_type > PLM_VIDEO_PICTURE_TYPE_B) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // forward full_px, f_code
 | |
|   if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE ||
 | |
|       self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) {
 | |
|     self->motion_forward.full_px = plm_buffer_read(self->buffer, 1);
 | |
|     int f_code = plm_buffer_read(self->buffer, 3);
 | |
|     if (f_code == 0) {
 | |
|       // Ignore picture with zero f_code
 | |
|       return;
 | |
|     }
 | |
|     self->motion_forward.r_size = f_code - 1;
 | |
|   }
 | |
| 
 | |
|   // backward full_px, f_code
 | |
|   if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) {
 | |
|     self->motion_backward.full_px = plm_buffer_read(self->buffer, 1);
 | |
|     int f_code = plm_buffer_read(self->buffer, 3);
 | |
|     if (f_code == 0) {
 | |
|       // Ignore picture with zero f_code
 | |
|       return;
 | |
|     }
 | |
|     self->motion_backward.r_size = f_code - 1;
 | |
|   }
 | |
| 
 | |
|   plm_frame_t frame_temp = self->frame_forward;
 | |
| 
 | |
|   if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_INTRA ||
 | |
|       self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE) {
 | |
|     self->frame_forward = self->frame_backward;
 | |
|   }
 | |
| 
 | |
|   // Skip extensions, user data
 | |
|   do {
 | |
|     self->start_code = plm_buffer_next_start_code(self->buffer);
 | |
|   } while (self->start_code == PLM_START_EXTENSION ||
 | |
|            self->start_code == PLM_START_USER_DATA);
 | |
| 
 | |
|   while (self->start_code >= PLM_START_SLICE_FIRST &&
 | |
|          self->start_code <= PLM_START_SLICE_LAST) {
 | |
|     plm_video_decode_slice(self, self->start_code & 0x000000FF);
 | |
|     if (self->macroblock_address == self->mb_size - 1) {
 | |
|       break;
 | |
|     }
 | |
|     self->start_code = plm_buffer_next_start_code(self->buffer);
 | |
|   }
 | |
| 
 | |
|   // If this is a reference picutre rotate the prediction pointers
 | |
|   if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_INTRA ||
 | |
|       self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE) {
 | |
|     self->frame_backward = self->frame_current;
 | |
|     self->frame_current = frame_temp;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static plm_frame_t *plm_video_decode_impl(plm_video_t *self) {
 | |
|   plm_frame_t *frame = NULL;
 | |
|   if (!self->has_sequence_header) {
 | |
|     self->start_code =
 | |
|         plm_buffer_find_start_code(self->buffer, PLM_START_SEQUENCE);
 | |
|     if (self->start_code == -1) {
 | |
|       return NULL;
 | |
|     }
 | |
|     plm_video_decode_sequence_header(self);
 | |
|   }
 | |
|   do {
 | |
|     if (self->start_code != PLM_START_PICTURE) {
 | |
|       self->start_code =
 | |
|           plm_buffer_find_start_code(self->buffer, PLM_START_PICTURE);
 | |
|     }
 | |
|     if (self->start_code == -1) {
 | |
|       return NULL;
 | |
|     }
 | |
|     plm_video_decode_picture(self);
 | |
|     if (self->assume_no_b_frames) {
 | |
|       frame = &self->frame_backward;
 | |
|     } else if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) {
 | |
|       frame = &self->frame_current;
 | |
|     } else if (self->has_reference_frame) {
 | |
|       frame = &self->frame_forward;
 | |
|     } else {
 | |
|       self->has_reference_frame = true;
 | |
|     }
 | |
|   } while (!frame);
 | |
|   frame->time = self->time;
 | |
|   self->frames_decoded++;
 | |
|   self->time = (double)self->frames_decoded / self->framerate;
 | |
|   return frame;
 | |
| }
 | |
| 
 | |
| plm_frame_t *plm_video_decode(plm_video_t *self) {
 | |
|   long double tsc;
 | |
|   plm_frame_t *res;
 | |
|   LOGF("plm_video_decode");
 | |
|   tsc = nowl();
 | |
|   res = plm_video_decode_impl(self);
 | |
|   plmpegdecode_latency_ = lroundl((nowl() - tsc) * 1e6l);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| plm_video_t *plm_video_create_with_buffer(plm_buffer_t *buffer,
 | |
|                                           int destroy_when_done) {
 | |
|   plm_video_t *self = (plm_video_t *)memalign(64, sizeof(plm_video_t));
 | |
|   memset(self, 0, sizeof(plm_video_t));
 | |
|   self->buffer = buffer;
 | |
|   self->destroy_buffer_when_done = destroy_when_done;
 | |
|   self->start_code =
 | |
|       plm_buffer_find_start_code(self->buffer, PLM_START_SEQUENCE);
 | |
|   if (self->start_code != -1) {
 | |
|     plm_video_decode_sequence_header(self);
 | |
|   }
 | |
|   return self;
 | |
| }
 | |
| 
 | |
| static textstartup void plm_video_init(void) {
 | |
|   PLM_VIDEO_MACROBLOCK_TYPE[0] = NULL;
 | |
|   PLM_VIDEO_MACROBLOCK_TYPE[1] = PLM_VIDEO_MACROBLOCK_TYPE_INTRA;
 | |
|   PLM_VIDEO_MACROBLOCK_TYPE[2] = PLM_VIDEO_MACROBLOCK_TYPE_PREDICTIVE,
 | |
|   PLM_VIDEO_MACROBLOCK_TYPE[3] = PLM_VIDEO_MACROBLOCK_TYPE_B;
 | |
|   PLM_VIDEO_DCT_SIZE[0] = PLM_VIDEO_DCT_SIZE_LUMINANCE;
 | |
|   PLM_VIDEO_DCT_SIZE[1] = PLM_VIDEO_DCT_SIZE_CHROMINANCE;
 | |
|   PLM_VIDEO_DCT_SIZE[2] = PLM_VIDEO_DCT_SIZE_CHROMINANCE;
 | |
| }
 | |
| 
 | |
| const void *const plm_video_init_ctor[] initarray = {plm_video_init};
 |