├── makefile ├── pl_mpeg_extract_frames.c ├── README.md ├── pl_mpeg_player_sdl.c ├── pl_mpeg_player_gl.c └── pl_mpeg.h /makefile: -------------------------------------------------------------------------------- 1 | # By default, this makefile builds 3 example applications for pl_mpeg 2 | # - pl_mpeg_extract_frames: a command line tool that dumps all frames into BMPs 3 | # - pl_mpeg_player_gl: a video player using SDL2 and OpenGL 4 | # - pl_mpeg_player_sdl: a video player using SDL2 and it's built-in 2d renderer 5 | 6 | # The players require the SDL2 library to be installed. Please note that the 7 | # linker flags for the GL version have only been tested on Linux. PRs welcome! 8 | 9 | # To build individually: 10 | # make extract 11 | # make player_sdl 12 | # make player_gl 13 | 14 | 15 | CC ?= gcc 16 | CFLAGS ?= -std=gnu99 -O3 17 | LFLAGS ?= 18 | 19 | TARGET_EXTRACT ?= pl_mpeg_extract_frames 20 | TARGET_SDL ?= pl_mpeg_player_sdl 21 | TARGET_GL ?= pl_mpeg_player_gl 22 | 23 | SDL_CFLAGS = $(shell sdl2-config --cflags) 24 | SDL_LFLAGS = $(shell sdl2-config --libs) 25 | 26 | 27 | # Determine the right opengl libs for this platform 28 | UNAME_S := $(shell uname -s) 29 | 30 | # macOS 31 | ifeq ($(UNAME_S), Darwin) 32 | GL_LFLAGS = -GLU -framework OpenGL 33 | 34 | # Linux 35 | else ifeq ($(UNAME_S), Linux) 36 | GL_LFLAGS = -lOpenGL -lGLEW 37 | 38 | # Windows Msys 39 | else ifeq ($(shell uname -o), Msys) 40 | GL_LFLAGS = -lopengl32 -lglew32 41 | endif 42 | 43 | all: $(TARGET_EXTRACT) $(TARGET_SDL) $(TARGET_GL) 44 | extract: $(TARGET_EXTRACT) 45 | player_sdl: $(TARGET_SDL) 46 | player_gl: $(TARGET_GL) 47 | 48 | $(TARGET_EXTRACT):pl_mpeg_extract_frames.c pl_mpeg.h 49 | $(CC) $(CFLAGS) pl_mpeg_extract_frames.c -o $(TARGET_EXTRACT) $(LFLAGS) 50 | 51 | $(TARGET_SDL):pl_mpeg_player_sdl.c pl_mpeg.h 52 | $(CC) $(CFLAGS) $(SDL_CFLAGS) pl_mpeg_player_sdl.c -o $(TARGET_SDL) $(LFLAGS) $(SDL_LFLAGS) 53 | 54 | $(TARGET_GL):pl_mpeg_player_gl.c pl_mpeg.h 55 | $(CC) $(CFLAGS) $(SDL_CFLAGS) pl_mpeg_player_gl.c -o $(TARGET_GL) $(LFLAGS) $(GL_LFLAGS) $(SDL_LFLAGS) 56 | 57 | .PHONY: clean 58 | clean: 59 | $(RM) $(TARGET_EXTRACT) $(TARGET_SDL) $(TARGET_GL) 60 | -------------------------------------------------------------------------------- /pl_mpeg_extract_frames.c: -------------------------------------------------------------------------------- 1 | /* 2 | PL_MPEG Example - extract all frames of an mpg file and store as BMP 3 | SPDX-License-Identifier: MIT 4 | 5 | Dominic Szablewski - https://phoboslab.org 6 | 7 | 8 | -- Usage 9 | 10 | pl_mpeg_extract_frames 11 | 12 | Images named 000001.bmp, 000002.bmp... will be created in the current directory 13 | 14 | 15 | -- About 16 | 17 | This program demonstrates how to extract all video frames from an MPEG-PS file. 18 | Frames are saved as BMP. 19 | 20 | */ 21 | 22 | #include 23 | 24 | #define PL_MPEG_IMPLEMENTATION 25 | #include "pl_mpeg.h" 26 | 27 | int write_bmp(const char *path, int width, int height, uint8_t *pixels) { 28 | FILE *fh = fopen(path, "wb"); 29 | if (!fh) { 30 | return 0; 31 | } 32 | 33 | int padded_width = (width * 3 + 3) & (~3); 34 | int padding = padded_width - (width * 3); 35 | int data_size = padded_width * height; 36 | int file_size = 54 + data_size; 37 | 38 | fwrite("BM", 1, 2, fh); 39 | fwrite(&file_size, 1, 4, fh); 40 | fwrite("\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00\x00\x00", 1, 12, fh); 41 | fwrite(&width, 1, 4, fh); 42 | fwrite(&height, 1, 4, fh); 43 | fwrite("\x01\x00\x18\x00\x00\x00\x00\x00", 1, 8, fh); // planes, bpp, compression 44 | fwrite(&data_size, 1, 4, fh); 45 | fwrite("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 1, 16, fh); 46 | 47 | for (int y = height - 1; y >= 0; y--) { 48 | fwrite(pixels + y * width * 3, 3, width, fh); 49 | fwrite("\x00\x00\x00\x00", 1, padding, fh); 50 | } 51 | fclose(fh); 52 | return file_size; 53 | } 54 | 55 | int main(int argc, char *argv[]) { 56 | if (argc < 2) { 57 | printf("Usage: pl_mpeg_extract_frames \n"); 58 | return 1; 59 | } 60 | 61 | plm_t *plm = plm_create_with_filename(argv[1]); 62 | if (!plm) { 63 | printf("Couldn't open file %s\n", argv[1]); 64 | return 1; 65 | } 66 | 67 | plm_set_audio_enabled(plm, FALSE); 68 | 69 | int w = plm_get_width(plm); 70 | int h = plm_get_height(plm); 71 | uint8_t *pixels = (uint8_t *)malloc(w * h * 3); 72 | 73 | char bmp_name[16]; 74 | plm_frame_t *frame = NULL; 75 | 76 | for (int i = 1; frame = plm_decode_video(plm); i++) { 77 | plm_frame_to_bgr(frame, pixels, w * 3); // BMP expects BGR ordering 78 | 79 | sprintf(bmp_name, "%06d.bmp", i); 80 | printf("Writing %s\n", bmp_name); 81 | write_bmp(bmp_name, w, h, pixels); 82 | } 83 | 84 | return 0; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer 2 | 3 | Single-file MIT licensed library for C/C++ 4 | 5 | See [pl_mpeg.h](https://github.com/phoboslab/pl_mpeg/blob/master/pl_mpeg.h) for 6 | the documentation. 7 | 8 | 9 | ## Why? 10 | 11 | This is meant as a simple way to get video playback into your app or game. Other 12 | solutions, such as ffmpeg require huge libraries and a lot of glue code. 13 | 14 | MPEG1 is an old and inefficient codec, but it's still good enough for many use 15 | cases. All patents related to MPEG1 and MP2 have expired, so it's completely 16 | free now. 17 | 18 | This library does not make use of any SIMD instructions, but because of 19 | the relative simplicity of the codec it still manages to decode 4k60fps video 20 | on a single CPU core (on my i7-6700k at least). 21 | 22 | 23 | ## Building 24 | 25 | A Makefile to build the example applications is included. 26 | 27 | - `pl_mpeg_extract_frames`: a command line tool that dumps all frames into BMPs 28 | - `pl_mpeg_player_gl`: a video player using SDL2 and OpenGL 29 | - `pl_mpeg_player_sdl`: a video player using SDL2 and it's built-in 2d renderer 30 | 31 | The players require the SDL2 library to be installed. 32 | 33 | ```shell 34 | make # build all 35 | make extract # only build pl_mpeg_extract_frames 36 | make player_gl # only build pl_mpeg_player_gl 37 | make player_sdl # only build pl_mpeg_player_sdl 38 | ``` 39 | 40 | 41 | ## Encoding for PL_MPEG 42 | 43 | Most [MPEG-PS](https://en.wikipedia.org/wiki/MPEG_program_stream) (`.mpg`) files 44 | containing MPEG1 Video ("mpeg1") and MPEG1 Audio Layer II ("mp2") streams should 45 | work with PL_MPEG. Note that `.mpg` files can also contain MPEG2 Video, which is 46 | not supported by this library. 47 | 48 | You can encode video in a suitable format using ffmpeg: 49 | 50 | ``` 51 | ffmpeg -i input.mp4 -c:v mpeg1video -q:v 0 -c:a libtwolame -b:a 224k -format mpeg output.mpg 52 | ``` 53 | 54 | `-q:v` sets a fixed video quality with a variable bitrate, where `0` is the 55 | highest. You may use `-b:v` to set a fixed bitrate instead; e.g. 56 | `-b:v 2000k` for 2000 kbit/s. Please refer to the 57 | [ffmpeg documentation](http://ffmpeg.org/ffmpeg.html#Options) for more details. 58 | 59 | If you just want to quickly test the library, try this file: 60 | 61 | https://phoboslab.org/files/bjork-all-is-full-of-love.mpg 62 | 63 | 64 | ## Limitations 65 | 66 | - no error reporting. PL_MPEG will silently ignore any invalid data. 67 | - the pts (presentation time stamp) for packets in the MPEG-PS container is 68 | ignored. This may cause sync issues with some files. 69 | - bugs, probably. 70 | -------------------------------------------------------------------------------- /pl_mpeg_player_sdl.c: -------------------------------------------------------------------------------- 1 | /* 2 | PL_MPEG Example - Video player using SDL2's accelerated 2d renderer 3 | SPDX-License-Identifier: MIT 4 | 5 | Dominic Szablewski - https://phoboslab.org 6 | Siteswap - https://github.com/siteswapv4 7 | 8 | 9 | -- Usage 10 | 11 | pl_mpeg_player_sdl 12 | 13 | Use the arrow keys to seek forward/backward by 3 seconds. Click anywhere on the 14 | window to seek to seek through the whole file. 15 | 16 | 17 | -- About 18 | 19 | This program demonstrates a simple video/audio player using plmpeg for decoding 20 | and SDL2 with the accelerated 2d renderer. 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #define PL_MPEG_IMPLEMENTATION 28 | #include "pl_mpeg.h" 29 | 30 | #define SDL_MAIN_HANDLED 31 | #include 32 | 33 | typedef struct { 34 | plm_t *plm; 35 | double last_time; 36 | int wants_to_quit; 37 | 38 | SDL_Window *window; 39 | SDL_Renderer *renderer; 40 | SDL_Texture *texture; 41 | SDL_Rect rectangle; 42 | SDL_AudioDeviceID audio_device; 43 | } app_t; 44 | 45 | app_t * app_create(const char *filename); 46 | void app_update(app_t *self); 47 | void app_destroy(app_t *self); 48 | 49 | void app_on_video(plm_t *player, plm_frame_t *frame, void *user); 50 | void app_on_audio(plm_t *player, plm_samples_t *samples, void *user); 51 | 52 | 53 | 54 | app_t * app_create(const char *filename) { 55 | app_t *self = (app_t *)malloc(sizeof(app_t)); 56 | memset(self, 0, sizeof(app_t)); 57 | 58 | // Initialize plmpeg, load the video file, install decode callbacks 59 | self->plm = plm_create_with_filename(filename); 60 | if (!self->plm) { 61 | SDL_Log("Couldn't open %s", filename); 62 | exit(1); 63 | } 64 | 65 | if (!plm_probe(self->plm, 5000 * 1024)) { 66 | SDL_Log("No MPEG video or audio streams found in %s", filename); 67 | exit(1); 68 | } 69 | 70 | int samplerate = plm_get_samplerate(self->plm); 71 | 72 | SDL_Log( 73 | "Opened %s - framerate: %f, samplerate: %d, duration: %f", 74 | filename, 75 | plm_get_framerate(self->plm), 76 | plm_get_samplerate(self->plm), 77 | plm_get_duration(self->plm) 78 | ); 79 | 80 | plm_set_video_decode_callback(self->plm, app_on_video, self); 81 | plm_set_audio_decode_callback(self->plm, app_on_audio, self); 82 | 83 | plm_set_loop(self->plm, TRUE); 84 | plm_set_audio_enabled(self->plm, TRUE); 85 | plm_set_audio_stream(self->plm, 0); 86 | 87 | if (plm_get_num_audio_streams(self->plm) > 0) { 88 | // Initialize SDL Audio 89 | SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); 90 | SDL_AudioSpec audio_spec; 91 | SDL_memset(&audio_spec, 0, sizeof(audio_spec)); 92 | audio_spec.freq = samplerate; 93 | audio_spec.format = AUDIO_F32; 94 | audio_spec.channels = 2; 95 | audio_spec.samples = 4096; 96 | 97 | self->audio_device = SDL_OpenAudioDevice(NULL, 0, &audio_spec, NULL, 0); 98 | if (self->audio_device == 0) { 99 | SDL_Log("Failed to open audio device: %s", SDL_GetError()); 100 | } 101 | SDL_PauseAudioDevice(self->audio_device, 0); 102 | 103 | // Adjust the audio lead time according to the audio_spec buffer size 104 | plm_set_audio_lead_time(self->plm, (double)audio_spec.samples / (double)samplerate); 105 | } 106 | 107 | // Create SDL Window 108 | self->window = SDL_CreateWindow( 109 | "pl_mpeg", 110 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 111 | plm_get_width(self->plm), plm_get_height(self->plm), 112 | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE 113 | ); 114 | 115 | // Create SDL Renderer with vsync 116 | self->renderer = SDL_CreateRenderer( 117 | self->window, 118 | -1, 119 | SDL_RENDERER_PRESENTVSYNC 120 | ); 121 | 122 | // Create SDL texture with YUV format 123 | self->texture = SDL_CreateTexture( 124 | self->renderer, 125 | SDL_PIXELFORMAT_IYUV, 126 | SDL_TEXTUREACCESS_STREAMING, 127 | plm_get_width(self->plm), 128 | plm_get_height(self->plm) 129 | ); 130 | 131 | // Adjust rectangle to video size 132 | self->rectangle.w = plm_get_width(self->plm); 133 | self->rectangle.h = plm_get_height(self->plm); 134 | 135 | // Renderer will keep the aspect ratio of the video and center the content if the window is resized 136 | SDL_RenderSetLogicalSize(self->renderer, plm_get_width(self->plm), plm_get_height(self->plm)); 137 | 138 | // Mouse position based on the renderer logical size 139 | SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_SCALING, "1"); 140 | 141 | // Best render scale quality 142 | SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); 143 | 144 | return self; 145 | } 146 | 147 | void app_destroy(app_t *self) { 148 | plm_destroy(self->plm); 149 | 150 | if (self->audio_device) { 151 | SDL_CloseAudioDevice(self->audio_device); 152 | } 153 | 154 | if (self->texture) { 155 | SDL_DestroyTexture(self->texture); 156 | } 157 | 158 | if (self->renderer) { 159 | SDL_DestroyRenderer(self->renderer); 160 | } 161 | 162 | if (self->window) { 163 | SDL_DestroyWindow(self->window); 164 | } 165 | 166 | SDL_Quit(); 167 | 168 | free(self); 169 | } 170 | 171 | void app_update(app_t *self) { 172 | double seek_to = -1; 173 | 174 | SDL_Event ev; 175 | while (SDL_PollEvent(&ev)) { 176 | if ( 177 | ev.type == SDL_QUIT || 178 | (ev.type == SDL_KEYUP && ev.key.keysym.sym == SDLK_ESCAPE) 179 | ) { 180 | self->wants_to_quit = TRUE; 181 | } 182 | // Seek 3sec forward/backward using arrow keys 183 | if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_RIGHT) { 184 | seek_to = plm_get_time(self->plm) + 3; 185 | } 186 | else if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_LEFT) { 187 | seek_to = plm_get_time(self->plm) - 3; 188 | } 189 | } 190 | 191 | 192 | // Clear renderer, copy texture and present 193 | SDL_RenderClear(self->renderer); 194 | 195 | SDL_RenderCopy(self->renderer, self->texture, NULL, &self->rectangle); 196 | 197 | SDL_RenderPresent(self->renderer); 198 | 199 | // Compute the delta time since the last app_update(), limit max step to 200 | // 1/30th of a second 201 | double current_time = (double)SDL_GetTicks() / 1000.0; 202 | double elapsed_time = current_time - self->last_time; 203 | if (elapsed_time > 1.0 / 30.0) { 204 | elapsed_time = 1.0 / 30.0; 205 | } 206 | self->last_time = current_time; 207 | 208 | // Seek using mouse position 209 | int mouse_x, mouse_y; 210 | if (SDL_GetMouseState(&mouse_x, &mouse_y) & SDL_BUTTON(SDL_BUTTON_LEFT)) { 211 | int sx, sy; 212 | SDL_GetWindowSize(self->window, &sx, &sy); 213 | seek_to = plm_get_duration(self->plm) * ((float)mouse_x / (float)sx); 214 | } 215 | 216 | // Seek or advance decode 217 | if (seek_to != -1) { 218 | SDL_ClearQueuedAudio(self->audio_device); 219 | plm_seek(self->plm, seek_to, FALSE); 220 | } 221 | else { 222 | plm_decode(self->plm, elapsed_time); 223 | } 224 | 225 | if (plm_has_ended(self->plm)) { 226 | self->wants_to_quit = TRUE; 227 | } 228 | } 229 | 230 | void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user) { 231 | app_t *self = (app_t *)user; 232 | 233 | SDL_UpdateYUVTexture(self->texture, NULL, frame->y.data, frame->y.width, frame->cb.data, frame->cb.width, frame->cr.data, frame->cr.width); 234 | } 235 | 236 | void app_on_audio(plm_t *mpeg, plm_samples_t *samples, void *user) { 237 | app_t *self = (app_t *)user; 238 | 239 | // Hand the decoded samples over to SDL 240 | 241 | int size = sizeof(float) * samples->count * 2; 242 | SDL_QueueAudio(self->audio_device, samples->interleaved, size); 243 | } 244 | 245 | 246 | 247 | int main(int argc, char *argv[]) { 248 | if (argc < 2) { 249 | SDL_Log("Usage: pl_mpeg_player_sdl "); 250 | exit(1); 251 | } 252 | 253 | SDL_SetMainReady(); 254 | 255 | app_t *app = app_create(argv[1]); 256 | while (!app->wants_to_quit) { 257 | app_update(app); 258 | } 259 | app_destroy(app); 260 | 261 | return EXIT_SUCCESS; 262 | } 263 | -------------------------------------------------------------------------------- /pl_mpeg_player_gl.c: -------------------------------------------------------------------------------- 1 | /* 2 | PL_MPEG Example - Video player using SDL2/OpenGL for rendering 3 | SPDX-License-Identifier: MIT 4 | 5 | Dominic Szablewski - https://phoboslab.org 6 | 7 | 8 | -- Usage 9 | 10 | pl_mpeg_player_gl 11 | 12 | Use the arrow keys to seek forward/backward by 3 seconds. Click anywhere on the 13 | window to seek to seek through the whole file. 14 | 15 | 16 | -- About 17 | 18 | This program demonstrates a simple video/audio player using plmpeg for decoding 19 | and SDL2 with OpenGL for rendering and sound output. It was tested on Windows 20 | using Microsoft Visual Studio 2015 and on macOS using XCode 10.2 21 | 22 | This program can be configured to either convert the raw YCrCb data to RGB on 23 | the GPU (default), or to do it on CPU. Just pass APP_TEXTURE_MODE_RGB to 24 | app_create() to switch to do the conversion on the CPU. 25 | 26 | YCrCb->RGB conversion on the CPU is a very costly operation and should be 27 | avoided if possible. It easily takes as much time as all other mpeg1 decoding 28 | steps combined. 29 | 30 | */ 31 | 32 | #include 33 | #include 34 | 35 | #if defined(__APPLE__) && defined(__MACH__) 36 | // OSX 37 | #include 38 | #include 39 | #include 40 | 41 | void glCreateTextures(GLuint ignored, GLsizei n, GLuint *name) { 42 | glGenTextures(1, name); 43 | } 44 | #elif defined(__unix__) 45 | // Linux 46 | #include 47 | #include 48 | #else 49 | // WINDOWS 50 | #include 51 | 52 | #define GL3_PROTOTYPES 1 53 | #include 54 | #pragma comment(lib, "glew32.lib") 55 | 56 | #include 57 | #pragma comment(lib, "opengl32.lib") 58 | 59 | #include 60 | #include 61 | #pragma comment(lib, "SDL2.lib") 62 | #pragma comment(lib, "SDL2main.lib") 63 | #endif 64 | 65 | #define PL_MPEG_IMPLEMENTATION 66 | #include "pl_mpeg.h" 67 | 68 | 69 | #define APP_SHADER_SOURCE(...) #__VA_ARGS__ 70 | 71 | const char * const APP_VERTEX_SHADER = APP_SHADER_SOURCE( 72 | uniform vec2 texture_crop_size; 73 | attribute vec2 vertex; 74 | varying vec2 tex_coord; 75 | 76 | void main() { 77 | tex_coord = vertex * texture_crop_size; 78 | gl_Position = vec4((vertex * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0); 79 | } 80 | ); 81 | 82 | const char * const APP_FRAGMENT_SHADER_YCRCB = APP_SHADER_SOURCE( 83 | uniform sampler2D texture_y; 84 | uniform sampler2D texture_cb; 85 | uniform sampler2D texture_cr; 86 | varying vec2 tex_coord; 87 | 88 | mat4 rec601 = mat4( 89 | 1.16438, 0.00000, 1.59603, -0.87079, 90 | 1.16438, -0.39176, -0.81297, 0.52959, 91 | 1.16438, 2.01723, 0.00000, -1.08139, 92 | 0, 0, 0, 1 93 | ); 94 | 95 | void main() { 96 | float y = texture2D(texture_y, tex_coord).r; 97 | float cb = texture2D(texture_cb, tex_coord).r; 98 | float cr = texture2D(texture_cr, tex_coord).r; 99 | 100 | gl_FragColor = vec4(y, cb, cr, 1.0) * rec601; 101 | } 102 | ); 103 | 104 | const char * const APP_FRAGMENT_SHADER_RGB = APP_SHADER_SOURCE( 105 | uniform sampler2D texture_rgb; 106 | varying vec2 tex_coord; 107 | 108 | void main() { 109 | gl_FragColor = vec4(texture2D(texture_rgb, tex_coord).rgb, 1.0); 110 | } 111 | ); 112 | 113 | #undef APP_SHADER_SOURCE 114 | 115 | #define APP_TEXTURE_MODE_YCRCB 1 116 | #define APP_TEXTURE_MODE_RGB 2 117 | 118 | typedef struct { 119 | plm_t *plm; 120 | double last_time; 121 | int wants_to_quit; 122 | 123 | SDL_Window *window; 124 | SDL_AudioDeviceID audio_device; 125 | 126 | SDL_GLContext gl; 127 | 128 | GLuint shader_program; 129 | GLuint vertex_shader; 130 | GLuint fragment_shader; 131 | 132 | int texture_mode; 133 | GLuint texture_y; 134 | GLuint texture_cb; 135 | GLuint texture_cr; 136 | GLuint texture_crop_size; 137 | 138 | GLuint texture_rgb; 139 | uint8_t *rgb_data; 140 | } app_t; 141 | 142 | app_t * app_create(const char *filename, int texture_mode); 143 | void app_update(app_t *self); 144 | void app_destroy(app_t *self); 145 | 146 | GLuint app_compile_shader(app_t *self, GLenum type, const char *source); 147 | GLuint app_create_texture(app_t *self, GLuint index, const char *name); 148 | void app_update_texture(app_t *self, GLuint unit, GLuint texture, plm_plane_t *plane); 149 | 150 | void app_on_video(plm_t *player, plm_frame_t *frame, void *user); 151 | void app_on_audio(plm_t *player, plm_samples_t *samples, void *user); 152 | 153 | 154 | 155 | app_t * app_create(const char *filename, int texture_mode) { 156 | app_t *self = (app_t *)malloc(sizeof(app_t)); 157 | memset(self, 0, sizeof(app_t)); 158 | 159 | self->texture_mode = texture_mode; 160 | 161 | // Initialize plmpeg, load the video file, install decode callbacks 162 | self->plm = plm_create_with_filename(filename); 163 | if (!self->plm) { 164 | SDL_Log("Couldn't open %s", filename); 165 | exit(1); 166 | } 167 | 168 | if (!plm_probe(self->plm, 5000 * 1024)) { 169 | SDL_Log("No MPEG video or audio streams found in %s", filename); 170 | exit(1); 171 | } 172 | 173 | int samplerate = plm_get_samplerate(self->plm); 174 | 175 | SDL_Log( 176 | "Opened %s - framerate: %f, samplerate: %d, duration: %f", 177 | filename, 178 | plm_get_framerate(self->plm), 179 | plm_get_samplerate(self->plm), 180 | plm_get_duration(self->plm) 181 | ); 182 | 183 | plm_set_video_decode_callback(self->plm, app_on_video, self); 184 | plm_set_audio_decode_callback(self->plm, app_on_audio, self); 185 | 186 | plm_set_loop(self->plm, TRUE); 187 | plm_set_audio_enabled(self->plm, TRUE); 188 | plm_set_audio_stream(self->plm, 0); 189 | 190 | if (plm_get_num_audio_streams(self->plm) > 0) { 191 | // Initialize SDL Audio 192 | SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); 193 | SDL_AudioSpec audio_spec; 194 | SDL_memset(&audio_spec, 0, sizeof(audio_spec)); 195 | audio_spec.freq = samplerate; 196 | audio_spec.format = AUDIO_F32; 197 | audio_spec.channels = 2; 198 | audio_spec.samples = 4096; 199 | 200 | self->audio_device = SDL_OpenAudioDevice(NULL, 0, &audio_spec, NULL, 0); 201 | if (self->audio_device == 0) { 202 | SDL_Log("Failed to open audio device: %s", SDL_GetError()); 203 | } 204 | SDL_PauseAudioDevice(self->audio_device, 0); 205 | 206 | // Adjust the audio lead time according to the audio_spec buffer size 207 | plm_set_audio_lead_time(self->plm, (double)audio_spec.samples / (double)samplerate); 208 | } 209 | 210 | // Create SDL Window 211 | self->window = SDL_CreateWindow( 212 | "pl_mpeg", 213 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 214 | plm_get_width(self->plm), plm_get_height(self->plm), 215 | SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE 216 | ); 217 | self->gl = SDL_GL_CreateContext(self->window); 218 | 219 | SDL_GL_SetSwapInterval(1); 220 | 221 | #if defined(__APPLE__) && defined(__MACH__) 222 | // OSX 223 | // (nothing to do here) 224 | #else 225 | // Windows, Linux 226 | glewExperimental = GL_TRUE; 227 | glewInit(); 228 | #endif 229 | 230 | 231 | // Setup OpenGL shaders and textures 232 | const char * fsh = self->texture_mode == APP_TEXTURE_MODE_YCRCB 233 | ? APP_FRAGMENT_SHADER_YCRCB 234 | : APP_FRAGMENT_SHADER_RGB; 235 | 236 | self->fragment_shader = app_compile_shader(self, GL_FRAGMENT_SHADER, fsh); 237 | self->vertex_shader = app_compile_shader(self, GL_VERTEX_SHADER, APP_VERTEX_SHADER); 238 | 239 | self->shader_program = glCreateProgram(); 240 | glAttachShader(self->shader_program, self->vertex_shader); 241 | glAttachShader(self->shader_program, self->fragment_shader); 242 | glLinkProgram(self->shader_program); 243 | glUseProgram(self->shader_program); 244 | 245 | // Create textures for YCrCb or RGB rendering 246 | if (self->texture_mode == APP_TEXTURE_MODE_YCRCB) { 247 | self->texture_y = app_create_texture(self, 0, "texture_y"); 248 | self->texture_cb = app_create_texture(self, 1, "texture_cb"); 249 | self->texture_cr = app_create_texture(self, 2, "texture_cr"); 250 | } 251 | else { 252 | self->texture_rgb = app_create_texture(self, 0, "texture_rgb"); 253 | int num_pixels = plm_get_width(self->plm) * plm_get_height(self->plm); 254 | self->rgb_data = (uint8_t*)malloc(num_pixels * 3); 255 | } 256 | self->texture_crop_size = glGetUniformLocation(self->shader_program, "texture_crop_size"); 257 | 258 | return self; 259 | } 260 | 261 | void app_destroy(app_t *self) { 262 | plm_destroy(self->plm); 263 | 264 | if (self->texture_mode == APP_TEXTURE_MODE_RGB) { 265 | free(self->rgb_data); 266 | } 267 | 268 | if (self->audio_device) { 269 | SDL_CloseAudioDevice(self->audio_device); 270 | } 271 | 272 | SDL_GL_DeleteContext(self->gl); 273 | SDL_Quit(); 274 | 275 | free(self); 276 | } 277 | 278 | void app_update(app_t *self) { 279 | double seek_to = -1; 280 | 281 | SDL_Event ev; 282 | while (SDL_PollEvent(&ev)) { 283 | if ( 284 | ev.type == SDL_QUIT || 285 | (ev.type == SDL_KEYUP && ev.key.keysym.sym == SDLK_ESCAPE) 286 | ) { 287 | self->wants_to_quit = TRUE; 288 | } 289 | 290 | if ( 291 | ev.type == SDL_WINDOWEVENT && 292 | ev.window.event == SDL_WINDOWEVENT_SIZE_CHANGED 293 | ) { 294 | glViewport(0, 0, ev.window.data1, ev.window.data2); 295 | } 296 | 297 | // Seek 3sec forward/backward using arrow keys 298 | if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_RIGHT) { 299 | seek_to = plm_get_time(self->plm) + 3; 300 | } 301 | else if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_LEFT) { 302 | seek_to = plm_get_time(self->plm) - 3; 303 | } 304 | } 305 | 306 | // Compute the delta time since the last app_update(), limit max step to 307 | // 1/30th of a second 308 | double current_time = (double)SDL_GetTicks() / 1000.0; 309 | double elapsed_time = current_time - self->last_time; 310 | if (elapsed_time > 1.0 / 30.0) { 311 | elapsed_time = 1.0 / 30.0; 312 | } 313 | self->last_time = current_time; 314 | 315 | // Seek using mouse position 316 | int mouse_x, mouse_y; 317 | if (SDL_GetMouseState(&mouse_x, &mouse_y) & SDL_BUTTON(SDL_BUTTON_LEFT)) { 318 | int sx, sy; 319 | SDL_GetWindowSize(self->window, &sx, &sy); 320 | seek_to = plm_get_duration(self->plm) * ((float)mouse_x / (float)sx); 321 | } 322 | 323 | // Seek or advance decode 324 | if (seek_to != -1) { 325 | SDL_ClearQueuedAudio(self->audio_device); 326 | plm_seek(self->plm, seek_to, FALSE); 327 | } 328 | else { 329 | plm_decode(self->plm, elapsed_time); 330 | } 331 | 332 | if (plm_has_ended(self->plm)) { 333 | self->wants_to_quit = TRUE; 334 | } 335 | 336 | glClear(GL_COLOR_BUFFER_BIT); 337 | glRectf(0.0, 0.0, 1.0, 1.0); 338 | SDL_GL_SwapWindow(self->window); 339 | } 340 | 341 | GLuint app_compile_shader(app_t *self, GLenum type, const char *source) { 342 | GLuint shader = glCreateShader(type); 343 | glShaderSource(shader, 1, &source, NULL); 344 | glCompileShader(shader); 345 | 346 | GLint success; 347 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 348 | if (!success) { 349 | int log_written; 350 | char log[256]; 351 | glGetShaderInfoLog(shader, 256, &log_written, log); 352 | SDL_Log("Error compiling shader: %s.\n", log); 353 | } 354 | return shader; 355 | } 356 | 357 | GLuint app_create_texture(app_t *self, GLuint index, const char *name) { 358 | GLuint texture; 359 | glCreateTextures(GL_TEXTURE_2D, 1, &texture); 360 | 361 | glBindTexture(GL_TEXTURE_2D, texture); 362 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 363 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 364 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 365 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 366 | 367 | glUniform1i(glGetUniformLocation(self->shader_program, name), index); 368 | return texture; 369 | } 370 | 371 | void app_update_texture(app_t *self, GLuint unit, GLuint texture, plm_plane_t *plane) { 372 | glActiveTexture(unit); 373 | glBindTexture(GL_TEXTURE_2D, texture); 374 | glTexImage2D( 375 | GL_TEXTURE_2D, 0, GL_LUMINANCE, plane->width, plane->height, 0, 376 | GL_LUMINANCE, GL_UNSIGNED_BYTE, plane->data 377 | ); 378 | } 379 | 380 | void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user) { 381 | app_t *self = (app_t *)user; 382 | 383 | // Hand the decoded data over to OpenGL. For the RGB texture mode, the 384 | // YCrCb->RGB conversion is done on the CPU. 385 | 386 | if (self->texture_mode == APP_TEXTURE_MODE_YCRCB) { 387 | app_update_texture(self, GL_TEXTURE0, self->texture_y, &frame->y); 388 | app_update_texture(self, GL_TEXTURE1, self->texture_cb, &frame->cb); 389 | app_update_texture(self, GL_TEXTURE2, self->texture_cr, &frame->cr); 390 | 391 | // The dimensions of the planes are always rounded up to the next 392 | // multiple of 16. We don't want to display these extra pixels, so 393 | // calculate the crop w/h and hand it over to the shader program. 394 | float cw = (float)frame->width / (float)frame->y.width; 395 | float ch = (float)frame->height / (float)frame->y.height; 396 | glUniform2f(self->texture_crop_size, cw, ch); 397 | } 398 | else { 399 | plm_frame_to_rgb(frame, self->rgb_data, frame->width * 3); 400 | 401 | glBindTexture(GL_TEXTURE_2D, self->texture_rgb); 402 | glTexImage2D( 403 | GL_TEXTURE_2D, 0, GL_RGB, frame->width, frame->height, 0, 404 | GL_RGB, GL_UNSIGNED_BYTE, self->rgb_data 405 | ); 406 | 407 | // plm_frame_to_rgb() always returns the cropped portion of the display 408 | // size, so the crop size is always 1.0, 1.0 409 | glUniform2f(self->texture_crop_size, 1.0, 1.0); 410 | } 411 | } 412 | 413 | void app_on_audio(plm_t *mpeg, plm_samples_t *samples, void *user) { 414 | app_t *self = (app_t *)user; 415 | 416 | // Hand the decoded samples over to SDL 417 | 418 | int size = sizeof(float) * samples->count * 2; 419 | SDL_QueueAudio(self->audio_device, samples->interleaved, size); 420 | } 421 | 422 | 423 | 424 | int main(int argc, char *argv[]) { 425 | if (argc < 2) { 426 | SDL_Log("Usage: pl_mpeg_player_gl "); 427 | exit(1); 428 | } 429 | 430 | app_t *app = app_create(argv[1], APP_TEXTURE_MODE_YCRCB); 431 | while (!app->wants_to_quit) { 432 | app_update(app); 433 | } 434 | app_destroy(app); 435 | 436 | return EXIT_SUCCESS; 437 | } 438 | -------------------------------------------------------------------------------- /pl_mpeg.h: -------------------------------------------------------------------------------- 1 | /* 2 | PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer 3 | SPDX-License-Identifier: MIT 4 | 5 | Dominic Szablewski - https://phoboslab.org 6 | 7 | 8 | -- Synopsis 9 | 10 | // Define `PL_MPEG_IMPLEMENTATION` in *one* C/C++ file before including this 11 | // library to create the implementation. 12 | 13 | #define PL_MPEG_IMPLEMENTATION 14 | #include "plmpeg.h" 15 | 16 | // This function gets called for each decoded video frame 17 | void my_video_callback(plm_t *plm, plm_frame_t *frame, void *user) { 18 | // Do something with frame->y.data, frame->cr.data, frame->cb.data 19 | } 20 | 21 | // This function gets called for each decoded audio frame 22 | void my_audio_callback(plm_t *plm, plm_samples_t *frame, void *user) { 23 | // Do something with samples->interleaved 24 | } 25 | 26 | // Load a .mpg (MPEG Program Stream) file 27 | plm_t *plm = plm_create_with_filename("some-file.mpg"); 28 | 29 | // Install the video & audio decode callbacks 30 | plm_set_video_decode_callback(plm, my_video_callback, my_data); 31 | plm_set_audio_decode_callback(plm, my_audio_callback, my_data); 32 | 33 | 34 | // Decode 35 | do { 36 | plm_decode(plm, time_since_last_call); 37 | } while (!plm_has_ended(plm)); 38 | 39 | // All done 40 | plm_destroy(plm); 41 | 42 | 43 | 44 | -- Documentation 45 | 46 | This library provides several interfaces to load, demux and decode MPEG video 47 | and audio data. A high-level API combines the demuxer, video & audio decoders 48 | in an easy to use wrapper. 49 | 50 | Lower-level APIs for accessing the demuxer, video decoder and audio decoder, 51 | as well as providing different data sources are also available. 52 | 53 | Interfaces are written in an object oriented style, meaning you create object 54 | instances via various different constructor functions (plm_*create()), 55 | do some work on them and later dispose them via plm_*destroy(). 56 | 57 | plm_* ......... the high-level interface, combining demuxer and decoders 58 | plm_buffer_* .. the data source used by all interfaces 59 | plm_demux_* ... the MPEG-PS demuxer 60 | plm_video_* ... the MPEG1 Video ("mpeg1") decoder 61 | plm_audio_* ... the MPEG1 Audio Layer II ("mp2") decoder 62 | 63 | 64 | With the high-level interface you have two options to decode video & audio: 65 | 66 | 1. Use plm_decode() and just hand over the delta time since the last call. 67 | It will decode everything needed and call your callbacks (specified through 68 | plm_set_{video|audio}_decode_callback()) any number of times. 69 | 70 | 2. Use plm_decode_video() and plm_decode_audio() to decode exactly one 71 | frame of video or audio data at a time. How you handle the synchronization 72 | of both streams is up to you. 73 | 74 | If you only want to decode video *or* audio through these functions, you should 75 | disable the other stream (plm_set_{video|audio}_enabled(FALSE)) 76 | 77 | Video data is decoded into a struct with all 3 planes (Y, Cr, Cb) stored in 78 | separate buffers. You can either convert this to RGB on the CPU (slow) via the 79 | plm_frame_to_rgb() function or do it on the GPU with the following matrix: 80 | 81 | mat4 bt601 = mat4( 82 | 1.16438, 0.00000, 1.59603, -0.87079, 83 | 1.16438, -0.39176, -0.81297, 0.52959, 84 | 1.16438, 2.01723, 0.00000, -1.08139, 85 | 0, 0, 0, 1 86 | ); 87 | gl_FragColor = vec4(y, cb, cr, 1.0) * bt601; 88 | 89 | Audio data is decoded into a struct with either one single float array with the 90 | samples for the left and right channel interleaved, or if the 91 | PLM_AUDIO_SEPARATE_CHANNELS is defined *before* including this library, into 92 | two separate float arrays - one for each channel. 93 | 94 | 95 | Data can be supplied to the high level interface, the demuxer and the decoders 96 | in three different ways: 97 | 98 | 1. Using plm_create_from_filename() or with a file handle with 99 | plm_create_from_file(). 100 | 101 | 2. Using plm_create_with_memory() and supplying a pointer to memory that 102 | contains the whole file. 103 | 104 | 3. Using plm_create_with_buffer(), supplying your own plm_buffer_t instance and 105 | periodically writing to this buffer. 106 | 107 | When using your own plm_buffer_t instance, you can fill this buffer using 108 | plm_buffer_write(). You can either monitor plm_buffer_get_remaining() and push 109 | data when appropriate, or install a callback on the buffer with 110 | plm_buffer_set_load_callback() that gets called whenever the buffer needs more 111 | data. 112 | 113 | A buffer created with plm_buffer_create_with_capacity() is treated as a ring 114 | buffer, meaning that data that has already been read, will be discarded. In 115 | contrast, a buffer created with plm_buffer_create_for_appending() will keep all 116 | data written to it in memory. This enables seeking in the already loaded data. 117 | 118 | 119 | There should be no need to use the lower level plm_demux_*, plm_video_* and 120 | plm_audio_* functions, if all you want to do is read/decode an MPEG-PS file. 121 | However, if you get raw mpeg1video data or raw mp2 audio data from a different 122 | source, these functions can be used to decode the raw data directly. Similarly, 123 | if you only want to analyze an MPEG-PS file or extract raw video or audio 124 | packets from it, you can use the plm_demux_* functions. 125 | 126 | 127 | This library uses malloc(), realloc() and free() to manage memory. Typically 128 | all allocation happens up-front when creating the interface. However, the 129 | default buffer size may be too small for certain inputs. In these cases plmpeg 130 | will realloc() the buffer with a larger size whenever needed. You can configure 131 | the default buffer size by defining PLM_BUFFER_DEFAULT_SIZE *before* 132 | including this library. 133 | 134 | You can also define PLM_MALLOC, PLM_REALLOC and PLM_FREE to provide your own 135 | memory management functions. 136 | 137 | 138 | See below for detailed the API documentation. 139 | 140 | */ 141 | 142 | 143 | #ifndef PL_MPEG_H 144 | #define PL_MPEG_H 145 | 146 | #include 147 | 148 | 149 | #ifdef __cplusplus 150 | extern "C" { 151 | #endif 152 | 153 | // ----------------------------------------------------------------------------- 154 | // Public Data Types 155 | 156 | 157 | // Object types for the various interfaces 158 | 159 | typedef struct plm_t plm_t; 160 | typedef struct plm_buffer_t plm_buffer_t; 161 | typedef struct plm_demux_t plm_demux_t; 162 | typedef struct plm_video_t plm_video_t; 163 | typedef struct plm_audio_t plm_audio_t; 164 | 165 | 166 | // Demuxed MPEG PS packet 167 | // The type maps directly to the various MPEG-PES start codes. PTS is the 168 | // presentation time stamp of the packet in seconds. Note that not all packets 169 | // have a PTS value, indicated by PLM_PACKET_INVALID_TS. 170 | 171 | #define PLM_PACKET_INVALID_TS -1 172 | 173 | typedef struct { 174 | int type; 175 | double pts; 176 | size_t length; 177 | uint8_t *data; 178 | } plm_packet_t; 179 | 180 | 181 | // Decoded Video Plane 182 | // The byte length of the data is width * height. Note that different planes 183 | // have different sizes: the Luma plane (Y) is double the size of each of 184 | // the two Chroma planes (Cr, Cb) - i.e. 4 times the byte length. 185 | // Also note that the size of the plane does *not* denote the size of the 186 | // displayed frame. The sizes of planes are always rounded up to the nearest 187 | // macroblock (16px). 188 | 189 | typedef struct { 190 | unsigned int width; 191 | unsigned int height; 192 | uint8_t *data; 193 | } plm_plane_t; 194 | 195 | 196 | // Decoded Video Frame 197 | // width and height denote the desired display size of the frame. This may be 198 | // different from the internal size of the 3 planes. 199 | 200 | typedef struct { 201 | double time; 202 | unsigned int width; 203 | unsigned int height; 204 | plm_plane_t y; 205 | plm_plane_t cr; 206 | plm_plane_t cb; 207 | } plm_frame_t; 208 | 209 | 210 | // Callback function type for decoded video frames used by the high-level 211 | // plm_* interface 212 | 213 | typedef void(*plm_video_decode_callback) 214 | (plm_t *self, plm_frame_t *frame, void *user); 215 | 216 | 217 | // Decoded Audio Samples 218 | // Samples are stored as normalized (-1, 1) float either interleaved, or if 219 | // PLM_AUDIO_SEPARATE_CHANNELS is defined, in two separate arrays. 220 | // The `count` is always PLM_AUDIO_SAMPLES_PER_FRAME and just there for 221 | // convenience. 222 | 223 | #define PLM_AUDIO_SAMPLES_PER_FRAME 1152 224 | 225 | typedef struct { 226 | double time; 227 | unsigned int count; 228 | #ifdef PLM_AUDIO_SEPARATE_CHANNELS 229 | float left[PLM_AUDIO_SAMPLES_PER_FRAME]; 230 | float right[PLM_AUDIO_SAMPLES_PER_FRAME]; 231 | #else 232 | float interleaved[PLM_AUDIO_SAMPLES_PER_FRAME * 2]; 233 | #endif 234 | } plm_samples_t; 235 | 236 | 237 | // Callback function type for decoded audio samples used by the high-level 238 | // plm_* interface 239 | 240 | typedef void(*plm_audio_decode_callback) 241 | (plm_t *self, plm_samples_t *samples, void *user); 242 | 243 | 244 | // Callback function for plm_buffer when it needs more data 245 | 246 | typedef void(*plm_buffer_load_callback)(plm_buffer_t *self, void *user); 247 | 248 | 249 | // Callback function for plm_buffer when it needs to seek 250 | 251 | typedef void(*plm_buffer_seek_callback)(plm_buffer_t *self, size_t offset, void *user); 252 | 253 | 254 | // Callback function for plm_buffer when it needs to tell the position 255 | 256 | typedef size_t(*plm_buffer_tell_callback)(plm_buffer_t *self, void *user); 257 | 258 | 259 | // ----------------------------------------------------------------------------- 260 | // plm_* public API 261 | // High-Level API for loading/demuxing/decoding MPEG-PS data 262 | 263 | #ifndef PLM_NO_STDIO 264 | 265 | // Create a plmpeg instance with a filename. Returns NULL if the file could not 266 | // be opened. 267 | 268 | plm_t *plm_create_with_filename(const char *filename); 269 | 270 | 271 | // Create a plmpeg instance with a file handle. Pass TRUE to close_when_done to 272 | // let plmpeg call fclose() on the handle when plm_destroy() is called. 273 | 274 | plm_t *plm_create_with_file(FILE *fh, int close_when_done); 275 | 276 | #endif // PLM_NO_STDIO 277 | 278 | 279 | // Create a plmpeg instance with a pointer to memory as source. This assumes the 280 | // whole file is in memory. The memory is not copied. Pass TRUE to 281 | // free_when_done to let plmpeg call free() on the pointer when plm_destroy() 282 | // is called. 283 | 284 | plm_t *plm_create_with_memory(uint8_t *bytes, size_t length, int free_when_done); 285 | 286 | 287 | // Create a plmpeg instance with a plm_buffer as source. Pass TRUE to 288 | // destroy_when_done to let plmpeg call plm_buffer_destroy() on the buffer when 289 | // plm_destroy() is called. 290 | 291 | plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done); 292 | 293 | 294 | // Destroy a plmpeg instance and free all data. 295 | 296 | void plm_destroy(plm_t *self); 297 | 298 | 299 | // Get whether we have headers on all available streams and we can report the 300 | // number of video/audio streams, video dimensions, framerate and audio 301 | // samplerate. 302 | // This returns FALSE if the file is not an MPEG-PS file or - when not using a 303 | // file as source - when not enough data is available yet. 304 | 305 | int plm_has_headers(plm_t *self); 306 | 307 | 308 | // Probe the MPEG-PS data to find the actual number of video and audio streams 309 | // within the buffer. For certain files (e.g. VideoCD) this can be more accurate 310 | // than just reading the number of streams from the headers. 311 | // This should only be used when the underlying plm_buffer is seekable, i.e. for 312 | // files, fixed memory buffers or _for_appending buffers. If used with dynamic 313 | // memory buffers it will skip decoding the probesize! 314 | // The necessary probesize is dependent on the files you expect to read. Usually 315 | // a few hundred KB should be enough to find all streams. 316 | // Use plm_get_num_{audio|video}_streams() afterwards to get the number of 317 | // streams in the file. 318 | // Returns TRUE if any streams were found within the probesize. 319 | 320 | int plm_probe(plm_t *self, size_t probesize); 321 | 322 | 323 | // Get or set whether video decoding is enabled. Default TRUE. 324 | 325 | int plm_get_video_enabled(plm_t *self); 326 | void plm_set_video_enabled(plm_t *self, int enabled); 327 | 328 | 329 | // Get the number of video streams (0--1) reported in the system header. 330 | 331 | int plm_get_num_video_streams(plm_t *self); 332 | 333 | 334 | // Get the display width/height of the video stream. 335 | 336 | int plm_get_width(plm_t *self); 337 | int plm_get_height(plm_t *self); 338 | double plm_get_pixel_aspect_ratio(plm_t *self); 339 | 340 | 341 | // Get the framerate of the video stream in frames per second. 342 | 343 | double plm_get_framerate(plm_t *self); 344 | 345 | 346 | // Get or set whether audio decoding is enabled. Default TRUE. 347 | 348 | int plm_get_audio_enabled(plm_t *self); 349 | void plm_set_audio_enabled(plm_t *self, int enabled); 350 | 351 | 352 | // Get the number of audio streams (0--4) reported in the system header. 353 | 354 | int plm_get_num_audio_streams(plm_t *self); 355 | 356 | 357 | // Set the desired audio stream (0--3). Default 0. 358 | 359 | void plm_set_audio_stream(plm_t *self, int stream_index); 360 | 361 | 362 | // Get the samplerate of the audio stream in samples per second. 363 | 364 | int plm_get_samplerate(plm_t *self); 365 | 366 | 367 | // Get or set the audio lead time in seconds - the time in which audio samples 368 | // are decoded in advance (or behind) the video decode time. Typically this 369 | // should be set to the duration of the buffer of the audio API that you use 370 | // for output. E.g. for SDL2: (SDL_AudioSpec.samples / samplerate) 371 | 372 | double plm_get_audio_lead_time(plm_t *self); 373 | void plm_set_audio_lead_time(plm_t *self, double lead_time); 374 | 375 | 376 | // Get the current internal time in seconds. 377 | 378 | double plm_get_time(plm_t *self); 379 | 380 | 381 | // Get the video duration of the underlying source in seconds. 382 | 383 | double plm_get_duration(plm_t *self); 384 | 385 | 386 | // Rewind all buffers back to the beginning. 387 | 388 | void plm_rewind(plm_t *self); 389 | 390 | 391 | // Get or set looping. Default FALSE. 392 | 393 | int plm_get_loop(plm_t *self); 394 | void plm_set_loop(plm_t *self, int loop); 395 | 396 | 397 | // Get whether the file has ended. If looping is enabled, this will always 398 | // return FALSE. 399 | 400 | int plm_has_ended(plm_t *self); 401 | 402 | 403 | // Set the callback for decoded video frames used with plm_decode(). If no 404 | // callback is set, video data will be ignored and not be decoded. The *user 405 | // Parameter will be passed to your callback. 406 | 407 | void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp, void *user); 408 | 409 | 410 | // Set the callback for decoded audio samples used with plm_decode(). If no 411 | // callback is set, audio data will be ignored and not be decoded. The *user 412 | // Parameter will be passed to your callback. 413 | 414 | void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp, void *user); 415 | 416 | 417 | // Advance the internal timer by seconds and decode video/audio up to this time. 418 | // This will call the video_decode_callback and audio_decode_callback any number 419 | // of times. A frame-skip is not implemented, i.e. everything up to current time 420 | // will be decoded. 421 | 422 | void plm_decode(plm_t *self, double seconds); 423 | 424 | 425 | // Decode and return one video frame. Returns NULL if no frame could be decoded 426 | // (either because the source ended or data is corrupt). If you only want to 427 | // decode video, you should disable audio via plm_set_audio_enabled(). 428 | // The returned plm_frame_t is valid until the next call to plm_decode_video() 429 | // or until plm_destroy() is called. 430 | 431 | plm_frame_t *plm_decode_video(plm_t *self); 432 | 433 | 434 | // Decode and return one audio frame. Returns NULL if no frame could be decoded 435 | // (either because the source ended or data is corrupt). If you only want to 436 | // decode audio, you should disable video via plm_set_video_enabled(). 437 | // The returned plm_samples_t is valid until the next call to plm_decode_audio() 438 | // or until plm_destroy() is called. 439 | 440 | plm_samples_t *plm_decode_audio(plm_t *self); 441 | 442 | 443 | // Seek to the specified time, clamped between 0 -- duration. This can only be 444 | // used when the underlying plm_buffer is seekable, i.e. for files, fixed 445 | // memory buffers or _for_appending buffers. 446 | // If seek_exact is TRUE this will seek to the exact time, otherwise it will 447 | // seek to the last intra frame just before the desired time. Exact seeking can 448 | // be slow, because all frames up to the seeked one have to be decoded on top of 449 | // the previous intra frame. 450 | // If seeking succeeds, this function will call the video_decode_callback 451 | // exactly once with the target frame. If audio is enabled, it will also call 452 | // the audio_decode_callback any number of times, until the audio_lead_time is 453 | // satisfied. 454 | // Returns TRUE if seeking succeeded or FALSE if no frame could be found. 455 | 456 | int plm_seek(plm_t *self, double time, int seek_exact); 457 | 458 | 459 | // Similar to plm_seek(), but will not call the video_decode_callback, 460 | // audio_decode_callback or make any attempts to sync audio. 461 | // Returns the found frame or NULL if no frame could be found. 462 | 463 | plm_frame_t *plm_seek_frame(plm_t *self, double time, int seek_exact); 464 | 465 | 466 | 467 | // ----------------------------------------------------------------------------- 468 | // plm_buffer public API 469 | // Provides the data source for all other plm_* interfaces 470 | 471 | 472 | // The default size for buffers created from files or by the high-level API 473 | 474 | #ifndef PLM_BUFFER_DEFAULT_SIZE 475 | #define PLM_BUFFER_DEFAULT_SIZE (128 * 1024) 476 | #endif 477 | 478 | #ifndef PLM_NO_STDIO 479 | 480 | // Create a buffer instance with a filename. Returns NULL if the file could not 481 | // be opened. 482 | 483 | plm_buffer_t *plm_buffer_create_with_filename(const char *filename); 484 | 485 | 486 | // Create a buffer instance with a file handle. Pass TRUE to close_when_done 487 | // to let plmpeg call fclose() on the handle when plm_destroy() is called. 488 | 489 | plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done); 490 | 491 | #endif // PLM_NO_STDIO 492 | 493 | 494 | // Create a buffer instance with custom callbacks for loading, seeking and 495 | // telling the position. This behaves like a file handle, but with user-defined 496 | // callbacks, useful for file handles that don't use the standard FILE API. 497 | // Setting the length and closing/freeing has to be done manually. 498 | 499 | plm_buffer_t *plm_buffer_create_with_callbacks( 500 | plm_buffer_load_callback load_callback, 501 | plm_buffer_seek_callback seek_callback, 502 | plm_buffer_tell_callback tell_callback, 503 | size_t length, 504 | void *user 505 | ); 506 | 507 | 508 | // Create a buffer instance with a pointer to memory as source. This assumes 509 | // the whole file is in memory. The bytes are not copied. Pass 1 to 510 | // free_when_done to let plmpeg call free() on the pointer when plm_destroy() 511 | // is called. 512 | 513 | plm_buffer_t *plm_buffer_create_with_memory(uint8_t *bytes, size_t length, int free_when_done); 514 | 515 | 516 | // Create an empty buffer with an initial capacity. The buffer will grow 517 | // as needed. Data that has already been read, will be discarded. 518 | 519 | plm_buffer_t *plm_buffer_create_with_capacity(size_t capacity); 520 | 521 | 522 | // Create an empty buffer with an initial capacity. The buffer will grow 523 | // as needed. Decoded data will *not* be discarded. This can be used when 524 | // loading a file over the network, without needing to throttle the download. 525 | // It also allows for seeking in the already loaded data. 526 | 527 | plm_buffer_t *plm_buffer_create_for_appending(size_t initial_capacity); 528 | 529 | 530 | // Destroy a buffer instance and free all data 531 | 532 | void plm_buffer_destroy(plm_buffer_t *self); 533 | 534 | 535 | // Copy data into the buffer. If the data to be written is larger than the 536 | // available space, the buffer will realloc() with a larger capacity. 537 | // Returns the number of bytes written. This will always be the same as the 538 | // passed in length, except when the buffer was created _with_memory() for 539 | // which _write() is forbidden. 540 | 541 | size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length); 542 | 543 | 544 | // Mark the current byte length as the end of this buffer and signal that no 545 | // more data is expected to be written to it. This function should be called 546 | // just after the last plm_buffer_write(). 547 | // For _with_capacity buffers, this is cleared on a plm_buffer_rewind(). 548 | 549 | void plm_buffer_signal_end(plm_buffer_t *self); 550 | 551 | 552 | // Set a callback that is called whenever the buffer needs more data 553 | 554 | void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback fp, void *user); 555 | 556 | 557 | // Rewind the buffer back to the beginning. When loading from a file handle, 558 | // this also seeks to the beginning of the file. 559 | 560 | void plm_buffer_rewind(plm_buffer_t *self); 561 | 562 | 563 | // Get the total size. For files, this returns the file size. For all other 564 | // types it returns the number of bytes currently in the buffer. 565 | 566 | size_t plm_buffer_get_size(plm_buffer_t *self); 567 | 568 | 569 | // Get the number of remaining (yet unread) bytes in the buffer. This can be 570 | // useful to throttle writing. 571 | 572 | size_t plm_buffer_get_remaining(plm_buffer_t *self); 573 | 574 | 575 | // Get whether the read position of the buffer is at the end and no more data 576 | // is expected. 577 | 578 | int plm_buffer_has_ended(plm_buffer_t *self); 579 | 580 | 581 | 582 | // ----------------------------------------------------------------------------- 583 | // plm_demux public API 584 | // Demux an MPEG Program Stream (PS) data into separate packages 585 | 586 | 587 | // Various Packet Types 588 | 589 | static const int PLM_DEMUX_PACKET_PRIVATE = 0xBD; 590 | static const int PLM_DEMUX_PACKET_AUDIO_1 = 0xC0; 591 | static const int PLM_DEMUX_PACKET_AUDIO_2 = 0xC1; 592 | static const int PLM_DEMUX_PACKET_AUDIO_3 = 0xC2; 593 | static const int PLM_DEMUX_PACKET_AUDIO_4 = 0xC3; 594 | static const int PLM_DEMUX_PACKET_VIDEO_1 = 0xE0; 595 | 596 | 597 | // Create a demuxer with a plm_buffer as source. This will also attempt to read 598 | // the pack and system headers from the buffer. 599 | 600 | plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done); 601 | 602 | 603 | // Destroy a demuxer and free all data. 604 | 605 | void plm_demux_destroy(plm_demux_t *self); 606 | 607 | 608 | // Returns TRUE/FALSE whether pack and system headers have been found. This will 609 | // attempt to read the headers if non are present yet. 610 | 611 | int plm_demux_has_headers(plm_demux_t *self); 612 | 613 | 614 | // Probe the file for the actual number of video/audio streams. See 615 | // plm_probe() for the details. 616 | 617 | int plm_demux_probe(plm_demux_t *self, size_t probesize); 618 | 619 | 620 | // Returns the number of video streams found in the system header. This will 621 | // attempt to read the system header if non is present yet. 622 | 623 | int plm_demux_get_num_video_streams(plm_demux_t *self); 624 | 625 | 626 | // Returns the number of audio streams found in the system header. This will 627 | // attempt to read the system header if non is present yet. 628 | 629 | int plm_demux_get_num_audio_streams(plm_demux_t *self); 630 | 631 | 632 | // Rewind the internal buffer. See plm_buffer_rewind(). 633 | 634 | void plm_demux_rewind(plm_demux_t *self); 635 | 636 | 637 | // Get whether the file has ended. This will be cleared on seeking or rewind. 638 | 639 | int plm_demux_has_ended(plm_demux_t *self); 640 | 641 | 642 | // Seek to a packet of the specified type with a PTS just before specified time. 643 | // If force_intra is TRUE, only packets containing an intra frame will be 644 | // considered - this only makes sense when the type is PLM_DEMUX_PACKET_VIDEO_1. 645 | // Note that the specified time is considered 0-based, regardless of the first 646 | // PTS in the data source. 647 | 648 | plm_packet_t *plm_demux_seek(plm_demux_t *self, double time, int type, int force_intra); 649 | 650 | 651 | // Get the PTS of the first packet of this type. Returns PLM_PACKET_INVALID_TS 652 | // if not packet of this packet type can be found. 653 | 654 | double plm_demux_get_start_time(plm_demux_t *self, int type); 655 | 656 | 657 | // Get the duration for the specified packet type - i.e. the span between the 658 | // the first PTS and the last PTS in the data source. This only makes sense when 659 | // the underlying data source is a file or fixed memory. 660 | 661 | double plm_demux_get_duration(plm_demux_t *self, int type); 662 | 663 | 664 | // Decode and return the next packet. The returned packet_t is valid until 665 | // the next call to plm_demux_decode() or until the demuxer is destroyed. 666 | 667 | plm_packet_t *plm_demux_decode(plm_demux_t *self); 668 | 669 | 670 | 671 | // ----------------------------------------------------------------------------- 672 | // plm_video public API 673 | // Decode MPEG1 Video ("mpeg1") data into raw YCrCb frames 674 | 675 | 676 | // Create a video decoder with a plm_buffer as source. 677 | 678 | plm_video_t *plm_video_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done); 679 | 680 | 681 | // Destroy a video decoder and free all data. 682 | 683 | void plm_video_destroy(plm_video_t *self); 684 | 685 | 686 | // Get whether a sequence header was found and we can accurately report on 687 | // dimensions and framerate. 688 | 689 | int plm_video_has_header(plm_video_t *self); 690 | 691 | 692 | // Get the framerate in frames per second. 693 | 694 | double plm_video_get_framerate(plm_video_t *self); 695 | double plm_video_get_pixel_aspect_ratio(plm_video_t *self); 696 | 697 | 698 | // Get the display width/height. 699 | 700 | int plm_video_get_width(plm_video_t *self); 701 | int plm_video_get_height(plm_video_t *self); 702 | 703 | 704 | // Set "no delay" mode. When enabled, the decoder assumes that the video does 705 | // *not* contain any B-Frames. This is useful for reducing lag when streaming. 706 | // The default is FALSE. 707 | 708 | void plm_video_set_no_delay(plm_video_t *self, int no_delay); 709 | 710 | 711 | // Get the current internal time in seconds. 712 | 713 | double plm_video_get_time(plm_video_t *self); 714 | 715 | 716 | // Set the current internal time in seconds. This is only useful when you 717 | // manipulate the underlying video buffer and want to enforce a correct 718 | // timestamps. 719 | 720 | void plm_video_set_time(plm_video_t *self, double time); 721 | 722 | 723 | // Rewind the internal buffer. See plm_buffer_rewind(). 724 | 725 | void plm_video_rewind(plm_video_t *self); 726 | 727 | 728 | // Get whether the file has ended. This will be cleared on rewind. 729 | 730 | int plm_video_has_ended(plm_video_t *self); 731 | 732 | 733 | // Decode and return one frame of video and advance the internal time by 734 | // 1/framerate seconds. The returned frame_t is valid until the next call of 735 | // plm_video_decode() or until the video decoder is destroyed. 736 | 737 | plm_frame_t *plm_video_decode(plm_video_t *self); 738 | 739 | 740 | // Convert the YCrCb data of a frame into interleaved R G B data. The stride 741 | // specifies the width in bytes of the destination buffer. I.e. the number of 742 | // bytes from one line to the next. The stride must be at least 743 | // (frame->width * bytes_per_pixel). The buffer pointed to by *dest must have a 744 | // size of at least (stride * frame->height). 745 | // Note that the alpha component of the dest buffer is always left untouched. 746 | 747 | void plm_frame_to_rgb(plm_frame_t *frame, uint8_t *dest, int stride); 748 | void plm_frame_to_bgr(plm_frame_t *frame, uint8_t *dest, int stride); 749 | void plm_frame_to_rgba(plm_frame_t *frame, uint8_t *dest, int stride); 750 | void plm_frame_to_bgra(plm_frame_t *frame, uint8_t *dest, int stride); 751 | void plm_frame_to_argb(plm_frame_t *frame, uint8_t *dest, int stride); 752 | void plm_frame_to_abgr(plm_frame_t *frame, uint8_t *dest, int stride); 753 | 754 | 755 | // ----------------------------------------------------------------------------- 756 | // plm_audio public API 757 | // Decode MPEG-1 Audio Layer II ("mp2") data into raw samples 758 | 759 | 760 | // Create an audio decoder with a plm_buffer as source. 761 | 762 | plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done); 763 | 764 | 765 | // Destroy an audio decoder and free all data. 766 | 767 | void plm_audio_destroy(plm_audio_t *self); 768 | 769 | 770 | // Get whether a frame header was found and we can accurately report on 771 | // samplerate. 772 | 773 | int plm_audio_has_header(plm_audio_t *self); 774 | 775 | 776 | // Get the samplerate in samples per second. 777 | 778 | int plm_audio_get_samplerate(plm_audio_t *self); 779 | 780 | 781 | // Get the current internal time in seconds. 782 | 783 | double plm_audio_get_time(plm_audio_t *self); 784 | 785 | 786 | // Set the current internal time in seconds. This is only useful when you 787 | // manipulate the underlying video buffer and want to enforce a correct 788 | // timestamps. 789 | 790 | void plm_audio_set_time(plm_audio_t *self, double time); 791 | 792 | 793 | // Rewind the internal buffer. See plm_buffer_rewind(). 794 | 795 | void plm_audio_rewind(plm_audio_t *self); 796 | 797 | 798 | // Get whether the file has ended. This will be cleared on rewind. 799 | 800 | int plm_audio_has_ended(plm_audio_t *self); 801 | 802 | 803 | // Decode and return one "frame" of audio and advance the internal time by 804 | // (PLM_AUDIO_SAMPLES_PER_FRAME/samplerate) seconds. The returned samples_t 805 | // is valid until the next call of plm_audio_decode() or until the audio 806 | // decoder is destroyed. 807 | 808 | plm_samples_t *plm_audio_decode(plm_audio_t *self); 809 | 810 | 811 | 812 | #ifdef __cplusplus 813 | } 814 | #endif 815 | 816 | #endif // PL_MPEG_H 817 | 818 | 819 | 820 | 821 | 822 | // ----------------------------------------------------------------------------- 823 | // ----------------------------------------------------------------------------- 824 | // IMPLEMENTATION 825 | 826 | #ifdef PL_MPEG_IMPLEMENTATION 827 | 828 | #include 829 | #include 830 | #ifndef PLM_NO_STDIO 831 | #include 832 | #endif 833 | 834 | #ifndef TRUE 835 | #define TRUE 1 836 | #define FALSE 0 837 | #endif 838 | 839 | #ifndef PLM_MALLOC 840 | #define PLM_MALLOC(sz) malloc(sz) 841 | #define PLM_FREE(p) free(p) 842 | #define PLM_REALLOC(p, sz) realloc(p, sz) 843 | #endif 844 | 845 | #define PLM_UNUSED(expr) (void)(expr) 846 | #ifdef _MSC_VER 847 | #pragma warning(disable:4996) 848 | #endif 849 | 850 | // ----------------------------------------------------------------------------- 851 | // plm (high-level interface) implementation 852 | 853 | struct plm_t { 854 | plm_demux_t *demux; 855 | double time; 856 | int has_ended; 857 | int loop; 858 | int has_decoders; 859 | 860 | int video_enabled; 861 | int video_packet_type; 862 | plm_buffer_t *video_buffer; 863 | plm_video_t *video_decoder; 864 | 865 | int audio_enabled; 866 | int audio_stream_index; 867 | int audio_packet_type; 868 | double audio_lead_time; 869 | plm_buffer_t *audio_buffer; 870 | plm_audio_t *audio_decoder; 871 | 872 | plm_video_decode_callback video_decode_callback; 873 | void *video_decode_callback_user_data; 874 | 875 | plm_audio_decode_callback audio_decode_callback; 876 | void *audio_decode_callback_user_data; 877 | }; 878 | 879 | int plm_init_decoders(plm_t *self); 880 | void plm_handle_end(plm_t *self); 881 | void plm_read_video_packet(plm_buffer_t *buffer, void *user); 882 | void plm_read_audio_packet(plm_buffer_t *buffer, void *user); 883 | void plm_read_packets(plm_t *self, int requested_type); 884 | 885 | #ifndef PLM_NO_STDIO 886 | 887 | plm_t *plm_create_with_filename(const char *filename) { 888 | plm_buffer_t *buffer = plm_buffer_create_with_filename(filename); 889 | if (!buffer) { 890 | return NULL; 891 | } 892 | return plm_create_with_buffer(buffer, TRUE); 893 | } 894 | 895 | plm_t *plm_create_with_file(FILE *fh, int close_when_done) { 896 | plm_buffer_t *buffer = plm_buffer_create_with_file(fh, close_when_done); 897 | return plm_create_with_buffer(buffer, TRUE); 898 | } 899 | 900 | #endif // PLM_NO_STDIO 901 | 902 | plm_t *plm_create_with_memory(uint8_t *bytes, size_t length, int free_when_done) { 903 | plm_buffer_t *buffer = plm_buffer_create_with_memory(bytes, length, free_when_done); 904 | return plm_create_with_buffer(buffer, TRUE); 905 | } 906 | 907 | plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) { 908 | plm_t *self = (plm_t *)PLM_MALLOC(sizeof(plm_t)); 909 | memset(self, 0, sizeof(plm_t)); 910 | 911 | self->demux = plm_demux_create(buffer, destroy_when_done); 912 | self->video_enabled = TRUE; 913 | self->audio_enabled = TRUE; 914 | plm_init_decoders(self); 915 | 916 | return self; 917 | } 918 | 919 | int plm_init_decoders(plm_t *self) { 920 | if (self->has_decoders) { 921 | return TRUE; 922 | } 923 | 924 | if (!plm_demux_has_headers(self->demux)) { 925 | return FALSE; 926 | } 927 | 928 | if (plm_demux_get_num_video_streams(self->demux) > 0) { 929 | if (self->video_enabled) { 930 | self->video_packet_type = PLM_DEMUX_PACKET_VIDEO_1; 931 | } 932 | if (!self->video_decoder) { 933 | self->video_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE); 934 | plm_buffer_set_load_callback(self->video_buffer, plm_read_video_packet, self); 935 | self->video_decoder = plm_video_create_with_buffer(self->video_buffer, TRUE); 936 | } 937 | } 938 | 939 | if (plm_demux_get_num_audio_streams(self->demux) > 0) { 940 | if (self->audio_enabled) { 941 | self->audio_packet_type = PLM_DEMUX_PACKET_AUDIO_1 + self->audio_stream_index; 942 | } 943 | if (!self->audio_decoder) { 944 | self->audio_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE); 945 | plm_buffer_set_load_callback(self->audio_buffer, plm_read_audio_packet, self); 946 | self->audio_decoder = plm_audio_create_with_buffer(self->audio_buffer, TRUE); 947 | } 948 | } 949 | 950 | self->has_decoders = TRUE; 951 | return TRUE; 952 | } 953 | 954 | void plm_destroy(plm_t *self) { 955 | if (self->video_decoder) { 956 | plm_video_destroy(self->video_decoder); 957 | } 958 | if (self->audio_decoder) { 959 | plm_audio_destroy(self->audio_decoder); 960 | } 961 | 962 | plm_demux_destroy(self->demux); 963 | PLM_FREE(self); 964 | } 965 | 966 | int plm_get_audio_enabled(plm_t *self) { 967 | return self->audio_enabled; 968 | } 969 | 970 | int plm_has_headers(plm_t *self) { 971 | if (!plm_demux_has_headers(self->demux)) { 972 | return FALSE; 973 | } 974 | 975 | if (!plm_init_decoders(self)) { 976 | return FALSE; 977 | } 978 | 979 | if ( 980 | (self->video_decoder && !plm_video_has_header(self->video_decoder)) || 981 | (self->audio_decoder && !plm_audio_has_header(self->audio_decoder)) 982 | ) { 983 | return FALSE; 984 | } 985 | 986 | return TRUE; 987 | } 988 | 989 | int plm_probe(plm_t *self, size_t probesize) { 990 | int found_streams = plm_demux_probe(self->demux, probesize); 991 | if (!found_streams) { 992 | return FALSE; 993 | } 994 | 995 | // Re-init decoders 996 | self->has_decoders = FALSE; 997 | self->video_packet_type = 0; 998 | self->audio_packet_type = 0; 999 | return plm_init_decoders(self); 1000 | } 1001 | 1002 | void plm_set_audio_enabled(plm_t *self, int enabled) { 1003 | self->audio_enabled = enabled; 1004 | 1005 | if (!enabled) { 1006 | self->audio_packet_type = 0; 1007 | return; 1008 | } 1009 | 1010 | self->audio_packet_type = (plm_init_decoders(self) && self->audio_decoder) 1011 | ? PLM_DEMUX_PACKET_AUDIO_1 + self->audio_stream_index 1012 | : 0; 1013 | } 1014 | 1015 | void plm_set_audio_stream(plm_t *self, int stream_index) { 1016 | if (stream_index < 0 || stream_index > 3) { 1017 | return; 1018 | } 1019 | self->audio_stream_index = stream_index; 1020 | 1021 | // Set the correct audio_packet_type 1022 | plm_set_audio_enabled(self, self->audio_enabled); 1023 | } 1024 | 1025 | int plm_get_video_enabled(plm_t *self) { 1026 | return self->video_enabled; 1027 | } 1028 | 1029 | void plm_set_video_enabled(plm_t *self, int enabled) { 1030 | self->video_enabled = enabled; 1031 | 1032 | if (!enabled) { 1033 | self->video_packet_type = 0; 1034 | return; 1035 | } 1036 | 1037 | self->video_packet_type = (plm_init_decoders(self) && self->video_decoder) 1038 | ? PLM_DEMUX_PACKET_VIDEO_1 1039 | : 0; 1040 | } 1041 | 1042 | int plm_get_num_video_streams(plm_t *self) { 1043 | return plm_demux_get_num_video_streams(self->demux); 1044 | } 1045 | 1046 | int plm_get_width(plm_t *self) { 1047 | return (plm_init_decoders(self) && self->video_decoder) 1048 | ? plm_video_get_width(self->video_decoder) 1049 | : 0; 1050 | } 1051 | 1052 | int plm_get_height(plm_t *self) { 1053 | return (plm_init_decoders(self) && self->video_decoder) 1054 | ? plm_video_get_height(self->video_decoder) 1055 | : 0; 1056 | } 1057 | 1058 | double plm_get_framerate(plm_t *self) { 1059 | return (plm_init_decoders(self) && self->video_decoder) 1060 | ? plm_video_get_framerate(self->video_decoder) 1061 | : 0; 1062 | } 1063 | 1064 | double plm_get_pixel_aspect_ratio(plm_t *self) { 1065 | return (plm_init_decoders(self) && self->video_decoder) 1066 | ? plm_video_get_pixel_aspect_ratio(self->video_decoder) 1067 | : 0; 1068 | } 1069 | 1070 | int plm_get_num_audio_streams(plm_t *self) { 1071 | return plm_demux_get_num_audio_streams(self->demux); 1072 | } 1073 | 1074 | int plm_get_samplerate(plm_t *self) { 1075 | return (plm_init_decoders(self) && self->audio_decoder) 1076 | ? plm_audio_get_samplerate(self->audio_decoder) 1077 | : 0; 1078 | } 1079 | 1080 | double plm_get_audio_lead_time(plm_t *self) { 1081 | return self->audio_lead_time; 1082 | } 1083 | 1084 | void plm_set_audio_lead_time(plm_t *self, double lead_time) { 1085 | self->audio_lead_time = lead_time; 1086 | } 1087 | 1088 | double plm_get_time(plm_t *self) { 1089 | return self->time; 1090 | } 1091 | 1092 | double plm_get_duration(plm_t *self) { 1093 | return plm_demux_get_duration(self->demux, PLM_DEMUX_PACKET_VIDEO_1); 1094 | } 1095 | 1096 | void plm_rewind(plm_t *self) { 1097 | if (self->video_decoder) { 1098 | plm_video_rewind(self->video_decoder); 1099 | } 1100 | 1101 | if (self->audio_decoder) { 1102 | plm_audio_rewind(self->audio_decoder); 1103 | } 1104 | 1105 | plm_demux_rewind(self->demux); 1106 | self->time = 0; 1107 | self->has_ended = FALSE; 1108 | } 1109 | 1110 | int plm_get_loop(plm_t *self) { 1111 | return self->loop; 1112 | } 1113 | 1114 | void plm_set_loop(plm_t *self, int loop) { 1115 | self->loop = loop; 1116 | } 1117 | 1118 | int plm_has_ended(plm_t *self) { 1119 | return self->has_ended; 1120 | } 1121 | 1122 | void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp, void *user) { 1123 | self->video_decode_callback = fp; 1124 | self->video_decode_callback_user_data = user; 1125 | } 1126 | 1127 | void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp, void *user) { 1128 | self->audio_decode_callback = fp; 1129 | self->audio_decode_callback_user_data = user; 1130 | } 1131 | 1132 | void plm_decode(plm_t *self, double tick) { 1133 | if (!plm_init_decoders(self)) { 1134 | return; 1135 | } 1136 | 1137 | int decode_video = (self->video_decode_callback && self->video_packet_type); 1138 | int decode_audio = (self->audio_decode_callback && self->audio_packet_type); 1139 | 1140 | if (!decode_video && !decode_audio) { 1141 | // Nothing to do here 1142 | return; 1143 | } 1144 | 1145 | int did_decode = FALSE; 1146 | int decode_video_failed = FALSE; 1147 | int decode_audio_failed = FALSE; 1148 | 1149 | double video_target_time = self->time + tick; 1150 | double audio_target_time = self->time + tick + self->audio_lead_time; 1151 | 1152 | do { 1153 | did_decode = FALSE; 1154 | 1155 | if (decode_video && plm_video_get_time(self->video_decoder) < video_target_time) { 1156 | plm_frame_t *frame = plm_video_decode(self->video_decoder); 1157 | if (frame) { 1158 | self->video_decode_callback(self, frame, self->video_decode_callback_user_data); 1159 | did_decode = TRUE; 1160 | } 1161 | else { 1162 | decode_video_failed = TRUE; 1163 | } 1164 | } 1165 | 1166 | if (decode_audio && plm_audio_get_time(self->audio_decoder) < audio_target_time) { 1167 | plm_samples_t *samples = plm_audio_decode(self->audio_decoder); 1168 | if (samples) { 1169 | self->audio_decode_callback(self, samples, self->audio_decode_callback_user_data); 1170 | did_decode = TRUE; 1171 | } 1172 | else { 1173 | decode_audio_failed = TRUE; 1174 | } 1175 | } 1176 | } while (did_decode); 1177 | 1178 | // Did all sources we wanted to decode fail and the demuxer is at the end? 1179 | if ( 1180 | (!decode_video || decode_video_failed) && 1181 | (!decode_audio || decode_audio_failed) && 1182 | plm_demux_has_ended(self->demux) 1183 | ) { 1184 | plm_handle_end(self); 1185 | return; 1186 | } 1187 | 1188 | self->time += tick; 1189 | } 1190 | 1191 | plm_frame_t *plm_decode_video(plm_t *self) { 1192 | if (!plm_init_decoders(self)) { 1193 | return NULL; 1194 | } 1195 | 1196 | if (!self->video_packet_type) { 1197 | return NULL; 1198 | } 1199 | 1200 | plm_frame_t *frame = plm_video_decode(self->video_decoder); 1201 | if (frame) { 1202 | self->time = frame->time; 1203 | } 1204 | else if (plm_demux_has_ended(self->demux)) { 1205 | plm_handle_end(self); 1206 | } 1207 | return frame; 1208 | } 1209 | 1210 | plm_samples_t *plm_decode_audio(plm_t *self) { 1211 | if (!plm_init_decoders(self)) { 1212 | return NULL; 1213 | } 1214 | 1215 | if (!self->audio_packet_type) { 1216 | return NULL; 1217 | } 1218 | 1219 | plm_samples_t *samples = plm_audio_decode(self->audio_decoder); 1220 | if (samples) { 1221 | self->time = samples->time; 1222 | } 1223 | else if (plm_demux_has_ended(self->demux)) { 1224 | plm_handle_end(self); 1225 | } 1226 | return samples; 1227 | } 1228 | 1229 | void plm_handle_end(plm_t *self) { 1230 | if (self->loop) { 1231 | plm_rewind(self); 1232 | } 1233 | else { 1234 | self->has_ended = TRUE; 1235 | } 1236 | } 1237 | 1238 | void plm_read_video_packet(plm_buffer_t *buffer, void *user) { 1239 | PLM_UNUSED(buffer); 1240 | plm_t *self = (plm_t *)user; 1241 | plm_read_packets(self, self->video_packet_type); 1242 | } 1243 | 1244 | void plm_read_audio_packet(plm_buffer_t *buffer, void *user) { 1245 | PLM_UNUSED(buffer); 1246 | plm_t *self = (plm_t *)user; 1247 | plm_read_packets(self, self->audio_packet_type); 1248 | } 1249 | 1250 | void plm_read_packets(plm_t *self, int requested_type) { 1251 | plm_packet_t *packet; 1252 | while ((packet = plm_demux_decode(self->demux))) { 1253 | if (packet->type == self->video_packet_type) { 1254 | plm_buffer_write(self->video_buffer, packet->data, packet->length); 1255 | } 1256 | else if (packet->type == self->audio_packet_type) { 1257 | plm_buffer_write(self->audio_buffer, packet->data, packet->length); 1258 | } 1259 | 1260 | if (packet->type == requested_type) { 1261 | return; 1262 | } 1263 | } 1264 | 1265 | if (plm_demux_has_ended(self->demux)) { 1266 | if (self->video_buffer) { 1267 | plm_buffer_signal_end(self->video_buffer); 1268 | } 1269 | if (self->audio_buffer) { 1270 | plm_buffer_signal_end(self->audio_buffer); 1271 | } 1272 | } 1273 | } 1274 | 1275 | plm_frame_t *plm_seek_frame(plm_t *self, double time, int seek_exact) { 1276 | if (!plm_init_decoders(self)) { 1277 | return NULL; 1278 | } 1279 | 1280 | if (!self->video_packet_type) { 1281 | return NULL; 1282 | } 1283 | 1284 | int type = self->video_packet_type; 1285 | 1286 | double start_time = plm_demux_get_start_time(self->demux, type); 1287 | double duration = plm_demux_get_duration(self->demux, type); 1288 | 1289 | if (time < 0) { 1290 | time = 0; 1291 | } 1292 | else if (time > duration) { 1293 | time = duration; 1294 | } 1295 | 1296 | plm_packet_t *packet = plm_demux_seek(self->demux, time, type, TRUE); 1297 | if (!packet) { 1298 | return NULL; 1299 | } 1300 | 1301 | // Disable writing to the audio buffer while decoding video 1302 | int previous_audio_packet_type = self->audio_packet_type; 1303 | self->audio_packet_type = 0; 1304 | 1305 | // Clear video buffer and decode the found packet 1306 | plm_video_rewind(self->video_decoder); 1307 | plm_video_set_time(self->video_decoder, packet->pts - start_time); 1308 | plm_buffer_write(self->video_buffer, packet->data, packet->length); 1309 | plm_frame_t *frame = plm_video_decode(self->video_decoder); 1310 | 1311 | // If we want to seek to an exact frame, we have to decode all frames 1312 | // on top of the intra frame we just jumped to. 1313 | if (seek_exact) { 1314 | while (frame && frame->time < time) { 1315 | frame = plm_video_decode(self->video_decoder); 1316 | } 1317 | } 1318 | 1319 | // Enable writing to the audio buffer again? 1320 | self->audio_packet_type = previous_audio_packet_type; 1321 | 1322 | if (frame) { 1323 | self->time = frame->time; 1324 | } 1325 | 1326 | self->has_ended = FALSE; 1327 | return frame; 1328 | } 1329 | 1330 | int plm_seek(plm_t *self, double time, int seek_exact) { 1331 | plm_frame_t *frame = plm_seek_frame(self, time, seek_exact); 1332 | 1333 | if (!frame) { 1334 | return FALSE; 1335 | } 1336 | 1337 | if (self->video_decode_callback) { 1338 | self->video_decode_callback(self, frame, self->video_decode_callback_user_data); 1339 | } 1340 | 1341 | // If audio is not enabled we are done here. 1342 | if (!self->audio_packet_type) { 1343 | return TRUE; 1344 | } 1345 | 1346 | // Sync up Audio. This demuxes more packets until the first audio packet 1347 | // with a PTS greater than the current time is found. plm_decode() is then 1348 | // called to decode enough audio data to satisfy the audio_lead_time. 1349 | 1350 | double start_time = plm_demux_get_start_time(self->demux, self->video_packet_type); 1351 | plm_audio_rewind(self->audio_decoder); 1352 | 1353 | plm_packet_t *packet = NULL; 1354 | while ((packet = plm_demux_decode(self->demux))) { 1355 | if (packet->type == self->video_packet_type) { 1356 | plm_buffer_write(self->video_buffer, packet->data, packet->length); 1357 | } 1358 | else if ( 1359 | packet->type == self->audio_packet_type && 1360 | packet->pts - start_time > self->time 1361 | ) { 1362 | plm_audio_set_time(self->audio_decoder, packet->pts - start_time); 1363 | plm_buffer_write(self->audio_buffer, packet->data, packet->length); 1364 | plm_decode(self, 0); 1365 | break; 1366 | } 1367 | } 1368 | 1369 | return TRUE; 1370 | } 1371 | 1372 | 1373 | 1374 | // ----------------------------------------------------------------------------- 1375 | // plm_buffer implementation 1376 | 1377 | enum plm_buffer_mode { 1378 | PLM_BUFFER_MODE_FILE, 1379 | PLM_BUFFER_MODE_FIXED_MEM, 1380 | PLM_BUFFER_MODE_RING, 1381 | PLM_BUFFER_MODE_APPEND 1382 | }; 1383 | 1384 | struct plm_buffer_t { 1385 | size_t bit_index; 1386 | size_t capacity; 1387 | size_t length; 1388 | size_t total_size; 1389 | int discard_read_bytes; 1390 | int has_ended; 1391 | int free_when_done; 1392 | #ifndef PLM_NO_STDIO 1393 | int close_when_done; 1394 | FILE *fh; 1395 | #endif 1396 | plm_buffer_load_callback load_callback; 1397 | plm_buffer_seek_callback seek_callback; 1398 | plm_buffer_tell_callback tell_callback; 1399 | void *load_callback_user_data; 1400 | uint8_t *bytes; 1401 | enum plm_buffer_mode mode; 1402 | }; 1403 | 1404 | typedef struct { 1405 | int16_t index; 1406 | int16_t value; 1407 | } plm_vlc_t; 1408 | 1409 | typedef struct { 1410 | int16_t index; 1411 | uint16_t value; 1412 | } plm_vlc_uint_t; 1413 | 1414 | 1415 | void plm_buffer_seek(plm_buffer_t *self, size_t pos); 1416 | size_t plm_buffer_tell(plm_buffer_t *self); 1417 | void plm_buffer_discard_read_bytes(plm_buffer_t *self); 1418 | 1419 | #ifndef PLM_NO_STDIO 1420 | void plm_buffer_load_file_callback(plm_buffer_t *self, void *user); 1421 | void plm_buffer_seek_file_callback(plm_buffer_t *self, size_t offset, void *user); 1422 | size_t plm_buffer_tell_file_callback(plm_buffer_t *self, void *user); 1423 | #endif 1424 | 1425 | int plm_buffer_has(plm_buffer_t *self, size_t count); 1426 | int plm_buffer_read(plm_buffer_t *self, int count); 1427 | void plm_buffer_align(plm_buffer_t *self); 1428 | void plm_buffer_skip(plm_buffer_t *self, size_t count); 1429 | int plm_buffer_skip_bytes(plm_buffer_t *self, uint8_t v); 1430 | int plm_buffer_next_start_code(plm_buffer_t *self); 1431 | int plm_buffer_find_start_code(plm_buffer_t *self, int code); 1432 | int plm_buffer_no_start_code(plm_buffer_t *self); 1433 | int16_t plm_buffer_read_vlc(plm_buffer_t *self, const plm_vlc_t *table); 1434 | uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self, const plm_vlc_uint_t *table); 1435 | 1436 | #ifndef PLM_NO_STDIO 1437 | 1438 | plm_buffer_t *plm_buffer_create_with_filename(const char *filename) { 1439 | FILE *fh = fopen(filename, "rb"); 1440 | if (!fh) { 1441 | return NULL; 1442 | } 1443 | return plm_buffer_create_with_file(fh, TRUE); 1444 | } 1445 | 1446 | plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done) { 1447 | plm_buffer_t *self = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE); 1448 | self->fh = fh; 1449 | self->close_when_done = close_when_done; 1450 | self->mode = PLM_BUFFER_MODE_FILE; 1451 | self->discard_read_bytes = TRUE; 1452 | 1453 | fseek(self->fh, 0, SEEK_END); 1454 | self->total_size = ftell(self->fh); 1455 | fseek(self->fh, 0, SEEK_SET); 1456 | 1457 | self->load_callback = plm_buffer_load_file_callback; 1458 | self->seek_callback = plm_buffer_seek_file_callback; 1459 | self->tell_callback = plm_buffer_tell_file_callback; 1460 | return self; 1461 | } 1462 | 1463 | #endif // PLM_NO_STDIO 1464 | 1465 | plm_buffer_t *plm_buffer_create_with_callbacks( 1466 | plm_buffer_load_callback load_callback, 1467 | plm_buffer_seek_callback seek_callback, 1468 | plm_buffer_tell_callback tell_callback, 1469 | size_t length, 1470 | void *user 1471 | ) { 1472 | plm_buffer_t *self = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE); 1473 | self->mode = PLM_BUFFER_MODE_FILE; 1474 | self->total_size = length; 1475 | self->load_callback = load_callback; 1476 | self->seek_callback = seek_callback; 1477 | self->tell_callback = tell_callback; 1478 | self->load_callback_user_data = user; 1479 | return self; 1480 | } 1481 | 1482 | plm_buffer_t *plm_buffer_create_with_memory(uint8_t *bytes, size_t length, int free_when_done) { 1483 | plm_buffer_t *self = (plm_buffer_t *)PLM_MALLOC(sizeof(plm_buffer_t)); 1484 | memset(self, 0, sizeof(plm_buffer_t)); 1485 | self->capacity = length; 1486 | self->length = length; 1487 | self->total_size = length; 1488 | self->free_when_done = free_when_done; 1489 | self->bytes = bytes; 1490 | self->mode = PLM_BUFFER_MODE_FIXED_MEM; 1491 | self->discard_read_bytes = FALSE; 1492 | return self; 1493 | } 1494 | 1495 | plm_buffer_t *plm_buffer_create_with_capacity(size_t capacity) { 1496 | plm_buffer_t *self = (plm_buffer_t *)PLM_MALLOC(sizeof(plm_buffer_t)); 1497 | memset(self, 0, sizeof(plm_buffer_t)); 1498 | self->capacity = capacity; 1499 | self->free_when_done = TRUE; 1500 | self->bytes = (uint8_t *)PLM_MALLOC(capacity); 1501 | self->mode = PLM_BUFFER_MODE_RING; 1502 | self->discard_read_bytes = TRUE; 1503 | return self; 1504 | } 1505 | 1506 | plm_buffer_t *plm_buffer_create_for_appending(size_t initial_capacity) { 1507 | plm_buffer_t *self = plm_buffer_create_with_capacity(initial_capacity); 1508 | self->mode = PLM_BUFFER_MODE_APPEND; 1509 | self->discard_read_bytes = FALSE; 1510 | return self; 1511 | } 1512 | 1513 | void plm_buffer_destroy(plm_buffer_t *self) { 1514 | #ifndef PLM_NO_STDIO 1515 | if (self->fh && self->close_when_done) { 1516 | fclose(self->fh); 1517 | } 1518 | #endif 1519 | if (self->free_when_done) { 1520 | PLM_FREE(self->bytes); 1521 | } 1522 | PLM_FREE(self); 1523 | } 1524 | 1525 | size_t plm_buffer_get_size(plm_buffer_t *self) { 1526 | return (self->mode == PLM_BUFFER_MODE_FILE) 1527 | ? self->total_size 1528 | : self->length; 1529 | } 1530 | 1531 | size_t plm_buffer_get_remaining(plm_buffer_t *self) { 1532 | return self->length - (self->bit_index >> 3); 1533 | } 1534 | 1535 | size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length) { 1536 | if (self->mode == PLM_BUFFER_MODE_FIXED_MEM) { 1537 | return 0; 1538 | } 1539 | 1540 | if (self->discard_read_bytes) { 1541 | // This should be a ring buffer, but instead it just shifts all unread 1542 | // data to the beginning of the buffer and appends new data at the end. 1543 | // Seems to be good enough. 1544 | 1545 | plm_buffer_discard_read_bytes(self); 1546 | if (self->mode == PLM_BUFFER_MODE_RING) { 1547 | self->total_size = 0; 1548 | } 1549 | } 1550 | 1551 | // Do we have to resize to fit the new data? 1552 | size_t bytes_available = self->capacity - self->length; 1553 | if (bytes_available < length) { 1554 | size_t new_size = self->capacity; 1555 | do { 1556 | new_size *= 2; 1557 | } while (new_size - self->length < length); 1558 | self->bytes = (uint8_t *)PLM_REALLOC(self->bytes, new_size); 1559 | self->capacity = new_size; 1560 | } 1561 | 1562 | memcpy(self->bytes + self->length, bytes, length); 1563 | self->length += length; 1564 | self->has_ended = FALSE; 1565 | return length; 1566 | } 1567 | 1568 | void plm_buffer_signal_end(plm_buffer_t *self) { 1569 | self->total_size = self->length; 1570 | } 1571 | 1572 | void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback fp, void *user) { 1573 | self->load_callback = fp; 1574 | self->load_callback_user_data = user; 1575 | } 1576 | 1577 | void plm_buffer_rewind(plm_buffer_t *self) { 1578 | plm_buffer_seek(self, 0); 1579 | } 1580 | 1581 | void plm_buffer_seek(plm_buffer_t *self, size_t pos) { 1582 | self->has_ended = FALSE; 1583 | 1584 | if (self->seek_callback) { 1585 | self->seek_callback(self, pos, self->load_callback_user_data); 1586 | self->bit_index = 0; 1587 | self->length = 0; 1588 | } 1589 | else if (self->mode == PLM_BUFFER_MODE_RING) { 1590 | if (pos != 0) { 1591 | // Seeking to non-0 is forbidden for dynamic-mem buffers 1592 | return; 1593 | } 1594 | self->bit_index = 0; 1595 | self->length = 0; 1596 | self->total_size = 0; 1597 | } 1598 | else if (pos < self->length) { 1599 | self->bit_index = pos << 3; 1600 | } 1601 | } 1602 | 1603 | size_t plm_buffer_tell(plm_buffer_t *self) { 1604 | return self->tell_callback 1605 | ? self->tell_callback(self, self->load_callback_user_data) + (self->bit_index >> 3) - self->length 1606 | : self->bit_index >> 3; 1607 | } 1608 | 1609 | void plm_buffer_discard_read_bytes(plm_buffer_t *self) { 1610 | size_t byte_pos = self->bit_index >> 3; 1611 | if (byte_pos == self->length) { 1612 | self->bit_index = 0; 1613 | self->length = 0; 1614 | } 1615 | else if (byte_pos > 0) { 1616 | memmove(self->bytes, self->bytes + byte_pos, self->length - byte_pos); 1617 | self->bit_index -= byte_pos << 3; 1618 | self->length -= byte_pos; 1619 | } 1620 | } 1621 | 1622 | #ifndef PLM_NO_STDIO 1623 | 1624 | void plm_buffer_load_file_callback(plm_buffer_t *self, void *user) { 1625 | PLM_UNUSED(user); 1626 | 1627 | if (self->discard_read_bytes) { 1628 | plm_buffer_discard_read_bytes(self); 1629 | } 1630 | 1631 | size_t bytes_available = self->capacity - self->length; 1632 | size_t bytes_read = fread(self->bytes + self->length, 1, bytes_available, self->fh); 1633 | self->length += bytes_read; 1634 | 1635 | if (bytes_read == 0) { 1636 | self->has_ended = TRUE; 1637 | } 1638 | } 1639 | 1640 | void plm_buffer_seek_file_callback(plm_buffer_t *self, size_t offset, void *user) { 1641 | PLM_UNUSED(user); 1642 | fseek(self->fh, offset, SEEK_SET); 1643 | } 1644 | 1645 | size_t plm_buffer_tell_file_callback(plm_buffer_t *self, void *user) { 1646 | PLM_UNUSED(user); 1647 | return ftell(self->fh); 1648 | } 1649 | 1650 | #endif // PLM_NO_STDIO 1651 | 1652 | int plm_buffer_has_ended(plm_buffer_t *self) { 1653 | return self->has_ended; 1654 | } 1655 | 1656 | int plm_buffer_has(plm_buffer_t *self, size_t count) { 1657 | if (((self->length << 3) - self->bit_index) >= count) { 1658 | return TRUE; 1659 | } 1660 | 1661 | if (self->load_callback) { 1662 | self->load_callback(self, self->load_callback_user_data); 1663 | 1664 | if (((self->length << 3) - self->bit_index) >= count) { 1665 | return TRUE; 1666 | } 1667 | } 1668 | 1669 | if (self->total_size != 0 && self->length == self->total_size) { 1670 | self->has_ended = TRUE; 1671 | } 1672 | return FALSE; 1673 | } 1674 | 1675 | int plm_buffer_read(plm_buffer_t *self, int count) { 1676 | if (!plm_buffer_has(self, count)) { 1677 | return 0; 1678 | } 1679 | 1680 | int value = 0; 1681 | while (count) { 1682 | int current_byte = self->bytes[self->bit_index >> 3]; 1683 | 1684 | int remaining = 8 - (self->bit_index & 7); // Remaining bits in byte 1685 | int read = remaining < count ? remaining : count; // Bits in self run 1686 | int shift = remaining - read; 1687 | int mask = (0xff >> (8 - read)); 1688 | 1689 | value = (value << read) | ((current_byte & (mask << shift)) >> shift); 1690 | 1691 | self->bit_index += read; 1692 | count -= read; 1693 | } 1694 | 1695 | return value; 1696 | } 1697 | 1698 | void plm_buffer_align(plm_buffer_t *self) { 1699 | self->bit_index = ((self->bit_index + 7) >> 3) << 3; // Align to next byte 1700 | } 1701 | 1702 | void plm_buffer_skip(plm_buffer_t *self, size_t count) { 1703 | if (plm_buffer_has(self, count)) { 1704 | self->bit_index += count; 1705 | } 1706 | } 1707 | 1708 | int plm_buffer_skip_bytes(plm_buffer_t *self, uint8_t v) { 1709 | plm_buffer_align(self); 1710 | int skipped = 0; 1711 | while (plm_buffer_has(self, 8) && self->bytes[self->bit_index >> 3] == v) { 1712 | self->bit_index += 8; 1713 | skipped++; 1714 | } 1715 | return skipped; 1716 | } 1717 | 1718 | int plm_buffer_next_start_code(plm_buffer_t *self) { 1719 | plm_buffer_align(self); 1720 | 1721 | while (plm_buffer_has(self, (5 << 3))) { 1722 | size_t byte_index = (self->bit_index) >> 3; 1723 | if ( 1724 | self->bytes[byte_index] == 0x00 && 1725 | self->bytes[byte_index + 1] == 0x00 && 1726 | self->bytes[byte_index + 2] == 0x01 1727 | ) { 1728 | self->bit_index = (byte_index + 4) << 3; 1729 | return self->bytes[byte_index + 3]; 1730 | } 1731 | self->bit_index += 8; 1732 | } 1733 | return -1; 1734 | } 1735 | 1736 | int plm_buffer_find_start_code(plm_buffer_t *self, int code) { 1737 | int current = 0; 1738 | while (TRUE) { 1739 | current = plm_buffer_next_start_code(self); 1740 | if (current == code || current == -1) { 1741 | return current; 1742 | } 1743 | } 1744 | return -1; 1745 | } 1746 | 1747 | int plm_buffer_has_start_code(plm_buffer_t *self, int code) { 1748 | size_t previous_bit_index = self->bit_index; 1749 | int previous_discard_read_bytes = self->discard_read_bytes; 1750 | 1751 | self->discard_read_bytes = FALSE; 1752 | int current = plm_buffer_find_start_code(self, code); 1753 | 1754 | self->bit_index = previous_bit_index; 1755 | self->discard_read_bytes = previous_discard_read_bytes; 1756 | return current; 1757 | } 1758 | 1759 | int plm_buffer_peek_non_zero(plm_buffer_t *self, int bit_count) { 1760 | if (!plm_buffer_has(self, bit_count)) { 1761 | return FALSE; 1762 | } 1763 | 1764 | int val = plm_buffer_read(self, bit_count); 1765 | self->bit_index -= bit_count; 1766 | return val != 0; 1767 | } 1768 | 1769 | int16_t plm_buffer_read_vlc(plm_buffer_t *self, const plm_vlc_t *table) { 1770 | plm_vlc_t state = {0, 0}; 1771 | do { 1772 | state = table[state.index + plm_buffer_read(self, 1)]; 1773 | } while (state.index > 0); 1774 | return state.value; 1775 | } 1776 | 1777 | uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self, const plm_vlc_uint_t *table) { 1778 | return (uint16_t)plm_buffer_read_vlc(self, (const plm_vlc_t *)table); 1779 | } 1780 | 1781 | 1782 | 1783 | // ---------------------------------------------------------------------------- 1784 | // plm_demux implementation 1785 | 1786 | static const int PLM_START_PACK = 0xBA; 1787 | static const int PLM_START_END = 0xB9; 1788 | static const int PLM_START_SYSTEM = 0xBB; 1789 | 1790 | struct plm_demux_t { 1791 | plm_buffer_t *buffer; 1792 | int destroy_buffer_when_done; 1793 | double system_clock_ref; 1794 | 1795 | size_t last_file_size; 1796 | double last_decoded_pts; 1797 | double start_time; 1798 | double duration; 1799 | 1800 | int start_code; 1801 | int has_pack_header; 1802 | int has_system_header; 1803 | int has_headers; 1804 | 1805 | int num_audio_streams; 1806 | int num_video_streams; 1807 | plm_packet_t current_packet; 1808 | plm_packet_t next_packet; 1809 | }; 1810 | 1811 | 1812 | void plm_demux_buffer_seek(plm_demux_t *self, size_t pos); 1813 | double plm_demux_decode_time(plm_demux_t *self); 1814 | plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int type); 1815 | plm_packet_t *plm_demux_get_packet(plm_demux_t *self); 1816 | 1817 | plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done) { 1818 | plm_demux_t *self = (plm_demux_t *)PLM_MALLOC(sizeof(plm_demux_t)); 1819 | memset(self, 0, sizeof(plm_demux_t)); 1820 | 1821 | self->buffer = buffer; 1822 | self->destroy_buffer_when_done = destroy_when_done; 1823 | 1824 | self->start_time = PLM_PACKET_INVALID_TS; 1825 | self->duration = PLM_PACKET_INVALID_TS; 1826 | self->start_code = -1; 1827 | 1828 | plm_demux_has_headers(self); 1829 | return self; 1830 | } 1831 | 1832 | void plm_demux_destroy(plm_demux_t *self) { 1833 | if (self->destroy_buffer_when_done) { 1834 | plm_buffer_destroy(self->buffer); 1835 | } 1836 | PLM_FREE(self); 1837 | } 1838 | 1839 | int plm_demux_has_headers(plm_demux_t *self) { 1840 | if (self->has_headers) { 1841 | return TRUE; 1842 | } 1843 | 1844 | // Decode pack header 1845 | if (!self->has_pack_header) { 1846 | if ( 1847 | self->start_code != PLM_START_PACK && 1848 | plm_buffer_find_start_code(self->buffer, PLM_START_PACK) == -1 1849 | ) { 1850 | return FALSE; 1851 | } 1852 | 1853 | self->start_code = PLM_START_PACK; 1854 | if (!plm_buffer_has(self->buffer, 64)) { 1855 | return FALSE; 1856 | } 1857 | self->start_code = -1; 1858 | 1859 | if (plm_buffer_read(self->buffer, 4) != 0x02) { 1860 | return FALSE; 1861 | } 1862 | 1863 | self->system_clock_ref = plm_demux_decode_time(self); 1864 | plm_buffer_skip(self->buffer, 1); 1865 | plm_buffer_skip(self->buffer, 22); // mux_rate * 50 1866 | plm_buffer_skip(self->buffer, 1); 1867 | 1868 | self->has_pack_header = TRUE; 1869 | } 1870 | 1871 | // Decode system header 1872 | if (!self->has_system_header) { 1873 | if ( 1874 | self->start_code != PLM_START_SYSTEM && 1875 | plm_buffer_find_start_code(self->buffer, PLM_START_SYSTEM) == -1 1876 | ) { 1877 | return FALSE; 1878 | } 1879 | 1880 | self->start_code = PLM_START_SYSTEM; 1881 | if (!plm_buffer_has(self->buffer, 56)) { 1882 | return FALSE; 1883 | } 1884 | self->start_code = -1; 1885 | 1886 | plm_buffer_skip(self->buffer, 16); // header_length 1887 | plm_buffer_skip(self->buffer, 24); // rate bound 1888 | self->num_audio_streams = plm_buffer_read(self->buffer, 6); 1889 | plm_buffer_skip(self->buffer, 5); // misc flags 1890 | self->num_video_streams = plm_buffer_read(self->buffer, 5); 1891 | 1892 | self->has_system_header = TRUE; 1893 | } 1894 | 1895 | self->has_headers = TRUE; 1896 | return TRUE; 1897 | } 1898 | 1899 | int plm_demux_probe(plm_demux_t *self, size_t probesize) { 1900 | int previous_pos = plm_buffer_tell(self->buffer); 1901 | 1902 | int video_stream = FALSE; 1903 | int audio_streams[4] = {FALSE, FALSE, FALSE, FALSE}; 1904 | do { 1905 | self->start_code = plm_buffer_next_start_code(self->buffer); 1906 | if (self->start_code == PLM_DEMUX_PACKET_VIDEO_1) { 1907 | video_stream = TRUE; 1908 | } 1909 | else if ( 1910 | self->start_code >= PLM_DEMUX_PACKET_AUDIO_1 && 1911 | self->start_code <= PLM_DEMUX_PACKET_AUDIO_4 1912 | ) { 1913 | audio_streams[self->start_code - PLM_DEMUX_PACKET_AUDIO_1] = TRUE; 1914 | } 1915 | } while ( 1916 | self->start_code != -1 && 1917 | plm_buffer_tell(self->buffer) - previous_pos < probesize 1918 | ); 1919 | 1920 | self->num_video_streams = video_stream ? 1 : 0; 1921 | self->num_audio_streams = 0; 1922 | for (int i = 0; i < 4; i++) { 1923 | if (audio_streams[i]) { 1924 | self->num_audio_streams++; 1925 | } 1926 | } 1927 | 1928 | plm_demux_buffer_seek(self, previous_pos); 1929 | return (self->num_video_streams || self->num_audio_streams); 1930 | } 1931 | 1932 | int plm_demux_get_num_video_streams(plm_demux_t *self) { 1933 | return plm_demux_has_headers(self) 1934 | ? self->num_video_streams 1935 | : 0; 1936 | } 1937 | 1938 | int plm_demux_get_num_audio_streams(plm_demux_t *self) { 1939 | return plm_demux_has_headers(self) 1940 | ? self->num_audio_streams 1941 | : 0; 1942 | } 1943 | 1944 | void plm_demux_rewind(plm_demux_t *self) { 1945 | plm_buffer_rewind(self->buffer); 1946 | self->current_packet.length = 0; 1947 | self->next_packet.length = 0; 1948 | self->start_code = -1; 1949 | } 1950 | 1951 | int plm_demux_has_ended(plm_demux_t *self) { 1952 | return plm_buffer_has_ended(self->buffer); 1953 | } 1954 | 1955 | void plm_demux_buffer_seek(plm_demux_t *self, size_t pos) { 1956 | plm_buffer_seek(self->buffer, pos); 1957 | self->current_packet.length = 0; 1958 | self->next_packet.length = 0; 1959 | self->start_code = -1; 1960 | } 1961 | 1962 | double plm_demux_get_start_time(plm_demux_t *self, int type) { 1963 | if (self->start_time != PLM_PACKET_INVALID_TS) { 1964 | return self->start_time; 1965 | } 1966 | 1967 | int previous_pos = plm_buffer_tell(self->buffer); 1968 | int previous_start_code = self->start_code; 1969 | 1970 | // Find first video PTS 1971 | plm_demux_rewind(self); 1972 | do { 1973 | plm_packet_t *packet = plm_demux_decode(self); 1974 | if (!packet) { 1975 | break; 1976 | } 1977 | if (packet->type == type) { 1978 | self->start_time = packet->pts; 1979 | } 1980 | } while (self->start_time == PLM_PACKET_INVALID_TS); 1981 | 1982 | plm_demux_buffer_seek(self, previous_pos); 1983 | self->start_code = previous_start_code; 1984 | return self->start_time; 1985 | } 1986 | 1987 | double plm_demux_get_duration(plm_demux_t *self, int type) { 1988 | size_t file_size = plm_buffer_get_size(self->buffer); 1989 | 1990 | if ( 1991 | self->duration != PLM_PACKET_INVALID_TS && 1992 | self->last_file_size == file_size 1993 | ) { 1994 | return self->duration; 1995 | } 1996 | 1997 | size_t previous_pos = plm_buffer_tell(self->buffer); 1998 | int previous_start_code = self->start_code; 1999 | 2000 | // Find last video PTS. Start searching 64kb from the end and go further 2001 | // back if needed. 2002 | long start_range = 64 * 1024; 2003 | long max_range = 4096 * 1024; 2004 | for (long range = start_range; range <= max_range; range *= 2) { 2005 | long seek_pos = file_size - range; 2006 | if (seek_pos < 0) { 2007 | seek_pos = 0; 2008 | range = max_range; // Make sure to bail after this round 2009 | } 2010 | plm_demux_buffer_seek(self, seek_pos); 2011 | self->current_packet.length = 0; 2012 | 2013 | double last_pts = PLM_PACKET_INVALID_TS; 2014 | plm_packet_t *packet = NULL; 2015 | while ((packet = plm_demux_decode(self))) { 2016 | if (packet->pts != PLM_PACKET_INVALID_TS && packet->type == type) { 2017 | last_pts = packet->pts; 2018 | } 2019 | } 2020 | if (last_pts != PLM_PACKET_INVALID_TS) { 2021 | self->duration = last_pts - plm_demux_get_start_time(self, type); 2022 | break; 2023 | } 2024 | } 2025 | 2026 | plm_demux_buffer_seek(self, previous_pos); 2027 | self->start_code = previous_start_code; 2028 | self->last_file_size = file_size; 2029 | return self->duration; 2030 | } 2031 | 2032 | plm_packet_t *plm_demux_seek(plm_demux_t *self, double seek_time, int type, int force_intra) { 2033 | if (!plm_demux_has_headers(self)) { 2034 | return NULL; 2035 | } 2036 | 2037 | // Using the current time, current byte position and the average bytes per 2038 | // second for this file, try to jump to a byte position that hopefully has 2039 | // packets containing timestamps within one second before to the desired 2040 | // seek_time. 2041 | 2042 | // If we hit close to the seek_time scan through all packets to find the 2043 | // last one (just before the seek_time) containing an intra frame. 2044 | // Otherwise we should at least be closer than before. Calculate the bytes 2045 | // per second for the jumped range and jump again. 2046 | 2047 | // The number of retries here is hard-limited to a generous amount. Usually 2048 | // the correct range is found after 1--5 jumps, even for files with very 2049 | // variable bitrates. If significantly more jumps are needed, there's 2050 | // probably something wrong with the file and we just avoid getting into an 2051 | // infinite loop. 32 retries should be enough for anybody. 2052 | 2053 | double duration = plm_demux_get_duration(self, type); 2054 | long file_size = plm_buffer_get_size(self->buffer); 2055 | long byterate = file_size / duration; 2056 | 2057 | double cur_time = self->last_decoded_pts; 2058 | double scan_span = 1; 2059 | 2060 | if (seek_time > duration) { 2061 | seek_time = duration; 2062 | } 2063 | else if (seek_time < 0) { 2064 | seek_time = 0; 2065 | } 2066 | seek_time += self->start_time; 2067 | 2068 | for (int retry = 0; retry < 32; retry++) { 2069 | int found_packet_with_pts = FALSE; 2070 | int found_packet_in_range = FALSE; 2071 | long last_valid_packet_start = -1; 2072 | double first_packet_time = PLM_PACKET_INVALID_TS; 2073 | 2074 | long cur_pos = plm_buffer_tell(self->buffer); 2075 | 2076 | // Estimate byte offset and jump to it. 2077 | long offset = (seek_time - cur_time - scan_span) * byterate; 2078 | long seek_pos = cur_pos + offset; 2079 | if (seek_pos < 0) { 2080 | seek_pos = 0; 2081 | } 2082 | else if (seek_pos > file_size - 256) { 2083 | seek_pos = file_size - 256; 2084 | } 2085 | 2086 | plm_demux_buffer_seek(self, seek_pos); 2087 | 2088 | // Scan through all packets up to the seek_time to find the last packet 2089 | // containing an intra frame. 2090 | while (plm_buffer_find_start_code(self->buffer, type) != -1) { 2091 | long packet_start = plm_buffer_tell(self->buffer); 2092 | plm_packet_t *packet = plm_demux_decode_packet(self, type); 2093 | 2094 | // Skip packet if it has no PTS 2095 | if (!packet || packet->pts == PLM_PACKET_INVALID_TS) { 2096 | continue; 2097 | } 2098 | 2099 | // Bail scanning through packets if we hit one that is outside 2100 | // seek_time - scan_span. 2101 | // We also adjust the cur_time and byterate values here so the next 2102 | // iteration can be a bit more precise. 2103 | if (packet->pts > seek_time || packet->pts < seek_time - scan_span) { 2104 | found_packet_with_pts = TRUE; 2105 | byterate = (seek_pos - cur_pos) / (packet->pts - cur_time); 2106 | cur_time = packet->pts; 2107 | break; 2108 | } 2109 | 2110 | // If we are still here, it means this packet is in close range to 2111 | // the seek_time. If this is the first packet for this jump position 2112 | // record the PTS. If we later have to back off, when there was no 2113 | // intra frame in this range, we can lower the seek_time to not scan 2114 | // this range again. 2115 | if (!found_packet_in_range) { 2116 | found_packet_in_range = TRUE; 2117 | first_packet_time = packet->pts; 2118 | } 2119 | 2120 | // Check if this is an intra frame packet. If so, record the buffer 2121 | // position of the start of this packet. We want to jump back to it 2122 | // later, when we know it's the last intra frame before desired 2123 | // seek time. 2124 | if (force_intra) { 2125 | for (size_t i = 0; i < packet->length - 6; i++) { 2126 | // Find the START_PICTURE code 2127 | if ( 2128 | packet->data[i] == 0x00 && 2129 | packet->data[i + 1] == 0x00 && 2130 | packet->data[i + 2] == 0x01 && 2131 | packet->data[i + 3] == 0x00 2132 | ) { 2133 | // Bits 11--13 in the picture header contain the frame 2134 | // type, where 1=Intra 2135 | if ((packet->data[i + 5] & 0x38) == 8) { 2136 | last_valid_packet_start = packet_start; 2137 | } 2138 | break; 2139 | } 2140 | } 2141 | } 2142 | 2143 | // If we don't want intra frames, just use the last PTS found. 2144 | else { 2145 | last_valid_packet_start = packet_start; 2146 | } 2147 | } 2148 | 2149 | // If there was at least one intra frame in the range scanned above, 2150 | // our search is over. Jump back to the packet and decode it again. 2151 | if (last_valid_packet_start != -1) { 2152 | plm_demux_buffer_seek(self, last_valid_packet_start); 2153 | return plm_demux_decode_packet(self, type); 2154 | } 2155 | 2156 | // If we hit the right range, but still found no intra frame, we have 2157 | // to increases the scan_span. This is done exponentially to also handle 2158 | // video files with very few intra frames. 2159 | else if (found_packet_in_range) { 2160 | scan_span *= 2; 2161 | seek_time = first_packet_time; 2162 | } 2163 | 2164 | // If we didn't find any packet with a PTS, it probably means we reached 2165 | // the end of the file. Estimate byterate and cur_time accordingly. 2166 | else if (!found_packet_with_pts) { 2167 | byterate = (seek_pos - cur_pos) / (duration - cur_time); 2168 | cur_time = duration; 2169 | } 2170 | } 2171 | 2172 | return NULL; 2173 | } 2174 | 2175 | plm_packet_t *plm_demux_decode(plm_demux_t *self) { 2176 | if (!plm_demux_has_headers(self)) { 2177 | return NULL; 2178 | } 2179 | 2180 | if (self->current_packet.length) { 2181 | size_t bits_till_next_packet = self->current_packet.length << 3; 2182 | if (!plm_buffer_has(self->buffer, bits_till_next_packet)) { 2183 | return NULL; 2184 | } 2185 | plm_buffer_skip(self->buffer, bits_till_next_packet); 2186 | self->current_packet.length = 0; 2187 | } 2188 | 2189 | // Pending packet waiting for data? 2190 | if (self->next_packet.length) { 2191 | return plm_demux_get_packet(self); 2192 | } 2193 | 2194 | // Pending packet waiting for header? 2195 | if (self->start_code != -1) { 2196 | return plm_demux_decode_packet(self, self->start_code); 2197 | } 2198 | 2199 | do { 2200 | self->start_code = plm_buffer_next_start_code(self->buffer); 2201 | if ( 2202 | self->start_code == PLM_DEMUX_PACKET_VIDEO_1 || 2203 | self->start_code == PLM_DEMUX_PACKET_PRIVATE || ( 2204 | self->start_code >= PLM_DEMUX_PACKET_AUDIO_1 && 2205 | self->start_code <= PLM_DEMUX_PACKET_AUDIO_4 2206 | ) 2207 | ) { 2208 | return plm_demux_decode_packet(self, self->start_code); 2209 | } 2210 | } while (self->start_code != -1); 2211 | 2212 | return NULL; 2213 | } 2214 | 2215 | double plm_demux_decode_time(plm_demux_t *self) { 2216 | int64_t clock = plm_buffer_read(self->buffer, 3) << 30; 2217 | plm_buffer_skip(self->buffer, 1); 2218 | clock |= plm_buffer_read(self->buffer, 15) << 15; 2219 | plm_buffer_skip(self->buffer, 1); 2220 | clock |= plm_buffer_read(self->buffer, 15); 2221 | plm_buffer_skip(self->buffer, 1); 2222 | return (double)clock / 90000.0; 2223 | } 2224 | 2225 | plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int type) { 2226 | if (!plm_buffer_has(self->buffer, 16 << 3)) { 2227 | return NULL; 2228 | } 2229 | 2230 | self->start_code = -1; 2231 | 2232 | self->next_packet.type = type; 2233 | self->next_packet.length = plm_buffer_read(self->buffer, 16); 2234 | self->next_packet.length -= plm_buffer_skip_bytes(self->buffer, 0xff); // stuffing 2235 | 2236 | // skip P-STD 2237 | if (plm_buffer_read(self->buffer, 2) == 0x01) { 2238 | plm_buffer_skip(self->buffer, 16); 2239 | self->next_packet.length -= 2; 2240 | } 2241 | 2242 | int pts_dts_marker = plm_buffer_read(self->buffer, 2); 2243 | if (pts_dts_marker == 0x03) { 2244 | self->next_packet.pts = plm_demux_decode_time(self); 2245 | self->last_decoded_pts = self->next_packet.pts; 2246 | plm_buffer_skip(self->buffer, 40); // skip dts 2247 | self->next_packet.length -= 10; 2248 | } 2249 | else if (pts_dts_marker == 0x02) { 2250 | self->next_packet.pts = plm_demux_decode_time(self); 2251 | self->last_decoded_pts = self->next_packet.pts; 2252 | self->next_packet.length -= 5; 2253 | } 2254 | else if (pts_dts_marker == 0x00) { 2255 | self->next_packet.pts = PLM_PACKET_INVALID_TS; 2256 | plm_buffer_skip(self->buffer, 4); 2257 | self->next_packet.length -= 1; 2258 | } 2259 | else { 2260 | return NULL; // invalid 2261 | } 2262 | 2263 | return plm_demux_get_packet(self); 2264 | } 2265 | 2266 | plm_packet_t *plm_demux_get_packet(plm_demux_t *self) { 2267 | if (!plm_buffer_has(self->buffer, self->next_packet.length << 3)) { 2268 | return NULL; 2269 | } 2270 | 2271 | self->current_packet.data = self->buffer->bytes + (self->buffer->bit_index >> 3); 2272 | self->current_packet.length = self->next_packet.length; 2273 | self->current_packet.type = self->next_packet.type; 2274 | self->current_packet.pts = self->next_packet.pts; 2275 | 2276 | self->next_packet.length = 0; 2277 | return &self->current_packet; 2278 | } 2279 | 2280 | 2281 | 2282 | // ----------------------------------------------------------------------------- 2283 | // plm_video implementation 2284 | 2285 | // Inspired by Java MPEG-1 Video Decoder and Player by Zoltan Korandi 2286 | // https://sourceforge.net/projects/javampeg1video/ 2287 | 2288 | static const int PLM_VIDEO_PICTURE_TYPE_INTRA = 1; 2289 | static const int PLM_VIDEO_PICTURE_TYPE_PREDICTIVE = 2; 2290 | static const int PLM_VIDEO_PICTURE_TYPE_B = 3; 2291 | 2292 | static const int PLM_START_SEQUENCE = 0xB3; 2293 | static const int PLM_START_SLICE_FIRST = 0x01; 2294 | static const int PLM_START_SLICE_LAST = 0xAF; 2295 | static const int PLM_START_PICTURE = 0x00; 2296 | static const int PLM_START_EXTENSION = 0xB5; 2297 | static const int PLM_START_USER_DATA = 0xB2; 2298 | 2299 | #define PLM_START_IS_SLICE(c) \ 2300 | (c >= PLM_START_SLICE_FIRST && c <= PLM_START_SLICE_LAST) 2301 | 2302 | static const float PLM_VIDEO_PIXEL_ASPECT_RATIO[] = { 2303 | 1.0000, /* square pixels */ 2304 | 0.6735, /* 3:4? */ 2305 | 0.7031, /* MPEG-1 / MPEG-2 video encoding divergence? */ 2306 | 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 2307 | 1.0255, 1.0695, 1.0950, 1.1575, 1.2051, 2308 | }; 2309 | 2310 | static const double PLM_VIDEO_PICTURE_RATE[] = { 2311 | 0.000, 23.976, 24.000, 25.000, 29.970, 30.000, 50.000, 59.940, 2312 | 60.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000 2313 | }; 2314 | 2315 | static const uint8_t PLM_VIDEO_ZIG_ZAG[] = { 2316 | 0, 1, 8, 16, 9, 2, 3, 10, 2317 | 17, 24, 32, 25, 18, 11, 4, 5, 2318 | 12, 19, 26, 33, 40, 48, 41, 34, 2319 | 27, 20, 13, 6, 7, 14, 21, 28, 2320 | 35, 42, 49, 56, 57, 50, 43, 36, 2321 | 29, 22, 15, 23, 30, 37, 44, 51, 2322 | 58, 59, 52, 45, 38, 31, 39, 46, 2323 | 53, 60, 61, 54, 47, 55, 62, 63 2324 | }; 2325 | 2326 | static const uint8_t PLM_VIDEO_INTRA_QUANT_MATRIX[] = { 2327 | 8, 16, 19, 22, 26, 27, 29, 34, 2328 | 16, 16, 22, 24, 27, 29, 34, 37, 2329 | 19, 22, 26, 27, 29, 34, 34, 38, 2330 | 22, 22, 26, 27, 29, 34, 37, 40, 2331 | 22, 26, 27, 29, 32, 35, 40, 48, 2332 | 26, 27, 29, 32, 35, 40, 48, 58, 2333 | 26, 27, 29, 34, 38, 46, 56, 69, 2334 | 27, 29, 35, 38, 46, 56, 69, 83 2335 | }; 2336 | 2337 | static const uint8_t PLM_VIDEO_NON_INTRA_QUANT_MATRIX[] = { 2338 | 16, 16, 16, 16, 16, 16, 16, 16, 2339 | 16, 16, 16, 16, 16, 16, 16, 16, 2340 | 16, 16, 16, 16, 16, 16, 16, 16, 2341 | 16, 16, 16, 16, 16, 16, 16, 16, 2342 | 16, 16, 16, 16, 16, 16, 16, 16, 2343 | 16, 16, 16, 16, 16, 16, 16, 16, 2344 | 16, 16, 16, 16, 16, 16, 16, 16, 2345 | 16, 16, 16, 16, 16, 16, 16, 16 2346 | }; 2347 | 2348 | static const uint8_t PLM_VIDEO_PREMULTIPLIER_MATRIX[] = { 2349 | 32, 44, 42, 38, 32, 25, 17, 9, 2350 | 44, 62, 58, 52, 44, 35, 24, 12, 2351 | 42, 58, 55, 49, 42, 33, 23, 12, 2352 | 38, 52, 49, 44, 38, 30, 20, 10, 2353 | 32, 44, 42, 38, 32, 25, 17, 9, 2354 | 25, 35, 33, 30, 25, 20, 14, 7, 2355 | 17, 24, 23, 20, 17, 14, 9, 5, 2356 | 9, 12, 12, 10, 9, 7, 5, 2 2357 | }; 2358 | 2359 | static const plm_vlc_t PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT[] = { 2360 | { 1 << 1, 0}, { 0, 1}, // 0: x 2361 | { 2 << 1, 0}, { 3 << 1, 0}, // 1: 0x 2362 | { 4 << 1, 0}, { 5 << 1, 0}, // 2: 00x 2363 | { 0, 3}, { 0, 2}, // 3: 01x 2364 | { 6 << 1, 0}, { 7 << 1, 0}, // 4: 000x 2365 | { 0, 5}, { 0, 4}, // 5: 001x 2366 | { 8 << 1, 0}, { 9 << 1, 0}, // 6: 0000x 2367 | { 0, 7}, { 0, 6}, // 7: 0001x 2368 | { 10 << 1, 0}, { 11 << 1, 0}, // 8: 0000 0x 2369 | { 12 << 1, 0}, { 13 << 1, 0}, // 9: 0000 1x 2370 | { 14 << 1, 0}, { 15 << 1, 0}, // 10: 0000 00x 2371 | { 16 << 1, 0}, { 17 << 1, 0}, // 11: 0000 01x 2372 | { 18 << 1, 0}, { 19 << 1, 0}, // 12: 0000 10x 2373 | { 0, 9}, { 0, 8}, // 13: 0000 11x 2374 | { -1, 0}, { 20 << 1, 0}, // 14: 0000 000x 2375 | { -1, 0}, { 21 << 1, 0}, // 15: 0000 001x 2376 | { 22 << 1, 0}, { 23 << 1, 0}, // 16: 0000 010x 2377 | { 0, 15}, { 0, 14}, // 17: 0000 011x 2378 | { 0, 13}, { 0, 12}, // 18: 0000 100x 2379 | { 0, 11}, { 0, 10}, // 19: 0000 101x 2380 | { 24 << 1, 0}, { 25 << 1, 0}, // 20: 0000 0001x 2381 | { 26 << 1, 0}, { 27 << 1, 0}, // 21: 0000 0011x 2382 | { 28 << 1, 0}, { 29 << 1, 0}, // 22: 0000 0100x 2383 | { 30 << 1, 0}, { 31 << 1, 0}, // 23: 0000 0101x 2384 | { 32 << 1, 0}, { -1, 0}, // 24: 0000 0001 0x 2385 | { -1, 0}, { 33 << 1, 0}, // 25: 0000 0001 1x 2386 | { 34 << 1, 0}, { 35 << 1, 0}, // 26: 0000 0011 0x 2387 | { 36 << 1, 0}, { 37 << 1, 0}, // 27: 0000 0011 1x 2388 | { 38 << 1, 0}, { 39 << 1, 0}, // 28: 0000 0100 0x 2389 | { 0, 21}, { 0, 20}, // 29: 0000 0100 1x 2390 | { 0, 19}, { 0, 18}, // 30: 0000 0101 0x 2391 | { 0, 17}, { 0, 16}, // 31: 0000 0101 1x 2392 | { 0, 35}, { -1, 0}, // 32: 0000 0001 00x 2393 | { -1, 0}, { 0, 34}, // 33: 0000 0001 11x 2394 | { 0, 33}, { 0, 32}, // 34: 0000 0011 00x 2395 | { 0, 31}, { 0, 30}, // 35: 0000 0011 01x 2396 | { 0, 29}, { 0, 28}, // 36: 0000 0011 10x 2397 | { 0, 27}, { 0, 26}, // 37: 0000 0011 11x 2398 | { 0, 25}, { 0, 24}, // 38: 0000 0100 00x 2399 | { 0, 23}, { 0, 22}, // 39: 0000 0100 01x 2400 | }; 2401 | 2402 | static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_INTRA[] = { 2403 | { 1 << 1, 0}, { 0, 0x01}, // 0: x 2404 | { -1, 0}, { 0, 0x11}, // 1: 0x 2405 | }; 2406 | 2407 | static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_PREDICTIVE[] = { 2408 | { 1 << 1, 0}, { 0, 0x0a}, // 0: x 2409 | { 2 << 1, 0}, { 0, 0x02}, // 1: 0x 2410 | { 3 << 1, 0}, { 0, 0x08}, // 2: 00x 2411 | { 4 << 1, 0}, { 5 << 1, 0}, // 3: 000x 2412 | { 6 << 1, 0}, { 0, 0x12}, // 4: 0000x 2413 | { 0, 0x1a}, { 0, 0x01}, // 5: 0001x 2414 | { -1, 0}, { 0, 0x11}, // 6: 0000 0x 2415 | }; 2416 | 2417 | static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_B[] = { 2418 | { 1 << 1, 0}, { 2 << 1, 0}, // 0: x 2419 | { 3 << 1, 0}, { 4 << 1, 0}, // 1: 0x 2420 | { 0, 0x0c}, { 0, 0x0e}, // 2: 1x 2421 | { 5 << 1, 0}, { 6 << 1, 0}, // 3: 00x 2422 | { 0, 0x04}, { 0, 0x06}, // 4: 01x 2423 | { 7 << 1, 0}, { 8 << 1, 0}, // 5: 000x 2424 | { 0, 0x08}, { 0, 0x0a}, // 6: 001x 2425 | { 9 << 1, 0}, { 10 << 1, 0}, // 7: 0000x 2426 | { 0, 0x1e}, { 0, 0x01}, // 8: 0001x 2427 | { -1, 0}, { 0, 0x11}, // 9: 0000 0x 2428 | { 0, 0x16}, { 0, 0x1a}, // 10: 0000 1x 2429 | }; 2430 | 2431 | static const plm_vlc_t *PLM_VIDEO_MACROBLOCK_TYPE[] = { 2432 | NULL, 2433 | PLM_VIDEO_MACROBLOCK_TYPE_INTRA, 2434 | PLM_VIDEO_MACROBLOCK_TYPE_PREDICTIVE, 2435 | PLM_VIDEO_MACROBLOCK_TYPE_B 2436 | }; 2437 | 2438 | static const plm_vlc_t PLM_VIDEO_CODE_BLOCK_PATTERN[] = { 2439 | { 1 << 1, 0}, { 2 << 1, 0}, // 0: x 2440 | { 3 << 1, 0}, { 4 << 1, 0}, // 1: 0x 2441 | { 5 << 1, 0}, { 6 << 1, 0}, // 2: 1x 2442 | { 7 << 1, 0}, { 8 << 1, 0}, // 3: 00x 2443 | { 9 << 1, 0}, { 10 << 1, 0}, // 4: 01x 2444 | { 11 << 1, 0}, { 12 << 1, 0}, // 5: 10x 2445 | { 13 << 1, 0}, { 0, 60}, // 6: 11x 2446 | { 14 << 1, 0}, { 15 << 1, 0}, // 7: 000x 2447 | { 16 << 1, 0}, { 17 << 1, 0}, // 8: 001x 2448 | { 18 << 1, 0}, { 19 << 1, 0}, // 9: 010x 2449 | { 20 << 1, 0}, { 21 << 1, 0}, // 10: 011x 2450 | { 22 << 1, 0}, { 23 << 1, 0}, // 11: 100x 2451 | { 0, 32}, { 0, 16}, // 12: 101x 2452 | { 0, 8}, { 0, 4}, // 13: 110x 2453 | { 24 << 1, 0}, { 25 << 1, 0}, // 14: 0000x 2454 | { 26 << 1, 0}, { 27 << 1, 0}, // 15: 0001x 2455 | { 28 << 1, 0}, { 29 << 1, 0}, // 16: 0010x 2456 | { 30 << 1, 0}, { 31 << 1, 0}, // 17: 0011x 2457 | { 0, 62}, { 0, 2}, // 18: 0100x 2458 | { 0, 61}, { 0, 1}, // 19: 0101x 2459 | { 0, 56}, { 0, 52}, // 20: 0110x 2460 | { 0, 44}, { 0, 28}, // 21: 0111x 2461 | { 0, 40}, { 0, 20}, // 22: 1000x 2462 | { 0, 48}, { 0, 12}, // 23: 1001x 2463 | { 32 << 1, 0}, { 33 << 1, 0}, // 24: 0000 0x 2464 | { 34 << 1, 0}, { 35 << 1, 0}, // 25: 0000 1x 2465 | { 36 << 1, 0}, { 37 << 1, 0}, // 26: 0001 0x 2466 | { 38 << 1, 0}, { 39 << 1, 0}, // 27: 0001 1x 2467 | { 40 << 1, 0}, { 41 << 1, 0}, // 28: 0010 0x 2468 | { 42 << 1, 0}, { 43 << 1, 0}, // 29: 0010 1x 2469 | { 0, 63}, { 0, 3}, // 30: 0011 0x 2470 | { 0, 36}, { 0, 24}, // 31: 0011 1x 2471 | { 44 << 1, 0}, { 45 << 1, 0}, // 32: 0000 00x 2472 | { 46 << 1, 0}, { 47 << 1, 0}, // 33: 0000 01x 2473 | { 48 << 1, 0}, { 49 << 1, 0}, // 34: 0000 10x 2474 | { 50 << 1, 0}, { 51 << 1, 0}, // 35: 0000 11x 2475 | { 52 << 1, 0}, { 53 << 1, 0}, // 36: 0001 00x 2476 | { 54 << 1, 0}, { 55 << 1, 0}, // 37: 0001 01x 2477 | { 56 << 1, 0}, { 57 << 1, 0}, // 38: 0001 10x 2478 | { 58 << 1, 0}, { 59 << 1, 0}, // 39: 0001 11x 2479 | { 0, 34}, { 0, 18}, // 40: 0010 00x 2480 | { 0, 10}, { 0, 6}, // 41: 0010 01x 2481 | { 0, 33}, { 0, 17}, // 42: 0010 10x 2482 | { 0, 9}, { 0, 5}, // 43: 0010 11x 2483 | { -1, 0}, { 60 << 1, 0}, // 44: 0000 000x 2484 | { 61 << 1, 0}, { 62 << 1, 0}, // 45: 0000 001x 2485 | { 0, 58}, { 0, 54}, // 46: 0000 010x 2486 | { 0, 46}, { 0, 30}, // 47: 0000 011x 2487 | { 0, 57}, { 0, 53}, // 48: 0000 100x 2488 | { 0, 45}, { 0, 29}, // 49: 0000 101x 2489 | { 0, 38}, { 0, 26}, // 50: 0000 110x 2490 | { 0, 37}, { 0, 25}, // 51: 0000 111x 2491 | { 0, 43}, { 0, 23}, // 52: 0001 000x 2492 | { 0, 51}, { 0, 15}, // 53: 0001 001x 2493 | { 0, 42}, { 0, 22}, // 54: 0001 010x 2494 | { 0, 50}, { 0, 14}, // 55: 0001 011x 2495 | { 0, 41}, { 0, 21}, // 56: 0001 100x 2496 | { 0, 49}, { 0, 13}, // 57: 0001 101x 2497 | { 0, 35}, { 0, 19}, // 58: 0001 110x 2498 | { 0, 11}, { 0, 7}, // 59: 0001 111x 2499 | { 0, 39}, { 0, 27}, // 60: 0000 0001x 2500 | { 0, 59}, { 0, 55}, // 61: 0000 0010x 2501 | { 0, 47}, { 0, 31}, // 62: 0000 0011x 2502 | }; 2503 | 2504 | static const plm_vlc_t PLM_VIDEO_MOTION[] = { 2505 | { 1 << 1, 0}, { 0, 0}, // 0: x 2506 | { 2 << 1, 0}, { 3 << 1, 0}, // 1: 0x 2507 | { 4 << 1, 0}, { 5 << 1, 0}, // 2: 00x 2508 | { 0, 1}, { 0, -1}, // 3: 01x 2509 | { 6 << 1, 0}, { 7 << 1, 0}, // 4: 000x 2510 | { 0, 2}, { 0, -2}, // 5: 001x 2511 | { 8 << 1, 0}, { 9 << 1, 0}, // 6: 0000x 2512 | { 0, 3}, { 0, -3}, // 7: 0001x 2513 | { 10 << 1, 0}, { 11 << 1, 0}, // 8: 0000 0x 2514 | { 12 << 1, 0}, { 13 << 1, 0}, // 9: 0000 1x 2515 | { -1, 0}, { 14 << 1, 0}, // 10: 0000 00x 2516 | { 15 << 1, 0}, { 16 << 1, 0}, // 11: 0000 01x 2517 | { 17 << 1, 0}, { 18 << 1, 0}, // 12: 0000 10x 2518 | { 0, 4}, { 0, -4}, // 13: 0000 11x 2519 | { -1, 0}, { 19 << 1, 0}, // 14: 0000 001x 2520 | { 20 << 1, 0}, { 21 << 1, 0}, // 15: 0000 010x 2521 | { 0, 7}, { 0, -7}, // 16: 0000 011x 2522 | { 0, 6}, { 0, -6}, // 17: 0000 100x 2523 | { 0, 5}, { 0, -5}, // 18: 0000 101x 2524 | { 22 << 1, 0}, { 23 << 1, 0}, // 19: 0000 0011x 2525 | { 24 << 1, 0}, { 25 << 1, 0}, // 20: 0000 0100x 2526 | { 26 << 1, 0}, { 27 << 1, 0}, // 21: 0000 0101x 2527 | { 28 << 1, 0}, { 29 << 1, 0}, // 22: 0000 0011 0x 2528 | { 30 << 1, 0}, { 31 << 1, 0}, // 23: 0000 0011 1x 2529 | { 32 << 1, 0}, { 33 << 1, 0}, // 24: 0000 0100 0x 2530 | { 0, 10}, { 0, -10}, // 25: 0000 0100 1x 2531 | { 0, 9}, { 0, -9}, // 26: 0000 0101 0x 2532 | { 0, 8}, { 0, -8}, // 27: 0000 0101 1x 2533 | { 0, 16}, { 0, -16}, // 28: 0000 0011 00x 2534 | { 0, 15}, { 0, -15}, // 29: 0000 0011 01x 2535 | { 0, 14}, { 0, -14}, // 30: 0000 0011 10x 2536 | { 0, 13}, { 0, -13}, // 31: 0000 0011 11x 2537 | { 0, 12}, { 0, -12}, // 32: 0000 0100 00x 2538 | { 0, 11}, { 0, -11}, // 33: 0000 0100 01x 2539 | }; 2540 | 2541 | static const plm_vlc_t PLM_VIDEO_DCT_SIZE_LUMINANCE[] = { 2542 | { 1 << 1, 0}, { 2 << 1, 0}, // 0: x 2543 | { 0, 1}, { 0, 2}, // 1: 0x 2544 | { 3 << 1, 0}, { 4 << 1, 0}, // 2: 1x 2545 | { 0, 0}, { 0, 3}, // 3: 10x 2546 | { 0, 4}, { 5 << 1, 0}, // 4: 11x 2547 | { 0, 5}, { 6 << 1, 0}, // 5: 111x 2548 | { 0, 6}, { 7 << 1, 0}, // 6: 1111x 2549 | { 0, 7}, { 8 << 1, 0}, // 7: 1111 1x 2550 | { 0, 8}, { -1, 0}, // 8: 1111 11x 2551 | }; 2552 | 2553 | static const plm_vlc_t PLM_VIDEO_DCT_SIZE_CHROMINANCE[] = { 2554 | { 1 << 1, 0}, { 2 << 1, 0}, // 0: x 2555 | { 0, 0}, { 0, 1}, // 1: 0x 2556 | { 0, 2}, { 3 << 1, 0}, // 2: 1x 2557 | { 0, 3}, { 4 << 1, 0}, // 3: 11x 2558 | { 0, 4}, { 5 << 1, 0}, // 4: 111x 2559 | { 0, 5}, { 6 << 1, 0}, // 5: 1111x 2560 | { 0, 6}, { 7 << 1, 0}, // 6: 1111 1x 2561 | { 0, 7}, { 8 << 1, 0}, // 7: 1111 11x 2562 | { 0, 8}, { -1, 0}, // 8: 1111 111x 2563 | }; 2564 | 2565 | static const plm_vlc_t *PLM_VIDEO_DCT_SIZE[] = { 2566 | PLM_VIDEO_DCT_SIZE_LUMINANCE, 2567 | PLM_VIDEO_DCT_SIZE_CHROMINANCE, 2568 | PLM_VIDEO_DCT_SIZE_CHROMINANCE 2569 | }; 2570 | 2571 | 2572 | // dct_coeff bitmap: 2573 | // 0xff00 run 2574 | // 0x00ff level 2575 | 2576 | // Decoded values are unsigned. Sign bit follows in the stream. 2577 | 2578 | static const plm_vlc_uint_t PLM_VIDEO_DCT_COEFF[] = { 2579 | { 1 << 1, 0}, { 0, 0x0001}, // 0: x 2580 | { 2 << 1, 0}, { 3 << 1, 0}, // 1: 0x 2581 | { 4 << 1, 0}, { 5 << 1, 0}, // 2: 00x 2582 | { 6 << 1, 0}, { 0, 0x0101}, // 3: 01x 2583 | { 7 << 1, 0}, { 8 << 1, 0}, // 4: 000x 2584 | { 9 << 1, 0}, { 10 << 1, 0}, // 5: 001x 2585 | { 0, 0x0002}, { 0, 0x0201}, // 6: 010x 2586 | { 11 << 1, 0}, { 12 << 1, 0}, // 7: 0000x 2587 | { 13 << 1, 0}, { 14 << 1, 0}, // 8: 0001x 2588 | { 15 << 1, 0}, { 0, 0x0003}, // 9: 0010x 2589 | { 0, 0x0401}, { 0, 0x0301}, // 10: 0011x 2590 | { 16 << 1, 0}, { 0, 0xffff}, // 11: 0000 0x 2591 | { 17 << 1, 0}, { 18 << 1, 0}, // 12: 0000 1x 2592 | { 0, 0x0701}, { 0, 0x0601}, // 13: 0001 0x 2593 | { 0, 0x0102}, { 0, 0x0501}, // 14: 0001 1x 2594 | { 19 << 1, 0}, { 20 << 1, 0}, // 15: 0010 0x 2595 | { 21 << 1, 0}, { 22 << 1, 0}, // 16: 0000 00x 2596 | { 0, 0x0202}, { 0, 0x0901}, // 17: 0000 10x 2597 | { 0, 0x0004}, { 0, 0x0801}, // 18: 0000 11x 2598 | { 23 << 1, 0}, { 24 << 1, 0}, // 19: 0010 00x 2599 | { 25 << 1, 0}, { 26 << 1, 0}, // 20: 0010 01x 2600 | { 27 << 1, 0}, { 28 << 1, 0}, // 21: 0000 000x 2601 | { 29 << 1, 0}, { 30 << 1, 0}, // 22: 0000 001x 2602 | { 0, 0x0d01}, { 0, 0x0006}, // 23: 0010 000x 2603 | { 0, 0x0c01}, { 0, 0x0b01}, // 24: 0010 001x 2604 | { 0, 0x0302}, { 0, 0x0103}, // 25: 0010 010x 2605 | { 0, 0x0005}, { 0, 0x0a01}, // 26: 0010 011x 2606 | { 31 << 1, 0}, { 32 << 1, 0}, // 27: 0000 0000x 2607 | { 33 << 1, 0}, { 34 << 1, 0}, // 28: 0000 0001x 2608 | { 35 << 1, 0}, { 36 << 1, 0}, // 29: 0000 0010x 2609 | { 37 << 1, 0}, { 38 << 1, 0}, // 30: 0000 0011x 2610 | { 39 << 1, 0}, { 40 << 1, 0}, // 31: 0000 0000 0x 2611 | { 41 << 1, 0}, { 42 << 1, 0}, // 32: 0000 0000 1x 2612 | { 43 << 1, 0}, { 44 << 1, 0}, // 33: 0000 0001 0x 2613 | { 45 << 1, 0}, { 46 << 1, 0}, // 34: 0000 0001 1x 2614 | { 0, 0x1001}, { 0, 0x0502}, // 35: 0000 0010 0x 2615 | { 0, 0x0007}, { 0, 0x0203}, // 36: 0000 0010 1x 2616 | { 0, 0x0104}, { 0, 0x0f01}, // 37: 0000 0011 0x 2617 | { 0, 0x0e01}, { 0, 0x0402}, // 38: 0000 0011 1x 2618 | { 47 << 1, 0}, { 48 << 1, 0}, // 39: 0000 0000 00x 2619 | { 49 << 1, 0}, { 50 << 1, 0}, // 40: 0000 0000 01x 2620 | { 51 << 1, 0}, { 52 << 1, 0}, // 41: 0000 0000 10x 2621 | { 53 << 1, 0}, { 54 << 1, 0}, // 42: 0000 0000 11x 2622 | { 55 << 1, 0}, { 56 << 1, 0}, // 43: 0000 0001 00x 2623 | { 57 << 1, 0}, { 58 << 1, 0}, // 44: 0000 0001 01x 2624 | { 59 << 1, 0}, { 60 << 1, 0}, // 45: 0000 0001 10x 2625 | { 61 << 1, 0}, { 62 << 1, 0}, // 46: 0000 0001 11x 2626 | { -1, 0}, { 63 << 1, 0}, // 47: 0000 0000 000x 2627 | { 64 << 1, 0}, { 65 << 1, 0}, // 48: 0000 0000 001x 2628 | { 66 << 1, 0}, { 67 << 1, 0}, // 49: 0000 0000 010x 2629 | { 68 << 1, 0}, { 69 << 1, 0}, // 50: 0000 0000 011x 2630 | { 70 << 1, 0}, { 71 << 1, 0}, // 51: 0000 0000 100x 2631 | { 72 << 1, 0}, { 73 << 1, 0}, // 52: 0000 0000 101x 2632 | { 74 << 1, 0}, { 75 << 1, 0}, // 53: 0000 0000 110x 2633 | { 76 << 1, 0}, { 77 << 1, 0}, // 54: 0000 0000 111x 2634 | { 0, 0x000b}, { 0, 0x0802}, // 55: 0000 0001 000x 2635 | { 0, 0x0403}, { 0, 0x000a}, // 56: 0000 0001 001x 2636 | { 0, 0x0204}, { 0, 0x0702}, // 57: 0000 0001 010x 2637 | { 0, 0x1501}, { 0, 0x1401}, // 58: 0000 0001 011x 2638 | { 0, 0x0009}, { 0, 0x1301}, // 59: 0000 0001 100x 2639 | { 0, 0x1201}, { 0, 0x0105}, // 60: 0000 0001 101x 2640 | { 0, 0x0303}, { 0, 0x0008}, // 61: 0000 0001 110x 2641 | { 0, 0x0602}, { 0, 0x1101}, // 62: 0000 0001 111x 2642 | { 78 << 1, 0}, { 79 << 1, 0}, // 63: 0000 0000 0001x 2643 | { 80 << 1, 0}, { 81 << 1, 0}, // 64: 0000 0000 0010x 2644 | { 82 << 1, 0}, { 83 << 1, 0}, // 65: 0000 0000 0011x 2645 | { 84 << 1, 0}, { 85 << 1, 0}, // 66: 0000 0000 0100x 2646 | { 86 << 1, 0}, { 87 << 1, 0}, // 67: 0000 0000 0101x 2647 | { 88 << 1, 0}, { 89 << 1, 0}, // 68: 0000 0000 0110x 2648 | { 90 << 1, 0}, { 91 << 1, 0}, // 69: 0000 0000 0111x 2649 | { 0, 0x0a02}, { 0, 0x0902}, // 70: 0000 0000 1000x 2650 | { 0, 0x0503}, { 0, 0x0304}, // 71: 0000 0000 1001x 2651 | { 0, 0x0205}, { 0, 0x0107}, // 72: 0000 0000 1010x 2652 | { 0, 0x0106}, { 0, 0x000f}, // 73: 0000 0000 1011x 2653 | { 0, 0x000e}, { 0, 0x000d}, // 74: 0000 0000 1100x 2654 | { 0, 0x000c}, { 0, 0x1a01}, // 75: 0000 0000 1101x 2655 | { 0, 0x1901}, { 0, 0x1801}, // 76: 0000 0000 1110x 2656 | { 0, 0x1701}, { 0, 0x1601}, // 77: 0000 0000 1111x 2657 | { 92 << 1, 0}, { 93 << 1, 0}, // 78: 0000 0000 0001 0x 2658 | { 94 << 1, 0}, { 95 << 1, 0}, // 79: 0000 0000 0001 1x 2659 | { 96 << 1, 0}, { 97 << 1, 0}, // 80: 0000 0000 0010 0x 2660 | { 98 << 1, 0}, { 99 << 1, 0}, // 81: 0000 0000 0010 1x 2661 | {100 << 1, 0}, {101 << 1, 0}, // 82: 0000 0000 0011 0x 2662 | {102 << 1, 0}, {103 << 1, 0}, // 83: 0000 0000 0011 1x 2663 | { 0, 0x001f}, { 0, 0x001e}, // 84: 0000 0000 0100 0x 2664 | { 0, 0x001d}, { 0, 0x001c}, // 85: 0000 0000 0100 1x 2665 | { 0, 0x001b}, { 0, 0x001a}, // 86: 0000 0000 0101 0x 2666 | { 0, 0x0019}, { 0, 0x0018}, // 87: 0000 0000 0101 1x 2667 | { 0, 0x0017}, { 0, 0x0016}, // 88: 0000 0000 0110 0x 2668 | { 0, 0x0015}, { 0, 0x0014}, // 89: 0000 0000 0110 1x 2669 | { 0, 0x0013}, { 0, 0x0012}, // 90: 0000 0000 0111 0x 2670 | { 0, 0x0011}, { 0, 0x0010}, // 91: 0000 0000 0111 1x 2671 | {104 << 1, 0}, {105 << 1, 0}, // 92: 0000 0000 0001 00x 2672 | {106 << 1, 0}, {107 << 1, 0}, // 93: 0000 0000 0001 01x 2673 | {108 << 1, 0}, {109 << 1, 0}, // 94: 0000 0000 0001 10x 2674 | {110 << 1, 0}, {111 << 1, 0}, // 95: 0000 0000 0001 11x 2675 | { 0, 0x0028}, { 0, 0x0027}, // 96: 0000 0000 0010 00x 2676 | { 0, 0x0026}, { 0, 0x0025}, // 97: 0000 0000 0010 01x 2677 | { 0, 0x0024}, { 0, 0x0023}, // 98: 0000 0000 0010 10x 2678 | { 0, 0x0022}, { 0, 0x0021}, // 99: 0000 0000 0010 11x 2679 | { 0, 0x0020}, { 0, 0x010e}, // 100: 0000 0000 0011 00x 2680 | { 0, 0x010d}, { 0, 0x010c}, // 101: 0000 0000 0011 01x 2681 | { 0, 0x010b}, { 0, 0x010a}, // 102: 0000 0000 0011 10x 2682 | { 0, 0x0109}, { 0, 0x0108}, // 103: 0000 0000 0011 11x 2683 | { 0, 0x0112}, { 0, 0x0111}, // 104: 0000 0000 0001 000x 2684 | { 0, 0x0110}, { 0, 0x010f}, // 105: 0000 0000 0001 001x 2685 | { 0, 0x0603}, { 0, 0x1002}, // 106: 0000 0000 0001 010x 2686 | { 0, 0x0f02}, { 0, 0x0e02}, // 107: 0000 0000 0001 011x 2687 | { 0, 0x0d02}, { 0, 0x0c02}, // 108: 0000 0000 0001 100x 2688 | { 0, 0x0b02}, { 0, 0x1f01}, // 109: 0000 0000 0001 101x 2689 | { 0, 0x1e01}, { 0, 0x1d01}, // 110: 0000 0000 0001 110x 2690 | { 0, 0x1c01}, { 0, 0x1b01}, // 111: 0000 0000 0001 111x 2691 | }; 2692 | 2693 | typedef struct { 2694 | int full_px; 2695 | int is_set; 2696 | int r_size; 2697 | int h; 2698 | int v; 2699 | } plm_video_motion_t; 2700 | 2701 | struct plm_video_t { 2702 | double framerate; 2703 | double pixel_aspect_ratio; 2704 | double time; 2705 | int frames_decoded; 2706 | int width; 2707 | int height; 2708 | int mb_width; 2709 | int mb_height; 2710 | int mb_size; 2711 | 2712 | int luma_width; 2713 | int luma_height; 2714 | 2715 | int chroma_width; 2716 | int chroma_height; 2717 | 2718 | int start_code; 2719 | int picture_type; 2720 | 2721 | plm_video_motion_t motion_forward; 2722 | plm_video_motion_t motion_backward; 2723 | 2724 | int has_sequence_header; 2725 | 2726 | int quantizer_scale; 2727 | int slice_begin; 2728 | int macroblock_address; 2729 | 2730 | int mb_row; 2731 | int mb_col; 2732 | 2733 | int macroblock_type; 2734 | int macroblock_intra; 2735 | 2736 | int dc_predictor[3]; 2737 | 2738 | plm_buffer_t *buffer; 2739 | int destroy_buffer_when_done; 2740 | 2741 | plm_frame_t frame_current; 2742 | plm_frame_t frame_forward; 2743 | plm_frame_t frame_backward; 2744 | 2745 | uint8_t *frames_data; 2746 | 2747 | int block_data[64]; 2748 | uint8_t intra_quant_matrix[64]; 2749 | uint8_t non_intra_quant_matrix[64]; 2750 | 2751 | int has_reference_frame; 2752 | int assume_no_b_frames; 2753 | }; 2754 | 2755 | static inline uint8_t plm_clamp(int n) { 2756 | if (n > 255) { 2757 | n = 255; 2758 | } 2759 | else if (n < 0) { 2760 | n = 0; 2761 | } 2762 | return n; 2763 | } 2764 | 2765 | int plm_video_decode_sequence_header(plm_video_t *self); 2766 | void plm_video_init_frame(plm_video_t *self, plm_frame_t *frame, uint8_t *base); 2767 | void plm_video_decode_picture(plm_video_t *self); 2768 | void plm_video_decode_slice(plm_video_t *self, int slice); 2769 | void plm_video_decode_macroblock(plm_video_t *self); 2770 | void plm_video_decode_motion_vectors(plm_video_t *self); 2771 | int plm_video_decode_motion_vector(plm_video_t *self, int r_size, int motion); 2772 | void plm_video_predict_macroblock(plm_video_t *self); 2773 | void plm_video_copy_macroblock(plm_video_t *self, plm_frame_t *s, int motion_h, int motion_v); 2774 | void plm_video_interpolate_macroblock(plm_video_t *self, plm_frame_t *s, int motion_h, int motion_v); 2775 | void plm_video_process_macroblock(plm_video_t *self, uint8_t *s, uint8_t *d, int mh, int mb, int bs, int interp); 2776 | void plm_video_decode_block(plm_video_t *self, int block); 2777 | void plm_video_idct(int *block); 2778 | 2779 | plm_video_t * plm_video_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) { 2780 | plm_video_t *self = (plm_video_t *)PLM_MALLOC(sizeof(plm_video_t)); 2781 | memset(self, 0, sizeof(plm_video_t)); 2782 | 2783 | self->buffer = buffer; 2784 | self->destroy_buffer_when_done = destroy_when_done; 2785 | 2786 | // Attempt to decode the sequence header 2787 | self->start_code = plm_buffer_find_start_code(self->buffer, PLM_START_SEQUENCE); 2788 | if (self->start_code != -1) { 2789 | plm_video_decode_sequence_header(self); 2790 | } 2791 | return self; 2792 | } 2793 | 2794 | void plm_video_destroy(plm_video_t *self) { 2795 | if (self->destroy_buffer_when_done) { 2796 | plm_buffer_destroy(self->buffer); 2797 | } 2798 | 2799 | if (self->has_sequence_header) { 2800 | PLM_FREE(self->frames_data); 2801 | } 2802 | 2803 | PLM_FREE(self); 2804 | } 2805 | 2806 | double plm_video_get_framerate(plm_video_t *self) { 2807 | return plm_video_has_header(self) 2808 | ? self->framerate 2809 | : 0; 2810 | } 2811 | 2812 | double plm_video_get_pixel_aspect_ratio(plm_video_t *self) { 2813 | return plm_video_has_header(self) 2814 | ? self->pixel_aspect_ratio 2815 | : 0; 2816 | } 2817 | 2818 | int plm_video_get_width(plm_video_t *self) { 2819 | return plm_video_has_header(self) 2820 | ? self->width 2821 | : 0; 2822 | } 2823 | 2824 | int plm_video_get_height(plm_video_t *self) { 2825 | return plm_video_has_header(self) 2826 | ? self->height 2827 | : 0; 2828 | } 2829 | 2830 | void plm_video_set_no_delay(plm_video_t *self, int no_delay) { 2831 | self->assume_no_b_frames = no_delay; 2832 | } 2833 | 2834 | double plm_video_get_time(plm_video_t *self) { 2835 | return self->time; 2836 | } 2837 | 2838 | void plm_video_set_time(plm_video_t *self, double time) { 2839 | self->frames_decoded = self->framerate * time; 2840 | self->time = time; 2841 | } 2842 | 2843 | void plm_video_rewind(plm_video_t *self) { 2844 | plm_buffer_rewind(self->buffer); 2845 | self->time = 0; 2846 | self->frames_decoded = 0; 2847 | self->has_reference_frame = FALSE; 2848 | self->start_code = -1; 2849 | } 2850 | 2851 | int plm_video_has_ended(plm_video_t *self) { 2852 | return plm_buffer_has_ended(self->buffer); 2853 | } 2854 | 2855 | plm_frame_t *plm_video_decode(plm_video_t *self) { 2856 | if (!plm_video_has_header(self)) { 2857 | return NULL; 2858 | } 2859 | 2860 | plm_frame_t *frame = NULL; 2861 | do { 2862 | if (self->start_code != PLM_START_PICTURE) { 2863 | self->start_code = plm_buffer_find_start_code(self->buffer, PLM_START_PICTURE); 2864 | 2865 | if (self->start_code == -1) { 2866 | // If we reached the end of the file and the previously decoded 2867 | // frame was a reference frame, we still have to return it. 2868 | if ( 2869 | self->has_reference_frame && 2870 | !self->assume_no_b_frames && 2871 | plm_buffer_has_ended(self->buffer) && ( 2872 | self->picture_type == PLM_VIDEO_PICTURE_TYPE_INTRA || 2873 | self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE 2874 | ) 2875 | ) { 2876 | self->has_reference_frame = FALSE; 2877 | frame = &self->frame_backward; 2878 | break; 2879 | } 2880 | 2881 | return NULL; 2882 | } 2883 | } 2884 | 2885 | // Make sure we have a full picture in the buffer before attempting to 2886 | // decode it. Sadly, this can only be done by seeking for the start code 2887 | // of the next picture. Also, if we didn't find the start code for the 2888 | // next picture, but the source has ended, we assume that this last 2889 | // picture is in the buffer. 2890 | if ( 2891 | plm_buffer_has_start_code(self->buffer, PLM_START_PICTURE) == -1 && 2892 | !plm_buffer_has_ended(self->buffer) 2893 | ) { 2894 | return NULL; 2895 | } 2896 | plm_buffer_discard_read_bytes(self->buffer); 2897 | 2898 | plm_video_decode_picture(self); 2899 | 2900 | if (self->assume_no_b_frames) { 2901 | frame = &self->frame_backward; 2902 | } 2903 | else if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) { 2904 | frame = &self->frame_current; 2905 | } 2906 | else if (self->has_reference_frame) { 2907 | frame = &self->frame_forward; 2908 | } 2909 | else { 2910 | self->has_reference_frame = TRUE; 2911 | } 2912 | } while (!frame); 2913 | 2914 | frame->time = self->time; 2915 | self->frames_decoded++; 2916 | self->time = (double)self->frames_decoded / self->framerate; 2917 | 2918 | return frame; 2919 | } 2920 | 2921 | int plm_video_has_header(plm_video_t *self) { 2922 | if (self->has_sequence_header) { 2923 | return TRUE; 2924 | } 2925 | 2926 | if (self->start_code != PLM_START_SEQUENCE) { 2927 | self->start_code = plm_buffer_find_start_code(self->buffer, PLM_START_SEQUENCE); 2928 | } 2929 | if (self->start_code == -1) { 2930 | return FALSE; 2931 | } 2932 | 2933 | if (!plm_video_decode_sequence_header(self)) { 2934 | return FALSE; 2935 | } 2936 | 2937 | return TRUE; 2938 | } 2939 | 2940 | int plm_video_decode_sequence_header(plm_video_t *self) { 2941 | int max_header_size = 64 + 2 * 64 * 8; // 64 bit header + 2x 64 byte matrix 2942 | if (!plm_buffer_has(self->buffer, max_header_size)) { 2943 | return FALSE; 2944 | } 2945 | 2946 | self->width = plm_buffer_read(self->buffer, 12); 2947 | self->height = plm_buffer_read(self->buffer, 12); 2948 | 2949 | if (self->width <= 0 || self->height <= 0) { 2950 | return FALSE; 2951 | } 2952 | 2953 | // Get pixel aspect ratio 2954 | int pixel_aspect_ratio_code; 2955 | pixel_aspect_ratio_code = plm_buffer_read(self->buffer, 4); 2956 | pixel_aspect_ratio_code -= 1; 2957 | if (pixel_aspect_ratio_code < 0) { 2958 | pixel_aspect_ratio_code = 0; 2959 | } 2960 | int par_last = (sizeof(PLM_VIDEO_PIXEL_ASPECT_RATIO) / 2961 | sizeof(PLM_VIDEO_PIXEL_ASPECT_RATIO[0]) - 1); 2962 | if (pixel_aspect_ratio_code > par_last) { 2963 | pixel_aspect_ratio_code = par_last; 2964 | } 2965 | self->pixel_aspect_ratio = 2966 | PLM_VIDEO_PIXEL_ASPECT_RATIO[pixel_aspect_ratio_code]; 2967 | 2968 | // Get frame rate 2969 | self->framerate = PLM_VIDEO_PICTURE_RATE[plm_buffer_read(self->buffer, 4)]; 2970 | 2971 | // Skip bit_rate, marker, buffer_size and constrained bit 2972 | plm_buffer_skip(self->buffer, 18 + 1 + 10 + 1); 2973 | 2974 | // Load custom intra quant matrix? 2975 | if (plm_buffer_read(self->buffer, 1)) { 2976 | for (int i = 0; i < 64; i++) { 2977 | int idx = PLM_VIDEO_ZIG_ZAG[i]; 2978 | self->intra_quant_matrix[idx] = plm_buffer_read(self->buffer, 8); 2979 | } 2980 | } 2981 | else { 2982 | memcpy(self->intra_quant_matrix, PLM_VIDEO_INTRA_QUANT_MATRIX, 64); 2983 | } 2984 | 2985 | // Load custom non intra quant matrix? 2986 | if (plm_buffer_read(self->buffer, 1)) { 2987 | for (int i = 0; i < 64; i++) { 2988 | int idx = PLM_VIDEO_ZIG_ZAG[i]; 2989 | self->non_intra_quant_matrix[idx] = plm_buffer_read(self->buffer, 8); 2990 | } 2991 | } 2992 | else { 2993 | memcpy(self->non_intra_quant_matrix, PLM_VIDEO_NON_INTRA_QUANT_MATRIX, 64); 2994 | } 2995 | 2996 | self->mb_width = (self->width + 15) >> 4; 2997 | self->mb_height = (self->height + 15) >> 4; 2998 | self->mb_size = self->mb_width * self->mb_height; 2999 | 3000 | self->luma_width = self->mb_width << 4; 3001 | self->luma_height = self->mb_height << 4; 3002 | 3003 | self->chroma_width = self->mb_width << 3; 3004 | self->chroma_height = self->mb_height << 3; 3005 | 3006 | 3007 | // Allocate one big chunk of data for all 3 frames = 9 planes 3008 | size_t luma_plane_size = self->luma_width * self->luma_height; 3009 | size_t chroma_plane_size = self->chroma_width * self->chroma_height; 3010 | size_t frame_data_size = (luma_plane_size + 2 * chroma_plane_size); 3011 | 3012 | self->frames_data = (uint8_t*)PLM_MALLOC(frame_data_size * 3); 3013 | plm_video_init_frame(self, &self->frame_current, self->frames_data + frame_data_size * 0); 3014 | plm_video_init_frame(self, &self->frame_forward, self->frames_data + frame_data_size * 1); 3015 | plm_video_init_frame(self, &self->frame_backward, self->frames_data + frame_data_size * 2); 3016 | 3017 | self->has_sequence_header = TRUE; 3018 | return TRUE; 3019 | } 3020 | 3021 | void plm_video_init_frame(plm_video_t *self, plm_frame_t *frame, uint8_t *base) { 3022 | size_t luma_plane_size = self->luma_width * self->luma_height; 3023 | size_t chroma_plane_size = self->chroma_width * self->chroma_height; 3024 | 3025 | frame->width = self->width; 3026 | frame->height = self->height; 3027 | frame->y.width = self->luma_width; 3028 | frame->y.height = self->luma_height; 3029 | frame->y.data = base; 3030 | 3031 | frame->cr.width = self->chroma_width; 3032 | frame->cr.height = self->chroma_height; 3033 | frame->cr.data = base + luma_plane_size; 3034 | 3035 | frame->cb.width = self->chroma_width; 3036 | frame->cb.height = self->chroma_height; 3037 | frame->cb.data = base + luma_plane_size + chroma_plane_size; 3038 | } 3039 | 3040 | void plm_video_decode_picture(plm_video_t *self) { 3041 | plm_buffer_skip(self->buffer, 10); // skip temporalReference 3042 | self->picture_type = plm_buffer_read(self->buffer, 3); 3043 | plm_buffer_skip(self->buffer, 16); // skip vbv_delay 3044 | 3045 | // D frames or unknown coding type 3046 | if (self->picture_type <= 0 || self->picture_type > PLM_VIDEO_PICTURE_TYPE_B) { 3047 | return; 3048 | } 3049 | 3050 | // Forward full_px, f_code 3051 | if ( 3052 | self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE || 3053 | self->picture_type == PLM_VIDEO_PICTURE_TYPE_B 3054 | ) { 3055 | self->motion_forward.full_px = plm_buffer_read(self->buffer, 1); 3056 | int f_code = plm_buffer_read(self->buffer, 3); 3057 | if (f_code == 0) { 3058 | // Ignore picture with zero f_code 3059 | return; 3060 | } 3061 | self->motion_forward.r_size = f_code - 1; 3062 | } 3063 | 3064 | // Backward full_px, f_code 3065 | if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) { 3066 | self->motion_backward.full_px = plm_buffer_read(self->buffer, 1); 3067 | int f_code = plm_buffer_read(self->buffer, 3); 3068 | if (f_code == 0) { 3069 | // Ignore picture with zero f_code 3070 | return; 3071 | } 3072 | self->motion_backward.r_size = f_code - 1; 3073 | } 3074 | 3075 | plm_frame_t frame_temp = self->frame_forward; 3076 | if ( 3077 | self->picture_type == PLM_VIDEO_PICTURE_TYPE_INTRA || 3078 | self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE 3079 | ) { 3080 | self->frame_forward = self->frame_backward; 3081 | } 3082 | 3083 | 3084 | // Find first slice start code; skip extension and user data 3085 | do { 3086 | self->start_code = plm_buffer_next_start_code(self->buffer); 3087 | } while ( 3088 | self->start_code == PLM_START_EXTENSION || 3089 | self->start_code == PLM_START_USER_DATA 3090 | ); 3091 | 3092 | // Decode all slices 3093 | while (PLM_START_IS_SLICE(self->start_code)) { 3094 | plm_video_decode_slice(self, self->start_code & 0x000000FF); 3095 | if (self->macroblock_address >= self->mb_size - 2) { 3096 | break; 3097 | } 3098 | self->start_code = plm_buffer_next_start_code(self->buffer); 3099 | } 3100 | 3101 | // If this is a reference picture rotate the prediction pointers 3102 | if ( 3103 | self->picture_type == PLM_VIDEO_PICTURE_TYPE_INTRA || 3104 | self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE 3105 | ) { 3106 | self->frame_backward = self->frame_current; 3107 | self->frame_current = frame_temp; 3108 | } 3109 | } 3110 | 3111 | void plm_video_decode_slice(plm_video_t *self, int slice) { 3112 | self->slice_begin = TRUE; 3113 | self->macroblock_address = (slice - 1) * self->mb_width - 1; 3114 | 3115 | // Reset motion vectors and DC predictors 3116 | self->motion_backward.h = self->motion_forward.h = 0; 3117 | self->motion_backward.v = self->motion_forward.v = 0; 3118 | self->dc_predictor[0] = 128; 3119 | self->dc_predictor[1] = 128; 3120 | self->dc_predictor[2] = 128; 3121 | 3122 | self->quantizer_scale = plm_buffer_read(self->buffer, 5); 3123 | 3124 | // Skip extra 3125 | while (plm_buffer_read(self->buffer, 1)) { 3126 | plm_buffer_skip(self->buffer, 8); 3127 | } 3128 | 3129 | do { 3130 | plm_video_decode_macroblock(self); 3131 | } while ( 3132 | self->macroblock_address < self->mb_size - 1 && 3133 | plm_buffer_peek_non_zero(self->buffer, 23) 3134 | ); 3135 | } 3136 | 3137 | void plm_video_decode_macroblock(plm_video_t *self) { 3138 | // Decode increment 3139 | int increment = 0; 3140 | int t = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT); 3141 | 3142 | while (t == 34) { 3143 | // macroblock_stuffing 3144 | t = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT); 3145 | } 3146 | while (t == 35) { 3147 | // macroblock_escape 3148 | increment += 33; 3149 | t = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT); 3150 | } 3151 | increment += t; 3152 | 3153 | // Process any skipped macroblocks 3154 | if (self->slice_begin) { 3155 | // The first increment of each slice is relative to beginning of the 3156 | // previous row, not the previous macroblock 3157 | self->slice_begin = FALSE; 3158 | self->macroblock_address += increment; 3159 | } 3160 | else { 3161 | if (self->macroblock_address + increment >= self->mb_size) { 3162 | return; // invalid 3163 | } 3164 | if (increment > 1) { 3165 | // Skipped macroblocks reset DC predictors 3166 | self->dc_predictor[0] = 128; 3167 | self->dc_predictor[1] = 128; 3168 | self->dc_predictor[2] = 128; 3169 | 3170 | // Skipped macroblocks in P-pictures reset motion vectors 3171 | if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE) { 3172 | self->motion_forward.h = 0; 3173 | self->motion_forward.v = 0; 3174 | } 3175 | } 3176 | 3177 | // Predict skipped macroblocks 3178 | while (increment > 1) { 3179 | self->macroblock_address++; 3180 | self->mb_row = self->macroblock_address / self->mb_width; 3181 | self->mb_col = self->macroblock_address % self->mb_width; 3182 | 3183 | plm_video_predict_macroblock(self); 3184 | increment--; 3185 | } 3186 | self->macroblock_address++; 3187 | } 3188 | 3189 | self->mb_row = self->macroblock_address / self->mb_width; 3190 | self->mb_col = self->macroblock_address % self->mb_width; 3191 | 3192 | if (self->mb_col >= self->mb_width || self->mb_row >= self->mb_height) { 3193 | return; // corrupt stream; 3194 | } 3195 | 3196 | // Process the current macroblock 3197 | const plm_vlc_t *table = PLM_VIDEO_MACROBLOCK_TYPE[self->picture_type]; 3198 | self->macroblock_type = plm_buffer_read_vlc(self->buffer, table); 3199 | 3200 | self->macroblock_intra = (self->macroblock_type & 0x01); 3201 | self->motion_forward.is_set = (self->macroblock_type & 0x08); 3202 | self->motion_backward.is_set = (self->macroblock_type & 0x04); 3203 | 3204 | // Quantizer scale 3205 | if ((self->macroblock_type & 0x10) != 0) { 3206 | self->quantizer_scale = plm_buffer_read(self->buffer, 5); 3207 | } 3208 | 3209 | if (self->macroblock_intra) { 3210 | // Intra-coded macroblocks reset motion vectors 3211 | self->motion_backward.h = self->motion_forward.h = 0; 3212 | self->motion_backward.v = self->motion_forward.v = 0; 3213 | } 3214 | else { 3215 | // Non-intra macroblocks reset DC predictors 3216 | self->dc_predictor[0] = 128; 3217 | self->dc_predictor[1] = 128; 3218 | self->dc_predictor[2] = 128; 3219 | 3220 | plm_video_decode_motion_vectors(self); 3221 | plm_video_predict_macroblock(self); 3222 | } 3223 | 3224 | // Decode blocks 3225 | int cbp = ((self->macroblock_type & 0x02) != 0) 3226 | ? plm_buffer_read_vlc(self->buffer, PLM_VIDEO_CODE_BLOCK_PATTERN) 3227 | : (self->macroblock_intra ? 0x3f : 0); 3228 | 3229 | for (int block = 0, mask = 0x20; block < 6; block++) { 3230 | if ((cbp & mask) != 0) { 3231 | plm_video_decode_block(self, block); 3232 | } 3233 | mask >>= 1; 3234 | } 3235 | } 3236 | 3237 | void plm_video_decode_motion_vectors(plm_video_t *self) { 3238 | 3239 | // Forward 3240 | if (self->motion_forward.is_set) { 3241 | int r_size = self->motion_forward.r_size; 3242 | self->motion_forward.h = plm_video_decode_motion_vector(self, r_size, self->motion_forward.h); 3243 | self->motion_forward.v = plm_video_decode_motion_vector(self, r_size, self->motion_forward.v); 3244 | } 3245 | else if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE) { 3246 | // No motion information in P-picture, reset vectors 3247 | self->motion_forward.h = 0; 3248 | self->motion_forward.v = 0; 3249 | } 3250 | 3251 | if (self->motion_backward.is_set) { 3252 | int r_size = self->motion_backward.r_size; 3253 | self->motion_backward.h = plm_video_decode_motion_vector(self, r_size, self->motion_backward.h); 3254 | self->motion_backward.v = plm_video_decode_motion_vector(self, r_size, self->motion_backward.v); 3255 | } 3256 | } 3257 | 3258 | int plm_video_decode_motion_vector(plm_video_t *self, int r_size, int motion) { 3259 | int fscale = 1 << r_size; 3260 | int m_code = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MOTION); 3261 | int r = 0; 3262 | int d; 3263 | 3264 | if ((m_code != 0) && (fscale != 1)) { 3265 | r = plm_buffer_read(self->buffer, r_size); 3266 | d = ((abs(m_code) - 1) << r_size) + r + 1; 3267 | if (m_code < 0) { 3268 | d = -d; 3269 | } 3270 | } 3271 | else { 3272 | d = m_code; 3273 | } 3274 | 3275 | motion += d; 3276 | if (motion > (fscale << 4) - 1) { 3277 | motion -= fscale << 5; 3278 | } 3279 | else if (motion < (int)((unsigned)(-fscale) << 4)) { 3280 | motion += fscale << 5; 3281 | } 3282 | 3283 | return motion; 3284 | } 3285 | 3286 | void plm_video_predict_macroblock(plm_video_t *self) { 3287 | int fw_h = self->motion_forward.h; 3288 | int fw_v = self->motion_forward.v; 3289 | 3290 | if (self->motion_forward.full_px) { 3291 | fw_h <<= 1; 3292 | fw_v <<= 1; 3293 | } 3294 | 3295 | if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) { 3296 | int bw_h = self->motion_backward.h; 3297 | int bw_v = self->motion_backward.v; 3298 | 3299 | if (self->motion_backward.full_px) { 3300 | bw_h <<= 1; 3301 | bw_v <<= 1; 3302 | } 3303 | 3304 | if (self->motion_forward.is_set) { 3305 | plm_video_copy_macroblock(self, &self->frame_forward, fw_h, fw_v); 3306 | if (self->motion_backward.is_set) { 3307 | plm_video_interpolate_macroblock(self, &self->frame_backward, bw_h, bw_v); 3308 | } 3309 | } 3310 | else { 3311 | plm_video_copy_macroblock(self, &self->frame_backward, bw_h, bw_v); 3312 | } 3313 | } 3314 | else { 3315 | plm_video_copy_macroblock(self, &self->frame_forward, fw_h, fw_v); 3316 | } 3317 | } 3318 | 3319 | void plm_video_copy_macroblock(plm_video_t *self, plm_frame_t *s, int motion_h, int motion_v) { 3320 | plm_frame_t *d = &self->frame_current; 3321 | plm_video_process_macroblock(self, s->y.data, d->y.data, motion_h, motion_v, 16, FALSE); 3322 | plm_video_process_macroblock(self, s->cr.data, d->cr.data, motion_h / 2, motion_v / 2, 8, FALSE); 3323 | plm_video_process_macroblock(self, s->cb.data, d->cb.data, motion_h / 2, motion_v / 2, 8, FALSE); 3324 | } 3325 | 3326 | void plm_video_interpolate_macroblock(plm_video_t *self, plm_frame_t *s, int motion_h, int motion_v) { 3327 | plm_frame_t *d = &self->frame_current; 3328 | plm_video_process_macroblock(self, s->y.data, d->y.data, motion_h, motion_v, 16, TRUE); 3329 | plm_video_process_macroblock(self, s->cr.data, d->cr.data, motion_h / 2, motion_v / 2, 8, TRUE); 3330 | plm_video_process_macroblock(self, s->cb.data, d->cb.data, motion_h / 2, motion_v / 2, 8, TRUE); 3331 | } 3332 | 3333 | #define PLM_BLOCK_SET(DEST, DEST_INDEX, DEST_WIDTH, SOURCE_INDEX, SOURCE_WIDTH, BLOCK_SIZE, OP) do { \ 3334 | int dest_scan = DEST_WIDTH - BLOCK_SIZE; \ 3335 | int source_scan = SOURCE_WIDTH - BLOCK_SIZE; \ 3336 | for (int y = 0; y < BLOCK_SIZE; y++) { \ 3337 | for (int x = 0; x < BLOCK_SIZE; x++) { \ 3338 | DEST[DEST_INDEX] = OP; \ 3339 | SOURCE_INDEX++; DEST_INDEX++; \ 3340 | } \ 3341 | SOURCE_INDEX += source_scan; \ 3342 | DEST_INDEX += dest_scan; \ 3343 | }} while(FALSE) 3344 | 3345 | void plm_video_process_macroblock( 3346 | plm_video_t *self, uint8_t *s, uint8_t *d, 3347 | int motion_h, int motion_v, int block_size, int interpolate 3348 | ) { 3349 | int dw = self->mb_width * block_size; 3350 | 3351 | int hp = motion_h >> 1; 3352 | int vp = motion_v >> 1; 3353 | int odd_h = (motion_h & 1) == 1; 3354 | int odd_v = (motion_v & 1) == 1; 3355 | 3356 | unsigned int si = ((self->mb_row * block_size) + vp) * dw + (self->mb_col * block_size) + hp; 3357 | unsigned int di = (self->mb_row * dw + self->mb_col) * block_size; 3358 | 3359 | unsigned int max_address = (dw * (self->mb_height * block_size - block_size + 1) - block_size); 3360 | if (si > max_address || di > max_address) { 3361 | return; // corrupt video 3362 | } 3363 | 3364 | #define PLM_MB_CASE(INTERPOLATE, ODD_H, ODD_V, OP) \ 3365 | case ((INTERPOLATE << 2) | (ODD_H << 1) | (ODD_V)): \ 3366 | PLM_BLOCK_SET(d, di, dw, si, dw, block_size, OP); \ 3367 | break 3368 | 3369 | switch ((interpolate << 2) | (odd_h << 1) | (odd_v)) { 3370 | PLM_MB_CASE(0, 0, 0, (s[si])); 3371 | PLM_MB_CASE(0, 0, 1, (s[si] + s[si + dw] + 1) >> 1); 3372 | PLM_MB_CASE(0, 1, 0, (s[si] + s[si + 1] + 1) >> 1); 3373 | PLM_MB_CASE(0, 1, 1, (s[si] + s[si + 1] + s[si + dw] + s[si + dw + 1] + 2) >> 2); 3374 | 3375 | PLM_MB_CASE(1, 0, 0, (d[di] + (s[si]) + 1) >> 1); 3376 | PLM_MB_CASE(1, 0, 1, (d[di] + ((s[si] + s[si + dw] + 1) >> 1) + 1) >> 1); 3377 | PLM_MB_CASE(1, 1, 0, (d[di] + ((s[si] + s[si + 1] + 1) >> 1) + 1) >> 1); 3378 | PLM_MB_CASE(1, 1, 1, (d[di] + ((s[si] + s[si + 1] + s[si + dw] + s[si + dw + 1] + 2) >> 2) + 1) >> 1); 3379 | } 3380 | 3381 | #undef PLM_MB_CASE 3382 | } 3383 | 3384 | void plm_video_decode_block(plm_video_t *self, int block) { 3385 | 3386 | int n = 0; 3387 | uint8_t *quant_matrix; 3388 | 3389 | // Decode DC coefficient of intra-coded blocks 3390 | if (self->macroblock_intra) { 3391 | int predictor; 3392 | int dct_size; 3393 | 3394 | // DC prediction 3395 | int plane_index = block > 3 ? block - 3 : 0; 3396 | predictor = self->dc_predictor[plane_index]; 3397 | dct_size = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_DCT_SIZE[plane_index]); 3398 | 3399 | // Read DC coeff 3400 | if (dct_size > 0) { 3401 | int differential = plm_buffer_read(self->buffer, dct_size); 3402 | if ((differential & (1 << (dct_size - 1))) != 0) { 3403 | self->block_data[0] = predictor + differential; 3404 | } 3405 | else { 3406 | self->block_data[0] = predictor + (-(1 << dct_size) | (differential + 1)); 3407 | } 3408 | } 3409 | else { 3410 | self->block_data[0] = predictor; 3411 | } 3412 | 3413 | // Save predictor value 3414 | self->dc_predictor[plane_index] = self->block_data[0]; 3415 | 3416 | // Dequantize + premultiply 3417 | self->block_data[0] <<= (3 + 5); 3418 | 3419 | quant_matrix = self->intra_quant_matrix; 3420 | n = 1; 3421 | } 3422 | else { 3423 | quant_matrix = self->non_intra_quant_matrix; 3424 | } 3425 | 3426 | // Decode AC coefficients (+DC for non-intra) 3427 | int level = 0; 3428 | while (TRUE) { 3429 | int run = 0; 3430 | uint16_t coeff = plm_buffer_read_vlc_uint(self->buffer, PLM_VIDEO_DCT_COEFF); 3431 | 3432 | if ((coeff == 0x0001) && (n > 0) && (plm_buffer_read(self->buffer, 1) == 0)) { 3433 | // end_of_block 3434 | break; 3435 | } 3436 | if (coeff == 0xffff) { 3437 | // escape 3438 | run = plm_buffer_read(self->buffer, 6); 3439 | level = plm_buffer_read(self->buffer, 8); 3440 | if (level == 0) { 3441 | level = plm_buffer_read(self->buffer, 8); 3442 | } 3443 | else if (level == 128) { 3444 | level = plm_buffer_read(self->buffer, 8) - 256; 3445 | } 3446 | else if (level > 128) { 3447 | level = level - 256; 3448 | } 3449 | } 3450 | else { 3451 | run = coeff >> 8; 3452 | level = coeff & 0xff; 3453 | if (plm_buffer_read(self->buffer, 1)) { 3454 | level = -level; 3455 | } 3456 | } 3457 | 3458 | n += run; 3459 | if (n < 0 || n >= 64) { 3460 | return; // invalid 3461 | } 3462 | 3463 | int de_zig_zagged = PLM_VIDEO_ZIG_ZAG[n]; 3464 | n++; 3465 | 3466 | // Dequantize, oddify, clip 3467 | level = (unsigned)level << 1; 3468 | if (!self->macroblock_intra) { 3469 | level += (level < 0 ? -1 : 1); 3470 | } 3471 | level = (level * self->quantizer_scale * quant_matrix[de_zig_zagged]) >> 4; 3472 | if ((level & 1) == 0) { 3473 | level -= level > 0 ? 1 : -1; 3474 | } 3475 | if (level > 2047) { 3476 | level = 2047; 3477 | } 3478 | else if (level < -2048) { 3479 | level = -2048; 3480 | } 3481 | 3482 | // Save premultiplied coefficient 3483 | self->block_data[de_zig_zagged] = level * PLM_VIDEO_PREMULTIPLIER_MATRIX[de_zig_zagged]; 3484 | } 3485 | 3486 | // Move block to its place 3487 | uint8_t *d; 3488 | int dw; 3489 | int di; 3490 | 3491 | if (block < 4) { 3492 | d = self->frame_current.y.data; 3493 | dw = self->luma_width; 3494 | di = (self->mb_row * self->luma_width + self->mb_col) << 4; 3495 | if ((block & 1) != 0) { 3496 | di += 8; 3497 | } 3498 | if ((block & 2) != 0) { 3499 | di += self->luma_width << 3; 3500 | } 3501 | } 3502 | else { 3503 | d = (block == 4) ? self->frame_current.cb.data : self->frame_current.cr.data; 3504 | dw = self->chroma_width; 3505 | di = ((self->mb_row * self->luma_width) << 2) + (self->mb_col << 3); 3506 | } 3507 | 3508 | int *s = self->block_data; 3509 | int si = 0; 3510 | if (self->macroblock_intra) { 3511 | // Overwrite (no prediction) 3512 | if (n == 1) { 3513 | int clamped = plm_clamp((s[0] + 128) >> 8); 3514 | PLM_BLOCK_SET(d, di, dw, si, 8, 8, clamped); 3515 | s[0] = 0; 3516 | } 3517 | else { 3518 | plm_video_idct(s); 3519 | PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(s[si])); 3520 | memset(self->block_data, 0, sizeof(self->block_data)); 3521 | } 3522 | } 3523 | else { 3524 | // Add data to the predicted macroblock 3525 | if (n == 1) { 3526 | int value = (s[0] + 128) >> 8; 3527 | PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(d[di] + value)); 3528 | s[0] = 0; 3529 | } 3530 | else { 3531 | plm_video_idct(s); 3532 | PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(d[di] + s[si])); 3533 | memset(self->block_data, 0, sizeof(self->block_data)); 3534 | } 3535 | } 3536 | } 3537 | 3538 | void plm_video_idct(int *block) { 3539 | int 3540 | b1, b3, b4, b6, b7, tmp1, tmp2, m0, 3541 | x0, x1, x2, x3, x4, y3, y4, y5, y6, y7; 3542 | 3543 | // Transform columns 3544 | for (int i = 0; i < 8; ++i) { 3545 | b1 = block[4 * 8 + i]; 3546 | b3 = block[2 * 8 + i] + block[6 * 8 + i]; 3547 | b4 = block[5 * 8 + i] - block[3 * 8 + i]; 3548 | tmp1 = block[1 * 8 + i] + block[7 * 8 + i]; 3549 | tmp2 = block[3 * 8 + i] + block[5 * 8 + i]; 3550 | b6 = block[1 * 8 + i] - block[7 * 8 + i]; 3551 | b7 = tmp1 + tmp2; 3552 | m0 = block[0 * 8 + i]; 3553 | x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7; 3554 | x0 = x4 - (((tmp1 - tmp2) * 362 + 128) >> 8); 3555 | x1 = m0 - b1; 3556 | x2 = (((block[2 * 8 + i] - block[6 * 8 + i]) * 362 + 128) >> 8) - b3; 3557 | x3 = m0 + b1; 3558 | y3 = x1 + x2; 3559 | y4 = x3 + b3; 3560 | y5 = x1 - x2; 3561 | y6 = x3 - b3; 3562 | y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8); 3563 | block[0 * 8 + i] = b7 + y4; 3564 | block[1 * 8 + i] = x4 + y3; 3565 | block[2 * 8 + i] = y5 - x0; 3566 | block[3 * 8 + i] = y6 - y7; 3567 | block[4 * 8 + i] = y6 + y7; 3568 | block[5 * 8 + i] = x0 + y5; 3569 | block[6 * 8 + i] = y3 - x4; 3570 | block[7 * 8 + i] = y4 - b7; 3571 | } 3572 | 3573 | // Transform rows 3574 | for (int i = 0; i < 64; i += 8) { 3575 | b1 = block[4 + i]; 3576 | b3 = block[2 + i] + block[6 + i]; 3577 | b4 = block[5 + i] - block[3 + i]; 3578 | tmp1 = block[1 + i] + block[7 + i]; 3579 | tmp2 = block[3 + i] + block[5 + i]; 3580 | b6 = block[1 + i] - block[7 + i]; 3581 | b7 = tmp1 + tmp2; 3582 | m0 = block[0 + i]; 3583 | x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7; 3584 | x0 = x4 - (((tmp1 - tmp2) * 362 + 128) >> 8); 3585 | x1 = m0 - b1; 3586 | x2 = (((block[2 + i] - block[6 + i]) * 362 + 128) >> 8) - b3; 3587 | x3 = m0 + b1; 3588 | y3 = x1 + x2; 3589 | y4 = x3 + b3; 3590 | y5 = x1 - x2; 3591 | y6 = x3 - b3; 3592 | y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8); 3593 | block[0 + i] = (b7 + y4 + 128) >> 8; 3594 | block[1 + i] = (x4 + y3 + 128) >> 8; 3595 | block[2 + i] = (y5 - x0 + 128) >> 8; 3596 | block[3 + i] = (y6 - y7 + 128) >> 8; 3597 | block[4 + i] = (y6 + y7 + 128) >> 8; 3598 | block[5 + i] = (x0 + y5 + 128) >> 8; 3599 | block[6 + i] = (y3 - x4 + 128) >> 8; 3600 | block[7 + i] = (y4 - b7 + 128) >> 8; 3601 | } 3602 | } 3603 | 3604 | // YCbCr conversion following the BT.601 standard: 3605 | // https://infogalactic.com/info/YCbCr#ITU-R_BT.601_conversion 3606 | 3607 | #define PLM_PUT_PIXEL(RI, GI, BI, Y_OFFSET, DEST_OFFSET) \ 3608 | y = ((frame->y.data[y_index + Y_OFFSET]-16) * 76309) >> 16; \ 3609 | dest[d_index + DEST_OFFSET + RI] = plm_clamp(y + r); \ 3610 | dest[d_index + DEST_OFFSET + GI] = plm_clamp(y - g); \ 3611 | dest[d_index + DEST_OFFSET + BI] = plm_clamp(y + b); 3612 | 3613 | #define PLM_DEFINE_FRAME_CONVERT_FUNCTION(NAME, BYTES_PER_PIXEL, RI, GI, BI) \ 3614 | void NAME(plm_frame_t *frame, uint8_t *dest, int stride) { \ 3615 | int cols = frame->width >> 1; \ 3616 | int rows = frame->height >> 1; \ 3617 | int yw = frame->y.width; \ 3618 | int cw = frame->cb.width; \ 3619 | for (int row = 0; row < rows; row++) { \ 3620 | int c_index = row * cw; \ 3621 | int y_index = row * 2 * yw; \ 3622 | int d_index = row * 2 * stride; \ 3623 | for (int col = 0; col < cols; col++) { \ 3624 | int y; \ 3625 | int cr = frame->cr.data[c_index] - 128; \ 3626 | int cb = frame->cb.data[c_index] - 128; \ 3627 | int r = (cr * 104597) >> 16; \ 3628 | int g = (cb * 25674 + cr * 53278) >> 16; \ 3629 | int b = (cb * 132201) >> 16; \ 3630 | PLM_PUT_PIXEL(RI, GI, BI, 0, 0); \ 3631 | PLM_PUT_PIXEL(RI, GI, BI, 1, BYTES_PER_PIXEL); \ 3632 | PLM_PUT_PIXEL(RI, GI, BI, yw, stride); \ 3633 | PLM_PUT_PIXEL(RI, GI, BI, yw + 1, stride + BYTES_PER_PIXEL); \ 3634 | c_index += 1; \ 3635 | y_index += 2; \ 3636 | d_index += 2 * BYTES_PER_PIXEL; \ 3637 | } \ 3638 | } \ 3639 | } 3640 | 3641 | PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_rgb, 3, 0, 1, 2) 3642 | PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_bgr, 3, 2, 1, 0) 3643 | PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_rgba, 4, 0, 1, 2) 3644 | PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_bgra, 4, 2, 1, 0) 3645 | PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_argb, 4, 1, 2, 3) 3646 | PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_abgr, 4, 3, 2, 1) 3647 | 3648 | 3649 | #undef PLM_PUT_PIXEL 3650 | #undef PLM_DEFINE_FRAME_CONVERT_FUNCTION 3651 | 3652 | 3653 | 3654 | // ----------------------------------------------------------------------------- 3655 | // plm_audio implementation 3656 | 3657 | // Based on kjmp2 by Martin J. Fiedler 3658 | // http://keyj.emphy.de/kjmp2/ 3659 | 3660 | static const int PLM_AUDIO_FRAME_SYNC = 0x7ff; 3661 | 3662 | static const int PLM_AUDIO_MPEG_2_5 = 0x0; 3663 | static const int PLM_AUDIO_MPEG_2 = 0x2; 3664 | static const int PLM_AUDIO_MPEG_1 = 0x3; 3665 | 3666 | static const int PLM_AUDIO_LAYER_III = 0x1; 3667 | static const int PLM_AUDIO_LAYER_II = 0x2; 3668 | static const int PLM_AUDIO_LAYER_I = 0x3; 3669 | 3670 | static const int PLM_AUDIO_MODE_STEREO = 0x0; 3671 | static const int PLM_AUDIO_MODE_JOINT_STEREO = 0x1; 3672 | static const int PLM_AUDIO_MODE_DUAL_CHANNEL = 0x2; 3673 | static const int PLM_AUDIO_MODE_MONO = 0x3; 3674 | 3675 | static const unsigned short PLM_AUDIO_SAMPLE_RATE[] = { 3676 | 44100, 48000, 32000, 0, // MPEG-1 3677 | 22050, 24000, 16000, 0 // MPEG-2 3678 | }; 3679 | 3680 | static const short PLM_AUDIO_BIT_RATE[] = { 3681 | 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, // MPEG-1 3682 | 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 // MPEG-2 3683 | }; 3684 | 3685 | static const int PLM_AUDIO_SCALEFACTOR_BASE[] = { 3686 | 0x02000000, 0x01965FEA, 0x01428A30 3687 | }; 3688 | 3689 | static const float PLM_AUDIO_SYNTHESIS_WINDOW[] = { 3690 | 0.0, -0.5, -0.5, -0.5, -0.5, -0.5, 3691 | -0.5, -1.0, -1.0, -1.0, -1.0, -1.5, 3692 | -1.5, -2.0, -2.0, -2.5, -2.5, -3.0, 3693 | -3.5, -3.5, -4.0, -4.5, -5.0, -5.5, 3694 | -6.5, -7.0, -8.0, -8.5, -9.5, -10.5, 3695 | -12.0, -13.0, -14.5, -15.5, -17.5, -19.0, 3696 | -20.5, -22.5, -24.5, -26.5, -29.0, -31.5, 3697 | -34.0, -36.5, -39.5, -42.5, -45.5, -48.5, 3698 | -52.0, -55.5, -58.5, -62.5, -66.0, -69.5, 3699 | -73.5, -77.0, -80.5, -84.5, -88.0, -91.5, 3700 | -95.0, -98.0, -101.0, -104.0, 106.5, 109.0, 3701 | 111.0, 112.5, 113.5, 114.0, 114.0, 113.5, 3702 | 112.0, 110.5, 107.5, 104.0, 100.0, 94.5, 3703 | 88.5, 81.5, 73.0, 63.5, 53.0, 41.5, 3704 | 28.5, 14.5, -1.0, -18.0, -36.0, -55.5, 3705 | -76.5, -98.5, -122.0, -147.0, -173.5, -200.5, 3706 | -229.5, -259.5, -290.5, -322.5, -355.5, -389.5, 3707 | -424.0, -459.5, -495.5, -532.0, -568.5, -605.0, 3708 | -641.5, -678.0, -714.0, -749.0, -783.5, -817.0, 3709 | -849.0, -879.5, -908.5, -935.0, -959.5, -981.0, 3710 | -1000.5, -1016.0, -1028.5, -1037.5, -1042.5, -1043.5, 3711 | -1040.0, -1031.5, 1018.5, 1000.0, 976.0, 946.5, 3712 | 911.0, 869.5, 822.0, 767.5, 707.0, 640.0, 3713 | 565.5, 485.0, 397.0, 302.5, 201.0, 92.5, 3714 | -22.5, -144.0, -272.5, -407.0, -547.5, -694.0, 3715 | -846.0, -1003.0, -1165.0, -1331.5, -1502.0, -1675.5, 3716 | -1852.5, -2031.5, -2212.5, -2394.0, -2576.5, -2758.5, 3717 | -2939.5, -3118.5, -3294.5, -3467.5, -3635.5, -3798.5, 3718 | -3955.0, -4104.5, -4245.5, -4377.5, -4499.0, -4609.5, 3719 | -4708.0, -4792.5, -4863.5, -4919.0, -4958.0, -4979.5, 3720 | -4983.0, -4967.5, -4931.5, -4875.0, -4796.0, -4694.5, 3721 | -4569.5, -4420.0, -4246.0, -4046.0, -3820.0, -3567.0, 3722 | 3287.0, 2979.5, 2644.0, 2280.5, 1888.0, 1467.5, 3723 | 1018.5, 541.0, 35.0, -499.0, -1061.0, -1650.0, 3724 | -2266.5, -2909.0, -3577.0, -4270.0, -4987.5, -5727.5, 3725 | -6490.0, -7274.0, -8077.5, -8899.5, -9739.0, -10594.5, 3726 | -11464.5, -12347.0, -13241.0, -14144.5, -15056.0, -15973.5, 3727 | -16895.5, -17820.0, -18744.5, -19668.0, -20588.0, -21503.0, 3728 | -22410.5, -23308.5, -24195.0, -25068.5, -25926.5, -26767.0, 3729 | -27589.0, -28389.0, -29166.5, -29919.0, -30644.5, -31342.0, 3730 | -32009.5, -32645.0, -33247.0, -33814.5, -34346.0, -34839.5, 3731 | -35295.0, -35710.0, -36084.5, -36417.5, -36707.5, -36954.0, 3732 | -37156.5, -37315.0, -37428.0, -37496.0, 37519.0, 37496.0, 3733 | 37428.0, 37315.0, 37156.5, 36954.0, 36707.5, 36417.5, 3734 | 36084.5, 35710.0, 35295.0, 34839.5, 34346.0, 33814.5, 3735 | 33247.0, 32645.0, 32009.5, 31342.0, 30644.5, 29919.0, 3736 | 29166.5, 28389.0, 27589.0, 26767.0, 25926.5, 25068.5, 3737 | 24195.0, 23308.5, 22410.5, 21503.0, 20588.0, 19668.0, 3738 | 18744.5, 17820.0, 16895.5, 15973.5, 15056.0, 14144.5, 3739 | 13241.0, 12347.0, 11464.5, 10594.5, 9739.0, 8899.5, 3740 | 8077.5, 7274.0, 6490.0, 5727.5, 4987.5, 4270.0, 3741 | 3577.0, 2909.0, 2266.5, 1650.0, 1061.0, 499.0, 3742 | -35.0, -541.0, -1018.5, -1467.5, -1888.0, -2280.5, 3743 | -2644.0, -2979.5, 3287.0, 3567.0, 3820.0, 4046.0, 3744 | 4246.0, 4420.0, 4569.5, 4694.5, 4796.0, 4875.0, 3745 | 4931.5, 4967.5, 4983.0, 4979.5, 4958.0, 4919.0, 3746 | 4863.5, 4792.5, 4708.0, 4609.5, 4499.0, 4377.5, 3747 | 4245.5, 4104.5, 3955.0, 3798.5, 3635.5, 3467.5, 3748 | 3294.5, 3118.5, 2939.5, 2758.5, 2576.5, 2394.0, 3749 | 2212.5, 2031.5, 1852.5, 1675.5, 1502.0, 1331.5, 3750 | 1165.0, 1003.0, 846.0, 694.0, 547.5, 407.0, 3751 | 272.5, 144.0, 22.5, -92.5, -201.0, -302.5, 3752 | -397.0, -485.0, -565.5, -640.0, -707.0, -767.5, 3753 | -822.0, -869.5, -911.0, -946.5, -976.0, -1000.0, 3754 | 1018.5, 1031.5, 1040.0, 1043.5, 1042.5, 1037.5, 3755 | 1028.5, 1016.0, 1000.5, 981.0, 959.5, 935.0, 3756 | 908.5, 879.5, 849.0, 817.0, 783.5, 749.0, 3757 | 714.0, 678.0, 641.5, 605.0, 568.5, 532.0, 3758 | 495.5, 459.5, 424.0, 389.5, 355.5, 322.5, 3759 | 290.5, 259.5, 229.5, 200.5, 173.5, 147.0, 3760 | 122.0, 98.5, 76.5, 55.5, 36.0, 18.0, 3761 | 1.0, -14.5, -28.5, -41.5, -53.0, -63.5, 3762 | -73.0, -81.5, -88.5, -94.5, -100.0, -104.0, 3763 | -107.5, -110.5, -112.0, -113.5, -114.0, -114.0, 3764 | -113.5, -112.5, -111.0, -109.0, 106.5, 104.0, 3765 | 101.0, 98.0, 95.0, 91.5, 88.0, 84.5, 3766 | 80.5, 77.0, 73.5, 69.5, 66.0, 62.5, 3767 | 58.5, 55.5, 52.0, 48.5, 45.5, 42.5, 3768 | 39.5, 36.5, 34.0, 31.5, 29.0, 26.5, 3769 | 24.5, 22.5, 20.5, 19.0, 17.5, 15.5, 3770 | 14.5, 13.0, 12.0, 10.5, 9.5, 8.5, 3771 | 8.0, 7.0, 6.5, 5.5, 5.0, 4.5, 3772 | 4.0, 3.5, 3.5, 3.0, 2.5, 2.5, 3773 | 2.0, 2.0, 1.5, 1.5, 1.0, 1.0, 3774 | 1.0, 1.0, 0.5, 0.5, 0.5, 0.5, 3775 | 0.5, 0.5 3776 | }; 3777 | 3778 | // Quantizer lookup, step 1: bitrate classes 3779 | static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_1[2][16] = { 3780 | // 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384 <- bitrate 3781 | { 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // mono 3782 | // 16, 24, 28, 32, 40, 48, 56, 64, 80, 96,112,128,160,192 <- bitrate / chan 3783 | { 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 } // stereo 3784 | }; 3785 | 3786 | // Quantizer lookup, step 2: bitrate class, sample rate -> B2 table idx, sblimit 3787 | #define PLM_AUDIO_QUANT_TAB_A (27 | 64) // Table 3-B.2a: high-rate, sblimit = 27 3788 | #define PLM_AUDIO_QUANT_TAB_B (30 | 64) // Table 3-B.2b: high-rate, sblimit = 30 3789 | #define PLM_AUDIO_QUANT_TAB_C 8 // Table 3-B.2c: low-rate, sblimit = 8 3790 | #define PLM_AUDIO_QUANT_TAB_D 12 // Table 3-B.2d: low-rate, sblimit = 12 3791 | 3792 | static const uint8_t QUANT_LUT_STEP_2[3][3] = { 3793 | //44.1 kHz, 48 kHz, 32 kHz 3794 | { PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_D }, // 32 - 48 kbit/sec/ch 3795 | { PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A }, // 56 - 80 kbit/sec/ch 3796 | { PLM_AUDIO_QUANT_TAB_B, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_B } // 96+ kbit/sec/ch 3797 | }; 3798 | 3799 | // Quantizer lookup, step 3: B2 table, subband -> nbal, row index 3800 | // (upper 4 bits: nbal, lower 4 bits: row index) 3801 | static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_3[3][32] = { 3802 | // Low-rate table (3-B.2c and 3-B.2d) 3803 | { 3804 | 0x44,0x44, 3805 | 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34 3806 | }, 3807 | // High-rate table (3-B.2a and 3-B.2b) 3808 | { 3809 | 0x43,0x43,0x43, 3810 | 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, 3811 | 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 3812 | 0x20,0x20,0x20,0x20,0x20,0x20,0x20 3813 | }, 3814 | // MPEG-2 LSR table (B.2 in ISO 13818-3) 3815 | { 3816 | 0x45,0x45,0x45,0x45, 3817 | 0x34,0x34,0x34,0x34,0x34,0x34,0x34, 3818 | 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 3819 | 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24 3820 | } 3821 | }; 3822 | 3823 | // Quantizer lookup, step 4: table row, allocation[] value -> quant table index 3824 | static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_4[6][16] = { 3825 | { 0, 1, 2, 17 }, 3826 | { 0, 1, 2, 3, 4, 5, 6, 17 }, 3827 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17 }, 3828 | { 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }, 3829 | { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17 }, 3830 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } 3831 | }; 3832 | 3833 | typedef struct plm_quantizer_spec_t { 3834 | unsigned short levels; 3835 | unsigned char group; 3836 | unsigned char bits; 3837 | } plm_quantizer_spec_t; 3838 | 3839 | static const plm_quantizer_spec_t PLM_AUDIO_QUANT_TAB[] = { 3840 | { 3, 1, 5 }, // 1 3841 | { 5, 1, 7 }, // 2 3842 | { 7, 0, 3 }, // 3 3843 | { 9, 1, 10 }, // 4 3844 | { 15, 0, 4 }, // 5 3845 | { 31, 0, 5 }, // 6 3846 | { 63, 0, 6 }, // 7 3847 | { 127, 0, 7 }, // 8 3848 | { 255, 0, 8 }, // 9 3849 | { 511, 0, 9 }, // 10 3850 | { 1023, 0, 10 }, // 11 3851 | { 2047, 0, 11 }, // 12 3852 | { 4095, 0, 12 }, // 13 3853 | { 8191, 0, 13 }, // 14 3854 | { 16383, 0, 14 }, // 15 3855 | { 32767, 0, 15 }, // 16 3856 | { 65535, 0, 16 } // 17 3857 | }; 3858 | 3859 | struct plm_audio_t { 3860 | double time; 3861 | int samples_decoded; 3862 | int samplerate_index; 3863 | int bitrate_index; 3864 | int version; 3865 | int layer; 3866 | int mode; 3867 | int bound; 3868 | int v_pos; 3869 | int next_frame_data_size; 3870 | int has_header; 3871 | 3872 | plm_buffer_t *buffer; 3873 | int destroy_buffer_when_done; 3874 | 3875 | const plm_quantizer_spec_t *allocation[2][32]; 3876 | uint8_t scale_factor_info[2][32]; 3877 | int scale_factor[2][32][3]; 3878 | int sample[2][32][3]; 3879 | 3880 | plm_samples_t samples; 3881 | float D[1024]; 3882 | float V[2][1024]; 3883 | float U[32]; 3884 | }; 3885 | 3886 | int plm_audio_find_frame_sync(plm_audio_t *self); 3887 | int plm_audio_decode_header(plm_audio_t *self); 3888 | void plm_audio_decode_frame(plm_audio_t *self); 3889 | const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3); 3890 | void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part); 3891 | void plm_audio_idct36(int s[32][3], int ss, float *d, int dp); 3892 | 3893 | plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) { 3894 | plm_audio_t *self = (plm_audio_t *)PLM_MALLOC(sizeof(plm_audio_t)); 3895 | memset(self, 0, sizeof(plm_audio_t)); 3896 | 3897 | self->samples.count = PLM_AUDIO_SAMPLES_PER_FRAME; 3898 | self->buffer = buffer; 3899 | self->destroy_buffer_when_done = destroy_when_done; 3900 | self->samplerate_index = 3; // Indicates 0 3901 | 3902 | memcpy(self->D, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float)); 3903 | memcpy(self->D + 512, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float)); 3904 | 3905 | // Attempt to decode first header 3906 | self->next_frame_data_size = plm_audio_decode_header(self); 3907 | 3908 | return self; 3909 | } 3910 | 3911 | void plm_audio_destroy(plm_audio_t *self) { 3912 | if (self->destroy_buffer_when_done) { 3913 | plm_buffer_destroy(self->buffer); 3914 | } 3915 | PLM_FREE(self); 3916 | } 3917 | 3918 | int plm_audio_has_header(plm_audio_t *self) { 3919 | if (self->has_header) { 3920 | return TRUE; 3921 | } 3922 | 3923 | self->next_frame_data_size = plm_audio_decode_header(self); 3924 | return self->has_header; 3925 | } 3926 | 3927 | int plm_audio_get_samplerate(plm_audio_t *self) { 3928 | return plm_audio_has_header(self) 3929 | ? PLM_AUDIO_SAMPLE_RATE[self->samplerate_index] 3930 | : 0; 3931 | } 3932 | 3933 | double plm_audio_get_time(plm_audio_t *self) { 3934 | return self->time; 3935 | } 3936 | 3937 | void plm_audio_set_time(plm_audio_t *self, double time) { 3938 | self->samples_decoded = time * 3939 | (double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index]; 3940 | self->time = time; 3941 | } 3942 | 3943 | void plm_audio_rewind(plm_audio_t *self) { 3944 | plm_buffer_rewind(self->buffer); 3945 | self->time = 0; 3946 | self->samples_decoded = 0; 3947 | self->next_frame_data_size = 0; 3948 | } 3949 | 3950 | int plm_audio_has_ended(plm_audio_t *self) { 3951 | return plm_buffer_has_ended(self->buffer); 3952 | } 3953 | 3954 | plm_samples_t *plm_audio_decode(plm_audio_t *self) { 3955 | // Do we have at least enough information to decode the frame header? 3956 | if (!self->next_frame_data_size) { 3957 | if (!plm_buffer_has(self->buffer, 48)) { 3958 | return NULL; 3959 | } 3960 | self->next_frame_data_size = plm_audio_decode_header(self); 3961 | } 3962 | 3963 | if ( 3964 | self->next_frame_data_size == 0 || 3965 | !plm_buffer_has(self->buffer, self->next_frame_data_size << 3) 3966 | ) { 3967 | return NULL; 3968 | } 3969 | 3970 | plm_audio_decode_frame(self); 3971 | self->next_frame_data_size = 0; 3972 | 3973 | self->samples.time = self->time; 3974 | 3975 | self->samples_decoded += PLM_AUDIO_SAMPLES_PER_FRAME; 3976 | self->time = (double)self->samples_decoded / 3977 | (double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index]; 3978 | 3979 | return &self->samples; 3980 | } 3981 | 3982 | int plm_audio_find_frame_sync(plm_audio_t *self) { 3983 | size_t i; 3984 | for (i = self->buffer->bit_index >> 3; i < self->buffer->length-1; i++) { 3985 | if ( 3986 | self->buffer->bytes[i] == 0xFF && 3987 | (self->buffer->bytes[i+1] & 0xFE) == 0xFC 3988 | ) { 3989 | self->buffer->bit_index = ((i+1) << 3) + 3; 3990 | return TRUE; 3991 | } 3992 | } 3993 | self->buffer->bit_index = (i + 1) << 3; 3994 | return FALSE; 3995 | } 3996 | 3997 | int plm_audio_decode_header(plm_audio_t *self) { 3998 | if (!plm_buffer_has(self->buffer, 48)) { 3999 | return 0; 4000 | } 4001 | 4002 | plm_buffer_skip_bytes(self->buffer, 0x00); 4003 | int sync = plm_buffer_read(self->buffer, 11); 4004 | 4005 | 4006 | // Attempt to resync if no syncword was found. This sucks balls. The MP2 4007 | // stream contains a syncword just before every frame (11 bits set to 1). 4008 | // However, this syncword is not guaranteed to not occur elsewhere in the 4009 | // stream. So, if we have to resync, we also have to check if the header 4010 | // (samplerate, bitrate) differs from the one we had before. This all 4011 | // may still lead to garbage data being decoded :/ 4012 | 4013 | if (sync != PLM_AUDIO_FRAME_SYNC && !plm_audio_find_frame_sync(self)) { 4014 | return 0; 4015 | } 4016 | 4017 | self->version = plm_buffer_read(self->buffer, 2); 4018 | self->layer = plm_buffer_read(self->buffer, 2); 4019 | int hasCRC = !plm_buffer_read(self->buffer, 1); 4020 | 4021 | if ( 4022 | self->version != PLM_AUDIO_MPEG_1 || 4023 | self->layer != PLM_AUDIO_LAYER_II 4024 | ) { 4025 | return 0; 4026 | } 4027 | 4028 | int bitrate_index = plm_buffer_read(self->buffer, 4) - 1; 4029 | if (bitrate_index > 13) { 4030 | return 0; 4031 | } 4032 | 4033 | int samplerate_index = plm_buffer_read(self->buffer, 2); 4034 | if (samplerate_index == 3) { 4035 | return 0; 4036 | } 4037 | 4038 | int padding = plm_buffer_read(self->buffer, 1); 4039 | plm_buffer_skip(self->buffer, 1); // f_private 4040 | int mode = plm_buffer_read(self->buffer, 2); 4041 | 4042 | // If we already have a header, make sure the samplerate, bitrate and mode 4043 | // are still the same, otherwise we might have missed sync. 4044 | if ( 4045 | self->has_header && ( 4046 | self->bitrate_index != bitrate_index || 4047 | self->samplerate_index != samplerate_index || 4048 | self->mode != mode 4049 | ) 4050 | ) { 4051 | return 0; 4052 | } 4053 | 4054 | self->bitrate_index = bitrate_index; 4055 | self->samplerate_index = samplerate_index; 4056 | self->mode = mode; 4057 | self->has_header = TRUE; 4058 | 4059 | // Parse the mode_extension, set up the stereo bound 4060 | if (mode == PLM_AUDIO_MODE_JOINT_STEREO) { 4061 | self->bound = (plm_buffer_read(self->buffer, 2) + 1) << 2; 4062 | } 4063 | else { 4064 | plm_buffer_skip(self->buffer, 2); 4065 | self->bound = (mode == PLM_AUDIO_MODE_MONO) ? 0 : 32; 4066 | } 4067 | 4068 | // Discard the last 4 bits of the header and the CRC value, if present 4069 | plm_buffer_skip(self->buffer, 4); // copyright(1), original(1), emphasis(2) 4070 | if (hasCRC) { 4071 | plm_buffer_skip(self->buffer, 16); 4072 | } 4073 | 4074 | // Compute frame size, check if we have enough data to decode the whole 4075 | // frame. 4076 | int bitrate = PLM_AUDIO_BIT_RATE[self->bitrate_index]; 4077 | int samplerate = PLM_AUDIO_SAMPLE_RATE[self->samplerate_index]; 4078 | int frame_size = (144000 * bitrate / samplerate) + padding; 4079 | return frame_size - (hasCRC ? 6 : 4); 4080 | } 4081 | 4082 | void plm_audio_decode_frame(plm_audio_t *self) { 4083 | // Prepare the quantizer table lookups 4084 | int tab3 = 0; 4085 | int sblimit = 0; 4086 | 4087 | int tab1 = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 1; 4088 | int tab2 = PLM_AUDIO_QUANT_LUT_STEP_1[tab1][self->bitrate_index]; 4089 | tab3 = QUANT_LUT_STEP_2[tab2][self->samplerate_index]; 4090 | sblimit = tab3 & 63; 4091 | tab3 >>= 6; 4092 | 4093 | if (self->bound > sblimit) { 4094 | self->bound = sblimit; 4095 | } 4096 | 4097 | // Read the allocation information 4098 | for (int sb = 0; sb < self->bound; sb++) { 4099 | self->allocation[0][sb] = plm_audio_read_allocation(self, sb, tab3); 4100 | self->allocation[1][sb] = plm_audio_read_allocation(self, sb, tab3); 4101 | } 4102 | 4103 | for (int sb = self->bound; sb < sblimit; sb++) { 4104 | self->allocation[0][sb] = 4105 | self->allocation[1][sb] = 4106 | plm_audio_read_allocation(self, sb, tab3); 4107 | } 4108 | 4109 | // Read scale factor selector information 4110 | int channels = (self->mode == PLM_AUDIO_MODE_MONO) ? 1 : 2; 4111 | for (int sb = 0; sb < sblimit; sb++) { 4112 | for (int ch = 0; ch < channels; ch++) { 4113 | if (self->allocation[ch][sb]) { 4114 | self->scale_factor_info[ch][sb] = plm_buffer_read(self->buffer, 2); 4115 | } 4116 | } 4117 | if (self->mode == PLM_AUDIO_MODE_MONO) { 4118 | self->scale_factor_info[1][sb] = self->scale_factor_info[0][sb]; 4119 | } 4120 | } 4121 | 4122 | // Read scale factors 4123 | for (int sb = 0; sb < sblimit; sb++) { 4124 | for (int ch = 0; ch < channels; ch++) { 4125 | if (self->allocation[ch][sb]) { 4126 | int *sf = self->scale_factor[ch][sb]; 4127 | switch (self->scale_factor_info[ch][sb]) { 4128 | case 0: 4129 | sf[0] = plm_buffer_read(self->buffer, 6); 4130 | sf[1] = plm_buffer_read(self->buffer, 6); 4131 | sf[2] = plm_buffer_read(self->buffer, 6); 4132 | break; 4133 | case 1: 4134 | sf[0] = 4135 | sf[1] = plm_buffer_read(self->buffer, 6); 4136 | sf[2] = plm_buffer_read(self->buffer, 6); 4137 | break; 4138 | case 2: 4139 | sf[0] = 4140 | sf[1] = 4141 | sf[2] = plm_buffer_read(self->buffer, 6); 4142 | break; 4143 | case 3: 4144 | sf[0] = plm_buffer_read(self->buffer, 6); 4145 | sf[1] = 4146 | sf[2] = plm_buffer_read(self->buffer, 6); 4147 | break; 4148 | } 4149 | } 4150 | } 4151 | if (self->mode == PLM_AUDIO_MODE_MONO) { 4152 | self->scale_factor[1][sb][0] = self->scale_factor[0][sb][0]; 4153 | self->scale_factor[1][sb][1] = self->scale_factor[0][sb][1]; 4154 | self->scale_factor[1][sb][2] = self->scale_factor[0][sb][2]; 4155 | } 4156 | } 4157 | 4158 | // Coefficient input and reconstruction 4159 | int out_pos = 0; 4160 | for (int part = 0; part < 3; part++) { 4161 | for (int granule = 0; granule < 4; granule++) { 4162 | 4163 | // Read the samples 4164 | for (int sb = 0; sb < self->bound; sb++) { 4165 | plm_audio_read_samples(self, 0, sb, part); 4166 | plm_audio_read_samples(self, 1, sb, part); 4167 | } 4168 | for (int sb = self->bound; sb < sblimit; sb++) { 4169 | plm_audio_read_samples(self, 0, sb, part); 4170 | self->sample[1][sb][0] = self->sample[0][sb][0]; 4171 | self->sample[1][sb][1] = self->sample[0][sb][1]; 4172 | self->sample[1][sb][2] = self->sample[0][sb][2]; 4173 | } 4174 | for (int sb = sblimit; sb < 32; sb++) { 4175 | self->sample[0][sb][0] = 0; 4176 | self->sample[0][sb][1] = 0; 4177 | self->sample[0][sb][2] = 0; 4178 | self->sample[1][sb][0] = 0; 4179 | self->sample[1][sb][1] = 0; 4180 | self->sample[1][sb][2] = 0; 4181 | } 4182 | 4183 | // Synthesis loop 4184 | for (int p = 0; p < 3; p++) { 4185 | // Shifting step 4186 | self->v_pos = (self->v_pos - 64) & 1023; 4187 | 4188 | for (int ch = 0; ch < 2; ch++) { 4189 | plm_audio_idct36(self->sample[ch], p, self->V[ch], self->v_pos); 4190 | 4191 | // Build U, windowing, calculate output 4192 | memset(self->U, 0, sizeof(self->U)); 4193 | 4194 | int d_index = 512 - (self->v_pos >> 1); 4195 | int v_index = (self->v_pos % 128) >> 1; 4196 | while (v_index < 1024) { 4197 | for (int i = 0; i < 32; ++i) { 4198 | self->U[i] += self->D[d_index++] * self->V[ch][v_index++]; 4199 | } 4200 | 4201 | v_index += 128 - 32; 4202 | d_index += 64 - 32; 4203 | } 4204 | 4205 | d_index -= (512 - 32); 4206 | v_index = (128 - 32 + 1024) - v_index; 4207 | while (v_index < 1024) { 4208 | for (int i = 0; i < 32; ++i) { 4209 | self->U[i] += self->D[d_index++] * self->V[ch][v_index++]; 4210 | } 4211 | 4212 | v_index += 128 - 32; 4213 | d_index += 64 - 32; 4214 | } 4215 | 4216 | // Output samples 4217 | #ifdef PLM_AUDIO_SEPARATE_CHANNELS 4218 | float *out_channel = ch == 0 4219 | ? self->samples.left 4220 | : self->samples.right; 4221 | for (int j = 0; j < 32; j++) { 4222 | out_channel[out_pos + j] = self->U[j] / -1090519040.0f; 4223 | } 4224 | #else 4225 | for (int j = 0; j < 32; j++) { 4226 | self->samples.interleaved[((out_pos + j) << 1) + ch] = 4227 | self->U[j] / -1090519040.0f; 4228 | } 4229 | #endif 4230 | } // End of synthesis channel loop 4231 | out_pos += 32; 4232 | } // End of synthesis sub-block loop 4233 | 4234 | } // Decoding of the granule finished 4235 | } 4236 | 4237 | plm_buffer_align(self->buffer); 4238 | } 4239 | 4240 | const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3) { 4241 | int tab4 = PLM_AUDIO_QUANT_LUT_STEP_3[tab3][sb]; 4242 | int qtab = PLM_AUDIO_QUANT_LUT_STEP_4[tab4 & 15][plm_buffer_read(self->buffer, tab4 >> 4)]; 4243 | return qtab ? (&PLM_AUDIO_QUANT_TAB[qtab - 1]) : 0; 4244 | } 4245 | 4246 | void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part) { 4247 | const plm_quantizer_spec_t *q = self->allocation[ch][sb]; 4248 | int sf = self->scale_factor[ch][sb][part]; 4249 | int *sample = self->sample[ch][sb]; 4250 | int val = 0; 4251 | 4252 | if (!q) { 4253 | // No bits allocated for this subband 4254 | sample[0] = sample[1] = sample[2] = 0; 4255 | return; 4256 | } 4257 | 4258 | // Resolve scalefactor 4259 | if (sf == 63) { 4260 | sf = 0; 4261 | } 4262 | else { 4263 | int shift = (sf / 3) | 0; 4264 | sf = (PLM_AUDIO_SCALEFACTOR_BASE[sf % 3] + ((1 << shift) >> 1)) >> shift; 4265 | } 4266 | 4267 | // Decode samples 4268 | int adj = q->levels; 4269 | if (q->group) { 4270 | // Decode grouped samples 4271 | val = plm_buffer_read(self->buffer, q->bits); 4272 | sample[0] = val % adj; 4273 | val /= adj; 4274 | sample[1] = val % adj; 4275 | sample[2] = val / adj; 4276 | } 4277 | else { 4278 | // Decode direct samples 4279 | sample[0] = plm_buffer_read(self->buffer, q->bits); 4280 | sample[1] = plm_buffer_read(self->buffer, q->bits); 4281 | sample[2] = plm_buffer_read(self->buffer, q->bits); 4282 | } 4283 | 4284 | // Postmultiply samples 4285 | int scale = 65536 / (adj + 1); 4286 | adj = ((adj + 1) >> 1) - 1; 4287 | 4288 | val = (adj - sample[0]) * scale; 4289 | sample[0] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12; 4290 | 4291 | val = (adj - sample[1]) * scale; 4292 | sample[1] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12; 4293 | 4294 | val = (adj - sample[2]) * scale; 4295 | sample[2] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12; 4296 | } 4297 | 4298 | void plm_audio_idct36(int s[32][3], int ss, float *d, int dp) { 4299 | float t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, 4300 | t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, 4301 | t25, t26, t27, t28, t29, t30, t31, t32, t33; 4302 | 4303 | t01 = (float)(s[0][ss] + s[31][ss]); t02 = (float)(s[0][ss] - s[31][ss]) * 0.500602998235f; 4304 | t03 = (float)(s[1][ss] + s[30][ss]); t04 = (float)(s[1][ss] - s[30][ss]) * 0.505470959898f; 4305 | t05 = (float)(s[2][ss] + s[29][ss]); t06 = (float)(s[2][ss] - s[29][ss]) * 0.515447309923f; 4306 | t07 = (float)(s[3][ss] + s[28][ss]); t08 = (float)(s[3][ss] - s[28][ss]) * 0.53104259109f; 4307 | t09 = (float)(s[4][ss] + s[27][ss]); t10 = (float)(s[4][ss] - s[27][ss]) * 0.553103896034f; 4308 | t11 = (float)(s[5][ss] + s[26][ss]); t12 = (float)(s[5][ss] - s[26][ss]) * 0.582934968206f; 4309 | t13 = (float)(s[6][ss] + s[25][ss]); t14 = (float)(s[6][ss] - s[25][ss]) * 0.622504123036f; 4310 | t15 = (float)(s[7][ss] + s[24][ss]); t16 = (float)(s[7][ss] - s[24][ss]) * 0.674808341455f; 4311 | t17 = (float)(s[8][ss] + s[23][ss]); t18 = (float)(s[8][ss] - s[23][ss]) * 0.744536271002f; 4312 | t19 = (float)(s[9][ss] + s[22][ss]); t20 = (float)(s[9][ss] - s[22][ss]) * 0.839349645416f; 4313 | t21 = (float)(s[10][ss] + s[21][ss]); t22 = (float)(s[10][ss] - s[21][ss]) * 0.972568237862f; 4314 | t23 = (float)(s[11][ss] + s[20][ss]); t24 = (float)(s[11][ss] - s[20][ss]) * 1.16943993343f; 4315 | t25 = (float)(s[12][ss] + s[19][ss]); t26 = (float)(s[12][ss] - s[19][ss]) * 1.48416461631f; 4316 | t27 = (float)(s[13][ss] + s[18][ss]); t28 = (float)(s[13][ss] - s[18][ss]) * 2.05778100995f; 4317 | t29 = (float)(s[14][ss] + s[17][ss]); t30 = (float)(s[14][ss] - s[17][ss]) * 3.40760841847f; 4318 | t31 = (float)(s[15][ss] + s[16][ss]); t32 = (float)(s[15][ss] - s[16][ss]) * 10.1900081235f; 4319 | 4320 | t33 = t01 + t31; t31 = (t01 - t31) * 0.502419286188f; 4321 | t01 = t03 + t29; t29 = (t03 - t29) * 0.52249861494f; 4322 | t03 = t05 + t27; t27 = (t05 - t27) * 0.566944034816f; 4323 | t05 = t07 + t25; t25 = (t07 - t25) * 0.64682178336f; 4324 | t07 = t09 + t23; t23 = (t09 - t23) * 0.788154623451f; 4325 | t09 = t11 + t21; t21 = (t11 - t21) * 1.06067768599f; 4326 | t11 = t13 + t19; t19 = (t13 - t19) * 1.72244709824f; 4327 | t13 = t15 + t17; t17 = (t15 - t17) * 5.10114861869f; 4328 | t15 = t33 + t13; t13 = (t33 - t13) * 0.509795579104f; 4329 | t33 = t01 + t11; t01 = (t01 - t11) * 0.601344886935f; 4330 | t11 = t03 + t09; t09 = (t03 - t09) * 0.899976223136f; 4331 | t03 = t05 + t07; t07 = (t05 - t07) * 2.56291544774f; 4332 | t05 = t15 + t03; t15 = (t15 - t03) * 0.541196100146f; 4333 | t03 = t33 + t11; t11 = (t33 - t11) * 1.30656296488f; 4334 | t33 = t05 + t03; t05 = (t05 - t03) * 0.707106781187f; 4335 | t03 = t15 + t11; t15 = (t15 - t11) * 0.707106781187f; 4336 | t03 += t15; 4337 | t11 = t13 + t07; t13 = (t13 - t07) * 0.541196100146f; 4338 | t07 = t01 + t09; t09 = (t01 - t09) * 1.30656296488f; 4339 | t01 = t11 + t07; t07 = (t11 - t07) * 0.707106781187f; 4340 | t11 = t13 + t09; t13 = (t13 - t09) * 0.707106781187f; 4341 | t11 += t13; t01 += t11; 4342 | t11 += t07; t07 += t13; 4343 | t09 = t31 + t17; t31 = (t31 - t17) * 0.509795579104f; 4344 | t17 = t29 + t19; t29 = (t29 - t19) * 0.601344886935f; 4345 | t19 = t27 + t21; t21 = (t27 - t21) * 0.899976223136f; 4346 | t27 = t25 + t23; t23 = (t25 - t23) * 2.56291544774f; 4347 | t25 = t09 + t27; t09 = (t09 - t27) * 0.541196100146f; 4348 | t27 = t17 + t19; t19 = (t17 - t19) * 1.30656296488f; 4349 | t17 = t25 + t27; t27 = (t25 - t27) * 0.707106781187f; 4350 | t25 = t09 + t19; t19 = (t09 - t19) * 0.707106781187f; 4351 | t25 += t19; 4352 | t09 = t31 + t23; t31 = (t31 - t23) * 0.541196100146f; 4353 | t23 = t29 + t21; t21 = (t29 - t21) * 1.30656296488f; 4354 | t29 = t09 + t23; t23 = (t09 - t23) * 0.707106781187f; 4355 | t09 = t31 + t21; t31 = (t31 - t21) * 0.707106781187f; 4356 | t09 += t31; t29 += t09; t09 += t23; t23 += t31; 4357 | t17 += t29; t29 += t25; t25 += t09; t09 += t27; 4358 | t27 += t23; t23 += t19; t19 += t31; 4359 | t21 = t02 + t32; t02 = (t02 - t32) * 0.502419286188f; 4360 | t32 = t04 + t30; t04 = (t04 - t30) * 0.52249861494f; 4361 | t30 = t06 + t28; t28 = (t06 - t28) * 0.566944034816f; 4362 | t06 = t08 + t26; t08 = (t08 - t26) * 0.64682178336f; 4363 | t26 = t10 + t24; t10 = (t10 - t24) * 0.788154623451f; 4364 | t24 = t12 + t22; t22 = (t12 - t22) * 1.06067768599f; 4365 | t12 = t14 + t20; t20 = (t14 - t20) * 1.72244709824f; 4366 | t14 = t16 + t18; t16 = (t16 - t18) * 5.10114861869f; 4367 | t18 = t21 + t14; t14 = (t21 - t14) * 0.509795579104f; 4368 | t21 = t32 + t12; t32 = (t32 - t12) * 0.601344886935f; 4369 | t12 = t30 + t24; t24 = (t30 - t24) * 0.899976223136f; 4370 | t30 = t06 + t26; t26 = (t06 - t26) * 2.56291544774f; 4371 | t06 = t18 + t30; t18 = (t18 - t30) * 0.541196100146f; 4372 | t30 = t21 + t12; t12 = (t21 - t12) * 1.30656296488f; 4373 | t21 = t06 + t30; t30 = (t06 - t30) * 0.707106781187f; 4374 | t06 = t18 + t12; t12 = (t18 - t12) * 0.707106781187f; 4375 | t06 += t12; 4376 | t18 = t14 + t26; t26 = (t14 - t26) * 0.541196100146f; 4377 | t14 = t32 + t24; t24 = (t32 - t24) * 1.30656296488f; 4378 | t32 = t18 + t14; t14 = (t18 - t14) * 0.707106781187f; 4379 | t18 = t26 + t24; t24 = (t26 - t24) * 0.707106781187f; 4380 | t18 += t24; t32 += t18; 4381 | t18 += t14; t26 = t14 + t24; 4382 | t14 = t02 + t16; t02 = (t02 - t16) * 0.509795579104f; 4383 | t16 = t04 + t20; t04 = (t04 - t20) * 0.601344886935f; 4384 | t20 = t28 + t22; t22 = (t28 - t22) * 0.899976223136f; 4385 | t28 = t08 + t10; t10 = (t08 - t10) * 2.56291544774f; 4386 | t08 = t14 + t28; t14 = (t14 - t28) * 0.541196100146f; 4387 | t28 = t16 + t20; t20 = (t16 - t20) * 1.30656296488f; 4388 | t16 = t08 + t28; t28 = (t08 - t28) * 0.707106781187f; 4389 | t08 = t14 + t20; t20 = (t14 - t20) * 0.707106781187f; 4390 | t08 += t20; 4391 | t14 = t02 + t10; t02 = (t02 - t10) * 0.541196100146f; 4392 | t10 = t04 + t22; t22 = (t04 - t22) * 1.30656296488f; 4393 | t04 = t14 + t10; t10 = (t14 - t10) * 0.707106781187f; 4394 | t14 = t02 + t22; t02 = (t02 - t22) * 0.707106781187f; 4395 | t14 += t02; t04 += t14; t14 += t10; t10 += t02; 4396 | t16 += t04; t04 += t08; t08 += t14; t14 += t28; 4397 | t28 += t10; t10 += t20; t20 += t02; t21 += t16; 4398 | t16 += t32; t32 += t04; t04 += t06; t06 += t08; 4399 | t08 += t18; t18 += t14; t14 += t30; t30 += t28; 4400 | t28 += t26; t26 += t10; t10 += t12; t12 += t20; 4401 | t20 += t24; t24 += t02; 4402 | 4403 | d[dp + 48] = -t33; 4404 | d[dp + 49] = d[dp + 47] = -t21; 4405 | d[dp + 50] = d[dp + 46] = -t17; 4406 | d[dp + 51] = d[dp + 45] = -t16; 4407 | d[dp + 52] = d[dp + 44] = -t01; 4408 | d[dp + 53] = d[dp + 43] = -t32; 4409 | d[dp + 54] = d[dp + 42] = -t29; 4410 | d[dp + 55] = d[dp + 41] = -t04; 4411 | d[dp + 56] = d[dp + 40] = -t03; 4412 | d[dp + 57] = d[dp + 39] = -t06; 4413 | d[dp + 58] = d[dp + 38] = -t25; 4414 | d[dp + 59] = d[dp + 37] = -t08; 4415 | d[dp + 60] = d[dp + 36] = -t11; 4416 | d[dp + 61] = d[dp + 35] = -t18; 4417 | d[dp + 62] = d[dp + 34] = -t09; 4418 | d[dp + 63] = d[dp + 33] = -t14; 4419 | d[dp + 32] = -t05; 4420 | d[dp + 0] = t05; d[dp + 31] = -t30; 4421 | d[dp + 1] = t30; d[dp + 30] = -t27; 4422 | d[dp + 2] = t27; d[dp + 29] = -t28; 4423 | d[dp + 3] = t28; d[dp + 28] = -t07; 4424 | d[dp + 4] = t07; d[dp + 27] = -t26; 4425 | d[dp + 5] = t26; d[dp + 26] = -t23; 4426 | d[dp + 6] = t23; d[dp + 25] = -t10; 4427 | d[dp + 7] = t10; d[dp + 24] = -t15; 4428 | d[dp + 8] = t15; d[dp + 23] = -t12; 4429 | d[dp + 9] = t12; d[dp + 22] = -t19; 4430 | d[dp + 10] = t19; d[dp + 21] = -t20; 4431 | d[dp + 11] = t20; d[dp + 20] = -t13; 4432 | d[dp + 12] = t13; d[dp + 19] = -t24; 4433 | d[dp + 13] = t24; d[dp + 18] = -t31; 4434 | d[dp + 14] = t31; d[dp + 17] = -t02; 4435 | d[dp + 15] = t02; d[dp + 16] = 0.0; 4436 | } 4437 | 4438 | 4439 | #endif // PL_MPEG_IMPLEMENTATION 4440 | --------------------------------------------------------------------------------