conf->subtitle_format == SUBTITLE_FORMAT_SMPTE_TT)
+ {
+ reference_track = (*adaptation_set->first) + filtered_clip_offset;
+ p = vod_sprintf(p, VOD_DASH_MANIFEST_ADAPTATION_HEADER_SUBTITLE_SMPTE_TT,
+ lang_get_rfc_5646_name(reference_track->media_info.language),
+ &reference_track->media_info.label);
+ break;
+ }
+
cur_track = (*adaptation_set->first) + filtered_clip_offset;
cur_sequence = cur_track->file_info.source->sequence;
@@ -837,7 +865,7 @@ dash_packager_write_mpd_period(
}
lang_code = lang_get_rfc_5646_name(cur_track->media_info.language);
- p = vod_sprintf(p, VOD_DASH_MANIFEST_ADAPTATION_SUBTITLE,
+ p = vod_sprintf(p, VOD_DASH_MANIFEST_ADAPTATION_SUBTITLE_VTT,
lang_code,
&cur_track->media_info.label,
lang_code,
@@ -951,6 +979,17 @@ dash_packager_write_mpd_period(
cur_track->media_info.u.audio.sample_rate,
cur_track->media_info.bitrate);
break;
+
+ case MEDIA_TYPE_SUBTITLE:
+ if (representation_id.len > 0 && representation_id.data[representation_id.len - 1] == '-')
+ {
+ representation_id.len--;
+ }
+
+ p = vod_sprintf(p,
+ VOD_DASH_MANIFEST_REPRESENTATION_HEADER_SUBTITLE_SMPTE_TT,
+ &representation_id);
+ break;
}
if (context->conf->manifest_format == FORMAT_SEGMENT_LIST)
@@ -998,6 +1037,7 @@ dash_packager_get_segment_list_total_size(
media_track_t* last_track;
media_track_t* cur_track;
uint32_t filtered_clip_offset;
+ uint32_t max_media_type;
uint32_t period_count = media_set->use_discontinuity ? media_set->timing.total_count : 1;
uint32_t segment_count;
uint32_t clip_index;
@@ -1005,7 +1045,18 @@ dash_packager_get_segment_list_total_size(
size_t base_url_len = 0;
size_t result = 0;
- for (media_type = 0; media_type < MEDIA_TYPE_SUBTITLE; media_type++)
+ switch (conf->subtitle_format)
+ {
+ case SUBTITLE_FORMAT_WEBVTT:
+ max_media_type = MEDIA_TYPE_SUBTITLE;
+ break;
+
+ default: // SUBTITLE_FORMAT_SMPTE_TT
+ max_media_type = MEDIA_TYPE_COUNT;
+ break;
+ }
+
+ for (media_type = 0; media_type < max_media_type; media_type++)
{
if (media_set->track_count[media_type] == 0)
{
@@ -1109,7 +1160,7 @@ dash_packager_remove_redundant_tracks(
}
// remove the track from all clips
media_set->track_count[remove->media_info.media_type]--;
-
+
for (clip_index = 0; clip_index < media_set->clip_count; clip_index++)
{
remove[clip_index * media_set->total_track_count].media_info.media_type = MEDIA_TYPE_NONE;
@@ -1196,8 +1247,10 @@ dash_packager_build_mpd(
uint32_t filtered_clip_offset;
uint32_t presentation_delay;
uint32_t min_update_period;
- uint32_t window_size;
+ uint32_t adaptation_count;
+ uint32_t max_media_type;
uint32_t period_count = media_set->use_discontinuity ? media_set->timing.total_count : 1;
+ uint32_t window_size;
uint32_t media_type;
uint32_t clip_index;
vod_status_t rc;
@@ -1279,14 +1332,31 @@ dash_packager_build_mpd(
// audio representations
(sizeof(VOD_DASH_MANIFEST_REPRESENTATION_HEADER_AUDIO) - 1 + MAX_TRACK_SPEC_LENGTH + MAX_MIME_TYPE_SIZE + MAX_CODEC_NAME_SIZE + 2 * VOD_INT32_LEN +
sizeof(VOD_DASH_MANIFEST_REPRESENTATION_FOOTER) - 1) * media_set->track_count[MEDIA_TYPE_AUDIO] +
- // subtitle adaptations
- (sizeof(VOD_DASH_MANIFEST_ADAPTATION_SUBTITLE) - 1 + 2 * LANG_ISO639_3_LEN + VOD_INT32_LEN +
- context.base_url.len + conf->subtitle_file_name_prefix.len + MAX_CLIP_SPEC_LENGTH + MAX_TRACK_SPEC_LENGTH) *
- context.adaptation_sets.count[ADAPTATION_TYPE_SUBTITLE] +
sizeof(VOD_DASH_MANIFEST_PERIOD_FOOTER) - 1 +
extensions->representation.size +
extensions->adaptation_set.size;
+ switch (conf->subtitle_format)
+ {
+ case SUBTITLE_FORMAT_WEBVTT:
+ base_period_size +=
+ // subtitle adaptations
+ (sizeof(VOD_DASH_MANIFEST_ADAPTATION_SUBTITLE_VTT) - 1 + 2 * LANG_ISO639_3_LEN + VOD_INT32_LEN +
+ context.base_url.len + conf->subtitle_file_name_prefix.len + MAX_CLIP_SPEC_LENGTH + MAX_TRACK_SPEC_LENGTH) *
+ context.adaptation_sets.count[ADAPTATION_TYPE_SUBTITLE];
+ break;
+
+ default: // SUBTITLE_FORMAT_SMPTE_TT
+ base_period_size +=
+ // subtitle adaptations
+ (sizeof(VOD_DASH_MANIFEST_ADAPTATION_HEADER_SUBTITLE_SMPTE_TT) - 1 + LANG_ISO639_3_LEN +
+ sizeof(VOD_DASH_MANIFEST_ADAPTATION_FOOTER) - 1) * context.adaptation_sets.count[ADAPTATION_TYPE_SUBTITLE] +
+ // subtitle representations
+ (sizeof(VOD_DASH_MANIFEST_REPRESENTATION_HEADER_SUBTITLE_SMPTE_TT) - 1 + MAX_TRACK_SPEC_LENGTH +
+ sizeof(VOD_DASH_MANIFEST_REPRESENTATION_FOOTER) - 1) * media_set->track_count[MEDIA_TYPE_SUBTITLE];
+ break;
+ }
+
switch (media_set->type)
{
case MEDIA_SET_VOD:
@@ -1322,15 +1392,37 @@ dash_packager_build_mpd(
switch (conf->manifest_format)
{
case FORMAT_SEGMENT_TEMPLATE:
+
+ switch (conf->subtitle_format)
+ {
+ case SUBTITLE_FORMAT_WEBVTT:
+ adaptation_count = context.adaptation_sets.count[MEDIA_TYPE_VIDEO] + context.adaptation_sets.count[MEDIA_TYPE_AUDIO];
+ break;
+
+ default: // SUBTITLE_FORMAT_SMPTE_TT
+ adaptation_count = context.adaptation_sets.total_count;
+ break;
+ }
+
result_size +=
(sizeof(VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FIXED) - 1 + VOD_INT32_LEN + VOD_INT64_LEN +
- MAX_INDEX_SHIFT_LENGTH + urls_length) *
- (context.adaptation_sets.count[MEDIA_TYPE_VIDEO] + context.adaptation_sets.count[MEDIA_TYPE_AUDIO]) *
- period_count;
+ MAX_INDEX_SHIFT_LENGTH + urls_length) * adaptation_count * period_count;
break;
case FORMAT_SEGMENT_TIMELINE:
- for (media_type = 0; media_type < MEDIA_TYPE_SUBTITLE; media_type++)
+
+ switch (conf->subtitle_format)
+ {
+ case SUBTITLE_FORMAT_WEBVTT:
+ max_media_type = MEDIA_TYPE_SUBTITLE;
+ break;
+
+ default: // SUBTITLE_FORMAT_SMPTE_TT
+ max_media_type = MEDIA_TYPE_COUNT;
+ break;
+ }
+
+ for (media_type = 0; media_type < max_media_type; media_type++)
{
if (context.adaptation_sets.count[media_type] == 0)
{
@@ -1499,7 +1591,7 @@ dash_packager_get_earliest_pres_time(media_set_t* media_set, media_track_t* trac
{
uint64_t result;
uint64_t clip_start_time;
-
+
if (media_set->use_discontinuity)
{
clip_start_time = media_set->timing.original_first_time;
@@ -1582,6 +1674,14 @@ dash_packager_init_sidx_params(media_set_t* media_set, media_sequence_t* sequenc
uint64_t total_frames_duration;
bool_t frame_found = FALSE;
+ if (sequence->media_type == MEDIA_TYPE_SUBTITLE)
+ {
+ result->timescale = DASH_TIMESCALE;
+ result->earliest_pres_time = rescale_time(media_set->segment_start_time, 1000, DASH_TIMESCALE);
+ result->total_frames_duration = rescale_time(media_set->segment_duration, 1000, DASH_TIMESCALE);
+ return;
+ }
+
// initialize according to the first clip
cur_clip = sequence->filtered_clips;
track = cur_clip->first_track;
@@ -1620,6 +1720,7 @@ dash_packager_build_fragment_header(
media_sequence_t* sequence = &media_set->sequences[0];
media_track_t* first_track = sequence->filtered_clips[0].first_track;
sidx_params_t sidx_params;
+ uint32_t duration;
size_t first_frame_offset;
size_t mdat_atom_size;
size_t trun_atom_size;
@@ -1627,7 +1728,9 @@ dash_packager_build_fragment_header(
size_t moof_atom_size;
size_t traf_atom_size;
size_t result_size;
+ u_char* mdat_start;
u_char* p;
+ u_char* sample_size;
// calculate sizes
dash_packager_init_sidx_params(media_set, sequence, &sidx_params);
@@ -1645,21 +1748,21 @@ dash_packager_build_fragment_header(
ATOM_HEADER_SIZE +
tfhd_atom_size +
ATOM_HEADER_SIZE + (sidx_params.earliest_pres_time > UINT_MAX ? sizeof(tfdt64_atom_t) : sizeof(tfdt_atom_t)) +
- trun_atom_size +
+ trun_atom_size +
extensions->extra_traf_atoms_size;
moof_atom_size =
ATOM_HEADER_SIZE +
- ATOM_HEADER_SIZE + sizeof(mfhd_atom_t)+
+ ATOM_HEADER_SIZE + sizeof(mfhd_atom_t) +
traf_atom_size;
- *total_fragment_size =
+ *total_fragment_size =
(media_set->version >= 2 ? sizeof(styp_atom_v2) : sizeof(styp_atom)) +
ATOM_HEADER_SIZE + (sidx_params.earliest_pres_time > UINT_MAX ? sizeof(sidx64_atom_t) : sizeof(sidx_atom_t)) +
moof_atom_size +
mdat_atom_size;
- result_size = *total_fragment_size - sequence->total_frame_size;
+ result_size = *total_fragment_size - sequence->total_frame_size + extensions->mdat_atom_max_size;
// head request optimization
if (size_only)
@@ -1723,11 +1826,23 @@ dash_packager_build_fragment_header(
// moof.traf.trun
first_frame_offset = moof_atom_size + ATOM_HEADER_SIZE;
- p = mp4_fragment_write_trun_atom(
- p,
- sequence,
- first_frame_offset,
- media_set->version >= 2 ? 1 : 0);
+ sample_size = NULL;
+
+ switch (sequence->media_type)
+ {
+ case MEDIA_TYPE_VIDEO:
+ p = mp4_fragment_write_video_trun_atom(p, sequence, first_frame_offset, media_set->version >= 2 ? 1 : 0);
+ break;
+
+ case MEDIA_TYPE_AUDIO:
+ p = mp4_fragment_write_audio_trun_atom(p, sequence, first_frame_offset);
+ break;
+
+ case MEDIA_TYPE_SUBTITLE:
+ duration = rescale_time(media_set->segment_duration, 1000, DASH_TIMESCALE);
+ p = mp4_fragment_write_subtitle_trun_atom(p, first_frame_offset, duration, &sample_size);
+ break;
+ }
// moof.traf.xxx
if (extensions->write_extra_traf_atoms_callback != NULL)
@@ -1736,11 +1851,24 @@ dash_packager_build_fragment_header(
}
// mdat
+ mdat_start = p;
write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't');
+ if (extensions->write_mdat_atom_callback != NULL)
+ {
+ p = extensions->write_mdat_atom_callback(extensions->write_mdat_atom_context, p);
+ mdat_atom_size = p - mdat_start;
+ write_be32(mdat_start, mdat_atom_size);
+
+ if (sample_size != NULL)
+ {
+ write_be32(sample_size, mdat_atom_size - ATOM_HEADER_SIZE);
+ }
+ }
+
result->len = p - result->data;
- if (result->len != result_size)
+ if (result->len > result_size)
{
vod_log_error(VOD_LOG_ERR, request_context->log, 0,
"dash_packager_build_fragment_header: result length %uz exceeded allocated length %uz",
diff --git a/vod/dash/dash_packager.h b/vod/dash/dash_packager.h
index 6eae60eb..f36610c9 100644
--- a/vod/dash/dash_packager.h
+++ b/vod/dash/dash_packager.h
@@ -16,8 +16,15 @@ enum {
FORMAT_SEGMENT_TEMPLATE,
};
+enum {
+ SUBTITLE_FORMAT_WEBVTT,
+ SUBTITLE_FORMAT_SMPTE_TT,
+};
+
typedef u_char* (*dash_write_extra_traf_atoms_callback_t)(void* context, u_char* p, size_t mdat_atom_start);
+typedef u_char* (*dash_write_mdat_atom_callback_t)(void* context, u_char* p);
+
typedef u_char* (*write_tags_callback_t)(void* context, u_char* p, media_track_t* track);
typedef struct {
@@ -32,6 +39,7 @@ typedef struct {
vod_str_t fragment_file_name_prefix;
vod_str_t subtitle_file_name_prefix;
vod_uint_t manifest_format;
+ vod_uint_t subtitle_format;
vod_uint_t duplicate_bitrate_threshold;
bool_t write_playready_kid; // TODO: remove
bool_t use_base_url_tag; // TODO: remove - if supported by all devices, always use BaseURL
@@ -46,6 +54,10 @@ typedef struct {
size_t extra_traf_atoms_size;
dash_write_extra_traf_atoms_callback_t write_extra_traf_atoms_callback;
void* write_extra_traf_atoms_context;
+
+ size_t mdat_atom_max_size;
+ dash_write_mdat_atom_callback_t write_mdat_atom_callback;
+ void* write_mdat_atom_context;
} dash_fragment_header_extensions_t;
// functions
diff --git a/vod/dash/edash_packager.c b/vod/dash/edash_packager.c
index 410de808..2767ec9d 100644
--- a/vod/dash/edash_packager.c
+++ b/vod/dash/edash_packager.c
@@ -369,6 +369,8 @@ edash_packager_video_build_fragment_header(
dash_fragment_header_extensions_t header_extensions;
// get the header extensions
+ vod_memzero(&header_extensions, sizeof(header_extensions));
+
header_extensions.extra_traf_atoms_size =
state->base.saiz_atom_size +
state->base.saio_atom_size +
@@ -420,6 +422,8 @@ edash_packager_audio_build_fragment_header(
vod_status_t rc;
// get the header extensions
+ vod_memzero(&header_extensions, sizeof(header_extensions));
+
header_extensions.extra_traf_atoms_size =
state->saiz_atom_size +
state->saio_atom_size +
@@ -502,6 +506,8 @@ edash_packager_get_fragment_writer(
"edash_packager_get_fragment_writer: using encryption passthrough");
// get the header extensions
+ vod_memzero(&header_extensions, sizeof(header_extensions));
+
header_extensions.extra_traf_atoms_size = passthrough_context.total_size + ATOM_HEADER_SIZE + sizeof(senc_atom_t);
header_extensions.write_extra_traf_atoms_callback = edash_packager_passthrough_write_encryption_atoms;
header_extensions.write_extra_traf_atoms_context = &passthrough_context;
diff --git a/vod/mp4/mp4_fragment.c b/vod/mp4/mp4_fragment.c
index 31b5e2e9..73a095f7 100644
--- a/vod/mp4/mp4_fragment.c
+++ b/vod/mp4/mp4_fragment.c
@@ -72,14 +72,17 @@ mp4_fragment_get_trun_atom_size(uint32_t media_type, uint32_t frame_count)
case MEDIA_TYPE_AUDIO:
return ATOM_HEADER_SIZE + sizeof(trun_atom_t) + frame_count * sizeof(trun_audio_frame_t);
+
+ case MEDIA_TYPE_SUBTITLE:
+ return ATOM_HEADER_SIZE + sizeof(trun_atom_t) + sizeof(trun_audio_frame_t);
}
return 0;
}
-static u_char*
+u_char*
mp4_fragment_write_video_trun_atom(
- u_char* p,
- media_sequence_t* sequence,
+ u_char* p,
+ media_sequence_t* sequence,
uint32_t first_frame_offset,
uint32_t version)
{
@@ -139,8 +142,11 @@ mp4_fragment_write_video_trun_atom(
return p;
}
-static u_char*
-mp4_fragment_write_audio_trun_atom(u_char* p, media_sequence_t* sequence, uint32_t first_frame_offset)
+u_char*
+mp4_fragment_write_audio_trun_atom(
+ u_char* p,
+ media_sequence_t* sequence,
+ uint32_t first_frame_offset)
{
media_clip_filtered_t* cur_clip;
frame_list_part_t* part;
@@ -180,22 +186,24 @@ mp4_fragment_write_audio_trun_atom(u_char* p, media_sequence_t* sequence, uint32
}
u_char*
-mp4_fragment_write_trun_atom(
- u_char* p,
- media_sequence_t* sequence,
+mp4_fragment_write_subtitle_trun_atom(
+ u_char* p,
uint32_t first_frame_offset,
- uint32_t version)
+ uint32_t duration,
+ u_char** sample_size)
{
- switch (sequence->media_type)
- {
- case MEDIA_TYPE_VIDEO:
- p = mp4_fragment_write_video_trun_atom(p, sequence, first_frame_offset, version);
- break;
+ uint32_t atom_size;
+
+ atom_size = ATOM_HEADER_SIZE + sizeof(trun_atom_t) + sizeof(trun_audio_frame_t);
+ write_atom_header(p, atom_size, 't', 'r', 'u', 'n');
+ write_be32(p, TRUN_AUDIO_FLAGS); // flags = data offset, duration, size
+ write_be32(p, 1); // sample count = 1
+ write_be32(p, first_frame_offset);
+
+ write_be32(p, duration);
+ *sample_size = p;
+ write_be32(p, 0);
- case MEDIA_TYPE_AUDIO:
- p = mp4_fragment_write_audio_trun_atom(p, sequence, first_frame_offset);
- break;
- }
return p;
}
diff --git a/vod/mp4/mp4_fragment.h b/vod/mp4/mp4_fragment.h
index cc5bfae2..16cfb344 100644
--- a/vod/mp4/mp4_fragment.h
+++ b/vod/mp4/mp4_fragment.h
@@ -62,12 +62,24 @@ u_char* mp4_fragment_write_tfdt64_atom(u_char* p, uint64_t earliest_pres_time);
size_t mp4_fragment_get_trun_atom_size(uint32_t media_type, uint32_t frame_count);
-u_char* mp4_fragment_write_trun_atom(
- u_char* p,
- media_sequence_t* sequence,
+u_char* mp4_fragment_write_video_trun_atom(
+ u_char* p,
+ media_sequence_t* sequence,
uint32_t first_frame_offset,
uint32_t version);
+u_char* mp4_fragment_write_audio_trun_atom(
+ u_char* p,
+ media_sequence_t* sequence,
+ uint32_t first_frame_offset);
+
+u_char* mp4_fragment_write_subtitle_trun_atom(
+ u_char* p,
+ uint32_t first_frame_offset,
+ uint32_t duration,
+ u_char** sample_size);
+
+
vod_status_t mp4_fragment_frame_writer_init(
request_context_t* request_context,
media_sequence_t* sequence,
diff --git a/vod/mp4/mp4_init_segment.c b/vod/mp4/mp4_init_segment.c
index e9fe34eb..d3319f28 100644
--- a/vod/mp4/mp4_init_segment.c
+++ b/vod/mp4/mp4_init_segment.c
@@ -105,6 +105,19 @@ static const u_char hdlr_audio_atom[] = {
0x00
};
+static const u_char hdlr_subtitle_atom[] = {
+ 0x00, 0x00, 0x00, 0x25, // size
+ 0x68, 0x64, 0x6c, 0x72, // hdlr
+ 0x00, 0x00, 0x00, 0x00, // version + flags
+ 0x00, 0x00, 0x00, 0x00, // pre defined
+ 0x73, 0x75, 0x62, 0x74, // handler type = subt
+ 0x00, 0x00, 0x00, 0x00, // reserved1
+ 0x00, 0x00, 0x00, 0x00, // reserved2
+ 0x00, 0x00, 0x00, 0x00, // reserved3
+ 0x73, 0x75, 0x62, 0x74, // name = subt\0
+ 0x00
+};
+
static const u_char dinf_atom[] = {
0x00, 0x00, 0x00, 0x24, // atom size
0x64, 0x69, 0x6e, 0x66, // dinf
@@ -132,6 +145,79 @@ static const u_char smhd_atom[] = {
0x00, 0x00, 0x00, 0x00, // reserved
};
+static const u_char sthd_atom[] = {
+ 0x00, 0x00, 0x00, 0x0C, // atom size
+ 0x73, 0x74, 0x68, 0x64, // sthd
+ 0x00, 0x00, 0x00, 0x00, // version & flags
+};
+
+static const u_char smpte_tt_stsd_atom[] = {
+ 0x00, 0x00, 0x00, 0xff, // size
+ 0x73, 0x74, 0x73, 0x64, // stsd
+ 0x00, 0x00, 0x00, 0x00, // version & flags
+ 0x00, 0x00, 0x00, 0x01, // entries
+ 0x00, 0x00, 0x00, 0xef, // size
+ 0x73, 0x74, 0x70, 0x70, // stpp
+ 0x00, 0x00, 0x00, 0x00, // reserved
+ 0x00, 0x00, 0x00, 0x01, // reserved + data_reference_index
+ 0x68, 0x74, 0x74, 0x70, // namespace:
+ 0x3a, 0x2f, 0x2f, 0x77, // http://www.smpte-ra.org/schemas/2052-1/2013/smpte-tt
+ 0x77, 0x77, 0x2e, 0x73, // http://www.w3.org/ns/ttml
+ 0x6d, 0x70, 0x74, 0x65, // http://www.w3.org/ns/ttml#metadata
+ 0x2d, 0x72, 0x61, 0x2e, // http://www.w3.org/ns/ttml#parameter
+ 0x6f, 0x72, 0x67, 0x2f, // http://www.w3.org/ns/ttml#styling
+ 0x73, 0x63, 0x68, 0x65, // urn:ebu:tt:metadata
+ 0x6d, 0x61, 0x73, 0x2f, // urn:ebu:tt:style
+ 0x32, 0x30, 0x35, 0x32,
+ 0x2d, 0x31, 0x2f, 0x32,
+ 0x30, 0x31, 0x33, 0x2f,
+ 0x73, 0x6d, 0x70, 0x74,
+ 0x65, 0x2d, 0x74, 0x74,
+ 0x20, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e,
+ 0x77, 0x33, 0x2e, 0x6f,
+ 0x72, 0x67, 0x2f, 0x6e,
+ 0x73, 0x2f, 0x74, 0x74,
+ 0x6d, 0x6c, 0x20, 0x68,
+ 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x77, 0x33,
+ 0x2e, 0x6f, 0x72, 0x67,
+ 0x2f, 0x6e, 0x73, 0x2f,
+ 0x74, 0x74, 0x6d, 0x6c,
+ 0x23, 0x6d, 0x65, 0x74,
+ 0x61, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x77, 0x33, 0x2e,
+ 0x6f, 0x72, 0x67, 0x2f,
+ 0x6e, 0x73, 0x2f, 0x74,
+ 0x74, 0x6d, 0x6c, 0x23,
+ 0x70, 0x61, 0x72, 0x61,
+ 0x6d, 0x65, 0x74, 0x65,
+ 0x72, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x77, 0x33, 0x2e,
+ 0x6f, 0x72, 0x67, 0x2f,
+ 0x6e, 0x73, 0x2f, 0x74,
+ 0x74, 0x6d, 0x6c, 0x23,
+ 0x73, 0x74, 0x79, 0x6c,
+ 0x69, 0x6e, 0x67, 0x20,
+ 0x75, 0x72, 0x6e, 0x3a,
+ 0x65, 0x62, 0x75, 0x3a,
+ 0x74, 0x74, 0x3a, 0x6d,
+ 0x65, 0x74, 0x61, 0x64,
+ 0x61, 0x74, 0x61, 0x20,
+ 0x75, 0x72, 0x6e, 0x3a,
+ 0x65, 0x62, 0x75, 0x3a,
+ 0x74, 0x74, 0x3a, 0x73,
+ 0x74, 0x79, 0x6c, 0x65,
+ 0x00, 0x00, 0x00, // schema_location + auxiliary_mime_types (null)
+};
+
static const u_char fixed_stbl_atoms[] = {
0x00, 0x00, 0x00, 0x10, // atom size
0x73, 0x74, 0x74, 0x73, // stts
@@ -165,7 +251,11 @@ mp4_init_segment_get_track_sizes(
size_t mdhd_atom_size;
size_t hdlr_atom_size = 0;
- if (stsd_atom_writer != NULL)
+ if (cur_track->media_info.media_type == MEDIA_TYPE_SUBTITLE)
+ {
+ result->stsd_size = sizeof(smpte_tt_stsd_atom);
+ }
+ else if (stsd_atom_writer != NULL)
{
result->stsd_size = stsd_atom_writer->atom_size;
}
@@ -198,6 +288,10 @@ mp4_init_segment_get_track_sizes(
result->minf_size += sizeof(smhd_atom);
hdlr_atom_size = sizeof(hdlr_audio_atom);
break;
+ case MEDIA_TYPE_SUBTITLE:
+ result->minf_size += sizeof(sthd_atom);
+ hdlr_atom_size = sizeof(hdlr_subtitle_atom);
+ break;
}
result->mdia_size = ATOM_HEADER_SIZE + mdhd_atom_size + hdlr_atom_size + result->minf_size;
result->trak_size = ATOM_HEADER_SIZE + tkhd_atom_size + result->mdia_size;
@@ -691,6 +785,9 @@ mp4_init_segment_write(
case MEDIA_TYPE_AUDIO:
p = vod_copy(p, hdlr_audio_atom, sizeof(hdlr_audio_atom));
break;
+ case MEDIA_TYPE_SUBTITLE:
+ p = vod_copy(p, hdlr_subtitle_atom, sizeof(hdlr_subtitle_atom));
+ break;
}
// moov.trak.mdia.minf
@@ -703,12 +800,19 @@ mp4_init_segment_write(
case MEDIA_TYPE_AUDIO:
p = vod_copy(p, smhd_atom, sizeof(smhd_atom));
break;
+ case MEDIA_TYPE_SUBTITLE:
+ p = vod_copy(p, sthd_atom, sizeof(sthd_atom));
+ break;
}
p = vod_copy(p, dinf_atom, sizeof(dinf_atom));
// moov.trak.mdia.minf.stbl
write_atom_header(p, track_sizes->stbl_size, 's', 't', 'b', 'l');
- if (stsd_atom_writers != NULL)
+ if (cur_track->media_info.media_type == MEDIA_TYPE_SUBTITLE)
+ {
+ p = vod_copy(p, smpte_tt_stsd_atom, sizeof(smpte_tt_stsd_atom));
+ }
+ else if (stsd_atom_writers != NULL)
{
p = stsd_atom_writers[i].write(stsd_atom_writers[i].context, p);
}
diff --git a/vod/mss/mss_packager.c b/vod/mss/mss_packager.c
index 3cdfe557..7a206ef3 100644
--- a/vod/mss/mss_packager.c
+++ b/vod/mss/mss_packager.c
@@ -773,11 +773,16 @@ mss_packager_build_fragment_header(
}
// moof.traf.trun
- p = mp4_fragment_write_trun_atom(
- p,
- sequence,
- moof_atom_size + ATOM_HEADER_SIZE,
- 0);
+ switch (sequence->media_type)
+ {
+ case MEDIA_TYPE_VIDEO:
+ p = mp4_fragment_write_video_trun_atom(p, sequence, moof_atom_size + ATOM_HEADER_SIZE, 0);
+ break;
+
+ case MEDIA_TYPE_AUDIO:
+ p = mp4_fragment_write_audio_trun_atom(p, sequence, moof_atom_size + ATOM_HEADER_SIZE);
+ break;
+ }
// moof.traf.tfxd
mss_get_segment_timing_info(sequence, &timing_info);
diff --git a/vod/subtitle/ttml_builder.c b/vod/subtitle/ttml_builder.c
index b7ef6d59..01414973 100644
--- a/vod/subtitle/ttml_builder.c
+++ b/vod/subtitle/ttml_builder.c
@@ -9,6 +9,7 @@
#define TTML_HEADER \
"\n" \
"\n" \
+ " \n" \
" \n" \
" \n"
@@ -127,10 +128,25 @@ ttml_copy_payload_without_styles(
return p;
}
-static u_char*
-ttml_builder_write(
- media_set_t* media_set,
- u_char* p)
+size_t
+ttml_builder_get_max_size(media_set_t* media_set)
+{
+ media_track_t* cur_track;
+ size_t result;
+
+ result =
+ sizeof(TTML_HEADER) - 1 +
+ sizeof(TTML_FOOTER) - 1;
+ for (cur_track = media_set->filtered_tracks; cur_track < media_set->filtered_tracks_end; cur_track++)
+ {
+ result += cur_track->total_frames_size + TTML_P_MAX_SIZE * cur_track->frame_count;
+ }
+
+ return result;
+}
+
+u_char*
+ttml_builder_write(media_set_t* media_set, u_char* p)
{
frame_list_part_t* part;
media_track_t* cur_track;
@@ -191,7 +207,6 @@ ttml_build_mp4(
uint32_t timescale,
vod_str_t* result)
{
- media_track_t* cur_track;
size_t traf_atom_size;
size_t moof_atom_size;
size_t mdat_atom_size;
@@ -202,13 +217,7 @@ ttml_build_mp4(
u_char* p;
// get the result size
- ttml_size =
- sizeof(TTML_HEADER) - 1 +
- sizeof(TTML_FOOTER) - 1;
- for (cur_track = media_set->filtered_tracks; cur_track < media_set->filtered_tracks_end; cur_track++)
- {
- ttml_size += cur_track->total_frames_size + TTML_P_MAX_SIZE * cur_track->frame_count;
- }
+ ttml_size = ttml_builder_get_max_size(media_set);
traf_atom_size = ATOM_HEADER_SIZE +
ATOM_HEADER_SIZE + sizeof(ttml_tfhd_atom_t) +
diff --git a/vod/subtitle/ttml_builder.h b/vod/subtitle/ttml_builder.h
index a41d3fde..61e601e7 100644
--- a/vod/subtitle/ttml_builder.h
+++ b/vod/subtitle/ttml_builder.h
@@ -7,7 +7,11 @@
// constants
#define TTML_TIMESCALE (1000)
-// globals
+// functions
+size_t ttml_builder_get_max_size(media_set_t* media_set);
+
+u_char* ttml_builder_write(media_set_t* media_set, u_char* p);
+
vod_status_t ttml_build_mp4(
request_context_t* request_context,
media_set_t* media_set,