Older/MediaServer/libmov/source/mov-track.c

386 lines
12 KiB
C
Raw Normal View History

2024-10-01 00:12:57 +08:00
#include "mov-internal.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define FREE(p) do { if(p) free(p); } while(0)
struct mov_track_t* mov_add_track(struct mov_t* mov)
{
void* ptr = NULL;
struct mov_track_t* track;
ptr = realloc(mov->tracks, sizeof(struct mov_track_t) * (mov->track_count + 1));
if (NULL == ptr) return NULL;
mov->tracks = ptr;
track = &mov->tracks[mov->track_count];
memset(track, 0, sizeof(struct mov_track_t));
track->start_dts = INT64_MIN;
track->last_dts = INT64_MIN;
track->stsd.entries = calloc(1, sizeof(struct mov_sample_entry_t));
if (NULL == track->stsd.entries)
return NULL;
track->stsd.current = track->stsd.entries;
return track;
}
void mov_free_track(struct mov_track_t* track)
{
size_t i;
for (i = 0; i < track->sample_count; i++)
{
if (track->samples[i].data)
free(track->samples[i].data);
}
for (i = 0; i < track->stsd.entry_count; i++)
{
if (track->stsd.entries[i].extra_data)
free(track->stsd.entries[i].extra_data);
}
FREE(track->elst);
FREE(track->frags);
FREE(track->samples);
// FREE(track->extra_data);
FREE(track->stsd.entries);
FREE(track->stbl.stco);
FREE(track->stbl.stsc);
FREE(track->stbl.stss);
FREE(track->stbl.stts);
FREE(track->stbl.ctts);
}
struct mov_track_t* mov_find_track(const struct mov_t* mov, uint32_t track)
{
int i;
for (i = 0; i < mov->track_count; i++)
{
if (mov->tracks[i].tkhd.track_ID == track)
return mov->tracks + i;
}
return NULL;
}
struct mov_track_t* mov_fetch_track(struct mov_t* mov, uint32_t track)
{
struct mov_track_t* t;
t = mov_find_track(mov, track);
if (NULL == t)
{
t = mov_add_track(mov);
if (NULL != t)
{
++mov->track_count;
t->tkhd.track_ID = track;
}
}
return t;
}
int mov_add_audio(struct mov_track_t* track, const struct mov_mvhd_t* mvhd, uint32_t timescale, uint8_t object, int channel_count, int bits_per_sample, int sample_rate, const void* extra_data, size_t extra_data_size)
{
struct mov_sample_entry_t* audio;
if (MOV_OBJECT_MP3 == object && sample_rate > 24000)
object = MOV_OBJECT_MP1A; // use mpeg1 sample rate table, see more @libflv/source/mp3-header.c
audio = &track->stsd.entries[0];
audio->data_reference_index = 1;
audio->object_type_indication = object;
audio->stream_type = MP4_STREAM_AUDIO;
audio->u.audio.channelcount = (uint16_t)channel_count;
audio->u.audio.samplesize = (uint16_t)bits_per_sample;
audio->u.audio.samplerate = ((uint32_t)(sample_rate > 56635 ? 0 : sample_rate)) << 16;
assert(0 != mov_object_to_tag(object));
track->tag = mov_object_to_tag(object);
track->handler_type = MOV_AUDIO;
track->handler_descr = "SoundHandler";
track->stsd.entry_count = 1;
track->offset = 0;
track->tkhd.flags = MOV_TKHD_FLAG_TRACK_ENABLE | MOV_TKHD_FLAG_TRACK_IN_MOVIE;
track->tkhd.track_ID = mvhd->next_track_ID;
track->tkhd.creation_time = mvhd->creation_time;
track->tkhd.modification_time = mvhd->modification_time;
track->tkhd.width = 0;
track->tkhd.height = 0;
track->tkhd.volume = 0x0100;
track->tkhd.duration = 0; // placeholder
track->mdhd.creation_time = track->tkhd.creation_time;
track->mdhd.modification_time = track->tkhd.modification_time;
track->mdhd.timescale = timescale; //sample_rate
track->mdhd.language = 0x55c4;
track->mdhd.duration = 0; // placeholder
audio->extra_data = malloc(extra_data_size + 1);
if (NULL == audio->extra_data)
return -ENOMEM;
memcpy(audio->extra_data, extra_data, extra_data_size);
audio->extra_data_size = (int)extra_data_size;
return 0;
}
int mov_add_video(struct mov_track_t* track, const struct mov_mvhd_t* mvhd, uint32_t timescale, uint8_t object, int width, int height, const void* extra_data, size_t extra_data_size)
{
struct mov_sample_entry_t* video;
video = &track->stsd.entries[0];
video->data_reference_index = 1;
video->object_type_indication = object;
video->stream_type = MP4_STREAM_VISUAL;
video->u.visual.width = (uint16_t)width;
video->u.visual.height = (uint16_t)height;
video->u.visual.depth = 0x0018;
video->u.visual.frame_count = 1;
video->u.visual.horizresolution = 0x00480000;
video->u.visual.vertresolution = 0x00480000;
assert(0 != mov_object_to_tag(object));
track->tag = mov_object_to_tag(object);
track->handler_type = MOV_VIDEO;
track->handler_descr = "VideoHandler";
track->stsd.entry_count = 1;
track->offset = 0;
track->tkhd.flags = MOV_TKHD_FLAG_TRACK_ENABLE | MOV_TKHD_FLAG_TRACK_IN_MOVIE;
track->tkhd.track_ID = mvhd->next_track_ID;
track->tkhd.creation_time = mvhd->creation_time;
track->tkhd.modification_time = mvhd->modification_time;
track->tkhd.width = width << 16;
track->tkhd.height = height << 16;
track->tkhd.volume = 0;
track->tkhd.duration = 0; // placeholder
track->mdhd.creation_time = track->tkhd.creation_time;
track->mdhd.modification_time = track->tkhd.modification_time;
track->mdhd.timescale = timescale; //mov->mvhd.timescale
track->mdhd.language = 0x55c4;
track->mdhd.duration = 0; // placeholder
video->extra_data = malloc(extra_data_size + 1);
if (NULL == video->extra_data)
return -ENOMEM;
memcpy(video->extra_data, extra_data, extra_data_size);
video->extra_data_size = (int)extra_data_size;
return 0;
}
int mov_add_subtitle(struct mov_track_t* track, const struct mov_mvhd_t* mvhd, uint32_t timescale, uint8_t object, const void* extra_data, size_t extra_data_size)
{
static const uint8_t chapter_extra_data[] = {
// TextSampleEntry
0x00, 0x00, 0x00, 0x01, // displayFlags
0x00, 0x00, // horizontal + vertical justification
0x00, 0x00, 0x00, 0x00, // bgColourRed/Green/Blue/Alpha
// BoxRecord
0x00, 0x00, 0x00, 0x00, // defTextBoxTop/Left
0x00, 0x00, 0x00, 0x00, // defTextBoxBottom/Right
// StyleRecord
0x00, 0x00, 0x00, 0x00, // startChar + endChar
0x00, 0x01, // fontID
0x00, 0x00, // fontStyleFlags + fontSize
0x00, 0x00, 0x00, 0x00, // fgColourRed/Green/Blue/Alpha
// FontTableBox
0x00, 0x00, 0x00, 0x0D, // box size
'f', 't', 'a', 'b', // box atom name
0x00, 0x01, // entry count
// FontRecord
0x00, 0x01, // font ID
0x00, // font name length
};
struct mov_sample_entry_t* subtitle;
subtitle = &track->stsd.entries[0];
subtitle->data_reference_index = 1;
subtitle->object_type_indication = object;
subtitle->stream_type = MP4_STREAM_VISUAL; // Visually composed tracks including video and text are layered using the 'layer' value.
assert(0 != mov_object_to_tag(object));
track->tag = mov_object_to_tag(object);
track->handler_type = track->tag == MOV_TAG('t', 'e', 'x', 't') ? MOV_TEXT : MOV_SBTL;
track->handler_descr = "SubtitleHandler";
track->stsd.entry_count = 1;
track->offset = 0;
track->tkhd.flags = (track->tag == MOV_TAG('t', 'e', 'x', 't') ? 0 : MOV_TKHD_FLAG_TRACK_ENABLE) | MOV_TKHD_FLAG_TRACK_IN_MOVIE;
track->tkhd.track_ID = mvhd->next_track_ID;
track->tkhd.creation_time = mvhd->creation_time;
track->tkhd.modification_time = mvhd->modification_time;
track->tkhd.width = 0;
track->tkhd.height = 0;
track->tkhd.volume = 0;
track->tkhd.duration = 0; // placeholder
track->mdhd.creation_time = track->tkhd.creation_time;
track->mdhd.modification_time = track->tkhd.modification_time;
track->mdhd.timescale = timescale;
track->mdhd.language = 0x55c4;
track->mdhd.duration = 0; // placeholder
if (object == MOV_OBJECT_CHAPTER && 0 == extra_data_size)
{
extra_data = chapter_extra_data;
extra_data_size = sizeof(chapter_extra_data);
}
subtitle->extra_data = malloc(extra_data_size + 1);
if (NULL == subtitle->extra_data)
return -ENOMEM;
memcpy(subtitle->extra_data, extra_data, extra_data_size);
subtitle->extra_data_size = (int)extra_data_size;
return 0;
}
// ISO/IEC 14496-12:2012(E) 6.2.3 Box Order (p23)
// It is recommended that the boxes within the Sample Table Box be in the following order:
// Sample Description, Time to Sample, Sample to Chunk, Sample Size, Chunk Offset.
size_t mov_write_stbl(const struct mov_t* mov)
{
size_t size;
uint32_t count;
uint64_t offset;
struct mov_track_t* track;
track = (struct mov_track_t*)mov->track;
size = 8 /* Box */;
offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 0); /* size */
mov_buffer_write(&mov->io, "stbl", 4);
size += mov_write_stsd(mov);
count = mov_build_stts(track);
size += mov_write_stts(mov, count);
if (track->tkhd.width > 0 && track->tkhd.height > 0)
size += mov_write_stss(mov); // video only
count = mov_build_ctts(track);
if (track->sample_count > 0 && (count > 1 || track->samples[0].samples_per_chunk != 0))
size += mov_write_ctts(mov, count);
count = mov_build_stco(track);
size += mov_write_stsc(mov);
size += mov_write_stsz(mov);
size += mov_write_stco(mov, count);
mov_write_size(mov, offset, size); /* update size */
return size;
}
size_t mov_write_minf(const struct mov_t* mov)
{
size_t size;
uint64_t offset;
const struct mov_track_t* track = mov->track;
size = 8 /* Box */;
offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 0); /* size */
mov_buffer_write(&mov->io, "minf", 4);
if (MOV_VIDEO == track->handler_type)
{
size += mov_write_vmhd(mov);
}
else if (MOV_AUDIO == track->handler_type)
{
size += mov_write_smhd(mov);
}
else if (MOV_TEXT == track->handler_type)
{
size += mov_write_gmhd(mov);
}
else if (MOV_SUBT == track->handler_type || MOV_SBTL == track->handler_type)
{
size += mov_write_nmhd(mov);
}
else
{
assert(0);
}
size += mov_write_dinf(mov);
size += mov_write_stbl(mov);
mov_write_size(mov, offset, size); /* update size */
return size;
}
size_t mov_write_mdia(const struct mov_t* mov)
{
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, "mdia", 4);
size += mov_write_mdhd(mov);
size += mov_write_hdlr(mov);
size += mov_write_minf(mov);
mov_write_size(mov, offset, size); /* update size */
return size;
}
size_t mov_write_trak(const struct mov_t* mov)
{
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, "trak", 4);
size += mov_write_tkhd(mov);
size += mov_write_edts(mov);
if(mov->track->chpl_track != 0)
size += mov_write_tref(mov);
size += mov_write_mdia(mov);
mov_write_size(mov, offset, size); /* update size */
return size;
}
size_t mov_write_edts(const struct mov_t* mov)
{
size_t size;
uint64_t offset;
if (mov->track->sample_count < 1)
return 0;
size = 8 /* Box */;
offset = mov_buffer_tell(&mov->io);
mov_buffer_w32(&mov->io, 0); /* size */
mov_buffer_write(&mov->io, "edts", 4);
size += mov_write_elst(mov);
mov_write_size(mov, offset, size); /* update size */
return size;
}
size_t mov_write_tref(const struct mov_t* mov)
{
mov_buffer_w32(&mov->io, 20); /* size */
mov_buffer_write(&mov->io, "tref", 4);
mov_buffer_w32(&mov->io, 12); /* size */
mov_buffer_write(&mov->io, "chap", 4);
mov_buffer_w32(&mov->io, mov->track->chpl_track);
return 20;
}