vod_status_t
read_cache_allocate_buffer_slots(read_cache_state_t* state, size_t buffer_count)
{
	size_t alloc_size;

	if (buffer_count < MIN_BUFFER_COUNT)
	{
		buffer_count = MIN_BUFFER_COUNT;
	}

	if (state->buffer_count >= buffer_count)
	{
		return VOD_OK;
	}

	alloc_size = sizeof(state->buffers[0]) * buffer_count;

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

	state->buffers_end = state->buffers + buffer_count;
	state->buffer_count = buffer_count;

	vod_memzero(state->buffers, alloc_size);

	return VOD_OK;
}
void 
read_cache_init(read_cache_state_t* state, request_context_t* request_context, size_t buffer_size, size_t alignment)
{
	vod_memzero(state, sizeof(*state));
	state->request_context = request_context;
	state->buffer_size = buffer_size;
	state->alignment = alignment;
}
void 
mp4_aes_ctr_set_iv(
	mp4_aes_ctr_state_t* state, 
	u_char* iv)
{
	vod_memcpy(state->counter, iv, MP4_AES_CTR_IV_SIZE);
	vod_memzero(state->counter + MP4_AES_CTR_IV_SIZE, sizeof(state->counter) - MP4_AES_CTR_IV_SIZE);
	state->block_offset = 0;
}
void 
mp4_aes_ctr_set_iv(
	mp4_aes_ctr_state_t* state, 
	u_char* iv)
{
	vod_memcpy(state->counter, iv, MP4_AES_CTR_IV_SIZE);
	vod_memzero(state->counter + MP4_AES_CTR_IV_SIZE, sizeof(state->counter) - MP4_AES_CTR_IV_SIZE);
	state->encrypted_pos = NULL;
	state->encrypted_end = NULL;
}
static void
ngx_http_vod_hls_init_encryption_iv(u_char* iv, uint32_t segment_index)
{
	u_char* p;

	// the IV is the segment index in big endian
	vod_memzero(iv, AES_BLOCK_SIZE - sizeof(uint32_t));
	segment_index++;
	p = iv + AES_BLOCK_SIZE - sizeof(uint32_t);
	*p++ = (u_char)(segment_index >> 24);
	*p++ = (u_char)(segment_index >> 16);
	*p++ = (u_char)(segment_index >> 8);
	*p++ = (u_char)(segment_index);
}
void
audio_filter_free_state(void* context)
{
	audio_filter_state_t* state = (audio_filter_state_t*)context;
	audio_filter_source_t* sources_cur;

	for (sources_cur = state->sources; sources_cur < state->sources_end; sources_cur++)
	{
		avcodec_close(sources_cur->decoder);
		av_free(sources_cur->decoder);
	}
	avcodec_close(state->sink.encoder);
	av_free(state->sink.encoder);
	avfilter_graph_free(&state->filter_graph);
	av_frame_free(&state->filtered_frame);
	av_frame_free(&state->decoded_frame);

	vod_memzero(state, sizeof(*state));		// support calling free twice
}
vod_status_t
mp4_decrypt_init(
	request_context_t* request_context,
	frames_source_t* frames_source,
	void* frames_source_context,
	u_char* key,
	media_encryption_t* encryption, 
	void** result)
{
	mp4_decrypt_state_t* state;
	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,
			"mp4_decrypt_init: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	vod_memzero(state, sizeof(*state));

	rc = mp4_aes_ctr_init(&state->cipher, request_context, key);
	if (rc != VOD_OK)
	{
		return rc;
	}

	vod_memcpy(state->key, key, sizeof(state->key));
	state->request_context = request_context;
	state->frames_source = frames_source;
	state->frames_source_context = frames_source_context;
	state->reuse_buffers = TRUE;

	state->auxiliary_info_pos = encryption->auxiliary_info;
	state->auxiliary_info_end = encryption->auxiliary_info_end;
	state->use_subsamples = encryption->use_subsamples;

	*result = state;

	return VOD_OK;
}
vod_status_t
adts_encoder_init(
    adts_encoder_state_t* state,
    request_context_t* request_context,
    hls_encryption_params_t* encryption_params,
    const media_filter_t* next_filter,
    void* next_filter_context,
    const u_char* extra_data,
    uint32_t extra_data_size)
{
    mp4a_config_t codec_config;
    vod_status_t rc;

    state->next_filter = next_filter;
    state->next_filter_context = next_filter_context;

    if (request_context->simulation_only)
    {
        return VOD_OK;
    }

    if (encryption_params->type == HLS_ENC_SAMPLE_AES)
    {
        rc = sample_aes_aac_filter_init(
                 &state->sample_aes_context,
                 request_context,
                 next_filter->write,
                 next_filter_context,
                 encryption_params->key,
                 encryption_params->iv);
        if (rc != VOD_OK)
        {
            return rc;
        }

        state->body_write = sample_aes_aac_filter_write_frame_body;
        state->body_write_context = state->sample_aes_context;
    }
    else
    {
        state->sample_aes_context = NULL;

        state->body_write = next_filter->write;
        state->body_write_context = next_filter_context;
    }

    rc = codec_config_mp4a_config_parse(request_context, extra_data, extra_data_size, &codec_config);
    if (rc != VOD_OK)
    {
        return rc;
    }

    // Note: not parsing all the special cases handled in ffmpeg's avpriv_mpeg4audio_get_config
    // Note: not handling pce_data

    vod_log_debug3(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
                   "adts_encoder_init: object_type=%d sample_rate_index=%d channel_config=%d",
                   codec_config.object_type, codec_config.sample_rate_index, codec_config.channel_config);

    vod_memzero(&state->header, sizeof(state->header));

    adts_frame_header_set_syncword(state->header, 0xfff);
    adts_frame_header_set_protection_absent(state->header, 1);
    adts_frame_header_set_profile_object_type(state->header, codec_config.object_type - 1);
    adts_frame_header_set_sample_rate_index(state->header, codec_config.sample_rate_index);
    adts_frame_header_set_channel_configuration(state->header, codec_config.channel_config);
    adts_frame_header_set_adts_buffer_fullness(state->header, 0x7ff);

    return VOD_OK;
}
vod_status_t
edash_packager_get_fragment_writer(
	segment_writer_t* result,
	request_context_t* request_context,
	media_set_t* media_set,
	uint32_t segment_index,
	bool_t single_nalu_per_frame,
	segment_writer_t* segment_writer,
	const u_char* iv,
	bool_t size_only,
	vod_str_t* fragment_header,
	size_t* total_fragment_size)
{
	dash_fragment_header_extensions_t header_extensions;
	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,
			"edash_packager_get_fragment_writer: using encryption passthrough");

		// get the header extensions
		header_extensions.extra_traf_atoms_size = passthrough_context.total_size + ATOM_HEADER_SIZE + sizeof(senc_atom_t);
		header_extensions.write_extra_traf_atoms_callback = edash_packager_passthrough_write_encryption_atoms;
		header_extensions.write_extra_traf_atoms_context = &passthrough_context;

		// build the fragment header
		rc = dash_packager_build_fragment_header(
			request_context,
			media_set,
			segment_index,
			0,
			&header_extensions,
			size_only,
			fragment_header,
			total_fragment_size);
		if (rc != VOD_OK)
		{
			vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"edash_packager_get_fragment_writer: dash_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, 
			single_nalu_per_frame,
			edash_packager_video_build_fragment_header,
			segment_writer, 
			iv, 
			fragment_header,
			total_fragment_size);

	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 = edash_packager_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,
		"edash_packager_get_fragment_writer: invalid media type %uD", media_type);
	return VOD_UNEXPECTED;
}
static ngx_int_t
ngx_http_vod_extract_uri_params(
	ngx_http_request_t* r,
	ngx_hash_t* params_hash,
	ngx_str_t* uri,
	media_sequence_t* sequence,
	uint32_t* clip_id,
	media_clip_source_t* source_clip,
	media_clip_t** result)
{
	ngx_http_vod_uri_param_def_t* param_def = NULL;
	request_context_t request_context;
	media_clip_t* rate_filter = NULL;
	ngx_uint_t  cur_key_hash = 0;
	ngx_str_t cur_param;
	ngx_int_t rc;
	uint32_t parsed_params_mask = 0;
	uint32_t param_index;
	u_char param_name[MAX_URI_PARAM_NAME_LEN + 1];
	u_char* param_name_end = param_name + sizeof(param_name);
	u_char* param_name_pos = param_name;
	u_char* copy_start = uri->data;
	u_char* cur_pos;
	u_char* end_pos = uri->data + uri->len;
	u_char* last_slash = NULL;
	u_char* p;

	// set the source defaults
	vod_memzero(source_clip, sizeof(*source_clip));

	source_clip->base.type = MEDIA_CLIP_SOURCE;
	source_clip->base.id = (*clip_id)++;

	source_clip->clip_to = UINT_MAX;
	source_clip->tracks_mask[MEDIA_TYPE_AUDIO] = 0xffffffff;
	source_clip->tracks_mask[MEDIA_TYPE_VIDEO] = 0xffffffff;
	source_clip->uri = *uri;
	source_clip->sequence = sequence;
	
	*result = &source_clip->base;

	// allocate the stripped uri
	p = ngx_palloc(r->pool, uri->len + 1);
	if (p == NULL)
	{
		ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
			"ngx_http_vod_extract_uri_params: ngx_palloc failed (1)");
		return NGX_HTTP_INTERNAL_SERVER_ERROR;
	}
	source_clip->stripped_uri.data = p;

	for (cur_pos = uri->data; cur_pos <= end_pos; cur_pos++)
	{
		if (cur_pos < end_pos && *cur_pos != '/')
		{
			if (param_name_pos < param_name_end)
			{
				*param_name_pos = ngx_tolower(*cur_pos);
				cur_key_hash = ngx_hash(cur_key_hash, *param_name_pos);
				param_name_pos++;
			}
			continue;
		}

		if (last_slash == NULL)
		{
			last_slash = cur_pos;
			cur_key_hash = 0;
			param_name_pos = param_name;
			continue;
		}

		if (param_def == NULL)
		{
			param_def = ngx_hash_find(params_hash, cur_key_hash, param_name, param_name_pos - param_name);
			if (param_def != NULL)
			{
				p = ngx_copy(p, copy_start, last_slash - copy_start);
				copy_start = last_slash;
			}
		}
		else
		{
			param_index = param_def - uri_param_defs;
			if ((parsed_params_mask & (1 << param_index)) == 0)		// first instance of a param takes priority
			{
				parsed_params_mask |= (1 << param_index);
				cur_param.data = last_slash + 1;
				cur_param.len = cur_pos - (last_slash + 1);

				if (param_def->name_conf_offset == offsetof(ngx_http_vod_loc_conf_t, speed_param_name))
				{
					request_context.pool = r->pool;
					request_context.log = r->connection->log;

					rc = rate_filter_create_from_string(
						&request_context,
						&cur_param,
						&source_clip->base, 
						&rate_filter);
					if (rc != VOD_OK)
					{
						return ngx_http_vod_status_to_ngx_error(rc);
					}

					rate_filter->id = (*clip_id)++;
					*result = rate_filter;
				}
				else
				{
					rc = param_def->parser(&cur_param, source_clip, param_def->target_offset);
					if (rc != NGX_OK)
					{
						ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
							"ngx_http_vod_extract_uri_params: %s parser failed %i", param_def->name, rc);
						return rc;
					}
				}
			}
			copy_start = cur_pos;
			param_def = NULL;
		}

		last_slash = cur_pos;
		cur_key_hash = 0;
		param_name_pos = param_name;
	}

	if (source_clip->clip_from >= source_clip->clip_to)
	{
		ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
			"ngx_http_vod_extract_uri_params: clip from %uD is larger than clip to %uD", source_clip->clip_from, source_clip->clip_to);
		return NGX_HTTP_BAD_REQUEST;
	}

	p = ngx_copy(p, copy_start, end_pos - copy_start);
	*p = '\0';

	source_clip->stripped_uri.len = p - source_clip->stripped_uri.data;
	source_clip->mapped_uri = source_clip->stripped_uri;

	return NGX_OK;
}
vod_status_t
audio_filter_alloc_state(
	request_context_t* request_context,
	media_sequence_t* sequence,
	media_clip_t* clip,
	media_track_t* output_track,
	size_t* cache_buffer_count,
	void** result)
{
	audio_filter_init_context_t init_context;
	u_char filter_name[VOD_INT32_LEN + 1];
	audio_filter_state_t* state;
	vod_pool_cleanup_t *cln;
	AVFilterInOut *outputs = NULL;
	AVFilterInOut *inputs = NULL;
	uint32_t initial_alloc_size;
	vod_status_t rc;
	int avrc;

	if (!initialized)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: module failed to initialize successfully");
		return VOD_UNEXPECTED;
	}

	// get the source count and graph desc size
	init_context.request_context = request_context;
	init_context.graph_desc_size = 0;
	init_context.source_count = 0;
	init_context.output_frame_count = 0;

	rc = audio_filter_walk_filters_prepare_init(&init_context, &clip, 100, 100);
	if (rc != VOD_OK)
	{
		return rc;
	}

	if (clip == NULL || init_context.source_count <= 0)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: unexpected - no sources found");
		return VOD_UNEXPECTED;
	}

	if (clip->type == MEDIA_CLIP_SOURCE)
	{
		// got left with a source, following a mix of a single source, nothing to do
		return VOD_OK;
	}

	if (init_context.output_frame_count > MAX_FRAME_COUNT)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: expected output frame count %uD too big", init_context.output_frame_count);
		return VOD_BAD_REQUEST;
	}

	// allocate the state
	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	vod_memzero(state, sizeof(*state));
	
	// add to the cleanup pool
	cln = vod_pool_cleanup_add(request_context->pool, 0);
	if (cln == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_pool_cleanup_add failed");
		return VOD_ALLOC_FAILED;
	}

	cln->handler = audio_filter_free_state;
	cln->data = state;

	// allocate the filter graph
	state->filter_graph = avfilter_graph_alloc();
	if (state->filter_graph == NULL)
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_filter_alloc_state: avfilter_graph_alloc failed");
		return VOD_ALLOC_FAILED;
	}

	// allocate the graph desc and sources
	init_context.graph_desc = vod_alloc(request_context->pool, init_context.graph_desc_size + 
		sizeof(state->sources[0]) * init_context.source_count);
	if (init_context.graph_desc == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	state->sources = (void*)(init_context.graph_desc + init_context.graph_desc_size);
	state->sources_end = state->sources + init_context.source_count;
	vod_memzero(state->sources, (u_char*)state->sources_end - (u_char*)state->sources);

	// initialize the sources and the graph description
	init_context.filter_graph = state->filter_graph;
	init_context.outputs = &outputs;
	init_context.cur_source = state->sources;
	init_context.graph_desc_pos = init_context.graph_desc;
	init_context.max_frame_size = 0;
	init_context.cache_slot_id = 0;

	rc = audio_filter_init_sources_and_graph_desc(&init_context, clip);
	if (rc != VOD_OK)
	{
		goto end;
	}

	*init_context.graph_desc_pos = '\0';

	// initialize the sink
	vod_sprintf(filter_name, "%uD%Z", clip->id);

	rc = audio_filter_init_sink(
		request_context,
		state->filter_graph,
		output_track,
		filter_name,
		&state->sink,
		&inputs);
	if (rc != VOD_OK)
	{
		goto end;
	}

	// parse the graph description
	avrc = avfilter_graph_parse_ptr(state->filter_graph, (char*)init_context.graph_desc, &inputs, &outputs, NULL);
	if (avrc < 0)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avfilter_graph_parse_ptr failed %d", avrc);
		rc = VOD_UNEXPECTED;
		goto end;
	}

	// validate and configure the graph
	avrc = avfilter_graph_config(state->filter_graph, NULL);
	if (avrc < 0)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avfilter_graph_config failed %d", avrc);
		rc = VOD_UNEXPECTED;
		goto end;
	}

	// set the buffer sink frame size
	if ((state->sink.encoder->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) == 0)
	{
		av_buffersink_set_frame_size(state->sink.buffer_sink, state->sink.encoder->frame_size);
	}
	
	// allocate frames
	state->decoded_frame = av_frame_alloc();
	if (state->decoded_frame == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: av_frame_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}
	state->filtered_frame = av_frame_alloc();
	if (state->filtered_frame == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: av_frame_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}

	// allocate the frame buffer
	state->frame_buffer = vod_alloc(request_context->pool, init_context.max_frame_size);
	if (state->frame_buffer == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_alloc failed (2)");
		rc = VOD_ALLOC_FAILED;
		goto end;
	}

	// initialize the output arrays
	initial_alloc_size = init_context.output_frame_count + 10;

	if (vod_array_init(&state->frames_array, request_context->pool, initial_alloc_size, sizeof(input_frame_t)) != VOD_OK)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_array_init failed (1)");
		return VOD_ALLOC_FAILED;
	}

	state->request_context = request_context;
	state->sequence = sequence;
	state->output = output_track;
	state->cur_frame_pos = 0;
	state->first_time = TRUE;
	state->cur_source = NULL;

	*cache_buffer_count = init_context.cache_slot_id;
	*result = state;

