FFFF minimp4/minimp4.h at master · lieff/minimp4 · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
3785 lines (3415 loc) · 140 KB

minimp4.h

File metadata and controls

3785 lines (3415 loc) · 140 KB
BOX_hdlr = FOUR_CHAR_INT( 'h', 'd', 'l', 'r' ),//HandlerAtomType
BOX_hmhd = FOUR_CHAR_INT( 'h', 'm', 'h', 'd' ),//HintMediaHeaderAtomType
BOX_hint = FOUR_CHAR_INT( 'h', 'i', 'n', 't' ),//HintTrackReferenceAtomType
BOX_mdia = FOUR_CHAR_INT( 'm', 'd', 'i', 'a' ),//MediaAtomType
BOX_mdat = FOUR_CHAR_INT( 'm', 'd', 'a', 't' ),//MediaDataAtomType
BOX_mdhd = FOUR_CHAR_INT( 'm', 'd', 'h', 'd' ),//MediaHeaderAtomType
BOX_minf = FOUR_CHAR_INT( 'm', 'i', 'n', 'f' ),//MediaInformationAtomType
BOX_moov = FOUR_CHAR_INT( 'm', 'o', 'o', 'v' ),//MovieAtomType
BOX_mvhd = FOUR_CHAR_INT( 'm', 'v', 'h', 'd' ),//MovieHeaderAtomType
BOX_stsd = FOUR_CHAR_INT( 's', 't', 's', 'd' ),//SampleDescriptionAtomType
BOX_stsz = FOUR_CHAR_INT( 's', 't', 's', 'z' ),//SampleSizeAtomType
BOX_stz2 = FOUR_CHAR_INT( 's', 't', 'z', '2' ),//CompactSampleSizeAtomType
BOX_stbl = FOUR_CHAR_INT( 's', 't', 'b', 'l' ),//SampleTableAtomType
BOX_stsc = FOUR_CHAR_INT( 's', 't', 's', 'c' ),//SampleToChunkAtomType
BOX_stsh = FOUR_CHAR_INT( 's', 't', 's', 'h' ),//ShadowSyncAtomType
BOX_skip = FOUR_CHAR_INT( 's', 'k', 'i', 'p' ),//SkipAtomType
BOX_smhd = FOUR_CHAR_INT( 's', 'm', 'h', 'd' ),//SoundMediaHeaderAtomType
BOX_stss = FOUR_CHAR_INT( 's', 't', 's', 's' ),//SyncSampleAtomType
BOX_stts = FOUR_CHAR_INT( 's', 't', 't', 's' ),//TimeToSampleAtomType
BOX_trak = FOUR_CHAR_INT( 't', 'r', 'a', 'k' ),//TrackAtomType
BOX_tkhd = FOUR_CHAR_INT( 't', 'k', 'h', 'd' ),//TrackHeaderAtomType
BOX_tref = FOUR_CHAR_INT( 't', 'r', 'e', 'f' ),//TrackReferenceAtomType
BOX_udta = FOUR_CHAR_INT( 'u', 'd', 't', 'a' ),//UserDataAtomType
BOX_vmhd = FOUR_CHAR_INT( 'v', 'm', 'h', 'd' ),//VideoMediaHeaderAtomType
BOX_url = FOUR_CHAR_INT( 'u', 'r', 'l', ' ' ),
BOX_urn = FOUR_CHAR_INT( 'u', 'r', 'n', ' ' ),
BOX_gnrv = FOUR_CHAR_INT( 'g', 'n', 'r', 'v' ),//GenericVisualSampleEntryAtomType
BOX_gnra = FOUR_CHAR_INT( 'g', 'n', 'r', 'a' ),//GenericAudioSampleEntryAtomType
//V2 atoms
BOX_ftyp = FOUR_CHAR_INT( 'f', 't', 'y', 'p' ),//FileTypeAtomType
BOX_padb = FOUR_CHAR_INT( 'p', 'a', 'd', 'b' ),//PaddingBitsAtomType
//MP4 Atoms
BOX_sdhd = FOUR_CHAR_INT( 's', 'd', 'h', 'd' ),//SceneDescriptionMediaHeaderAtomType
BOX_dpnd = FOUR_CHAR_INT( 'd', 'p', 'n', 'd' ),//StreamDependenceAtomType
BOX_iods = FOUR_CHAR_INT( 'i', 'o', 'd', 's' ),//ObjectDescriptorAtomType
BOX_odhd = FOUR_CHAR_INT( 'o', 'd', 'h', 'd' ),//ObjectDescriptorMediaHeaderAtomType
BOX_mpod = FOUR_CHAR_INT( 'm', 'p', 'o', 'd' ),//ODTrackReferenceAtomType
BOX_nmhd = FOUR_CHAR_INT( 'n', 'm', 'h', 'd' ),//MPEGMediaHeaderAtomType
BOX_esds = FOUR_CHAR_INT( 'e', 's', 'd', 's' ),//ESDAtomType
BOX_sync = FOUR_CHAR_INT( 's', 'y', 'n', 'c' ),//OCRReferenceAtomType
BOX_ipir = FOUR_CHAR_INT( 'i', 'p', 'i', 'r' ),//IPIReferenceAtomType
BOX_mp4s = FOUR_CHAR_INT( 'm', 'p', '4', 's' ),//MPEGSampleEntryAtomType
BOX_mp4a = FOUR_CHAR_INT( 'm', 'p', '4', 'a' ),//MPEGAudioSampleEntryAtomType
BOX_sowt = FOUR_CHAR_INT( 's', 'o', 'w', 't' ),//QuickTime uncompressed 16-bit little-endian PCM sample entry
BOX_chan = FOUR_CHAR_INT( 'c', 'h', 'a', 'n' ),//QuickTime channel layout box (inside 'sowt')
BOX_mp4v = FOUR_CHAR_INT( 'm', 'p', '4', 'v' ),//MPEGVisualSampleEntryAtomType
// http://www.itscj.ipsj.or.jp/sc29/open/29view/29n7644t.doc
BOX_avc1 = FOUR_CHAR_INT( 'a', 'v', 'c', '1' ),
BOX_avc2 = FOUR_CHAR_INT( 'a', 'v', 'c', '2' ),
BOX_svc1 = FOUR_CHAR_INT( 's', 'v', 'c', '1' ),
BOX_avcC = FOUR_CHAR_INT( 'a', 'v', 'c', 'C' ),
BOX_svcC = FOUR_CHAR_INT( 's', 'v', 'c', 'C' ),
BOX_btrt = FOUR_CHAR_INT( 'b', 't', 'r', 't' ),
BOX_m4ds = FOUR_CHAR_INT( 'm', '4', 'd', 's' ),
BOX_seib = FOUR_CHAR_INT( 's', 'e', 'i', 'b' ),
// H264/HEVC
BOX_hev1 = FOUR_CHAR_INT( 'h', 'e', 'v', '1' ),
BOX_hvc1 = FOUR_CHAR_INT( 'h', 'v', 'c', '1' ),
BOX_hvcC = FOUR_CHAR_INT( 'h', 'v', 'c', 'C' ),
//3GPP atoms
BOX_samr = FOUR_CHAR_INT( 's', 'a', 'm', 'r' ),//AMRSampleEntryAtomType
BOX_sawb = FOUR_CHAR_INT( 's', 'a', 'w', 'b' ),//WB_AMRSampleEntryAtomType
BOX_damr = FOUR_CHAR_INT( 'd', 'a', 'm', 'r' ),//AMRConfigAtomType
BOX_s263 = FOUR_CHAR_INT( 's', '2', '6', '3' ),//H263SampleEntryAtomType
BOX_d263 = FOUR_CHAR_INT( 'd', '2', '6', '3' ),//H263ConfigAtomType
//V2 atoms - Movie Fragments
BOX_mvex = FOUR_CHAR_INT( 'm', 'v', 'e', 'x' ),//MovieExtendsAtomType
BOX_trex = FOUR_CHAR_INT( 't', 'r', 'e', 'x' ),//TrackExtendsAtomType
BOX_moof = FOUR_CHAR_INT( 'm', 'o', 'o', 'f' ),//MovieFragmentAtomType
BOX_mfhd = FOUR_CHAR_INT( 'm', 'f', 'h', 'd' ),//MovieFragmentHeaderAtomType
BOX_traf = FOUR_CHAR_INT( 't', 'r', 'a', 'f' ),//TrackFragmentAtomType
BOX_tfhd = FOUR_CHAR_INT( 't', 'f', 'h', 'd' ),//TrackFragmentHeaderAtomType
BOX_tfdt = FOUR_CHAR_INT( 't', 'f', 'd', 't' ),//TrackFragmentBaseMediaDecodeTimeBox
BOX_trun = FOUR_CHAR_INT( 't', 'r', 'u', 'n' ),//TrackFragmentRunAtomType
BOX_mehd = FOUR_CHAR_INT( 'm', 'e', 'h', 'd' ),//MovieExtendsHeaderBox
// Object Descriptors (OD) data coding
// These takes only 1 byte; this implementation translate <od_tag> to
// <od_tag> + OD_BASE to keep API uniform and safe for string functions
OD_BASE = FOUR_CHAR_INT( '$', '$', '$', '0' ),//
OD_ESD = FOUR_CHAR_INT( '$', '$', '$', '3' ),//SDescriptor_Tag
OD_DCD = FOUR_CHAR_INT( '$', '$', '$', '4' ),//DecoderConfigDescriptor_Tag
OD_DSI = FOUR_CHAR_INT( '$', '$', '$', '5' ),//DecoderSpecificInfo_Tag
OD_SLC = FOUR_CHAR_INT( '$', '$', '$', '6' ),//SLConfigDescriptor_Tag
BOX_meta = FOUR_CHAR_INT( 'm', 'e', 't', 'a' ),
BOX_ilst = FOUR_CHAR_INT( 'i', 'l', 's', 't' ),
// Metagata tags, see http://atomicparsley.sourceforge.net/mpeg-4files.html
BOX_calb = FOUR_CHAR_INT( '\xa9', 'a', 'l', 'b'), // album
BOX_cart = FOUR_CHAR_INT( '\xa9', 'a', 'r', 't'), // artist
BOX_aART = FOUR_CHAR_INT( 'a', 'A', 'R', 'T' ), // album artist
BOX_ccmt = FOUR_CHAR_INT( '\xa9', 'c', 'm', 't'), // comment
BOX_cday = FOUR_CHAR_INT( '\xa9', 'd', 'a', 'y'), // year (as string)
BOX_cnam = FOUR_CHAR_INT( '\xa9', 'n', 'a', 'm'), // title
BOX_cgen = FOUR_CHAR_INT( '\xa9', 'g', 'e', 'n'), // custom genre (as string or as byte!)
BOX_trkn = FOUR_CHAR_INT( 't', 'r', 'k', 'n'), // track number (byte)
BOX_disk = FOUR_CHAR_INT( 'd', 'i', 's', 'k'), // disk number (byte)
BOX_cwrt = FOUR_CHAR_INT( '\xa9', 'w', 'r', 't'), // composer
BOX_ctoo = FOUR_CHAR_INT( '\xa9', 't', 'o', 'o'), // encoder
BOX_tmpo = FOUR_CHAR_INT( 't', 'm', 'p', 'o'), // bpm (byte)
BOX_cpil = FOUR_CHAR_INT( 'c', 'p', 'i', 'l'), // compilation (byte)
BOX_covr = FOUR_CHAR_INT( 'c', 'o', 'v', 'r'), // cover art (JPEG/PNG)
BOX_rtng = FOUR_CHAR_INT( 'r', 't', 'n', 'g'), // rating/advisory (byte)
BOX_cgrp = FOUR_CHAR_INT( '\xa9', 'g', 'r', 'p'), // grouping
BOX_stik = FOUR_CHAR_INT( 's', 't', 'i', 'k'), // stik (byte) 0 = Movie 1 = Normal 2 = Audiobook 5 = Whacked Bookmark 6 = Music Video 9 = Short Film 10 = TV Show 11 = Booklet 14 = Ringtone
BOX_pcst = FOUR_CHAR_INT( 'p', 'c', 's', 't'), // podcast (byte)
BOX_catg = FOUR_CHAR_INT( 'c', 'a', 't', 'g'), // category
BOX_keyw = FOUR_CHAR_INT( 'k', 'e', 'y', 'w'), // keyword
BOX_purl = FOUR_CHAR_INT( 'p', 'u', 'r', 'l'), // podcast URL (byte)
BOX_egid = FOUR_CHAR_INT( 'e', 'g', 'i', 'd'), // episode global unique ID (byte)
BOX_desc = FOUR_CHAR_INT( 'd', 'e', 's', 'c'), // description
BOX_clyr = FOUR_CHAR_INT( '\xa9', 'l', 'y', 'r'), // lyrics (may be > 255 bytes)
BOX_tven = FOUR_CHAR_INT( 't', 'v', 'e', 'n'), // tv episode number
BOX_tves = FOUR_CHAR_INT( 't', 'v', 'e', 's'), // tv episode (byte)
BOX_tvnn = FOUR_CHAR_INT( 't', 'v', 'n', 'n'), // tv network name
BOX_tvsh = FOUR_CHAR_INT( 't', 'v', 's', 'h'), // tv show name
BOX_tvsn = FOUR_CHAR_INT( 't', 'v', 's', 'n'), // tv season (byte)
BOX_purd = FOUR_CHAR_INT( 'p', 'u', 'r', 'd'), // purchase date
BOX_pgap = FOUR_CHAR_INT( 'p', 'g', 'a', 'p'), // Gapless Playback (byte)
//BOX_aart = FOUR_CHAR_INT( 'a', 'a', 'r', 't' ), // Album artist
BOX_cART = FOUR_CHAR_INT( '\xa9', 'A', 'R', 'T'), // artist
BOX_gnre = FOUR_CHAR_INT( 'g', 'n', 'r', 'e'),
// 3GPP metatags (http://cpansearch.perl.org/src/JHAR/MP4-Info-1.12/Info.pm)
BOX_auth = FOUR_CHAR_INT( 'a', 'u', 't', 'h'), // author
BOX_titl = FOUR_CHAR_INT( 't', 'i', 't', 'l'), // title
BOX_dscp = FOUR_CHAR_INT( 'd', 's', 'c', 'p'), // description
BOX_perf = FOUR_CHAR_INT( 'p', 'e', 'r', 'f'), // performer
BOX_mean = FOUR_CHAR_INT( 'm', 'e', 'a', 'n'), //
BOX_name = FOUR_CHAR_INT( 'n', 'a', 'm', 'e'), //
BOX_data = FOUR_CHAR_INT( 'd', 'a', 't', 'a'), //
// these from http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2008-September/053151.html
BOX_albm = FOUR_CHAR_INT( 'a', 'l', 'b', 'm'), // album
BOX_yrrc = FOUR_CHAR_INT( 'y', 'r', 'r', 'c') // album
};
// Video track : 'vide'
#define MP4E_HANDLER_TYPE_VIDE 0x76696465
// Audio track : 'soun'
#define MP4E_HANDLER_TYPE_SOUN 0x736F756E
// General MPEG-4 systems streams (without specific handler).
// Used for private stream, as suggested in http://www.mp4ra.org/handler.html
#define MP4E_HANDLER_TYPE_GESM 0x6765736D
typedef struct
{
boxsize_t size;
boxsize_t offset;
unsigned duration;
unsigned flag_random_access;
} sample_t;
typedef struct {
unsigned char *data;
int bytes;
int capacity;
} minimp4_vector_t;
typedef struct
{
MP4E_track_t info;
minimp4_vector_t smpl; // sample descriptor
minimp4_vector_t pending_sample;
minimp4_vector_t vsps; // or dsi for audio
minimp4_vector_t vpps; // not used for audio
minimp4_vector_t vvps; // used for HEVC
} track_t;
typedef struct MP4E_mux_tag
{
minimp4_vector_t tracks;
int64_t write_pos;
int (*write_callback)(int64_t offset, const void *buffer, size_t size, void *token);
void *token;
char *text_comment;
int sequential_mode_flag;
int enable_fragmentation; // flag, indicating streaming-friendly 'fragmentation' mode
int fragments_count; // # of fragments in 'fragmentation' mode
} MP4E_mux_t;
static const unsigned char box_ftyp[] = {
#if 1
0,0,0,0x18,'f','t','y','p',
'm','p','4','2',0,0,0,0,
'm','p','4','2','i','s','o','m',
#else
// as in ffmpeg
0,0,0,0x20,'f','t','y','p',
'i','s','o','m',0,0,2,0,
'm','p','4','1','i','s','o','m',
'i','s','o','2','a','v','c','1',
#endif
};
/**
* Endian-independent byte-write macros
*/
#define WR(x, n) *p++ = (unsigned char)((x) >> 8*n)
#define WRITE_1(x) WR(x, 0);
#define WRITE_2(x) WR(x, 1); WR(x, 0);
#define WRITE_3(x) WR(x, 2); WR(x, 1); WR(x, 0);
#define WRITE_4(x) WR(x, 3); WR(x, 2); WR(x, 1); WR(x, 0);
#define WR4(p, x) (p)[0] = (char)((x) >> 8*3); (p)[1] = (char)((x) >> 8*2); (p)[2] = (char)((x) >> 8*1); (p)[3] = (char)((x));
// Finish atom: update atom size field
#define END_ATOM --stack; WR4((unsigned char*)*stack, p - *stack);
// Initiate atom: save position of size field on stack
#define ATOM(x) *stack++ = p; p += 4; WRITE_4(x);
// Atom with 'FullAtomVersionFlags' field
#define ATOM_FULL(x, flag) ATOM(x); WRITE_4(flag);
#define ERR(func) { int err = func; if (err) return err; }
/**
Allocate vector with given size, return 1 on success, 0 on fail
*/
static int minimp4_vector_init(minimp4_vector_t *h, int capacity)
{
h->bytes = 0;
h->capacity = capacity;
h->data = capacity ? (unsigned char *)malloc(capacity) : NULL;
return !capacity || !!h->data;
}
/**
Deallocates vector memory
*/
static void minimp4_vector_reset(minimp4_vector_t *h)
{
if (h->data)
free(h->data);
memset(h, 0, sizeof(minimp4_vector_t));
}
/**
Reallocate vector memory to the given size
*/
static int minimp4_vector_grow(minimp4_vector_t *h, int bytes)
{
void *p;
int new_size = h->capacity*2 + 1024;
if (new_size < h->capacity + bytes)
new_size = h->capacity + bytes + 1024;
p = realloc(h->data, new_size);
if (!p)
return 0;
h->data = (unsigned char*)p;
h->capacity = new_size;
return 1;
}
/**
Allocates given number of bytes at the end of vector data, increasing
vector memory if necessary.
Return allocated memory.
*/
static unsigned char *minimp4_vector_alloc_tail(minimp4_vector_t *h, int bytes)
{
unsigned char *p;
if (!h->data && !minimp4_vector_init(h, 2*bytes + 1024))
return NULL;
if ((h->capacity - h->bytes) < bytes && !minimp4_vector_grow(h, bytes))
return NULL;
assert(h->data);
assert((h->capacity - h->bytes) >= bytes);
p = h->data + h->bytes;
h->bytes += bytes;
return p;
}
/**
Append data to the end of the vector (accumulate ot enqueue)
*/
static unsigned char *minimp4_vector_put(minimp4_vector_t *h, const void *buf, int bytes)
{
unsigned char *tail = minimp4_vector_alloc_tail(h, bytes);
if (tail)
memcpy(tail, buf, bytes);
return tail;
}
/**
* Allocates and initialize mp4 multiplexer
* return multiplexor handle on success; NULL on failure
*/
MP4E_mux_t *MP4E_open(int sequential_mode_flag, int enable_fragmentation, void *token,
int (*write_callback)(int64_t offset, const void *buffer, size_t size, void *token))
{
if (write_callback(0, box_ftyp, sizeof(box_ftyp), token)) // Write fixed header: 'ftyp' box
return 0;
MP4E_mux_t *mux = (MP4E_mux_t*)malloc(sizeof(MP4E_mux_t));
if (!mux)
return mux;
mux->sequential_mode_flag = sequential_mode_flag || enable_fragmentation;
mux->enable_fragmentation = enable_fragmentation;
mux->fragments_count = 0;
mux->write_callback = write_callback;
mux->token = token;
mux->text_comment = NULL;
mux->write_pos = sizeof(box_ftyp);
if (!mux->sequential_mode_flag)
{ // Write filler, which would be updated later
if (mux->write_callback(mux->write_pos, box_ftyp, 8, mux->token))
{
free(mux);
return 0;
}
mux->write_pos += 16; // box_ftyp + box_free for 32bit or 64bit size encoding
}
minimp4_vector_init(&mux->tracks, 2*sizeof(track_t));
return mux;
}
/**
* Add new track
*/
int MP4E_add_track(MP4E_mux_t *mux, const MP4E_track_t *track_data)
{
track_t *tr;
int ntr = mux->tracks.bytes / sizeof(track_t);
if (!mux || !track_data)
return MP4E_STATUS_BAD_ARGUMENTS;
tr = (track_t*)minimp4_vector_alloc_tail(&mux->tracks, sizeof(track_t));
if (!tr)
return MP4E_STATUS_NO_MEMORY;
memset(tr, 0, sizeof(track_t));
memcpy(&tr->info, track_data, sizeof(*track_data));
if (!minimp4_vector_init(&tr->smpl, 256))
return MP4E_STATUS_NO_MEMORY;
minimp4_vector_init(&tr->vsps, 0);
minimp4_vector_init(&tr->vpps, 0);
minimp4_vector_init(&tr->pending_sample, 0);
return ntr;
}
static const unsigned char *next_dsi(const unsigned char *p, const unsigned char *end, int *bytes)
{
if (p < end + 2)
{
*bytes = p[0]*256 + p[1];
return p + 2;
} else
return NULL;
}
static int append_mem(minimp4_vector_t *v, const void *mem, int bytes)
{
int i;
unsigned char size[2];
const unsigned char *p = v->data;
for (i = 0; i + 2 < v->bytes;)
{
int cb = p[i]*256 + p[i + 1];
if (cb == bytes && !memcmp(p + i + 2, mem, cb))
return 1;
i += 2 + cb;
}
size[0] = bytes >> 8;
size[1] = bytes;
return minimp4_vector_put(v, size, 2) && minimp4_vector_put(v, mem, bytes);
}
static int items_count(minimp4_vector_t *v)
{
int i, count = 0;
const unsigned char *p = v->data;
for (i = 0; i + 2 < v->bytes;)
{
int cb = p[i]*256 + p[i + 1];
count++;
i += 2 + cb;
}
return count;
}
int MP4E_set_dsi(MP4E_mux_t *mux, int track_id, const void *dsi, int bytes)
{
track_t* tr = ((track_t*)mux->tracks.data) + track_id;
assert(tr->info.track_media_kind == e_audio ||
tr->info.track_media_kind == e_private);
if (tr->vsps.bytes)
return MP4E_STATUS_ONLY_ONE_DSI_ALLOWED; // only one DSI allowed
return append_mem(&tr->vsps, dsi, bytes) ? MP4E_STATUS_OK : MP4E_STATUS_NO_MEMORY;
}
int MP4E_set_vps(MP4E_mux_t *mux, int track_id, const void *vps, int bytes)
{
track_t* tr = ((track_t*)mux->tracks.data) + track_id;
assert(tr->info.track_media_kind == e_video);
return append_mem(&tr->vvps, vps, bytes) ? MP4E_STATUS_OK : MP4E_STATUS_NO_MEMORY;
}
int MP4E_set_sps(MP4E_mux_t *mux, int track_id, const void *sps, int bytes)
{
track_t* tr = ((track_t*)mux->tracks.data) + track_id;
assert(tr->info.track_media_kind == e_video);
return append_mem(&tr->vsps, sps, bytes) ? MP4E_STATUS_OK : MP4E_STATUS_NO_MEMORY;
}
int MP4E_set_pps(MP4E_mux_t *mux, int track_id, const void *pps, int bytes)
{
track_t* tr = ((track_t*)mux->tracks.data) + track_id;
assert(tr->info.track_media_kind == e_video);
return append_mem(&tr->vpps, pps, bytes) ? MP4E_STATUS_OK : MP4E_STATUS_NO_MEMORY;
}
static unsigned get_duration(const track_t *tr)
{
unsigned i, sum_duration = 0;
const sample_t *s = (const sample_t *)tr->smpl.data;
for (i = 0; i < tr->smpl.bytes/sizeof(sample_t); i++)
{
sum_duration += s[i].duration;
}
return sum_duration;
}
static int write_pending_data(MP4E_mux_t *mux, track_t *tr)
{
// if have pending sample && have at least one sample in the index
if (tr->pending_sample.bytes > 0 && tr->smpl.bytes >= sizeof(sample_t))
{
// Complete pending sample
sample_t *smpl_desc;
unsigned char base[8], *p = base;
assert(mux->sequential_mode_flag);
// Write each sample to a separate atom
assert(mux->sequential_mode_flag); // Separate atom needed for sequential_mode only
WRITE_4(tr->pending_sample.bytes + 8);
WRITE_4(BOX_mdat);
ERR(mux->write_callback(mux->write_pos, base, p - base, mux->token));
mux->write_pos += p - base;
// Update sample descriptor with size and offset
smpl_desc = ((sample_t*)minimp4_vector_alloc_tail(&tr->smpl, 0)) - 1;
smpl_desc->size = tr->pending_sample.bytes;
smpl_desc->offset = (boxsize_t)mux->write_pos;
// Write data
ERR(mux->write_callback(mux->write_pos, tr->pending_sample.data, tr->pending_sample.bytes, mux->token));
mux->write_pos += tr->pending_sample.bytes;
// reset buffer
tr->pending_sample.bytes = 0;
}
return MP4E_STATUS_OK;
}
0