Older/MediaServer/libmov/source/fmp4-writer.c

546 lines
15 KiB
C
Raw Permalink Normal View History

2024-10-01 00:12:57 +08:00
#include "fmp4-writer.h"
#include "mov-internal.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
struct fmp4_writer_t
{
struct mov_t mov;
size_t mdat_size;
int has_moov;
uint32_t frag_interleave;
uint32_t fragment_id; // start from 1
uint32_t sn; // sample sn
};
static int fmp4_write_app(struct mov_t* mov)
{
mov_buffer_w32(&mov->io, 8 + strlen(MOV_APP)); /* size */
mov_buffer_write(&mov->io, "free", 4);
mov_buffer_write(&mov->io, MOV_APP, strlen(MOV_APP));
return 0;
}
static size_t fmp4_write_mvex(struct mov_t* mov)
{
int i;
size_t size;
uint64_t offset;
size = 8 /* Box */;
offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 0); /* size */
mov_buffer_write(&mov->io, "mvex", 4);
size += mov_write_mehd(mov);
for (i = 0; i < mov->track_count; i++)
{
mov->track = mov->tracks + i;
size += mov_write_trex(mov);
}
//size += mov_write_leva(mov);
mov_write_size(mov, offset, size); /* update size */
return size;
}
static size_t fmp4_write_traf(struct mov_t* mov, uint32_t moof)
{
uint32_t i, start;
size_t size;
uint64_t offset;
struct mov_track_t* track;
size = 8 /* Box */;
offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 0); /* size */
mov_buffer_write(&mov->io, "traf", 4);
track = mov->track;
track->tfhd.flags = MOV_TFHD_FLAG_DEFAULT_FLAGS /*| MOV_TFHD_FLAG_BASE_DATA_OFFSET*/;
track->tfhd.flags |= MOV_TFHD_FLAG_SAMPLE_DESCRIPTION_INDEX;
// ISO/IEC 23009-1:2014(E) 6.3.4.2 General format type (p93)
// The 'moof' boxes shall use movie-fragment relative addressing for media data that
// does not use external data references, the flag 'default-base-is-moof' shall be set,
// and data-offset shall be used, i.e. base-data-offset-present shall not be used.
//if (mov->flags & MOV_FLAG_SEGMENT)
{
//track->tfhd.flags &= ~MOV_TFHD_FLAG_BASE_DATA_OFFSET;
track->tfhd.flags |= MOV_TFHD_FLAG_DEFAULT_BASE_IS_MOOF;
}
track->tfhd.base_data_offset = mov->moof_offset;
track->tfhd.sample_description_index = 1;
track->tfhd.default_sample_flags = MOV_AUDIO == track->handler_type ? MOV_TREX_FLAG_SAMPLE_DEPENDS_ON_I_PICTURE : (MOV_TREX_FLAG_SAMPLE_IS_NO_SYNC_SAMPLE| MOV_TREX_FLAG_SAMPLE_DEPENDS_ON_NOT_I_PICTURE);
if (track->sample_count > 0)
{
track->tfhd.flags |= MOV_TFHD_FLAG_DEFAULT_DURATION | MOV_TFHD_FLAG_DEFAULT_SIZE;
track->tfhd.default_sample_duration = track->sample_count > 1 ? (uint32_t)(track->samples[1].dts - track->samples[0].dts) : (uint32_t)track->turn_last_duration;
track->tfhd.default_sample_size = track->samples[0].bytes;
}
else
{
track->tfhd.flags |= MOV_TFHD_FLAG_DURATION_IS_EMPTY;
track->tfhd.default_sample_duration = 0; // not set
track->tfhd.default_sample_size = 0; // not set
}
size += mov_write_tfhd(mov);
// ISO/IEC 23009-1:2014(E) 6.3.4.2 General format type (p93)
// Each 'traf' box shall contain a 'tfdt' box.
size += mov_write_tfdt(mov);
for (start = 0, i = 1; i < track->sample_count; i++)
{
if (track->samples[i - 1].offset + track->samples[i - 1].bytes != track->samples[i].offset)
{
size += mov_write_trun(mov, start, i-start, moof);
start = i;
}
}
size += mov_write_trun(mov, start, i-start, moof);
mov_write_size(mov, offset, size); /* update size */
return size;
}
static size_t fmp4_write_moof(struct mov_t* mov, uint32_t fragment, uint32_t moof)
{
int i;
size_t size, j;
uint64_t offset;
uint64_t n;
size = 8 /* Box */;
offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 0); /* size */
mov_buffer_write(&mov->io, "moof", 4);
size += mov_write_mfhd(mov, fragment);
n = 0;
for (i = 0; i < mov->track_count; i++)
{
mov->track = mov->tracks + i;
// rewrite offset, write only one trun
// 2017/10/17 Dale Curtis SHA-1: a5fd8aa45b11c10613e6e576033a6b5a16b9cbb9 (libavformat/mov.c)
for (j = 0; j < mov->track->sample_count; j++)
{
mov->track->samples[j].offset = n;
n += mov->track->samples[j].bytes;
}
if (mov->track->sample_count > 0)
size += fmp4_write_traf(mov, moof);
}
mov_write_size(mov, offset, size); /* update size */
return size;
}
static size_t fmp4_write_moov(struct mov_t* mov)
{
int i;
size_t size;
uint32_t count;
uint64_t offset;
size = 8 /* Box */;
offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 0); /* size */
mov_buffer_write(&mov->io, "moov", 4);
size += mov_write_mvhd(mov);
// size += fmp4_write_iods(mov);
for (i = 0; i < mov->track_count; i++)
{
mov->track = mov->tracks + i;
count = mov->track->sample_count;
mov->track->sample_count = 0;
size += mov_write_trak(mov);
mov->track->sample_count = count; // restore sample count
}
size += fmp4_write_mvex(mov);
size += mov_write_udta(mov);
mov_write_size(mov, offset, size); /* update size */
return size;
}
static size_t fmp4_write_sidx(struct mov_t* mov)
{
int i;
for (i = 0; i < mov->track_count; i++)
{
mov->track = mov->tracks + i;
mov_write_sidx(mov, 52 * (uint64_t)(mov->track_count - i - 1)); /* first_offset */
}
return 52 * mov->track_count;
}
static int fmp4_write_mfra(struct mov_t* mov)
{
int i;
uint64_t mfra_offset;
uint64_t mfro_offset;
// mfra
mfra_offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 0); /* size */
mov_buffer_write(&mov->io, "mfra", 4);
// tfra
for (i = 0; i < mov->track_count; i++)
{
mov->track = mov->tracks + i;
mov_write_tfra(mov);
}
// mfro
mfro_offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 16); /* size */
mov_buffer_write(&mov->io, "mfro", 4);
mov_buffer_w32(&mov->io, 0); /* version & flags */
mov_buffer_w32(&mov->io, (uint32_t)(mfro_offset - mfra_offset + 16));
mov_write_size(mov, mfra_offset, (size_t)(mfro_offset - mfra_offset + 16));
return (int)(mfro_offset - mfra_offset + 16);
}
static int fmp4_add_fragment_entry(struct mov_track_t* track, uint64_t time, uint64_t offset)
{
if (track->frag_count >= track->frag_capacity)
{
void* p = realloc(track->frags, sizeof(struct mov_fragment_t) * (track->frag_capacity + 64));
if (!p) return -ENOMEM;
track->frags = p;
track->frag_capacity += 64;
}
track->frags[track->frag_count].time = time;
track->frags[track->frag_count].offset = offset;
++track->frag_count;
return 0;
}
static int fmp4_write_fragment(struct fmp4_writer_t* writer)
{
int i;
size_t n;
size_t refsize;
struct mov_t* mov;
mov = &writer->mov;
if (writer->mdat_size < 1)
return 0; // empty
// write moov
if (mov->flags & MOV_FLAG_SEGMENT)
{
// write stype
mov_write_styp(mov);
// ISO/IEC 23009-1:2014(E) 6.3.4.2 General format type (p93)
// Each Media Segment may contain one or more 'sidx' boxes.
// If present, the first 'sidx' box shall be placed before any 'moof' box
// and the first Segment Index box shall document the entire Segment.
fmp4_write_sidx(mov);
}
else if (!writer->has_moov)
{
mov_write_ftyp(mov);
fmp4_write_app(mov);
fmp4_write_moov(mov);
writer->has_moov = 1;
}
// moof
mov->moof_offset = mov_buffer_tell(&mov->io);
refsize = fmp4_write_moof(mov, ++writer->fragment_id, 0); // start from 1
// rewrite moof with trun data offset
mov_buffer_seek(&mov->io, mov->moof_offset);
fmp4_write_moof(mov, writer->fragment_id, (uint32_t)refsize+8);
refsize += writer->mdat_size + 8/*mdat box*/;
// add mfra entry
for (i = 0; i < mov->track_count; i++)
{
mov->track = mov->tracks + i;
if (mov->track->sample_count > 0 && 0 == (mov->flags & MOV_FLAG_SEGMENT))
fmp4_add_fragment_entry(mov->track, mov->track->samples[0].dts, mov->moof_offset);
// hack: write sidx referenced_size
if (mov->flags & MOV_FLAG_SEGMENT)
mov_write_size(mov, mov->moof_offset - 52 * (uint64_t)(mov->track_count - i) + 40, (0 << 31) | (refsize & 0x7fffffff));
mov->track->offset = 0; // reset
}
// mdat
if (writer->mdat_size + 8 <= UINT32_MAX)
{
mov_buffer_w32(&mov->io, (uint32_t)writer->mdat_size + 8); /* size */
mov_buffer_write(&mov->io, "mdat", 4);
}
else
{
mov_buffer_w32(&mov->io, 1);
mov_buffer_write(&mov->io, "mdat", 4);
mov_buffer_w64(&mov->io, writer->mdat_size + 16);
}
// interleave write samples
n = 0;
while(n < writer->mdat_size)
{
for (i = 0; i < mov->track_count; i++)
{
mov->track = mov->tracks + i;
while (mov->track->offset < mov->track->sample_count && n == mov->track->samples[mov->track->offset].offset)
{
mov_buffer_write(&mov->io, mov->track->samples[mov->track->offset].data, mov->track->samples[mov->track->offset].bytes);
free(mov->track->samples[mov->track->offset].data); // free av packet memory
n += mov->track->samples[mov->track->offset].bytes;
++mov->track->offset;
}
}
}
// clear track samples(don't free samples memory)
for (i = 0; i < mov->track_count; i++)
{
mov->tracks[i].sample_count = 0;
mov->tracks[i].offset = 0;
}
writer->mdat_size = 0;
return mov_buffer_error(&mov->io);
}
static int fmp4_writer_init(struct mov_t* mov)
{
if (mov->flags & MOV_FLAG_SEGMENT)
{
mov->ftyp.major_brand = MOV_BRAND_MSDH;
mov->ftyp.minor_version = 0;
mov->ftyp.brands_count = 6;
mov->ftyp.compatible_brands[0] = MOV_BRAND_ISOM;
mov->ftyp.compatible_brands[1] = MOV_BRAND_MP42;
mov->ftyp.compatible_brands[2] = MOV_BRAND_MSDH;
mov->ftyp.compatible_brands[3] = MOV_BRAND_MSIX;
mov->ftyp.compatible_brands[4] = MOV_BRAND_ISO5; // default<6C>\base<73>\is<69>\moof flag
mov->ftyp.compatible_brands[5] = MOV_BRAND_ISO6; // styp
mov->header = 0;
}
else
{
mov->ftyp.major_brand = MOV_BRAND_ISOM;
mov->ftyp.minor_version = 1;
mov->ftyp.brands_count = 5;
mov->ftyp.compatible_brands[0] = MOV_BRAND_ISOM;
mov->ftyp.compatible_brands[1] = MOV_BRAND_MP42;
mov->ftyp.compatible_brands[2] = MOV_BRAND_AVC1;
mov->ftyp.compatible_brands[3] = MOV_BRAND_DASH;
mov->ftyp.compatible_brands[4] = MOV_BRAND_ISO5; // default<6C>\base<73>\is<69>\moof flag
mov->header = 0;
}
return 0;
}
struct fmp4_writer_t* fmp4_writer_create(const struct mov_buffer_t *buffer, void* param, int flags)
{
struct mov_t* mov;
struct fmp4_writer_t* writer;
writer = (struct fmp4_writer_t*)calloc(1, sizeof(struct fmp4_writer_t));
if (NULL == writer)
return NULL;
writer->frag_interleave = 5;
mov = &writer->mov;
mov->flags = flags;
mov->mvhd.next_track_ID = 1;
mov->mvhd.creation_time = time(NULL) + 0x7C25B080; // 1970 based -> 1904 based;
mov->mvhd.modification_time = mov->mvhd.creation_time;
mov->mvhd.timescale = 1000;
mov->mvhd.duration = 0; // placeholder
fmp4_writer_init(mov);
mov->io.param = param;
memcpy(&mov->io.io, buffer, sizeof(mov->io.io));
return writer;
}
void fmp4_writer_destroy(struct fmp4_writer_t* writer)
{
int i;
struct mov_t* mov;
mov = &writer->mov;
fmp4_writer_save_segment(writer);
// write mfra
if (0 == (mov->flags & MOV_FLAG_SEGMENT))
{
fmp4_write_mfra(mov);
for (i = 0; i < mov->track_count; i++)
mov->tracks[i].frag_count = 0; // don't free frags memory
}
// mov_buffer_error(&mov->io);
for (i = 0; i < mov->track_count; i++)
mov_free_track(mov->tracks + i);
if (mov->tracks)
free(mov->tracks);
free(writer);
}
int fmp4_writer_write(struct fmp4_writer_t* writer, int idx, const void* data, size_t bytes, int64_t pts, int64_t dts, int flags)
{
int64_t duration;
struct mov_track_t* track;
struct mov_sample_t* sample;
if (idx < 0 || idx >= (int)writer->mov.track_count)
return -ENOENT;
track = &writer->mov.tracks[idx];
duration = dts > track->last_dts && INT64_MIN != track->last_dts ? dts - track->last_dts : 0;
#if 1
track->turn_last_duration = duration;
#else
track->turn_last_duration = track->turn_last_duration > 0 ? track->turn_last_duration * 7 / 8 + duration / 8 : duration;
#endif
// 1. force segment or
// 2. video key frame
if (0 == (flags & MOV_AV_FLAG_SEGMENT_DISABLE) && (0 != (flags & MOV_AV_FLAG_SEGMENT_FORCE) || (MOV_VIDEO == track->handler_type && (flags & MOV_AV_FLAG_KEYFREAME))) )
fmp4_write_fragment(writer); // fragment per video keyframe
if (track->sample_count + 1 >= track->sample_offset)
{
void* ptr = realloc(track->samples, sizeof(struct mov_sample_t) * (track->sample_offset + 1024));
if (NULL == ptr) return -ENOMEM;
track->samples = (struct mov_sample_t*)ptr;
track->sample_offset += 1024;
}
pts = pts * track->mdhd.timescale / 1000;
dts = dts * track->mdhd.timescale / 1000;
sample = &track->samples[track->sample_count];
sample->sample_description_index = 1;
sample->bytes = (uint32_t)bytes;
sample->flags = flags;
sample->pts = pts;
sample->dts = dts;
sample->offset = writer->mdat_size;
sample->data = malloc(bytes);
if (NULL == sample->data)
return -ENOMEM;
memcpy(sample->data, data, bytes);
if (INT64_MIN == track->start_dts)
track->start_dts = sample->dts;
writer->mdat_size += bytes; // update media data size
track->sample_count += 1;
track->last_dts = sample->dts;
return mov_buffer_error(&writer->mov.io);
}
int fmp4_writer_add_audio(struct fmp4_writer_t* writer, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size)
{
struct mov_t* mov;
struct mov_track_t* track;
mov = &writer->mov;
track = mov_add_track(mov);
if (NULL == track)
return -ENOMEM;
if (0 != mov_add_audio(track, &mov->mvhd, 1000, object, channel_count, bits_per_sample, sample_rate, extra_data, extra_data_size))
return -ENOMEM;
mov->mvhd.next_track_ID++;
return mov->track_count++;
}
int fmp4_writer_add_video(struct fmp4_writer_t* writer, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size)
{
struct mov_t* mov;
struct mov_track_t* track;
mov = &writer->mov;
track = mov_add_track(mov);
if (NULL == track)
return -ENOMEM;
if (0 != mov_add_video(track, &mov->mvhd, 1000, object, width, height, extra_data, extra_data_size))
return -ENOMEM;
mov->mvhd.next_track_ID++;
return mov->track_count++;
}
int fmp4_writer_add_subtitle(struct fmp4_writer_t* writer, uint8_t object, const void* extra_data, size_t extra_data_size)
{
struct mov_t* mov;
struct mov_track_t* track;
mov = &writer->mov;
track = mov_add_track(mov);
if (NULL == track)
return -ENOMEM;
if (0 != mov_add_subtitle(track, &mov->mvhd, 1000, object, extra_data, extra_data_size))
return -ENOMEM;
mov->mvhd.next_track_ID++;
return mov->track_count++;
}
int fmp4_writer_add_udta(fmp4_writer_t* writer, const void* data, size_t size)
{
writer->mov.udta = data;
writer->mov.udta_size = size;
return 0;
}
int fmp4_writer_save_segment(fmp4_writer_t* writer)
{
//int i;
//struct mov_t* mov;
//mov = &writer->mov;
// flush fragment
return fmp4_write_fragment(writer);
//// write mfra
//if (0 == (mov->flags & MOV_FLAG_SEGMENT))
//{
// fmp4_write_mfra(mov);
// for (i = 0; i < mov->track_count; i++)
// mov->tracks[i].frag_count = 0; // don't free frags memory
//}
//return mov_buffer_error(&mov->io);
}
int fmp4_writer_init_segment(fmp4_writer_t* writer)
{
struct mov_t* mov;
mov = &writer->mov;
mov_write_ftyp(mov);
fmp4_write_moov(mov);
writer->has_moov = 1;
return mov_buffer_error(&mov->io);
}