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;
}
Example #3
0
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;
}