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/conv/conv.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/macros.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];
|
|
|
|
static textstartup void __init_mpeg1(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;
|
|
}
|
|
|
|
INITIALIZER(300, _init_mpeg1, __init_mpeg1());
|
|
|
|
#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;
|
|
}
|