vod_status_t hds_muxer_init_fragment( request_context_t* request_context, hds_fragment_config_t* conf, uint32_t segment_index, media_sequence_t* sequence, write_callback_t write_callback, void* write_context, bool_t size_only, vod_str_t* header, size_t* total_fragment_size, hds_muxer_state_t** processor_state) { media_clip_filtered_t* cur_clip; media_track_t* cur_track; hds_muxer_stream_state_t* cur_stream; input_frame_t* cur_frame; input_frame_t* frames_end; hds_muxer_state_t* state; vod_status_t rc; uint32_t track_id = 1; uint32_t* output_offset; uint32_t frame_metadata_size; uint32_t video_key_frame_count; uint32_t codec_config_size; size_t afra_atom_size; size_t moof_atom_size; size_t traf_atom_size; size_t mdat_atom_size; size_t result_size; u_char* p; // initialize the muxer state rc = hds_muxer_init_state( request_context, sequence, write_callback, write_context, &state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: hds_muxer_init_state failed %i", rc); return rc; } // get moof atom size mdat_atom_size = ATOM_HEADER_SIZE; for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { codec_config_size = 0; video_key_frame_count = 0; for (cur_track = cur_clip->first_track; cur_track < cur_clip->last_track; cur_track++) { frame_metadata_size = tag_size_by_media_type[cur_track->media_info.media_type] + sizeof(uint32_t); codec_config_size += frame_metadata_size + cur_track->media_info.extra_data_size; mdat_atom_size += cur_track->total_frames_size + cur_track->frame_count * frame_metadata_size; if (cur_track->media_info.media_type == MEDIA_TYPE_VIDEO) { video_key_frame_count += cur_track->key_frame_count; } } mdat_atom_size += video_key_frame_count * codec_config_size; } // get the fragment header size if (conf->generate_moof_atom) { afra_atom_size = ATOM_HEADER_SIZE + sizeof(afra_atom_t) + sizeof(afra_entry_t) * sequence->video_key_frame_count; moof_atom_size = ATOM_HEADER_SIZE + ATOM_HEADER_SIZE + sizeof(mfhd_atom_t); for (cur_stream = state->first_stream; cur_stream < state->last_stream; cur_stream++) { moof_atom_size += hds_get_traf_atom_size(cur_stream); } } else { afra_atom_size = 0; moof_atom_size = 0; } result_size = afra_atom_size + moof_atom_size + ATOM_HEADER_SIZE; // mdat // audio only - output the codec config up front, video - output the codec config before every key frame if (sequence->video_key_frame_count == 0) { result_size += state->codec_config_size; mdat_atom_size += state->codec_config_size; } *total_fragment_size = afra_atom_size + moof_atom_size + mdat_atom_size; // head request optimization if (size_only) { return VOD_OK; } // allocate the response header->data = vod_alloc(request_context->pool, result_size); if (header->data == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: vod_alloc failed"); return VOD_ALLOC_FAILED; } p = header->data; if (conf->generate_moof_atom) { // afra p = hds_write_afra_atom_header(p, afra_atom_size, sequence->video_key_frame_count); rc = hds_calculate_output_offsets_and_write_afra_entries(state, ATOM_HEADER_SIZE, afra_atom_size + moof_atom_size, &p); if (rc != VOD_OK) { return rc; } // moof write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f'); // moof.mfhd p = mp4_builder_write_mfhd_atom(p, segment_index); for (cur_stream = state->first_stream; cur_stream < state->last_stream; cur_stream++) { // moof.traf traf_atom_size = hds_get_traf_atom_size(cur_stream); write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f'); // moof.traf.tfhd p = hds_write_tfhd_atom(p, track_id, ATOM_HEADER_SIZE + sizeof(afra_atom_t) + moof_atom_size); // moof.traf.trun switch (cur_stream->media_type) { case MEDIA_TYPE_VIDEO: for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { cur_track = cur_clip->first_track + cur_stream->index; frames_end = cur_track->last_frame; for (cur_frame = cur_track->first_frame, output_offset = cur_stream->first_frame_output_offset; cur_frame < frames_end; cur_frame++, output_offset++) { p = hds_write_single_video_frame_trun_atom(p, cur_frame, *output_offset); } } break; case MEDIA_TYPE_AUDIO: for (cur_clip = sequence->filtered_clips; cur_clip < sequence->filtered_clips_end; cur_clip++) { cur_track = cur_clip->first_track + cur_stream->index; frames_end = cur_track->last_frame; for (cur_frame = cur_track->first_frame, output_offset = cur_stream->first_frame_output_offset; cur_frame < frames_end; cur_frame++, output_offset++) { p = hds_write_single_audio_frame_trun_atom(p, cur_frame, *output_offset); } } break; } } } else { // calculate the output offsets rc = hds_calculate_output_offsets_and_write_afra_entries(state, 0, 0, NULL); if (rc != VOD_OK) { return rc; } } // mdat write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't'); if (sequence->video_key_frame_count == 0) { p = hds_muxer_write_codec_config( p, state, state->first_stream->next_frame_dts + state->first_stream->clip_start_time); } header->len = p - header->data; if (header->len != result_size) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "hds_muxer_init_fragment: result length %uz exceeded allocated length %uz", header->len, result_size); return VOD_UNEXPECTED; } rc = hds_muxer_start_frame(state); if (rc != VOD_OK) { if (rc == VOD_NOT_FOUND) { *processor_state = NULL; // no frames, nothing to do return VOD_OK; } vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: hds_muxer_start_frame failed %i", rc); return rc; } *processor_state = state; return VOD_OK; }
vod_status_t hds_muxer_init_fragment( request_context_t* request_context, uint32_t segment_index, mpeg_metadata_t *mpeg_metadata, read_cache_state_t* read_cache_state, write_callback_t write_callback, void* write_context, bool_t size_only, vod_str_t* header, size_t* total_fragment_size, hds_muxer_state_t** processor_state) { mpeg_stream_metadata_t* cur_stream; hds_muxer_stream_state_t* stream_state; input_frame_t* cur_frame; input_frame_t* frames_end; hds_muxer_state_t* state; vod_status_t rc; uint32_t track_id = 1; uint32_t* output_offset; uint32_t frame_metadata_size; size_t afra_atom_size; size_t moof_atom_size; size_t traf_atom_size; size_t mdat_atom_size; size_t result_size; u_char* p; // initialize the muxer state rc = hds_muxer_init_state(request_context, mpeg_metadata, read_cache_state, write_callback, write_context, &state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: hds_muxer_init_state failed %i", rc); return rc; } // get moof atom size moof_atom_size = ATOM_HEADER_SIZE + ATOM_HEADER_SIZE + sizeof(mfhd_atom_t); mdat_atom_size = ATOM_HEADER_SIZE; state->codec_config_size = 0; for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++) { moof_atom_size += hds_get_traf_atom_size(cur_stream); frame_metadata_size = tag_size_by_media_type[cur_stream->media_info.media_type] + sizeof(uint32_t); state->codec_config_size += frame_metadata_size + cur_stream->media_info.extra_data_size; mdat_atom_size += cur_stream->total_frames_size + cur_stream->frame_count * frame_metadata_size; } mdat_atom_size += mpeg_metadata->video_key_frame_count * state->codec_config_size; // get the fragment header size afra_atom_size = ATOM_HEADER_SIZE + sizeof(afra_atom_t) + sizeof(afra_entry_t) * mpeg_metadata->video_key_frame_count; result_size = afra_atom_size + moof_atom_size + ATOM_HEADER_SIZE; // mdat // audio only - output the codec config up front, video - output the codec config before every key frame if (mpeg_metadata->video_key_frame_count == 0) { result_size += state->codec_config_size; mdat_atom_size += state->codec_config_size; } *total_fragment_size = afra_atom_size + moof_atom_size + mdat_atom_size; // head request optimization if (size_only) { return VOD_OK; } // allocate the response header->data = vod_alloc(request_context->pool, result_size); if (header->data == NULL) { vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, "hds_muxer_init_fragment: vod_alloc failed"); return VOD_ALLOC_FAILED; } // afra p = hds_write_afra_atom_header(header->data, afra_atom_size, mpeg_metadata->video_key_frame_count); p = hds_calculate_output_offsets_and_write_afra_entries(state, ATOM_HEADER_SIZE, afra_atom_size + moof_atom_size, p); // moof write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f'); // moof.mfhd p = mp4_builder_write_mfhd_atom(p, segment_index); for (stream_state = state->first_stream; stream_state < state->last_stream; stream_state++) { cur_stream = stream_state->metadata; // moof.traf traf_atom_size = hds_get_traf_atom_size(cur_stream); write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f'); // moof.traf.tfhd p = hds_write_tfhd_atom(p, track_id, ATOM_HEADER_SIZE + sizeof(afra_atom_t) + moof_atom_size); // moof.traf.trun frames_end = cur_stream->frames + cur_stream->frame_count; switch (cur_stream->media_info.media_type) { case MEDIA_TYPE_VIDEO: for (cur_frame = cur_stream->frames, output_offset = stream_state->first_frame_output_offset; cur_frame < frames_end; cur_frame++, output_offset++) { p = hds_write_single_video_frame_trun_atom(p, cur_frame, *output_offset); } break; case MEDIA_TYPE_AUDIO: for (cur_frame = cur_stream->frames, output_offset = stream_state->first_frame_output_offset; cur_frame < frames_end; cur_frame++, output_offset++) { p = hds_write_single_audio_frame_trun_atom(p, cur_frame, *output_offset); } break; } } // mdat write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't'); if (mpeg_metadata->video_key_frame_count == 0) { p = hds_muxer_write_codec_config(p, state, state->first_stream->next_frame_dts); } header->len = p - header->data; if (header->len != result_size) { vod_log_error(VOD_LOG_ERR, request_context->log, 0, "hds_muxer_init_fragment: result length %uz exceeded allocated length %uz", header->len, result_size); return VOD_UNEXPECTED; } *processor_state = state; return VOD_OK; }