cosmopolitan/dsp/mpeg/mpeg1.c

1117 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/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];
#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};