/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│ │vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer │ │ Dominic Szablewski - https://phoboslab.org │ │ │ │ The MIT License(MIT) │ │ Copyright(c) 2019 Dominic Szablewski │ │ │ │ Permission is hereby granted, free of charge, to any person obtaining │ │ a copy of this software and associated documentation files(the │ │ "Software"), to deal in the Software without restriction, including │ │ without limitation the rights to use, copy, modify, merge, publish, │ │ distribute, sublicense, and / or sell copies of the Software, and to │ │ permit persons to whom the Software is furnished to do so, subject to │ │ the following conditions: │ │ │ │ The above copyright notice and this permission notice shall be │ │ included in all copies or substantial portions of the Software. │ │ │ │ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ │ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF │ │ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND │ │ NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE │ │ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN │ │ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN │ │ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE │ │ SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/mpeg/buffer.h" #include "dsp/mpeg/demux.h" #include "dsp/mpeg/mpeg.h" #include "libc/mem/mem.h" #include "libc/str/str.h" asm(".ident\t\"\\n\\n\ PL_MPEG (MIT License)\\n\ Copyright(c) 2019 Dominic Szablewski\\n\ https://phoboslab.org\""); asm(".include \"libc/disclaimer.inc\""); /* clang-format off */ // ---------------------------------------------------------------------------- // plm_demux implementation plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done) { plm_demux_t *self = (plm_demux_t *)malloc(sizeof(plm_demux_t)); memset(self, 0, sizeof(plm_demux_t)); self->buffer = buffer; self->destroy_buffer_when_done = destroy_when_done; if (plm_buffer_find_start_code(self->buffer, START_PACK) != -1) { plm_demux_decode_pack_header(self); } if (plm_buffer_find_start_code(self->buffer, START_SYSTEM) != -1) { plm_demux_decode_system_header(self); } return self; } void plm_demux_destroy(plm_demux_t *self) { if (self->destroy_buffer_when_done) { plm_buffer_destroy(self->buffer); } free(self); } int plm_demux_get_num_video_streams(plm_demux_t *self) { return self->num_video_streams; } int plm_demux_get_num_audio_streams(plm_demux_t *self) { return self->num_audio_streams; } void plm_demux_rewind(plm_demux_t *self) { plm_buffer_rewind(self->buffer); } plm_packet_t *plm_demux_decode(plm_demux_t *self) { if (self->current_packet.length) { size_t bits_till_next_packet = self->current_packet.length << 3; if (!plm_buffer_has(self->buffer, bits_till_next_packet)) { return NULL; } plm_buffer_skip(self->buffer, bits_till_next_packet); self->current_packet.length = 0; } if (!self->has_pack_header) { if (plm_buffer_find_start_code(self->buffer, START_PACK) != -1) { plm_demux_decode_pack_header(self); } else { return NULL; } } if (!self->has_system_header) { if (plm_buffer_find_start_code(self->buffer, START_SYSTEM) != -1) { plm_demux_decode_system_header(self); } else { return NULL; } } // pending packet just waiting for data? if (self->next_packet.length) { return plm_demux_get_packet(self); } int code; do { code = plm_buffer_next_start_code(self->buffer); if ( code == PLM_DEMUX_PACKET_VIDEO_1 || code == PLM_DEMUX_PACKET_PRIVATE || (code >= PLM_DEMUX_PACKET_AUDIO_1 && code <= PLM_DEMUX_PACKET_AUDIO_4) ) { return plm_demux_decode_packet(self, code); } } while (code != -1); return NULL; } double plm_demux_read_time(plm_demux_t *self) { int64_t clock = plm_buffer_read(self->buffer, 3) << 30; plm_buffer_skip(self->buffer, 1); clock |= plm_buffer_read(self->buffer, 15) << 15; plm_buffer_skip(self->buffer, 1); clock |= plm_buffer_read(self->buffer, 15); plm_buffer_skip(self->buffer, 1); return (double)clock / 90000.0; } void plm_demux_decode_pack_header(plm_demux_t *self) { if (plm_buffer_read(self->buffer, 4) != 0x02) { return; // invalid } self->system_clock_ref = plm_demux_read_time(self); plm_buffer_skip(self->buffer, 1); plm_buffer_skip(self->buffer, 22); // mux_rate * 50 plm_buffer_skip(self->buffer, 1); self->has_pack_header = true; } void plm_demux_decode_system_header(plm_demux_t *self) { plm_buffer_skip(self->buffer, 16); // header_length plm_buffer_skip(self->buffer, 24); // rate bound self->num_audio_streams = plm_buffer_read(self->buffer, 6); plm_buffer_skip(self->buffer, 5); // misc flags self->num_video_streams = plm_buffer_read(self->buffer, 5); self->has_system_header = true; } plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int start_code) { if (!plm_buffer_has(self->buffer, 8 << 3)) { return NULL; } self->next_packet.type = start_code; self->next_packet.length = plm_buffer_read(self->buffer, 16); self->next_packet.length -= plm_buffer_skip_bytes(self->buffer, 0xff); // stuffing // skip P-STD if (plm_buffer_read(self->buffer, 2) == 0x01) { plm_buffer_skip(self->buffer, 16); self->next_packet.length -= 2; } int pts_dts_marker = plm_buffer_read(self->buffer, 2); if (pts_dts_marker == 0x03) { self->next_packet.pts = plm_demux_read_time(self); plm_buffer_skip(self->buffer, 40); // skip dts self->next_packet.length -= 10; } else if (pts_dts_marker == 0x02) { self->next_packet.pts = plm_demux_read_time(self); self->next_packet.length -= 5; } else if (pts_dts_marker == 0x00) { self->next_packet.pts = 0; plm_buffer_skip(self->buffer, 4); self->next_packet.length -= 1; } else { return NULL; // invalid } return plm_demux_get_packet(self); } plm_packet_t *plm_demux_get_packet(plm_demux_t *self) { if (!plm_buffer_has(self->buffer, self->next_packet.length << 3)) { return NULL; } self->current_packet.data = self->buffer->bytes + (self->buffer->bit_index >> 3); self->current_packet.length = self->next_packet.length; self->current_packet.type = self->next_packet.type; self->current_packet.pts = self->next_packet.pts; self->next_packet.length = 0; return &self->current_packet; }