end:

	avfilter_inout_free(&inputs);
	avfilter_inout_free(&outputs);

	return rc;
}
static vod_status_t 
audio_filter_process_frame(audio_filter_state_t* state, u_char* buffer)
{
	audio_filter_source_t* source = state->cur_source;
	input_frame_t* frame = source->cur_frame;
	AVPacket input_packet;
	int got_frame;
	int avrc;
#ifdef AUDIO_FILTER_DEBUG
	size_t data_size;
#endif // AUDIO_FILTER_DEBUG
	
#ifdef AUDIO_FILTER_DEBUG
	audio_filter_append_debug_data("input", "aac", buffer, frame->size);
#endif // AUDIO_FILTER_DEBUG
	
	vod_memzero(&input_packet, sizeof(input_packet));
	input_packet.data = buffer;
	input_packet.size = frame->size;
	input_packet.dts = state->dts;
	input_packet.pts = state->dts + frame->pts_delay;
	input_packet.duration = frame->duration;
	input_packet.flags = AV_PKT_FLAG_KEY;
	state->dts += frame->duration;
	
	av_frame_unref(state->decoded_frame);

	got_frame = 0;
	avrc = avcodec_decode_audio4(source->decoder, state->decoded_frame, &got_frame, &input_packet);
	if (avrc < 0) 
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_filter_process_frame: avcodec_decode_audio4 failed %d", avrc);
		return VOD_BAD_DATA;
	}

	if (!got_frame)
	{
		return VOD_OK;
	}

