client: update mpeg video decoder

ce50e1da59

Fixes video streams issues. Look to: https://github.com/phoboslab/pl_mpeg/issues/25
This commit is contained in:
Denis Pauk 2024-02-25 23:09:11 +02:00
parent f9f54c7efe
commit d76584a457
2 changed files with 185 additions and 118 deletions

View file

@ -69,10 +69,10 @@ This library provides several interfaces to load, demux and decode MPEG video
and audio data. A high-level API combines the demuxer, video & audio decoders
in an easy to use wrapper.
Lower-level APIs for accessing the demuxer, video decoder and audio decoder,
Lower-level APIs for accessing the demuxer, video decoder and audio decoder,
as well as providing different data sources are also available.
Interfaces are written in an object oriented style, meaning you create object
Interfaces are written in an object oriented style, meaning you create object
instances via various different constructor functions (plm_*create()),
do some work on them and later dispose them via plm_*destroy().
@ -90,7 +90,7 @@ With the high-level interface you have two options to decode video & audio:
plm_set_{video|audio}_decode_callback()) any number of times.
2. Use plm_decode_video() and plm_decode_audio() to decode exactly one
frame of video or audio data at a time. How you handle the synchronization
frame of video or audio data at a time. How you handle the synchronization
of both streams is up to you.
If you only want to decode video *or* audio through these functions, you should
@ -109,7 +109,7 @@ mat4 bt601 = mat4(
gl_FragColor = vec4(y, cb, cr, 1.0) * bt601;
Audio data is decoded into a struct with either one single float array with the
samples for the left and right channel interleaved, or if the
samples for the left and right channel interleaved, or if the
PLM_AUDIO_SEPARATE_CHANNELS is defined *before* including this library, into
two separate float arrays - one for each channel.
@ -117,7 +117,7 @@ two separate float arrays - one for each channel.
Data can be supplied to the high level interface, the demuxer and the decoders
in three different ways:
1. Using plm_create_from_filename() or with a file handle with
1. Using plm_create_from_filename() or with a file handle with
plm_create_from_file().
2. Using plm_create_with_memory() and supplying a pointer to memory that
@ -126,10 +126,10 @@ in three different ways:
3. Using plm_create_with_buffer(), supplying your own plm_buffer_t instance and
periodically writing to this buffer.
When using your own plm_buffer_t instance, you can fill this buffer using
plm_buffer_write(). You can either monitor plm_buffer_get_remaining() and push
data when appropriate, or install a callback on the buffer with
plm_buffer_set_load_callback() that gets called whenever the buffer needs more
When using your own plm_buffer_t instance, you can fill this buffer using
plm_buffer_write(). You can either monitor plm_buffer_get_remaining() and push
data when appropriate, or install a callback on the buffer with
plm_buffer_set_load_callback() that gets called whenever the buffer needs more
data.
A buffer created with plm_buffer_create_with_capacity() is treated as a ring
@ -138,19 +138,19 @@ contrast, a buffer created with plm_buffer_create_for_appending() will keep all
data written to it in memory. This enables seeking in the already loaded data.
There should be no need to use the lower level plm_demux_*, plm_video_* and
There should be no need to use the lower level plm_demux_*, plm_video_* and
plm_audio_* functions, if all you want to do is read/decode an MPEG-PS file.
However, if you get raw mpeg1video data or raw mp2 audio data from a different
source, these functions can be used to decode the raw data directly. Similarly,
source, these functions can be used to decode the raw data directly. Similarly,
if you only want to analyze an MPEG-PS file or extract raw video or audio
packets from it, you can use the plm_demux_* functions.
This library uses malloc(), realloc() and free() to manage memory. Typically
This library uses malloc(), realloc() and free() to manage memory. Typically
all allocation happens up-front when creating the interface. However, the
default buffer size may be too small for certain inputs. In these cases plmpeg
will realloc() the buffer with a larger size whenever needed. You can configure
the default buffer size by defining PLM_BUFFER_DEFAULT_SIZE *before*
the default buffer size by defining PLM_BUFFER_DEFAULT_SIZE *before*
including this library.
You can also define PLM_MALLOC, PLM_REALLOC and PLM_FREE to provide your own
@ -201,11 +201,11 @@ typedef struct {
} plm_packet_t;
// Decoded Video Plane
// Decoded Video Plane
// The byte length of the data is width * height. Note that different planes
// have different sizes: the Luma plane (Y) is double the size of each of
// have different sizes: the Luma plane (Y) is double the size of each of
// the two Chroma planes (Cr, Cb) - i.e. 4 times the byte length.
// Also note that the size of the plane does *not* denote the size of the
// Also note that the size of the plane does *not* denote the size of the
// displayed frame. The sizes of planes are always rounded up to the nearest
// macroblock (16px).
@ -288,8 +288,8 @@ plm_t *plm_create_with_file(FILE *fh, int close_when_done);
// Create a plmpeg instance with a pointer to memory as source. This assumes the
// whole file is in memory. The memory is not copied. Pass TRUE to
// free_when_done to let plmpeg call free() on the pointer when plm_destroy()
// whole file is in memory. The memory is not copied. Pass TRUE to
// free_when_done to let plmpeg call free() on the pointer when plm_destroy()
// is called.
plm_t *plm_create_with_memory(uint8_t *bytes, size_t length, int free_when_done);
@ -307,15 +307,30 @@ plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done);
void plm_destroy(plm_t *self);
// Get whether we have headers on all available streams and we can accurately
// report the number of video/audio streams, video dimensions, framerate and
// audio samplerate.
// Get whether we have headers on all available streams and we can report the
// number of video/audio streams, video dimensions, framerate and audio
// samplerate.
// This returns FALSE if the file is not an MPEG-PS file or - when not using a
// file as source - when not enough data is available yet.
int plm_has_headers(plm_t *self);
// Probe the MPEG-PS data to find the actual number of video and audio streams
// within the buffer. For certain files (e.g. VideoCD) this can be more accurate
// than just reading the number of streams from the headers.
// This should only be used when the underlying plm_buffer is seekable, i.e. for
// files, fixed memory buffers or _for_appending buffers. If used with dynamic
// memory buffers it will skip decoding the probesize!
// The necessary probesize is dependent on the files you expect to read. Usually
// a few hundred KB should be enough to find all streams.
// Use plm_get_num_{audio|video}_streams() afterwards to get the number of
// streams in the file.
// Returns TRUE if any streams were found within the probesize.
int plm_probe(plm_t *self, size_t probesize);
// Get or set whether video decoding is enabled. Default TRUE.
int plm_get_video_enabled(plm_t *self);
@ -395,14 +410,14 @@ void plm_set_loop(plm_t *self, int loop);
int plm_has_ended(plm_t *self);
// Set the callback for decoded video frames used with plm_decode(). If no
// Set the callback for decoded video frames used with plm_decode(). If no
// callback is set, video data will be ignored and not be decoded. The *user
// Parameter will be passed to your callback.
void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp, void *user);
// Set the callback for decoded audio samples used with plm_decode(). If no
// Set the callback for decoded audio samples used with plm_decode(). If no
// callback is set, audio data will be ignored and not be decoded. The *user
// Parameter will be passed to your callback.
@ -418,16 +433,16 @@ void plm_decode(plm_t *self, double seconds);
// Decode and return one video frame. Returns NULL if no frame could be decoded
// (either because the source ended or data is corrupt). If you only want to
// (either because the source ended or data is corrupt). If you only want to
// decode video, you should disable audio via plm_set_audio_enabled().
// The returned plm_frame_t is valid until the next call to plm_decode_video()
// The returned plm_frame_t is valid until the next call to plm_decode_video()
// or until plm_destroy() is called.
plm_frame_t *plm_decode_video(plm_t *self);
// Decode and return one audio frame. Returns NULL if no frame could be decoded
// (either because the source ended or data is corrupt). If you only want to
// (either because the source ended or data is corrupt). If you only want to
// decode audio, you should disable video via plm_set_video_enabled().
// The returned plm_samples_t is valid until the next call to plm_decode_audio()
// or until plm_destroy() is called.
@ -435,14 +450,14 @@ plm_frame_t *plm_decode_video(plm_t *self);
plm_samples_t *plm_decode_audio(plm_t *self);
// Seek to the specified time, clamped between 0 -- duration. This can only be
// used when the underlying plm_buffer is seekable, i.e. for files, fixed
// memory buffers or _for_appending buffers.
// If seek_exact is TRUE this will seek to the exact time, otherwise it will
// seek to the last intra frame just before the desired time. Exact seeking can
// Seek to the specified time, clamped between 0 -- duration. This can only be
// used when the underlying plm_buffer is seekable, i.e. for files, fixed
// memory buffers or _for_appending buffers.
// If seek_exact is TRUE this will seek to the exact time, otherwise it will
// seek to the last intra frame just before the desired time. Exact seeking can
// be slow, because all frames up to the seeked one have to be decoded on top of
// the previous intra frame.
// If seeking succeeds, this function will call the video_decode_callback
// If seeking succeeds, this function will call the video_decode_callback
// exactly once with the target frame. If audio is enabled, it will also call
// the audio_decode_callback any number of times, until the audio_lead_time is
// satisfied.
@ -484,8 +499,8 @@ plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done);
// Create a buffer instance with a pointer to memory as source. This assumes
// the whole file is in memory. The bytes are not copied. Pass 1 to
// free_when_done to let plmpeg call free() on the pointer when plm_destroy()
// the whole file is in memory. The bytes are not copied. Pass 1 to
// free_when_done to let plmpeg call free() on the pointer when plm_destroy()
// is called.
plm_buffer_t *plm_buffer_create_with_memory(uint8_t *bytes, size_t length, int free_when_done);
@ -499,7 +514,7 @@ plm_buffer_t *plm_buffer_create_with_capacity(size_t capacity);
// Create an empty buffer with an initial capacity. The buffer will grow
// as needed. Decoded data will *not* be discarded. This can be used when
// loading a file over the network, without needing to throttle the download.
// loading a file over the network, without needing to throttle the download.
// It also allows for seeking in the already loaded data.
plm_buffer_t *plm_buffer_create_for_appending(size_t initial_capacity);
@ -510,8 +525,8 @@ plm_buffer_t *plm_buffer_create_for_appending(size_t initial_capacity);
void plm_buffer_destroy(plm_buffer_t *self);
// Copy data into the buffer. If the data to be written is larger than the
// available space, the buffer will realloc() with a larger capacity.
// Copy data into the buffer. If the data to be written is larger than the
// available space, the buffer will realloc() with a larger capacity.
// Returns the number of bytes written. This will always be the same as the
// passed in length, except when the buffer was created _with_memory() for
// which _write() is forbidden.
@ -519,7 +534,7 @@ void plm_buffer_destroy(plm_buffer_t *self);
size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length);
// Mark the current byte length as the end of this buffer and signal that no
// Mark the current byte length as the end of this buffer and signal that no
// more data is expected to be written to it. This function should be called
// just after the last plm_buffer_write().
// For _with_capacity buffers, this is cleared on a plm_buffer_rewind().
@ -538,7 +553,7 @@ void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback f
void plm_buffer_rewind(plm_buffer_t *self);
// Get the total size. For files, this returns the file size. For all other
// Get the total size. For files, this returns the file size. For all other
// types it returns the number of bytes currently in the buffer.
size_t plm_buffer_get_size(plm_buffer_t *self);
@ -550,7 +565,7 @@ size_t plm_buffer_get_size(plm_buffer_t *self);
size_t plm_buffer_get_remaining(plm_buffer_t *self);
// Get whether the read position of the buffer is at the end and no more data
// Get whether the read position of the buffer is at the end and no more data
// is expected.
int plm_buffer_has_ended(plm_buffer_t *self);
@ -589,6 +604,12 @@ void plm_demux_destroy(plm_demux_t *self);
int plm_demux_has_headers(plm_demux_t *self);
// Probe the file for the actual number of video/audio streams. See
// plm_probe() for the details.
int plm_demux_probe(plm_demux_t *self, size_t probesize);
// Returns the number of video streams found in the system header. This will
// attempt to read the system header if non is present yet.
@ -612,9 +633,9 @@ int plm_demux_has_ended(plm_demux_t *self);
// Seek to a packet of the specified type with a PTS just before specified time.
// If force_intra is TRUE, only packets containing an intra frame will be
// If force_intra is TRUE, only packets containing an intra frame will be
// considered - this only makes sense when the type is PLM_DEMUX_PACKET_VIDEO_1.
// Note that the specified time is considered 0-based, regardless of the first
// Note that the specified time is considered 0-based, regardless of the first
// PTS in the data source.
plm_packet_t *plm_demux_seek(plm_demux_t *self, double time, int type, int force_intra);
@ -701,7 +722,7 @@ void plm_video_rewind(plm_video_t *self);
int plm_video_has_ended(plm_video_t *self);
// Decode and return one frame of video and advance the internal time by
// Decode and return one frame of video and advance the internal time by
// 1/framerate seconds. The returned frame_t is valid until the next call of
// plm_video_decode() or until the video decoder is destroyed.
@ -710,7 +731,7 @@ plm_frame_t *plm_video_decode(plm_video_t *self);
// Convert the YCrCb data of a frame into interleaved R G B data. The stride
// specifies the width in bytes of the destination buffer. I.e. the number of
// bytes from one line to the next. The stride must be at least
// bytes from one line to the next. The stride must be at least
// (frame->width * bytes_per_pixel). The buffer pointed to by *dest must have a
// size of at least (stride * frame->height).
// Note that the alpha component of the dest buffer is always left untouched.
@ -771,8 +792,8 @@ void plm_audio_rewind(plm_audio_t *self);
int plm_audio_has_ended(plm_audio_t *self);
// Decode and return one "frame" of audio and advance the internal time by
// (PLM_AUDIO_SAMPLES_PER_FRAME/samplerate) seconds. The returned samples_t
// Decode and return one "frame" of audio and advance the internal time by
// (PLM_AUDIO_SAMPLES_PER_FRAME/samplerate) seconds. The returned samples_t
// is valid until the next call of plm_audio_decode() or until the audio
// decoder is destroyed.
@ -891,24 +912,22 @@ int plm_init_decoders(plm_t *self) {
if (self->video_enabled) {
self->video_packet_type = PLM_DEMUX_PACKET_VIDEO_1;
}
self->video_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
plm_buffer_set_load_callback(self->video_buffer, plm_read_video_packet, self);
if (!self->video_decoder) {
self->video_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
plm_buffer_set_load_callback(self->video_buffer, plm_read_video_packet, self);
self->video_decoder = plm_video_create_with_buffer(self->video_buffer, TRUE);
}
}
if (plm_demux_get_num_audio_streams(self->demux) > 0) {
if (self->audio_enabled) {
self->audio_packet_type = PLM_DEMUX_PACKET_AUDIO_1 + self->audio_stream_index;
}
self->audio_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
plm_buffer_set_load_callback(self->audio_buffer, plm_read_audio_packet, self);
}
if (self->video_buffer) {
self->video_decoder = plm_video_create_with_buffer(self->video_buffer, TRUE);
}
if (self->audio_buffer) {
self->audio_decoder = plm_audio_create_with_buffer(self->audio_buffer, TRUE);
if (!self->audio_decoder) {
self->audio_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
plm_buffer_set_load_callback(self->audio_buffer, plm_read_audio_packet, self);
self->audio_decoder = plm_audio_create_with_buffer(self->audio_buffer, TRUE);
}
}
self->has_decoders = TRUE;
@ -935,7 +954,7 @@ int plm_has_headers(plm_t *self) {
if (!plm_demux_has_headers(self->demux)) {
return FALSE;
}
if (!plm_init_decoders(self)) {
return FALSE;
}
@ -950,6 +969,19 @@ int plm_has_headers(plm_t *self) {
return TRUE;
}
int plm_probe(plm_t *self, size_t probesize) {
int found_streams = plm_demux_probe(self->demux, probesize);
if (!found_streams) {
return FALSE;
}
// Re-init decoders
self->has_decoders = FALSE;
self->video_packet_type = 0;
self->audio_packet_type = 0;
return plm_init_decoders(self);
}
void plm_set_audio_enabled(plm_t *self, int enabled) {
self->audio_enabled = enabled;
@ -1095,7 +1127,7 @@ void plm_decode(plm_t *self, double tick) {
do {
did_decode = FALSE;
if (decode_video && plm_video_get_time(self->video_decoder) < video_target_time) {
plm_frame_t *frame = plm_video_decode(self->video_decoder);
if (frame) {
@ -1118,10 +1150,10 @@ void plm_decode(plm_t *self, double tick) {
}
}
} while (did_decode);
// Did all sources we wanted to decode fail and the demuxer is at the end?
if (
(!decode_video || decode_video_failed) &&
(!decode_video || decode_video_failed) &&
(!decode_audio || decode_audio_failed) &&
plm_demux_has_ended(self->demux)
) {
@ -1236,7 +1268,7 @@ plm_frame_t *plm_seek_frame(plm_t *self, double time, int seek_exact) {
else if (time > duration) {
time = duration;
}
plm_packet_t *packet = plm_demux_seek(self->demux, time, type, TRUE);
if (!packet) {
return NULL;
@ -1250,7 +1282,7 @@ plm_frame_t *plm_seek_frame(plm_t *self, double time, int seek_exact) {
plm_video_rewind(self->video_decoder);
plm_video_set_time(self->video_decoder, packet->pts - start_time);
plm_buffer_write(self->video_buffer, packet->data, packet->length);
plm_frame_t *frame = plm_video_decode(self->video_decoder);
plm_frame_t *frame = plm_video_decode(self->video_decoder);
// If we want to seek to an exact frame, we have to decode all frames
// on top of the intra frame we just jumped to.
@ -1273,13 +1305,13 @@ plm_frame_t *plm_seek_frame(plm_t *self, double time, int seek_exact) {
int plm_seek(plm_t *self, double time, int seek_exact) {
plm_frame_t *frame = plm_seek_frame(self, time, seek_exact);
if (!frame) {
return FALSE;
}
if (self->video_decode_callback) {
self->video_decode_callback(self, frame, self->video_decode_callback_user_data);
self->video_decode_callback(self, frame, self->video_decode_callback_user_data);
}
// If audio is not enabled we are done here.
@ -1308,8 +1340,8 @@ int plm_seek(plm_t *self, double time, int seek_exact) {
plm_decode(self, 0);
break;
}
}
}
return TRUE;
}
@ -1382,7 +1414,7 @@ plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done) {
self->close_when_done = close_when_done;
self->mode = PLM_BUFFER_MODE_FILE;
self->discard_read_bytes = TRUE;
fseek(self->fh, 0, SEEK_END);
self->total_size = ftell(self->fh);
fseek(self->fh, 0, SEEK_SET);
@ -1448,8 +1480,8 @@ size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length) {
}
if (self->discard_read_bytes) {
// This should be a ring buffer, but instead it just shifts all unread
// data to the beginning of the buffer and appends new data at the end.
// This should be a ring buffer, but instead it just shifts all unread
// data to the beginning of the buffer and appends new data at the end.
// Seems to be good enough.
plm_buffer_discard_read_bytes(self);
@ -1499,7 +1531,7 @@ void plm_buffer_seek(plm_buffer_t *self, size_t pos) {
else if (self->mode == PLM_BUFFER_MODE_RING) {
if (pos != 0) {
// Seeking to non-0 is forbidden for dynamic-mem buffers
return;
return;
}
self->bit_index = 0;
self->length = 0;
@ -1531,7 +1563,7 @@ void plm_buffer_discard_read_bytes(plm_buffer_t *self) {
void plm_buffer_load_file_callback(plm_buffer_t *self, void *user) {
PLM_UNUSED(user);
if (self->discard_read_bytes) {
plm_buffer_discard_read_bytes(self);
}
@ -1556,12 +1588,12 @@ int plm_buffer_has(plm_buffer_t *self, size_t count) {
if (self->load_callback) {
self->load_callback(self, self->load_callback_user_data);
if (((self->length << 3) - self->bit_index) >= count) {
return TRUE;
}
}
}
if (self->total_size != 0 && self->length == self->total_size) {
self->has_ended = TRUE;
}
@ -1640,10 +1672,10 @@ int plm_buffer_find_start_code(plm_buffer_t *self, int code) {
return -1;
}
static int plm_buffer_has_start_code(plm_buffer_t *self, int code) {
int plm_buffer_has_start_code(plm_buffer_t *self, int code) {
size_t previous_bit_index = self->bit_index;
int previous_discard_read_bytes = self->discard_read_bytes;
self->discard_read_bytes = FALSE;
int current = plm_buffer_find_start_code(self, code);
@ -1652,7 +1684,7 @@ static int plm_buffer_has_start_code(plm_buffer_t *self, int code) {
return current;
}
static int plm_buffer_peek_non_zero(plm_buffer_t *self, int bit_count) {
int plm_buffer_peek_non_zero(plm_buffer_t *self, int bit_count) {
if (!plm_buffer_has(self, bit_count)) {
return FALSE;
}
@ -1792,6 +1824,39 @@ int plm_demux_has_headers(plm_demux_t *self) {
return TRUE;
}
int plm_demux_probe(plm_demux_t *self, size_t probesize) {
int previous_pos = plm_buffer_tell(self->buffer);
int video_stream = FALSE;
int audio_streams[4] = {FALSE, FALSE, FALSE, FALSE};
do {
self->start_code = plm_buffer_next_start_code(self->buffer);
if (self->start_code == PLM_DEMUX_PACKET_VIDEO_1) {
video_stream = TRUE;
}
else if (
self->start_code >= PLM_DEMUX_PACKET_AUDIO_1 &&
self->start_code <= PLM_DEMUX_PACKET_AUDIO_4
) {
audio_streams[self->start_code - PLM_DEMUX_PACKET_AUDIO_1] = TRUE;
}
} while (
self->start_code != -1 &&
plm_buffer_tell(self->buffer) - previous_pos < probesize
);
self->num_video_streams = video_stream ? 1 : 0;
self->num_audio_streams = 0;
for (int i = 0; i < 4; i++) {
if (audio_streams[i]) {
self->num_audio_streams++;
}
}
plm_demux_buffer_seek(self, previous_pos);
return (self->num_video_streams || self->num_audio_streams);
}
int plm_demux_get_num_video_streams(plm_demux_t *self) {
return plm_demux_has_headers(self)
? self->num_video_streams
@ -1829,7 +1894,7 @@ double plm_demux_get_start_time(plm_demux_t *self, int type) {
int previous_pos = plm_buffer_tell(self->buffer);
int previous_start_code = self->start_code;
// Find first video PTS
plm_demux_rewind(self);
do {
@ -1859,8 +1924,8 @@ double plm_demux_get_duration(plm_demux_t *self, int type) {
size_t previous_pos = plm_buffer_tell(self->buffer);
int previous_start_code = self->start_code;
// Find last video PTS. Start searching 64kb from the end and go further
// Find last video PTS. Start searching 64kb from the end and go further
// back if needed.
long start_range = 64 * 1024;
long max_range = 4096 * 1024;
@ -1899,7 +1964,7 @@ plm_packet_t *plm_demux_seek(plm_demux_t *self, double seek_time, int type, int
// Using the current time, current byte position and the average bytes per
// second for this file, try to jump to a byte position that hopefully has
// packets containing timestamps within one second before to the desired
// packets containing timestamps within one second before to the desired
// seek_time.
// If we hit close to the seek_time scan through all packets to find the
@ -1908,7 +1973,7 @@ plm_packet_t *plm_demux_seek(plm_demux_t *self, double seek_time, int type, int
// per second for the jumped range and jump again.
// The number of retries here is hard-limited to a generous amount. Usually
// the correct range is found after 1--5 jumps, even for files with very
// the correct range is found after 1--5 jumps, even for files with very
// variable bitrates. If significantly more jumps are needed, there's
// probably something wrong with the file and we just avoid getting into an
// infinite loop. 32 retries should be enough for anybody.
@ -1961,7 +2026,7 @@ plm_packet_t *plm_demux_seek(plm_demux_t *self, double seek_time, int type, int
// Bail scanning through packets if we hit one that is outside
// seek_time - scan_span.
// We also adjust the cur_time and byterate values here so the next
// We also adjust the cur_time and byterate values here so the next
// iteration can be a bit more precise.
if (packet->pts > seek_time || packet->pts < seek_time - scan_span) {
found_packet_with_pts = TRUE;
@ -1981,7 +2046,7 @@ plm_packet_t *plm_demux_seek(plm_demux_t *self, double seek_time, int type, int
}
// Check if this is an intra frame packet. If so, record the buffer
// position of the start of this packet. We want to jump back to it
// position of the start of this packet. We want to jump back to it
// later, when we know it's the last intra frame before desired
// seek time.
if (force_intra) {
@ -1993,7 +2058,7 @@ plm_packet_t *plm_demux_seek(plm_demux_t *self, double seek_time, int type, int
packet->data[i + 2] == 0x01 &&
packet->data[i + 3] == 0x00
) {
// Bits 11--13 in the picture header contain the frame
// Bits 11--13 in the picture header contain the frame
// type, where 1=Intra
if ((packet->data[i + 5] & 0x38) == 8) {
last_valid_packet_start = packet_start;
@ -2062,9 +2127,9 @@ plm_packet_t *plm_demux_decode(plm_demux_t *self) {
do {
self->start_code = plm_buffer_next_start_code(self->buffer);
if (
self->start_code == PLM_DEMUX_PACKET_VIDEO_1 ||
self->start_code == PLM_DEMUX_PACKET_VIDEO_1 ||
self->start_code == PLM_DEMUX_PACKET_PRIVATE || (
self->start_code >= PLM_DEMUX_PACKET_AUDIO_1 &&
self->start_code >= PLM_DEMUX_PACKET_AUDIO_1 &&
self->start_code <= PLM_DEMUX_PACKET_AUDIO_4
)
) {
@ -2122,7 +2187,7 @@ plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int type) {
else {
return NULL; // invalid
}
return plm_demux_get_packet(self);
}
@ -2145,7 +2210,7 @@ plm_packet_t *plm_demux_get_packet(plm_demux_t *self) {
// -----------------------------------------------------------------------------
// plm_video implementation
// Inspired by Java MPEG-1 Video Decoder and Player by Zoltan Korandi
// Inspired by Java MPEG-1 Video Decoder and Player by Zoltan Korandi
// https://sourceforge.net/projects/javampeg1video/
static const int PLM_VIDEO_PICTURE_TYPE_INTRA = 1;
@ -2633,7 +2698,7 @@ void plm_video_idct(int *block);
plm_video_t * plm_video_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) {
plm_video_t *self = (plm_video_t *)PLM_MALLOC(sizeof(plm_video_t));
memset(self, 0, sizeof(plm_video_t));
self->buffer = buffer;
self->destroy_buffer_when_done = destroy_when_done;
@ -2704,12 +2769,12 @@ plm_frame_t *plm_video_decode(plm_video_t *self) {
if (!plm_video_has_header(self)) {
return NULL;
}
plm_frame_t *frame = NULL;
do {
if (self->start_code != PLM_START_PICTURE) {
self->start_code = plm_buffer_find_start_code(self->buffer, PLM_START_PICTURE);
if (self->start_code == -1) {
// If we reached the end of the file and the previously decoded
// frame was a reference frame, we still have to return it.
@ -2742,7 +2807,7 @@ plm_frame_t *plm_video_decode(plm_video_t *self) {
return NULL;
}
plm_buffer_discard_read_bytes(self->buffer);
plm_video_decode_picture(self);
if (self->assume_no_b_frames) {
@ -2758,11 +2823,11 @@ plm_frame_t *plm_video_decode(plm_video_t *self) {
self->has_reference_frame = TRUE;
}
} while (!frame);
frame->time = self->time;
self->frames_decoded++;
self->time = (double)self->frames_decoded / self->framerate;
return frame;
}
@ -2777,7 +2842,7 @@ int plm_video_has_header(plm_video_t *self) {
if (self->start_code == -1) {
return FALSE;
}
if (!plm_video_decode_sequence_header(self)) {
return FALSE;
}
@ -2807,7 +2872,7 @@ int plm_video_decode_sequence_header(plm_video_t *self) {
plm_buffer_skip(self->buffer, 18 + 1 + 10 + 1);
// Load custom intra quant matrix?
if (plm_buffer_read(self->buffer, 1)) {
if (plm_buffer_read(self->buffer, 1)) {
for (int i = 0; i < 64; i++) {
int idx = PLM_VIDEO_ZIG_ZAG[i];
self->intra_quant_matrix[idx] = plm_buffer_read(self->buffer, 8);
@ -2818,7 +2883,7 @@ int plm_video_decode_sequence_header(plm_video_t *self) {
}
// Load custom non intra quant matrix?
if (plm_buffer_read(self->buffer, 1)) {
if (plm_buffer_read(self->buffer, 1)) {
for (int i = 0; i < 64; i++) {
int idx = PLM_VIDEO_ZIG_ZAG[i];
self->non_intra_quant_matrix[idx] = plm_buffer_read(self->buffer, 8);
@ -2920,7 +2985,7 @@ void plm_video_decode_picture(plm_video_t *self) {
do {
self->start_code = plm_buffer_next_start_code(self->buffer);
} while (
self->start_code == PLM_START_EXTENSION ||
self->start_code == PLM_START_EXTENSION ||
self->start_code == PLM_START_USER_DATA
);
@ -3190,7 +3255,7 @@ void plm_video_process_macroblock(
unsigned int si = ((self->mb_row * block_size) + vp) * dw + (self->mb_col * block_size) + hp;
unsigned int di = (self->mb_row * dw + self->mb_col) * block_size;
unsigned int max_address = (dw * (self->mb_height * block_size - block_size + 1) - block_size);
if (si > max_address || di > max_address) {
return; // corrupt video
@ -3703,7 +3768,7 @@ struct plm_audio_t {
int v_pos;
int next_frame_data_size;
int has_header;
plm_buffer_t *buffer;
int destroy_buffer_when_done;
@ -3722,7 +3787,7 @@ int plm_audio_find_frame_sync(plm_audio_t *self);
int plm_audio_decode_header(plm_audio_t *self);
void plm_audio_decode_frame(plm_audio_t *self);
const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3);
void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part);
void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part);
void plm_audio_idct36(int s[32][3], int ss, float *d, int dp);
plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) {
@ -3754,7 +3819,7 @@ int plm_audio_has_header(plm_audio_t *self) {
if (self->has_header) {
return TRUE;
}
self->next_frame_data_size = plm_audio_decode_header(self);
return self->has_header;
}
@ -3770,7 +3835,7 @@ double plm_audio_get_time(plm_audio_t *self) {
}
void plm_audio_set_time(plm_audio_t *self, double time) {
self->samples_decoded = time *
self->samples_decoded = time *
(double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
self->time = time;
}
@ -3804,13 +3869,13 @@ plm_samples_t *plm_audio_decode(plm_audio_t *self) {
plm_audio_decode_frame(self);
self->next_frame_data_size = 0;
self->samples.time = self->time;
self->samples_decoded += PLM_AUDIO_SAMPLES_PER_FRAME;
self->time = (double)self->samples_decoded /
self->time = (double)self->samples_decoded /
(double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
return &self->samples;
}
@ -3838,10 +3903,10 @@ int plm_audio_decode_header(plm_audio_t *self) {
int sync = plm_buffer_read(self->buffer, 11);
// Attempt to resync if no syncword was found. This sucks balls. The MP2
// Attempt to resync if no syncword was found. This sucks balls. The MP2
// stream contains a syncword just before every frame (11 bits set to 1).
// However, this syncword is not guaranteed to not occur elsewhere in the
// stream. So, if we have to resync, we also have to check if the header
// stream. So, if we have to resync, we also have to check if the header
// (samplerate, bitrate) differs from the one we had before. This all
// may still lead to garbage data being decoded :/
@ -3918,7 +3983,7 @@ void plm_audio_decode_frame(plm_audio_t *self) {
// Prepare the quantizer table lookups
int tab3 = 0;
int sblimit = 0;
int tab1 = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 1;
int tab2 = PLM_AUDIO_QUANT_LUT_STEP_1[tab1][self->bitrate_index];
tab3 = QUANT_LUT_STEP_2[tab2][self->samplerate_index];
@ -3966,18 +4031,18 @@ void plm_audio_decode_frame(plm_audio_t *self) {
sf[2] = plm_buffer_read(self->buffer, 6);
break;
case 1:
sf[0] =
sf[0] =
sf[1] = plm_buffer_read(self->buffer, 6);
sf[2] = plm_buffer_read(self->buffer, 6);
break;
case 2:
sf[0] =
sf[1] =
sf[0] =
sf[1] =
sf[2] = plm_buffer_read(self->buffer, 6);
break;
case 3:
sf[0] = plm_buffer_read(self->buffer, 6);
sf[1] =
sf[1] =
sf[2] = plm_buffer_read(self->buffer, 6);
break;
}
@ -4058,7 +4123,7 @@ void plm_audio_decode_frame(plm_audio_t *self) {
}
#else
for (int j = 0; j < 32; j++) {
self->samples.interleaved[((out_pos + j) << 1) + ch] =
self->samples.interleaved[((out_pos + j) << 1) + ch] =
self->U[j] / 2147418112.0f;
}
#endif

View file

@ -1030,7 +1030,9 @@ SCR_PlayCinematic(char *arg)
}
cin.plm_video = plm_create_with_memory(cin.raw_video, len, 0);
if (!cin.plm_video || !cin.plm_video->demux)
if (!cin.plm_video ||
!plm_probe(cin.plm_video, len) ||
!cin.plm_video->demux)
{
FS_FreeFile(cin.raw_video);
cin.raw_video = NULL;