static u_char* mss_write_uuid_tfrf_atom(u_char* p, segment_timing_info_t* timing_info) { size_t atom_size = TFRF_ATOM_SIZE; uint64_t timestamp; uint64_t duration; int i; timestamp = timing_info->timestamp; duration = timing_info->duration; write_atom_header(p, atom_size, 'u', 'u', 'i', 'd'); p = vod_copy(p, tfrf_uuid, sizeof(tfrf_uuid)); write_be32(p, 0x01000000); // version / flags *p++ = MSS_LOOK_AHEAD_COUNT; for (i = 0; i < MSS_LOOK_AHEAD_COUNT; i++) { timestamp += duration; write_be64(p, timestamp); write_be64(p, duration); } return p; }
static u_char* edash_packager_passthrough_write_encryption_atoms(void* ctx, u_char* p, size_t mdat_atom_start) { mp4_encrypt_passthrough_context_t* context = ctx; media_clip_filtered_t* cur_clip; media_sequence_t* sequence = context->sequence; media_track_t* cur_track; size_t senc_atom_size; uint32_t flags; // saiz / saio p = mp4_encrypt_passthrough_write_saiz_saio(ctx, p, mdat_atom_start - context->auxiliary_info_size); // senc senc_atom_size = ATOM_HEADER_SIZE + sizeof(senc_atom_t) + context->auxiliary_info_size; write_atom_header(p, senc_atom_size, 's', 'e', 'n', 'c'); flags = context->use_subsamples ? 0x2 : 0x0; write_be32(p, flags); // flags write_be32(p, sequence->total_frame_count); for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { cur_track = cur_clip->first_track; p = vod_copy(p, cur_track->encryption_info.auxiliary_info, cur_track->encryption_info.auxiliary_info_end - cur_track->encryption_info.auxiliary_info); } return p; }
static u_char* mp4_builder_write_audio_trun_atom(u_char* p, media_sequence_t* sequence, uint32_t first_frame_offset) { media_clip_filtered_t* cur_clip; input_frame_t* cur_frame; input_frame_t* last_frame; size_t atom_size; atom_size = ATOM_HEADER_SIZE + sizeof(trun_atom_t) + sequence->total_frame_count * 2 * sizeof(uint32_t); write_atom_header(p, atom_size, 't', 'r', 'u', 'n'); write_be32(p, 0x301); // flags = data offset, duration, size write_be32(p, sequence->total_frame_count); write_be32(p, first_frame_offset); // first frame offset relative to moof start offset for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { cur_frame = cur_clip->first_track->first_frame; last_frame = cur_clip->first_track->last_frame; for (; cur_frame < last_frame; cur_frame++) { write_be32(p, cur_frame->duration); write_be32(p, cur_frame->size); } } return p; }
static u_char* hds_write_afra_atom_header(u_char* p, size_t atom_size, uint32_t video_key_frame_count) { write_atom_header(p, atom_size, 'a', 'f', 'r', 'a'); write_be32(p, 0); *p++ = 0xC0; // LongIDs | LongOffsets write_be32(p, HDS_TIMESCALE); // timescale write_be32(p, video_key_frame_count); // entries return p; }
u_char* mp4_builder_write_mfhd_atom(u_char* p, uint32_t segment_index) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(mfhd_atom_t); write_atom_header(p, atom_size, 'm', 'f', 'h', 'd'); write_be32(p, 0); write_be32(p, segment_index); return p; }
static u_char* dash_packager_write_tfdt64_atom(u_char* p, uint64_t earliest_pres_time) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(tfdt64_atom_t); write_atom_header(p, atom_size, 't', 'f', 'd', 't'); write_dword(p, 0x01000000); // version = 1 write_qword(p, earliest_pres_time); return p; }
static u_char* mss_playready_audio_write_uuid_piff_atom(u_char* p, mp4_encrypt_state_t* state, media_sequence_t* sequence, size_t atom_size) { write_atom_header(p, atom_size, 'u', 'u', 'i', 'd'); p = vod_copy(p, piff_uuid, sizeof(piff_uuid)); write_dword(p, 0); write_dword(p, sequence->total_frame_count); p = mp4_encrypt_audio_write_auxiliary_data(state, p); return p; }
static u_char* mss_write_tfhd_atom(u_char* p, uint32_t track_id, uint32_t flags) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(tfhd_atom_t); write_atom_header(p, atom_size, 't', 'f', 'h', 'd'); write_dword(p, 0x20); // default sample flags write_dword(p, track_id); write_dword(p, flags); return p; }
static u_char* mss_playready_video_write_uuid_piff_atom(u_char* p, mp4_encrypt_video_state_t* state, media_sequence_t* sequence, size_t atom_size) { write_atom_header(p, atom_size, 'u', 'u', 'i', 'd'); p = vod_copy(p, piff_uuid, sizeof(piff_uuid)); write_be32(p, 2); write_be32(p, sequence->total_frame_count); p = vod_copy(p, state->auxiliary_data.start, state->auxiliary_data.pos - state->auxiliary_data.start); return p; }
u_char* mp4_encrypt_audio_write_saiz_saio(mp4_encrypt_state_t* state, u_char* p, size_t auxiliary_data_offset) { size_t saiz_atom_size = ATOM_HEADER_SIZE + sizeof(saiz_atom_t); size_t saio_atom_size = ATOM_HEADER_SIZE + sizeof(saio_atom_t); // moof.traf.saiz write_atom_header(p, saiz_atom_size, 's', 'a', 'i', 'z'); write_be32(p, 0); // version, flags *p++ = MP4_AES_CTR_IV_SIZE; // default auxiliary sample size write_be32(p, state->sequence->total_frame_count); // moof.traf.saio write_atom_header(p, saio_atom_size, 's', 'a', 'i', 'o'); write_be32(p, 0); // version, flags write_be32(p, 1); // entry count write_be32(p, auxiliary_data_offset); return p; }
static u_char* mss_write_uuid_tfxd_atom(u_char* p, segment_timing_info_t* timing_info) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(uuid_tfxd_atom_t); write_atom_header(p, atom_size, 'u', 'u', 'i', 'd'); p = vod_copy(p, tfxd_uuid, sizeof(tfxd_uuid)); write_be32(p, 0x01000000); // version / flags write_be64(p, timing_info->timestamp); write_be64(p, timing_info->duration); return p; }
static u_char* hds_write_tfhd_atom(u_char* p, uint32_t track_id, uint64_t base_data_offset) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(tfhd_atom_t); write_atom_header(p, atom_size, 't', 'f', 'h', 'd'); write_be32(p, 3); // flags - base data offset | sample description write_be32(p, track_id); write_be64(p, base_data_offset); write_be32(p, 1); // sample_desc_index return p; }
u_char* mp4_encrypt_video_write_saiz_saio(mp4_encrypt_video_state_t* state, u_char* p, size_t auxiliary_data_offset) { // moof.traf.saiz write_atom_header(p, state->base.saiz_atom_size, 's', 'a', 'i', 'z'); write_be32(p, 0); // version, flags *p++ = state->default_auxiliary_sample_size; write_be32(p, state->saiz_sample_count); if (state->default_auxiliary_sample_size == 0) { p = vod_copy(p, state->auxiliary_sample_sizes, state->saiz_sample_count); } // moof.traf.saio write_atom_header(p, state->base.saio_atom_size, 's', 'a', 'i', 'o'); write_be32(p, 0); // version, flags write_be32(p, 1); // entry count write_be32(p, auxiliary_data_offset); return p; }
static u_char* edash_packager_write_pssh(u_char* p, drm_system_info_t* cur_info) { size_t pssh_atom_size; pssh_atom_size = ATOM_HEADER_SIZE + sizeof(pssh_atom_t) + cur_info->data.len; write_atom_header(p, pssh_atom_size, 'p', 's', 's', 'h'); write_be32(p, 0); // version + flags p = vod_copy(p, cur_info->system_id, DRM_SYSTEM_ID_SIZE); // system id write_be32(p, cur_info->data.len); // data size p = vod_copy(p, cur_info->data.data, cur_info->data.len); return p; }
static u_char* mss_write_uuid_tfxd_atom(u_char* p, mpeg_stream_metadata_t* stream_metadata) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(uuid_tfxd_atom_t); uint64_t timestamp = rescale_time(stream_metadata->first_frame_time_offset, stream_metadata->media_info.timescale, MSS_TIMESCALE); uint64_t duration = rescale_time(stream_metadata->total_frames_duration, stream_metadata->media_info.timescale, MSS_TIMESCALE); write_atom_header(p, atom_size, 'u', 'u', 'i', 'd'); p = vod_copy(p, tfxd_uuid, sizeof(tfxd_uuid)); write_dword(p, 0x01000000); // version / flags write_qword(p, timestamp); write_qword(p, duration); return p; }
static u_char* dash_packager_write_trex_atom(u_char* p, uint32_t track_id) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(trex_atom_t); write_atom_header(p, atom_size, 't', 'r', 'e', 'x'); write_dword(p, 0); // version + flags write_dword(p, track_id); // track id write_dword(p, 1); // default sample description index write_dword(p, 0); // default sample duration write_dword(p, 0); // default sample size write_dword(p, 0); // default sample size return p; }
static u_char* hds_write_single_audio_frame_trun_atom(u_char* p, input_frame_t* frame, uint32_t offset) { size_t atom_size; atom_size = TRUN_SIZE_SINGLE_AUDIO_FRAME; write_atom_header(p, atom_size, 't', 'r', 'u', 'n'); write_be32(p, 0x301); // flags = data offset, duration, size write_be32(p, 1); // frame count write_be32(p, offset); // offset from mdat start to frame raw data (excluding the tag) write_be32(p, frame->duration); write_be32(p, frame->size); return p; }
static u_char* dash_packager_write_mvhd64_atom(u_char* p, uint32_t timescale, uint64_t duration) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(mvhd64_atom_t); write_atom_header(p, atom_size, 'm', 'v', 'h', 'd'); write_dword(p, 0x01000000); // version + flags write_qword(p, 0LL); // creation time write_qword(p, 0LL); // modification time write_dword(p, timescale); // timescale write_qword(p, duration); // duration p = dash_packager_write_mvhd_constants(p); write_dword(p, 0xffffffff); // next track id return p; }
static u_char* mp4_builder_write_video_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; input_frame_t* cur_frame; input_frame_t* last_frame; size_t atom_size; atom_size = ATOM_HEADER_SIZE + sizeof(trun_atom_t) + sequence->total_frame_count * 4 * sizeof(uint32_t); write_atom_header(p, atom_size, 't', 'r', 'u', 'n'); write_be32(p, 0xF01); // flags = data offset, duration, size, key, delay write_be32(p, sequence->total_frame_count); write_be32(p, first_frame_offset); // first frame offset relative to moof start offset for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { part = &cur_clip->first_track->frames; last_frame = part->last_frame; for (cur_frame = part->first_frame;; cur_frame++) { if (cur_frame >= last_frame) { if (part->next == NULL) { break; } part = part->next; cur_frame = part->first_frame; last_frame = part->last_frame; } write_be32(p, cur_frame->duration); write_be32(p, cur_frame->size); if (cur_frame->key_frame) { write_be32(p, 0x00000000); } else { write_be32(p, 0x00010000); } write_be32(p, cur_frame->pts_delay); } } return p; }
static u_char* edash_packager_audio_write_encryption_atoms(void* context, u_char* p, size_t mdat_atom_start) { mp4_encrypt_state_t* state = (mp4_encrypt_state_t*)context; size_t senc_data_size = MP4_AES_CTR_IV_SIZE * state->sequence->total_frame_count; size_t senc_atom_size = ATOM_HEADER_SIZE + sizeof(senc_atom_t) + senc_data_size; // saiz / saio p = mp4_encrypt_audio_write_saiz_saio(state, p, mdat_atom_start - senc_data_size); // senc write_atom_header(p, senc_atom_size, 's', 'e', 'n', 'c'); write_be32(p, 0x0); // flags write_be32(p, state->sequence->total_frame_count); p = mp4_encrypt_audio_write_auxiliary_data(state, p); return p; }
static u_char* edash_packager_video_write_encryption_atoms(void* context, u_char* p, size_t mdat_atom_start) { mp4_encrypt_video_state_t* state = (mp4_encrypt_video_state_t*)context; size_t senc_data_size = state->auxiliary_data.pos - state->auxiliary_data.start; size_t senc_atom_size = ATOM_HEADER_SIZE + sizeof(senc_atom_t) + senc_data_size; // saiz / saio p = mp4_encrypt_video_write_saiz_saio(state, p, mdat_atom_start - senc_data_size); // senc write_atom_header(p, senc_atom_size, 's', 'e', 'n', 'c'); write_be32(p, 0x2); // flags write_be32(p, state->base.sequence->total_frame_count); p = vod_copy(p, state->auxiliary_data.start, senc_data_size); return p; }
static u_char* dash_packager_write_sidx64_atom( u_char* p, sidx_params_t* sidx_params, uint32_t reference_size) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(sidx64_atom_t); write_atom_header(p, atom_size, 's', 'i', 'd', 'x'); write_dword(p, 0x01000000); // version + flags write_dword(p, 1); // reference id write_dword(p, sidx_params->timescale); // timescale write_qword(p, sidx_params->earliest_pres_time); // earliest presentation time write_qword(p, 0LL); // first offset write_dword(p, 1); // reserved + reference count write_dword(p, reference_size); // referenced size write_dword(p, sidx_params->total_frames_duration); // subsegment duration write_dword(p, 0x90000000); // starts with SAP / SAP type return p; }
static u_char* edash_packager_write_pssh(void* context, u_char* p) { mp4_encrypt_system_info_array_t* pssh_array = (mp4_encrypt_system_info_array_t*)context; mp4_encrypt_system_info_t* cur_info; size_t pssh_atom_size; for (cur_info = pssh_array->first; cur_info < pssh_array->last; cur_info++) { pssh_atom_size = ATOM_HEADER_SIZE + sizeof(pssh_atom_t) + cur_info->data.len; write_atom_header(p, pssh_atom_size, 'p', 's', 's', 'h'); write_dword(p, 0); // version + flags p = vod_copy(p, cur_info->system_id, MP4_ENCRYPT_SYSTEM_ID_SIZE); // system id write_dword(p, cur_info->data.len); // data size p = vod_copy(p, cur_info->data.data, cur_info->data.len); } return p; }
static u_char* dash_packager_write_sidx_atom( u_char* p, mpeg_stream_metadata_t* stream_metadata, uint32_t earliest_pres_time, uint32_t reference_size) { size_t atom_size = ATOM_HEADER_SIZE + sizeof(sidx_atom_t); write_atom_header(p, atom_size, 's', 'i', 'd', 'x'); write_dword(p, 0); // version + flags write_dword(p, 1); // reference id write_dword(p, stream_metadata->media_info.timescale); // timescale write_dword(p, earliest_pres_time); // earliest presentation time write_dword(p, 0); // first offset write_dword(p, 1); // reserved + reference count write_dword(p, reference_size); // referenced size write_dword(p, stream_metadata->total_frames_duration); // subsegment duration write_dword(p, 0x90000000); // starts with SAP / SAP type return p; }
static u_char* edash_packager_write_stsd(void* ctx, u_char* p) { stsd_writer_context_t* context = (stsd_writer_context_t*)ctx; u_char format_by_media_type[MEDIA_TYPE_COUNT] = { 'v', 'a' }; // stsd write_atom_header(p, context->stsd_atom_size, 's', 't', 's', 'd'); write_dword(p, 0); // version + flags write_dword(p, context->has_clear_lead ? 2 : 1); // entries // stsd encrypted entry write_dword(p, context->encrypted_stsd_entry_size); // size write_atom_name(p, 'e', 'n', 'c', format_by_media_type[context->media_type]); // format p = vod_copy(p, context->original_stsd_entry + 1, context->original_stsd_entry_size - sizeof(stsd_entry_header_t)); // sinf write_atom_header(p, context->sinf_atom_size, 's', 'i', 'n', 'f'); // sinf.frma write_atom_header(p, context->frma_atom_size, 'f', 'r', 'm', 'a'); write_dword(p, context->original_stsd_entry_format); // sinf.schm write_atom_header(p, context->schm_atom_size, 's', 'c', 'h', 'm'); write_dword(p, 0); // version + flags write_atom_name(p, 'c', 'e', 'n', 'c'); // scheme type write_dword(p, 0x10000); // scheme version // sinf.schi write_atom_header(p, context->schi_atom_size, 's', 'c', 'h', 'i'); // sinf.schi.tenc write_atom_header(p, context->tenc_atom_size, 't', 'e', 'n', 'c'); write_dword(p, 0); // version + flags write_dword(p, 0x108); // default is encrypted (1) + iv size (8) p = vod_copy(p, context->default_kid, MP4_ENCRYPT_KID_SIZE); // default key id // clear entry if (context->has_clear_lead) { p = vod_copy(p, context->original_stsd_entry, context->original_stsd_entry_size); } return p; }
static u_char* dash_packager_write_tfhd_atom(u_char* p, uint32_t track_id, uint32_t sample_description_index) { size_t atom_size; uint32_t flags; flags = 0x020000; // default-base-is-moof atom_size = ATOM_HEADER_SIZE + sizeof(tfhd_atom_t); if (sample_description_index > 0) { flags |= 0x02; // sample-description-index-present atom_size += sizeof(uint32_t); } write_atom_header(p, atom_size, 't', 'f', 'h', 'd'); write_dword(p, flags); // flags write_dword(p, track_id); // track id if (sample_description_index > 0) { write_dword(p, sample_description_index); } return p; }
static u_char* mss_playready_passthrough_write_encryption_atoms(void* ctx, u_char* p, size_t mdat_atom_start) { mp4_encrypt_passthrough_context_t* context = ctx; media_clip_filtered_t* cur_clip; media_sequence_t* sequence = context->sequence; media_track_t* cur_track; size_t auxiliary_data_offset; size_t uuid_atom_size; uint32_t flags; // uuid piff uuid_atom_size = ATOM_HEADER_SIZE + sizeof(uuid_piff_atom_t) + context->auxiliary_info_size; write_atom_header(p, uuid_atom_size, 'u', 'u', 'i', 'd'); p = vod_copy(p, piff_uuid, sizeof(piff_uuid)); flags = context->use_subsamples ? 0x2 : 0x0; write_be32(p, flags); // flags write_be32(p, sequence->total_frame_count); for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { cur_track = cur_clip->first_track; p = vod_copy(p, cur_track->encryption_info.auxiliary_info, cur_track->encryption_info.auxiliary_info_end - cur_track->encryption_info.auxiliary_info); } // saiz / saio auxiliary_data_offset = mdat_atom_start - (context->auxiliary_info_size + context->saiz_atom_size + context->saio_atom_size); p = mp4_encrypt_passthrough_write_saiz_saio(ctx, p, auxiliary_data_offset); return p; }
static u_char* hds_write_single_video_frame_trun_atom(u_char* p, input_frame_t* frame, uint32_t offset) { size_t atom_size; atom_size = TRUN_SIZE_SINGLE_VIDEO_FRAME; write_atom_header(p, atom_size, 't', 'r', 'u', 'n'); write_be32(p, 0xF01); // flags = data offset, duration, size, key, delay write_be32(p, 1); // frame count write_be32(p, offset); // offset from mdat start to frame raw data (excluding the tag) write_be32(p, frame->duration); write_be32(p, frame->size); if (frame->key_frame) { write_be32(p, 0x02000000); // I-frame } else { write_be32(p, 0x01010000); // not I-frame + non key sample } write_be32(p, frame->pts_delay); return p; }
static u_char* hds_write_abst_atom( u_char* p, segment_durations_t* segment_durations) { segment_duration_item_t* cur_item; segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count; uint64_t start_offset = 0; uint64_t timestamp; uint32_t duration; size_t afrt_atom_size = AFRT_BASE_ATOM_SIZE; size_t asrt_atom_size = ASRT_ATOM_SIZE; size_t abst_atom_size = ABST_BASE_ATOM_SIZE; afrt_atom_size += (segment_durations->item_count + 1) * sizeof(afrt_entry_t); abst_atom_size += (segment_durations->item_count + 1) * sizeof(afrt_entry_t); // abst write_atom_header(p, abst_atom_size, 'a', 'b', 's', 't'); write_dword(p, 0); // version + flags write_dword(p, 1); // bootstrap info version *p++ = 0; // profile, live, update write_dword(p, HDS_TIMESCALE); // timescale write_dword(p, 0); // current media time - high write_dword(p, segment_durations->duration_millis); // current media time - low write_qword(p, 0LL); // smpte offset *p++ = 0; // movie identifier *p++ = 0; // server entries *p++ = 0; // quality entries *p++ = 0; // drm data *p++ = 0; // metadata *p++ = 1; // segment run table count // abst.asrt write_atom_header(p, asrt_atom_size, 'a', 's', 'r', 't'); write_dword(p, 0); // version + flags *p++ = 0; // quality entries write_dword(p, 1); // segment run entries // entry #1 write_dword(p, 1); // first segment write_dword(p, segment_durations->segment_count); // fragments per segment // abst *p++ = 1; // fragment run table count // abst.afrt write_atom_header(p, afrt_atom_size, 'a', 'f', 'r', 't'); write_dword(p, 0); // version + flags write_dword(p, HDS_TIMESCALE); // timescale *p++ = 0; // quality entries write_dword(p, segment_durations->item_count + 1); // fragment run entries // write the afrt entries for (cur_item = segment_durations->items; cur_item < last_item; cur_item++) { timestamp = rescale_time(start_offset, segment_durations->timescale, HDS_TIMESCALE); duration = rescale_time(cur_item->duration, segment_durations->timescale, HDS_TIMESCALE); write_dword(p, cur_item->segment_index + 1); // first fragment write_qword(p, timestamp); // first fragment timestamp write_dword(p, duration); // fragment duration start_offset += cur_item->duration * cur_item->repeat_count; } // last entry write_dword(p, 0); // first fragment write_qword(p, 0LL); // first fragment timestamp write_dword(p, 0); // fragment duration *p++ = 0; // discontinuity indicator (0 = end of presentation) return p; }
static u_char* hds_write_abst_atom( u_char* p, media_set_t* media_set, hds_segment_durations_t* segments) { segment_durations_t* segment_durations = &segments->durations; segment_duration_item_t* cur_item; segment_duration_item_t* last_item = segment_durations->items + segment_durations->item_count; uint64_t timestamp; uint32_t fragment_run_entries; uint32_t segment_index; uint32_t duration; size_t afrt_atom_size = AFRT_BASE_ATOM_SIZE; size_t asrt_atom_size = ASRT_ATOM_SIZE; size_t abst_atom_size = ABST_BASE_ATOM_SIZE; size_t extra_afrt_size = 0; fragment_run_entries = segment_durations->item_count; if (media_set->presentation_end) { fragment_run_entries++; // zero entry extra_afrt_size += sizeof(u_char); // discontinuity indicator } fragment_run_entries += segment_durations->discontinuities; // zero entry extra_afrt_size += fragment_run_entries * sizeof(afrt_entry_t) + sizeof(u_char) * (segment_durations->discontinuities + segments->zero_segments); // discontinuity indicator afrt_atom_size += extra_afrt_size; abst_atom_size += extra_afrt_size; // abst write_atom_header(p, abst_atom_size, 'a', 'b', 's', 't'); write_be32(p, 0); // version + flags write_be32(p, 1); // bootstrap info version *p++ = (media_set->type == MEDIA_SET_LIVE ? 0x20 : 0); // profile, live, update write_be32(p, HDS_TIMESCALE); // timescale write_be64(p, segment_durations->end_time); // current media time write_be64(p, 0LL); // smpte offset *p++ = 0; // movie identifier *p++ = 0; // server entries *p++ = 0; // quality entries *p++ = 0; // drm data *p++ = 0; // metadata *p++ = 1; // segment run table count // abst.asrt write_atom_header(p, asrt_atom_size, 'a', 's', 'r', 't'); write_be32(p, 0); // version + flags *p++ = 0; // quality entries write_be32(p, 1); // segment run entries // entry #1 write_be32(p, 1); // first segment write_be32(p, segment_durations->segment_count); // fragments per segment // abst *p++ = 1; // fragment run table count // abst.afrt write_atom_header(p, afrt_atom_size, 'a', 'f', 'r', 't'); write_be32(p, 0); // version + flags write_be32(p, HDS_TIMESCALE); // timescale *p++ = 0; // quality entries write_be32(p, fragment_run_entries); // fragment run entries // write the afrt entries for (cur_item = segment_durations->items; cur_item < last_item; cur_item++) { segment_index = cur_item->segment_index + 1; timestamp = cur_item->time; duration = cur_item->duration; write_be32(p, segment_index); // first fragment write_be64(p, timestamp); // first fragment timestamp write_be32(p, duration); // fragment duration if (duration == 0) { *p++ = 1; // discontinuity indicator (1 = discontinuity in fragment numbering) } if (cur_item + 1 < last_item && cur_item[1].discontinuity) { segment_index += cur_item->repeat_count; timestamp += duration * cur_item->repeat_count; write_be32(p, segment_index); // first fragment write_be64(p, timestamp); // first fragment timestamp write_be32(p, 0); // fragment duration *p++ = 3; // discontinuity indicator (3 = discontinuity in both timestamps and fragment numbering) } } if (media_set->presentation_end) { // last entry write_be32(p, 0); // first fragment write_be64(p, 0LL); // first fragment timestamp write_be32(p, 0); // fragment duration *p++ = 0; // discontinuity indicator (0 = end of presentation) } return p; }