#ifdef AUDIO_FILTER_DEBUG
	data_size = av_samples_get_buffer_size(
		NULL, 
		source->decoder->channels,
		state->decoded_frame->nb_samples,
		source->decoder->sample_fmt,
		1);
	audio_filter_append_debug_data(source->buffer_src->name, "pcm", state->decoded_frame->data[0], data_size);
#endif // AUDIO_FILTER_DEBUG
	
	avrc = av_buffersrc_add_frame_flags(source->buffer_src, state->decoded_frame, AV_BUFFERSRC_FLAG_PUSH);
	if (avrc < 0) 
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_filter_process_frame: av_buffersrc_add_frame_flags failed %d", avrc);
		return VOD_ALLOC_FAILED;
	}

	return audio_filter_read_filter_sink(state);
}
ngx_int_t
ngx_http_vod_parse_uri_path(
	ngx_http_request_t* r,
	ngx_str_t* multi_uri_suffix,
	ngx_hash_t* params_hash,
	ngx_str_t* uri,
	request_params_t* request_params,
	media_set_t* media_set)
{
	media_sequence_t* cur_sequence;
	media_clip_source_t* cur_source;
	media_clip_source_t* sources_head;
	ngx_http_vod_multi_uri_t multi_uri;
	media_clip_t** cur_clip_ptr;
	media_clip_t* cur_clip;
	ngx_str_t parts[3];
	ngx_str_t cur_uri;
	ngx_int_t rc;
	uint32_t sequences_mask;
	uint32_t parts_mask;
	uint32_t media_type;
	uint32_t clip_id = 1;
	uint32_t i;
	bool_t has_tracks;
	int uri_count;

	media_set->uri = *uri;		// must save the uri before calling ngx_http_vod_parse_multi_uri as it may change

	multi_uri.parts_count = 0;

	rc = ngx_http_vod_parse_multi_uri(r, uri, multi_uri_suffix, &multi_uri);
	if (rc != NGX_OK)
	{
		ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
			"ngx_http_vod_parse_uri_path: ngx_http_vod_parse_multi_uri failed %i", rc);
		return rc;
	}

	if (multi_uri.parts_count > 1 && 
		request_params->sequence_ids[0].len == 0)
	{
		sequences_mask = request_params->sequences_mask;
		request_params->sequences_mask = 0xffffffff;	// reset the sequences mask so that it won't be applied again on the mapping request
	}
	else
	{
		sequences_mask = 0xffffffff;
	}

	parts_mask = (1 << multi_uri.parts_count) - 1;
	
	uri_count = vod_get_number_of_set_bits(sequences_mask & parts_mask);
	if (uri_count == 0)
	{
		ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
			"ngx_http_vod_parse_uri_path: request has no uris");
		return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST);
	}

	cur_sequence = ngx_palloc(r->pool,
		(sizeof(*cur_sequence) + sizeof(*cur_source) + sizeof(*cur_clip_ptr)) * uri_count);
	if (cur_sequence == NULL)
	{
		ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
			"ngx_http_vod_parse_uri_path: ngx_palloc failed");
		return ngx_http_vod_status_to_ngx_error(r, VOD_ALLOC_FAILED);
	}
	media_set->sequences = cur_sequence;

	cur_source = (void*)(cur_sequence + uri_count);

	cur_clip_ptr = (void*)(cur_source + uri_count);

	sources_head = NULL;

	parts[0] = multi_uri.prefix;
	parts[2] = multi_uri.postfix;

	for (i = 0; i < multi_uri.parts_count; i++)
	{
		if ((sequences_mask & (1 << i)) == 0)
		{
			continue;
		}

		cur_sequence->id.len = 0;
		cur_sequence->language = 0;
		cur_sequence->label.len = 0;
		cur_sequence->first_key_frame_offset = 0;
		cur_sequence->key_frame_durations = NULL;
		cur_sequence->drm_info = NULL;
		vod_memzero(cur_sequence->bitrate, sizeof(cur_sequence->bitrate));

		parts[1] = multi_uri.middle_parts[i];
		rc = ngx_http_vod_merge_string_parts(r, parts, 3, &cur_uri);
		if (rc != NGX_OK)
		{
			ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
				"ngx_http_vod_parse_uri_path: ngx_http_vod_merge_string_parts failed %i", rc);
			return rc;
		}

		rc = ngx_http_vod_extract_uri_params(r, params_hash, &cur_uri, cur_sequence, &clip_id, cur_source, &cur_clip);
		if (rc != NGX_OK)
		{
			ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
				"ngx_http_vod_parse_uri_path: ngx_http_vod_extract_uri_params failed %i", rc);
			return rc;
		}

		has_tracks = FALSE;
		for (media_type = 0; media_type < MEDIA_TYPE_COUNT; media_type++)
		{
			if ((cur_source->tracks_mask[media_type] & request_params->tracks_mask[media_type]) != 0)
			{
				has_tracks = TRUE;
				break;
			}
		}

		if (!has_tracks)
		{
			continue;
		}
		
		*cur_clip_ptr = cur_clip;

		cur_source->next = sources_head;
		sources_head = cur_source;

		cur_sequence->clips = cur_clip_ptr;
		cur_sequence->index = i;
		cur_sequence->stripped_uri = cur_source->stripped_uri;
		cur_sequence->mapped_uri = cur_source->stripped_uri;

		cur_source++;
		cur_sequence++;
		cur_clip_ptr++;
	}

	// need to test again since we filtered sub uris that didn't have any required tracks
	media_set->sequence_count = cur_sequence - media_set->sequences;
	if (media_set->sequence_count <= 0)
	{
		ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
			"ngx_http_vod_parse_uri_path: request has no uris after track filtering");
		return ngx_http_vod_status_to_ngx_error(r, VOD_BAD_REQUEST);
	}

	media_set->sources_head = sources_head;
	media_set->sequences_end = cur_sequence;
	media_set->has_multi_sequences = (multi_uri.parts_count > 1);
	media_set->timing.total_count = 1;
	media_set->clip_count = 1;
	media_set->presentation_end = TRUE;

	return NGX_OK;
}
vod_status_t 
audio_filter_process_frame(void* context, input_frame_t* frame, u_char* buffer)
{
	audio_filter_state_t* state = (audio_filter_state_t*)context;
	vod_status_t rc;
	AVPacket output_packet;
	AVPacket input_packet;
	int got_packet;
	int got_frame;
	int ret;
#ifdef AUDIO_FILTER_DEBUG
	size_t data_size;
#endif // AUDIO_FILTER_DEBUG
	
	if (frame == NULL)
	{
		return audio_filter_flush_encoder(state);
	}

#ifdef AUDIO_FILTER_DEBUG
	audio_filter_append_debug_data(AUDIO_FILTER_DEBUG_FILENAME_INPUT, buffer, frame->size);
#endif // AUDIO_FILTER_DEBUG
	
	vod_memzero(&input_packet, sizeof(input_packet));
	input_packet.data = buffer;
	input_packet.size = frame->size;
	input_packet.dts = state->dts;
	input_packet.pts = (state->dts + frame->pts_delay);
	input_packet.duration = frame->duration;
	input_packet.flags = AV_PKT_FLAG_KEY;
	state->dts += frame->duration;
	
	avcodec_get_frame_defaults(state->decoded_frame);

	got_frame = 0;
	ret = avcodec_decode_audio4(state->decoder, state->decoded_frame, &got_frame, &input_packet);
	if (ret < 0) 
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_filter_process_frame: avcodec_decode_audio4 failed %d", ret);
		return VOD_BAD_DATA;
	}

	if (!got_frame)
	{
		return VOD_OK;
	}

