mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Upgrade pl_mpeg
This change gets printvideo working on aarch64. Performance improvements have been introduced for magikarp decimation on aarch64. The last of the old portable x86 intrinsics library is gone, but it still lives in Blink
This commit is contained in:
parent
5d3b91d8b9
commit
d1157d471f
46 changed files with 4587 additions and 4449 deletions
|
@ -178,6 +178,7 @@ COSMOAUDIO_ABI int cosmoaudio_open(struct CosmoAudio** cap, int sampleRate,
|
|||
COSMOAUDIO_ABI int cosmoaudio_close(struct CosmoAudio* ca) {
|
||||
ma_device_uninit(&ca->device);
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
ma_pcm_rb_uninit(&ca->input);
|
||||
free(ca);
|
||||
return COSMOAUDIO_SUCCESS;
|
||||
}
|
||||
|
@ -221,15 +222,7 @@ COSMOAUDIO_ABI int cosmoaudio_write(struct CosmoAudio* ca, const float* data,
|
|||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_read(struct CosmoAudio* ca, float* data,
|
||||
int frames) {
|
||||
int read;
|
||||
for (int i = 0; i < frames; i += read) {
|
||||
int remaining = frames - i;
|
||||
read = read_ring_buffer(&ca->input, data + i * ca->channels, remaining,
|
||||
ca->channels);
|
||||
if (read < 0)
|
||||
return read;
|
||||
}
|
||||
return frames;
|
||||
return read_ring_buffer(&ca->input, data, frames, ca->channels);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
|
2
dsp/mpeg/.clang-format
Normal file
2
dsp/mpeg/.clang-format
Normal file
|
@ -0,0 +1,2 @@
|
|||
DisableFormat: true
|
||||
SortIncludes: Never
|
|
@ -25,18 +25,13 @@ DSP_MPEG_A_CHECKS = \
|
|||
|
||||
DSP_MPEG_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_LOG \
|
||||
LIBC_LOG \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_TINYMATH \
|
||||
THIRD_PARTY_COMPILER_RT
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
|
||||
DSP_MPEG_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(DSP_MPEG_A_DIRECTDEPS),$($(x))))
|
||||
|
@ -49,9 +44,10 @@ $(DSP_MPEG_A).pkg: \
|
|||
$(DSP_MPEG_A_OBJS) \
|
||||
$(foreach x,$(DSP_MPEG_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/dsp/mpeg/clamp4int256-k8.o: private \
|
||||
o/$(MODE)/dsp/mpeg/pl_mpeg.o: private \
|
||||
CFLAGS += \
|
||||
-Os
|
||||
-ffunction-sections \
|
||||
-fdata-sections
|
||||
|
||||
DSP_MPEG_LIBS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)))
|
||||
DSP_MPEG_SRCS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)_SRCS))
|
||||
|
|
17
dsp/mpeg/README.cosmo
Normal file
17
dsp/mpeg/README.cosmo
Normal file
|
@ -0,0 +1,17 @@
|
|||
DESCRIPTION
|
||||
|
||||
pl_mpeg lets you decode .mpg files
|
||||
|
||||
ORIGIN
|
||||
|
||||
https://github.com/phoboslab/pl_mpeg/
|
||||
9e40dd6536269d788728e32c39bfacf2ab7a0866
|
||||
|
||||
LICENSE
|
||||
|
||||
MIT
|
||||
|
||||
LOCAL CHANGES
|
||||
|
||||
- Added API for extracting pixel aspect ratio
|
||||
https://github.com/phoboslab/pl_mpeg/pull/42
|
68
dsp/mpeg/README.md
Executable file
68
dsp/mpeg/README.md
Executable file
|
@ -0,0 +1,68 @@
|
|||
# PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
|
||||
|
||||
Single-file MIT licensed library for C/C++
|
||||
|
||||
See [pl_mpeg.h](https://github.com/phoboslab/pl_mpeg/blob/master/pl_mpeg.h) for
|
||||
the documentation.
|
||||
|
||||
|
||||
## Why?
|
||||
|
||||
This is meant as a simple way to get video playback into your app or game. Other
|
||||
solutions, such as ffmpeg require huge libraries and a lot of glue code.
|
||||
|
||||
MPEG1 is an old and inefficient codec, but it's still good enough for many use
|
||||
cases. All patents related to MPEG1 and MP2 have expired, so it's completely
|
||||
free now.
|
||||
|
||||
This library does not make use of any SIMD instructions, but because of
|
||||
the relative simplicity of the codec it still manages to decode 4k60fps video
|
||||
on a single CPU core (on my i7-6700k at least).
|
||||
|
||||
## Compilation on Linux
|
||||
|
||||
Use a GCC invocation like the following to build the example `pl_mpeg_player`
|
||||
program:
|
||||
|
||||
```shell
|
||||
gcc -o pl_mpeg_player pl_mpeg_player.c $(pkg-config --cflags --libs sdl2 glew)
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
||||
- [pl_mpeg_extract_frames.c](https://github.com/phoboslab/pl_mpeg/blob/master/pl_mpeg_extract_frames.c)
|
||||
extracts all frames from a video and saves them as PNG.
|
||||
- [pl_mpeg_player.c](https://github.com/phoboslab/pl_mpeg/blob/master/pl_mpeg_player.c)
|
||||
implements a video player using SDL2 and OpenGL for rendering.
|
||||
|
||||
|
||||
|
||||
## Encoding for PL_MPEG
|
||||
|
||||
Most [MPEG-PS](https://en.wikipedia.org/wiki/MPEG_program_stream) (`.mpg`) files
|
||||
containing MPEG1 Video ("mpeg1") and MPEG1 Audio Layer II ("mp2") streams should
|
||||
work with PL_MPEG. Note that `.mpg` files can also contain MPEG2 Video, which is
|
||||
not supported by this library.
|
||||
|
||||
You can encode video in a suitable format using ffmpeg:
|
||||
|
||||
```
|
||||
ffmpeg -i input.mp4 -c:v mpeg1video -q:v 0 -c:a mp2 -format mpeg output.mpg
|
||||
```
|
||||
|
||||
`-q:v` sets a fixed video quality with a variable bitrate, where `0` is the
|
||||
highest. You may use `-b:v` to set a fixed bitrate instead; e.g.
|
||||
`-b:v 2000k` for 2000 kbit/s. Please refer to the
|
||||
[ffmpeg documentation](http://ffmpeg.org/ffmpeg.html#Options) for more details.
|
||||
|
||||
If you just want to quickly test the library, try this file:
|
||||
|
||||
https://phoboslab.org/files/bjork-all-is-full-of-love.mpg
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
- no error reporting. PL_MPEG will silently ignore any invalid data.
|
||||
- the pts (presentation time stamp) for packets in the MPEG-PS container is
|
||||
ignored. This may cause sync issues with some files.
|
||||
- bugs, probably.
|
|
@ -1,92 +0,0 @@
|
|||
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
|
||||
Dominic Szablewski - https://phoboslab.org
|
||||
|
||||
-- Synopsis
|
||||
|
||||
// This function gets called for each decoded video frame
|
||||
void my_video_callback(plm_t *plm, plm_frame_t *frame, void *user) {
|
||||
// Do something with frame->y.data, frame->cr.data, frame->cb.data
|
||||
}
|
||||
|
||||
// This function gets called for each decoded audio frame
|
||||
void my_audio_callback(plm_t *plm, plm_samples_t *frame, void *user) {
|
||||
// Do something with samples->interleaved
|
||||
}
|
||||
|
||||
// Load a .mpg (MPEG Program Stream) file
|
||||
plm_t *plm = plm_create_with_filename("some-file.mpg");
|
||||
|
||||
// Install the video & audio decode callbacks
|
||||
plm_set_video_decode_callback(plm, my_video_callback, my_data);
|
||||
plm_set_audio_decode_callback(plm, my_audio_callback, my_data);
|
||||
|
||||
|
||||
// Decode
|
||||
do {
|
||||
plm_decode(plm, time_since_last_call);
|
||||
} while (!plm_has_ended(plm));
|
||||
|
||||
// All done
|
||||
plm_destroy(plm);
|
||||
|
||||
|
||||
|
||||
-- Documentation
|
||||
|
||||
This library provides several interfaces to load, demux and decode MPEG video
|
||||
and audio data. A high-level API combines the demuxer, video & audio decoders
|
||||
in an easy to use wrapper.
|
||||
|
||||
Lower-level APIs for accessing the demuxer, video decoder and audio decoder,
|
||||
as well as providing different data sources are also available.
|
||||
|
||||
Interfaces are written in an object orientet style, meaning you create object
|
||||
instances via various different constructor functions (plm_*create()),
|
||||
do some work on them and later dispose them via plm_*destroy().
|
||||
|
||||
plm_* -- the high-level interface, combining demuxer and decoders
|
||||
plm_buffer_* -- the data source used by all interfaces
|
||||
plm_demux_* -- the MPEG-PS demuxer
|
||||
plm_video_* -- the MPEG1 Video ("mpeg1") decoder
|
||||
plm_audio_* -- the MPEG1 Audio Layer II ("mp2") decoder
|
||||
|
||||
|
||||
This library uses malloc(), realloc() and free() to manage memory. Typically
|
||||
all allocation happens up-front when creating the interface. However, the
|
||||
default buffer size may be too small for certain inputs. In these cases plmpeg
|
||||
will realloc() the buffer with a larger size whenever needed. You can configure
|
||||
the default buffer size by defining PLM_BUFFER_DEFAULT_SIZE *before*
|
||||
including this library.
|
||||
|
||||
With the high-level interface you have two options to decode video & audio:
|
||||
|
||||
1) Use plm_decode() and just hand over the delta time since the last call.
|
||||
It will decode everything needed and call your callbacks (specified through
|
||||
plm_set_{video|audio}_decode_callback()) any number of times.
|
||||
|
||||
2) Use plm_decode_video() and plm_decode_audio() to decode exactly one
|
||||
frame of video or audio data at a time. How you handle the synchronization of
|
||||
both streams is up to you.
|
||||
|
||||
If you only want to decode video *or* audio through these functions, you should
|
||||
disable the other stream (plm_set_{video|audio}_enabled(false))
|
||||
|
||||
|
||||
Video data is decoded into a struct with all 3 planes (Y, Cr, Cb) stored in
|
||||
separate buffers. You can either convert this to RGB on the CPU (slow) via the
|
||||
plm_frame_to_rgb() function or do it on the GPU with the following matrix:
|
||||
|
||||
mat4 rec601 = mat4(
|
||||
1.16438, 0.00000, 1.59603, -0.87079,
|
||||
1.16438, -0.39176, -0.81297, 0.52959,
|
||||
1.16438, 2.01723, 0.00000, -1.08139,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
gl_FragColor = vec4(y, cb, cr, 1.0) * rec601;
|
||||
|
||||
Audio data is decoded into a struct with either one single float array with the
|
||||
samples for the left and right channel interleaved, or if the
|
||||
PLM_AUDIO_SEPARATE_CHANNELS is defined *before* including this library, into
|
||||
two separate float arrays - one for each channel.
|
||||
|
||||
See below for detailed the API documentation.
|
|
@ -1,20 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_
|
||||
|
||||
#define PLM_BLOCK_SET(DEST, DEST_INDEX, DEST_WIDTH, SOURCE_INDEX, \
|
||||
SOURCE_WIDTH, BLOCK_SIZE, OP) \
|
||||
do { \
|
||||
int dest_scan = DEST_WIDTH - BLOCK_SIZE; \
|
||||
int source_scan = SOURCE_WIDTH - BLOCK_SIZE; \
|
||||
for (int y = 0; y < BLOCK_SIZE; y++) { \
|
||||
for (int x = 0; x < BLOCK_SIZE; x++) { \
|
||||
DEST[DEST_INDEX] = OP; \
|
||||
SOURCE_INDEX++; \
|
||||
DEST_INDEX++; \
|
||||
} \
|
||||
SOURCE_INDEX += source_scan; \
|
||||
DEST_INDEX += dest_scan; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_ */
|
|
@ -1,153 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set noet 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/buffer.h"
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/madv.h"
|
||||
__static_yoink("pl_mpeg_notice");
|
||||
|
||||
/* clang-format off */
|
||||
// -----------------------------------------------------------------------------
|
||||
// plm_buffer implementation
|
||||
|
||||
plm_buffer_t *plm_buffer_create_with_filename(const char *filename) {
|
||||
FILE *fh = fopen(filename, "rb");
|
||||
if (!fh) {
|
||||
return NULL;
|
||||
}
|
||||
fadvise(fileno(fh), 0, 0, MADV_SEQUENTIAL);
|
||||
return plm_buffer_create_with_file(fh, true);
|
||||
}
|
||||
|
||||
plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done) {
|
||||
plm_buffer_t *b;
|
||||
b = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
|
||||
b->fh = fh;
|
||||
b->close_when_done = close_when_done;
|
||||
b->mode = PLM_BUFFER_MODE_FILE;
|
||||
plm_buffer_set_load_callback(b, plm_buffer_load_file_callback, NULL);
|
||||
return b;
|
||||
}
|
||||
|
||||
plm_buffer_t *plm_buffer_create_with_memory(unsigned char *bytes, size_t length, int free_when_done) {
|
||||
plm_buffer_t *b;
|
||||
b = memalign(_Alignof(plm_buffer_t), sizeof(plm_buffer_t));
|
||||
memset(b, 0, sizeof(plm_buffer_t));
|
||||
b->capacity = length;
|
||||
b->length = length;
|
||||
b->free_when_done = free_when_done;
|
||||
b->bytes = bytes;
|
||||
b->mode = PLM_BUFFER_MODE_FIXED_MEM;
|
||||
return b;
|
||||
}
|
||||
|
||||
plm_buffer_t * plm_buffer_create_with_capacity(size_t capacity) {
|
||||
plm_buffer_t *b;
|
||||
b = memalign(_Alignof(plm_buffer_t), sizeof(plm_buffer_t));
|
||||
memset(b, 0, sizeof(plm_buffer_t));
|
||||
b->capacity = capacity;
|
||||
b->free_when_done = true;
|
||||
b->bytes = (unsigned char *)malloc(capacity);
|
||||
b->mode = PLM_BUFFER_MODE_DYNAMIC_MEM;
|
||||
return b;
|
||||
}
|
||||
|
||||
void plm_buffer_destroy(plm_buffer_t *self) {
|
||||
if (self->fh && self->close_when_done) {
|
||||
fclose(self->fh);
|
||||
}
|
||||
if (self->free_when_done) {
|
||||
free(self->bytes);
|
||||
}
|
||||
free(self);
|
||||
}
|
||||
|
||||
size_t plm_buffer_write(plm_buffer_t *self, unsigned char *bytes, size_t length) {
|
||||
if (self->mode == PLM_BUFFER_MODE_FIXED_MEM) {
|
||||
return 0;
|
||||
}
|
||||
// This should be a ring buffer, but instead it just shifts all unread data
|
||||
// to the beginning of the buffer and appends new data at the end. Seems
|
||||
// to be good enough.
|
||||
plm_buffer_discard_read_bytes(self);
|
||||
// Do we have to resize to fit the new data?
|
||||
size_t bytes_available = self->capacity - self->length;
|
||||
if (bytes_available < length) {
|
||||
size_t new_size = self->capacity;
|
||||
do {
|
||||
new_size *= 2;
|
||||
} while (new_size - self->length < length);
|
||||
self->bytes = (unsigned char *)realloc(self->bytes, new_size);
|
||||
self->capacity = new_size;
|
||||
}
|
||||
memcpy(self->bytes + self->length, bytes, length);
|
||||
self->length += length;
|
||||
return length;
|
||||
}
|
||||
|
||||
void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback fp, void *user) {
|
||||
self->load_callback = fp;
|
||||
self->load_callback_user_data = user;
|
||||
}
|
||||
|
||||
void plm_buffer_rewind(plm_buffer_t *self) {
|
||||
if (self->fh) {
|
||||
fseek(self->fh, 0, SEEK_SET);
|
||||
self->length = 0;
|
||||
}
|
||||
if (self->mode != PLM_BUFFER_MODE_FIXED_MEM) {
|
||||
self->length = 0;
|
||||
}
|
||||
self->bit_index = 0;
|
||||
}
|
||||
|
||||
void plm_buffer_discard_read_bytes(plm_buffer_t *self) {
|
||||
size_t byte_pos = self->bit_index >> 3;
|
||||
if (byte_pos == self->length) {
|
||||
self->bit_index = 0;
|
||||
self->length = 0;
|
||||
}
|
||||
else if (byte_pos > 0) {
|
||||
memmove(self->bytes, self->bytes + byte_pos, self->length - byte_pos);
|
||||
self->bit_index -= byte_pos << 3;
|
||||
self->length -= byte_pos;
|
||||
}
|
||||
}
|
||||
|
||||
void plm_buffer_load_file_callback(plm_buffer_t *self, void *user) {
|
||||
plm_buffer_discard_read_bytes(self);
|
||||
unsigned bytes_available = self->capacity - self->length;
|
||||
unsigned bytes_read = fread(self->bytes + self->length, 1, bytes_available, self->fh);
|
||||
self->length += bytes_read;
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_DSP_MPEG_BUFFER_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_BUFFER_H_
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
enum plm_buffer_mode {
|
||||
PLM_BUFFER_MODE_FILE,
|
||||
PLM_BUFFER_MODE_FIXED_MEM,
|
||||
PLM_BUFFER_MODE_DYNAMIC_MEM
|
||||
};
|
||||
|
||||
typedef struct plm_buffer_t {
|
||||
unsigned bit_index;
|
||||
unsigned capacity;
|
||||
unsigned length;
|
||||
int free_when_done;
|
||||
int close_when_done;
|
||||
FILE *fh;
|
||||
plm_buffer_load_callback load_callback;
|
||||
void *load_callback_user_data;
|
||||
unsigned char *bytes;
|
||||
enum plm_buffer_mode mode;
|
||||
} plm_buffer_t;
|
||||
|
||||
typedef struct {
|
||||
int16_t index;
|
||||
int16_t value;
|
||||
} plm_vlc_t;
|
||||
|
||||
typedef struct {
|
||||
int16_t index;
|
||||
uint16_t value;
|
||||
} plm_vlc_uint_t;
|
||||
|
||||
/* bool plm_buffer_has(plm_buffer_t *, size_t); */
|
||||
/* int plm_buffer_read(plm_buffer_t *, int); */
|
||||
/* void plm_buffer_align(plm_buffer_t *); */
|
||||
/* void plm_buffer_skip(plm_buffer_t *, size_t); */
|
||||
/* int plm_buffer_skip_bytes(plm_buffer_t *, unsigned char); */
|
||||
/* int plm_buffer_next_start_code(plm_buffer_t *); */
|
||||
/* int plm_buffer_find_start_code(plm_buffer_t *, int); */
|
||||
/* int plm_buffer_no_start_code(plm_buffer_t *); */
|
||||
/* int16_t plm_buffer_read_vlc(plm_buffer_t *, const plm_vlc_t *); */
|
||||
/* uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *, const plm_vlc_uint_t *); */
|
||||
|
||||
void plm_buffer_discard_read_bytes(plm_buffer_t *);
|
||||
relegated void plm_buffer_load_file_callback(plm_buffer_t *, void *);
|
||||
|
||||
forceinline bool plm_buffer_has(plm_buffer_t *b, size_t bits) {
|
||||
unsigned have;
|
||||
have = b->length;
|
||||
have <<= 3;
|
||||
have -= b->bit_index;
|
||||
if (bits <= have) {
|
||||
return true;
|
||||
} else {
|
||||
if (b->load_callback) {
|
||||
b->load_callback(b, b->load_callback_user_data);
|
||||
return ((b->length << 3) - b->bit_index) >= bits;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_read(plm_buffer_t *self, int count) {
|
||||
if (!plm_buffer_has(self, count))
|
||||
return 0;
|
||||
int value = 0;
|
||||
while (count) {
|
||||
int current_byte = self->bytes[self->bit_index >> 3];
|
||||
int remaining = 8 - (self->bit_index & 7); // Remaining bits in byte
|
||||
int read = remaining < count ? remaining : count; // Bits in self run
|
||||
int shift = remaining - read;
|
||||
int mask = (0xff >> (8 - read));
|
||||
value = (value << read) | ((current_byte & (mask << shift)) >> shift);
|
||||
self->bit_index += read;
|
||||
count -= read;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
forceinline void plm_buffer_align(plm_buffer_t *self) {
|
||||
self->bit_index = ((self->bit_index + 7) >> 3) << 3;
|
||||
}
|
||||
|
||||
forceinline void plm_buffer_skip(plm_buffer_t *self, size_t count) {
|
||||
if (plm_buffer_has(self, count)) {
|
||||
self->bit_index += count;
|
||||
}
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_skip_bytes(plm_buffer_t *self, unsigned char v) {
|
||||
unsigned skipped;
|
||||
plm_buffer_align(self);
|
||||
skipped = 0;
|
||||
while (plm_buffer_has(self, 8)) {
|
||||
if (v == self->bytes[self->bit_index >> 3]) {
|
||||
self->bit_index += 8;
|
||||
++skipped;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return skipped;
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_next_start_code(plm_buffer_t *self) {
|
||||
plm_buffer_align(self);
|
||||
while (plm_buffer_has(self, (5 << 3))) {
|
||||
size_t byte_index = (self->bit_index) >> 3;
|
||||
if (self->bytes[byte_index] == 0x00 &&
|
||||
self->bytes[byte_index + 1] == 0x00 &&
|
||||
self->bytes[byte_index + 2] == 0x01) {
|
||||
self->bit_index = (byte_index + 4) << 3;
|
||||
return self->bytes[byte_index + 3];
|
||||
}
|
||||
self->bit_index += 8;
|
||||
}
|
||||
self->bit_index = (self->length << 3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_find_start_code(plm_buffer_t *self, int code) {
|
||||
int current = 0;
|
||||
while (true) {
|
||||
current = plm_buffer_next_start_code(self);
|
||||
if (current == code || current == -1) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_no_start_code(plm_buffer_t *self) {
|
||||
if (!plm_buffer_has(self, (5 << 3))) {
|
||||
return false;
|
||||
}
|
||||
size_t byte_index = ((self->bit_index + 7) >> 3);
|
||||
return !(self->bytes[byte_index] == 0x00 &&
|
||||
self->bytes[byte_index + 1] == 0x00 &&
|
||||
self->bytes[byte_index + 2] == 0x01);
|
||||
}
|
||||
|
||||
forceinline int16_t plm_buffer_read_vlc(plm_buffer_t *self,
|
||||
const plm_vlc_t *table) {
|
||||
plm_vlc_t state = {0, 0};
|
||||
do {
|
||||
state = table[state.index + plm_buffer_read(self, 1)];
|
||||
} while (state.index > 0);
|
||||
return state.value;
|
||||
}
|
||||
|
||||
forceinline uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self,
|
||||
const plm_vlc_uint_t *table) {
|
||||
return (uint16_t)plm_buffer_read_vlc(self, (plm_vlc_t *)table);
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_BUFFER_H_ */
|
|
@ -1,30 +0,0 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.h"
|
||||
|
||||
clamp4int256$core:
|
||||
.leafprologue
|
||||
pxor %xmm1,%xmm1
|
||||
pmaxsd %xmm1,%xmm0
|
||||
pminsd 0f(%rip),%xmm0
|
||||
.leafepilogue
|
||||
.endfn clamp4int256$core,globl
|
||||
|
||||
.rodata.cst16
|
||||
0: .long 255,255,255,255
|
203
dsp/mpeg/demux.c
203
dsp/mpeg/demux.c
|
@ -1,203 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set noet 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/demux.h"
|
||||
#include "dsp/mpeg/buffer.h"
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
__static_yoink("pl_mpeg_notice");
|
||||
|
||||
/* clang-format off */
|
||||
// ----------------------------------------------------------------------------
|
||||
// plm_demux implementation
|
||||
|
||||
plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done) {
|
||||
plm_demux_t *self = (plm_demux_t *)malloc(sizeof(plm_demux_t));
|
||||
memset(self, 0, sizeof(plm_demux_t));
|
||||
|
||||
self->buffer = buffer;
|
||||
self->destroy_buffer_when_done = destroy_when_done;
|
||||
|
||||
if (plm_buffer_find_start_code(self->buffer, START_PACK) != -1) {
|
||||
plm_demux_decode_pack_header(self);
|
||||
}
|
||||
if (plm_buffer_find_start_code(self->buffer, START_SYSTEM) != -1) {
|
||||
plm_demux_decode_system_header(self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
void plm_demux_destroy(plm_demux_t *self) {
|
||||
if (self->destroy_buffer_when_done) {
|
||||
plm_buffer_destroy(self->buffer);
|
||||
}
|
||||
free(self);
|
||||
}
|
||||
|
||||
int plm_demux_get_num_video_streams(plm_demux_t *self) {
|
||||
return self->num_video_streams;
|
||||
}
|
||||
|
||||
int plm_demux_get_num_audio_streams(plm_demux_t *self) {
|
||||
return self->num_audio_streams;
|
||||
}
|
||||
|
||||
void plm_demux_rewind(plm_demux_t *self) {
|
||||
plm_buffer_rewind(self->buffer);
|
||||
}
|
||||
|
||||
plm_packet_t *plm_demux_decode(plm_demux_t *self) {
|
||||
if (self->current_packet.length) {
|
||||
size_t bits_till_next_packet = self->current_packet.length << 3;
|
||||
if (!plm_buffer_has(self->buffer, bits_till_next_packet)) {
|
||||
return NULL;
|
||||
}
|
||||
plm_buffer_skip(self->buffer, bits_till_next_packet);
|
||||
self->current_packet.length = 0;
|
||||
}
|
||||
|
||||
if (!self->has_pack_header) {
|
||||
if (plm_buffer_find_start_code(self->buffer, START_PACK) != -1) {
|
||||
plm_demux_decode_pack_header(self);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->has_system_header) {
|
||||
if (plm_buffer_find_start_code(self->buffer, START_SYSTEM) != -1) {
|
||||
plm_demux_decode_system_header(self);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// pending packet just waiting for data?
|
||||
if (self->next_packet.length) {
|
||||
return plm_demux_get_packet(self);
|
||||
}
|
||||
|
||||
int code;
|
||||
do {
|
||||
code = plm_buffer_next_start_code(self->buffer);
|
||||
if (
|
||||
code == PLM_DEMUX_PACKET_VIDEO_1 ||
|
||||
code == PLM_DEMUX_PACKET_PRIVATE ||
|
||||
(code >= PLM_DEMUX_PACKET_AUDIO_1 && code <= PLM_DEMUX_PACKET_AUDIO_4)
|
||||
) {
|
||||
return plm_demux_decode_packet(self, code);
|
||||
}
|
||||
} while (code != -1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double plm_demux_read_time(plm_demux_t *self) {
|
||||
int64_t clock = plm_buffer_read(self->buffer, 3) << 30;
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
clock |= plm_buffer_read(self->buffer, 15) << 15;
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
clock |= plm_buffer_read(self->buffer, 15);
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
return (double)clock / 90000.0;
|
||||
}
|
||||
|
||||
void plm_demux_decode_pack_header(plm_demux_t *self) {
|
||||
if (plm_buffer_read(self->buffer, 4) != 0x02) {
|
||||
return; // invalid
|
||||
}
|
||||
self->system_clock_ref = plm_demux_read_time(self);
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 22); // mux_rate * 50
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
|
||||
self->has_pack_header = true;
|
||||
}
|
||||
|
||||
void plm_demux_decode_system_header(plm_demux_t *self) {
|
||||
plm_buffer_skip(self->buffer, 16); // header_length
|
||||
plm_buffer_skip(self->buffer, 24); // rate bound
|
||||
self->num_audio_streams = plm_buffer_read(self->buffer, 6);
|
||||
plm_buffer_skip(self->buffer, 5); // misc flags
|
||||
self->num_video_streams = plm_buffer_read(self->buffer, 5);
|
||||
|
||||
self->has_system_header = true;
|
||||
}
|
||||
|
||||
plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int start_code) {
|
||||
if (!plm_buffer_has(self->buffer, 8 << 3)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->next_packet.type = start_code;
|
||||
self->next_packet.length = plm_buffer_read(self->buffer, 16);
|
||||
self->next_packet.length -= plm_buffer_skip_bytes(self->buffer, 0xff); // stuffing
|
||||
|
||||
// skip P-STD
|
||||
if (plm_buffer_read(self->buffer, 2) == 0x01) {
|
||||
plm_buffer_skip(self->buffer, 16);
|
||||
self->next_packet.length -= 2;
|
||||
}
|
||||
|
||||
int pts_dts_marker = plm_buffer_read(self->buffer, 2);
|
||||
if (pts_dts_marker == 0x03) {
|
||||
self->next_packet.pts = plm_demux_read_time(self);
|
||||
plm_buffer_skip(self->buffer, 40); // skip dts
|
||||
self->next_packet.length -= 10;
|
||||
}
|
||||
else if (pts_dts_marker == 0x02) {
|
||||
self->next_packet.pts = plm_demux_read_time(self);
|
||||
self->next_packet.length -= 5;
|
||||
}
|
||||
else if (pts_dts_marker == 0x00) {
|
||||
self->next_packet.pts = 0;
|
||||
plm_buffer_skip(self->buffer, 4);
|
||||
self->next_packet.length -= 1;
|
||||
}
|
||||
else {
|
||||
return NULL; // invalid
|
||||
}
|
||||
|
||||
return plm_demux_get_packet(self);
|
||||
}
|
||||
|
||||
plm_packet_t *plm_demux_get_packet(plm_demux_t *self) {
|
||||
if (!plm_buffer_has(self->buffer, self->next_packet.length << 3)) {
|
||||
return NULL;
|
||||
}
|
||||
self->current_packet.data = self->buffer->bytes + (self->buffer->bit_index >> 3);
|
||||
self->current_packet.length = self->next_packet.length;
|
||||
self->current_packet.type = self->next_packet.type;
|
||||
self->current_packet.pts = self->next_packet.pts;
|
||||
self->next_packet.length = 0;
|
||||
return &self->current_packet;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_DSP_MPEG_DEMUX_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_DEMUX_H_
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define START_PACK 0xBA
|
||||
#define START_END 0xB9
|
||||
#define START_SYSTEM 0xBB
|
||||
|
||||
typedef struct plm_demux_t {
|
||||
plm_buffer_t *buffer;
|
||||
int destroy_buffer_when_done;
|
||||
double system_clock_ref;
|
||||
int has_pack_header;
|
||||
int has_system_header;
|
||||
int num_audio_streams;
|
||||
int num_video_streams;
|
||||
plm_packet_t current_packet;
|
||||
plm_packet_t next_packet;
|
||||
} plm_demux_t;
|
||||
|
||||
double plm_demux_read_time(plm_demux_t *self);
|
||||
void plm_demux_decode_pack_header(plm_demux_t *self);
|
||||
void plm_demux_decode_system_header(plm_demux_t *self);
|
||||
plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int start_code);
|
||||
plm_packet_t *plm_demux_get_packet(plm_demux_t *self);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_DEMUX_H_ */
|
101
dsp/mpeg/idct.c
101
dsp/mpeg/idct.c
|
@ -1,101 +0,0 @@
|
|||
/*-*- 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/core/half.h"
|
||||
__static_yoink("pl_mpeg_notice");
|
||||
|
||||
/**
|
||||
* Computes Fixed-Point 8x8 Inverse Discrete Cosine Transform.
|
||||
*
|
||||
* @note discovered by Nasir Ahmed
|
||||
*/
|
||||
void plm_video_idct(int block[8][8]) {
|
||||
int i, t1, t2, m0;
|
||||
int b1, b3, b4, b6, b7;
|
||||
int y3, y4, y5, y6, y7;
|
||||
int x0, x1, x2, x3, x4;
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
b1 = block[4][i];
|
||||
b3 = block[2][i] + block[6][i];
|
||||
b4 = block[5][i] - block[3][i];
|
||||
t1 = block[1][i] + block[7][i];
|
||||
t2 = block[3][i] + block[5][i];
|
||||
b6 = block[1][i] - block[7][i];
|
||||
b7 = t1 + t2;
|
||||
m0 = block[0][i];
|
||||
x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7;
|
||||
x0 = x4 - (((t1 - t2) * 362 + 128) >> 8);
|
||||
x1 = m0 - b1;
|
||||
x2 = (((block[2][i] - block[6][i]) * 362 + 128) >> 8) - b3;
|
||||
x3 = m0 + b1;
|
||||
y3 = x1 + x2;
|
||||
y4 = x3 + b3;
|
||||
y5 = x1 - x2;
|
||||
y6 = x3 - b3;
|
||||
y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8);
|
||||
block[0][i] = b7 + y4;
|
||||
block[1][i] = x4 + y3;
|
||||
block[2][i] = y5 - x0;
|
||||
block[3][i] = y6 - y7;
|
||||
block[4][i] = y6 + y7;
|
||||
block[5][i] = x0 + y5;
|
||||
block[6][i] = y3 - x4;
|
||||
block[7][i] = y4 - b7;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
b1 = block[i][4];
|
||||
b3 = block[i][2] + block[i][6];
|
||||
b4 = block[i][5] - block[i][3];
|
||||
t1 = block[i][1] + block[i][7];
|
||||
t2 = block[i][3] + block[i][5];
|
||||
b6 = block[i][1] - block[i][7];
|
||||
b7 = t1 + t2;
|
||||
m0 = block[i][0];
|
||||
x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7;
|
||||
x0 = x4 - (((t1 - t2) * 362 + 128) >> 8);
|
||||
x1 = m0 - b1;
|
||||
x2 = (((block[i][2] - block[i][6]) * 362 + 128) >> 8) - b3;
|
||||
x3 = m0 + b1;
|
||||
y3 = x1 + x2;
|
||||
y4 = x3 + b3;
|
||||
y5 = x1 - x2;
|
||||
y6 = x3 - b3;
|
||||
y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8);
|
||||
block[i][0] = (b7 + y4 + 128) >> 8;
|
||||
block[i][1] = (x4 + y3 + 128) >> 8;
|
||||
block[i][2] = (y5 - x0 + 128) >> 8;
|
||||
block[i][3] = (y6 - y7 + 128) >> 8;
|
||||
block[i][4] = (y6 + y7 + 128) >> 8;
|
||||
block[i][5] = (x0 + y5 + 128) >> 8;
|
||||
block[i][6] = (y3 - x4 + 128) >> 8;
|
||||
block[i][7] = (y4 - b7 + 128) >> 8;
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_DSP_MPEG_IDCT_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_IDCT_H_
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void plm_video_idct(int *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_IDCT_H_ */
|
|
@ -1,171 +0,0 @@
|
|||
/*-*- 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/mpeg.h"
|
||||
#include "dsp/mpeg/video.h"
|
||||
#include "libc/log/check.h"
|
||||
|
||||
forceinline void plm_video_process_macroblock(plm_video_t *self, uint8_t *d,
|
||||
uint8_t *s, int motion_h,
|
||||
int motion_v, bool interpolate,
|
||||
unsigned BW) {
|
||||
unsigned si, di, max_address;
|
||||
int y, x, dest_scan, source_scan, dw, hp, vp, odd_h, odd_v;
|
||||
dw = self->mb_width * BW;
|
||||
hp = motion_h >> 1;
|
||||
vp = motion_v >> 1;
|
||||
odd_h = (motion_h & 1) == 1;
|
||||
odd_v = (motion_v & 1) == 1;
|
||||
si = ((self->mb_row * BW) + vp) * dw + (self->mb_col * BW) + hp;
|
||||
di = (self->mb_row * dw + self->mb_col) * BW;
|
||||
max_address = (dw * (self->mb_height * BW - BW + 1) - BW);
|
||||
if (si > max_address || di > max_address)
|
||||
return;
|
||||
d += di;
|
||||
s += si;
|
||||
switch (((interpolate << 2) | (odd_h << 1) | (odd_v)) & 7) {
|
||||
case 0:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
*d++ = *s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
*d++ = (s[0] + s[dw] + 1) >> 1;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
*d++ = (s[0] + s[1] + 1) >> 1;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
*d++ = (s[0] + s[1] + s[dw] + s[dw + 1] + 2) >> 2;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
d[0] = (d[0] + (s[0]) + 1) >> 1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
d[0] = (d[0] + ((s[0] + s[dw] + 1) >> 1) + 1) >> 1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
d[0] = (d[0] + ((s[0] + s[1] + 1) >> 1) + 1) >> 1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
d[0] = (d[0] + ((s[0] + s[1] + s[dw] + s[dw + 1] + 2) >> 2) + 1) >> 1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void plm_video_process_macroblock_8(plm_video_t *self, uint8_t *d, uint8_t *s,
|
||||
int motion_h, int motion_v,
|
||||
bool interpolate) {
|
||||
DCHECK_ALIGNED(8, d);
|
||||
DCHECK_ALIGNED(8, s);
|
||||
plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 8);
|
||||
}
|
||||
|
||||
void plm_video_process_macroblock_16(plm_video_t *self, uint8_t *d, uint8_t *s,
|
||||
int motion_h, int motion_v,
|
||||
bool interpolate) {
|
||||
DCHECK_ALIGNED(16, d);
|
||||
DCHECK_ALIGNED(16, s);
|
||||
plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 16);
|
||||
}
|
769
dsp/mpeg/mp2.c
769
dsp/mpeg/mp2.c
|
@ -1,769 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set noet 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/buffer.h"
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/* clang-format off */
|
||||
// -----------------------------------------------------------------------------
|
||||
// plm_audio implementation
|
||||
|
||||
// Based on kjmp2 by Martin J. Fiedler
|
||||
// http://keyj.emphy.de/kjmp2/
|
||||
|
||||
#define PLM_AUDIO_FRAME_SYNC 0x7ff
|
||||
|
||||
#define PLM_AUDIO_MPEG_2_5 0x0
|
||||
#define PLM_AUDIO_MPEG_2 0x2
|
||||
#define PLM_AUDIO_MPEG_1 0x3
|
||||
|
||||
#define PLM_AUDIO_LAYER_III 0x1
|
||||
#define PLM_AUDIO_LAYER_II 0x2
|
||||
#define PLM_AUDIO_LAYER_I 0x3
|
||||
|
||||
#define PLM_AUDIO_MODE_STEREO 0x0
|
||||
#define PLM_AUDIO_MODE_JOINT_STEREO 0x1
|
||||
#define PLM_AUDIO_MODE_DUAL_CHANNEL 0x2
|
||||
#define PLM_AUDIO_MODE_MONO 0x3
|
||||
|
||||
static const unsigned short PLM_AUDIO_SAMPLE_RATE[] = {
|
||||
44100, 48000, 32000, 0, // MPEG-1
|
||||
22050, 24000, 16000, 0 // MPEG-2
|
||||
};
|
||||
|
||||
static const short PLM_AUDIO_BIT_RATE[] = {
|
||||
32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, // MPEG-1
|
||||
8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 // MPEG-2
|
||||
};
|
||||
|
||||
static const int PLM_AUDIO_SCALEFACTOR_BASE[] = {
|
||||
0x02000000, 0x01965FEA, 0x01428A30
|
||||
};
|
||||
|
||||
static const float PLM_AUDIO_SYNTHESIS_WINDOW[] = {
|
||||
0.0, -0.5, -0.5, -0.5, -0.5, -0.5,
|
||||
-0.5, -1.0, -1.0, -1.0, -1.0, -1.5,
|
||||
-1.5, -2.0, -2.0, -2.5, -2.5, -3.0,
|
||||
-3.5, -3.5, -4.0, -4.5, -5.0, -5.5,
|
||||
-6.5, -7.0, -8.0, -8.5, -9.5, -10.5,
|
||||
-12.0, -13.0, -14.5, -15.5, -17.5, -19.0,
|
||||
-20.5, -22.5, -24.5, -26.5, -29.0, -31.5,
|
||||
-34.0, -36.5, -39.5, -42.5, -45.5, -48.5,
|
||||
-52.0, -55.5, -58.5, -62.5, -66.0, -69.5,
|
||||
-73.5, -77.0, -80.5, -84.5, -88.0, -91.5,
|
||||
-95.0, -98.0, -101.0, -104.0, 106.5, 109.0,
|
||||
111.0, 112.5, 113.5, 114.0, 114.0, 113.5,
|
||||
112.0, 110.5, 107.5, 104.0, 100.0, 94.5,
|
||||
88.5, 81.5, 73.0, 63.5, 53.0, 41.5,
|
||||
28.5, 14.5, -1.0, -18.0, -36.0, -55.5,
|
||||
-76.5, -98.5, -122.0, -147.0, -173.5, -200.5,
|
||||
-229.5, -259.5, -290.5, -322.5, -355.5, -389.5,
|
||||
-424.0, -459.5, -495.5, -532.0, -568.5, -605.0,
|
||||
-641.5, -678.0, -714.0, -749.0, -783.5, -817.0,
|
||||
-849.0, -879.5, -908.5, -935.0, -959.5, -981.0,
|
||||
-1000.5, -1016.0, -1028.5, -1037.5, -1042.5, -1043.5,
|
||||
-1040.0, -1031.5, 1018.5, 1000.0, 976.0, 946.5,
|
||||
911.0, 869.5, 822.0, 767.5, 707.0, 640.0,
|
||||
565.5, 485.0, 397.0, 302.5, 201.0, 92.5,
|
||||
-22.5, -144.0, -272.5, -407.0, -547.5, -694.0,
|
||||
-846.0, -1003.0, -1165.0, -1331.5, -1502.0, -1675.5,
|
||||
-1852.5, -2031.5, -2212.5, -2394.0, -2576.5, -2758.5,
|
||||
-2939.5, -3118.5, -3294.5, -3467.5, -3635.5, -3798.5,
|
||||
-3955.0, -4104.5, -4245.5, -4377.5, -4499.0, -4609.5,
|
||||
-4708.0, -4792.5, -4863.5, -4919.0, -4958.0, -4979.5,
|
||||
-4983.0, -4967.5, -4931.5, -4875.0, -4796.0, -4694.5,
|
||||
-4569.5, -4420.0, -4246.0, -4046.0, -3820.0, -3567.0,
|
||||
3287.0, 2979.5, 2644.0, 2280.5, 1888.0, 1467.5,
|
||||
1018.5, 541.0, 35.0, -499.0, -1061.0, -1650.0,
|
||||
-2266.5, -2909.0, -3577.0, -4270.0, -4987.5, -5727.5,
|
||||
-6490.0, -7274.0, -8077.5, -8899.5, -9739.0, -10594.5,
|
||||
-11464.5, -12347.0, -13241.0, -14144.5, -15056.0, -15973.5,
|
||||
-16895.5, -17820.0, -18744.5, -19668.0, -20588.0, -21503.0,
|
||||
-22410.5, -23308.5, -24195.0, -25068.5, -25926.5, -26767.0,
|
||||
-27589.0, -28389.0, -29166.5, -29919.0, -30644.5, -31342.0,
|
||||
-32009.5, -32645.0, -33247.0, -33814.5, -34346.0, -34839.5,
|
||||
-35295.0, -35710.0, -36084.5, -36417.5, -36707.5, -36954.0,
|
||||
-37156.5, -37315.0, -37428.0, -37496.0, 37519.0, 37496.0,
|
||||
37428.0, 37315.0, 37156.5, 36954.0, 36707.5, 36417.5,
|
||||
36084.5, 35710.0, 35295.0, 34839.5, 34346.0, 33814.5,
|
||||
33247.0, 32645.0, 32009.5, 31342.0, 30644.5, 29919.0,
|
||||
29166.5, 28389.0, 27589.0, 26767.0, 25926.5, 25068.5,
|
||||
24195.0, 23308.5, 22410.5, 21503.0, 20588.0, 19668.0,
|
||||
18744.5, 17820.0, 16895.5, 15973.5, 15056.0, 14144.5,
|
||||
13241.0, 12347.0, 11464.5, 10594.5, 9739.0, 8899.5,
|
||||
8077.5, 7274.0, 6490.0, 5727.5, 4987.5, 4270.0,
|
||||
3577.0, 2909.0, 2266.5, 1650.0, 1061.0, 499.0,
|
||||
-35.0, -541.0, -1018.5, -1467.5, -1888.0, -2280.5,
|
||||
-2644.0, -2979.5, 3287.0, 3567.0, 3820.0, 4046.0,
|
||||
4246.0, 4420.0, 4569.5, 4694.5, 4796.0, 4875.0,
|
||||
4931.5, 4967.5, 4983.0, 4979.5, 4958.0, 4919.0,
|
||||
4863.5, 4792.5, 4708.0, 4609.5, 4499.0, 4377.5,
|
||||
4245.5, 4104.5, 3955.0, 3798.5, 3635.5, 3467.5,
|
||||
3294.5, 3118.5, 2939.5, 2758.5, 2576.5, 2394.0,
|
||||
2212.5, 2031.5, 1852.5, 1675.5, 1502.0, 1331.5,
|
||||
1165.0, 1003.0, 846.0, 694.0, 547.5, 407.0,
|
||||
272.5, 144.0, 22.5, -92.5, -201.0, -302.5,
|
||||
-397.0, -485.0, -565.5, -640.0, -707.0, -767.5,
|
||||
-822.0, -869.5, -911.0, -946.5, -976.0, -1000.0,
|
||||
1018.5, 1031.5, 1040.0, 1043.5, 1042.5, 1037.5,
|
||||
1028.5, 1016.0, 1000.5, 981.0, 959.5, 935.0,
|
||||
908.5, 879.5, 849.0, 817.0, 783.5, 749.0,
|
||||
714.0, 678.0, 641.5, 605.0, 568.5, 532.0,
|
||||
495.5, 459.5, 424.0, 389.5, 355.5, 322.5,
|
||||
290.5, 259.5, 229.5, 200.5, 173.5, 147.0,
|
||||
122.0, 98.5, 76.5, 55.5, 36.0, 18.0,
|
||||
1.0, -14.5, -28.5, -41.5, -53.0, -63.5,
|
||||
-73.0, -81.5, -88.5, -94.5, -100.0, -104.0,
|
||||
-107.5, -110.5, -112.0, -113.5, -114.0, -114.0,
|
||||
-113.5, -112.5, -111.0, -109.0, 106.5, 104.0,
|
||||
101.0, 98.0, 95.0, 91.5, 88.0, 84.5,
|
||||
80.5, 77.0, 73.5, 69.5, 66.0, 62.5,
|
||||
58.5, 55.5, 52.0, 48.5, 45.5, 42.5,
|
||||
39.5, 36.5, 34.0, 31.5, 29.0, 26.5,
|
||||
24.5, 22.5, 20.5, 19.0, 17.5, 15.5,
|
||||
14.5, 13.0, 12.0, 10.5, 9.5, 8.5,
|
||||
8.0, 7.0, 6.5, 5.5, 5.0, 4.5,
|
||||
4.0, 3.5, 3.5, 3.0, 2.5, 2.5,
|
||||
2.0, 2.0, 1.5, 1.5, 1.0, 1.0,
|
||||
1.0, 1.0, 0.5, 0.5, 0.5, 0.5,
|
||||
0.5, 0.5
|
||||
};
|
||||
|
||||
// Quantizer lookup, step 1: bitrate classes
|
||||
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_1[2][16] = {
|
||||
// 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384 <- bitrate
|
||||
{ 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // mono
|
||||
// 16, 24, 28, 32, 40, 48, 56, 64, 80, 96,112,128,160,192 <- bitrate / chan
|
||||
{ 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 } // stereo
|
||||
};
|
||||
|
||||
// Quantizer lookup, step 2: bitrate class, sample rate -> B2 table idx, sblimit
|
||||
static const uint8_t PLM_AUDIO_QUANT_TAB_A = (27 | 64); // Table 3-B.2a: high-rate, sblimit = 27
|
||||
static const uint8_t PLM_AUDIO_QUANT_TAB_B = (30 | 64); // Table 3-B.2b: high-rate, sblimit = 30
|
||||
static const uint8_t PLM_AUDIO_QUANT_TAB_C = 8; // Table 3-B.2c: low-rate, sblimit = 8
|
||||
static const uint8_t PLM_AUDIO_QUANT_TAB_D = 12; // Table 3-B.2d: low-rate, sblimit = 12
|
||||
|
||||
static const uint8_t QUANT_LUT_STEP_2[3][3] = {
|
||||
// 44.1 kHz, 48 kHz, 32 kHz
|
||||
{ PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_D }, // 32 - 48 kbit/sec/ch
|
||||
{ PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A }, // 56 - 80 kbit/sec/ch
|
||||
{ PLM_AUDIO_QUANT_TAB_B, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_B } // 96+ kbit/sec/ch
|
||||
};
|
||||
|
||||
// Quantizer lookup, step 3: B2 table, subband -> nbal, row index
|
||||
// (upper 4 bits: nbal, lower 4 bits: row index)
|
||||
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_3[3][32] = {
|
||||
// Low-rate table (3-B.2c and 3-B.2d)
|
||||
{
|
||||
0x44,0x44,
|
||||
0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34
|
||||
},
|
||||
// High-rate table (3-B.2a and 3-B.2b)
|
||||
{
|
||||
0x43,0x43,0x43,
|
||||
0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,
|
||||
0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20
|
||||
},
|
||||
// MPEG-2 LSR table (B.2 in ISO 13818-3)
|
||||
{
|
||||
0x45,0x45,0x45,0x45,
|
||||
0x34,0x34,0x34,0x34,0x34,0x34,0x34,
|
||||
0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,
|
||||
0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24
|
||||
}
|
||||
};
|
||||
|
||||
// Quantizer lookup, step 4: table row, allocation[] value -> quant table index
|
||||
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP4[6][16] = {
|
||||
{ 0, 1, 2, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17 },
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }
|
||||
};
|
||||
|
||||
typedef struct plm_quantizer_spec_t {
|
||||
unsigned short levels;
|
||||
unsigned char group;
|
||||
unsigned char bits;
|
||||
} plm_quantizer_spec_t;
|
||||
|
||||
static const plm_quantizer_spec_t PLM_AUDIO_QUANT_TAB[] = {
|
||||
{ 3, 1, 5 }, // 1
|
||||
{ 5, 1, 7 }, // 2
|
||||
{ 7, 0, 3 }, // 3
|
||||
{ 9, 1, 10 }, // 4
|
||||
{ 15, 0, 4 }, // 5
|
||||
{ 31, 0, 5 }, // 6
|
||||
{ 63, 0, 6 }, // 7
|
||||
{ 127, 0, 7 }, // 8
|
||||
{ 255, 0, 8 }, // 9
|
||||
{ 511, 0, 9 }, // 10
|
||||
{ 1023, 0, 10 }, // 11
|
||||
{ 2047, 0, 11 }, // 12
|
||||
{ 4095, 0, 12 }, // 13
|
||||
{ 8191, 0, 13 }, // 14
|
||||
{ 16383, 0, 14 }, // 15
|
||||
{ 32767, 0, 15 }, // 16
|
||||
{ 65535, 0, 16 } // 17
|
||||
};
|
||||
|
||||
struct plm_audio_t {
|
||||
double time;
|
||||
int samples_decoded;
|
||||
int samplerate_index;
|
||||
int bitrate_index;
|
||||
int version;
|
||||
int layer;
|
||||
int mode;
|
||||
int bound;
|
||||
int v_pos;
|
||||
int next_frame_data_size;
|
||||
plm_buffer_t *buffer;
|
||||
int destroy_buffer_when_done;
|
||||
const plm_quantizer_spec_t *allocation[2][32];
|
||||
uint8_t scale_factor_info[2][32];
|
||||
int scale_factor[2][32][3];
|
||||
int sample[2][32][3];
|
||||
plm_samples_t samples;
|
||||
float D[1024];
|
||||
float V[1024];
|
||||
float U[32];
|
||||
} forcealign(64);
|
||||
|
||||
typedef plm_audio_t plm_audio_t;
|
||||
|
||||
int plm_audio_decode_header(plm_audio_t *self);
|
||||
void plm_audio_decode_frame(plm_audio_t *self);
|
||||
const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3);
|
||||
void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part);
|
||||
void plm_audio_matrix_transform(int s[32][3], int ss, float *d, int dp);
|
||||
|
||||
plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) {
|
||||
plm_audio_t *self = (plm_audio_t *)memalign(_Alignof(plm_audio_t), sizeof(plm_audio_t));
|
||||
memset(self, 0, sizeof(plm_audio_t));
|
||||
|
||||
self->samples.count = PLM_AUDIO_SAMPLES_PER_FRAME;
|
||||
self->buffer = buffer;
|
||||
self->destroy_buffer_when_done = destroy_when_done;
|
||||
self->samplerate_index = 3; // indicates 0 samplerate
|
||||
|
||||
memcpy(self->D, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float));
|
||||
memcpy(self->D + 512, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float));
|
||||
|
||||
// Decode first header
|
||||
if (plm_buffer_has(self->buffer, 48)) {
|
||||
self->next_frame_data_size = plm_audio_decode_header(self);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void plm_audio_destroy(plm_audio_t *self) {
|
||||
if (self->destroy_buffer_when_done) {
|
||||
plm_buffer_destroy(self->buffer);
|
||||
}
|
||||
free(self);
|
||||
}
|
||||
|
||||
int plm_audio_get_samplerate(plm_audio_t *self) {
|
||||
return PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
|
||||
}
|
||||
|
||||
double plm_audio_get_time(plm_audio_t *self) {
|
||||
return self->time;
|
||||
}
|
||||
|
||||
void plm_audio_rewind(plm_audio_t *self) {
|
||||
plm_buffer_rewind(self->buffer);
|
||||
self->time = 0;
|
||||
self->samples_decoded = 0;
|
||||
self->next_frame_data_size = 0;
|
||||
|
||||
// TODO: needed?
|
||||
memset(self->V, 0, sizeof(self->V));
|
||||
memset(self->U, 0, sizeof(self->U));
|
||||
}
|
||||
|
||||
plm_samples_t *plm_audio_decode(plm_audio_t *self) {
|
||||
DEBUGF("%s", "plm_audio_decode");
|
||||
// Do we have at least enough information to decode the frame header?
|
||||
if (!self->next_frame_data_size) {
|
||||
if (!plm_buffer_has(self->buffer, 48)) {
|
||||
return NULL;
|
||||
}
|
||||
self->next_frame_data_size = plm_audio_decode_header(self);
|
||||
}
|
||||
|
||||
if (
|
||||
self->next_frame_data_size == 0 ||
|
||||
!plm_buffer_has(self->buffer, self->next_frame_data_size << 3)
|
||||
) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
plm_audio_decode_frame(self);
|
||||
self->next_frame_data_size = 0;
|
||||
|
||||
self->samples.time = self->time;
|
||||
|
||||
self->samples_decoded += PLM_AUDIO_SAMPLES_PER_FRAME;
|
||||
self->time = (double)self->samples_decoded /
|
||||
(double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
|
||||
|
||||
return &self->samples;
|
||||
}
|
||||
|
||||
int plm_audio_decode_header(plm_audio_t *self) {
|
||||
// Check for valid header: syncword OK, MPEG-Audio Layer 2
|
||||
plm_buffer_skip_bytes(self->buffer, 0x00);
|
||||
|
||||
int sync = plm_buffer_read(self->buffer, 11);
|
||||
self->version = plm_buffer_read(self->buffer, 2);
|
||||
self->layer = plm_buffer_read(self->buffer, 2);
|
||||
int hasCRC = !plm_buffer_read(self->buffer, 1);
|
||||
|
||||
if (
|
||||
sync != PLM_AUDIO_FRAME_SYNC ||
|
||||
self->version != PLM_AUDIO_MPEG_1 ||
|
||||
self->layer != PLM_AUDIO_LAYER_II
|
||||
) {
|
||||
return false; // Invalid header or unsupported version
|
||||
}
|
||||
|
||||
self->bitrate_index = plm_buffer_read(self->buffer, 4) - 1;
|
||||
if (self->bitrate_index > 13) {
|
||||
return false; // Invalid bit rate or 'free format'
|
||||
}
|
||||
|
||||
self->samplerate_index = plm_buffer_read(self->buffer, 2);
|
||||
if (self->samplerate_index == 3) {
|
||||
return false; // Invalid sample rate
|
||||
}
|
||||
|
||||
if (self->version == PLM_AUDIO_MPEG_2) {
|
||||
self->samplerate_index += 4;
|
||||
self->bitrate_index += 14;
|
||||
}
|
||||
int padding = plm_buffer_read(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 1); // f_private
|
||||
self->mode = plm_buffer_read(self->buffer, 2);
|
||||
|
||||
// Parse the mode_extension, set up the stereo bound
|
||||
self->bound = 0;
|
||||
if (self->mode == PLM_AUDIO_MODE_JOINT_STEREO) {
|
||||
self->bound = (plm_buffer_read(self->buffer, 2) + 1) << 2;
|
||||
}
|
||||
else {
|
||||
plm_buffer_skip(self->buffer, 2);
|
||||
self->bound = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 32;
|
||||
}
|
||||
|
||||
// Discard the last 4 bits of the header and the CRC value, if present
|
||||
plm_buffer_skip(self->buffer, 4);
|
||||
if (hasCRC) {
|
||||
plm_buffer_skip(self->buffer, 16);
|
||||
}
|
||||
|
||||
// Compute frame size, check if we have enough data to decode the whole
|
||||
// frame.
|
||||
int bitrate = PLM_AUDIO_BIT_RATE[self->bitrate_index];
|
||||
int samplerate = PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
|
||||
int frame_size = (144000 * bitrate / samplerate) + padding;
|
||||
return frame_size - (hasCRC ? 6 : 4);
|
||||
}
|
||||
|
||||
void plm_audio_decode_frame(plm_audio_t *self) {
|
||||
// Prepare the quantizer table lookups
|
||||
int tab3 = 0;
|
||||
int sblimit = 0;
|
||||
if (self->version == PLM_AUDIO_MPEG_2) {
|
||||
// MPEG-2 (LSR)
|
||||
tab3 = 2;
|
||||
sblimit = 30;
|
||||
}
|
||||
else {
|
||||
// MPEG-1
|
||||
int tab1 = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 1;
|
||||
int tab2 = PLM_AUDIO_QUANT_LUT_STEP_1[tab1][self->bitrate_index];
|
||||
tab3 = QUANT_LUT_STEP_2[tab2][self->samplerate_index];
|
||||
sblimit = tab3 & 63;
|
||||
tab3 >>= 6;
|
||||
}
|
||||
|
||||
if (self->bound > sblimit) {
|
||||
self->bound = sblimit;
|
||||
}
|
||||
|
||||
// Read the allocation information
|
||||
for (int sb = 0; sb < self->bound; sb++) {
|
||||
self->allocation[0][sb] = plm_audio_read_allocation(self, sb, tab3);
|
||||
self->allocation[1][sb] = plm_audio_read_allocation(self, sb, tab3);
|
||||
}
|
||||
|
||||
for (int sb = self->bound; sb < sblimit; sb++) {
|
||||
self->allocation[0][sb] =
|
||||
self->allocation[1][sb] =
|
||||
plm_audio_read_allocation(self, sb, tab3);
|
||||
}
|
||||
|
||||
// Read scale factor selector information
|
||||
int channels = (self->mode == PLM_AUDIO_MODE_MONO) ? 1 : 2;
|
||||
for (int sb = 0; sb < sblimit; sb++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
if (self->allocation[ch][sb]) {
|
||||
self->scale_factor_info[ch][sb] = plm_buffer_read(self->buffer, 2);
|
||||
}
|
||||
}
|
||||
if (self->mode == PLM_AUDIO_MODE_MONO) {
|
||||
self->scale_factor_info[1][sb] = self->scale_factor_info[0][sb];
|
||||
}
|
||||
}
|
||||
|
||||
// Read scale factors
|
||||
for (int sb = 0; sb < sblimit; sb++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
if (self->allocation[ch][sb]) {
|
||||
int *sf = self->scale_factor[ch][sb];
|
||||
switch (self->scale_factor_info[ch][sb]) {
|
||||
case 0:
|
||||
sf[0] = plm_buffer_read(self->buffer, 6);
|
||||
sf[1] = plm_buffer_read(self->buffer, 6);
|
||||
sf[2] = plm_buffer_read(self->buffer, 6);
|
||||
break;
|
||||
case 1:
|
||||
sf[0] =
|
||||
sf[1] = plm_buffer_read(self->buffer, 6);
|
||||
sf[2] = plm_buffer_read(self->buffer, 6);
|
||||
break;
|
||||
case 2:
|
||||
sf[0] =
|
||||
sf[1] =
|
||||
sf[2] = plm_buffer_read(self->buffer, 6);
|
||||
break;
|
||||
case 3:
|
||||
sf[0] = plm_buffer_read(self->buffer, 6);
|
||||
sf[1] =
|
||||
sf[2] = plm_buffer_read(self->buffer, 6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (self->mode == PLM_AUDIO_MODE_MONO) {
|
||||
self->scale_factor[1][sb][0] = self->scale_factor[0][sb][0];
|
||||
self->scale_factor[1][sb][1] = self->scale_factor[0][sb][1];
|
||||
self->scale_factor[1][sb][2] = self->scale_factor[0][sb][2];
|
||||
}
|
||||
}
|
||||
|
||||
// Coefficient input and reconstruction
|
||||
int out_pos = 0;
|
||||
for (int part = 0; part < 3; part++) {
|
||||
for (int granule = 0; granule < 4; granule++) {
|
||||
|
||||
// Read the samples
|
||||
for (int sb = 0; sb < self->bound; sb++) {
|
||||
plm_audio_read_samples(self, 0, sb, part);
|
||||
plm_audio_read_samples(self, 1, sb, part);
|
||||
}
|
||||
for (int sb = self->bound; sb < sblimit; sb++) {
|
||||
plm_audio_read_samples(self, 0, sb, part);
|
||||
self->sample[1][sb][0] = self->sample[0][sb][0];
|
||||
self->sample[1][sb][1] = self->sample[0][sb][1];
|
||||
self->sample[1][sb][2] = self->sample[0][sb][2];
|
||||
}
|
||||
for (int sb = sblimit; sb < 32; sb++) {
|
||||
self->sample[0][sb][0] = 0;
|
||||
self->sample[0][sb][1] = 0;
|
||||
self->sample[0][sb][2] = 0;
|
||||
self->sample[1][sb][0] = 0;
|
||||
self->sample[1][sb][1] = 0;
|
||||
self->sample[1][sb][2] = 0;
|
||||
}
|
||||
|
||||
// Synthesis loop
|
||||
for (int p = 0; p < 3; p++) {
|
||||
// Shifting step
|
||||
self->v_pos = (self->v_pos - 64) & 1023;
|
||||
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
plm_audio_matrix_transform(self->sample[ch], p, self->V, self->v_pos);
|
||||
|
||||
// Build U, windowing, calculate output
|
||||
memset(self->U, 0, sizeof(self->U));
|
||||
|
||||
int d_index = 512 - (self->v_pos >> 1);
|
||||
int v_index = (self->v_pos % 128) >> 1;
|
||||
while (v_index < 1024) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
self->U[i] += self->D[d_index++] * self->V[v_index++];
|
||||
}
|
||||
|
||||
v_index += 128 - 32;
|
||||
d_index += 64 - 32;
|
||||
}
|
||||
|
||||
d_index -= (512 - 32);
|
||||
v_index = (128 - 32 + 1024) - v_index;
|
||||
while (v_index < 1024) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
self->U[i] += self->D[d_index++] * self->V[v_index++];
|
||||
}
|
||||
|
||||
v_index += 128 - 32;
|
||||
d_index += 64 - 32;
|
||||
}
|
||||
|
||||
// Output samples
|
||||
#ifdef PLM_AUDIO_SEPARATE_CHANNELS
|
||||
float *out_channel = ch == 0
|
||||
? self->samples.left
|
||||
: self->samples.right;
|
||||
for (int j = 0; j < 32; j++) {
|
||||
out_channel[out_pos + j] = self->U[j] / 2147418112.0f;
|
||||
}
|
||||
#else
|
||||
for (int j = 0; j < 32; j++) {
|
||||
self->samples.interleaved[((out_pos + j) << 1) + ch] =
|
||||
self->U[j] / 2147418112.0f;
|
||||
}
|
||||
#endif
|
||||
} // End of synthesis channel loop
|
||||
out_pos += 32;
|
||||
} // End of synthesis sub-block loop
|
||||
|
||||
} // Decoding of the granule finished
|
||||
}
|
||||
|
||||
plm_buffer_align(self->buffer);
|
||||
}
|
||||
|
||||
const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3) {
|
||||
int tab4 = PLM_AUDIO_QUANT_LUT_STEP_3[tab3][sb];
|
||||
int qtab = PLM_AUDIO_QUANT_LUT_STEP4[tab4 & 15][plm_buffer_read(self->buffer, tab4 >> 4)];
|
||||
return qtab ? (&PLM_AUDIO_QUANT_TAB[qtab - 1]) : 0;
|
||||
}
|
||||
|
||||
void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part) {
|
||||
const plm_quantizer_spec_t *q = self->allocation[ch][sb];
|
||||
int sf = self->scale_factor[ch][sb][part];
|
||||
int *sample = self->sample[ch][sb];
|
||||
int val = 0;
|
||||
|
||||
if (!q) {
|
||||
// No bits allocated for this subband
|
||||
sample[0] = sample[1] = sample[2] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve scalefactor
|
||||
if (sf == 63) {
|
||||
sf = 0;
|
||||
}
|
||||
else {
|
||||
int shift = (sf / 3) | 0;
|
||||
sf = (PLM_AUDIO_SCALEFACTOR_BASE[sf % 3] + ((1u << shift) >> 1)) >> shift;
|
||||
}
|
||||
|
||||
// Decode samples
|
||||
int adj = q->levels;
|
||||
if (q->group) {
|
||||
// Decode grouped samples
|
||||
val = plm_buffer_read(self->buffer, q->bits);
|
||||
sample[0] = val % adj;
|
||||
val /= adj;
|
||||
sample[1] = val % adj;
|
||||
sample[2] = val / adj;
|
||||
}
|
||||
else {
|
||||
// Decode direct samples
|
||||
sample[0] = plm_buffer_read(self->buffer, q->bits);
|
||||
sample[1] = plm_buffer_read(self->buffer, q->bits);
|
||||
sample[2] = plm_buffer_read(self->buffer, q->bits);
|
||||
}
|
||||
|
||||
// Postmultiply samples
|
||||
int scale = 65536 / (adj + 1);
|
||||
adj = ((adj + 1) >> 1) - 1;
|
||||
|
||||
val = (adj - sample[0]) * scale;
|
||||
sample[0] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
|
||||
|
||||
val = (adj - sample[1]) * scale;
|
||||
sample[1] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
|
||||
|
||||
val = (adj - sample[2]) * scale;
|
||||
sample[2] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
|
||||
}
|
||||
|
||||
void plm_audio_matrix_transform(int s[32][3], int ss, float *d, int dp) {
|
||||
float t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12,
|
||||
t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24,
|
||||
t25, t26, t27, t28, t29, t30, t31, t32, t33;
|
||||
|
||||
t01 = (float)(s[0][ss] + s[31][ss]); t02 = (float)(s[0][ss] - s[31][ss]) * 0.500602998235f;
|
||||
t03 = (float)(s[1][ss] + s[30][ss]); t04 = (float)(s[1][ss] - s[30][ss]) * 0.505470959898f;
|
||||
t05 = (float)(s[2][ss] + s[29][ss]); t06 = (float)(s[2][ss] - s[29][ss]) * 0.515447309923f;
|
||||
t07 = (float)(s[3][ss] + s[28][ss]); t08 = (float)(s[3][ss] - s[28][ss]) * 0.53104259109f;
|
||||
t09 = (float)(s[4][ss] + s[27][ss]); t10 = (float)(s[4][ss] - s[27][ss]) * 0.553103896034f;
|
||||
t11 = (float)(s[5][ss] + s[26][ss]); t12 = (float)(s[5][ss] - s[26][ss]) * 0.582934968206f;
|
||||
t13 = (float)(s[6][ss] + s[25][ss]); t14 = (float)(s[6][ss] - s[25][ss]) * 0.622504123036f;
|
||||
t15 = (float)(s[7][ss] + s[24][ss]); t16 = (float)(s[7][ss] - s[24][ss]) * 0.674808341455f;
|
||||
t17 = (float)(s[8][ss] + s[23][ss]); t18 = (float)(s[8][ss] - s[23][ss]) * 0.744536271002f;
|
||||
t19 = (float)(s[9][ss] + s[22][ss]); t20 = (float)(s[9][ss] - s[22][ss]) * 0.839349645416f;
|
||||
t21 = (float)(s[10][ss] + s[21][ss]); t22 = (float)(s[10][ss] - s[21][ss]) * 0.972568237862f;
|
||||
t23 = (float)(s[11][ss] + s[20][ss]); t24 = (float)(s[11][ss] - s[20][ss]) * 1.16943993343f;
|
||||
t25 = (float)(s[12][ss] + s[19][ss]); t26 = (float)(s[12][ss] - s[19][ss]) * 1.48416461631f;
|
||||
t27 = (float)(s[13][ss] + s[18][ss]); t28 = (float)(s[13][ss] - s[18][ss]) * 2.05778100995f;
|
||||
t29 = (float)(s[14][ss] + s[17][ss]); t30 = (float)(s[14][ss] - s[17][ss]) * 3.40760841847f;
|
||||
t31 = (float)(s[15][ss] + s[16][ss]); t32 = (float)(s[15][ss] - s[16][ss]) * 10.1900081235f;
|
||||
|
||||
t33 = t01 + t31; t31 = (t01 - t31) * 0.502419286188f;
|
||||
t01 = t03 + t29; t29 = (t03 - t29) * 0.52249861494f;
|
||||
t03 = t05 + t27; t27 = (t05 - t27) * 0.566944034816f;
|
||||
t05 = t07 + t25; t25 = (t07 - t25) * 0.64682178336f;
|
||||
t07 = t09 + t23; t23 = (t09 - t23) * 0.788154623451f;
|
||||
t09 = t11 + t21; t21 = (t11 - t21) * 1.06067768599f;
|
||||
t11 = t13 + t19; t19 = (t13 - t19) * 1.72244709824f;
|
||||
t13 = t15 + t17; t17 = (t15 - t17) * 5.10114861869f;
|
||||
t15 = t33 + t13; t13 = (t33 - t13) * 0.509795579104f;
|
||||
t33 = t01 + t11; t01 = (t01 - t11) * 0.601344886935f;
|
||||
t11 = t03 + t09; t09 = (t03 - t09) * 0.899976223136f;
|
||||
t03 = t05 + t07; t07 = (t05 - t07) * 2.56291544774f;
|
||||
t05 = t15 + t03; t15 = (t15 - t03) * 0.541196100146f;
|
||||
t03 = t33 + t11; t11 = (t33 - t11) * 1.30656296488f;
|
||||
t33 = t05 + t03; t05 = (t05 - t03) * 0.707106781187f;
|
||||
t03 = t15 + t11; t15 = (t15 - t11) * 0.707106781187f;
|
||||
t03 += t15;
|
||||
t11 = t13 + t07; t13 = (t13 - t07) * 0.541196100146f;
|
||||
t07 = t01 + t09; t09 = (t01 - t09) * 1.30656296488f;
|
||||
t01 = t11 + t07; t07 = (t11 - t07) * 0.707106781187f;
|
||||
t11 = t13 + t09; t13 = (t13 - t09) * 0.707106781187f;
|
||||
t11 += t13; t01 += t11;
|
||||
t11 += t07; t07 += t13;
|
||||
t09 = t31 + t17; t31 = (t31 - t17) * 0.509795579104f;
|
||||
t17 = t29 + t19; t29 = (t29 - t19) * 0.601344886935f;
|
||||
t19 = t27 + t21; t21 = (t27 - t21) * 0.899976223136f;
|
||||
t27 = t25 + t23; t23 = (t25 - t23) * 2.56291544774f;
|
||||
t25 = t09 + t27; t09 = (t09 - t27) * 0.541196100146f;
|
||||
t27 = t17 + t19; t19 = (t17 - t19) * 1.30656296488f;
|
||||
t17 = t25 + t27; t27 = (t25 - t27) * 0.707106781187f;
|
||||
t25 = t09 + t19; t19 = (t09 - t19) * 0.707106781187f;
|
||||
t25 += t19;
|
||||
t09 = t31 + t23; t31 = (t31 - t23) * 0.541196100146f;
|
||||
t23 = t29 + t21; t21 = (t29 - t21) * 1.30656296488f;
|
||||
t29 = t09 + t23; t23 = (t09 - t23) * 0.707106781187f;
|
||||
t09 = t31 + t21; t31 = (t31 - t21) * 0.707106781187f;
|
||||
t09 += t31; t29 += t09; t09 += t23; t23 += t31;
|
||||
t17 += t29; t29 += t25; t25 += t09; t09 += t27;
|
||||
t27 += t23; t23 += t19; t19 += t31;
|
||||
t21 = t02 + t32; t02 = (t02 - t32) * 0.502419286188f;
|
||||
t32 = t04 + t30; t04 = (t04 - t30) * 0.52249861494f;
|
||||
t30 = t06 + t28; t28 = (t06 - t28) * 0.566944034816f;
|
||||
t06 = t08 + t26; t08 = (t08 - t26) * 0.64682178336f;
|
||||
t26 = t10 + t24; t10 = (t10 - t24) * 0.788154623451f;
|
||||
t24 = t12 + t22; t22 = (t12 - t22) * 1.06067768599f;
|
||||
t12 = t14 + t20; t20 = (t14 - t20) * 1.72244709824f;
|
||||
t14 = t16 + t18; t16 = (t16 - t18) * 5.10114861869f;
|
||||
t18 = t21 + t14; t14 = (t21 - t14) * 0.509795579104f;
|
||||
t21 = t32 + t12; t32 = (t32 - t12) * 0.601344886935f;
|
||||
t12 = t30 + t24; t24 = (t30 - t24) * 0.899976223136f;
|
||||
t30 = t06 + t26; t26 = (t06 - t26) * 2.56291544774f;
|
||||
t06 = t18 + t30; t18 = (t18 - t30) * 0.541196100146f;
|
||||
t30 = t21 + t12; t12 = (t21 - t12) * 1.30656296488f;
|
||||
t21 = t06 + t30; t30 = (t06 - t30) * 0.707106781187f;
|
||||
t06 = t18 + t12; t12 = (t18 - t12) * 0.707106781187f;
|
||||
t06 += t12;
|
||||
t18 = t14 + t26; t26 = (t14 - t26) * 0.541196100146f;
|
||||
t14 = t32 + t24; t24 = (t32 - t24) * 1.30656296488f;
|
||||
t32 = t18 + t14; t14 = (t18 - t14) * 0.707106781187f;
|
||||
t18 = t26 + t24; t24 = (t26 - t24) * 0.707106781187f;
|
||||
t18 += t24; t32 += t18;
|
||||
t18 += t14; t26 = t14 + t24;
|
||||
t14 = t02 + t16; t02 = (t02 - t16) * 0.509795579104f;
|
||||
t16 = t04 + t20; t04 = (t04 - t20) * 0.601344886935f;
|
||||
t20 = t28 + t22; t22 = (t28 - t22) * 0.899976223136f;
|
||||
t28 = t08 + t10; t10 = (t08 - t10) * 2.56291544774f;
|
||||
t08 = t14 + t28; t14 = (t14 - t28) * 0.541196100146f;
|
||||
t28 = t16 + t20; t20 = (t16 - t20) * 1.30656296488f;
|
||||
t16 = t08 + t28; t28 = (t08 - t28) * 0.707106781187f;
|
||||
t08 = t14 + t20; t20 = (t14 - t20) * 0.707106781187f;
|
||||
t08 += t20;
|
||||
t14 = t02 + t10; t02 = (t02 - t10) * 0.541196100146f;
|
||||
t10 = t04 + t22; t22 = (t04 - t22) * 1.30656296488f;
|
||||
t04 = t14 + t10; t10 = (t14 - t10) * 0.707106781187f;
|
||||
t14 = t02 + t22; t02 = (t02 - t22) * 0.707106781187f;
|
||||
t14 += t02; t04 += t14; t14 += t10; t10 += t02;
|
||||
t16 += t04; t04 += t08; t08 += t14; t14 += t28;
|
||||
t28 += t10; t10 += t20; t20 += t02; t21 += t16;
|
||||
t16 += t32; t32 += t04; t04 += t06; t06 += t08;
|
||||
t08 += t18; t18 += t14; t14 += t30; t30 += t28;
|
||||
t28 += t26; t26 += t10; t10 += t12; t12 += t20;
|
||||
t20 += t24; t24 += t02;
|
||||
|
||||
d[dp + 48] = -t33;
|
||||
d[dp + 49] = d[dp + 47] = -t21;
|
||||
d[dp + 50] = d[dp + 46] = -t17;
|
||||
d[dp + 51] = d[dp + 45] = -t16;
|
||||
d[dp + 52] = d[dp + 44] = -t01;
|
||||
d[dp + 53] = d[dp + 43] = -t32;
|
||||
d[dp + 54] = d[dp + 42] = -t29;
|
||||
d[dp + 55] = d[dp + 41] = -t04;
|
||||
d[dp + 56] = d[dp + 40] = -t03;
|
||||
d[dp + 57] = d[dp + 39] = -t06;
|
||||
d[dp + 58] = d[dp + 38] = -t25;
|
||||
d[dp + 59] = d[dp + 37] = -t08;
|
||||
d[dp + 60] = d[dp + 36] = -t11;
|
||||
d[dp + 61] = d[dp + 35] = -t18;
|
||||
d[dp + 62] = d[dp + 34] = -t09;
|
||||
d[dp + 63] = d[dp + 33] = -t14;
|
||||
d[dp + 32] = -t05;
|
||||
d[dp + 0] = t05; d[dp + 31] = -t30;
|
||||
d[dp + 1] = t30; d[dp + 30] = -t27;
|
||||
d[dp + 2] = t27; d[dp + 29] = -t28;
|
||||
d[dp + 3] = t28; d[dp + 28] = -t07;
|
||||
d[dp + 4] = t07; d[dp + 27] = -t26;
|
||||
d[dp + 5] = t26; d[dp + 26] = -t23;
|
||||
d[dp + 6] = t23; d[dp + 25] = -t10;
|
||||
d[dp + 7] = t10; d[dp + 24] = -t15;
|
||||
d[dp + 8] = t15; d[dp + 23] = -t12;
|
||||
d[dp + 9] = t12; d[dp + 22] = -t19;
|
||||
d[dp + 10] = t19; d[dp + 21] = -t20;
|
||||
d[dp + 11] = t20; d[dp + 20] = -t13;
|
||||
d[dp + 12] = t13; d[dp + 19] = -t24;
|
||||
d[dp + 13] = t24; d[dp + 18] = -t31;
|
||||
d[dp + 14] = t31; d[dp + 17] = -t02;
|
||||
d[dp + 15] = t02; d[dp + 16] = 0.0;
|
||||
};
|
||||
|
447
dsp/mpeg/mpeg.h
447
dsp/mpeg/mpeg.h
|
@ -1,447 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_DSP_MPEG_MPEG_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_MPEG_H_
|
||||
#include "libc/stdio/stdio.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
typedef struct plm_t plm_t;
|
||||
typedef struct plm_buffer_t plm_buffer_t;
|
||||
typedef struct plm_demux_t plm_demux_t;
|
||||
typedef struct plm_video_t plm_video_t;
|
||||
typedef struct plm_audio_t plm_audio_t;
|
||||
|
||||
/**
|
||||
* Demuxed MPEG PS packet
|
||||
*
|
||||
* The type maps directly to the various MPEG-PES start codes. pts is
|
||||
* the presentation time stamp of the packet in seconds. Not all packets
|
||||
* have a pts value.
|
||||
*/
|
||||
typedef struct plm_packet_t {
|
||||
int type;
|
||||
double pts;
|
||||
size_t length;
|
||||
uint8_t *data;
|
||||
} plm_packet_t;
|
||||
|
||||
/**
|
||||
* Decoded Video Plane
|
||||
*
|
||||
* The byte length of the data is width * height. Note that different
|
||||
* planes have different sizes: the Luma plane (Y) is double the size of
|
||||
* each of the two Chroma planes (Cr, Cb) - i.e. 4 times the byte
|
||||
* length. Also note that the size of the plane does *not* denote the
|
||||
* size of the displayed frame. The sizes of planes are always rounded
|
||||
* up to the nearest macroblock (16px).
|
||||
*/
|
||||
typedef struct plm_plane_t {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
uint8_t *data;
|
||||
} plm_plane_t;
|
||||
|
||||
/**
|
||||
* Decoded Video Frame
|
||||
*
|
||||
* Width and height denote the desired display size of the frame. This
|
||||
* may be different from the internal size of the 3 planes.
|
||||
*/
|
||||
typedef struct plm_frame_t {
|
||||
double time;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
plm_plane_t y;
|
||||
plm_plane_t cr;
|
||||
plm_plane_t cb;
|
||||
} plm_frame_t;
|
||||
|
||||
/**
|
||||
* Callback function type for decoded video frames used by the high-level
|
||||
* plm_* interface
|
||||
*/
|
||||
typedef void (*plm_video_decode_callback)(plm_t *self, plm_frame_t *frame,
|
||||
void *user);
|
||||
|
||||
/**
|
||||
* Decoded Audio Samples
|
||||
*
|
||||
* Samples are stored as normalized (-1, 1) float either interleaved, or if
|
||||
* PLM_AUDIO_SEPARATE_CHANNELS is defined, in two separate arrays.
|
||||
* The `count` is always PLM_AUDIO_SAMPLES_PER_FRAME and just there for
|
||||
* convenience.
|
||||
*/
|
||||
#define PLM_AUDIO_SAMPLES_PER_FRAME 1152
|
||||
|
||||
struct plm_samples_t {
|
||||
double time;
|
||||
unsigned int count;
|
||||
#ifdef PLM_AUDIO_SEPARATE_CHANNELS
|
||||
float left[PLM_AUDIO_SAMPLES_PER_FRAME] forcealign(32);
|
||||
float right[PLM_AUDIO_SAMPLES_PER_FRAME] forcealign(32);
|
||||
#else
|
||||
float interleaved[PLM_AUDIO_SAMPLES_PER_FRAME * 2] forcealign(32);
|
||||
#endif
|
||||
} forcealign(32);
|
||||
|
||||
typedef struct plm_samples_t plm_samples_t;
|
||||
|
||||
/**
|
||||
* Callback function type for decoded audio samples used by the high-level
|
||||
* plm_* interface
|
||||
*/
|
||||
typedef void (*plm_audio_decode_callback)(plm_t *self, plm_samples_t *samples,
|
||||
void *user);
|
||||
|
||||
/**
|
||||
* Callback function for plm_buffer when it needs more data
|
||||
*/
|
||||
typedef void (*plm_buffer_load_callback)(plm_buffer_t *self, void *user);
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------------------------------
|
||||
* plm_* public API
|
||||
* High-Level API for loading/demuxing/decoding MPEG-PS data
|
||||
*
|
||||
* Create a plmpeg instance with a filename. Returns NULL if the file could not
|
||||
* be opened.
|
||||
*/
|
||||
plm_t *plm_create_with_filename(const char *filename);
|
||||
|
||||
/**
|
||||
* Create a plmpeg instance with file handle. Pass true to close_when_done
|
||||
* to let plmpeg call fclose() on the handle when plm_destroy() is
|
||||
* called.
|
||||
*/
|
||||
plm_t *plm_create_with_file(FILE *fh, int close_when_done);
|
||||
|
||||
/**
|
||||
* Create a plmpeg instance with pointer to memory as source. This assumes the
|
||||
* whole file is in memory. Pass true to free_when_done to let plmpeg call
|
||||
* free() on the pointer when plm_destroy() is called.
|
||||
*/
|
||||
plm_t *plm_create_with_memory(uint8_t *bytes, size_t length,
|
||||
int free_when_done);
|
||||
|
||||
/**
|
||||
* Create a plmpeg instance with a plm_buffer as source. This is also
|
||||
* called internally by all the above constructor functions.
|
||||
*/
|
||||
plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done);
|
||||
|
||||
/**
|
||||
* Destroy a plmpeg instance and free all data
|
||||
*/
|
||||
void plm_destroy(plm_t *self);
|
||||
|
||||
/**
|
||||
* Get or set whether video decoding is enabled.
|
||||
*/
|
||||
int plm_get_video_enabled(plm_t *self);
|
||||
void plm_set_video_enabled(plm_t *self, int enabled);
|
||||
|
||||
/**
|
||||
* Get or set whether audio decoding is enabled. When enabling, you can set the
|
||||
* desired audio stream (0-3) to decode.
|
||||
*/
|
||||
int plm_get_audio_enabled(plm_t *self);
|
||||
void plm_set_audio_enabled(plm_t *self, int enabled, int stream_index);
|
||||
|
||||
/**
|
||||
* Get the display width/height of the video stream
|
||||
*/
|
||||
int plm_get_width(plm_t *self);
|
||||
int plm_get_height(plm_t *self);
|
||||
|
||||
double plm_get_pixel_aspect_ratio(plm_t *);
|
||||
|
||||
/**
|
||||
* Get the framerate of the video stream in frames per second
|
||||
*/
|
||||
double plm_get_framerate(plm_t *self);
|
||||
|
||||
/**
|
||||
* Get the number of available audio streams in the file
|
||||
*/
|
||||
int plm_get_num_audio_streams(plm_t *self);
|
||||
|
||||
/**
|
||||
* Get the samplerate of the audio stream in samples per second
|
||||
*/
|
||||
int plm_get_samplerate(plm_t *self);
|
||||
|
||||
/**
|
||||
* Get or set the audio lead time in seconds - the time in which audio samples
|
||||
* are decoded in advance (or behind) the video decode time. Default 0.
|
||||
*/
|
||||
double plm_get_audio_lead_time(plm_t *self);
|
||||
void plm_set_audio_lead_time(plm_t *self, double lead_time);
|
||||
|
||||
/**
|
||||
* Get the current internal time in seconds
|
||||
*/
|
||||
double plm_get_time(plm_t *self);
|
||||
|
||||
/**
|
||||
* Rewind all buffers back to the beginning.
|
||||
*/
|
||||
void plm_rewind(plm_t *self);
|
||||
|
||||
/**
|
||||
* Get or set looping. Default false.
|
||||
*/
|
||||
int plm_get_loop(plm_t *self);
|
||||
void plm_set_loop(plm_t *self, int loop);
|
||||
|
||||
/**
|
||||
* Get whether the file has ended. If looping is enabled, this will always
|
||||
* return false.
|
||||
*/
|
||||
int plm_has_ended(plm_t *self);
|
||||
|
||||
/**
|
||||
* Set the callback for decoded video frames used with plm_decode(). If no
|
||||
* callback is set, video data will be ignored and not be decoded.
|
||||
*/
|
||||
void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp,
|
||||
void *user);
|
||||
|
||||
/**
|
||||
* Set the callback for decoded audio samples used with plm_decode(). If no
|
||||
* callback is set, audio data will be ignored and not be decoded.
|
||||
*/
|
||||
void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp,
|
||||
void *user);
|
||||
|
||||
/**
|
||||
* Advance the internal timer by seconds and decode video/audio up to
|
||||
* this time. Returns true/false whether anything was decoded.
|
||||
*/
|
||||
int plm_decode(plm_t *self, double seconds);
|
||||
|
||||
/**
|
||||
* Decode and return one video frame. Returns NULL if no frame could be decoded
|
||||
* (either because the source ended or data is corrupt). If you only want to
|
||||
* decode video, you should disable audio via plm_set_audio_enabled().
|
||||
* The returned plm_frame_t is valid until the next call to
|
||||
* plm_decode_video call or until the plm_destroy is called.
|
||||
*/
|
||||
plm_frame_t *plm_decode_video(plm_t *self);
|
||||
|
||||
/**
|
||||
* Decode and return one audio frame. Returns NULL if no frame could be decoded
|
||||
* (either because the source ended or data is corrupt). If you only want to
|
||||
* decode audio, you should disable video via plm_set_video_enabled().
|
||||
* The returned plm_samples_t is valid until the next call to
|
||||
* plm_decode_video or until the plm_destroy is called.
|
||||
*/
|
||||
plm_samples_t *plm_decode_audio(plm_t *self);
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* plm_buffer public API
|
||||
* Provides the data source for all other plm_* interfaces
|
||||
*
|
||||
* The default size for buffers created from files or by the high-level API
|
||||
*/
|
||||
#ifndef PLM_BUFFER_DEFAULT_SIZE
|
||||
#define PLM_BUFFER_DEFAULT_SIZE (128 * 1024)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Create a buffer instance with a filename. Returns NULL if the file could not
|
||||
* be opened.
|
||||
*/
|
||||
plm_buffer_t *plm_buffer_create_with_filename(const char *filename);
|
||||
|
||||
/**
|
||||
* Create a buffer instance with file handle. Pass true to close_when_done
|
||||
* to let plmpeg call fclose() on the handle when plm_destroy() is
|
||||
* called.
|
||||
*/
|
||||
plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done);
|
||||
|
||||
/**
|
||||
* Create a buffer instance with a pointer to memory as source. This assumes
|
||||
* the whole file is in memory. Pass 1 to free_when_done to let plmpeg call
|
||||
* free() on the pointer when plm_destroy() is called.
|
||||
*/
|
||||
plm_buffer_t *plm_buffer_create_with_memory(uint8_t *bytes, size_t length,
|
||||
int free_when_done);
|
||||
|
||||
/**
|
||||
* Create an empty buffer with an initial capacity. The buffer will grow
|
||||
* as needed.
|
||||
*/
|
||||
plm_buffer_t *plm_buffer_create_with_capacity(size_t capacity);
|
||||
|
||||
/**
|
||||
* Destroy a buffer instance and free all data
|
||||
*/
|
||||
void plm_buffer_destroy(plm_buffer_t *self);
|
||||
|
||||
/**
|
||||
* Copy data into the buffer. If the data to be written is larger than the
|
||||
* available space, the buffer will realloc() with a larger capacity.
|
||||
* Returns the number of bytes written. This will always be the same as the
|
||||
* passed in length, except when the buffer was created _with_memory() for
|
||||
* which _write() is forbidden.
|
||||
*/
|
||||
size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length);
|
||||
|
||||
/**
|
||||
* Set a callback that is called whenever the buffer needs more data
|
||||
*/
|
||||
void plm_buffer_set_load_callback(plm_buffer_t *self,
|
||||
plm_buffer_load_callback fp, void *user);
|
||||
|
||||
/**
|
||||
* Rewind the buffer back to the beginning. When loading from a file handle,
|
||||
* this also seeks to the beginning of the file.
|
||||
*/
|
||||
void plm_buffer_rewind(plm_buffer_t *self);
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------------------------------
|
||||
* plm_demux public API
|
||||
* Demux an MPEG Program Stream (PS) data into separate packages
|
||||
*
|
||||
* Various Packet Types
|
||||
*/
|
||||
#define PLM_DEMUX_PACKET_PRIVATE 0xBD
|
||||
#define PLM_DEMUX_PACKET_AUDIO_1 0xC0
|
||||
#define PLM_DEMUX_PACKET_AUDIO_2 0xC1
|
||||
#define PLM_DEMUX_PACKET_AUDIO_3 0xC2
|
||||
#define PLM_DEMUX_PACKET_AUDIO_4 0xC2
|
||||
#define PLM_DEMUX_PACKET_VIDEO_1 0xE0
|
||||
|
||||
/**
|
||||
* Create a demuxer with a plm_buffer as source
|
||||
*/
|
||||
plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done);
|
||||
|
||||
/**
|
||||
* Destroy a demuxer and free all data
|
||||
*/
|
||||
void plm_demux_destroy(plm_demux_t *self);
|
||||
|
||||
/**
|
||||
* Returns the number of video streams found in the system header.
|
||||
*/
|
||||
int plm_demux_get_num_video_streams(plm_demux_t *self);
|
||||
|
||||
/**
|
||||
* Returns the number of audio streams found in the system header.
|
||||
*/
|
||||
int plm_demux_get_num_audio_streams(plm_demux_t *self);
|
||||
|
||||
/**
|
||||
* Rewinds the internal buffer. See plm_buffer_rewind().
|
||||
*/
|
||||
void plm_demux_rewind(plm_demux_t *self);
|
||||
|
||||
/**
|
||||
* Decode and return the next packet. The returned packet_t is valid until
|
||||
* the next call to plm_demux_decode() or until the demuxer is destroyed.
|
||||
*/
|
||||
plm_packet_t *plm_demux_decode(plm_demux_t *self);
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* plm_video public API
|
||||
* Decode MPEG1 Video ("mpeg1") data into raw YCrCb frames
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a video decoder with a plm_buffer as source
|
||||
*/
|
||||
plm_video_t *plm_video_create_with_buffer(plm_buffer_t *buffer,
|
||||
int destroy_when_done);
|
||||
|
||||
/**
|
||||
* Destroy a video decoder and free all data
|
||||
*/
|
||||
void plm_video_destroy(plm_video_t *self);
|
||||
|
||||
/**
|
||||
* Get the framerate in frames per second
|
||||
*/
|
||||
double plm_video_get_framerate(plm_video_t *);
|
||||
|
||||
double plm_video_get_pixel_aspect_ratio(plm_video_t *);
|
||||
|
||||
/**
|
||||
* Get the display width/height
|
||||
*/
|
||||
int plm_video_get_width(plm_video_t *);
|
||||
int plm_video_get_height(plm_video_t *);
|
||||
|
||||
/**
|
||||
* Set "no delay" mode. When enabled, the decoder assumes that the video does
|
||||
* *not* contain any B-Frames. This is useful for reducing lag when streaming.
|
||||
*/
|
||||
void plm_video_set_no_delay(plm_video_t *self, int no_delay);
|
||||
|
||||
/**
|
||||
* Get the current internal time in seconds
|
||||
*/
|
||||
double plm_video_get_time(plm_video_t *self);
|
||||
|
||||
/**
|
||||
* Rewinds the internal buffer. See plm_buffer_rewind().
|
||||
*/
|
||||
void plm_video_rewind(plm_video_t *self);
|
||||
|
||||
/**
|
||||
* Decode and return one frame of video and advance the internal time by
|
||||
* 1/framerate seconds. The returned frame_t is valid until the next call of
|
||||
* plm_video_decode() or until the video decoder is destroyed.
|
||||
*/
|
||||
plm_frame_t *plm_video_decode(plm_video_t *self);
|
||||
|
||||
/**
|
||||
* Convert the YCrCb data of a frame into an interleaved RGB buffer. The buffer
|
||||
* pointed to by *rgb must have a size of (frame->width * frame->height * 3)
|
||||
* bytes.
|
||||
*/
|
||||
void plm_frame_to_rgb(plm_frame_t *frame, uint8_t *rgb);
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* plm_audio public API
|
||||
* Decode MPEG-1 Audio Layer II ("mp2") data into raw samples
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create an audio decoder with a plm_buffer as source.
|
||||
*/
|
||||
plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer,
|
||||
int destroy_when_done);
|
||||
|
||||
/**
|
||||
* Destroy an audio decoder and free all data
|
||||
*/
|
||||
void plm_audio_destroy(plm_audio_t *self);
|
||||
|
||||
/**
|
||||
* Get the samplerate in samples per second
|
||||
*/
|
||||
int plm_audio_get_samplerate(plm_audio_t *self);
|
||||
|
||||
/**
|
||||
* Get the current internal time in seconds
|
||||
*/
|
||||
double plm_audio_get_time(plm_audio_t *self);
|
||||
|
||||
/**
|
||||
* Rewinds the internal buffer. See plm_buffer_rewind().
|
||||
*/
|
||||
void plm_audio_rewind(plm_audio_t *self);
|
||||
|
||||
/**
|
||||
* Decode and return one "frame" of audio and advance the internal time by
|
||||
* (PLM_AUDIO_SAMPLES_PER_FRAME/samplerate) seconds. The returned samples_t
|
||||
* is valid until the next call of plm_audio_decode() or until the audio
|
||||
* decoder is destroyed.
|
||||
*/
|
||||
plm_samples_t *plm_audio_decode(plm_audio_t *self);
|
||||
|
||||
extern long plmpegdecode_latency_;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_MPEG_H_ */
|
1110
dsp/mpeg/mpeg1.c
1110
dsp/mpeg/mpeg1.c
File diff suppressed because it is too large
Load diff
|
@ -2,3 +2,8 @@ __notice(pl_mpeg_notice, "\
|
|||
PL_MPEG (MIT License)\n\
|
||||
Copyright(c) 2019 Dominic Szablewski\n\
|
||||
https://phoboslab.org");
|
||||
|
||||
long plmpegdecode_latency_;
|
||||
|
||||
#define PL_MPEG_IMPLEMENTATION
|
||||
#include "pl_mpeg.h"
|
4379
dsp/mpeg/pl_mpeg.h
Executable file
4379
dsp/mpeg/pl_mpeg.h
Executable file
File diff suppressed because it is too large
Load diff
332
dsp/mpeg/plm.c
332
dsp/mpeg/plm.c
|
@ -1,332 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set noet 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/mpeg.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
__static_yoink("pl_mpeg_notice");
|
||||
|
||||
/* clang-format off */
|
||||
// -----------------------------------------------------------------------------
|
||||
// plm (high-level interface) implementation
|
||||
|
||||
typedef struct plm_t {
|
||||
plm_demux_t *demux;
|
||||
double time;
|
||||
int has_ended;
|
||||
int loop;
|
||||
|
||||
int video_packet_type;
|
||||
plm_buffer_t *video_buffer;
|
||||
plm_video_t *video_decoder;
|
||||
|
||||
int audio_packet_type;
|
||||
double audio_lead_time;
|
||||
plm_buffer_t *audio_buffer;
|
||||
plm_audio_t *audio_decoder;
|
||||
|
||||
plm_video_decode_callback video_decode_callback;
|
||||
void *video_decode_callback_user_data;
|
||||
|
||||
plm_audio_decode_callback audio_decode_callback;
|
||||
void *audio_decode_callback_user_data;
|
||||
} plm_t;
|
||||
|
||||
void plm_handle_end(plm_t *self);
|
||||
void plm_read_video_packet(plm_buffer_t *buffer, void *user);
|
||||
void plm_read_audio_packet(plm_buffer_t *buffer, void *user);
|
||||
void plm_read_packets(plm_t *self, int requested_type);
|
||||
|
||||
plm_t *plm_create_with_filename(const char *filename) {
|
||||
plm_buffer_t *buffer = plm_buffer_create_with_filename(filename);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
return plm_create_with_buffer(buffer, true);
|
||||
}
|
||||
|
||||
plm_t *plm_create_with_file(FILE *fh, int close_when_done) {
|
||||
plm_buffer_t *buffer = plm_buffer_create_with_file(fh, close_when_done);
|
||||
return plm_create_with_buffer(buffer, true);
|
||||
}
|
||||
|
||||
plm_t *plm_create_with_memory(uint8_t *bytes, size_t length, int free_when_done) {
|
||||
plm_buffer_t *buffer = plm_buffer_create_with_memory(bytes, length, free_when_done);
|
||||
return plm_create_with_buffer(buffer, true);
|
||||
}
|
||||
|
||||
plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) {
|
||||
plm_t *self = (plm_t *)malloc(sizeof(plm_t));
|
||||
memset(self, 0, sizeof(plm_t));
|
||||
|
||||
self->demux = plm_demux_create(buffer, destroy_when_done);
|
||||
|
||||
// In theory we should check plm_demux_get_num_video_streams() and
|
||||
// plm_demux_get_num_audio_streams() here, but older files typically
|
||||
// do not specify these correctly. So we just assume we have a video and
|
||||
// audio stream and create the decoders.
|
||||
|
||||
self->video_packet_type = PLM_DEMUX_PACKET_VIDEO_1;
|
||||
self->video_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
|
||||
plm_buffer_set_load_callback(self->video_buffer, plm_read_video_packet, self);
|
||||
|
||||
self->audio_packet_type = PLM_DEMUX_PACKET_AUDIO_1;
|
||||
self->audio_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
|
||||
plm_buffer_set_load_callback(self->audio_buffer, plm_read_audio_packet, self);
|
||||
|
||||
self->video_decoder = plm_video_create_with_buffer(self->video_buffer, true);
|
||||
self->audio_decoder = plm_audio_create_with_buffer(self->audio_buffer, true);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void plm_destroy(plm_t *self) {
|
||||
plm_video_destroy(self->video_decoder);
|
||||
plm_audio_destroy(self->audio_decoder);
|
||||
plm_demux_destroy(self->demux);
|
||||
free(self);
|
||||
}
|
||||
|
||||
int plm_get_audio_enabled(plm_t *self) {
|
||||
return (self->audio_packet_type != 0);
|
||||
}
|
||||
|
||||
void plm_set_audio_enabled(plm_t *self, int enabled, int stream_index) {
|
||||
/* int num_streams = plm_demux_get_num_audio_streams(self->demux); */
|
||||
self->audio_packet_type = (enabled && stream_index >= 0 && stream_index < 4)
|
||||
? PLM_DEMUX_PACKET_AUDIO_1 + stream_index
|
||||
: 0;
|
||||
}
|
||||
|
||||
int plm_get_video_enabled(plm_t *self) {
|
||||
return (self->video_packet_type != 0);
|
||||
}
|
||||
|
||||
void plm_set_video_enabled(plm_t *self, int enabled) {
|
||||
self->video_packet_type = (enabled)
|
||||
? PLM_DEMUX_PACKET_VIDEO_1
|
||||
: 0;
|
||||
}
|
||||
|
||||
int plm_get_width(plm_t *self) {
|
||||
return plm_video_get_width(self->video_decoder);
|
||||
}
|
||||
|
||||
double plm_get_pixel_aspect_ratio(plm_t *self) {
|
||||
return plm_video_get_pixel_aspect_ratio(self->video_decoder);
|
||||
}
|
||||
|
||||
int plm_get_height(plm_t *self) {
|
||||
return plm_video_get_height(self->video_decoder);
|
||||
}
|
||||
|
||||
double plm_get_framerate(plm_t *self) {
|
||||
return plm_video_get_framerate(self->video_decoder);
|
||||
}
|
||||
|
||||
int plm_get_num_audio_streams(plm_t *self) {
|
||||
// Some files do not specify the number of audio streams in the system header.
|
||||
// If the reported number of streams is 0, we check if we have a samplerate,
|
||||
// indicating at least one audio stream.
|
||||
int num_streams = plm_demux_get_num_audio_streams(self->demux);
|
||||
return num_streams == 0 && plm_get_samplerate(self) ? 1 : num_streams;
|
||||
}
|
||||
|
||||
int plm_get_samplerate(plm_t *self) {
|
||||
return plm_audio_get_samplerate(self->audio_decoder);
|
||||
}
|
||||
|
||||
double plm_get_audio_lead_time(plm_t *self) {
|
||||
return self->audio_lead_time;
|
||||
}
|
||||
|
||||
void plm_set_audio_lead_time(plm_t *self, double lead_time) {
|
||||
self->audio_lead_time = lead_time;
|
||||
}
|
||||
|
||||
double plm_get_time(plm_t *self) {
|
||||
return self->time;
|
||||
}
|
||||
|
||||
void plm_rewind(plm_t *self) {
|
||||
plm_video_rewind(self->video_decoder);
|
||||
plm_audio_rewind(self->audio_decoder);
|
||||
plm_demux_rewind(self->demux);
|
||||
self->time = 0;
|
||||
}
|
||||
|
||||
int plm_get_loop(plm_t *self) {
|
||||
return self->loop;
|
||||
}
|
||||
|
||||
void plm_set_loop(plm_t *self, int loop) {
|
||||
self->loop = loop;
|
||||
}
|
||||
|
||||
int plm_has_ended(plm_t *self) {
|
||||
return self->has_ended;
|
||||
}
|
||||
|
||||
void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp, void *user) {
|
||||
self->video_decode_callback = fp;
|
||||
self->video_decode_callback_user_data = user;
|
||||
}
|
||||
|
||||
void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp, void *user) {
|
||||
self->audio_decode_callback = fp;
|
||||
self->audio_decode_callback_user_data = user;
|
||||
}
|
||||
|
||||
int plm_decode(plm_t *self, double tick) {
|
||||
DEBUGF("%s", "plm_decode");
|
||||
|
||||
int decode_video = (self->video_decode_callback && self->video_packet_type);
|
||||
int decode_audio = (self->audio_decode_callback && self->audio_packet_type);
|
||||
|
||||
if (!decode_video && !decode_audio) {
|
||||
// Nothing to do here
|
||||
return false;
|
||||
}
|
||||
|
||||
int did_decode = false;
|
||||
int video_ended = false;
|
||||
int audio_ended = false;
|
||||
|
||||
double video_target_time = self->time + tick;
|
||||
double audio_target_time = self->time + tick;
|
||||
|
||||
if (self->audio_lead_time > 0 && decode_audio) {
|
||||
video_target_time -= self->audio_lead_time;
|
||||
}
|
||||
else {
|
||||
audio_target_time -= self->audio_lead_time;
|
||||
}
|
||||
|
||||
do {
|
||||
did_decode = false;
|
||||
|
||||
if (decode_video && plm_video_get_time(self->video_decoder) < video_target_time) {
|
||||
plm_frame_t *frame = plm_video_decode(self->video_decoder);
|
||||
if (frame) {
|
||||
self->video_decode_callback(self, frame, self->video_decode_callback_user_data);
|
||||
did_decode = true;
|
||||
}
|
||||
else {
|
||||
video_ended = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_audio && plm_audio_get_time(self->audio_decoder) < audio_target_time) {
|
||||
plm_samples_t *samples = plm_audio_decode(self->audio_decoder);
|
||||
if (samples) {
|
||||
self->audio_decode_callback(self, samples, self->audio_decode_callback_user_data);
|
||||
did_decode = true;
|
||||
}
|
||||
else {
|
||||
audio_ended = true;
|
||||
}
|
||||
}
|
||||
} while (did_decode);
|
||||
|
||||
// We wanted to decode something but failed -> the source must have ended
|
||||
if ((!decode_video || video_ended) && (!decode_audio || audio_ended)) {
|
||||
plm_handle_end(self);
|
||||
}
|
||||
else {
|
||||
self->time += tick;
|
||||
}
|
||||
|
||||
return did_decode ? true : false;
|
||||
}
|
||||
|
||||
plm_frame_t *plm_decode_video(plm_t *self) {
|
||||
if (!self->video_packet_type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
plm_frame_t *frame = plm_video_decode(self->video_decoder);
|
||||
if (frame) {
|
||||
self->time = frame->time;
|
||||
}
|
||||
else {
|
||||
plm_handle_end(self);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
plm_samples_t *plm_decode_audio(plm_t *self) {
|
||||
if (!self->audio_packet_type) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
plm_samples_t *samples = plm_audio_decode(self->audio_decoder);
|
||||
if (samples) {
|
||||
self->time = samples->time;
|
||||
}
|
||||
else {
|
||||
plm_handle_end(self);
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
void plm_handle_end(plm_t *self) {
|
||||
if (self->loop) {
|
||||
plm_rewind(self);
|
||||
}
|
||||
else {
|
||||
self->has_ended = true;
|
||||
}
|
||||
}
|
||||
|
||||
void plm_read_video_packet(plm_buffer_t *buffer, void *user) {
|
||||
plm_t *self = (plm_t *)user;
|
||||
plm_read_packets(self, self->video_packet_type);
|
||||
}
|
||||
|
||||
void plm_read_audio_packet(plm_buffer_t *buffer, void *user) {
|
||||
plm_t *self = (plm_t *)user;
|
||||
plm_read_packets(self, self->audio_packet_type);
|
||||
}
|
||||
|
||||
void plm_read_packets(plm_t *self, int requested_type) {
|
||||
plm_packet_t *packet;
|
||||
while ((packet = plm_demux_decode(self->demux))) {
|
||||
if (packet->type == self->video_packet_type) {
|
||||
plm_buffer_write(self->video_buffer, packet->data, packet->length);
|
||||
}
|
||||
else if (packet->type == self->audio_packet_type) {
|
||||
plm_buffer_write(self->audio_buffer, packet->data, packet->length);
|
||||
}
|
||||
if (packet->type == requested_type) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*-*- 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/mpeg.h"
|
||||
#include "libc/macros.h"
|
||||
__static_yoink("pl_mpeg_notice");
|
||||
|
||||
/**
|
||||
* @see YCbCr2RGB() in tool/viz/lib/ycbcr2rgb.c
|
||||
*/
|
||||
void plm_frame_to_rgb(plm_frame_t *frame, uint8_t *rgb) {
|
||||
// Chroma values are the same for each block of 4 pixels, so we process
|
||||
// 2 lines at a time, 2 neighboring pixels each.
|
||||
int w = frame->y.width, w2 = w >> 1;
|
||||
int y_index1 = 0, y_index2 = w, y_next_2_lines = w + (w - frame->width);
|
||||
int c_index = 0, c_next_line = w2 - (frame->width >> 1);
|
||||
int rgb_index1 = 0, rgb_index2 = frame->width * 3,
|
||||
rgb_next_2_lines = frame->width * 3;
|
||||
int cols = frame->width >> 1, rows = frame->height >> 1;
|
||||
int ccb, ccr, r, g, b;
|
||||
uint8_t *y = frame->y.data, *cb = frame->cb.data, *cr = frame->cr.data;
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
ccb = cb[c_index];
|
||||
ccr = cr[c_index];
|
||||
c_index++;
|
||||
r = (ccr + ((ccr * 103) >> 8)) - 179;
|
||||
g = ((ccb * 88) >> 8) - 44 + ((ccr * 183) >> 8) - 91;
|
||||
b = (ccb + ((ccb * 198) >> 8)) - 227;
|
||||
// Line 1
|
||||
int y1 = y[y_index1++];
|
||||
int y2 = y[y_index1++];
|
||||
rgb[rgb_index1 + 0] = MAX(0, MIN(255, y1 + r));
|
||||
rgb[rgb_index1 + 1] = MAX(0, MIN(255, y1 - g));
|
||||
rgb[rgb_index1 + 2] = MAX(0, MIN(255, y1 + b));
|
||||
rgb[rgb_index1 + 3] = MAX(0, MIN(255, y2 + r));
|
||||
rgb[rgb_index1 + 4] = MAX(0, MIN(255, y2 - g));
|
||||
rgb[rgb_index1 + 5] = MAX(0, MIN(255, y2 + b));
|
||||
rgb_index1 += 6;
|
||||
// Line 2
|
||||
int y3 = y[y_index2++];
|
||||
int y4 = y[y_index2++];
|
||||
rgb[rgb_index2 + 0] = MAX(0, MIN(255, y3 + r));
|
||||
rgb[rgb_index2 + 1] = MAX(0, MIN(255, y3 - g));
|
||||
rgb[rgb_index2 + 2] = MAX(0, MIN(255, y3 + b));
|
||||
rgb[rgb_index2 + 3] = MAX(0, MIN(255, y4 + r));
|
||||
rgb[rgb_index2 + 4] = MAX(0, MIN(255, y4 - g));
|
||||
rgb[rgb_index2 + 5] = MAX(0, MIN(255, y4 + b));
|
||||
rgb_index2 += 6;
|
||||
}
|
||||
y_index1 += y_next_2_lines;
|
||||
y_index2 += y_next_2_lines;
|
||||
rgb_index1 += rgb_next_2_lines;
|
||||
rgb_index2 += rgb_next_2_lines;
|
||||
c_index += c_next_line;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_DSP_MPEG_VIDEO_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_VIDEO_H_
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
typedef struct {
|
||||
int full_px;
|
||||
int is_set;
|
||||
int r_size;
|
||||
int h;
|
||||
int v;
|
||||
} plm_video_motion_t;
|
||||
|
||||
typedef struct plm_video_t {
|
||||
double framerate;
|
||||
double time;
|
||||
double pixel_aspect_ratio;
|
||||
int frames_decoded;
|
||||
int width;
|
||||
int height;
|
||||
int mb_width;
|
||||
int mb_height;
|
||||
int mb_size;
|
||||
int luma_width;
|
||||
int luma_height;
|
||||
int chroma_width;
|
||||
int chroma_height;
|
||||
int start_code;
|
||||
int picture_type;
|
||||
plm_video_motion_t motion_forward;
|
||||
plm_video_motion_t motion_backward;
|
||||
int has_sequence_header;
|
||||
int quantizer_scale;
|
||||
int slice_begin;
|
||||
int macroblock_address;
|
||||
int mb_row;
|
||||
int mb_col;
|
||||
int macroblock_type;
|
||||
int macroblock_intra;
|
||||
int dc_predictor[3];
|
||||
plm_buffer_t *buffer;
|
||||
int destroy_buffer_when_done;
|
||||
plm_frame_t frame_current;
|
||||
plm_frame_t frame_forward;
|
||||
plm_frame_t frame_backward;
|
||||
uint8_t *frames_data;
|
||||
int block_data[64];
|
||||
uint8_t intra_quant_matrix[64];
|
||||
uint8_t non_intra_quant_matrix[64];
|
||||
int has_reference_frame;
|
||||
int assume_no_b_frames;
|
||||
} plm_video_t;
|
||||
|
||||
void plm_video_process_macroblock_8(plm_video_t *, uint8_t *, uint8_t *, int,
|
||||
int, bool);
|
||||
void plm_video_process_macroblock_16(plm_video_t *, uint8_t *, uint8_t *, int,
|
||||
int, bool);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_VIDEO_H_ */
|
|
@ -16,17 +16,20 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/intrin/packuswb.h"
|
||||
#include "libc/intrin/paddw.h"
|
||||
#include "libc/intrin/palignr.h"
|
||||
#include "libc/intrin/pmaddubsw.h"
|
||||
#include "libc/intrin/psraw.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "third_party/intel/immintrin.internal.h"
|
||||
|
||||
/**
|
||||
* Performs 2D Motion Picture Convolution Acceleration by Leveraging SSSE3.
|
||||
*
|
||||
* @note H/T John Costella, Jean-Baptiste Joseph Fourier
|
||||
* @note RIP Huixiang Chen
|
||||
*/
|
||||
void *cDecimate2xUint8x8(unsigned long n, unsigned char A[n],
|
||||
const signed char K[8]) {
|
||||
#ifdef __x86_64__
|
||||
#define TAPS 8
|
||||
#define RATIO 2
|
||||
#define OFFSET 3
|
||||
|
@ -37,62 +40,107 @@
|
|||
#define LOOKAHEAD (SPREAD - LOOKBEHIND)
|
||||
#define SCALE 5
|
||||
#define ROUND (1 << (SCALE - 1))
|
||||
|
||||
/**
|
||||
* Performs 2D Motion Picture Convolution Acceleration by Leveraging SSSE3.
|
||||
*
|
||||
* @note H/T John Costella, Jean-Baptiste Joseph Fourier
|
||||
* @note RIP Huixiang Chen
|
||||
*/
|
||||
void *cDecimate2xUint8x8(unsigned long n, unsigned char A[n],
|
||||
const signed char K[8]) {
|
||||
short kRound[8] = {ROUND, ROUND, ROUND, ROUND, ROUND, ROUND, ROUND, ROUND};
|
||||
signed char kMadd1[16] = {K[0], K[1], K[0], K[1], K[0], K[1], K[0], K[1],
|
||||
K[0], K[1], K[0], K[1], K[0], K[1], K[0], K[1]};
|
||||
signed char kMadd2[16] = {K[2], K[3], K[2], K[3], K[2], K[3], K[2], K[3],
|
||||
K[2], K[3], K[2], K[3], K[2], K[3], K[2], K[3]};
|
||||
signed char kMadd3[16] = {K[4], K[5], K[4], K[5], K[4], K[5], K[4], K[5],
|
||||
K[4], K[5], K[4], K[5], K[4], K[5], K[4], K[5]};
|
||||
signed char kMadd4[16] = {K[6], K[7], K[6], K[7], K[6], K[7], K[6], K[7],
|
||||
K[6], K[7], K[6], K[7], K[6], K[7], K[6], K[7]};
|
||||
unsigned char bv0[16], bv1[16], bv2[16], bv3[16];
|
||||
unsigned char in1[16], in2[16], in3[16];
|
||||
short wv0[8], wv1[8], wv2[8], wv3[8];
|
||||
__m128i kRound = _mm_set1_epi16(ROUND);
|
||||
__m128i kMadd1 = _mm_set_epi8(K[1], K[0], K[1], K[0], K[1], K[0], K[1], K[0],
|
||||
K[1], K[0], K[1], K[0], K[1], K[0], K[1], K[0]);
|
||||
__m128i kMadd2 = _mm_set_epi8(K[3], K[2], K[3], K[2], K[3], K[2], K[3], K[2],
|
||||
K[3], K[2], K[3], K[2], K[3], K[2], K[3], K[2]);
|
||||
__m128i kMadd3 = _mm_set_epi8(K[5], K[4], K[5], K[4], K[5], K[4], K[5], K[4],
|
||||
K[5], K[4], K[5], K[4], K[5], K[4], K[5], K[4]);
|
||||
__m128i kMadd4 = _mm_set_epi8(K[7], K[6], K[7], K[6], K[7], K[6], K[7], K[6],
|
||||
K[7], K[6], K[7], K[6], K[7], K[6], K[7], K[6]);
|
||||
__m128i bv0, bv1, bv2, bv3;
|
||||
__m128i in1, in2, in3;
|
||||
__m128i wv0, wv1, wv2, wv3;
|
||||
unsigned long i, j, w;
|
||||
if (n >= STRIDE) {
|
||||
i = 0;
|
||||
w = (n + RATIO / 2) / RATIO;
|
||||
memset(in1, A[0], sizeof(in1));
|
||||
memset(in2, A[n - 1], 16);
|
||||
memcpy(in2, A, MIN(16, n));
|
||||
in1 = _mm_set1_epi8(A[0]);
|
||||
in2 = _mm_set1_epi8(A[n - 1]);
|
||||
_mm_storeu_si128((__m128i *)&in2, _mm_loadu_si128((__m128i *)A));
|
||||
for (; i < w; i += STRIDE) {
|
||||
j = i * RATIO + 16;
|
||||
if (j + 16 <= n) {
|
||||
memcpy(in3, &A[j], 16);
|
||||
in3 = _mm_loadu_si128((__m128i *)&A[j]);
|
||||
} else {
|
||||
memset(in3, A[n - 1], 16);
|
||||
in3 = _mm_set1_epi8(A[n - 1]);
|
||||
if (j < n) {
|
||||
memcpy(in3, &A[j], n - j);
|
||||
// SSSE3-compatible way to handle partial loads
|
||||
__m128i mask = _mm_loadu_si128((__m128i *)&A[j]);
|
||||
__m128i shuffle_mask = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7,
|
||||
6, 5, 4, 3, 2, 1, 0);
|
||||
__m128i index = _mm_set1_epi8(n - j);
|
||||
__m128i cmp = _mm_cmplt_epi8(shuffle_mask, index);
|
||||
in3 = _mm_or_si128(_mm_and_si128(cmp, mask),
|
||||
_mm_andnot_si128(cmp, in3));
|
||||
}
|
||||
}
|
||||
palignr(bv0, in2, in1, 13);
|
||||
palignr(bv1, in2, in1, 15);
|
||||
palignr(bv2, in3, in2, 1);
|
||||
palignr(bv3, in3, in2, 3);
|
||||
pmaddubsw(wv0, bv0, kMadd1);
|
||||
pmaddubsw(wv1, bv1, kMadd2);
|
||||
pmaddubsw(wv2, bv2, kMadd3);
|
||||
pmaddubsw(wv3, bv3, kMadd4);
|
||||
paddw(wv0, wv0, kRound);
|
||||
paddw(wv0, wv0, wv1);
|
||||
paddw(wv0, wv0, wv2);
|
||||
paddw(wv0, wv0, wv3);
|
||||
psraw(wv0, wv0, SCALE);
|
||||
packuswb(bv2, wv0, wv0);
|
||||
memcpy(&A[i], bv2, STRIDE);
|
||||
memcpy(in1, in2, 16);
|
||||
memcpy(in2, in3, 16);
|
||||
bv0 = _mm_alignr_epi8(in2, in1, 13);
|
||||
bv1 = _mm_alignr_epi8(in2, in1, 15);
|
||||
bv2 = _mm_alignr_epi8(in3, in2, 1);
|
||||
bv3 = _mm_alignr_epi8(in3, in2, 3);
|
||||
wv0 = _mm_maddubs_epi16(bv0, kMadd1);
|
||||
wv1 = _mm_maddubs_epi16(bv1, kMadd2);
|
||||
wv2 = _mm_maddubs_epi16(bv2, kMadd3);
|
||||
wv3 = _mm_maddubs_epi16(bv3, kMadd4);
|
||||
wv0 = _mm_add_epi16(wv0, kRound);
|
||||
wv0 = _mm_add_epi16(wv0, wv1);
|
||||
wv0 = _mm_add_epi16(wv0, wv2);
|
||||
wv0 = _mm_add_epi16(wv0, wv3);
|
||||
wv0 = _mm_srai_epi16(wv0, SCALE);
|
||||
bv2 = _mm_packus_epi16(wv0, wv0);
|
||||
_mm_storel_epi64((__m128i *)&A[i], bv2);
|
||||
in1 = in2;
|
||||
in2 = in3;
|
||||
}
|
||||
}
|
||||
return A;
|
||||
#else
|
||||
long h, i;
|
||||
if (n < 2)
|
||||
return A;
|
||||
unsigned char M[3 + n + 4];
|
||||
unsigned char *q = M;
|
||||
q[0] = A[0];
|
||||
q[1] = A[0];
|
||||
q[2] = A[0];
|
||||
memcpy(q + 3, A, n);
|
||||
q[3 + n + 0] = A[n - 1];
|
||||
q[3 + n + 1] = A[n - 1];
|
||||
q[3 + n + 2] = A[n - 1];
|
||||
q[3 + n + 3] = A[n - 1];
|
||||
q += 3;
|
||||
h = (n + 1) >> 1;
|
||||
for (i = 0; i < h; ++i) {
|
||||
short x0, x1, x2, x3, x4, x5, x6, x7;
|
||||
x0 = q[i * 2 - 3];
|
||||
x1 = q[i * 2 - 2];
|
||||
x2 = q[i * 2 - 1];
|
||||
x3 = q[i * 2 + 0];
|
||||
x4 = q[i * 2 + 1];
|
||||
x5 = q[i * 2 + 2];
|
||||
x6 = q[i * 2 + 3];
|
||||
x7 = q[i * 2 + 4];
|
||||
x0 *= K[0];
|
||||
x1 *= K[1];
|
||||
x2 *= K[2];
|
||||
x3 *= K[3];
|
||||
x4 *= K[4];
|
||||
x5 *= K[5];
|
||||
x6 *= K[6];
|
||||
x7 *= K[7];
|
||||
x0 += x1;
|
||||
x2 += x3;
|
||||
x4 += x5;
|
||||
x6 += x7;
|
||||
x0 += x2;
|
||||
x4 += x6;
|
||||
x0 += x4;
|
||||
x0 += 1 << 4;
|
||||
x0 >>= 5;
|
||||
A[i] = MIN(255, MAX(0, x0));
|
||||
}
|
||||
return A;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_MACROS_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_MACROS_H_
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
|
||||
#define INTRIN_COMMUTATIVE "%"
|
||||
#define INTRIN_NONCOMMUTATIVE
|
||||
|
||||
#if defined(__x86_64__) && !defined(__STRICT_ANSI__)
|
||||
|
||||
typedef char __intrin_xmm_t
|
||||
__attribute__((__vector_size__(16), __aligned__(16), __may_alias__));
|
||||
|
||||
#define INTRIN_SSEVEX_X_X_X_(PURE, ISA, OP, FLAGS, A, B, C) \
|
||||
do { \
|
||||
if (X86_HAVE(ISA)) { \
|
||||
__intrin_xmm_t *Xmm0 = (void *)(A); \
|
||||
const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(B); \
|
||||
const __intrin_xmm_t *Xmm2 = (const __intrin_xmm_t *)(C); \
|
||||
if (!X86_NEED(AVX)) { \
|
||||
asm(OP "\t%1,%0" : "=x"(*Xmm0) : FLAGS "x"(*Xmm2), "0"(*Xmm1)); \
|
||||
} else { \
|
||||
asm("v" OP "\t%2,%1,%0" : "=x"(*Xmm0) : FLAGS "x"(*Xmm1), "x"(*Xmm2)); \
|
||||
} \
|
||||
} else { \
|
||||
PURE(A, B, C); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define INTRIN_SSEVEX_X_X_I_(PURE, ISA, OP, A, B, I) \
|
||||
do { \
|
||||
if (X86_HAVE(ISA)) { \
|
||||
__intrin_xmm_t *Xmm0 = (void *)(A); \
|
||||
const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(B); \
|
||||
if (!X86_NEED(AVX)) { \
|
||||
asm(OP "\t%2,%1,%0" : "=x"(*Xmm0) : "x"(*Xmm1), "i"(I)); \
|
||||
} else { \
|
||||
asm("v" OP "\t%2,%1,%0" : "=x"(*Xmm0) : "x"(*Xmm1), "i"(I)); \
|
||||
} \
|
||||
} else { \
|
||||
PURE(A, B, I); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define INTRIN_SSEVEX_X_X_(PURE, ISA, OP, A, B) \
|
||||
do { \
|
||||
if (X86_HAVE(ISA)) { \
|
||||
__intrin_xmm_t *Xmm0 = (void *)(A); \
|
||||
const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(B); \
|
||||
if (!X86_NEED(AVX)) { \
|
||||
asm(OP "\t%1,%0" : "=x"(*Xmm0) : "0"(*Xmm1)); \
|
||||
} else { \
|
||||
asm("v" OP "\t%1,%0" : "=x"(*Xmm0) : "x"(*Xmm1)); \
|
||||
} \
|
||||
} else { \
|
||||
PURE(A, B); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define INTRIN_SSEVEX_X_I_(PURE, ISA, OP, A, B, I) \
|
||||
do { \
|
||||
if (!IsModeDbg() && X86_HAVE(ISA)) { \
|
||||
__intrin_xmm_t *Xmm0 = (void *)(A); \
|
||||
const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(B); \
|
||||
if (!X86_NEED(AVX)) { \
|
||||
asm(OP "\t%1,%0" : "=x"(*Xmm0) : "i"(I), "0"(*Xmm1)); \
|
||||
} else { \
|
||||
asm("v" OP "\t%2,%1,%0" : "=x"(*Xmm0) : "x"(*Xmm1), "i"(I)); \
|
||||
} \
|
||||
} else { \
|
||||
PURE(A, B, I); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else
|
||||
#define INTRIN_SSEVEX_X_X_X_(PURE, ISA, OP, FLAGS, A, B, C) PURE(A, B, C)
|
||||
#define INTRIN_SSEVEX_X_X_I_(PURE, ISA, OP, A, B, I) PURE(A, B, I)
|
||||
#define INTRIN_SSEVEX_X_I_(PURE, ISA, OP, A, B, I) PURE(A, B, I)
|
||||
#define INTRIN_SSEVEX_X_X_(PURE, ISA, OP, A, B) PURE(A, B)
|
||||
#endif /* X86 && !ANSI */
|
||||
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_MACROS_H_ */
|
|
@ -1,40 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/packsswb.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Casts shorts to signed chars w/ saturation.
|
||||
*
|
||||
* 𝑎 ← {CLAMP[𝑏ᵢ]|𝑖∈[0,4)} ║ {CLAMP[𝑐ᵢ]|𝑖∈[4,8)}
|
||||
*
|
||||
* @see packuswb()
|
||||
* @mayalias
|
||||
*/
|
||||
void(packsswb)(int8_t a[16], const int16_t b[8], const int16_t c[8]) {
|
||||
unsigned i;
|
||||
int8_t r[16];
|
||||
for (i = 0; i < 8; ++i)
|
||||
r[i + 0] = MIN(INT8_MAX, MAX(INT8_MIN, b[i]));
|
||||
for (i = 0; i < 8; ++i)
|
||||
r[i + 8] = MIN(INT8_MAX, MAX(INT8_MIN, c[i]));
|
||||
__builtin_memcpy(a, r, 16);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_PACKSSWB_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_PACKSSWB_H_
|
||||
#include "libc/intrin/macros.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void packsswb(int8_t[16], const int16_t[8], const int16_t[8]);
|
||||
|
||||
#define packsswb(A, B, C) \
|
||||
INTRIN_SSEVEX_X_X_X_(packsswb, SSE2, "packsswb", INTRIN_NONCOMMUTATIVE, A, \
|
||||
B, C)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_PACKSSWB_H_ */
|
|
@ -1,40 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/packuswb.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Casts shorts to unsigned chars w/ saturation.
|
||||
*
|
||||
* 𝑎 ← {CLAMP[𝑏ᵢ]|𝑖∈[0,4)} ║ {CLAMP[𝑐ᵢ]|𝑖∈[4,8)}
|
||||
*
|
||||
* @see packsswb()
|
||||
* @mayalias
|
||||
*/
|
||||
void(packuswb)(uint8_t a[16], const int16_t b[8], const int16_t c[8]) {
|
||||
unsigned i;
|
||||
uint8_t r[16];
|
||||
for (i = 0; i < 8; ++i)
|
||||
r[i + 0] = MIN(UINT8_MAX, MAX(UINT8_MIN, b[i]));
|
||||
for (i = 0; i < 8; ++i)
|
||||
r[i + 8] = MIN(UINT8_MAX, MAX(UINT8_MIN, c[i]));
|
||||
__builtin_memcpy(a, r, 16);
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_PACKUSWB_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_PACKUSWB_H_
|
||||
#include "libc/intrin/macros.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void packuswb(uint8_t[16], const int16_t[8], const int16_t[8]);
|
||||
|
||||
#define packuswb(A, B, C) \
|
||||
INTRIN_SSEVEX_X_X_X_(packuswb, SSE2, "packuswb", INTRIN_NONCOMMUTATIVE, A, \
|
||||
B, C)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_PACKUSWB_H_ */
|
|
@ -1,39 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/paddw.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Adds 16-bit integers.
|
||||
*
|
||||
* @param 𝑎 [w/o] receives result
|
||||
* @param 𝑏 [r/o] supplies first input vector
|
||||
* @param 𝑐 [r/o] supplies second input vector
|
||||
* @note shorts can't overflow so ubsan won't report it when it happens
|
||||
* @see paddsw()
|
||||
* @mayalias
|
||||
*/
|
||||
void(paddw)(int16_t a[8], const int16_t b[8], const int16_t c[8]) {
|
||||
unsigned i;
|
||||
int16_t r[8];
|
||||
for (i = 0; i < 8; ++i) {
|
||||
r[i] = b[i] + c[i];
|
||||
}
|
||||
__builtin_memcpy(a, r, 16);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_PADDW_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_PADDW_H_
|
||||
#include "libc/intrin/macros.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void paddw(int16_t[8], const int16_t[8], const int16_t[8]);
|
||||
|
||||
#define paddw(A, B, C) \
|
||||
INTRIN_SSEVEX_X_X_X_(paddw, SSE2, "paddw", INTRIN_COMMUTATIVE, A, B, C)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_PADDW_H_ */
|
|
@ -1,43 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/palignr.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
/**
|
||||
* Overlaps vectors.
|
||||
*
|
||||
* 𝑖= 0 means 𝑐←𝑎
|
||||
* 0<𝑖<16 means 𝑐←𝑎║𝑏
|
||||
* 𝑖=16 means 𝑐←𝑏
|
||||
* 16<𝑖<32 means 𝑐←𝑏║0
|
||||
* 𝑖≥32 means 𝑐←0
|
||||
*
|
||||
* @param 𝑖 goes faster as constexpr
|
||||
* @note not compatible with mmx
|
||||
* @see pvalignr()
|
||||
* @mayalias
|
||||
*/
|
||||
void(palignr)(void *c, const void *b, const void *a, unsigned long i) {
|
||||
char t[48];
|
||||
__builtin_memcpy(t, a, 16);
|
||||
__builtin_memcpy(t + 16, b, 16);
|
||||
__builtin_memset(t + 32, 0, 16);
|
||||
__builtin_memcpy(c, t + MIN(i, 32), 16);
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_PALIGNR_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_PALIGNR_H_
|
||||
#include "libc/intrin/macros.h"
|
||||
#include "libc/str/str.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void palignr(void *, const void *, const void *, unsigned long);
|
||||
|
||||
#if !defined(__STRICT_ANSI__) && !defined(__chibicc__) && defined(__x86_64__)
|
||||
__intrin_xmm_t __palignrs(__intrin_xmm_t, __intrin_xmm_t);
|
||||
#define palignr(C, B, A, I) \
|
||||
do { \
|
||||
if (__builtin_expect(!IsModeDbg() && X86_NEED(SSE) && X86_HAVE(SSSE3), \
|
||||
1)) { \
|
||||
__intrin_xmm_t *Xmm0 = (void *)(C); \
|
||||
const __intrin_xmm_t *Xmm1 = (const __intrin_xmm_t *)(B); \
|
||||
const __intrin_xmm_t *Xmm2 = (const __intrin_xmm_t *)(A); \
|
||||
if (__builtin_constant_p(I)) { \
|
||||
if (!X86_NEED(AVX)) { \
|
||||
asm("palignr\t%2,%1,%0" \
|
||||
: "=x"(*Xmm0) \
|
||||
: "x"(*Xmm2), "i"(I), "0"(*Xmm1)); \
|
||||
} else { \
|
||||
asm("vpalignr\t%3,%2,%1,%0" \
|
||||
: "=x"(*Xmm0) \
|
||||
: "x"(*Xmm1), "x"(*Xmm2), "i"(I)); \
|
||||
} \
|
||||
} else { \
|
||||
unsigned long Vimm = (I); \
|
||||
typeof(__palignrs) *Fn; \
|
||||
if (__builtin_expect(Vimm < 32, 1)) { \
|
||||
Fn = (typeof(__palignrs) *)((uintptr_t) & __palignrs + Vimm * 8); \
|
||||
*Xmm0 = Fn(*Xmm1, *Xmm2); \
|
||||
} else { \
|
||||
memset(Xmm0, 0, 16); \
|
||||
} \
|
||||
} \
|
||||
} else { \
|
||||
palignr(C, B, A, I); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_PALIGNR_H_ */
|
|
@ -1,125 +0,0 @@
|
|||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.h"
|
||||
|
||||
// Jump table for palignr() with non-constexpr immediate parameter.
|
||||
//
|
||||
// @note needs ssse3 cf. prescott c. 2004 cf. bulldozer c. 2011
|
||||
// @see palignr()
|
||||
.balign 8
|
||||
__palignrs:
|
||||
palignr $0,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $1,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $2,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $3,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $4,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $5,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $6,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $7,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $8,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $9,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $10,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $11,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $12,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $13,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $14,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $15,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $16,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $17,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $18,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $19,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $20,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $21,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $22,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $23,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $24,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $25,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $26,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $27,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $28,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $29,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $30,%xmm1,%xmm0
|
||||
ret
|
||||
nop
|
||||
palignr $31,%xmm1,%xmm0
|
||||
ret
|
||||
.if . - __palignrs != 8 * 32 - 1
|
||||
.error "bad assemblage"
|
||||
.endif
|
||||
.endfn __palignrs,globl
|
|
@ -1,42 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pmaddubsw.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Multiplies bytes and adds adjacent results w/ short saturation.
|
||||
*
|
||||
* 𝑤ᵢ ← CLAMP[ 𝑏₂ᵢ𝑐₂ᵢ + 𝑏₍₂ᵢ₊₁₎𝑐₍₂ᵢ₊₁₎ ]
|
||||
*
|
||||
* @param 𝑤 [w/o] receives shorts
|
||||
* @param 𝑏 [r/o] is your byte data
|
||||
* @param 𝑐 [r/o] are your int8 coefficients
|
||||
* @note SSSE3 w/ Prescott c. 2004, Bulldozer c. 2011
|
||||
* @note greatest simd op, like, ever
|
||||
* @mayalias
|
||||
*/
|
||||
void(pmaddubsw)(int16_t w[8], const uint8_t b[16], const int8_t c[16]) {
|
||||
unsigned i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
w[i] = MIN(SHRT_MAX, MAX(SHRT_MIN, (c[i * 2 + 0] * b[i * 2 + 0] +
|
||||
c[i * 2 + 1] * b[i * 2 + 1])));
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_PMADDUBSW_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_PMADDUBSW_H_
|
||||
#include "libc/intrin/macros.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void pmaddubsw(int16_t[8], const uint8_t[16], const int8_t[16]);
|
||||
|
||||
#define pmaddubsw(W, B, C) \
|
||||
INTRIN_SSEVEX_X_X_X_(pmaddubsw, SSSE3, "pmaddubsw", INTRIN_NONCOMMUTATIVE, \
|
||||
W, B, C)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_PMADDUBSW_H_ */
|
|
@ -1,36 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/pmulhrsw.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/**
|
||||
* Multiplies Q15 numbers.
|
||||
*
|
||||
* @note goes fast w/ ssse3 (intel c. 2004, amd c. 2011)
|
||||
* @note a.k.a. packed multiply high w/ round & scale
|
||||
* @see Q2F(15,𝑥), F2Q(15,𝑥)
|
||||
* @mayalias
|
||||
*/
|
||||
void(pmulhrsw)(int16_t a[8], const int16_t b[8], const int16_t c[8]) {
|
||||
unsigned i;
|
||||
int16_t r[8];
|
||||
for (i = 0; i < 8; ++i)
|
||||
r[i] = (((b[i] * c[i]) >> 14) + 1) >> 1;
|
||||
__builtin_memcpy(a, r, 16);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_PMULHRSW_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_PMULHRSW_H_
|
||||
#include "libc/intrin/macros.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void pmulhrsw(int16_t a[8], const int16_t b[8], const int16_t c[8]);
|
||||
|
||||
#define pmulhrsw(A, B, C) \
|
||||
INTRIN_SSEVEX_X_X_X_(pmulhrsw, SSSE3, "pmulhrsw", INTRIN_COMMUTATIVE, A, B, C)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_PMULHRSW_H_ */
|
|
@ -1,35 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/psraw.h"
|
||||
|
||||
/**
|
||||
* Divides shorts by two power.
|
||||
*
|
||||
* @note c needs to be a literal, asmconstexpr, or linkconstsym
|
||||
* @note arithmetic shift right will sign extend negatives
|
||||
* @mayalias
|
||||
*/
|
||||
void(psraw)(int16_t a[8], const int16_t b[8], unsigned char k) {
|
||||
unsigned i;
|
||||
if (k > 15)
|
||||
k = 15;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
a[i] = b[i] >> k;
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_PSRAW_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_PSRAW_H_
|
||||
#include "libc/intrin/macros.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void psraw(int16_t[8], const int16_t[8], unsigned char) libcesque;
|
||||
|
||||
#define psraw(A, B, I) INTRIN_SSEVEX_X_I_(psraw, SSE2, "psraw", A, B, I)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_PSRAW_H_ */
|
|
@ -17,7 +17,6 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/core/core.h"
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/macros.h"
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
|
1
third_party/python/Python/marshal.c
vendored
1
third_party/python/Python/marshal.c
vendored
|
@ -5,7 +5,6 @@
|
|||
│ https://docs.python.org/3/license.html │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "dsp/mpeg/video.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/weirdtypes.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/intrin/bsr.h"
|
||||
#include "libc/intrin/pmulhrsw.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.h"
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "dsp/core/core.h"
|
||||
#include "dsp/core/half.h"
|
||||
#include "dsp/core/illumination.h"
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "dsp/mpeg/pl_mpeg.h"
|
||||
#include "dsp/scale/scale.h"
|
||||
#include "dsp/tty/quant.h"
|
||||
#include "dsp/tty/tty.h"
|
||||
|
@ -239,7 +239,7 @@ static const struct NamedVector kLightings[] = {
|
|||
|
||||
static plm_t *plm_;
|
||||
static float gamma_;
|
||||
static int volscale_;
|
||||
static float volscale_;
|
||||
struct CosmoAudio *ca_;
|
||||
static enum Blur blur_;
|
||||
static enum Sharp sharp_;
|
||||
|
@ -425,6 +425,9 @@ static bool OpenSpeaker(void) {
|
|||
static void OnAudio(plm_t *mpeg, plm_samples_t *samples, void *user) {
|
||||
if (!ca_)
|
||||
return;
|
||||
if (volscale_ != 1.f)
|
||||
for (unsigned i = 0; i < samples->count * chans_; ++i)
|
||||
samples->interleaved[i] *= volscale_;
|
||||
cosmoaudio_write(ca_, samples->interleaved, samples->count);
|
||||
}
|
||||
|
||||
|
@ -752,9 +755,9 @@ static void OpenVideo(void) {
|
|||
FormatInt64(chansstr_, (chans_ = 2));
|
||||
FormatInt64(sratestr_, (srate_ = plm_get_samplerate(plm_)));
|
||||
if (plm_get_num_audio_streams(plm_) && OpenSpeaker()) {
|
||||
plm_set_audio_enabled(plm_, true, 0);
|
||||
plm_set_audio_enabled(plm_, true);
|
||||
} else {
|
||||
plm_set_audio_enabled(plm_, false, 0);
|
||||
plm_set_audio_enabled(plm_, false);
|
||||
}
|
||||
g2_ = g1_ = resizegraphic(&graphic_[0], yn, xn);
|
||||
}
|
||||
|
@ -942,10 +945,10 @@ static optimizesize void ReadKeyboard(void) {
|
|||
case '[':
|
||||
switch (b[i++]) {
|
||||
case 'A': /* "\e[A" is up arrow */
|
||||
++volscale_;
|
||||
volscale_ *= 1.05f;
|
||||
break;
|
||||
case 'B': /* "\e[B" is down arrow */
|
||||
--volscale_;
|
||||
volscale_ *= 0.95f;
|
||||
break;
|
||||
case 'C': /* "\e[C" is right arrow */
|
||||
break;
|
||||
|
@ -1405,7 +1408,7 @@ int main(int argc, char *argv[]) {
|
|||
sigset_t wut;
|
||||
ShowCrashReports();
|
||||
gamma_ = 2.4;
|
||||
volscale_ -= 2;
|
||||
volscale_ = 1.f;
|
||||
dither_ = true;
|
||||
sigemptyset(&wut);
|
||||
sigaddset(&wut, SIGCHLD);
|
||||
|
|
Loading…
Reference in a new issue