451 lines
13 KiB
C
451 lines
13 KiB
C
|
#ifndef COSMOPOLITAN_DSP_MPEG_MPEG_H_
|
||
|
#define COSMOPOLITAN_DSP_MPEG_MPEG_H_
|
||
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||
|
COSMOPOLITAN_C_START_
|
||
|
|
||
|
struct FILE;
|
||
|
|
||
|
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. Not all packets
|
||
|
* have a pts value.
|
||
|
*/
|
||
|
typedef struct plm_packet_t {
|
||
|
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 plm_plane_t {
|
||
|
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 plm_frame_t {
|
||
|
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
|
||
|
|
||
|
struct plm_samples_t {
|
||
|
double time;
|
||
|
unsigned int count;
|
||
|
#ifdef PLM_AUDIO_SEPARATE_CHANNELS
|
||
|
float left[PLM_AUDIO_SAMPLES_PER_FRAME] aligned(32);
|
||
|
float right[PLM_AUDIO_SAMPLES_PER_FRAME] aligned(32);
|
||
|
#else
|
||
|
float interleaved[PLM_AUDIO_SAMPLES_PER_FRAME * 2] aligned(32);
|
||
|
#endif
|
||
|
} aligned(32);
|
||
|
|
||
|
typedef struct plm_samples_t 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 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(struct FILE *fh, int close_when_done);
|
||
|
|
||
|
/**
|
||
|
* Create a plmpeg instance with pointer to memory as source. This assumes the
|
||
|
* whole file is in memory. 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. This is also
|
||
|
* called internally by all the above constructor functions.
|
||
|
*/
|
||
|
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 or set whether video decoding is enabled.
|
||
|
*/
|
||
|
int plm_get_video_enabled(plm_t *self);
|
||
|
void plm_set_video_enabled(plm_t *self, int enabled);
|
||
|
|
||
|
/**
|
||
|
* Get or set whether audio decoding is enabled. When enabling, you can set the
|
||
|
* desired audio stream (0-3) to decode.
|
||
|
*/
|
||
|
int plm_get_audio_enabled(plm_t *self);
|
||
|
void plm_set_audio_enabled(plm_t *self, int enabled, int stream_index);
|
||
|
|
||
|
/**
|
||
|
* Get the display width/height of the video stream
|
||
|
*/
|
||
|
int plm_get_width(plm_t *self);
|
||
|
int plm_get_height(plm_t *self);
|
||
|
|
||
|
double plm_get_pixel_aspect_ratio(plm_t *);
|
||
|
|
||
|
/**
|
||
|
* Get the framerate of the video stream in frames per second
|
||
|
*/
|
||
|
double plm_get_framerate(plm_t *self);
|
||
|
|
||
|
/**
|
||
|
* Get the number of available audio streams in the file
|
||
|
*/
|
||
|
int plm_get_num_audio_streams(plm_t *self);
|
||
|
|
||
|
/**
|
||
|
* 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. Default 0.
|
||
|
*/
|
||
|
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);
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*/
|
||
|
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.
|
||
|
*/
|
||
|
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. Returns true/false whether anything was decoded.
|
||
|
*/
|
||
|
int 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 call or until the 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_video or until the plm_destroy is called.
|
||
|
*/
|
||
|
plm_samples_t *plm_decode_audio(plm_t *self);
|
||
|
|
||
|
/* -----------------------------------------------------------------------------
|
||
|
* 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 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(struct 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. 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.
|
||
|
*/
|
||
|
plm_buffer_t *plm_buffer_create_with_capacity(size_t 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);
|
||
|
|
||
|
/**
|
||
|
* 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);
|
||
|
|
||
|
/**
|
||
|
* -----------------------------------------------------------------------------
|
||
|
* plm_demux public API
|
||
|
* Demux an MPEG Program Stream (PS) data into separate packages
|
||
|
*
|
||
|
* Various Packet Types
|
||
|
*/
|
||
|
#define PLM_DEMUX_PACKET_PRIVATE 0xBD
|
||
|
#define PLM_DEMUX_PACKET_AUDIO_1 0xC0
|
||
|
#define PLM_DEMUX_PACKET_AUDIO_2 0xC1
|
||
|
#define PLM_DEMUX_PACKET_AUDIO_3 0xC2
|
||
|
#define PLM_DEMUX_PACKET_AUDIO_4 0xC2
|
||
|
#define PLM_DEMUX_PACKET_VIDEO_1 0xE0
|
||
|
|
||
|
/**
|
||
|
* Create a demuxer with a plm_buffer as source
|
||
|
*/
|
||
|
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 the number of video streams found in the system header.
|
||
|
*/
|
||
|
int plm_demux_get_num_video_streams(plm_demux_t *self);
|
||
|
|
||
|
/**
|
||
|
* Returns the number of audio streams found in the system header.
|
||
|
*/
|
||
|
int plm_demux_get_num_audio_streams(plm_demux_t *self);
|
||
|
|
||
|
/**
|
||
|
* Rewinds the internal buffer. See plm_buffer_rewind().
|
||
|
*/
|
||
|
void plm_demux_rewind(plm_demux_t *self);
|
||
|
|
||
|
/**
|
||
|
* 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 the framerate in frames per second
|
||
|
*/
|
||
|
double plm_video_get_framerate(plm_video_t *);
|
||
|
|
||
|
double plm_video_get_pixel_aspect_ratio(plm_video_t *);
|
||
|
|
||
|
/**
|
||
|
* Get the display width/height
|
||
|
*/
|
||
|
int plm_video_get_width(plm_video_t *);
|
||
|
int plm_video_get_height(plm_video_t *);
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
*/
|
||
|
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);
|
||
|
|
||
|
/**
|
||
|
* Rewinds the internal buffer. See plm_buffer_rewind().
|
||
|
*/
|
||
|
void plm_video_rewind(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 an interleaved RGB buffer. The buffer
|
||
|
* pointed to by *rgb must have a size of (frame->width * frame->height * 3)
|
||
|
* bytes.
|
||
|
*/
|
||
|
void plm_frame_to_rgb(plm_frame_t *frame, uint8_t *rgb);
|
||
|
|
||
|
/* -----------------------------------------------------------------------------
|
||
|
* 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 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);
|
||
|
|
||
|
/**
|
||
|
* Rewinds the internal buffer. See plm_buffer_rewind().
|
||
|
*/
|
||
|
void plm_audio_rewind(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);
|
||
|
|
||
|
extern long plmpegdecode_latency_;
|
||
|
|
||
|
COSMOPOLITAN_C_END_
|
||
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||
|
#endif /* COSMOPOLITAN_DSP_MPEG_MPEG_H_ */
|