Пример #1
0
vod_status_t 
read_cache_get_read_buffer(read_cache_state_t* state, uint32_t* file_index, uint64_t* out_offset, u_char** buffer, uint32_t* size)
{
	cache_buffer_t* target_buffer;
	cache_buffer_t* cur_buffer;
	cache_buffer_t* buffers_end = state->buffers + CACHED_BUFFERS;
	uint32_t read_size;

	// select a buffer
	target_buffer = state->target_buffer;
	
	// make sure the buffer is allocated
	if (target_buffer->buffer == NULL)
	{
		target_buffer->buffer = vod_memalign(state->request_context->pool, state->buffer_size + 1, state->alignment);
		if (target_buffer->buffer == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
				"read_cache_get_read_buffer: vod_memalign failed");
			return VOD_ALLOC_FAILED;
		}
	}
	
	// make sure we don't read anything we already have in cache
	read_size = state->buffer_size;
	for (cur_buffer = state->buffers; cur_buffer < buffers_end; cur_buffer++)
	{
		if (cur_buffer != target_buffer && cur_buffer->start_offset > target_buffer->start_offset)
		{
			read_size = MIN(read_size, cur_buffer->start_offset - target_buffer->start_offset);
		}
	}
	
	// return the target buffer pointer and size
	*file_index = target_buffer->file_index;
	*out_offset = target_buffer->start_offset;
	*buffer = target_buffer->buffer;
	*size = read_size;

	return VOD_OK;
}
Пример #2
0
vod_status_t
write_buffer_flush(write_buffer_state_t* state, bool_t reallocate)
{
	vod_status_t rc;

	if (state->cur_pos > state->start_pos)
	{
		rc = state->write_callback(state->write_context, state->start_pos, state->cur_pos - state->start_pos);
		if (rc != VOD_OK)
		{
			vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
				"write_buffer_flush: write_callback failed %i", rc);
			return rc;
		}

		if (state->reuse_buffers)
		{
			state->cur_pos = state->start_pos;
			return VOD_OK;
		}
	}

	if (reallocate)
	{
		state->start_pos = vod_alloc(state->request_context->pool, WRITE_BUFFER_SIZE);
		if (state->start_pos == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
				"write_buffer_flush: vod_alloc failed");
			return VOD_ALLOC_FAILED;
		}
		state->end_pos = state->start_pos + WRITE_BUFFER_SIZE;
		state->cur_pos = state->start_pos;
	}
	else
	{
		state->start_pos = state->end_pos = state->cur_pos = NULL;
	}

	return VOD_OK;
}
Пример #3
0
vod_status_t
audio_encoder_update_media_info(
	void* context,
	media_info_t* media_info)
{
	audio_encoder_state_t* state = context;
	AVCodecContext *encoder = state->encoder;
	u_char* new_extra_data;

	if (encoder->time_base.num != 1)
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_encoder_update_media_info: unexpected encoder time base %d/%d",
			encoder->time_base.num, encoder->time_base.den);
		return VOD_UNEXPECTED;
	}

	media_info->timescale = encoder->time_base.den;
	media_info->bitrate = encoder->bit_rate;

	media_info->u.audio.object_type_id = 0x40;		// ffmpeg always writes 0x40 (ff_mp4_obj_type)
	media_info->u.audio.channels = encoder->channels;
	media_info->u.audio.bits_per_sample = AUDIO_ENCODER_BITS_PER_SAMPLE;
	media_info->u.audio.packet_size = 0;			// ffmpeg always writes 0 (mov_write_audio_tag)
	media_info->u.audio.sample_rate = encoder->sample_rate;

	new_extra_data = vod_alloc(state->request_context->pool, encoder->extradata_size);
	if (new_extra_data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
			"audio_encoder_update_media_info: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	vod_memcpy(new_extra_data, encoder->extradata, encoder->extradata_size);

	media_info->extra_data.data = new_extra_data;
	media_info->extra_data.len = encoder->extradata_size;

	return VOD_OK;
}
Пример #4
0
void*
buffer_pool_alloc(request_context_t* request_context, buffer_pool_t* buffer_pool, size_t* buffer_size)
{
	buffer_pool_cleanup_t* buf_cln;
	vod_pool_cleanup_t* cln;
	void* result;

	if (buffer_pool == NULL)
	{
		return vod_alloc(request_context->pool, *buffer_size);
	}

	if (buffer_pool->head == NULL)
	{
		*buffer_size = buffer_pool->size;
		return vod_alloc(request_context->pool, *buffer_size);
	}

	cln = vod_pool_cleanup_add(request_context->pool, sizeof(buffer_pool_cleanup_t));
	if (cln == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"buffer_pool_alloc: vod_pool_cleanup_add failed");
		return NULL;
	}

	result = buffer_pool->head;
	buffer_pool->head = next_buffer(result);

	cln->handler = buffer_pool_buffer_cleanup;

	buf_cln = cln->data;
	buf_cln->buffer = result;
	buf_cln->buffer_pool = buffer_pool;

	*buffer_size = buffer_pool->size;

	return result;
}
Пример #5
0
static vod_status_t
mp4_metadata_reader_init(
	request_context_t* request_context, 
	vod_str_t* buffer, 
	size_t initial_read_size,
	size_t max_metadata_size,
	void** ctx)
{
	mp4_read_metadata_state_t* state;
	bool_t atom_found = FALSE;

	mp4_parser_parse_atoms(
		request_context,
		buffer->data,
		buffer->len,
		FALSE,
		mp4_reader_identify_callback,
		&atom_found);
	if (!atom_found)
	{
		return VOD_NOT_FOUND;
	}

	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mp4_metadata_reader_init: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	state->request_context = request_context;
	state->moov_start_reads = MAX_MOOV_START_READS;
	state->max_moov_size = max_metadata_size;
	state->state = STATE_READ_MOOV_HEADER;
	state->parts[MP4_METADATA_PART_FTYP].len = 0;
	*ctx = state;
	return VOD_OK;
}
vod_status_t
frame_joiner_init(
	media_filter_t* filter,
	media_filter_context_t* context)
{
	frame_joiner_t* state;
	request_context_t* request_context = context->request_context;

	// allocate state
	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"frame_joiner_init: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	state->frame_dts = NO_TIMESTAMP;

	// save required functions
	state->start_frame = filter->start_frame;
	state->flush_frame = filter->flush_frame;
	state->simulated_start_frame = filter->simulated_start_frame;
	state->simulated_flush_frame = filter->simulated_flush_frame;

	// override functions
	filter->start_frame = frame_joiner_start_frame;
	filter->flush_frame = frame_joiner_flush_frame;
	filter->simulated_start_frame = frame_joiner_simulated_start_frame;
	filter->simulated_flush_frame = frame_joiner_simulated_flush_frame;

	// save the context
	context->context[THIS_FILTER] = state;

	return VOD_OK;
}
Пример #7
0
vod_status_t
manifest_utils_get_adaptation_sets(
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t flags,
	adaptation_sets_t* output)
{
	label_track_count_t* last_label;
	label_track_count_t temp_label;
	label_track_count_array_t labels[MEDIA_TYPE_COUNT];
	vod_status_t rc;

	rc = manifest_utils_get_unique_labels(
		request_context,
		media_set,
		MEDIA_TYPE_AUDIO,
		&labels[MEDIA_TYPE_AUDIO]);
	if (rc != VOD_OK)
	{
		return rc;
	}

	if (media_set->track_count[MEDIA_TYPE_SUBTITLE] > 0 &&
		media_set->track_count[MEDIA_TYPE_VIDEO] > 0)		// ignore subtitles if there is no video
	{
		rc = manifest_utils_get_unique_labels(
			request_context,
			media_set,
			MEDIA_TYPE_SUBTITLE,
			&labels[MEDIA_TYPE_SUBTITLE]);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}
	else
	{
		labels[MEDIA_TYPE_SUBTITLE].first = NULL;
		labels[MEDIA_TYPE_SUBTITLE].last = NULL;
		labels[MEDIA_TYPE_SUBTITLE].count = 0;
	}

	if (labels[MEDIA_TYPE_AUDIO].count > 1)
	{
		if ((flags & ADAPTATION_SETS_FLAG_DEFAULT_LANG_LAST) != 0)
		{
			last_label = labels[MEDIA_TYPE_AUDIO].last - 1;
			temp_label = *labels[MEDIA_TYPE_AUDIO].first;
			*labels[MEDIA_TYPE_AUDIO].first = *last_label;
			*last_label = temp_label;
		}

		rc = manifest_utils_get_multilingual_adaptation_sets(
			request_context,
			media_set,
			flags,
			labels,
			output);
	}
	else if ((flags & (ADAPTATION_SETS_FLAG_MUXED | ADAPTATION_SETS_FLAG_FORCE_MUXED)) != 0)
	{
		// cannot generate muxed media set if there are only subtitles
		if (media_set->track_count[MEDIA_TYPE_VIDEO] + media_set->track_count[MEDIA_TYPE_AUDIO] <= 0)
		{
			vod_log_error(VOD_LOG_ERR, request_context->log, 0,
				"manifest_utils_get_adaptation_sets: no audio/video tracks");
			return VOD_BAD_REQUEST;
		}

		output->count[ADAPTATION_TYPE_MUXED] = 1;
		output->count[ADAPTATION_TYPE_AUDIO] = 0;
		output->count[ADAPTATION_TYPE_VIDEO] = 0;
		output->count[ADAPTATION_TYPE_SUBTITLE] = labels[MEDIA_TYPE_SUBTITLE].count;
		output->total_count = 1 + output->count[ADAPTATION_TYPE_SUBTITLE];

		output->first = vod_alloc(request_context->pool, output->total_count * sizeof(output->first[0]));
		if (output->first == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"manifest_utils_get_adaptation_sets: vod_alloc failed");
			return VOD_ALLOC_FAILED;
		}

		output->last = output->first + output->total_count;
		output->first_by_type[ADAPTATION_TYPE_MUXED] = output->first;

		rc = manifest_utils_get_muxed_adaptation_set(
			request_context,
			media_set,
			flags,
			NULL,
			output->first);
		if (rc != VOD_OK)
		{
			return rc;
		}

		if (labels[MEDIA_TYPE_SUBTITLE].count > 0)
		{
			output->first_by_type[ADAPTATION_TYPE_SUBTITLE] = output->first + 1;
			manifest_utils_add_subtitle_adaptation_sets(
				media_set,
				&labels[MEDIA_TYPE_SUBTITLE],
				output->first_by_type[ADAPTATION_TYPE_SUBTITLE],
				output->first->last);
		}
	}
	else
	{
		rc = manifest_utils_get_unmuxed_adaptation_sets(
			request_context,
			media_set,
			&labels[MEDIA_TYPE_SUBTITLE],
			output);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}

	return VOD_OK;
}
Пример #8
0
static vod_status_t
manifest_utils_get_multilingual_adaptation_sets(
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t flags,
	label_track_count_array_t* labels,
	adaptation_sets_t* output)
{
	adaptation_set_t* cur_adaptation_set;
	adaptation_set_t* adaptation_sets;
	media_track_t** cur_track_ptr;
	media_track_t* last_track;
	media_track_t* cur_track;
	label_track_count_t* cur_label;
	vod_status_t rc;
	uint32_t media_type;
	size_t adaptation_sets_count;
	size_t index;

	// get the number of adaptation sets
	adaptation_sets_count = labels[MEDIA_TYPE_AUDIO].count + labels[MEDIA_TYPE_SUBTITLE].count;
	output->count[ADAPTATION_TYPE_SUBTITLE] = labels[MEDIA_TYPE_SUBTITLE].count;
	if (media_set->track_count[MEDIA_TYPE_VIDEO] > 0)
	{
		if ((flags & ADAPTATION_SETS_FLAG_FORCE_MUXED) != 0)
		{
			output->count[ADAPTATION_TYPE_MUXED] = 1;
			output->count[ADAPTATION_TYPE_VIDEO] = 0;
			output->count[ADAPTATION_TYPE_AUDIO] = labels[MEDIA_TYPE_AUDIO].count - 1;
		}
		else
		{
			adaptation_sets_count++;
			output->count[ADAPTATION_TYPE_MUXED] = 0;
			output->count[ADAPTATION_TYPE_VIDEO] = 1;
			output->count[ADAPTATION_TYPE_AUDIO] = labels[MEDIA_TYPE_AUDIO].count;
		}
	}
	else
	{
		output->count[ADAPTATION_TYPE_MUXED] = 0;
		output->count[ADAPTATION_TYPE_VIDEO] = 0;
		output->count[ADAPTATION_TYPE_AUDIO] = labels[MEDIA_TYPE_AUDIO].count;
	}

	// allocate the adaptation sets and tracks
	adaptation_sets = vod_alloc(request_context->pool,
		sizeof(adaptation_sets[0]) * adaptation_sets_count +
		sizeof(adaptation_sets[0].first[0]) * media_set->total_track_count);
	if (adaptation_sets == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"manifest_utils_get_multilingual_adaptation_sets: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	cur_track_ptr = (void*)(adaptation_sets + adaptation_sets_count);
	cur_adaptation_set = adaptation_sets;

	// initialize the video adaptation set
	if (media_set->track_count[MEDIA_TYPE_VIDEO] > 0)
	{
		if (output->count[ADAPTATION_TYPE_MUXED] > 0)
		{
			output->first_by_type[ADAPTATION_TYPE_MUXED] = cur_adaptation_set;
			rc = manifest_utils_get_muxed_adaptation_set(
				request_context,
				media_set,
				flags,
				&labels[MEDIA_TYPE_AUDIO].first->label,
				cur_adaptation_set);
			if (rc != VOD_OK)
			{
				return rc;
			}
			cur_adaptation_set++;
			labels[MEDIA_TYPE_AUDIO].first++;		// do not output this label separately
		}
		else
		{
			output->first_by_type[ADAPTATION_TYPE_VIDEO] = cur_adaptation_set;
			cur_adaptation_set->first = cur_track_ptr;
			cur_adaptation_set->count = 0;
			cur_adaptation_set->type = ADAPTATION_TYPE_VIDEO;
			cur_track_ptr += media_set->track_count[MEDIA_TYPE_VIDEO];
			cur_adaptation_set->last = cur_track_ptr;
			cur_adaptation_set++;
		}
	}

	// initialize the audio/subtitle adaptation sets
	for (media_type = MEDIA_TYPE_AUDIO; media_type <= MEDIA_TYPE_SUBTITLE; media_type++)
	{
		output->first_by_type[media_type] = cur_adaptation_set;

		for (cur_label = labels[media_type].first; cur_label < labels[media_type].last; cur_label++)
		{
			cur_adaptation_set->first = cur_track_ptr;
			cur_adaptation_set->count = 0;
			cur_adaptation_set->type = media_type;
			if (media_type != MEDIA_TYPE_AUDIO || (flags & ADAPTATION_SETS_FLAG_SINGLE_LANG_TRACK) != 0)
			{
				cur_track_ptr++;
			}
			else
			{
				cur_track_ptr += cur_label->track_count;
			}
			cur_adaptation_set->last = cur_track_ptr;
			cur_adaptation_set++;
		}
	}

	last_track = media_set->filtered_tracks + media_set->total_track_count;
	for (cur_track = media_set->filtered_tracks; cur_track < last_track; cur_track++)
	{
		media_type = cur_track->media_info.media_type;
		switch (media_type)
		{
		case MEDIA_TYPE_AUDIO:
		case MEDIA_TYPE_SUBTITLE:
			if (cur_track->media_info.label.len == 0)
			{
				continue;
			}

			// find the label index
			cur_label = manifest_utils_find_label(
				&cur_track->media_info.label,
				&labels[media_type],
				&index);
			if (cur_label == NULL)
			{
				continue;
			}

			// find the adaptation set
			cur_adaptation_set = output->first_by_type[media_type] + index;

			if ((media_type != MEDIA_TYPE_AUDIO || (flags & ADAPTATION_SETS_FLAG_SINGLE_LANG_TRACK) != 0) &&
				cur_adaptation_set->count != 0)
			{
				continue;
			}
			break;

		case MEDIA_TYPE_VIDEO:
			// in forced muxed mode, all video tracks were already added
			if (output->count[ADAPTATION_TYPE_MUXED] > 0)
			{
				continue;
			}

			cur_adaptation_set = adaptation_sets;
			break;

		default:
			continue;
		}

		// add the track to the adaptation set
		cur_adaptation_set->first[cur_adaptation_set->count++] = cur_track;
	}

	output->first = adaptation_sets;
	output->last = adaptation_sets + adaptation_sets_count;
	output->total_count = adaptation_sets_count;

	return VOD_OK;
}
Пример #9
0
static vod_status_t
manifest_utils_build_request_params_string_per_sequence_tracks(
	request_context_t* request_context,
	uint32_t segment_index,
	uint32_t sequences_mask,
	uint32_t* sequence_tracks_mask,
	vod_str_t* result)
{
	u_char* p;
	size_t result_size;
	uint32_t* tracks_mask;
	uint32_t i;

	result_size = 0;

	// segment index
	if (segment_index != INVALID_SEGMENT_INDEX)
	{
		result_size += 1 + vod_get_int_print_len(segment_index + 1);
	}

	for (i = 0, tracks_mask = sequence_tracks_mask;
		i < MAX_SEQUENCES;
		i++, tracks_mask += MEDIA_TYPE_COUNT)
	{
		if ((sequences_mask & (1 << i)) == 0)
		{
			continue;
		}

		// sequence
		result_size += sizeof("-f32") - 1;

		// video tracks
		if (tracks_mask[MEDIA_TYPE_VIDEO] == 0xffffffff)
		{
			result_size += sizeof("-v0") - 1;
		}
		else
		{
			result_size += vod_get_number_of_set_bits(tracks_mask[MEDIA_TYPE_VIDEO]) * (sizeof("-v32") - 1);
		}

		// audio tracks
		if (tracks_mask[MEDIA_TYPE_AUDIO] == 0xffffffff)
		{
			result_size += sizeof("-a0") - 1;
		}
		else
		{
			result_size += vod_get_number_of_set_bits(tracks_mask[MEDIA_TYPE_AUDIO]) * (sizeof("-a32") - 1);
		}
	}

	p = vod_alloc(request_context->pool, result_size + 1);
	if (p == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"manifest_utils_build_request_params_string_per_sequence_tracks: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	result->data = p;

	// segment index
	if (segment_index != INVALID_SEGMENT_INDEX)
	{
		p = vod_sprintf(p, "-%uD", segment_index + 1);
	}

	for (i = 0, tracks_mask = sequence_tracks_mask;
		i < MAX_SEQUENCES;
		i++, tracks_mask += MEDIA_TYPE_COUNT)
	{
		if ((sequences_mask & (1 << i)) == 0)
		{
			continue;
		}

		// sequence
		p = vod_sprintf(p, "-f%uD", i + 1);

		// video tracks
		switch (tracks_mask[MEDIA_TYPE_VIDEO])
		{
		case 0xffffffff:
			p = vod_copy(p, "-v0", sizeof("-v0") - 1);
			break;

		case 0:
			break;

		default:
			p = manifest_utils_write_bitmask(p, tracks_mask[MEDIA_TYPE_VIDEO], 'v');
			break;
		}

		// audio tracks
		switch (tracks_mask[MEDIA_TYPE_AUDIO])
		{
		case 0xffffffff:
			p = vod_copy(p, "-a0", sizeof("-a0") - 1);
			break;

		case 0:
			break;

		default:
			p = manifest_utils_write_bitmask(p, tracks_mask[MEDIA_TYPE_AUDIO], 'a');
			break;
		}
	}

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"manifest_utils_build_request_params_string_per_sequence_tracks: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
Пример #10
0
vod_status_t
manifest_utils_build_request_params_string(
	request_context_t* request_context,
	uint32_t* has_tracks,
	uint32_t segment_index,
	uint32_t sequences_mask,
	uint32_t* sequence_tracks_mask,
	uint32_t* tracks_mask,
	vod_str_t* suffix,
	vod_str_t* args_str,
	vod_str_t* result)
{
	u_char* p;
	size_t result_size;
	bool_t newline_shift = FALSE;

	if (sequence_tracks_mask != NULL)
	{
		return manifest_utils_build_request_params_string_per_sequence_tracks(
			request_context,
			segment_index,
			sequences_mask,
			sequence_tracks_mask,
			result);
	}

	result_size = suffix->len + args_str->len;

	if (suffix->data[suffix->len - 1] == '\n')
	{
		newline_shift = TRUE;
	}

	// segment index
	if (segment_index != INVALID_SEGMENT_INDEX)
	{
		result_size += 1 + vod_get_int_print_len(segment_index + 1);
	}

	// sequence mask
	if (sequences_mask != 0xffffffff)
	{
		 result_size += vod_get_number_of_set_bits(sequences_mask) * (sizeof("-f32") - 1);
	}

	// video tracks
	if (tracks_mask[MEDIA_TYPE_VIDEO] == 0xffffffff)
	{
		result_size += sizeof("-v0") - 1;
	}
	else
	{
		result_size += vod_get_number_of_set_bits(tracks_mask[MEDIA_TYPE_VIDEO]) * (sizeof("-v32") - 1);
	}

	// audio tracks
	if (tracks_mask[MEDIA_TYPE_AUDIO] == 0xffffffff)
	{
		result_size += sizeof("-a0") - 1;
	}
	else
	{
		result_size += vod_get_number_of_set_bits(tracks_mask[MEDIA_TYPE_AUDIO]) * (sizeof("-a32") - 1);
	}

	p = vod_alloc(request_context->pool, result_size + 1);
	if (p == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"manifest_utils_build_request_params_string: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	result->data = p;

	// segment index
	if (segment_index != INVALID_SEGMENT_INDEX)
	{
		p = vod_sprintf(p, "-%uD", segment_index + 1);
	}

	// sequence mask
	if (sequences_mask != 0xffffffff)
	{
		p = manifest_utils_write_bitmask(p, sequences_mask, 'f');
	}

	// video tracks
	if (has_tracks[MEDIA_TYPE_VIDEO])
	{
		if (tracks_mask[MEDIA_TYPE_VIDEO] == 0xffffffff)
		{
			p = vod_copy(p, "-v0", sizeof("-v0") - 1);
		}
		else
		{
			p = manifest_utils_write_bitmask(p, tracks_mask[MEDIA_TYPE_VIDEO], 'v');
		}
	}

	// audio tracks
	if (has_tracks[MEDIA_TYPE_AUDIO])
	{
		if (tracks_mask[MEDIA_TYPE_AUDIO] == 0xffffffff)
		{
			p = vod_copy(p, "-a0", sizeof("-a0") - 1);
		}
		else
		{
			p = manifest_utils_write_bitmask(p, tracks_mask[MEDIA_TYPE_AUDIO], 'a');
		}
	}

	p = vod_copy(p, suffix->data, newline_shift ? suffix->len - 1 : suffix->len);
	p = vod_copy(p, args_str->data, args_str->len);

	if (newline_shift)
	{
		*p++ = '\n';
	}

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"manifest_utils_build_request_params_string: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
Пример #11
0
static vod_status_t 
hls_muxer_init_base(
	hls_muxer_state_t* state,
	request_context_t* request_context,
	hls_muxer_conf_t* conf,
	hls_encryption_params_t* encryption_params,
	uint32_t segment_index,
	media_set_t* media_set,
	write_callback_t write_callback,
	void* write_context,
	bool_t* simulation_supported, 
	vod_str_t* response_header)
{
	mpegts_encoder_init_streams_state_t init_streams_state;
	media_track_t* track;
	hls_muxer_stream_state_t* cur_stream;
	const media_filter_t* next_filter;
	void* next_filter_context;
	vod_status_t rc;
	bool_t reuse_buffers;

	*simulation_supported = hls_muxer_simulation_supported(media_set, encryption_params);

	state->request_context = request_context;
	state->cur_frame = NULL;
	state->video_duration = 0;
	state->first_time = TRUE;

	state->media_set = media_set;
	state->use_discontinuity = media_set->use_discontinuity;

	if (encryption_params->type == HLS_ENC_AES_128)
	{
		rc = aes_cbc_encrypt_init(
			&state->encrypted_write_context,
			request_context,
			write_callback,
			write_context,
			encryption_params->key,
			encryption_params->iv);

		write_callback = (write_callback_t)aes_cbc_encrypt_write;
		write_context = state->encrypted_write_context;
		reuse_buffers = TRUE;		// aes_cbc_encrypt allocates new buffers
	}
	else
	{
		state->encrypted_write_context = NULL;
		reuse_buffers = FALSE;
	}

	// init the write queue
	write_buffer_queue_init(
		&state->queue,
		request_context,
		write_callback,
		write_context,
		reuse_buffers);

	// init the packetizer streams and get the packet ids / stream ids
	rc = mpegts_encoder_init_streams(
		request_context,
		encryption_params,
		&state->queue,
		&init_streams_state,
		segment_index);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// allocate the streams
	state->first_stream = vod_alloc(request_context->pool, 
		sizeof(*state->first_stream) * (media_set->total_track_count + 1));
	if (state->first_stream == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hls_muxer_init_base: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	state->last_stream = state->first_stream + media_set->total_track_count;

	track = media_set->filtered_tracks;
	for (cur_stream = state->first_stream; cur_stream < state->last_stream; cur_stream++, track++)
	{
		cur_stream->segment_limit = ULLONG_MAX;

		rc = mpegts_encoder_init(
			&cur_stream->mpegts_encoder_state,
			&init_streams_state,
			track,
			&state->queue,
			conf->interleave_frames,
			conf->align_frames);
		if (rc != VOD_OK)
		{
			return rc;
		}

		switch (track->media_info.media_type)
		{
		case MEDIA_TYPE_VIDEO:
			if (track->media_info.duration_millis > state->video_duration)
			{
				state->video_duration = track->media_info.duration_millis;
			}

			cur_stream->buffer_state = NULL;
			cur_stream->top_filter_context = vod_alloc(request_context->pool, sizeof(mp4_to_annexb_state_t));
			if (cur_stream->top_filter_context == NULL)
			{
				vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
					"hls_muxer_init_base: vod_alloc failed (2)");
				return VOD_ALLOC_FAILED;
			}

			rc = mp4_to_annexb_init(
				cur_stream->top_filter_context,
				request_context,
				encryption_params,
				&mpegts_encoder,
				&cur_stream->mpegts_encoder_state);
			if (rc != VOD_OK)
			{
				return rc;
			}

			cur_stream->top_filter = &mp4_to_annexb;
			break;

		case MEDIA_TYPE_AUDIO:
			if (conf->interleave_frames)
			{
				// frame interleaving enabled, just join several audio frames according to timestamp
				cur_stream->buffer_state = NULL;

				next_filter_context = vod_alloc(request_context->pool, sizeof(frame_joiner_t));
				if (next_filter_context == NULL)
				{
					vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
						"hls_muxer_init_base: vod_alloc failed (3)");
					return VOD_ALLOC_FAILED;
				}

				frame_joiner_init(next_filter_context, &mpegts_encoder, &cur_stream->mpegts_encoder_state);

				next_filter = &frame_joiner;
			}
			else
			{
				// no frame interleaving, buffer the audio until it reaches a certain size / delay from video
				cur_stream->buffer_state = vod_alloc(request_context->pool, sizeof(buffer_filter_t));
				if (cur_stream->buffer_state == NULL)
				{
					vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
						"hls_muxer_init_base: vod_alloc failed (4)");
					return VOD_ALLOC_FAILED;
				}

				rc = buffer_filter_init(
					cur_stream->buffer_state,
					request_context,
					&mpegts_encoder,
					&cur_stream->mpegts_encoder_state,
					conf->align_frames,
					DEFAULT_PES_PAYLOAD_SIZE);
				if (rc != VOD_OK)
				{
					return rc;
				}

				next_filter = &buffer_filter;
				next_filter_context = cur_stream->buffer_state;
			}

			if (track->media_info.codec_id == VOD_CODEC_ID_AAC)
			{
				cur_stream->top_filter_context = vod_alloc(request_context->pool, sizeof(adts_encoder_state_t));
				if (cur_stream->top_filter_context == NULL)
				{
					vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
						"hls_muxer_init_base: vod_alloc failed (5)");
					return VOD_ALLOC_FAILED;
				}

				rc = adts_encoder_init(
					cur_stream->top_filter_context,
					request_context,
					encryption_params,
					next_filter,
					next_filter_context);
				if (rc != VOD_OK)
				{
					return rc;
				}

				cur_stream->top_filter = &adts_encoder;
			}
			else
			{
				if (encryption_params->type == HLS_ENC_SAMPLE_AES)
				{
					vod_log_error(VOD_LOG_ERR, request_context->log, 0,
						"hls_muxer_init_base: sample aes encryption is supported only for aac");
					return VOD_BAD_REQUEST;
				}

				cur_stream->top_filter = next_filter;
				cur_stream->top_filter_context = next_filter_context;
			}
			break;
		}

		rc = hls_muxer_init_track(cur_stream, track);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}

	state->first_clip_track = track;

	// init the id3 stream
	rc = hls_muxer_init_id3_stream(state, conf, &init_streams_state);
	if (rc != VOD_OK)
	{
		return rc;
	}

	mpegts_encoder_finalize_streams(&init_streams_state, response_header);

	if (media_set->timing.durations != NULL)
	{
		state->video_duration = media_set->timing.total_duration;
	}

	return VOD_OK;
}
Пример #12
0
vod_status_t
hds_packager_build_manifest(
	request_context_t* request_context,
	hds_manifest_config_t* conf,
	vod_str_t* base_url,
	vod_str_t* manifest_id,
	media_set_t* media_set,
	bool_t drm_enabled,
	vod_str_t* result)
{
	hds_segment_durations_t* segment_durations;
	adaptation_sets_t adaptation_sets;
	adaptation_set_t* adaptation_set;
	segmenter_conf_t* segmenter_conf = media_set->segmenter_conf;
	media_sequence_t* cur_sequence;
	media_track_t** tracks;
	media_track_t** cur_track_ptr;
	media_track_t* tracks_array[MEDIA_TYPE_COUNT];
	media_track_t* track;
	vod_str_t* drm_metadata;
	uint32_t initial_muxed_tracks;
	uint32_t muxed_tracks;
	uint32_t media_count;
	uint32_t bitrate;
	uint32_t index;
	uint32_t abst_atom_size;
	uint32_t max_abst_atom_size = 0;
	size_t result_size;
	vod_status_t rc;
	u_char* temp_buffer;
	u_char* p;

	// get the adaptations sets
	rc = manifest_utils_get_adaptation_sets(
		request_context, 
		media_set, 
		ADAPTATION_SETS_FLAG_FORCE_MUXED | 
		ADAPTATION_SETS_FLAG_SINGLE_LANG_TRACK | 
		ADAPTATION_SETS_FLAG_AVOID_AUDIO_ONLY,
		&adaptation_sets);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// allocate the segment durations
	media_count = adaptation_sets.first->count + adaptation_sets.total_count - 1;
	segment_durations = vod_alloc(request_context->pool, sizeof(segment_durations[0]) * media_count);
	if (segment_durations == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_packager_build_manifest: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	// calculate the result size
	result_size = 
		sizeof(HDS_MANIFEST_HEADER) - 1 + manifest_id->len + 
		sizeof(HDS_MANIFEST_HEADER_BASE_URL) - 1 + base_url->len +
		sizeof(HDS_MANIFEST_HEADER_LANG) - 1 + LANG_ISO639_2_LEN +
		sizeof(HDS_MANIFEST_FOOTER);

	switch (media_set->type)
	{
	case MEDIA_SET_VOD:
		result_size += 
			sizeof(HDS_MANIFEST_HEADER_VOD) - 1 + 2 * VOD_INT32_LEN + 
			(sizeof(HDS_BOOTSTRAP_VOD_HEADER) - 1 + VOD_INT32_LEN +
			sizeof(HDS_BOOTSTRAP_VOD_FOOTER) - 1) * media_count;
		break;

	case MEDIA_SET_LIVE:
		result_size += 
			sizeof(HDS_MANIFEST_HEADER_LIVE) - 1 + 
			(sizeof(HDS_BOOTSTRAP_LIVE_PREFIX) - 1 + VOD_INT32_LEN +
			conf->bootstrap_file_name_prefix.len +
			MANIFEST_UTILS_TRACKS_SPEC_MAX_SIZE +
			sizeof(HDS_BOOTSTRAP_LIVE_SUFFIX) - 1) * media_count;
		break;
	}

	if (drm_enabled)
	{
		result_size += 
			(sizeof(HDS_DRM_ADDITIONAL_HEADER_PREFIX) - 1 + VOD_INT32_LEN +
			sizeof(HDS_DRM_ADDITIONAL_HEADER_SUFFIX) - 1) * media_count;
	}

	result_size +=
		(vod_max(sizeof(HDS_MEDIA_HEADER_PREFIX_VIDEO) - 1 + 3 * VOD_INT32_LEN, 
			sizeof(HDS_MEDIA_HEADER_PREFIX_AUDIO_LANG) - 1 + VOD_INT32_LEN + LANG_ISO639_2_LEN) +
		conf->fragment_file_name_prefix.len +
		MANIFEST_UTILS_TRACKS_SPEC_MAX_SIZE + 1 +		// 1 = -
		sizeof(HDS_MEDIA_HEADER_SUFFIX_DRM) - 1 + 2 * VOD_INT32_LEN +
		vod_base64_encoded_length(amf0_max_total_size) +
		sizeof(HDS_MEDIA_FOOTER) - 1) * media_count;

	initial_muxed_tracks = adaptation_sets.first->type == ADAPTATION_TYPE_MUXED ? MEDIA_TYPE_COUNT : 1;

	muxed_tracks = initial_muxed_tracks;
	index = 0;
	for (adaptation_set = adaptation_sets.first; adaptation_set < adaptation_sets.last; adaptation_set++)
	{
		if (adaptation_set->type == ADAPTATION_TYPE_MUXED)
		{
			if (adaptation_set->first[MEDIA_TYPE_AUDIO] != NULL)
			{
				result_size += adaptation_set->first[MEDIA_TYPE_AUDIO]->media_info.label.len;
			}
		}
		else
		{
			result_size += adaptation_set->first[0]->media_info.label.len;
		}

		for (cur_track_ptr = adaptation_set->first; cur_track_ptr < adaptation_set->last; cur_track_ptr += muxed_tracks)
		{
			if (cur_track_ptr[0] != NULL)
			{
				cur_sequence = cur_track_ptr[0]->file_info.source->sequence;
			}
			else
			{
				cur_sequence = cur_track_ptr[1]->file_info.source->sequence;
			}

			switch (media_set->type)
			{
			case MEDIA_SET_VOD:
				rc = segmenter_conf->get_segment_durations(
					request_context,
					segmenter_conf,
					media_set,
					cur_sequence,		// XXXXX change to work with tracks instead of sequence
					MEDIA_TYPE_NONE,
					&segment_durations[index].durations);
				if (rc != VOD_OK)
				{
					return rc;
				}

				hds_scale_segment_durations(&segment_durations[index]);

				abst_atom_size = hds_get_abst_atom_size(media_set, &segment_durations[index]);
				if (abst_atom_size > max_abst_atom_size)
				{
					max_abst_atom_size = abst_atom_size;
				}

				result_size += vod_base64_encoded_length(abst_atom_size);

				index++;
				break;
			}

			if (drm_enabled)
			{
				drm_metadata = &((drm_info_t*)cur_sequence->drm_info)->pssh_array.first->data;

				result_size += drm_metadata->len;
			}
		}

		muxed_tracks = 1;
	}

	// allocate the buffers
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_packager_build_manifest: vod_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}

	temp_buffer = vod_alloc(request_context->pool, vod_max(amf0_max_total_size, max_abst_atom_size));
	if (temp_buffer == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_packager_build_manifest: vod_alloc failed (3)");
		return VOD_ALLOC_FAILED;
	}

	// print the manifest header
	p = vod_sprintf(result->data, HDS_MANIFEST_HEADER, manifest_id);

	if (base_url->len != 0)
	{
		p = vod_sprintf(p, HDS_MANIFEST_HEADER_BASE_URL, base_url);
	}

	switch (media_set->type)
	{
	case MEDIA_SET_VOD:
		p = vod_sprintf(p, HDS_MANIFEST_HEADER_VOD, 
			(uint32_t)(media_set->timing.total_duration / 1000),
			(uint32_t)(media_set->timing.total_duration % 1000));
		break;

	case MEDIA_SET_LIVE:
		p = vod_copy(p, HDS_MANIFEST_HEADER_LIVE, sizeof(HDS_MANIFEST_HEADER_LIVE) - 1);
		break;
	}

	if (adaptation_sets.total_count > 1)
	{
		if (adaptation_sets.first->type == ADAPTATION_TYPE_MUXED)
		{
			track = adaptation_sets.first->first[MEDIA_TYPE_AUDIO];
		}
		else
		{
			track = adaptation_sets.first->first[0];
		}

		p = vod_sprintf(p, HDS_MANIFEST_HEADER_LANG, 
			&track->media_info.label,
			lang_get_iso639_2t_name(track->media_info.language));
	}

	// bootstrap tags
	muxed_tracks = initial_muxed_tracks;
	tracks_array[MEDIA_TYPE_SUBTITLE] = NULL;
	index = 0;
	for (adaptation_set = adaptation_sets.first; adaptation_set < adaptation_sets.last; adaptation_set++)
	{
		for (cur_track_ptr = adaptation_set->first; cur_track_ptr < adaptation_set->last; cur_track_ptr += muxed_tracks)
		{
			switch (media_set->type)
			{
			case MEDIA_SET_VOD:
				p = vod_sprintf(p, HDS_BOOTSTRAP_VOD_HEADER, index);
				p = hds_write_base64_abst_atom(p, temp_buffer, media_set, &segment_durations[index]);
				p = vod_copy(p, HDS_BOOTSTRAP_VOD_FOOTER, sizeof(HDS_BOOTSTRAP_VOD_FOOTER) - 1);
				break;

			case MEDIA_SET_LIVE:
				// get the tracks
				if (muxed_tracks == 1)
				{
					if (cur_track_ptr[0]->media_info.media_type == MEDIA_TYPE_VIDEO)
					{
						tracks_array[MEDIA_TYPE_VIDEO] = cur_track_ptr[0];
						tracks_array[MEDIA_TYPE_AUDIO] = NULL;
					}
					else
					{
						tracks_array[MEDIA_TYPE_VIDEO] = NULL;
						tracks_array[MEDIA_TYPE_AUDIO] = cur_track_ptr[0];
					}
					tracks = tracks_array;
				}
				else
				{
					tracks = cur_track_ptr;
				}

				p = vod_sprintf(p, HDS_BOOTSTRAP_LIVE_PREFIX, index);
				p = vod_copy(p, conf->bootstrap_file_name_prefix.data, conf->bootstrap_file_name_prefix.len);
				p = manifest_utils_append_tracks_spec(p, tracks, MEDIA_TYPE_COUNT, media_set->has_multi_sequences);
				p = vod_copy(p, HDS_BOOTSTRAP_LIVE_SUFFIX, sizeof(HDS_BOOTSTRAP_LIVE_SUFFIX) - 1);
				break;
			}

			index++;
		}

		muxed_tracks = 1;
	}

	if (drm_enabled)
	{
		muxed_tracks = initial_muxed_tracks;
		index = 0;
		for (adaptation_set = adaptation_sets.first; adaptation_set < adaptation_sets.last; adaptation_set++)
		{
			for (cur_track_ptr = adaptation_set->first; cur_track_ptr < adaptation_set->last; cur_track_ptr += muxed_tracks)
			{
				if (cur_track_ptr[0] != NULL)
				{
					cur_sequence = cur_track_ptr[0]->file_info.source->sequence;
				}
				else
				{
					cur_sequence = cur_track_ptr[1]->file_info.source->sequence;
				}

				drm_metadata = &((drm_info_t*)cur_sequence->drm_info)->pssh_array.first->data;

				p = vod_sprintf(p, HDS_DRM_ADDITIONAL_HEADER_PREFIX, index);
				p = vod_copy(p, drm_metadata->data, drm_metadata->len);
				p = vod_copy(p, HDS_DRM_ADDITIONAL_HEADER_SUFFIX, sizeof(HDS_DRM_ADDITIONAL_HEADER_SUFFIX) - 1);

				index++;
			}

			muxed_tracks = 1;
		}
	}

	// media tags
	muxed_tracks = initial_muxed_tracks;
	index = 0;
	for (adaptation_set = adaptation_sets.first; adaptation_set < adaptation_sets.last; adaptation_set++)
	{
		for (cur_track_ptr = adaptation_set->first; cur_track_ptr < adaptation_set->last; cur_track_ptr += muxed_tracks)
		{
			// get the tracks
			if (muxed_tracks == 1)
			{
				if (cur_track_ptr[0]->media_info.media_type == MEDIA_TYPE_VIDEO)
				{
					tracks_array[MEDIA_TYPE_VIDEO] = cur_track_ptr[0];
					tracks_array[MEDIA_TYPE_AUDIO] = NULL;
				}
				else
				{
					tracks_array[MEDIA_TYPE_VIDEO] = NULL;
					tracks_array[MEDIA_TYPE_AUDIO] = cur_track_ptr[0];
				}
				tracks = tracks_array;
			}
			else
			{
				tracks = cur_track_ptr;
			}

			if (tracks[MEDIA_TYPE_VIDEO] != NULL)
			{
				bitrate = tracks[MEDIA_TYPE_VIDEO]->media_info.bitrate;
				if (tracks[MEDIA_TYPE_AUDIO] != NULL)
				{
					bitrate += tracks[MEDIA_TYPE_AUDIO]->media_info.bitrate;
				}

				p = vod_sprintf(p, HDS_MEDIA_HEADER_PREFIX_VIDEO,
					bitrate / 1000,
					(uint32_t)tracks[MEDIA_TYPE_VIDEO]->media_info.u.video.width,
					(uint32_t)tracks[MEDIA_TYPE_VIDEO]->media_info.u.video.height);
			}
			else
			{
				bitrate = tracks[MEDIA_TYPE_AUDIO]->media_info.bitrate;
				if (adaptation_sets.total_count > 1 && adaptation_set > adaptation_sets.first)
				{
					p = vod_sprintf(p, HDS_MEDIA_HEADER_PREFIX_AUDIO_LANG,
						bitrate / 1000, 
						&tracks[MEDIA_TYPE_AUDIO]->media_info.label, 
						lang_get_iso639_2t_name(tracks[MEDIA_TYPE_AUDIO]->media_info.language));
				}
				else
				{
					p = vod_sprintf(p, HDS_MEDIA_HEADER_PREFIX_AUDIO,
						bitrate / 1000);
				}
			}

			// url
			p = vod_copy(p, conf->fragment_file_name_prefix.data, conf->fragment_file_name_prefix.len);
			p = manifest_utils_append_tracks_spec(p, tracks, MEDIA_TYPE_COUNT, media_set->has_multi_sequences);
			*p++ = '-';

			if (drm_enabled)
			{
				p = vod_sprintf(p, HDS_MEDIA_HEADER_SUFFIX_DRM, index, index);
			}
			else
			{
				p = vod_sprintf(p, HDS_MEDIA_HEADER_SUFFIX, index);
			}

			p = hds_amf0_write_base64_metadata(p, temp_buffer, media_set, tracks);

			p = vod_copy(p, HDS_MEDIA_FOOTER, sizeof(HDS_MEDIA_FOOTER) - 1);

			index++;
		}

		muxed_tracks = 1;
	}

	// manifest footer
	p = vod_copy(p, HDS_MANIFEST_FOOTER, sizeof(HDS_MANIFEST_FOOTER) - 1);

	result->len = p - result->data;
	
	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"hds_packager_build_manifest: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	vod_free(request_context->pool, temp_buffer);

	return VOD_OK;
}
Пример #13
0
static vod_status_t
m3u8_builder_build_required_tracks_string(
	request_context_t* request_context, 
	media_set_t* media_set,
	uint32_t sequence_index,
	request_params_t* request_params,
	vod_str_t* tracks_spec)
{
	u_char* p;
	size_t result_size;
	uint32_t i;

	result_size = 0;
	if (request_params->tracks_mask[MEDIA_TYPE_VIDEO] != 0xffffffff)
	{
		result_size += vod_get_number_of_set_bits(request_params->tracks_mask[MEDIA_TYPE_VIDEO]) * (sizeof("-v32") - 1);
	}
	if (request_params->tracks_mask[MEDIA_TYPE_AUDIO] != 0xffffffff)
	{
		result_size += vod_get_number_of_set_bits(request_params->tracks_mask[MEDIA_TYPE_AUDIO]) * (sizeof("-a32") - 1);
	}
	if (sequence_index != INVALID_SEQUENCE_INDEX)
	{
		result_size += sizeof("-f") - 1 + vod_get_int_print_len(sequence_index + 1);
	}

	p = vod_alloc(request_context->pool, result_size + 1);
	if (p == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_required_tracks_string: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	tracks_spec->data = p;

	if (sequence_index != INVALID_SEQUENCE_INDEX)
	{
		p = vod_sprintf(p, "-f%uD", sequence_index + 1);
	}

	if (media_set->track_count[MEDIA_TYPE_VIDEO] != 0)
	{
		if (request_params->tracks_mask[MEDIA_TYPE_VIDEO] == 0xffffffff)
		{
			p = vod_copy(p, "-v0", sizeof("-v0") - 1);
		}
		else
		{
			for (i = 0; i < 32; i++)
			{
				if ((request_params->tracks_mask[MEDIA_TYPE_VIDEO] & (1 << i)) == 0)
				{
					continue;
				}

				p = vod_sprintf(p, "-v%uD", i + 1);
			}
		}
	}
	
	if (media_set->track_count[MEDIA_TYPE_AUDIO] != 0)
	{
		if (request_params->tracks_mask[MEDIA_TYPE_AUDIO] == 0xffffffff)
		{
			p = vod_copy(p, "-a0", sizeof("-a0") - 1);
		}
		else
		{
			for (i = 0; i < 32; i++)
			{
				if ((request_params->tracks_mask[MEDIA_TYPE_AUDIO] & (1 << i)) == 0)
				{
					continue;
				}

				p = vod_sprintf(p, "-a%uD", i + 1);
			}
		}
	}

	tracks_spec->len = p - tracks_spec->data;

	if (tracks_spec->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_required_tracks_string: result length %uz exceeded allocated length %uz", 
			tracks_spec->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
Пример #14
0
vod_status_t
m3u8_builder_build_master_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	vod_str_t* base_url,
	media_set_t* media_set,
	vod_str_t* result)
{
	media_sequence_t* cur_sequence;	
	media_info_t* video;
	media_info_t* audio = NULL;
	uint32_t sequence_index;
	uint32_t bitrate;
	u_char* p;
	size_t max_video_stream_inf;
	size_t result_size;
	uint32_t main_media_type;
	media_track_t* cur_track;
	media_track_t* audio_track;

	// calculate the result size
	max_video_stream_inf =
		sizeof(m3u8_stream_inf_video) - 1 + 3 * VOD_INT32_LEN + MAX_CODEC_NAME_SIZE +
		MAX_CODEC_NAME_SIZE + 1 +
		sizeof(m3u8_stream_inf_suffix) - 1;
	result_size = sizeof(m3u8_header);	
	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		main_media_type = cur_sequence->track_count[MEDIA_TYPE_VIDEO] != 0 ? MEDIA_TYPE_VIDEO : MEDIA_TYPE_AUDIO;
		for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < cur_sequence->filtered_clips[0].last_track; cur_track++)
		{			
			if (cur_track->media_info.media_type != main_media_type)
			{
				continue;
			}
			if (base_url->len != 0)
			{
				result_size += base_url->len + 1;
				if (cur_track->file_info.uri.len > 0)
				{
					result_size += cur_track->file_info.uri.len;
				}
				else
				{
					result_size += media_set->uri.len;
				}
			}
			result_size += max_video_stream_inf; // using only video since it's larger than audio
			result_size += conf->index_file_name_prefix.len;
			result_size += sizeof("-f-v-a") - 1 + VOD_INT32_LEN * 3;
			result_size += sizeof(m3u8_url_suffix) - 1;
		}
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_master_playlist: vod_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}

	// write the header

	p = vod_copy(result->data, m3u8_header, sizeof(m3u8_header) - 1);
	// write the streams
	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		audio_track = cur_sequence->filtered_clips[0].longest_track[MEDIA_TYPE_AUDIO];
		main_media_type = cur_sequence->track_count[MEDIA_TYPE_VIDEO] != 0 ? MEDIA_TYPE_VIDEO : MEDIA_TYPE_AUDIO;
		for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < cur_sequence->filtered_clips[0].last_track; cur_track++)
		{
			if (cur_track->media_info.media_type != main_media_type)
			{
				continue;
			}
			// write the track information
			if (main_media_type == MEDIA_TYPE_VIDEO)
			{
				video = &cur_track->media_info;
				bitrate = video->bitrate;
				if (audio_track != NULL)
				{
					audio = &audio_track->media_info;
					bitrate += audio->bitrate;
				}
				p = vod_sprintf(p, m3u8_stream_inf_video,
						bitrate,
						(uint32_t)video->u.video.width,
						(uint32_t)video->u.video.height,
						&video->codec_name);
				if (audio_track != NULL)
				{
					*p++ = ',';
					p = vod_copy(p, audio->codec_name.data, audio->codec_name.len);
				}
			}
			else
			{
				audio = &cur_track->media_info;
				p = vod_sprintf(p, m3u8_stream_inf_audio, audio->bitrate, &audio->codec_name);
			}
			p = vod_copy(p, m3u8_stream_inf_suffix, sizeof(m3u8_stream_inf_suffix) - 1);
			// write the track url
			sequence_index = cur_sequence->index;
			if (base_url->len != 0)
			{
				// absolute url only
				p = vod_copy(p, base_url->data, base_url->len);
				if (cur_track->file_info.uri.len != 0)
				{
					p = vod_copy(p, cur_track->file_info.uri.data, cur_track->file_info.uri.len);
					sequence_index = INVALID_SEQUENCE_INDEX;		// no need to pass the sequence index since we have a direct uri
				}
				else
				{
					p = vod_copy(p, media_set->uri.data, media_set->uri.len);
				}
				*p++ = '/';
			}

			p = vod_copy(p, conf->index_file_name_prefix.data, conf->index_file_name_prefix.len);
			if (media_set->has_multi_sequences && sequence_index != INVALID_SEQUENCE_INDEX)
			{
				p = vod_sprintf(p, "-f%uD", cur_sequence->index + 1);
			}

			if (main_media_type == MEDIA_TYPE_VIDEO)
			{
				p = vod_sprintf(p, "-v%uD", cur_track->index + 1);
			}

			if (audio_track != NULL)
			{
				p = vod_sprintf(p, "-a%uD", audio_track->index + 1);
			}

			p = vod_copy(p, m3u8_url_suffix, sizeof(m3u8_url_suffix) - 1);

		}
	}

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_master_playlist: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	return VOD_OK;
}
Пример #15
0
vod_status_t
m3u8_builder_build_index_playlist(
	request_context_t* request_context,
	m3u8_config_t* conf,
	vod_str_t* base_url,
	vod_str_t* segments_base_url,
	request_params_t* request_params,
	hls_encryption_params_t* encryption_params,
	media_set_t* media_set,
	vod_str_t* result)
{
	segment_durations_t segment_durations;
	segment_duration_item_t* cur_item;
	segment_duration_item_t* last_item;
	segmenter_conf_t* segmenter_conf = media_set->segmenter_conf;
	uint64_t duration_millis;
	uint32_t sequence_index;
	vod_str_t extinf;
	uint32_t segment_index;
	uint32_t last_segment_index;
	vod_str_t tracks_spec;
	uint32_t scale;
	size_t segment_length;
	size_t result_size;
	vod_status_t rc;
	u_char* p;

	sequence_index = media_set->has_multi_sequences ? media_set->sequences[0].index : INVALID_SEQUENCE_INDEX;

	// build the required tracks string
	rc = m3u8_builder_build_required_tracks_string(
		request_context,
		media_set,
		sequence_index,
		request_params,
		&tracks_spec);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the segment durations
	rc = segmenter_conf->get_segment_durations(
		request_context,
		segmenter_conf,
		media_set,
		NULL,
		MEDIA_TYPE_NONE,
		&segment_durations);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// get the required buffer length
	duration_millis = segment_durations.end_time - segment_durations.start_time;
	last_segment_index = media_set->initial_segment_index + segment_durations.segment_count;
	segment_length = sizeof("#EXTINF:.000,\n") - 1 + vod_get_int_print_len(vod_div_ceil(duration_millis, 1000)) +
		segments_base_url->len + conf->segment_file_name_prefix.len + 1 + vod_get_int_print_len(last_segment_index) + tracks_spec.len + sizeof(".ts\n") - 1;

	result_size =
		sizeof(M3U8_HEADER_PART1) + VOD_INT64_LEN +
		sizeof(M3U8_HEADER_VOD) +
		sizeof(M3U8_HEADER_PART2) + VOD_INT64_LEN + VOD_INT32_LEN +
		segment_length * segment_durations.segment_count +
		segment_durations.discontinuities * (sizeof(m3u8_discontinuity) - 1) +
		sizeof(m3u8_footer);

	if (encryption_params->type != HLS_ENC_NONE)
	{
		result_size +=
			sizeof(encryption_key_tag_part1) - 1 +
			sizeof(encryption_type_sample_aes) - 1 +
			sizeof(encryption_key_tag_part2) - 1 +
			base_url->len +
			conf->encryption_key_file_name.len +
			sizeof("-f") - 1 + VOD_INT32_LEN +
			sizeof(encryption_key_tag_part3) - 1;
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"m3u8_builder_build_index_playlist: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// write the header
	p = vod_sprintf(
		result->data,
		M3U8_HEADER_PART1,
		(segmenter_conf->max_segment_duration + 500) / 1000);

	if (media_set->type == MEDIA_SET_VOD)
	{
		p = vod_copy(p, M3U8_HEADER_VOD, sizeof(M3U8_HEADER_VOD) - 1);
	}

	if (encryption_params->type != HLS_ENC_NONE)
	{
		p = vod_copy(p, encryption_key_tag_part1, sizeof(encryption_key_tag_part1) - 1);
		switch (encryption_params->type)
		{
		case HLS_ENC_SAMPLE_AES:
			p = vod_copy(p, encryption_type_sample_aes, sizeof(encryption_type_sample_aes) - 1);
			break;

		default:		// HLS_ENC_AES_128
			p = vod_copy(p, encryption_type_aes_128, sizeof(encryption_type_aes_128) - 1);
			break;
		}
		p = vod_copy(p, encryption_key_tag_part2, sizeof(encryption_key_tag_part2) - 1);
		p = vod_copy(p, base_url->data, base_url->len);
		p = vod_copy(p, conf->encryption_key_file_name.data, conf->encryption_key_file_name.len);
		if (sequence_index != INVALID_SEQUENCE_INDEX)
		{
			p = vod_sprintf(p, "-f%uD", sequence_index + 1);
		}
		p = vod_copy(p, encryption_key_tag_part3, sizeof(encryption_key_tag_part3) - 1);
	}

	p = vod_sprintf(
		p,
		M3U8_HEADER_PART2,
		conf->m3u8_version, 
		media_set->initial_segment_index + 1);

	// write the segments
	scale = conf->m3u8_version >= 3 ? 1000 : 1;
	last_item = segment_durations.items + segment_durations.item_count;

	for (cur_item = segment_durations.items; cur_item < last_item; cur_item++)
	{
		segment_index = cur_item->segment_index;
		last_segment_index = segment_index + cur_item->repeat_count;

		if (cur_item->discontinuity)
		{
			p = vod_copy(p, m3u8_discontinuity, sizeof(m3u8_discontinuity) - 1);
		}

		// write the first segment
		extinf.data = p;
		p = m3u8_builder_append_extinf_tag(p, rescale_time(cur_item->duration, segment_durations.timescale, scale), scale);
		extinf.len = p - extinf.data;
		p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &tracks_spec);
		segment_index++;

		// write any additional segments
		for (; segment_index < last_segment_index; segment_index++)
		{
			p = vod_copy(p, extinf.data, extinf.len);
			p = m3u8_builder_append_segment_name(p, segments_base_url, &conf->segment_file_name_prefix, segment_index, &tracks_spec);
		}
	}

	// write the footer
	if (media_set->type == MEDIA_SET_VOD)
	{
		p = vod_copy(p, m3u8_footer, sizeof(m3u8_footer) - 1);
	}

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"m3u8_builder_build_index_playlist: result length %uz exceeded allocated length %uz", 
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
Пример #16
0
static vod_status_t
segmenter_get_segment_durations_estimate_internal(
	request_context_t* request_context,
	segmenter_conf_t* conf,
	media_set_t* media_set,
	media_sequence_t* sequence,
	uint32_t* clip_durations,
	uint32_t total_clip_count,
	uint64_t cur_clip_duration,
	segment_durations_t* result)
{
	align_to_key_frames_context_t align_context;
	segment_duration_item_t* cur_item;
	uint64_t clip_start_offset;
	uint64_t ignore;
	uint64_t next_clip_offset;
	uint64_t next_aligned_offset;
	uint64_t aligned_offset = 0;
	uint64_t clip_offset = 0;
	uint32_t* end_duration = clip_durations + total_clip_count;
	uint32_t* cur_duration = clip_durations;
	uint32_t bootstrap_segment_limit;
	uint32_t segment_index = media_set->initial_segment_index;
	uint32_t clip_segment_limit;
	uint32_t segment_duration;
	uint32_t alloc_count;
	bool_t discontinuity;

	if (sequence->key_frame_durations != NULL)
	{
		align_context.request_context = request_context;
		align_context.part = sequence->key_frame_durations;
		align_context.offset = sequence->first_key_frame_offset;
		align_context.cur_pos = align_context.part->first;
		alloc_count = conf->bootstrap_segments_count + total_clip_count +
			vod_div_ceil(result->end_time - result->start_time, conf->segment_duration);
	}
	else
	{
		vod_memzero(&align_context, sizeof(align_context));
		alloc_count = conf->bootstrap_segments_count + 2 * total_clip_count;
	}

	// allocate the result buffer
	result->items = vod_alloc(request_context->pool, sizeof(result->items[0]) * alloc_count);
	if (result->items == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"segmenter_get_segment_durations_estimate_internal: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	cur_item = result->items - 1;

	discontinuity = FALSE;
	for (;;)
	{
		// find the clip start offset
		segmenter_get_start_end_offsets(conf, segment_index, &clip_start_offset, &ignore);

		// get segment limit for the current clip
		clip_segment_limit = conf->get_segment_count(conf, clip_start_offset + cur_clip_duration);
		if (clip_segment_limit == INVALID_SEGMENT_COUNT)
		{
			vod_log_error(VOD_LOG_ERR, request_context->log, 0,
				"segmenter_get_segment_durations_estimate_internal: segment count is invalid");
			return VOD_BAD_DATA;
		}

		if (clip_segment_limit <= segment_index)
		{
			clip_segment_limit = segment_index + 1;
		}

		next_clip_offset = clip_offset + cur_clip_duration;

		// bootstrap segments
		bootstrap_segment_limit = vod_min(clip_segment_limit - 1, conf->bootstrap_segments_count);
		for (; segment_index < bootstrap_segment_limit; segment_index++)
		{
			segment_duration = conf->bootstrap_segments_durations[segment_index];

			clip_offset += segment_duration;

			if (sequence->key_frame_durations != NULL)
			{
				next_aligned_offset = segmenter_align_to_key_frames(&align_context, clip_offset, next_clip_offset);
				segment_duration = next_aligned_offset - aligned_offset;
				aligned_offset = next_aligned_offset;
			}

			if (cur_item < result->items || segment_duration != cur_item->duration || discontinuity)
			{
				cur_item++;
				cur_item->repeat_count = 0;
				cur_item->segment_index = segment_index;
				cur_item->duration = segment_duration;
				cur_item->discontinuity = discontinuity;
				discontinuity = FALSE;
			}
			cur_item->repeat_count++;
		}

		// remaining segments
		if (sequence->key_frame_durations != NULL)
		{
			for (; segment_index + 1 < clip_segment_limit; segment_index++)
			{
				clip_offset += conf->segment_duration;

				next_aligned_offset = segmenter_align_to_key_frames(&align_context, clip_offset, next_clip_offset);
				segment_duration = next_aligned_offset - aligned_offset;
				aligned_offset = next_aligned_offset;

				if (cur_item < result->items || segment_duration != cur_item->duration || discontinuity)
				{
					cur_item++;
					cur_item->repeat_count = 0;
					cur_item->segment_index = segment_index;
					cur_item->duration = segment_duration;
					cur_item->discontinuity = discontinuity;
					discontinuity = FALSE;
				}
				cur_item->repeat_count++;
			}

			clip_offset = aligned_offset; // the last segment duration should be calcuated according to the aligned offset
		}
		else if (segment_index + 1 < clip_segment_limit)
		{
			segment_duration = conf->segment_duration;
			if (cur_item < result->items || segment_duration != cur_item->duration || discontinuity)
			{
				cur_item++;
				cur_item->repeat_count = 0;
				cur_item->segment_index = segment_index;
				cur_item->duration = segment_duration;
				cur_item->discontinuity = discontinuity;
				discontinuity = FALSE;
			}
			cur_item->repeat_count += clip_segment_limit - segment_index - 1;

			clip_offset += (uint64_t)segment_duration * (clip_segment_limit - segment_index - 1);
			segment_index = clip_segment_limit - 1;
		}

		// last segment
		if (segment_index < clip_segment_limit && clip_offset < next_clip_offset)
		{
			segment_duration = next_clip_offset - clip_offset;
			if (cur_item < result->items || segment_duration != cur_item->duration || discontinuity)
			{
				cur_item++;
				cur_item->repeat_count = 0;
				cur_item->segment_index = segment_index;
				cur_item->duration = segment_duration;
				cur_item->discontinuity = discontinuity;
			}
			cur_item->repeat_count++;

			segment_index = clip_segment_limit;
		}

		// move to the next clip
		cur_duration++;
		if (cur_duration >= end_duration)
		{
			break;
		}

		clip_offset = next_clip_offset;
		cur_clip_duration = *cur_duration;

		// update clip_start_offset
		discontinuity = TRUE;
	}

	// finalize the result
	result->segment_count = clip_segment_limit - media_set->initial_segment_index;
	if (result->segment_count > MAX_SEGMENT_COUNT)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_segment_durations_estimate_internal: segment count %uD is invalid", result->segment_count);
		return VOD_BAD_MAPPING;
	}

	result->item_count = cur_item + 1 - result->items;
	result->timescale = 1000;
	result->discontinuities = total_clip_count - 1;

	return VOD_OK;
}
Пример #17
0
vod_status_t
hds_packager_build_manifest(
	request_context_t* request_context,
	hds_manifest_config_t* conf,
	vod_str_t* manifest_id,
	segmenter_conf_t* segmenter_conf,
	media_set_t* media_set,
	vod_str_t* result)
{
	media_track_t** cur_sequence_tracks;
	media_sequence_t* cur_sequence;
	media_track_t* track;
	segment_durations_t* segment_durations;
	uint32_t bitrate;
	uint32_t index;
	uint32_t abst_atom_size;
	uint32_t max_abst_atom_size = 0;
	size_t result_size;
	vod_status_t rc;
	u_char* temp_buffer;
	u_char* p;

	segment_durations = vod_alloc(
		request_context->pool, 
		media_set->sequence_count * sizeof(*segment_durations));
	if (segment_durations == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_packager_build_manifest: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	// calculate the result size
	result_size = 
		sizeof(HDS_MANIFEST_HEADER) - 1 + 2 * VOD_INT32_LEN + manifest_id->len + 
		sizeof(HDS_MANIFEST_FOOTER);

	index = 0;
	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		cur_sequence_tracks = cur_sequence->filtered_clips[0].longest_track;

		rc = segmenter_conf->get_segment_durations(
			request_context,
			segmenter_conf,
			media_set,
			cur_sequence,
			MEDIA_TYPE_NONE,
			&segment_durations[index]);
		if (rc != VOD_OK)
		{
			return rc;
		}

		abst_atom_size = ABST_BASE_ATOM_SIZE + (segment_durations[index].item_count + 1) * sizeof(afrt_entry_t);
		if (abst_atom_size > max_abst_atom_size)
		{
			max_abst_atom_size = abst_atom_size;
		}

		result_size += 
			sizeof(HDS_BOOTSTRAP_HEADER) - 1 + VOD_INT32_LEN + 
				vod_base64_encoded_length(abst_atom_size) +
			sizeof(HDS_BOOTSTRAP_FOOTER) - 1;
		
		result_size += 
			sizeof(HDS_MEDIA_HEADER_PREFIX_VIDEO) - 1 + 3 * VOD_INT32_LEN +
			conf->fragment_file_name_prefix.len + sizeof("-f-v-a-") - 1 + 3 * VOD_INT32_LEN + 
			sizeof(HDS_MEDIA_HEADER_SUFFIX) - 1 + VOD_INT32_LEN + 
				vod_base64_encoded_length(amf0_max_total_size) +
			sizeof(HDS_MEDIA_FOOTER) - 1;

		index++;
	}

	// allocate the buffers
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_packager_build_manifest: vod_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}

	temp_buffer = vod_alloc(request_context->pool, vod_max(amf0_max_total_size, max_abst_atom_size));
	if (temp_buffer == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hds_packager_build_manifest: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	// print the manifest header
	p = vod_sprintf(result->data, HDS_MANIFEST_HEADER,
		manifest_id,
		(uint32_t)(media_set->total_duration / 1000),
		(uint32_t)(media_set->total_duration % 1000));

	// bootstrap tags
	index = 0;
	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		cur_sequence_tracks = cur_sequence->filtered_clips[0].longest_track;

		p = vod_sprintf(p, HDS_BOOTSTRAP_HEADER, index);

		p = hds_write_base64_abst_atom(p, temp_buffer, &segment_durations[index]);

		p = vod_copy(p, HDS_BOOTSTRAP_FOOTER, sizeof(HDS_BOOTSTRAP_FOOTER) - 1);

		index++;

	}

	// media tags
	index = 0;
	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		cur_sequence_tracks = cur_sequence->filtered_clips[0].longest_track;

		if (cur_sequence_tracks[MEDIA_TYPE_VIDEO] != NULL)
		{
			track = cur_sequence_tracks[MEDIA_TYPE_VIDEO];
			bitrate = track->media_info.bitrate;
			if (cur_sequence_tracks[MEDIA_TYPE_AUDIO] != NULL)
			{
				bitrate += cur_sequence_tracks[MEDIA_TYPE_AUDIO]->media_info.bitrate;
			}

			p = vod_sprintf(p, HDS_MEDIA_HEADER_PREFIX_VIDEO,
				bitrate / 1000,
				(uint32_t)track->media_info.u.video.width,
				(uint32_t)track->media_info.u.video.height);
		}
		else
		{
			track = cur_sequence_tracks[MEDIA_TYPE_AUDIO];
			p = vod_sprintf(p, HDS_MEDIA_HEADER_PREFIX_AUDIO,
				track->media_info.bitrate / 1000);
		}

		// url
		p = vod_copy(p, conf->fragment_file_name_prefix.data, conf->fragment_file_name_prefix.len);
		if (media_set->has_multi_sequences)
		{
			p = vod_sprintf(p, "-f%uD", cur_sequence->index + 1);
		}

		if (cur_sequence_tracks[MEDIA_TYPE_VIDEO] != NULL)
		{
			p = vod_sprintf(p, "-v%uD", cur_sequence_tracks[MEDIA_TYPE_VIDEO]->index + 1);
		}

		if (cur_sequence_tracks[MEDIA_TYPE_AUDIO] != NULL)
		{
			p = vod_sprintf(p, "-a%uD", cur_sequence_tracks[MEDIA_TYPE_AUDIO]->index + 1);
		}
		*p++ = '-';

		p = vod_sprintf(p, HDS_MEDIA_HEADER_SUFFIX, index++);

		p = hds_amf0_write_base64_metadata(p, temp_buffer, cur_sequence_tracks);

		p = vod_copy(p, HDS_MEDIA_FOOTER, sizeof(HDS_MEDIA_FOOTER) - 1);
	}

	// manifest footer
	p = vod_copy(p, HDS_MANIFEST_FOOTER, sizeof(HDS_MANIFEST_FOOTER) - 1);

	result->len = p - result->data;
	
	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"hds_packager_build_manifest: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	vod_free(request_context->pool, temp_buffer);

	return VOD_OK;
}
Пример #18
0
vod_status_t
dash_packager_build_fragment_header(
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t segment_index,
	uint32_t sample_description_index,
	dash_fragment_header_extensions_t* extensions,
	bool_t size_only,
	vod_str_t* result,
	size_t* total_fragment_size)
{
	media_sequence_t* sequence = &media_set->sequences[0];
	media_track_t* first_track = sequence->filtered_clips[0].first_track;
	uint64_t earliest_pres_time = dash_packager_get_earliest_pres_time(media_set, first_track);
	sidx_params_t sidx_params;
	size_t first_frame_offset;
	size_t mdat_atom_size;
	size_t trun_atom_size;
	size_t tfhd_atom_size;
	size_t moof_atom_size;
	size_t traf_atom_size;
	size_t result_size;
	u_char* p;

	// calculate sizes
	dash_packager_init_sidx_params(media_set, sequence, &sidx_params);

	mdat_atom_size = ATOM_HEADER_SIZE + sequence->total_frame_size;
	if (extensions->mdat_prefix_writer != NULL)
	{
		mdat_atom_size += extensions->mdat_prefix_writer->atom_size;
	}
	trun_atom_size = mp4_builder_get_trun_atom_size(first_track->media_info.media_type, sequence->total_frame_count);

	tfhd_atom_size = ATOM_HEADER_SIZE + sizeof(tfhd_atom_t);
	if (sample_description_index > 0)
	{
		tfhd_atom_size += sizeof(uint32_t);
	}

	traf_atom_size =
		ATOM_HEADER_SIZE +
		tfhd_atom_size +
		ATOM_HEADER_SIZE + (earliest_pres_time > UINT_MAX ? sizeof(tfdt64_atom_t) : sizeof(tfdt_atom_t)) +
		trun_atom_size + 
		extensions->extra_traf_atoms_size;

	moof_atom_size =
		ATOM_HEADER_SIZE +
		ATOM_HEADER_SIZE + sizeof(mfhd_atom_t)+
		traf_atom_size;

	*total_fragment_size = 
		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;

	// head request optimization
	if (size_only)
	{
		return VOD_OK;
	}

	// allocate the buffer
	p = vod_alloc(request_context->pool, result_size);
	if (p == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"dash_packager_build_fragment_header: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	result->data = p;

	// styp
	p = vod_copy(p, styp_atom, sizeof(styp_atom));

	// sidx
	if (sidx_params.earliest_pres_time > UINT_MAX)
	{
		p = dash_packager_write_sidx64_atom(p, &sidx_params, moof_atom_size + mdat_atom_size);
	}
	else
	{
		p = dash_packager_write_sidx_atom(p, &sidx_params, moof_atom_size + mdat_atom_size);
	}

	// moof
	write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f');

	// moof.mfhd
	p = mp4_builder_write_mfhd_atom(p, segment_index);

	// moof.traf
	write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f');

	// moof.traf.tfhd
	p = dash_packager_write_tfhd_atom(p, first_track->media_info.track_id, sample_description_index);

	// moof.traf.tfdt
	if (earliest_pres_time > UINT_MAX)
	{
		p = dash_packager_write_tfdt64_atom(p, earliest_pres_time);
	}
	else
	{
		p = dash_packager_write_tfdt_atom(p, (uint32_t)earliest_pres_time);
	}

	// moof.traf.trun
	first_frame_offset = moof_atom_size + ATOM_HEADER_SIZE;
	if (extensions->mdat_prefix_writer != NULL)
	{
		first_frame_offset += extensions->mdat_prefix_writer->atom_size;
	}

	p = mp4_builder_write_trun_atom(
		p, 
		sequence, 
		first_frame_offset);

	// moof.traf.xxx
	if (extensions->write_extra_traf_atoms_callback != NULL)
	{
		p = extensions->write_extra_traf_atoms_callback(extensions->write_extra_traf_atoms_context, p, moof_atom_size + ATOM_HEADER_SIZE);
	}

	// mdat
	write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't');

	// mdat prefix
	if (extensions->mdat_prefix_writer != NULL)
	{
		p = extensions->mdat_prefix_writer->write(extensions->mdat_prefix_writer->context, p);
	}

	result->len = p - result->data;

	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",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
Пример #19
0
static vod_status_t
hls_muxer_init_id3_stream(
	hls_muxer_state_t* state,
	hls_muxer_conf_t* conf,
	mpegts_encoder_init_streams_state_t* init_streams_state)
{
	hls_muxer_stream_state_t* cur_stream;
	hls_muxer_stream_state_t* reference_stream;
	id3_context_t* context;
	vod_status_t rc;

	cur_stream = state->last_stream;

	// init the mpeg ts encoder
	rc = mpegts_encoder_init(
		&cur_stream->mpegts_encoder_state,
		init_streams_state,
		NULL,
		&state->queue,
		conf->interleave_frames,
		conf->align_frames);
	if (rc != VOD_OK)
	{
		return rc;
	}

	if (!conf->output_id3_timestamps)
	{
		return VOD_OK;
	}

	// get the stream that has the first frame
	rc = hls_muxer_choose_stream(state, &reference_stream);
	if (rc != VOD_OK)
	{
		if (rc == VOD_NOT_FOUND)
		{
			return VOD_OK;
		}
		return rc;
	}

	// allocate the context
	context = vod_alloc(state->request_context->pool, sizeof(*context));
	if (context == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
			"hls_muxer_init_id3_stream: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// init the memory frames source
	rc = frames_source_memory_init(state->request_context, &cur_stream->cur_frame_part.frames_source_context);
	if (rc != VOD_OK)
	{
		return rc;
	}

	cur_stream->cur_frame_part.frames_source = &frames_source_memory;

	// base initialization
	cur_stream->media_type = MEDIA_TYPE_NONE;
	cur_stream->segment_limit = ULLONG_MAX;
	cur_stream->buffer_state = NULL;

	// init the id3 encoder
	id3_encoder_init(&context->encoder, &mpegts_encoder, &cur_stream->mpegts_encoder_state);

	cur_stream->top_filter = &id3_encoder;
	cur_stream->top_filter_context = &context->encoder;

	// copy the time stamps
	cur_stream->first_frame_time_offset = reference_stream->first_frame_time_offset;
	cur_stream->next_frame_time_offset = reference_stream->next_frame_time_offset;
	cur_stream->clip_from_frame_offset = reference_stream->clip_from_frame_offset;

	// init the frame part
	cur_stream->cur_frame = &context->frame;
	cur_stream->first_frame_part = &cur_stream->cur_frame_part;
	cur_stream->cur_frame_part.next = NULL;
	cur_stream->cur_frame_part.first_frame = &context->frame;
	cur_stream->cur_frame_part.last_frame = &context->frame + 1;
	cur_stream->source = NULL;

	// init the frame
	context->frame.size = vod_sprintf(context->data, ID3_TEXT_JSON_FORMAT,
		hls_rescale_to_millis(cur_stream->first_frame_time_offset)) - context->data;
	context->frame.duration = 0;
	context->frame.key_frame = 1;
	context->frame.pts_delay = 0;
	context->frame.offset = (uintptr_t)&context->data;

	state->last_stream++;

	return VOD_OK;
}
Пример #20
0
vod_status_t 
dash_packager_build_mpd(
	request_context_t* request_context, 
	dash_manifest_config_t* conf,
	vod_str_t* base_url,
	segmenter_conf_t* segmenter_conf,
	media_set_t* media_set,
	size_t representation_tags_size,
	write_tags_callback_t write_representation_tags,
	void* representation_tags_writer_context,
	vod_str_t* result)
{
	segment_durations_t segment_durations[MEDIA_TYPE_COUNT];
	segment_duration_item_t* cur_duration_items[MEDIA_TYPE_COUNT];
	size_t base_url_temp_buffer_size = 0;
	size_t base_period_size;
	size_t result_size;
	size_t urls_length;
	uint32_t period_count = media_set->use_discontinuity ? media_set->total_clip_count : 1;
	uint32_t clip_index;
	uint32_t media_type;
	vod_status_t rc;
	u_char* base_url_temp_buffer = NULL;
	u_char* p;

	// get segment durations and count for each media type
	for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++)
	{
		if (media_set->track_count[media_type] == 0)
		{
			continue;
		}

		rc = segmenter_conf->get_segment_durations(
			request_context,
			segmenter_conf,
			media_set,
			NULL,
			media_type,
			&segment_durations[media_type]);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}

	// remove redundant tracks
	dash_packager_remove_redundant_tracks(
		conf->duplicate_bitrate_threshold,
		media_set);

	// calculate the total size
	urls_length = 2 * base_url->len + conf->init_file_name_prefix.len + conf->fragment_file_name_prefix.len;

	base_period_size =
		sizeof(VOD_DASH_MANIFEST_PERIOD_DURATION_HEADER) - 1 + 3 * VOD_INT32_LEN +
			sizeof(VOD_DASH_MANIFEST_VIDEO_HEADER) - 1 + 4 * VOD_INT32_LEN + 
				media_set->track_count[MEDIA_TYPE_VIDEO] * (
				sizeof(VOD_DASH_MANIFEST_VIDEO_PREFIX) - 1 + MAX_TRACK_SPEC_LENGTH + MAX_CODEC_NAME_SIZE + 5 * VOD_INT32_LEN +
				sizeof(VOD_DASH_MANIFEST_VIDEO_SUFFIX) - 1) +
			sizeof(VOD_DASH_MANIFEST_VIDEO_FOOTER) - 1 +
			sizeof(VOD_DASH_MANIFEST_AUDIO_HEADER) - 1 + 
				media_set->track_count[MEDIA_TYPE_AUDIO] * (
				sizeof(VOD_DASH_MANIFEST_AUDIO_PREFIX) - 1 + MAX_TRACK_SPEC_LENGTH + MAX_CODEC_NAME_SIZE + 2 * VOD_INT32_LEN +
				sizeof(VOD_DASH_MANIFEST_AUDIO_SUFFIX) - 1) +
			sizeof(VOD_DASH_MANIFEST_AUDIO_FOOTER) - 1 +
		sizeof(VOD_DASH_MANIFEST_PERIOD_FOOTER) - 1 +
		representation_tags_size;

	result_size =
		sizeof(VOD_DASH_MANIFEST_HEADER) - 1 + 3 * VOD_INT32_LEN +
			base_period_size * period_count +
		sizeof(VOD_DASH_MANIFEST_FOOTER);

	switch (conf->manifest_format)
	{
	case FORMAT_SEGMENT_TEMPLATE:
		result_size += 
			(sizeof(VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FIXED) - 1 + urls_length + VOD_INT64_LEN) * MEDIA_TYPE_COUNT * period_count;
		break;

	case FORMAT_SEGMENT_TIMELINE:
		for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++)
		{
			if (media_set->track_count[media_type] == 0)
			{
				continue;
			}

			result_size += 
				(sizeof(VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_HEADER) - 1 + urls_length +
				sizeof(VOD_DASH_MANIFEST_SEGMENT_TEMPLATE_FOOTER) - 1) * period_count +
				(sizeof(VOD_DASH_MANIFEST_SEGMENT_REPEAT) - 1 + 2 * VOD_INT32_LEN) * segment_durations[media_type].item_count;
		}
		break;

	case FORMAT_SEGMENT_LIST:
		result_size += dash_packager_get_segment_list_total_size(
			conf,
			media_set,
			segment_durations,
			base_url, 
			&base_url_temp_buffer_size);
		break;
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"dash_packager_build_mpd: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	if (base_url_temp_buffer_size != 0)
	{
		base_url_temp_buffer = vod_alloc(request_context->pool, base_url_temp_buffer_size);
		if (base_url_temp_buffer == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"dash_packager_build_mpd: vod_alloc failed (2)");
			return VOD_ALLOC_FAILED;
		}
	}
	
	// print the manifest header
	p = vod_sprintf(result->data, 
		VOD_DASH_MANIFEST_HEADER,
		(uint32_t)(media_set->total_duration / 1000),
		(uint32_t)(media_set->total_duration % 1000),
		(uint32_t)(segmenter_conf->max_segment_duration / 1000));

	cur_duration_items[MEDIA_TYPE_VIDEO] = segment_durations[MEDIA_TYPE_VIDEO].items;
	cur_duration_items[MEDIA_TYPE_AUDIO] = segment_durations[MEDIA_TYPE_AUDIO].items;

	for (clip_index = 0; clip_index < period_count; clip_index++)
	{
		p = dash_packager_write_mpd_period(
			p,
			base_url_temp_buffer,
			segment_durations,
			cur_duration_items,
			clip_index,
			conf,
			base_url,
			segmenter_conf,
			media_set,
			write_representation_tags,
			representation_tags_writer_context);
	}

	p = vod_copy(p, VOD_DASH_MANIFEST_FOOTER, sizeof(VOD_DASH_MANIFEST_FOOTER) - 1);

	result->len = p - result->data;

	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"dash_packager_build_mpd: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
Пример #21
0
vod_status_t
hls_muxer_init_segment(
	request_context_t* request_context,
	hls_muxer_conf_t* conf,
	hls_encryption_params_t* encryption_params,
	uint32_t segment_index,
	media_set_t* media_set,
	write_callback_t write_callback,
	void* write_context,
	size_t* response_size,
	vod_str_t* response_header,
	hls_muxer_state_t** processor_state)
{
	hls_muxer_state_t* state;
	bool_t simulation_supported;
	vod_status_t rc;

	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"hls_muxer_init_segment: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	rc = hls_muxer_init_base(
		state, 
		request_context, 
		conf, 
		encryption_params, 
		segment_index, 
		media_set, 
		write_callback, 
		write_context, 
		&simulation_supported, 
		response_header);
	if (rc != VOD_OK)
	{
		return rc;
	}

	if (simulation_supported)
	{
		rc = hls_muxer_simulate_get_segment_size(state, response_size);
		if (rc != VOD_OK)
		{
			return rc;
		}
		hls_muxer_simulation_reset(state);
	}

	rc = hls_muxer_start_frame(state);
	if (rc != VOD_OK)
	{
		if (rc != VOD_NOT_FOUND)
		{
			return rc;
		}

		*processor_state = NULL;		// no frames, nothing to do
	}
	else
	{
		*processor_state = state;
	}

	if (state->encrypted_write_context != NULL)
	{
		rc = aes_cbc_encrypt(
			state->encrypted_write_context,
			response_header,
			response_header,
			*processor_state == NULL);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}

	return VOD_OK;
}
Пример #22
0
vod_status_t 
mss_packager_build_manifest(
	request_context_t* request_context, 
	segmenter_conf_t* segmenter_conf, 
	mpeg_metadata_t* mpeg_metadata, 
	size_t extra_tags_size,
	mss_write_tags_callback_t write_extra_tags,
	void* extra_tags_writer_context,
	vod_str_t* result)
{
	mpeg_stream_metadata_t* cur_stream;
	segment_durations_t segment_durations[MEDIA_TYPE_COUNT];
	uint64_t duration_100ns;
	uint32_t media_type;
	uint32_t stream_index;
	uint32_t bitrate;
	vod_status_t rc;
	size_t result_size;
	u_char* p;

	// calculate the result size
	result_size = 
		sizeof(MSS_MANIFEST_HEADER) - 1 + VOD_INT64_LEN + 
		extra_tags_size +
		sizeof(MSS_MANIFEST_FOOTER);
	for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++)
	{
		if (mpeg_metadata->longest_stream[media_type] == NULL)
		{
			continue;
		}

		rc = segmenter_conf->get_segment_durations(
			request_context,
			segmenter_conf,
			&mpeg_metadata->longest_stream[media_type],
			1,
			&segment_durations[media_type]);
		if (rc != VOD_OK)
		{
			return rc;
		}

		result_size +=
			sizeof(MSS_STREAM_INDEX_HEADER) - 1 + 2 * sizeof(MSS_STREAM_TYPE_VIDEO) + 2 * VOD_INT32_LEN +
			sizeof(MSS_STREAM_INDEX_FOOTER);

		result_size += segment_durations[media_type].segment_count * (sizeof(MSS_CHUNK_TAG) + VOD_INT32_LEN + VOD_INT64_LEN);
	}

	for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
	{
		switch (cur_stream->media_info.media_type)
		{
		case MEDIA_TYPE_VIDEO:
			result_size += 
				sizeof(MSS_VIDEO_QUALITY_LEVEL_HEADER) - 1 + 4 * VOD_INT32_LEN + cur_stream->media_info.extra_data_size * 2 + 
				sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1;
			break;

		case MEDIA_TYPE_AUDIO:
			result_size += 
				sizeof(MSS_AUDIO_QUALITY_LEVEL_HEADER) - 1 + 6 * VOD_INT32_LEN + cur_stream->media_info.extra_data_size * 2 +
				sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1;
			break;
		}
	}

	// allocate the result
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mss_packager_build_manifest: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	duration_100ns = rescale_time(mpeg_metadata->duration, mpeg_metadata->timescale, MSS_TIMESCALE);
	p = vod_sprintf(result->data, MSS_MANIFEST_HEADER, duration_100ns);

	if (mpeg_metadata->longest_stream[MEDIA_TYPE_VIDEO] != NULL)
	{
		p = vod_sprintf(p, 
			MSS_STREAM_INDEX_HEADER, 
			MSS_STREAM_TYPE_VIDEO,
			mpeg_metadata->stream_count[MEDIA_TYPE_VIDEO],
			segment_durations[MEDIA_TYPE_VIDEO].segment_count,
			MSS_STREAM_TYPE_VIDEO);

		stream_index = 0;
		for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
		{
			if (cur_stream->media_info.media_type != MEDIA_TYPE_VIDEO)
			{
				continue;
			}

			bitrate = cur_stream->media_info.bitrate;
			bitrate = mss_encode_indexes(bitrate, cur_stream->file_info.file_index, cur_stream->track_index);
			p = vod_sprintf(p, MSS_VIDEO_QUALITY_LEVEL_HEADER,
				stream_index++,
				bitrate,
				(uint32_t)cur_stream->media_info.u.video.width,
				(uint32_t)cur_stream->media_info.u.video.height);

			p = mss_append_hex_string(p, cur_stream->media_info.extra_data, cur_stream->media_info.extra_data_size);

			p = vod_copy(p, MSS_QUALITY_LEVEL_FOOTER, sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1);
		}

		p = mss_write_manifest_chunks(p, &segment_durations[MEDIA_TYPE_VIDEO]);

		p = vod_copy(p, MSS_STREAM_INDEX_FOOTER, sizeof(MSS_STREAM_INDEX_FOOTER) - 1);
	}

	if (mpeg_metadata->longest_stream[MEDIA_TYPE_AUDIO] != NULL)
	{
		p = vod_sprintf(p, MSS_STREAM_INDEX_HEADER, 
			MSS_STREAM_TYPE_AUDIO,
			mpeg_metadata->stream_count[MEDIA_TYPE_AUDIO],
			segment_durations[MEDIA_TYPE_AUDIO].segment_count,
			MSS_STREAM_TYPE_AUDIO);

		stream_index = 0;
		for (cur_stream = mpeg_metadata->first_stream; cur_stream < mpeg_metadata->last_stream; cur_stream++)
		{
			if (cur_stream->media_info.media_type != MEDIA_TYPE_AUDIO)
			{
				continue;
			}

			bitrate = cur_stream->media_info.bitrate;
			bitrate = mss_encode_indexes(bitrate, cur_stream->file_info.file_index, cur_stream->track_index);
			p = vod_sprintf(p, MSS_AUDIO_QUALITY_LEVEL_HEADER,
				stream_index++,
				bitrate,
				cur_stream->media_info.u.audio.sample_rate,
				(uint32_t)cur_stream->media_info.u.audio.channels,
				(uint32_t)cur_stream->media_info.u.audio.bits_per_sample,
				(uint32_t)cur_stream->media_info.u.audio.packet_size);

			p = mss_append_hex_string(p, cur_stream->media_info.extra_data, cur_stream->media_info.extra_data_size);

			p = vod_copy(p, MSS_QUALITY_LEVEL_FOOTER, sizeof(MSS_QUALITY_LEVEL_FOOTER) - 1);
		}

		p = mss_write_manifest_chunks(p, &segment_durations[MEDIA_TYPE_AUDIO]);

		p = vod_copy(p, MSS_STREAM_INDEX_FOOTER, sizeof(MSS_STREAM_INDEX_FOOTER) - 1);
	}

	if (write_extra_tags != NULL)
	{
		p = write_extra_tags(extra_tags_writer_context, p, mpeg_metadata);
	}

	p = vod_copy(p, MSS_MANIFEST_FOOTER, sizeof(MSS_MANIFEST_FOOTER) - 1);

	result->len = p - result->data;
	
	if (result->len > result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"mss_packager_build_manifest: result length %uz exceeded allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}
	
	return VOD_OK;
}
Пример #23
0
static vod_status_t
manifest_utils_get_unique_labels(
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t media_type,
	label_track_count_array_t* output)
{
	vod_str_t* cur_track_label;
	label_track_count_t* first_label;
	label_track_count_t* last_label;
	label_track_count_t* cur_label;
	media_track_t* last_track;
	media_track_t* cur_track;
	bool_t label_found;

	first_label = vod_alloc(request_context->pool,
		media_set->total_track_count * sizeof(first_label[0]));
	if (first_label == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"manifest_utils_get_unique_labels: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	last_label = first_label;

	last_track = media_set->filtered_tracks + media_set->total_track_count;
	for (cur_track = media_set->filtered_tracks; cur_track < last_track; cur_track++)
	{
		if (cur_track->media_info.media_type != media_type ||
			cur_track->media_info.label.len == 0)
		{
			continue;
		}

		cur_track_label = &cur_track->media_info.label;

		label_found = FALSE;
		for (cur_label = first_label; cur_label < last_label; cur_label++)
		{
			if (vod_str_equals(*cur_track_label, cur_label->label))
			{
				label_found = TRUE;
				break;
			}
		}

		if (label_found)
		{
			cur_label->track_count++;
			continue;
		}

		last_label->label = *cur_track_label;
		last_label->track_count = 1;
		last_label++;
	}

	output->first = first_label;
	output->last = last_label;
	output->count = last_label - first_label;

	return VOD_OK;
}
Пример #24
0
vod_status_t
mss_packager_build_fragment_header(
	request_context_t* request_context,
	mpeg_stream_metadata_t* stream_metadata,
	uint32_t segment_index,
	size_t extra_traf_atoms_size,
	write_extra_traf_atoms_callback_t write_extra_traf_atoms_callback,
	void* write_extra_traf_atoms_context,
	bool_t size_only,
	vod_str_t* result,
	size_t* total_fragment_size)
{
	input_frame_t* last_frame;
	input_frame_t* cur_frame;
	size_t mdat_atom_size;
	size_t trun_atom_size;
	size_t moof_atom_size;
	size_t traf_atom_size;
	size_t result_size;
	u_char* p;

	// calculate sizes
	mdat_atom_size = ATOM_HEADER_SIZE + stream_metadata->total_frames_size;
	trun_atom_size = mp4_builder_get_trun_atom_size(stream_metadata->media_info.media_type, stream_metadata->frame_count);

	traf_atom_size =
		ATOM_HEADER_SIZE +
		ATOM_HEADER_SIZE + sizeof(tfhd_atom_t) +
		trun_atom_size +
		ATOM_HEADER_SIZE + sizeof(uuid_tfxd_atom_t) + 
		extra_traf_atoms_size;

	moof_atom_size =
		ATOM_HEADER_SIZE +
		ATOM_HEADER_SIZE + sizeof(mfhd_atom_t)+
		traf_atom_size;

	result_size =
		moof_atom_size +
		ATOM_HEADER_SIZE;		// mdat

	*total_fragment_size = result_size + stream_metadata->total_frames_size;

	// head request optimization
	if (size_only)
	{
		return VOD_OK;
	}

	// allocate the buffer
	result->data = vod_alloc(request_context->pool, result_size);
	if (result->data == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mss_packager_build_fragment_header: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	p = result->data;

	// moof
	write_atom_header(p, moof_atom_size, 'm', 'o', 'o', 'f');

	// moof.mfhd
	p = mp4_builder_write_mfhd_atom(p, segment_index);

	// moof.traf
	write_atom_header(p, traf_atom_size, 't', 'r', 'a', 'f');

	// moof.traf.tfhd
	switch (stream_metadata->media_info.media_type)
	{
	case MEDIA_TYPE_VIDEO:
		p = mss_write_tfhd_atom(p, stream_metadata->media_info.track_id, 0x01010000);
		break;

	case MEDIA_TYPE_AUDIO:
		p = mss_write_tfhd_atom(p, stream_metadata->media_info.track_id, 0x02000000);
		break;
	}

	// moof.traf.trun
	last_frame = stream_metadata->frames + stream_metadata->frame_count;
	for (cur_frame = stream_metadata->frames; cur_frame < last_frame; cur_frame++)
	{
		cur_frame->duration = rescale_time(cur_frame->duration, stream_metadata->media_info.timescale, MSS_TIMESCALE);
	}

	p = mp4_builder_write_trun_atom(
		p,
		stream_metadata->media_info.media_type,
		stream_metadata->frames,
		stream_metadata->frame_count,
		moof_atom_size + ATOM_HEADER_SIZE);

	p = mss_write_uuid_tfxd_atom(p, stream_metadata);

	// moof.traf.xxx
	if (write_extra_traf_atoms_callback != NULL)
	{
		p = write_extra_traf_atoms_callback(write_extra_traf_atoms_context, p, moof_atom_size + ATOM_HEADER_SIZE);
	}

	// mdat
	write_atom_header(p, mdat_atom_size, 'm', 'd', 'a', 't');

	result->len = p - result->data;

	if (result->len != result_size)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"mss_packager_build_fragment_header: result length %uz is different than allocated length %uz",
			result->len, result_size);
		return VOD_UNEXPECTED;
	}

	return VOD_OK;
}
Пример #25
0
static vod_status_t
manifest_utils_get_muxed_adaptation_set(
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t flags,
	vod_str_t* label,
	adaptation_set_t* output)
{
	media_sequence_t* cur_sequence;
	media_track_t** cur_track_ptr;
	media_track_t* audio_track;
	media_track_t* last_track;
	media_track_t* cur_track;
	uint32_t main_media_type;

	// allocate the tracks array
	cur_track_ptr = vod_alloc(request_context->pool,
		sizeof(output->first[0]) * media_set->total_track_count * MEDIA_TYPE_COUNT);
	if (cur_track_ptr == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"manifest_utils_get_muxed_adaptation_set: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	output->type = ADAPTATION_TYPE_MUXED;
	output->first = cur_track_ptr;
	output->count = 0;

	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		// find the main media type
		if (cur_sequence->track_count[MEDIA_TYPE_VIDEO] > 0)
		{
			main_media_type = MEDIA_TYPE_VIDEO;
		}
		else if (cur_sequence->track_count[MEDIA_TYPE_AUDIO] > 0)
		{
			if ((flags & ADAPTATION_SETS_FLAG_AVOID_AUDIO_ONLY) != 0 &&
				media_set->track_count[MEDIA_TYPE_VIDEO] > 0)
			{
				continue;
			}

			main_media_type = MEDIA_TYPE_AUDIO;
		}
		else
		{
			continue;
		}

		// find the audio track
		audio_track = cur_sequence->filtered_clips[0].longest_track[MEDIA_TYPE_AUDIO];
		if ((audio_track == NULL || (label != NULL && !vod_str_equals(*label, audio_track->media_info.label))) &&
			media_set->track_count[MEDIA_TYPE_AUDIO] > 0)
		{
			if (cur_sequence->track_count[MEDIA_TYPE_VIDEO] <= 0)
			{
				continue;
			}

			// find some audio track from another sequence to mux with this video
			last_track = media_set->filtered_tracks + media_set->total_track_count;
			for (cur_track = media_set->filtered_tracks; cur_track < last_track; cur_track++)
			{
				if (cur_track->media_info.media_type == MEDIA_TYPE_AUDIO &&
					(label == NULL || vod_str_equals(*label, cur_track->media_info.label)))
				{
					audio_track = cur_track;
					break;
				}
			}
		}

		for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < cur_sequence->filtered_clips[0].last_track; cur_track++)
		{
			if (cur_track->media_info.media_type != main_media_type)
			{
				continue;
			}

			// add the track
			if (main_media_type == MEDIA_TYPE_VIDEO)
			{
				cur_track_ptr[MEDIA_TYPE_VIDEO] = cur_track;
				cur_track_ptr[MEDIA_TYPE_AUDIO] = audio_track;
			}
			else
			{
				cur_track_ptr[MEDIA_TYPE_VIDEO] = NULL;
				cur_track_ptr[MEDIA_TYPE_AUDIO] = cur_track;
			}
			cur_track_ptr[MEDIA_TYPE_SUBTITLE] = NULL;

			cur_track_ptr += MEDIA_TYPE_COUNT;

			output->count++;
		}
	}

	output->last = cur_track_ptr;

	return VOD_OK;
}
Пример #26
0
vod_status_t
segmenter_get_segment_durations_accurate(
	request_context_t* request_context,
	segmenter_conf_t* conf,
	media_set_t* media_set,
	media_sequence_t* sequence,
	uint32_t media_type,
	segment_durations_t* result)
{
	segmenter_boundary_iterator_context_t boundary_iterator;
	media_track_t* cur_track;
	media_track_t* last_track;
	media_track_t* main_track = NULL;
	media_track_t* longest_track = NULL;
	segment_duration_item_t* cur_item;
	media_sequence_t* sequences_end;
	media_sequence_t* cur_sequence;
	input_frame_t* last_frame;
	input_frame_t* cur_frame;
	uint64_t total_duration;
	uint32_t segment_index = 0;
	uint64_t accum_duration = 0;
	uint64_t segment_start = 0;
	uint64_t segment_limit_millis;
	uint64_t segment_limit;
	uint64_t cur_duration;
	uint32_t duration_millis;
	bool_t align_to_key_frames;

	SEGMENT_CHOOSE_HEADER(conf);

	if (media_set->durations != NULL)
	{
		// in case of a playlist fall back to estimate
		return segmenter_get_segment_durations_estimate(
			request_context,
			conf,
			media_set,
			sequence,
			media_type,
			result);
	}

	// get the maximum duration and main track (=first video track if exists, or first audio track otherwise)
	if (sequence != NULL)
	{
		cur_sequence = sequence;
		sequences_end = sequence + 1;
	}
	else
	{
		cur_sequence = media_set->sequences;
		sequences_end = media_set->sequences_end;
	}

	duration_millis = 0;
	for (; cur_sequence < sequences_end; cur_sequence++)
	{
		last_track = cur_sequence->filtered_clips[0].last_track;
		for (cur_track = cur_sequence->filtered_clips[0].first_track; cur_track < last_track; cur_track++)
		{
			if (media_type != MEDIA_TYPE_NONE && cur_track->media_info.media_type != media_type)
			{
				continue;
			}

			if (main_track == NULL ||
				(cur_track->media_info.media_type < main_track->media_info.media_type))
			{
				main_track = cur_track;
			}

			if (cur_track->media_info.duration_millis > duration_millis)
			{
				longest_track = cur_track;
				duration_millis = cur_track->media_info.duration_millis;
			}
		}
	}

	if (main_track == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_segment_durations_accurate: didnt get any tracks");
		return VOD_UNEXPECTED;
	}

	// if the main track is not audio/video, or main track is audio and requires filtering, fall back to estimate
	switch (main_track->media_info.media_type)
	{
	case MEDIA_TYPE_VIDEO:
		break;

	case MEDIA_TYPE_AUDIO:
		if (!media_set->audio_filtering_needed)
		{
			break;
		}

	default:
		return segmenter_get_segment_durations_estimate(
			request_context,
			conf,
			media_set,
			sequence,
			media_type,
			result);
	}

	// get the segment count
	result->segment_count = conf->get_segment_count(conf, duration_millis);
	if (result->segment_count > MAX_SEGMENT_COUNT)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_segment_durations_accurate: segment count %uD is invalid", result->segment_count);
		return VOD_BAD_DATA;
	}

	// allocate the result buffer
	result->items = vod_alloc(request_context->pool, sizeof(*result->items) * result->segment_count);
	if (result->items == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"segmenter_get_segment_durations_accurate: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	result->timescale = main_track->media_info.timescale;
	result->discontinuities = 0;

	// Note: assuming a single frame list part
	cur_item = result->items - 1;
	last_frame = main_track->frames.last_frame;
	cur_frame = main_track->frames.first_frame;

	align_to_key_frames = conf->align_to_key_frames && main_track->media_info.media_type == MEDIA_TYPE_VIDEO;

	// bootstrap segments
	if (conf->bootstrap_segments_count > 0)
	{
		segment_limit = rescale_time(conf->bootstrap_segments_end[0], 1000, result->timescale);

		for (; cur_frame < last_frame; cur_frame++)
		{
			while (accum_duration >= segment_limit && segment_index + 1 < result->segment_count &&
				(!align_to_key_frames || cur_frame->key_frame))
			{
				// get the current duration and update to array
				cur_duration = accum_duration - segment_start;
				if (cur_item < result->items || cur_duration != cur_item->duration)
				{
					cur_item++;
					cur_item->repeat_count = 0;
					cur_item->segment_index = segment_index;
					cur_item->duration = cur_duration;
					cur_item->discontinuity = FALSE;
				}
				cur_item->repeat_count++;

				// move to the next segment
				segment_start = accum_duration;
				segment_index++;
				if (segment_index >= conf->bootstrap_segments_count)
				{
					goto post_bootstrap;
				}
				segment_limit = rescale_time(conf->bootstrap_segments_end[segment_index], 1000, result->timescale);
			}
			accum_duration += cur_frame->duration;
		}
	}

post_bootstrap:

	// remaining segments
	segment_limit_millis = conf->bootstrap_segments_total_duration + conf->segment_duration;
	segment_limit = rescale_time(segment_limit_millis, 1000, result->timescale);

	for (; cur_frame < last_frame; cur_frame++)
	{
		while (accum_duration >= segment_limit && segment_index + 1 < result->segment_count &&
			(!align_to_key_frames || cur_frame->key_frame))
		{
			// get the current duration and update to array
			cur_duration = accum_duration - segment_start;
			if (cur_item < result->items || cur_duration != cur_item->duration)
			{
				cur_item++;
				cur_item->repeat_count = 0;
				cur_item->segment_index = segment_index;
				cur_item->duration = cur_duration;
				cur_item->discontinuity = FALSE;
			}
			cur_item->repeat_count++;

			// move to the next segment
			segment_index++;
			segment_start = accum_duration;
			segment_limit_millis += conf->segment_duration;
			segment_limit = rescale_time(segment_limit_millis, 1000, result->timescale);
		}
		accum_duration += cur_frame->duration;
	}

	// in case the main video track is shorter than the audio track, add the estimated durations of the remaining audio-only segments
	if (main_track->media_info.duration_millis < duration_millis &&
		!align_to_key_frames)
	{
		segmenter_boundary_iterator_init(&boundary_iterator, conf, result->segment_count);
		segmenter_boundary_iterator_skip(&boundary_iterator, segment_index);

		total_duration = rescale_time(longest_track->media_info.duration, longest_track->media_info.timescale, result->timescale);

		while (accum_duration < total_duration &&
			segment_index + 1 < result->segment_count)
		{
			segment_limit_millis = segmenter_boundary_iterator_next(&boundary_iterator);
			segment_limit = rescale_time(segment_limit_millis, 1000, result->timescale);
			segment_limit = vod_min(segment_limit, total_duration);

			accum_duration = segment_limit;

			cur_duration = accum_duration - segment_start;
			if (cur_item < result->items || cur_duration != cur_item->duration)
			{
				cur_item++;
				cur_item->repeat_count = 0;
				cur_item->segment_index = segment_index;
				cur_item->duration = cur_duration;
				cur_item->discontinuity = FALSE;
			}
			cur_item->repeat_count++;

			// move to the next segment
			segment_index++;
			segment_start = accum_duration;
		}

		accum_duration = total_duration;
	}

	// add the last segment / empty segments after the last keyframe (in case align to key frames is on)
	while (segment_index < result->segment_count)
	{
		// get the current duration and update to array
		cur_duration = accum_duration - segment_start;
		if (cur_item < result->items || cur_duration != cur_item->duration)
		{
			cur_item++;
			cur_item->repeat_count = 0;
			cur_item->segment_index = segment_index;
			cur_item->duration = cur_duration;
			cur_item->discontinuity = FALSE;
		}
		cur_item->repeat_count++;

		// move to the next segment
		segment_index++;
		segment_start = accum_duration;
	}

	result->item_count = cur_item + 1 - result->items;

	// remove any empty segments from the end
	if (result->item_count > 0 && cur_item->duration == 0)
	{
		result->item_count--;
		result->segment_count -= cur_item->repeat_count;
	}

	result->start_time = 0;
	result->end_time = duration_millis;

	return VOD_OK;
}
Пример #27
0
static vod_status_t
manifest_utils_get_unmuxed_adaptation_sets(
	request_context_t* request_context,
	media_set_t* media_set,
	label_track_count_array_t* subtitle_labels,
	adaptation_sets_t* output)
{
	label_track_count_t* cur_label;
	adaptation_set_t* cur_adaptation_set;
	adaptation_set_t* adaptation_sets;
	media_track_t** cur_track_ptr;
	media_track_t* last_track;
	media_track_t* cur_track;
	uint32_t media_type;
	size_t adaptation_sets_count;
	size_t index;

	// get the number of adaptation sets
	adaptation_sets_count = subtitle_labels->count;
	output->count[ADAPTATION_TYPE_MUXED] = 0;
	output->count[ADAPTATION_TYPE_SUBTITLE] = subtitle_labels->count;

	if (media_set->track_count[MEDIA_TYPE_VIDEO] > 0)
	{
		adaptation_sets_count++;
		output->count[ADAPTATION_TYPE_VIDEO] = 1;
	}
	else
	{
		output->count[ADAPTATION_TYPE_VIDEO] = 0;
	}

	if (media_set->track_count[MEDIA_TYPE_AUDIO] > 0)
	{
		adaptation_sets_count++;
		output->count[ADAPTATION_TYPE_AUDIO] = 1;
	}
	else
	{
		output->count[ADAPTATION_TYPE_AUDIO] = 0;
	}

	// allocate the adaptation sets
	adaptation_sets = vod_alloc(request_context->pool,
		sizeof(adaptation_sets[0]) * adaptation_sets_count +
		sizeof(adaptation_sets[0].first[0]) * media_set->total_track_count);
	if (adaptation_sets == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"manifest_utils_get_unmuxed_adaptation_sets: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	cur_track_ptr = (void*)(adaptation_sets + adaptation_sets_count);

	// initialize the audio/video adaptation sets
	cur_adaptation_set = adaptation_sets;
	for (media_type = 0; media_type < MEDIA_TYPE_SUBTITLE; media_type++)
	{
		if (media_set->track_count[media_type] == 0)
		{
			continue;
		}

		output->first_by_type[media_type] = cur_adaptation_set;
		cur_adaptation_set->first = cur_track_ptr;
		cur_adaptation_set->count = 0;
		cur_adaptation_set->type = media_type;
		cur_track_ptr += media_set->track_count[media_type];
		cur_adaptation_set->last = cur_track_ptr;
		cur_adaptation_set++;
	}

	// initialize the subtitle adaptation sets
	output->first_by_type[MEDIA_TYPE_SUBTITLE] = cur_adaptation_set;
	for (cur_label = subtitle_labels->first; cur_label < subtitle_labels->last; cur_label++)
	{
		cur_adaptation_set->first = cur_track_ptr;
		cur_adaptation_set->count = 0;
		cur_adaptation_set->type = MEDIA_TYPE_SUBTITLE;
		cur_track_ptr++;
		cur_adaptation_set->last = cur_track_ptr;
		cur_adaptation_set++;
	}

	// add the tracks to the adaptation sets
	last_track = media_set->filtered_tracks + media_set->total_track_count;
	for (cur_track = media_set->filtered_tracks; cur_track < last_track; cur_track++)
	{
		media_type = cur_track->media_info.media_type;
		switch (media_type)
		{
		case MEDIA_TYPE_AUDIO:
		case MEDIA_TYPE_VIDEO:
			cur_adaptation_set = output->first_by_type[media_type];
			break;

		case MEDIA_TYPE_SUBTITLE:
			if (cur_track->media_info.label.len == 0)
			{
				continue;
			}

			// find the label index
			cur_label = manifest_utils_find_label(
				&cur_track->media_info.label,
				subtitle_labels,
				&index);
			if (cur_label == NULL)
			{
				continue;
			}

			// find the adaptation set
			cur_adaptation_set = output->first_by_type[MEDIA_TYPE_SUBTITLE] + index;
			if (cur_adaptation_set->count != 0)
			{
				continue;
			}
			break;

		default:		// MEDIA_TYPE_NONE
			continue;
		}

		cur_adaptation_set->first[cur_adaptation_set->count++] = cur_track;
	}

	output->first = adaptation_sets;
	output->last = adaptation_sets + adaptation_sets_count;
	output->total_count = adaptation_sets_count;

	return VOD_OK;
}
Пример #28
0
vod_status_t
segmenter_get_start_end_ranges_no_discontinuity(
	get_clip_ranges_params_t* params,
	get_clip_ranges_result_t* result)
{
	align_to_key_frames_context_t align_context;
	request_context_t* request_context = params->request_context;
	uint64_t start_time = params->start_time;
	uint64_t clip_start_offset = start_time;
	uint64_t next_start_offset;
	uint64_t start;
	uint64_t end;
	media_range_t* cur_clip_range;
	uint32_t* clip_durations = params->clip_durations;
	uint32_t* end_duration = clip_durations + params->total_clip_count;
	uint32_t* cur_duration;
	uint32_t segment_count;
	uint32_t index;

	SEGMENT_CHOOSE_HEADER(params->conf);

	result->clip_index_segment_index = 0;
	result->first_clip_segment_index = 0;

	// get the segment count
	segment_count = params->conf->get_segment_count(params->conf, params->end_time);
	if (segment_count == INVALID_SEGMENT_COUNT)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_start_end_ranges_no_discontinuity: segment count is invalid for total duration %uL", params->end_time);
		return VOD_BAD_DATA;
	}

	if (params->segment_index >= segment_count)
	{
		result->clip_count = 0;
		result->min_clip_index = 1;
		result->max_clip_index = 0;
		return VOD_OK;
	}

	// get the start / end offsets
	segmenter_get_start_end_offsets(
		params->conf,
		params->segment_index,
		&start,
		&end);

	if (start < start_time)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_start_end_ranges_no_discontinuity: segment start time %uL is less than sequence start time %uL",
			start, start_time);
		return VOD_BAD_REQUEST;
	}

	if (params->key_frame_durations != NULL)
	{
		align_context.request_context = request_context;
		align_context.part = params->key_frame_durations;
		align_context.offset = start_time + params->first_key_frame_offset;
		align_context.cur_pos = align_context.part->first;
		start = segmenter_align_to_key_frames(&align_context, start, params->last_segment_end);
		end = segmenter_align_to_key_frames(&align_context, end, params->last_segment_end);
	}

	if (params->segment_index + 1 >= segment_count)
	{
		end = params->last_segment_end;
	}

	// find min/max clip indexes and initial sequence offset
	result->min_clip_index = INVALID_CLIP_INDEX;
	result->max_clip_index = params->total_clip_count - 1;

	for (cur_duration = clip_durations; cur_duration < end_duration; cur_duration++, clip_start_offset = next_start_offset)
	{
		next_start_offset = clip_start_offset + *cur_duration;
		if (start >= next_start_offset)
		{
			continue;
		}

		if (start >= clip_start_offset)
		{
			result->min_clip_index = cur_duration - clip_durations;
			result->initial_sequence_offset = clip_start_offset;
		}

		if (end <= next_start_offset)
		{
			result->max_clip_index = cur_duration - clip_durations;
			break;
		}
	}

	if (result->min_clip_index == INVALID_CLIP_INDEX)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_start_end_ranges_no_discontinuity: invalid segment index %uD", params->segment_index);
		return VOD_BAD_REQUEST;
	}

	// allocate the clip ranges
	result->clip_count = result->max_clip_index - result->min_clip_index + 1;
	cur_clip_range = vod_alloc(request_context->pool, sizeof(result->clip_ranges[0]) * result->clip_count);
	if (cur_clip_range == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"segmenter_get_start_end_ranges_no_discontinuity: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	result->clip_ranges = cur_clip_range;

	// initialize the clip ranges
	start -= result->initial_sequence_offset;
	end -= result->initial_sequence_offset;
	for (index = result->min_clip_index;; index++, cur_clip_range++)
	{
		cur_clip_range->timescale = 1000;
		cur_clip_range->start = start;
		if (index >= result->max_clip_index)
		{
			cur_clip_range->end = end;
			break;
		}

		cur_clip_range->end = clip_durations[index];

		start = 0;
		end -= clip_durations[index];
	}

	result->initial_sequence_offset -= start_time;

	return VOD_OK;
}
Пример #29
0
vod_status_t
mss_playready_get_fragment_writer(
	segment_writer_t* result,
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t segment_index,
	segment_writer_t* segment_writer,
	const u_char* iv,
	bool_t size_only,
	vod_str_t* fragment_header,
	size_t* total_fragment_size)
{
	mp4_encrypt_passthrough_context_t passthrough_context;
	uint32_t media_type = media_set->sequences[0].media_type;
	vod_status_t rc;

	if (mp4_encrypt_passthrough_init(&passthrough_context, media_set->sequences))
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"mss_playready_get_fragment_writer: using encryption passthrough");

		// build the fragment header
		rc = mss_packager_build_fragment_header(
			request_context,
			media_set,
			segment_index,
			passthrough_context.total_size + ATOM_HEADER_SIZE + sizeof(uuid_piff_atom_t),
			mss_playready_passthrough_write_encryption_atoms, 
			&passthrough_context,
			size_only,
			fragment_header,
			total_fragment_size);
		if (rc != VOD_OK)
		{
			vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"mss_playready_get_fragment_writer: mss_packager_build_fragment_header failed %i", rc);
			return rc;
		}

		// use original writer
		vod_memzero(result, sizeof(*result));

		return VOD_OK;
	}

	switch (media_type)
	{
	case MEDIA_TYPE_VIDEO:
		return mp4_encrypt_video_get_fragment_writer(
			result,
			request_context,
			media_set,
			segment_index,
			mss_playready_video_write_fragment_header,
			segment_writer,
			iv);

	case MEDIA_TYPE_AUDIO:
		rc = mp4_encrypt_audio_get_fragment_writer(
			result,
			request_context,
			media_set,
			segment_index,
			segment_writer,
			iv);
		if (rc != VOD_OK)
		{
			return rc;
		}

		rc = mss_playready_audio_build_fragment_header(
			result->context,
			size_only,
			fragment_header,
			total_fragment_size);
		if (rc != VOD_OK)
		{
			return rc;
		}

		return VOD_OK;
	}

	vod_log_error(VOD_LOG_ERR, request_context->log, 0,
		"mss_playready_get_fragment_writer: invalid media type %uD", media_type);
	return VOD_UNEXPECTED;
}
Пример #30
0
vod_status_t
segmenter_get_start_end_ranges_discontinuity(
	get_clip_ranges_params_t* params,
	get_clip_ranges_result_t* result)
{
	align_to_key_frames_context_t align_context;
	request_context_t* request_context = params->request_context;
	segmenter_conf_t* conf = params->conf;
	uint64_t clip_start_offset;
	uint64_t start;
	uint64_t end;
	uint64_t ignore;
	uint32_t* end_duration = params->clip_durations + params->total_clip_count;
	uint32_t* cur_duration;
	uint32_t clip_index_segment_index = 0;
	uint32_t last_segment_limit = params->initial_segment_index;
	uint32_t cur_segment_limit;
	uint32_t segment_index = params->segment_index;
	uint32_t clip_index = params->clip_index;
	media_range_t* cur_clip_range;
	uint64_t prev_clips_duration = 0;

	SEGMENT_CHOOSE_HEADER(conf);

	for (cur_duration = params->clip_durations;; cur_duration++)
	{
		if (cur_duration >= end_duration)
		{
			vod_log_error(VOD_LOG_ERR, request_context->log, 0,
				"segmenter_get_start_end_ranges_discontinuity: invalid segment index %uD or clip index", segment_index);
			return VOD_BAD_REQUEST;
		}

		// get the clip start offset
		segmenter_get_start_end_offsets(conf, last_segment_limit, &clip_start_offset, &ignore);

		// get segment limit for the current clip
		cur_segment_limit = conf->get_segment_count(conf, clip_start_offset + *cur_duration);
		if (cur_segment_limit == INVALID_SEGMENT_COUNT)
		{
			vod_log_error(VOD_LOG_ERR, request_context->log, 0,
				"segmenter_get_start_end_ranges_discontinuity: invalid segment count");
			return VOD_BAD_DATA;
		}

		if (cur_segment_limit <= last_segment_limit)
		{
			cur_segment_limit = last_segment_limit + 1;
		}

		if (clip_index == 1)
		{
			clip_index_segment_index = cur_segment_limit - params->initial_segment_index;
			segment_index += clip_index_segment_index;
		}

		if (clip_index > 0 && clip_index != INVALID_CLIP_INDEX)
		{
			clip_index--;
		}
		else if (segment_index < cur_segment_limit)
		{
			// the segment index is within this clip, break
			break;
		}

		// move to the next clip
		prev_clips_duration += *cur_duration;
		last_segment_limit = cur_segment_limit;
	}

	if (segment_index < last_segment_limit)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"segmenter_get_start_end_ranges_discontinuity: segment index %uD smaller than last segment index %uD",
			segment_index, last_segment_limit);
		return VOD_BAD_REQUEST;
	}

	// get start / end position relative to the clip start
	segmenter_get_start_end_offsets(
		conf,
		segment_index,
		&start,
		&end);

	start -= clip_start_offset;
	if (segment_index + 1 >= cur_segment_limit)
	{
		end = *cur_duration;		// last segment in clip
	}
	else
	{
		end -= clip_start_offset;
	}

	if (params->key_frame_durations != NULL)
	{
		align_context.request_context = request_context;
		align_context.part = params->key_frame_durations;
		align_context.offset = params->first_key_frame_offset - prev_clips_duration;
		align_context.cur_pos = align_context.part->first;

		start = segmenter_align_to_key_frames(&align_context, start, *cur_duration);
		end = segmenter_align_to_key_frames(&align_context, end, *cur_duration);
	}

	// initialize the clip range
	cur_clip_range = vod_alloc(request_context->pool, sizeof(cur_clip_range[0]));
	if (cur_clip_range == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"segmenter_get_start_end_ranges_discontinuity: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	cur_clip_range->timescale = 1000;
	cur_clip_range->start = start;
	cur_clip_range->end = end;

	// initialize the result
	result->initial_sequence_offset = prev_clips_duration;
	result->min_clip_index = result->max_clip_index = cur_duration - params->clip_durations;
	result->clip_count = 1;
	result->clip_ranges = cur_clip_range;
	result->clip_index_segment_index = clip_index_segment_index;
	result->first_clip_segment_index = last_segment_limit;

	return VOD_OK;
}