#ifdef AUDIO_FILTER_DEBUG
	data_size = av_samples_get_buffer_size(
		NULL, 
		state->decoder->channels,
		state->decoded_frame->nb_samples,
		state->decoder->sample_fmt, 
		1);
	audio_filter_append_debug_data(AUDIO_FILTER_DEBUG_FILENAME_DECODED, state->decoded_frame->data[0], data_size);
#endif // AUDIO_FILTER_DEBUG
	
	ret = av_buffersrc_add_frame_flags(state->buffer_src, state->decoded_frame, AV_BUFFERSRC_FLAG_PUSH);
	if (ret < 0) 
	{
		vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
			"audio_filter_process_frame: av_buffersrc_add_frame_flags failed %d", ret);
		return VOD_ALLOC_FAILED;
	}

	for (;;)
	{
		ret = av_buffersink_get_frame_flags(state->buffer_sink, state->filtered_frame, AV_BUFFERSINK_FLAG_NO_REQUEST);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		{
			break;
		}
		
		if (ret < 0)
		{
			vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
				"audio_filter_process_frame: av_buffersink_get_frame_flags failed %d", ret);
			return VOD_UNEXPECTED;
		}

#ifdef AUDIO_FILTER_DEBUG
		data_size = av_samples_get_buffer_size(
			NULL, 
			state->encoder->channels,
			state->filtered_frame->nb_samples,
			state->encoder->sample_fmt, 
			1);
		audio_filter_append_debug_data(AUDIO_FILTER_DEBUG_FILENAME_FILTERED, state->filtered_frame->data[0], data_size);
