ngx_int_t
ngx_http_vod_mss_parse_uri_file_name(
	ngx_http_request_t *r,
	ngx_http_vod_loc_conf_t *conf,
	u_char* start_pos,
	u_char* end_pos,
	ngx_http_vod_request_params_t* request_params)
{
	fragment_params_t fragment_params;
	ngx_int_t rc;

	// fragment
	if (end_pos[-1] == ')')
	{
		if (!ngx_http_vod_parse_string(fragment_match_definition, start_pos, end_pos, &fragment_params))
		{
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
				"ngx_http_vod_mss_parse_uri_file_name: ngx_http_vod_parse_string failed");
			return NGX_HTTP_BAD_REQUEST;
		}

		if (fragment_params.media_type.len != sizeof(MSS_STREAM_TYPE_VIDEO) - 1)
		{
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
				"ngx_http_vod_mss_parse_uri_file_name: invalid media type length %uz", fragment_params.media_type.len);
			return NGX_HTTP_BAD_REQUEST;
		}

		request_params->required_files = (1 << MSS_FILE_INDEX(fragment_params.bitrate));

		if (ngx_memcmp(fragment_params.media_type.data, MSS_STREAM_TYPE_VIDEO, sizeof(MSS_STREAM_TYPE_VIDEO) - 1) == 0)
		{
			request_params->required_tracks[MEDIA_TYPE_VIDEO] = (1 << MSS_TRACK_INDEX(fragment_params.bitrate));
		}
		else if (ngx_memcmp(fragment_params.media_type.data, MSS_STREAM_TYPE_AUDIO, sizeof(MSS_STREAM_TYPE_AUDIO) - 1) == 0)
		{
			request_params->required_tracks[MEDIA_TYPE_AUDIO] = (1 << MSS_TRACK_INDEX(fragment_params.bitrate));
		}
		else
		{
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
				"ngx_http_vod_mss_parse_uri_file_name: invalid media type %V", &fragment_params.media_type);
			return NGX_HTTP_BAD_REQUEST;
		}

		request_params->segment_index = segmenter_get_segment_index(&conf->segmenter, fragment_params.time / 10000);

		request_params->request = conf->drm_enabled ? &mss_playready_fragment_request : &mss_fragment_request;

		return NGX_OK;
	}

	// manifest
	if (!ngx_http_vod_starts_with(start_pos, end_pos, &conf->mss.manifest_file_name_prefix))
	{
		ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
			"ngx_http_vod_mss_parse_uri_file_name: unidentified request");
		return NGX_HTTP_BAD_REQUEST;
	}

	request_params->request = &mss_manifest_request;
	start_pos += conf->mss.manifest_file_name_prefix.len;

	rc = ngx_http_vod_parse_uri_file_name(r, start_pos, end_pos, FALSE, request_params);
	if (rc != NGX_OK)
	{
		ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
			"ngx_http_vod_mss_parse_uri_file_name: ngx_http_vod_parse_uri_file_name failed %i", rc);
		return rc;
	}

	return NGX_OK;
}
ngx_int_t
ngx_http_vod_mss_parse_uri_file_name(
	ngx_http_request_t *r,
	ngx_http_vod_loc_conf_t *conf,
	u_char* start_pos,
	u_char* end_pos,
	request_params_t* request_params,
	const ngx_http_vod_request_t** request)
{
	fragment_params_t fragment_params;
	ngx_int_t rc;

	// fragment
	if (end_pos[-1] == ')')
	{
		if (!ngx_http_vod_parse_string(fragment_match_definition, start_pos, end_pos, &fragment_params))
		{
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
				"ngx_http_vod_mss_parse_uri_file_name: ngx_http_vod_parse_string failed");
			return NGX_HTTP_BAD_REQUEST;
		}

		if (fragment_params.media_type.len != sizeof(MSS_STREAM_TYPE_VIDEO) - 1)
		{
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
				"ngx_http_vod_mss_parse_uri_file_name: invalid media type length %uz", fragment_params.media_type.len);
			return NGX_HTTP_BAD_REQUEST;
		}

		request_params->sequences_mask = (1 << mss_sequence_index(fragment_params.bitrate));

		if (ngx_memcmp(fragment_params.media_type.data, MSS_STREAM_TYPE_VIDEO, sizeof(MSS_STREAM_TYPE_VIDEO) - 1) == 0)
		{
			request_params->tracks_mask[MEDIA_TYPE_VIDEO] = (1 << mss_track_index(fragment_params.bitrate));
		}
		else if (ngx_memcmp(fragment_params.media_type.data, MSS_STREAM_TYPE_AUDIO, sizeof(MSS_STREAM_TYPE_AUDIO) - 1) == 0)
		{
			request_params->tracks_mask[MEDIA_TYPE_AUDIO] = (1 << mss_track_index(fragment_params.bitrate));
		}
		else
		{
			ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
				"ngx_http_vod_mss_parse_uri_file_name: invalid media type %V", &fragment_params.media_type);
			return NGX_HTTP_BAD_REQUEST;
		}

		request_params->segment_time = fragment_params.time / 10000;

		// Note: assuming no discontinuity, if this changes the segment index will be recalculated
		request_params->segment_index = segmenter_get_segment_index_no_discontinuity(
			&conf->segmenter, 
			request_params->segment_time);

		*request = conf->drm_enabled ? &mss_playready_fragment_request : &mss_fragment_request;

		return NGX_OK;
	}
	// manifest
	else if (ngx_http_vod_starts_with(start_pos, end_pos, &conf->mss.manifest_file_name_prefix))
	{
		*request = &mss_manifest_request;
		start_pos += conf->mss.manifest_file_name_prefix.len;

		rc = ngx_http_vod_parse_uri_file_name(r, start_pos, end_pos, FALSE, request_params);
		if (rc != NGX_OK)
		{
			ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
				"ngx_http_vod_mss_parse_uri_file_name: ngx_http_vod_parse_uri_file_name failed %i", rc);
			return rc;
		}

		return NGX_OK;
	}
	// fragment with non-standard format (used with redirect)
	else if (ngx_http_vod_match_prefix_postfix(start_pos, end_pos, &conf->hls.m3u8_config.segment_file_name_prefix, m4s_file_ext))
	{
		start_pos += conf->hls.m3u8_config.segment_file_name_prefix.len;
		end_pos -= (sizeof(m4s_file_ext) - 1);
		*request = conf->drm_enabled ? &mss_playready_fragment_request : &mss_fragment_request;

		return ngx_http_vod_parse_uri_file_name(r, start_pos, end_pos, TRUE, request_params);
	}
	else
	{
		ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
			"ngx_http_vod_mss_parse_uri_file_name: unidentified request");
		return NGX_HTTP_BAD_REQUEST;
	}
}