624 lines
19 KiB
C
Executable file
624 lines
19 KiB
C
Executable file
#ifndef PL_MPEG_H
|
|
#define PL_MPEG_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Public Data Types
|
|
|
|
|
|
// Object types for the various interfaces
|
|
|
|
typedef struct plm_t plm_t;
|
|
typedef struct plm_buffer_t plm_buffer_t;
|
|
typedef struct plm_demux_t plm_demux_t;
|
|
typedef struct plm_video_t plm_video_t;
|
|
typedef struct plm_audio_t plm_audio_t;
|
|
|
|
|
|
// Demuxed MPEG PS packet
|
|
// The type maps directly to the various MPEG-PES start codes. PTS is the
|
|
// presentation time stamp of the packet in seconds. Note that not all packets
|
|
// have a PTS value, indicated by PLM_PACKET_INVALID_TS.
|
|
|
|
#define PLM_PACKET_INVALID_TS -1
|
|
|
|
typedef struct {
|
|
int type;
|
|
double pts;
|
|
size_t length;
|
|
uint8_t *data;
|
|
} plm_packet_t;
|
|
|
|
|
|
// Decoded Video Plane
|
|
// The byte length of the data is width * height. Note that different planes
|
|
// have different sizes: the Luma plane (Y) is double the size of each of
|
|
// the two Chroma planes (Cr, Cb) - i.e. 4 times the byte length.
|
|
// Also note that the size of the plane does *not* denote the size of the
|
|
// displayed frame. The sizes of planes are always rounded up to the nearest
|
|
// macroblock (16px).
|
|
|
|
typedef struct {
|
|
unsigned int width;
|
|
unsigned int height;
|
|
uint8_t *data;
|
|
} plm_plane_t;
|
|
|
|
|
|
// Decoded Video Frame
|
|
// width and height denote the desired display size of the frame. This may be
|
|
// different from the internal size of the 3 planes.
|
|
|
|
typedef struct {
|
|
double time;
|
|
unsigned int width;
|
|
unsigned int height;
|
|
plm_plane_t y;
|
|
plm_plane_t cr;
|
|
plm_plane_t cb;
|
|
} plm_frame_t;
|
|
|
|
|
|
// Callback function type for decoded video frames used by the high-level
|
|
// plm_* interface
|
|
|
|
typedef void(*plm_video_decode_callback)
|
|
(plm_t *self, plm_frame_t *frame, void *user);
|
|
|
|
|
|
// Decoded Audio Samples
|
|
// Samples are stored as normalized (-1, 1) float either interleaved, or if
|
|
// PLM_AUDIO_SEPARATE_CHANNELS is defined, in two separate arrays.
|
|
// The `count` is always PLM_AUDIO_SAMPLES_PER_FRAME and just there for
|
|
// convenience.
|
|
|
|
#define PLM_AUDIO_SAMPLES_PER_FRAME 1152
|
|
|
|
typedef struct {
|
|
double time;
|
|
unsigned int count;
|
|
#ifdef PLM_AUDIO_SEPARATE_CHANNELS
|
|
float left[PLM_AUDIO_SAMPLES_PER_FRAME];
|
|
float right[PLM_AUDIO_SAMPLES_PER_FRAME];
|
|
#else
|
|
float interleaved[PLM_AUDIO_SAMPLES_PER_FRAME * 2];
|
|
#endif
|
|
} plm_samples_t;
|
|
|
|
|
|
// Callback function type for decoded audio samples used by the high-level
|
|
// plm_* interface
|
|
|
|
typedef void(*plm_audio_decode_callback)
|
|
(plm_t *self, plm_samples_t *samples, void *user);
|
|
|
|
|
|
// Callback function for plm_buffer when it needs more data
|
|
|
|
typedef void(*plm_buffer_load_callback)(plm_buffer_t *self, void *user);
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// plm_* public API
|
|
// High-Level API for loading/demuxing/decoding MPEG-PS data
|
|
|
|
|
|
// Create a plmpeg instance with a filename. Returns NULL if the file could not
|
|
// be opened.
|
|
|
|
plm_t *plm_create_with_filename(const char *filename);
|
|
|
|
|
|
// Create a plmpeg instance with a file handle. Pass TRUE to close_when_done to
|
|
// let plmpeg call fclose() on the handle when plm_destroy() is called.
|
|
|
|
plm_t *plm_create_with_file(FILE *fh, int close_when_done);
|
|
|
|
|
|
// Create a plmpeg instance with 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()
|
|
// is called.
|
|
|
|
plm_t *plm_create_with_memory(uint8_t *bytes, size_t length, int free_when_done);
|
|
|
|
|
|
// Create a plmpeg instance with a plm_buffer as source. Pass TRUE to
|
|
// destroy_when_done to let plmpeg call plm_buffer_destroy() on the buffer when
|
|
// plm_destroy() is called.
|
|
|
|
plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done);
|
|
|
|
|
|
// Destroy a plmpeg instance and free all data.
|
|
|
|
void plm_destroy(plm_t *self);
|
|
|
|
|
|
// Get 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.
|
|
// 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);
|
|
|
|
|
|
// Get or set whether video decoding is enabled. Default TRUE.
|
|
|
|
int plm_get_video_enabled(plm_t *self);
|
|
void plm_set_video_enabled(plm_t *self, int enabled);
|
|
|
|
|
|
// Get the number of video streams (0--1) reported in the system header.
|
|
|
|
int plm_get_num_video_streams(plm_t *self);
|
|
|
|
|
|
// Get the display width/height of the video stream.
|
|
|
|
int plm_get_width(plm_t *self);
|
|
int plm_get_height(plm_t *self);
|
|
|
|
|
|
// Get the framerate of the video stream in frames per second.
|
|
|
|
double plm_get_framerate(plm_t *self);
|
|
|
|
|
|
// Get or set whether audio decoding is enabled. Default TRUE.
|
|
|
|
int plm_get_audio_enabled(plm_t *self);
|
|
void plm_set_audio_enabled(plm_t *self, int enabled);
|
|
|
|
|
|
// Get the number of audio streams (0--4) reported in the system header.
|
|
|
|
int plm_get_num_audio_streams(plm_t *self);
|
|
|
|
|
|
// Set the desired audio stream (0--3). Default 0.
|
|
|
|
void plm_set_audio_stream(plm_t *self, int stream_index);
|
|
|
|
|
|
// Get the samplerate of the audio stream in samples per second.
|
|
|
|
int plm_get_samplerate(plm_t *self);
|
|
|
|
|
|
// Get or set the audio lead time in seconds - the time in which audio samples
|
|
// are decoded in advance (or behind) the video decode time. Typically this
|
|
// should be set to the duration of the buffer of the audio API that you use
|
|
// for output. E.g. for SDL2: (SDL_AudioSpec.samples / samplerate)
|
|
|
|
double plm_get_audio_lead_time(plm_t *self);
|
|
void plm_set_audio_lead_time(plm_t *self, double lead_time);
|
|
|
|
|
|
// Get the current internal time in seconds.
|
|
|
|
double plm_get_time(plm_t *self);
|
|
|
|
|
|
// Get the video duration of the underlying source in seconds.
|
|
|
|
double plm_get_duration(plm_t *self);
|
|
|
|
|
|
// Rewind all buffers back to the beginning.
|
|
|
|
void plm_rewind(plm_t *self);
|
|
|
|
|
|
// Get or set looping. Default FALSE.
|
|
|
|
int plm_get_loop(plm_t *self);
|
|
void plm_set_loop(plm_t *self, int loop);
|
|
|
|
|
|
// Get whether the file has ended. If looping is enabled, this will always
|
|
// return FALSE.
|
|
|
|
int plm_has_ended(plm_t *self);
|
|
|
|
|
|
// Set the callback for decoded video frames used with plm_decode(). If no
|
|
// callback is set, video data will be ignored and not be decoded. 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
|
|
// callback is set, audio data will be ignored and not be decoded. The *user
|
|
// Parameter will be passed to your callback.
|
|
|
|
void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp, void *user);
|
|
|
|
|
|
// Advance the internal timer by seconds and decode video/audio up to this time.
|
|
// This will call the video_decode_callback and audio_decode_callback any number
|
|
// of times. A frame-skip is not implemented, i.e. everything up to current time
|
|
// will be decoded.
|
|
|
|
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
|
|
// 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()
|
|
// 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
|
|
// 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.
|
|
|
|
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
|
|
// 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
|
|
// 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.
|
|
// Returns TRUE if seeking succeeded or FALSE if no frame could be found.
|
|
|
|
int plm_seek(plm_t *self, double time, int seek_exact);
|
|
|
|
|
|
// Similar to plm_seek(), but will not call the video_decode_callback,
|
|
// audio_decode_callback or make any attempts to sync audio.
|
|
// Returns the found frame or NULL if no frame could be found.
|
|
|
|
plm_frame_t *plm_seek_frame(plm_t *self, double time, int seek_exact);
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// plm_buffer public API
|
|
// Provides the data source for all other plm_* interfaces
|
|
|
|
|
|
// The default size for buffers created from files or by the high-level API
|
|
|
|
#ifndef PLM_BUFFER_DEFAULT_SIZE
|
|
#define PLM_BUFFER_DEFAULT_SIZE (128 * 1024)
|
|
#endif
|
|
|
|
|
|
// Create a buffer instance with a filename. Returns NULL if the file could not
|
|
// be opened.
|
|
|
|
plm_buffer_t *plm_buffer_create_with_filename(const char *filename);
|
|
|
|
|
|
// Create a buffer instance with a file handle. Pass TRUE to close_when_done
|
|
// to let plmpeg call fclose() on the handle when plm_destroy() is called.
|
|
|
|
plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done);
|
|
|
|
|
|
// Create a buffer instance with a pointer to memory as source. This assumes
|
|
// the whole file is in memory. 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);
|
|
|
|
|
|
// Create an empty buffer with an initial capacity. The buffer will grow
|
|
// as needed. Data that has already been read, will be discarded.
|
|
|
|
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.
|
|
// It also allows for seeking in the already loaded data.
|
|
|
|
plm_buffer_t *plm_buffer_create_for_appending(size_t initial_capacity);
|
|
|
|
|
|
// Destroy a buffer instance and free all data
|
|
|
|
void plm_buffer_destroy(plm_buffer_t *self);
|
|
|
|
|
|
// Copy data into the buffer. If the data to be written is larger than the
|
|
// available space, the buffer will realloc() with a larger capacity.
|
|
// Returns the number of bytes written. This will always be the same as the
|
|
// passed in length, except when the buffer was created _with_memory() for
|
|
// which _write() is forbidden.
|
|
|
|
size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length);
|
|
|
|
|
|
// 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().
|
|
|
|
void plm_buffer_signal_end(plm_buffer_t *self);
|
|
|
|
|
|
// Set a callback that is called whenever the buffer needs more data
|
|
|
|
void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback fp, void *user);
|
|
|
|
|
|
// Rewind the buffer back to the beginning. When loading from a file handle,
|
|
// this also seeks to the beginning of the file.
|
|
|
|
void plm_buffer_rewind(plm_buffer_t *self);
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
// Get the number of remaining (yet unread) bytes in the buffer. This can be
|
|
// useful to throttle writing.
|
|
|
|
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
|
|
// is expected.
|
|
|
|
int plm_buffer_has_ended(plm_buffer_t *self);
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// plm_demux public API
|
|
// Demux an MPEG Program Stream (PS) data into separate packages
|
|
|
|
|
|
// Various Packet Types
|
|
|
|
static const int PLM_DEMUX_PACKET_PRIVATE = 0xBD;
|
|
static const int PLM_DEMUX_PACKET_AUDIO_1 = 0xC0;
|
|
static const int PLM_DEMUX_PACKET_AUDIO_2 = 0xC1;
|
|
static const int PLM_DEMUX_PACKET_AUDIO_3 = 0xC2;
|
|
static const int PLM_DEMUX_PACKET_AUDIO_4 = 0xC2;
|
|
static const int PLM_DEMUX_PACKET_VIDEO_1 = 0xE0;
|
|
|
|
|
|
// Create a demuxer with a plm_buffer as source. This will also attempt to read
|
|
// the pack and system headers from the buffer.
|
|
|
|
plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done);
|
|
|
|
|
|
// Destroy a demuxer and free all data.
|
|
|
|
void plm_demux_destroy(plm_demux_t *self);
|
|
|
|
|
|
// Returns TRUE/FALSE whether pack and system headers have been found. This will
|
|
// attempt to read the headers if non are present yet.
|
|
|
|
int plm_demux_has_headers(plm_demux_t *self);
|
|
|
|
|
|
// Returns the number of video streams found in the system header. This will
|
|
// attempt to read the system header if non is present yet.
|
|
|
|
int plm_demux_get_num_video_streams(plm_demux_t *self);
|
|
|
|
|
|
// Returns the number of audio streams found in the system header. This will
|
|
// attempt to read the system header if non is present yet.
|
|
|
|
int plm_demux_get_num_audio_streams(plm_demux_t *self);
|
|
|
|
|
|
// Rewind the internal buffer. See plm_buffer_rewind().
|
|
|
|
void plm_demux_rewind(plm_demux_t *self);
|
|
|
|
|
|
// Get whether the file has ended. This will be cleared on seeking or rewind.
|
|
|
|
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
|
|
// 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
|
|
// PTS in the data source.
|
|
|
|
plm_packet_t *plm_demux_seek(plm_demux_t *self, double time, int type, int force_intra);
|
|
|
|
|
|
// Get the PTS of the first packet of this type. Returns PLM_PACKET_INVALID_TS
|
|
// if not packet of this packet type can be found.
|
|
|
|
double plm_demux_get_start_time(plm_demux_t *self, int type);
|
|
|
|
|
|
// Get the duration for the specified packet type - i.e. the span between the
|
|
// the first PTS and the last PTS in the data source. This only makes sense when
|
|
// the underlying data source is a file or fixed memory.
|
|
|
|
double plm_demux_get_duration(plm_demux_t *self, int type);
|
|
|
|
|
|
// Decode and return the next packet. The returned packet_t is valid until
|
|
// the next call to plm_demux_decode() or until the demuxer is destroyed.
|
|
|
|
plm_packet_t *plm_demux_decode(plm_demux_t *self);
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// plm_video public API
|
|
// Decode MPEG1 Video ("mpeg1") data into raw YCrCb frames
|
|
|
|
|
|
// Create a video decoder with a plm_buffer as source.
|
|
|
|
plm_video_t *plm_video_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done);
|
|
|
|
|
|
// Destroy a video decoder and free all data.
|
|
|
|
void plm_video_destroy(plm_video_t *self);
|
|
|
|
|
|
// Get whether a sequence header was found and we can accurately report on
|
|
// dimensions and framerate.
|
|
|
|
int plm_video_has_header(plm_video_t *self);
|
|
|
|
|
|
// Get the framerate in frames per second.
|
|
|
|
double plm_video_get_framerate(plm_video_t *self);
|
|
|
|
|
|
// Get the display width/height.
|
|
|
|
int plm_video_get_width(plm_video_t *self);
|
|
int plm_video_get_height(plm_video_t *self);
|
|
|
|
|
|
// Set "no delay" mode. When enabled, the decoder assumes that the video does
|
|
// *not* contain any B-Frames. This is useful for reducing lag when streaming.
|
|
// The default is FALSE.
|
|
|
|
void plm_video_set_no_delay(plm_video_t *self, int no_delay);
|
|
|
|
|
|
// Get the current internal time in seconds.
|
|
|
|
double plm_video_get_time(plm_video_t *self);
|
|
|
|
|
|
// Set the current internal time in seconds. This is only useful when you
|
|
// manipulate the underlying video buffer and want to enforce a correct
|
|
// timestamps.
|
|
|
|
void plm_video_set_time(plm_video_t *self, double time);
|
|
|
|
|
|
// Rewind the internal buffer. See plm_buffer_rewind().
|
|
|
|
void plm_video_rewind(plm_video_t *self);
|
|
|
|
|
|
// Get whether the file has ended. This will be cleared on rewind.
|
|
|
|
int plm_video_has_ended(plm_video_t *self);
|
|
|
|
|
|
// Decode and return one frame of video and advance the internal time by
|
|
// 1/framerate seconds. The returned frame_t is valid until the next call of
|
|
// plm_video_decode() or until the video decoder is destroyed.
|
|
|
|
plm_frame_t *plm_video_decode(plm_video_t *self);
|
|
|
|
|
|
// Convert the YCrCb data of a frame into 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
|
|
// (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.
|
|
|
|
void plm_frame_to_rgb(plm_frame_t *frame, uint8_t *dest, int stride);
|
|
void plm_frame_to_bgr(plm_frame_t *frame, uint8_t *dest, int stride);
|
|
void plm_frame_to_rgba(plm_frame_t *frame, uint8_t *dest, int stride);
|
|
void plm_frame_to_bgra(plm_frame_t *frame, uint8_t *dest, int stride);
|
|
void plm_frame_to_argb(plm_frame_t *frame, uint8_t *dest, int stride);
|
|
void plm_frame_to_abgr(plm_frame_t *frame, uint8_t *dest, int stride);
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// plm_audio public API
|
|
// Decode MPEG-1 Audio Layer II ("mp2") data into raw samples
|
|
|
|
|
|
// Create an audio decoder with a plm_buffer as source.
|
|
|
|
plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done);
|
|
|
|
|
|
// Destroy an audio decoder and free all data.
|
|
|
|
void plm_audio_destroy(plm_audio_t *self);
|
|
|
|
|
|
// Get whether a frame header was found and we can accurately report on
|
|
// samplerate.
|
|
|
|
int plm_audio_has_header(plm_audio_t *self);
|
|
|
|
|
|
// Get the samplerate in samples per second.
|
|
|
|
int plm_audio_get_samplerate(plm_audio_t *self);
|
|
|
|
|
|
// Get the current internal time in seconds.
|
|
|
|
double plm_audio_get_time(plm_audio_t *self);
|
|
|
|
|
|
// Set the current internal time in seconds. This is only useful when you
|
|
// manipulate the underlying video buffer and want to enforce a correct
|
|
// timestamps.
|
|
|
|
void plm_audio_set_time(plm_audio_t *self, double time);
|
|
|
|
|
|
// Rewind the internal buffer. See plm_buffer_rewind().
|
|
|
|
void plm_audio_rewind(plm_audio_t *self);
|
|
|
|
|
|
// Get whether the file has ended. This will be cleared on rewind.
|
|
|
|
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
|
|
// is valid until the next call of plm_audio_decode() or until the audio
|
|
// decoder is destroyed.
|
|
|
|
plm_samples_t *plm_audio_decode(plm_audio_t *self);
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|