#endif // AUDIO_FILTER_DEBUG

		av_init_packet(&output_packet);
		output_packet.data = NULL; // packet data will be allocated by the encoder
		output_packet.size = 0;

		got_packet = 0;
		ret = avcodec_encode_audio2(state->encoder, &output_packet, state->filtered_frame, &got_packet);
		if (ret < 0)
		{
			vod_log_error(VOD_LOG_ERR, state->request_context->log, 0,
				"audio_filter_process_frame: avcodec_encode_audio2 failed %d", ret);
			return VOD_ALLOC_FAILED;
		}
		
		if (got_packet)
		{
			rc = audio_filter_write_frame(state, &output_packet);

			av_free_packet(&output_packet);
			
			if (rc != VOD_OK)
			{
				return rc;
			}
		}
		
		av_frame_unref(state->filtered_frame);
	}
	
	return VOD_OK;
}
vod_status_t
audio_filter_alloc_state(
	request_context_t* request_context,
	mpeg_stream_metadata_t* stream_metadata,
	void** result)
{
	char filter_desc[sizeof(ATEMPO_FILTER_DESCRIPTION) + 2 * VOD_INT64_LEN];
	audio_filter_state_t* state;
	uint32_t initial_alloc_size;
	mp4a_config_t codec_config;
	vod_status_t rc;
	int ret;
	
	if (!initialized)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: module failed to initialize successfully");
		return VOD_UNEXPECTED;
	}

	if (stream_metadata->media_info.speed_denom != 10)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: unexpected speed denom, must be 10");
		return VOD_UNEXPECTED;
	}

	// parse the codec config
	rc = codec_config_mp4a_config_parse(
		request_context, 
		stream_metadata->media_info.extra_data,
		stream_metadata->media_info.extra_data_size,
		&codec_config);
	if (rc != VOD_OK)
	{
		return rc;
	}

	// allocate the state
	state = vod_alloc(request_context->pool, sizeof(*state));
	if (state == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_alloc failed");
		return VOD_ALLOC_FAILED;
	}
	vod_memzero(state, sizeof(*state));
	
	state->request_context = request_context;
	state->speed_nom = stream_metadata->media_info.speed_nom;
	state->speed_denom = stream_metadata->media_info.speed_denom;
	state->output = stream_metadata;

	// init the decoder	
	state->decoder = avcodec_alloc_context3(decoder_codec);
	if (state->decoder == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avcodec_alloc_context3 failed (1)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}

	state->decoder->codec_tag = stream_metadata->media_info.format;
	state->decoder->bit_rate = stream_metadata->media_info.bitrate;
	state->decoder->time_base.num = 1;
	// Note: changing the timescale in order to revert the speed change that was applied to the frames while parsing the mp4
	state->decoder->time_base.den = stream_metadata->media_info.timescale / stream_metadata->media_info.speed_nom * stream_metadata->media_info.speed_denom;
	state->decoder->pkt_timebase = state->decoder->time_base;
	state->decoder->extradata = (u_char*)stream_metadata->media_info.extra_data;
	state->decoder->extradata_size = stream_metadata->media_info.extra_data_size;
	state->decoder->channels = stream_metadata->media_info.u.audio.channels;
	state->decoder->bits_per_coded_sample = stream_metadata->media_info.u.audio.bits_per_sample;
	state->decoder->sample_rate = stream_metadata->media_info.u.audio.sample_rate;
	state->decoder->channel_layout = 0;
	if (codec_config.channel_config < ARRAY_ENTRIES(aac_channel_layout))
	{
		state->decoder->channel_layout = aac_channel_layout[codec_config.channel_config];
	}
	
	ret = avcodec_open2(state->decoder, decoder_codec, NULL);
	if (ret < 0) 
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avcodec_open2(decoder) failed %d", ret);
		rc = VOD_UNEXPECTED;
		goto error;
	}
	
	// init the encoder
	state->encoder = avcodec_alloc_context3(encoder_codec);
	if (state->encoder == NULL) 
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avcodec_alloc_context3 failed (2)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}

	state->encoder->sample_fmt = ENCODER_INPUT_SAMPLE_FORMAT;
	state->encoder->sample_rate = state->decoder->sample_rate;
	state->encoder->channel_layout = state->decoder->channel_layout;
	state->encoder->channels = state->decoder->channels;
	state->encoder->bit_rate = state->decoder->bit_rate;
	state->encoder->flags |= CODEC_FLAG_GLOBAL_HEADER;		// make the codec generate the extra data

	ret = avcodec_open2(state->encoder, encoder_codec, NULL);
	if (ret < 0) 
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: avcodec_open2(encoder) failed %d", ret);
		rc = VOD_UNEXPECTED;
		goto error;
	}
	
	// allocate frames
	state->decoded_frame = av_frame_alloc();
	if (state->decoded_frame == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: av_frame_alloc failed (1)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}
	state->filtered_frame = av_frame_alloc();
	if (state->filtered_frame == NULL)
	{
		vod_log_error(VOD_LOG_ERR, request_context->log, 0,
			"audio_filter_alloc_state: av_frame_alloc failed (2)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}

	// initialize the output arrays
	initial_alloc_size = (stream_metadata->frame_count * stream_metadata->media_info.speed_denom) / stream_metadata->media_info.speed_nom + 10;
	
	if (vod_array_init(&state->frames_array, request_context->pool, initial_alloc_size, sizeof(input_frame_t)) != VOD_OK)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_array_init failed (1)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}
	
	if (vod_array_init(&state->frame_offsets_array, request_context->pool, initial_alloc_size, sizeof(uint64_t)) != VOD_OK)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"audio_filter_alloc_state: vod_array_init failed (2)");
		rc = VOD_ALLOC_FAILED;
		goto error;
	}
	
	// initialize the filter graph
	vod_sprintf(filter_desc, 
		ATEMPO_FILTER_DESCRIPTION, 
		(int)(stream_metadata->media_info.speed_nom / 10), 
		(int)(stream_metadata->media_info.speed_nom % 10));

	rc = audio_filter_init_filters(state, stream_metadata, filter_desc);
	if (rc != VOD_OK)
	{
		goto error;
	}
	
	*result = state;
	
	return VOD_OK;

error:

	audio_filter_free_state(state);

	return rc;
}
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;
}
Beispiel #17
0
vod_status_t
filter_init_filtered_clips(
	request_context_t* request_context,
	media_set_t* media_set)
{
	filters_init_state_t init_state;
	media_clip_filtered_t* output_clip;
	media_sequence_t* sequence;
	media_clip_t** clips_end;
	media_clip_t** cur_clip;
	media_clip_t* input_clip;
	media_track_t* new_track;
	uint32_t track_count[MEDIA_TYPE_COUNT];
	uint32_t max_duration = 0;
	uint32_t clip_index;

	media_set->audio_filtering_needed = FALSE;
	media_set->track_count[MEDIA_TYPE_VIDEO] = 0;
	media_set->track_count[MEDIA_TYPE_AUDIO] = 0;

	// allocate the filtered clips
	output_clip = vod_alloc(
		request_context->pool,
		sizeof(output_clip[0]) * media_set->sequence_count * media_set->clip_count);
	if (output_clip == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"filter_init_filtered_clips: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}

	for (sequence = media_set->sequences;
		sequence < media_set->sequences_end;
		sequence++)
	{
		// init sequence counters
		sequence->total_frame_size = 0;
		sequence->video_key_frame_count = 0;
		sequence->total_frame_count = 0;

		// get max number of tracks in the clips of the sequence
		sequence->track_count[MEDIA_TYPE_VIDEO] = 0;
		sequence->track_count[MEDIA_TYPE_AUDIO] = 0;

		clips_end = sequence->clips + media_set->clip_count;
		for (cur_clip = sequence->clips; cur_clip < clips_end; cur_clip++)
		{
			track_count[MEDIA_TYPE_VIDEO] = 0;
			track_count[MEDIA_TYPE_AUDIO] = 0;
			filter_get_clip_track_count(*cur_clip, track_count);

			if (cur_clip[0]->type != MEDIA_CLIP_SOURCE && track_count[MEDIA_TYPE_AUDIO] > 1)
			{
				track_count[MEDIA_TYPE_AUDIO] = 1;		// audio filtering supports only a single output track
			}

			if (cur_clip == sequence->clips)
			{
				sequence->track_count[MEDIA_TYPE_VIDEO] = track_count[MEDIA_TYPE_VIDEO];
				sequence->track_count[MEDIA_TYPE_AUDIO] = track_count[MEDIA_TYPE_AUDIO];
			}
			else if (sequence->track_count[MEDIA_TYPE_VIDEO] != track_count[MEDIA_TYPE_VIDEO] ||
				sequence->track_count[MEDIA_TYPE_AUDIO] != track_count[MEDIA_TYPE_AUDIO])
			{
				vod_log_error(VOD_LOG_ERR, request_context->log, 0,
					"filter_init_filtered_clips: track count mismatch, first clip had v=%uD,a=%uD current clip has v=%uD,a=%uD",
					sequence->track_count[MEDIA_TYPE_VIDEO], sequence->track_count[MEDIA_TYPE_AUDIO],
					track_count[MEDIA_TYPE_VIDEO], track_count[MEDIA_TYPE_AUDIO]);
				return VOD_BAD_MAPPING;
			}
		}

		// update the media set total track count
		media_set->track_count[MEDIA_TYPE_VIDEO] += sequence->track_count[MEDIA_TYPE_VIDEO];
		media_set->track_count[MEDIA_TYPE_AUDIO] += sequence->track_count[MEDIA_TYPE_AUDIO];
		sequence->total_track_count = sequence->track_count[MEDIA_TYPE_VIDEO] + sequence->track_count[MEDIA_TYPE_AUDIO];

		// set the sequence media type
		if (sequence->total_track_count == 1)
		{
			sequence->media_type = sequence->track_count[MEDIA_TYPE_VIDEO] > 0 ? MEDIA_TYPE_VIDEO : MEDIA_TYPE_AUDIO;
		}
		else
		{
			sequence->media_type = MEDIA_TYPE_NONE;
		}

		// initialize the filtered clips array
		sequence->filtered_clips = output_clip;
		output_clip += media_set->clip_count;
		sequence->filtered_clips_end = output_clip;
	}

	// allocate the output tracks
	media_set->total_track_count = media_set->track_count[MEDIA_TYPE_VIDEO] + media_set->track_count[MEDIA_TYPE_AUDIO];
	init_state.output_track = vod_alloc(
		request_context->pool,
		sizeof(*init_state.output_track)* media_set->total_track_count * media_set->clip_count);
	if (init_state.output_track == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"filter_init_filtered_clips: vod_alloc failed (2)");
		return VOD_ALLOC_FAILED;
	}
	media_set->filtered_tracks = init_state.output_track;

	for (clip_index = 0; clip_index < media_set->clip_count; clip_index++)
	{
		for (sequence = media_set->sequences;
			sequence < media_set->sequences_end;
			sequence++)
		{
			input_clip = sequence->clips[clip_index];
			output_clip = &sequence->filtered_clips[clip_index];

			output_clip->first_track = init_state.output_track;

			output_clip->longest_track[MEDIA_TYPE_VIDEO] = NULL;
			output_clip->longest_track[MEDIA_TYPE_AUDIO] = NULL;

			// initialize the state
			init_state.sequence = sequence;
			init_state.output_clip = output_clip;
			init_state.audio_reference_track = NULL;

			// in case of source, just copy all tracks as is
			if (input_clip->type == MEDIA_CLIP_SOURCE)
			{
				filter_init_filtered_clip_from_source(&init_state, (media_clip_source_t*)input_clip);
			}
			else
			{
				init_state.has_audio_frames = FALSE;
				init_state.source_count = 0;

				filter_scale_video_tracks(&init_state, input_clip, 100, 100);

				if (init_state.source_count != 1)
				{
					// got more than one mvhd, clear it
					vod_memzero(&output_clip->mvhd_atom, sizeof(output_clip->mvhd_atom));
				}
			}

			if (init_state.audio_reference_track != NULL)
			{
				// add the audio filter output track
				new_track = filter_copy_track_to_clip(&init_state, init_state.audio_reference_track);
				if (init_state.audio_reference_track_speed_nom != init_state.audio_reference_track_speed_denom)
				{
					rate_filter_scale_track_timestamps(
						new_track, 
						init_state.audio_reference_track_speed_nom, 
						init_state.audio_reference_track_speed_denom);
				}

				if (init_state.has_audio_frames)
				{
					new_track->source_clip = input_clip;
					media_set->audio_filtering_needed = TRUE;
				}
			}

			output_clip->last_track = init_state.output_track;

			// calculate the max duration, only relevant in case of single clip
			if (output_clip->longest_track[MEDIA_TYPE_VIDEO] != NULL &&
				output_clip->longest_track[MEDIA_TYPE_VIDEO]->media_info.duration_millis > max_duration)
			{
				max_duration = output_clip->longest_track[MEDIA_TYPE_VIDEO]->media_info.duration_millis;
			}

			if (output_clip->longest_track[MEDIA_TYPE_AUDIO] != NULL &&
				output_clip->longest_track[MEDIA_TYPE_AUDIO]->media_info.duration_millis > max_duration)
			{
				max_duration = output_clip->longest_track[MEDIA_TYPE_AUDIO]->media_info.duration_millis;
			}
		}
	}

	media_set->filtered_tracks_end = init_state.output_track;

	if (media_set->durations == NULL)
	{
		media_set->total_duration = max_duration;
	}

	return VOD_OK;
}
Beispiel #18
0
vod_status_t
rate_filter_parse(
	void* ctx,
	vod_json_value_t* element,
	void** result)
{
	media_filter_parse_context_t* context = ctx;
	media_clip_rate_filter_t* filter;
	media_range_t* new_range;
	media_range_t* old_range;
	vod_json_value_t* params[RATE_FILTER_PARAM_COUNT];
	vod_json_value_t* source;
	vod_json_value_t* rate;
	uint32_t old_duration;
	vod_status_t rc;

	vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0,
		"rate_filter_parse: started");

	vod_memzero(params, sizeof(params));
		
	vod_json_get_object_values(
		element,
		&rate_filter_hash,
		params);

	rate = params[RATE_FILTER_PARAM_RATE];
	source = params[RATE_FILTER_PARAM_SOURCE];

	if (rate == NULL || source == NULL)
	{
		vod_log_error(VOD_LOG_ERR, context->request_context->log, 0,
			"rate_filter_parse: \"rate\" and \"source\" are mandatory for rate filter");
		return VOD_BAD_MAPPING;
	}

	if (rate->v.num.denom > 100)
	{
		vod_log_error(VOD_LOG_ERR, context->request_context->log, 0,
			"rate_filter_parse: invalid rate, only 2 decimal points are allowed");
		return VOD_BAD_MAPPING;
	}
	
	if (rate->v.num.nom < 0 ||
		rate->v.num.denom > (uint64_t)rate->v.num.nom * 2 || (uint64_t)rate->v.num.nom > rate->v.num.denom * 2)
	{
		vod_log_error(VOD_LOG_ERR, context->request_context->log, 0,
			"rate_filter_parse: invalid rate %L/%uL, must be between 0.5 and 2", rate->v.num.nom, rate->v.num.denom);
		return VOD_BAD_MAPPING;
	}

	filter = vod_alloc(context->request_context->pool, sizeof(*filter) + sizeof(filter->base.sources[0]));
	if (filter == NULL)
	{
		vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0,
			"rate_filter_parse: vod_alloc failed (1)");
		return VOD_ALLOC_FAILED;
	}
	filter->base.sources = (void*)(filter + 1);
	filter->base.source_count = 1;

	filter->base.type = MEDIA_CLIP_RATE_FILTER;
	filter->base.audio_filter = &rate_filter;
	filter->rate.nom = rate->v.num.nom;
	filter->rate.denom = rate->v.num.denom;

	old_range = context->range;
	if (old_range != NULL)
	{
		new_range = vod_alloc(context->request_context->pool, sizeof(*new_range));
		if (new_range == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0,
				"rate_filter_parse: vod_alloc failed (2)");
			return VOD_ALLOC_FAILED;
		}

		new_range->start = (old_range->start * filter->rate.nom) / filter->rate.denom;
		new_range->end = (old_range->end * filter->rate.nom) / filter->rate.denom;
		new_range->timescale = old_range->timescale;

		context->range = new_range;
	}

	old_duration = context->duration;
	context->duration = ((uint64_t)old_duration * filter->rate.nom) / filter->rate.denom;

	rc = media_set_parse_clip(
		context, 
		source, 
		&filter->base,
		&filter->base.sources[0]);
	if (rc != VOD_JSON_OK)
	{
		return rc;
	}

	context->range = old_range;
	context->duration = old_duration;

	*result = &filter->base;

	vod_log_debug2(VOD_LOG_DEBUG_LEVEL, context->request_context->log, 0,
		"rate_filter_parse: done, rate=%uD/%uD", filter->rate.nom, filter->rate.denom);

	return VOD_OK;
}
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;
}
Beispiel #20
0
vod_status_t
edash_packager_build_mpd(
	request_context_t* request_context,
	dash_manifest_config_t* conf,
	vod_str_t* base_url,
	media_set_t* media_set,
	bool_t drm_single_key,
	vod_str_t* result)
{
	write_content_protection_context_t context;
	dash_manifest_extensions_t extensions;
	media_sequence_t* cur_sequence;
	drm_system_info_t* cur_info;
	tags_writer_t content_prot_writer;
	drm_info_t* drm_info;
	size_t representation_tags_size;
	size_t cur_drm_tags_size;
	size_t cur_pssh_size;
	size_t max_pssh_size = 0;
	vod_status_t rc;

	representation_tags_size = 0;

	for (cur_sequence = media_set->sequences; cur_sequence < media_set->sequences_end; cur_sequence++)
	{
		drm_info = (drm_info_t*)cur_sequence->drm_info;

		cur_drm_tags_size = sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC) - 1;

		for (cur_info = drm_info->pssh_array.first; cur_info < drm_info->pssh_array.last; cur_info++)
		{
			if (vod_memcmp(cur_info->system_id, edash_playready_system_id, sizeof(edash_playready_system_id)) == 0)
			{
				cur_drm_tags_size +=
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_V2_PART1) - 1 +
					VOD_GUID_LENGTH +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_V2_PART2) - 1 +
					VOD_GUID_LENGTH +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_PART3) - 1 +
					vod_base64_encoded_length(cur_info->data.len) +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_PLAYREADY_PART4) - 1;
			}
			else
			{
				cur_pssh_size = ATOM_HEADER_SIZE + sizeof(pssh_atom_t) + cur_info->data.len;
				if (cur_pssh_size > max_pssh_size)
				{
					max_pssh_size = cur_pssh_size;
				}

				cur_drm_tags_size +=
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART1) - 1 +
					VOD_GUID_LENGTH +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART2) - 1 +
					VOD_GUID_LENGTH +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART3) - 1 +
					vod_base64_encoded_length(cur_pssh_size) +
					sizeof(VOD_EDASH_MANIFEST_CONTENT_PROTECTION_CENC_PART4) - 1;

				continue;
			}
		}

		representation_tags_size += cur_drm_tags_size * cur_sequence->total_track_count;
	}

	context.write_playready_kid = conf->write_playready_kid;
	if (max_pssh_size > 0)
	{
		context.temp_buffer = vod_alloc(request_context->pool, max_pssh_size);
		if (context.temp_buffer == NULL)
		{
			vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
				"edash_packager_build_mpd: vod_alloc failed");
			return VOD_ALLOC_FAILED;
		}
	}

	content_prot_writer.size = representation_tags_size;
	content_prot_writer.write = edash_packager_write_content_protection;
	content_prot_writer.context = &context;

	if (drm_single_key)
	{
		// write the ContentProtection tags under AdaptationSet
		extensions.adaptation_set = content_prot_writer;
		vod_memzero(&extensions.representation, sizeof(extensions.representation));
	}
	else
	{
		// write the ContentProtection tags under Representation
		vod_memzero(&extensions.adaptation_set, sizeof(extensions.adaptation_set));
		extensions.representation = content_prot_writer;
	}

	rc = dash_packager_build_mpd(
		request_context,
		conf,
		base_url,
		media_set,
		&extensions,
		result);
	if (rc != VOD_OK)
	{
		vod_log_debug1(VOD_LOG_DEBUG_LEVEL, request_context->log, 0,
			"edash_packager_build_mpd: dash_packager_build_mpd failed %i", rc);
		return rc;
	}

	return VOD_OK;
}