static vod_status_t
mp4_encrypt_write_encrypted(
	mp4_encrypt_state_t* state,
	u_char* cur_pos, 
	uint32_t write_size)
{
	uint32_t cur_write_size;
	size_t alloc_size;
	u_char* write_end;
	u_char* output;
	vod_status_t rc;

	write_end = cur_pos + write_size;
	while (cur_pos < write_end)
	{
		rc = write_buffer_get_bytes(&state->write_buffer, MIN_ALLOC_SIZE, &alloc_size, &output);
		if (rc != VOD_OK)
		{
			return rc;
		}

		cur_write_size = write_end - cur_pos;
		cur_write_size = vod_min(cur_write_size, alloc_size);

		rc = mp4_aes_ctr_process(&state->cipher, output, cur_pos, cur_write_size);
		if (rc != VOD_OK)
		{
			return rc;
		}
		cur_pos += cur_write_size;
		state->write_buffer.cur_pos += cur_write_size;
	}

	return VOD_OK;
}
static void
segmenter_boundary_iterator_skip(segmenter_boundary_iterator_context_t* context, uint32_t count)
{
	context->segment_index = vod_min(context->segment_index + count, context->segment_count);

	if (context->segment_index > context->conf->bootstrap_segments_count)
	{
		context->last_boundary = context->conf->bootstrap_segments_total_duration +
			(context->segment_index - context->conf->bootstrap_segments_count) * context->conf->segment_duration;
	}
}
static vod_status_t
mp4_encrypt_audio_write_buffer(void* context, u_char* buffer, uint32_t size)
{
	mp4_encrypt_state_t* state = (mp4_encrypt_state_t*)context;
	u_char* buffer_end = buffer + size;
	u_char* cur_pos = buffer;
	uint32_t write_size;
	bool_t ignore;
	vod_status_t rc;

	while (cur_pos < buffer_end)
	{
		if (state->frame_size_left <= 0)
		{
			rc = mp4_encrypt_start_frame(state);
			if (rc != VOD_OK)
			{
				return rc;
			}
		}

		write_size = (uint32_t)(buffer_end - cur_pos);
		write_size = vod_min(write_size, state->frame_size_left);
		
		rc = mp4_encrypt_write_encrypted(state, cur_pos, write_size);
		if (rc != VOD_OK)
		{
			return rc;
		}

		cur_pos += write_size;
		state->frame_size_left -= write_size;

		if (state->frame_size_left > 0)
		{
			break;
		}

		// finished a frame
		if (!mp4_encrypt_move_to_next_frame(state, &ignore))
		{
			// finished all frames
			rc = write_buffer_flush(&state->write_buffer, FALSE);
			if (rc != VOD_OK)
			{
				return rc;
			}
		}
	}

	return VOD_OK;
}
vod_status_t
mp4_aes_ctr_process(mp4_aes_ctr_state_t* state, u_char* dest, const u_char* src, uint32_t size)
{
	const u_char* src_end = src + size;
	const u_char* cur_end_pos;
	u_char* encrypted_counter_pos;
	int out_size;

	while (src < src_end)
	{
		if (state->block_offset == 0)
		{
			if (1 != EVP_EncryptUpdate(
				&state->cipher,
				state->encrypted_counter,
				&out_size,
				state->counter,
				sizeof(state->counter)) ||
				out_size != sizeof(state->encrypted_counter))
			{
				vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
					"mp4_aes_ctr_process: EVP_EncryptUpdate failed");
				return VOD_UNEXPECTED;
			}

			mp4_aes_ctr_increment_be64(state->counter + 8);
		}

		encrypted_counter_pos = state->encrypted_counter + state->block_offset;
		cur_end_pos = src + MP4_AES_CTR_COUNTER_SIZE - state->block_offset;
		cur_end_pos = vod_min(cur_end_pos, src_end);

		state->block_offset += cur_end_pos - src;
		state->block_offset &= (MP4_AES_CTR_COUNTER_SIZE - 1);

		while (src < cur_end_pos)
		{
			*dest++ = *src++ ^ *encrypted_counter_pos++;
		}
	}

	return VOD_OK;
}
static int64_t
segmenter_align_to_key_frames(
	align_to_key_frames_context_t* context,
	int64_t offset,
	int64_t limit)
{
	int64_t cur_duration;

	for (;
		context->offset < offset;
		context->cur_pos++)
	{
		if ((void*)context->cur_pos >= context->part->last)
		{
			if (context->part->next == NULL)
			{
				return limit;
			}

			context->part = context->part->next;
			context->cur_pos = context->part->first;
		}

		cur_duration = *context->cur_pos;

		if (cur_duration <= 0 || cur_duration > MAX_CLIP_DURATION)
		{
			vod_log_error(VOD_LOG_WARN, context->request_context->log, 0,
				"segmenter_align_to_key_frames: ignoring invalid key frame duration %L", cur_duration);
			continue;
		}

		context->offset += cur_duration;
		if (context->offset >= limit)
		{
			return limit;
		}
	}

	return vod_min(context->offset, limit);
}
Beispiel #6
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 = vod_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;
}
bool_t 
read_cache_get_from_cache(
	read_cache_state_t* state, 
	read_cache_request_t* request,
	u_char** buffer,
	uint32_t* size)
{
	media_clip_source_t* source = request->source;
	cache_buffer_t* target_buffer;
	cache_buffer_t* cur_buffer;
	uint32_t read_size;
	uint64_t aligned_last_offset;
	uint64_t offset = request->cur_offset;
	size_t alignment;

	// check whether we already have the requested offset
	for (cur_buffer = state->buffers; cur_buffer < state->buffers_end; cur_buffer++)
	{
		if (cur_buffer->source == source && 
			offset >= cur_buffer->start_offset && offset < cur_buffer->end_offset)
		{
			*buffer = cur_buffer->buffer_pos + (offset - cur_buffer->start_offset);
			*size = cur_buffer->end_offset - offset;
			return TRUE;
		}
	}

	// don't have the offset in cache
	alignment = state->alignment - 1;
	target_buffer = &state->buffers[request->cache_slot_id % state->buffer_count];

	// start reading from the min offset, if that would contain the whole frame
	// Note: this condition is intended to optimize the case in which the frame order 
	//		in the output segment is <video1><audio1> while on disk it's <audio1><video1>. 
	//		in this case it would be better to start reading from the beginning, even 
	//		though the first frame that is requested is the second one
	if (request->min_offset < offset && 
		request->end_offset < (request->min_offset & ~alignment) + state->buffer_size)
	{
		offset = request->min_offset;
	}
	offset &= ~alignment;

	// calculate the read size
	read_size = state->buffer_size;

	// don't read anything that is already in the cache
	for (cur_buffer = state->buffers; cur_buffer < state->buffers_end; cur_buffer++)
	{
		if (cur_buffer != target_buffer &&
			cur_buffer->source == source &&
			cur_buffer->start_offset > offset)
		{
			read_size = vod_min(read_size, cur_buffer->start_offset - offset);
		}
	}

	// don't read past the max required offset
	if (offset + read_size > source->last_offset)
	{
		aligned_last_offset = (source->last_offset + alignment) & ~alignment;
		if (aligned_last_offset > offset)
		{
			read_size = aligned_last_offset - offset;
		}
	}

	target_buffer->source = source;
	target_buffer->start_offset = offset;
	target_buffer->buffer_size = read_size;
	state->target_buffer = target_buffer;

	return FALSE;
}
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;
}
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;
}
Beispiel #10
0
static vod_status_t
segmenter_get_segment_durations_estimate_internal(
	request_context_t* request_context,
	segmenter_conf_t* conf,
	uint32_t* clip_durations,
	uint32_t total_clip_count,
	segment_durations_t* result)
{
	segment_duration_item_t* cur_item;
	uint64_t clip_start_offset = 0;
	uint64_t ignore;
	uint32_t* end_duration = clip_durations + total_clip_count;
	uint32_t* cur_duration;
	uint32_t bootstrap_segment_limit;
	uint32_t segment_index = 0;
	uint32_t clip_segment_limit = 0;
	uint32_t clip_offset;
	uint32_t segment_duration;
	bool_t discontinuity;

	// allocate the result buffer
	result->items = vod_alloc(request_context->pool, sizeof(result->items[0]) * (conf->bootstrap_segments_count + 2 * total_clip_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 (cur_duration = clip_durations; cur_duration < end_duration; cur_duration++)
	{
		// get segment limit for the current clip
		clip_segment_limit = conf->get_segment_count(conf, clip_start_offset + *cur_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;
		}

		clip_offset = 0;

		// 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];
			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 += segment_duration;
		}

		// remaining segments
		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 += segment_duration * (clip_segment_limit - segment_index - 1);
			segment_index = clip_segment_limit - 1;
		}

		// last segment
		if (segment_index < clip_segment_limit && clip_offset < *cur_duration)
		{
			segment_duration = *cur_duration - 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;
		}

		// update clip_start_offset
		segmenter_get_start_end_offsets(conf, clip_segment_limit, &clip_start_offset, &ignore);

		discontinuity = TRUE;
	}

	// finalize the result
	result->segment_count = clip_segment_limit;
	result->item_count = cur_item + 1 - result->items;
	result->timescale = 1000;
	result->discontinuities = total_clip_count - 1;

	return VOD_OK;
}
Beispiel #11
0
static vod_status_t
mp4_decrypt_read(void* ctx, u_char** buffer, uint32_t* size, bool_t* frame_done)
{
	mp4_decrypt_state_t* state = ctx;
	vod_status_t rc;
	uint32_t cur_size;
	size_t buffer_size;

	// make sure there is some output space
	if (state->output_pos + MIN_BUFFER_SIZE >= state->output_end)
	{
		if (!state->reuse_buffers || state->output_start == NULL)
		{
			buffer_size = BUFFER_SIZE;
			state->output_start = buffer_pool_alloc(
				state->request_context, 
				state->request_context->output_buffer_pool, 
				&buffer_size);
			if (state->output_start == NULL)
			{
				vod_log_debug0(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
					"mp4_decrypt_read: vod_alloc failed");
				return VOD_ALLOC_FAILED;
			}
			state->output_end = state->output_start + buffer_size;
		}

		state->output_pos = state->output_start;
	}

	// make sure there is some input buffer
	if (state->input_size <= 0)
	{
		rc = state->frames_source->read(
			state->frames_source_context, 
			&state->input_pos, 
			&state->input_size, 
			&state->frame_done);
		if (rc != VOD_OK)
		{
			return rc;
		}
	}

	// process the min of input size and output size
	cur_size = state->output_end - state->output_pos;
	cur_size = vod_min(cur_size, state->input_size);
	state->input_size -= cur_size;

	*buffer = state->output_pos;
	*size = cur_size;
	*frame_done = state->input_size <= 0 ? state->frame_done : FALSE;

	rc = mp4_decrypt_process(state, cur_size);
	if (rc != VOD_OK)
	{
		return rc;
	}

	return VOD_OK;
}
Beispiel #12
0
static vod_status_t
mp4_decrypt_process(
	mp4_decrypt_state_t* state, 
	size_t size)
{
	u_char* dest = state->output_pos;
	u_char* src = state->input_pos;
	vod_status_t rc;
	size_t cur_size;

	while (size > 0)
	{
		if (state->clear_bytes <= 0 && state->encrypted_bytes <= 0)
		{
			// finished a subsample, read the next one
			if (state->subsample_count <= 0)
			{
				vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
					"mp4_decrypt_process: exhausted subsample bytes");
				return VOD_BAD_DATA;
			}

			if (state->auxiliary_info_pos + sizeof(cenc_sample_auxiliary_data_subsample_t) > state->auxiliary_info_end)
			{
				vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
					"mp4_decrypt_process: failed to get subsample info from auxiliary info");
				return VOD_BAD_DATA;
			}

			read_be16(state->auxiliary_info_pos, state->clear_bytes);
			read_be32(state->auxiliary_info_pos, state->encrypted_bytes);

			state->subsample_count--;
		}

		if (state->clear_bytes > 0)
		{
			// copy clear bytes
			cur_size = vod_min(state->clear_bytes, size);
			dest = vod_copy(dest, src, cur_size);
			src += cur_size;
			size -= cur_size;
			state->clear_bytes -= cur_size;
		}

		// decrypt encrypted bytes
		cur_size = vod_min(state->encrypted_bytes, size);
		rc = mp4_aes_ctr_process(&state->cipher, dest, src, cur_size);
		if (rc != VOD_OK)
		{
			return rc;
		}

		dest += cur_size;
		src += cur_size;
		size -= cur_size;
		state->encrypted_bytes -= cur_size;
	}

	state->output_pos = dest;
	state->input_pos = src;

	return VOD_OK;
}
vod_status_t
hds_muxer_process_frames(hds_muxer_state_t* state)
{
	u_char* read_buffer;
	uint32_t read_size;
	uint32_t write_size;
	uint64_t offset;
	vod_status_t rc;
	bool_t first_time = (state->cur_frame == NULL);
	bool_t wrote_data = FALSE;

	for (;;)
	{
		// start a new frame if we don't have a frame
		if (state->cur_frame == NULL)
		{
			rc = hds_muxer_start_frame(state);
			if (rc != VOD_OK)
			{
				vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
					"hds_muxer_process_frames: hds_muxer_start_frame failed %i", rc);
				return rc;
			}

			if (state->cur_frame == NULL)
			{
				break;		// done
			}
		}

		// read some data from the frame
		offset = state->cur_frame_offset + state->cur_frame_pos;
		if (!read_cache_get_from_cache(state->read_cache_state, state->cur_frame->size - state->cur_frame_pos, state->cache_slot_id, state->cur_file_index, offset, &read_buffer, &read_size))
		{
			if (!wrote_data && !first_time)
			{
				vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
					"hds_muxer_process_frames: no data was handled, probably a truncated file");
				return VOD_BAD_DATA;
			}
			return VOD_AGAIN;
		}

		wrote_data = TRUE;

		// write the frame
		write_size = vod_min(state->cur_frame->size - state->cur_frame_pos, read_size);
		rc = write_buffer_write(&state->write_buffer_state, read_buffer, write_size);
		if (rc != VOD_OK)
		{
			vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
				"hds_muxer_process_frames: write_buffer_write failed %i", rc);
			return rc;
		}
		state->cur_frame_pos += write_size;

		// flush the frame if we finished writing it
		if (state->cur_frame_pos >= state->cur_frame->size)
		{
			rc = hds_muxer_end_frame(state);
			if (rc != VOD_OK)
			{
				vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
					"hds_muxer_process_frames: write_buffer_write failed %i", rc);
				return rc;
			}

			state->cur_frame = NULL;
		}
	}

	// flush the buffer
	rc = write_buffer_flush(&state->write_buffer_state, FALSE);
	if (rc != VOD_OK)
	{
		vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->request_context->log, 0,
			"hds_muxer_process_frames: write_buffer_flush failed %i", rc);
		return rc;
	}
	return VOD_OK;
}
vod_status_t
sample_aes_avc_filter_write_nal_body(void* context, const u_char* buffer, uint32_t size)
{
	sample_aes_avc_filter_state_t* state = (sample_aes_avc_filter_state_t*)context;
	uint32_t end_offset;
	uint32_t cur_size;
	u_char encrypted_block[AES_BLOCK_SIZE];
	vod_status_t rc;
	int out_size;

	if (!state->encrypt)
	{
		return state->write_callback(state->write_context, buffer, size);
	}

	for (end_offset = state->cur_offset + size;
		state->cur_offset < end_offset;
		state->cur_offset += cur_size, buffer += cur_size)
	{
		if (state->cur_offset < state->next_encrypt_offset)
		{
			// unencrypted part
			cur_size = vod_min(state->next_encrypt_offset, end_offset) - state->cur_offset;
			rc = sample_aes_avc_write_emulation_prevention(state, buffer, cur_size);
			if (rc != VOD_OK)
			{
				return rc;
			}
			continue;
		}

		// encrypted block
		cur_size = vod_min(state->next_encrypt_offset + AES_BLOCK_SIZE, end_offset) - state->cur_offset;

		if (1 != EVP_EncryptUpdate(&state->cipher, encrypted_block, &out_size, buffer, cur_size))
		{
			vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
				"sample_aes_avc_filter_write_nal_body: EVP_EncryptUpdate failed");
			return VOD_UNEXPECTED;
		}

		if (out_size <= 0)
		{
			continue;
		}

		// write the encrypted block
		rc = sample_aes_avc_write_emulation_prevention(state, encrypted_block, AES_BLOCK_SIZE);
		if (rc != VOD_OK)
		{
			return rc;
		}

		// find the next encrypt offset
		state->next_encrypt_offset += ENCRYPTED_BLOCK_PERIOD * AES_BLOCK_SIZE;
		if (state->next_encrypt_offset >= state->max_encrypt_offset)
		{
			state->next_encrypt_offset = UINT_MAX;
		}
	}

	return VOD_OK;
}
Beispiel #15
0
static vod_status_t
mp4_encrypt_video_write_buffer(void* context, u_char* buffer, uint32_t size)
{
	mp4_encrypt_video_state_t* state = (mp4_encrypt_video_state_t*)context;
	vod_str_t fragment_header;
	u_char* buffer_end = buffer + size;
	u_char* cur_pos = buffer;
	u_char* output;
	uint32_t write_size;
	int32_t cur_shift;
	size_t ignore;
	bool_t init_track;
	vod_status_t rc;

	while (cur_pos < buffer_end)
	{
		switch (state->cur_state)
		{
		case STATE_PACKET_SIZE:
			if (state->base.frame_size_left <= 0)
			{
				rc = mp4_encrypt_video_start_frame(state);
				if (rc != VOD_OK)
				{
					return rc;
				}

				if (state->base.frame_size_left <= 0)
				{
					state->cur_state = STATE_PACKET_DATA;
					break;
				}
			}

			for (; state->length_bytes_left && cur_pos < buffer_end; state->length_bytes_left--)
			{
				state->packet_size_left = (state->packet_size_left << 8) | *cur_pos++;
			}

			if (cur_pos >= buffer_end)
			{
				break;
			}

			if (state->base.frame_size_left < state->nal_packet_size_length + state->packet_size_left)
			{
				vod_log_error(VOD_LOG_ERR, state->base.request_context->log, 0,
					"mp4_encrypt_video_write_buffer: frame size %uD too small, nalu size %uD packet size %uD",
					state->base.frame_size_left, state->nal_packet_size_length, state->packet_size_left);
				return VOD_BAD_DATA;
			}

			state->base.frame_size_left -= state->nal_packet_size_length + state->packet_size_left;

			state->cur_state++;
			// fall through

		case STATE_NAL_TYPE:

			// write the packet size and nal type
			rc = write_buffer_get_bytes(&state->base.write_buffer, state->nal_packet_size_length + 1, NULL, &output);
			if (rc != VOD_OK)
			{
				return rc;
			}

			for (cur_shift = (state->nal_packet_size_length - 1) * 8; cur_shift >= 0; cur_shift -= 8)
			{
				*output++ = (state->packet_size_left >> cur_shift) & 0xff;
			}

			*output++ = *cur_pos++;		// nal type

			// update the packet size
			if (state->packet_size_left <= 0)
			{
				vod_log_error(VOD_LOG_ERR, state->base.request_context->log, 0,
					"mp4_encrypt_video_write_buffer: zero size packet");
				return VOD_BAD_DATA;
			}
			state->packet_size_left--;

			// add the subsample
			rc = mp4_encrypt_video_add_subsample(state, state->nal_packet_size_length + 1, state->packet_size_left);
			if (rc != VOD_OK)
			{
				return rc;
			}

			state->cur_state++;
			// fall through

		case STATE_PACKET_DATA:
			write_size = (uint32_t)(buffer_end - cur_pos);
			write_size = vod_min(write_size, state->packet_size_left);
			
			rc = mp4_encrypt_write_encrypted(&state->base, cur_pos, write_size);
			if (rc != VOD_OK)
			{
				return rc;
			}

			cur_pos += write_size;
			state->packet_size_left -= write_size;
			if (state->packet_size_left > 0)
			{
				break;
			}

			// finished a packet
			state->cur_state = STATE_PACKET_SIZE;
			state->length_bytes_left = state->nal_packet_size_length;
			state->packet_size_left = 0;

			if (state->base.frame_size_left > 0)
			{
				break;
			}

			// finished a frame
			rc = mp4_encrypt_video_end_frame(state);
			if (rc != VOD_OK)
			{
				return rc;
			}

			// move to the next frame
			if (mp4_encrypt_move_to_next_frame(&state->base, &init_track))
			{
				if (init_track)
				{
					rc = mp4_encrypt_video_init_track(state, state->base.cur_clip->first_track);
					if (rc != VOD_OK)
					{
						return rc;
					}
				}

				break;
			}

			// finished all frames
			rc = write_buffer_flush(&state->base.write_buffer, FALSE);
			if (rc != VOD_OK)
			{
				return rc;
			}

			mp4_encrypt_video_prepare_saiz_saio(state);

			rc = state->build_fragment_header(state, &fragment_header, &ignore);
			if (rc != VOD_OK)
			{
				return rc;
			}

			rc = state->base.segment_writer.write_head(
				state->base.segment_writer.context,
				fragment_header.data,
				fragment_header.len);
			if (rc != VOD_OK)
			{
				vod_log_debug1(VOD_LOG_DEBUG_LEVEL, state->base.request_context->log, 0,
					"mp4_encrypt_video_write_buffer: write_head failed %i", rc);
				return rc;
			}

			break;
		}
	}

	return VOD_OK;
}
Beispiel #16
0
static vod_status_t
mp4_encrypt_video_snpf_write_buffer(void* context, u_char* buffer, uint32_t size)
{
	mp4_encrypt_video_state_t* state = (mp4_encrypt_video_state_t*)context;
	u_char* buffer_end = buffer + size;
	u_char* cur_pos = buffer;
	u_char* cur_end_pos;
	u_char* output;
	uint32_t write_size;
	bool_t init_track;
	vod_status_t rc;

	while (cur_pos < buffer_end)
	{
		switch (state->cur_state)
		{
		case STATE_CLEAR_BYTES:
			// start a new frame if needed
			if (state->base.frame_size_left <= 0)
			{
				rc = mp4_encrypt_start_frame(&state->base);
				if (rc != VOD_OK)
				{
					return rc;
				}

				if (state->base.frame_size_left <= 0)
				{
					state->cur_state = STATE_ENCRYPTED_BYTES;
					break;
				}
			}

			// copy the clear bytes
			write_size = (uint32_t)(buffer_end - cur_pos);
			write_size = vod_min(write_size, state->length_bytes_left + 1);

			rc = write_buffer_get_bytes(&state->base.write_buffer, write_size, NULL, &output);
			if (rc != VOD_OK)
			{
				return rc;
			}

			// copy the nalu length
			if (write_size >= state->length_bytes_left)
			{
				cur_end_pos = cur_pos + state->length_bytes_left;
				state->length_bytes_left = 0;
			}
			else
			{
				cur_end_pos = cur_pos + write_size;
				state->length_bytes_left -= write_size;
			}

			while (cur_pos < cur_end_pos)
			{
				state->packet_size_left = (state->packet_size_left << 8) | *cur_pos;
				*output++ = *cur_pos++;
			}

			if (cur_pos >= buffer_end)
			{
				break;
			}

			// copy the nalu type
			*output++ = *cur_pos++;

			if (state->base.frame_size_left < state->nal_packet_size_length + 1)
			{
				vod_log_error(VOD_LOG_ERR, state->base.request_context->log, 0,
					"mp4_encrypt_video_snpf_write_buffer: frame size %uD too small, nalu size %uD",
					state->base.frame_size_left, state->nal_packet_size_length);
				return VOD_BAD_DATA;
			}

			state->base.frame_size_left -= state->nal_packet_size_length;

			if (state->packet_size_left != state->base.frame_size_left && 
				!state->single_nalu_warning_printed)
			{
				vod_log_error(VOD_LOG_WARN, state->base.request_context->log, 0,
					"mp4_encrypt_video_snpf_write_buffer: frame does not contain a single nalu, "
					"consider changing vod_min_single_nalu_per_frame_segment, "
					"packet size=%uD, frame size=%uD",
					state->packet_size_left, state->base.frame_size_left);
				state->single_nalu_warning_printed = TRUE;
			}

			state->base.frame_size_left--;

			state->cur_state++;
			// fall through

		case STATE_ENCRYPTED_BYTES:
			write_size = (uint32_t)(buffer_end - cur_pos);
			write_size = vod_min(write_size, state->base.frame_size_left);

			rc = mp4_encrypt_write_encrypted(&state->base, cur_pos, write_size);
			if (rc != VOD_OK)
			{
				return rc;
			}

			cur_pos += write_size;

			state->base.frame_size_left -= write_size;
			if (state->base.frame_size_left > 0)
			{
				break;
			}

			// finished a packet
			state->cur_state = STATE_CLEAR_BYTES;
			state->length_bytes_left = state->nal_packet_size_length;
			state->packet_size_left = 0;

			// move to the next frame
			if (mp4_encrypt_move_to_next_frame(&state->base, &init_track))
			{
				if (init_track)
				{
					rc = mp4_encrypt_video_init_track(state, state->base.cur_clip->first_track);
					if (rc != VOD_OK)
					{
						return rc;
					}
				}

				break;
			}

			// finished all frames
			rc = write_buffer_flush(&state->base.write_buffer, FALSE);
			if (rc != VOD_OK)
			{
				return rc;
			}

			break;
		}
	}

	return VOD_OK;
}