mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-10 07:12:07 +00:00
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:
parent
f9f54c7efe
commit
d76584a457
2 changed files with 185 additions and 118 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue