/* mp4.h
 * mpeg4 container format header (ISO/IEC 14496-12)
 * Copyright(c) 2010 Sensoray Co., Inc.
 */

#ifndef _MP4_H
#define _MP4_H

#include <stdio.h> // for FILE

#if defined(_WIN32) && !defined(__MINGW32__)
// Windows
// calling convention
#define MP4CC __stdcall
// additional parameters
#define MP4API
#else
// Linux
#define MP4CC
#define MP4API extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct muxer_state;
typedef struct muxer_state *muxer_handle; 

/* creation and deletion */
MP4API muxer_handle MP4CC mp4_writer_create(void);
MP4API muxer_handle MP4CC mov_writer_create(void);
MP4API muxer_handle MP4CC mp4_reader_create(void);
MP4API muxer_handle MP4CC mp4_remuxer_create(void);
MP4API muxer_handle MP4CC mp4_refrag_create(void);
MP4API void MP4CC mp4_delete(muxer_handle mux);
/* turn on/off mp4 atom debugging */
MP4API void MP4CC mp4_debug(muxer_handle mux, int debug);

/* associate a reader/writer with an open file descriptor or pointer */
MP4API void MP4CC mp4_file_descriptor(muxer_handle mux, int fd);
MP4API void MP4CC mp4_file_pointer(muxer_handle mux, FILE *file);
MP4API void MP4CC mp4_file2_pointer(muxer_handle mux, FILE *file);
MP4API void MP4CC mp4_file_name_ascii(muxer_handle mux, char *fname);
#ifdef _WIN32
MP4API void MP4CC mp4_file_name_unicode(muxer_handle mux, wchar_t *fname);
#endif
/* NOTE: if remuxing, file must be opened for read/write */

/* when a reader/writer is not associated with a file, 
   memory buffers must be transferred for read/write using these two functions: */
/* give a buffer to the muxer to write to, or read from */
MP4API void MP4CC mp4_buf_put(muxer_handle mux, unsigned char* buf, unsigned int size);
/* take back the filled buffer after writing tracks or headers, returns actual length */
MP4API unsigned int MP4CC mp4_buf_get(muxer_handle mux);
/* note: if writing would exceed buffer size, it will be saved for later in an overflow buffer.
 * also, if no buffer is queued, writes go directly to the overflow buffer, and will be copied to the next queued buffer.
 */

/* Transcode a mp4 from a reader to a writer */
MP4API void MP4CC mp4_remux(muxer_handle mux);

/* demux a mp4 from a reader, using a callback  (NYI) */
//void mp4_demux(muxer_handle mux, void (*callback)(void *priv, unsigned int trackno, unsigned int duration, unsigned char *data, unsigned int size), void *priv);

/* scan a reader muxer for mdat and return when we find one, or end of input. */
MP4API void MP4CC mp4_get_mdat(muxer_handle mux, unsigned char **pdata, unsigned int *plen);


/* create a track, tracks must created be before mp4_write_header, returns trackno to be used with mp4_write_track() */
MP4API unsigned int MP4CC mp4_video_track_create(muxer_handle mux, unsigned int fourcc, unsigned int timebase, unsigned int default_duration, unsigned int empty_edit_duration, unsigned int width, unsigned int height, unsigned int profileIdc, unsigned int levelIdc);
/* note: profileIdc, levelIdc are only used with AVCC fourcc, otherwise ignored */
MP4API unsigned int MP4CC mp4_audio_track_create(muxer_handle mux, unsigned int fourcc, unsigned int timebase, unsigned int default_duration, unsigned int empty_edit_duration, unsigned int samplerate, unsigned int channels);

// mp4a with subtype (fourcc = MP4A, but subtype may be AAC(default) or MP3)
// Mpeg1Layer3 audio and AAC audio can both use the MP4A fourcc
#define MP4A_SUBTYPE_AAC  0 // default or unspecified
#define MP4A_SUBTYPE_UNSPECIFIED MP4A_SUBTYPE_AAC
#define MP4A_SUBTYPE_MP3  1 // Mpeg-1 Layer 3 audio (.mp3)

MP4API unsigned int MP4CC mp4_mp4a_track_create(muxer_handle mux, unsigned int subtype, unsigned int timebase, unsigned int default_duration, unsigned int empty_edit_duration, unsigned int samplerate, unsigned int channels);

MP4API unsigned int MP4CC mp4_subtitle_track_create(muxer_handle mux, unsigned int fourcc, char *language, unsigned int timebase, unsigned int default_duration, unsigned int empty_edit_duration, unsigned int width, unsigned int height, unsigned int *palette);

/* write the mp4 moov atom after creating tracks, (if no buf is queued, it writes into overflow buffer) */
MP4API void MP4CC mp4_write_movie(muxer_handle mux);

/* write track data */
MP4API void MP4CC mp4_write_track(muxer_handle mux, unsigned int trackno, unsigned char* buf, unsigned int len, unsigned int dur);

/* write fragment header for all tracks */
MP4API void MP4CC mp4_write_frag_header(muxer_handle mux);

/* write skip (with byte buffer) box */
MP4API void MP4CC mp4_write_skip_data(muxer_handle mux, unsigned char *buf, unsigned int len);

/* set avcC or esds parameter data, to be written during mp4_write_header.
   Note: the pointer is copied, not the data, so don't free buf until after the call mp4_write_header. */
MP4API void MP4CC mp4_set_parameter_data(muxer_handle mux, unsigned char *buf, unsigned int len);
/* get parameter size for buffer passed to mp4_get_parameter_data */
MP4API unsigned int MP4CC mp4_get_parameter_size(muxer_handle mux);
/* get parameter data from avcC or mp4v decoder-specific info, after reading the header. 
   buf should be at least size returned by mp4_get_parameter_size */
MP4API void MP4CC mp4_get_parameter_data(muxer_handle mux, unsigned char *buf);
/* returns a fourcc code for a track for a reader muxer */
MP4API unsigned int MP4CC mp4_get_fourcc(muxer_handle mux, unsigned int trackno);
/* return a mostly-correct timestamp for a particular track (and internally increment it) */
MP4API unsigned int MP4CC mp4_get_timestamp(muxer_handle mux, int trackno);
/* get the default duration for a track */
MP4API unsigned int MP4CC mp4_get_default_duration(muxer_handle mux, int trackno);
/* determine type of mp4 file: returns 0 if non-fragmented, 1 if fragmented, -1 on error */
MP4API int MP4CC mp4_is_fragmented(muxer_handle mux);
MP4API unsigned int mp4_get_timescale(muxer_handle mux, unsigned int trackno);
MP4API unsigned int mp4_get_samplerate(muxer_handle mux, unsigned int trackno);

MP4API int MP4CC mp4_was_write_error(muxer_handle mux);

MP4API const char * MP4CC mp4_get_error(muxer_handle mux);
MP4API int MP4CC mp4_get_error_code(muxer_handle mux);
MP4API void MP4CC mp4_set_error_code(muxer_handle mux, int errcode);
#define MP4_ERR_SEEK 1
#define MP4_ERR_MEM  2
#define MP4_ERR_BAD_INPUT 3
#define MP4_ERR_NULL_BOX 4
#define MP4_ERR_RECURSION 5
#define MP4_ERR_OVERFLOW 6
#define MP4_ERR_REMUX 7
#define MP4_ERR_WRITE 8
#define MP4_ERR_MAX_SIZE 9
#define MP4_ERR_FILE 10
#define MP4_ERR_BIG_DURATION 11

// set the compressor name for VisualSampleEntry box
MP4API void MP4CC mp4_compressor_name(char *name);

/* set the duration for interleaving audio and video frames */
MP4API void MP4CC mp4_interleave_duration(muxer_handle mux, unsigned int dur);

typedef void (MP4CC *mp4_callback_ptr)(unsigned long long offset, unsigned long long timestamp, unsigned int len, unsigned int flags);
#define MP4_CALLBACK_FLAG_VIDEO_KEYFRAME 1
#define MP4_CALLBACK_FLAG_AUDIO 2

/* set the moov header mode, maximum size in bytes and update frequency */
MP4API void MP4CC mp4_set_moov_mode(muxer_handle mux, unsigned int max_size, unsigned int interval);
MP4API void MP4CC mp4_set_moov_max_duration(muxer_handle mux, unsigned int max_minutes, unsigned int interval_seconds);
MP4API void MP4CC mp4_set_callback(muxer_handle mux, mp4_callback_ptr cb);

// find the next frame in the mp4_reader and call the callback, returns 0 else -1 at end of file
MP4API int MP4CC mp4_play(muxer_handle mux);
MP4API int MP4CC mp4_play_reverse(muxer_handle mux);
MP4API int MP4CC mp4_seek(muxer_handle mux, unsigned long long timestamp);

// the mp4_get_error will return file size error if this size would be exceeded (taking moov into account)
MP4API void MP4CC mp4_max_file_size(muxer_handle mux, unsigned long long max_size);



#ifdef __cplusplus
}
#endif

/* define some useful fourcc codes */
#define UUID(c3,c2,c1,c0) (c0 + (c1 << 8) + (c2 << 16) + (c3 << 24))
#define AVC1    UUID('a','v','c','1')
#define MP4A    UUID('m','p','4','a')
#define MP4V    UUID('m','p','4','v')
#define MP2V    UUID('m','p','2','v')
#define MP1V    UUID('m','p','1','v')
#define MPG2    UUID('m','p','g','2')
#define MPG1    UUID('m','p','g','1')
#define MP4S    UUID('m','p','4','s')
#define SOWT    UUID('s','o','w','t')
#define TWOS    UUID('t','w','o','s')

#endif
