244 lines
6.9 KiB
C
244 lines
6.9 KiB
C
|
#include "mov-internal.h"
|
||
|
#include <errno.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
// 8.6.1.2 Decoding Time to Sample Box (p47)
|
||
|
int mov_read_stts(struct mov_t* mov, const struct mov_box_t* box)
|
||
|
{
|
||
|
uint32_t i, entry_count;
|
||
|
struct mov_stbl_t* stbl = &mov->track->stbl;
|
||
|
|
||
|
mov_buffer_r8(&mov->io); /* version */
|
||
|
mov_buffer_r24(&mov->io); /* flags */
|
||
|
entry_count = mov_buffer_r32(&mov->io);
|
||
|
|
||
|
assert(0 == stbl->stts_count && NULL == stbl->stts); // duplicated STTS atom
|
||
|
if (stbl->stts_count < entry_count)
|
||
|
{
|
||
|
void* p = realloc(stbl->stts, sizeof(struct mov_stts_t) * entry_count);
|
||
|
if (NULL == p) return -ENOMEM;
|
||
|
stbl->stts = (struct mov_stts_t*)p;
|
||
|
}
|
||
|
stbl->stts_count = entry_count;
|
||
|
|
||
|
for (i = 0; i < entry_count; i++)
|
||
|
{
|
||
|
stbl->stts[i].sample_count = mov_buffer_r32(&mov->io);
|
||
|
stbl->stts[i].sample_delta = mov_buffer_r32(&mov->io);
|
||
|
}
|
||
|
|
||
|
(void)box;
|
||
|
return mov_buffer_error(&mov->io);
|
||
|
}
|
||
|
|
||
|
// 8.6.1.3 Composition Time to Sample Box (p47)
|
||
|
int mov_read_ctts(struct mov_t* mov, const struct mov_box_t* box)
|
||
|
{
|
||
|
uint32_t i, entry_count;
|
||
|
struct mov_stbl_t* stbl = &mov->track->stbl;
|
||
|
|
||
|
mov_buffer_r8(&mov->io); /* version */
|
||
|
mov_buffer_r24(&mov->io); /* flags */
|
||
|
entry_count = mov_buffer_r32(&mov->io);
|
||
|
|
||
|
assert(0 == stbl->ctts_count && NULL == stbl->ctts); // duplicated CTTS atom
|
||
|
if (stbl->ctts_count < entry_count)
|
||
|
{
|
||
|
void* p = realloc(stbl->ctts, sizeof(struct mov_stts_t) * entry_count);
|
||
|
if (NULL == p) return -ENOMEM;
|
||
|
stbl->ctts = (struct mov_stts_t*)p;
|
||
|
}
|
||
|
stbl->ctts_count = entry_count;
|
||
|
|
||
|
for (i = 0; i < entry_count; i++)
|
||
|
{
|
||
|
stbl->ctts[i].sample_count = mov_buffer_r32(&mov->io);
|
||
|
stbl->ctts[i].sample_delta = mov_buffer_r32(&mov->io); // parse at int32_t
|
||
|
}
|
||
|
|
||
|
(void)box;
|
||
|
return mov_buffer_error(&mov->io);
|
||
|
}
|
||
|
|
||
|
// 8.6.1.4 Composition to Decode Box (p53)
|
||
|
int mov_read_cslg(struct mov_t* mov, const struct mov_box_t* box)
|
||
|
{
|
||
|
uint8_t version;
|
||
|
// struct mov_stbl_t* stbl = &mov->track->stbl;
|
||
|
|
||
|
version = (uint8_t)mov_buffer_r8(&mov->io); /* version */
|
||
|
mov_buffer_r24(&mov->io); /* flags */
|
||
|
|
||
|
if (0 == version)
|
||
|
{
|
||
|
mov_buffer_r32(&mov->io); /* compositionToDTSShift */
|
||
|
mov_buffer_r32(&mov->io); /* leastDecodeToDisplayDelta */
|
||
|
mov_buffer_r32(&mov->io); /* greatestDecodeToDisplayDelta */
|
||
|
mov_buffer_r32(&mov->io); /* compositionStartTime */
|
||
|
mov_buffer_r32(&mov->io); /* compositionEndTime */
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mov_buffer_r64(&mov->io);
|
||
|
mov_buffer_r64(&mov->io);
|
||
|
mov_buffer_r64(&mov->io);
|
||
|
mov_buffer_r64(&mov->io);
|
||
|
mov_buffer_r64(&mov->io);
|
||
|
}
|
||
|
|
||
|
(void)box;
|
||
|
return mov_buffer_error(&mov->io);
|
||
|
}
|
||
|
|
||
|
size_t mov_write_stts(const struct mov_t* mov, uint32_t count)
|
||
|
{
|
||
|
uint32_t size, i;
|
||
|
const struct mov_sample_t* sample;
|
||
|
const struct mov_track_t* track = mov->track;
|
||
|
|
||
|
size = 12/* full box */ + 4/* entry count */ + count * 8/* entry */;
|
||
|
|
||
|
mov_buffer_w32(&mov->io, size); /* size */
|
||
|
mov_buffer_write(&mov->io, "stts", 4);
|
||
|
mov_buffer_w32(&mov->io, 0); /* version & flags */
|
||
|
mov_buffer_w32(&mov->io, count); /* entry count */
|
||
|
|
||
|
for (i = 0; i < track->sample_count; i++)
|
||
|
{
|
||
|
sample = &track->samples[i];
|
||
|
if(0 == sample->first_chunk)
|
||
|
continue;
|
||
|
mov_buffer_w32(&mov->io, sample->first_chunk); // count
|
||
|
mov_buffer_w32(&mov->io, sample->samples_per_chunk); // delta * timescale / 1000
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
size_t mov_write_ctts(const struct mov_t* mov, uint32_t count)
|
||
|
{
|
||
|
uint32_t size, i;
|
||
|
const struct mov_sample_t* sample;
|
||
|
const struct mov_track_t* track = mov->track;
|
||
|
|
||
|
size = 12/* full box */ + 4/* entry count */ + count * 8/* entry */;
|
||
|
|
||
|
mov_buffer_w32(&mov->io, size); /* size */
|
||
|
mov_buffer_write(&mov->io, "ctts", 4);
|
||
|
mov_buffer_w8(&mov->io, (track->flags & MOV_TRACK_FLAG_CTTS_V1) ? 1 : 0); /* version */
|
||
|
mov_buffer_w24(&mov->io, 0); /* flags */
|
||
|
mov_buffer_w32(&mov->io, count); /* entry count */
|
||
|
|
||
|
for (i = 0; i < track->sample_count; i++)
|
||
|
{
|
||
|
sample = &track->samples[i];
|
||
|
if(0 == sample->first_chunk)
|
||
|
continue;
|
||
|
mov_buffer_w32(&mov->io, sample->first_chunk); // count
|
||
|
mov_buffer_w32(&mov->io, sample->samples_per_chunk); // offset * timescale / 1000
|
||
|
}
|
||
|
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
uint32_t mov_build_stts(struct mov_track_t* track)
|
||
|
{
|
||
|
size_t i;
|
||
|
uint32_t delta, count = 0;
|
||
|
struct mov_sample_t* sample = NULL;
|
||
|
|
||
|
for (i = 0; i < track->sample_count; i++)
|
||
|
{
|
||
|
assert(track->samples[i + 1].dts >= track->samples[i].dts || i + 1 == track->sample_count);
|
||
|
delta = (uint32_t)(i + 1 < track->sample_count && track->samples[i + 1].dts > track->samples[i].dts ? track->samples[i + 1].dts - track->samples[i].dts : 1);
|
||
|
if (NULL != sample && (delta == sample->samples_per_chunk || i + 1 == track->sample_count) )
|
||
|
{
|
||
|
track->samples[i].first_chunk = 0;
|
||
|
assert(sample->first_chunk > 0);
|
||
|
++sample->first_chunk; // compress
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sample = &track->samples[i];
|
||
|
sample->first_chunk = 1;
|
||
|
sample->samples_per_chunk = delta;
|
||
|
++count;
|
||
|
}
|
||
|
}
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
uint32_t mov_build_ctts(struct mov_track_t* track)
|
||
|
{
|
||
|
size_t i;
|
||
|
uint32_t delta;
|
||
|
uint32_t count = 0;
|
||
|
struct mov_sample_t* sample = NULL;
|
||
|
|
||
|
for (i = 0; i < track->sample_count; i++)
|
||
|
{
|
||
|
delta = (uint32_t)(track->samples[i].pts - track->samples[i].dts);
|
||
|
if (i > 0 && delta == sample->samples_per_chunk)
|
||
|
{
|
||
|
track->samples[i].first_chunk = 0;
|
||
|
assert(sample->first_chunk > 0);
|
||
|
++sample->first_chunk; // compress
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sample = &track->samples[i];
|
||
|
sample->first_chunk = 1;
|
||
|
sample->samples_per_chunk = delta;
|
||
|
++count;
|
||
|
|
||
|
// fixed: firefox version 51 don't support version 1
|
||
|
if (track->samples[i].pts < track->samples[i].dts)
|
||
|
track->flags |= MOV_TRACK_FLAG_CTTS_V1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
void mov_apply_stts(struct mov_track_t* track)
|
||
|
{
|
||
|
size_t i, j, n;
|
||
|
struct mov_stbl_t* stbl = &track->stbl;
|
||
|
|
||
|
for (i = 0, n = 1; i < stbl->stts_count; i++)
|
||
|
{
|
||
|
for (j = 0; j < stbl->stts[i].sample_count; j++, n++)
|
||
|
{
|
||
|
track->samples[n].dts = track->samples[n - 1].dts + stbl->stts[i].sample_delta;
|
||
|
track->samples[n].pts = track->samples[n].dts;
|
||
|
}
|
||
|
}
|
||
|
assert(n - 1 == track->sample_count); // see more mov_read_stsz
|
||
|
}
|
||
|
|
||
|
void mov_apply_ctts(struct mov_track_t* track)
|
||
|
{
|
||
|
size_t i, j, n;
|
||
|
int32_t delta, dts_shift;
|
||
|
struct mov_stbl_t* stbl = &track->stbl;
|
||
|
|
||
|
// make sure pts >= dts
|
||
|
dts_shift = 0;
|
||
|
for (i = 0; i < stbl->ctts_count; i++)
|
||
|
{
|
||
|
delta = (int32_t)stbl->ctts[i].sample_delta;
|
||
|
if (delta < 0 && dts_shift > delta && delta != -1 /* see more cslg box*/)
|
||
|
dts_shift = delta;
|
||
|
}
|
||
|
assert(dts_shift <= 0);
|
||
|
|
||
|
// sample cts/pts
|
||
|
for (i = 0, n = 0; i < stbl->ctts_count; i++)
|
||
|
{
|
||
|
for (j = 0; j < stbl->ctts[i].sample_count; j++, n++)
|
||
|
track->samples[n].pts += (int64_t)((int32_t)stbl->ctts[i].sample_delta - dts_shift); // always as int, fixed mp4box delta version error
|
||
|
}
|
||
|
assert(0 == stbl->ctts_count || n == track->sample_count);
|
||
|
}
|