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_process_frames(hds_muxer_state_t* state) { u_char* read_buffer; uint32_t read_size; vod_status_t rc; bool_t wrote_data = FALSE; bool_t frame_done; for (;;) { // read some data from the frame rc = state->frames_source->read(state->frames_source_context, &read_buffer, &read_size, &frame_done); if (rc != VOD_OK) { if (rc != VOD_AGAIN) { return rc; } if (!wrote_data && !state->first_time) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "hds_muxer_process_frames: no data was handled, probably a truncated file"); return VOD_BAD_DATA; } state->first_time = FALSE; return VOD_AGAIN; } wrote_data = TRUE; // write the frame rc = write_buffer_write(&state->write_buffer_state, read_buffer, read_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_write failed %i", rc); return rc; } // if not done, ask the cache for more data if (!frame_done) { continue; } // end the frame and start a new one rc = hds_muxer_end_frame(state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_write failed %i", rc); return rc; } rc = hds_muxer_start_frame(state); if (rc != VOD_OK) { if (rc == VOD_NOT_FOUND) { break; // done } vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: hds_muxer_start_frame failed %i", rc); return rc; } } // flush the buffer rc = write_buffer_flush(&state->write_buffer_state, FALSE); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_flush failed %i", rc); return rc; } return VOD_OK; }
vod_status_t hds_muxer_process_frames(hds_muxer_state_t* state, uint64_t* required_offset) { u_char* read_buffer; uint32_t read_size; uint32_t write_size; uint64_t offset; vod_status_t rc; bool_t first_time = (state->cur_frame == NULL); bool_t wrote_data = FALSE; for (;;) { // start a new frame if we don't have a frame if (state->cur_frame == NULL) { rc = hds_muxer_start_frame(state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: hds_muxer_start_frame failed %i", rc); return rc; } if (state->cur_frame == NULL) { break; // done } } // read some data from the frame offset = state->cur_frame_offset + state->cur_frame_pos; if (!read_cache_get_from_cache(state->read_cache_state, state->cache_slot_id, offset, &read_buffer, &read_size)) { if (!wrote_data && !first_time) { vod_log_error(VOD_LOG_ERR, state->request_context->log, 0, "hds_muxer_process_frames: no data was handled, probably a truncated file"); return VOD_BAD_DATA; } *required_offset = offset; return VOD_AGAIN; } wrote_data = TRUE; // write the frame write_size = MIN(state->cur_frame->size - state->cur_frame_pos, read_size); rc = write_buffer_write(&state->write_buffer_state, read_buffer, write_size); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_write failed %i", rc); return rc; } state->cur_frame_pos += write_size; // flush the frame if we finished writing it if (state->cur_frame_pos >= state->cur_frame->size) { rc = hds_muxer_end_frame(state); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_write failed %i", rc); return rc; } state->cur_frame = NULL; } } // flush the buffer rc = write_buffer_flush(&state->write_buffer_state, FALSE); if (rc != VOD_OK) { vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0, "hds_muxer_process_frames: write_buffer_flush failed %i", rc); return rc; } return VOD_OK; }